From f3127df74d8e78aa26637c2f3cbc5a9dc9e5e56c Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Fri, 13 Feb 2026 11:29:00 +0100 Subject: [PATCH] MINOR: proxy: add refcount to proxies Implement refcount notion into proxy structure. The objective is to be able to increment refcount on proxy to prevent its deletion temporarily. This is similar to the server refcount : "del backend" is not blocked and will remove the targetted instance from the global proxies_list. However, the final free operation is delayed until the refcount is null. As stated above, the API is similar to servers. Proxies are initialized with a refcount of 1. Refcount can be incremented via proxy_take(). When no longer useful, refcount is decremented via proxy_drop() which replaces the older free_proxy(). Deinit is only performed once refcount is null. This commit also defines flag PR_FL_DELETED. It is set when a proxy instance has been removed via a "del backend" CLI command. This should serve as indication to modules which may still have a refcount on the target proxy so that they can release it as soon as possible. Note that this new refcount is completely ignored for a default proxy instance. For them, proxy_take() is pure noop. Free is immediately performed on first proxy_drop() invokation. --- include/haproxy/proxy-t.h | 2 ++ include/haproxy/proxy.h | 3 ++- src/haproxy.c | 2 +- src/hlua.c | 2 +- src/http_client.c | 2 +- src/log.c | 2 +- src/proxy.c | 29 +++++++++++++++++++++++++---- src/resolvers.c | 4 ++-- src/sink.c | 4 ++-- 9 files changed, 37 insertions(+), 13 deletions(-) diff --git a/include/haproxy/proxy-t.h b/include/haproxy/proxy-t.h index fcf9732bd..c233d2909 100644 --- a/include/haproxy/proxy-t.h +++ b/include/haproxy/proxy-t.h @@ -247,6 +247,7 @@ enum PR_SRV_STATE_FILE { #define PR_FL_PAUSED 0x00000020 /* The proxy was paused at run time (reversible) */ #define PR_FL_CHECKED 0x00000040 /* The proxy configuration was fully checked (including postparsing checks) */ #define PR_FL_BE_UNPUBLISHED 0x00000080 /* The proxy cannot be targetted by content switching rules */ +#define PR_FL_DELETED 0x00000100 /* Proxy has been deleted and must be manipulated with care */ struct stream; @@ -508,6 +509,7 @@ struct proxy { struct guid_node guid; /* GUID global tree node */ struct mt_list watcher_list; /* list of elems which currently references this proxy instance (currently only used with backends) */ + uint refcount; /* refcount to keep proxy from being deleted during runtime */ EXTRA_COUNTERS(extra_counters_fe); EXTRA_COUNTERS(extra_counters_be); diff --git a/include/haproxy/proxy.h b/include/haproxy/proxy.h index fa19924f7..5b73b25d4 100644 --- a/include/haproxy/proxy.h +++ b/include/haproxy/proxy.h @@ -58,7 +58,7 @@ void stop_proxy(struct proxy *p); int stream_set_backend(struct stream *s, struct proxy *be); void deinit_proxy(struct proxy *p); -void free_proxy(struct proxy *p); +void proxy_drop(struct proxy *p); const char *proxy_cap_str(int cap); const char *proxy_mode_str(int mode); enum pr_mode str_to_proxy_mode(const char *mode); @@ -82,6 +82,7 @@ void proxy_unref_defaults(struct proxy *px); int setup_new_proxy(struct proxy *px, const char *name, unsigned int cap, char **errmsg); struct proxy *alloc_new_proxy(const char *name, unsigned int cap, char **errmsg); +void proxy_take(struct proxy *px); struct proxy *parse_new_proxy(const char *name, unsigned int cap, const char *file, int linenum, const struct proxy *defproxy); diff --git a/src/haproxy.c b/src/haproxy.c index 49640f6eb..9b13bd2f3 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -2814,7 +2814,7 @@ void deinit(void) while (p) { p0 = p; p = p->next; - free_proxy(p0); + proxy_drop(p0); }/* end while(p) */ /* we don't need to free sink_proxies_list nor cfg_log_forward proxies since diff --git a/src/hlua.c b/src/hlua.c index 52884221a..764c0fb84 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -14726,7 +14726,7 @@ static void hlua_deinit() lua_close(hlua_states[thr]); } - free_proxy(socket_proxy); + proxy_drop(socket_proxy); } REGISTER_POST_DEINIT(hlua_deinit); diff --git a/src/http_client.c b/src/http_client.c index 64fbc8707..762fc0946 100644 --- a/src/http_client.c +++ b/src/http_client.c @@ -1214,7 +1214,7 @@ err: if (err_code & ERR_CODE) { ha_alert("httpclient: cannot initialize: %s\n", errmsg); free(errmsg); - free_proxy(px); + proxy_drop(px); return NULL; } diff --git a/src/log.c b/src/log.c index 9fc0d86aa..f26079b22 100644 --- a/src/log.c +++ b/src/log.c @@ -3739,7 +3739,7 @@ void deinit_log_forward() while (p) { p0 = p; p = p->next; - free_proxy(p0); + proxy_drop(p0); } } diff --git a/src/proxy.c b/src/proxy.c index f9241ea1d..04982b705 100644 --- a/src/proxy.c +++ b/src/proxy.c @@ -456,9 +456,19 @@ void deinit_proxy(struct proxy *p) proxy_unref_defaults(p); } -/* deinit and free

proxy */ -void free_proxy(struct proxy *p) +/* Decrement

refcount and free it if null. For a default proxy instance, + * refcount is ignored and free is immediately performed. + */ +void proxy_drop(struct proxy *p) { + if (!p) + return; + + if (!(p->cap & PR_CAP_DEF)) { + if (HA_ATOMIC_SUB_FETCH(&p->refcount, 1)) + return; + } + deinit_proxy(p); ha_free(&p); } @@ -3211,6 +3221,8 @@ struct proxy *alloc_new_proxy(const char *name, unsigned int cap, char **errmsg) if (!setup_new_proxy(curproxy, name, cap, errmsg)) goto fail; + proxy_take(curproxy); + done: return curproxy; @@ -3225,6 +3237,13 @@ struct proxy *alloc_new_proxy(const char *name, unsigned int cap, char **errmsg) return NULL; } +/* Increment refcount. Does nothing for a default proxy instance. */ +void proxy_take(struct proxy *px) +{ + if (!(px->cap & PR_CAP_DEF)) + HA_ATOMIC_INC(&px->refcount); +} + /* post-check for proxies */ static int proxy_postcheck(struct proxy *px) { @@ -4955,8 +4974,8 @@ static int cli_parse_add_backend(char **args, char *payload, struct appctx *appc return 1; err: - /* free_proxy() ensures any potential refcounting on defpx is decremented. */ - free_proxy(px); + /* This ensures any potential refcounting on defpx is decremented. */ + proxy_drop(px); thread_release(); if (msg) { @@ -5053,6 +5072,8 @@ static int cli_parse_delete_backend(char **args, char *payload, struct appctx *a goto out; } + px->flags |= PR_FL_DELETED; + thread_release(); ha_notice("Backend deleted.\n"); diff --git a/src/resolvers.c b/src/resolvers.c index b8306d33d..5aa2a595f 100644 --- a/src/resolvers.c +++ b/src/resolvers.c @@ -2637,7 +2637,7 @@ static void resolvers_destroy(struct resolvers *resolvers) resolv_free_resolution(res); } - free_proxy(resolvers->px); + proxy_drop(resolvers->px); free(resolvers->id); free((char *)resolvers->conf.file); task_destroy(resolvers->t); @@ -3586,7 +3586,7 @@ out: err_free_conf_file: ha_free((void **)&r->conf.file); err_free_p: - free_proxy(p); + proxy_drop(p); err_free_r: ha_free(&r); return err_code; diff --git a/src/sink.c b/src/sink.c index 03f57abd8..e207db871 100644 --- a/src/sink.c +++ b/src/sink.c @@ -818,7 +818,7 @@ static void sink_free(struct sink *sink) } LIST_DEL_INIT(&sink->sink_list); // remove from parent list task_destroy(sink->forward_task); - free_proxy(sink->forward_px); + proxy_drop(sink->forward_px); ha_free(&sink->name); ha_free(&sink->desc); while (sink->sft) { @@ -866,7 +866,7 @@ static struct sink *sink_new_ringbuf(const char *id, const char *description, return sink; err: - free_proxy(p); + proxy_drop(p); return NULL; }