45d01a
---
45d01a
 libmultipath/checkers.c    |   19 +++-
45d01a
 libmultipath/checkers.h    |    3 
45d01a
 libmultipath/discovery.c   |  183 +++++++++++++++++++++++++++++++++++++++------
45d01a
 libmultipath/discovery.h   |    2 
45d01a
 libmultipath/hwtable.c     |   10 ++
45d01a
 libmultipath/structs.h     |    1 
45d01a
 libmultipath/uevent.c      |    2 
45d01a
 multipath/multipath.conf.5 |    3 
45d01a
 multipathd/main.c          |   27 ------
45d01a
 9 files changed, 194 insertions(+), 56 deletions(-)
45d01a
45d01a
Index: multipath-tools-130222/libmultipath/discovery.c
45d01a
===================================================================
45d01a
--- multipath-tools-130222.orig/libmultipath/discovery.c
45d01a
+++ multipath-tools-130222/libmultipath/discovery.c
45d01a
@@ -13,6 +13,7 @@
45d01a
 #include <libgen.h>
45d01a
 #include <libudev.h>
45d01a
 #include <libdevmapper.h>
45d01a
+#include <ctype.h>
45d01a
 
45d01a
 #include "checkers.h"
45d01a
 #include "vector.h"
45d01a
@@ -881,6 +882,46 @@ scsi_sysfs_pathinfo (struct path * pp)
45d01a
 }
45d01a
 
45d01a
 static int
45d01a
+nvme_sysfs_pathinfo (struct path * pp)
45d01a
+{
45d01a
+	struct udev_device *parent;
45d01a
+	const char *attr_path = NULL;
45d01a
+
45d01a
+
45d01a
+	attr_path = udev_device_get_sysname(pp->udev);
45d01a
+	if (!attr_path)
45d01a
+		return 1;
45d01a
+
45d01a
+	if (sscanf(attr_path, "nvme%dn%d",
45d01a
+		   &pp->sg_id.host_no,
45d01a
+		   &pp->sg_id.scsi_id) != 2)
45d01a
+		return 1;
45d01a
+	pp->sg_id.channel = 0;
45d01a
+	pp->sg_id.lun = 0;
45d01a
+
45d01a
+	parent = udev_device_get_parent(pp->udev);
45d01a
+	if (!parent)
45d01a
+		return 1;
45d01a
+
45d01a
+	snprintf(pp->vendor_id, SCSI_VENDOR_SIZE, "NVME");
45d01a
+	snprintf(pp->product_id, SCSI_PRODUCT_SIZE, "%s",
45d01a
+		 udev_device_get_sysattr_value(parent, "model"));
45d01a
+	snprintf(pp->serial, SERIAL_SIZE, "%s",
45d01a
+		 udev_device_get_sysattr_value(parent, "serial"));
45d01a
+	snprintf(pp->rev, SCSI_REV_SIZE, "%s",
45d01a
+		 udev_device_get_sysattr_value(parent, "firmware_rev"));
45d01a
+
45d01a
+	condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
45d01a
+	condlog(3, "%s: product = %s", pp->dev, pp->product_id);
45d01a
+	condlog(3, "%s: serial = %s", pp->dev, pp->serial);
45d01a
+	condlog(3, "%s: rev = %s", pp->dev, pp->rev);
45d01a
+
45d01a
+	pp->hwe = find_hwe(conf->hwtable, pp->vendor_id, pp->product_id, NULL);
45d01a
+
45d01a
+	return 0;
45d01a
+}
45d01a
+
45d01a
+static int
45d01a
 rbd_sysfs_pathinfo (struct path * pp)
45d01a
 {
45d01a
 	sprintf(pp->vendor_id, "Ceph");
45d01a
@@ -1040,14 +1081,20 @@ path_offline (struct path * pp)
45d01a
 {
45d01a
 	struct udev_device * parent;
45d01a
 	char buff[SCSI_STATE_SIZE];
45d01a
+	const char *subsys_type;
45d01a
 
45d01a
-	if (pp->bus != SYSFS_BUS_SCSI)
45d01a
+	if (pp->bus == SYSFS_BUS_SCSI)
45d01a
+		subsys_type = "scsi";
45d01a
+	else if (pp->bus == SYSFS_BUS_NVME)
45d01a
+		subsys_type = "nvme";
45d01a
+	else
45d01a
 		return PATH_UP;
45d01a
 
45d01a
 	parent = pp->udev;
45d01a
 	while (parent) {
45d01a
 		const char *subsys = udev_device_get_subsystem(parent);
45d01a
-		if (subsys && !strncmp(subsys, "scsi", 4))
45d01a
+		if (subsys && !strncmp(subsys, subsys_type,
45d01a
+		    		       strlen(subsys_type)))
45d01a
 			break;
45d01a
 		parent = udev_device_get_parent(parent);
45d01a
 	}
45d01a
@@ -1063,15 +1110,30 @@ path_offline (struct path * pp)
45d01a
 
45d01a
 	condlog(3, "%s: path state = %s", pp->dev, buff);
45d01a
 
45d01a
-	if (!strncmp(buff, "offline", 7)) {
45d01a
-		pp->offline = 1;
45d01a
-		return PATH_DOWN;
45d01a
+	if (pp->bus == SYSFS_BUS_SCSI) {
45d01a
+		if (!strncmp(buff, "offline", 7)) {
45d01a
+			pp->offline = 1;
45d01a
+			return PATH_DOWN;
45d01a
+		}
45d01a
+		pp->offline = 0;
45d01a
+		if (!strncmp(buff, "blocked", 7) ||
45d01a
+		    !strncmp(buff, "quiesce", 7))
45d01a
+			return PATH_PENDING;
45d01a
+		else if (!strncmp(buff, "running", 7))
45d01a
+			return PATH_UP;
45d01a
+	}
45d01a
+	else if (pp->bus == SYSFS_BUS_NVME) {
45d01a
+		if (!strncmp(buff, "dead", 4)) {
45d01a
+			pp->offline = 1;
45d01a
+			return PATH_DOWN;
45d01a
+		}
45d01a
+		pp->offline = 0;
45d01a
+		if (!strncmp(buff, "new", 3) ||
45d01a
+		    !strncmp(buff, "deleting", 8))
45d01a
+			return PATH_PENDING;
45d01a
+		else if (!strncmp(buff, "live", 4))
45d01a
+			return PATH_UP;
45d01a
 	}
45d01a
-	pp->offline = 0;
45d01a
-	if (!strncmp(buff, "blocked", 7) || !strncmp(buff, "quiesce", 7))
45d01a
-		return PATH_PENDING;
45d01a
-	else if (!strncmp(buff, "running", 7))
45d01a
-		return PATH_UP;
45d01a
 
45d01a
 	return PATH_DOWN;
45d01a
 }
45d01a
@@ -1091,6 +1153,8 @@ sysfs_pathinfo(struct path * pp)
45d01a
 		pp->bus = SYSFS_BUS_SCSI;
45d01a
 	if (!strncmp(pp->dev,"rbd", 3))
45d01a
 		pp->bus = SYSFS_BUS_RBD;
45d01a
+	if (!strncmp(pp->dev,"nvme", 4))
45d01a
+		pp->bus = SYSFS_BUS_NVME;
45d01a
 
45d01a
 	if (pp->bus == SYSFS_BUS_UNDEF)
45d01a
 		return 0;
45d01a
@@ -1106,6 +1170,9 @@ sysfs_pathinfo(struct path * pp)
45d01a
 	} else if (pp->bus == SYSFS_BUS_RBD) {
45d01a
 		if (rbd_sysfs_pathinfo(pp))
45d01a
 			return 1;
45d01a
+	} else if (pp->bus == SYSFS_BUS_NVME) {
45d01a
+		if (nvme_sysfs_pathinfo(pp))
45d01a
+			return 1;
45d01a
 	}
45d01a
 	return 0;
45d01a
 }
45d01a
@@ -1132,7 +1199,7 @@ cciss_ioctl_pathinfo (struct path * pp,
45d01a
 }
45d01a
 
45d01a
 int
45d01a
-get_state (struct path * pp, int daemon)
45d01a
+get_state (struct path * pp, int daemon, int oldstate)
45d01a
 {
45d01a
 	struct checker * c = &pp->checker;
45d01a
 	int state;
45d01a
@@ -1171,8 +1238,9 @@ get_state (struct path * pp, int daemon)
45d01a
 	    (pp->bus != SYSFS_BUS_SCSI ||
45d01a
 	     sysfs_get_timeout(pp, &(c->timeout))))
45d01a
 		c->timeout = DEF_TIMEOUT;
45d01a
-	state = checker_check(c);
45d01a
-	condlog(3, "%s: state = %s", pp->dev, checker_state_name(state));
45d01a
+	state = checker_check(c, oldstate);
45d01a
+	condlog(3, "%s: %s state = %s", pp->dev,
45d01a
+		checker_name(c), checker_state_name(state));
45d01a
 	if (state != PATH_UP && state != PATH_GHOST &&
45d01a
 	    strlen(checker_message(c)))
45d01a
 		condlog(3, "%s: checker msg is \"%s\"",
45d01a
@@ -1256,6 +1324,82 @@ free_dev:
45d01a
 	return ret;
45d01a
 }
45d01a
 
45d01a
+/*
45d01a
+ * Mangle string of length *len starting at start
45d01a
+ * by removing character sequence "00" (hex for a 0 byte),
45d01a
+ * starting at end, backwards.
45d01a
+ * Changes the value of *len if characters were removed.
45d01a
+ * Returns a pointer to the position where "end" was moved to.
45d01a
+ */
45d01a
+static char *
45d01a
+skip_zeroes_backward(char* start, int *len, char *end)
45d01a
+{
45d01a
+	char *p = end;
45d01a
+
45d01a
+	while (p >= start + 2 && *(p - 1) == '0' && *(p - 2) == '0')
45d01a
+		p -= 2;
45d01a
+
45d01a
+	if (p == end)
45d01a
+		return p;
45d01a
+
45d01a
+	memmove(p, end, start + *len + 1 - end);
45d01a
+	*len -= end - p;
45d01a
+
45d01a
+	return p;
45d01a
+}
45d01a
+
45d01a
+/*
45d01a
+ * Fix for NVME wwids looking like this:
45d01a
+ * nvme.0000-3163653363666438366239656630386200-4c696e75780000000000000000000000000000000000000000000000000000000000000000000000-00000002
45d01a
+ * which are encountered in some combinations of Linux NVME host and target.
45d01a
+ * The '00' are hex-encoded 0-bytes which are forbidden in the serial (SN)
45d01a
+ * and model (MN) fields. Discard them.
45d01a
+ * If a WWID of the above type is found, sets pp->wwid and returns a value > 0.
45d01a
+ * Otherwise, returns 0.
45d01a
+ */
45d01a
+static int
45d01a
+fix_broken_nvme_wwid(struct path *pp, const char *value, int size)
45d01a
+{
45d01a
+	static const char _nvme[] = "nvme.";
45d01a
+	int len, i;
45d01a
+	char mangled[256];
45d01a
+	char *p;
45d01a
+
45d01a
+	len = strlen(value);
45d01a
+	if (len >= sizeof(mangled))
45d01a
+		return 0;
45d01a
+
45d01a
+	/* Check that value starts with "nvme.%04x-" */
45d01a
+	if (memcmp(value, _nvme, sizeof(_nvme) - 1) || value[9] != '-')
45d01a
+		return 0;
45d01a
+	for (i = 5; i < 9; i++)
45d01a
+		if (!isxdigit(value[i]))
45d01a
+			return 0;
45d01a
+
45d01a
+	memcpy(mangled, value, len + 1);
45d01a
+
45d01a
+	/* search end of "model" part and strip trailing '00' */
45d01a
+	p = memrchr(mangled, '-', len);
45d01a
+	if (p == NULL)
45d01a
+		return 0;
45d01a
+
45d01a
+	p = skip_zeroes_backward(mangled, &len, p);
45d01a
+
45d01a
+	/* search end of "serial" part */
45d01a
+	p = memrchr(mangled, '-', p - mangled);
45d01a
+	if (p == NULL || memrchr(mangled, '-', p - mangled) != mangled + 9)
45d01a
+		/* We expect exactly 3 '-' in the value */
45d01a
+		return 0;
45d01a
+
45d01a
+	p = skip_zeroes_backward(mangled, &len, p);
45d01a
+	if (len >= size)
45d01a
+		return 0;
45d01a
+
45d01a
+	memcpy(pp->wwid, mangled, len + 1);
45d01a
+	condlog(2, "%s: over-long WWID shortened to %s", pp->dev, pp->wwid);
45d01a
+	return len;
45d01a
+}
45d01a
+
45d01a
 int
45d01a
 get_uid (struct path * pp, struct udev_device *udev)
45d01a
 {
45d01a
@@ -1287,14 +1431,10 @@ get_uid (struct path * pp, struct udev_d
45d01a
 		     conf->cmd == CMD_VALID_PATH)
45d01a
 			value = getenv(pp->uid_attribute);
45d01a
 		if (value && strlen(value)) {
45d01a
-			size_t len = WWID_SIZE;
45d01a
-
45d01a
-			if (strlen(value) + 1 > WWID_SIZE) {
45d01a
+			size_t len = strlcpy(pp->wwid, value, WWID_SIZE);
45d01a
+			if (len > WWID_SIZE &&
45d01a
+			    !fix_broken_nvme_wwid(pp, value, WWID_SIZE))
45d01a
 				condlog(0, "%s: wwid overflow", pp->dev);
45d01a
-			} else {
45d01a
-				len = strlen(value);
45d01a
-			}
45d01a
-			strncpy(pp->wwid, value, len);
45d01a
 			condlog(4, "%s: got wwid of '%s'", pp->dev, pp->wwid);
45d01a
 			pp->missing_udev_info = INFO_OK;
45d01a
 			pp->tick = 0;
45d01a
@@ -1381,7 +1521,8 @@ pathinfo (struct path *pp, vector hwtabl
45d01a
 
45d01a
 	if (mask & DI_CHECKER) {
45d01a
 		if (path_state == PATH_UP) {
45d01a
-			pp->chkrstate = pp->state = get_state(pp, 0);
45d01a
+			pp->chkrstate = pp->state = get_state(pp, 0,
45d01a
+							      path_state);
45d01a
 			if (pp->state == PATH_UNCHECKED ||
45d01a
 			    pp->state == PATH_WILD)
45d01a
 				goto blank;
45d01a
Index: multipath-tools-130222/libmultipath/hwtable.c
45d01a
===================================================================
45d01a
--- multipath-tools-130222.orig/libmultipath/hwtable.c
45d01a
+++ multipath-tools-130222/libmultipath/hwtable.c
45d01a
@@ -1185,7 +1185,15 @@ static struct hwentry default_hw[] = {
45d01a
 		.checker_name  = RBD,
45d01a
 		.deferred_remove = DEFERRED_REMOVE_ON,
45d01a
 	},
45d01a
-
45d01a
+	/*
45d01a
+	 *  Generic NVMe devices
45d01a
+	 */
45d01a
+	{
45d01a
+		.vendor        = "NVME",
45d01a
+		.product       = ".*",
45d01a
+		.uid_attribute = "ID_WWN",
45d01a
+		.checker_name  = NONE,
45d01a
+	},
45d01a
 	/*
45d01a
 	 * EOL
45d01a
 	 */
45d01a
Index: multipath-tools-130222/libmultipath/structs.h
45d01a
===================================================================
45d01a
--- multipath-tools-130222.orig/libmultipath/structs.h
45d01a
+++ multipath-tools-130222/libmultipath/structs.h
45d01a
@@ -54,6 +54,7 @@ enum sysfs_buses {
45d01a
 	SYSFS_BUS_CCW,
45d01a
 	SYSFS_BUS_CCISS,
45d01a
 	SYSFS_BUS_RBD,
45d01a
+	SYSFS_BUS_NVME,
45d01a
 };
45d01a
 
45d01a
 enum pathstates {
45d01a
Index: multipath-tools-130222/libmultipath/checkers.c
45d01a
===================================================================
45d01a
--- multipath-tools-130222.orig/libmultipath/checkers.c
45d01a
+++ multipath-tools-130222/libmultipath/checkers.c
45d01a
@@ -101,6 +101,8 @@ struct checker * add_checker (char * nam
45d01a
 	if (!c)
45d01a
 		return NULL;
45d01a
 	snprintf(c->name, CHECKER_NAME_LEN, "%s", name);
45d01a
+	if (!strncmp(c->name, NONE, 4))
45d01a
+		goto done;
45d01a
 	snprintf(libname, LIB_CHECKER_NAMELEN, "%s/libcheck%s.so",
45d01a
 		 conf->multipath_dir, name);
45d01a
 	if (stat(libname,&stbuf) < 0) {
45d01a
@@ -144,7 +146,7 @@ struct checker * add_checker (char * nam
45d01a
 		condlog(0, "A dynamic linking error occurred: (%s)", errstr);
45d01a
 	if (!c->repair)
45d01a
 		goto out;
45d01a
-
45d01a
+done:
45d01a
 	c->fd = 0;
45d01a
 	c->sync = 1;
45d01a
 	list_add(&c->node, &checkers);
45d01a
@@ -194,14 +196,16 @@ int checker_init (struct checker * c, vo
45d01a
 	if (!c)
45d01a
 		return 1;
45d01a
 	c->mpcontext = mpctxt_addr;
45d01a
-	return c->init(c);
45d01a
+	if (c->init)
45d01a
+		return c->init(c);
45d01a
+	return 0;
45d01a
 }
45d01a
 
45d01a
 void checker_put (struct checker * dst)
45d01a
 {
45d01a
 	struct checker * src;
45d01a
 
45d01a
-	if (!dst)
45d01a
+	if (!dst || !strlen(dst->name))
45d01a
 		return;
45d01a
 	src = checker_lookup(dst->name);
45d01a
 	if (dst->free)
45d01a
@@ -221,10 +225,11 @@ void checker_repair (struct checker * c)
45d01a
 		return;
45d01a
 	}
45d01a
 
45d01a
-	c->repair(c);
45d01a
+	if (c->repair)
45d01a
+		c->repair(c);
45d01a
 }
45d01a
 
45d01a
-int checker_check (struct checker * c)
45d01a
+int checker_check (struct checker * c, int path_state)
45d01a
 {
45d01a
 	int r;
45d01a
 
45d01a
@@ -236,6 +241,8 @@ int checker_check (struct checker * c)
45d01a
 		MSG(c, "checker disabled");
45d01a
 		return PATH_UNCHECKED;
45d01a
 	}
45d01a
+	if (!strncmp(c->name, NONE, 4))
45d01a
+		return path_state;
45d01a
 	if (c->fd <= 0) {
45d01a
 		MSG(c, "no usable fd");
45d01a
 		return PATH_WILD;
45d01a
@@ -249,6 +256,8 @@ int checker_selected (struct checker * c
45d01a
 {
45d01a
 	if (!c)
45d01a
 		return 0;
45d01a
+	if (!strncmp(c->name, NONE, 4))
45d01a
+		return 1;
45d01a
 	return (c->check) ? 1 : 0;
45d01a
 }
45d01a
 
45d01a
Index: multipath-tools-130222/libmultipath/checkers.h
45d01a
===================================================================
45d01a
--- multipath-tools-130222.orig/libmultipath/checkers.h
45d01a
+++ multipath-tools-130222/libmultipath/checkers.h
45d01a
@@ -75,6 +75,7 @@ enum path_check_state {
45d01a
 #define EMC_CLARIION "emc_clariion"
45d01a
 #define READSECTOR0  "readsector0"
45d01a
 #define CCISS_TUR    "cciss_tur"
45d01a
+#define NONE         "none"
45d01a
 #define RBD          "rbd"
45d01a
 
45d01a
 #define DEFAULT_CHECKER DIRECTIO
45d01a
@@ -129,7 +130,7 @@ void checker_set_fd (struct checker *, i
45d01a
 void checker_enable (struct checker *);
45d01a
 void checker_disable (struct checker *);
45d01a
 void checker_repair (struct checker *);
45d01a
-int checker_check (struct checker *);
45d01a
+int checker_check (struct checker *, int);
45d01a
 int checker_selected (struct checker *);
45d01a
 char * checker_name (struct checker *);
45d01a
 char * checker_message (struct checker *);
45d01a
Index: multipath-tools-130222/libmultipath/discovery.h
45d01a
===================================================================
45d01a
--- multipath-tools-130222.orig/libmultipath/discovery.h
45d01a
+++ multipath-tools-130222/libmultipath/discovery.h
45d01a
@@ -35,7 +35,7 @@ int path_discovery (vector pathvec, stru
45d01a
 
45d01a
 int do_tur (char *);
45d01a
 int path_offline (struct path *);
45d01a
-int get_state (struct path * pp, int daemon);
45d01a
+int get_state (struct path * pp, int daemon, int state);
45d01a
 int pathinfo (struct path *, vector hwtable, int mask);
45d01a
 int store_pathinfo (vector pathvec, vector hwtable,
45d01a
 		    struct udev_device *udevice, int flag,
45d01a
Index: multipath-tools-130222/libmultipath/uevent.c
45d01a
===================================================================
45d01a
--- multipath-tools-130222.orig/libmultipath/uevent.c
45d01a
+++ multipath-tools-130222/libmultipath/uevent.c
45d01a
@@ -447,7 +447,7 @@ int uevent_listen(struct udev *udev)
45d01a
 		goto out;
45d01a
 	}
45d01a
 	err = udev_monitor_filter_add_match_subsystem_devtype(monitor, "block",
45d01a
-							      NULL);
45d01a
+							      "disk");
45d01a
 	if (err)
45d01a
 		condlog(2, "failed to create filter : %s", strerror(-err));
45d01a
 	err = udev_monitor_enable_receiving(monitor);
45d01a
Index: multipath-tools-130222/multipath/multipath.conf.5
45d01a
===================================================================
45d01a
--- multipath-tools-130222.orig/multipath/multipath.conf.5
45d01a
+++ multipath-tools-130222/multipath/multipath.conf.5
45d01a
@@ -284,6 +284,9 @@ Check the path state for LSI/Engenio/Net
45d01a
 .B directio
45d01a
 Read the first sector with direct I/O.
45d01a
 .TP
45d01a
+.B none
45d01a
+Do not check the device, fallback to use the values retrieved from sysfs
45d01a
+.TP
45d01a
 .B rbd
45d01a
 Check if the path is in the Ceph blacklist.
45d01a
 .TP
45d01a
Index: multipath-tools-130222/multipathd/main.c
45d01a
===================================================================
45d01a
--- multipath-tools-130222.orig/multipathd/main.c
45d01a
+++ multipath-tools-130222/multipathd/main.c
45d01a
@@ -908,28 +908,6 @@ out:
45d01a
 	return r;
45d01a
 }
45d01a
 
45d01a
-static int
45d01a
-uev_discard(char * devpath)
45d01a
-{
45d01a
-	char *tmp;
45d01a
-	char a[11], b[11];
45d01a
-
45d01a
-	/*
45d01a
-	 * keep only block devices, discard partitions
45d01a
-	 */
45d01a
-	tmp = strstr(devpath, "/block/");
45d01a
-	if (tmp == NULL){
45d01a
-		condlog(4, "no /block/ in '%s'", devpath);
45d01a
-		return 1;
45d01a
-	}
45d01a
-	if (sscanf(tmp, "/block/%10s", a) != 1 ||
45d01a
-	    sscanf(tmp, "/block/%10[^/]/%10s", a, b) == 2) {
45d01a
-		condlog(4, "discard event on %s", devpath);
45d01a
-		return 1;
45d01a
-	}
45d01a
-	return 0;
45d01a
-}
45d01a
-
45d01a
 int
45d01a
 uev_trigger (struct uevent * uev, void * trigger_data)
45d01a
 {
45d01a
@@ -938,9 +916,6 @@ uev_trigger (struct uevent * uev, void *
45d01a
 
45d01a
 	vecs = (struct vectors *)trigger_data;
45d01a
 
45d01a
-	if (uev_discard(uev->devpath))
45d01a
-		return 0;
45d01a
-
45d01a
 	pthread_cleanup_push(cleanup_lock, &vecs->lock);
45d01a
 	lock(vecs->lock);
45d01a
 	pthread_testcancel();
45d01a
@@ -1358,7 +1333,7 @@ check_path (struct vectors * vecs, struc
45d01a
 
45d01a
 	newstate = path_offline(pp);
45d01a
 	if (newstate == PATH_UP)
45d01a
-		newstate = get_state(pp, 1);
45d01a
+		newstate = get_state(pp, 1, newstate);
45d01a
 	else
45d01a
 		checker_clear_message(&pp->checker);
45d01a