MINOR: server: allocate a per-thread struct for the per-thread connections stuff

There are multiple per-thread lists in the listeners, which isn't the
most efficient in terms of cache, and doesn't easily allow to store all
the per-thread stuff.

Now we introduce an srv_per_thread structure which the servers will have an
array of, and place the idle/safe/avail conns tree heads into. Overall this
was a fairly mechanical change, and the array is now always initialized for
all servers since we'll put more stuff there. It's worth noting that the Lua
code still has to deal with its own deinit by itself despite being in a
global list, because its server is not dynamically allocated.
diff --git a/src/backend.c b/src/backend.c
index 54a4c17..66fa5ea 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -1123,7 +1123,6 @@
  */
 static struct connection *conn_backend_get(struct stream *s, struct server *srv, int is_safe, int64_t hash)
 {
-	struct eb_root *tree = is_safe ? srv->safe_conns_tree : srv->idle_conns_tree;
 	struct connection *conn = NULL;
 	int i; // thread number
 	int found = 0;
@@ -1135,7 +1134,7 @@
 	 */
 	i = tid;
 	HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
-	conn = srv_lookup_conn(&tree[tid], hash);
+	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->hash_node->node);
 
@@ -1143,11 +1142,10 @@
 	 * the safe list.
 	 */
 	if (!conn && !is_safe && srv->curr_safe_nb > 0) {
-		conn = srv_lookup_conn(&srv->safe_conns_tree[tid], hash);
+		conn = srv_lookup_conn(&srv->per_thr[tid].safe_conns, hash);
 		if (conn) {
 			conn_delete_from_tree(&conn->hash_node->node);
 			is_safe = 1;
-			tree = srv->safe_conns_tree;
 		}
 	}
 	HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
@@ -1185,7 +1183,7 @@
 
 		if (HA_SPIN_TRYLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock) != 0)
 			continue;
-		conn = srv_lookup_conn(&tree[i], hash);
+		conn = srv_lookup_conn(is_safe ? &srv->per_thr[i].safe_conns : &srv->per_thr[i].idle_conns, hash);
 		while (conn) {
 			if (conn->mux->takeover && conn->mux->takeover(conn, i) == 0) {
 				conn_delete_from_tree(&conn->hash_node->node);
@@ -1198,14 +1196,13 @@
 		}
 
 		if (!found && !is_safe && srv->curr_safe_nb > 0) {
-			conn = srv_lookup_conn(&srv->safe_conns_tree[i], hash);
+			conn = srv_lookup_conn(&srv->per_thr[i].safe_conns, hash);
 			while (conn) {
 				if (conn->mux->takeover && conn->mux->takeover(conn, i) == 0) {
 					conn_delete_from_tree(&conn->hash_node->node);
 					_HA_ATOMIC_ADD(&activity[tid].fd_takeover, 1);
 					found = 1;
 					is_safe = 1;
-					tree = srv->safe_conns_tree;
 					break;
 				}
 
@@ -1237,7 +1234,7 @@
 			session_add_conn(s->sess, conn, conn->target);
 		}
 		else {
-			ebmb_insert(&srv->available_conns_tree[tid],
+			ebmb_insert(&srv->per_thr[tid].avail_conns,
 			            &conn->hash_node->node,
 			            sizeof(conn->hash_node->hash));
 		}
@@ -1342,8 +1339,8 @@
 	 */
 	si_release_endpoint(&s->si[1]);
 
-	/* do not reuse if mode is not http or if avail list is not allocated */
-	if ((s->be->mode != PR_MODE_HTTP) || (srv && !srv->available_conns_tree))
+	/* do not reuse if mode is not http */
+	if (s->be->mode != PR_MODE_HTTP)
 		goto skip_reuse;
 
 	/* first, search for a matching connection in the session's idle conns */
@@ -1370,8 +1367,8 @@
 		 * Idle conns are necessarily looked up on the same thread so
 		 * that there is no concurrency issues.
 		 */
-		if (!eb_is_empty(&srv->available_conns_tree[tid])) {
-			srv_conn = srv_lookup_conn(&srv->available_conns_tree[tid], hash);
+		if (!eb_is_empty(&srv->per_thr[tid].avail_conns)) {
+			srv_conn = srv_lookup_conn(&srv->per_thr[tid].avail_conns, hash);
 			if (srv_conn)
 				reuse = 1;
 		}
@@ -1414,7 +1411,7 @@
 	 * is OK.
 	 */
 
-	if (ha_used_fds > global.tune.pool_high_count && srv && srv->idle_conns_tree) {
+	if (ha_used_fds > global.tune.pool_high_count && srv) {
 		struct connection *tokill_conn = NULL;
 		struct conn_hash_node *conn_node = NULL;
 		struct ebmb_node *node = NULL;
@@ -1424,7 +1421,7 @@
 		 */
 		/* First, try from our own idle list */
 		HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
-		node = ebmb_first(&srv->idle_conns_tree[tid]);
+		node = ebmb_first(&srv->per_thr[tid].idle_conns);
 		if (node) {
 			conn_node = ebmb_entry(node, struct conn_hash_node, node);
 			tokill_conn = conn_node->conn;
@@ -1445,7 +1442,7 @@
 				ALREADY_CHECKED(i);
 
 				HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock);
-				node = ebmb_first(&srv->idle_conns_tree[i]);
+				node = ebmb_first(&srv->per_thr[i].idle_conns);
 				if (node) {
 					conn_node = ebmb_entry(node, struct conn_hash_node, node);
 					tokill_conn = conn_node->conn;
@@ -1453,7 +1450,7 @@
 				}
 
 				if (!tokill_conn) {
-					node = ebmb_first(&srv->safe_conns_tree[i]);
+					node = ebmb_first(&srv->per_thr[i].safe_conns);
 					if (node) {
 						conn_node = ebmb_entry(node, struct conn_hash_node, node);
 						tokill_conn = conn_node->conn;
@@ -1654,7 +1651,7 @@
 			if (srv && reuse_mode == PR_O_REUSE_ALWS &&
 			    !(srv_conn->flags & CO_FL_PRIVATE) &&
 			    srv_conn->mux->avail_streams(srv_conn) > 0) {
-				ebmb_insert(&srv->available_conns_tree[tid], &srv_conn->hash_node->node, sizeof(srv_conn->hash_node->hash));
+				ebmb_insert(&srv->per_thr[tid].avail_conns, &srv_conn->hash_node->node, sizeof(srv_conn->hash_node->hash));
 			}
 			else if (srv_conn->flags & CO_FL_PRIVATE ||
 			         (reuse_mode == PR_O_REUSE_SAFE &&
diff --git a/src/cfgparse.c b/src/cfgparse.c
index c3c1ea9..eafd8ca 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -3247,17 +3247,19 @@
 		/* initialize idle conns lists */
 		int i;
 
-		newsrv->available_conns_tree = calloc(global.nbthread, sizeof(*newsrv->available_conns_tree));
-
-		if (!newsrv->available_conns_tree) {
-			ha_alert("parsing [%s:%d] : failed to allocate idle connections for server '%s'.\n",
-				 newsrv->conf.file, newsrv->conf.line, newsrv->id);
+		newsrv->per_thr = calloc(global.nbthread, sizeof(*newsrv->per_thr));
+		if (!newsrv->per_thr) {
+			ha_alert("parsing [%s:%d] : failed to allocate per-thread lists for server '%s'.\n",
+			         newsrv->conf.file, newsrv->conf.line, newsrv->id);
 			cfgerr++;
 			continue;
 		}
 
-		for (i = 0; i < global.nbthread; i++)
-			newsrv->available_conns_tree[i] = EB_ROOT;
+		for (i = 0; i < global.nbthread; i++) {
+			newsrv->per_thr[i].idle_conns = EB_ROOT;
+			newsrv->per_thr[i].safe_conns = EB_ROOT;
+			newsrv->per_thr[i].avail_conns = EB_ROOT;
+		}
 
 		if (newsrv->max_idle_conns != 0) {
 			if (idle_conn_task == NULL) {
@@ -3279,28 +3281,6 @@
 				}
 			}
 
-			newsrv->idle_conns_tree = calloc((unsigned short)global.nbthread, sizeof(*newsrv->idle_conns_tree));
-			if (!newsrv->idle_conns_tree) {
-				ha_alert("parsing [%s:%d] : failed to allocate idle connections for server '%s'.\n",
-					 newsrv->conf.file, newsrv->conf.line, newsrv->id);
-				cfgerr++;
-				continue;
-			}
-
-			for (i = 0; i < global.nbthread; i++)
-				newsrv->idle_conns_tree[i] = EB_ROOT;
-
-			newsrv->safe_conns_tree = calloc(global.nbthread, sizeof(*newsrv->safe_conns_tree));
-			if (!newsrv->safe_conns_tree) {
-				ha_alert("parsing [%s:%d] : failed to allocate idle connections for server '%s'.\n",
-					 newsrv->conf.file, newsrv->conf.line, newsrv->id);
-				cfgerr++;
-				continue;
-			}
-
-			for (i = 0; i < global.nbthread; i++)
-				newsrv->safe_conns_tree[i] = EB_ROOT;
-
 			newsrv->curr_idle_thr = calloc(global.nbthread, sizeof(*newsrv->curr_idle_thr));
 			if (!newsrv->curr_idle_thr)
 				goto err;
diff --git a/src/connection.c b/src/connection.c
index 535fb6b..c394d0f 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -70,7 +70,7 @@
 		 */
 		if (srv && ((srv->proxy->options & PR_O_REUSE_MASK) == PR_O_REUSE_ALWS) &&
 		    !(conn->flags & CO_FL_PRIVATE) && conn->mux->avail_streams(conn) > 0)
-			ebmb_insert(&srv->available_conns_tree[tid], &conn->hash_node->node, sizeof(conn->hash_node->hash));
+			ebmb_insert(&srv->per_thr[tid].avail_conns, &conn->hash_node->node, sizeof(conn->hash_node->hash));
 		else if (conn->flags & CO_FL_PRIVATE) {
 			/* If it fail now, the same will be done in mux->detach() callback */
 			session_add_conn(sess, conn, conn->target);
diff --git a/src/haproxy.c b/src/haproxy.c
index a4d42dc..49f6957 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -2658,9 +2658,7 @@
 			free(s->hostname);
 			free(s->hostname_dn);
 			free((char*)s->conf.file);
-			free(s->idle_conns_tree);
-			free(s->safe_conns_tree);
-			free(s->available_conns_tree);
+			free(s->per_thr);
 			free(s->curr_idle_thr);
 			free(s->resolvers_id);
 			free(s->addr_node.key);
diff --git a/src/hlua.c b/src/hlua.c
index a093faf..5e5414c 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -9177,8 +9177,6 @@
 	socket_tcp.obj_type = OBJ_TYPE_SERVER;
 	MT_LIST_INIT(&socket_tcp.actconns);
 	socket_tcp.pendconns = EB_ROOT;
-	socket_tcp.idle_conns_tree = NULL;
-	socket_tcp.safe_conns_tree = NULL;
 	LIST_ADD(&servers_list, &socket_tcp.global_list);
 	socket_tcp.next_state = SRV_ST_RUNNING; /* early server setup */
 	socket_tcp.last_change = 0;
@@ -9225,8 +9223,6 @@
 	socket_ssl.obj_type = OBJ_TYPE_SERVER;
 	MT_LIST_INIT(&socket_ssl.actconns);
 	socket_ssl.pendconns = EB_ROOT;
-	socket_ssl.idle_conns_tree = NULL;
-	socket_ssl.safe_conns_tree = NULL;
 	LIST_ADD(&servers_list, &socket_ssl.global_list);
 	socket_ssl.next_state = SRV_ST_RUNNING; /* early server setup */
 	socket_ssl.last_change = 0;
@@ -9296,8 +9292,12 @@
 		if (hlua_states[thr])
 			lua_close(hlua_states[thr]);
 	}
+
+	ha_free(&socket_tcp.per_thr);
 	ha_free((char**)&socket_tcp.conf.file);
+
 #ifdef USE_OPENSSL
+	ha_free(&socket_ssl.per_thr);
 	ha_free((char**)&socket_ssl.conf.file);
 #endif
 }
diff --git a/src/mux_fcgi.c b/src/mux_fcgi.c
index b7d8e94..b1833af 100644
--- a/src/mux_fcgi.c
+++ b/src/mux_fcgi.c
@@ -3029,9 +3029,9 @@
 
 		HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
 		if (conn_in_list == CO_FL_SAFE_LIST)
-			ebmb_insert(&srv->safe_conns_tree[tid], &conn->hash_node->node, sizeof(conn->hash_node->hash));
+			ebmb_insert(&srv->per_thr[tid].safe_conns, &conn->hash_node->node, sizeof(conn->hash_node->hash));
 		else
-			ebmb_insert(&srv->idle_conns_tree[tid], &conn->hash_node->node, sizeof(conn->hash_node->hash));
+			ebmb_insert(&srv->per_thr[tid].idle_conns, &conn->hash_node->node, sizeof(conn->hash_node->hash));
 		HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
 	}
 	return NULL;
@@ -3639,7 +3639,7 @@
 			else if (!fconn->conn->hash_node->node.node.leaf_p &&
 				 fcgi_avail_streams(fconn->conn) > 0 && objt_server(fconn->conn->target) &&
 				 !LIST_ADDED(&fconn->conn->session_list)) {
-				ebmb_insert(&__objt_server(fconn->conn->target)->available_conns_tree[tid],
+				ebmb_insert(&__objt_server(fconn->conn->target)->per_thr[tid].avail_conns,
 				            &fconn->conn->hash_node->node,
 				            sizeof(fconn->conn->hash_node->hash));
 			}
diff --git a/src/mux_h1.c b/src/mux_h1.c
index c8dc71c..b44de54 100644
--- a/src/mux_h1.c
+++ b/src/mux_h1.c
@@ -2855,9 +2855,9 @@
 
 		HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
 		if (conn_in_list == CO_FL_SAFE_LIST)
-			ebmb_insert(&srv->safe_conns_tree[tid], &conn->hash_node->node, sizeof(conn->hash_node->hash));
+			ebmb_insert(&srv->per_thr[tid].safe_conns, &conn->hash_node->node, sizeof(conn->hash_node->hash));
 		else
-			ebmb_insert(&srv->idle_conns_tree[tid], &conn->hash_node->node, sizeof(conn->hash_node->hash));
+			ebmb_insert(&srv->per_thr[tid].idle_conns, &conn->hash_node->node, sizeof(conn->hash_node->hash));
 		HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
 	}
 	return NULL;
diff --git a/src/mux_h2.c b/src/mux_h2.c
index 9a4660e..20d343a 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -3831,9 +3831,9 @@
 
 		HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
 		if (conn_in_list == CO_FL_SAFE_LIST)
-			ebmb_insert(&srv->safe_conns_tree[tid], &conn->hash_node->node, sizeof(conn->hash_node->hash));
+			ebmb_insert(&srv->per_thr[tid].safe_conns, &conn->hash_node->node, sizeof(conn->hash_node->hash));
 		else
-			ebmb_insert(&srv->idle_conns_tree[tid], &conn->hash_node->node, sizeof(conn->hash_node->hash));
+			ebmb_insert(&srv->per_thr[tid].idle_conns, &conn->hash_node->node, sizeof(conn->hash_node->hash));
 		HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
 	}
 
@@ -4274,7 +4274,7 @@
 				else if (!h2c->conn->hash_node->node.node.leaf_p &&
 					 h2_avail_streams(h2c->conn) > 0 && objt_server(h2c->conn->target) &&
 					 !LIST_ADDED(&h2c->conn->session_list)) {
-					ebmb_insert(&__objt_server(h2c->conn->target)->available_conns_tree[tid],
+					ebmb_insert(&__objt_server(h2c->conn->target)->per_thr[tid].avail_conns,
 					            &h2c->conn->hash_node->node,
 					            sizeof(h2c->conn->hash_node->hash));
 				}
diff --git a/src/server.c b/src/server.c
index dd6340b..5844c63 100644
--- a/src/server.c
+++ b/src/server.c
@@ -4622,9 +4622,9 @@
 	for (i = tid;;) {
 		did_remove = 0;
 		HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock);
-		if (srv_migrate_conns_to_remove(&srv->idle_conns_tree[i], &idle_conns[i].toremove_conns, -1) > 0)
+		if (srv_migrate_conns_to_remove(&srv->per_thr[i].idle_conns, &idle_conns[i].toremove_conns, -1) > 0)
 			did_remove = 1;
-		if (srv_migrate_conns_to_remove(&srv->safe_conns_tree[i], &idle_conns[i].toremove_conns, -1) > 0)
+		if (srv_migrate_conns_to_remove(&srv->per_thr[i].safe_conns, &idle_conns[i].toremove_conns, -1) > 0)
 			did_remove = 1;
 		HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock);
 		if (did_remove)
@@ -4697,11 +4697,11 @@
 			           curr_idle + 1;
 
 			HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock);
-			j = srv_migrate_conns_to_remove(&srv->idle_conns_tree[i], &idle_conns[i].toremove_conns, max_conn);
+			j = srv_migrate_conns_to_remove(&srv->per_thr[i].idle_conns, &idle_conns[i].toremove_conns, max_conn);
 			if (j > 0)
 				did_remove = 1;
 			if (max_conn - j > 0 &&
-			    srv_migrate_conns_to_remove(&srv->safe_conns_tree[i], &idle_conns[i].toremove_conns, max_conn - j) > 0)
+			    srv_migrate_conns_to_remove(&srv->per_thr[i].safe_conns, &idle_conns[i].toremove_conns, max_conn - j) > 0)
 				did_remove = 1;
 			HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[i].idle_conns_lock);
 
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index bd24ae7..4ff1d33 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -5910,9 +5910,9 @@
 
 		HA_SPIN_LOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
 		if (conn_in_list == CO_FL_SAFE_LIST)
-			ebmb_insert(&srv->safe_conns_tree[tid], &conn->hash_node->node, sizeof(conn->hash_node->hash));
+			ebmb_insert(&srv->per_thr[tid].safe_conns, &conn->hash_node->node, sizeof(conn->hash_node->hash));
 		else
-			ebmb_insert(&srv->idle_conns_tree[tid], &conn->hash_node->node, sizeof(conn->hash_node->hash));
+			ebmb_insert(&srv->per_thr[tid].idle_conns, &conn->hash_node->node, sizeof(conn->hash_node->hash));
 		HA_SPIN_UNLOCK(IDLE_CONNS_LOCK, &idle_conns[tid].idle_conns_lock);
 	}
 	return NULL;