diff --git a/include/haproxy/session.h b/include/haproxy/session.h index 7a8a73361..ffa750965 100644 --- a/include/haproxy/session.h +++ b/include/haproxy/session.h @@ -44,6 +44,7 @@ void __session_add_glitch_ctr(struct session *sess, uint inc); void session_embryonic_build_legacy_err(struct session *sess, struct buffer *out); int session_add_conn(struct session *sess, struct connection *conn); +int session_reinsert_idle_conn(struct session *sess, struct connection *conn); 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); diff --git a/src/connection.c b/src/connection.c index 6dbca2c12..0299532b3 100644 --- a/src/connection.c +++ b/src/connection.c @@ -213,16 +213,25 @@ int conn_notify_mux(struct connection *conn, int old_flags, int forced_wake) goto done; if (conn_in_list) { - if (srv->cur_admin & SRV_ADMF_MAINT) { + if (srv && (srv->cur_admin & SRV_ADMF_MAINT)) { /* Do not store an idle conn if server in maintenance. */ conn->mux->destroy(conn->ctx); ret = -1; goto done; } - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_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); + if (conn->flags & CO_FL_SESS_IDLE) { + if (!session_reinsert_idle_conn(conn->owner, conn)) { + /* session add conn failure */ + conn->mux->destroy(conn->ctx); + ret = -1; + } + } + else { + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_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); + } } } done: diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c index c0778f44c..7afbaf3e7 100644 --- a/src/mux_fcgi.c +++ b/src/mux_fcgi.c @@ -3089,12 +3089,20 @@ struct task *fcgi_io_cb(struct task *t, void *ctx, unsigned int state) t = NULL; if (!ret && conn_in_list) { - struct server *srv = __objt_server(conn->target); + struct server *srv = objt_server(conn->target); - if (!(srv->cur_admin & SRV_ADMF_MAINT)) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_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); + if (!srv || !(srv->cur_admin & SRV_ADMF_MAINT)) { + if (conn->flags & CO_FL_SESS_IDLE) { + if (!session_reinsert_idle_conn(conn->owner, conn)) { + /* session add conn failure */ + goto release; + } + } + else { + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_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); + } } else { /* Do not store an idle conn if server in maintenance. */ diff --git a/src/mux_h1.c b/src/mux_h1.c index 86ca866b4..bf17dfcb6 100644 --- a/src/mux_h1.c +++ b/src/mux_h1.c @@ -4334,12 +4334,20 @@ struct task *h1_io_cb(struct task *t, void *ctx, unsigned int state) t = NULL; if (!ret && conn_in_list) { - struct server *srv = __objt_server(conn->target); + struct server *srv = objt_server(conn->target); - if (!(srv->cur_admin & SRV_ADMF_MAINT)) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_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); + if (!srv || !(srv->cur_admin & SRV_ADMF_MAINT)) { + if (conn->flags & CO_FL_SESS_IDLE) { + if (!session_reinsert_idle_conn(conn->owner, conn)) { + /* session add conn failure */ + goto release; + } + } + else { + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_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); + } } else { /* Do not store an idle conn if server in maintenance. */ diff --git a/src/mux_h2.c b/src/mux_h2.c index 57fea0b5a..84f30469d 100644 --- a/src/mux_h2.c +++ b/src/mux_h2.c @@ -4993,12 +4993,20 @@ struct task *h2_io_cb(struct task *t, void *ctx, unsigned int state) h2c->next_tasklet = NULL; if (!ret && conn_in_list) { - struct server *srv = __objt_server(conn->target); + struct server *srv = objt_server(conn->target); - if (!(srv->cur_admin & SRV_ADMF_MAINT)) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_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); + if (!srv || !(srv->cur_admin & SRV_ADMF_MAINT)) { + if (conn->flags & CO_FL_SESS_IDLE) { + if (!session_reinsert_idle_conn(conn->owner, conn)) { + /* session add conn failure */ + goto release; + } + } + else { + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_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); + } } else { /* Do not store an idle conn if server in maintenance. */ diff --git a/src/mux_spop.c b/src/mux_spop.c index 4ba8dabca..d167e522d 100644 --- a/src/mux_spop.c +++ b/src/mux_spop.c @@ -2585,12 +2585,20 @@ static struct task *spop_io_cb(struct task *t, void *ctx, unsigned int state) t = NULL; if (!ret && conn_in_list) { - struct server *srv = __objt_server(conn->target); + struct server *srv = objt_server(conn->target); - if (!(srv->cur_admin & SRV_ADMF_MAINT)) { - HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_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); + if (!srv || !(srv->cur_admin & SRV_ADMF_MAINT)) { + if (conn->flags & CO_FL_SESS_IDLE) { + if (!session_reinsert_idle_conn(conn->owner, conn)) { + /* session add conn failure */ + goto release; + } + } + else { + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_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); + } } else { /* Do not store an idle conn if server in maintenance. */ diff --git a/src/session.c b/src/session.c index 89f81fde8..803a60d2d 100644 --- a/src/session.c +++ b/src/session.c @@ -697,6 +697,38 @@ int session_add_conn(struct session *sess, struct connection *conn) return ret; } +/* Reinsert a backend connection into session. This function is + * reserved for idle conns which were previously temporarily removed from + * session to protect it against other threads. + * + * Returns true on success else false. + */ +int session_reinsert_idle_conn(struct session *sess, struct connection *conn) +{ + struct sess_priv_conns *pconns; + int ret = 0; + + /* This function is reserved for idle private connections. */ + BUG_ON(!(conn->flags & CO_FL_SESS_IDLE)); + + HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + + pconns = sess_get_sess_conns(sess, conn->target); + if (!pconns) { + pconns = sess_alloc_sess_conns(sess, conn->target); + if (!pconns) + goto out; + } + + LIST_APPEND(&pconns->conn_list, &conn->sess_el); + ++sess->idle_conns; + ret = 1; + + out: + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + return ret; +} + /* Check that session is able to keep idle connection . This must * be called each time a connection stored in a session becomes idle. * diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 9342bd6d1..fffa1802c 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -6478,19 +6478,29 @@ struct task *ssl_sock_io_cb(struct task *t, void *context, unsigned int state) #endif leave: if (!ret && conn_in_list) { - struct server *srv = __objt_server(conn->target); + struct server *srv = objt_server(conn->target); - if (!(srv->cur_admin & SRV_ADMF_MAINT)) { - 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); - _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); - HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + /* Connection is idle which means MUX layer is already initialized. */ + BUG_ON(!conn->mux); + + if (!srv || !(srv->cur_admin & SRV_ADMF_MAINT)) { + if (conn->flags & CO_FL_SESS_IDLE) { + TRACE_DEVEL("adding conn back to session list", SSL_EV_CONN_IO_CB, conn); + if (!session_reinsert_idle_conn(conn->owner, conn)) { + /* session add conn failure */ + conn->mux->destroy(conn->ctx); + t = NULL; + } + } + else { + 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); + _srv_add_idle(srv, conn, conn_in_list == CO_FL_SAFE_LIST); + HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock); + } } else { /* Do not store an idle conn if server in maintenance. */ - - /* Connection is idle which means MUX layer is already initialized. */ - BUG_ON(!conn->mux); conn->mux->destroy(conn->ctx); t = NULL; }