From b33a0abc0b4c64118f7b21f314b5783197ade895 Mon Sep 17 00:00:00 2001 From: Amaury Denoyelle Date: Thu, 29 Jul 2021 15:51:45 +0200 Subject: [PATCH] MEDIUM: check: implement check deletion for dynamic servers Implement a mechanism to free a started check on runtime for dynamic servers. A new function check_purge is created for this. The check task will be marked for deletion and scheduled to properly close connection elements and free the task/tasklet/buf_wait elements. This function will be useful to delete a dynamic server wich checks. --- include/haproxy/check-t.h | 1 + include/haproxy/check.h | 1 + src/check.c | 43 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/include/haproxy/check-t.h b/include/haproxy/check-t.h index b78a9f9f5..084b15eda 100644 --- a/include/haproxy/check-t.h +++ b/include/haproxy/check-t.h @@ -53,6 +53,7 @@ enum chk_result { #define CHK_ST_IN_ALLOC 0x0040 /* check blocked waiting for input buffer allocation */ #define CHK_ST_OUT_ALLOC 0x0080 /* check blocked waiting for output buffer allocation */ #define CHK_ST_CLOSE_CONN 0x0100 /* check is waiting that the connection gets closed */ +#define CHK_ST_PURGE 0x0200 /* check must be freed */ /* check status */ enum healthcheck_status { diff --git a/include/haproxy/check.h b/include/haproxy/check.h index 228f9d64d..fdc447150 100644 --- a/include/haproxy/check.h +++ b/include/haproxy/check.h @@ -79,6 +79,7 @@ struct buffer *check_get_buf(struct check *check, struct buffer *bptr); void check_release_buf(struct check *check, struct buffer *bptr); const char *init_check(struct check *check, int type); void free_check(struct check *check); +void check_purge(struct check *check); int init_srv_check(struct server *srv); int init_srv_agent_check(struct server *srv); diff --git a/src/check.c b/src/check.c index c2ab8e830..c40a47ddf 100644 --- a/src/check.c +++ b/src/check.c @@ -1102,7 +1102,10 @@ struct task *process_chk_conn(struct task *t, void *context, unsigned int state) if (check->server) HA_SPIN_LOCK(SERVER_LOCK, &check->server->lock); - if (!(check->state & CHK_ST_INPROGRESS)) { + if (unlikely(check->state & CHK_ST_PURGE)) { + TRACE_STATE("health-check state to purge", CHK_EV_TASK_WAKE, check); + } + else if (!(check->state & (CHK_ST_INPROGRESS))) { /* no check currently running */ if (!expired) /* woke up too early */ { TRACE_STATE("health-check wake up too early", CHK_EV_TASK_WAKE, check); @@ -1139,7 +1142,7 @@ struct task *process_chk_conn(struct task *t, void *context, unsigned int state) * First, let's check whether there was an uncaught error, * which can happen on connect timeout or error. */ - if (check->result == CHK_RES_UNKNOWN) { + if (check->result == CHK_RES_UNKNOWN && likely(!(check->state & CHK_ST_PURGE))) { /* Here the connection must be defined. Otherwise the * error would have already been detected */ @@ -1197,7 +1200,7 @@ struct task *process_chk_conn(struct task *t, void *context, unsigned int state) check->sess = NULL; } - if (check->server) { + if (check->server && likely(!(check->state & CHK_ST_PURGE))) { if (check->result == CHK_RES_FAILED) { /* a failure or timeout detected */ TRACE_DEVEL("report failure", CHK_EV_TASK_WAKE|CHK_EV_HCHK_END|CHK_EV_HCHK_ERR, check); @@ -1236,6 +1239,23 @@ struct task *process_chk_conn(struct task *t, void *context, unsigned int state) HA_SPIN_UNLOCK(SERVER_LOCK, &check->server->lock); TRACE_LEAVE(CHK_EV_TASK_WAKE, check); + + /* Free the check if set to PURGE. After this, the check instance may be + * freed via the free_server invocation, so it must not be accessed + * after this point. + */ + if (unlikely(check->state & CHK_ST_PURGE)) { + /* buf_wait */ + LIST_DELETE(&check->buf_wait.list); + /* tasklet */ + pool_free(pool_head_tasklet, check->wait_list.tasklet); + /* task */ + task_destroy(check->task); + t = NULL; + + free_server(check->server); + } + return t; } @@ -1314,6 +1334,11 @@ const char *init_check(struct check *check, int type) return NULL; } +/* Liberates the resources allocated for a check. + * + * This function must only be used at startup when it is known that the check + * has never been executed. + */ void free_check(struct check *check) { task_destroy(check->task); @@ -1329,6 +1354,18 @@ void free_check(struct check *check) } } +/* This function must be used in order to free a started check. The check will + * be scheduled for a next execution in order to properly close and free all + * check elements. + * + * Non thread-safe. + */ +void check_purge(struct check *check) +{ + check->state = CHK_ST_PURGE; + task_wakeup(check->task, TASK_WOKEN_OTHER); +} + /* manages a server health-check. Returns the time the task accepts to wait, or * TIME_ETERNITY for infinity. */