From d46e81bc30d8152bd9ac5954d2294c287254f7ff Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Mon, 4 Aug 2025 14:55:07 +0200 Subject: [PATCH] MINOR: connection/server: refactor idle conn purge --- include/haproxy/connection-t.h | 10 +-- include/haproxy/server-t.h | 2 +- include/haproxy/server.h | 7 +- src/backend.c | 33 ++++---- src/cfgparse.c | 24 ------ src/connection.c | 57 ++++++++++--- src/haproxy.c | 4 +- src/mux_fcgi.c | 18 ++-- src/mux_h1.c | 24 +++--- src/mux_h2.c | 34 ++++---- src/mux_spop.c | 22 ++--- src/server.c | 150 ++++++++++++++++++--------------- src/session.c | 28 +++--- src/ssl_sock.c | 10 +-- 14 files changed, 222 insertions(+), 201 deletions(-) diff --git a/include/haproxy/connection-t.h b/include/haproxy/connection-t.h index 571943131..6bb47c6bd 100644 --- a/include/haproxy/connection-t.h +++ b/include/haproxy/connection-t.h @@ -608,7 +608,7 @@ struct connection { struct wait_event *subs; /* Task to wake when awaited events are ready */ union { struct list idle_list; /* list element for idle connection in server idle list */ - struct mt_list toremove_list; /* list element when idle connection is ready to be purged */ + struct mt_list purge_el; /* list element when idle connection is ready to be purged */ }; union { struct list sess_el; /* used by private backend conns, list elem into session */ @@ -772,11 +772,11 @@ union mux_sctl_dbg_str_ctx { * accessible from foreign threads. */ struct idle_conns { - struct mt_list toremove_conns; - struct task *cleanup_task; - __decl_thread(HA_SPINLOCK_T idle_conns_lock); -} THREAD_ALIGNED(64); + struct mt_list purged_list; + struct task *task_free_purged; + __decl_thread(HA_SPINLOCK_T lock); +} THREAD_ALIGNED(64); /* Termination events logs: * Each event is stored on 8 bits: 4 bits bor the event location and diff --git a/include/haproxy/server-t.h b/include/haproxy/server-t.h index 8f15c21e9..204483a08 100644 --- a/include/haproxy/server-t.h +++ b/include/haproxy/server-t.h @@ -379,7 +379,7 @@ struct server { * thread, and generally at the same time. */ THREAD_ALIGN(64); - struct eb32_node idle_node; /* When to next do cleanup in the idle connections */ + struct eb32_node purge_node; /* When to next do cleanup in the idle connections */ unsigned int curr_idle_conns; /* Current number of orphan idling connections, both the idle and the safe lists */ unsigned int curr_idle_nb; /* Current number of connections in the idle list */ unsigned int curr_safe_nb; /* Current number of connections in the safe list */ diff --git a/include/haproxy/server.h b/include/haproxy/server.h index 9d401ffc9..7503f4b35 100644 --- a/include/haproxy/server.h +++ b/include/haproxy/server.h @@ -38,9 +38,9 @@ #include -__decl_thread(extern HA_SPINLOCK_T idle_conn_srv_lock); -extern struct idle_conns idle_conns[MAX_THREADS]; -extern struct task *idle_conn_task; +__decl_thread(extern HA_SPINLOCK_T purge_conns_lock); +extern struct task *task_purge_servers; + extern struct mt_list servers_list; extern struct dict server_key_dict; @@ -98,7 +98,6 @@ struct connection *srv_lookup_conn_next(struct connection *conn); void _srv_add_idle(struct server *srv, struct connection *conn, int is_safe); int srv_add_to_idle_list(struct server *srv, struct connection *conn, int is_safe); void srv_add_to_avail_list(struct server *srv, struct connection *conn); -struct task *srv_cleanup_toremove_conns(struct task *task, void *context, unsigned int state); int srv_apply_track(struct server *srv, struct proxy *curproxy); diff --git a/src/backend.c b/src/backend.c index 3b4aef681..dc836348c 100644 --- a/src/backend.c +++ b/src/backend.c @@ -1310,7 +1310,7 @@ struct connection *conn_backend_get(int reuse_mode, * to end up with two threads using the same connection. */ i = tid; - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); conn = srv_lookup_conn(is_safe ? &srv->per_thr[tid].safe_conns : &srv->per_thr[tid].idle_conns, hash); if (conn) conn_delete_from_tree(conn); @@ -1325,7 +1325,7 @@ struct connection *conn_backend_get(int reuse_mode, is_safe = 1; } } - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); /* If we found a connection in our own list, and we don't have to * steal one from another thread, then we're done. @@ -1364,7 +1364,7 @@ check_tgid: if (!srv->curr_idle_thr[i] || i == tid) continue; - if (HA_SPIN_TRYLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock) != 0) + if (HA_SPIN_TRYLOCK(IDLE_CONNS_LOCK, &idle_conns[i].lock) != 0) continue; conn = srv_lookup_conn(is_safe ? &srv->per_thr[i].safe_conns : &srv->per_thr[i].idle_conns, hash); while (conn) { @@ -1392,7 +1392,7 @@ check_tgid: conn = srv_lookup_conn_next(conn); } } - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].lock); } while (!found && (i = (i + 1 == curtg->base + curtg->count) ? curtg->base : i + 1) != stop); if (!found && (global.tune.tg_takeover == FULL_THREADGROUP_TAKEOVER || @@ -1517,12 +1517,12 @@ kill_random_idle_conn(struct server *srv) for (i = 0; i < global.nbthread; i++) { curtid = (i + tid) % global.nbthread; - if (HA_SPIN_TRYLOCK(IDLE_CONNS_LOCK, &idle_conns[curtid].idle_conns_lock) != 0) + if (HA_SPIN_TRYLOCK(IDLE_CONNS_LOCK, &idle_conns[curtid].lock) != 0) continue; conn = takeover_random_idle_conn(&srv->per_thr[curtid].idle_conns, curtid); if (!conn) conn = takeover_random_idle_conn(&srv->per_thr[curtid].safe_conns, curtid); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[curtid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[curtid].lock); if (conn) break; } @@ -1744,9 +1744,9 @@ int be_reuse_connection(int64_t hash, struct session *sess, if (avail <= 1) { /* no more streams available, remove it from the list */ - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); conn_delete_from_tree(srv_conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } if (avail >= 1) { @@ -1848,11 +1848,11 @@ int connect_server(struct stream *s) /* We have more FDs than deemed acceptable, attempt to kill an idling connection. */ struct connection *tokill_conn = NULL; /* First, try from our own idle list */ - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); if (!LIST_ISEMPTY(&srv->per_thr[tid].idle_conn_list)) { tokill_conn = LIST_ELEM(srv->per_thr[tid].idle_conn_list.n, struct connection *, idle_list); conn_delete_from_tree(tokill_conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); /* Release the idle lock before calling mux->destroy. * It will in turn call srv_release_conn through @@ -1861,7 +1861,7 @@ int connect_server(struct stream *s) tokill_conn->mux->destroy(tokill_conn->ctx); } else { - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } /* If not, iterate over other thread's idling pool, and try to grab one */ @@ -1875,21 +1875,20 @@ int connect_server(struct stream *s) // see it possibly larger. ALREADY_CHECKED(i); - if (HA_SPIN_TRYLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock) != 0) + if (HA_SPIN_TRYLOCK(IDLE_CONNS_LOCK, &idle_conns[i].lock) != 0) continue; if (!LIST_ISEMPTY(&srv->per_thr[i].idle_conn_list)) { tokill_conn = LIST_ELEM(srv->per_thr[i].idle_conn_list.n, struct connection *, idle_list); conn_delete_from_tree(tokill_conn); } - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].lock); if (tokill_conn) { /* We got one, put it into the concerned thread's to kill list, and wake it's kill task */ - - MT_LIST_APPEND(&idle_conns[i].toremove_conns, - &tokill_conn->toremove_list); - task_wakeup(idle_conns[i].cleanup_task, TASK_WOKEN_OTHER); + MT_LIST_APPEND(&idle_conns[i].purged_list, + &tokill_conn->purge_el); + task_wakeup(idle_conns[i].task_free_purged, TASK_WOKEN_OTHER); break; } diff --git a/src/cfgparse.c b/src/cfgparse.c index 040e35549..5109ce0b7 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -4201,30 +4201,6 @@ out_uri_auth_compat: /* At this point, target names have already been resolved. */ /***********************************************************/ - idle_conn_task = task_new_anywhere(); - if (!idle_conn_task) { - ha_alert("parsing : failed to allocate global idle connection task.\n"); - cfgerr++; - } - else { - idle_conn_task->process = srv_cleanup_idle_conns; - idle_conn_task->context = NULL; - - for (i = 0; i < global.nbthread; i++) { - idle_conns[i].cleanup_task = task_new_on(i); - if (!idle_conns[i].cleanup_task) { - ha_alert("parsing : failed to allocate idle connection tasks for thread '%d'.\n", i); - cfgerr++; - break; - } - - idle_conns[i].cleanup_task->process = srv_cleanup_toremove_conns; - idle_conns[i].cleanup_task->context = NULL; - HA_SPIN_INIT(&idle_conns[i].idle_conns_lock); - MT_LIST_INIT(&idle_conns[i].toremove_conns); - } - } - /* perform the final checks before creating tasks */ /* starting to initialize the main proxies list */ diff --git a/src/connection.c b/src/connection.c index 65a45b90f..0323df9a1 100644 --- a/src/connection.c +++ b/src/connection.c @@ -76,7 +76,7 @@ struct conn_tlv_list *conn_get_tlv(struct connection *conn, int type) /* Remove idle connection from its attached tree (idle, safe or avail). * If also present in the secondary server idle list, conn is removed from it. * - * Must be called with idle_conns_lock held. + * Must be called with lock held. */ void conn_delete_from_tree(struct connection *conn) { @@ -225,9 +225,9 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake) struct server *srv = objt_server(conn->target); if (conn_in_list) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); conn_delete_from_tree(conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } ret = conn->mux->wake(conn); @@ -250,9 +250,9 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake) } } else { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } } } @@ -505,7 +505,7 @@ void conn_init(struct connection *conn, void *target) conn->target = target; conn->destroy_cb = NULL; conn->proxy_netns = NULL; - MT_LIST_INIT(&conn->toremove_list); + MT_LIST_INIT(&conn->purge_el); if (conn_is_back(conn)) LIST_INIT(&conn->sess_el); else @@ -602,13 +602,13 @@ void conn_free(struct connection *conn) if (conn_is_back(conn)) conn_backend_deinit(conn); - /* Remove the conn from toremove_list. + /* Remove the conn from purge list. * * This is needed to prevent a double-free in case the connection was * already scheduled from cleaning but is freed before via another * call. */ - MT_LIST_DELETE(&conn->toremove_list); + MT_LIST_DELETE(&conn->purge_el); sockaddr_free(&conn->src); sockaddr_free(&conn->dst); @@ -3022,6 +3022,21 @@ int conn_reverse(struct connection *conn) return 0; } +/* Handler for task_free_purged of idle_conns. Free all connections registered + * in the purge list of the current thread. + */ +static struct task *free_purged_conns(struct task *task, void *context, unsigned int state) +{ + struct connection *conn; + + while ((conn = MT_LIST_POP(&idle_conns[tid].purged_list, + struct connection *, purge_el)) != NULL) { + conn->mux->destroy(conn->ctx); + } + + return task; +} + /* Handler of the task of mux_stopping_data. * Called on soft-stop. */ @@ -3060,12 +3075,28 @@ static int deallocate_mux_cleanup(void) } REGISTER_PER_THREAD_FREE(deallocate_mux_cleanup); -static void deinit_idle_conns(void) +static int allocate_idle_conns(void) +{ + idle_conns[tid].task_free_purged = task_new_on(tid); + if (!idle_conns[tid].task_free_purged) { + ha_alert("Failed to allocate idle connection tasks for thread '%d'.\n", tid); + return 0; + } + + idle_conns[tid].task_free_purged->process = free_purged_conns; + idle_conns[tid].task_free_purged->context = NULL; + HA_SPIN_INIT(&idle_conns[tid].lock); + MT_LIST_INIT(&idle_conns[tid].purged_list); + + return 1; +} +REGISTER_PER_THREAD_ALLOC(allocate_idle_conns); + +static void deinit_gc_purged_conns(void) { int i; - for (i = 0; i < global.nbthread; i++) { - task_destroy(idle_conns[i].cleanup_task); - } + for (i = 0; i < global.nbthread; i++) + task_destroy(idle_conns[i].task_free_purged); } -REGISTER_POST_DEINIT(deinit_idle_conns); +REGISTER_POST_DEINIT(deinit_gc_purged_conns); diff --git a/src/haproxy.c b/src/haproxy.c index e0f196a34..b2c69c2b1 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2694,8 +2694,8 @@ void deinit(void) ha_free(&global.server_state_base); ha_free(&global.server_state_file); ha_free(&global.stats_file); - task_destroy(idle_conn_task); - idle_conn_task = NULL; + task_destroy(task_purge_servers); + task_purge_servers = NULL; list_for_each_entry_safe(log, logb, &global.loggers, list) { LIST_DEL_INIT(&log->list); diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c index 5b0ec5ba3..fd4f7d4fb 100644 --- a/src/mux_fcgi.c +++ b/src/mux_fcgi.c @@ -3048,13 +3048,13 @@ struct task *fcgi_io_cb(struct task *t, void *ctx, unsigned int state) /* the tasklet was idling on an idle connection, it might have * been stolen, let's be careful! */ - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); if (tl->context == NULL) { /* The connection has been taken over by another thread, * we're no longer responsible for it, so just free the * tasklet, and do nothing. */ - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); tasklet_free(tl); return NULL; } @@ -3069,7 +3069,7 @@ struct task *fcgi_io_cb(struct task *t, void *ctx, unsigned int state) conn_delete_from_tree(conn); } - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } else { /* we're certain the connection was not in an idle list */ conn = fconn->conn; @@ -3104,9 +3104,9 @@ struct task *fcgi_io_cb(struct task *t, void *ctx, unsigned int state) } } else { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } } else { @@ -3302,19 +3302,19 @@ struct task *fcgi_timeout_task(struct task *t, void *context, unsigned int state TRACE_ENTER(FCGI_EV_FCONN_WAKE, (fconn ? fconn->conn : NULL)); if (fconn) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); /* Somebody already stole the connection from us, so we should not * free it, we just have to free the task. */ if (!t->context) { - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); fconn = NULL; goto do_leave; } if (!expired) { - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); TRACE_DEVEL("leaving (not expired)", FCGI_EV_FCONN_WAKE, fconn->conn); return t; } @@ -3327,7 +3327,7 @@ struct task *fcgi_timeout_task(struct task *t, void *context, unsigned int state else if (fconn->conn->flags & CO_FL_SESS_IDLE) conn_delete_from_sess(fconn->conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); fcgi_conn_report_term_evt(fconn, muxc_tevt_type_tout); } diff --git a/src/mux_h1.c b/src/mux_h1.c index 59768e3d3..dc75843ff 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -4290,13 +4290,13 @@ struct task *h1_io_cb(struct task *t, void *ctx, unsigned int state) /* the tasklet was idling on an idle connection, it might have * been stolen, let's be careful! */ - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); if (tl->context == NULL) { /* The connection has been taken over by another thread, * we're no longer responsible for it, so just free the * tasklet, and do nothing. */ - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); tasklet_free(tl); return NULL; } @@ -4314,7 +4314,7 @@ struct task *h1_io_cb(struct task *t, void *ctx, unsigned int state) conn_delete_from_tree(conn); } - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } else { /* we're certain the connection was not in an idle list */ conn = h1c->conn; @@ -4349,9 +4349,9 @@ struct task *h1_io_cb(struct task *t, void *ctx, unsigned int state) } } else { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } } else { @@ -4393,19 +4393,19 @@ struct task *h1_timeout_task(struct task *t, void *context, unsigned int state) if (h1c) { /* Make sure nobody stole the connection from us */ - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); /* Somebody already stole the connection from us, so we should not * free it, we just have to free the task. */ if (!t->context) { h1c = NULL; - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); goto do_leave; } if (!expired) { - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); TRACE_DEVEL("leaving (not expired)", H1_EV_H1C_WAKE, h1c->conn, h1c->h1s); return t; } @@ -4414,7 +4414,7 @@ struct task *h1_timeout_task(struct task *t, void *context, unsigned int state) * stream's timeout */ if (h1c->state == H1_CS_RUNNING) { - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); t->expire = TICK_ETERNITY; TRACE_DEVEL("leaving (SC still attached)", H1_EV_H1C_WAKE, h1c->conn, h1c->h1s); return t; @@ -4427,7 +4427,7 @@ struct task *h1_timeout_task(struct task *t, void *context, unsigned int state) h1_send(h1c); if (b_data(&h1c->obuf) || (h1c->flags & H1C_F_ABRT_PENDING)) { h1_refresh_timeout(h1c); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); return t; } } @@ -4438,7 +4438,7 @@ struct task *h1_timeout_task(struct task *t, void *context, unsigned int state) se_fl_set(h1c->h1s->sd, SE_FL_EOS | SE_FL_ERROR); h1_alert(h1c->h1s); h1_refresh_timeout(h1c); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); TRACE_DEVEL("waiting to release the SC before releasing the connection", H1_EV_H1C_WAKE); return t; } @@ -4449,7 +4449,7 @@ struct task *h1_timeout_task(struct task *t, void *context, unsigned int state) if (h1c->conn->flags & CO_FL_LIST_MASK) conn_delete_from_tree(h1c->conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); h1c_report_term_evt(h1c, muxc_tevt_type_tout); } diff --git a/src/mux_h2.c b/src/mux_h2.c index 16e86aff8..be605fe2b 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -4946,13 +4946,13 @@ struct task *h2_io_cb(struct task *t, void *ctx, unsigned int state) /* the tasklet was idling on an idle connection, it might have * been stolen, let's be careful! */ - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); if (t->context == NULL) { /* The connection has been taken over by another thread, * we're no longer responsible for it, so just free the * tasklet, and do nothing. */ - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); tasklet_free(tl); t = NULL; goto leave; @@ -4971,7 +4971,7 @@ struct task *h2_io_cb(struct task *t, void *ctx, unsigned int state) conn_delete_from_tree(conn); } - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } else { /* we're certain the connection was not in an idle list */ conn = h2c->conn; @@ -5009,9 +5009,9 @@ struct task *h2_io_cb(struct task *t, void *ctx, unsigned int state) } } else { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } } else { @@ -5135,17 +5135,17 @@ static int h2_process(struct h2c *h2c) /* connections in error must be removed from the idle lists */ if (conn->flags & CO_FL_LIST_MASK) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); conn_delete_from_tree(conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } } else if (h2c->st0 == H2_CS_ERROR) { /* connections in error must be removed from the idle lists */ if (conn->flags & CO_FL_LIST_MASK) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); conn_delete_from_tree(conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } } @@ -5206,20 +5206,20 @@ struct task *h2_timeout_task(struct task *t, void *context, unsigned int state) if (h2c) { /* Make sure nobody stole the connection from us */ - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); /* Somebody already stole the connection from us, so we should not * free it, we just have to free the task. */ if (!t->context) { h2c = NULL; - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); goto do_leave; } if (!expired) { - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); TRACE_DEVEL("leaving (not expired)", H2_EV_H2C_WAKE, h2c->conn); return t; } @@ -5228,7 +5228,7 @@ struct task *h2_timeout_task(struct task *t, void *context, unsigned int state) /* we do still have streams but all of them are idle, waiting * for the data layer, so we must not enforce the timeout here. */ - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); t->expire = TICK_ETERNITY; return t; } @@ -5238,7 +5238,7 @@ struct task *h2_timeout_task(struct task *t, void *context, unsigned int state) tasklet_wakeup(h2c->wait_event.tasklet); TRACE_DEVEL("leaving (idle ping)", H2_EV_H2C_WAKE, h2c->conn); t->expire = conn_idle_ping(h2c->conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); return t; } @@ -5250,7 +5250,7 @@ struct task *h2_timeout_task(struct task *t, void *context, unsigned int state) else if (h2c->conn->flags & CO_FL_SESS_IDLE) session_unown_conn(h2c->conn->owner, h2c->conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); if (h2c->flags & H2_CF_IDL_PING_SENT) { TRACE_STATE("expired on idle ping", H2_EV_H2C_WAKE, h2c->conn); @@ -5327,9 +5327,9 @@ do_leave: /* in any case this connection must not be considered idle anymore */ if (h2c->conn->flags & CO_FL_LIST_MASK) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); conn_delete_from_tree(h2c->conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } /* either we can release everything now or it will be done later once diff --git a/src/mux_spop.c b/src/mux_spop.c index 6685dc8ef..4fe70f98f 100644 --- a/src/mux_spop.c +++ b/src/mux_spop.c @@ -2541,13 +2541,13 @@ static struct task *spop_io_cb(struct task *t, void *ctx, unsigned int state) /* the tasklet was idling on an idle connection, it might have * been stolen, let's be careful! */ - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); if (tl->context == NULL) { /* The connection has been taken over by another thread, * we're no longer responsible for it, so just free the * tasklet, and do nothing. */ - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); tasklet_free(tl); return NULL; } @@ -2562,7 +2562,7 @@ static struct task *spop_io_cb(struct task *t, void *ctx, unsigned int state) conn_delete_from_tree(conn); } - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } else { /* we're certain the connection was not in an idle list */ conn = spop_conn->conn; @@ -2597,9 +2597,9 @@ static struct task *spop_io_cb(struct task *t, void *ctx, unsigned int state) } } else { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } } else { @@ -2666,9 +2666,9 @@ static int spop_process(struct spop_conn *spop_conn) /* connections in error must be removed from the idle lists */ if (conn->flags & CO_FL_LIST_MASK) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); conn_delete_from_tree(conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } } @@ -2769,19 +2769,19 @@ static struct task *spop_timeout_task(struct task *t, void *context, unsigned in TRACE_ENTER(SPOP_EV_SPOP_CONN_WAKE, (spop_conn ? spop_conn->conn : NULL)); if (spop_conn) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); /* Somebody already stole the connection from us, so we should not * free it, we just have to free the task. */ if (!t->context) { - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); spop_conn = NULL; goto do_leave; } if (!expired) { - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); TRACE_DEVEL("leaving (not expired)", SPOP_EV_SPOP_CONN_WAKE, spop_conn->conn); return t; } @@ -2794,7 +2794,7 @@ static struct task *spop_timeout_task(struct task *t, void *context, unsigned in else if (spop_conn->conn->flags & CO_FL_SESS_IDLE) conn_delete_from_sess(spop_conn->conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); spop_conn_report_term_evt(spop_conn, muxc_tevt_type_tout); } diff --git a/src/server.c b/src/server.c index 95560d595..d7d5f2441 100644 --- a/src/server.c +++ b/src/server.c @@ -57,7 +57,7 @@ static void srv_update_status(struct server *s, int type, int cause); static int srv_apply_lastaddr(struct server *srv, int *err_code); -static void srv_cleanup_connections(struct server *srv); +static void srv_purge_all_idle_conns(struct server *srv); /* extra keywords used as value for other arguments. They are used as * suggestions for mistyped words. @@ -73,9 +73,11 @@ struct srv_kw_list srv_keywords = { .list = LIST_HEAD_INIT(srv_keywords.list) }; -__decl_thread(HA_SPINLOCK_T idle_conn_srv_lock); -struct eb_root idle_conn_srv = EB_ROOT; -struct task *idle_conn_task __read_mostly = NULL; +/* Periodic idle conns purge elements. */ +__decl_thread(HA_SPINLOCK_T purge_conns_lock); +struct eb_root servers_purge_tree = EB_ROOT; +struct task *task_purge_servers __read_mostly = NULL; + struct mt_list servers_list = MT_LIST_HEAD_INIT(servers_list); static struct task *server_atomic_sync_task = NULL; static event_hdl_async_equeue server_atomic_sync_queue; @@ -318,7 +320,7 @@ static struct task *server_atomic_sync(struct task *task, void *context, unsigne send_log(srv->proxy, LOG_NOTICE, "%s.\n", trash.area); } } - srv_cleanup_connections(srv); + srv_purge_all_idle_conns(srv); srv_set_dyncookie(srv); srv_set_addr_desc(srv, 1); } @@ -5565,7 +5567,7 @@ static int cli_parse_set_server(char **args, char *payload, struct appctx *appct cli_err(appctx, "'set server ssl' expects 'on' or 'off'.\n"); goto out; } - srv_cleanup_connections(sv); + srv_purge_all_idle_conns(sv); HA_SPIN_UNLOCK(SERVER_LOCK, &sv->lock); cli_msg(appctx, LOG_NOTICE, "server ssl setting updated.\n"); #else @@ -6420,8 +6422,8 @@ static int cli_parse_delete_server(char **args, char *payload, struct appctx *ap ebpt_delete(&srv->conf.name); ebpt_delete(&srv->addr_node); - /* remove srv from idle_node tree for idle conn cleanup */ - eb32_delete(&srv->idle_node); + /* remove srv from purge tree for idle conn cleanup */ + eb32_delete(&srv->purge_node); /* flag the server as deleted * (despite the server being removed from primary server list, @@ -6738,7 +6740,7 @@ static int _srv_update_status_adm(struct server *s, enum srv_adm_st_chg_cause ca srv_shutdown_streams(s, SF_ERR_DOWN); /* force connection cleanup on the given server */ - srv_cleanup_connections(s); + srv_purge_all_idle_conns(s); /* we might have streams queued on this server and waiting for * a connection. Those which are redispatchable will be queued * to another server or to the proxy itself. @@ -7094,46 +7096,39 @@ static void srv_update_status(struct server *s, int type, int cause) } } -struct task *srv_cleanup_toremove_conns(struct task *task, void *context, unsigned int state) -{ - struct connection *conn; - - while ((conn = MT_LIST_POP(&idle_conns[tid].toremove_conns, - struct connection *, toremove_list)) != NULL) { - conn->mux->destroy(conn->ctx); - } - - return task; -} - -/* Move count connections from storage to - * list storage. -1 means moving all of them. +/* Move connections attached on a server from list into the + * purgeable list of thread. If is negative, is emptied. * - * Returns the number of connections moved. + * Must be called with IDLE_CONNS_LOCK held. * - * Must be called with idle_conns_lock held. + * Returns the number of connections moved. */ -static int srv_migrate_conns_to_remove(struct list *list, struct mt_list *toremove_list, int toremove_nb) +static int srv_purge_conns(struct list *conns, int t, int count) { struct connection *conn; int i = 0; - while (!LIST_ISEMPTY(list)) { - if (toremove_nb != -1 && i >= toremove_nb) + while (!LIST_ISEMPTY(conns)) { + if (count >= 0 && i >= count) break; - conn = LIST_ELEM(list->n, struct connection *, idle_list); + conn = LIST_ELEM(conns->n, struct connection *, idle_list); conn_delete_from_tree(conn); - MT_LIST_APPEND(toremove_list, &conn->toremove_list); + MT_LIST_APPEND(&idle_conns[t].purged_list, &conn->purge_el); i++; } return i; } -/* cleanup connections for a given server - * might be useful when going on forced maintenance or live changing ip/port + +/* Mark as purgeable all idle backend connections on server. Purge task + * is immediately scheduled on necessary threads to remove them. + * + * This operation is useful when the server is going on forced maintenance or + * some of its network configuration such as IP/port are updated during + * runtime. */ -static void srv_cleanup_connections(struct server *srv) +static void srv_purge_all_idle_conns(struct server *srv) { struct sess_priv_conns *sess_conns; int did_remove; @@ -7146,10 +7141,10 @@ static void srv_cleanup_connections(struct server *srv) /* check all threads starting with ours */ for (i = tid;;) { did_remove = 0; - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[i].lock); /* idle connections */ - if (srv_migrate_conns_to_remove(&srv->per_thr[i].idle_conn_list, &idle_conns[i].toremove_conns, -1) > 0) + if (srv_purge_conns(&srv->per_thr[i].idle_conn_list, i, -1) > 0) did_remove = 1; /* session attached connections */ @@ -7164,9 +7159,9 @@ static void srv_cleanup_connections(struct server *srv) } } - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].lock); if (did_remove) - task_wakeup(idle_conns[i].cleanup_task, TASK_WOKEN_OTHER); + task_wakeup(idle_conns[i].task_free_purged, TASK_WOKEN_OTHER); if ((i = ((i + 1 == global.nbthread) ? 0 : i + 1)) == tid) break; @@ -7196,10 +7191,10 @@ void srv_release_conn(struct server *srv, struct connection *conn) /* Remove the connection from any tree (safe, idle or available) */ if (conn->hash_node) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); conn_delete_from_tree(conn); conn->flags &= ~CO_FL_LIST_MASK; - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } } @@ -7247,7 +7242,7 @@ struct connection *srv_lookup_conn_next(struct connection *conn) * on it before reinserting it with this function. In other context, prefer to * use the full feature srv_add_to_idle_list(). * - * Must be called with idle_conns_lock. + * Must be called with IDLE_CONNS_LOCK. */ void _srv_add_idle(struct server *srv, struct connection *conn, int is_safe) { @@ -7294,7 +7289,7 @@ int srv_add_to_idle_list(struct server *srv, struct connection *conn, int is_saf } _HA_ATOMIC_DEC(&srv->curr_used_conns); - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); conn_delete_from_tree(conn); if (is_safe) { @@ -7306,24 +7301,28 @@ int srv_add_to_idle_list(struct server *srv, struct connection *conn, int is_saf _srv_add_idle(srv, conn, 0); _HA_ATOMIC_INC(&srv->curr_idle_nb); } - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); _HA_ATOMIC_INC(&srv->curr_idle_thr[tid]); __ha_barrier_full(); - if ((volatile void *)srv->idle_node.node.leaf_p == NULL) { - HA_SPIN_LOCK(OTHER_LOCK, &idle_conn_srv_lock); - if ((volatile void *)srv->idle_node.node.leaf_p == NULL) { - srv->idle_node.key = tick_add(srv->pool_purge_delay, - now_ms); - eb32_insert(&idle_conn_srv, &srv->idle_node); - if (!task_in_wq(idle_conn_task) && ! - task_in_rq(idle_conn_task)) { - task_schedule(idle_conn_task, - srv->idle_node.key); + + /* Register server for purge if not already the case. */ + if ((volatile void *)srv->purge_node.node.leaf_p == NULL) { + HA_SPIN_LOCK(OTHER_LOCK, &purge_conns_lock); + if ((volatile void *)srv->purge_node.node.leaf_p == NULL) { + srv->purge_node.key = tick_add(srv->pool_purge_delay, + now_ms); + eb32_insert(&servers_purge_tree, &srv->purge_node); + + /* Schedule task_purge_servers if needed. */ + if (!task_in_wq(task_purge_servers) && + !task_in_rq(task_purge_servers)) { + task_schedule(task_purge_servers, + srv->purge_node.key); } } - HA_SPIN_UNLOCK(OTHER_LOCK, &idle_conn_srv_lock); + HA_SPIN_UNLOCK(OTHER_LOCK, &purge_conns_lock); } return 1; } @@ -7340,7 +7339,10 @@ void srv_add_to_avail_list(struct server *srv, struct connection *conn) eb64_insert(&srv->per_thr[tid].avail_conns, &conn->hash_node->node); } -struct task *srv_cleanup_idle_conns(struct task *task, void *context, unsigned int state) +/* Handler for . Loop over registered servers and purge it + * if expired : detach some idle conns to reach current estimated needed level. + */ +struct task *do_servers_purge(struct task *task, void *context, unsigned int state) { struct server *srv; struct eb32_node *eb; @@ -7348,20 +7350,20 @@ struct task *srv_cleanup_idle_conns(struct task *task, void *context, unsigned i unsigned int next_wakeup; next_wakeup = TICK_ETERNITY; - HA_SPIN_LOCK(OTHER_LOCK, &idle_conn_srv_lock); + HA_SPIN_LOCK(OTHER_LOCK, &purge_conns_lock); while (1) { int exceed_conns; int to_kill; int curr_idle; - eb = eb32_lookup_ge(&idle_conn_srv, now_ms - TIMER_LOOK_BACK); + eb = eb32_lookup_ge(&servers_purge_tree, now_ms - TIMER_LOOK_BACK); if (!eb) { /* we might have reached the end of the tree, typically because * is in the first half and we're first scanning the last * half. Let's loop back to the beginning of the tree now. */ - eb = eb32_first(&idle_conn_srv); + eb = eb32_first(&servers_purge_tree); if (likely(!eb)) break; } @@ -7370,7 +7372,7 @@ struct task *srv_cleanup_idle_conns(struct task *task, void *context, unsigned i next_wakeup = eb->key; break; } - srv = eb32_entry(eb, struct server, idle_node); + srv = eb32_entry(eb, struct server, purge_node); /* Calculate how many idle connections we want to kill : * we want to remove half the difference between the total @@ -7401,31 +7403,31 @@ struct task *srv_cleanup_idle_conns(struct task *task, void *context, unsigned i max_conn = (exceed_conns * srv->curr_idle_thr[i]) / curr_idle + 1; - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock); - j = srv_migrate_conns_to_remove(&srv->per_thr[i].idle_conn_list, &idle_conns[i].toremove_conns, max_conn); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[i].lock); + j = srv_purge_conns(&srv->per_thr[i].idle_conn_list, i, max_conn); if (j > 0) did_remove = 1; - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].lock); if (did_remove) - task_wakeup(idle_conns[i].cleanup_task, TASK_WOKEN_OTHER); + task_wakeup(idle_conns[i].task_free_purged, TASK_WOKEN_OTHER); if ((i = ((i + 1 == global.nbthread) ? 0 : i + 1)) == tid) break; } remove: - eb32_delete(&srv->idle_node); + eb32_delete(&srv->purge_node); if (srv->curr_idle_conns) { /* There are still more idle connections, add the * server back in the tree. */ - srv->idle_node.key = tick_add(srv->pool_purge_delay, now_ms); - eb32_insert(&idle_conn_srv, &srv->idle_node); - next_wakeup = tick_first(next_wakeup, srv->idle_node.key); + srv->purge_node.key = tick_add(srv->pool_purge_delay, now_ms); + eb32_insert(&servers_purge_tree, &srv->purge_node); + next_wakeup = tick_first(next_wakeup, srv->purge_node.key); } } - HA_SPIN_UNLOCK(OTHER_LOCK, &idle_conn_srv_lock); + HA_SPIN_UNLOCK(OTHER_LOCK, &purge_conns_lock); task->expire = next_wakeup; return task; @@ -7522,6 +7524,20 @@ static struct cfg_kw_list cfg_kws = {ILH, { INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); +int alloc_servers_purge_task(void) +{ + task_purge_servers = task_new_anywhere(); + if (!task_purge_servers) { + ha_alert("Failed to allocate purge servers global task.\n"); + return ERR_FATAL|ERR_ABORT; + } + + task_purge_servers->process = do_servers_purge; + task_purge_servers->context = NULL; + return ERR_NONE; +} +REGISTER_POST_CHECK(alloc_servers_purge_task); + /* * Local variables: * c-indent-level: 8 diff --git a/src/session.c b/src/session.c index b0e9fbefb..f82de2b5c 100644 --- a/src/session.c +++ b/src/session.c @@ -134,7 +134,7 @@ void session_free(struct session *sess) if (conn != NULL && conn->mux) conn->mux->destroy(conn->ctx); - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); list_for_each_entry_safe(pconns, pconns_back, &sess->priv_conns, sess_el) { list_for_each_entry_safe(conn, conn_back, &pconns->conn_list, sess_el) { LIST_DEL_INIT(&conn->sess_el); @@ -144,7 +144,7 @@ void session_free(struct session *sess) MT_LIST_DELETE(&pconns->srv_el); pool_free(pool_head_sess_priv_conns, pconns); } - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); /* Release connections outside of idle lock. */ while (!LIST_ISEMPTY(&conn_tmp_list)) { @@ -662,7 +662,7 @@ int session_add_conn(struct session *sess, struct connection *conn) */ BUG_ON(conn->owner && conn->owner != sess); - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); /* Already attach to the session */ if (!LIST_ISEMPTY(&conn->sess_el)) @@ -679,11 +679,11 @@ int session_add_conn(struct session *sess, struct connection *conn) conn->owner = sess; out: - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); return 1; err: - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); return 0; } @@ -698,7 +698,7 @@ int session_reinsert_idle_conn(struct session *sess, struct connection *conn) { struct sess_priv_conns *pconns; - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); pconns = sess_get_sess_conns(sess, conn->target); if (!pconns) @@ -707,13 +707,13 @@ int session_reinsert_idle_conn(struct session *sess, struct connection *conn) LIST_APPEND(&pconns->conn_list, &conn->sess_el); ++sess->idle_conns; - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); return 1; err: /* Idle conn cannot be reinserted, hence session counter must be adjusted. */ --sess->idle_conns; - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); return 0; } @@ -763,7 +763,7 @@ struct connection *session_get_conn(struct session *sess, void *target, int64_t struct sess_priv_conns *pconns; struct server *srv; - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); list_for_each_entry(pconns, &sess->priv_conns, sess_el) { if (pconns->target == target) { @@ -789,7 +789,7 @@ struct connection *session_get_conn(struct session *sess, void *target, int64_t } end: - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); return srv_conn; } @@ -802,7 +802,7 @@ void session_unown_conn(struct session *sess, struct connection *conn) BUG_ON(objt_listener(conn->target)); - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); /* WT: this currently is a workaround for an inconsistency between * the link status of the connection in the session list and the @@ -832,7 +832,7 @@ void session_unown_conn(struct session *sess, struct connection *conn) } out: - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } int session_purge_conns(struct sess_priv_conns *sess_conns) @@ -849,8 +849,8 @@ int session_purge_conns(struct sess_priv_conns *sess_conns) --((struct session *)conn->owner)->idle_conns; LIST_DEL_INIT(&conn->sess_el); - MT_LIST_APPEND(&idle_conns[conn_tid].toremove_conns, - &conn->toremove_list); + MT_LIST_APPEND(&idle_conns[conn_tid].purged_list, + &conn->purge_el); i++; } diff --git a/src/ssl_sock.c b/src/ssl_sock.c index e06239efb..6928c717a 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -5808,9 +5808,9 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state) /* the tasklet was idling on an idle connection, it might have * been stolen, let's be careful! */ - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); if (tl->context == NULL) { - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); tasklet_free(tl); return NULL; } @@ -5819,7 +5819,7 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state) conn_in_list = conn->flags & CO_FL_LIST_MASK; if (conn_in_list) conn_delete_from_tree(conn); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } else { ctx = context; conn = ctx->conn; @@ -5888,9 +5888,9 @@ leave: struct server *srv = objt_server(conn->target); TRACE_DEVEL("adding conn back to idle list", SSL_EV_CONN_IO_CB, conn); - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].lock); } TRACE_LEAVE(SSL_EV_CONN_IO_CB, conn); return t; -- 2.47.2