summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2022-01-14 13:50:17 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2022-01-14 13:50:17 +0900
commit7e244d28c5b51809837b8679e0b547e1432829b4 (patch)
tree81ea816dca9b2feec6e4e9b674d33660b3357063
parent28bc88e5da7ffd00fcdac1d23c729d19e646f3fd (diff)
downloadmultipath-tools-7e244d28c5b51809837b8679e0b547e1432829b4.tar.gz
multipath-tools-7e244d28c5b51809837b8679e0b547e1432829b4.tar.bz2
multipath-tools-7e244d28c5b51809837b8679e0b547e1432829b4.zip
Imported Upstream version 0.7.3upstream/0.7.3
-rw-r--r--README5
-rw-r--r--kpartx/Makefile4
-rw-r--r--kpartx/dasd.c3
-rw-r--r--kpartx/del-part-nodes.rules32
-rw-r--r--kpartx/devmapper.c194
-rw-r--r--kpartx/devmapper.h18
-rw-r--r--kpartx/dm-parts.rules39
-rw-r--r--kpartx/kpartx.c105
-rw-r--r--kpartx/kpartx.rules48
-rwxr-xr-xkpartx/test-kpartx50
-rw-r--r--libdmmp/libdmmp.c277
-rw-r--r--libdmmp/libdmmp/libdmmp.h61
-rw-r--r--libdmmp/libdmmp_misc.c5
-rw-r--r--libdmmp/test/libdmmp_test.c49
-rw-r--r--libmpathcmd/mpath_cmd.c4
-rw-r--r--libmpathcmd/mpath_cmd.h1
-rw-r--r--libmpathpersist/mpath_persist.c78
-rw-r--r--libmpathpersist/mpath_updatepr.c43
-rw-r--r--libmpathpersist/mpathpr.h3
-rw-r--r--libmultipath/Makefile2
-rw-r--r--libmultipath/byteorder.h44
-rw-r--r--libmultipath/checkers.c26
-rw-r--r--libmultipath/checkers.h3
-rw-r--r--libmultipath/checkers/rbd.c16
-rw-r--r--libmultipath/config.c9
-rw-r--r--libmultipath/config.h10
-rw-r--r--libmultipath/configure.c58
-rw-r--r--libmultipath/configure.h1
-rw-r--r--libmultipath/defaults.h1
-rw-r--r--libmultipath/devmapper.c87
-rw-r--r--libmultipath/devmapper.h5
-rw-r--r--libmultipath/dict.c105
-rw-r--r--libmultipath/dict.h4
-rw-r--r--libmultipath/discovery.c62
-rw-r--r--libmultipath/discovery.h4
-rw-r--r--libmultipath/hwtable.c54
-rw-r--r--libmultipath/print.c2
-rw-r--r--libmultipath/prkey.c166
-rw-r--r--libmultipath/prkey.h19
-rw-r--r--libmultipath/propsel.c51
-rw-r--r--libmultipath/structs.c77
-rw-r--r--libmultipath/structs.h12
-rw-r--r--libmultipath/uevent.c17
-rw-r--r--libmultipath/util.c33
-rw-r--r--libmultipath/util.h4
-rw-r--r--libmultipath/version.h4
-rw-r--r--mpathpersist/Makefile4
-rw-r--r--multipath/11-dm-mpath.rules65
-rw-r--r--multipath/Makefile4
-rw-r--r--multipath/main.c92
-rw-r--r--multipath/multipath.815
-rw-r--r--multipath/multipath.conf.566
-rw-r--r--multipath/multipath.rules2
-rw-r--r--multipathd/Makefile4
-rw-r--r--multipathd/cli.c8
-rw-r--r--multipathd/cli.h8
-rw-r--r--multipathd/cli_handlers.c84
-rw-r--r--multipathd/cli_handlers.h3
-rw-r--r--multipathd/main.c54
59 files changed, 1773 insertions, 531 deletions
diff --git a/README b/README
index a18b325..570224d 100644
--- a/README
+++ b/README
@@ -29,6 +29,11 @@ To get latest devel code: git clone https://git.opensvc.com/multipath-tools/.git
Gitweb: https://git.opensvc.com/?p=multipath-tools/.git
+Add storage devices
+===================
+Follow the instructions in the libmultipath/hwtable.c header.
+
+
Mailing list (subscribers-only)
============
To subscribe and archives: https://www.redhat.com/mailman/listinfo/dm-devel
diff --git a/kpartx/Makefile b/kpartx/Makefile
index 7b75032..bf7362d 100644
--- a/kpartx/Makefile
+++ b/kpartx/Makefile
@@ -29,7 +29,9 @@ install: $(EXEC) $(EXEC).8
$(INSTALL_PROGRAM) -d $(DESTDIR)$(libudevdir)
$(INSTALL_PROGRAM) -m 755 kpartx_id $(DESTDIR)$(libudevdir)
$(INSTALL_PROGRAM) -d $(DESTDIR)$(libudevdir)/rules.d
+ $(INSTALL_PROGRAM) -m 644 dm-parts.rules $(DESTDIR)$(libudevdir)/rules.d/11-dm-parts.rules
$(INSTALL_PROGRAM) -m 644 kpartx.rules $(DESTDIR)$(libudevdir)/rules.d/66-kpartx.rules
+ $(INSTALL_PROGRAM) -m 644 del-part-nodes.rules $(DESTDIR)$(libudevdir)/rules.d/68-del-part-nodes.rules
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
$(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir)
@@ -37,8 +39,10 @@ uninstall:
$(RM) $(DESTDIR)$(bindir)/$(EXEC)
$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
$(RM) $(DESTDIR)$(libudevdir)/kpartx_id
+ $(RM) $(DESTDIR)$(libudevdir)/rules.d/11-dm-parts.rules
$(RM) $(DESTDIR)$(libudevdir)/rules.d/66-kpartx.rules
$(RM) $(DESTDIR)$(libudevdir)/rules.d/67-kpartx-compat.rules
+ $(RM) $(DESTDIR)$(libudevdir)/rules.d/68-del-part-nodes.rules
clean:
$(RM) core *.o $(EXEC) *.gz
diff --git a/kpartx/dasd.c b/kpartx/dasd.c
index f50c1bd..e418d5a 100644
--- a/kpartx/dasd.c
+++ b/kpartx/dasd.c
@@ -133,6 +133,9 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns)
/* Couldn't open the device */
return -1;
}
+ } else if ((unsigned int)major(sbuf.st_rdev) != 94) {
+ /* Not a DASD */
+ return -1;
} else {
fd_dasd = fd;
}
diff --git a/kpartx/del-part-nodes.rules b/kpartx/del-part-nodes.rules
new file mode 100644
index 0000000..cee945d
--- /dev/null
+++ b/kpartx/del-part-nodes.rules
@@ -0,0 +1,32 @@
+# These rules can delete partitions devnodes for slave devices
+# for certain aggregate devices such as multipath.
+# This is desirable to avoid confusion and keep the number
+# of device nodes and symlinks within limits.
+#
+# This is done only once on the first "add" or "change" event for
+# any given device.
+#
+# To suppress this, use the kernel parameter "dont_del_part_nodes",
+# or create an udev rule file that sets ENV{DONT_DEL_PART_NODES}="1".
+
+SUBSYSTEM!="block", GOTO="end_del_part_nodes"
+KERNEL!="sd*|dasd*|rbd*", GOTO="end_del_part_nodes"
+ACTION!="add|change", GOTO="end_del_part_nodes"
+
+IMPORT{cmdline}="dont_del_part_nodes"
+ENV{dont_del_part_nodes}=="1", GOTO="end_del_part_nodes"
+ENV{DONT_DEL_PART_NODES}=="1", GOTO="end_del_part_nodes"
+
+# dm-multipath
+ENV{DM_MULTIPATH_DEVICE_PATH}=="1", GOTO="del_part_nodes"
+
+# Other aggregate device types can be added here.
+
+GOTO="end_del_part_nodes"
+
+LABEL="del_part_nodes"
+IMPORT{db}="DM_DEL_PART_NODES"
+ENV{DM_DEL_PART_NODES}!="1", ENV{DM_DEL_PART_NODES}="1", \
+ RUN+="/usr/sbin/partx -d --nr 1-1024 $env{DEVNAME}"
+
+LABEL="end_del_part_nodes"
diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c
index 4ab58ce..4469fa8 100644
--- a/kpartx/devmapper.c
+++ b/kpartx/devmapper.c
@@ -92,6 +92,42 @@ out:
return r;
}
+static void
+strip_slash (char * device)
+{
+ char * p = device;
+
+ while (*(p++) != 0x0) {
+
+ if (*p == '/')
+ *p = '!';
+ }
+}
+
+static int format_partname(char *buf, size_t bufsiz,
+ const char *mapname, const char *delim, int part)
+{
+ if (snprintf(buf, bufsiz, "%s%s%d", mapname, delim, part) >= bufsiz)
+ return 0;
+ strip_slash(buf);
+ return 1;
+}
+
+static char *make_prefixed_uuid(int part, const char *uuid)
+{
+ char *prefixed_uuid;
+ int len = MAX_PREFIX_LEN + strlen(uuid) + 1;
+
+ prefixed_uuid = malloc(len);
+ if (!prefixed_uuid) {
+ fprintf(stderr, "cannot create prefixed uuid : %s\n",
+ strerror(errno));
+ return NULL;
+ }
+ snprintf(prefixed_uuid, len, UUID_PREFIX "%s", part, uuid);
+ return prefixed_uuid;
+}
+
int dm_addmap(int task, const char *name, const char *target,
const char *params, uint64_t size, int ro, const char *uuid,
int part, mode_t mode, uid_t uid, gid_t gid)
@@ -117,13 +153,9 @@ int dm_addmap(int task, const char *name, const char *target,
goto addout;
if (task == DM_DEVICE_CREATE && uuid) {
- prefixed_uuid = malloc(MAX_PREFIX_LEN + strlen(uuid) + 1);
- if (!prefixed_uuid) {
- fprintf(stderr, "cannot create prefixed uuid : %s\n",
- strerror(errno));
+ prefixed_uuid = make_prefixed_uuid(part, uuid);
+ if (prefixed_uuid == NULL)
goto addout;
- }
- sprintf(prefixed_uuid, UUID_PREFIX "%s", part, uuid);
if (!dm_task_set_uuid(dmt, prefixed_uuid))
goto addout;
}
@@ -156,7 +188,7 @@ addout:
return r;
}
-int dm_map_present(char * str, char **uuid)
+static int dm_map_present(char *str, char **uuid)
{
int r = 0;
struct dm_task *dmt;
@@ -194,6 +226,51 @@ out:
return r;
}
+static int dm_rename (const char *old, const char *new)
+{
+ int r = 0;
+ struct dm_task *dmt;
+ uint16_t udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK;
+ uint32_t cookie = 0;
+
+ dmt = dm_task_create(DM_DEVICE_RENAME);
+ if (!dmt)
+ return r;
+
+ if (!dm_task_set_name(dmt, old) ||
+ !dm_task_set_newname(dmt, new) ||
+ !dm_task_no_open_count(dmt) ||
+ !dm_task_set_cookie(dmt, &cookie, udev_flags))
+ goto out;
+
+ r = dm_task_run(dmt);
+ dm_udev_wait(cookie);
+
+out:
+ dm_task_destroy(dmt);
+ return r;
+}
+
+static const char *dm_find_uuid(const char *uuid)
+{
+ struct dm_task *dmt;
+ const char *name = NULL, *tmp;
+
+ if ((dmt = dm_task_create(DM_DEVICE_INFO)) == NULL)
+ return NULL;
+
+ if (!dm_task_set_uuid(dmt, uuid) ||
+ !dm_task_run(dmt))
+ goto out;
+
+ tmp = dm_task_get_name(dmt);
+ if (tmp != NULL && *tmp != '\0')
+ name = strdup(tmp);
+
+out:
+ dm_task_destroy(dmt);
+ return name;
+}
char *
dm_mapname(int major, int minor)
@@ -308,7 +385,7 @@ out:
}
static int
-dm_get_map(char *mapname, char * outparams)
+dm_get_map(const char *mapname, char * outparams)
{
int r = 1;
struct dm_task *dmt;
@@ -557,3 +634,104 @@ dm_remove_partmaps (char * mapname, char *uuid, dev_t devt, int verbose)
struct remove_data rd = { verbose };
return do_foreach_partmaps(mapname, uuid, devt, remove_partmap, &rd);
}
+
+int dm_find_part(const char *parent, const char *delim, int part,
+ const char *parent_uuid,
+ char *name, size_t namesiz, char **part_uuid, int verbose)
+{
+ int r;
+ char params[PARAMS_SIZE];
+ const char *tmp;
+ char *uuid;
+ int major, minor;
+ char dev_t[32];
+
+ if (!format_partname(name, namesiz, parent, delim, part)) {
+ if (verbose)
+ fprintf(stderr, "partname too small\n");
+ return 0;
+ }
+
+ r = dm_map_present(name, part_uuid);
+ if (r == 1 || parent_uuid == NULL || *parent_uuid == '\0')
+ return r;
+
+ uuid = make_prefixed_uuid(part, parent_uuid);
+ if (!uuid)
+ return 0;
+
+ tmp = dm_find_uuid(uuid);
+ if (tmp == NULL)
+ return r;
+
+ /* Sanity check on partition, see dm_foreach_partmaps */
+ if (dm_type(tmp, "linear") != 1)
+ goto out;
+
+ /*
+ * Try nondm uuid first. That way we avoid confusing
+ * a device name with a device mapper name.
+ */
+ if (!nondm_parse_uuid(parent_uuid, &major, &minor) &&
+ dm_devn(parent, &major, &minor))
+ goto out;
+ snprintf(dev_t, sizeof(dev_t), "%d:%d", major, minor);
+
+ if (dm_get_map(tmp, params))
+ goto out;
+
+ if (!strstr(params, dev_t))
+ goto out;
+
+ if (verbose)
+ fprintf(stderr, "found map %s for uuid %s, renaming to %s\n",
+ tmp, uuid, name);
+
+ r = dm_rename(tmp, name);
+ if (r == 0) {
+ free(uuid);
+ if (verbose)
+ fprintf(stderr, "renaming %s->%s failed\n", tmp, name);
+ } else
+ *part_uuid = uuid;
+out:
+ free((void*)tmp);
+ return r;
+}
+
+char *nondm_create_uuid(dev_t devt)
+{
+#define NONDM_UUID_BUFLEN (34 + sizeof(NONDM_UUID_PREFIX) + \
+ sizeof(NONDM_UUID_SUFFIX))
+ static char uuid_buf[NONDM_UUID_BUFLEN];
+ snprintf(uuid_buf, sizeof(uuid_buf), "%s_%u:%u_%s",
+ NONDM_UUID_PREFIX, major(devt), minor(devt),
+ NONDM_UUID_SUFFIX);
+ uuid_buf[NONDM_UUID_BUFLEN-1] = '\0';
+ return uuid_buf;
+}
+
+int nondm_parse_uuid(const char *uuid, int *major, int *minor)
+{
+ const char *p;
+ char *e;
+ int ma, mi;
+
+ if (strncmp(uuid, NONDM_UUID_PREFIX "_", sizeof(NONDM_UUID_PREFIX)))
+ return 0;
+ p = uuid + sizeof(NONDM_UUID_PREFIX);
+ ma = strtoul(p, &e, 10);
+ if (e == p || *e != ':')
+ return 0;
+ p = e + 1;
+ mi = strtoul(p, &e, 10);
+ if (e == p || *e != '_')
+ return 0;
+ p = e + 1;
+ if (strcmp(p, NONDM_UUID_SUFFIX))
+ return 0;
+
+ *major = ma;
+ *minor = mi;
+ return 1;
+}
diff --git a/kpartx/devmapper.h b/kpartx/devmapper.h
index 2e28c78..73b80f2 100644
--- a/kpartx/devmapper.h
+++ b/kpartx/devmapper.h
@@ -13,12 +13,26 @@ int dm_prereq (char *, int, int, int);
int dm_simplecmd (int, const char *, int, uint16_t);
int dm_addmap (int, const char *, const char *, const char *, uint64_t,
int, const char *, int, mode_t, uid_t, gid_t);
-int dm_map_present (char *, char **);
char * dm_mapname(int major, int minor);
dev_t dm_get_first_dep(char *devname);
char * dm_mapuuid(const char *mapname);
int dm_devn (const char * mapname, int *major, int *minor);
int dm_remove_partmaps (char * mapname, char *uuid, dev_t devt, int verbose);
-int dm_no_partitions(char * mapname);
+int dm_find_part(const char *parent, const char *delim, int part,
+ const char *parent_uuid,
+ char *name, size_t namesiz, char **part_uuid, int verbose);
+/*
+ * UUID format for partitions created on non-DM devices
+ * ${UUID_PREFIX}devnode_${MAJOR}:${MINOR}_${NONDM_UUID_SUFFIX}"
+ * where ${UUID_PREFIX} is "part${PARTNO}-" (see devmapper.c).
+ *
+ * The suffix should be sufficiently unique to avoid incidental conflicts;
+ * the value below is a base64-encoded random number.
+ * The UUID format shouldn't be changed between kpartx releases.
+ */
+#define NONDM_UUID_PREFIX "devnode"
+#define NONDM_UUID_SUFFIX "Wh5pYvM"
+char *nondm_create_uuid(dev_t devt);
+int nondm_parse_uuid(const char *uuid, int *major, int *minor);
#endif /* _KPARTX_DEVMAPPER_H */
diff --git a/kpartx/dm-parts.rules b/kpartx/dm-parts.rules
new file mode 100644
index 0000000..235642f
--- /dev/null
+++ b/kpartx/dm-parts.rules
@@ -0,0 +1,39 @@
+# Rules for partitions created by kpartx
+
+KERNEL!="dm-*", GOTO="dm_parts_end"
+ACTION!="add|change", GOTO="dm_parts_end"
+ENV{DM_UUID}!="part[0-9]*", GOTO="dm_parts_end"
+
+# We must take care that symlinks don't get lost,
+# even if blkid fails in 13-dm-disk.rules later.
+#
+# Fixme: we have currently no way to avoid calling blkid on
+# partitions of broken mpath maps such as DM_NOSCAN.
+# But when partition devices appear, kpartx has likely read
+# the partition table shortly before, so odds are not bad
+# that blkid will also succeed.
+
+IMPORT{db}="ID_FS_USAGE"
+IMPORT{db}="ID_FS_UUID_ENC"
+IMPORT{db}="ID_FS_LABEL_ENC"
+IMPORT{db}="ID_PART_ENTRY_NAME"
+IMPORT{db}="ID_PART_ENTRY_UUID"
+IMPORT{db}="ID_PART_ENTRY_SCHEME"
+
+# Maps should take precedence over their members.
+ENV{DM_UDEV_LOW_PRIORITY_FLAG}!="1", OPTIONS+="link_priority=50"
+
+# Set some additional symlinks that typically exist for mpath
+# path members, too, and should be overridden.
+#
+# kpartx_id is very robust, it works for suspended maps and maps
+# with 0 dependencies. It sets DM_TYPE, DM_PART, DM_WWN
+IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}"
+
+# DM_TYPE only has a reasonable value for partitions on multipath.
+ENV{DM_UUID}=="*-mpath-*", ENV{DM_TYPE}=="?*" \
+ SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}"
+ENV{DM_WWN}=="?*", ENV{DM_PART}=="?*", \
+ SYMLINK+="disk/by-id/wwn-$env{DM_WWN}-part$env{DM_PART}"
+
+LABEL="dm_parts_end"
diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c
index 9ba78d0..442b6bd 100644
--- a/kpartx/kpartx.c
+++ b/kpartx/kpartx.c
@@ -60,31 +60,6 @@ struct pt {
int ptct = 0;
int udev_sync = 1;
-/*
- * UUID format for partitions created on non-DM devices
- * ${UUID_PREFIX}devnode_${MAJOR}:${MINOR}_${NONDM_UUID_SUFFIX}"
- * where ${UUID_PREFIX} is "part${PARTNO}-" (see devmapper.c).
- *
- * The suffix should be sufficiently unique to avoid incidental conflicts;
- * the value below is a base64-encoded random number.
- * The UUID format shouldn't be changed between kpartx releases.
- */
-#define NONDM_UUID_PREFIX "devnode"
-#define NONDM_UUID_SUFFIX "Wh5pYvM"
-
-static char *
-nondm_create_uuid(dev_t devt)
-{
-#define NONDM_UUID_BUFLEN (34 + sizeof(NONDM_UUID_PREFIX) + \
- sizeof(NONDM_UUID_SUFFIX))
- static char uuid_buf[NONDM_UUID_BUFLEN];
- snprintf(uuid_buf, sizeof(uuid_buf), "%s_%u:%u_%s",
- NONDM_UUID_PREFIX, major(devt), minor(devt),
- NONDM_UUID_SUFFIX);
- uuid_buf[NONDM_UUID_BUFLEN-1] = '\0';
- return uuid_buf;
-}
-
static void
addpts(char *t, ptreader f)
{
@@ -150,18 +125,6 @@ set_delimiter (char * device, char * delimiter)
*delimiter = 'p';
}
-static void
-strip_slash (char * device)
-{
- char * p = device;
-
- while (*(p++) != 0x0) {
-
- if (*p == '/')
- *p = '!';
- }
-}
-
static int
find_devname_offset (char * device)
{
@@ -431,6 +394,22 @@ main(int argc, char **argv){
/* add/remove partitions to the kernel devmapper tables */
int r = 0;
+
+ if (what == DELETE) {
+ r = dm_remove_partmaps(mapname, uuid, buf.st_rdev,
+ verbose);
+ if (loopdev) {
+ if (del_loop(loopdev)) {
+ if (verbose)
+ fprintf(stderr, "can't del loop : %s\n",
+ loopdev);
+ r = 1;
+ } else
+ fprintf(stderr, "loop deleted : %s\n", loopdev);
+ }
+ goto end;
+ }
+
for (i = 0; i < ptct; i++) {
ptp = &pts[i];
@@ -498,20 +477,6 @@ main(int argc, char **argv){
break;
- case DELETE:
- r = dm_remove_partmaps(mapname, uuid, buf.st_rdev,
- verbose);
- if (loopdev) {
- if (del_loop(loopdev)) {
- if (verbose)
- printf("can't del loop : %s\n",
- loopdev);
- exit(1);
- }
- printf("loop deleted : %s\n", loopdev);
- }
- break;
-
case ADD:
case UPDATE:
/* ADD and UPDATE share the same code that adds new partitions. */
@@ -527,21 +492,16 @@ main(int argc, char **argv){
continue;
}
- if (safe_sprintf(partname, "%s%s%d",
- mapname, delim, j+1)) {
- fprintf(stderr, "partname too small\n");
- exit(1);
- }
- strip_slash(partname);
-
if (safe_sprintf(params, "%d:%d %" PRIu64 ,
major(buf.st_rdev), minor(buf.st_rdev), slices[j].start)) {
fprintf(stderr, "params too small\n");
exit(1);
}
- op = (dm_map_present(partname, &part_uuid) ?
- DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
+ op = (dm_find_part(mapname, delim, j + 1, uuid,
+ partname, sizeof(partname),
+ &part_uuid, verbose) ?
+ DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
if (part_uuid && uuid) {
if (check_uuid(uuid, part_uuid, &reason) != 0) {
@@ -603,13 +563,6 @@ main(int argc, char **argv){
fprintf(stderr, "Invalid slice %d\n",
k);
- if (safe_sprintf(partname, "%s%s%d",
- mapname, delim, j+1)) {
- fprintf(stderr, "partname too small\n");
- exit(1);
- }
- strip_slash(partname);
-
if (safe_sprintf(params, "%d:%d %" PRIu64,
major(buf.st_rdev), minor(buf.st_rdev),
slices[j].start)) {
@@ -617,8 +570,10 @@ main(int argc, char **argv){
exit(1);
}
- op = (dm_map_present(partname,
- &part_uuid) ?
+ op = (dm_find_part(mapname, delim, j + 1, uuid,
+ partname,
+ sizeof(partname),
+ &part_uuid, verbose) ?
DM_DEVICE_RELOAD : DM_DEVICE_CREATE);
if (part_uuid && uuid) {
@@ -660,15 +615,10 @@ main(int argc, char **argv){
for (j = MAXSLICES-1; j >= 0; j--) {
char *part_uuid, *reason;
- if (safe_sprintf(partname, "%s%s%d",
- mapname, delim, j+1)) {
- fprintf(stderr, "partname too small\n");
- exit(1);
- }
- strip_slash(partname);
-
if (slices[j].size ||
- !dm_map_present(partname, &part_uuid))
+ !dm_find_part(mapname, delim, j + 1, uuid,
+ partname, sizeof(partname),
+ &part_uuid, verbose))
continue;
if (part_uuid && uuid) {
@@ -708,6 +658,7 @@ main(int argc, char **argv){
printf("loop deleted : %s\n", device);
}
+end:
dm_lib_release();
dm_lib_exit();
diff --git a/kpartx/kpartx.rules b/kpartx/kpartx.rules
index 64d550d..8f99049 100644
--- a/kpartx/kpartx.rules
+++ b/kpartx/kpartx.rules
@@ -5,41 +5,33 @@
#
KERNEL!="dm-*", GOTO="kpartx_end"
-ACTION=="remove", GOTO="kpartx_end"
+ACTION!="add|change", GOTO="kpartx_end"
+ENV{DM_UUID}!="?*", GOTO="kpartx_end"
-ENV{DM_TABLE_STATE}!="LIVE", GOTO="kpartx_end"
-ENV{DM_DEPS}=="0", GOTO="kpartx_end"
+# Create dm tables for partitions on multipath devices.
+ENV{DM_UUID}!="mpath-?*", GOTO="mpath_kpartx_end"
-ENV{DM_UUID}=="?*", IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}"
+# DM_SUBSYSTEM_UDEV_FLAG1 is the "skip_kpartx" flag.
+# For events not generated by libdevmapper, we need to fetch it from db.
+ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}!="1", IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
+ENV{DM_SUBSYSTEM_UDEV_FLAG1}=="1", GOTO="mpath_kpartx_end"
-OPTIONS="link_priority=50"
+# 11-dm-mpath.rules sets MPATH_UNCHANGED for events that can be ignored.
+ENV{MPATH_UNCHANGED}=="1", GOTO="mpath_kpartx_end"
-ENV{DM_UUID}=="?*", ENV{DM_TYPE}=="?*" \
- SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}"
+# Don't run kpartx now if we know it will fail or hang.
+ENV{DM_SUSPENDED}=="1", GOTO="mpath_kpartx_end"
+ENV{DM_NOSCAN}=="1", GOTO="mpath_kpartx_end"
-# Create persistent links for multipath tables
-ENV{DM_WWN}=="?*", ENV{DM_PART}!="?*", \
- SYMLINK+="disk/by-id/wwn-$env{DM_WWN}"
+# Run kpartx
+GOTO="run_kpartx"
+LABEL="mpath_kpartx_end"
-# Create persistent links for partitions
-ENV{DM_WWN}=="?*", ENV{DM_PART}=="?*", \
- SYMLINK+="disk/by-id/wwn-$env{DM_WWN}-part$env{DM_PART}"
+## Code for other subsystems (non-multipath) could be placed here ##
-# Create persistent by-label/by-uuid links
-ENV{ID_FS_USAGE}=="?*", IMPORT{db}="ID_FS_USAGE"
-ENV{ID_FS_UUID_ENC}=="?*", IMPORT{db}="ID_FS_UUID_ENC"
-ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", \
- SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}"
-ENV{ID_FS_LABEL_ENC}=="?*", IMPORT{db}="ID_FS_LABEL_ENC"
-ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", \
- SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
+GOTO="kpartx_end"
-# Create dm tables for partitions
-ENV{DM_ACTION}=="PATH_FAILED|PATH_REINSTATED", GOTO="kpartx_end"
-ENV{DM_NR_VALID_PATHS}=="0", GOTO="kpartx_end"
-ENV{DM_UDEV_PRIMARY_SOURCE_FLAG}!="1", IMPORT{db}="DM_SUBSYSTEM_UDEV_FLAG1"
-ENV{DM_SUBSYSTEM_UDEV_FLAG1}=="1", GOTO="kpartx_end"
-ENV{DM_STATE}!="SUSPENDED", ENV{DM_UUID}=="mpath-*", \
- RUN+="/sbin/kpartx -un -p -part /dev/$name"
+LABEL="run_kpartx"
+RUN+="/sbin/kpartx -un -p -part /dev/$name"
LABEL="kpartx_end"
diff --git a/kpartx/test-kpartx b/kpartx/test-kpartx
index 7c45cd1..60b3eb2 100755
--- a/kpartx/test-kpartx
+++ b/kpartx/test-kpartx
@@ -68,6 +68,10 @@ mk_partitions() {
parted -s -- $1 mkpart prim ext2 1MiB -1s
}
+wipe_ptable() {
+ dd if=/dev/zero of=$1 bs=1b count=1
+}
+
step preparation
[[ $UID -eq 0 ]]
@@ -165,8 +169,18 @@ mk_partitions $LO2
# Test invocation of kpartx with regular file here
LO2P1=/dev/mapper/$(basename $LO2)-foo1
$KPARTX $KPARTX_OPTS -a -p -foo $FILE2
+[[ -b $LO2P1 ]]
push_cleanup 'dmsetup remove -f $(basename $LO2P1)'
+step "remove partitions with deleted ptable"
+wipe_ptable $LO2
+$KPARTX $KPARTX_OPTS -d $LO2
+[[ ! -b $LO2P1 ]]
+
+mk_partitions $LO2
+$KPARTX $KPARTX_OPTS -a -p -foo $FILE2
+[[ -b $LO2P1 ]]
+
LO1P1=/dev/mapper/$(basename $LO1)-eggs1
$KPARTX $KPARTX_OPTS -a -p -eggs $LO1
push_cleanup 'dmsetup remove -f $(basename $LO1P1)'
@@ -207,6 +221,17 @@ usleep $WAIT_US
[[ -b $SPAN2P1 ]]
[[ -b $SPAN1P1 ]]
+step "rename partitions on DM device to default"
+$KPARTX $KPARTX_OPTS -u /dev/mapper/$SPAN1
+[[ ! -b ${SPAN1P1} ]]
+# This assumes that $SPAN1 ends in a non-digit
+[[ -b ${SPAN1P1//-spam/} ]]
+
+step "rename partitions on DM device back from default"
+$KPARTX $KPARTX_OPTS -u -p -spam /dev/mapper/$SPAN1
+[[ -b ${SPAN1P1} ]]
+[[ ! -b ${SPANP1//-foo/} ]]
+
step "delete partitions on DM devices"
$KPARTX $KPARTX_OPTS -d /dev/mapper/$SPAN1 >&2
usleep $WAIT_US
@@ -223,6 +248,31 @@ usleep $WAIT_US
[[ -b $LO2P1 ]]
[[ ! -b $SPAN2P1 ]]
+step "rename partitions on loop device"
+$KPARTX $KPARTX_OPTS -u -p -spam $LO2
+[[ ! -b ${LO2P1} ]]
+[[ -b ${LO2P1//-foo/-spam} ]]
+
+step "rename partitions on loop device back"
+$KPARTX $KPARTX_OPTS -u -p -foo $LO2
+[[ -b ${LO2P1} ]]
+[[ ! -b ${LO2P1//-foo/-spam} ]]
+
+step "rename partitions on loop device to default"
+$KPARTX $KPARTX_OPTS -u $LO2
+#read a
+[[ ! -b ${LO2P1} ]]
+# $LO1 ends in a digit
+[[ -b ${LO2P1//-foo/p} ]]
+
+step "rename partitions on loop device back from default"
+$KPARTX $KPARTX_OPTS -u -p -foo $LO2
+[[ -b ${LO2P1} ]]
+[[ ! -b ${LO2P1//-foo/p} ]]
+
+step "rename partitions on loop devices"
+$KPARTX $KPARTX_OPTS -u -p spam $LO2
+
step "delete partitions on loop devices"
$KPARTX $KPARTX_OPTS -d $LO3
diff --git a/libdmmp/libdmmp.c b/libdmmp/libdmmp.c
index 3906335..b4e7f08 100644
--- a/libdmmp/libdmmp.c
+++ b/libdmmp/libdmmp.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 - 2016 Red Hat, Inc.
+ * Copyright (C) 2015 - 2017 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
*/
#include <stdint.h>
+#include <stdbool.h>
#include <string.h>
#include <sys/time.h>
#include <sys/resource.h>
@@ -29,6 +30,7 @@
#include <unistd.h>
#include <assert.h>
#include <json.h>
+#include <time.h>
#include <mpath_cmd.h>
#include "libdmmp/libdmmp.h"
@@ -44,6 +46,8 @@
#define _DMMP_JSON_MAJOR_VERSION 0
#define _DMMP_JSON_MAPS_KEY "maps"
#define _ERRNO_STR_BUFF_SIZE 256
+#define _IPC_MAX_CMD_LEN 512
+/* ^ Was _MAX_CMD_LEN in ./libmultipath/uxsock.h */
struct dmmp_context {
void (*log_func)(struct dmmp_context *ctx, int priority,
@@ -54,6 +58,19 @@ struct dmmp_context {
unsigned int tmo;
};
+/*
+ * The multipathd daemon are using "uxsock_timeout" to define timeout value,
+ * if timeout at daemon side, we will get message "timeout\n".
+ * To unify this timeout with `dmmp_context_timeout_set()`, this function
+ * will keep retry mpath_process_cmd() tile meet the time of
+ * dmmp_context_timeout_get().
+ * Need to free `*output` string manually.
+ */
+static int _process_cmd(struct dmmp_context *ctx, int fd, const char *cmd,
+ char **output);
+
+static int _ipc_connect(struct dmmp_context *ctx, int *fd);
+
_dmmp_getter_func_gen(dmmp_context_log_priority_get,
struct dmmp_context, ctx, log_priority,
int);
@@ -141,9 +158,7 @@ int dmmp_mpath_array_get(struct dmmp_context *ctx,
uint32_t i = 0;
int cur_json_major_version = -1;
int ar_maps_len = -1;
- int socket_fd = -1;
- int errno_save = 0;
- char errno_str_buff[_ERRNO_STR_BUFF_SIZE];
+ int ipc_fd = -1;
assert(ctx != NULL);
assert(dmmp_mps != NULL);
@@ -152,49 +167,13 @@ int dmmp_mpath_array_get(struct dmmp_context *ctx,
*dmmp_mps = NULL;
*dmmp_mp_count = 0;
- socket_fd = mpath_connect();
- if (socket_fd == -1) {
- errno_save = errno;
- memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
- strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
- if (errno_save == ECONNREFUSED) {
- rc = DMMP_ERR_NO_DAEMON;
- _error(ctx, "Socket connection refuse. "
- "Maybe multipathd daemon is not running");
- } else {
- _error(ctx, "IPC failed with error %d(%s)", errno_save,
- errno_str_buff);
- rc = DMMP_ERR_IPC_ERROR;
- }
- goto out;
- }
+ _good(_ipc_connect(ctx, &ipc_fd), rc, out);
- if (mpath_process_cmd(socket_fd, _DMMP_IPC_SHOW_JSON_CMD,
- &j_str, ctx->tmo) != 0) {
- errno_save = errno;
- memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
- strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
- if (errno_save == ETIMEDOUT) {
- rc = DMMP_ERR_IPC_TIMEOUT;
- _error(ctx, "IPC communication timeout, try to "
- "increase it via dmmp_context_timeout_set()");
- goto out;
- }
- _error(ctx, "IPC failed when process command '%s' with "
- "error %d(%s)", _DMMP_IPC_SHOW_JSON_CMD, errno_save,
- errno_str_buff);
- rc = DMMP_ERR_IPC_ERROR;
- goto out;
- }
-
- if ((j_str == NULL) || (strlen(j_str) == 0)) {
- _error(ctx, "IPC return empty reply for command %s",
- _DMMP_IPC_SHOW_JSON_CMD);
- rc = DMMP_ERR_IPC_ERROR;
- goto out;
- }
+ _good(_process_cmd(ctx, ipc_fd, _DMMP_IPC_SHOW_JSON_CMD, &j_str),
+ rc, out);
_debug(ctx, "Got json output from multipathd: '%s'", j_str);
+
j_token = json_tokener_new();
if (j_token == NULL) {
rc = DMMP_ERR_BUG;
@@ -267,8 +246,8 @@ int dmmp_mpath_array_get(struct dmmp_context *ctx,
}
out:
- if (socket_fd >= 0)
- mpath_disconnect(socket_fd);
+ if (ipc_fd >= 0)
+ mpath_disconnect(ipc_fd);
free(j_str);
if (j_token != NULL)
json_tokener_free(j_token);
@@ -283,3 +262,209 @@ out:
return rc;
}
+
+static int _process_cmd(struct dmmp_context *ctx, int fd, const char *cmd,
+ char **output)
+{
+ int errno_save = 0;
+ int rc = DMMP_OK;
+ char errno_str_buff[_ERRNO_STR_BUFF_SIZE];
+ struct timespec start_ts;
+ struct timespec cur_ts;
+ unsigned int ipc_tmo = 0;
+ bool flag_check_tmo = false;
+ unsigned int elapsed = 0;
+
+ assert(output != NULL);
+ assert(ctx != NULL);
+ assert(cmd != NULL);
+
+ *output = NULL;
+
+ if (clock_gettime(CLOCK_MONOTONIC, &start_ts) != 0) {
+ _error(ctx, "BUG: Failed to get CLOCK_MONOTONIC time "
+ "via clock_gettime(), error %d", errno);
+ return DMMP_ERR_BUG;
+ }
+
+ ipc_tmo = ctx->tmo;
+ if (ctx->tmo == 0)
+ ipc_tmo = _DEFAULT_UXSOCK_TIMEOUT;
+
+invoke:
+ _debug(ctx, "Invoking IPC command '%s' with IPC tmo %u miliseconds",
+ cmd, ipc_tmo);
+ flag_check_tmo = false;
+ if (mpath_process_cmd(fd, cmd, output, ipc_tmo) != 0) {
+ errno_save = errno;
+ memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
+ strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
+ if (errno_save == ETIMEDOUT) {
+ flag_check_tmo = true;
+ } else {
+ _error(ctx, "IPC failed when process command '%s' with "
+ "error %d(%s)", cmd, errno_save, errno_str_buff);
+ _debug(ctx, "%s", *output);
+ rc = DMMP_ERR_IPC_ERROR;
+ goto out;
+ }
+ }
+ if ((*output != NULL) &&
+ (strncmp(*output, "timeout", strlen("timeout")) == 0))
+ flag_check_tmo = true;
+
+ if (flag_check_tmo == true) {
+ free(*output);
+ *output = NULL;
+ if (ctx->tmo == 0) {
+ _debug(ctx, "IPC timeout, but user requested infinite "
+ "timeout");
+ goto invoke;
+ }
+
+ if (clock_gettime(CLOCK_MONOTONIC, &cur_ts) != 0) {
+ _error(ctx, "BUG: Failed to get CLOCK_MONOTONIC time "
+ "via clock_gettime(), error %d", errno);
+ rc = DMMP_ERR_BUG;
+ goto out;
+ }
+ elapsed = (cur_ts.tv_sec - start_ts.tv_sec) * 1000 +
+ (cur_ts.tv_nsec - start_ts.tv_nsec) / 1000000;
+
+ if (elapsed >= ctx->tmo) {
+ rc = DMMP_ERR_IPC_TIMEOUT;
+ _error(ctx, "Timeout, try to increase it via "
+ "dmmp_context_timeout_set()");
+ goto out;
+ }
+ if (ctx->tmo != 0)
+ ipc_tmo = ctx->tmo - elapsed;
+
+ _debug(ctx, "IPC timeout, but user requested timeout has not "
+ "reached yet, still have %u milliseconds", ipc_tmo);
+ goto invoke;
+ } else {
+ if ((*output == NULL) || (strlen(*output) == 0)) {
+ _error(ctx, "IPC return empty reply for command %s",
+ cmd);
+ rc = DMMP_ERR_IPC_ERROR;
+ goto out;
+ }
+ }
+
+out:
+ if (rc != DMMP_OK) {
+ free(*output);
+ *output = NULL;
+ }
+ return rc;
+}
+
+static int _ipc_connect(struct dmmp_context *ctx, int *fd)
+{
+ int rc = DMMP_OK;
+ int errno_save = 0;
+ char errno_str_buff[_ERRNO_STR_BUFF_SIZE];
+
+ assert(ctx != NULL);
+ assert(fd != NULL);
+
+ *fd = -1;
+
+ *fd = mpath_connect();
+ if (*fd == -1) {
+ errno_save = errno;
+ memset(errno_str_buff, 0, _ERRNO_STR_BUFF_SIZE);
+ strerror_r(errno_save, errno_str_buff, _ERRNO_STR_BUFF_SIZE);
+ if (errno_save == ECONNREFUSED) {
+ rc = DMMP_ERR_NO_DAEMON;
+ _error(ctx, "Socket connection refuse. "
+ "Maybe multipathd daemon is not running");
+ } else {
+ _error(ctx, "IPC failed with error %d(%s)", errno_save,
+ errno_str_buff);
+ rc = DMMP_ERR_IPC_ERROR;
+ }
+ }
+ return rc;
+}
+
+int dmmp_flush_mpath(struct dmmp_context *ctx, const char *mpath_name)
+{
+ int rc = DMMP_OK;
+ struct dmmp_mpath **dmmp_mps = NULL;
+ uint32_t dmmp_mp_count = 0;
+ uint32_t i = 0;
+ bool found = false;
+ int ipc_fd = -1;
+ char cmd[_IPC_MAX_CMD_LEN];
+ char *output = NULL;
+
+ assert(ctx != NULL);
+ assert(mpath_name != NULL);
+
+ snprintf(cmd, _IPC_MAX_CMD_LEN, "del map %s", mpath_name);
+ if (strlen(cmd) == _IPC_MAX_CMD_LEN - 1) {
+ rc = DMMP_ERR_INVALID_ARGUMENT;
+ _error(ctx, "Invalid mpath name %s", mpath_name);
+ goto out;
+ }
+
+ _good(_ipc_connect(ctx, &ipc_fd), rc, out);
+ _good(_process_cmd(ctx, ipc_fd, cmd, &output), rc, out);
+
+ /* _process_cmd() already make sure output is not NULL */
+
+ if (strncmp(output, "fail", strlen("fail")) == 0) {
+ /* Check whether specified mpath exits */
+ _good(dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count),
+ rc, out);
+
+ for (i = 0; i < dmmp_mp_count; ++i) {
+ if (strcmp(dmmp_mpath_name_get(dmmp_mps[i]),
+ mpath_name) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found == false) {
+ rc = DMMP_ERR_MPATH_NOT_FOUND;
+ _error(ctx, "Specified mpath %s not found", mpath_name);
+ goto out;
+ }
+
+ rc = DMMP_ERR_MPATH_BUSY;
+ _error(ctx, "Specified mpath is in use");
+ } else if (strncmp(output, "ok", strlen("ok")) != 0) {
+ rc = DMMP_ERR_BUG;
+ _error(ctx, "Got unexpected output for cmd '%s': '%s'",
+ cmd, output);
+ }
+
+out:
+ if (ipc_fd >= 0)
+ mpath_disconnect(ipc_fd);
+ dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count);
+ free(output);
+ return rc;
+}
+
+int dmmp_reconfig(struct dmmp_context *ctx)
+{
+ int rc = DMMP_OK;
+ int ipc_fd = -1;
+ char *output = NULL;
+ char cmd[_IPC_MAX_CMD_LEN];
+
+ snprintf(cmd, _IPC_MAX_CMD_LEN, "%s", "reconfigure");
+
+ _good(_ipc_connect(ctx, &ipc_fd), rc, out);
+ _good(_process_cmd(ctx, ipc_fd, cmd, &output), rc, out);
+
+out:
+ if (ipc_fd >= 0)
+ mpath_disconnect(ipc_fd);
+ free(output);
+ return rc;
+}
diff --git a/libdmmp/libdmmp/libdmmp.h b/libdmmp/libdmmp/libdmmp.h
index 0679158..72b79b9 100644
--- a/libdmmp/libdmmp/libdmmp.h
+++ b/libdmmp/libdmmp/libdmmp.h
@@ -39,6 +39,9 @@ extern "C" {
#define DMMP_ERR_IPC_ERROR 4
#define DMMP_ERR_NO_DAEMON 5
#define DMMP_ERR_INCOMPATIBLE 6
+#define DMMP_ERR_MPATH_BUSY 7
+#define DMMP_ERR_MPATH_NOT_FOUND 8
+#define DMMP_ERR_INVALID_ARGUMENT 9
/*
* Use the syslog severity level as log priority
@@ -171,6 +174,7 @@ DMMP_DLL_EXPORT void dmmp_context_free(struct dmmp_context *ctx);
*
* @tmo:
* Timeout in milliseconds(1 seconds equal 1000 milliseconds).
+ * 0 means infinite, function only return when error or pass.
*
* Return:
* void
@@ -646,6 +650,63 @@ DMMP_DLL_EXPORT uint32_t dmmp_path_status_get(struct dmmp_path *dmmp_p);
*/
DMMP_DLL_EXPORT const char *dmmp_path_status_str(uint32_t path_status);
+/**
+ * dmmp_flush_mpath() - Flush specified multipath device map if unused.
+ *
+ * Flush a multipath device map specified as parameter, if unused.
+ *
+ * @ctx:
+ * Pointer of 'struct dmmp_context'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ * @mpath_name:
+ * const char *. The name of multipath device map.
+ *
+ * Return:
+ * int. Valid error codes are:
+ *
+ * * DMMP_OK
+ *
+ * * DMMP_ERR_BUG
+ *
+ * * DMMP_ERR_NO_MEMORY
+ *
+ * * DMMP_ERR_NO_DAEMON
+ *
+ * * DMMP_ERR_MPATH_BUSY
+ *
+ * * DMMP_ERR_MPATH_NOT_FOUND
+ *
+ * * DMMP_ERR_INVALID_ARGUMENT
+ *
+ * Error number could be converted to string by dmmp_strerror().
+ */
+DMMP_DLL_EXPORT int dmmp_flush_mpath(struct dmmp_context *ctx,
+ const char *mpath_name);
+
+/**
+ * dmmp_reconfig() - Instruct multipathd daemon to do reconfiguration.
+ *
+ * Instruct multipathd daemon to do reconfiguration.
+ *
+ * @ctx:
+ * Pointer of 'struct dmmp_context'.
+ * If this pointer is NULL, your program will be terminated by assert.
+ *
+ * Return:
+ * int. Valid error codes are:
+ *
+ * * DMMP_OK
+ *
+ * * DMMP_ERR_BUG
+ *
+ * * DMMP_ERR_NO_MEMORY
+ *
+ * * DMMP_ERR_NO_DAEMON
+ *
+ * Error number could be converted to string by dmmp_strerror().
+ */
+DMMP_DLL_EXPORT int dmmp_reconfig(struct dmmp_context *ctx);
+
#ifdef __cplusplus
} /* End of extern "C" */
#endif
diff --git a/libdmmp/libdmmp_misc.c b/libdmmp/libdmmp_misc.c
index 27f1161..435ddfa 100644
--- a/libdmmp/libdmmp_misc.c
+++ b/libdmmp/libdmmp_misc.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 - 2016 Red Hat, Inc.
+ * Copyright (C) 2015 - 2017 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -46,6 +46,9 @@ static const struct _num_str_conv _DMMP_RC_MSG_CONV[] = {
{DMMP_ERR_IPC_ERROR, "Error when communicate with multipathd daemon"},
{DMMP_ERR_NO_DAEMON, "The multipathd daemon not started"},
{DMMP_ERR_INCOMPATIBLE, "Incompatible multipathd daemon version"},
+ {DMMP_ERR_MPATH_BUSY, "Specified multipath device map is in use"},
+ {DMMP_ERR_MPATH_NOT_FOUND, "Specified multipath not found"},
+ {DMMP_ERR_INVALID_ARGUMENT, "Invalid argument"},
};
_dmmp_str_func_gen(dmmp_strerror, int, rc, _DMMP_RC_MSG_CONV);
diff --git a/libdmmp/test/libdmmp_test.c b/libdmmp/test/libdmmp_test.c
index 00b40e9..56bd03e 100644
--- a/libdmmp/test/libdmmp_test.c
+++ b/libdmmp/test/libdmmp_test.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015-2016 Red Hat, Inc.
+ * Copyright (C) 2015-2017 Red Hat, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -24,6 +24,7 @@
#include <string.h>
#include <pthread.h>
#include <unistd.h>
+#include <stdbool.h>
#include <libdmmp/libdmmp.h>
@@ -35,7 +36,7 @@
} while(0)
#define PASS(...) fprintf(stdout, "PASS: "__VA_ARGS__ );
#define FILE_NAME_SIZE 256
-#define TMO 10000 /* Forcing timeout to 10 seconds */
+#define TMO 60000 /* Forcing timeout to 60 seconds */
int test_paths(struct dmmp_path_group *mp_pg)
{
@@ -106,11 +107,14 @@ int main(int argc, char *argv[])
struct dmmp_context *ctx = NULL;
struct dmmp_mpath **dmmp_mps = NULL;
uint32_t dmmp_mp_count = 0;
+ uint32_t old_dmmp_mp_count = 0;
const char *name = NULL;
const char *wwid = NULL;
const char *kdev = NULL;
uint32_t i = 0;
int rc = EXIT_SUCCESS;
+ const char *old_name = NULL;
+ bool found = false;
ctx = dmmp_context_new();
dmmp_context_log_priority_set(ctx, DMMP_LOG_PRIORITY_DEBUG);
@@ -119,7 +123,7 @@ int main(int argc, char *argv[])
dmmp_context_timeout_set(ctx, TMO);
if (dmmp_context_timeout_get(ctx) != TMO)
FAIL(rc, out, "dmmp_context_timeout_set(): Failed to set "
- "timeout to %u", TMO);
+ "timeout to %u\n", TMO);
if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0)
FAIL(rc, out, "dmmp_mpath_array_get(): rc != 0\n");
@@ -140,7 +144,46 @@ int main(int argc, char *argv[])
if (rc != 0)
goto out;
}
+
+ old_name = strdup(name);
+ if (old_name == NULL)
+ FAIL(rc, out, "strdup(): no memory\n");
+
+ old_dmmp_mp_count = dmmp_mp_count;
+
dmmp_mpath_array_free(dmmp_mps, dmmp_mp_count);
+
+ if (dmmp_flush_mpath(ctx, old_name) != DMMP_OK)
+ FAIL(rc, out, "dmmp_flush_mpath(): Failed\n");
+
+ PASS("dmmp_flush_mpath(): OK\n");
+
+ if (dmmp_reconfig(ctx) != DMMP_OK)
+ FAIL(rc, out, "dmmp_reconfig(): Failed\n");
+
+ PASS("dmmp_reconfig(): OK\n");
+
+ if (dmmp_mpath_array_get(ctx, &dmmp_mps, &dmmp_mp_count) != 0)
+ FAIL(rc, out, "dmmp_mpath_array_get(): rc != 0\n");
+ if (dmmp_mp_count == 0)
+ FAIL(rc, out, "dmmp_mpath_array_get(): "
+ "Got no multipath devices\n");
+
+ if (dmmp_mp_count != old_dmmp_mp_count)
+ FAIL(rc, out, "Got different mpath count after reconfig: "
+ "old %" PRIu32 ", new %" PRIu32 "\n", old_dmmp_mp_count,
+ dmmp_mp_count);
+
+ for (i = 0; i < dmmp_mp_count; ++i) {
+ if (strcmp(old_name, dmmp_mpath_name_get(dmmp_mps[i])) == 0) {
+ found = true;
+ break;
+ }
+ }
+ if (found == false)
+ FAIL(rc, out, "dmmp_reconfig() does not recreate deleted "
+ "mpath %s\n", old_name);
+
out:
dmmp_context_free(ctx);
exit(rc);
diff --git a/libmpathcmd/mpath_cmd.c b/libmpathcmd/mpath_cmd.c
index 1496b68..af618cf 100644
--- a/libmpathcmd/mpath_cmd.c
+++ b/libmpathcmd/mpath_cmd.c
@@ -142,10 +142,6 @@ int mpath_recv_reply(int fd, char **reply, unsigned int timeout)
len = mpath_recv_reply_len(fd, timeout);
if (len <= 0)
return len;
- if (len > MAX_REPLY_LEN) {
- errno = EINVAL;
- return -1;
- }
*reply = malloc(len);
if (!*reply)
return -1;
diff --git a/libmpathcmd/mpath_cmd.h b/libmpathcmd/mpath_cmd.h
index 6534474..b57b708 100644
--- a/libmpathcmd/mpath_cmd.h
+++ b/libmpathcmd/mpath_cmd.h
@@ -26,7 +26,6 @@ extern "C" {
#define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd"
#define DEFAULT_REPLY_TIMEOUT 4000
-#define MAX_REPLY_LEN 65536
/*
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index aab6d95..b5ed556 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -261,8 +261,6 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
int map_present;
int major, minor;
int ret;
- int j;
- unsigned char *keyp;
uint64_t prkey;
struct config *conf;
@@ -339,6 +337,26 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
select_reservation_key(conf, mpp);
put_multipath_config(conf);
+ memcpy(&prkey, paramp->sa_key, 8);
+ if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey &&
+ ((!get_be64(mpp->reservation_key) && MPATH_PROUT_REG_SA) ||
+ MPATH_PROUT_REG_IGN_SA)) {
+ memcpy(&mpp->reservation_key, paramp->sa_key, 8);
+ if (update_prkey(alias, get_be64(mpp->reservation_key))) {
+ condlog(0, "%s: failed to set prkey for multipathd.",
+ alias);
+ ret = MPATH_PR_DMMP_ERROR;
+ goto out1;
+ }
+ }
+
+ if (memcmp(paramp->key, &mpp->reservation_key, 8) &&
+ memcmp(paramp->sa_key, &mpp->reservation_key, 8)) {
+ condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64, alias, get_be64(mpp->reservation_key));
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out1;
+ }
+
switch(rq_servact)
{
case MPATH_PROUT_REG_SA:
@@ -362,23 +380,14 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) ||
(rq_servact == MPATH_PROUT_REG_IGN_SA)))
{
- keyp=paramp->sa_key;
- prkey = 0;
- for (j = 0; j < 8; ++j) {
- if (j > 0)
- prkey <<= 8;
- prkey |= *keyp;
- ++keyp;
- }
- if (prkey == 0)
- update_prflag(alias, "unset", noisy);
- else
- update_prflag(alias, "set", noisy);
- } else {
- if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_CLEAR_SA) ||
- (rq_servact == MPATH_PROUT_PREE_AB_SA ))){
- update_prflag(alias, "unset", noisy);
- }
+ if (prkey == 0) {
+ update_prflag(alias, 0);
+ update_prkey(alias, 0);
+ } else
+ update_prflag(alias, 1);
+ } else if ((ret == MPATH_PR_SUCCESS) && (rq_servact == MPATH_PROUT_CLEAR_SA)) {
+ update_prflag(alias, 0);
+ update_prkey(alias, 0);
}
out1:
free_multipathvec(curmp, KEEP_PATHS);
@@ -775,8 +784,8 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
goto out1;
}
- if (mpp->reservation_key ){
- memcpy (pamp->key, mpp->reservation_key, 8);
+ if (get_be64(mpp->reservation_key)){
+ memcpy (pamp->key, &mpp->reservation_key, 8);
condlog (3, "%s: reservation key set.", mpp->wwid);
}
@@ -792,9 +801,9 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
pptr=pamp->trnptid_list[0];
for (i = 0; i < num; i++){
- if (mpp->reservation_key &&
+ if (get_be64(mpp->reservation_key) &&
memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key,
- mpp->reservation_key, 8)){
+ &mpp->reservation_key, 8)){
/*register with tarnsport id*/
memset(pamp, 0, length);
pamp->trnptid_list[0] = pptr;
@@ -819,7 +828,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
}
else
{
- if (mpp->reservation_key)
+ if (get_be64(mpp->reservation_key))
found = 1;
}
@@ -828,7 +837,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
if (found){
memset (pamp, 0, length);
- memcpy (pamp->sa_key, mpp->reservation_key, 8);
+ memcpy (pamp->sa_key, &mpp->reservation_key, 8);
memset (pamp->key, 0, 8);
status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
}
@@ -873,11 +882,9 @@ int update_map_pr(struct multipath *mpp)
{
int noisy=0;
struct prin_resp *resp;
- int i,j, ret, isFound;
- unsigned char *keyp;
- uint64_t prkey;
+ int i, ret, isFound;
- if (!mpp->reservation_key)
+ if (!get_be64(mpp->reservation_key))
{
/* Nothing to do. Assuming pr mgmt feature is disabled*/
condlog(3, "%s: reservation_key not set in multipath.conf", mpp->alias);
@@ -906,15 +913,8 @@ int update_map_pr(struct multipath *mpp)
return MPATH_PR_SUCCESS;
}
- prkey = 0;
- keyp = mpp->reservation_key;
- for (j = 0; j < 8; ++j) {
- if (j > 0)
- prkey <<= 8;
- prkey |= *keyp;
- ++keyp;
- }
- condlog(2, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias, prkey);
+ condlog(2, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
+ get_be64(mpp->reservation_key));
isFound =0;
for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ )
@@ -922,7 +922,7 @@ int update_map_pr(struct multipath *mpp)
condlog(2, "%s: PR IN READKEYS[%d] reservation key:", mpp->alias, i);
dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , 1);
- if (!memcmp(mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
+ if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
{
condlog(2, "%s: reservation key found in pr in readkeys response", mpp->alias);
isFound =1;
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
index b3701b2..8063e90 100644
--- a/libmpathpersist/mpath_updatepr.c
+++ b/libmpathpersist/mpath_updatepr.c
@@ -18,42 +18,53 @@
#include "mpathpr.h"
-int update_prflag(char * arg1, char * arg2, int noisy)
+static int do_update_pr(char *alias, char *arg)
{
int fd;
- char str[64];
+ char str[256];
char *reply;
int ret = 0;
fd = mpath_connect();
if (fd == -1) {
condlog (0, "ux socket connect error");
- return 1 ;
+ return -1;
}
- snprintf(str,sizeof(str),"map %s %s", arg1, arg2);
- condlog (2, "%s: pr flag message=%s", arg1, str);
+ snprintf(str,sizeof(str),"map %s %s", alias, arg);
+ condlog (2, "%s: pr message=%s", alias, str);
if (send_packet(fd, str) != 0) {
- condlog(2, "%s: message=%s send error=%d", arg1, str, errno);
+ condlog(2, "%s: message=%s send error=%d", alias, str, errno);
mpath_disconnect(fd);
- return -2;
+ return -1;
}
ret = recv_packet(fd, &reply, DEFAULT_REPLY_TIMEOUT);
if (ret < 0) {
- condlog(2, "%s: message=%s recv error=%d", arg1, str, errno);
- ret = -2;
+ condlog(2, "%s: message=%s recv error=%d", alias, str, errno);
+ ret = -1;
} else {
- condlog (2, "%s: message=%s reply=%s", arg1, str, reply);
- if (!reply || strncmp(reply,"ok", 2) == 0)
+ condlog (2, "%s: message=%s reply=%s", alias, str, reply);
+ if (reply && strncmp(reply,"ok", 2) == 0)
+ ret = 0;
+ else
ret = -1;
- else if (strncmp(reply, "fail", 4) == 0)
- ret = -2;
- else{
- ret = atoi(reply);
- }
}
free(reply);
mpath_disconnect(fd);
return ret;
}
+
+int update_prflag(char *mapname, int set) {
+ return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus");
+}
+
+int update_prkey(char *mapname, uint64_t prkey) {
+ char str[256];
+
+ if (prkey)
+ sprintf(str, "setprkey key %" PRIx64, prkey);
+ else
+ sprintf(str, "unsetprkey");
+ return do_update_pr(mapname, str);
+}
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 99e641b..72feb60 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -45,7 +45,8 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
int send_prout_activepath(char * dev, int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
-int update_prflag(char * arg1, char * arg2, int noisy);
+int update_prflag(char *mapname, int set);
+int update_prkey(char *mapname, uint64_t prkey);
void * mpath_alloc_prin_response(int prin_sa);
int update_map_pr(struct multipath *mpp);
diff --git a/libmultipath/Makefile b/libmultipath/Makefile
index b3244fc..928bc25 100644
--- a/libmultipath/Makefile
+++ b/libmultipath/Makefile
@@ -42,7 +42,7 @@ OBJS = memory.o parser.o vector.o devmapper.o callout.o \
pgpolicies.o debug.o defaults.o uevent.o time-util.o \
switchgroup.o uxsock.o print.o alias.o log_pthread.o \
log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \
- lock.o waiter.o file.o wwids.o prioritizers/alua_rtpg.o
+ lock.o waiter.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o
all: $(LIBS)
diff --git a/libmultipath/byteorder.h b/libmultipath/byteorder.h
new file mode 100644
index 0000000..5c77146
--- /dev/null
+++ b/libmultipath/byteorder.h
@@ -0,0 +1,44 @@
+#ifndef BYTEORDER_H_INCLUDED
+#define BYTEORDER_H_INCLUDED
+
+#ifdef __linux__
+# include <endian.h>
+# include <byteswap.h>
+#else
+# error unsupported
+#endif
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+# define le16_to_cpu(x) (x)
+# define be16_to_cpu(x) bswap_16(x)
+# define le32_to_cpu(x) (x)
+# define le64_to_cpu(x) (x)
+# define be32_to_cpu(x) bswap_32(x)
+# define be64_to_cpu(x) bswap_64(x)
+#elif BYTE_ORDER == BIG_ENDIAN
+# define le16_to_cpu(x) bswap_16(x)
+# define be16_to_cpu(x) (x)
+# define le32_to_cpu(x) bswap_32(x)
+# define le64_to_cpu(x) bswap_64(x)
+# define be32_to_cpu(x) (x)
+# define be64_to_cpu(x) (x)
+#else
+# error unsupported
+#endif
+
+#define cpu_to_le16(x) le16_to_cpu(x)
+#define cpu_to_be16(x) be16_to_cpu(x)
+#define cpu_to_le32(x) le32_to_cpu(x)
+#define cpu_to_be32(x) be32_to_cpu(x)
+#define cpu_to_le64(x) le64_to_cpu(x)
+#define cpu_to_be64(x) be64_to_cpu(x)
+
+struct be64 {
+ uint64_t _v;
+};
+
+#define get_be64(x) be64_to_cpu((x)._v)
+#define put_be64(x, y) do { (x)._v = cpu_to_be64(y); } while (0)
+
+
+#endif /* BYTEORDER_H_INCLUDED */
diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c
index 05e024f..cd6d6a3 100644
--- a/libmultipath/checkers.c
+++ b/libmultipath/checkers.c
@@ -19,6 +19,7 @@ char *checker_state_names[] = {
"timeout",
"removed",
"delayed",
+ "none",
};
static LIST_HEAD(checkers);
@@ -102,6 +103,8 @@ struct checker * add_checker (char *multipath_dir, char * name)
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",
multipath_dir, name);
if (stat(libname,&stbuf) < 0) {
@@ -145,8 +148,8 @@ struct checker * add_checker (char *multipath_dir, char * name)
condlog(0, "A dynamic linking error occurred: (%s)", errstr);
if (!c->repair)
goto out;
-
- c->fd = 0;
+done:
+ c->fd = -1;
c->sync = 1;
list_add(&c->node, &checkers);
return c;
@@ -195,14 +198,16 @@ int checker_init (struct checker * c, void ** mpctxt_addr)
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 || !dst->check)
+ if (!dst || !strlen(dst->name))
return;
src = checker_lookup(dst->name);
if (dst->free)
@@ -221,11 +226,11 @@ void checker_repair (struct checker * c)
MSG(c, "checker disabled");
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;
@@ -237,7 +242,10 @@ int checker_check (struct checker * c)
MSG(c, "checker disabled");
return PATH_UNCHECKED;
}
- if (c->fd <= 0) {
+ if (!strncmp(c->name, NONE, 4))
+ return path_state;
+
+ if (c->fd < 0) {
MSG(c, "no usable fd");
return PATH_WILD;
}
@@ -250,6 +258,8 @@ int checker_selected (struct checker * c)
{
if (!c)
return 0;
+ if (!strncmp(c->name, NONE, 4))
+ return 1;
return (c->check) ? 1 : 0;
}
diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h
index 1d225de..713399f 100644
--- a/libmultipath/checkers.h
+++ b/libmultipath/checkers.h
@@ -85,6 +85,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 ASYNC_TIMEOUT_SEC 30
@@ -135,7 +136,7 @@ void checker_set_fd (struct checker *, int);
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 *);
diff --git a/libmultipath/checkers/rbd.c b/libmultipath/checkers/rbd.c
index 9ea0572..2c18009 100644
--- a/libmultipath/checkers/rbd.c
+++ b/libmultipath/checkers/rbd.c
@@ -28,6 +28,7 @@
#include "../libmultipath/debug.h"
#include "../libmultipath/util.h"
#include "../libmultipath/time-util.h"
+#include "../libmultipath/util.h"
struct rbd_checker_context;
typedef int (thread_fn)(struct rbd_checker_context *ct, char *msg);
@@ -356,21 +357,6 @@ static int rbd_check(struct rbd_checker_context *ct, char *msg)
return PATH_UP;
}
-static int safe_write(int fd, const void *buf, size_t count)
-{
- while (count > 0) {
- ssize_t r = write(fd, buf, count);
- if (r < 0) {
- if (errno == EINTR)
- continue;
- return -errno;
- }
- count -= r;
- buf = (char *)buf + r;
- }
- return 0;
-}
-
static int sysfs_write_rbd_bus(const char *which, const char *buf,
size_t buf_len)
{
diff --git a/libmultipath/config.c b/libmultipath/config.c
index b21a3aa..ea2359a 100644
--- a/libmultipath/config.c
+++ b/libmultipath/config.c
@@ -515,6 +515,9 @@ free_config (struct config * conf)
if (conf->wwids_file)
FREE(conf->wwids_file);
+ if (conf->prkeys_file)
+ FREE(conf->prkeys_file);
+
if (conf->prio_name)
FREE(conf->prio_name);
@@ -532,9 +535,6 @@ free_config (struct config * conf)
if (conf->config_dir)
FREE(conf->config_dir);
- if (conf->reservation_key)
- FREE(conf->reservation_key);
-
free_blacklist(conf->blist_devnode);
free_blacklist(conf->blist_wwid);
free_blacklist(conf->blist_property);
@@ -606,6 +606,7 @@ load_config (char * file)
get_sys_max_fds(&conf->max_fds);
conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE);
conf->wwids_file = set_default(DEFAULT_WWIDS_FILE);
+ conf->prkeys_file = set_default(DEFAULT_PRKEYS_FILE);
conf->multipath_dir = set_default(DEFAULT_MULTIPATHDIR);
conf->attribute_flags = 0;
conf->reassign_maps = DEFAULT_REASSIGN_MAPS;
@@ -731,7 +732,7 @@ load_config (char * file)
conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE);
if (!conf->multipath_dir || !conf->bindings_file ||
- !conf->wwids_file)
+ !conf->wwids_file || !conf->prkeys_file)
goto out;
return conf;
diff --git a/libmultipath/config.h b/libmultipath/config.h
index ffc69b5..240730b 100644
--- a/libmultipath/config.h
+++ b/libmultipath/config.h
@@ -4,6 +4,8 @@
#include <sys/types.h>
#include <stdint.h>
#include <urcu.h>
+#include <inttypes.h>
+#include "byteorder.h"
#define ORIGIN_DEFAULT 0
#define ORIGIN_CONFIG 1
@@ -34,6 +36,7 @@ enum mpath_cmds {
CMD_REMOVE_WWID,
CMD_RESET_WWIDS,
CMD_ADD_WWID,
+ CMD_USABLE_PATHS,
};
enum force_reload_types {
@@ -90,7 +93,8 @@ struct mpentry {
char * prio_name;
char * prio_args;
- unsigned char * reservation_key;
+ int prkey_source;
+ struct be64 reservation_key;
int pgpolicy;
int pgfailback;
int rr_weight;
@@ -177,13 +181,15 @@ struct config {
char * hwhandler;
char * bindings_file;
char * wwids_file;
+ char * prkeys_file;
char * prio_name;
char * prio_args;
char * checker_name;
char * alias_prefix;
char * partition_delim;
char * config_dir;
- unsigned char * reservation_key;
+ int prkey_source;
+ struct be64 reservation_key;
vector keywords;
vector mptable;
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index 74b6f52..7a3db31 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -1106,6 +1106,41 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
return 0;
}
+struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type)
+{
+ struct udev_device *ud = NULL;
+ const char *base;
+
+ if (dev == NULL || *dev == '\0')
+ return NULL;
+
+ switch (dev_type) {
+ case DEV_DEVNODE:
+ case DEV_DEVMAP:
+ /* This should be GNU basename, compiler will warn if not */
+ base = basename(dev);
+ if (*base == '\0')
+ break;
+ ud = udev_device_new_from_subsystem_sysname(udev, "block",
+ base);
+ break;
+ case DEV_DEVT:
+ ud = udev_device_new_from_devnum(udev, 'b', parse_devt(dev));
+ break;
+ case DEV_UEVENT:
+ ud = udev_device_new_from_environment(udev);
+ break;
+ default:
+ condlog(0, "Internal error: get_udev_device called with invalid type %d\n",
+ dev_type);
+ break;
+ }
+ if (ud == NULL)
+ condlog(2, "get_udev_device: failed to look up %s with type %d",
+ dev, dev_type);
+ return ud;
+}
+
/*
* returns:
* 0 - success
@@ -1141,12 +1176,12 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
pp = find_path_by_dev(pathvec, buff);
if (!pp) {
- struct udev_device *udevice = udev_device_new_from_subsystem_sysname(udev, "block", buff);
+ struct udev_device *udevice =
+ get_udev_device(buff, dev_type);
- if (!udevice) {
- condlog(2, "%s: can't get udev device", buff);
+ if (!udevice)
return 1;
- }
+
conf = get_multipath_config();
ret = store_pathinfo(pathvec, conf, udevice,
flags, &pp);
@@ -1179,12 +1214,12 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
}
pp = find_path_by_dev(pathvec, buff);
if (!pp) {
- struct udev_device *udevice = udev_device_new_from_devnum(udev, 'b', parse_devt(dev));
+ struct udev_device *udevice =
+ get_udev_device(dev, dev_type);
- if (!udevice) {
- condlog(2, "%s: can't get udev device", dev);
+ if (!udevice)
return 1;
- }
+
conf = get_multipath_config();
ret = store_pathinfo(pathvec, conf, udevice,
flags, &pp);
@@ -1209,12 +1244,11 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type,
}
if (dev_type == DEV_UEVENT) {
- struct udev_device *udevice = udev_device_new_from_environment(udev);
+ struct udev_device *udevice = get_udev_device(dev, dev_type);
- if (!udevice) {
- condlog(2, "%s: can't get udev device", dev);
+ if (!udevice)
return 1;
- }
+
conf = get_multipath_config();
ret = store_pathinfo(pathvec, conf, udevice,
flags, &pp);
diff --git a/libmultipath/configure.h b/libmultipath/configure.h
index fd7f581..0ffc28e 100644
--- a/libmultipath/configure.h
+++ b/libmultipath/configure.h
@@ -36,3 +36,4 @@ int get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type,
vector pathvec, char **wwid);
int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, int is_daemon);
int sysfs_get_host_adapter_name(struct path *pp, char *adapter_name);
+struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type);
diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h
index db2b756..740ccf4 100644
--- a/libmultipath/defaults.h
+++ b/libmultipath/defaults.h
@@ -50,6 +50,7 @@
#define DEFAULT_CONFIGFILE "/etc/multipath.conf"
#define DEFAULT_BINDINGS_FILE "/etc/multipath/bindings"
#define DEFAULT_WWIDS_FILE "/etc/multipath/wwids"
+#define DEFAULT_PRKEYS_FILE "/etc/multipath/prkeys"
#define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d"
char * set_default (char * str);
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index 3b41a48..fcac6bc 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -300,12 +300,14 @@ dm_device_remove (const char *name, int needsync, int deferred_remove) {
static int
dm_addmap (int task, const char *target, struct multipath *mpp,
- char * params, int ro, int skip_kpartx) {
+ char * params, int ro, uint16_t udev_flags) {
int r = 0;
struct dm_task *dmt;
char *prefixed_uuid = NULL;
uint32_t cookie = 0;
- uint16_t udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK | ((skip_kpartx == SKIP_KPARTX_ON)? MPATH_UDEV_NO_KPARTX_FLAG : 0);
+
+ /* Need to add this here to allow 0 to be passed in udev_flags */
+ udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK;
if (!(dmt = libmp_dm_task_create (task)))
return 0;
@@ -371,15 +373,27 @@ addout:
return r;
}
+static uint16_t build_udev_flags(const struct multipath *mpp, int reload)
+{
+ /* DM_UDEV_DISABLE_LIBRARY_FALLBACK is added in dm_addmap */
+ return (mpp->skip_kpartx == SKIP_KPARTX_ON ?
+ MPATH_UDEV_NO_KPARTX_FLAG : 0) |
+ (mpp->nr_active == 0 ?
+ MPATH_UDEV_NO_PATHS_FLAG : 0) |
+ (reload && !mpp->force_udev_reload ?
+ MPATH_UDEV_RELOAD_FLAG : 0);
+}
+
int dm_addmap_create (struct multipath *mpp, char * params)
{
int ro;
+ uint16_t udev_flags = build_udev_flags(mpp, 0);
for (ro = 0; ro <= 1; ro++) {
int err;
if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, ro,
- mpp->skip_kpartx))
+ udev_flags))
return 1;
/*
* DM_DEVICE_CREATE is actually DM_DEV_CREATE + DM_TABLE_LOAD.
@@ -405,11 +419,7 @@ int dm_addmap_create (struct multipath *mpp, char * params)
int dm_addmap_reload(struct multipath *mpp, char *params, int flush)
{
int r = 0;
- uint16_t udev_flags = ((mpp->force_udev_reload)?
- 0 : MPATH_UDEV_RELOAD_FLAG) |
- ((mpp->skip_kpartx == SKIP_KPARTX_ON)?
- MPATH_UDEV_NO_KPARTX_FLAG : 0) |
- ((mpp->nr_active)? 0 : MPATH_UDEV_NO_PATHS_FLAG);
+ uint16_t udev_flags = build_udev_flags(mpp, 1);
/*
* DM_DEVICE_RELOAD cannot wait on a cookie, as
@@ -419,12 +429,12 @@ int dm_addmap_reload(struct multipath *mpp, char *params, int flush)
*/
if (!mpp->force_readonly)
r = dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp, params,
- ADDMAP_RW, SKIP_KPARTX_OFF);
+ ADDMAP_RW, 0);
if (!r) {
if (!mpp->force_readonly && errno != EROFS)
return 0;
r = dm_addmap(DM_DEVICE_RELOAD, TGT_MPATH, mpp,
- params, ADDMAP_RO, SKIP_KPARTX_OFF);
+ params, ADDMAP_RO, 0);
}
if (r)
r = dm_simplecmd(DM_DEVICE_RESUME, mpp->alias, !flush,
@@ -543,7 +553,7 @@ uuidout:
return r;
}
-int dm_get_uuid(char *name, char *uuid)
+int dm_get_uuid(const char *name, char *uuid)
{
if (dm_get_prefixed_uuid(name, uuid))
return 1;
@@ -994,6 +1004,31 @@ dm_disablegroup(char * mapname, int index)
return dm_groupmsg("disable", mapname, index);
}
+struct multipath *dm_get_multipath(const char *name)
+{
+ struct multipath *mpp = NULL;
+
+ mpp = alloc_multipath();
+ if (!mpp)
+ return NULL;
+
+ mpp->alias = STRDUP(name);
+
+ if (!mpp->alias)
+ goto out;
+
+ if (dm_get_map(name, &mpp->size, NULL))
+ goto out;
+
+ dm_get_uuid(name, mpp->wwid);
+ dm_get_info(name, &mpp->dmi);
+
+ return mpp;
+out:
+ free_multipath(mpp, KEEP_PATHS);
+ return NULL;
+}
+
int
dm_get_maps (vector mp)
{
@@ -1026,24 +1061,12 @@ dm_get_maps (vector mp)
if (!dm_is_mpath(names->name))
goto next;
- mpp = alloc_multipath();
-
+ mpp = dm_get_multipath(names->name);
if (!mpp)
goto out;
- mpp->alias = STRDUP(names->name);
-
- if (!mpp->alias)
- goto out1;
-
- if (dm_get_map(names->name, &mpp->size, NULL))
- goto out1;
-
- dm_get_uuid(names->name, mpp->wwid);
- dm_get_info(names->name, &mpp->dmi);
-
if (!vector_alloc_slot(mp))
- goto out1;
+ goto out;
vector_set_slot(mp, mpp);
mpp = NULL;
@@ -1054,8 +1077,6 @@ next:
r = 0;
goto out;
-out1:
- free_multipath(mpp, KEEP_PATHS);
out:
dm_task_destroy (dmt);
return r;
@@ -1298,7 +1319,7 @@ alloc_dminfo (void)
}
int
-dm_get_info (char * mapname, struct dm_info ** dmi)
+dm_get_info (const char * mapname, struct dm_info ** dmi)
{
if (!mapname)
return 1;
@@ -1351,10 +1372,12 @@ dm_rename_partmaps (const char * old, char * new, char *delim)
if (delim)
rd.delim = delim;
- if (isdigit(new[strlen(new)-1]))
- rd.delim = "p";
- else
- rd.delim = "";
+ else {
+ if (isdigit(new[strlen(new)-1]))
+ rd.delim = "p";
+ else
+ rd.delim = "";
+ }
return do_foreach_partmaps(old, rename_partmap, &rd);
}
diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h
index 99a554b..62e14d1 100644
--- a/libmultipath/devmapper.h
+++ b/libmultipath/devmapper.h
@@ -59,12 +59,13 @@ int dm_get_major_minor (const char *name, int *major, int *minor);
char * dm_mapname(int major, int minor);
int dm_remove_partmaps (const char * mapname, int need_sync,
int deferred_remove);
-int dm_get_uuid(char *name, char *uuid);
-int dm_get_info (char * mapname, struct dm_info ** dmi);
+int dm_get_uuid(const char *name, char *uuid);
+int dm_get_info (const char * mapname, struct dm_info ** dmi);
int dm_rename (const char * old, char * new, char * delim, int skip_kpartx);
int dm_reassign(const char * mapname);
int dm_reassign_table(const char *name, char *old, char *new);
int dm_setgeometry(struct multipath *mpp);
+struct multipath *dm_get_multipath(const char *name);
#define VERSION_GE(v, minv) ( \
(v[0] > minv[0]) || \
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
index 9dc1090..36cccc9 100644
--- a/libmultipath/dict.c
+++ b/libmultipath/dict.c
@@ -19,6 +19,7 @@
#include "blacklist.h"
#include "defaults.h"
#include "prio.h"
+#include "util.h"
#include <errno.h>
#include <inttypes.h>
#include "mpath_cmd.h"
@@ -367,6 +368,9 @@ declare_def_snprint(bindings_file, print_str)
declare_def_handler(wwids_file, set_str)
declare_def_snprint(wwids_file, print_str)
+declare_def_handler(prkeys_file, set_str)
+declare_def_snprint(prkeys_file, print_str)
+
declare_def_handler(retain_hwhandler, set_yes_no_undef)
declare_def_snprint_defint(retain_hwhandler, print_yes_no_undef, YNU_NO)
declare_ovr_handler(retain_hwhandler, set_yes_no_undef)
@@ -959,77 +963,75 @@ snprint_def_log_checker_err (struct config *conf, char * buff, int len, void * d
}
static int
-set_reservation_key(vector strvec, void *ptr)
+set_reservation_key(vector strvec, struct be64 *be64_ptr, int *source_ptr)
{
- unsigned char **uchar_ptr = (unsigned char **)ptr;
char *buff;
- char *tbuff;
- int j, k;
- int len;
uint64_t prkey;
buff = set_value(strvec);
if (!buff)
return 1;
- tbuff = buff;
-
- if (!memcmp("0x",buff, 2))
- buff = buff + 2;
-
- len = strlen(buff);
-
- k = strspn(buff, "0123456789aAbBcCdDeEfF");
-
- if (len != k) {
- FREE(tbuff);
- return 1;
+ if (strcmp(buff, "file") == 0) {
+ *source_ptr = PRKEY_SOURCE_FILE;
+ put_be64(*be64_ptr, 0);
+ FREE(buff);
+ return 0;
}
- if (1 != sscanf (buff, "%" SCNx64 "", &prkey))
- {
- FREE(tbuff);
+ if (parse_prkey(buff, &prkey) != 0) {
+ FREE(buff);
return 1;
}
-
- if (!*uchar_ptr)
- *uchar_ptr = (unsigned char *) malloc(8);
-
- memset(*uchar_ptr, 0, 8);
-
- for (j = 7; j >= 0; --j) {
- (*uchar_ptr)[j] = (prkey & 0xff);
- prkey >>= 8;
- }
-
- FREE(tbuff);
+ *source_ptr = PRKEY_SOURCE_CONF;
+ put_be64(*be64_ptr, prkey);
+ FREE(buff);
return 0;
}
int
-print_reservation_key(char * buff, int len, void * ptr)
+print_reservation_key(char * buff, int len, struct be64 key, int source)
{
- unsigned char **uchar_ptr = (unsigned char **)ptr;
- int i;
- unsigned char *keyp;
- uint64_t prkey = 0;
-
- if (!*uchar_ptr)
+ if (source == PRKEY_SOURCE_NONE)
return 0;
- keyp = (unsigned char *)(*uchar_ptr);
- for (i = 0; i < 8; i++) {
- if (i > 0)
- prkey <<= 8;
- prkey |= *keyp;
- keyp++;
- }
- return snprintf(buff, len, "0x%" PRIx64, prkey);
+ if (source == PRKEY_SOURCE_FILE)
+ return snprintf(buff, len, "file");
+ return snprintf(buff, len, "0x%" PRIx64, get_be64(key));
+}
+
+static int
+def_reservation_key_handler(struct config *conf, vector strvec)
+{
+ return set_reservation_key(strvec, &conf->reservation_key,
+ &conf->prkey_source);
+}
+
+static int
+snprint_def_reservation_key (struct config *conf, char * buff, int len,
+ void * data)
+{
+ return print_reservation_key(buff, len, conf->reservation_key,
+ conf->prkey_source);
+}
+
+static int
+mp_reservation_key_handler(struct config *conf, vector strvec)
+{
+ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable);
+ if (!mpe)
+ return 1;
+ return set_reservation_key(strvec, &mpe->reservation_key,
+ &mpe->prkey_source);
}
-declare_def_handler(reservation_key, set_reservation_key)
-declare_def_snprint(reservation_key, print_reservation_key)
-declare_mp_handler(reservation_key, set_reservation_key)
-declare_mp_snprint(reservation_key, print_reservation_key)
+static int
+snprint_mp_reservation_key (struct config *conf, char * buff, int len,
+ void * data)
+{
+ struct mpentry * mpe = (struct mpentry *)data;
+ return print_reservation_key(buff, len, mpe->reservation_key,
+ mpe->prkey_source);
+}
static int
set_off_int_undef(vector strvec, void *ptr)
@@ -1428,6 +1430,7 @@ init_keywords(vector keywords)
install_keyword("dev_loss_tmo", &def_dev_loss_handler, &snprint_def_dev_loss);
install_keyword("bindings_file", &def_bindings_file_handler, &snprint_def_bindings_file);
install_keyword("wwids_file", &def_wwids_file_handler, &snprint_def_wwids_file);
+ install_keyword("prkeys_file", &def_prkeys_file_handler, &snprint_def_prkeys_file);
install_keyword("log_checker_err", &def_log_checker_err_handler, &snprint_def_log_checker_err);
install_keyword("reservation_key", &def_reservation_key_handler, &snprint_def_reservation_key);
install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler);
diff --git a/libmultipath/dict.h b/libmultipath/dict.h
index 2d6097d..0442227 100644
--- a/libmultipath/dict.h
+++ b/libmultipath/dict.h
@@ -5,6 +5,8 @@
#include "vector.h"
#endif
+#include "byteorder.h"
+
void init_keywords(vector keywords);
int get_sys_max_fds(int *);
int print_rr_weight (char * buff, int len, void *ptr);
@@ -13,6 +15,6 @@ int print_pgpolicy(char * buff, int len, void *ptr);
int print_no_path_retry(char * buff, int len, void *ptr);
int print_fast_io_fail(char * buff, int len, void *ptr);
int print_dev_loss(char * buff, int len, void *ptr);
-int print_reservation_key(char * buff, int len, void * ptr);
+int print_reservation_key(char * buff, int len, struct be64 key, int source);
int print_off_int_undef(char * buff, int len, void *ptr);
#endif /* _DICT_H */
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index 3a912d7..efac824 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -1194,28 +1194,37 @@ scsi_sysfs_pathinfo (struct path * pp, vector hwtable)
static int
nvme_sysfs_pathinfo (struct path * pp, vector hwtable)
{
- struct udev_device *parent, *nvme = NULL;
+ struct udev_device *parent;
+ const char *attr_path = NULL;
- parent = pp->udev;
- while (parent) {
- const char *subsys = udev_device_get_subsystem(parent);
+ attr_path = udev_device_get_sysname(pp->udev);
+ if (!attr_path)
+ return 1;
- if (subsys && !strncmp(subsys, "nvme", 4)) {
- nvme = parent;
- break;
- }
- parent = udev_device_get_parent(parent);
- }
- if (!nvme)
+ 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(nvme, "model"));
- snprintf(pp->serial, SERIAL_SIZE, "%s", udev_device_get_sysattr_value(nvme, "serial"));
- snprintf(pp->rev, SCSI_REV_SIZE, "%s", udev_device_get_sysattr_value(nvme, "firmware_rev"));
+ 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);
- condlog(3, "%s: vendor:%s product:%s serial:%s rev:%s", pp->dev,
- pp->vendor_id, pp->product_id, pp->serial, pp->rev);
pp->hwe = find_hwe(hwtable, pp->vendor_id, pp->product_id, NULL);
return 0;
@@ -1440,8 +1449,7 @@ path_offline (struct path * pp)
}
pp->offline = 0;
if (!strncmp(buff, "new", 3) ||
- !strncmp(buff, "reconnecting", 12) ||
- !strncmp(buff, "resetting", 9))
+ !strncmp(buff, "deleting", 8))
return PATH_PENDING;
else if (!strncmp(buff, "live", 4))
return PATH_UP;
@@ -1542,7 +1550,7 @@ cciss_ioctl_pathinfo (struct path * pp, int mask)
}
int
-get_state (struct path * pp, struct config *conf, int daemon)
+get_state (struct path * pp, struct config *conf, int daemon, int oldstate)
{
struct checker * c = &pp->checker;
int state;
@@ -1580,8 +1588,9 @@ get_state (struct path * pp, struct config *conf, int daemon)
if (!conf->checker_timeout &&
sysfs_get_timeout(pp, &(c->timeout)) <= 0)
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\"",
@@ -1926,6 +1935,14 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
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;
+ return PATHINFO_OK;
+ }
/*
* fetch info not available through sysfs
@@ -1952,7 +1969,8 @@ 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);
+ pp->chkrstate = pp->state = get_state(pp, conf, 0,
+ path_state);
if (pp->state == PATH_UNCHECKED ||
pp->state == PATH_WILD)
goto blank;
diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h
index 51c23d6..b151cc7 100644
--- a/libmultipath/discovery.h
+++ b/libmultipath/discovery.h
@@ -34,7 +34,7 @@ int path_discovery (vector pathvec, int flag);
int do_tur (char *);
int path_offline (struct path *);
-int get_state (struct path * pp, struct config * conf, int daemon);
+int get_state (struct path * pp, struct config * conf, int daemon, int state);
int get_vpd_sgio (int fd, int pg, char * str, int maxlen);
int pathinfo (struct path * pp, struct config * conf, int mask);
int alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,
@@ -62,6 +62,7 @@ enum discovery_mode {
__DI_PRIO,
__DI_WWID,
__DI_BLACKLIST,
+ __DI_NOIO,
};
#define DI_SYSFS (1 << __DI_SYSFS)
@@ -70,6 +71,7 @@ enum discovery_mode {
#define DI_PRIO (1 << __DI_PRIO)
#define DI_WWID (1 << __DI_WWID)
#define DI_BLACKLIST (1 << __DI_BLACKLIST)
+#define DI_NOIO (1 << __DI_NOIO) /* Avoid IO on the device */
#define DI_ALL (DI_SYSFS | DI_SERIAL | DI_CHECKER | DI_PRIO | \
DI_WWID)
diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c
index 9e14ec1..b018ddf 100644
--- a/libmultipath/hwtable.c
+++ b/libmultipath/hwtable.c
@@ -796,9 +796,9 @@ static struct hwentry default_hw[] = {
.no_path_retry = 30,
},
{
- /* 3510 / 6020 and 6120 */
+ /* (Dot Hill) 3310, 3320, 3510 and 3511 */
.vendor = "SUN",
- .product = "(StorEdge 3510|T4)",
+ .product = "StorEdge 3",
.pgpolicy = MULTIBUS,
},
{
@@ -964,13 +964,24 @@ static struct hwentry default_hw[] = {
},
/*
* Infinidat
+ *
+ * Maintainer: Arnon Yaari
+ * Mail: arnony@infinidat.com
*/
{
.vendor = "NFINIDAT",
.product = "InfiniBox",
.pgpolicy = GROUP_BY_PRIO,
- .pgfailback = -FAILBACK_IMMEDIATE,
+ .pgfailback = 30,
.prio_name = PRIO_ALUA,
+ .selector = "round-robin 0",
+ .rr_weight = RR_WEIGHT_PRIO,
+ .no_path_retry = NO_PATH_RETRY_FAIL,
+ .minio = 1,
+ .minio_rq = 1,
+ .flush_on_last_del = FLUSH_ENABLED,
+ .fast_io_fail = 15,
+ .dev_loss = 15,
},
/*
* Nimble Storage
@@ -1045,6 +1056,13 @@ static struct hwentry default_hw[] = {
.pgpolicy = MULTIBUS,
.no_path_retry = 12,
},
+ {
+ /* iglu blaze family */
+ .vendor = "(XIOTECH|XIOtech)",
+ .product = "IGLU DISK",
+ .pgpolicy = MULTIBUS,
+ .no_path_retry = 30,
+ },
/*
* Violin Memory
*/
@@ -1113,10 +1131,38 @@ static struct hwentry default_hw[] = {
.vendor = "NVME",
.product = ".*",
.uid_attribute = "ID_WWN",
- .checker_name = DIRECTIO,
+ .checker_name = NONE,
.retain_hwhandler = RETAIN_HWHANDLER_OFF,
},
/*
+ * Dot Hill Systems - Seagate Technology
+ */
+ {
+ /* SANnet family */
+ .vendor = "DotHill",
+ .product = "SANnet",
+ .pgpolicy = MULTIBUS,
+ .no_path_retry = 30,
+ },
+ {
+ /* R/Evolution family */
+ .vendor = "DotHill",
+ .product = "R/Evo",
+ .pgpolicy = GROUP_BY_PRIO,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .prio_name = PRIO_ALUA,
+ .no_path_retry = 30,
+ },
+ {
+ /* AssuredSAN family */
+ .vendor = "DotHill",
+ .product = "^DH",
+ .pgpolicy = GROUP_BY_PRIO,
+ .pgfailback = -FAILBACK_IMMEDIATE,
+ .prio_name = PRIO_ALUA,
+ .no_path_retry = 30,
+ },
+ /*
* EOL
*/
{
diff --git a/libmultipath/print.c b/libmultipath/print.c
index 95dff90..65a9824 100644
--- a/libmultipath/print.c
+++ b/libmultipath/print.c
@@ -1677,7 +1677,7 @@ int snprint_status(char *buff, int len, struct vectors *vecs)
int monitored_count = 0;
vector_foreach_slot(vecs->pathvec, pp, i)
- if (pp->fd != -1)
+ if (pp->fd >= 0)
monitored_count++;
fwd += snprintf(buff + fwd, len - fwd, "\npaths: %d\nbusy: %s\n",
monitored_count, is_uevent_busy()? "True" : "False");
diff --git a/libmultipath/prkey.c b/libmultipath/prkey.c
new file mode 100644
index 0000000..89b90ed
--- /dev/null
+++ b/libmultipath/prkey.c
@@ -0,0 +1,166 @@
+#include "structs.h"
+#include "file.h"
+#include "debug.h"
+#include "config.h"
+#include "util.h"
+#include "propsel.h"
+#include "prkey.h"
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <errno.h>
+
+#define PRKEY_READ 0
+#define PRKEY_WRITE 1
+
+static int do_prkey(int fd, char *wwid, char *keystr, int cmd)
+{
+ char buf[4097];
+ char *ptr;
+ off_t start = 0;
+ int bytes;
+
+ while (1) {
+ if (lseek(fd, start, SEEK_SET) < 0) {
+ condlog(0, "prkey file read lseek failed : %s",
+ strerror(errno));
+ return 1;
+ }
+ bytes = read(fd, buf, 4096);
+ if (bytes < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ condlog(0, "failed to read from prkey file : %s",
+ strerror(errno));
+ return 1;
+ }
+ if (!bytes) {
+ ptr = NULL;
+ break;
+ }
+ buf[bytes] = '\0';
+ ptr = strstr(buf, wwid);
+ while (ptr) {
+ if (ptr == buf || *(ptr - 1) != ' ' ||
+ *(ptr + strlen(wwid)) != '\n')
+ ptr = strstr(ptr + strlen(wwid), wwid);
+ else
+ break;
+ }
+ if (ptr) {
+ condlog(3, "found prkey for '%s'", wwid);
+ ptr[strlen(wwid)] = '\0';
+ if (ptr - PRKEY_SIZE < buf ||
+ (ptr - PRKEY_SIZE != buf &&
+ *(ptr - PRKEY_SIZE - 1) != '\n')) {
+ condlog(0, "malformed prkey file line for wwid: '%s'", ptr);
+ return 1;
+ }
+ ptr = ptr - PRKEY_SIZE;
+ break;
+ }
+ ptr = strrchr(buf, '\n');
+ if (ptr == NULL) {
+ condlog(4, "couldn't file newline, assuming end of file");
+ break;
+ }
+ start = start + (ptr - buf) + 1;
+ }
+ if (cmd == PRKEY_READ) {
+ if (!ptr || *ptr == '#')
+ return 1;
+ memcpy(keystr, ptr, PRKEY_SIZE - 1);
+ keystr[PRKEY_SIZE - 1] = '\0';
+ return 0;
+ }
+ if (!ptr && !keystr)
+ return 0;
+ if (ptr) {
+ if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) {
+ condlog(0, "prkey write lseek failed : %s",
+ strerror(errno));
+ return 1;
+ }
+ }
+ if (!keystr) {
+ if (safe_write(fd, "#", 1) < 0) {
+ condlog(0, "failed to write to prkey file : %s",
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+ }
+ if (!ptr) {
+ if (lseek(fd, 0, SEEK_END) < 0) {
+ condlog(0, "prkey write lseek failed : %s",
+ strerror(errno));
+ return 1;
+ }
+ }
+ bytes = sprintf(buf, "%s %s\n", keystr, wwid);
+ if (safe_write(fd, buf, bytes) < 0) {
+ condlog(0, "failed to write to prkey file: %s",
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey)
+{
+ int fd;
+ int unused;
+ int ret = 1;
+ char keystr[PRKEY_SIZE];
+
+ if (!strlen(mpp->wwid))
+ goto out;
+
+ fd = open_file(conf->prkeys_file, &unused, PRKEYS_FILE_HEADER);
+ if (fd < 0)
+ goto out;
+ ret = do_prkey(fd, mpp->wwid, keystr, PRKEY_READ);
+ if (ret)
+ goto out_file;
+ ret = !!parse_prkey(keystr, prkey);
+out_file:
+ close(fd);
+out:
+ return ret;
+}
+
+int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey)
+{
+ int fd;
+ int can_write = 1;
+ int ret = 1;
+ char keystr[PRKEY_SIZE];
+
+ if (!strlen(mpp->wwid))
+ goto out;
+
+ fd = open_file(conf->prkeys_file, &can_write, PRKEYS_FILE_HEADER);
+ if (fd < 0)
+ goto out;
+ if (!can_write) {
+ condlog(0, "cannot set prkey, prkeys file is read-only");
+ goto out_file;
+ }
+ if (prkey) {
+ snprintf(keystr, PRKEY_SIZE, "0x%016" PRIx64, prkey);
+ keystr[PRKEY_SIZE - 1] = '\0';
+ ret = do_prkey(fd, mpp->wwid, keystr, PRKEY_WRITE);
+ }
+ else
+ ret = do_prkey(fd, mpp->wwid, NULL, PRKEY_WRITE);
+ if (ret == 0)
+ select_reservation_key(conf, mpp);
+ if (get_be64(mpp->reservation_key) != prkey)
+ ret = 1;
+out_file:
+ close(fd);
+out:
+ return ret;
+}
diff --git a/libmultipath/prkey.h b/libmultipath/prkey.h
new file mode 100644
index 0000000..4028e70
--- /dev/null
+++ b/libmultipath/prkey.h
@@ -0,0 +1,19 @@
+#ifndef _PRKEY_H
+#define _PRKEY_H
+
+#include "structs.h"
+#include <inttypes.h>
+
+#define PRKEYS_FILE_HEADER \
+"# Multipath persistent reservation keys, Version : 1.0\n" \
+"# NOTE: this file is automatically maintained by the multipathd program.\n" \
+"# You should not need to edit this file in normal circumstances.\n" \
+"#\n" \
+"# Format:\n" \
+"# prkey wwid\n" \
+"#\n"
+
+int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey);
+int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey);
+
+#endif /* _PRKEY_H */
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
index 175fbe1..00adc0d 100644
--- a/libmultipath/propsel.c
+++ b/libmultipath/propsel.c
@@ -20,6 +20,7 @@
#include "dict.h"
#include "util.h"
#include "prioritizers/alua_rtpg.h"
+#include "prkey.h"
#include <inttypes.h>
pgpolicyfn *pgpolicies[] = {
@@ -48,7 +49,7 @@ do { \
#define mp_set_mpe(var) \
do_set(var, mp->mpe, mp->var, "(setting: multipath.conf multipaths section)")
#define mp_set_hwe(var) \
-do_set(var, mp->hwe, mp->var, "(setting: array configuration)")
+do_set(var, mp->hwe, mp->var, "(setting: storage device configuration)")
#define mp_set_ovr(var) \
do_set(var, conf->overrides, mp->var, "(setting: multipath.conf overrides section)")
#define mp_set_conf(var) \
@@ -59,7 +60,7 @@ do_default(mp->var, value)
#define pp_set_mpe(var) \
do_set(var, mpe, pp->var, "(setting: multipath.conf multipaths section)")
#define pp_set_hwe(var) \
-do_set(var, pp->hwe, pp->var, "(setting: array configuration)")
+do_set(var, pp->hwe, pp->var, "(setting: storage device configuration)")
#define pp_set_conf(var) \
do_set(var, conf, pp->var, "(setting: multipath.conf defaults/devices section)")
#define pp_set_ovr(var) \
@@ -82,6 +83,16 @@ do_attr_set(var, mp->mpe, shift, "(setting: multipath.conf multipaths section)")
#define set_attr_conf(var, shift) \
do_attr_set(var, conf, shift, "(setting: multipath.conf defaults/devices section)")
+#define do_prkey_set(src, msg) \
+do { \
+ if (src && src->prkey_source != PRKEY_SOURCE_NONE) { \
+ mp->prkey_source = src->prkey_source; \
+ mp->reservation_key = src->reservation_key; \
+ origin = msg; \
+ goto out; \
+ } \
+} while (0)
+
int select_mode(struct config *conf, struct multipath *mp)
{
char *origin;
@@ -219,7 +230,7 @@ want_user_friendly_names(struct config *conf, struct multipath * mp)
do_set(user_friendly_names, conf->overrides, user_friendly_names,
"(setting: multipath.conf overrides section)");
do_set(user_friendly_names, mp->hwe, user_friendly_names,
- "(setting: array configuration)");
+ "(setting: storage device configuration)");
do_set(user_friendly_names, conf, user_friendly_names,
"(setting: multipath.conf defaults/devices section)");
do_default(user_friendly_names, DEFAULT_USER_FRIENDLY_NAMES);
@@ -363,11 +374,11 @@ int select_checker(struct config *conf, struct path *pp)
if (pp->detect_checker == DETECT_CHECKER_ON && pp->tpgs > 0) {
checker_name = TUR;
- origin = "(setting: array autodetected)";
+ origin = "(setting: storage device autodetected)";
goto out;
}
do_set(checker_name, conf->overrides, checker_name, "(setting: multipath.conf overrides section)");
- do_set(checker_name, pp->hwe, checker_name, "(setting: array configuration)");
+ do_set(checker_name, pp->hwe, checker_name, "(setting: storage device configuration)");
do_set(checker_name, conf, checker_name, "(setting: multipath.conf defaults/devices section)");
do_default(checker_name, DEFAULT_CHECKER);
out:
@@ -468,14 +479,14 @@ int select_prio(struct config *conf, struct path *pp)
if (pp->detect_prio == DETECT_PRIO_ON) {
detect_prio(conf, pp);
if (prio_selected(p)) {
- origin = "(setting: array autodetected)";
+ origin = "(setting: storage device autodetected)";
goto out;
}
}
mpe = find_mpe(conf->mptable, pp->wwid);
set_prio(conf->multipath_dir, mpe, "(setting: multipath.conf multipaths section)");
set_prio(conf->multipath_dir, conf->overrides, "(setting: multipath.conf overrides section)");
- set_prio(conf->multipath_dir, pp->hwe, "(setting: array configuration)");
+ set_prio(conf->multipath_dir, pp->hwe, "(setting: storage device configuration)");
set_prio(conf->multipath_dir, conf, "(setting: multipath.conf defaults/devices section)");
prio_get(conf->multipath_dir, p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
origin = "(setting: multipath internal)";
@@ -528,7 +539,7 @@ select_minio_rq (struct config *conf, struct multipath * mp)
do_set(minio_rq, mp->mpe, mp->minio, "(setting: multipath.conf multipaths section)");
do_set(minio_rq, conf->overrides, mp->minio, "(setting: multipath.conf overrides section)");
- do_set(minio_rq, mp->hwe, mp->minio, "(setting: array configuration)");
+ do_set(minio_rq, mp->hwe, mp->minio, "(setting: storage device configuration)");
do_set(minio_rq, conf, mp->minio, "(setting: multipath.conf defaults/devices section)");
do_default(mp->minio, DEFAULT_MINIO_RQ);
out:
@@ -609,15 +620,27 @@ out:
int select_reservation_key(struct config *conf, struct multipath *mp)
{
- char *origin, buff[12];
+ char *origin, buff[PRKEY_SIZE];
+ char *from_file = "";
+ uint64_t prkey = 0;
- mp_set_mpe(reservation_key);
- mp_set_conf(reservation_key);
- mp->reservation_key = NULL;
+ do_prkey_set(mp->mpe, "(setting: multipath.conf multipaths section)");
+ do_prkey_set(conf, "(setting: multipath.conf defaults/devices section)");
+ put_be64(mp->reservation_key, 0);
+ mp->prkey_source = PRKEY_SOURCE_NONE;
return 0;
out:
- print_reservation_key(buff, 12, &mp->reservation_key);
- condlog(3, "%s: reservation_key = %s %s", mp->alias, buff, origin);
+ if (mp->prkey_source == PRKEY_SOURCE_FILE) {
+ from_file = " (from prkeys file)";
+ if (get_prkey(conf, mp, &prkey) != 0)
+ put_be64(mp->reservation_key, 0);
+ else
+ put_be64(mp->reservation_key, prkey);
+ }
+ print_reservation_key(buff, PRKEY_SIZE, mp->reservation_key,
+ mp->prkey_source);
+ condlog(3, "%s: reservation_key = %s %s%s", mp->alias, buff, origin,
+ from_file);
return 0;
}
diff --git a/libmultipath/structs.c b/libmultipath/structs.c
index 2870467..828e790 100644
--- a/libmultipath/structs.c
+++ b/libmultipath/structs.c
@@ -515,9 +515,8 @@ void setup_feature(struct multipath *mpp, char *feature)
int add_feature(char **f, const char *n)
{
- int c = 0, d, l = 0;
- char *e, *p, *t;
- const char *q;
+ int c = 0, d, l;
+ char *e, *t;
if (!f)
return 1;
@@ -526,6 +525,11 @@ int add_feature(char **f, const char *n)
if (!n || *n == '0')
return 0;
+ if (strchr(n, ' ') != NULL) {
+ condlog(0, "internal error: feature \"%s\" contains spaces", n);
+ return 1;
+ }
+
/* default feature is null */
if(!*f)
{
@@ -538,63 +542,34 @@ int add_feature(char **f, const char *n)
}
/* Check if feature is already present */
- if (*f) {
- if (strstr(*f, n))
- return 0;
-
- /* Get feature count */
- c = strtoul(*f, &e, 10);
- if (*f == e)
- /* parse error */
- return 1;
+ if (strstr(*f, n))
+ return 0;
- /* Check if we need to increase feature count space */
- l = strlen(*f) + strlen(n) + 1;
+ /* Get feature count */
+ c = strtoul(*f, &e, 10);
+ if (*f == e || (*e != ' ' && *e != '\0')) {
+ condlog(0, "parse error in feature string \"%s\"", *f);
+ return 1;
}
- /* Count new features */
- if ((c % 10) == 9)
- l++;
+
+ /* Add 1 digit and 1 space */
+ l = strlen(e) + strlen(n) + 2;
+
c++;
- q = n;
- while (*q != '\0') {
- if (*q == ' ' && q[1] != '\0' && q[1] != ' ') {
- if ((c % 10) == 9)
- l++;
- c++;
- }
- q++;
- }
+ /* Check if we need more digits for feature count */
+ for (d = c; d >= 10; d /= 10)
+ l++;
t = MALLOC(l + 1);
if (!t)
return 1;
- memset(t, 0, l + 1);
-
- /* Update feature count */
- d = c;
- l = 1;
- while (d > 9) {
- d /= 10;
- l++;
- }
- p = t;
- snprintf(p, l + 2, "%0d ", c);
-
- /* Copy the feature string */
- p = NULL;
- if (*f)
- p = strchr(*f, ' ');
+ /* e: old feature string with leading space, or "" */
+ if (*e == ' ')
+ while (*(e + 1) == ' ')
+ e++;
- if (p) {
- while (*p == ' ')
- p++;
- strcat(t, p);
- strcat(t, " ");
- } else {
- p = t + strlen(t);
- }
- strcat(t, n);
+ snprintf(t, l + 1, "%0d%s %s", c, e, n);
FREE(*f);
*f = t;
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 8ea984d..f06824a 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -2,8 +2,10 @@
#define _STRUCTS_H
#include <sys/types.h>
+#include <inttypes.h>
#include "prio.h"
+#include "byteorder.h"
#define WWID_SIZE 128
#define SERIAL_SIZE 65
@@ -17,6 +19,7 @@
#define NAME_SIZE 512
#define HOST_NAME_LEN 16
#define SLOT_NAME_SIZE 40
+#define PRKEY_SIZE 19
#define SCSI_VENDOR_SIZE 9
#define SCSI_PRODUCT_SIZE 17
@@ -171,6 +174,12 @@ enum initialized_states {
INIT_OK,
};
+enum prkey_sources {
+ PRKEY_SOURCE_NONE,
+ PRKEY_SOURCE_CONF,
+ PRKEY_SOURCE_FILE,
+};
+
struct sg_id {
int host_no;
int channel;
@@ -306,7 +315,8 @@ struct multipath {
void * mpcontext;
/* persistent management data*/
- unsigned char * reservation_key;
+ int prkey_source;
+ struct be64 reservation_key;
unsigned char prflag;
};
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
index eb44da5..0cbcc59 100644
--- a/libmultipath/uevent.c
+++ b/libmultipath/uevent.c
@@ -361,6 +361,14 @@ static void uevent_cleanup(void *arg)
udev_unref(udev);
}
+static void monitor_cleanup(void *arg)
+{
+ struct udev_monitor *monitor = arg;
+
+ condlog(3, "Releasing uevent_monitor() resources");
+ udev_monitor_unref(monitor);
+}
+
/*
* Service the uevent queue.
*/
@@ -749,6 +757,7 @@ int uevent_listen(struct udev *udev)
condlog(2, "failed to create udev monitor");
goto out;
}
+ pthread_cleanup_push(monitor_cleanup, monitor);
#ifdef LIBUDEV_API_RECVBUF
if (udev_monitor_set_receive_buffer_size(monitor, 128 * 1024 * 1024))
condlog(2, "failed to increase buffer size");
@@ -834,7 +843,7 @@ int uevent_listen(struct udev *udev)
need_failback = 0;
out:
if (monitor)
- udev_monitor_unref(monitor);
+ pthread_cleanup_pop(1);
if (need_failback)
err = failback_listen();
pthread_cleanup_pop(1);
@@ -885,7 +894,7 @@ int uevent_get_disk_ro(struct uevent *uev)
int i, ro = -1;
for (i = 0; uev->envp[i] != NULL; i++) {
- if (!strncmp(uev->envp[i], "DISK_RO", 6) && strlen(uev->envp[i]) > 7) {
+ if (!strncmp(uev->envp[i], "DISK_RO", 7) && strlen(uev->envp[i]) > 8) {
p = uev->envp[i] + 8;
ro = strtoul(p, &q, 10);
if (p == q) {
@@ -904,8 +913,8 @@ char *uevent_get_dm_name(struct uevent *uev)
int i;
for (i = 0; uev->envp[i] != NULL; i++) {
- if (!strncmp(uev->envp[i], "DM_NAME", 6) &&
- strlen(uev->envp[i]) > 7) {
+ if (!strncmp(uev->envp[i], "DM_NAME", 7) &&
+ strlen(uev->envp[i]) > 8) {
p = MALLOC(strlen(uev->envp[i] + 8) + 1);
strcpy(p, uev->envp[i] + 8);
break;
diff --git a/libmultipath/util.c b/libmultipath/util.c
index dff2ed3..0800da5 100644
--- a/libmultipath/util.c
+++ b/libmultipath/util.c
@@ -11,6 +11,7 @@
#include <unistd.h>
#include <errno.h>
+#include "util.h"
#include "debug.h"
#include "memory.h"
#include "checkers.h"
@@ -416,3 +417,35 @@ int get_linux_version_code(void)
pthread_once(&_lvc_initialized, _set_linux_version_code);
return _linux_version_code;
}
+
+int parse_prkey(char *ptr, uint64_t *prkey)
+{
+ if (!ptr)
+ return 1;
+ if (*ptr == '0')
+ ptr++;
+ if (*ptr == 'x' || *ptr == 'X')
+ ptr++;
+ if (*ptr == '\0' || strlen(ptr) > 16)
+ return 1;
+ if (strlen(ptr) != strspn(ptr, "0123456789aAbBcCdDeEfF"))
+ return 1;
+ if (sscanf(ptr, "%" SCNx64 "", prkey) != 1)
+ return 1;
+ return 0;
+}
+
+int safe_write(int fd, const void *buf, size_t count)
+{
+ while (count > 0) {
+ ssize_t r = write(fd, buf, count);
+ if (r < 0) {
+ if (errno == EINTR)
+ continue;
+ return -errno;
+ }
+ count -= r;
+ buf = (char *)buf + r;
+ }
+ return 0;
+}
diff --git a/libmultipath/util.h b/libmultipath/util.h
index 45291be..3dc048e 100644
--- a/libmultipath/util.h
+++ b/libmultipath/util.h
@@ -2,6 +2,7 @@
#define _UTIL_H
#include <sys/types.h>
+#include <inttypes.h>
size_t strchop(char *);
int basenamecpy (const char * src, char * dst, int);
@@ -16,6 +17,9 @@ char *parse_uid_attribute_by_attrs(char *uid_attrs, char *path_dev);
void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached);
int systemd_service_enabled(const char *dev);
int get_linux_version_code(void);
+int parse_prkey(char *ptr, uint64_t *prkey);
+int safe_write(int fd, const void *buf, size_t count);
+
#define KERNEL_VERSION(maj, min, ptc) ((((maj) * 256) + (min)) * 256 + (ptc))
#define safe_sprintf(var, format, args...) \
diff --git a/libmultipath/version.h b/libmultipath/version.h
index 777d71a..0a0da9e 100644
--- a/libmultipath/version.h
+++ b/libmultipath/version.h
@@ -20,8 +20,8 @@
#ifndef _VERSION_H
#define _VERSION_H
-#define VERSION_CODE 0x000702
-#define DATE_CODE 0x080511
+#define VERSION_CODE 0x000703
+#define DATE_CODE 0x090514
#define PROG "multipath-tools"
diff --git a/mpathpersist/Makefile b/mpathpersist/Makefile
index bd1c0df..6e5acd3 100644
--- a/mpathpersist/Makefile
+++ b/mpathpersist/Makefile
@@ -3,8 +3,8 @@ include ../Makefile.inc
CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir)
LDFLAGS += $(BIN_LDFLAGS)
-LIBDEPS += -lpthread -ldevmapper -L$(mpathpersistdir) -lmpathpersist \
- -L$(multipathdir) -L$(mpathcmddir) -lmpathcmd -lmultipath -ludev
+LIBDEPS += -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath \
+ -L$(mpathcmddir) -lmpathcmd -lpthread -ldevmapper -ludev
EXEC = mpathpersist
diff --git a/multipath/11-dm-mpath.rules b/multipath/11-dm-mpath.rules
index b131a10..91d8058 100644
--- a/multipath/11-dm-mpath.rules
+++ b/multipath/11-dm-mpath.rules
@@ -18,8 +18,19 @@ ENV{DM_SUBSYSTEM_UDEV_FLAG2}=="1", ENV{MPATH_DEVICE_READY}="0",\
GOTO="mpath_action"
# If the last path has failed mark the device not ready
-ENV{DM_ACTION}=="PATH_FAILED", ENV{DM_NR_VALID_PATHS}=="0",\
- ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action"
+# Note that DM_NR_VALID_PATHS is only set for PATH_FAILED|PATH_REINSTATED
+# events.
+# This may not be reliable, as events aren't necessarily received in order.
+ENV{DM_NR_VALID_PATHS}=="0", ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action"
+
+ENV{MPATH_SBIN_PATH}="/sbin"
+TEST!="$env{MPATH_SBIN_PATH}/multipath", ENV{MPATH_SBIN_PATH}="/usr/sbin"
+
+# Check the map state directly with multipath -U.
+# This doesn't attempt I/O on the device.
+PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -U %k", GOTO="paths_ok"
+ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action"
+LABEL="paths_ok"
# Don't mark a device ready on a PATH_FAILED event. even if
# DM_NR_VALID_PATHS is greater than 0. Just keep the existing
@@ -28,7 +39,7 @@ ENV{DM_ACTION}=="PATH_FAILED", GOTO="mpath_action"
# This event is either a PATH_REINSTATED or a table reload where
# there are active paths. Mark the device ready
-ENV{MPATH_DEVICE_READY}=""
+ENV{MPATH_DEVICE_READY}="1"
LABEL="mpath_action"
# DM_SUBSYSTEM_UDEV_FLAG0 is the "RELOAD" flag for multipath subsystem.
@@ -37,7 +48,13 @@ LABEL="mpath_action"
# something that should be reacted upon since it would be useless extra work.
# It's exactly mpath's job to provide *seamless* device access to any of the
# paths that are available underneath.
-ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", ENV{DM_ACTIVATION}="0"
+ENV{DM_SUBSYSTEM_UDEV_FLAG0}=="1", \
+ ENV{DM_ACTIVATION}="0", ENV{MPATH_UNCHANGED}="1"
+
+# For path failed or reinstated events, unset DM_ACTIVATION.
+# This is similar to the DM_SUBSYSTEM_UDEV_FLAG0 case above.
+ENV{DM_ACTION}=="PATH_FAILED|PATH_REINSTATED", \
+ ENV{DM_ACTIVATION}="0", ENV{MPATH_UNCHANGED}="1"
# Do not initiate scanning if no path is available,
# otherwise there would be a hang or IO error on access.
@@ -47,21 +64,43 @@ ENV{MPATH_DEVICE_READY}=="0", ENV{DM_NOSCAN}="1"
# Also skip all foreign rules if no path is available.
# Remember the original value of DM_DISABLE_OTHER_RULES_FLAG
# and restore it back once we have at least one path available.
-ENV{MPATH_DEVICE_READY}=="0", ENV{.MPATH_DEVICE_READY_OLD}!="0",\
+ENV{MPATH_DEVICE_READY}=="0", ENV{.MPATH_DEVICE_READY_OLD}=="1",\
ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}=="",\
- ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}",\
- ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
+ ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}"
+ENV{MPATH_DEVICE_READY}=="0", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
ENV{MPATH_DEVICE_READY}!="0", ENV{.MPATH_DEVICE_READY_OLD}=="0",\
ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}",\
ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="",\
ENV{DM_ACTIVATION}="1"
+# The code to check multipath state ends here. We need to set
+# properties and symlinks regardless whether the map is usable or
+# not. If symlinks get lost, systemd may auto-unmount file systems.
+
LABEL="scan_import"
-ENV{DM_NOSCAN}!="1", GOTO="mpath_end"
-ENV{ID_FS_TYPE}!="?*", IMPORT{db}="ID_FS_TYPE"
-ENV{ID_FS_USAGE}!="?*", IMPORT{db}="ID_FS_USAGE"
-ENV{ID_FS_UUID}!="?*", IMPORT{db}="ID_FS_UUID"
-ENV{ID_FS_ENC}!="?*", IMPORT{db}="ID_FS_UUID_ENC"
-ENV{ID_FS_VERSION}!="?*", IMPORT{db}="ID_FS_VERSION"
+ENV{DM_NOSCAN}!="1", GOTO="import_end"
+IMPORT{db}="ID_FS_TYPE"
+IMPORT{db}="ID_FS_USAGE"
+IMPORT{db}="ID_FS_UUID"
+IMPORT{db}="ID_FS_UUID_ENC"
+IMPORT{db}="ID_FS_LABEL"
+IMPORT{db}="ID_FS_LABEL_ENC"
+IMPORT{db}="ID_FS_VERSION"
+
+LABEL="import_end"
+
+# Multipath maps should take precedence over their members.
+ENV{DM_UDEV_LOW_PRIORITY_FLAG}!="1", OPTIONS+="link_priority=50"
+
+# Set some additional symlinks that typically exist for mpath
+# path members, too, and should be overridden.
+
+# kpartx_id is very robust, it works for suspended maps and maps
+# with 0 dependencies. It sets DM_TYPE, DM_PART, DM_WWN
+TEST=="/usr/lib/udev/kpartx_id", \
+ IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}"
+
+ENV{DM_TYPE}=="?*", SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_NAME}"
+ENV{DM_WWN}=="?*", SYMLINK+="disk/by-id/wwn-$env{DM_WWN}"
LABEL="mpath_end"
diff --git a/multipath/Makefile b/multipath/Makefile
index c85314e..468c056 100644
--- a/multipath/Makefile
+++ b/multipath/Makefile
@@ -5,8 +5,8 @@ include ../Makefile.inc
CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir)
LDFLAGS += $(BIN_LDFLAGS)
-LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath -ludev \
- -L$(mpathcmddir) -lmpathcmd
+LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathcmddir) -lmpathcmd \
+ -lpthread -ldevmapper -ldl -ludev
EXEC = multipath
diff --git a/multipath/main.c b/multipath/main.c
index dede017..bffe065 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -117,9 +117,10 @@ usage (char * progname)
" -F flush all multipath device maps\n"
" -a add a device wwid to the wwids file\n"
" -c check if a device should be a path in a multipath device\n"
+ " -C check if a multipath device has usable paths\n"
" -q allow queue_if_no_path when multipathd is not running\n"
" -d dry run, do not create or update devmaps\n"
- " -t dump internal hardware table\n"
+ " -t display the currently used multipathd configuration\n"
" -r force devmap reload\n"
" -i ignore wwids file\n"
" -B treat the bindings file as read only\n"
@@ -258,6 +259,81 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
return 0;
}
+static int check_usable_paths(struct config *conf,
+ const char *devpath, enum devtypes dev_type)
+{
+ struct udev_device *ud = NULL;
+ struct multipath *mpp = NULL;
+ struct pathgroup *pg;
+ struct path *pp;
+ char *mapname;
+ vector pathvec = NULL;
+ char params[PARAMS_SIZE], status[PARAMS_SIZE];
+ dev_t devt;
+ int r = 1, i, j;
+
+ ud = get_udev_device(devpath, dev_type);
+ if (ud == NULL)
+ return r;
+
+ devt = udev_device_get_devnum(ud);
+ if (!dm_is_dm_major(major(devt))) {
+ condlog(1, "%s is not a dm device", devpath);
+ goto out;
+ }
+
+ mapname = dm_mapname(major(devt), minor(devt));
+ if (mapname == NULL) {
+ condlog(1, "dm device not found: %s", devpath);
+ goto out;
+ }
+
+ if (!dm_is_mpath(mapname)) {
+ condlog(1, "%s is not a multipath map", devpath);
+ goto free;
+ }
+
+ /* pathvec is needed for disassemble_map */
+ pathvec = vector_alloc();
+ if (pathvec == NULL)
+ goto free;
+
+ mpp = dm_get_multipath(mapname);
+ if (mpp == NULL)
+ goto free;
+
+ dm_get_map(mpp->alias, &mpp->size, params);
+ dm_get_status(mpp->alias, status);
+ disassemble_map(pathvec, params, mpp, 0);
+ disassemble_status(status, mpp);
+
+ vector_foreach_slot (mpp->pg, pg, i) {
+ vector_foreach_slot (pg->paths, pp, j) {
+ pp->udev = get_udev_device(pp->dev_t, DEV_DEVT);
+ if (pp->udev == NULL)
+ continue;
+ if (pathinfo(pp, conf, DI_SYSFS|DI_NOIO) != PATHINFO_OK)
+ continue;
+
+ if (pp->state == PATH_UP &&
+ pp->dmstate == PSTATE_ACTIVE) {
+ condlog(3, "%s: path %s is usable",
+ devpath, pp->dev);
+ r = 0;
+ goto found;
+ }
+ }
+ }
+found:
+ condlog(2, "%s:%s usable paths found", devpath, r == 0 ? "" : " no");
+free:
+ FREE(mapname);
+ free_multipath(mpp, FREE_PATHS);
+ vector_free(pathvec);
+out:
+ udev_device_unref(ud);
+ return r;
+}
/*
* Return value:
@@ -522,7 +598,7 @@ main (int argc, char *argv[])
exit(1);
multipath_conf = conf;
conf->retrigger_tries = 0;
- while ((arg = getopt(argc, argv, ":adchl::FfM:v:p:b:BrR:itquwW")) != EOF ) {
+ while ((arg = getopt(argc, argv, ":adcChl::FfM:v:p:b:BrR:itquUwW")) != EOF ) {
switch(arg) {
case 1: printf("optarg : %s\n",optarg);
break;
@@ -547,6 +623,9 @@ main (int argc, char *argv[])
case 'c':
cmd = CMD_VALID_PATH;
break;
+ case 'C':
+ cmd = CMD_USABLE_PATHS;
+ break;
case 'd':
if (cmd == CMD_CREATE)
cmd = CMD_DRY_RUN;
@@ -593,6 +672,10 @@ main (int argc, char *argv[])
cmd = CMD_VALID_PATH;
dev_type = DEV_UEVENT;
break;
+ case 'U':
+ cmd = CMD_USABLE_PATHS;
+ dev_type = DEV_UEVENT;
+ break;
case 'w':
cmd = CMD_REMOVE_WWID;
break;
@@ -674,7 +757,10 @@ main (int argc, char *argv[])
condlog(0, "failed to initialize prioritizers");
goto out;
}
-
+ if (cmd == CMD_USABLE_PATHS) {
+ r = check_usable_paths(conf, dev, dev_type);
+ goto out;
+ }
if (cmd == CMD_VALID_PATH &&
(!dev || dev_type == DEV_DEVMAP)) {
condlog(0, "the -c option requires a path to check");
diff --git a/multipath/multipath.8 b/multipath/multipath.8
index b9436e5..56f8703 100644
--- a/multipath/multipath.8
+++ b/multipath/multipath.8
@@ -25,7 +25,7 @@ multipath \- Device mapper target autoconfig.
.RB [\| \-b\ \c
.IR bindings_file \|]
.RB [\| \-d \|]
-.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-q | \|-r | \|-i | \-a | \|-u | \-w | \-W \|]
+.RB [\| \-h | \-l | \-ll | \-f | \-t | \-F | \-B | \-c | \-C | \-q | \-r | \-i | \-a | \-u | \-U | \-w | \-W \|]
.RB [\| \-p\ \c
.IR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|]
.RB [\| \-R\ \c
@@ -86,7 +86,7 @@ Flush all unused multipath device maps.
.
.TP
.B \-t
-Print internal hardware table to stdout.
+Display the currently used multipathd configuration.
.
.TP
.B \-r
@@ -110,6 +110,12 @@ Set user_friendly_names bindings file location. The default is
Check if a block device should be a path in a multipath device.
.
.TP
+.B \-C
+Check if a multipath device has usable paths. This can be used to
+test whether or not I/O on this device is likely to succeed. The command
+itself doesn't attempt to do I/O on the device.
+.
+.TP
.B \-q
Allow device tables with \fIqueue_if_no_path\fR when multipathd is not running.
.
@@ -123,6 +129,11 @@ Check if the device specified in the program environment should be
a path in a multipath device.
.
.TP
+.B \-U
+Check if the device specified in the program environment is a multipath device
+with usable paths. See \fB-C\fB.
+.
+.TP
.B \-w
Remove the WWID for the specified device from the WWIDs file.
.
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
index d9ac279..5b6dde7 100644
--- a/multipath/multipath.conf.5
+++ b/multipath/multipath.conf.5
@@ -2,12 +2,13 @@
.\" Update the date below if you make any significant change.
.\" Make sure there are no errors with:
.\" groff -z -wall -b -e -t multipath/multipath.conf.5
+.\" man --warnings -E UTF-8 -l -Tutf8 -Z multipath/multipath.conf.5 >/dev/null
.\"
.\" TODO: Look for XXX and ???
.\"
.\" ----------------------------------------------------------------------------
.
-.TH MULTIPATH.CONF 5 2017-05-11 "Linux"
+.TH MULTIPATH.CONF 5 2017-08-18 Linux
.
.
.\" ----------------------------------------------------------------------------
@@ -26,6 +27,9 @@ is the configuration file for the multipath daemon. It is used to
overwrite the built-in configuration table of \fBmultipathd\fP.
Any line whose first non-white-space character is a '#' is considered
a comment line. Empty lines are ignored.
+.PP
+Currently used multipathd configuration can be displayed with the \fBmultipath -t\fR
+or \fBmultipathd show config\fR command.
.
.
.\" ----------------------------------------------------------------------------
@@ -215,11 +219,9 @@ unique identifier in config file, and it would activate merging uevents
according to the identifier to promote effiecncy in processing uevents.
It has no default value, if you want to identify path by udev attribute
and to activate merging uevents for SCSI and DASD devices, you can set
-its value as:
+its value as: \fIuid_attrs "sd:ID_SERIAL dasd:ID_UID"\fR.
.RS
.TP
-\fBuid_attrs "sd:ID_SERIAL dasd:ID_UID"\fR
-.TP
The default is: \fB<unset>\fR
.RE
.
@@ -241,9 +243,13 @@ The default is: for NVME devices \fBID_WWN\fR
.B getuid_callout
(Superseded by \fIuid_attribute\fR) The default program and args to callout
to obtain a unique path identifier. Should be specified with an absolute path.
+.RS
.TP
+The default is: \fB<unset>\fR
+.RE
.
.
+.TP
.B prio
The name of the path priority routine. The specified routine
should return a numeric value specifying the relative priority
@@ -301,7 +307,7 @@ Requires prio_args keyword.
.TP
.I datacore
(Hardware-dependent)
-Generate the path priority for some Datacore storage arrays. Requires prio_args
+Generate the path priority for some DataCore storage arrays. Requires prio_args
keyword.
.TP
.I iet
@@ -328,25 +334,24 @@ Needs a value of the form
.RS
.TP 8
.I hbtl
-regex can be of SCSI H:B:T:L format. For example: 1:0:.:. , *:0:0:.
+Regex can be of SCSI H:B:T:L format. For example: 1:0:.:. , *:0:0:.
.TP
.I devname
-regex can be of device name format. For example: sda , sd.e
+Regex can be of device name format. For example: sda , sd.e
.TP
.I serial
-regex can be of serial number format. For example: .*J1FR.*324 . The serial can
+Regex can be of serial number format. For example: .*J1FR.*324 . The serial can
be looked up through sysfs or by running multipathd show paths format "%z". For
example: 0395J1FR904324
.TP
.I wwn
-regex can be of the form \fI"host_wwnn:host_wwpn:target_wwnn:target_wwpn"\fR
+Regex can be of the form \fI"host_wwnn:host_wwpn:target_wwnn:target_wwpn"\fR
these values can be looked up through sysfs or by running \fImultipathd show paths format
"%N:%R:%n:%r"\fR. For example: 0x200100e08ba0aea0:0x210100e08ba0aea0:.*:.* , .*:.*:iqn.2009-10.com.redhat.msp.lab.ask-06:.*
.RE
.TP 12
.I path_latency
-Needs a value of the form
-\fI"<io_num>|<base_num>"\fR
+Needs a value of the form \fI"<io_num>|<base_num>"\fR
.RS
.TP 8
.I io_num
@@ -369,12 +374,21 @@ If \fIexclusive_pref_bit\fR is set, paths with the \fIpreferred path\fR bit
set will always be in their own path group.
.TP
.I datacore
-\fIpreferredsds\fR (required) denotes the preferred "SDS name" for datacore
-arrays. \fItimeout\fR (optional) is the timeout for the INQUIRY, in ms.
+.RS
+.TP 8
+.I preferredsds
+(Mandatory) The preferred "SDS name".
.TP
+.I timeout
+(Optional) The timeout for the INQUIRY, in ms.
+.RE
+.TP 12
.I iet
-\fIpreferredip=...\fR (required) denotes the preferred IP address (in dotted decimal
-notation) for iSCSI targets.
+.RS
+.TP 8
+.I preferredip=...
+(Mandatory) Th preferred IP address, in dotted decimal notation, for iSCSI targets.
+.RE
.TP
The default is: \fB<unset>\fR
.RE
@@ -387,30 +401,27 @@ where \fInum\fR is the number, between 0 and 8, of features in \fIlist\fR.
Possible values for the feature list are:
.RS
.TP 12
-.\" XXX
.I queue_if_no_path
(Deprecated, superseded by \fIno_path_retry\fR) Queue I/O if no path is active.
Identical to the \fIno_path_retry\fR with \fIqueue\fR value. If both this
feature and \fIno_path_retry\fR are set, the latter value takes
precedence. See KNOWN ISSUES.
.TP
-.\" XXX
.I pg_init_retries <times>
(Since kernel 2.6.24) Number of times to retry pg_init, it must be between 1 and 50.
.TP
-.\" XXX
.I pg_init_delay_msecs <msecs>
(Since kernel 2.6.38) Number of msecs before pg_init retry, it must be between 0 and 60000.
.TP
-.\" XXX
.I queue_mode <mode>
(Since kernel 4.8) Select the the queueing mode per multipath device.
<mode> can be \fIbio\fR, \fIrq\fR or \fImq\fR, which corresponds to
bio-based, request-based, and block-multiqueue (blk-mq) request-based,
respectively.
-
The default depends on the kernel parameter \fBdm_mod.use_blk_mq\fR. It is
\fImq\fR if the latter is set, and \fIrq\fR otherwise.
+.TP
+The default is: \fB<unset>\fR
.RE
.
.
@@ -451,6 +462,9 @@ Please use \fItur\fR instead.
(Hardware-dependent)
Check the path state for HP/COMPAQ Smart Array(CCISS) controllers.
.TP
+.I none
+Do not check the device, fallback to use the values retrieved from sysfs
+.TP
.I rbd
Check if the path is in the Ceph blacklist and remap the path if it is.
.TP
@@ -550,15 +564,13 @@ Specify what to do when all paths are down. Possible values are:
.RS
.TP 12
.I value > 0
-number of retries until disable I/O queueing.
+Number of retries until disable I/O queueing.
.TP
.I fail
-for immediate failure (no I/O queueing).
+For immediate failure (no I/O queueing).
.TP
.I queue
-for never stop I/O queueing. Similar to \fIqueue_if_no_path\fR.
-.TP
-See KNOWN ISSUES.
+For never stop I/O queueing, similar to \fIqueue_if_no_path\fR. See KNOWN ISSUES.
.TP
The default is: \fBfail\fR
.RE
@@ -706,7 +718,9 @@ mutipath.conf. If the SCSI layer has not attached a hardware handler,
multipath will continue to use its configured hardware handler.
.RS
.PP
-The default is: \fByes\fR. Linux kernel 4.3 or newer always behaves as if
+The default is: \fByes\fR
+.PP
+\fBImportant Note:\fR Linux kernel 4.3 or newer always behaves as if
\fB"retain_attached_hw_handler yes"\fR was set.
.RE
.
diff --git a/multipath/multipath.rules b/multipath/multipath.rules
index 86defc0..bc1a852 100644
--- a/multipath/multipath.rules
+++ b/multipath/multipath.rules
@@ -21,7 +21,7 @@ TEST!="$env{MPATH_SBIN_PATH}/multipath", ENV{MPATH_SBIN_PATH}="/usr/sbin"
ENV{DM_MULTIPATH_DEVICE_PATH}!="1", \
PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -u %k", \
- ENV{DM_MULTIPATH_DEVICE_PATH}="1", ENV{ID_FS_TYPE}="none", \
+ ENV{DM_MULTIPATH_DEVICE_PATH}="1", ENV{ID_FS_TYPE}="mpath_member", \
ENV{SYSTEMD_READY}="0"
LABEL="end_mpath"
diff --git a/multipathd/Makefile b/multipathd/Makefile
index d5782a1..e6f140b 100644
--- a/multipathd/Makefile
+++ b/multipathd/Makefile
@@ -9,8 +9,8 @@ include ../Makefile.inc
CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) \
-I$(mpathcmddir) -I$(thirdpartydir)
LDFLAGS += $(BIN_LDFLAGS)
-LIBDEPS += -ludev -ldl -L$(multipathdir) -lmultipath -L$(mpathpersistdir) \
- -lmpathpersist -L$(mpathcmddir) -lmpathcmd -lurcu -lpthread \
+LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \
+ -L$(mpathcmddir) -lmpathcmd -ludev -ldl -lurcu -lpthread \
-ldevmapper -lreadline
ifdef SYSTEMD
diff --git a/multipathd/cli.c b/multipathd/cli.c
index 32d4976..deb72cb 100644
--- a/multipathd/cli.c
+++ b/multipathd/cli.c
@@ -208,6 +208,11 @@ load_keys (void)
r += add_key(keys, "unsetprstatus", UNSETPRSTATUS, 0);
r += add_key(keys, "format", FMT, 1);
r += add_key(keys, "json", JSON, 0);
+ r += add_key(keys, "getprkey", GETPRKEY, 0);
+ r += add_key(keys, "setprkey", SETPRKEY, 0);
+ r += add_key(keys, "unsetprkey", UNSETPRKEY, 0);
+ r += add_key(keys, "key", KEY, 1);
+
if (r) {
free_keys(keys);
@@ -571,6 +576,9 @@ cli_init (void) {
add_handler(GETPRSTATUS+MAP, NULL);
add_handler(SETPRSTATUS+MAP, NULL);
add_handler(UNSETPRSTATUS+MAP, NULL);
+ add_handler(GETPRKEY+MAP, NULL);
+ add_handler(SETPRKEY+MAP+KEY, NULL);
+ add_handler(UNSETPRKEY+MAP, NULL);
add_handler(FORCEQ+DAEMON, NULL);
add_handler(RESTOREQ+DAEMON, NULL);
diff --git a/multipathd/cli.h b/multipathd/cli.h
index 92cb41b..d289167 100644
--- a/multipathd/cli.h
+++ b/multipathd/cli.h
@@ -37,6 +37,10 @@ enum {
__UNSETPRSTATUS,
__FMT,
__JSON,
+ __GETPRKEY,
+ __SETPRKEY,
+ __UNSETPRKEY,
+ __KEY,
};
#define LIST (1 << __LIST)
@@ -76,6 +80,10 @@ enum {
#define UNSETPRSTATUS (1ULL << __UNSETPRSTATUS)
#define FMT (1ULL << __FMT)
#define JSON (1ULL << __JSON)
+#define GETPRKEY (1ULL << __GETPRKEY)
+#define SETPRKEY (1ULL << __SETPRKEY)
+#define UNSETPRKEY (1ULL << __UNSETPRKEY)
+#define KEY (1ULL << __KEY)
#define INITIAL_REPLY_LEN 1200
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index b4a95e3..e232fb2 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -21,6 +21,7 @@
#include <errno.h>
#include <libudev.h>
#include "util.h"
+#include "prkey.h"
#include "main.h"
#include "cli.h"
@@ -782,7 +783,7 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
vecs->pathvec, &refwwid);
if (refwwid) {
if (coalesce_paths(vecs, NULL, refwwid,
- FORCE_RELOAD_NONE, 1))
+ FORCE_RELOAD_NONE, CMD_NONE))
condlog(2, "%s: coalesce_paths failed",
param);
dm_lib_release();
@@ -1390,3 +1391,84 @@ cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
return 0;
}
+
+int
+cli_getprkey(void * v, char ** reply, int * len, void * data)
+{
+ struct multipath * mpp;
+ struct vectors * vecs = (struct vectors *)data;
+ char *mapname = get_keyparam(v, MAP);
+
+ mapname = convert_dev(mapname, 0);
+ condlog(3, "%s: get persistent reservation key (operator)", mapname);
+ mpp = find_mp_by_str(vecs->mpvec, mapname);
+
+ if (!mpp)
+ return 1;
+
+ *reply = malloc(20);
+
+ if (!get_be64(mpp->reservation_key)) {
+ sprintf(*reply, "none\n");
+ *len = strlen(*reply) + 1;
+ return 0;
+ }
+ snprintf(*reply, 20, "0x%" PRIx64 "\n",
+ get_be64(mpp->reservation_key));
+ (*reply)[19] = '\0';
+ *len = strlen(*reply) + 1;
+ return 0;
+}
+
+int
+cli_unsetprkey(void * v, char ** reply, int * len, void * data)
+{
+ struct multipath * mpp;
+ struct vectors * vecs = (struct vectors *)data;
+ char *mapname = get_keyparam(v, MAP);
+ int ret;
+ struct config *conf;
+
+ mapname = convert_dev(mapname, 0);
+ condlog(3, "%s: unset persistent reservation key (operator)", mapname);
+ mpp = find_mp_by_str(vecs->mpvec, mapname);
+
+ if (!mpp)
+ return 1;
+
+ conf = get_multipath_config();
+ ret = set_prkey(conf, mpp, 0);
+ put_multipath_config(conf);
+
+ return ret;
+}
+
+int
+cli_setprkey(void * v, char ** reply, int * len, void * data)
+{
+ struct multipath * mpp;
+ struct vectors * vecs = (struct vectors *)data;
+ char *mapname = get_keyparam(v, MAP);
+ char *keyparam = get_keyparam(v, KEY);
+ uint64_t prkey;
+ int ret;
+ struct config *conf;
+
+ mapname = convert_dev(mapname, 0);
+ condlog(3, "%s: set persistent reservation key (operator)", mapname);
+ mpp = find_mp_by_str(vecs->mpvec, mapname);
+
+ if (!mpp)
+ return 1;
+
+ if (parse_prkey(keyparam, &prkey) != 0) {
+ condlog(0, "%s: invalid prkey : '%s'", mapname, keyparam);
+ return 1;
+ }
+
+ conf = get_multipath_config();
+ ret = set_prkey(conf, mpp, prkey);
+ put_multipath_config(conf);
+
+ return ret;
+}
diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h
index f4d02cc..78a3a43 100644
--- a/multipathd/cli_handlers.h
+++ b/multipathd/cli_handlers.h
@@ -45,3 +45,6 @@ int cli_reassign (void * v, char ** reply, int * len, void * data);
int cli_getprstatus(void * v, char ** reply, int * len, void * data);
int cli_setprstatus(void * v, char ** reply, int * len, void * data);
int cli_unsetprstatus(void * v, char ** reply, int * len, void * data);
+int cli_getprkey(void * v, char ** reply, int * len, void * data);
+int cli_setprkey(void * v, char ** reply, int * len, void * data);
+int cli_unsetprkey(void * v, char ** reply, int * len, void * data);
diff --git a/multipathd/main.c b/multipathd/main.c
index 4be2c57..8049da2 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -161,15 +161,27 @@ sd_notify_status(void)
case DAEMON_CONFIGURE:
return "STATUS=configure";
case DAEMON_IDLE:
- return "STATUS=idle";
case DAEMON_RUNNING:
- return "STATUS=running";
+ return "STATUS=up";
case DAEMON_SHUTDOWN:
return "STATUS=shutdown";
}
return NULL;
}
+static void do_sd_notify(enum daemon_status old_state)
+{
+ /*
+ * Checkerloop switches back and forth between idle and running state.
+ * No need to tell systemd each time.
+ * These notifications cause a lot of overhead on dbus.
+ */
+ if ((running_state == DAEMON_IDLE || running_state == DAEMON_RUNNING) &&
+ (old_state == DAEMON_IDLE || old_state == DAEMON_RUNNING))
+ return;
+ sd_notify(0, sd_notify_status());
+}
+
static void config_cleanup(void *arg)
{
pthread_mutex_unlock(&config_lock);
@@ -179,10 +191,12 @@ void post_config_state(enum daemon_status state)
{
pthread_mutex_lock(&config_lock);
if (state != running_state) {
+ enum daemon_status old_state = running_state;
+
running_state = state;
pthread_cond_broadcast(&config_cond);
#ifdef USE_SYSTEMD
- sd_notify(0, sd_notify_status());
+ do_sd_notify(old_state);
#endif
}
pthread_mutex_unlock(&config_lock);
@@ -195,6 +209,8 @@ int set_config_state(enum daemon_status state)
pthread_cleanup_push(config_cleanup, NULL);
pthread_mutex_lock(&config_lock);
if (running_state != state) {
+ enum daemon_status old_state = running_state;
+
if (running_state != DAEMON_IDLE) {
struct timespec ts;
@@ -207,7 +223,7 @@ int set_config_state(enum daemon_status state)
running_state = state;
pthread_cond_broadcast(&config_cond);
#ifdef USE_SYSTEMD
- sd_notify(0, sd_notify_status());
+ do_sd_notify(old_state);
#endif
}
}
@@ -1249,6 +1265,9 @@ uxlsnrloop (void * ap)
set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus);
set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q);
set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q);
+ set_handler_callback(GETPRKEY+MAP, cli_getprkey);
+ set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey);
+ set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey);
umask(077);
uxsock_listen(&uxsock_trigger, ap);
@@ -1623,7 +1642,7 @@ check_path (struct vectors * vecs, struct path * pp, int ticks)
if (newstate == PATH_UP) {
conf = get_multipath_config();
- newstate = get_state(pp, conf, 1);
+ newstate = get_state(pp, conf, 1, newstate);
put_multipath_config(conf);
} else
checker_clear_message(&pp->checker);
@@ -2786,10 +2805,8 @@ main (int argc, char *argv[])
void * mpath_pr_event_handler_fn (void * pathp )
{
struct multipath * mpp;
- int i,j, ret, isFound;
+ int i, ret, isFound;
struct path * pp = (struct path *)pathp;
- unsigned char *keyp;
- uint64_t prkey;
struct prout_param_descriptor *param;
struct prin_resp *resp;
@@ -2817,22 +2834,15 @@ void * mpath_pr_event_handler_fn (void * pathp )
ret = MPATH_PR_SUCCESS;
goto out;
}
- prkey = 0;
- keyp = (unsigned char *)mpp->reservation_key;
- for (j = 0; j < 8; ++j) {
- if (j > 0)
- prkey <<= 8;
- prkey |= *keyp;
- ++keyp;
- }
- condlog(2, "Multipath reservation_key: 0x%" PRIx64 " ", prkey);
+ condlog(2, "Multipath reservation_key: 0x%" PRIx64 " ",
+ get_be64(mpp->reservation_key));
isFound =0;
for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ )
{
condlog(2, "PR IN READKEYS[%d] reservation key:",i);
dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , -1);
- if (!memcmp(mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
+ if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
{
condlog(2, "%s: pr key found in prin readkeys response", mpp->alias);
isFound =1;
@@ -2849,11 +2859,7 @@ void * mpath_pr_event_handler_fn (void * pathp )
param= malloc(sizeof(struct prout_param_descriptor));
memset(param, 0 , sizeof(struct prout_param_descriptor));
-
- for (j = 7; j >= 0; --j) {
- param->sa_key[j] = (prkey & 0xff);
- prkey >>= 8;
- }
+ memcpy(param->sa_key, &mpp->reservation_key, 8);
param->num_transportid = 0;
condlog(3, "device %s:%s", pp->dev, pp->mpp->wwid);
@@ -2880,7 +2886,7 @@ int mpath_pr_event_handle(struct path *pp)
mpp = pp->mpp;
- if (!mpp->reservation_key)
+ if (get_be64(mpp->reservation_key))
return -1;
pthread_attr_init(&attr);