libmultipath: add get_uid fallback code for NVMe devices
[multipath-tools/.git] / libmultipath / discovery.c
index 8ae170e..bece67c 100644 (file)
 #include "discovery.h"
 #include "prio.h"
 #include "defaults.h"
+#include "unaligned.h"
 #include "prioritizers/alua_rtpg.h"
+#include "foreign.h"
 
 int
 alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,
-                         char *wwid, int flag, struct path **pp_ptr)
+                         const char *wwid, int flag, struct path **pp_ptr)
 {
        int err = PATHINFO_FAILED;
        struct path * pp;
@@ -52,8 +54,8 @@ alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,
        if (!pp)
                return PATHINFO_FAILED;
 
-       if(wwid)
-               strncpy(pp->wwid, wwid, sizeof(pp->wwid));
+       if (wwid)
+               strlcpy(pp->wwid, wwid, sizeof(pp->wwid));
 
        if (safe_sprintf(pp->dev, "%s", devname)) {
                condlog(0, "pp->dev too small");
@@ -101,6 +103,7 @@ store_pathinfo (vector pathvec, struct config *conf,
        err = store_path(pathvec, pp);
        if (err)
                goto out;
+       pp->checkint = conf->checkint;
 
 out:
        if (err)
@@ -121,7 +124,7 @@ path_discover (vector pathvec, struct config * conf,
        if (!devname)
                return PATHINFO_FAILED;
 
-       pp = find_path_by_dev(pathvec, (char *)devname);
+       pp = find_path_by_dev(pathvec, devname);
        if (!pp) {
                char devt[BLK_DEV_SIZE];
                dev_t devnum = udev_device_get_devnum(udevice);
@@ -168,10 +171,11 @@ path_discovery (vector pathvec, int flag)
                if(devtype && !strncmp(devtype, "disk", 4)) {
                        total_paths++;
                        conf = get_multipath_config();
+                       pthread_cleanup_push(put_multipath_config, conf);
                        if (path_discover(pathvec, conf,
                                          udevice, flag) == PATHINFO_OK)
                                num_paths++;
-                       put_multipath_config(conf);
+                       pthread_cleanup_pop(1);
                }
                udev_device_unref(udevice);
        }
@@ -401,7 +405,7 @@ sysfs_get_tgt_nodename (struct path *pp, char * node)
        return 0;
 }
 
-int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name)
+int sysfs_get_host_adapter_name(const struct path *pp, char *adapter_name)
 {
        int proto_id;
 
@@ -427,7 +431,7 @@ int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name)
        return sysfs_get_host_pci_name(pp, adapter_name);
 }
 
-int sysfs_get_host_pci_name(struct path *pp, char *pci_name)
+int sysfs_get_host_pci_name(const struct path *pp, char *pci_name)
 {
        struct udev_device *hostdev, *parent;
        char host_name[HOST_NAME_LEN];
@@ -466,7 +470,7 @@ int sysfs_get_host_pci_name(struct path *pp, char *pci_name)
        return 1;
 }
 
-int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address)
+int sysfs_get_iscsi_ip_address(const struct path *pp, char *ip_address)
 {
        struct udev_device *hostdev;
        char host_name[HOST_NAME_LEN];
@@ -516,7 +520,7 @@ sysfs_get_asymmetric_access_state(struct path *pp, char *buff, int buflen)
                /* Parse error, ignore */
                return 0;
        }
-       return  preferred;
+       return !!preferred;
 }
 
 static void
@@ -658,7 +662,7 @@ sysfs_set_session_tmo(struct multipath *mpp, struct path *pp)
                } else {
                        snprintf(value, 11, "%u", mpp->fast_io_fail);
                        if (sysfs_attr_set_value(session_dev, "recovery_tmo",
-                                                value, 11) <= 0) {
+                                                value, strlen(value)) <= 0) {
                                condlog(3, "%s: Failed to set recovery_tmo, "
                                        " error %d", pp->dev, errno);
                        }
@@ -690,7 +694,7 @@ sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp)
        if (mpp->dev_loss) {
                snprintf(value, 11, "%u", mpp->dev_loss);
                if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout",
-                                        value, 11) <= 0)
+                                        value, strlen(value)) <= 0)
                        condlog(3, "%s: failed to update "
                                "I_T Nexus loss timeout, error %d",
                                pp->dev, errno);
@@ -707,7 +711,7 @@ sysfs_set_scsi_tmo (struct multipath *mpp, int checkint)
        int dev_loss_tmo = mpp->dev_loss;
 
        if (mpp->no_path_retry > 0) {
-               uint64_t no_path_retry_tmo = mpp->no_path_retry * checkint;
+               uint64_t no_path_retry_tmo = (uint64_t)mpp->no_path_retry * checkint;
 
                if (no_path_retry_tmo > MAX_DEV_LOSS_TMO)
                        no_path_retry_tmo = MAX_DEV_LOSS_TMO;
@@ -766,7 +770,7 @@ do_inq(int sg_fd, int cmddt, int evpd, unsigned int pg_op,
        io_hdr.dxferp = resp;
        io_hdr.cmdp = inqCmdBlk;
        io_hdr.sbp = sense_b;
-       io_hdr.timeout = DEF_TIMEOUT;
+       io_hdr.timeout = DEF_TIMEOUT * 1000;
 
        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
                return -1;
@@ -836,6 +840,8 @@ detect_alua(struct path * pp, struct config *conf)
 
 #define DEFAULT_SGIO_LEN 254
 
+/* Query VPD page @pg. Returns number of INQUIRY bytes
+   upon success and -1 upon failure. */
 static int
 sgio_get_vpd (unsigned char * buff, int maxlen, int fd, int pg)
 {
@@ -847,7 +853,7 @@ sgio_get_vpd (unsigned char * buff, int maxlen, int fd, int pg)
        }
 retry:
        if (0 == do_inq(fd, 0, 1, pg, buff, len)) {
-               len = buff[3] + (buff[2] << 8);
+               len = get_unaligned_be16(&buff[2]) + 4;
                if (len >= maxlen)
                        return len;
                if (len > DEFAULT_SGIO_LEN)
@@ -878,7 +884,7 @@ static int
 parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len)
 {
        char *p = NULL;
-       int len = in[3] + (in[2] << 8);
+       int len = get_unaligned_be16(&in[2]);
 
        if (len >= out_len) {
                condlog(2, "vpd pg80 overflow, %d/%d bytes required",
@@ -905,12 +911,12 @@ static int
 parse_vpd_pg83(const unsigned char *in, size_t in_len,
               char *out, size_t out_len)
 {
-       unsigned char *d;
-       unsigned char *vpd = NULL;
+       const unsigned char *d;
+       const unsigned char *vpd = NULL;
        int len = -ENODATA, vpd_type, vpd_len, prio = -1, i, naa_prio;
 
-       d = (unsigned char *)in + 4;
-       while (d < (unsigned char *)in + in_len) {
+       d = in + 4;
+       while (d < in + in_len) {
                /* Select 'association: LUN' */
                if ((d[1] & 0x30) != 0) {
                        d += d[3] + 4;
@@ -1027,7 +1033,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                                out[len] = '\0';
                        }
                } else if (vpd_type == 0x1) {
-                       unsigned char *p;
+                       const unsigned char *p;
                        int p_len;
 
                        out[0] = '1';
@@ -1078,7 +1084,7 @@ get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen)
                        pg, buff[1]);
                return -ENODATA;
        }
-       buff_len = (buff[2] << 8) + buff[3] + 4;
+       buff_len = get_unaligned_be16(&buff[2]) + 4;
        if (buff_len > 4096)
                condlog(3, "vpd pg%02x page truncated", pg);
 
@@ -1099,8 +1105,10 @@ get_vpd_sgio (int fd, int pg, char * str, int maxlen)
        unsigned char buff[4096];
 
        memset(buff, 0x0, 4096);
-       if (sgio_get_vpd(buff, 4096, fd, pg) <= 0) {
-               condlog(3, "failed to issue vpd inquiry for pg%02x",
+       if (sgio_get_vpd(buff, 4096, fd, pg) < 0) {
+               int lvl = pg == 0x80 || pg == 0x83 ? 3 : 4;
+
+               condlog(lvl, "failed to issue vpd inquiry for pg%02x",
                        pg);
                return -errno;
        }
@@ -1110,18 +1118,22 @@ get_vpd_sgio (int fd, int pg, char * str, int maxlen)
                        pg, buff[1]);
                return -ENODATA;
        }
-       buff_len = (buff[2] << 8) + buff[3] + 4;
-       if (buff_len > 4096)
+       buff_len = get_unaligned_be16(&buff[2]) + 4;
+       if (buff_len > 4096) {
                condlog(3, "vpd pg%02x page truncated", pg);
-
+               buff_len = 4096;
+       }
        if (pg == 0x80)
                len = parse_vpd_pg80(buff, str, maxlen);
        else if (pg == 0x83)
                len = parse_vpd_pg83(buff, buff_len, str, maxlen);
        else if (pg == 0xc9 && maxlen >= 8) {
-               len = buff_len < 8 ? -ENODATA :
-                       (buff_len <= maxlen ? buff_len : maxlen);
-               memcpy (str, buff, len);
+               if (buff_len < 8)
+                       len = -ENODATA;
+               else {
+                       len = (buff_len <= maxlen)? buff_len : maxlen;
+                       memcpy (str, buff, len);
+               }
        } else
                len = -ENOSYS;
 
@@ -1151,27 +1163,27 @@ scsi_sysfs_pathinfo (struct path * pp, vector hwtable)
                parent = udev_device_get_parent(parent);
        }
        if (!attr_path || pp->sg_id.host_no == -1)
-               return 1;
+               return PATHINFO_FAILED;
 
        if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0)
-               return 1;
+               return PATHINFO_FAILED;;
 
        condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
 
-       if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE) <= 0)
-               return 1;
+       if (sysfs_get_model(parent, pp->product_id, PATH_PRODUCT_SIZE) <= 0)
+               return PATHINFO_FAILED;;
 
        condlog(3, "%s: product = %s", pp->dev, pp->product_id);
 
-       if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) < 0)
-               return 1;
+       if (sysfs_get_rev(parent, pp->rev, PATH_REV_SIZE) < 0)
+               return PATHINFO_FAILED;;
 
        condlog(3, "%s: rev = %s", pp->dev, pp->rev);
 
        /*
         * set the hwe configlet pointer
         */
-       pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, pp->rev);
+       find_hwe(hwtable, pp->vendor_id, pp->product_id, pp->rev, pp->hwe);
 
        /*
         * host / bus / target / lun
@@ -1187,12 +1199,12 @@ scsi_sysfs_pathinfo (struct path * pp, vector hwtable)
         * target node name
         */
        if(sysfs_get_tgt_nodename(pp, pp->tgt_node_name))
-               return 1;
+               return PATHINFO_FAILED;
 
        condlog(3, "%s: tgt_node_name = %s",
                pp->dev, pp->tgt_node_name);
 
-       return 0;
+       return PATHINFO_OK;
 }
 
 static int
@@ -1200,30 +1212,34 @@ nvme_sysfs_pathinfo (struct path * pp, vector hwtable)
 {
        struct udev_device *parent;
        const char *attr_path = NULL;
+       const char *attr;
 
        attr_path = udev_device_get_sysname(pp->udev);
        if (!attr_path)
-               return 1;
+               return PATHINFO_FAILED;
 
        if (sscanf(attr_path, "nvme%dn%d",
                   &pp->sg_id.host_no,
                   &pp->sg_id.scsi_id) != 2)
-               return 1;
-       pp->sg_id.channel = 0;
-       pp->sg_id.lun = atoi(udev_device_get_sysattr_value(pp->udev, "nsid"));
+               return PATHINFO_FAILED;
 
-       parent = udev_device_get_parent(pp->udev);
+       parent = udev_device_get_parent_with_subsystem_devtype(pp->udev,
+                                                              "nvme", NULL);
        if (!parent)
-               return 1;
+               return PATHINFO_SKIPPED;
+
+       attr = udev_device_get_sysattr_value(pp->udev, "nsid");
+       pp->sg_id.lun = attr ? atoi(attr) : 0;
 
-       pp->sg_id.channel = atoi(udev_device_get_sysattr_value(parent, "cntlid"));
+       attr = udev_device_get_sysattr_value(parent, "cntlid");
+       pp->sg_id.channel = attr ? atoi(attr) : 0;
 
        snprintf(pp->vendor_id, SCSI_VENDOR_SIZE, "NVME");
-       snprintf(pp->product_id, SCSI_PRODUCT_SIZE, "%s",
+       snprintf(pp->product_id, PATH_PRODUCT_SIZE, "%s",
                 udev_device_get_sysattr_value(parent, "model"));
        snprintf(pp->serial, SERIAL_SIZE, "%s",
                 udev_device_get_sysattr_value(parent, "serial"));
-       snprintf(pp->rev, SCSI_REV_SIZE, "%s",
+       snprintf(pp->rev, PATH_REV_SIZE, "%s",
                 udev_device_get_sysattr_value(parent, "firmware_rev"));
 
        condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
@@ -1231,24 +1247,9 @@ nvme_sysfs_pathinfo (struct path * pp, vector hwtable)
        condlog(3, "%s: serial = %s", pp->dev, pp->serial);
        condlog(3, "%s: rev = %s", pp->dev, pp->rev);
 
-       pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL);
-
-       return 0;
-}
-
-static int
-rbd_sysfs_pathinfo (struct path * pp, vector hwtable)
-{
-       sprintf(pp->vendor_id, "Ceph");
-       sprintf(pp->product_id, "RBD");
+       find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL, pp->hwe);
 
-       condlog(3, "%s: vendor = %s product = %s", pp->dev, pp->vendor_id,
-               pp->product_id);
-       /*
-        * set the hwe configlet pointer
-        */
-       pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL);
-       return 0;
+       return PATHINFO_OK;
 }
 
 static int
@@ -1266,14 +1267,14 @@ ccw_sysfs_pathinfo (struct path * pp, vector hwtable)
                parent = udev_device_get_parent(parent);
        }
        if (!parent)
-               return 1;
+               return PATHINFO_FAILED;
 
        sprintf(pp->vendor_id, "IBM");
 
        condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
 
        if (sysfs_get_devtype(parent, attr_buff, FILE_NAME_SIZE) <= 0)
-               return 1;
+               return PATHINFO_FAILED;
 
        if (!strncmp(attr_buff, "3370", 4)) {
                sprintf(pp->product_id,"S/390 DASD FBA");
@@ -1288,7 +1289,7 @@ ccw_sysfs_pathinfo (struct path * pp, vector hwtable)
        /*
         * set the hwe configlet pointer
         */
-       pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL);
+       find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL, pp->hwe);
 
        /*
         * host / bus / target / lun
@@ -1307,7 +1308,7 @@ ccw_sysfs_pathinfo (struct path * pp, vector hwtable)
                        pp->sg_id.lun);
        }
 
-       return 0;
+       return PATHINFO_OK;
 }
 
 static int
@@ -1331,27 +1332,27 @@ cciss_sysfs_pathinfo (struct path * pp, vector hwtable)
                parent = udev_device_get_parent(parent);
        }
        if (!attr_path || pp->sg_id.host_no == -1)
-               return 1;
+               return PATHINFO_FAILED;
 
        if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0)
-               return 1;
+               return PATHINFO_FAILED;
 
        condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
 
-       if (sysfs_get_model(parent, pp->product_id, SCSI_PRODUCT_SIZE) <= 0)
-               return 1;
+       if (sysfs_get_model(parent, pp->product_id, PATH_PRODUCT_SIZE) <= 0)
+               return PATHINFO_FAILED;
 
        condlog(3, "%s: product = %s", pp->dev, pp->product_id);
 
-       if (sysfs_get_rev(parent, pp->rev, SCSI_REV_SIZE) <= 0)
-               return 1;
+       if (sysfs_get_rev(parent, pp->rev, PATH_REV_SIZE) <= 0)
+               return PATHINFO_FAILED;
 
        condlog(3, "%s: rev = %s", pp->dev, pp->rev);
 
        /*
         * set the hwe configlet pointer
         */
-       pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, pp->rev);
+       find_hwe(hwtable, pp->vendor_id, pp->product_id, pp->rev, pp->hwe);
 
        /*
         * host / bus / target / lun
@@ -1364,7 +1365,8 @@ cciss_sysfs_pathinfo (struct path * pp, vector hwtable)
                pp->sg_id.channel,
                pp->sg_id.scsi_id,
                pp->sg_id.lun);
-       return 0;
+
+       return PATHINFO_OK;
 }
 
 static int
@@ -1373,23 +1375,23 @@ common_sysfs_pathinfo (struct path * pp)
        dev_t devt;
 
        if (!pp)
-               return 1;
+               return PATHINFO_FAILED;
 
        if (!pp->udev) {
                condlog(4, "%s: udev not initialised", pp->dev);
-               return 1;
+               return PATHINFO_FAILED;
        }
        devt = udev_device_get_devnum(pp->udev);
        snprintf(pp->dev_t, BLK_DEV_SIZE, "%d:%d", major(devt), minor(devt));
 
-       condlog(3, "%s: dev_t = %s", pp->dev, pp->dev_t);
+       condlog(4, "%s: dev_t = %s", pp->dev, pp->dev_t);
 
        if (sysfs_get_size(pp, &pp->size))
-               return 1;
+               return PATHINFO_FAILED;
 
        condlog(3, "%s: size = %llu", pp->dev, pp->size);
 
-       return 0;
+       return PATHINFO_OK;
 }
 
 int
@@ -1433,7 +1435,7 @@ path_offline (struct path * pp)
        }
 
 
-       condlog(3, "%s: path state = %s", pp->dev, buff);
+       condlog(4, "%s: path state = %s", pp->dev, buff);
 
        if (pp->bus == SYSFS_BUS_SCSI) {
                if (!strncmp(buff, "offline", 7)) {
@@ -1467,8 +1469,10 @@ path_offline (struct path * pp)
 int
 sysfs_pathinfo(struct path * pp, vector hwtable)
 {
-       if (common_sysfs_pathinfo(pp))
-               return 1;
+       int r = common_sysfs_pathinfo(pp);
+
+       if (r != PATHINFO_OK)
+               return r;
 
        pp->bus = SYSFS_BUS_UNDEF;
        if (!strncmp(pp->dev,"cciss",5))
@@ -1477,33 +1481,25 @@ sysfs_pathinfo(struct path * pp, vector hwtable)
                pp->bus = SYSFS_BUS_CCW;
        if (!strncmp(pp->dev,"sd", 2))
                pp->bus = SYSFS_BUS_SCSI;
-       if (!strncmp(pp->dev,"rbd", 3))
-               pp->bus = SYSFS_BUS_RBD;
        if (!strncmp(pp->dev,"nvme", 4))
                pp->bus = SYSFS_BUS_NVME;
 
-       if (pp->bus == SYSFS_BUS_UNDEF)
-               return 0;
-       else if (pp->bus == SYSFS_BUS_SCSI) {
-               if (scsi_sysfs_pathinfo(pp, hwtable))
-                       return 1;
-       } else if (pp->bus == SYSFS_BUS_CCW) {
-               if (ccw_sysfs_pathinfo(pp, hwtable))
-                       return 1;
-       } else if (pp->bus == SYSFS_BUS_CCISS) {
-               if (cciss_sysfs_pathinfo(pp, hwtable))
-                       return 1;
-       } else if (pp->bus == SYSFS_BUS_RBD) {
-               if (rbd_sysfs_pathinfo(pp, hwtable))
-                       return 1;
-       } else if (pp->bus == SYSFS_BUS_NVME) {
-               if (nvme_sysfs_pathinfo(pp, hwtable))
-                       return 1;
+       switch (pp->bus) {
+       case SYSFS_BUS_SCSI:
+               return scsi_sysfs_pathinfo(pp, hwtable);
+       case SYSFS_BUS_CCW:
+               return ccw_sysfs_pathinfo(pp, hwtable);
+       case SYSFS_BUS_CCISS:
+               return cciss_sysfs_pathinfo(pp, hwtable);
+       case SYSFS_BUS_NVME:
+               return nvme_sysfs_pathinfo(pp, hwtable);
+       case SYSFS_BUS_UNDEF:
+       default:
+               return PATHINFO_OK;
        }
-       return 0;
 }
 
-static int
+static void
 scsi_ioctl_pathinfo (struct path * pp, struct config *conf, int mask)
 {
        struct udev_device *parent;
@@ -1513,7 +1509,7 @@ scsi_ioctl_pathinfo (struct path * pp, struct config *conf, int mask)
                detect_alua(pp, conf);
 
        if (!(mask & DI_SERIAL))
-               return 0;
+               return;
 
        parent = pp->udev;
        while (parent) {
@@ -1532,27 +1528,24 @@ scsi_ioctl_pathinfo (struct path * pp, struct config *conf, int mask)
                parent = udev_device_get_parent(parent);
        }
        if (!attr_path || pp->sg_id.host_no == -1)
-               return 0;
+               return;
 
        if (get_vpd_sysfs(parent, 0x80, pp->serial, SERIAL_SIZE) <= 0) {
                if (get_serial(pp->serial, SERIAL_SIZE, pp->fd)) {
-                       condlog(2, "%s: fail to get serial", pp->dev);
-                       return 0;
+                       condlog(3, "%s: fail to get serial", pp->dev);
+                       return;
                }
        }
 
        condlog(3, "%s: serial = %s", pp->dev, pp->serial);
-       return 0;
+       return;
 }
 
-static int
-cciss_ioctl_pathinfo (struct path * pp, int mask)
+static void
+cciss_ioctl_pathinfo(struct path *pp)
 {
-       if (mask & DI_SERIAL) {
-               get_serial(pp->serial, SERIAL_SIZE, pp->fd);
-               condlog(3, "%s: serial = %s", pp->dev, pp->serial);
-       }
-       return 0;
+       get_serial(pp->serial, SERIAL_SIZE, pp->fd);
+       condlog(3, "%s: serial = %s", pp->dev, pp->serial);
 }
 
 int
@@ -1561,8 +1554,6 @@ get_state (struct path * pp, struct config *conf, int daemon, int oldstate)
        struct checker * c = &pp->checker;
        int state;
 
-       condlog(3, "%s: get_state", pp->dev);
-
        if (!checker_selected(c)) {
                if (daemon) {
                        if (pathinfo(pp, conf, DI_SYSFS) != PATHINFO_OK) {
@@ -1599,8 +1590,8 @@ get_state (struct path * pp, struct config *conf, int daemon, int oldstate)
                checker_name(c), checker_state_name(state));
        if (state != PATH_UP && state != PATH_GHOST &&
            strlen(checker_message(c)))
-               condlog(3, "%s: checker msg is \"%s\"",
-                       pp->dev, checker_message(c));
+               condlog(3, "%s: %s checker%s",
+                       pp->dev, checker_name(c), checker_message(c));
        return state;
 }
 
@@ -1609,6 +1600,8 @@ get_prio (struct path * pp)
 {
        struct prio * p;
        struct config *conf;
+       int checker_timeout;
+       int old_prio;
 
        if (!pp)
                return 0;
@@ -1616,9 +1609,10 @@ get_prio (struct path * pp)
        p = &pp->prio;
        if (!prio_selected(p)) {
                conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
                select_detect_prio(conf, pp);
                select_prio(conf, pp);
-               put_multipath_config(conf);
+               pthread_cleanup_pop(1);
                if (!prio_selected(p)) {
                        condlog(3, "%s: no prio selected", pp->dev);
                        pp->priority = PRIO_UNDEF;
@@ -1626,14 +1620,16 @@ get_prio (struct path * pp)
                }
        }
        conf = get_multipath_config();
-       pp->priority = prio_getprio(p, pp, conf->checker_timeout);
+       checker_timeout = conf->checker_timeout;
        put_multipath_config(conf);
+       old_prio = pp->priority;
+       pp->priority = prio_getprio(p, pp, checker_timeout);
        if (pp->priority < 0) {
                condlog(3, "%s: %s prio error", pp->dev, prio_name(p));
                pp->priority = PRIO_UNDEF;
                return 1;
        }
-       condlog(3, "%s: %s prio = %u",
+       condlog((old_prio == pp->priority ? 4 : 3), "%s: %s prio = %u",
                pp->dev, prio_name(p), pp->priority);
        return 0;
 }
@@ -1740,53 +1736,6 @@ get_udev_uid(struct path * pp, char *uid_attribute, struct udev_device *udev)
        return len;
 }
 
-static int
-get_rbd_uid(struct path * pp)
-{
-       struct udev_device *rbd_bus_dev;
-       int ret, rbd_bus_id;
-       const char *pool, *image, *snap;
-       char sysfs_path[PATH_SIZE];
-       uint64_t snap_id, max_snap_id = -3;
-
-       ret = sscanf(pp->dev, "rbd%d", &rbd_bus_id);
-       if (ret != 1)
-               return -EINVAL;
-
-       snprintf(sysfs_path, sizeof(sysfs_path), "/sys/bus/rbd/devices/%d",
-                rbd_bus_id);
-       rbd_bus_dev = udev_device_new_from_syspath(udev, sysfs_path);
-       if (!rbd_bus_dev)
-               return -ENODEV;
-
-       ret = -EINVAL;
-       pool = udev_device_get_sysattr_value(rbd_bus_dev, "pool_id");
-       if (!pool)
-               goto free_dev;
-
-       image = udev_device_get_sysattr_value(rbd_bus_dev, "image_id");
-       if (!image)
-               goto free_dev;
-
-       snap = udev_device_get_sysattr_value(rbd_bus_dev, "snap_id");
-       if (!snap)
-               goto free_dev;
-       snap_id = strtoull(snap, NULL, 19);
-       if (snap_id >= max_snap_id)
-               ret = snprintf(pp->wwid, WWID_SIZE, "%s-%s", pool, image);
-       else
-               ret = snprintf(pp->wwid, WWID_SIZE, "%s-%s-%s", pool,
-                              image, snap);
-       if (ret >= WWID_SIZE) {
-               condlog(0, "%s: wwid overflow", pp->dev);
-               ret = -EOVERFLOW;
-       }
-
-free_dev:
-       udev_device_unref(rbd_bus_dev);
-       return ret;
-}
-
 static int
 get_vpd_uid(struct path * pp)
 {
@@ -1799,9 +1748,57 @@ get_vpd_uid(struct path * pp)
                parent = udev_device_get_parent(parent);
        }
 
+       if (!parent)
+               return -EINVAL;
+
        return get_vpd_sysfs(parent, 0x83, pp->wwid, WWID_SIZE);
 }
 
+static ssize_t uid_fallback(struct path *pp, int path_state,
+                           const char **origin, ssize_t old_len)
+{
+       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) {
+               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;
+}
+
 int
 get_uid (struct path * pp, int path_state, struct udev_device *udev)
 {
@@ -1812,13 +1809,9 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev)
 
        if (!pp->uid_attribute && !pp->getuid) {
                conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
                select_getuid(conf, pp);
-               put_multipath_config(conf);
-       }
-
-       if (!udev) {
-               condlog(1, "%s: no udev information", pp->dev);
-               return 1;
+               pthread_cleanup_pop(1);
        }
 
        memset(pp->wwid, 0, WWID_SIZE);
@@ -1839,13 +1832,9 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev)
                } else
                        len = strlen(pp->wwid);
                origin = "callout";
-       } else if (pp->bus == SYSFS_BUS_RBD) {
-               len = get_rbd_uid(pp);
-               origin = "sysfs";
        } else {
-               int retrigger;
 
-               if (pp->uid_attribute) {
+               if (udev && pp->uid_attribute) {
                        len = get_udev_uid(pp, pp->uid_attribute, udev);
                        origin = "udev";
                        if (len <= 0)
@@ -1853,31 +1842,18 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev)
                                        "%s: failed to get udev uid: %s",
                                        pp->dev, strerror(-len));
 
-               } else {
+               } else if (pp->bus == SYSFS_BUS_SCSI) {
                        len = get_vpd_uid(pp);
                        origin = "sysfs";
                }
-               conf = get_multipath_config();
-               retrigger = conf->retrigger_tries;
-               put_multipath_config(conf);
-               if (len <= 0 && 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 (len <= 0)
+                       len = uid_fallback(pp, path_state, &origin, len);
        }
        if ( len < 0 ) {
                condlog(1, "%s: failed to get %s uid: %s",
                        pp->dev, origin, strerror(-len));
                memset(pp->wwid, 0x0, WWID_SIZE);
+               return 1;
        } else {
                /* Strip any trailing blanks */
                c = strchr(pp->wwid, '\0');
@@ -1905,15 +1881,25 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
         * limited by DI_BLACKLIST and occurs before this debug
         * message with the mask value.
         */
-       if (pp->udev && filter_property(conf, pp->udev) > 0)
-               return PATHINFO_SKIPPED;
+       if (pp->udev) {
+               const char *hidden =
+                       udev_device_get_sysattr_value(pp->udev, "hidden");
+
+               if (hidden && !strcmp(hidden, "1")) {
+                       condlog(4, "%s: hidden", pp->dev);
+                       return PATHINFO_SKIPPED;
+               }
+               if (is_claimed_by_foreign(pp->udev) ||
+                   filter_property(conf, pp->udev, 4) > 0)
+                       return PATHINFO_SKIPPED;
+       }
 
        if (filter_devnode(conf->blist_devnode,
                           conf->elist_devnode,
                           pp->dev) > 0)
                return PATHINFO_SKIPPED;
 
-       condlog(3, "%s: mask = 0x%x", pp->dev, mask);
+       condlog(4, "%s: mask = 0x%x", pp->dev, mask);
 
        /*
         * Sanity check: we need the device number to
@@ -1928,25 +1914,31 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
        /*
         * fetch info available in sysfs
         */
-       if (mask & DI_SYSFS && sysfs_pathinfo(pp, conf->hwtable))
-               return PATHINFO_FAILED;
+       if (mask & DI_SYSFS) {
+               int rc = sysfs_pathinfo(pp, conf->hwtable);
+
+               if (rc != PATHINFO_OK)
+                       return rc;
+       }
 
        if (mask & DI_BLACKLIST && mask & DI_SYSFS) {
                if (filter_device(conf->blist_device, conf->elist_device,
-                                 pp->vendor_id, pp->product_id) > 0) {
+                                 pp->vendor_id, pp->product_id, pp->dev) > 0 ||
+                   filter_protocol(conf->blist_protocol, conf->elist_protocol,
+                                   pp) > 0)
                        return PATHINFO_SKIPPED;
-               }
        }
 
        path_state = path_offline(pp);
        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;
        }
 
@@ -1965,21 +1957,19 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
        if (mask & DI_SERIAL)
                get_geometry(pp);
 
-       if (path_state == PATH_UP && pp->bus == SYSFS_BUS_SCSI &&
-           scsi_ioctl_pathinfo(pp, conf, mask))
-               goto blank;
+       if (path_state == PATH_UP && pp->bus == SYSFS_BUS_SCSI)
+               scsi_ioctl_pathinfo(pp, conf, mask);
 
-       if (pp->bus == SYSFS_BUS_CCISS &&
-           cciss_ioctl_pathinfo(pp, mask))
-               goto blank;
+       if (pp->bus == SYSFS_BUS_CCISS && mask & DI_SERIAL)
+               cciss_ioctl_pathinfo(pp);
 
        if (mask & DI_CHECKER) {
                if (path_state == PATH_UP) {
-                       pp->chkrstate = pp->state = get_state(pp, conf, 0,
-                                                             path_state);
-                       if (pp->state == PATH_UNCHECKED ||
+                       int newstate = get_state(pp, conf, 0, path_state);
+                       if (newstate != PATH_PENDING ||
+                           pp->state == PATH_UNCHECKED ||
                            pp->state == PATH_WILD)
-                               goto blank;
+                               pp->chkrstate = pp->state = newstate;
                        if (pp->state == PATH_TIMEOUT)
                                pp->state = PATH_DOWN;
                        if (pp->state == PATH_UP && !pp->size) {
@@ -1999,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
@@ -2032,9 +2026,9 @@ blank:
        /*
         * Recoverable error, for example faulty or offline path
         */
-       memset(pp->wwid, 0, WWID_SIZE);
        pp->chkrstate = pp->state = PATH_DOWN;
-       pp->initialized = INIT_FAILED;
+       if (pp->initialized == INIT_NEW || pp->initialized == INIT_FAILED)
+               memset(pp->wwid, 0, WWID_SIZE);
 
        return PATHINFO_OK;
 }