libmultipath: add get_uid fallback code for NVMe devices
[multipath-tools/.git] / libmultipath / discovery.c
index 1748eeb..bece67c 100644 (file)
@@ -1754,27 +1754,46 @@ get_vpd_uid(struct path * pp)
        return get_vpd_sysfs(parent, 0x83, pp->wwid, WWID_SIZE);
 }
 
-static ssize_t scsi_uid_fallback(struct path *pp, int path_state,
-                            const char **origin)
+static ssize_t uid_fallback(struct path *pp, int path_state,
+                           const char **origin, ssize_t old_len)
 {
-       ssize_t len = 0;
+       ssize_t len = old_len;
        int retrigger;
        struct config *conf;
 
        conf = get_multipath_config();
        retrigger = conf->retrigger_tries;
        put_multipath_config(conf);
-       if (pp->retriggers >= retrigger &&
-           !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) {
-               len = get_vpd_uid(pp);
-               *origin = "sysfs";
-               pp->uid_attribute = NULL;
-               if (len < 0 && path_state == PATH_UP) {
-                       condlog(1, "%s: failed to get sysfs uid: %s",
-                               pp->dev, strerror(-len));
-                       len = get_vpd_sgio(pp->fd, 0x83, pp->wwid,
-                                          WWID_SIZE);
-                       *origin = "sgio";
+       if (pp->retriggers >= retrigger) {
+               if (pp->bus == SYSFS_BUS_SCSI &&
+                   !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) {
+                       len = get_vpd_uid(pp);
+                       *origin = "sysfs";
+                       pp->uid_attribute = NULL;
+                       if (len < 0 && path_state == PATH_UP) {
+                               condlog(1, "%s: failed to get sysfs uid: %s",
+                                       pp->dev, strerror(-len));
+                               len = get_vpd_sgio(pp->fd, 0x83, pp->wwid,
+                                                  WWID_SIZE);
+                               *origin = "sgio";
+                       }
+               } else if (pp->bus == SYSFS_BUS_NVME) {
+                       char value[256];
+                       len = sysfs_attr_get_value(pp->udev, "wwid", value,
+                                                  sizeof(value));
+                       if (len <= 0)
+                               return -1;
+                       len = strlcpy(pp->wwid, value, WWID_SIZE);
+                       if (len >= WWID_SIZE) {
+                               len = fix_broken_nvme_wwid(pp, value,
+                                                          WWID_SIZE);
+                               if (len > 0)
+                                       return len;
+                               condlog(0, "%s: wwid overflow", pp->dev);
+                               len = WWID_SIZE;
+                       }
+                       *origin = "sysfs";
+                       pp->uid_attribute = NULL;
                }
        }
        return len;
@@ -1827,8 +1846,8 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev)
                        len = get_vpd_uid(pp);
                        origin = "sysfs";
                }
-               if (len <= 0 && pp->bus == SYSFS_BUS_SCSI)
-                       len = scsi_uid_fallback(pp, path_state, &origin);
+               if (len <= 0)
+                       len = uid_fallback(pp, path_state, &origin, len);
        }
        if ( len < 0 ) {
                condlog(1, "%s: failed to get %s uid: %s",
@@ -1914,11 +1933,12 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
        if (path_state == PATH_REMOVED)
                goto blank;
        else if (mask & DI_NOIO) {
-               /*
-                * Avoid any IO on the device itself.
-                * Behave like DI_CHECKER in the "path unavailable" case.
-                */
-               pp->chkrstate = pp->state = path_state;
+               if (mask & DI_CHECKER)
+                       /*
+                        * Avoid any IO on the device itself.
+                        * simply use the path_offline() return as its state
+                        */
+                       pp->chkrstate = pp->state = path_state;
                return PATHINFO_OK;
        }
 
@@ -1945,8 +1965,11 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
 
        if (mask & DI_CHECKER) {
                if (path_state == PATH_UP) {
-                       pp->chkrstate = pp->state = get_state(pp, conf, 0,
-                                                             path_state);
+                       int newstate = get_state(pp, conf, 0, path_state);
+                       if (newstate != PATH_PENDING ||
+                           pp->state == PATH_UNCHECKED ||
+                           pp->state == PATH_WILD)
+                               pp->chkrstate = pp->state = newstate;
                        if (pp->state == PATH_TIMEOUT)
                                pp->state = PATH_DOWN;
                        if (pp->state == PATH_UP && !pp->size) {
@@ -1966,8 +1989,12 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
        if ((mask & DI_WWID) && !strlen(pp->wwid)) {
                get_uid(pp, path_state, pp->udev);
                if (!strlen(pp->wwid)) {
-                       pp->initialized = INIT_MISSING_UDEV;
-                       pp->tick = conf->retrigger_delay;
+                       if (pp->bus == SYSFS_BUS_UNDEF)
+                               return PATHINFO_SKIPPED;
+                       if (pp->initialized != INIT_FAILED) {
+                               pp->initialized = INIT_MISSING_UDEV;
+                               pp->tick = conf->retrigger_delay;
+                       }
                        return PATHINFO_OK;
                }
                else
@@ -2000,7 +2027,7 @@ blank:
         * Recoverable error, for example faulty or offline path
         */
        pp->chkrstate = pp->state = PATH_DOWN;
-       if (pp->initialized == INIT_FAILED)
+       if (pp->initialized == INIT_NEW || pp->initialized == INIT_FAILED)
                memset(pp->wwid, 0, WWID_SIZE);
 
        return PATHINFO_OK;