[dm-devel] [PATCH 16/72] libmultipath: make path_discovery() pthread_cancel()-safe

Benjamin Marzinski bmarzins at redhat.com
Wed Nov 6 20:51:14 UTC 2019


On Mon, Nov 04, 2019 at 08:29:21AM +0000, Martin Wilck wrote:
> 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 at suse.com>
> > > 
> > > The udev_enumerate and udev_device refs wouldn't be released
> > > if the thread was cancelled. Fix it.
> > > 
> > > Signed-off-by: Martin Wilck <mwilck at suse.com>
> > > ---
> > >  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?

Sure. ACK

-Ben
 
> Besides, to my taste at least, it improves readability of the code to
> move get_multipath_config() out of certain loops.
> 
> Thanks,
> Martin
> 




More information about the dm-devel mailing list