diff mbox series

[v2,19/20] multipathd: check paths in order by mpp

Message ID 20240717181106.2173527-20-bmarzins@redhat.com (mailing list archive)
State Not Applicable, archived
Delegated to: christophe varoqui
Headers show
Series path checker refactor and misc fixes | expand

Commit Message

Benjamin Marzinski July 17, 2024, 6:11 p.m. UTC
Instead of checking all of the paths in vecs->pathvec in order, first
check all the paths in each multipath device. Then check the
uninitialized paths.

One issue with checking paths this way is that the multipath device can
be resynced or even removed while a path is being checked. The path can
also be removed. If there is any change to the multipath device,
multipathd needs to loop through its paths again, because the current
indexes may not longer be valid.

To do this change mpp->is_checked to an int called mpp->synced_count,
and increment it whenever the multipath device gets resynced. After each
path is checked, make sure that the multipath device still exists, that
mpp->synced_count hasn't changed. If either has happened, restart
checking at the current index in mpvec (which will either be the same
mpp if it was just resynced, or the next mpp if the last one was
deleted). Since the multipath device is resynced when its first path is
checked, this restart will happen to every multipath device at least
once per loop. But the paths themselves aren't rechecked, so it's not
much overhead.

If resyncing a multipath device fails in do_check_mpp(), there may be
path devices that have pp->mpp set, but are no longer in one of the
multipath device's pathgroups, and thus will not get checked. This
almost definitely means the multipath device was deleted. If
do_check_mpp() failed to resync the device, but it wasn't deleted, it
will get called again in max_checkint seconds even if it no longer has
mpp->pg set, and the paths will get checked again after that.

Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 libmultipath/structs.h     |  2 +-
 libmultipath/structs_vec.c |  1 +
 multipathd/main.c          | 54 ++++++++++++++++++++++++++++++++------
 3 files changed, 48 insertions(+), 9 deletions(-)
diff mbox series

Patch

diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 457d7836..91509881 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -455,7 +455,7 @@  struct multipath {
 	int ghost_delay_tick;
 	int queue_mode;
 	unsigned int sync_tick;
-	bool is_checked;
+	int synced_count;
 	uid_t uid;
 	gid_t gid;
 	mode_t mode;
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 345d3069..cf9c6fe8 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -513,6 +513,7 @@  update_multipath_table (struct multipath *mpp, vector pathvec, int flags)
 	conf = get_multipath_config();
 	mpp->sync_tick = conf->max_checkint;
 	put_multipath_config(conf);
+	mpp->synced_count++;
 
 	r = libmp_mapinfo(DM_MAP_BY_NAME | MAPINFO_MPATH_ONLY,
 			  (mapid_t) { .str = mpp->alias },
diff --git a/multipathd/main.c b/multipathd/main.c
index 161c6962..5eefa475 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -2356,7 +2356,6 @@  do_sync_mpp(struct vectors * vecs, struct multipath *mpp)
 	int i, ret;
 	struct path *pp;
 
-	mpp->is_checked = true;
 	ret = update_multipath_strings(mpp, vecs->pathvec);
 	if (ret != DMP_OK) {
 		condlog(1, "%s: %s", mpp->alias, ret == DMP_NOT_FOUND ?
@@ -2431,7 +2430,7 @@  do_check_path (struct vectors * vecs, struct path * pp)
 		return handle_path_wwid_change(pp, vecs)? CHECK_PATH_REMOVED :
 							  CHECK_PATH_SKIPPED;
 	}
-	if (!pp->mpp->is_checked) {
+	if (pp->mpp->synced_count == 0) {
 		do_sync_mpp(vecs, pp->mpp);
 		/* if update_multipath_strings orphaned the path, quit early */
 		if (!pp->mpp)
@@ -2788,18 +2787,57 @@  check_paths(struct vectors *vecs, unsigned int ticks, int *num_paths_p)
 {
 	unsigned int paths_checked = 0;
 	struct timespec diff_time, start_time, end_time;
+	struct multipath *mpp;
 	struct path *pp;
 	int i, rc;
 
 	get_monotonic_time(&start_time);
+
+	vector_foreach_slot(vecs->mpvec, mpp, i) {
+		struct pathgroup *pgp;
+		struct path *pp;
+		int j, k;
+		bool check_for_waiters = false;
+		/* maps can be rechecked, so this is not always 0 */
+		int synced_count = mpp->synced_count;
+
+		vector_foreach_slot (mpp->pg, pgp, j) {
+			vector_foreach_slot (pgp->paths, pp, k) {
+				if (!pp->mpp || pp->is_checked)
+					continue;
+				pp->is_checked = true;
+				rc = check_path(vecs, pp, ticks,
+						start_time.tv_sec);
+				if (rc == CHECK_PATH_CHECKED)
+					(*num_paths_p)++;
+				if (++paths_checked % 128 == 0)
+					check_for_waiters = true;
+				/*
+				 * mpp has been removed or resynced. Path may
+				 * have been removed.
+				 */
+				if (VECTOR_SLOT(vecs->mpvec, i) != mpp ||
+				    synced_count != mpp->synced_count) {
+					i--;
+					goto next_mpp;
+				}
+			}
+		}
+next_mpp:
+		if (check_for_waiters &&
+		    (lock_has_waiters(&vecs->lock) || waiting_clients())) {
+			get_monotonic_time(&end_time);
+			timespecsub(&end_time, &start_time, &diff_time);
+			if (diff_time.tv_sec > 0)
+				return CHECKER_RUNNING;
+		}
+	}
 	vector_foreach_slot(vecs->pathvec, pp, i) {
-		if (pp->is_checked)
+		if (pp->mpp || pp->is_checked)
 			continue;
 		pp->is_checked = true;
-		if (pp->mpp)
-			rc = check_path(vecs, pp, ticks, start_time.tv_sec);
-		else
-			rc = handle_uninitialized_path(vecs, pp, ticks);
+
+		rc = handle_uninitialized_path(vecs, pp, ticks);
 		if (rc == CHECK_PATH_REMOVED)
 			i--;
 		else if (rc == CHECK_PATH_CHECKED)
@@ -2872,7 +2910,7 @@  checkerloop (void *ap)
 			lock(&vecs->lock);
 			pthread_testcancel();
 			vector_foreach_slot(vecs->mpvec, mpp, i)
-				mpp->is_checked = false;
+				mpp->synced_count = 0;
 			if (checker_state == CHECKER_STARTING) {
 				vector_foreach_slot(vecs->mpvec, mpp, i)
 					sync_mpp(vecs, mpp, ticks);