From: Amaury Denoyelle Date: Tue, 20 Jan 2026 13:33:46 +0000 (+0100) Subject: MINOR: proxy: simplify defaults proxies list storage X-Git-Tag: v3.4-dev3~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=848e0cd0523cca96be0d872ee7f1a79ce5c73b1c;p=thirdparty%2Fhaproxy.git MINOR: proxy: simplify defaults proxies list storage Defaults proxies instance are stored in a global name tree. When there is a name conflict and the older entry cannot be simply discarded as it is already referenced, the older entry is instead removed from the name tree and inserted into the orphaned list. The purpose of the orphaned list was to guarantee that any remaining unreferenced defaults are purged either on postparsing or deinit. However, this is in fact completely useless. Indeed on postparsing, orphaned entries are always referenced. On deinit instead, defaults are already freed along the cleanup of all frontend/backend instances clean up, thanks to their refcounting. This patch streamlines this by removing orphaned list. Instead, a defaults section is inserted into a new global defaults_list during their whole lifetime. This is not strictly necessary but it ensures that defaults instances can still be accessed easily in the future if needed even if not present in the name tree. On deinit, a BUG_ON() is added to ensure that defaults_list is indeed emptied. Another benefit from this patch is to simplify the defaults deletion procedure. Orphaned simple list is replaced by a proper double linked list implementation, so a single LIST_DELETE() is now performed. This will be notably useful as defaults may be removed at runtime in the future if backends deletion at runtime is implemented. --- diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h index 0b8ce6d38..0ec1fae66 100644 --- a/include/haproxy/proxy-t.h +++ b/include/haproxy/proxy-t.h @@ -316,6 +316,7 @@ struct proxy { unsigned long last_change; /* internal use only: last time the proxy state was changed */ struct list global_list; /* list member for global proxy list */ + struct list el; /* attach point in various list - currently used only on defaults_list for defaults section */ unsigned int maxconn; /* max # of active streams on the frontend */ diff --git a/include/haproxy/proxy.h b/include/haproxy/proxy.h index b0f10c99c..ad6f6905b 100644 --- a/include/haproxy/proxy.h +++ b/include/haproxy/proxy.h @@ -39,6 +39,7 @@ extern struct list proxies; extern struct ceb_root *used_proxy_id; /* list of proxy IDs in use */ extern unsigned int error_snapshot_id; /* global ID assigned to each error then incremented */ extern struct ceb_root *proxy_by_name; /* tree of proxies sorted by name */ +extern struct list defaults_list; /* all defaults proxies list */ extern const struct cfg_opt cfg_opts[]; extern const struct cfg_opt cfg_opts2[]; diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index 0c811a608..ebea79385 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -569,6 +569,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) } if (rc & PR_CAP_DEF) { + LIST_APPEND(&defaults_list, &curproxy->el); /* last and current proxies must be updated to this one */ curr_defproxy = last_defproxy = curproxy; } else { diff --git a/src/haproxy.c b/src/haproxy.c index c012c9d5c..95b2019d9 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2742,8 +2742,10 @@ void deinit(void) * they are respectively cleaned up in sink_deinit() and deinit_log_forward() */ - /* destroy all referenced defaults proxies */ - defaults_px_destroy_all_unref(); + /* All proxies are removed now, so every defaults should also be freed + * when their refcount reached zero. + */ + BUG_ON(!LIST_ISEMPTY(&defaults_list)); userlist_free(userlist); diff --git a/src/proxy.c b/src/proxy.c index a5bd572ad..b3aa60145 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -64,7 +64,7 @@ struct list proxies = LIST_HEAD_INIT(proxies); /* list of all proxies */ struct ceb_root *used_proxy_id = NULL; /* list of proxy IDs in use */ struct ceb_root *proxy_by_name = NULL; /* tree of proxies sorted by name */ struct ceb_root *defproxy_by_name = NULL; /* tree of default proxies sorted by name (dups possible) */ -struct proxy *orphaned_default_proxies = NULL; /* deleted ones with refcount != 0 */ +struct list defaults_list = LIST_HEAD_INIT(defaults_list); /* list of all defaults proxies */ unsigned int error_snapshot_id = 0; /* global ID assigned to each error then incremented */ /* CLI context used during "show servers {state|conn}" */ @@ -1500,6 +1500,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->el); LIST_INIT(&p->acl); LIST_INIT(&p->http_req_rules); LIST_INIT(&p->http_res_rules); @@ -1628,29 +1629,11 @@ static void defaults_px_free(struct proxy *defproxy) */ void defaults_px_destroy(struct proxy *px) { - struct proxy *prev; - BUG_ON(!(px->cap & PR_CAP_DEF)); BUG_ON(px->conf.refcount != 0); cebis_item_delete(&defproxy_by_name, conf.name_node, id, px); - - /* If orphaned defaults list is not empty, it may contain instance. - * In this case it is necessary to manually remove it from the list. - */ - if (orphaned_default_proxies) { - if (orphaned_default_proxies == px) { - orphaned_default_proxies = px->next; - } - else { - for (prev = orphaned_default_proxies; - prev && prev->next != px; prev = prev->next) - ; - if (prev) - prev->next = px->next; - } - px->next = NULL; - } + LIST_DELETE(&px->el); defaults_px_free(px); free(px); @@ -1669,35 +1652,19 @@ void defaults_px_destroy_all_unref(void) if (!px->conf.refcount) defaults_px_destroy(px); } - - px = orphaned_default_proxies; - while (px) { - BUG_ON(!(px->cap & PR_CAP_DEF)); - nx = px->next; - if (!px->conf.refcount) - defaults_px_destroy(px); - px = nx; - } } /* Removes defaults from the name tree. This operation is useful when a * section is made invisible by a newer instance with the same name. If is - * not referenced it is freed immediately, else it is moved in defaults - * orphaned list. + * not referenced it is freed immediately, else it is kept in defaults_list. */ void defaults_px_detach(struct proxy *px) { BUG_ON(!(px->cap & PR_CAP_DEF)); - cebis_item_delete(&defproxy_by_name, conf.name_node, id, px); - if (px->conf.refcount) { - /* still referenced just append it to the orphaned list */ - px->next = orphaned_default_proxies; - orphaned_default_proxies = px; - } - else { + if (!px->conf.refcount) defaults_px_destroy(px); - } + /* If not destroyed, can still be accessed in . */ } /* Add a reference on the default proxy for the proxy Nothing is