]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MAJOR: proxy: convert server list to a doubly linked struct list
authorAmaury Denoyelle <adenoyelle@haproxy.com>
Wed, 22 Jul 2026 13:01:59 +0000 (15:01 +0200)
committerAmaury Denoyelle <adenoyelle@haproxy.com>
Fri, 24 Jul 2026 07:33:31 +0000 (09:33 +0200)
Servers are stored in a list in their parent proxy. Prior to this patch,
this list was singly linked.

This patch converts the proxy server list to a doubly linked struct
list. Server <next> pointer is replaced by a struct list <el_px> attach
point.

The main benefit from this patch is that it removes the bottleneck
performance for add and delete server operations at runtime. As with
main proxies list conversion, this is labelled as major as it is an API
change.

Most of the changes are straightforward : for/while statements are
replaced by list_for_each_entry() macros. LIST_ISEMPTY() is now used to
detect if a proxy does not contain any server.

Server insertion at the front position during config parsing is kept at
the moment, with reordering on post parsing. With the current patch,
this is not strictly necessary so this will be removed in a next change.

27 files changed:
addons/promex/service-prometheus.c
include/haproxy/proxy-t.h
include/haproxy/proxy.h
include/haproxy/server-t.h
include/haproxy/server.h
src/backend.c
src/cfgdiag.c
src/check.c
src/fcgi-app.c
src/flt_spoe.c
src/haproxy.c
src/http_client.c
src/lb_chash.c
src/lb_fas.c
src/lb_fwlc.c
src/lb_fwrr.c
src/lb_map.c
src/lb_ss.c
src/log.c
src/proxy.c
src/resolvers.c
src/server.c
src/server_state.c
src/sink.c
src/stats-html.c
src/stats-proxy.c
src/stats.c

index e28dc7d554e410073f3d93ef4f31b4625f21cfa0..d655c4345017153dc5be569f3ec193ade5a99130 100644 (file)
@@ -966,13 +966,11 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
                        switch (ctx->field_num) {
                                case ST_I_PX_AGG_SRV_CHECK_STATUS: // DEPRECATED
                                case ST_I_PX_AGG_SRV_STATUS:
-                                       if (!px->srv)
+                                       if (LIST_ISEMPTY(&px->servers))
                                                goto next_px;
-                                       sv = px->srv;
-                                       while (sv) {
+                                       list_for_each_entry(sv, &px->servers, el_px) {
                                                srv_state = promex_srv_status(sv);
                                                srv_state_count[srv_state] += 1;
-                                               sv = sv->next;
                                        }
                                        for (; ctx->obj_state < PROMEX_SRV_STATE_COUNT; ctx->obj_state++) {
                                                val = mkf_u32(FN_GAUGE, srv_state_count[ctx->obj_state]);
@@ -986,15 +984,13 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
                                        ctx->obj_state = 0;
                                        goto next_px;
                                case ST_I_PX_AGG_CHECK_STATUS:
-                                       if (!px->srv)
+                                       if (LIST_ISEMPTY(&px->servers))
                                                goto next_px;
-                                       sv = px->srv;
-                                       while (sv) {
+                                       list_for_each_entry(sv, &px->servers, el_px) {
                                                if ((sv->check.state & (CHK_ST_ENABLED|CHK_ST_PAUSED)) == CHK_ST_ENABLED) {
                                                        srv_check_status = sv->check.status;
                                                        srv_check_count[srv_check_status] += 1;
                                                }
-                                               sv = sv->next;
                                        }
                                        for (; ctx->obj_state < HCHK_STATUS_SIZE; ctx->obj_state++) {
                                                if (get_check_status_result(ctx->obj_state) < CHK_RES_FAILED)
@@ -1011,7 +1007,7 @@ static int promex_dump_back_metrics(struct appctx *appctx, struct htx *htx)
                                        ctx->obj_state = 0;
                                        goto next_px;
                                case ST_I_PX_STATUS:
-                                       bkd_state = ((px->lbprm.tot_weight > 0 || !px->srv) ? 1 : 0);
+                                       bkd_state = ((px->lbprm.tot_weight > 0 || LIST_ISEMPTY(&px->servers)) ? 1 : 0);
                                        for (; ctx->obj_state < PROMEX_BACK_STATE_COUNT; ctx->obj_state++) {
                                                labels[lb_idx].name = ist("state");
                                                labels[lb_idx].value = promex_back_st[ctx->obj_state];
index 3d30cc28d6cde97091d7a41db05bcf2b0caa81da..dd6b186747941a5a469b800336b2a93be631ceab 100644 (file)
@@ -367,7 +367,8 @@ struct proxy {
 #ifdef USE_QUIC
        struct list quic_init_rules;            /* quic-initial rules */
 #endif
-       struct server *srv, *defsrv;            /* known servers; default server configuration */
+       struct list servers;            /* servers present in current backend */
+       struct server *defsrv;                  /* default server configuration */
        struct lbprm lbprm;                     /* load-balancing parameters */
        int srv_act, srv_bck;                   /* # of servers eligible for LB (UP|!checked) AND (enabled+weight!=0) */
        int load_server_state_from_file;        /* location of the file containing server state.
index 8470d61cc88a7ad1676d5eff477ba7edcbc17f96..0c0bc730d89104ef46adaf22af26351fa1beeff4 100644 (file)
@@ -348,12 +348,15 @@ static inline struct proxy *main_proxies_next(const struct proxy *px)
 
 static inline struct server *proxy_first_server(const struct proxy *px)
 {
-       return px->srv;
+       return !LIST_ISEMPTY(&px->servers) ?
+         LIST_NEXT(&px->servers, struct server *, el_px) : NULL;
 }
 
 static inline struct server *proxy_next_server(const struct server *srv)
 {
-       return srv->next;
+       if (srv->el_px.n == &srv->proxy->servers)
+               return NULL;
+       return LIST_ELEM(srv->el_px.n, struct server *, el_px);
 }
 
 #endif /* _HAPROXY_PROXY_H */
index 75e1cf6033bdcd3b8f8cef0d882e5a2ff3bf564e..a7dfd6de02b532277bdc2b793a8fbcd808beb8ff 100644 (file)
@@ -350,8 +350,8 @@ struct server {
        signed char use_ssl;                    /* ssl enabled (1: on, 0: disabled, -1 forced off)  */
        unsigned int flags;                     /* server flags (SRV_F_*) */
        unsigned int pp_opts;                   /* proxy protocol options (SRV_PP_*) */
-       struct mt_list global_list;             /* attach point in the global servers_list */
-       struct server *next;
+       struct mt_list global_list;             /* attach point in the global servers */
+       struct list el_px;                      /* attach point in parent proxy */
        int cklen;                              /* the len of the cookie, to speed up checks */
        int rdr_len;                            /* the length of the redirection prefix */
        char *cookie;                           /* the id set in the cookie */
index 6d47cb343ea73514dc990784cd7ca1e6e40445c9..17764aff9169bc2e68183b616bf26a0e1a190427 100644 (file)
@@ -395,18 +395,8 @@ static inline int srv_is_transparent(const struct server *srv)
 static inline void srv_detach(struct server *srv)
 {
        struct proxy *px = srv->proxy;
-       struct server *prev;
-
-       if (px->srv == srv) {
-               px->srv = srv->next;
-       }
-       else {
-               for (prev = px->srv; prev && prev->next != srv; prev = prev->next)
-                       ;
-               BUG_ON(!prev); /* Server instance not found in proxy list ? */
-               prev->next = srv->next;
-       }
 
+       LIST_DEL_INIT(&srv->el_px);
        /* Reset the proxy's ready_srv if it was this one. */
        HA_ATOMIC_CAS(&px->ready_srv, &srv, NULL);
 }
index 388e4ad032b064d369d3cedf88f90582a2e578ab..ab1a8d9635f382a2a507f5c328cf7c8e359d1256 100644 (file)
@@ -127,7 +127,7 @@ void recount_servers(struct proxy *px)
        px->srv_act = px->srv_bck = 0;
        px->lbprm.tot_wact = px->lbprm.tot_wbck = 0;
        px->lbprm.fbck = NULL;
-       for (srv = px->srv; srv != NULL; srv = srv->next) {
+       list_for_each_entry(srv, &px->servers, el_px) {
                if (!srv_willbe_usable(srv))
                        continue;
 
@@ -3011,7 +3011,7 @@ int tcp_persist_rdp_cookie(struct stream *s, struct channel *req, int an_bit)
        struct proxy    *px   = s->be;
        int              ret;
        struct sample    smp;
-       struct server *srv = px->srv;
+       struct server *srv;
        uint16_t port;
        uint32_t addr;
        char *p;
@@ -3042,7 +3042,7 @@ int tcp_persist_rdp_cookie(struct stream *s, struct channel *req, int an_bit)
                goto no_cookie;
 
        stream_set_target(s, NULL);
-       while (srv) {
+       list_for_each_entry(srv, &px->servers, el_px) {
                if (srv->addr.ss_family == AF_INET &&
                    port == srv->svc_port &&
                    addr == ((struct sockaddr_in *)&srv->addr)->sin_addr.s_addr) {
@@ -3053,7 +3053,6 @@ int tcp_persist_rdp_cookie(struct stream *s, struct channel *req, int an_bit)
                                break;
                        }
                }
-               srv = srv->next;
        }
 
 no_cookie:
@@ -3408,7 +3407,7 @@ smp_fetch_connslots(const struct arg *args, struct sample *smp, const char *kw,
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
 
-       for (iterator = px->srv; iterator; iterator = iterator->next) {
+       list_for_each_entry(iterator, &px->servers, el_px) {
                if (iterator->cur_state == SRV_ST_STOPPED)
                        continue;
 
@@ -3572,7 +3571,7 @@ smp_fetch_be_conn_free(const struct arg *args, struct sample *smp, const char *k
        smp->data.type = SMP_T_SINT;
        smp->data.u.sint = 0;
 
-       for (iterator = px->srv; iterator; iterator = iterator->next) {
+       list_for_each_entry(iterator, &px->servers, el_px) {
                if (iterator->cur_state == SRV_ST_STOPPED)
                        continue;
 
index 592120b3b6fb7e35bf5a2c9002e867cd8b98c427..4774dbf4215c4f40b6ae61f558e4493cd21c392e 100644 (file)
@@ -91,7 +91,7 @@ static void run_servers_diag(int *ret)
        struct server *srv;
 
        list_for_each_entry(px, &main_proxies, el) {
-               for (srv = px->srv; srv; srv = srv->next) {
+               list_for_each_entry(srv, &px->servers, el_px) {
                        srv_diag_cookies(ret, srv, &cookies_tree);
                        srv_diag_check_reuse(ret, srv, px);
                }
index 3a5442094cef45bba84e7c3df53b1ce9563fae37..78bde2e89a06d5d5518f61fb14464118f88508c3 100644 (file)
@@ -1698,7 +1698,7 @@ static int start_checks()
         * too short an interval for all others.
         */
        list_for_each_entry(px, &main_proxies, el) {
-               for (s = px->srv; s; s = s->next) {
+               list_for_each_entry(s, &px->servers, el_px) {
                        if ((px->options2 & PR_O2_USE_SBUF_CHECK) &&
                            (s->check.tcpcheck->rs && s->check.tcpcheck->rs->flags & TCPCHK_RULES_MAY_USE_SBUF))
                                s->check.state |= CHK_ST_USE_SMALL_BUFF;
@@ -1737,7 +1737,7 @@ static int start_checks()
                        }
                }
 
-               for (s = px->srv; s; s = s->next) {
+               list_for_each_entry(s, &px->servers, el_px) {
                        /* A task for the main check */
                        if (s->check.state & CHK_ST_CONFIGURED) {
                                if (s->check.type == PR_O2_EXT_CHK) {
index 49f87a70fb58e9ed1d89ff3ed313b5951c6bd163..efcfd98d834c7f678aa6471ed9d02045d105a79e 100644 (file)
@@ -643,7 +643,7 @@ static int cfg_fcgi_apps_postparser()
                if (fcgi_conf && !(px->options2 & PR_O2_RSTRICT_REQ_HDR_NAMES_MASK))
                        px->options2 |= PR_O2_RSTRICT_REQ_HDR_NAMES_DEL;
 
-               for (srv = px->srv; srv; srv = srv->next) {
+               list_for_each_entry(srv, &px->servers, el_px) {
                        if (srv->mux_proto && isteq(srv->mux_proto->mux_proto, ist("fcgi"))) {
                                nb_fcgi_srv++;
                                if (fcgi_conf)
index 48bce2507725ac959213792f7e474f9ac1da9df5..706c5c8f8a89c8ac746b6c2058a637e943a76e76 100644 (file)
@@ -1237,7 +1237,7 @@ static int spoe_init(struct proxy *px, struct flt_conf *fconf)
        conf->agent->fe.options2 |= PR_O2_INDEPSTR;
        conf->agent->fe.conn_retries = CONN_RETRIES;
        conf->agent->fe.accept = frontend_accept;
-       conf->agent->fe.srv = NULL;
+       LIST_INIT(&conf->agent->fe.servers);
        conf->agent->fe.timeout.client = TICK_ETERNITY;
        conf->agent->fe.fe_req_ana = AN_REQ_SWITCHING_RULES;
 
@@ -2870,7 +2870,7 @@ static int spoe_postcheck_spop_proxy(struct proxy *px)
        if (!(px->cap & PR_CAP_BE) || px->mode != PR_MODE_SPOP)
                goto out;
 
-       for (srv = px->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &px->servers, el_px) {
                if (srv->pool_conn_name) {
                        ha_free(&srv->pool_conn_name);
                        release_sample_expr(srv->pool_conn_name_expr);
index 5838271a4b98564bd54d768442560bba46bf6022..b242be44b0f1c80fa303d4a286bccac74c47bd3b 100644 (file)
@@ -908,10 +908,10 @@ static void sig_dump_state(struct sig_handler *sh)
 
        ha_warning("SIGHUP received, dumping servers states.\n");
        list_for_each_entry(p, &main_proxies, el) {
-               struct server *s = p->srv;
+               struct server *s;
 
                send_log(p, LOG_NOTICE, "SIGHUP received, dumping servers states for proxy %s.\n", p->id);
-               while (s) {
+               list_for_each_entry(s, &p->servers, el_px) {
                        chunk_printf(&trash,
                                     "SIGHUP: Server %s/%s is %s. Conn: %d act, %d pend, %llu tot.",
                                     p->id, s->id,
@@ -919,11 +919,10 @@ static void sig_dump_state(struct sig_handler *sh)
                                     s->cur_sess, s->queueslength, (ullong)COUNTERS_SHARED_TOTAL(s->counters.shared.tg, cum_sess, HA_ATOMIC_LOAD));
                        ha_warning("%s\n", trash.area);
                        send_log(p, LOG_NOTICE, "%s\n", trash.area);
-                       s = s->next;
                }
 
                /* FIXME: those info are a bit outdated. We should be able to distinguish between FE and BE. */
-               if (!p->srv) {
+               if (LIST_ISEMPTY(&p->servers)) {
                        chunk_printf(&trash,
                                     "SIGHUP: Proxy %s has no servers. Conn: act(FE+BE): %d+%d, %d pend (%d unass), tot(FE+BE): %llu+%llu.",
                                     p->id,
@@ -2267,7 +2266,7 @@ static void step_init_2(int argc, char** argv)
                        continue;
 
                list_for_each_entry(pscf, &post_server_check_list, list) {
-                       for (srv = px->srv; srv; srv = srv->next) {
+                       list_for_each_entry(srv, &px->servers, el_px) {
                                err_code |= pscf->fct(srv);
                                if (err_code & (ERR_ABORT|ERR_FATAL)) {
                                        ha_alert("Fatal errors found in configuration.\n");
index a34ad2bd211378a74d4cd064639ca2b58c1721ae..5e0b1ce971dacb2c31879e4c8349ec67364cd9af 100644 (file)
@@ -505,7 +505,7 @@ int httpclient_set_proxy(struct httpclient *hc, struct proxy *px)
 
        hc->px = px;
 
-       for (srv = px->srv; srv != NULL; srv = srv->next) {
+       list_for_each_entry(srv, &px->servers, el_px) {
                if (srv->xprt == xprt_get(XPRT_RAW)) {
                        hc->srv_raw = srv;
 #ifdef USE_OPENSSL
@@ -1277,7 +1277,7 @@ static int httpclient_postcheck_proxy(struct proxy *curproxy)
 #ifdef USE_OPENSSL
        /* initialize the SNI for the SSL servers */
 
-       for (srv = curproxy->srv; srv != NULL; srv = srv->next) {
+       list_for_each_entry(srv, &curproxy->servers, el_px) {
                if (srv->xprt == xprt_get(XPRT_SSL)) {
                        srv_ssl = srv;
                }
index d19f06de44a4a886146495575a7a849c6f027bb6..a92ba7cefec72d927eae8bf816c959154c216589 100644 (file)
@@ -606,7 +606,7 @@ static int chash_init_server_tree(struct proxy *p)
        struct eb_root init_head = EB_ROOT;
 
        p->lbprm.wdiv = BE_WEIGHT_SCALE;
-       for (srv = p->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &p->servers, el_px) {
                srv->next_eweight = (srv->uweight * p->lbprm.wdiv + p->lbprm.wmult - 1) / p->lbprm.wmult;
                srv_lb_commit_status(srv);
        }
@@ -619,7 +619,7 @@ static int chash_init_server_tree(struct proxy *p)
        p->lbprm.chash.last = NULL;
 
        /* queue active and backup servers in two distinct groups */
-       for (srv = p->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &p->servers, el_px) {
                srv->lb_tree = (srv->flags & SRV_F_BACKUP) ? &p->lbprm.chash.bck : &p->lbprm.chash.act;
                srv->lb_nodes_tot = srv->uweight * BE_WEIGHT_SCALE;
                srv->lb_nodes_now = 0;
index 3f601d8a57e040857f92920dade0b96aef03ce5c..845d317717ea07640da150fbd4bd645f0ec42b54 100644 (file)
@@ -262,7 +262,7 @@ static int fas_init_server_tree(struct proxy *p)
        struct eb_root init_head = EB_ROOT;
 
        p->lbprm.wdiv = BE_WEIGHT_SCALE;
-       for (srv = p->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &p->servers, el_px) {
                srv->next_eweight = (srv->uweight * p->lbprm.wdiv + p->lbprm.wmult - 1) / p->lbprm.wmult;
                srv_lb_commit_status(srv);
        }
@@ -274,7 +274,7 @@ static int fas_init_server_tree(struct proxy *p)
        p->lbprm.fas.bck = init_head;
 
        /* queue active and backup servers in two distinct groups */
-       for (srv = p->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &p->servers, el_px) {
                if (!srv_currently_usable(srv))
                        continue;
                srv->lb_tree = (srv->flags & SRV_F_BACKUP) ? &p->lbprm.fas.bck : &p->lbprm.fas.act;
index c635d5edc866d3d1c3bc1ccc15057071847e155c..8fd1e73836a1b788aac4e7b5af8c85b3748dcd02 100644 (file)
@@ -716,7 +716,7 @@ static int fwlc_init_server_tree(struct proxy *p)
        struct eb_root init_head = EB_ROOT;
 
        p->lbprm.wdiv = BE_WEIGHT_SCALE;
-       for (srv = p->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &p->servers, el_px) {
                srv->next_eweight = (srv->uweight * p->lbprm.wdiv + p->lbprm.wmult - 1) / p->lbprm.wmult;
                srv_lb_commit_status(srv);
        }
@@ -730,7 +730,7 @@ static int fwlc_init_server_tree(struct proxy *p)
        p->lbprm.fwlc.bck = init_head;
 
        /* queue active and backup servers in two distinct groups */
-       for (srv = p->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &p->servers, el_px) {
                if (!srv_currently_usable(srv))
                        continue;
                srv->lb_tree = (srv->flags & SRV_F_BACKUP) ? &p->lbprm.fwlc.bck : &p->lbprm.fwlc.act;
index 28fca679d64ddab1a7683c985bf52f737211529e..92d786fb346f0ec4b88081cc4439aff0492d8855 100644 (file)
@@ -301,7 +301,7 @@ static int fwrr_init_server_groups(struct proxy *p)
        int i, j;
 
        p->lbprm.wdiv = BE_WEIGHT_SCALE;
-       for (srv = p->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &p->servers, el_px) {
                srv->next_eweight = (srv->uweight * p->lbprm.wdiv + p->lbprm.wmult - 1) / p->lbprm.wmult;
                srv_lb_commit_status(srv);
        }
@@ -332,7 +332,7 @@ static int fwrr_init_server_groups(struct proxy *p)
 
                /* queue active and backup servers in two distinct groups */
                j = 0;
-               for (srv = p->srv; srv; srv = srv->next) {
+               list_for_each_entry(srv, &p->servers, el_px) {
                        j++;
                        if (!srv_currently_usable(srv))
                                continue;
index e71400349644c35f882877874fc15c86f92aa8e4..3252d4469f59145207477fb85efaa14e88c4c478 100644 (file)
@@ -50,13 +50,13 @@ static void recalc_server_map(struct proxy *px)
         * the first declared. This is an important assumption for the backup
         * case, where we want the first server only.
         */
-       for (cur = px->srv; cur; cur = cur->next)
+       list_for_each_entry(cur, &px->servers, el_px)
                cur->wscore = 0;
 
        for (o = 0; o < tot; o++) {
                int max = 0;
                best = NULL;
-               for (cur = px->srv; cur; cur = cur->next) {
+               list_for_each_entry(cur, &px->servers, el_px) {
                        if ((cur->flags & SRV_F_BACKUP) == flag &&
                            srv_willbe_usable(cur)) {
                                int v;
@@ -144,7 +144,7 @@ static int init_server_map(struct proxy *p)
        int pgcd;
        int act, bck;
 
-       if (!p->srv)
+       if (LIST_ISEMPTY(&p->servers))
                return 0;
 
        /* We will factor the weights to reduce the table,
@@ -176,7 +176,7 @@ static int init_server_map(struct proxy *p)
        p->lbprm.wmult = pgcd;
 
        act = bck = 0;
-       for (srv = p->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &p->servers, el_px) {
                srv->next_eweight = (srv->uweight * p->lbprm.wdiv + p->lbprm.wmult - 1) / p->lbprm.wmult;
 
                if (srv->flags & SRV_F_BACKUP)
index d9c18ee03e7a992d0a1acbf313a86dced0a23d4d..fd62a2a0b1e966d63c353ea687bdadcd0b76c41b 100644 (file)
@@ -44,7 +44,7 @@ static void recalc_server_ss(struct proxy *px)
 
        first = NULL;
 
-       for (cur = px->srv; cur; cur = cur->next) {
+       list_for_each_entry(cur, &px->servers, el_px) {
                if ((cur->flags & SRV_F_BACKUP) == flag &&
                    srv_willbe_usable(cur)) {
                        first = cur;
@@ -148,10 +148,10 @@ static int init_server_ss(struct proxy *p)
 {
        struct server *srv;
 
-       if (!p->srv)
+       if (LIST_ISEMPTY(&p->servers))
                return 0;
 
-       for (srv = p->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &p->servers, el_px) {
                srv->next_eweight = 1; /* ignore weights, all servers have the same weight */
                srv_lb_commit_status(srv);
        }
index bd3a03aa98b8ec466aadc1fbe8f56d405373a2a5..8b06061156a2ec9daac0fa78cd2b943f18f869b5 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1447,8 +1447,7 @@ static int postcheck_log_backend(struct proxy *be)
        }
 
        /* finish the initialization of proxy's servers */
-       srv = be->srv;
-       while (srv) {
+       list_for_each_entry(srv, &be->servers, el_px) {
                BUG_ON(srv->log_target);
                BUG_ON(srv->addr_type.proto_type != PROTO_TYPE_DGRAM &&
                       srv->addr_type.proto_type != PROTO_TYPE_STREAM);
@@ -1497,7 +1496,6 @@ static int postcheck_log_backend(struct proxy *be)
                        goto end;
                }
                srv->log_target->flags |= LOG_TARGET_FL_RESOLVED;
-               srv = srv->next;
        }
  end:
        if (err_code & ERR_CODE) {
index 4789a86f6017d1ed735f95d1f551a3595a0bc8fb..8b72f9d4525a86a6222d8833836aeb2b28bcaf6c 100644 (file)
@@ -326,7 +326,7 @@ static inline void proxy_free_common(struct proxy *px)
  */
 void deinit_proxy(struct proxy *p)
 {
-       struct server *s, *s_next;
+       struct server *s, *s_back;
        struct cap_hdr *h,*h_next;
        struct listener *l,*l_next;
        struct bind_conf *bind_conf, *bind_back;
@@ -406,17 +406,14 @@ void deinit_proxy(struct proxy *p)
                h = h_next;
        }/* end while(h) */
 
-       s = p->srv;
-       while (s) {
+       list_for_each_entry_safe(s, s_back, &p->servers, el_px) {
                list_for_each_entry(srvdf, &server_deinit_list, list)
                        srvdf->fct(s);
 
                if (p->lbprm.ops && p->lbprm.ops->server_deinit)
                        p->lbprm.ops->server_deinit(s);
 
-               s_next = s->next;
                srv_drop(s);
-               s = s_next;
        }/* end while(s) */
 
        /* also free default-server parameters since some of them might have
@@ -1571,6 +1568,7 @@ void init_new_proxy(struct proxy *p)
        memset(p, 0, sizeof(struct proxy));
        p->obj_type = OBJ_TYPE_PROXY;
        LIST_INIT(&p->global_list);
+       LIST_INIT(&p->servers);
        LIST_INIT(&p->el);
        LIST_INIT(&p->acl);
        LIST_INIT(&p->http_req_rules);
@@ -1663,8 +1661,9 @@ int proxy_init_per_thr(struct proxy *px)
 
 int proxy_finalize(struct proxy *px, int *err_code)
 {
+       struct list tmp_list = LIST_HEAD_INIT(tmp_list);
        struct bind_conf *bind_conf;
-       struct server *newsrv;
+       struct server *newsrv, *newsrv_back;
        struct switching_rule *rule;
        struct server_rule *srule;
        struct sticking_rule *mrule;
@@ -1997,7 +1996,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
                        free(px->defbe.name);
                        px->defbe.be = target;
                        /* Emit a warning if this proxy also has some servers */
-                       if (px->srv) {
+                       if (!LIST_ISEMPTY(&px->servers)) {
                                ha_warning("In proxy '%s', the 'default_backend' rule always has precedence over the servers, which will never be used.\n",
                                           px->id);
                                *err_code |= ERR_WARN;
@@ -2176,7 +2175,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
 #endif
 
        /* Warn is a switch-mode http is used on a TCP listener with servers but no backend */
-       if (!px->defbe.name && LIST_ISEMPTY(&px->switching_rules) && px->srv) {
+       if (!px->defbe.name && LIST_ISEMPTY(&px->switching_rules) && !LIST_ISEMPTY(&px->servers)) {
                if ((px->options & PR_O_HTTP_UPG) && px->mode == PR_MODE_TCP)
                        ha_warning("Proxy '%s' : 'switch-mode http' configured for a %s %s with no backend. "
                                   "Incoming connections upgraded to HTTP cannot be routed to TCP servers\n",
@@ -2436,7 +2435,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
 
        if (!(px->cap & PR_CAP_INT) && (px->mode == PR_MODE_TCP || px->mode == PR_MODE_HTTP) &&
            (((px->cap & PR_CAP_FE) && !px->timeout.client) ||
-            ((px->cap & PR_CAP_BE) && (px->srv) &&
+            ((px->cap & PR_CAP_BE) && !LIST_ISEMPTY(&px->servers) &&
              (!px->timeout.connect ||
               (!px->timeout.server && (px->mode == PR_MODE_HTTP || !px->timeout.tunnel)))))) {
                ha_warning("missing timeouts for %s '%s'.\n"
@@ -2497,17 +2496,11 @@ int proxy_finalize(struct proxy *px, int *err_code)
        }
 
        /* first, we will invert the servers list order */
-       newsrv = NULL;
-       while (px->srv) {
-               struct server *next;
-
-               next = px->srv->next;
-               px->srv->next = newsrv;
-               newsrv = px->srv;
-               if (!next)
-                       break;
-               px->srv = next;
+       list_for_each_entry_safe(newsrv, newsrv_back, &px->servers, el_px) {
+               LIST_DEL_INIT(&newsrv->el_px);
+               LIST_INSERT(&tmp_list, &newsrv->el_px);
        }
+       LIST_SPLICE(&px->servers, &tmp_list);
 
        /* Check that no server name conflicts. This causes trouble in the stats.
         * We only emit an error for the first conflict affecting each server,
@@ -2516,7 +2509,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
         * we simply have to check for the current server's duplicates to spot
         * conflicts.
         */
-       for (newsrv = px->srv; newsrv; newsrv = newsrv->next) {
+       list_for_each_entry(newsrv, &px->servers, el_px) {
                struct server *other_srv;
 
                /* Note: internal servers are not always registered and
@@ -2537,8 +2530,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
 
        /* assign automatic UIDs to servers which don't have one yet */
        next_id = 1;
-       newsrv = px->srv;
-       while (newsrv != NULL) {
+       list_for_each_entry(newsrv, &px->servers, el_px) {
                if (!newsrv->puid) {
                        /* server ID not set, use automatic numbering with first
                         * spare entry starting with next_svid.
@@ -2549,7 +2541,6 @@ int proxy_finalize(struct proxy *px, int *err_code)
                }
 
                next_id++;
-               newsrv = newsrv->next;
        }
 
        px->lbprm.wmult = 1; /* default weight multiplier */
@@ -2560,8 +2551,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
         * tasks to fill the emptied slots when a connection leaves.
         * Also, resolve deferred tracking dependency if needed.
         */
-       newsrv = px->srv;
-       while (newsrv != NULL) {
+       list_for_each_entry(newsrv, &px->servers, el_px) {
                set_usermsgs_ctx(newsrv->conf.file, newsrv->conf.line, &newsrv->obj_type);
 
                srv_minmax_conn_apply(newsrv);
@@ -2615,7 +2605,6 @@ int proxy_finalize(struct proxy *px, int *err_code)
 
        next_srv:
                reset_usermsgs_ctx();
-               newsrv = newsrv->next;
        }
 
        /*
@@ -2626,11 +2615,8 @@ int proxy_finalize(struct proxy *px, int *err_code)
         * have been provided yet.
         */
        if (px->ck_opts & PR_CK_DYNAMIC) {
-               newsrv = px->srv;
-               while (newsrv != NULL) {
+               list_for_each_entry(newsrv, &px->servers, el_px)
                        srv_set_dyncookie(newsrv);
-                       newsrv = newsrv->next;
-               }
 
        }
        /* We have to initialize the server lookup mechanism depending
@@ -2752,8 +2738,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
        /*
         * ensure that we're not cross-dressing a TCP server into HTTP.
         */
-       newsrv = px->srv;
-       while (newsrv != NULL) {
+       list_for_each_entry(newsrv, &px->servers, el_px) {
                if ((px->mode != PR_MODE_HTTP) && newsrv->rdr_len) {
                        ha_alert("%s '%s' : server cannot have cookie or redirect prefix in non-HTTP mode.\n",
                                 proxy_type_str(px), px->id);
@@ -2793,8 +2778,6 @@ int proxy_finalize(struct proxy *px, int *err_code)
                        *err_code |= ERR_FATAL | ERR_ALERT;
                        goto out;
                }
-
-               newsrv = newsrv->next;
        }
 
        /* Check filter configuration, if any */
@@ -2857,7 +2840,7 @@ int proxy_finalize(struct proxy *px, int *err_code)
 
        /* Check the mux protocols, if any, for each server attached to
         * the current proxy */
-       for (newsrv = px->srv; newsrv; newsrv = newsrv->next) {
+       list_for_each_entry(newsrv, &px->servers, el_px) {
                int mode = conn_pr_mode_to_proto_mode(px->mode);
                const struct mux_proto_list *mux_ent;
 
@@ -4682,7 +4665,7 @@ static int cli_parse_enable_dyncookie_backend(char **args, char *payload, struct
        px->ck_opts |= PR_CK_DYNAMIC;
        HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &px->lock);
 
-       for (s = px->srv; s != NULL; s = s->next) {
+       list_for_each_entry(s, &px->servers, el_px) {
                HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
                srv_set_dyncookie(s);
                HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
@@ -4717,7 +4700,7 @@ static int cli_parse_disable_dyncookie_backend(char **args, char *payload, struc
        px->ck_opts &= ~PR_CK_DYNAMIC;
        HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &px->lock);
 
-       for (s = px->srv; s != NULL; s = s->next) {
+       list_for_each_entry(s, &px->servers, el_px) {
                HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
                if (!(s->flags & SRV_F_COOKIESET))
                        ha_free(&s->cookie);
@@ -4762,7 +4745,7 @@ static int cli_parse_set_dyncookie_key_backend(char **args, char *payload, struc
        px->dyncookie_key = newkey;
        HA_RWLOCK_WRUNLOCK(PROXY_LOCK, &px->lock);
 
-       for (s = px->srv; s != NULL; s = s->next) {
+       list_for_each_entry(s, &px->servers, el_px) {
                HA_SPIN_LOCK(SERVER_LOCK, &s->lock);
                srv_set_dyncookie(s);
                HA_SPIN_UNLOCK(SERVER_LOCK, &s->lock);
@@ -5062,7 +5045,7 @@ int be_check_for_deletion(const char *bename, struct proxy **pb, const char **pm
                goto out;
        }
 
-       if (be->srv) {
+       if (!LIST_ISEMPTY(&be->servers)) {
                msg = "Only a backend without server can be deleted.";
                goto out;
        }
index 1d99dd9536cef006e99ae37b5fe633d86bef0c9f..c166ccaa65dbaade7dcdd1f708103c27a777e90f 100644 (file)
@@ -2818,7 +2818,7 @@ static int resolvers_finalize_config(void)
                        continue;
                }
 
-               for (srv = px->srv; srv; srv = srv->next) {
+               list_for_each_entry(srv, &px->servers, el_px) {
                        struct resolvers *resolvers;
 
                        if (!srv->resolvers_id)
@@ -4064,8 +4064,7 @@ static int cfg_post_check_resolvers(void)
        list_for_each_entry(r, &sec_resolvers, list) {
                /* prepare forward server descriptors */
                if (r->px) {
-                       srv = r->px->srv;
-                       while (srv) {
+                       list_for_each_entry(srv, &r->px->servers, el_px) {
                                /* init ssl if needed */
                                if (srv->use_ssl == 1 && xprt_get(XPRT_SSL) && xprt_get(XPRT_SSL)->prepare_srv) {
                                        if (xprt_get(XPRT_SSL)->prepare_srv(srv)) {
@@ -4074,7 +4073,6 @@ static int cfg_post_check_resolvers(void)
                                                break;
                                        }
                                }
-                               srv = srv->next;
                        }
                }
        }
index 1171c7364cc868f566cf72326ead24abcca7418a..0e66762fc0270cd7ce16ded8b50f26f98f8fcc16 100644 (file)
@@ -550,8 +550,7 @@ static inline void srv_check_for_dup_dyncookie(struct server *s)
        struct proxy *p = s->proxy;
        struct server *tmpserv;
 
-       for (tmpserv = p->srv; tmpserv != NULL;
-           tmpserv = tmpserv->next) {
+       list_for_each_entry(tmpserv, &p->servers, el_px) {
                if (tmpserv == s)
                        continue;
                if (tmpserv->next_admin & SRV_ADMF_FMAINT)
@@ -2135,9 +2134,10 @@ void srv_shutdown_backup_streams(struct proxy *px, int why)
 {
        struct server *srv;
 
-       for (srv = px->srv; srv != NULL; srv = srv->next)
+       list_for_each_entry(srv, &px->servers, el_px) {
                if (srv->flags & SRV_F_BACKUP)
                        srv_shutdown_streams(srv, why);
+       }
 }
 
 static void srv_append_op_chg_cause(struct buffer *msg, struct server *s, enum srv_op_st_chg_cause cause)
@@ -2393,7 +2393,7 @@ void srv_compute_all_admin_states(struct proxy *px)
 {
        struct server *srv;
 
-       for (srv = px->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &px->servers, el_px) {
                if (srv->track)
                        continue;
                srv_propagate_admin_state(srv);
@@ -3185,21 +3185,15 @@ struct server *new_server(struct proxy *proxy)
 #endif
 
        // add server to proxy list:
-       /* TODO use a double-linked list for px->srv */
-       if (!(proxy->flags & PR_FL_CHECKED) || !proxy->srv) {
+       if (!(proxy->flags & PR_FL_CHECKED)) {
                /* they are linked backwards first during parsing
                 * This will be restablished after parsing.
                 */
-               srv->next = proxy->srv;
-               proxy->srv = srv;
+               LIST_INSERT(&proxy->servers, &srv->el_px);
        }
        else {
-               struct server *sv = proxy->srv;
-
                // runtime, add the server at the end of the list
-               while (sv && sv->next)
-                       sv = sv->next;
-               sv->next = srv;
+               LIST_APPEND(&proxy->servers, &srv->el_px);
        }
 
        HA_RWLOCK_INIT(&srv->path_params.param_lock);
@@ -5194,7 +5188,7 @@ struct server *snr_check_ip_callback(struct server *srv, void *ip, unsigned char
                return NULL;
 
        be = srv->proxy;
-       for (tmpsrv = be->srv; tmpsrv; tmpsrv = tmpsrv->next) {
+       list_for_each_entry(tmpsrv, &be->servers, el_px) {
                /* we found the current server is the same, ignore it */
                if (srv == tmpsrv)
                        continue;
@@ -5460,7 +5454,7 @@ int srv_init_addr(void)
                if (!(curproxy->cap & PR_CAP_BE) || (curproxy->flags & (PR_FL_DISABLED|PR_FL_STOPPED)))
                        continue;
 
-               for (srv = curproxy->srv; srv; srv = srv->next) {
+               list_for_each_entry(srv, &curproxy->servers, el_px) {
                        set_usermsgs_ctx(srv->conf.file, srv->conf.line, &srv->obj_type);
                        if (srv->hostname || srv->srvrq)
                                return_code |= srv_iterate_initaddr(srv);
index 426cad95609d02014e8673458c92b6e0b14bf855..def8de56d67bbb0f2dca47b0b0d180c23bb8ae71 100644 (file)
@@ -503,7 +503,7 @@ static void srv_state_px_update(const struct proxy *px, int vsn, struct eb_root
        struct server *srv;
        unsigned long key;
 
-       for (srv = px->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &px->servers, el_px) {
                chunk_printf(&trash, "%s %s", px->id, srv->id);
                key = XXH3(trash.area, trash.data, 0);
                node = eb64_lookup(st_tree, key);
@@ -837,7 +837,7 @@ void apply_server_state(void)
                struct eb_root local_state_tree = EB_ROOT_UNIQUE;
 
                /* Must be an enabled backend with at least a server */
-               if (!(curproxy->cap & PR_CAP_BE) || (curproxy->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) || !curproxy->srv)
+               if (!(curproxy->cap & PR_CAP_BE) || (curproxy->flags & (PR_FL_DISABLED|PR_FL_STOPPED)) || LIST_ISEMPTY(&curproxy->servers))
                        continue; /* next proxy */
 
                /* Mode must be specified */
index 7fa559a579b56d285565ec40e678df5d51454646..de2a4518a9a0511f85766ea41f7bf6c2cad9e269 100644 (file)
@@ -929,13 +929,11 @@ static int sink_finalize(struct sink *sink)
                /* prepare forward server descriptors */
                if (sink->forward_px) {
                        /* sink proxy is set: register all servers from the proxy */
-                       srv = sink->forward_px->srv;
-                       while (srv) {
+                       list_for_each_entry(srv, &sink->forward_px->servers, el_px) {
                                if (!sink_add_srv(sink, srv)) {
                                        err_code |= ERR_ALERT | ERR_FATAL;
                                        break;
                                }
-                               srv = srv->next;
                        }
                }
                /* init forwarding if at least one sft is registered */
index 4deeb695ab71c0f2ae3c53cc8482fad56849cf5c..ae256fb02dc5c64a5cd593b433a9a02abe117ef7 100644 (file)
@@ -1374,7 +1374,7 @@ void stats_dump_html_px_hdr(struct stconn *sc, struct proxy *px)
        struct stats_module *mod;
        int stats_module_len = 0;
 
-       if (px->cap & PR_CAP_BE && px->srv && (ctx->flags & STAT_F_ADMIN)) {
+       if (px->cap & PR_CAP_BE && !LIST_ISEMPTY(&px->servers) && (ctx->flags & STAT_F_ADMIN)) {
                /* A form to enable/disable this proxy servers */
 
                /* scope_txt = search pattern + search query, ctx->scope_len is always <= STAT_SCOPE_TXT_MAXLEN */
@@ -1424,7 +1424,7 @@ void stats_dump_html_px_hdr(struct stconn *sc, struct proxy *px)
 
        if (ctx->flags & STAT_F_ADMIN) {
                /* Column heading for Enable or Disable server */
-               if ((px->cap & PR_CAP_BE) && px->srv)
+               if ((px->cap & PR_CAP_BE) && !LIST_ISEMPTY(&px->servers))
                        chunk_appendf(chk,
                                      "<th rowspan=2 width=1><input type=\"checkbox\" "
                                      "onclick=\"for(c in document.getElementsByClassName('%s-checkbox')) "
@@ -1485,7 +1485,7 @@ void stats_dump_html_px_end(struct stconn *sc, struct proxy *px)
 
        chunk_appendf(chk, "</table>");
 
-       if ((px->cap & PR_CAP_BE) && px->srv && (ctx->flags & STAT_F_ADMIN)) {
+       if ((px->cap & PR_CAP_BE) && !LIST_ISEMPTY(&px->servers) && (ctx->flags & STAT_F_ADMIN)) {
                /* close the form used to enable/disable this proxy servers */
                chunk_appendf(chk,
                              "Choose the action to perform on the checked servers : "
index e9337769f250b412851449437195dd1f00fd7cbe..51bd17a331ae201a0a35043c6da6712a6c6c847b 100644 (file)
@@ -1160,7 +1160,7 @@ static void stats_fill_be_computesrv(struct proxy *px, int *nbup, int *nbsrv, in
        const struct server *srv;
 
        nbup_tmp = nbsrv_tmp = totuw_tmp = 0;
-       for (srv = px->srv; srv; srv = srv->next) {
+       list_for_each_entry(srv, &px->servers, el_px) {
                if (srv->cur_state != SRV_ST_STOPPED) {
                        nbup_tmp++;
                        if (srv_currently_usable(srv) &&
@@ -1254,7 +1254,7 @@ int stats_fill_be_line(struct proxy *px, int flags, struct field *line, int len,
                                break;
                        case ST_I_PX_STATUS:
                                fld = chunk_newstr(out);
-                               chunk_appendf(out, "%s", (px->lbprm.tot_weight > 0 || !px->srv) ? "UP" : "DOWN");
+                               chunk_appendf(out, "%s", (px->lbprm.tot_weight > 0 || LIST_ISEMPTY(&px->servers)) ? "UP" : "DOWN");
                                if (px->flags & PR_FL_BE_UNPUBLISHED)
                                        chunk_appendf(out, " (UNPUB)");
                                if (flags & (STAT_F_HIDE_MAINT|STAT_F_HIDE_DOWN))
@@ -1281,7 +1281,7 @@ int stats_fill_be_line(struct proxy *px, int flags, struct field *line, int len,
                                field = mkf_u32(0, px->srv_bck);
                                break;
                        case ST_I_PX_DOWNTIME:
-                               if (px->srv)
+                               if (!LIST_ISEMPTY(&px->servers))
                                        field = mkf_u32(FN_COUNTER, be_downtime(px));
                                break;
                        case ST_I_PX_PID:
@@ -1689,7 +1689,7 @@ void proxy_stats_clear_counters(int clrall, struct list *stat_modules)
                        px->fe_counters.cps_max = 0;
                }
 
-               for (sv = px->srv; sv; sv = sv->next)
+               list_for_each_entry(sv, &px->servers, el_px) {
                        if (clrall)
                                memset(&sv->counters, 0, sizeof(sv->counters));
                        else {
@@ -1701,6 +1701,7 @@ void proxy_stats_clear_counters(int clrall, struct list *stat_modules)
                                sv->counters.dtime_max = 0;
                                sv->counters.ttime_max = 0;
                        }
+               }
 
                list_for_each_entry(li, &px->conf.listeners, by_fe)
                        if (li->counters) {
@@ -1733,7 +1734,7 @@ void proxy_stats_clear_counters(int clrall, struct list *stat_modules)
                        }
 
                        if (mod_cap & STATS_PX_CAP_SRV) {
-                               for (sv = px->srv; sv; sv = sv->next) {
+                               list_for_each_entry(sv, &px->servers, el_px) {
                                        EXTRA_COUNTERS_INIT(sv->extra_counters,
                                                            mod,
                                                            mod->counters,
index cf0d89e00256f943275bdd3a8fab657e17583aa2..f0737df0e2f0f679cb38c8a2148154d1bc8afafa 100644 (file)
@@ -1167,7 +1167,7 @@ int stats_allocate_proxy_counters(struct proxy *px)
                }
        }
 
-       for (sv = px->srv; sv; sv = sv->next) {
+       list_for_each_entry(sv, &px->servers, el_px) {
                if (!stats_allocate_proxy_counters_internal(&sv->extra_counters,
                                                            COUNTERS_SV,
                                                            STATS_PX_CAP_SRV,