@@ -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;
@@ -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 },
@@ -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);
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(-)