[dm-devel] multipathd: Add 'sysfs' prioritizer

Hannes Reinecke hare at suse.de
Mon May 23 10:20:28 UTC 2016


Recent kernels have an 'access_state' attribute which allows
us to read the asymmetric access state directly from sysfs.

Signed-off-by: Hannes Reinecke <hare at suse.de>
---
 libmultipath/discovery.c           | 33 +++++++++++++++++++++++++++++
 libmultipath/discovery.h           |  2 ++
 libmultipath/prio.h                |  1 +
 libmultipath/prioritizers/Makefile |  3 ++-
 libmultipath/prioritizers/sysfs.c  | 43 ++++++++++++++++++++++++++++++++++++++
 libmultipath/propsel.c             |  6 +++++-
 multipath/multipath.conf.5         | 14 ++++++++++++-
 7 files changed, 99 insertions(+), 3 deletions(-)
 create mode 100644 libmultipath/prioritizers/sysfs.c

diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index a364056..4a4b828 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -208,6 +208,8 @@ declare_sysfs_get_str(devtype);
 declare_sysfs_get_str(vendor);
 declare_sysfs_get_str(model);
 declare_sysfs_get_str(rev);
+declare_sysfs_get_str(access_state);
+declare_sysfs_get_str(preferred_path);
 
 ssize_t
 sysfs_get_vpd (struct udev_device * udev, int pg,
@@ -483,6 +485,37 @@ int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address)
 	return 1;
 }
 
+int
+sysfs_get_asymmetric_access_state(struct path *pp, char *buff, int buflen)
+{
+	struct udev_device *parent = pp->udev;
+	char value[16], *eptr;
+	unsigned int preferred;
+
+	while (parent) {
+		const char *subsys = udev_device_get_subsystem(parent);
+		if (subsys && !strncmp(subsys, "scsi", 4))
+			break;
+		parent = udev_device_get_parent(parent);
+	}
+
+	if (!parent)
+		return -1;
+
+	if (sysfs_get_access_state(parent, buff, buflen) <= 0)
+		return -1;
+
+	if (sysfs_get_preferred_path(parent, value, 16) <= 0)
+		return 0;
+
+	preferred = strtoul(value, &eptr, 0);
+	if (value == eptr || preferred == ULONG_MAX) {
+		/* Parse error, ignore */
+		return 0;
+	}
+	return  preferred;
+}
+
 static void
 sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
 {
diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h
index 5931bc6..b45c802 100644
--- a/libmultipath/discovery.h
+++ b/libmultipath/discovery.h
@@ -47,6 +47,8 @@ int sysfs_get_host_pci_name(struct path *pp, char *pci_name);
 int sysfs_get_iscsi_ip_address(struct path *pp, char *ip_address);
 ssize_t sysfs_get_vpd (struct udev_device * udev, int pg, unsigned char * buff,
 		       size_t len);
+int sysfs_get_asymmetric_access_state(struct path *pp,
+				      char *buff, int buflen);
 
 /*
  * discovery bitmask
diff --git a/libmultipath/prio.h b/libmultipath/prio.h
index 495688f..65abf54 100644
--- a/libmultipath/prio.h
+++ b/libmultipath/prio.h
@@ -29,6 +29,7 @@ struct path;
 #define PRIO_RDAC "rdac"
 #define PRIO_DATACORE "datacore"
 #define PRIO_WEIGHTED_PATH "weightedpath"
+#define PRIO_SYSFS "sysfs"
 
 /*
  * Value used to mark the fact prio was not defined
diff --git a/libmultipath/prioritizers/Makefile b/libmultipath/prioritizers/Makefile
index 6cfac88..0d1857f 100644
--- a/libmultipath/prioritizers/Makefile
+++ b/libmultipath/prioritizers/Makefile
@@ -15,7 +15,8 @@ LIBS = \
 	libpriodatacore.so \
 	libpriohds.so \
 	libprioweightedpath.so \
-	libprioiet.so
+	libprioiet.so \
+	libpriosysfs.so
 
 CFLAGS += -I..
 
diff --git a/libmultipath/prioritizers/sysfs.c b/libmultipath/prioritizers/sysfs.c
new file mode 100644
index 0000000..35a5c83
--- /dev/null
+++ b/libmultipath/prioritizers/sysfs.c
@@ -0,0 +1,43 @@
+/*
+ * sysfs.c
+ *
+ * Copyright(c) 2016 Hannes Reinecke, SUSE Linux GmbH
+ */
+
+#include <stdio.h>
+
+#include "structs.h"
+#include "discovery.h"
+#include "prio.h"
+
+static const struct {
+	unsigned char value;
+	char *name;
+} sysfs_access_state_map[] = {
+	{ 50, "active/optimized" },
+	{ 10, "active/non-optimized" },
+	{  5, "lba-dependent" },
+	{  1, "standby" },
+};
+
+int getprio (struct path * pp, char * args)
+{
+	int prio = 0, rc, i;
+	char buff[512];
+
+	rc = sysfs_get_asymmetric_access_state(pp, buff, 512);
+	if (rc < 0)
+		return PRIO_UNDEF;
+	prio = 0;
+	for (i = 0; i < 4; i++) {
+		if (!strncmp(buff, sysfs_access_state_map[i].name,
+			     strlen(sysfs_access_state_map[i].name))) {
+			prio = sysfs_access_state_map[i].value;
+			break;
+		}
+	}
+	if (rc > 0)
+		prio += 80;
+
+	return prio;
+}
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
index 8abe360..b0182de 100644
--- a/libmultipath/propsel.c
+++ b/libmultipath/propsel.c
@@ -374,6 +374,7 @@ detect_prio(struct path * pp)
 	int ret;
 	struct prio *p = &pp->prio;
 	int tpgs = 0;
+	char buff[512];
 
 	if ((tpgs = get_target_port_group_support(pp->fd)) <= 0)
 		return;
@@ -383,7 +384,10 @@ detect_prio(struct path * pp)
 		return;
 	if (get_asymmetric_access_state(pp->fd, ret) < 0)
 		return;
-	prio_get(p, PRIO_ALUA, DEFAULT_PRIO_ARGS);
+	if (sysfs_get_asymmetric_access_state(pp, buff, 512) < 0)
+		prio_get(p, PRIO_ALUA, DEFAULT_PRIO_ARGS);
+	else
+		prio_get(p, PRIO_SYSFS, DEFAULT_PRIO_ARGS);
 }
 
 #define set_prio(src, msg)						\
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
index 2ff88c4..aaaa01b 100644
--- a/multipath/multipath.conf.5
+++ b/multipath/multipath.conf.5
@@ -203,6 +203,10 @@ Generate the path priority for NetApp arrays.
 .B rdac
 Generate the path priority for LSI/Engenio/NetApp E-Series RDAC controller.
 .TP
+.B sysfs
+Use the sysfs attributes access_state and preferred_path to generate the
+path priority.
+.TP
 .B hp_sw
 Generate the path priority for Compaq/HP controller in
 active/standby mode.
@@ -449,8 +453,16 @@ If set to
 .I yes
 , multipath will try to detect if the device supports ALUA. If so, the device
 will automatically use the
+.I sysfs
+prioritizer if the required sysfs attributes
+.I access_state
+and
+.I preferred_path
+are supported, or the
 .I alua
-prioritizer. If not, the prioritizer will be selected as usual. Default is
+prioritizer if not. If set to
+.I no
+, the prioritizer will be selected as usual. Default is
 .I no
 .TP
 .B force_sync
-- 
2.6.6




More information about the dm-devel mailing list