All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/6] multipath: path validation library prep work
@ 2020-05-19  4:57 Benjamin Marzinski
  2020-05-19  4:57 ` [PATCH v2 1/6] libmultipath: make libmp_dm_init optional Benjamin Marzinski
                   ` (6 more replies)
  0 siblings, 7 replies; 9+ messages in thread
From: Benjamin Marzinski @ 2020-05-19  4:57 UTC (permalink / raw)
  To: Christophe Varoqui; +Cc: device-mapper development, Martin Wilck

I've been playing around with the SID code more and I've decided to hold
off on submitting the library until I have it working with the SID
multipath module better. Instead, I've pulled out the common code that
multipath -u/-c and the library can use, and put it into libmultipath.

I've also removed some of the ordering differences between the existing
code and my new code.  Right now, the only difference is that if a path
is currently multipathed, it will always be claimed as a valid path.

Patches 0001 & 0002 are the same as in my "RFC PATCH v2" set, and patch
0005 is the same as my "libmultipath: simplify failed wwid code" patch.

Only patches 0003 and 0004 haven't been posted before.

Changes from v1:
0003: Minor fixes suggested by Martin Wilck
0004: Fixed typo, added tests for filter_property() and switched some
      tests to pass the check_multipathd code in various ways, instead
      of skipping it, as suggested by Martin Wilck

Benjamin Marzinski (5):
  libmultipath: make libmp_dm_init optional
  libmultipath: make sysfs_is_multipathed able to return wwid
  multipath: centralize validation code
  Unit tests for is_path_valid()
  libmultipath: simplify failed wwid code

Martin Wilck (1):
  libmultipath: use atomic linkat() in mark_failed_wwid()

 libmultipath/Makefile    |   3 +-
 libmultipath/devmapper.c |  62 ++++-
 libmultipath/devmapper.h |   4 +-
 libmultipath/structs.h   |  24 +-
 libmultipath/sysfs.c     |  24 +-
 libmultipath/sysfs.h     |   2 +-
 libmultipath/valid.c     | 118 ++++++++++
 libmultipath/valid.h     |  42 ++++
 libmultipath/wwids.c     | 165 +++++++------
 multipath/main.c         | 295 ++++++++++--------------
 tests/Makefile           |   4 +-
 tests/valid.c            | 486 +++++++++++++++++++++++++++++++++++++++
 12 files changed, 944 insertions(+), 285 deletions(-)
 create mode 100644 libmultipath/valid.c
 create mode 100644 libmultipath/valid.h
 create mode 100644 tests/valid.c

-- 
2.17.2

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

* [PATCH v2 1/6] libmultipath: make libmp_dm_init optional
  2020-05-19  4:57 [PATCH v2 0/6] multipath: path validation library prep work Benjamin Marzinski
@ 2020-05-19  4:57 ` Benjamin Marzinski
  2020-05-19  4:57 ` [PATCH v2 2/6] libmultipath: make sysfs_is_multipathed able to return wwid Benjamin Marzinski
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Benjamin Marzinski @ 2020-05-19  4:57 UTC (permalink / raw)
  To: Christophe Varoqui; +Cc: device-mapper development, Martin Wilck

Move dm_initialized out of libmp_dm_task_create(), and add
a function skip_libmp_dm_init() so that users of libmultipath can skip
initializing device-mapper. This is needed for other programs that
use libmultipath (or a library that depends on it) but want to control
how device-mapper is set up.

Also make dm_prereq a global function.

Reviewed-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/devmapper.c | 17 +++++++++++++----
 libmultipath/devmapper.h |  3 ++-
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index 13a1cf53..7ed494a1 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -33,6 +33,8 @@
 #define MAX_WAIT 5
 #define LOOPS_PER_SEC 5
 
+static pthread_once_t dm_initialized = PTHREAD_ONCE_INIT;
+
 static int dm_conf_verbosity;
 
 #ifdef LIBDM_API_DEFERRED
@@ -229,7 +231,7 @@ dm_tgt_prereq (unsigned int *ver)
 	return 1;
 }
 
-static int dm_prereq(unsigned int *v)
+int dm_prereq(unsigned int *v)
 {
 	if (dm_lib_prereq())
 		return 1;
@@ -243,7 +245,7 @@ void libmp_udev_set_sync_support(int on)
 	libmp_dm_udev_sync = !!on;
 }
 
-void libmp_dm_init(void)
+static void libmp_dm_init(void)
 {
 	struct config *conf;
 	int verbosity;
@@ -262,11 +264,18 @@ void libmp_dm_init(void)
 	dm_udev_set_sync_support(libmp_dm_udev_sync);
 }
 
+static void _do_skip_libmp_dm_init(void)
+{
+}
+
+void skip_libmp_dm_init(void)
+{
+	pthread_once(&dm_initialized, _do_skip_libmp_dm_init);
+}
+
 struct dm_task*
 libmp_dm_task_create(int task)
 {
-	static pthread_once_t dm_initialized = PTHREAD_ONCE_INIT;
-
 	pthread_once(&dm_initialized, libmp_dm_init);
 	return dm_task_create(task);
 }
diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h
index 7557a86b..17fc9faf 100644
--- a/libmultipath/devmapper.h
+++ b/libmultipath/devmapper.h
@@ -28,7 +28,8 @@
 #define UUID_PREFIX_LEN (sizeof(UUID_PREFIX) - 1)
 
 void dm_init(int verbosity);
-void libmp_dm_init(void);
+int dm_prereq(unsigned int *v);
+void skip_libmp_dm_init(void);
 void libmp_udev_set_sync_support(int on);
 struct dm_task *libmp_dm_task_create(int task);
 int dm_drv_version (unsigned int * version);
-- 
2.17.2

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

* [PATCH v2 2/6] libmultipath: make sysfs_is_multipathed able to return wwid
  2020-05-19  4:57 [PATCH v2 0/6] multipath: path validation library prep work Benjamin Marzinski
  2020-05-19  4:57 ` [PATCH v2 1/6] libmultipath: make libmp_dm_init optional Benjamin Marzinski
@ 2020-05-19  4:57 ` Benjamin Marzinski
  2020-05-19  4:57 ` [PATCH v2 3/6] multipath: centralize validation code Benjamin Marzinski
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Benjamin Marzinski @ 2020-05-19  4:57 UTC (permalink / raw)
  To: Christophe Varoqui; +Cc: device-mapper development, Martin Wilck

sysfs_is_multipathed reads the wwid of the dm device holding a path to
check if its a multipath device.  Add code to optinally set pp->wwid to
that wwid.  This will be used by a future patch.

Reviewed-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/sysfs.c | 24 +++++++++++++++++++-----
 libmultipath/sysfs.h |  2 +-
 multipath/main.c     |  7 ++++---
 3 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c
index 62ec2ed7..12a82d95 100644
--- a/libmultipath/sysfs.c
+++ b/libmultipath/sysfs.c
@@ -295,7 +295,7 @@ static int select_dm_devs(const struct dirent *di)
 	return fnmatch("dm-*", di->d_name, FNM_FILE_NAME) == 0;
 }
 
-bool sysfs_is_multipathed(const struct path *pp)
+bool sysfs_is_multipathed(struct path *pp, bool set_wwid)
 {
 	char pathbuf[PATH_MAX];
 	struct scandir_result sr;
@@ -325,7 +325,7 @@ bool sysfs_is_multipathed(const struct path *pp)
 	for (i = 0; i < r && !found; i++) {
 		long fd;
 		int nr;
-		char uuid[6];
+		char uuid[WWID_SIZE + UUID_PREFIX_LEN];
 
 		if (safe_snprintf(pathbuf + n, sizeof(pathbuf) - n,
 				  "/%s/dm/uuid", di[i]->d_name))
@@ -339,12 +339,26 @@ bool sysfs_is_multipathed(const struct path *pp)
 
 		pthread_cleanup_push(close_fd, (void *)fd);
 		nr = read(fd, uuid, sizeof(uuid));
-		if (nr == sizeof(uuid) && !memcmp(uuid, "mpath-", sizeof(uuid)))
+		if (nr > (int)UUID_PREFIX_LEN &&
+		    !memcmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN))
 			found = true;
 		else if (nr < 0) {
-			condlog(1, "%s: error reading from %s: %s",
-				__func__, pathbuf, strerror(errno));
+			condlog(1, "%s: error reading from %s: %m",
+				__func__, pathbuf);
 		}
+		if (found && set_wwid) {
+			nr -= UUID_PREFIX_LEN;
+			memcpy(pp->wwid, uuid + UUID_PREFIX_LEN, nr);
+			if (nr == WWID_SIZE) {
+				condlog(4, "%s: overflow while reading from %s",
+					__func__, pathbuf);
+				pp->wwid[0] = '\0';
+			} else {
+				pp->wwid[nr] = '\0';
+				strchop(pp->wwid);
+			}
+                }
+
 		pthread_cleanup_pop(1);
 	}
 	pthread_cleanup_pop(1);
diff --git a/libmultipath/sysfs.h b/libmultipath/sysfs.h
index 9ae30b39..72b39ab2 100644
--- a/libmultipath/sysfs.h
+++ b/libmultipath/sysfs.h
@@ -14,5 +14,5 @@ ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name,
 				 unsigned char * value, size_t value_len);
 int sysfs_get_size (struct path *pp, unsigned long long * size);
 int sysfs_check_holders(char * check_devt, char * new_devt);
-bool sysfs_is_multipathed(const struct path *pp);
+bool sysfs_is_multipathed(struct path *pp, bool set_wwid);
 #endif
diff --git a/multipath/main.c b/multipath/main.c
index 78822ee1..f25b8693 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -640,7 +640,8 @@ configure (struct config *conf, enum mpath_cmds cmd,
 			 * Shortcut for find_multipaths smart:
 			 * Quick check if path is already multipathed.
 			 */
-			if (sysfs_is_multipathed(VECTOR_SLOT(pathvec, 0))) {
+			if (sysfs_is_multipathed(VECTOR_SLOT(pathvec, 0),
+						 false)) {
 				r = RTVL_YES;
 				goto print_valid;
 			}
@@ -749,8 +750,8 @@ configure (struct config *conf, enum mpath_cmds cmd,
 			/*
 			 * Check if we raced with multipathd
 			 */
-			r = sysfs_is_multipathed(VECTOR_SLOT(pathvec, 0)) ?
-				RTVL_YES : RTVL_NO;
+			r = sysfs_is_multipathed(VECTOR_SLOT(pathvec, 0),
+						 false) ? RTVL_YES : RTVL_NO;
 		}
 		goto print_valid;
 	}
-- 
2.17.2

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

* [PATCH v2 3/6] multipath: centralize validation code
  2020-05-19  4:57 [PATCH v2 0/6] multipath: path validation library prep work Benjamin Marzinski
  2020-05-19  4:57 ` [PATCH v2 1/6] libmultipath: make libmp_dm_init optional Benjamin Marzinski
  2020-05-19  4:57 ` [PATCH v2 2/6] libmultipath: make sysfs_is_multipathed able to return wwid Benjamin Marzinski
@ 2020-05-19  4:57 ` Benjamin Marzinski
  2020-05-19  4:57 ` [PATCH v2 4/6] Unit tests for is_path_valid() Benjamin Marzinski
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Benjamin Marzinski @ 2020-05-19  4:57 UTC (permalink / raw)
  To: Christophe Varoqui; +Cc: device-mapper development, Martin Wilck

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@redhat.com>
---
 libmultipath/Makefile    |   3 +-
 libmultipath/devmapper.c |  45 ++++++
 libmultipath/devmapper.h |   1 +
 libmultipath/structs.h   |  24 +---
 libmultipath/valid.c     | 118 ++++++++++++++++
 libmultipath/valid.h     |  42 ++++++
 libmultipath/wwids.c     |  10 +-
 multipath/main.c         | 296 +++++++++++++++++----------------------
 8 files changed, 344 insertions(+), 195 deletions(-)
 create mode 100644 libmultipath/valid.c
 create mode 100644 libmultipath/valid.h

diff --git a/libmultipath/Makefile b/libmultipath/Makefile
index ad690a49..436f479d 100644
--- a/libmultipath/Makefile
+++ b/libmultipath/Makefile
@@ -47,7 +47,8 @@ OBJS = memory.o parser.o vector.o devmapper.o callout.o \
 	switchgroup.o uxsock.o print.o alias.o log_pthread.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
+	io_err_stat.o dm-generic.o generic.o foreign.o nvme-lib.o \
+	valid.o
 
 all: $(LIBS)
 
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index 7ed494a1..27d52398 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -770,6 +770,51 @@ 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 = !!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,
 };
 
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..ce1c7cbf
--- /dev/null
+++ b/libmultipath/valid.h
@@ -0,0 +1,42 @@
+/*
+  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
+
+/*
+ * PATH_IS_VALID_NO_CHECK is returned when multipath should claim
+ * the path, regardless of whether is has been released to systemd
+ * already.
+ * PATH_IS_VALID is returned by is_path_valid, when the path is
+ * valid only if it hasn't been released to systemd already.
+ * PATH_IS_MAYBE_VALID is returned when the the path would be valid
+ * if other paths with the same wwid existed. It is up to the caller
+ * to check for these other paths.
+ */
+enum is_path_valid_result {
+	PATH_IS_ERROR = -1,
+	PATH_IS_NOT_VALID,
+	PATH_IS_VALID,
+	PATH_IS_VALID_NO_CHECK,
+	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 fab6fc8f..397f6e54 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);
 
diff --git a/multipath/main.c b/multipath/main.c
index f25b8693..5eac757a 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() */
 };
 
@@ -271,9 +268,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);
@@ -493,10 +487,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.
@@ -506,7 +501,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)
@@ -515,9 +510,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;
 }
 
 /*
@@ -550,7 +545,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
@@ -575,7 +569,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;
 	}
 
 	/*
@@ -583,14 +577,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) {
@@ -616,53 +606,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;
-			/* 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 */
-		}
 	}
 
 	/*
@@ -703,59 +646,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;
@@ -768,10 +658,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);
@@ -782,6 +668,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;
+
+	/* 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;
+	}
+
+	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;
@@ -863,32 +855,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[])
 {
@@ -972,7 +938,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;
 			break;
 		case 't':
 			r = dump_config(conf, NULL, NULL) ? RTVL_FAIL : RTVL_OK;
@@ -1070,15 +1040,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) {
@@ -1142,13 +1107,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();
 
-- 
2.17.2

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

* [PATCH v2 4/6] Unit tests for is_path_valid()
  2020-05-19  4:57 [PATCH v2 0/6] multipath: path validation library prep work Benjamin Marzinski
                   ` (2 preceding siblings ...)
  2020-05-19  4:57 ` [PATCH v2 3/6] multipath: centralize validation code Benjamin Marzinski
@ 2020-05-19  4:57 ` Benjamin Marzinski
  2020-05-19  4:57 ` [PATCH v2 5/6] libmultipath: simplify failed wwid code Benjamin Marzinski
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Benjamin Marzinski @ 2020-05-19  4:57 UTC (permalink / raw)
  To: Christophe Varoqui; +Cc: device-mapper development, Martin Wilck

Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 tests/Makefile |   4 +-
 tests/valid.c  | 486 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 489 insertions(+), 1 deletion(-)
 create mode 100644 tests/valid.c

diff --git a/tests/Makefile b/tests/Makefile
index 77ff3249..7fc261c3 100644
--- a/tests/Makefile
+++ b/tests/Makefile
@@ -13,7 +13,7 @@ CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) \
 LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka
 
 TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
-	 alias directio
+	 alias directio valid
 
 .SILENT: $(TESTS:%=%.o)
 .PRECIOUS: $(TESTS:%=%-test)
@@ -50,6 +50,8 @@ vpd-test_OBJDEPS :=  ../libmultipath/discovery.o
 vpd-test_LIBDEPS := -ludev -lpthread -ldl
 alias-test_TESTDEPS := test-log.o
 alias-test_LIBDEPS := -lpthread -ldl
+valid-test_OBJDEPS := ../libmultipath/valid.o
+valid-test_LIBDEPS := -ludev -lpthread -ldl
 ifneq ($(DIO_TEST_DEV),)
 directio-test_LIBDEPS := -laio
 endif
diff --git a/tests/valid.c b/tests/valid.c
new file mode 100644
index 00000000..693c72c5
--- /dev/null
+++ b/tests/valid.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2020 Benjamin Marzinski, Redhat
+ *
+ * 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/>.
+ *
+ */
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <cmocka.h>
+#include "globals.c"
+#include "util.h"
+#include "discovery.h"
+#include "wwids.h"
+#include "blacklist.h"
+#include "valid.h"
+
+int test_fd;
+struct udev_device {
+	int unused;
+} test_udev;
+
+bool __wrap_sysfs_is_multipathed(struct path *pp, bool set_wwid)
+{
+	bool is_multipathed = mock_type(bool);
+	assert_non_null(pp);
+	assert_int_not_equal(strlen(pp->dev), 0);
+	if (is_multipathed && set_wwid)
+		strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+	return is_multipathed;
+}
+
+int __wrap___mpath_connect(int nonblocking)
+{
+	bool connected = mock_type(bool);
+	assert_int_equal(nonblocking, 1);
+	if (connected)
+		return test_fd;
+	errno = mock_type(int);
+	return -1;
+}
+
+int __wrap_systemd_service_enabled(const char *dev)
+{
+	return (int)mock_type(bool);
+}
+
+/* There's no point in checking the return value here */
+int __wrap_mpath_disconnect(int fd)
+{
+	assert_int_equal(fd, test_fd);
+	return 0;
+}
+
+struct udev_device *__wrap_udev_device_new_from_subsystem_sysname(struct udev *udev, const char *subsystem, const char *sysname)
+{
+	bool passed = mock_type(bool);
+	assert_string_equal(sysname, mock_ptr_type(char *));
+	if (passed)
+		return &test_udev;
+	return NULL;
+}
+
+int __wrap_pathinfo(struct path *pp, struct config *conf, int mask)
+{
+	int ret = mock_type(int);
+	assert_string_equal(pp->dev, mock_ptr_type(char *));
+	assert_int_equal(mask, DI_SYSFS | DI_WWID | DI_BLACKLIST);
+	if (ret == PATHINFO_OK) {
+		pp->uid_attribute = "ID_TEST";
+		strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+	} else
+		memset(pp->wwid, 0, WWID_SIZE);
+	return ret;
+}
+
+int __wrap_filter_property(struct config *conf, struct udev_device *udev,
+			   int lvl, const char *uid_attribute)
+{
+	int ret = mock_type(int);
+	assert_string_equal(uid_attribute, "ID_TEST");
+	return ret;
+}
+
+int __wrap_is_failed_wwid(const char *wwid)
+{
+	int ret = mock_type(int);
+	assert_string_equal(wwid, mock_ptr_type(char *));
+	return ret;
+}
+
+int __wrap_check_wwids_file(char *wwid, int write_wwid)
+{
+	bool passed = mock_type(bool);
+	assert_int_equal(write_wwid, 0);
+	assert_string_equal(wwid, mock_ptr_type(char *));
+	if (passed)
+		return 0;
+	else
+		return -1;
+}
+
+int __wrap_dm_map_present_by_uuid(const char *uuid)
+{
+	int ret = mock_type(int);
+	assert_string_equal(uuid, mock_ptr_type(char *));
+	return ret;
+}
+
+enum {
+	STAGE_IS_MULTIPATHED,
+	STAGE_CHECK_MULTIPATHD,
+	STAGE_GET_UDEV_DEVICE,
+	STAGE_PATHINFO,
+	STAGE_FILTER_PROPERTY,
+	STAGE_IS_FAILED,
+	STAGE_CHECK_WWIDS,
+	STAGE_UUID_PRESENT,
+};
+
+enum {
+	CHECK_MPATHD_RUNNING,
+	CHECK_MPATHD_EAGAIN,
+	CHECK_MPATHD_ENABLED,
+	CHECK_MPATHD_SKIP,
+};
+
+/* setup the test to continue past the given stage in is_path_valid() */
+static void setup_passing(char *name, char *wwid, unsigned int check_multipathd,
+			  unsigned int stage)
+{
+	will_return(__wrap_sysfs_is_multipathed, false);
+	if (stage == STAGE_IS_MULTIPATHED)
+		return;
+	if (check_multipathd == CHECK_MPATHD_RUNNING)
+		will_return(__wrap___mpath_connect, true);
+	else if (check_multipathd == CHECK_MPATHD_EAGAIN) {
+		will_return(__wrap___mpath_connect, false);
+		will_return(__wrap___mpath_connect, EAGAIN);
+	} else if (check_multipathd == CHECK_MPATHD_ENABLED) {
+		will_return(__wrap___mpath_connect, false);
+		will_return(__wrap___mpath_connect, ECONNREFUSED);
+		will_return(__wrap_systemd_service_enabled, true);
+	}
+	/* nothing for CHECK_MPATHD_SKIP */
+	if (stage == STAGE_CHECK_MULTIPATHD)
+		return;
+	will_return(__wrap_udev_device_new_from_subsystem_sysname, true);
+	will_return(__wrap_udev_device_new_from_subsystem_sysname,
+		    name);
+	if (stage == STAGE_GET_UDEV_DEVICE)
+		return;
+	will_return(__wrap_pathinfo, PATHINFO_OK);
+	will_return(__wrap_pathinfo, name);
+	will_return(__wrap_pathinfo, wwid);
+	if (stage == STAGE_PATHINFO)
+		return;
+	will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_EXCEPT);
+	if (stage == STAGE_FILTER_PROPERTY)
+		return;
+	will_return(__wrap_is_failed_wwid, WWID_IS_NOT_FAILED);
+	will_return(__wrap_is_failed_wwid, wwid);
+	if (stage == STAGE_IS_FAILED)
+		return;
+	will_return(__wrap_check_wwids_file, false);
+	will_return(__wrap_check_wwids_file, wwid);
+	if (stage == STAGE_CHECK_WWIDS)
+		return;
+	will_return(__wrap_dm_map_present_by_uuid, 0);
+	will_return(__wrap_dm_map_present_by_uuid, wwid);
+}
+
+static void test_bad_arguments(void **state)
+{
+	struct path pp;
+	char too_long[FILE_NAME_SIZE + 1];
+
+	memset(&pp, 0, sizeof(pp));
+	/* test NULL pointers */
+	assert_int_equal(is_path_valid("test", &conf, NULL, true),
+			 PATH_IS_ERROR);
+	assert_int_equal(is_path_valid("test", NULL, &pp, true),
+			 PATH_IS_ERROR);
+	assert_int_equal(is_path_valid(NULL, &conf, &pp, true),
+			 PATH_IS_ERROR);
+	/* test undefined find_multipaths */
+	conf.find_multipaths = FIND_MULTIPATHS_UNDEF;
+	assert_int_equal(is_path_valid("test", &conf, &pp, true),
+			 PATH_IS_ERROR);
+	/* test name too long */
+	memset(too_long, 'x', sizeof(too_long));
+	too_long[sizeof(too_long) - 1] = '\0';
+	conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+	assert_int_equal(is_path_valid(too_long, &conf, &pp, true),
+			 PATH_IS_ERROR);
+}
+
+static void test_sysfs_is_multipathed(void **state)
+{
+	struct path pp;
+	char *name = "test";
+	char *wwid = "test_wwid";
+
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+	/* test for already existing multiapthed device */
+	will_return(__wrap_sysfs_is_multipathed, true);
+	will_return(__wrap_sysfs_is_multipathed, wwid);
+	assert_int_equal(is_path_valid(name, &conf, &pp, true),
+			 PATH_IS_VALID_NO_CHECK);
+	assert_string_equal(pp.dev, name);
+	assert_string_equal(pp.wwid, wwid);
+	/* test for wwid device with empty wwid */
+	will_return(__wrap_sysfs_is_multipathed, true);
+	will_return(__wrap_sysfs_is_multipathed, "");
+	assert_int_equal(is_path_valid(name, &conf, &pp, true),
+			 PATH_IS_ERROR);
+}
+
+static void test_check_multipathd(void **state)
+{
+	struct path pp;
+	char *name = "test";
+
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+	/* test failed check to see if multipathd is active */
+	will_return(__wrap_sysfs_is_multipathed, false);
+	will_return(__wrap___mpath_connect, false);
+	will_return(__wrap___mpath_connect, ECONNREFUSED);
+	will_return(__wrap_systemd_service_enabled, false);
+	assert_int_equal(is_path_valid(name, &conf, &pp, true),
+			 PATH_IS_NOT_VALID);
+	assert_string_equal(pp.dev, name);
+	/* test pass because service is enabled. fail getting udev */
+	memset(&pp, 0, sizeof(pp));
+	setup_passing(name, NULL, CHECK_MPATHD_ENABLED, STAGE_CHECK_MULTIPATHD);
+	will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+	will_return(__wrap_udev_device_new_from_subsystem_sysname,
+		    name);
+	assert_int_equal(is_path_valid(name, &conf, &pp, true),
+			 PATH_IS_ERROR);
+	assert_string_equal(pp.dev, name);
+	/* test pass because connect returned EAGAIN. fail getting udev */
+	setup_passing(name, NULL, CHECK_MPATHD_EAGAIN, STAGE_CHECK_MULTIPATHD);
+	will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+	will_return(__wrap_udev_device_new_from_subsystem_sysname,
+		    name);
+	assert_int_equal(is_path_valid(name, &conf, &pp, true),
+			 PATH_IS_ERROR);
+	/* test pass because connect succeeded. fail getting udev */
+	memset(&pp, 0, sizeof(pp));
+	setup_passing(name, NULL, CHECK_MPATHD_RUNNING, STAGE_CHECK_MULTIPATHD);
+	will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
+	will_return(__wrap_udev_device_new_from_subsystem_sysname,
+		    name);
+	assert_int_equal(is_path_valid(name, &conf, &pp, true),
+			 PATH_IS_ERROR);
+	assert_string_equal(pp.dev, name);
+}
+
+static void test_pathinfo(void **state)
+{
+	struct path pp;
+	char *name = "test";
+
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+	/* Test pathinfo blacklisting device */
+	setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+	will_return(__wrap_pathinfo, PATHINFO_SKIPPED);
+	will_return(__wrap_pathinfo, name);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_NOT_VALID);
+	assert_string_equal(pp.dev, name);
+	assert_ptr_equal(pp.udev, &test_udev);
+	/* Test pathinfo failing */
+	memset(&pp, 0, sizeof(pp));
+	setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+	will_return(__wrap_pathinfo, PATHINFO_FAILED);
+	will_return(__wrap_pathinfo, name);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_ERROR);
+	/* Test blank wwid */
+	memset(&pp, 0, sizeof(pp));
+	setup_passing(name, NULL, CHECK_MPATHD_SKIP, STAGE_GET_UDEV_DEVICE);
+	will_return(__wrap_pathinfo, PATHINFO_OK);
+	will_return(__wrap_pathinfo, name);
+	will_return(__wrap_pathinfo, "");
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_NOT_VALID);
+}
+
+static void test_filter_property(void **state)
+{
+	struct path pp;
+	char *name = "test";
+	char *wwid = "test-wwid";
+
+	/* test blacklist property */
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+	setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+	will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_NOT_VALID);
+	assert_ptr_equal(pp.udev, &test_udev);
+	assert_string_equal(pp.wwid, wwid);
+	/* test missing property */
+	memset(&pp, 0, sizeof(pp));
+	setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+	will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_MISSING);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_NOT_VALID);
+	/* test MATCH_NOTHING fail on is_failed_wwid */
+	memset(&pp, 0, sizeof(pp));
+	setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+	will_return(__wrap_filter_property, MATCH_NOTHING);
+	will_return(__wrap_is_failed_wwid, WWID_IS_FAILED);
+	will_return(__wrap_is_failed_wwid, wwid);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_NOT_VALID);
+}
+
+static void test_is_failed_wwid(void **state)
+{
+	struct path pp;
+	char *name = "test";
+	char *wwid = "test-wwid";
+
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+	/* Test wwid failed */
+	setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_FILTER_PROPERTY);
+	will_return(__wrap_is_failed_wwid, WWID_IS_FAILED);
+	will_return(__wrap_is_failed_wwid, wwid);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_NOT_VALID);
+	assert_string_equal(pp.dev, name);
+	assert_ptr_equal(pp.udev, &test_udev);
+	assert_string_equal(pp.wwid, wwid);
+	/* test is_failed_wwid error */
+	setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_FILTER_PROPERTY);
+	will_return(__wrap_is_failed_wwid, WWID_FAILED_ERROR);
+	will_return(__wrap_is_failed_wwid, wwid);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_ERROR);
+}
+
+static void test_greedy(void **state)
+{
+	struct path pp;
+	char *name = "test";
+	char *wwid = "test-wwid";
+
+	/* test greedy success with checking multipathd */
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_GREEDY;
+	setup_passing(name, wwid, CHECK_MPATHD_RUNNING, STAGE_IS_FAILED);
+	assert_int_equal(is_path_valid(name, &conf, &pp, true),
+			 PATH_IS_VALID);
+	assert_string_equal(pp.dev, name);
+	assert_ptr_equal(pp.udev, &test_udev);
+	assert_string_equal(pp.wwid, wwid);
+	/* test greedy success without checking multiapthd */
+	memset(&pp, 0, sizeof(pp));
+	setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_IS_FAILED);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_VALID);
+}
+
+static void test_check_wwids(void **state)
+{
+	struct path pp;
+	char *name = "test";
+	char *wwid = "test-wwid";
+
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+	setup_passing(name, wwid, CHECK_MPATHD_EAGAIN, STAGE_IS_FAILED);
+	will_return(__wrap_check_wwids_file, true);
+	will_return(__wrap_check_wwids_file, wwid);
+	assert_int_equal(is_path_valid(name, &conf, &pp, true),
+			 PATH_IS_VALID_NO_CHECK);
+	assert_string_equal(pp.dev, name);
+	assert_ptr_equal(pp.udev, &test_udev);
+	assert_string_equal(pp.wwid, wwid);
+}
+
+static void test_check_uuid_present(void **state)
+{
+	struct path pp;
+	char *name = "test";
+	char *wwid = "test-wwid";
+
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+	setup_passing(name, wwid, CHECK_MPATHD_ENABLED, STAGE_CHECK_WWIDS);
+	will_return(__wrap_dm_map_present_by_uuid, 1);
+	will_return(__wrap_dm_map_present_by_uuid, wwid);
+	assert_int_equal(is_path_valid(name, &conf, &pp, true),
+			 PATH_IS_VALID);
+	assert_string_equal(pp.dev, name);
+	assert_ptr_equal(pp.udev, &test_udev);
+	assert_string_equal(pp.wwid, wwid);
+}
+
+
+static void test_find_multipaths(void **state)
+{
+	struct path pp;
+	char *name = "test";
+	char *wwid = "test-wwid";
+
+	/* test find_multipaths = FIND_MULTIPATHS_STRICT */
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_STRICT;
+	setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_NOT_VALID);
+	assert_string_equal(pp.dev, name);
+	assert_ptr_equal(pp.udev, &test_udev);
+	assert_string_equal(pp.wwid, wwid);
+	/* test find_multipaths = FIND_MULTIPATHS_OFF */
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_OFF;
+	setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_NOT_VALID);
+	/* test find_multipaths = FIND_MULTIPATHS_ON */
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_ON;
+	setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_NOT_VALID);
+	/* test find_multipaths = FIND_MULTIPATHS_SMART */
+	memset(&pp, 0, sizeof(pp));
+	conf.find_multipaths = FIND_MULTIPATHS_SMART;
+	setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_UUID_PRESENT);
+	assert_int_equal(is_path_valid(name, &conf, &pp, false),
+			 PATH_IS_MAYBE_VALID);
+	assert_string_equal(pp.dev, name);
+	assert_ptr_equal(pp.udev, &test_udev);
+	assert_string_equal(pp.wwid, wwid);
+}
+
+int test_valid(void)
+{
+	const struct CMUnitTest tests[] = {
+		cmocka_unit_test(test_bad_arguments),
+		cmocka_unit_test(test_sysfs_is_multipathed),
+		cmocka_unit_test(test_check_multipathd),
+		cmocka_unit_test(test_pathinfo),
+		cmocka_unit_test(test_filter_property),
+		cmocka_unit_test(test_is_failed_wwid),
+		cmocka_unit_test(test_greedy),
+		cmocka_unit_test(test_check_wwids),
+		cmocka_unit_test(test_check_uuid_present),
+		cmocka_unit_test(test_find_multipaths),
+	};
+	return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+int main(void)
+{
+	int ret = 0;
+	ret += test_valid();
+	return ret;
+}
-- 
2.17.2

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

* [PATCH v2 5/6] libmultipath: simplify failed wwid code
  2020-05-19  4:57 [PATCH v2 0/6] multipath: path validation library prep work Benjamin Marzinski
                   ` (3 preceding siblings ...)
  2020-05-19  4:57 ` [PATCH v2 4/6] Unit tests for is_path_valid() Benjamin Marzinski
@ 2020-05-19  4:57 ` Benjamin Marzinski
  2020-05-19  4:57 ` [PATCH v2 6/6] libmultipath: use atomic linkat() in mark_failed_wwid() Benjamin Marzinski
  2020-05-19 12:55 ` [PATCH v2 0/6] multipath: path validation library prep work Martin Wilck
  6 siblings, 0 replies; 9+ messages in thread
From: Benjamin Marzinski @ 2020-05-19  4:57 UTC (permalink / raw)
  To: Christophe Varoqui; +Cc: device-mapper development, Martin Wilck

The (is|mark|unmark)_failed_wwid code is needlessly complicated.
Locking a file is necssary if multiple processes could otherwise be
writing to it at the same time. That is not the case with the
failed_wwids files. They can simply be empty files in a directory.  Even
with all the locking in place, two processes accessing or modifying a
file at the same time will still race. And even without the locking, if
two processes try to access or modify a file at the same time, they will
both see a reasonable result, and will leave the files in a valid state
afterwards.

Instead of doing all the locking work (which made it necessary to write
a file, even just to check if a file existed), simply check for files
with lstat(), create them with open(), and remove them with unlink().

Reviewed-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/wwids.c | 131 ++++++++++++++++++-------------------------
 1 file changed, 56 insertions(+), 75 deletions(-)

diff --git a/libmultipath/wwids.c b/libmultipath/wwids.c
index 397f6e54..da55924b 100644
--- a/libmultipath/wwids.c
+++ b/libmultipath/wwids.c
@@ -6,6 +6,7 @@
 #include <stdio.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 
 #include "util.h"
 #include "checkers.h"
@@ -348,113 +349,93 @@ remember_wwid(char *wwid)
 }
 
 static const char shm_dir[] = MULTIPATH_SHM_BASE "failed_wwids";
-static const char shm_lock[] = ".lock";
-static const char shm_header[] = "multipath shm lock file, don't edit";
-static char _shm_lock_path[sizeof(shm_dir)+sizeof(shm_lock)];
-static const char *shm_lock_path = &_shm_lock_path[0];
 
-static void init_shm_paths(void)
+static void print_failed_wwid_result(const char * msg, const char *wwid, int r)
 {
-	snprintf(_shm_lock_path, sizeof(_shm_lock_path),
-		 "%s/%s", shm_dir, shm_lock);
+	switch(r) {
+	case WWID_FAILED_ERROR:
+		condlog(1, "%s: %s: %m", msg, wwid);
+		return;
+	case WWID_IS_FAILED:
+	case WWID_IS_NOT_FAILED:
+		condlog(4, "%s: %s is %s", msg, wwid,
+			r == WWID_IS_FAILED ? "failed" : "good");
+		return;
+	case WWID_FAILED_CHANGED:
+		condlog(3, "%s: %s", msg, wwid);
+	}
 }
 
-static pthread_once_t shm_path_once = PTHREAD_ONCE_INIT;
-
-static int multipath_shm_open(bool rw)
+int is_failed_wwid(const char *wwid)
 {
-	int fd;
-	int can_write;
-
-	pthread_once(&shm_path_once, init_shm_paths);
-	fd = open_file(shm_lock_path, &can_write, shm_header);
+	struct stat st;
+	char path[PATH_MAX];
+	int r;
 
-	if (fd >= 0 && rw && !can_write) {
-		close(fd);
-		condlog(1, "failed to open %s for writing", shm_dir);
+	if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
+		condlog(1, "%s: path name overflow", __func__);
 		return -1;
 	}
 
-	return fd;
-}
-
-static void multipath_shm_close(void *arg)
-{
-	long fd = (long)arg;
+	if (lstat(path, &st) == 0)
+		r = WWID_IS_FAILED;
+	else if (errno == ENOENT)
+		r = WWID_IS_NOT_FAILED;
+	else
+		r = WWID_FAILED_ERROR;
 
-	close(fd);
-	unlink(shm_lock_path);
+	print_failed_wwid_result("is_failed", wwid, r);
+	return r;
 }
 
-static int _failed_wwid_op(const char *wwid, bool rw,
-			   int (*func)(const char *), const char *msg)
+int mark_failed_wwid(const char *wwid)
 {
 	char path[PATH_MAX];
-	long lockfd;
-	int r = -1;
+	int r, fd;
 
 	if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
 		condlog(1, "%s: path name overflow", __func__);
 		return -1;
 	}
-
-	lockfd = multipath_shm_open(rw);
-	if (lockfd == -1)
+	if (ensure_directories_exist(path, 0700) < 0) {
+		condlog(1, "%s: can't setup directories", __func__);
 		return -1;
+	}
 
-	pthread_cleanup_push(multipath_shm_close, (void *)lockfd);
-	r = func(path);
-	pthread_cleanup_pop(1);
-
-	if (r == WWID_FAILED_ERROR)
-		condlog(1, "%s: %s: %s", msg, wwid, strerror(errno));
-	else if (r == WWID_FAILED_CHANGED)
-		condlog(3, "%s: %s", msg, wwid);
-	else if (!rw)
-		condlog(4, "%s: %s is %s", msg, wwid,
-			r == WWID_IS_FAILED ? "failed" : "good");
+	fd = open(path, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR);
+	if (fd >= 0) {
+		close(fd);
+		r = WWID_FAILED_CHANGED;
+	} else if (errno == EEXIST)
+		r = WWID_FAILED_UNCHANGED;
+	else
+		r = WWID_FAILED_ERROR;
 
+	print_failed_wwid_result("mark_failed", wwid, r);
 	return r;
 }
 
-static int _is_failed(const char *path)
+int unmark_failed_wwid(const char *wwid)
 {
-	struct stat st;
+	char path[PATH_MAX];
+	int r;
 
-	if (lstat(path, &st) == 0)
-		return WWID_IS_FAILED;
+	if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
+		condlog(1, "%s: path name overflow", __func__);
+		return -1;
+	}
+
+	if (unlink(path) == 0)
+		r = WWID_FAILED_CHANGED;
 	else if (errno == ENOENT)
-		return WWID_IS_NOT_FAILED;
+		r = WWID_FAILED_UNCHANGED;
 	else
-		return WWID_FAILED_ERROR;
-}
-
-static int _mark_failed(const char *path)
-{
-	/* Called from _failed_wwid_op: we know that shm_lock_path exists */
-	if (_is_failed(path) == WWID_IS_FAILED)
-		return WWID_FAILED_UNCHANGED;
-	return (link(shm_lock_path, path) == 0 ? WWID_FAILED_CHANGED :
-		WWID_FAILED_ERROR);
-}
+		r = WWID_FAILED_ERROR;
 
-static int _unmark_failed(const char *path)
-{
-	if (_is_failed(path) == WWID_IS_NOT_FAILED)
-		return WWID_FAILED_UNCHANGED;
-	return (unlink(path) == 0 ? WWID_FAILED_CHANGED : WWID_FAILED_ERROR);
-}
-
-#define declare_failed_wwid_op(op, rw) \
-int op ## _wwid(const char *wwid) \
-{ \
-	return _failed_wwid_op(wwid, (rw), _ ## op, #op); \
+	print_failed_wwid_result("unmark_failed", wwid, r);
+	return r;
 }
 
-declare_failed_wwid_op(is_failed, false)
-declare_failed_wwid_op(mark_failed, true)
-declare_failed_wwid_op(unmark_failed, true)
-
 int remember_cmdline_wwid(void)
 {
 	FILE *f = NULL;
-- 
2.17.2

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

* [PATCH v2 6/6] libmultipath: use atomic linkat() in mark_failed_wwid()
  2020-05-19  4:57 [PATCH v2 0/6] multipath: path validation library prep work Benjamin Marzinski
                   ` (4 preceding siblings ...)
  2020-05-19  4:57 ` [PATCH v2 5/6] libmultipath: simplify failed wwid code Benjamin Marzinski
@ 2020-05-19  4:57 ` Benjamin Marzinski
  2020-05-19 12:55 ` [PATCH v2 0/6] multipath: path validation library prep work Martin Wilck
  6 siblings, 0 replies; 9+ messages in thread
From: Benjamin Marzinski @ 2020-05-19  4:57 UTC (permalink / raw)
  To: Christophe Varoqui; +Cc: device-mapper development, Martin Wilck, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

This keeps (almost) the simplicity of the previous patch, while
making sure that the return value of mark_failed_wwid()
(WWID_FAILED_CHANGED vs. WWID_FAILED_UNCHANGED) is correct, even
if several processes access this WWID at the same time.

Signed-off-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/wwids.c | 42 +++++++++++++++++++++++++++++-------------
 1 file changed, 29 insertions(+), 13 deletions(-)

diff --git a/libmultipath/wwids.c b/libmultipath/wwids.c
index da55924b..c7a16636 100644
--- a/libmultipath/wwids.c
+++ b/libmultipath/wwids.c
@@ -374,7 +374,7 @@ int is_failed_wwid(const char *wwid)
 
 	if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
 		condlog(1, "%s: path name overflow", __func__);
-		return -1;
+		return WWID_FAILED_ERROR;
 	}
 
 	if (lstat(path, &st) == 0)
@@ -390,27 +390,43 @@ int is_failed_wwid(const char *wwid)
 
 int mark_failed_wwid(const char *wwid)
 {
-	char path[PATH_MAX];
-	int r, fd;
+	char tmpfile[WWID_SIZE + 2 * sizeof(long) + 1];
+	int r = WWID_FAILED_ERROR, fd, dfd;
 
-	if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
-		condlog(1, "%s: path name overflow", __func__);
-		return -1;
+	dfd = open(shm_dir, O_RDONLY|O_DIRECTORY);
+	if (dfd == -1 && errno == ENOENT) {
+		char path[sizeof(shm_dir) + 2];
+
+		/* arg for ensure_directories_exist() must not end with "/" */
+		safe_sprintf(path, "%s/_", shm_dir);
+		ensure_directories_exist(path, 0700);
+		dfd = open(shm_dir, O_RDONLY|O_DIRECTORY);
 	}
-	if (ensure_directories_exist(path, 0700) < 0) {
-		condlog(1, "%s: can't setup directories", __func__);
-		return -1;
+	if (dfd == -1) {
+		condlog(1, "%s: can't setup %s: %m", __func__, shm_dir);
+		return WWID_FAILED_ERROR;
 	}
 
-	fd = open(path, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR);
-	if (fd >= 0) {
+	safe_sprintf(tmpfile, "%s.%lx", wwid, (long)getpid());
+	fd = openat(dfd, tmpfile, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR);
+	if (fd >= 0)
 		close(fd);
+	else
+		goto out_closedir;
+
+	if (linkat(dfd, tmpfile, dfd, wwid, 0) == 0)
 		r = WWID_FAILED_CHANGED;
-	} else if (errno == EEXIST)
+	else if (errno == EEXIST)
 		r = WWID_FAILED_UNCHANGED;
 	else
 		r = WWID_FAILED_ERROR;
 
+	if (unlinkat(dfd, tmpfile, 0) == -1)
+		condlog(2, "%s: failed to unlink %s/%s: %m",
+			__func__, shm_dir, tmpfile);
+
+out_closedir:
+	close(dfd);
 	print_failed_wwid_result("mark_failed", wwid, r);
 	return r;
 }
@@ -422,7 +438,7 @@ int unmark_failed_wwid(const char *wwid)
 
 	if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) {
 		condlog(1, "%s: path name overflow", __func__);
-		return -1;
+		return WWID_FAILED_ERROR;
 	}
 
 	if (unlink(path) == 0)
-- 
2.17.2

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

* Re: [PATCH v2 0/6] multipath: path validation library prep work
  2020-05-19  4:57 [PATCH v2 0/6] multipath: path validation library prep work Benjamin Marzinski
                   ` (5 preceding siblings ...)
  2020-05-19  4:57 ` [PATCH v2 6/6] libmultipath: use atomic linkat() in mark_failed_wwid() Benjamin Marzinski
@ 2020-05-19 12:55 ` Martin Wilck
  2020-05-19 14:23   ` Benjamin Marzinski
  6 siblings, 1 reply; 9+ messages in thread
From: Martin Wilck @ 2020-05-19 12:55 UTC (permalink / raw)
  To: Benjamin Marzinski, Christophe Varoqui; +Cc: device-mapper development

On Mon, 2020-05-18 at 23:57 -0500, Benjamin Marzinski wrote:
> I've been playing around with the SID code more and I've decided to
> hold
> off on submitting the library until I have it working with the SID
> multipath module better. Instead, I've pulled out the common code
> thatremember_cmdline_wwid
> multipath -u/-c and the library can use, and put it into
> libmultipath.
> 
> I've also removed some of the ordering differences between the
> existing
> code and my new code.  Right now, the only difference is that if a
> path
> is currently multipathed, it will always be claimed as a valid path.
> 
> Patches 0001 & 0002 are the same as in my "RFC PATCH v2" set, and
> patch
> 0005 is the same as my "libmultipath: simplify failed wwid code"
> patch.
> 
> Only patches 0003 and 0004 haven't been posted before.
> 
> Changes from v1:
> 0003: Minor fixes suggested by Martin Wilck
> 0004: Fixed typo, added tests for filter_property() and switched some
>       tests to pass the check_multipathd code in various ways,
> instead
>       of skipping it, as suggested by Martin Wilck
> 

This set (v2) doesn't apply cleanly to upstream, neither with or
without my late patches. It's been generated against a tree that
included "Make multipath add wwids from kernel cmdline mpath.wwids with
-A" (https://patchwork.kernel.org/patch/4445691/). From my series, it's
missing "libmultipath: move libsg into libmultipath".

Apart from that, for the series:

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

Martin

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

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

* Re: [PATCH v2 0/6] multipath: path validation library prep work
  2020-05-19 12:55 ` [PATCH v2 0/6] multipath: path validation library prep work Martin Wilck
@ 2020-05-19 14:23   ` Benjamin Marzinski
  0 siblings, 0 replies; 9+ messages in thread
From: Benjamin Marzinski @ 2020-05-19 14:23 UTC (permalink / raw)
  To: Martin Wilck; +Cc: device-mapper development

On Tue, May 19, 2020 at 02:55:05PM +0200, Martin Wilck wrote:
> On Mon, 2020-05-18 at 23:57 -0500, Benjamin Marzinski wrote:
> > I've been playing around with the SID code more and I've decided to
> > hold
> > off on submitting the library until I have it working with the SID
> > multipath module better. Instead, I've pulled out the common code
> > thatremember_cmdline_wwid
> > multipath -u/-c and the library can use, and put it into
> > libmultipath.
> > 
> > I've also removed some of the ordering differences between the
> > existing
> > code and my new code.  Right now, the only difference is that if a
> > path
> > is currently multipathed, it will always be claimed as a valid path.
> > 
> > Patches 0001 & 0002 are the same as in my "RFC PATCH v2" set, and
> > patch
> > 0005 is the same as my "libmultipath: simplify failed wwid code"
> > patch.
> > 
> > Only patches 0003 and 0004 haven't been posted before.
> > 
> > Changes from v1:
> > 0003: Minor fixes suggested by Martin Wilck
> > 0004: Fixed typo, added tests for filter_property() and switched some
> >       tests to pass the check_multipathd code in various ways,
> > instead
> >       of skipping it, as suggested by Martin Wilck
> > 
> 
> This set (v2) doesn't apply cleanly to upstream, neither with or
> without my late patches. It's been generated against a tree that
> included "Make multipath add wwids from kernel cmdline mpath.wwids with
> -A" (https://patchwork.kernel.org/patch/4445691/). From my series, it's
> missing "libmultipath: move libsg into libmultipath".
> 
> Apart from that, for the series:
> 
> Reviewed-by: Martin Wilck <mwilck@suse.com>

Whoops. I accidentally sent patches from the branch that's built on top
of my latest RHEL code. Resending.

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

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

end of thread, other threads:[~2020-05-19 14:23 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-19  4:57 [PATCH v2 0/6] multipath: path validation library prep work Benjamin Marzinski
2020-05-19  4:57 ` [PATCH v2 1/6] libmultipath: make libmp_dm_init optional Benjamin Marzinski
2020-05-19  4:57 ` [PATCH v2 2/6] libmultipath: make sysfs_is_multipathed able to return wwid Benjamin Marzinski
2020-05-19  4:57 ` [PATCH v2 3/6] multipath: centralize validation code Benjamin Marzinski
2020-05-19  4:57 ` [PATCH v2 4/6] Unit tests for is_path_valid() Benjamin Marzinski
2020-05-19  4:57 ` [PATCH v2 5/6] libmultipath: simplify failed wwid code Benjamin Marzinski
2020-05-19  4:57 ` [PATCH v2 6/6] libmultipath: use atomic linkat() in mark_failed_wwid() Benjamin Marzinski
2020-05-19 12:55 ` [PATCH v2 0/6] multipath: path validation library prep work Martin Wilck
2020-05-19 14:23   ` Benjamin Marzinski

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.