diff --git a/include/haproxy/session.h b/include/haproxy/session.h index 7014a8637..677300ca7 100644 --- a/include/haproxy/session.h +++ b/include/haproxy/session.h @@ -49,6 +49,7 @@ int session_check_idle_conn(struct session *sess, struct connection *conn); struct connection *session_get_conn(struct session *sess, void *target, int64_t hash); void session_unown_conn(struct session *sess, struct connection *conn); int session_detach_idle_conn(struct session *sess, struct connection *conn); +int sess_conns_cleanup_all_idle(struct sess_priv_conns *sess_conns); /* Remove the refcount from the session to the tracked counters, and clear the * pointer to ensure this is only performed once. The caller is responsible for diff --git a/src/server.c b/src/server.c index 88e85b724..9c120171f 100644 --- a/src/server.c +++ b/src/server.c @@ -7210,6 +7210,7 @@ static int srv_migrate_conns_to_remove(struct list *list, struct mt_list *toremo */ static void srv_cleanup_connections(struct server *srv) { + struct sess_priv_conns *sess_conns; int did_remove; int i; @@ -7221,8 +7222,23 @@ static void srv_cleanup_connections(struct server *srv) for (i = tid;;) { did_remove = 0; HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock); + + /* idle connections */ if (srv_migrate_conns_to_remove(&srv->per_thr[i].idle_conn_list, &idle_conns[i].toremove_conns, -1) > 0) did_remove = 1; + + /* session attached connections */ + while ((sess_conns = MT_LIST_POP(&srv->per_thr[i].sess_conns, struct sess_priv_conns *, srv_el))) { + if (sess_conns_cleanup_all_idle(sess_conns)) { + did_remove = 1; + + if (LIST_ISEMPTY(&sess_conns->conn_list)) { + LIST_DELETE(&sess_conns->sess_el); + pool_free(pool_head_sess_priv_conns, sess_conns); + } + } + } + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock); if (did_remove) task_wakeup(idle_conns[i].cleanup_task, TASK_WOKEN_OTHER); diff --git a/src/session.c b/src/session.c index cad66ddc6..43e54dc5e 100644 --- a/src/session.c +++ b/src/session.c @@ -874,6 +874,34 @@ int session_detach_idle_conn(struct session *sess, struct connection *conn) return 1; } +/* Remove every idle backend connections stored in and move them + * into the purge list. If is empty it is also removed from the + * session and freed. + * + * Returns the number of connections moved to purge list. + */ +int sess_conns_cleanup_all_idle(struct sess_priv_conns *sess_conns) +{ + struct connection *conn, *back; + int conn_tid = sess_conns->tid; + int i = 0; + + list_for_each_entry_safe(conn, back, &sess_conns->conn_list, sess_el) { + if (!(conn->flags & CO_FL_SESS_IDLE)) + continue; + + /* Decrement session idle counter. */ + --((struct session *)conn->owner)->idle_conns; + + LIST_DEL_INIT(&conn->sess_el); + MT_LIST_APPEND(&idle_conns[conn_tid].toremove_conns, + &conn->toremove_list); + ++i; + } + + return i; +} + /* * Local variables: