Use the generic runners to simplify the TUR checker code.
Signed-off-by: Martin Wilck <[email protected]>
---
libmultipath/checkers/tur.c | 363 +++++++++++-------------------------
1 file changed, 112 insertions(+), 251 deletions(-)
diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c
index ba4ca68..0a312c7 100644
--- a/libmultipath/checkers/tur.c
+++ b/libmultipath/checkers/tur.c
@@ -3,7 +3,6 @@
*
* Copyright (c) 2004 Christophe Varoqui
*/
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
@@ -14,16 +13,11 @@
#include <sys/sysmacros.h>
#include <errno.h>
#include <sys/time.h>
-#include <pthread.h>
-#include <urcu.h>
-#include <urcu/uatomic.h>
#include "checkers.h"
-
#include "debug.h"
#include "sg_include.h"
-#include "util.h"
-#include "time-util.h"
+#include "runner.h"
#define TUR_CMD_LEN 6
#define HEAVY_CHECK_COUNT 10
@@ -45,70 +39,47 @@ const char *libcheck_msgtable[] = {
NULL,
};
-struct tur_checker_context {
- dev_t devt;
- int state;
- int running; /* uatomic access only */
+struct tur_data {
int fd;
+ dev_t devt;
unsigned int timeout;
- time_t time;
- pthread_t thread;
- pthread_mutex_t lock;
- pthread_cond_t active;
- int holders; /* uatomic access only */
- int msgid;
- struct checker_context ctx;
- unsigned int nr_timeouts;
- bool checked_state;
+ int state;
+ short msgid;
};
-int libcheck_init (struct checker * c)
+struct tur_checker_context {
+ struct checker_context chkr;
+ int last_runner_state;
+ unsigned int nr_timeouts;
+ struct runner_context *rtx;
+ struct tur_data tdata;
+};
+
+int libcheck_init(struct checker *c)
{
- struct tur_checker_context *ct;
+ struct tur_checker_context *tcc;
struct stat sb;
- ct = malloc(sizeof(struct tur_checker_context));
- if (!ct)
- return 1;
- memset(ct, 0, sizeof(struct tur_checker_context));
-
- ct->state = PATH_UNCHECKED;
- ct->fd = -1;
- uatomic_set(&ct->holders, 1);
- pthread_cond_init_mono(&ct->active);
- pthread_mutex_init(&ct->lock, NULL);
+ tcc = calloc(1, sizeof(*tcc));
+ tcc->tdata.state = PATH_UNCHECKED;
+ tcc->tdata.fd = -1;
if (fstat(c->fd, &sb) == 0)
- ct->devt = sb.st_rdev;
- ct->ctx.cls = c->cls;
- c->context = ct;
-
+ tcc->tdata.devt = sb.st_rdev;
+ tcc->chkr.cls = c->cls;
+ c->context = tcc;
return 0;
}
-static void cleanup_context(struct tur_checker_context *ct)
-{
- pthread_mutex_destroy(&ct->lock);
- pthread_cond_destroy(&ct->active);
- free(ct);
-}
-
void libcheck_free (struct checker * c)
{
- if (c->context) {
- struct tur_checker_context *ct = c->context;
- int holders;
- int running;
+ struct tur_checker_context *tcc = c->context;
- running = uatomic_xchg(&ct->running, 0);
- if (running)
- pthread_cancel(ct->thread);
- ct->thread = 0;
- holders = uatomic_sub_return(&ct->holders, 1);
- if (!holders)
- cleanup_context(ct);
- c->context = NULL;
- }
- return;
+ if (!tcc)
+ return;
+ c->context = NULL;
+ if (tcc->rtx)
+ release_runner(tcc->rtx);
+ free(tcc);
}
static int
@@ -216,19 +187,6 @@ retry:
return PATH_UP;
}
-#define tur_thread_cleanup_push(ct) pthread_cleanup_push(cleanup_func, ct)
-#define tur_thread_cleanup_pop(ct) pthread_cleanup_pop(1)
-
-static void cleanup_func(void *data)
-{
- int holders;
- struct tur_checker_context *ct = data;
-
- holders = uatomic_sub_return(&ct->holders, 1);
- if (!holders)
- cleanup_context(ct);
-}
-
/*
* Test code for "zombie tur thread" handling.
* Compile e.g. with CFLAGS=-DTUR_TEST_MAJOR=8
@@ -249,13 +207,13 @@ static void cleanup_func(void *data)
#define TUR_SLEEP_SECS 60
#endif
-static void tur_deep_sleep(const struct tur_checker_context *ct)
+static void tur_deep_sleep(const struct tur_data *tdata)
{
static int sleep_cnt;
const struct timespec ts = { .tv_sec = TUR_SLEEP_SECS, .tv_nsec = 0 };
int oldstate;
- if (ct->devt != makedev(TUR_TEST_MAJOR, TUR_TEST_MINOR) ||
+ if (tdata->devt != makedev(TUR_TEST_MAJOR, TUR_TEST_MINOR) ||
++sleep_cnt % TUR_SLEEP_INTERVAL != 0)
return;
@@ -273,110 +231,92 @@ static void tur_deep_sleep(const struct
tur_checker_context *ct)
#define tur_deep_sleep(x) do {} while (0)
#endif /* TUR_TEST_MAJOR */
-void *libcheck_thread(struct checker_context *ctx)
+void runner_callback(void *arg)
{
- struct tur_checker_context *ct =
- container_of(ctx, struct tur_checker_context, ctx);
- int state, running;
- short msgid;
+ struct tur_data *tdata = arg;
+ int state;
- /* This thread can be canceled, so setup clean up */
- tur_thread_cleanup_push(ct);
+ condlog(4, "%d:%d : tur checker starting up", major(tdata->devt),
+ minor(tdata->devt));
- condlog(4, "%d:%d : tur checker starting up", major(ct->devt),
- minor(ct->devt));
-
- tur_deep_sleep(ct);
- state = tur_check(ct->fd, ct->timeout, &msgid);
+ tur_deep_sleep(tdata);
+ state = tur_check(tdata->fd, tdata->timeout, &tdata->msgid);
+ tdata->state = state;
pthread_testcancel();
-
- /* TUR checker done */
- pthread_mutex_lock(&ct->lock);
- ct->state = state;
- ct->msgid = msgid;
- pthread_cond_signal(&ct->active);
- pthread_mutex_unlock(&ct->lock);
-
- condlog(4, "%d:%d : tur checker finished, state %s", major(ct->devt),
- minor(ct->devt), checker_state_name(state));
-
- running = uatomic_xchg(&ct->running, 0);
- if (!running)
- pause();
-
- tur_thread_cleanup_pop(ct);
-
- return ((void *)0);
+ condlog(4, "%d:%d : tur checker finished, state %s", major(tdata->devt),
+ minor(tdata->devt), checker_state_name(state));
}
-static void tur_set_async_timeout(struct checker *c)
+static int check_runner_state(struct tur_checker_context *tcc)
{
- struct tur_checker_context *ct = c->context;
- struct timespec now;
+ struct runner_context *rtx = tcc->rtx;
+ int rc;
- get_monotonic_time(&now);
- ct->time = now.tv_sec + c->timeout;
-}
-
-static int tur_check_async_timeout(struct checker *c)
-{
- struct tur_checker_context *ct = c->context;
- struct timespec now;
-
- get_monotonic_time(&now);
- return (now.tv_sec > ct->time);
-}
-
-int check_pending(struct checker *c)
-{
- struct tur_checker_context *ct = c->context;
- int tur_status = PATH_PENDING;
-
- pthread_mutex_lock(&ct->lock);
-
- if (ct->state != PATH_PENDING || ct->msgid != MSG_TUR_RUNNING)
- {
- tur_status = ct->state;
- c->msgid = ct->msgid;
- }
- pthread_mutex_unlock(&ct->lock);
- if (tur_status == PATH_PENDING && c->msgid == MSG_TUR_RUNNING) {
+ rc = check_runner(rtx, &tcc->tdata, sizeof(tcc->tdata));
+ switch (rc) {
+ case RUNNER_DEAD:
+ tcc->tdata.state = PATH_TIMEOUT;
+ tcc->tdata.msgid = MSG_TUR_TIMEOUT;
+ /* fallthrough */
+ case RUNNER_DONE:
+ release_runner(tcc->rtx);
+ tcc->rtx = NULL;
+ tcc->last_runner_state = rc;
+ tcc->nr_timeouts = 0;
+ condlog(rc == RUNNER_DONE ? 4 : 3,
+ "%d:%d : tur checker finished, state %s, runner state
%s",
+ major(tcc->tdata.devt), minor(tcc->tdata.devt),
+ checker_state_name(tcc->tdata.state),
+ runner_state_name(rc));
+ break;
+ case RUNNER_CANCELLED:
+ tcc->last_runner_state = rc;
+ tcc->tdata.state = PATH_TIMEOUT;
+ tcc->tdata.msgid = MSG_TUR_TIMEOUT;
+ if (tcc->nr_timeouts < MAX_NR_TIMEOUTS) {
+ condlog(3, "%d:%d : tur checker timed out, releasing
it",
+ major(tcc->tdata.devt), minor(tcc->tdata.devt));
+ tcc->nr_timeouts++;
+ release_runner(tcc->rtx);
+ tcc->rtx = NULL;
+ } else if (tcc->nr_timeouts == MAX_NR_TIMEOUTS) {
+ tcc->nr_timeouts++;
+ condlog(3, "%d:%d : tur checker timed out, waiting for
it",
+ major(tcc->tdata.devt), minor(tcc->tdata.devt));
+ }
+ break;
+ default:
condlog(4, "%d:%d : tur checker still running",
- major(ct->devt), minor(ct->devt));
- } else {
- int running = uatomic_xchg(&ct->running, 0);
- if (running)
- pthread_cancel(ct->thread);
- ct->thread = 0;
+ major(tcc->tdata.devt), minor(tcc->tdata.devt));
+ tcc->tdata.msgid = MSG_TUR_RUNNING;
+ break;
}
-
- ct->checked_state = true;
- return tur_status;
+ return rc;
}
bool libcheck_need_wait(struct checker *c)
{
struct tur_checker_context *ct = c->context;
- return (ct && ct->thread && uatomic_read(&ct->running) != 0 &&
- !ct->checked_state);
+
+ return ct && ct->rtx;
}
int libcheck_pending(struct checker *c)
{
struct tur_checker_context *ct = c->context;
-
/* The if path checker isn't running, just return the exiting value. */
- if (!ct || !ct->thread)
+ if (!ct || !ct->rtx)
return c->path_state;
- return check_pending(c);
+ /* This may nullify ct->rtx */
+ check_runner_state(ct);
+ c->msgid = ct->tdata.msgid;
+ return ct->tdata.state;
}
int libcheck_check(struct checker * c)
{
struct tur_checker_context *ct = c->context;
- pthread_attr_t attr;
- int tur_status, r;
if (!ct)
return PATH_UNCHECKED;
@@ -384,109 +324,30 @@ int libcheck_check(struct checker * c)
if (checker_is_sync(c))
return tur_check(c->fd, c->timeout, &c->msgid);
- /*
- * Async mode
- */
- if (ct->thread) {
- ct->checked_state = true;
- if (tur_check_async_timeout(c)) {
- int running = uatomic_xchg(&ct->running, 0);
- if (running) {
- pthread_cancel(ct->thread);
- condlog(3, "%d:%d : tur checker timeout",
- major(ct->devt), minor(ct->devt));
- c->msgid = MSG_TUR_TIMEOUT;
- tur_status = PATH_TIMEOUT;
- } else {
- pthread_mutex_lock(&ct->lock);
- tur_status = ct->state;
- c->msgid = ct->msgid;
- pthread_mutex_unlock(&ct->lock);
- }
- ct->thread = 0;
- } else if (uatomic_read(&ct->running) != 0) {
- condlog(3, "%d:%d : tur checker not finished",
- major(ct->devt), minor(ct->devt));
- tur_status = PATH_PENDING;
- c->msgid = MSG_TUR_RUNNING;
- } else {
- /* TUR checker done */
- ct->thread = 0;
- pthread_mutex_lock(&ct->lock);
- tur_status = ct->state;
- c->msgid = ct->msgid;
- pthread_mutex_unlock(&ct->lock);
- }
- } else {
- if (uatomic_read(&ct->holders) > 1) {
- /* The thread has been cancelled but hasn't quit. */
- if (ct->nr_timeouts == MAX_NR_TIMEOUTS) {
- condlog(2, "%d:%d : waiting for stalled tur
thread to finish",
- major(ct->devt), minor(ct->devt));
- ct->nr_timeouts++;
- }
- /*
- * Don't start new threads until the last once has
- * finished.
- */
- if (ct->nr_timeouts > MAX_NR_TIMEOUTS) {
- c->msgid = MSG_TUR_TIMEOUT;
- return PATH_TIMEOUT;
- }
- ct->nr_timeouts++;
- /*
- * Start a new thread while the old one is stalled.
- * We have to prevent it from interfering with the new
- * thread. We create a new context and leave the old
- * one with the stale thread, hoping it will clean up
- * eventually.
- */
- condlog(3, "%d:%d : tur thread not responding",
- major(ct->devt), minor(ct->devt));
-
- /*
- * libcheck_init will replace c->context.
- * It fails only in OOM situations. In this case, return
- * PATH_UNCHECKED to avoid prematurely failing the path.
- */
- if (libcheck_init(c) != 0) {
- c->msgid = MSG_TUR_FAILED;
- return PATH_UNCHECKED;
- }
- ((struct tur_checker_context *)c->context)->nr_timeouts
= ct->nr_timeouts;
-
- if (!uatomic_sub_return(&ct->holders, 1)) {
- /* It did terminate, eventually */
- cleanup_context(ct);
- ((struct tur_checker_context
*)c->context)->nr_timeouts = 0;
- }
-
- ct = c->context;
- } else
- ct->nr_timeouts = 0;
- /* Start new TUR checker */
- pthread_mutex_lock(&ct->lock);
- tur_status = ct->state = PATH_PENDING;
- c->msgid = ct->msgid = MSG_TUR_RUNNING;
- pthread_mutex_unlock(&ct->lock);
- ct->fd = c->fd;
- ct->timeout = c->timeout;
- ct->checked_state = false;
- uatomic_add(&ct->holders, 1);
- uatomic_set(&ct->running, 1);
- tur_set_async_timeout(c);
- setup_thread_attr(&attr, 32 * 1024, 1);
- r = start_checker_thread(&ct->thread, &attr, &ct->ctx);
- pthread_attr_destroy(&attr);
- if (r) {
- uatomic_sub(&ct->holders, 1);
- uatomic_set(&ct->running, 0);
- ct->thread = 0;
- condlog(3, "%d:%d : failed to start tur thread, using"
- " sync mode", major(ct->devt), minor(ct->devt));
- return tur_check(c->fd, c->timeout, &c->msgid);
- }
+ /* Handle the case that the checker just completed */
+ if (ct->rtx) {
+ check_runner_state(ct);
+ c->msgid = ct->tdata.msgid;
+ return ct->tdata.state;
}
- return tur_status;
+ /* create new checker thread */
+ ct->tdata.fd = c->fd;
+ ct->tdata.timeout = c->timeout;
+
+ ct->tdata.state = PATH_PENDING;
+ ct->tdata.msgid = MSG_TUR_RUNNING;
+ condlog(3, "%d:%d : starting checker", major(ct->tdata.devt),
+ minor(ct->tdata.devt));
+ ct->rtx = get_runner(runner_callback, &ct->tdata, sizeof(ct->tdata),
+ 1000000 * c->timeout);
+
+ if (ct->rtx) {
+ c->msgid = ct->tdata.msgid;
+ return ct->tdata.state;
+ } else {
+ condlog(3, "%d:%d : failed to start tur thread, using sync
mode",
+ major(ct->tdata.devt), minor(ct->tdata.devt));
+ return tur_check(c->fd, c->timeout, &c->msgid);
+ }
}
--
2.53.0