@@ -26,6 +26,7 @@ struct checker_class {
void (*free)(struct checker *); /* to free the context */
void (*reset)(void); /* to reset the global variables */
void *(*thread)(void *); /* async thread entry point */
+ int (*pending)(struct checker *); /* to recheck pending paths */
const char **msgtable;
short msgtable_size;
};
@@ -180,7 +181,8 @@ static struct checker_class *add_checker_class(const char *name)
c->mp_init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_mp_init");
c->reset = (void (*)(void)) dlsym(c->handle, "libcheck_reset");
c->thread = (void *(*)(void*)) dlsym(c->handle, "libcheck_thread");
- /* These 3 functions can be NULL. call dlerror() to clear out any
+ c->pending = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_pending");
+ /* These 4 functions can be NULL. call dlerror() to clear out any
* error string */
dlerror();
@@ -190,6 +190,7 @@ void libcheck_free(struct checker *);
void *libcheck_thread(struct checker_context *ctx);
void libcheck_reset(void);
int libcheck_mp_init(struct checker *);
+int libcheck_pending(struct checker *c);
/*
@@ -288,14 +288,36 @@ get_events(struct aio_group *aio_grp, struct timespec *timeout)
return got_events;
}
+static void
+check_pending(struct directio_context *ct, struct timespec endtime)
+{
+ int r;
+ struct timespec currtime, timeout;
+
+ while(1) {
+ get_monotonic_time(&currtime);
+ timespecsub(&endtime, &currtime, &timeout);
+ if (timeout.tv_sec < 0)
+ timeout.tv_sec = timeout.tv_nsec = 0;
+
+ r = get_events(ct->aio_grp, &timeout);
+
+ if (ct->req->state != PATH_PENDING) {
+ ct->running = 0;
+ return;
+ } else if (r == 0 ||
+ (timeout.tv_sec == 0 && timeout.tv_nsec == 0))
+ return;
+ }
+}
+
static int
check_state(int fd, struct directio_context *ct, int sync, int timeout_secs)
{
struct timespec timeout = { .tv_nsec = 1000 };
struct stat sb;
int rc;
- long r;
- struct timespec currtime, endtime;
+ struct timespec endtime;
if (fstat(fd, &sb) == 0) {
LOG(4, "called for %x", (unsigned) sb.st_rdev);
@@ -330,21 +352,11 @@ check_state(int fd, struct directio_context *ct, int sync, int timeout_secs)
endtime.tv_sec += timeout.tv_sec;
endtime.tv_nsec += timeout.tv_nsec;
normalize_timespec(&endtime);
- while(1) {
- r = get_events(ct->aio_grp, &timeout);
- if (ct->req->state != PATH_PENDING) {
- ct->running = 0;
- return ct->req->state;
- } else if (r == 0 ||
- (timeout.tv_sec == 0 && timeout.tv_nsec == 0))
- break;
+ check_pending(ct, endtime);
+ if (ct->req->state != PATH_PENDING)
+ return ct->req->state;
- get_monotonic_time(&currtime);
- timespecsub(&endtime, &currtime, &timeout);
- if (timeout.tv_sec < 0)
- timeout.tv_sec = timeout.tv_nsec = 0;
- }
if (ct->running > timeout_secs || sync) {
struct io_event event;
@@ -360,17 +372,9 @@ check_state(int fd, struct directio_context *ct, int sync, int timeout_secs)
return rc;
}
-int libcheck_check (struct checker * c)
+static void set_msgid(struct checker *c, int state)
{
- int ret;
- struct directio_context * ct = (struct directio_context *)c->context;
-
- if (!ct)
- return PATH_UNCHECKED;
-
- ret = check_state(c->fd, ct, checker_is_sync(c), c->timeout);
-
- switch (ret)
+ switch (state)
{
case PATH_UNCHECKED:
c->msgid = MSG_DIRECTIO_UNKNOWN;
@@ -387,5 +391,37 @@ int libcheck_check (struct checker * c)
default:
break;
}
+}
+
+int libcheck_pending(struct checker *c)
+{
+ struct timespec endtime;
+ struct directio_context *ct = (struct directio_context *)c->context;
+
+ /* The if path checker isn't running, just return the exiting value. */
+ if (!ct || !ct->running)
+ return c->path_state;
+
+ if (ct->req->state == PATH_PENDING) {
+ get_monotonic_time(&endtime);
+ check_pending(ct, endtime);
+ } else
+ ct->running = 0;
+ set_msgid(c, ct->req->state);
+
+ return ct->req->state;
+}
+
+int libcheck_check (struct checker * c)
+{
+ int ret;
+ struct directio_context * ct = (struct directio_context *)c->context;
+
+ if (!ct)
+ return PATH_UNCHECKED;
+
+ ret = check_state(c->fd, ct, checker_is_sync(c), c->timeout);
+ set_msgid(c, ret);
+
return ret;
}
@@ -323,6 +323,49 @@ static int tur_check_async_timeout(struct checker *c)
return (now.tv_sec > ct->time);
}
+int check_pending(struct checker *c, struct timespec endtime)
+{
+ struct tur_checker_context *ct = c->context;
+ int r, tur_status = PATH_PENDING;
+
+ pthread_mutex_lock(&ct->lock);
+
+ for (r = 0;
+ r == 0 && ct->state == PATH_PENDING &&
+ ct->msgid == MSG_TUR_RUNNING;
+ r = pthread_cond_timedwait(&ct->active, &ct->lock, &endtime));
+
+ if (!r) {
+ tur_status = ct->state;
+ c->msgid = ct->msgid;
+ }
+ pthread_mutex_unlock(&ct->lock);
+ if (tur_status == PATH_PENDING && c->msgid == MSG_TUR_RUNNING) {
+ 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;
+ }
+
+ return tur_status;
+}
+
+int libcheck_pending(struct checker *c)
+{
+ struct timespec endtime;
+ struct tur_checker_context *ct = c->context;
+
+ /* The if path checker isn't running, just return the exiting value. */
+ if (!ct || !ct->thread)
+ return c->path_state;
+
+ get_monotonic_time(&endtime);
+ return check_pending(c, endtime);
+}
+
int libcheck_check(struct checker * c)
{
struct tur_checker_context *ct = c->context;
@@ -437,27 +480,7 @@ int libcheck_check(struct checker * c)
return tur_check(c->fd, c->timeout, &c->msgid);
}
tur_timeout(&tsp);
- pthread_mutex_lock(&ct->lock);
-
- for (r = 0;
- r == 0 && ct->state == PATH_PENDING &&
- ct->msgid == MSG_TUR_RUNNING;
- r = pthread_cond_timedwait(&ct->active, &ct->lock, &tsp));
-
- if (!r) {
- tur_status = ct->state;
- c->msgid = ct->msgid;
- }
- pthread_mutex_unlock(&ct->lock);
- if (tur_status == PATH_PENDING && c->msgid == MSG_TUR_RUNNING) {
- 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;
- }
+ tur_status = check_pending(c, tsp);
}
return tur_status;
This patch adds a new optional symbol for the dynamic path checker libraries, libcheck_pending. This is currently unused, but can be called on pending checkers to check if they've completed and return their value. The "tur" and "directio" checkers are the only ones which can return PATH_PENDING. They now implement libcheck_pending() as a wrapper around the code that libcheck_check uses to wait for pending paths, which has been broken out into its own function. Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com> --- libmultipath/checkers.c | 4 +- libmultipath/checkers.h | 1 + libmultipath/checkers/directio.c | 86 ++++++++++++++++++++++---------- libmultipath/checkers/tur.c | 65 ++++++++++++++++-------- 4 files changed, 109 insertions(+), 47 deletions(-)