Use the generic runners to simplify the TUR checker code.

Signed-off-by: Martin Wilck <[email protected]>
Reviewed-by: Benjamin Marzinski <[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


Reply via email to