All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 00/74] multipath-tools series part V: removed path handling
@ 2020-08-12 11:35 mwilck
  2020-08-12 11:35 ` [PATCH v2 55/74] libmultipath: add uninitialize_path() mwilck
                   ` (7 more replies)
  0 siblings, 8 replies; 15+ messages in thread
From: mwilck @ 2020-08-12 11:35 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Hi Christophe, hi Ben,

This is part V of a larger patch series for multipath-tools I've been preparing.
It's based on the previously submitted part IV.

The full series will also be available here:
https://github.com/mwilck/multipath-tools/tree/ups/submit-2008
This part is tagged "submit-200812-5".

The series addresses several issues with missing, unintialized, and removed
paths. The motivation was that I strongly dislike the side effects of
disassemble_map(), adding paths and setting WWIDs while parsing map
parameters. IMO this has always been a layering violation. This patch set adds
a new function dedicated to handling paths which have not been detected via
udev but are present as members of dm maps. That makes it much easier to
track and improve the logic applied when such devices are detected.

I believe that the new logic will also fix the issue recently reported by
Chongyun ("libmultipath/dmparser: add missing path with good status when sync
state with dm kernel"). At least, the approach is very similar, although
the call to pathinfo() for new devices now happens in update_pathvec_from_dm()
rather than in disassemble_map().

The patch set also gets rid of the "is_daemon" argument for disassemble_map(),
causing semantics in daemon and non-daemon mode. The reason for this parameter
goes way back into history; it was avoiding to re-add manually removed paths
because they were still showing up in maps. But OTOH we must add missing
devices which we've failed to detect. The patch set handles this by tracking
the state of "being removed" as a new init state, and only actually removing
the paths from pathvec when they don't show up in maps any more.

Changes v1 -> v2, as suggested by Ben Marzinski:

 * 55: "libmultipath: add uninitialize_path()"
 * 58: "libmultipath: verify_paths(): don't delete paths from pathvec"
    - context changes
 * 61: "libmultipath: adopt_paths(): skip removed paths"
    - set pp->mpp before checking for INIT_REMOVED
 * 63: "multipathd: deal with INIT_REMOVED during path addition"
    - BUG message if path not mpp member
    - set pp->tick to 1 to make sure paths are quickly reinstated
    - remove "was_removed" variable
 * 64: "multipathd: check_path(): set retrigger_delay if necessary"
    - drop, replace with "libmultipath: orphan_paths(): avoid BUG message"
 * 65: libmultipath: add update_pathvec_from_dm()
    - add comment about pp->udev being initialized
    - discard paths with pathinfo() errors
    - set must_reload if pathgroup was removed
 * 66: "libmultipath: update_pathvec_from_dm: handle pp->mpp mismatch"
    - handle the two failure cases "pp->mpp points to different map" and 
      "wrong wwid" differently. In the latter case, use orphan_path()
 * 71: "multipath: use update_pathvec_from_dm()"
    - use DI_NOIO for CMD_LIST_SHORT. In update_pathvec_from_dm, mask
      out DI_NOIO when checking existing paths

NOTE: In my v1 submission, I made a mistake when sending part V, so that
*patch number 54 is present twice* in the full series.
I deliberately didn't correct that this time, to preserve numbering.

Regards
Martin

Martin Wilck (21):
  libmultipath: protect use of pp->udev
  libmultipath: add uninitialize_path()
  multipath-tools: introduce INIT_REMOVED state
  libmultipath: update_mpp_paths(): handle INIT_REMOVED
  libmultipath: verify_paths(): don't delete paths from pathvec
  libmultipath: sync_paths(): handle INIT_REMOVED
  libmultipath: orphan_paths(): delete paths in INIT_REMOVED state
  libmultipath: adopt_paths(): skip removed paths
  multipathd: ev_remove_path(): use INIT_REMOVED
  multipathd: deal with INIT_REMOVED during path addition
  libmultipath: orphan_paths(): avoid BUG message
  libmultipath: add update_pathvec_from_dm()
  libmultipath: update_pathvec_from_dm: handle pp->mpp mismatch
  libmultipath: disassemble_map(): always search paths by dev_t
  libmultipath: disassemble_map(): require non-NULL pathvec
  libmultipath: disassemble_map(): get rid of "is_daemon" argument
  libmultipath: disassemble_map(): do not change pathvec and WWIDs
  multipath: use update_pathvec_from_dm()
  libmpathpersist: use update_pathvec_from_dm()
  libmultipath: decrease loglevel in store_path()
  libmultipath: dmparser: constify function arguments

 libmpathpersist/mpath_persist.c       |  56 +----
 libmultipath/configure.c              |   2 +-
 libmultipath/discovery.c              |  15 +-
 libmultipath/dmparser.c               |  70 ++----
 libmultipath/dmparser.h               |   4 +-
 libmultipath/prioritizers/alua_rtpg.c |   6 +-
 libmultipath/structs.c                |  21 +-
 libmultipath/structs.h                |   6 +
 libmultipath/structs_vec.c            | 293 +++++++++++++++++++++++---
 libmultipath/structs_vec.h            |  11 +-
 multipath/main.c                      |  72 +------
 multipathd/cli_handlers.c             |  54 ++++-
 multipathd/main.c                     | 112 ++++++++--
 13 files changed, 479 insertions(+), 243 deletions(-)

-- 
2.28.0

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

* [PATCH v2 55/74] libmultipath: add uninitialize_path()
  2020-08-12 11:35 [PATCH v2 00/74] multipath-tools series part V: removed path handling mwilck
@ 2020-08-12 11:35 ` mwilck
  2020-08-12 11:35 ` [PATCH v2 58/74] libmultipath: verify_paths(): don't delete paths from pathvec mwilck
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: mwilck @ 2020-08-12 11:35 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

This helper clears all fields of struct path (except pp->udev) that must be
re-ininitialized if the path ever is to be used again.

Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs.c     | 19 +++++++++++++++++--
 libmultipath/structs.h     |  1 +
 libmultipath/structs_vec.c |  9 +--------
 3 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index d227ec0..28bf8eb 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -114,19 +114,34 @@ alloc_path (void)
 }
 
 void
-free_path (struct path * pp)
+uninitialize_path(struct path *pp)
 {
 	if (!pp)
 		return;
 
+	pp->dmstate = PSTATE_UNDEF;
+	pp->uid_attribute = NULL;
+	pp->getuid = NULL;
+
 	if (checker_selected(&pp->checker))
 		checker_put(&pp->checker);
 
 	if (prio_selected(&pp->prio))
 		prio_put(&pp->prio);
 
-	if (pp->fd >= 0)
+	if (pp->fd >= 0) {
 		close(pp->fd);
+		pp->fd = -1;
+	}
+}
+
+void
+free_path (struct path * pp)
+{
+	if (!pp)
+		return;
+
+	uninitialize_path(pp);
 
 	if (pp->udev) {
 		udev_device_unref(pp->udev);
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 917e408..5f45f21 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -416,6 +416,7 @@ struct host_group {
 struct path * alloc_path (void);
 struct pathgroup * alloc_pathgroup (void);
 struct multipath * alloc_multipath (void);
+void uninitialize_path(struct path *pp);
 void free_path (struct path *);
 void free_pathvec (vector vec, enum free_path_mode free_paths);
 void free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths);
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 2b7c154..ea84a20 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -103,14 +103,7 @@ void orphan_path(struct path *pp, const char *reason)
 		pp->mpp->hwe = NULL;
 	}
 	pp->mpp = NULL;
-	pp->dmstate = PSTATE_UNDEF;
-	pp->uid_attribute = NULL;
-	pp->getuid = NULL;
-	prio_put(&pp->prio);
-	checker_put(&pp->checker);
-	if (pp->fd >= 0)
-		close(pp->fd);
-	pp->fd = -1;
+	uninitialize_path(pp);
 }
 
 void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
-- 
2.28.0

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

* [PATCH v2 58/74] libmultipath: verify_paths(): don't delete paths from pathvec
  2020-08-12 11:35 [PATCH v2 00/74] multipath-tools series part V: removed path handling mwilck
  2020-08-12 11:35 ` [PATCH v2 55/74] libmultipath: add uninitialize_path() mwilck
@ 2020-08-12 11:35 ` mwilck
  2020-08-12 11:35 ` [PATCH v2 61/74] libmultipath: adopt_paths(): skip removed paths mwilck
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 15+ messages in thread
From: mwilck @ 2020-08-12 11:35 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

If we encounter a non-existing device verify_paths(), just set
it to INIT_REMOVED state. Actual path deletion is postponed until
we don't see that path in the kernel map any more.

This allows us to get rid of the "pathvec" argument to this function.

Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/configure.c   |  2 +-
 libmultipath/structs_vec.c | 16 +++++++++-------
 libmultipath/structs_vec.h |  2 +-
 multipathd/main.c          |  4 ++--
 4 files changed, 13 insertions(+), 11 deletions(-)

diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index 5ecfae8..cc54818 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -1196,7 +1196,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
 				set_bit_in_bitfield(i, size_mismatch_seen);
 			}
 		}
-		verify_paths(mpp, vecs);
+		verify_paths(mpp);
 
 		params[0] = '\0';
 		if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index ee1004d..e0a84d4 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -499,11 +499,11 @@ out:
 	return NULL;
 }
 
-int verify_paths(struct multipath *mpp, struct vectors *vecs)
+int verify_paths(struct multipath *mpp)
 {
 	struct path * pp;
 	int count = 0;
-	int i, j;
+	int i;
 
 	if (!mpp)
 		return 0;
@@ -518,7 +518,7 @@ int verify_paths(struct multipath *mpp, struct vectors *vecs)
 				condlog(1, "%s: removing valid path %s in state %d",
 					mpp->alias, pp->dev, pp->state);
 			} else {
-				condlog(3, "%s: failed to access path %s",
+				condlog(2, "%s: failed to access path %s",
 					mpp->alias, pp->dev);
 			}
 			count++;
@@ -531,10 +531,12 @@ int verify_paths(struct multipath *mpp, struct vectors *vecs)
 			 */
 			if (mpp->hwe == pp->hwe)
 				mpp->hwe = NULL;
-			if ((j = find_slot(vecs->pathvec,
-					   (void *)pp)) != -1)
-				vector_del_slot(vecs->pathvec, j);
-			free_path(pp);
+			/*
+			 * Don't delete path from pathvec yet. We'll do this
+			 * after the path has been removed from the map, in
+			 * sync_paths().
+			 */
+			set_path_removed(pp);
 		} else {
 			condlog(4, "%s: verified path %s dev_t %s",
 				mpp->alias, pp->dev, pp->dev_t);
diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h
index 7f6dece..d7dfe32 100644
--- a/libmultipath/structs_vec.h
+++ b/libmultipath/structs_vec.h
@@ -20,7 +20,7 @@ void orphan_paths(vector pathvec, struct multipath *mpp,
 void orphan_path (struct path * pp, const char *reason);
 void set_path_removed(struct path *pp);
 
-int verify_paths(struct multipath * mpp, struct vectors * vecs);
+int verify_paths(struct multipath *mpp);
 int update_mpp_paths(struct multipath * mpp, vector pathvec);
 int update_multipath_strings (struct multipath *mpp, vector pathvec,
 			      int is_daemon);
diff --git a/multipathd/main.c b/multipathd/main.c
index 2c5b982..0209db4 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -492,7 +492,7 @@ retry:
 		retries = -1;
 		goto fail;
 	}
-	verify_paths(mpp, vecs);
+	verify_paths(mpp);
 	mpp->action = ACT_RELOAD;
 
 	if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
@@ -960,7 +960,7 @@ rescan:
 		    find_slot(vecs->pathvec, pp) == -1)
 			goto fail; /* leave path added to pathvec */
 
-		verify_paths(mpp, vecs);
+		verify_paths(mpp);
 		mpp->action = ACT_RELOAD;
 	} else {
 		if (!should_multipath(pp, vecs->pathvec, vecs->mpvec)) {
-- 
2.28.0

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

* [PATCH v2 61/74] libmultipath: adopt_paths(): skip removed paths
  2020-08-12 11:35 [PATCH v2 00/74] multipath-tools series part V: removed path handling mwilck
  2020-08-12 11:35 ` [PATCH v2 55/74] libmultipath: add uninitialize_path() mwilck
  2020-08-12 11:35 ` [PATCH v2 58/74] libmultipath: verify_paths(): don't delete paths from pathvec mwilck
@ 2020-08-12 11:35 ` mwilck
  2020-08-14  1:37   ` Benjamin Marzinski
  2020-08-12 11:35 ` [PATCH v2 63/74] multipathd: deal with INIT_REMOVED during path addition mwilck
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: mwilck @ 2020-08-12 11:35 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

If we don't do this, pathinfo() will fail on these paths, causing
adopt_paths() to fail.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs_vec.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index f139fc0..ba3165a 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -79,9 +79,11 @@ int adopt_paths(vector pathvec, struct multipath *mpp)
 					pp->dev, mpp->alias);
 				continue;
 			}
+			pp->mpp = mpp;
+			if (pp->initialized == INIT_REMOVED)
+				continue;
 			condlog(3, "%s: ownership set to %s",
 				pp->dev, mpp->alias);
-			pp->mpp = mpp;
 
 			if (!mpp->paths && !(mpp->paths = vector_alloc()))
 				return 1;
-- 
2.28.0

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

* [PATCH v2 63/74] multipathd: deal with INIT_REMOVED during path addition
  2020-08-12 11:35 [PATCH v2 00/74] multipath-tools series part V: removed path handling mwilck
                   ` (2 preceding siblings ...)
  2020-08-12 11:35 ` [PATCH v2 61/74] libmultipath: adopt_paths(): skip removed paths mwilck
@ 2020-08-12 11:35 ` mwilck
  2020-08-14  2:25   ` Benjamin Marzinski
  2020-08-12 11:35 ` [PATCH v2 64/74] libmultipath: orphan_paths(): avoid BUG message mwilck
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: mwilck @ 2020-08-12 11:35 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

With the introduction of INIT_REMOVED, we have to deal with the situation
when a path is re-added in this state. This enables us to detect the
situation where a path is added while still part of a map after a failed
removal, which we couldn't before. Dealing with this correctly requires
some additional logic. There's a good case (re-added path is still mapped
by a map with matching WWID) and a bad case (non-matching WWID).

The logic is very similar in uev_add_path() and cli_add_path().

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 multipathd/cli_handlers.c | 54 +++++++++++++++++++++++++++++++++++--
 multipathd/main.c         | 57 ++++++++++++++++++++++++++++++++++++---
 2 files changed, 105 insertions(+), 6 deletions(-)

diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 782bb00..c60182f 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -713,11 +713,61 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
 		goto blacklisted;
 
 	pp = find_path_by_dev(vecs->pathvec, param);
-	if (pp) {
+	if (pp && pp->initialized != INIT_REMOVED) {
 		condlog(2, "%s: path already in pathvec", param);
 		if (pp->mpp)
 			return 0;
-	} else {
+	} else if (pp) {
+		/* Trying to add a path in INIT_REMOVED state */
+		struct multipath *prev_mpp;
+
+		prev_mpp = pp->mpp;
+		if (prev_mpp == NULL)
+			condlog(0, "Bug: %s was in INIT_REMOVED state without being a multipath member",
+				pp->dev);
+		pp->mpp = NULL;
+		pp->initialized = INIT_NEW;
+		pp->wwid[0] = '\0';
+		conf = get_multipath_config();
+		pthread_cleanup_push(put_multipath_config, conf);
+		r = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST);
+		pthread_cleanup_pop(1);
+
+		if (prev_mpp) {
+			/* Similar logic as in uev_add_path() */
+			pp->mpp = prev_mpp;
+			if (r == PATHINFO_OK &&
+			    !strncmp(prev_mpp->wwid, pp->wwid, WWID_SIZE)) {
+				condlog(2, "%s: path re-added to %s", pp->dev,
+					pp->mpp->alias);
+				/* Have the checker reinstate this path asap */
+				pp->tick = 1;
+				return 0;
+			} else if (!ev_remove_path(pp, vecs, true))
+				/* Path removed in ev_remove_path() */
+				pp = NULL;
+			else {
+				/* Init state is now INIT_REMOVED again */
+				pp->dmstate = PSTATE_FAILED;
+				dm_fail_path(pp->mpp->alias, pp->dev_t);
+				condlog(1, "%s: failed to re-add path still mapped in %s",
+					pp->dev, pp->mpp->alias);
+				return 1;
+			}
+		} else {
+			switch (r) {
+			case PATHINFO_SKIPPED:
+				goto blacklisted;
+			case PATHINFO_OK:
+				break;
+			default:
+				condlog(0, "%s: failed to get pathinfo", param);
+				return 1;
+			}
+		}
+	}
+
+	if (!pp) {
 		struct udev_device *udevice;
 
 		udevice = udev_device_new_from_subsystem_sysname(udev,
diff --git a/multipathd/main.c b/multipathd/main.c
index 2013f20..739cc05 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -842,9 +842,23 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
 	pp = find_path_by_dev(vecs->pathvec, uev->kernel);
 	if (pp) {
 		int r;
+		struct multipath *prev_mpp = NULL;
+
+		if (pp->initialized == INIT_REMOVED) {
+			condlog(3, "%s: re-adding removed path", pp->dev);
+			pp->initialized = INIT_NEW;
+			prev_mpp = pp->mpp;
+			if (prev_mpp == NULL)
+				condlog(0, "Bug: %s was in INIT_REMOVED state without being a multipath member",
+					pp->dev);
+			pp->mpp = NULL;
+			/* make sure get_uid() is called */
+			pp->wwid[0] = '\0';
+		} else
+			condlog(3,
+				"%s: spurious uevent, path already in pathvec",
+				uev->kernel);
 
-		condlog(3, "%s: spurious uevent, path already in pathvec",
-			uev->kernel);
 		if (!pp->mpp && !strlen(pp->wwid)) {
 			condlog(3, "%s: reinitialize path", uev->kernel);
 			udev_device_unref(pp->udev);
@@ -854,9 +868,44 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
 			r = pathinfo(pp, conf,
 				     DI_ALL | DI_BLACKLIST);
 			pthread_cleanup_pop(1);
-			if (r == PATHINFO_OK)
+			if (r == PATHINFO_OK && !prev_mpp)
 				ret = ev_add_path(pp, vecs, need_do_map);
-			else if (r == PATHINFO_SKIPPED) {
+			else if (r == PATHINFO_OK &&
+				 !strncmp(pp->wwid, prev_mpp->wwid, WWID_SIZE)) {
+				/*
+				 * Path was unsuccessfully removed, but now
+				 * re-added, and still belongs to the right map
+				 * - all fine, reinstate asap
+				 */
+				pp->mpp = prev_mpp;
+				pp->tick = 1;
+				ret = 0;
+			} else if (prev_mpp) {
+				/*
+				 * Bad: re-added path still hangs in wrong map
+				 * Make another attempt to remove the path
+				 */
+				pp->mpp = prev_mpp;
+				ret = ev_remove_path(pp, vecs, true);
+				if (r == PATHINFO_OK && !ret)
+					/*
+					 * Path successfully freed, move on to
+					 * "new path" code path below
+					 */
+					pp = NULL;
+				else {
+					/*
+					 * Failure in ev_remove_path will keep
+					 * path in pathvec in INIT_REMOVED state
+					 * Fail the path to make sure it isn't
+					 * used any more.
+					 */
+					pp->dmstate = PSTATE_FAILED;
+					dm_fail_path(pp->mpp->alias, pp->dev_t);
+					condlog(1, "%s: failed to re-add path still mapped in %s",
+						pp->dev, pp->mpp->alias);
+				}
+			} else if (r == PATHINFO_SKIPPED) {
 				condlog(3, "%s: remove blacklisted path",
 					uev->kernel);
 				i = find_slot(vecs->pathvec, (void *)pp);
-- 
2.28.0

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

* [PATCH v2 64/74] libmultipath: orphan_paths(): avoid BUG message
  2020-08-12 11:35 [PATCH v2 00/74] multipath-tools series part V: removed path handling mwilck
                   ` (3 preceding siblings ...)
  2020-08-12 11:35 ` [PATCH v2 63/74] multipathd: deal with INIT_REMOVED during path addition mwilck
@ 2020-08-12 11:35 ` mwilck
  2020-08-14  2:26   ` Benjamin Marzinski
  2020-08-12 11:35 ` [PATCH v2 65/74] libmultipath: add update_pathvec_from_dm() mwilck
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 15+ messages in thread
From: mwilck @ 2020-08-12 11:35 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Since c44d769, we print a BUG message when we orphan a path that
holds the mpp->hwe pointer. But if this called via orphan_paths(),
this is expected and we shouldn't warn.

Fixes: c44d769 ("libmultipath: warn if freeing path that holds mpp->hwe")
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs_vec.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index ba3165a..b644d3f 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -124,6 +124,8 @@ void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
 	int i;
 	struct path * pp;
 
+	/* Avoid BUG message from orphan_path() */
+	mpp->hwe = NULL;
 	vector_foreach_slot (pathvec, pp, i) {
 		if (pp->mpp == mpp) {
 			if (pp->initialized == INIT_REMOVED) {
-- 
2.28.0

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

* [PATCH v2 65/74] libmultipath: add update_pathvec_from_dm()
  2020-08-12 11:35 [PATCH v2 00/74] multipath-tools series part V: removed path handling mwilck
                   ` (4 preceding siblings ...)
  2020-08-12 11:35 ` [PATCH v2 64/74] libmultipath: orphan_paths(): avoid BUG message mwilck
@ 2020-08-12 11:35 ` mwilck
  2020-08-14  5:08   ` Benjamin Marzinski
  2020-08-12 11:35 ` [PATCH v2 66/74] libmultipath: update_pathvec_from_dm: handle pp->mpp mismatch mwilck
  2020-08-12 11:35 ` [PATCH v2 71/74] multipath: use update_pathvec_from_dm() mwilck
  7 siblings, 1 reply; 15+ messages in thread
From: mwilck @ 2020-08-12 11:35 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

It can happen in particular during boot or startup that we encounter
paths as map members which haven't been discovered or fully initialized
yet, and are thus not in the pathvec. These paths need special treatment
in various ways. Currently this is dealt with in disassemble_map(). That's
a layer violation, and the way it is done is suboptimal in various ways.

As a preparation to change that, this patch introduces a new function,
update_pathvec_from_dm(), that is supposed to deal with newly discovered
paths from disassemble_map(). It has to be called after disassemble_map()
has finished.

The logic of the new function is similar but not identical to what
disassemble_map() was doing before. Firstly, the function will simply
remove path devices that don't exist - there's no point in carrying these
around. Map reloads can't be called from this code for reasons of the
overall program logic. But it's prepared to signal to the caller that
a map reload is in order. Using this return value will be future work.

Second, pathinfo() is now called on new paths rather than just setting
pp->dev. The pathinfo flags can be passed as argument to make the function
flexible for different callers.

Finally, treatment of WWIDs is different now. There'll be only one attempt
at guessing the map WWID if it's not set yet. If a non-matching path WWID
is found, the path is removed, and a new uevent triggered (this is the
most important change wrt the dangerous previous code that would simply
overwrite the path WWID). If the path WWID is empty, it will still be
set from the map WWID, which I consider not perfect, but no worse
than what we did before.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs_vec.c | 136 +++++++++++++++++++++++++++++++++++++
 libmultipath/structs_vec.h |   2 +
 2 files changed, 138 insertions(+)

diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index b644d3f..bd2d13b 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -59,6 +59,142 @@ int update_mpp_paths(struct multipath *mpp, vector pathvec)
 	return store_failure;
 }
 
+static bool guess_mpp_wwid(struct multipath *mpp)
+{
+	int i, j;
+	struct pathgroup *pgp;
+	struct path *pp;
+
+	if (strlen(mpp->wwid) || !mpp->pg)
+		return true;
+
+	vector_foreach_slot(mpp->pg, pgp, i) {
+		if (!pgp->paths)
+			continue;
+		vector_foreach_slot(pgp->paths, pp, j) {
+			if (pp->initialized == INIT_OK && strlen(pp->wwid)) {
+				strlcpy(mpp->wwid, pp->wwid, sizeof(mpp->wwid));
+				condlog(2, "%s: guessed WWID %s from path %s",
+					mpp->alias, mpp->wwid, pp->dev);
+				return true;
+			}
+		}
+	}
+	condlog(1, "%s: unable to guess WWID", mpp->alias);
+	return false;
+}
+
+/*
+ * update_pathvec_from_dm() - update pathvec after disassemble_map()
+ *
+ * disassemble_map() may return block devices that are members in
+ * multipath maps but haven't been discovered. Check whether they
+ * need to be added to pathvec or discarded.
+ *
+ * Returns: true if immediate map reload is desirable
+ *
+ * Side effects:
+ * - may delete non-existing paths and empty pathgroups from mpp
+ * - may set pp->wwid and / or mpp->wwid
+ * - calls pathinfo() on existing paths is pathinfo_flags is not 0
+ */
+bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
+	int pathinfo_flags)
+{
+	int i, j;
+	struct pathgroup *pgp;
+	struct path *pp;
+	struct config *conf;
+	bool mpp_has_wwid;
+	bool must_reload = false;
+
+	if (!mpp->pg)
+		return false;
+
+	/*
+	 * This will initialize mpp->wwid with an educated guess,
+	 * either from the dm uuid or from a member path with properly
+	 * determined WWID.
+	 */
+	mpp_has_wwid = guess_mpp_wwid(mpp);
+
+	vector_foreach_slot(mpp->pg, pgp, i) {
+		if (!pgp->paths)
+			goto delete_pg;
+
+		vector_foreach_slot(pgp->paths, pp, j) {
+			pp->mpp = mpp;
+
+			/*
+			 * The way disassemble_map() works: If it encounters a
+			 * path device which isn't found in pathvec, it adds an
+			 * uninitialized struct path to pgp->paths, with only
+			 * pp->dev_t filled in. Thus if pp->udev is set here,
+			 * we know that the path is in pathvec already.
+			 */
+			if (pp->udev) {
+				if (pathinfo_flags) {
+					conf = get_multipath_config();
+					pthread_cleanup_push(put_multipath_config,
+							     conf);
+					pathinfo(pp, conf, pathinfo_flags);
+					pthread_cleanup_pop(1);
+				}
+				continue;
+			}
+
+			/* If this fails, the device is not in sysfs */
+			pp->udev = get_udev_device(pp->dev_t, DEV_DEVT);
+			if (!pp->udev) {
+				condlog(2, "%s: discarding non-existing path %s",
+					mpp->alias, pp->dev_t);
+				vector_del_slot(pgp->paths, j--);
+				free_path(pp);
+				must_reload = true;
+			} else {
+				int rc;
+
+				devt2devname(pp->dev, sizeof(pp->dev),
+					     pp->dev_t);
+				conf = get_multipath_config();
+				pthread_cleanup_push(put_multipath_config,
+						     conf);
+				pp->checkint = conf->checkint;
+				rc = pathinfo(pp, conf,
+					      DI_SYSFS|DI_WWID|DI_BLACKLIST|
+					      pathinfo_flags);
+				pthread_cleanup_pop(1);
+				if (rc != PATHINFO_OK) {
+					condlog(1, "%s: error %d in pathinfo, discarding path",
+						pp->dev, rc);
+					vector_del_slot(pgp->paths, j--);
+					free_path(pp);
+					must_reload = true;
+				} else {
+					if (mpp_has_wwid && !strlen(pp->wwid)) {
+						condlog(3, "%s: setting wwid from map: %s",
+							pp->dev, mpp->wwid);
+						strlcpy(pp->wwid, mpp->wwid,
+							sizeof(pp->wwid));
+					}
+					condlog(2, "%s: adding new path %s",
+						mpp->alias, pp->dev);
+					store_path(pathvec, pp);
+					pp->tick = 1;
+				}
+			}
+		}
+		if (VECTOR_SIZE(pgp->paths) != 0)
+			continue;
+	delete_pg:
+		condlog(2, "%s: removing empty pathgroup %d", mpp->alias, i);
+		vector_del_slot(mpp->pg, i--);
+		free_pathgroup(pgp, KEEP_PATHS);
+		must_reload = true;
+	}
+	return must_reload;
+}
+
 int adopt_paths(vector pathvec, struct multipath *mpp)
 {
 	int i, ret;
diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h
index d7dfe32..6c79069 100644
--- a/libmultipath/structs_vec.h
+++ b/libmultipath/structs_vec.h
@@ -21,6 +21,8 @@ void orphan_path (struct path * pp, const char *reason);
 void set_path_removed(struct path *pp);
 
 int verify_paths(struct multipath *mpp);
+bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
+			    int pathinfo_flags);
 int update_mpp_paths(struct multipath * mpp, vector pathvec);
 int update_multipath_strings (struct multipath *mpp, vector pathvec,
 			      int is_daemon);
-- 
2.28.0

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

* [PATCH v2 66/74] libmultipath: update_pathvec_from_dm: handle pp->mpp mismatch
  2020-08-12 11:35 [PATCH v2 00/74] multipath-tools series part V: removed path handling mwilck
                   ` (5 preceding siblings ...)
  2020-08-12 11:35 ` [PATCH v2 65/74] libmultipath: add update_pathvec_from_dm() mwilck
@ 2020-08-12 11:35 ` mwilck
  2020-08-14  5:08   ` Benjamin Marzinski
  2020-08-12 11:35 ` [PATCH v2 71/74] multipath: use update_pathvec_from_dm() mwilck
  7 siblings, 1 reply; 15+ messages in thread
From: mwilck @ 2020-08-12 11:35 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

Treat this like a WWID mismatch.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs_vec.c | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index bd2d13b..e7a8d53 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -123,6 +123,23 @@ bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
 			goto delete_pg;
 
 		vector_foreach_slot(pgp->paths, pp, j) {
+
+			if (pp->mpp && pp->mpp != mpp) {
+				condlog(0, "BUG: %s: found path %s which is already in %s",
+					mpp->alias, pp->dev, pp->mpp->alias);
+
+				/*
+				 * Either we added this path to the other mpp
+				 * explicitly, or we came by here earlier and
+				 * decided it belonged there. In both cases,
+				 * the path should remain in the other map,
+				 * and be deleted here.
+				 */
+				must_reload = true;
+				dm_fail_path(mpp->alias, pp->dev_t);
+				vector_del_slot(pgp->paths, j--);
+				continue;
+			}
 			pp->mpp = mpp;
 
 			/*
@@ -170,6 +187,22 @@ bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
 					vector_del_slot(pgp->paths, j--);
 					free_path(pp);
 					must_reload = true;
+				} else if (mpp_has_wwid && pp->wwid[0] != '\0'
+					   && strcmp(mpp->wwid, pp->wwid)) {
+					condlog(0, "%s: path %s WWID %s doesn't match, removing from map",
+						mpp->wwid, pp->dev_t, pp->wwid);
+					/*
+					 * This path exists, but in the wrong map.
+					 * We can't reload the map from here.
+					 * Make sure it isn't used in this map
+					 * any more, and let the checker re-add
+					 * it as it sees fit.
+					 */
+					dm_fail_path(mpp->alias, pp->dev_t);
+					vector_del_slot(pgp->paths, j--);
+					orphan_path(pp, "WWID mismatch");
+					pp->tick = 1;
+					must_reload = true;
 				} else {
 					if (mpp_has_wwid && !strlen(pp->wwid)) {
 						condlog(3, "%s: setting wwid from map: %s",
-- 
2.28.0

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

* [PATCH v2 71/74] multipath: use update_pathvec_from_dm()
  2020-08-12 11:35 [PATCH v2 00/74] multipath-tools series part V: removed path handling mwilck
                   ` (6 preceding siblings ...)
  2020-08-12 11:35 ` [PATCH v2 66/74] libmultipath: update_pathvec_from_dm: handle pp->mpp mismatch mwilck
@ 2020-08-12 11:35 ` mwilck
  2020-08-17 21:00   ` Benjamin Marzinski
  7 siblings, 1 reply; 15+ messages in thread
From: mwilck @ 2020-08-12 11:35 UTC (permalink / raw)
  To: Christophe Varoqui, Benjamin Marzinski; +Cc: dm-devel, Martin Wilck

From: Martin Wilck <mwilck@suse.com>

The multipath-specific function update_paths() can now be replaced with
a call to update_pathvec_from_dm().

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmultipath/structs_vec.c |  2 +-
 multipath/main.c           | 68 +++-----------------------------------
 2 files changed, 5 insertions(+), 65 deletions(-)

diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 07027f5..2d85df9 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -150,7 +150,7 @@ bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
 			 * we know that the path is in pathvec already.
 			 */
 			if (pp->udev) {
-				if (pathinfo_flags) {
+				if (pathinfo_flags & ~DI_NOIO) {
 					conf = get_multipath_config();
 					pthread_cleanup_push(put_multipath_config,
 							     conf);
diff --git a/multipath/main.c b/multipath/main.c
index a144bc3..9d6b482 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -187,63 +187,6 @@ usage (char * progname)
 
 }
 
-static int
-update_paths (struct multipath * mpp, int quick)
-{
-	int i, j;
-	struct pathgroup * pgp;
-	struct path * pp;
-	struct config *conf;
-
-	if (!mpp->pg)
-		return 0;
-
-	vector_foreach_slot (mpp->pg, pgp, i) {
-		if (!pgp->paths)
-			continue;
-
-		vector_foreach_slot (pgp->paths, pp, j) {
-			if (!strlen(pp->dev)) {
-				if (devt2devname(pp->dev, FILE_NAME_SIZE,
-						 pp->dev_t)) {
-					/*
-					 * path is not in sysfs anymore
-					 */
-					pp->chkrstate = pp->state = PATH_DOWN;
-					pp->offline = 1;
-					continue;
-				}
-				pp->mpp = mpp;
-				if (quick)
-					continue;
-				conf = get_multipath_config();
-				if (pathinfo(pp, conf, DI_ALL))
-					pp->state = PATH_UNCHECKED;
-				put_multipath_config(conf);
-				continue;
-			}
-			pp->mpp = mpp;
-			if (quick)
-				continue;
-			if (pp->state == PATH_UNCHECKED ||
-			    pp->state == PATH_WILD) {
-				conf = get_multipath_config();
-				if (pathinfo(pp, conf, DI_CHECKER))
-					pp->state = PATH_UNCHECKED;
-				put_multipath_config(conf);
-			}
-
-			if (pp->priority == PRIO_UNDEF) {
-				conf = get_multipath_config();
-				if (pathinfo(pp, conf, DI_PRIO))
-					pp->priority = PRIO_UNDEF;
-				put_multipath_config(conf);
-			}
-		}
-	}
-	return 0;
-}
-
 static int
 get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
 {
@@ -273,13 +216,9 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
 		condlog(3, "status = %s", status);
 
 		disassemble_map(pathvec, params, mpp);
-
-		/*
-		 * disassemble_map() can add new paths to pathvec.
-		 * If not in "fast list mode", we need to fetch information
-		 * about them
-		 */
-		update_paths(mpp, (cmd == CMD_LIST_SHORT));
+		update_pathvec_from_dm(pathvec, mpp,
+				       (cmd == CMD_LIST_SHORT ?
+					DI_NOIO : DI_ALL));
 
 		if (cmd == CMD_LIST_LONG)
 			mpp->bestpg = select_path_group(mpp);
@@ -353,6 +292,7 @@ static int check_usable_paths(struct config *conf,
 	dm_get_map(mpp->alias, &mpp->size, params);
 	dm_get_status(mpp->alias, status);
 	disassemble_map(pathvec, params, mpp);
+	update_pathvec_from_dm(pathvec, mpp, 0);
 	disassemble_status(status, mpp);
 
 	vector_foreach_slot (mpp->pg, pg, i) {
-- 
2.28.0

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

* Re: [PATCH v2 61/74] libmultipath: adopt_paths(): skip removed paths
  2020-08-12 11:35 ` [PATCH v2 61/74] libmultipath: adopt_paths(): skip removed paths mwilck
@ 2020-08-14  1:37   ` Benjamin Marzinski
  0 siblings, 0 replies; 15+ messages in thread
From: Benjamin Marzinski @ 2020-08-14  1:37 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Wed, Aug 12, 2020 at 01:35:06PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> If we don't do this, pathinfo() will fail on these paths, causing
> adopt_paths() to fail.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/structs_vec.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
> index f139fc0..ba3165a 100644
> --- a/libmultipath/structs_vec.c
> +++ b/libmultipath/structs_vec.c
> @@ -79,9 +79,11 @@ int adopt_paths(vector pathvec, struct multipath *mpp)
>  					pp->dev, mpp->alias);
>  				continue;
>  			}
> +			pp->mpp = mpp;
> +			if (pp->initialized == INIT_REMOVED)
> +				continue;
>  			condlog(3, "%s: ownership set to %s",
>  				pp->dev, mpp->alias);
> -			pp->mpp = mpp;
>  
>  			if (!mpp->paths && !(mpp->paths = vector_alloc()))
>  				return 1;
> -- 
> 2.28.0

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

* Re: [PATCH v2 63/74] multipathd: deal with INIT_REMOVED during path addition
  2020-08-12 11:35 ` [PATCH v2 63/74] multipathd: deal with INIT_REMOVED during path addition mwilck
@ 2020-08-14  2:25   ` Benjamin Marzinski
  0 siblings, 0 replies; 15+ messages in thread
From: Benjamin Marzinski @ 2020-08-14  2:25 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Wed, Aug 12, 2020 at 01:35:07PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> With the introduction of INIT_REMOVED, we have to deal with the situation
> when a path is re-added in this state. This enables us to detect the
> situation where a path is added while still part of a map after a failed
> removal, which we couldn't before. Dealing with this correctly requires
> some additional logic. There's a good case (re-added path is still mapped
> by a map with matching WWID) and a bad case (non-matching WWID).
> 
> The logic is very similar in uev_add_path() and cli_add_path().
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  multipathd/cli_handlers.c | 54 +++++++++++++++++++++++++++++++++++--
>  multipathd/main.c         | 57 ++++++++++++++++++++++++++++++++++++---
>  2 files changed, 105 insertions(+), 6 deletions(-)
> 
> diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
> index 782bb00..c60182f 100644
> --- a/multipathd/cli_handlers.c
> +++ b/multipathd/cli_handlers.c
> @@ -713,11 +713,61 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
>  		goto blacklisted;
>  
>  	pp = find_path_by_dev(vecs->pathvec, param);
> -	if (pp) {
> +	if (pp && pp->initialized != INIT_REMOVED) {
>  		condlog(2, "%s: path already in pathvec", param);
>  		if (pp->mpp)
>  			return 0;
> -	} else {
> +	} else if (pp) {
> +		/* Trying to add a path in INIT_REMOVED state */
> +		struct multipath *prev_mpp;
> +
> +		prev_mpp = pp->mpp;
> +		if (prev_mpp == NULL)
> +			condlog(0, "Bug: %s was in INIT_REMOVED state without being a multipath member",
> +				pp->dev);
> +		pp->mpp = NULL;
> +		pp->initialized = INIT_NEW;
> +		pp->wwid[0] = '\0';
> +		conf = get_multipath_config();
> +		pthread_cleanup_push(put_multipath_config, conf);
> +		r = pathinfo(pp, conf, DI_ALL | DI_BLACKLIST);
> +		pthread_cleanup_pop(1);
> +
> +		if (prev_mpp) {
> +			/* Similar logic as in uev_add_path() */
> +			pp->mpp = prev_mpp;
> +			if (r == PATHINFO_OK &&
> +			    !strncmp(prev_mpp->wwid, pp->wwid, WWID_SIZE)) {
> +				condlog(2, "%s: path re-added to %s", pp->dev,
> +					pp->mpp->alias);
> +				/* Have the checker reinstate this path asap */
> +				pp->tick = 1;
> +				return 0;
> +			} else if (!ev_remove_path(pp, vecs, true))
> +				/* Path removed in ev_remove_path() */
> +				pp = NULL;
> +			else {
> +				/* Init state is now INIT_REMOVED again */
> +				pp->dmstate = PSTATE_FAILED;
> +				dm_fail_path(pp->mpp->alias, pp->dev_t);
> +				condlog(1, "%s: failed to re-add path still mapped in %s",
> +					pp->dev, pp->mpp->alias);
> +				return 1;
> +			}
> +		} else {
> +			switch (r) {
> +			case PATHINFO_SKIPPED:
> +				goto blacklisted;
> +			case PATHINFO_OK:
> +				break;
> +			default:
> +				condlog(0, "%s: failed to get pathinfo", param);
> +				return 1;
> +			}
> +		}
> +	}
> +
> +	if (!pp) {
>  		struct udev_device *udevice;
>  
>  		udevice = udev_device_new_from_subsystem_sysname(udev,
> diff --git a/multipathd/main.c b/multipathd/main.c
> index 2013f20..739cc05 100644
> --- a/multipathd/main.c
> +++ b/multipathd/main.c
> @@ -842,9 +842,23 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
>  	pp = find_path_by_dev(vecs->pathvec, uev->kernel);
>  	if (pp) {
>  		int r;
> +		struct multipath *prev_mpp = NULL;
> +
> +		if (pp->initialized == INIT_REMOVED) {
> +			condlog(3, "%s: re-adding removed path", pp->dev);
> +			pp->initialized = INIT_NEW;
> +			prev_mpp = pp->mpp;
> +			if (prev_mpp == NULL)
> +				condlog(0, "Bug: %s was in INIT_REMOVED state without being a multipath member",
> +					pp->dev);
> +			pp->mpp = NULL;
> +			/* make sure get_uid() is called */
> +			pp->wwid[0] = '\0';
> +		} else
> +			condlog(3,
> +				"%s: spurious uevent, path already in pathvec",
> +				uev->kernel);
>  
> -		condlog(3, "%s: spurious uevent, path already in pathvec",
> -			uev->kernel);
>  		if (!pp->mpp && !strlen(pp->wwid)) {
>  			condlog(3, "%s: reinitialize path", uev->kernel);
>  			udev_device_unref(pp->udev);
> @@ -854,9 +868,44 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
>  			r = pathinfo(pp, conf,
>  				     DI_ALL | DI_BLACKLIST);
>  			pthread_cleanup_pop(1);
> -			if (r == PATHINFO_OK)
> +			if (r == PATHINFO_OK && !prev_mpp)
>  				ret = ev_add_path(pp, vecs, need_do_map);
> -			else if (r == PATHINFO_SKIPPED) {
> +			else if (r == PATHINFO_OK &&
> +				 !strncmp(pp->wwid, prev_mpp->wwid, WWID_SIZE)) {
> +				/*
> +				 * Path was unsuccessfully removed, but now
> +				 * re-added, and still belongs to the right map
> +				 * - all fine, reinstate asap
> +				 */
> +				pp->mpp = prev_mpp;
> +				pp->tick = 1;
> +				ret = 0;
> +			} else if (prev_mpp) {
> +				/*
> +				 * Bad: re-added path still hangs in wrong map
> +				 * Make another attempt to remove the path
> +				 */
> +				pp->mpp = prev_mpp;
> +				ret = ev_remove_path(pp, vecs, true);
> +				if (r == PATHINFO_OK && !ret)
> +					/*
> +					 * Path successfully freed, move on to
> +					 * "new path" code path below
> +					 */
> +					pp = NULL;
> +				else {
> +					/*
> +					 * Failure in ev_remove_path will keep
> +					 * path in pathvec in INIT_REMOVED state
> +					 * Fail the path to make sure it isn't
> +					 * used any more.
> +					 */
> +					pp->dmstate = PSTATE_FAILED;
> +					dm_fail_path(pp->mpp->alias, pp->dev_t);
> +					condlog(1, "%s: failed to re-add path still mapped in %s",
> +						pp->dev, pp->mpp->alias);
> +				}
> +			} else if (r == PATHINFO_SKIPPED) {
>  				condlog(3, "%s: remove blacklisted path",
>  					uev->kernel);
>  				i = find_slot(vecs->pathvec, (void *)pp);
> -- 
> 2.28.0

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

* Re: [PATCH v2 64/74] libmultipath: orphan_paths(): avoid BUG message
  2020-08-12 11:35 ` [PATCH v2 64/74] libmultipath: orphan_paths(): avoid BUG message mwilck
@ 2020-08-14  2:26   ` Benjamin Marzinski
  0 siblings, 0 replies; 15+ messages in thread
From: Benjamin Marzinski @ 2020-08-14  2:26 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Wed, Aug 12, 2020 at 01:35:08PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Since c44d769, we print a BUG message when we orphan a path that
> holds the mpp->hwe pointer. But if this called via orphan_paths(),
> this is expected and we shouldn't warn.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Fixes: c44d769 ("libmultipath: warn if freeing path that holds mpp->hwe")
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/structs_vec.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
> index ba3165a..b644d3f 100644
> --- a/libmultipath/structs_vec.c
> +++ b/libmultipath/structs_vec.c
> @@ -124,6 +124,8 @@ void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
>  	int i;
>  	struct path * pp;
>  
> +	/* Avoid BUG message from orphan_path() */
> +	mpp->hwe = NULL;
>  	vector_foreach_slot (pathvec, pp, i) {
>  		if (pp->mpp == mpp) {
>  			if (pp->initialized == INIT_REMOVED) {
> -- 
> 2.28.0

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

* Re: [PATCH v2 65/74] libmultipath: add update_pathvec_from_dm()
  2020-08-12 11:35 ` [PATCH v2 65/74] libmultipath: add update_pathvec_from_dm() mwilck
@ 2020-08-14  5:08   ` Benjamin Marzinski
  0 siblings, 0 replies; 15+ messages in thread
From: Benjamin Marzinski @ 2020-08-14  5:08 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Wed, Aug 12, 2020 at 01:35:09PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> It can happen in particular during boot or startup that we encounter
> paths as map members which haven't been discovered or fully initialized
> yet, and are thus not in the pathvec. These paths need special treatment
> in various ways. Currently this is dealt with in disassemble_map(). That's
> a layer violation, and the way it is done is suboptimal in various ways.
> 
> As a preparation to change that, this patch introduces a new function,
> update_pathvec_from_dm(), that is supposed to deal with newly discovered
> paths from disassemble_map(). It has to be called after disassemble_map()
> has finished.
> 
> The logic of the new function is similar but not identical to what
> disassemble_map() was doing before. Firstly, the function will simply
> remove path devices that don't exist - there's no point in carrying these
> around. Map reloads can't be called from this code for reasons of the
> overall program logic. But it's prepared to signal to the caller that
> a map reload is in order. Using this return value will be future work.
> 
> Second, pathinfo() is now called on new paths rather than just setting
> pp->dev. The pathinfo flags can be passed as argument to make the function
> flexible for different callers.
> 
> Finally, treatment of WWIDs is different now. There'll be only one attempt
> at guessing the map WWID if it's not set yet. If a non-matching path WWID
> is found, the path is removed, and a new uevent triggered (this is the
> most important change wrt the dangerous previous code that would simply
> overwrite the path WWID). If the path WWID is empty, it will still be
> set from the map WWID, which I consider not perfect, but no worse
> than what we did before.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/structs_vec.c | 136 +++++++++++++++++++++++++++++++++++++
>  libmultipath/structs_vec.h |   2 +
>  2 files changed, 138 insertions(+)
> 
> diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
> index b644d3f..bd2d13b 100644
> --- a/libmultipath/structs_vec.c
> +++ b/libmultipath/structs_vec.c
> @@ -59,6 +59,142 @@ int update_mpp_paths(struct multipath *mpp, vector pathvec)
>  	return store_failure;
>  }
>  
> +static bool guess_mpp_wwid(struct multipath *mpp)
> +{
> +	int i, j;
> +	struct pathgroup *pgp;
> +	struct path *pp;
> +
> +	if (strlen(mpp->wwid) || !mpp->pg)
> +		return true;
> +
> +	vector_foreach_slot(mpp->pg, pgp, i) {
> +		if (!pgp->paths)
> +			continue;
> +		vector_foreach_slot(pgp->paths, pp, j) {
> +			if (pp->initialized == INIT_OK && strlen(pp->wwid)) {
> +				strlcpy(mpp->wwid, pp->wwid, sizeof(mpp->wwid));
> +				condlog(2, "%s: guessed WWID %s from path %s",
> +					mpp->alias, mpp->wwid, pp->dev);
> +				return true;
> +			}
> +		}
> +	}
> +	condlog(1, "%s: unable to guess WWID", mpp->alias);
> +	return false;
> +}
> +
> +/*
> + * update_pathvec_from_dm() - update pathvec after disassemble_map()
> + *
> + * disassemble_map() may return block devices that are members in
> + * multipath maps but haven't been discovered. Check whether they
> + * need to be added to pathvec or discarded.
> + *
> + * Returns: true if immediate map reload is desirable
> + *
> + * Side effects:
> + * - may delete non-existing paths and empty pathgroups from mpp
> + * - may set pp->wwid and / or mpp->wwid
> + * - calls pathinfo() on existing paths is pathinfo_flags is not 0
> + */
> +bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
> +	int pathinfo_flags)
> +{
> +	int i, j;
> +	struct pathgroup *pgp;
> +	struct path *pp;
> +	struct config *conf;
> +	bool mpp_has_wwid;
> +	bool must_reload = false;
> +
> +	if (!mpp->pg)
> +		return false;
> +
> +	/*
> +	 * This will initialize mpp->wwid with an educated guess,
> +	 * either from the dm uuid or from a member path with properly
> +	 * determined WWID.
> +	 */
> +	mpp_has_wwid = guess_mpp_wwid(mpp);
> +
> +	vector_foreach_slot(mpp->pg, pgp, i) {
> +		if (!pgp->paths)
> +			goto delete_pg;
> +
> +		vector_foreach_slot(pgp->paths, pp, j) {
> +			pp->mpp = mpp;
> +
> +			/*
> +			 * The way disassemble_map() works: If it encounters a
> +			 * path device which isn't found in pathvec, it adds an
> +			 * uninitialized struct path to pgp->paths, with only
> +			 * pp->dev_t filled in. Thus if pp->udev is set here,
> +			 * we know that the path is in pathvec already.
> +			 */
> +			if (pp->udev) {
> +				if (pathinfo_flags) {
> +					conf = get_multipath_config();
> +					pthread_cleanup_push(put_multipath_config,
> +							     conf);
> +					pathinfo(pp, conf, pathinfo_flags);
> +					pthread_cleanup_pop(1);
> +				}
> +				continue;
> +			}
> +
> +			/* If this fails, the device is not in sysfs */
> +			pp->udev = get_udev_device(pp->dev_t, DEV_DEVT);
> +			if (!pp->udev) {
> +				condlog(2, "%s: discarding non-existing path %s",
> +					mpp->alias, pp->dev_t);
> +				vector_del_slot(pgp->paths, j--);
> +				free_path(pp);
> +				must_reload = true;
> +			} else {
> +				int rc;
> +
> +				devt2devname(pp->dev, sizeof(pp->dev),
> +					     pp->dev_t);
> +				conf = get_multipath_config();
> +				pthread_cleanup_push(put_multipath_config,
> +						     conf);
> +				pp->checkint = conf->checkint;
> +				rc = pathinfo(pp, conf,
> +					      DI_SYSFS|DI_WWID|DI_BLACKLIST|
> +					      pathinfo_flags);
> +				pthread_cleanup_pop(1);
> +				if (rc != PATHINFO_OK) {
> +					condlog(1, "%s: error %d in pathinfo, discarding path",
> +						pp->dev, rc);
> +					vector_del_slot(pgp->paths, j--);
> +					free_path(pp);
> +					must_reload = true;
> +				} else {
> +					if (mpp_has_wwid && !strlen(pp->wwid)) {
> +						condlog(3, "%s: setting wwid from map: %s",
> +							pp->dev, mpp->wwid);
> +						strlcpy(pp->wwid, mpp->wwid,
> +							sizeof(pp->wwid));
> +					}
> +					condlog(2, "%s: adding new path %s",
> +						mpp->alias, pp->dev);
> +					store_path(pathvec, pp);
> +					pp->tick = 1;
> +				}
> +			}
> +		}
> +		if (VECTOR_SIZE(pgp->paths) != 0)
> +			continue;
> +	delete_pg:
> +		condlog(2, "%s: removing empty pathgroup %d", mpp->alias, i);
> +		vector_del_slot(mpp->pg, i--);
> +		free_pathgroup(pgp, KEEP_PATHS);
> +		must_reload = true;
> +	}
> +	return must_reload;
> +}
> +
>  int adopt_paths(vector pathvec, struct multipath *mpp)
>  {
>  	int i, ret;
> diff --git a/libmultipath/structs_vec.h b/libmultipath/structs_vec.h
> index d7dfe32..6c79069 100644
> --- a/libmultipath/structs_vec.h
> +++ b/libmultipath/structs_vec.h
> @@ -21,6 +21,8 @@ void orphan_path (struct path * pp, const char *reason);
>  void set_path_removed(struct path *pp);
>  
>  int verify_paths(struct multipath *mpp);
> +bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
> +			    int pathinfo_flags);
>  int update_mpp_paths(struct multipath * mpp, vector pathvec);
>  int update_multipath_strings (struct multipath *mpp, vector pathvec,
>  			      int is_daemon);
> -- 
> 2.28.0

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

* Re: [PATCH v2 66/74] libmultipath: update_pathvec_from_dm: handle pp->mpp mismatch
  2020-08-12 11:35 ` [PATCH v2 66/74] libmultipath: update_pathvec_from_dm: handle pp->mpp mismatch mwilck
@ 2020-08-14  5:08   ` Benjamin Marzinski
  0 siblings, 0 replies; 15+ messages in thread
From: Benjamin Marzinski @ 2020-08-14  5:08 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Wed, Aug 12, 2020 at 01:35:10PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> Treat this like a WWID mismatch.
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/structs_vec.c | 33 +++++++++++++++++++++++++++++++++
>  1 file changed, 33 insertions(+)
> 
> diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
> index bd2d13b..e7a8d53 100644
> --- a/libmultipath/structs_vec.c
> +++ b/libmultipath/structs_vec.c
> @@ -123,6 +123,23 @@ bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
>  			goto delete_pg;
>  
>  		vector_foreach_slot(pgp->paths, pp, j) {
> +
> +			if (pp->mpp && pp->mpp != mpp) {
> +				condlog(0, "BUG: %s: found path %s which is already in %s",
> +					mpp->alias, pp->dev, pp->mpp->alias);
> +
> +				/*
> +				 * Either we added this path to the other mpp
> +				 * explicitly, or we came by here earlier and
> +				 * decided it belonged there. In both cases,
> +				 * the path should remain in the other map,
> +				 * and be deleted here.
> +				 */
> +				must_reload = true;
> +				dm_fail_path(mpp->alias, pp->dev_t);
> +				vector_del_slot(pgp->paths, j--);
> +				continue;
> +			}
>  			pp->mpp = mpp;
>  
>  			/*
> @@ -170,6 +187,22 @@ bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
>  					vector_del_slot(pgp->paths, j--);
>  					free_path(pp);
>  					must_reload = true;
> +				} else if (mpp_has_wwid && pp->wwid[0] != '\0'
> +					   && strcmp(mpp->wwid, pp->wwid)) {
> +					condlog(0, "%s: path %s WWID %s doesn't match, removing from map",
> +						mpp->wwid, pp->dev_t, pp->wwid);
> +					/*
> +					 * This path exists, but in the wrong map.
> +					 * We can't reload the map from here.
> +					 * Make sure it isn't used in this map
> +					 * any more, and let the checker re-add
> +					 * it as it sees fit.
> +					 */
> +					dm_fail_path(mpp->alias, pp->dev_t);
> +					vector_del_slot(pgp->paths, j--);
> +					orphan_path(pp, "WWID mismatch");
> +					pp->tick = 1;
> +					must_reload = true;
>  				} else {
>  					if (mpp_has_wwid && !strlen(pp->wwid)) {
>  						condlog(3, "%s: setting wwid from map: %s",
> -- 
> 2.28.0

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

* Re: [PATCH v2 71/74] multipath: use update_pathvec_from_dm()
  2020-08-12 11:35 ` [PATCH v2 71/74] multipath: use update_pathvec_from_dm() mwilck
@ 2020-08-17 21:00   ` Benjamin Marzinski
  0 siblings, 0 replies; 15+ messages in thread
From: Benjamin Marzinski @ 2020-08-17 21:00 UTC (permalink / raw)
  To: mwilck; +Cc: dm-devel

On Wed, Aug 12, 2020 at 01:35:11PM +0200, mwilck@suse.com wrote:
> From: Martin Wilck <mwilck@suse.com>
> 
> The multipath-specific function update_paths() can now be replaced with
> a call to update_pathvec_from_dm().
> 
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
> Signed-off-by: Martin Wilck <mwilck@suse.com>
> ---
>  libmultipath/structs_vec.c |  2 +-
>  multipath/main.c           | 68 +++-----------------------------------
>  2 files changed, 5 insertions(+), 65 deletions(-)
> 
> diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
> index 07027f5..2d85df9 100644
> --- a/libmultipath/structs_vec.c
> +++ b/libmultipath/structs_vec.c
> @@ -150,7 +150,7 @@ bool update_pathvec_from_dm(vector pathvec, struct multipath *mpp,
>  			 * we know that the path is in pathvec already.
>  			 */
>  			if (pp->udev) {
> -				if (pathinfo_flags) {
> +				if (pathinfo_flags & ~DI_NOIO) {
>  					conf = get_multipath_config();
>  					pthread_cleanup_push(put_multipath_config,
>  							     conf);
> diff --git a/multipath/main.c b/multipath/main.c
> index a144bc3..9d6b482 100644
> --- a/multipath/main.c
> +++ b/multipath/main.c
> @@ -187,63 +187,6 @@ usage (char * progname)
>  
>  }
>  
> -static int
> -update_paths (struct multipath * mpp, int quick)
> -{
> -	int i, j;
> -	struct pathgroup * pgp;
> -	struct path * pp;
> -	struct config *conf;
> -
> -	if (!mpp->pg)
> -		return 0;
> -
> -	vector_foreach_slot (mpp->pg, pgp, i) {
> -		if (!pgp->paths)
> -			continue;
> -
> -		vector_foreach_slot (pgp->paths, pp, j) {
> -			if (!strlen(pp->dev)) {
> -				if (devt2devname(pp->dev, FILE_NAME_SIZE,
> -						 pp->dev_t)) {
> -					/*
> -					 * path is not in sysfs anymore
> -					 */
> -					pp->chkrstate = pp->state = PATH_DOWN;
> -					pp->offline = 1;
> -					continue;
> -				}
> -				pp->mpp = mpp;
> -				if (quick)
> -					continue;
> -				conf = get_multipath_config();
> -				if (pathinfo(pp, conf, DI_ALL))
> -					pp->state = PATH_UNCHECKED;
> -				put_multipath_config(conf);
> -				continue;
> -			}
> -			pp->mpp = mpp;
> -			if (quick)
> -				continue;
> -			if (pp->state == PATH_UNCHECKED ||
> -			    pp->state == PATH_WILD) {
> -				conf = get_multipath_config();
> -				if (pathinfo(pp, conf, DI_CHECKER))
> -					pp->state = PATH_UNCHECKED;
> -				put_multipath_config(conf);
> -			}
> -
> -			if (pp->priority == PRIO_UNDEF) {
> -				conf = get_multipath_config();
> -				if (pathinfo(pp, conf, DI_PRIO))
> -					pp->priority = PRIO_UNDEF;
> -				put_multipath_config(conf);
> -			}
> -		}
> -	}
> -	return 0;
> -}
> -
>  static int
>  get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
>  {
> @@ -273,13 +216,9 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
>  		condlog(3, "status = %s", status);
>  
>  		disassemble_map(pathvec, params, mpp);
> -
> -		/*
> -		 * disassemble_map() can add new paths to pathvec.
> -		 * If not in "fast list mode", we need to fetch information
> -		 * about them
> -		 */
> -		update_paths(mpp, (cmd == CMD_LIST_SHORT));
> +		update_pathvec_from_dm(pathvec, mpp,
> +				       (cmd == CMD_LIST_SHORT ?
> +					DI_NOIO : DI_ALL));
>  
>  		if (cmd == CMD_LIST_LONG)
>  			mpp->bestpg = select_path_group(mpp);
> @@ -353,6 +292,7 @@ static int check_usable_paths(struct config *conf,
>  	dm_get_map(mpp->alias, &mpp->size, params);
>  	dm_get_status(mpp->alias, status);
>  	disassemble_map(pathvec, params, mpp);
> +	update_pathvec_from_dm(pathvec, mpp, 0);
>  	disassemble_status(status, mpp);
>  
>  	vector_foreach_slot (mpp->pg, pg, i) {
> -- 
> 2.28.0

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

end of thread, other threads:[~2020-08-17 21:00 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-12 11:35 [PATCH v2 00/74] multipath-tools series part V: removed path handling mwilck
2020-08-12 11:35 ` [PATCH v2 55/74] libmultipath: add uninitialize_path() mwilck
2020-08-12 11:35 ` [PATCH v2 58/74] libmultipath: verify_paths(): don't delete paths from pathvec mwilck
2020-08-12 11:35 ` [PATCH v2 61/74] libmultipath: adopt_paths(): skip removed paths mwilck
2020-08-14  1:37   ` Benjamin Marzinski
2020-08-12 11:35 ` [PATCH v2 63/74] multipathd: deal with INIT_REMOVED during path addition mwilck
2020-08-14  2:25   ` Benjamin Marzinski
2020-08-12 11:35 ` [PATCH v2 64/74] libmultipath: orphan_paths(): avoid BUG message mwilck
2020-08-14  2:26   ` Benjamin Marzinski
2020-08-12 11:35 ` [PATCH v2 65/74] libmultipath: add update_pathvec_from_dm() mwilck
2020-08-14  5:08   ` Benjamin Marzinski
2020-08-12 11:35 ` [PATCH v2 66/74] libmultipath: update_pathvec_from_dm: handle pp->mpp mismatch mwilck
2020-08-14  5:08   ` Benjamin Marzinski
2020-08-12 11:35 ` [PATCH v2 71/74] multipath: use update_pathvec_from_dm() mwilck
2020-08-17 21:00   ` 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.