#include <netinet/ip.h>
#include <netinet/ip6.h>
-#include <import/ebtree-t.h>
+#include <import/cebtree.h>
#include <import/ist.h>
#include <haproxy/api-t.h>
#define CONN_HASH_PARAMS_TYPE_COUNT 7
#define CONN_HASH_PAYLOAD_LEN \
- (((sizeof(((struct conn_hash_node *)0)->node.key)) * 8) - CONN_HASH_PARAMS_TYPE_COUNT)
+ (((sizeof(((struct conn_hash_node *)0)->key)) * 8) - CONN_HASH_PARAMS_TYPE_COUNT)
#define CONN_HASH_GET_PAYLOAD(hash) \
(((hash) << CONN_HASH_PARAMS_TYPE_COUNT) >> CONN_HASH_PARAMS_TYPE_COUNT)
* A connection is identified by a hash generated from its specific parameters
*/
struct conn_hash_node {
- struct eb64_node node; /* contains the hashing key */
+ struct ceb_node node; /* indexes the hashing key for safe/idle/avail */
+ uint64_t key; /* the hashing key, also used by session-owned */
struct connection *conn; /* connection owner of the node */
};
/* Each server will have one occurrence of this structure per thread */
struct srv_per_thread {
struct mt_list streams; /* streams using this server (used by "shutdown server sessions") */
- struct eb_root idle_conns; /* Shareable idle connections */
- struct eb_root safe_conns; /* Safe idle connections */
- struct eb_root avail_conns; /* Connections in use, but with still new streams available */
struct mt_list sess_conns; /* Connections attached to a session which cannot be shared across clients */
/* Secondary idle conn storage used in parallel to idle/safe trees.
* Used to sort them by last usage and purge them in reverse order.
*/
struct list idle_conn_list;
+
+ /* connection trees to look them up by name */
+ struct ceb_root *idle_conns; /* Shareable idle connections */
+ struct ceb_root *safe_conns; /* Safe idle connections */
+ struct ceb_root *avail_conns; /* Connections in use, but with still new streams available */
};
/* Each server will have one occurrence of this structure per thread group */
struct server *snr_check_ip_callback(struct server *srv, void *ip, unsigned char *ip_family);
struct task *srv_cleanup_idle_conns(struct task *task, void *ctx, unsigned int state);
void srv_release_conn(struct server *srv, struct connection *conn);
-struct connection *srv_lookup_conn(struct eb_root *tree, uint64_t hash);
-struct connection *srv_lookup_conn_next(struct connection *conn);
+struct connection *srv_lookup_conn(struct ceb_root **tree, uint64_t hash);
+struct connection *srv_lookup_conn_next(struct ceb_root **tree, 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);
#include <ctype.h>
#include <sys/types.h>
-#include <import/ebmbtree.h>
+#include <import/ceb64_tree.h>
#include <haproxy/api.h>
#include <haproxy/acl.h>
* tree; unsafe requests are looked up in the safe conns tree.
*/
int search_tree = is_safe ? 1 : 0; // 0 = idle, 1 = safe
- struct eb_root *tree;
+ struct ceb_root **tree;
if (!srv->curr_idle_thr[i] || i == tid)
continue;
found = 1;
break;
}
- conn = srv_lookup_conn_next(conn);
+ conn = srv_lookup_conn_next(tree, conn);
}
} while (!found && ++search_tree <= 1);
* if any.
*/
static struct connection *
-takeover_random_idle_conn(struct eb_root *root, int curtid)
+takeover_random_idle_conn(struct ceb_root **root, int curtid)
{
struct conn_hash_node *hash_node;
struct connection *conn = NULL;
- struct eb64_node *node = eb64_first(root);
- while (node) {
- hash_node = eb64_entry(node, struct conn_hash_node, node);
+ hash_node = ceb64_item_first(root, node, key, struct conn_hash_node);
+ while (hash_node) {
conn = hash_node->conn;
- if (conn && conn->mux->takeover && conn->mux->takeover(conn, curtid, 1) == 0) {
+ if (conn->mux->takeover && conn->mux->takeover(conn, curtid, 1) == 0) {
conn_delete_from_tree(conn, curtid);
return conn;
}
- node = eb64_next(node);
+ hash_node = ceb64_item_next(root, node, key, hash_node);
}
return NULL;
* Idle conns are necessarily looked up on the same thread so
* that there is no concurrency issues.
*/
- if (!eb_is_empty(&srv->per_thr[tid].avail_conns)) {
+ if (!ceb_isempty(&srv->per_thr[tid].avail_conns)) {
srv_conn = srv_lookup_conn(&srv->per_thr[tid].avail_conns, hash);
if (srv_conn) {
/* connection cannot be in idle list if used as an avail idle conn. */
srv_conn->flags |= CO_FL_OPT_TOS;
}
- srv_conn->hash_node->node.key = hash;
+ srv_conn->hash_node->key = hash;
} else if (srv && (srv->flags & SRV_F_STRICT_MAXCONN))
_HA_ATOMIC_DEC(&srv->curr_total_conns);
}
#include <errno.h>
-#include <import/ebmbtree.h>
+#include <import/ceb64_tree.h>
#include <haproxy/api.h>
#include <haproxy/arg.h>
*/
void conn_delete_from_tree(struct connection *conn, int thr)
{
- LIST_DEL_INIT(&conn->idle_list);
- eb64_delete(&conn->hash_node->node);
+ struct ceb_root **conn_tree;
+ struct server *srv = __objt_server(conn->target);
+
+ /* We need to determine where the connection is attached, if at all.
+ * - if it's in a tree and not in the idle_list, it's the avail_tree
+ * - if it's in a tree and has CO_FL_SAFE_LIST, it's the safe_tree
+ * - if it's in a tree and has CO_FL_IDLE_LIST, it's the idle_tree
+ * - if it's not in a tree and has CO_FL_SESS_IDLE, it's in the
+ * session's list (but we don't care here).
+ */
+ if (LIST_INLIST(&conn->idle_list)) {
+ LIST_DEL_INIT(&conn->idle_list);
+ conn_tree = (conn->flags & CO_FL_SAFE_LIST) ?
+ &srv->per_thr[thr].safe_conns :
+ &srv->per_thr[thr].idle_conns;
+ } else {
+ conn_tree = &srv->per_thr[thr].avail_conns;
+ }
+
+ ceb64_item_delete(conn_tree, node, key, conn->hash_node);
}
int conn_create_mux(struct connection *conn, int *closed_connection)
/* Make sure the connection is not left in the idle connection tree */
if (conn->hash_node != NULL)
- BUG_ON(conn->hash_node->node.node.leaf_p != NULL);
+ BUG_ON(ceb_intree(&conn->hash_node->node));
pool_free(pool_head_conn_hash_node, conn->hash_node);
conn->hash_node = NULL;
}
hash = conn_calculate_hash(&hash_params);
- conn->hash_node->node.key = hash;
+ conn->hash_node->key = hash;
conn->target = &srv->obj_type;
srv_use_conn(srv, conn);
TRACE_DEVEL("reusable idle connection", FCGI_EV_STRM_END, fconn->conn);
return;
}
- else if (!fconn->conn->hash_node->node.node.leaf_p &&
+ else if (!ceb_intree(&fconn->conn->hash_node->node) &&
fcgi_avail_streams(fconn->conn) > 0 && objt_server(fconn->conn->target) &&
!LIST_INLIST(&fconn->conn->sess_el)) {
srv_add_to_avail_list(__objt_server(fconn->conn->target), fconn->conn);
return;
}
- else if (!h2c->conn->hash_node->node.node.leaf_p &&
+ else if (!ceb_intree(&h2c->conn->hash_node->node) &&
h2_avail_streams(h2c->conn) > 0 && objt_server(h2c->conn->target) &&
!LIST_INLIST(&h2c->conn->sess_el)) {
srv_add_to_avail_list(__objt_server(h2c->conn->target), h2c->conn);
conn = NULL;
goto end;
}
- else if (!conn->hash_node->node.node.leaf_p &&
+ else if (!ceb_intree(&conn->hash_node->node) &&
qmux_avail_streams(conn) &&
objt_server(conn->target)) {
TRACE_DEVEL("mark connection as available for reuse", QMUX_EV_STRM_END, conn);
TRACE_DEVEL("reusable idle connection", SPOP_EV_STRM_END);
return;
}
- else if (!spop_conn->conn->hash_node->node.node.leaf_p &&
+ else if (!ceb_intree(&spop_conn->conn->hash_node->node) &&
spop_avail_streams(spop_conn->conn) > 0 && objt_server(spop_conn->conn->target) &&
!LIST_INLIST(&spop_conn->conn->sess_el)) {
srv_add_to_avail_list(__objt_server(spop_conn->conn->target), spop_conn->conn);
#include <ctype.h>
#include <errno.h>
+#include <import/ceb64_tree.h>
#include <import/cebis_tree.h>
#include <import/eb64tree.h>
return -1;
for (i = 0; i < global.nbthread; i++) {
- srv->per_thr[i].idle_conns = EB_ROOT;
- srv->per_thr[i].safe_conns = EB_ROOT;
- srv->per_thr[i].avail_conns = EB_ROOT;
+ srv->per_thr[i].idle_conns = NULL;
+ srv->per_thr[i].safe_conns = NULL;
+ srv->per_thr[i].avail_conns = NULL;
MT_LIST_INIT(&srv->per_thr[i].sess_conns);
MT_LIST_INIT(&srv->per_thr[i].streams);
/* retrieve a connection from its <hash> in <tree>
* returns NULL if no connection found
*/
-struct connection *srv_lookup_conn(struct eb_root *tree, uint64_t hash)
+struct connection *srv_lookup_conn(struct ceb_root **tree, uint64_t hash)
{
- struct eb64_node *node = NULL;
+ struct conn_hash_node *hash_node;
struct connection *conn = NULL;
- struct conn_hash_node *hash_node = NULL;
- node = eb64_lookup(tree, hash);
- if (node) {
- hash_node = eb64_entry(node, struct conn_hash_node, node);
+ hash_node = ceb64_item_lookup(tree, node, key, hash, struct conn_hash_node);
+ if (hash_node)
conn = hash_node->conn;
- }
return conn;
}
/* retrieve the next connection sharing the same hash as <conn>
* returns NULL if no connection found
*/
-struct connection *srv_lookup_conn_next(struct connection *conn)
+struct connection *srv_lookup_conn_next(struct ceb_root **tree, struct connection *conn)
{
- struct eb64_node *node = NULL;
- struct connection *next_conn = NULL;
- struct conn_hash_node *hash_node = NULL;
+ struct conn_hash_node *hash_node;
- node = eb64_next_dup(&conn->hash_node->node);
- if (node) {
- hash_node = eb64_entry(node, struct conn_hash_node, node);
- next_conn = hash_node->conn;
- }
+ hash_node = ceb64_item_next_dup(tree, node, key, conn->hash_node);
+ if (hash_node)
+ conn = hash_node->conn;
- return next_conn;
+ return conn;
}
/* Add <conn> in <srv> idle trees. Set <is_safe> if connection is deemed safe
*/
void _srv_add_idle(struct server *srv, struct connection *conn, int is_safe)
{
- struct eb_root *tree = is_safe ? &srv->per_thr[tid].safe_conns :
+ struct ceb_root **tree = is_safe ? &srv->per_thr[tid].safe_conns :
&srv->per_thr[tid].idle_conns;
/* first insert in idle or safe tree. */
- eb64_insert(tree, &conn->hash_node->node);
+ ceb64_item_insert(tree, node, key, conn->hash_node);
/* insert in list sorted by connection usage. */
LIST_APPEND(&srv->per_thr[tid].idle_conn_list, &conn->idle_list);
((srv->proxy->options & PR_O_REUSE_MASK) != PR_O_REUSE_NEVR) &&
ha_used_fds < global.tune.pool_high_count &&
(srv->max_idle_conns == -1 || srv->max_idle_conns > srv->curr_idle_conns) &&
- ((eb_is_empty(&srv->per_thr[tid].safe_conns) &&
- (is_safe || eb_is_empty(&srv->per_thr[tid].idle_conns))) ||
+ ((ceb_isempty(&srv->per_thr[tid].safe_conns) &&
+ (is_safe || ceb_isempty(&srv->per_thr[tid].idle_conns))) ||
(ha_used_fds < global.tune.pool_low_count &&
(srv->curr_used_conns + srv->curr_idle_conns <=
MAX(srv->curr_used_conns, srv->est_need_conns) + srv->low_idle_conns ||
{
/* connection cannot be in idle list if used as an avail idle conn. */
BUG_ON(LIST_INLIST(&conn->idle_list));
- eb64_insert(&srv->per_thr[tid].avail_conns, &conn->hash_node->node);
+ ceb64_item_insert(&srv->per_thr[tid].avail_conns, node, key, conn->hash_node);
}
struct task *srv_cleanup_idle_conns(struct task *task, void *context, unsigned int state)
*/
static void srv_close_idle_conns(struct server *srv)
{
- struct eb_root **cleaned_tree;
+ struct ceb_root ***cleaned_tree;
int i;
for (i = 0; i < global.nbthread; ++i) {
- struct eb_root *conn_trees[] = {
+ struct ceb_root **conn_trees[] = {
&srv->per_thr[i].idle_conns,
&srv->per_thr[i].safe_conns,
&srv->per_thr[i].avail_conns,
};
for (cleaned_tree = conn_trees; *cleaned_tree; ++cleaned_tree) {
- while (!eb_is_empty(*cleaned_tree)) {
- struct eb64_node *node = eb64_first(*cleaned_tree);
- struct conn_hash_node *conn_hash_node = eb64_entry(node, struct conn_hash_node, node);
- struct connection *conn = conn_hash_node->conn;
+ while (!ceb_isempty(*cleaned_tree)) {
+ struct connection *conn;
+ conn = ceb64_item_first(*cleaned_tree, node, key, struct conn_hash_node)->conn;
if (conn->ctrl->ctrl_close)
conn->ctrl->ctrl_close(conn);
conn_delete_from_tree(conn, i);
/* Search into pconns for a connection with matching params and available streams. */
list_for_each_entry(srv_conn, &pconns->conn_list, sess_el) {
- if ((srv_conn->hash_node && srv_conn->hash_node->node.key == hash) &&
+ if ((srv_conn->hash_node && srv_conn->hash_node->key == hash) &&
srv_conn->mux &&
(srv_conn->mux->avail_streams(srv_conn) > 0) &&
!(srv_conn->flags & CO_FL_WAIT_XPRT)) {