multipathd: Add 'sysfs' prioritizer
authorHannes Reinecke <hare@suse.de>
Fri, 15 Jul 2016 06:48:57 +0000 (08:48 +0200)
committerChristophe Varoqui <christophe.varoqui@opensvc.com>
Fri, 22 Jul 2016 09:17:57 +0000 (11:17 +0200)
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@suse.de>
libmultipath/discovery.c
libmultipath/discovery.h
libmultipath/prio.h
libmultipath/prioritizers/Makefile
libmultipath/prioritizers/sysfs.c [new file with mode: 0644]
libmultipath/propsel.c
multipath/multipath.conf.5

index d9ed634..1fb4db4 100644 (file)
@@ -209,6 +209,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,
@@ -484,6 +486,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)
 {
index 321d930..0f5b1e6 100644 (file)
@@ -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
index 7195986..032028e 100644 (file)
@@ -30,6 +30,7 @@ struct path;
 #define PRIO_RANDOM            "random"
 #define PRIO_RDAC              "rdac"
 #define PRIO_WEIGHTED_PATH     "weightedpath"
+#define PRIO_SYSFS             "sysfs"
 
 /*
  * Value used to mark the fact prio was not defined
index 903a139..bb76700 100644 (file)
@@ -15,7 +15,8 @@ LIBS = \
        libprioontap.so \
        libpriorandom.so \
        libpriordac.so \
-       libprioweightedpath.so
+       libprioweightedpath.so \
+       libpriosysfs.so
 
 CFLAGS += -I..
 
diff --git a/libmultipath/prioritizers/sysfs.c b/libmultipath/prioritizers/sysfs.c
new file mode 100644 (file)
index 0000000..ff567df
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * 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 get_exclusive_pref_arg(char *args)
+{
+       char *ptr;
+
+       if (args == NULL)
+               return 0;
+       ptr = strstr(args, "exclusive_pref_bit");
+       if (!ptr)
+               return 0;
+       if (ptr[18] != '\0' && ptr[18] != ' ' && ptr[18] != '\t')
+               return 0;
+       if (ptr != args && ptr[-1] != ' ' && ptr[-1] != '\t')
+               return 0;
+       return 1;
+}
+
+int getprio (struct path * pp, char * args, unsigned int timeout)
+{
+       int prio = 0, rc, i;
+       char buff[512];
+       int exclusive_pref;
+
+       exclusive_pref = get_exclusive_pref_arg(args);
+       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 != 50 || exclusive_pref))
+               prio += 80;
+
+       return prio;
+}
index beb0798..0caf269 100644 (file)
@@ -375,6 +375,8 @@ detect_prio(struct config *conf, struct path * pp)
        struct prio *p = &pp->prio;
        int tpgs = 0;
        unsigned int timeout = conf->checker_timeout;
+       char buff[512];
+       char *default_prio = PRIO_ALUA;
 
        if ((tpgs = get_target_port_group_support(pp->fd, timeout)) <= 0)
                return;
@@ -384,7 +386,9 @@ detect_prio(struct config *conf, struct path * pp)
                return;
        if (get_asymmetric_access_state(pp->fd, ret, timeout) < 0)
                return;
-       prio_get(conf->multipath_dir, p, PRIO_ALUA, DEFAULT_PRIO_ARGS);
+       if (sysfs_get_asymmetric_access_state(pp, buff, 512) >= 0)
+               default_prio = PRIO_SYSFS;
+       prio_get(conf->multipath_dir, p, default_prio, DEFAULT_PRIO_ARGS);
 }
 
 #define set_prio(dir, src, msg)                                        \
index a8b5dba..01acf63 100644 (file)
@@ -187,6 +187,11 @@ are implemented:
 .I const
 Return a constant priority of \fI1\fR.
 .TP
+.I sysfs
+Use the sysfs attributes \fIaccess_state\fR and \fIpreferred_path\fR to
+generate the path priority. This prioritizer accepts the optional prio_arg
+.I exclusive_pref_bit
+.TP
 .I emc
 (Hardware-dependent)
 Generate the path priority for DGC class arrays as CLARiiON CX/AX and
@@ -252,8 +257,8 @@ these values can be looked up through sysfs or by running
 .I alua
 If
 .I exclusive_pref_bit
-is set, paths with the TPGS pref bit set will always be in their own path
-group.
+is set, paths with the \fIpreferred path\fR bit set will always
+be in their own path group.
 .TP
 .I datacore
 .I preferredsds