Blame SOURCES/0235-RHBZ-1480638-NVMe-support.patch

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