[dm-devel] [PATCH 3/6] multipath: centralize validation code

Martin Wilck Martin.Wilck at suse.com
Fri May 15 20:37:16 UTC 2020


On Thu, 2020-05-14 at 20:59 -0500, Benjamin Marzinski wrote:
> This code pulls the multipath path validation code out of
> configure(),
> and puts it into its own function, check_path_valid(). This function
> calls a new libmultipath function, is_path_valid() to check just path
> requested. This seperation exists so that is_path_valid() can be
> reused
> by future code. This code will give almost the same answer as the
> existing code, with the exception that now, if a device is currently
> multipathed, it will always be a valid multipath path.
> 
> Signed-off-by: Benjamin Marzinski <bmarzins at redhat.com>

Great job getting the logic right! Readability massively improved.
Almost ack, a few comments and questions below.

Regards,
Martin


> ---
>  libmultipath/Makefile    |   2 +-
>  libmultipath/devmapper.c |  49 +++++++
>  libmultipath/devmapper.h |   1 +
>  libmultipath/structs.h   |  24 +---
>  libmultipath/valid.c     | 118 ++++++++++++++++
>  libmultipath/valid.h     |  32 +++++
>  libmultipath/wwids.c     |  10 +-
>  multipath/main.c         | 296 +++++++++++++++++------------------
> ----
>  8 files changed, 337 insertions(+), 195 deletions(-)
>  create mode 100644 libmultipath/valid.c
>  create mode 100644 libmultipath/valid.h
> 
> diff --git a/libmultipath/Makefile b/libmultipath/Makefile
> index f19b7ad2..e5dac5ea 100644
> --- a/libmultipath/Makefile
> +++ b/libmultipath/Makefile
> @@ -48,7 +48,7 @@ OBJS = memory.o parser.o vector.o devmapper.o
> callout.o \
>  	log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \
>  	lock.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \
>  	io_err_stat.o dm-generic.o generic.o foreign.o nvme-lib.o \
> -	libsg.o
> +	libsg.o valid.o
>  
>  all: $(LIBS)
>  
> diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
> index 7ed494a1..92f61133 100644
> --- a/libmultipath/devmapper.c
> +++ b/libmultipath/devmapper.c
> @@ -770,6 +770,55 @@ out:
>  	return r;
>  }
>  
> +/*
> + * Return
> + *   1 : map with uuid exists
> + *   0 : map with uuid doesn't exist
> + *  -1 : error
> + */
> +int
> +dm_map_present_by_uuid(const char *uuid)
> +{
> +	struct dm_task *dmt;
> +	struct dm_info info;
> +	char prefixed_uuid[WWID_SIZE + UUID_PREFIX_LEN];
> +	int r = -1;
> +
> +	if (!uuid || uuid[0] == '\0')
> +		return 0;
> +
> +	if (safe_sprintf(prefixed_uuid, UUID_PREFIX "%s", uuid))
> +		goto out;
> +
> +	if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
> +		goto out;
> +
> +	dm_task_no_open_count(dmt);
> +
> +	if (!dm_task_set_uuid(dmt, prefixed_uuid))
> +		goto out_task;
> +
> +	if (!dm_task_run(dmt))
> +		goto out_task;
> +
> +	if (!dm_task_get_info(dmt, &info))
> +		goto out_task;
> +
> +	r = 0;
> +
> +	if (!info.exists)
> +		goto out_task;
> +
> +	r = 1;

I have nothing against goto for error handling, but here I'd prefer 

        r = !!info.exists;

> +out_task:
> +	dm_task_destroy(dmt);
> +out:
> +	if (r < 0)
> +		condlog(3, "%s: dm command failed in %s: %s", uuid,
> +			__FUNCTION__, strerror(errno));
> +	return r;
> +}
> +
>  static int
>  dm_dev_t (const char * mapname, char * dev_t, int len)
>  {
> diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h
> index 17fc9faf..5ed7edc5 100644
> --- a/libmultipath/devmapper.h
> +++ b/libmultipath/devmapper.h
> @@ -39,6 +39,7 @@ int dm_simplecmd_noflush (int, const char *,
> uint16_t);
>  int dm_addmap_create (struct multipath *mpp, char *params);
>  int dm_addmap_reload (struct multipath *mpp, char *params, int
> flush);
>  int dm_map_present (const char *);
> +int dm_map_present_by_uuid(const char *uuid);
>  int dm_get_map(const char *, unsigned long long *, char *);
>  int dm_get_status(const char *, char *);
>  int dm_type(const char *, char *);
> diff --git a/libmultipath/structs.h b/libmultipath/structs.h
> index 9bd39eb1..d69bc2e9 100644
> --- a/libmultipath/structs.h
> +++ b/libmultipath/structs.h
> @@ -101,29 +101,13 @@ enum yes_no_undef_states {
>  	YNU_YES,
>  };
>  
> -#define _FIND_MULTIPATHS_F (1 << 1)
> -#define _FIND_MULTIPATHS_I (1 << 2)
> -#define _FIND_MULTIPATHS_N (1 << 3)
> -/*
> - * _FIND_MULTIPATHS_F must have the same value as YNU_YES.
> - * Generate a compile time error if that isn't the case.
> - */
> -extern char ___error1___[-(_FIND_MULTIPATHS_F != YNU_YES)];
> -
> -#define find_multipaths_on(conf) \
> -	(!!((conf)->find_multipaths & _FIND_MULTIPATHS_F))
> -#define ignore_wwids_on(conf) \
> -	(!!((conf)->find_multipaths & _FIND_MULTIPATHS_I))
> -#define ignore_new_devs_on(conf) \
> -	(!!((conf)->find_multipaths & _FIND_MULTIPATHS_N))
> -
>  enum find_multipaths_states {
>  	FIND_MULTIPATHS_UNDEF = YNU_UNDEF,
>  	FIND_MULTIPATHS_OFF = YNU_NO,
> -	FIND_MULTIPATHS_ON = _FIND_MULTIPATHS_F,
> -	FIND_MULTIPATHS_GREEDY = _FIND_MULTIPATHS_I,
> -	FIND_MULTIPATHS_SMART = _FIND_MULTIPATHS_F|_FIND_MULTIPATHS_I,
> -	FIND_MULTIPATHS_STRICT = _FIND_MULTIPATHS_F|_FIND_MULTIPATHS_N,
> +	FIND_MULTIPATHS_ON = YNU_YES,
> +	FIND_MULTIPATHS_GREEDY,
> +	FIND_MULTIPATHS_SMART,
> +	FIND_MULTIPATHS_STRICT,
>  	__FIND_MULTIPATHS_LAST,
>  };
>  

What was the reason you changed these definitions? AFAICS you've got
the logic right, and I'm not saying it shouldn't be done, but I'd like
to see a rationale. Is it just simplification / readability?

(Note to self: _FIND_MULTIPATHS_F etc. were introduced to make it
obvious at the time that these flags had the same effect as the
previous "ignore_wwids", "ignore_new_devs", and "find_multipaths"
command line options).


> diff --git a/libmultipath/valid.c b/libmultipath/valid.c
> new file mode 100644
> index 00000000..456b1f6e
> --- /dev/null
> +++ b/libmultipath/valid.c
> @@ -0,0 +1,118 @@
> +/*
> +  Copyright (c) 2020 Benjamin Marzinski, IBM
> +
> +  This program is free software; you can redistribute it and/or
> +  modify it under the terms of the GNU General Public License
> +  as published by the Free Software Foundation; either version 2
> +  of the License, or (at your option) any later version.
> +
> +  This program is distributed in the hope that it will be useful,
> +  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +  GNU General Public License for more details.
> +
> +  You should have received a copy of the GNU General Public License
> +  along with this program.  If not, see <
> https://www.gnu.org/licenses/>;.
> + */
> +#include <stddef.h>
> +#include <errno.h>
> +#include <libudev.h>
> +
> +#include "vector.h"
> +#include "config.h"
> +#include "debug.h"
> +#include "util.h"
> +#include "devmapper.h"
> +#include "discovery.h"
> +#include "wwids.h"
> +#include "sysfs.h"
> +#include "blacklist.h"
> +#include "mpath_cmd.h"
> +#include "valid.h"
> +
> +int
> +is_path_valid(const char *name, struct config *conf, struct path
> *pp,
> +	      bool check_multipathd)
> +{
> +	int r;
> +	int fd;
> +
> +	if (!pp || !name || !conf)
> +		return PATH_IS_ERROR;
> +
> +	if (conf->find_multipaths <= FIND_MULTIPATHS_UNDEF ||
> +	    conf->find_multipaths >= __FIND_MULTIPATHS_LAST)
> +		return PATH_IS_ERROR;
> +
> +	if (safe_sprintf(pp->dev, "%s", name))
> +		return PATH_IS_ERROR;
> +
> +	if (sysfs_is_multipathed(pp, true)) {
> +		if (pp->wwid[0] == '\0')
> +			return PATH_IS_ERROR;
> +		return PATH_IS_VALID_NO_CHECK;
> +	}
> +
> +	/*
> +	 * "multipath -u" may be run before the daemon is started. In
> this
> +	 * case, systemd might own the socket but might delay
> multipathd
> +	 * startup until some other unit (udev settle!)  has finished
> +	 * starting. With many LUNs, the listen backlog may be
> exceeded, which
> +	 * would cause connect() to block. This causes udev workers
> calling
> +	 * "multipath -u" to hang, and thus creates a deadlock, until
> "udev
> +	 * settle" times out.  To avoid this, call connect() in non-
> blocking
> +	 * mode here, and take EAGAIN as indication for a filled-up
> systemd
> +	 * backlog.
> +	 */
> +
> +	if (check_multipathd) {
> +		fd = __mpath_connect(1);
> +		if (fd < 0) {
> +			if (errno != EAGAIN &&
> !systemd_service_enabled(name)) {
> +				condlog(3, "multipathd not running or
> enabled");
> +				return PATH_IS_NOT_VALID;
> +			}
> +		} else
> +			mpath_disconnect(fd);
> +	}
> +
> +	pp->udev = udev_device_new_from_subsystem_sysname(udev,
> "block", name);
> +	if (!pp->udev)
> +		return PATH_IS_ERROR;
> +
> +	r = pathinfo(pp, conf, DI_SYSFS | DI_WWID | DI_BLACKLIST);
> +	if (r == PATHINFO_SKIPPED)
> +		return PATH_IS_NOT_VALID;
> +	else if (r)
> +		return PATH_IS_ERROR;
> +
> +	if (pp->wwid[0] == '\0')
> +		return PATH_IS_NOT_VALID;
> +
> +	if (pp->udev && pp->uid_attribute &&
> +	    filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
> +		return PATH_IS_NOT_VALID;
> +
> +	r = is_failed_wwid(pp->wwid);
> +	if (r != WWID_IS_NOT_FAILED) {
> +		if (r == WWID_IS_FAILED)
> +			return PATH_IS_NOT_VALID;
> +		return PATH_IS_ERROR;
> +	}
> +
> +	if (conf->find_multipaths == FIND_MULTIPATHS_GREEDY)
> +		return PATH_IS_VALID;
> +
> +	if (check_wwids_file(pp->wwid, 0) == 0)
> +		return PATH_IS_VALID_NO_CHECK;
> +
> +	if (dm_map_present_by_uuid(pp->wwid) == 1)
> +		return PATH_IS_VALID;
> +
> +	/* all these act like FIND_MULTIPATHS_STRICT for finding if a
> +	 * path is valid */
> +	if (conf->find_multipaths != FIND_MULTIPATHS_SMART)
> +		return PATH_IS_NOT_VALID;
> +
> +	return PATH_IS_MAYBE_VALID;
> +}
> diff --git a/libmultipath/valid.h b/libmultipath/valid.h
> new file mode 100644
> index 00000000..778658ad
> --- /dev/null
> +++ b/libmultipath/valid.h
> @@ -0,0 +1,32 @@
> +/*
> +  Copyright (c) 2020 Benjamin Marzinski, IBM
> +
> +  This program is free software; you can redistribute it and/or
> +  modify it under the terms of the GNU General Public License
> +  as published by the Free Software Foundation; either version 2
> +  of the License, or (at your option) any later version.
> +
> +  This program is distributed in the hope that it will be useful,
> +  but WITHOUT ANY WARRANTY; without even the implied warranty of
> +  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +  GNU General Public License for more details.
> +
> +  You should have received a copy of the GNU General Public License
> +  along with this program.  If not, see <
> https://www.gnu.org/licenses/>;.
> + */
> +#ifndef _VALID_H
> +#define _VALID_H
> +
> +enum is_path_valid_result {
> +	PATH_IS_ERROR = -1,
> +	PATH_IS_NOT_VALID,
> +	PATH_IS_VALID,
> +	PATH_IS_VALID_NO_CHECK,

I'd like to see the comment explaining the difference between VALID
and VALID_NO_CHECK here, too.

> +	PATH_IS_MAYBE_VALID,
> +	PATH_MAX_VALID_RESULT, /* only for bounds checking */
> +};
> +
> +int is_path_valid(const char *name, struct config *conf, struct path
> *pp,
> +		  bool check_multipathd);
> +
> +#endif /* _VALID_D */
> diff --git a/libmultipath/wwids.c b/libmultipath/wwids.c
> index 28a2150d..637cb0ab 100644
> --- a/libmultipath/wwids.c
> +++ b/libmultipath/wwids.c
> @@ -289,19 +289,19 @@ out:
>  int
>  should_multipath(struct path *pp1, vector pathvec, vector mpvec)
>  {
> -	int i, ignore_new_devs, find_multipaths;
> +	int i, find_multipaths;
>  	struct path *pp2;
>  	struct config *conf;
>  
>  	conf = get_multipath_config();
> -	ignore_new_devs = ignore_new_devs_on(conf);
> -	find_multipaths = find_multipaths_on(conf);
> +	find_multipaths = conf->find_multipaths;
>  	put_multipath_config(conf);
> -	if (!find_multipaths && !ignore_new_devs)
> +	if (find_multipaths == FIND_MULTIPATHS_OFF ||
> +	    find_multipaths == FIND_MULTIPATHS_GREEDY)
>  		return 1;
>  
>  	condlog(4, "checking if %s should be multipathed", pp1->dev);
> -	if (!ignore_new_devs) {
> +	if (find_multipaths != FIND_MULTIPATHS_STRICT) {
>  		char tmp_wwid[WWID_SIZE];
>  		struct multipath *mp = find_mp_by_wwid(mpvec, pp1-
> >wwid);

Noting explicitly here: you got the complex logic right. Kudos :-)

>  
> diff --git a/multipath/main.c b/multipath/main.c
> index 545ead87..953fab27 100644
> --- a/multipath/main.c
> +++ b/multipath/main.c
> @@ -63,21 +63,18 @@
>  #include "propsel.h"
>  #include "time-util.h"
>  #include "file.h"
> +#include "valid.h"
>  
>  int logsink;
>  struct udev *udev;
>  struct config *multipath_conf;
>  
>  /*
> - * Return values of configure(), print_cmd_valid(), and main().
> - * RTVL_{YES,NO} are synonyms for RTVL_{OK,FAIL} for the
> CMD_VALID_PATH case.
> + * Return values of configure(), check_path_valid(), and main().
>   */
>  enum {
>  	RTVL_OK = 0,
> -	RTVL_YES = RTVL_OK,
>  	RTVL_FAIL = 1,
> -	RTVL_NO = RTVL_FAIL,
> -	RTVL_MAYBE, /* only used internally, never returned */
>  	RTVL_RETRY, /* returned by configure(), not by main() */
>  };
>  
> @@ -269,9 +266,6 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp,
> vector pathvec, char * refwwid)
>  			continue;
>  		}
>  
> -		if (cmd == CMD_VALID_PATH)
> -			continue;
> -
>  		dm_get_map(mpp->alias, &mpp->size, params);
>  		condlog(3, "params = %s", params);
>  		dm_get_status(mpp->alias, status);
> @@ -491,10 +485,11 @@ static int print_cmd_valid(int k, const vector
> pathvec,
>  	struct timespec until;
>  	struct path *pp;
>  
> -	if (k != RTVL_YES && k != RTVL_NO && k != RTVL_MAYBE)
> -		return RTVL_NO;
> +	if (k != PATH_IS_VALID && k != PATH_IS_NOT_VALID &&
> +	    k != PATH_IS_MAYBE_VALID)
> +		return PATH_IS_NOT_VALID;
>  
> -	if (k == RTVL_MAYBE) {
> +	if (k == PATH_IS_MAYBE_VALID) {
>  		/*
>  		 * Caller ensures that pathvec[0] is the path to
>  		 * examine.
> @@ -504,7 +499,7 @@ static int print_cmd_valid(int k, const vector
> pathvec,
>  		wait = find_multipaths_check_timeout(
>  			pp, pp->find_multipaths_timeout, &until);
>  		if (wait != FIND_MULTIPATHS_WAITING)
> -			k = RTVL_NO;
> +			k = PATH_IS_NOT_VALID;
>  	} else if (pathvec != NULL && (pp = VECTOR_SLOT(pathvec, 0)))
>  		wait = find_multipaths_check_timeout(pp, 0, &until);
>  	if (wait == FIND_MULTIPATHS_WAITING)
> @@ -513,9 +508,9 @@ static int print_cmd_valid(int k, const vector
> pathvec,
>  	else if (wait == FIND_MULTIPATHS_WAIT_DONE)
>  		printf("FIND_MULTIPATHS_WAIT_UNTIL=\"0\"\n");
>  	printf("DM_MULTIPATH_DEVICE_PATH=\"%d\"\n",
> -	       k == RTVL_MAYBE ? 2 : k == RTVL_YES ? 1 : 0);
> +	       k == PATH_IS_MAYBE_VALID ? 2 : k == PATH_IS_VALID ? 1 :
> 0);
>  	/* Never return RTVL_MAYBE */
> -	return k == RTVL_NO ? RTVL_NO : RTVL_YES;
> +	return k == PATH_IS_NOT_VALID ? PATH_IS_NOT_VALID :
> PATH_IS_VALID;
>  }
>  
>  /*
> @@ -548,7 +543,6 @@ configure (struct config *conf, enum mpath_cmds
> cmd,
>  	int di_flag = 0;
>  	char * refwwid = NULL;
>  	char * dev = NULL;
> -	bool released = released_to_systemd();
>  
>  	/*
>  	 * allocate core vectors to store paths and multipaths
> @@ -573,7 +567,7 @@ configure (struct config *conf, enum mpath_cmds
> cmd,
>  	    cmd != CMD_REMOVE_WWID &&
>  	    (filter_devnode(conf->blist_devnode,
>  			    conf->elist_devnode, dev) > 0)) {
> -		goto print_valid;
> +		goto out;
>  	}
>  
>  	/*
> @@ -581,14 +575,10 @@ configure (struct config *conf, enum mpath_cmds
> cmd,
>  	 * failing the translation is fatal (by policy)
>  	 */
>  	if (devpath) {
> -		int failed = get_refwwid(cmd, devpath, dev_type,
> -					 pathvec, &refwwid);
> +		get_refwwid(cmd, devpath, dev_type, pathvec, &refwwid);
>  		if (!refwwid) {
>  			condlog(4, "%s: failed to get wwid", devpath);
> -			if (failed == 2 && cmd == CMD_VALID_PATH)
> -				goto print_valid;
> -			else
> -				condlog(3, "scope is null");
> +			condlog(3, "scope is null");
>  			goto out;
>  		}
>  		if (cmd == CMD_REMOVE_WWID) {
> @@ -614,53 +604,6 @@ configure (struct config *conf, enum mpath_cmds
> cmd,
>  			goto out;
>  		}
>  		condlog(3, "scope limited to %s", refwwid);
> -		/* If you are ignoring the wwids file and
> find_multipaths is
> -		 * set, you need to actually check if there are two
> available
> -		 * paths to determine if this path should be
> multipathed. To
> -		 * do this, we put off the check until after
> discovering all
> -		 * the paths.
> -		 * Paths listed in the wwids file are always considered
> valid.
> -		 */
> -		if (cmd == CMD_VALID_PATH) {
> -			if (is_failed_wwid(refwwid) == WWID_IS_FAILED)
> {
> -				r = RTVL_NO;
> -				goto print_valid;
> -			}
> -			if ((!find_multipaths_on(conf) &&
> -				    ignore_wwids_on(conf)) ||
> -				   check_wwids_file(refwwid, 0) == 0)
> -				r = RTVL_YES;
> -			if (!ignore_wwids_on(conf))
> -				goto print_valid;PATH_IS_VALID_NO_CHECK
> 
> -			/* At this point, either r==0 or
> find_multipaths_on. */
> -
> -			/*
> -			 * Shortcut for find_multipaths smart:
> -			 * Quick check if path is already multipathed.
> -			 */
> -			if (sysfs_is_multipathed(VECTOR_SLOT(pathvec,
> 0),
> -						 false)) {
> -				r = RTVL_YES;
> -				goto print_valid;
> -			}
> -
> -			/*
> -			 * DM_MULTIPATH_DEVICE_PATH=="0" means that we
> have
> -			 * been called for this device already, and
> have
> -			 * released it to systemd. Unless the device is
> now
> -			 * already multipathed (see above), we can't
> try to
> -			 * grab it, because setting SYSTEMD_READY=0
> would
> -			 * cause file systems to be unmounted.
> -			 * Leave DM_MULTIPATH_DEVICE_PATH="0".
> -			 */
> -			if (released) {
> -				r = RTVL_NO;
> -				goto print_valid;
> -			}
> -			if (r == RTVL_YES)
> -				goto print_valid;
> -			/* find_multipaths_on: Fall through to path
> detection */
> -		}
>  	}
>  
>  	/*
> @@ -701,59 +644,6 @@ configure (struct config *conf, enum mpath_cmds
> cmd,
>  		goto out;
>  	}
>  
> -	if (cmd == CMD_VALID_PATH) {
> -		struct path *pp;
> -		int fd;
> -
> -		/* This only happens if find_multipaths and
> -		 * ignore_wwids is set, and the path is not in WWIDs
> -		 * file, not currently multipathed, and has
> -		 * never been released to systemd.
> -		 * If there is currently a multipath device matching
> -		 * the refwwid, or there is more than one path matching
> -		 * the refwwid, then the path is valid */
> -		if (VECTOR_SIZE(curmp) != 0) {
> -			r = RTVL_YES;
> -			goto print_valid;
> -		} else if (VECTOR_SIZE(pathvec) > 1)
> -			r = RTVL_YES;
> -		else
> -			r = RTVL_MAYBE;
> -
> -		/*
> -		 * If opening the path with O_EXCL fails, the path
> -		 * is in use (e.g. mounted during initramfs
> processing).
> -		 * We know that it's not used by dm-multipath.
> -		 * We may not set SYSTEMD_READY=0 on such devices, it
> -		 * might cause systemd to umount the device.
> -		 * Use O_RDONLY, because udevd would trigger another
> -		 * uevent for close-after-write.
> -		 *
> -		 * The O_EXCL check is potentially dangerous, because
> it may
> -		 * race with other tasks trying to access the device.
> Therefore
> -		 * this code is only executed if the path hasn't been
> released
> -		 * to systemd earlier (see above).
> -		 *
> -		 * get_refwwid() above stores the path we examine in
> slot 0.
> -		 */
> -		pp = VECTOR_SLOT(pathvec, 0);
> -		fd = open(udev_device_get_devnode(pp->udev),
> -			  O_RDONLY|O_EXCL);
> -		if (fd >= 0)
> -			close(fd);
> -		else {
> -			condlog(3, "%s: path %s is in use: %s",
> -				__func__, pp->dev,
> -				strerror(errno));
> -			/*
> -			 * Check if we raced with multipathd
> -			 */
> -			r = sysfs_is_multipathed(VECTOR_SLOT(pathvec,
> 0),
> -						 false) ? RTVL_YES :
> RTVL_NO;
> -		}
> -		goto print_valid;
> -	}
> -
>  	if (cmd != CMD_CREATE && cmd != CMD_DRY_RUN) {
>  		r = RTVL_OK;
>  		goto out;
> @@ -766,10 +656,6 @@ configure (struct config *conf, enum mpath_cmds
> cmd,
>  			   conf->force_reload, cmd);
>  	r = rc == CP_RETRY ? RTVL_RETRY : rc == CP_OK ? RTVL_OK :
> RTVL_FAIL;
>  
> -print_valid:
> -	if (cmd == CMD_VALID_PATH)
> -		r = print_cmd_valid(r, pathvec, conf);
> -
>  out:
>  	if (refwwid)
>  		FREE(refwwid);
> @@ -780,6 +666,112 @@ out:
>  	return r;
>  }
>  
> +static int
> +check_path_valid(const char *name, struct config *conf, bool
> is_uevent)
> +{
> +	int fd, r = PATH_IS_ERROR;
> +	struct path *pp = NULL;
> +	vector pathvec = NULL;
> +
> +	pp = alloc_path();
> +	if (!pp)
> +		return RTVL_FAIL;
> +
> +	r = is_path_valid(name, conf, pp, is_uevent);
> +	if (r <= PATH_IS_ERROR || r >= PATH_MAX_VALID_RESULT)
> +		goto fail;
> +

About the following block ...

> +	/* set path values if is_path_valid() didn't */
> +	if (!pp->udev)
> +		pp->udev = udev_device_new_from_subsystem_sysname(udev,
> "block",
> +								  name)
> ;
> +	if (!pp->udev)
> +		goto fail;
> +
> +	if (!strlen(pp->dev_t)) {
> +		dev_t devt = udev_device_get_devnum(pp->udev);
> +		if (major(devt) == 0 && minor(devt) == 0)
> +			goto fail;
> +		snprintf(pp->dev_t, BLK_DEV_SIZE, "%d:%d", major(devt),
> +			 minor(devt));
> +	}
> +
> +	pathvec = vector_alloc();
> +	if (!pathvec)
> +		goto fail;
> +
> +	if (store_path(pathvec, pp) != 0) {
> +		free_path(pp);
> +		goto fail;
> +	}

... why do you do this here, rather than after the (r !=
PATH_IS_MAYBE_VALID) clause below? AFAICS you don't need pathvec until
you run path_discovery().


> +
> +	if ((r == PATH_IS_VALID || r == PATH_IS_MAYBE_VALID) &&
> +	    released_to_systemd())
> +		r = PATH_IS_NOT_VALID;
> +
> +	/* This state is only used to skip the released_to_systemd()
> check */
> +	if (r == PATH_IS_VALID_NO_CHECK)
> +		r = PATH_IS_VALID;
> +
> +	if (r != PATH_IS_MAYBE_VALID)
> +		goto out;
> +
> +	/*
> +	 * If opening the path with O_EXCL fails, the path
> +	 * is in use (e.g. mounted during initramfs processing).
> +	 * We know that it's not used by dm-multipath.
> +	 * We may not set SYSTEMD_READY=0 on such devices, it
> +	 * might cause systemd to umount the device.
> +	 * Use O_RDONLY, because udevd would trigger another
> +	 * uevent for close-after-write.
> +	 *
> +	 * The O_EXCL check is potentially dangerous, because it may
> +	 * race with other tasks trying to access the device. Therefore
> +	 * this code is only executed if the path hasn't been released
> +	 * to systemd earlier (see above).
> +	 */
> +	fd = open(udev_device_get_devnode(pp->udev), O_RDONLY|O_EXCL);
> +	if (fd >= 0)
> +		close(fd);
> +	else {
> +		condlog(3, "%s: path %s is in use: %m", __func__, pp-
> >dev);
> +		/* Check if we raced with multipathd */
> +		if (sysfs_is_multipathed(pp, false))
> +			r = PATH_IS_VALID;
> +		else
> +			r = PATH_IS_NOT_VALID;
> +		goto out;
> +	}
> +
> +	/* For find_multipaths = SMART, if there is more than one path
> +	 * matching the refwwid, then the path is valid */
> +	if (path_discovery(pathvec, DI_SYSFS | DI_WWID) < 0)
> +		goto fail;
> +	filter_pathvec(pathvec, pp->wwid);
> +	if (VECTOR_SIZE(pathvec) > 1)
> +		r = PATH_IS_VALID;
> +	else
> +		r = PATH_IS_MAYBE_VALID;
> +
> +out:
> +	r = print_cmd_valid(r, pathvec, conf);
> +	free_pathvec(pathvec, FREE_PATHS);
> +	/*
> +	 * multipath -u must exit with status 0, otherwise udev won't
> +	 * import its output.
> +	 */
> +	if (!is_uevent && r == PATH_IS_NOT_VALID)
> +		return RTVL_FAIL;
> +	return RTVL_OK;
> +
> +fail:
> +	if (pathvec)
> +		free_pathvec(pathvec, FREE_PATHS);
> +	else
> +		free_path(pp);
> +	return RTVL_FAIL;
> +}
> +
>  static int
>  get_dev_type(char *dev) {
>  	struct stat buf;
> @@ -861,32 +853,6 @@ out:
>  	return r;
>  }
>  
> -static int test_multipathd_socket(void)
> -{
> -	int fd;
> -	/*
> -	 * "multipath -u" may be run before the daemon is started. In
> this
> -	 * case, systemd might own the socket but might delay
> multipathd
> -	 * startup until some other unit (udev settle!)  has finished
> -	 * starting. With many LUNs, the listen backlog may be
> exceeded, which
> -	 * would cause connect() to block. This causes udev workers
> calling
> -	 * "multipath -u" to hang, and thus creates a deadlock, until
> "udev
> -	 * settle" times out.  To avoid this, call connect() in non-
> blocking
> -	 * mode here, and take EAGAIN as indication for a filled-up
> systemd
> -	 * backlog.
> -	 */
> -
> -	fd = __mpath_connect(1);
> -	if (fd == -1) {
> -		if (errno == EAGAIN)
> -			condlog(3, "daemon backlog exceeded");
> -		else
> -			return 0;
> -	} else
> -		close(fd);
> -	return 1;
> -}
> -
>  int
>  main (int argc, char *argv[])
>  {
> @@ -970,7 +936,11 @@ main (int argc, char *argv[])
>  			conf->force_reload = FORCE_RELOAD_YES;
>  			break;
>  		case 'i':
> -			conf->find_multipaths |= _FIND_MULTIPATHS_I;
> +			if (conf->find_multipaths == FIND_MULTIPATHS_ON
> ||
> +			    conf->find_multipaths ==
> FIND_MULTIPATHS_STRICT)
> +				conf->find_multipaths =
> FIND_MULTIPATHS_SMART;
> +			else if (conf->find_multipaths ==
> FIND_MULTIPATHS_OFF)
> +				conf->find_multipaths =
> FIND_MULTIPATHS_GREEDY;

Ok. Previously FIND_MULTIPATHS_SMART was not the same value as
FIND_MULTIPATHS_STRICT + _FIND_MULTIPATHS_I. Effectively, this doesn't
change logic, but only because the check for ignore_new_devs_on() in
should_multipath() is actually redundant. (IIRC in the past we'd
determined that "strict" + "ignore_wwids" makes no sense).


>  			break;
>  		case 't':
>  			r = dump_config(conf, NULL, NULL) ? RTVL_FAIL :
> RTVL_OK;
> @@ -1064,15 +1034,10 @@ main (int argc, char *argv[])
>  		condlog(0, "the -c option requires a path to check");
>  		goto out;
>  	}
> -	if (cmd == CMD_VALID_PATH &&
> -	    dev_type == DEV_UEVENT) {
> -		if (!test_multipathd_socket()) {
> -			condlog(3, "%s: daemon is not running", dev);
> -			if (!systemd_service_enabled(dev)) {
> -				r = print_cmd_valid(RTVL_NO, NULL,
> conf);
> -				goto out;
> -			}
> -		}
> +	if (cmd == CMD_VALID_PATH) {
> +		char * name = convert_dev(dev, (dev_type ==
> DEV_DEVNODE));
> +		r = check_path_valid(name, conf, dev_type ==
> DEV_UEVENT);
> +		goto out;
>  	}
>  
>  	if (cmd == CMD_REMOVE_WWID && !dev) {
> @@ -1136,13 +1101,6 @@ out:
>  	cleanup_prio();
>  	cleanup_checkers();
>  
> -	/*
> -	 * multipath -u must exit with status 0, otherwise udev won't
> -	 * import its output.
> -	 */
> -	if (cmd == CMD_VALID_PATH && dev_type == DEV_UEVENT && r ==
> RTVL_NO)
> -		r = RTVL_OK;
> -
>  	if (dev_type == DEV_UEVENT)
>  		closelog();
>  

-- 
Dr. Martin Wilck <mwilck at suse.com>, Tel. +49 (0)911 74053 2107
SUSE  Software Solutions Germany GmbH
HRB 36809, AG Nürnberg GF: Felix
Imendörffer






More information about the dm-devel mailing list