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.
This commit is contained in:
Amaury Denoyelle 2026-01-20 14:33:46 +01:00
parent 434e979046
commit 848e0cd052
5 changed files with 13 additions and 41 deletions

View File

@ -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 */

View File

@ -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[];

View File

@ -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 {

View File

@ -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);

View File

@ -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 <px> 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 <px> defaults from the name tree. This operation is useful when a
* section is made invisible by a newer instance with the same name. If <px> 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, <px> can still be accessed in <defaults_list>. */
}
/* Add a reference on the default proxy <defpx> for the proxy <px> Nothing is