[dm-devel] [PATCH] multipath-tools: support for coalescing heterogenous paths

Guan Junxiong guanjunxiong at huawei.com
Thu Aug 17 08:15:18 UTC 2017


This patch supports coalescing heterogenous paths by using
udev rules files. This is useful in the scenario of migrating
data for heterogenous arrays without interrupting uplayer
transaction. The udev rules file, which is located in the
/etc/udev/rules.d/ directory, is automatically generated by
multipath with new options -m and -n.

For example:
multipath -m dm-0 -n  1000036006016028

The geneated udev rules is as follows:
KERNEL=="sd*[!0-9]|nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk",
ENV{ID_SERIAL}=="1000036006016028", ENV{ID_SERIAL}="36006016-
028f03600588db521dc7ce71"

KERNEL=="sd*[!0-9]|nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk",
ENV{ID_WWN}=="1000036006016028", ENV{ID_WWN}="36006016028f036-
00588db521dc7ce71"

KERNEL=="sd*[!0-9]|nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk",
ENV{ID_UUID}=="1000036006016028", ENV{ID_UUID}="36006016028f03-
600588db521dc7ce71"

Signed-off-by: Junxiong Guan <guanjunxiong at huawei.com>
---
 libmultipath/Makefile   |   3 +-
 libmultipath/config.h   |   1 +
 libmultipath/udevrule.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++++
 libmultipath/udevrule.h |  13 ++++
 multipath/main.c        |  29 +++++++-
 multipath/multipath.8   |  12 +++
 6 files changed, 247 insertions(+), 2 deletions(-)
 create mode 100644 libmultipath/udevrule.c
 create mode 100644 libmultipath/udevrule.h

diff --git a/libmultipath/Makefile b/libmultipath/Makefile
index b3244fc7..ae2fdc2d 100644
--- a/libmultipath/Makefile
+++ b/libmultipath/Makefile
@@ -42,7 +42,8 @@ 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 \
+	udevrule.o
 
 all: $(LIBS)
 
diff --git a/libmultipath/config.h b/libmultipath/config.h
index ffc69b5f..1f8438b7 100644
--- a/libmultipath/config.h
+++ b/libmultipath/config.h
@@ -34,6 +34,7 @@ enum mpath_cmds {
 	CMD_REMOVE_WWID,
 	CMD_RESET_WWIDS,
 	CMD_ADD_WWID,
+	CMD_GEN_UDEVRULE,
 };
 
 enum force_reload_types {
diff --git a/libmultipath/udevrule.c b/libmultipath/udevrule.c
new file mode 100644
index 00000000..e927bf9a
--- /dev/null
+++ b/libmultipath/udevrule.c
@@ -0,0 +1,191 @@
+/*
+ * udevrule.c
+ *
+ * Copyright (c) 2017 Guan Junxiong <guanjunxiong at huawei.com>, Huawei
+ *
+ * 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 the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * See the file COPYING included with this distribution for more details.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "vector.h"
+#include "structs.h"
+#include "debug.h"
+#include "uxsock.h"
+#include "file.h"
+#include "udevrule.h"
+
+#define UUID_PREFIX "mpath-"
+#define UUID_PREFIX_LEN 6
+
+static int has_wwid(FILE *f, char *wwid)
+{
+	char buf[LINE_MAX];
+
+	while (fgets(buf, LINE_MAX, f) != NULL) {
+		if (strstr(buf, wwid))
+			return 1;
+	}
+
+	return 0;
+}
+
+static ssize_t get_md_uuid(char *md, char *dm_uuid)
+{
+	int fd;
+	ssize_t size = -1;
+	char devpath[PATH_SIZE];
+
+	if (strlen(md) < 4 || strncmp(md, "dm-", 3)) {
+		condlog(0, "%s: device name should start with \"dm-\"", md);
+		return -EINVAL;
+	}
+	snprintf(devpath, PATH_SIZE, "/sys/devices/virtual/block/%s/dm/uuid",
+			md);
+	fd = open(devpath, O_RDONLY);
+	if (fd < 0) {
+		condlog(4, "attribute '%s' can not be opened: %s",
+			devpath, strerror(errno));
+		return -errno;
+	}
+	size = read(fd, dm_uuid, WWID_SIZE);
+	if (size < 0) {
+		condlog(4, "read from %s failed: %s", devpath, strerror(errno));
+		size = -errno;
+		dm_uuid[0] = '\0';
+	} else {
+		dm_uuid[size] = '\0';
+	}
+
+	if (!strncmp(dm_uuid, UUID_PREFIX, UUID_PREFIX_LEN)) {
+		memmove(dm_uuid, dm_uuid + UUID_PREFIX_LEN,
+			strlen(dm_uuid + UUID_PREFIX_LEN) + 1);
+		size -= UUID_PREFIX_LEN;
+	}
+	close(fd);
+
+	return size;
+}
+
+static int write_udev_rule(int fd, char *kname, char *dm_wwid,
+		char *wwid, char *id_type)
+{
+	int ret;
+	off_t offset;
+	char buf[LINE_MAX];
+
+	ret = snprintf(buf, LINE_MAX,
+		"KERNEL==\"%s\", ENV{DEVTYPE}==\"disk\", ENV{%s}==\"%s\", ENV{%s}=\"%s\"\n",
+		kname, id_type, wwid, id_type, dm_wwid);
+
+	if (ret >= LINE_MAX || ret < 0) {
+		condlog(0, "can't format udev rule for writing (%d) : %s",
+			ret, strerror(errno));
+		return 1;
+	}
+	offset = lseek(fd, 0, SEEK_END);
+	if (offset < 0) {
+		condlog(0, "can't seek to the end of %s: %s",
+			UDEV_RULES_FILE, strerror(errno));
+		return 1;
+	}
+	if (write(fd, buf, strlen(buf)) != strlen(buf)) {
+		condlog(0, "cannot write udev rule to %s: %s",
+			UDEV_RULES_FILE, strerror(errno));
+		if (ftruncate(fd, offset))
+			condlog(0, "cannot truncate failed udev rule line: %s",
+				strerror(errno));
+		return 1;
+	}
+
+	return 0;
+}
+
+static int write_udev_rules(int fd, char *md, char *wwid)
+{
+	int r = 0;
+	char *kname = "sd*[!0-9]|nvme*[0-9]n*[0-9]";
+	char dm_uuid[WWID_SIZE + 1] = {0};
+	ssize_t size;
+
+	size = get_md_uuid(md, dm_uuid);
+	if (size < 1)
+		return 1;
+	r += write_udev_rule(fd, kname, dm_uuid, wwid, "ID_SERIAL");
+	r += write_udev_rule(fd, kname, dm_uuid, wwid, "ID_WWN");
+	r += write_udev_rule(fd, kname, dm_uuid, wwid, "ID_UUID");
+
+	return r;
+}
+
+int gen_udevrules(char *md, char *wwid)
+{
+	int fd, can_write, found, ret = -1;
+	FILE *f;
+
+	if (!wwid) {
+		condlog(0, "the -m option should combined with -n wwid ");
+		return -1;
+	}
+	if (!md) {
+		condlog(0, "the argument for -m option is null");
+		return -1;
+	}
+	fd = open_file(UDEV_RULES_FILE, &can_write,
+			UDEV_RULE_FILE_HEADHER);
+	if (fd < 0)
+		return -1;
+
+	f = fdopen(fd, "r");
+	if (!f) {
+		condlog(0, "can't fdopen %s: %s",
+			UDEV_RULES_FILE, strerror(errno));
+		close(fd);
+		return -1;
+	}
+
+	found = has_wwid(f, wwid);
+	if (found) {
+		condlog(0, "%s has already contained %s",
+			UDEV_RULES_FILE, wwid);
+		goto out;
+	}
+	if (!can_write) {
+		condlog(0, "%s is read-only. Not writable", UDEV_RULES_FILE);
+		goto out;
+	}
+
+	if (fflush(f) != 0) {
+		condlog(0, "cannot fflush %s stream : %s", UDEV_RULES_FILE,
+			strerror(errno));
+		goto out;
+	}
+
+	ret = write_udev_rules(fd, md, wwid);
+	if (ret == 0) {
+		condlog(0, "write %s file OK. You could use \"udevadm control --reload-rules && udevadm trigger\" to apply this change",
+		UDEV_RULES_FILE);
+	}
+out:
+	fclose(f);
+
+	return ret;
+}
diff --git a/libmultipath/udevrule.h b/libmultipath/udevrule.h
new file mode 100644
index 00000000..8823633e
--- /dev/null
+++ b/libmultipath/udevrule.h
@@ -0,0 +1,13 @@
+#ifndef _UDEVRULE_H
+#define _UDEVRULE_H
+
+#define UDEV_RULES_FILE "/etc/udev/rules.d/99-multipath-udev.rules"
+
+#define UDEV_RULE_FILE_HEADHER \
+"# This file was automatically generated by the multipath program\n" \
+"# You can modify it,as long as you keep each rule on a single\n" \
+"# line,and change only the single line by deleting it .\n"
+
+int gen_udevrules(char *md, char *wwid);
+
+#endif /* _UDEVRULE_H */
diff --git a/multipath/main.c b/multipath/main.c
index dede017e..bc9fdb61 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -58,6 +58,7 @@
 #include "wwids.h"
 #include "uxsock.h"
 #include "mpath_cmd.h"
+#include "udevrule.h"
 
 int logsink;
 struct udev *udev;
@@ -107,6 +108,7 @@ usage (char * progname)
 	fprintf (stderr, "  %s -F [-v lvl] [-R num]\n", progname);
 	fprintf (stderr, "  %s -t\n", progname);
 	fprintf (stderr, "  %s -h\n", progname);
+	fprintf (stderr, "  %s -m dm -n wwid\n", progname);
 	fprintf (stderr,
 		"\n"
 		"Where:\n"
@@ -138,6 +140,9 @@ usage (char * progname)
 		"          . 2 default verbosity\n"
 		"          . 3 print debug information\n"
 		"  -R num  number of times to retry removes of in-use devices\n"
+		"  -m dm  specify the multipath device to be coalesced with\n"
+		"  -n wwid the wwid of the device to be coalesced with the multipath device\n"
+		"          . It is the same as unique identifier the path given the uid_attribute\n"
 		"  dev     action limited to:\n"
 		"          . multipath named 'dev' (ex: mpath0) or\n"
 		"          . multipath whose wwid is 'dev' (ex: 60051..)\n"
@@ -514,6 +519,8 @@ main (int argc, char *argv[])
 	char *dev = NULL;
 	struct config *conf;
 	int retries = -1;
+	char *mpathname = NULL;
+	char *wwid = NULL;
 
 	udev = udev_new();
 	logsink = 0;
@@ -522,7 +529,8 @@ 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,
+		":adchl::FfM:v:p:b:BrR:itquwWm:n:")) != EOF) {
 		switch(arg) {
 		case 1: printf("optarg : %s\n",optarg);
 			break;
@@ -605,6 +613,14 @@ main (int argc, char *argv[])
 		case 'R':
 			retries = atoi(optarg);
 			break;
+		case 'm':
+			cmd = CMD_GEN_UDEVRULE;
+			mpathname = strdup(optarg);
+			break;
+		case 'n':
+			wwid = strdup(optarg);
+			break;
+
 		case ':':
 			fprintf(stderr, "Missing option argument\n");
 			usage(argv[0]);
@@ -680,6 +696,13 @@ main (int argc, char *argv[])
 		condlog(0, "the -c option requires a path to check");
 		goto out;
 	}
+
+	if (cmd == CMD_GEN_UDEVRULE) {
+		if (gen_udevrules(mpathname, wwid))
+			condlog(0, "cannot generate udev rule file");
+		goto out;
+	}
+
 	if (cmd == CMD_VALID_PATH &&
 	    dev_type == DEV_UEVENT) {
 		int fd;
@@ -758,6 +781,10 @@ out_free_config:
 	udev_unref(udev);
 	if (dev)
 		FREE(dev);
+	if (mpathname)
+		free(mpathname);
+	if (wwid)
+		free(wwid);
 #ifdef _DEBUG_
 	dbg_free_final(NULL);
 #endif
diff --git a/multipath/multipath.8 b/multipath/multipath.8
index b9436e52..3297ce05 100644
--- a/multipath/multipath.8
+++ b/multipath/multipath.8
@@ -30,6 +30,10 @@ multipath \- Device mapper target autoconfig.
 .IR failover | multibus | group_by_serial | group_by_prio | group_by_node_name \|]
 .RB [\| \-R\ \c
 .IR retries \|]
+.RB [\| \-m\ \c
+.IR dm \ \|\c
+.RB \| \-n\ \c
+.IR wwid \|]
 .RB [\| device \|]
 .
 .
@@ -162,6 +166,14 @@ Number of times to retry flushing multipath devices that are in-use. The default
 is \fI0\fR.
 .
 .TP
+.BI \-m " dm"
+Specify the multipath device to be coalesced with.
+.
+.TP
+.BI \-n " wwid"
+The wwid of the device to be coalesced into the multipath device
+.
+.TP
 .BI device
 Update only the devmap specified by
 .IR device ,
-- 
2.11.1





More information about the dm-devel mailing list