diff mbox series

[03/15] libmultipath: split out the code to wait for pending checkers

Message ID 20240828221757.4060548-4-bmarzins@redhat.com (mailing list archive)
State Not Applicable, archived
Delegated to: Benjamin Marzinski
Headers show
Series Yet Another path checker refactor | expand

Commit Message

Benjamin Marzinski Aug. 28, 2024, 10:17 p.m. UTC
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(-)

Comments

Martin Wilck Sept. 4, 2024, 3:01 p.m. UTC | #1
On Wed, 2024-08-28 at 18:17 -0400, Benjamin Marzinski wrote:
> 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(-)
> 
> diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c
> index c4918d28..298aec78 100644
> --- a/libmultipath/checkers.c
> +++ b/libmultipath/checkers.c
> @@ -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();
>  
> diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h
> index fb1160af..b2342a1b 100644
> --- a/libmultipath/checkers.h
> +++ b/libmultipath/checkers.h
> @@ -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);
>  
>  
>  /*
> diff --git a/libmultipath/checkers/directio.c
> b/libmultipath/checkers/directio.c
> index 8e87878b..3f88b40d 100644
> --- a/libmultipath/checkers/directio.c
> +++ b/libmultipath/checkers/directio.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;
>  }
> diff --git a/libmultipath/checkers/tur.c
> b/libmultipath/checkers/tur.c
> index a2905af5..95af5214 100644
> --- a/libmultipath/checkers/tur.c
> +++ b/libmultipath/checkers/tur.c
> @@ -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);

Does this work? 

https://pubs.opengroup.org/onlinepubs/009695299/functions/pthread_cond_timedwait.html
says that "an error is returned [...] if the absolute time specified by
abstime has already been passed at the time of the call".

Which would always be the case if you pass a timestamp that you 
fetched previously unmodified, meaning that you'd always get ETIMEDOUT.
Or am I misreading the docs?

Martin







> +}
> +
>  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;
Benjamin Marzinski Sept. 4, 2024, 6:16 p.m. UTC | #2
On Wed, Sep 04, 2024 at 05:01:39PM +0200, Martin Wilck wrote:
> On Wed, 2024-08-28 at 18:17 -0400, Benjamin Marzinski wrote:

<snip>

> > diff --git a/libmultipath/checkers/tur.c
> > b/libmultipath/checkers/tur.c
> > index a2905af5..95af5214 100644
> > --- a/libmultipath/checkers/tur.c
> > +++ b/libmultipath/checkers/tur.c
> > @@ -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);
> 
> Does this work? 
> 
> https://pubs.opengroup.org/onlinepubs/009695299/functions/pthread_cond_timedwait.html
> says that "an error is returned [...] if the absolute time specified by
> abstime has already been passed at the time of the call".
> 
> Which would always be the case if you pass a timestamp that you 
> fetched previously unmodified, meaning that you'd always get ETIMEDOUT.
> Or am I misreading the docs?

No. You are reading the docs right.  I'm just don't understand why
that's a problem.  When you start the checker, you set a timeout that
you want to wait for, to give the checker a chance to complete in this
checker loop. Once that timeout has passed, if the thread is still
running, then libcheck_pending should return PATH_PENDING.
pthread_cond_timedwait() doesn't get called unless the thread is
running. So I would argue that pthread_cond_timedwait() always failing
once that timeout has passed is the correct thing to do.

Or are you arguing for having check_pending() manually check if the
timeout has passed, and skipping the call to pthread_cond_timedwait() in
that case? 

With the directio checker, we need to call get_events() even once our
timeout has passed, since we need to handle any async IOs that have
completed since the last call to check_pending(). But in there's no need
to call pthread_cond_timedwait() for the tur checker, so we could skip
it if we saw that the timeout had already passed. I would argue that
this is just duplicating the check from pthread_cond_timedwait() in
check_pending() for no real benfit though.

Or am I missing something here?

-Ben

> Martin
> 
> 
> 
> 
> 
> 
> 
> > +}
> > +
> >  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;
Martin Wilck Sept. 4, 2024, 7:48 p.m. UTC | #3
On Wed, 2024-09-04 at 14:16 -0400, Benjamin Marzinski wrote:
> On Wed, Sep 04, 2024 at 05:01:39PM +0200, Martin Wilck wrote:
> > On Wed, 2024-08-28 at 18:17 -0400, Benjamin Marzinski wrote:
> 
> <snip>
> 
> > > diff --git a/libmultipath/checkers/tur.c
> > > b/libmultipath/checkers/tur.c
> > > index a2905af5..95af5214 100644
> > > --- a/libmultipath/checkers/tur.c
> > > +++ b/libmultipath/checkers/tur.c
> > > @@ -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);
> > 
> > Does this work? 
> > 
> > https://pubs.opengroup.org/onlinepubs/009695299/functions/pthread_cond_timedwait.html
> > says that "an error is returned [...] if the absolute time
> > specified by
> > abstime has already been passed at the time of the call".
> > 
> > Which would always be the case if you pass a timestamp that you 
> > fetched previously unmodified, meaning that you'd always get
> > ETIMEDOUT.
> > Or am I misreading the docs?
> 
> No. You are reading the docs right.  I'm just don't understand why
> that's a problem.  When you start the checker, you set a timeout that
> you want to wait for, to give the checker a chance to complete in
> this
> checker loop. 
> 
> Once that timeout has passed, if the thread is still
> running, then libcheck_pending should return PATH_PENDING.
> pthread_cond_timedwait() doesn't get called unless the thread is
> running. So I would argue that pthread_cond_timedwait() always
> failing
> once that timeout has passed is the correct thing to do.
> 
> Or are you arguing for having check_pending() manually check if the
> timeout has passed, and skipping the call to pthread_cond_timedwait()
> in
> that case? 

I was confused by the missing patch 04/15. So let me read that one and
come back to you.

Martin
diff mbox series

Patch

diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c
index c4918d28..298aec78 100644
--- a/libmultipath/checkers.c
+++ b/libmultipath/checkers.c
@@ -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();
 
diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h
index fb1160af..b2342a1b 100644
--- a/libmultipath/checkers.h
+++ b/libmultipath/checkers.h
@@ -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);
 
 
 /*
diff --git a/libmultipath/checkers/directio.c b/libmultipath/checkers/directio.c
index 8e87878b..3f88b40d 100644
--- a/libmultipath/checkers/directio.c
+++ b/libmultipath/checkers/directio.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;
 }
diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c
index a2905af5..95af5214 100644
--- a/libmultipath/checkers/tur.c
+++ b/libmultipath/checkers/tur.c
@@ -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;