Hi Ben, thanks for looking into this. On Wed, 2019-10-30 at 09:53 -0500, Benjamin Marzinski wrote: > On Sat, Oct 12, 2019 at 09:27:57PM +0000, Martin Wilck wrote: > > From: Martin Wilck <mwilck@xxxxxxxx> > > > > The udev_enumerate and udev_device refs wouldn't be released > > if the thread was cancelled. Fix it. > > > > Signed-off-by: Martin Wilck <mwilck@xxxxxxxx> > > --- > > libmultipath/discovery.c | 51 +++++++++++++++++++++++++++++++----- > > ---- > > 1 file changed, 40 insertions(+), 11 deletions(-) > > > > diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c > > index e68b0e9f..d217ca92 100644 > > --- a/libmultipath/discovery.c > > +++ b/libmultipath/discovery.c > > @@ -140,19 +140,47 @@ path_discover (vector pathvec, struct config > > * conf, > > return pathinfo(pp, conf, flag); > > } > > > > +static void cleanup_udev_enumerate_ptr(void *arg) > > +{ > > + struct udev_enumerate *ue; > > + > > + if (!arg) > > + return; > > + ue = *((struct udev_enumerate**) arg); > > + if (ue) > > + (void)udev_enumerate_unref(ue); > > +} > > + > > +static void cleanup_udev_device_ptr(void *arg) > > +{ > > + struct udev_device *ud; > > + > > + if (!arg) > > + return; > > + ud = *((struct udev_device**) arg); > > + if (ud) > > + (void)udev_device_unref(ud); > > +} > > + > > int > > path_discovery (vector pathvec, int flag) > > { > > - struct udev_enumerate *udev_iter; > > + struct udev_enumerate *udev_iter = NULL; > > struct udev_list_entry *entry; > > - struct udev_device *udevice; > > + struct udev_device *udevice = NULL; > > struct config *conf; > > - const char *devpath; > > int num_paths = 0, total_paths = 0, ret; > > > > + pthread_cleanup_push(cleanup_udev_enumerate_ptr, &udev_iter); > > + pthread_cleanup_push(cleanup_udev_device_ptr, &udevice); > > + conf = get_multipath_config(); > > + pthread_cleanup_push(put_multipath_config, conf); > > + > > udev_iter = udev_enumerate_new(udev); > > - if (!udev_iter) > > - return -ENOMEM; > > + if (!udev_iter) { > > + ret = -ENOMEM; > > + goto out; > > + } > > > > if (udev_enumerate_add_match_subsystem(udev_iter, "block") < 0 > > || > > udev_enumerate_add_match_is_initialized(udev_iter) < 0 || > > @@ -165,6 +193,8 @@ path_discovery (vector pathvec, int flag) > > udev_list_entry_foreach(entry, > > udev_enumerate_get_list_entry(udev_iter > > )) { > > const char *devtype; > > + const char *devpath; > > + > > devpath = udev_list_entry_get_name(entry); > > condlog(4, "Discover device %s", devpath); > > udevice = udev_device_new_from_syspath(udev, devpath); > > @@ -175,19 +205,18 @@ path_discovery (vector pathvec, int flag) > > devtype = udev_device_get_devtype(udevice); > > if(devtype && !strncmp(devtype, "disk", 4)) { > > total_paths++; > > - conf = get_multipath_config(); > > - pthread_cleanup_push(put_multipath_config, > > conf); > > Why move grabbing the config RCU lock out of the loop? Yes, that was the idea. > All things being > equal, it seems like we'd rather hold this for less time, and > rcu_read_lock() is designed to be lightweight, so calling it more > times > shouldn't be an issue. It's not the execution time of rcu_read_lock() that I'm concerned about. In this particular loop, my estimate is that >90% of time is spent in path_discover()/pathinfo(), so time-during-which-lock-is-held-wise, we gain little by taking and releasing the RCU lock in every iteration. Right, we might catch a configuration change _earlier_ if we release the lock between pathinfo() invocations. But - do we actually want that? This lock protects us against corruption of the multipathd configuration, basically against someone calling "multipathd reconfigure" while our code is running. But if the configuration ins really changed, what we're currently doing is vain anyway - once the configure() call is finished, we will go through yet another full reconfigure cycle. IOW: Do we seriously want to call pathinfo() for the different paths in the system with different configuration, once with and once without "user_friendly_names", for example? Given that the code we're talking about is only called from reconfigure(), multipath_conf having just been reassigned, IMO it's an improvement to hold the lock through the entire loop. It might even be good to hold the lock for the complete invocation of configure(), but I haven't thought about that in detail yet. Does this make sense? Besides, to my taste at least, it improves readability of the code to move get_multipath_config() out of certain loops. Thanks, Martin -- dm-devel mailing list dm-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/dm-devel