If a path in a mulitpath device is offline while multipathd is
reconfigured, it will get added to the updated multipath device, just
like it was in the old multipath device. However the device will still
be in the INIT_NEW state because it can't get initilized while offline.
This is different than the INIT_PARTIAL state because the path was
discovered in path_discovery(). INIT_PARTIAL is for paths that
multipathd did not discover in path_discovery() or receive a uevent for,
but are part of a multipath device that was added, and which should
receive a uevent shortly. There is no reason to expect a uevent for
these offline paths.

When the path comes back online, multipathd will run the checker and
prioritizer on it. The only two pathinfo checks that won't happen
are the DI_WWID and DI_IOCTL ones. Modify pathinfo() to make sure
that if DI_IOCTL was skipped for offline paths, it gets called the
next time pathinfo() is called after the fd can be opened. Also,
make sure that when one of these offline paths becomes usable, its
WWID is rechecked. With those changes, all the DI_ALL checks will
be accounted for, and the path can be marked INIT_OK.

Signed-off-by: Benjamin Marzinski <[email protected]>
---
 libmultipath/discovery.c |  2 ++
 multipathd/main.c        | 53 ++++++++++++++++++++++++++++++++++------
 2 files changed, 48 insertions(+), 7 deletions(-)

diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index 0c5e5ca6..0efb8213 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -2585,6 +2585,8 @@ blank:
         * Recoverable error, for example faulty or offline path
         */
        pp->chkrstate = pp->state = PATH_DOWN;
+       if (mask & DI_IOCTL && pp->ioctl_info == IOCTL_INFO_NOT_REQUESTED)
+               pp->ioctl_info = IOCTL_INFO_SKIPPED;
        if (pp->initialized == INIT_NEW || pp->initialized == INIT_FAILED)
                memset(pp->wwid, 0, WWID_SIZE);
 
diff --git a/multipathd/main.c b/multipathd/main.c
index 61e0ea34..90472d69 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -2572,6 +2572,26 @@ static int sync_mpp(struct vectors *vecs, struct 
multipath *mpp, unsigned int ti
        return do_sync_mpp(vecs, mpp);
 }
 
+/*
+ * pp->wwid should never be empty when this function is called, but if it
+ * is, this function can set it.
+ */
+static bool new_path_wwid_changed(struct path *pp, int state)
+{
+       char wwid[WWID_SIZE];
+
+       strlcpy(wwid, pp->wwid, WWID_SIZE);
+       if (get_uid(pp, state, pp->udev, 1) != 0) {
+               strlcpy(pp->wwid, wwid, WWID_SIZE);
+               return false;
+       }
+       if (strlen(wwid) && strncmp(wwid, pp->wwid, WWID_SIZE) != 0) {
+               strlcpy(pp->wwid, wwid, WWID_SIZE);
+               return true;
+       }
+       return false;
+}
+
 static int
 update_path_state (struct vectors * vecs, struct path * pp)
 {
@@ -2601,14 +2621,33 @@ update_path_state (struct vectors * vecs, struct path * 
pp)
                return CHECK_PATH_SKIPPED;
        }
 
-       if (pp->recheck_wwid == RECHECK_WWID_ON &&
-           (newstate == PATH_UP || newstate == PATH_GHOST) &&
+       if ((newstate == PATH_UP || newstate == PATH_GHOST) &&
            ((pp->state != PATH_UP && pp->state != PATH_GHOST) ||
-            pp->dmstate == PSTATE_FAILED) &&
-           check_path_wwid_change(pp)) {
-               condlog(0, "%s: path wwid change detected. Removing", pp->dev);
-               return handle_path_wwid_change(pp, vecs)? CHECK_PATH_REMOVED :
-                                                         CHECK_PATH_SKIPPED;
+            pp->dmstate == PSTATE_FAILED)) {
+               bool wwid_changed = false;
+
+               if (pp->initialized == INIT_NEW) {
+                       /*
+                        * Path was added to map while offline, mark it as
+                        * initialized.
+                        * DI_SYSFS was checked when the path was added
+                        * DI_IOCTL was checked when the checker was selected
+                        * DI_CHECKER just got checked
+                        * DI_WWID is about to be checked
+                        * DI_PRIO will get checked at the end of this checker
+                        * loop
+                        */
+                       pp->initialized = INIT_OK;
+                       wwid_changed = new_path_wwid_changed(pp, newstate);
+               } else if (pp->recheck_wwid == RECHECK_WWID_ON)
+                       wwid_changed = check_path_wwid_change(pp);
+               if (wwid_changed) {
+                       condlog(0, "%s: path wwid change detected. Removing",
+                               pp->dev);
+                       return handle_path_wwid_change(pp, vecs)
+                                      ? CHECK_PATH_REMOVED
+                                      : CHECK_PATH_SKIPPED;
+               }
        }
        if ((newstate != PATH_UP && newstate != PATH_GHOST &&
             newstate != PATH_PENDING) && (pp->state == PATH_DELAYED)) {
-- 
2.50.1


Reply via email to