All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] Fix RCU hang
@ 2018-03-23 20:00 Benjamin Marzinski
  2018-03-23 20:00 ` [PATCH 1/2] multipathd: register threads that use rcu calls Benjamin Marzinski
  2018-03-23 20:00 ` [PATCH 2/2] multipath: fix rcu thread cancellation hang Benjamin Marzinski
  0 siblings, 2 replies; 5+ messages in thread
From: Benjamin Marzinski @ 2018-03-23 20:00 UTC (permalink / raw)
  To: device-mapper development; +Cc: Martin Wilck

I have run into a RCU related hang with multipathd, when I reconfigured
and then shutdown multipathd in quick succession.  I haven't been able
to recreate it without manually adding a sleep() call to the code, but I
have figured out what could have caused it.

While the rcu code is waiting for a grace period to elapse, no threads
can register or unregister as rcu reader threads. If for some reason, a
thread never calls put_multipath_config() to exit a read side critical
section, then any threads trying to start or stop will hang. This can
happen if a thread is cancelled between calls to get_multipath_config()
and put_multipath_config(), and multipathd is reconfigured (which causes
the rcu code to wait for a grace period).

The only threads that get cancelled before the end of multipathd (at
least in the setup I was running) are the waiter threads and the tur
checker threads. The tur checker threads never hit a cancellation point
while in a read side critcal section (although they do have read side
critical sections due to using condlog, so they need to register with
the rcu code). The waiter threads can do this, but only while holding
the vecs lock, which should protect them from being cancelled.

The other threads occasionally hit cancellation points inside read side
critical sections while not protected by the vecs lock.  What appears to
have happened is that one of the threads was taking a long time in a
read side critical section starting before the reconfigure call to
call_rcu().  It remained in that section until the reconfigure ended,
and multipathd was shutdown, cancelling the thread.  I can easily
reproduce this by adding a sleep() into one of these sections.

The second patch may be overkill. In it, I fix all the multipathd code
to either not have a cancellation point in a read side critical section,
or to use a pthread_cleanup_handler. I could just fix the code that is
called without holding the vecs lock, since you can guarantee that the
waiter threads will never be cancelled while holding the vecs lock, and
that main threads can't hold a vecs lock while reconfigure is running,
and reconfigure will never run after the main threads have been
cancelled.  I did this for three reasons.

1. Without digging more into the rcu code, I couldn't be sure that the
reader thread simply needed to be in a quiescent state after call_rcu()
to count for the grace period. Since call_rcu() uses another thread to
wait for the grace period, it seemed possible that this rcu thread would
start checking threads for being in a queiescent state some time after
call_rcu() happened.  If this is the case, then holding the vecs lock
would protect a thread from having the rcu code start to wait for a
grace period.  This could probably be overcome be forcing the main
threads to be cancelled while holding the vecs lock, so that they 
could simply never be cancelled in these sections.

2. It seems sloppy to have threads exit without correctly finishing
their read side critical sections, even if we know that the rcu won't be
waiting for a grace period again before multipathd is shut down.

3. This would be adding one more surprising thing for the vecs lock to
be be in charge of. 

Benjamin Marzinski (2):
  multipathd: register threads that use rcu calls
  multipath: fix rcu thread cancellation hang

 libmultipath/checkers/rbd.c |  7 +++-
 libmultipath/checkers/tur.c |  9 +++--
 libmultipath/config.h       |  2 +-
 libmultipath/configure.c    | 90 +++++++++++++++++++++++++++------------------
 libmultipath/devmapper.c    | 10 +++--
 libmultipath/discovery.c    | 13 +++++--
 libmultipath/parser.c       |  3 +-
 libmultipath/structs_vec.c  |  9 +++--
 libmultipath/uevent.c       | 15 +++++---
 libmultipath/wwids.c        | 18 +++++----
 mpathpersist/main.c         |  2 +-
 multipath/main.c            |  2 +-
 multipathd/cli_handlers.c   | 86 +++++++++++++++++++++++++++----------------
 multipathd/main.c           | 64 +++++++++++++++++++++-----------
 tests/globals.c             |  2 +-
 15 files changed, 209 insertions(+), 123 deletions(-)

-- 
2.7.4

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [PATCH 1/2] multipathd: register threads that use rcu calls
  2018-03-23 20:00 [PATCH 0/2] Fix RCU hang Benjamin Marzinski
@ 2018-03-23 20:00 ` Benjamin Marzinski
  2018-03-23 20:56   ` Martin Wilck
  2018-03-23 20:00 ` [PATCH 2/2] multipath: fix rcu thread cancellation hang Benjamin Marzinski
  1 sibling, 1 reply; 5+ messages in thread
From: Benjamin Marzinski @ 2018-03-23 20:00 UTC (permalink / raw)
  To: device-mapper development; +Cc: Martin Wilck

All calls to condlog() are rcu reader side calls, so any thread that
uses condlog() must register itself. The only threads that are exempt
are log_thread, since it never calls condlog (or any other function that
calls get_multipath_config) and mpath_pr_event_handler_fn, which is only
called by mpath_persist, which disables the rcu handling.

Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/checkers/rbd.c | 7 +++++--
 libmultipath/checkers/tur.c | 9 ++++++---
 multipathd/main.c           | 7 +++++--
 3 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/libmultipath/checkers/rbd.c b/libmultipath/checkers/rbd.c
index b1d99b4..4ff54f4 100644
--- a/libmultipath/checkers/rbd.c
+++ b/libmultipath/checkers/rbd.c
@@ -19,6 +19,7 @@
 #include <sys/ioctl.h>
 #include <sys/time.h>
 #include <sys/wait.h>
+#include <urcu.h>
 
 #include "rados/librados.h"
 
@@ -517,6 +518,7 @@ static void cleanup_func(void *data)
 	pthread_spin_unlock(&ct->hldr_lock);
 	if (!holders)
 		cleanup_context(ct);
+	rcu_unregister_thread();
 }
 
 static void *rbd_thread(void *ctx)
@@ -524,11 +526,12 @@ static void *rbd_thread(void *ctx)
 	struct rbd_checker_context *ct = ctx;
 	int state;
 
+	/* This thread can be canceled, so setup clean up */
+	rbd_thread_cleanup_push(ct)
+	rcu_register_thread();
 	condlog(3, "rbd%d: thread starting up", ct->rbd_bus_id);
 
 	ct->message[0] = '\0';
-	/* This thread can be canceled, so setup clean up */
-	rbd_thread_cleanup_push(ct)
 
 	/* checker start up */
 	pthread_mutex_lock(&ct->lock);
diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c
index 9155960..eb3348d 100644
--- a/libmultipath/checkers/tur.c
+++ b/libmultipath/checkers/tur.c
@@ -15,6 +15,7 @@
 #include <errno.h>
 #include <sys/time.h>
 #include <pthread.h>
+#include <urcu.h>
 #include <urcu/uatomic.h>
 
 #include "checkers.h"
@@ -220,6 +221,7 @@ static void cleanup_func(void *data)
 	holders = uatomic_sub_return(&ct->holders, 1);
 	if (!holders)
 		cleanup_context(ct);
+	rcu_unregister_thread();
 }
 
 static void copy_msg_to_tcc(void *ct_p, const char *msg)
@@ -237,11 +239,12 @@ static void *tur_thread(void *ctx)
 	int state, running;
 	char devt[32];
 
-	condlog(3, "%s: tur checker starting up",
-		tur_devt(devt, sizeof(devt), ct));
-
 	/* This thread can be canceled, so setup clean up */
 	tur_thread_cleanup_push(ct);
+	rcu_register_thread();
+
+	condlog(3, "%s: tur checker starting up",
+		tur_devt(devt, sizeof(devt), ct));
 
 	/* TUR checker start up */
 	pthread_mutex_lock(&ct->lock);
diff --git a/multipathd/main.c b/multipathd/main.c
index 3ae0442..eccb046 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3041,12 +3041,13 @@ void *  mpath_pr_event_handler_fn (void * pathp )
 	struct prout_param_descriptor *param;
 	struct prin_resp *resp;
 
+	rcu_register_thread();
 	mpp = pp->mpp;
 
 	resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
 	if (!resp){
 		condlog(0,"%s Alloc failed for prin response", pp->dev);
-		return NULL;
+		goto out;
 	}
 
 	ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, 0);
@@ -3104,7 +3105,9 @@ void *  mpath_pr_event_handler_fn (void * pathp )
 
 	free(param);
 out:
-	free(resp);
+	if (resp)
+		free(resp);
+	rcu_unregister_thread();
 	return NULL;
 }
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [PATCH 2/2] multipath: fix rcu thread cancellation hang
  2018-03-23 20:00 [PATCH 0/2] Fix RCU hang Benjamin Marzinski
  2018-03-23 20:00 ` [PATCH 1/2] multipathd: register threads that use rcu calls Benjamin Marzinski
@ 2018-03-23 20:00 ` Benjamin Marzinski
  2018-03-23 21:09   ` Martin Wilck
  1 sibling, 1 reply; 5+ messages in thread
From: Benjamin Marzinski @ 2018-03-23 20:00 UTC (permalink / raw)
  To: device-mapper development; +Cc: Martin Wilck

While the rcu code is waiting for a grace period to elapse, no threads
can register or unregister as rcu reader threads. If for some reason, a
thread never calls put_multipath_config() to exit a read side critical
section, then any threads trying to start or stop will hang. This can
happen if a thread is cancelled between calls to get_multipath_config()
and put_multipath_config(), and multipathd is reconfigured (which causes
the rcu code to wait for a grace period).

This patch fixes this issue in two ways. Where possible, it reorders the
code or saves config values into local variables to remove cancellation
points between calls to get_multipath_config() and
put_multipath_config().  In cases where this isn't possible (or where it
would cause a significant amount of extra work to be done) multipath now
pushes a cleanup handler to call put_multipath_config().

The only functions that were not modified were ones that were only
called by multipath or mpathpersist, since these are single threaded
and already disable rcu thread registration.

Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/config.h      |  2 +-
 libmultipath/configure.c   | 90 +++++++++++++++++++++++++++-------------------
 libmultipath/devmapper.c   | 10 ++++--
 libmultipath/discovery.c   | 13 ++++---
 libmultipath/parser.c      |  3 +-
 libmultipath/structs_vec.c |  9 +++--
 libmultipath/uevent.c      | 15 ++++----
 libmultipath/wwids.c       | 18 +++++-----
 mpathpersist/main.c        |  2 +-
 multipath/main.c           |  2 +-
 multipathd/cli_handlers.c  | 86 ++++++++++++++++++++++++++++----------------
 multipathd/main.c          | 57 ++++++++++++++++++-----------
 tests/globals.c            |  2 +-
 13 files changed, 193 insertions(+), 116 deletions(-)

diff --git a/libmultipath/config.h b/libmultipath/config.h
index a20ac2a..329f3ed 100644
--- a/libmultipath/config.h
+++ b/libmultipath/config.h
@@ -232,6 +232,6 @@ struct config *load_config (char * file);
 struct config * alloc_config (void);
 void free_config (struct config * conf);
 extern struct config *get_multipath_config(void);
-extern void put_multipath_config(struct config *);
+extern void put_multipath_config(void *);
 
 #endif
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index fa6e21c..61f68f8 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -288,6 +288,8 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
 	 * propsel.c if in doubt.
 	 */
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
+
 	select_pgfailback(conf, mpp);
 	select_pgpolicy(conf, mpp);
 	select_selector(conf, mpp);
@@ -316,7 +318,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
 	select_flush_on_last_del(conf, mpp);
 
 	sysfs_set_scsi_tmo(mpp, conf->checkint);
-	put_multipath_config(conf);
+	pthread_cleanup_pop(1);
 
 	if (mpp->marginal_path_double_failed_time > 0 &&
 	    mpp->marginal_path_err_sample_time > 0 &&
@@ -742,14 +744,16 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
 {
 	int r = DOMAP_FAIL;
 	struct config *conf;
+	int verbosity;
 
 	/*
 	 * last chance to quit before touching the devmaps
 	 */
 	if (mpp->action == ACT_DRY_RUN) {
 		conf = get_multipath_config();
-		print_multipath_topology(mpp, conf->verbosity);
+		verbosity = conf->verbosity;
 		put_multipath_config(conf);
+		print_multipath_topology(mpp, verbosity);
 		return DOMAP_DRY;
 	}
 
@@ -807,16 +811,18 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
 
 	case ACT_RENAME:
 		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		r = dm_rename(mpp->alias_old, mpp->alias,
 			      conf->partition_delim, mpp->skip_kpartx);
-		put_multipath_config(conf);
+		pthread_cleanup_pop(1);
 		break;
 
 	case ACT_FORCERENAME:
 		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		r = dm_rename(mpp->alias_old, mpp->alias,
 			      conf->partition_delim, mpp->skip_kpartx);
-		put_multipath_config(conf);
+		pthread_cleanup_pop(1);
 		if (r) {
 			sysfs_set_max_sectors_kb(mpp, 1);
 			if (mpp->ghost_delay_tick > 0 &&
@@ -952,17 +958,19 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
 		}
 	}
 	vector_foreach_slot (pathvec, pp1, k) {
+		int invalid = 0;
 		/* skip this path for some reason */
 
 		/* 1. if path has no unique id or wwid blacklisted */
 		conf = get_multipath_config();
-		if (strlen(pp1->wwid) == 0 ||
-		    filter_path(conf, pp1) > 0) {
-			put_multipath_config(conf);
+		pthread_cleanup_push(put_multipath_config, conf);
+		if (strlen(pp1->wwid) == 0 || filter_path(conf, pp1) > 0)
+			invalid = 1;
+		pthread_cleanup_pop(1);
+		if (invalid) {
 			orphan_path(pp1, "wwid blacklisted");
 			continue;
 		}
-		put_multipath_config(conf);
 
 		/* 2. if path already coalesced */
 		if (pp1->mpp)
@@ -1082,9 +1090,12 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
 		}
 
 		if (!is_daemon && mpp->action != ACT_NOTHING) {
+			int verbosity;
+
 			conf = get_multipath_config();
-			print_multipath_topology(mpp, conf->verbosity);
+			verbosity = conf->verbosity;
 			put_multipath_config(conf);
+			print_multipath_topology(mpp, verbosity);
 		}
 
 		if (newmp) {
@@ -1174,6 +1185,7 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
 	char * refwwid = NULL, tmpwwid[WWID_SIZE];
 	int flags = DI_SYSFS | DI_WWID;
 	struct config *conf;
+	int invalid = 0;
 
 	if (!wwid)
 		return 1;
@@ -1201,9 +1213,10 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
 				return 1;
 
 			conf = get_multipath_config();
+			pthread_cleanup_push(put_multipath_config, conf);
 			ret = store_pathinfo(pathvec, conf, udevice,
 					     flags, &pp);
-			put_multipath_config(conf);
+			pthread_cleanup_pop(1);
 			udev_device_unref(udevice);
 			if (!pp) {
 				if (ret == 1)
@@ -1213,12 +1226,13 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
 			}
 		}
 		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		if (pp->udev && pp->uid_attribute &&
-		    filter_property(conf, pp->udev) > 0) {
-			put_multipath_config(conf);
+		    filter_property(conf, pp->udev) > 0)
+			invalid = 1;
+		pthread_cleanup_pop(1);
+		if (invalid)
 			return 2;
-		}
-		put_multipath_config(conf);
 
 		refwwid = pp->wwid;
 		goto out;
@@ -1239,9 +1253,10 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
 				return 1;
 
 			conf = get_multipath_config();
+			pthread_cleanup_push(put_multipath_config, conf);
 			ret = store_pathinfo(pathvec, conf, udevice,
 					     flags, &pp);
-			put_multipath_config(conf);
+			pthread_cleanup_pop(1);
 			udev_device_unref(udevice);
 			if (!pp) {
 				if (ret == 1)
@@ -1251,12 +1266,13 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
 			}
 		}
 		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		if (pp->udev && pp->uid_attribute &&
-		    filter_property(conf, pp->udev) > 0) {
-			put_multipath_config(conf);
+		    filter_property(conf, pp->udev) > 0)
+			invalid = 1;
+		pthread_cleanup_pop(1);
+		if (invalid)
 			return 2;
-		}
-		put_multipath_config(conf);
 		refwwid = pp->wwid;
 		goto out;
 	}
@@ -1268,22 +1284,24 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
 			return 1;
 
 		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		ret = store_pathinfo(pathvec, conf, udevice,
 				     flags, &pp);
+		pthread_cleanup_pop(1);
 		udev_device_unref(udevice);
 		if (!pp) {
 			if (ret == 1)
-				condlog(0, "%s: can't store path info",
-					dev);
-			put_multipath_config(conf);
+				condlog(0, "%s: can't store path info", dev);
 			return ret;
 		}
+		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		if (pp->udev && pp->uid_attribute &&
-		    filter_property(conf, pp->udev) > 0) {
-			put_multipath_config(conf);
+		    filter_property(conf, pp->udev) > 0)
+			invalid = 1;
+		pthread_cleanup_pop(1);
+		if (invalid)
 			return 2;
-		}
-		put_multipath_config(conf);
 		refwwid = pp->wwid;
 		goto out;
 	}
@@ -1291,6 +1309,7 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
 	if (dev_type == DEV_DEVMAP) {
 
 		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) {
 			refwwid = tmpwwid;
 			goto check;
@@ -1302,7 +1321,6 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
 		if (get_user_friendly_wwid(dev, tmpwwid,
 					   conf->bindings_file) == 0) {
 			refwwid = tmpwwid;
-			put_multipath_config(conf);
 			goto check;
 		}
 
@@ -1318,14 +1336,13 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
 			refwwid = dev;
 
 check:
-		if (refwwid && strlen(refwwid)) {
-			if (filter_wwid(conf->blist_wwid, conf->elist_wwid,
-					refwwid, NULL) > 0) {
-				put_multipath_config(conf);
-				return 2;
-			}
-		}
-		put_multipath_config(conf);
+		if (refwwid && strlen(refwwid) &&
+		    filter_wwid(conf->blist_wwid, conf->elist_wwid, refwwid,
+				NULL) > 0)
+			invalid = 1;
+		pthread_cleanup_pop(1);
+		if (invalid)
+			return 2;
 	}
 out:
 	if (refwwid && strlen(refwwid)) {
@@ -1347,8 +1364,9 @@ int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
 	if (refresh) {
 		vector_foreach_slot (mpp->paths, pp, i) {
 			struct config *conf = get_multipath_config();
+			pthread_cleanup_push(put_multipath_config, conf);
 			r = pathinfo(pp, conf, DI_PRIO);
-			put_multipath_config(conf);
+			pthread_cleanup_pop(1);
 			if (r) {
 				condlog(2, "%s: failed to refresh pathinfo",
 					mpp->alias);
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index 9bafbc6..2a92105 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -241,12 +241,16 @@ void libmp_udev_set_sync_support(int on)
 void libmp_dm_init(void)
 {
 	struct config *conf;
+	int verbosity;
+	unsigned int version[3];
 
 	conf = get_multipath_config();
-	dm_init(conf->verbosity);
-	if (dm_prereq(conf->version))
-		exit(1);
+	verbosity = conf->verbosity;
+	memcpy(version, conf->version, sizeof(version));
 	put_multipath_config(conf);
+	dm_init(verbosity);
+	if (dm_prereq(version))
+		exit(1);
 	dm_udev_set_sync_support(libmp_dm_udev_sync);
 }
 
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index 9f2a9c9..1ef1dfa 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -170,10 +170,11 @@ path_discovery (vector pathvec, int flag)
 		if(devtype && !strncmp(devtype, "disk", 4)) {
 			total_paths++;
 			conf = get_multipath_config();
+			pthread_cleanup_push(put_multipath_config, conf);
 			if (path_discover(pathvec, conf,
 					  udevice, flag) == PATHINFO_OK)
 				num_paths++;
-			put_multipath_config(conf);
+			pthread_cleanup_pop(1);
 		}
 		udev_device_unref(udevice);
 	}
@@ -1617,6 +1618,7 @@ get_prio (struct path * pp)
 {
 	struct prio * p;
 	struct config *conf;
+	int checker_timeout;
 
 	if (!pp)
 		return 0;
@@ -1624,9 +1626,10 @@ get_prio (struct path * pp)
 	p = &pp->prio;
 	if (!prio_selected(p)) {
 		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		select_detect_prio(conf, pp);
 		select_prio(conf, pp);
-		put_multipath_config(conf);
+		pthread_cleanup_pop(1);
 		if (!prio_selected(p)) {
 			condlog(3, "%s: no prio selected", pp->dev);
 			pp->priority = PRIO_UNDEF;
@@ -1634,8 +1637,9 @@ get_prio (struct path * pp)
 		}
 	}
 	conf = get_multipath_config();
-	pp->priority = prio_getprio(p, pp, conf->checker_timeout);
+	checker_timeout = conf->checker_timeout;
 	put_multipath_config(conf);
+	pp->priority = prio_getprio(p, pp, checker_timeout);
 	if (pp->priority < 0) {
 		condlog(3, "%s: %s prio error", pp->dev, prio_name(p));
 		pp->priority = PRIO_UNDEF;
@@ -1849,8 +1853,9 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev)
 
 	if (!pp->uid_attribute && !pp->getuid) {
 		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		select_getuid(conf, pp);
-		put_multipath_config(conf);
+		pthread_cleanup_pop(1);
 	}
 
 	memset(pp->wwid, 0, WWID_SIZE);
diff --git a/libmultipath/parser.c b/libmultipath/parser.c
index 2f9ab6e..b8b7e0d 100644
--- a/libmultipath/parser.c
+++ b/libmultipath/parser.c
@@ -171,8 +171,9 @@ snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
 			break;
 		case 'v':
 			conf = get_multipath_config();
+			pthread_cleanup_push(put_multipath_config, conf);
 			r = kw->print(conf, buff + fwd, len - fwd, data);
-			put_multipath_config(conf);
+			pthread_cleanup_pop(1);
 			if (!r) { /* no output if no value */
 				buff[0] = '\0';
 				return 0;
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 8c8fb25..38f0438 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -71,9 +71,10 @@ int adopt_paths(vector pathvec, struct multipath *mpp)
 			    store_path(mpp->paths, pp))
 					return 1;
 			conf = get_multipath_config();
+			pthread_cleanup_push(put_multipath_config, conf);
 			ret = pathinfo(pp, conf,
 				       DI_PRIO | DI_CHECKER);
-			put_multipath_config(conf);
+			pthread_cleanup_pop(1);
 			if (ret)
 				return 1;
 		}
@@ -276,7 +277,10 @@ update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
 
 void enter_recovery_mode(struct multipath *mpp)
 {
+	int checkint;
 	struct config *conf = get_multipath_config();
+	checkint = conf->checkint;
+	put_multipath_config(conf);
 
 	/*
 	 * Enter retry mode.
@@ -284,10 +288,9 @@ void enter_recovery_mode(struct multipath *mpp)
 	 * starting retry.
 	 */
 	mpp->stat_queueing_timeouts++;
-	mpp->retry_tick = mpp->no_path_retry * conf->checkint + 1;
+	mpp->retry_tick = mpp->no_path_retry * checkint + 1;
 	condlog(1, "%s: Entering recovery mode: max_retries=%d",
 		mpp->alias, mpp->no_path_retry);
-	put_multipath_config(conf);
 }
 
 void
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
index c6a9e8b..3955c49 100644
--- a/libmultipath/uevent.c
+++ b/libmultipath/uevent.c
@@ -162,8 +162,9 @@ uevent_get_wwid(struct uevent *uev)
 	struct config * conf;
 
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, uev->kernel);
-	put_multipath_config(conf);
+	pthread_cleanup_pop(1);
 
 	val = uevent_get_env_var(uev, uid_attribute);
 	if (val)
@@ -188,6 +189,7 @@ uevent_need_merge(void)
 bool
 uevent_can_discard(struct uevent *uev)
 {
+	int invalid = 0;
 	struct config * conf;
 
 	/*
@@ -199,13 +201,14 @@ uevent_can_discard(struct uevent *uev)
 	 * filter paths devices by devnode
 	 */
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
-			   uev->kernel) > 0) {
-		put_multipath_config(conf);
-		return true;
-	}
-	put_multipath_config(conf);
+			   uev->kernel) > 0)
+		invalid = 1;
+	pthread_cleanup_pop(1);
 
+	if (invalid)
+		return true;
 	return false;
 }
 
diff --git a/libmultipath/wwids.c b/libmultipath/wwids.c
index bc70a27..0ec9f25 100644
--- a/libmultipath/wwids.c
+++ b/libmultipath/wwids.c
@@ -92,8 +92,9 @@ replace_wwids(vector mp)
 	struct config *conf;
 
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
-	put_multipath_config(conf);
+	pthread_cleanup_pop(1);
 	if (fd < 0)
 		goto out;
 	if (!can_write) {
@@ -206,8 +207,9 @@ remove_wwid(char *wwid) {
 	}
 	condlog(3, "removing line '%s' from wwids file", str);
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
-	put_multipath_config(conf);
+	pthread_cleanup_pop(1);
 	if (fd < 0)
 		goto out;
 	if (!can_write) {
@@ -231,8 +233,9 @@ check_wwids_file(char *wwid, int write_wwid)
 	struct config *conf;
 
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
-	put_multipath_config(conf);
+	pthread_cleanup_pop(1);
 	if (fd < 0)
 		return -1;
 
@@ -273,17 +276,16 @@ out:
 int
 should_multipath(struct path *pp1, vector pathvec)
 {
-	int i, ignore_new_devs;
+	int i, ignore_new_devs, find_multipaths;
 	struct path *pp2;
 	struct config *conf;
 
 	conf = get_multipath_config();
 	ignore_new_devs = conf->ignore_new_devs;
-	if (!conf->find_multipaths && !ignore_new_devs) {
-		put_multipath_config(conf);
-		return 1;
-	}
+	find_multipaths = conf->find_multipaths;
 	put_multipath_config(conf);
+	if (find_multipaths && !ignore_new_devs)
+		return 1;
 
 	condlog(4, "checking if %s should be multipathed", pp1->dev);
 	if (!ignore_new_devs) {
diff --git a/mpathpersist/main.c b/mpathpersist/main.c
index 79b89e5..983599a 100644
--- a/mpathpersist/main.c
+++ b/mpathpersist/main.c
@@ -47,7 +47,7 @@ struct config *get_multipath_config(void)
 	return multipath_conf;
 }
 
-void put_multipath_config(struct config *conf)
+void put_multipath_config(void * arg)
 {
 	/* Noop for now */
 }
diff --git a/multipath/main.c b/multipath/main.c
index 716203e..d08c688 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -70,7 +70,7 @@ struct config *get_multipath_config(void)
 	return multipath_conf;
 }
 
-void put_multipath_config(struct config *conf)
+void put_multipath_config(void *arg)
 {
 	/* Noop for now */
 }
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 60ec48b..568060d 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -252,14 +252,16 @@ show_config (char ** r, int * len)
 	unsigned int maxlen = INITIAL_REPLY_LEN;
 	int again = 1;
 	struct config *conf;
+	int fail = 0;
 
 	c = reply = MALLOC(maxlen);
 
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	while (again) {
 		if (!reply) {
-			put_multipath_config(conf);
-			return 1;
+			fail = 1;
+			break;
 		}
 		c = reply;
 		c += snprint_defaults(conf, c, reply + maxlen - c);
@@ -296,7 +298,9 @@ show_config (char ** r, int * len)
 			REALLOC_REPLY(reply, again, maxlen);
 		}
 	}
-	put_multipath_config(conf);
+	pthread_cleanup_pop(1);
+	if (fail)
+		return 1;
 	*r = reply;
 	*len = (int)(c - reply + 1);
 	return 0;
@@ -714,34 +718,37 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
 	struct path *pp;
 	int r;
 	struct config *conf;
+	int invalid = 0;
 
 	param = convert_dev(param, 1);
 	condlog(2, "%s: add path (operator)", param);
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
-			   param) > 0) {
-		put_multipath_config(conf);
+			   param) > 0)
+		invalid = 1;
+	pthread_cleanup_pop(1);
+	if (invalid)
 		goto blacklisted;
-	}
 
 	pp = find_path_by_dev(vecs->pathvec, param);
 	if (pp) {
 		condlog(2, "%s: path already in pathvec", param);
-		if (pp->mpp) {
-			put_multipath_config(conf);
+		if (pp->mpp)
 			return 0;
-		}
 	} else {
 		struct udev_device *udevice;
 
 		udevice = udev_device_new_from_subsystem_sysname(udev,
 								 "block",
 								 param);
+		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		r = store_pathinfo(vecs->pathvec, conf,
 				   udevice, DI_ALL, &pp);
+		pthread_cleanup_pop(1);
 		udev_device_unref(udevice);
 		if (!pp) {
-			put_multipath_config(conf);
 			if (r == 2)
 				goto blacklisted;
 			condlog(0, "%s: failed to store path info", param);
@@ -749,7 +756,6 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
 		}
 		pp->checkint = conf->checkint;
 	}
-	put_multipath_config(conf);
 	return ev_add_path(pp, vecs, 1);
 blacklisted:
 	*reply = strdup("blacklisted\n");
@@ -785,19 +791,22 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
 	char *refwwid, *alias = NULL;
 	int rc, count = 0;
 	struct config *conf;
+	int invalid = 0;
 
 	param = convert_dev(param, 0);
 	condlog(2, "%s: add map (operator)", param);
 
 	conf = get_multipath_config();
-	if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param, NULL) > 0) {
-		put_multipath_config(conf);
+	pthread_cleanup_push(put_multipath_config, conf);
+	if (filter_wwid(conf->blist_wwid, conf->elist_wwid, param, NULL) > 0)
+		invalid = 1;
+	pthread_cleanup_pop(1);
+	if (invalid) {
 		*reply = strdup("blacklisted\n");
 		*len = strlen(*reply) + 1;
 		condlog(2, "%s: map blacklisted", param);
 		return 1;
 	}
-	put_multipath_config(conf);
 	do {
 		if (dm_get_major_minor(param, &major, &minor) < 0)
 			condlog(2, "%s: not a device mapper table", param);
@@ -975,9 +984,10 @@ cli_resize(void *v, char **reply, int *len, void *data)
 int
 cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
 {
-	struct config *conf = get_multipath_config();
+	struct config *conf;
 
 	condlog(2, "force queue_without_daemon (operator)");
+	conf = get_multipath_config();
 	if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF)
 		conf->queue_without_daemon = QUE_NO_DAEMON_FORCE;
 	put_multipath_config(conf);
@@ -987,9 +997,10 @@ cli_force_no_daemon_q(void * v, char ** reply, int * len, void * data)
 int
 cli_restore_no_daemon_q(void * v, char ** reply, int * len, void * data)
 {
-	struct config *conf = get_multipath_config();
+	struct config *conf;
 
 	condlog(2, "restore queue_without_daemon (operator)");
+	conf = get_multipath_config();
 	if (conf->queue_without_daemon == QUE_NO_DAEMON_FORCE)
 		conf->queue_without_daemon = QUE_NO_DAEMON_OFF;
 	put_multipath_config(conf);
@@ -1017,10 +1028,11 @@ cli_restore_queueing(void *v, char **reply, int *len, void *data)
 		return 1;
 	}
 
-	conf = get_multipath_config();
 	mpp->disable_queueing = 0;
+	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	select_no_path_retry(conf, mpp);
-	put_multipath_config(conf);
+	pthread_cleanup_pop(1);
 
 	if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
 			mpp->no_path_retry != NO_PATH_RETRY_FAIL) {
@@ -1044,10 +1056,11 @@ cli_restore_all_queueing(void *v, char **reply, int *len, void *data)
 
 	condlog(2, "restore queueing (operator)");
 	vector_foreach_slot(vecs->mpvec, mpp, i) {
-		struct config *conf = get_multipath_config();
 		mpp->disable_queueing = 0;
+		struct config *conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		select_no_path_retry(conf, mpp);
-		put_multipath_config(conf);
+		pthread_cleanup_pop(1);
 		if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
 		    mpp->no_path_retry != NO_PATH_RETRY_FAIL) {
 			dm_queue_if_no_path(mpp->alias, 1);
@@ -1279,14 +1292,17 @@ show_blacklist (char ** r, int * len)
 	char *reply = NULL;
 	unsigned int maxlen = INITIAL_REPLY_LEN;
 	int again = 1;
-	struct config *conf = get_multipath_config();
+	struct config *conf;
+	int fail = 0;
 
 	reply = MALLOC(maxlen);
 
+	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	while (again) {
 		if (!reply) {
-			put_multipath_config(conf);
-			return 1;
+			fail = 1;
+			break;
 		}
 
 		c = reply;
@@ -1294,11 +1310,12 @@ show_blacklist (char ** r, int * len)
 		again = ((c - reply) == maxlen);
 		REALLOC_REPLY(reply, again, maxlen);
 	}
+	pthread_cleanup_pop(1);
 
+	if (fail)
+		return 1;
 	*r = reply;
 	*len = (int)(c - reply + 1);
-	put_multipath_config(conf);
-
 	return 0;
 }
 
@@ -1317,14 +1334,17 @@ show_devices (char ** r, int * len, struct vectors *vecs)
 	char *reply = NULL;
 	unsigned int maxlen = INITIAL_REPLY_LEN;
 	int again = 1;
-	struct config *conf = get_multipath_config();
+	struct config *conf;
+	int fail = 0;
 
 	reply = MALLOC(maxlen);
 
+	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	while (again) {
 		if (!reply) {
-			put_multipath_config(conf);
-			return 1;
+			fail = 1;
+			break;
 		}
 
 		c = reply;
@@ -1332,10 +1352,12 @@ show_devices (char ** r, int * len, struct vectors *vecs)
 		again = ((c - reply) == maxlen);
 		REALLOC_REPLY(reply, again, maxlen);
 	}
+	pthread_cleanup_pop(1);
 
+	if (fail)
+		return 1;
 	*r = reply;
 	*len = (int)(c - reply + 1);
-	put_multipath_config(conf);
 
 	return 0;
 }
@@ -1479,8 +1501,9 @@ cli_unsetprkey(void * v, char ** reply, int * len, void * data)
 		return 1;
 
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	ret = set_prkey(conf, mpp, 0);
-	put_multipath_config(conf);
+	pthread_cleanup_pop(1);
 
 	return ret;
 }
@@ -1509,8 +1532,9 @@ cli_setprkey(void * v, char ** reply, int * len, void * data)
 	}
 
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	ret = set_prkey(conf, mpp, prkey);
-	put_multipath_config(conf);
+	pthread_cleanup_pop(1);
 
 	return ret;
 }
diff --git a/multipathd/main.c b/multipathd/main.c
index eccb046..9a4f671 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -246,7 +246,7 @@ struct config *get_multipath_config(void)
 	return rcu_dereference(multipath_conf);
 }
 
-void put_multipath_config(struct config *conf)
+void put_multipath_config(void *arg)
 {
 	rcu_read_unlock();
 }
@@ -270,8 +270,10 @@ need_switch_pathgroup (struct multipath * mpp, int refresh)
 		vector_foreach_slot (mpp->pg, pgp, i) {
 			vector_foreach_slot (pgp->paths, pp, j) {
 				conf = get_multipath_config();
+				pthread_cleanup_push(put_multipath_config,
+						     conf);
 				pathinfo(pp, conf, DI_PRIO);
-				put_multipath_config(conf);
+				pthread_cleanup_pop(1);
 			}
 		}
 	}
@@ -430,6 +432,11 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset)
 			if (pp->state != PATH_DOWN) {
 				struct config *conf = get_multipath_config();
 				int oldstate = pp->state;
+				int checkint;
+
+				conf = get_multipath_config();
+				checkint = conf->checkint;
+				put_multipath_config(conf);
 				condlog(2, "%s: mark as failed", pp->dev);
 				mpp->stat_path_failures++;
 				pp->state = PATH_DOWN;
@@ -441,9 +448,8 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset)
 				 * if opportune,
 				 * schedule the next check earlier
 				 */
-				if (pp->tick > conf->checkint)
-					pp->tick = conf->checkint;
-				put_multipath_config(conf);
+				if (pp->tick > checkint)
+					pp->tick = checkint;
 			}
 		}
 	}
@@ -821,9 +827,10 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
 			udev_device_unref(pp->udev);
 			pp->udev = udev_device_ref(uev->udev);
 			conf = get_multipath_config();
+			pthread_cleanup_push(put_multipath_config, conf);
 			r = pathinfo(pp, conf,
 				     DI_ALL | DI_BLACKLIST);
-			put_multipath_config(conf);
+			pthread_cleanup_pop(1);
 			if (r == PATHINFO_OK)
 				ret = ev_add_path(pp, vecs, need_do_map);
 			else if (r == PATHINFO_SKIPPED) {
@@ -848,9 +855,10 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
 	 * get path vital state
 	 */
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	ret = alloc_path_with_pathinfo(conf, uev->udev,
 				       uev->wwid, DI_ALL, &pp);
-	put_multipath_config(conf);
+	pthread_cleanup_pop(1);
 	if (!pp) {
 		if (ret == PATHINFO_SKIPPED)
 			return 0;
@@ -1215,10 +1223,11 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
 			udev_device_unref(pp->udev);
 			pp->udev = udev_device_ref(uev->udev);
 			conf = get_multipath_config();
+			pthread_cleanup_push(put_multipath_config, conf);
 			if (pathinfo(pp, conf, DI_SYSFS|DI_NOIO) != PATHINFO_OK)
 				condlog(1, "%s: pathinfo failed after change uevent",
 					uev->kernel);
-			put_multipath_config(conf);
+			pthread_cleanup_pop(1);
 		}
 
 		if (pp->initialized == INIT_REQUESTED_UDEV)
@@ -1246,8 +1255,9 @@ out:
 			int flag = DI_SYSFS | DI_WWID;
 
 			conf = get_multipath_config();
+			pthread_cleanup_push(put_multipath_config, conf);
 			retval = alloc_path_with_pathinfo(conf, uev->udev, uev->wwid, flag, NULL);
-			put_multipath_config(conf);
+			pthread_cleanup_pop(1);
 
 			if (retval == PATHINFO_SKIPPED) {
 				condlog(3, "%s: spurious uevent, path is blacklisted", uev->kernel);
@@ -1739,8 +1749,10 @@ int update_prio(struct path *pp, int refresh_all)
 			vector_foreach_slot (pgp->paths, pp1, j) {
 				oldpriority = pp1->priority;
 				conf = get_multipath_config();
+				pthread_cleanup_push(put_multipath_config,
+						     conf);
 				pathinfo(pp1, conf, DI_PRIO);
-				put_multipath_config(conf);
+				pthread_cleanup_pop(1);
 				if (pp1->priority != oldpriority)
 					changed = 1;
 			}
@@ -1749,9 +1761,10 @@ int update_prio(struct path *pp, int refresh_all)
 	}
 	oldpriority = pp->priority;
 	conf = get_multipath_config();
+	pthread_cleanup_push(put_multipath_config, conf);
 	if (pp->state != PATH_DOWN)
 		pathinfo(pp, conf, DI_PRIO);
-	put_multipath_config(conf);
+	pthread_cleanup_pop(1);
 
 	if (pp->priority == oldpriority)
 		return 0;
@@ -1838,8 +1851,9 @@ check_path (struct vectors * vecs, struct path * pp, int ticks)
 
 	if (newstate == PATH_UP) {
 		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		newstate = get_state(pp, conf, 1, newstate);
-		put_multipath_config(conf);
+		pthread_cleanup_pop(1);
 	} else
 		checker_clear_message(&pp->checker);
 
@@ -1852,8 +1866,9 @@ check_path (struct vectors * vecs, struct path * pp, int ticks)
 	if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) {
 		condlog(2, "%s: unusable path", pp->dev);
 		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		pathinfo(pp, conf, 0);
-		put_multipath_config(conf);
+		pthread_cleanup_pop(1);
 		return 1;
 	}
 	if (!pp->mpp) {
@@ -1861,15 +1876,14 @@ check_path (struct vectors * vecs, struct path * pp, int ticks)
 		    (newstate == PATH_UP || newstate == PATH_GHOST)) {
 			condlog(2, "%s: add missing path", pp->dev);
 			conf = get_multipath_config();
+			pthread_cleanup_push(put_multipath_config, conf);
 			ret = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST);
+			pthread_cleanup_pop(1);
 			if (ret == PATHINFO_OK) {
 				ev_add_path(pp, vecs, 1);
 				pp->tick = 1;
-			} else if (ret == PATHINFO_SKIPPED) {
-				put_multipath_config(conf);
+			} else if (ret == PATHINFO_SKIPPED)
 				return -1;
-			}
-			put_multipath_config(conf);
 		}
 		return 0;
 	}
@@ -2268,6 +2282,7 @@ configure (struct vectors * vecs)
 
 	vector_foreach_slot (vecs->pathvec, pp, i){
 		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
 		if (filter_path(conf, pp) > 0){
 			vector_del_slot(vecs->pathvec, i);
 			free_path(pp);
@@ -2275,7 +2290,7 @@ configure (struct vectors * vecs)
 		}
 		else
 			pp->checkint = conf->checkint;
-		put_multipath_config(conf);
+		pthread_cleanup_pop(1);
 	}
 	if (map_discovery(vecs)) {
 		condlog(0, "configure failed at map discovery");
@@ -2587,6 +2602,7 @@ child (void * param)
 	int pid_fd = -1;
 	struct config *conf;
 	char *envp;
+	int queue_without_daemon;
 
 	mlockall(MCL_CURRENT | MCL_FUTURE);
 	signal_init();
@@ -2778,10 +2794,11 @@ child (void * param)
 
 	lock(&vecs->lock);
 	conf = get_multipath_config();
-	if (conf->queue_without_daemon == QUE_NO_DAEMON_OFF)
+	queue_without_daemon = conf->queue_without_daemon;
+	put_multipath_config(conf);
+	if (queue_without_daemon == QUE_NO_DAEMON_OFF)
 		vector_foreach_slot(vecs->mpvec, mpp, i)
 			dm_queue_if_no_path(mpp->alias, 0);
-	put_multipath_config(conf);
 	remove_maps_and_stop_waiters(vecs);
 	unlock(&vecs->lock);
 
diff --git a/tests/globals.c b/tests/globals.c
index 80f57bd..07d970e 100644
--- a/tests/globals.c
+++ b/tests/globals.c
@@ -14,5 +14,5 @@ struct config *get_multipath_config(void)
 	return &conf;
 }
 
-void put_multipath_config(struct config* c)
+void put_multipath_config(void *arg)
 {}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* Re: [PATCH 1/2] multipathd: register threads that use rcu calls
  2018-03-23 20:00 ` [PATCH 1/2] multipathd: register threads that use rcu calls Benjamin Marzinski
@ 2018-03-23 20:56   ` Martin Wilck
  0 siblings, 0 replies; 5+ messages in thread
From: Martin Wilck @ 2018-03-23 20:56 UTC (permalink / raw)
  To: Benjamin Marzinski, device-mapper development

On Fr, 2018-03-23 at 15:00 -0500, Benjamin Marzinski wrote:
> All calls to condlog() are rcu reader side calls, so any thread that
> uses condlog() must register itself. The only threads that are exempt
> are log_thread, since it never calls condlog (or any other function
> that
> calls get_multipath_config) and mpath_pr_event_handler_fn, which is
> only
> called by mpath_persist, which disables the rcu handling.
> 
> Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>

Reviewed-by: Martin Wilck <mwilck@suse.com>

-- 
Dr. Martin Wilck <mwilck@suse.com>, Tel. +49 (0)911 74053 2107
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] multipath: fix rcu thread cancellation hang
  2018-03-23 20:00 ` [PATCH 2/2] multipath: fix rcu thread cancellation hang Benjamin Marzinski
@ 2018-03-23 21:09   ` Martin Wilck
  0 siblings, 0 replies; 5+ messages in thread
From: Martin Wilck @ 2018-03-23 21:09 UTC (permalink / raw)
  To: Benjamin Marzinski, device-mapper development

On Fr, 2018-03-23 at 15:00 -0500, Benjamin Marzinski wrote:
> While the rcu code is waiting for a grace period to elapse, no
> threads
> can register or unregister as rcu reader threads. If for some reason,
> a
> thread never calls put_multipath_config() to exit a read side
> critical
> section, then any threads trying to start or stop will hang. This can
> happen if a thread is cancelled between calls to
> get_multipath_config()
> and put_multipath_config(), and multipathd is reconfigured (which
> causes
> the rcu code to wait for a grace period).
> 
> This patch fixes this issue in two ways. Where possible, it reorders
> the
> code or saves config values into local variables to remove
> cancellation
> points between calls to get_multipath_config() and
> put_multipath_config().  In cases where this isn't possible (or where
> it
> would cause a significant amount of extra work to be done) multipath
> now
> pushes a cleanup handler to call put_multipath_config().
> 
> The only functions that were not modified were ones that were only
> called by multipath or mpathpersist, since these are single threaded
> and already disable rcu thread registration.
> 
> Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>

Kudos for doing this meticulous work!

Reviewed-by: Martin Wilck <mwilck@suse.com>

(I admit my review wasn't in depth. I fully ack the idea of the patch, 
and I scanned through it without spotting obvious errors. I did not
check whether you should have changed more code as you already did).

Here's a suggestion, as I think this is getting pretty ugly (not your
fault). Maybe we should rename get_multipath_config() to
__get_multipath_config() and do something like

#define begin_with_config(conf) \
    __get_multipath_config(conf); \
    pthread_cleanup_push(__put_multipath_config, conf); \
    do

#define end_with_config(conf) \
    while(0); \
    pthread_cleanup_pop(1)

... and require that all code blocks accessing the configuration should
be coded like this:

begin_with_config(conf) {
    ... CODE ...
} end_with_config(conf);

IMO that'd improve readability and reduce likelihood of errors.

As you're touching so many lines of code anyway, that wouldn't be that
much harder :-/

Regards,
Martin


-- 
Dr. Martin Wilck <mwilck@suse.com>, Tel. +49 (0)911 74053 2107
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton
HRB 21284 (AG Nürnberg)

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2018-03-23 21:09 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2018-03-23 20:00 [PATCH 0/2] Fix RCU hang Benjamin Marzinski
2018-03-23 20:00 ` [PATCH 1/2] multipathd: register threads that use rcu calls Benjamin Marzinski
2018-03-23 20:56   ` Martin Wilck
2018-03-23 20:00 ` [PATCH 2/2] multipath: fix rcu thread cancellation hang Benjamin Marzinski
2018-03-23 21:09   ` Martin Wilck

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.