You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

504 lines
14 KiB

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