Imported Upstream version 0.9.0 upstream/0.9.0
authorJinWang An <jinwang.an@samsung.com>
Fri, 13 Jan 2023 06:40:25 +0000 (15:40 +0900)
committerJinWang An <jinwang.an@samsung.com>
Fri, 13 Jan 2023 06:40:25 +0000 (15:40 +0900)
59 files changed:
.github/workflows/foreign.yaml
Makefile.inc
README.md
kpartx/Makefile
kpartx/devmapper.c
libdmmp/Makefile
libdmmp/test/Makefile
libmpathvalid/Makefile
libmultipath/Makefile
libmultipath/callout.c [deleted file]
libmultipath/callout.h [deleted file]
libmultipath/checkers.c
libmultipath/checkers.h
libmultipath/checkers/Makefile
libmultipath/checkers/rdac.c
libmultipath/checkers/tur.c
libmultipath/config.c
libmultipath/config.h
libmultipath/configure.c
libmultipath/defaults.h
libmultipath/devmapper.c
libmultipath/dict.c
libmultipath/discovery.c
libmultipath/discovery.h
libmultipath/foreign.c
libmultipath/foreign.h
libmultipath/foreign/Makefile
libmultipath/foreign/nvme.c
libmultipath/hwtable.c
libmultipath/libmultipath.version
libmultipath/list.h
libmultipath/print.c
libmultipath/prio.c
libmultipath/prio.h
libmultipath/prioritizers/Makefile
libmultipath/prioritizers/iet.c
libmultipath/propsel.c
libmultipath/propsel.h
libmultipath/structs.c
libmultipath/structs.h
libmultipath/structs_vec.c
libmultipath/uevent.c
libmultipath/uevent.h
libmultipath/version.h
mpathpersist/Makefile
multipath/Makefile
multipath/main.c
multipath/multipath.conf.5
multipathd/Makefile
multipathd/fpin_handlers.c
multipathd/main.c
multipathd/uxlsnr.c
tests/Makefile
tests/directio.c
tests/hwtable.c
tests/mpathvalid.c
tests/test-lib.c
tests/test-lib.h
tests/uevent.c

index c164cb3..e9ffd3d 100644 (file)
@@ -24,7 +24,8 @@ jobs:
         run: make test
       - name: build
         if: ${{ matrix.arch != '' && matrix.arch != '-i386' }}
-        run: make test-progs
+        # The build path is different between builder and runner
+        run: make TESTDIR=${{ github.workspace }}/tests test-progs
       - name: archive
         if: ${{ matrix.arch != '' && matrix.arch != '-i386' }}
         run: >
@@ -61,6 +62,8 @@ jobs:
         uses: mosteo-actions/docker-run@v1
         with:
           image: mwilck/multipath-run-${{ matrix.os }}-${{ matrix.arch }}
-          # The runner is an image that has "make" as entrypoint
+          # The runner is an image that has "make" as entrypoint and uses
+          # github.workspace as both host dir and guest volume by default.
+          # See https://github.com/mosteo-actions/docker-run/blob/v1/action.yml
           # So run "make -C tests" here
           command: -C tests
index d24da43..bcd2212 100644 (file)
@@ -13,6 +13,7 @@
 # SCSI_DH_MODULES_PRELOAD := scsi_dh_alua scsi_dh_rdac
 SCSI_DH_MODULES_PRELOAD :=
 
+EXTRAVERSION := $(shell rev=$$(git rev-parse --short=7 HEAD 2>/dev/null); echo $${rev:+-g$$rev})
 
 PKGCONFIG      ?= pkg-config
 
@@ -95,6 +96,8 @@ libdmmpdir    = $(TOPDIR)/libdmmp
 nvmedir                = $(TOPDIR)/libmultipath/nvme
 includedir     = $(prefix)/usr/include
 pkgconfdir     = $(usrlibdir)/pkgconfig
+plugindir       := $(prefix)/$(LIB)/multipath
+configdir       := $(prefix)/etc/multipath/conf.d
 
 GZIP_PROG      = gzip -9 -c
 RM             = rm -f
@@ -114,6 +117,20 @@ TEST_CC_OPTION = $(shell \
                echo "$(2)"; \
        fi)
 
+# "make" on some distros will fail on explicit '#' or '\#' in the program text below
+__HASH__ := \#
+# Check if _DFORTIFY_SOURCE=3 is supported.
+# On some distros (e.g. Debian Buster) it will be falsely reported as supported
+# but it doesn't seem to make a difference wrt the compilation result.
+FORTIFY_OPT := $(shell \
+       if /bin/echo -e '$(__HASH__)include <string.h>\nint main(void) { return 0; }' | \
+               $(CC) -o /dev/null -c -O2 -Werror -D_FORTIFY_SOURCE=3 -xc - 2>/dev/null; \
+       then \
+               echo "-D_FORTIFY_SOURCE=3"; \
+       else \
+               echo "-D_FORTIFY_SOURCE=2"; \
+       fi)
+
 STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)
 ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,)
 WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered -Wno-error=clobbered,)
@@ -123,10 +140,10 @@ OPTFLAGS  := -O2 -g $(STACKPROT) --param=ssp-buffer-size=4
 WARNFLAGS      := -Werror -Wall -Wextra -Wformat=2 $(WFORMATOVERFLOW) -Werror=implicit-int \
                  -Werror=implicit-function-declaration -Werror=format-security \
                  $(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS)
-CPPFLAGS       := -Wp,-D_FORTIFY_SOURCE=2
-CFLAGS         := --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe \
-                  -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \
-                  -MMD -MP
+CPPFLAGS       := $(FORTIFY_OPT) \
+                  -DBIN_DIR=\"$(bindir)\" -DMULTIPATH_DIR=\"$(plugindir)\" -DRUN_DIR=\"${RUN}\" \
+                  -DCONFIG_DIR=\"$(configdir)\" -DEXTRAVERSION=\"$(EXTRAVERSION)\" -MMD -MP
+CFLAGS         := --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe
 BIN_CFLAGS     = -fPIE -DPIE
 LIB_CFLAGS     = -fPIC
 SHARED_FLAGS   = -shared
@@ -174,7 +191,7 @@ check_var = $(shell \
 
 %.o:   %.c
        @echo building $@ because of $?
-       $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
+       $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
 
 %.abi:  %.so.0
        abidw $< >$@
index 1547862..f06f8ce 100644 (file)
--- a/README.md
+++ b/README.md
@@ -72,9 +72,15 @@ Customizing the build
 
 The following variables can be passed to the `make` command line:
 
+ * `plugindir="/some/path"`: directory where libmultipath plugins (path
+   checkers, prioritizers, and foreign multipath support) will be looked up.
+   This used to be the run-time option `multipath_dir` in earlier versions.
+ * `configdir="/some/path"` : directory to search for configuration files.
+    This used to be the run-time option `config_dir` in earlier versions.
+       The default is `/etc/multipath/conf.d`.
  * `ENABLE_LIBDMMP=0`: disable building libdmmp
  * `ENABLE_DMEVENTS_POLL=0`: disable support for the device-mapper event
-   polling API. For use with pre-5.0 kernels that don't supprt dmevent polling
+   polling API. For use with pre-5.0 kernels that don't support dmevent polling
    (but even if you don't use this option, multipath-tools will work with
    these kernels).
  * `SCSI_DH_MODULES_PRELOAD="(list)"`: specify a space-separated list of SCSI
index e9900fb..742d3bc 100644 (file)
@@ -3,13 +3,14 @@
 #
 include ../Makefile.inc
 
-CFLAGS += $(BIN_CFLAGS) -I. -I$(multipathdir) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+CPPFLAGS += -I. -I$(multipathdir) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+CFLAGS += $(BIN_CFLAGS)
 LDFLAGS += $(BIN_LDFLAGS)
 
 LIBDEPS += -ldevmapper
 
 ifneq ($(call check_func,dm_task_set_cookie,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
-       CFLAGS += -DLIBDM_API_COOKIE
+       CPPFLAGS += -DLIBDM_API_COOKIE
 endif
 
 OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o dasd.o sun.o \
index 49ffd31..bf14c78 100644 (file)
@@ -412,8 +412,10 @@ dm_get_map(const char *mapname, char * outparams)
                goto out;
 
        /* Fetch 1st target */
-       dm_get_next_target(dmt, NULL, &start, &length,
-                          &target_type, &params);
+       if (dm_get_next_target(dmt, NULL, &start, &length,
+                              &target_type, &params) != NULL || !params)
+               /* more than one target or not found target */
+               goto out;
 
        if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE)
                r = 0;
index 00fc852..2e99b3e 100644 (file)
@@ -15,8 +15,8 @@ HEADERS = libdmmp/libdmmp.h
 
 OBJS = libdmmp.o libdmmp_mp.o libdmmp_pg.o libdmmp_path.o libdmmp_misc.o
 
-CFLAGS += $(LIB_CFLAGS) -fvisibility=hidden -I$(libdmmpdir) -I$(mpathcmddir) \
-         $(shell $(PKGCONFIG) --cflags json-c)
+CPPFLAGS += -I$(libdmmpdir) -I$(mpathcmddir) $(shell $(PKGCONFIG) --cflags json-c)
+CFLAGS += $(LIB_CFLAGS) -fvisibility=hidden
 
 LIBDEPS += $(shell $(PKGCONFIG) --libs json-c) -L$(mpathcmddir) -lmpathcmd -lpthread
 
index 20b3945..76b24d6 100644 (file)
@@ -9,7 +9,7 @@ _mpathcmddir=../$(mpathcmddir)
 
 TEST_EXEC = libdmmp_test
 SPD_TEST_EXEC = libdmmp_speed_test
-CFLAGS += -I$(_libdmmpdir)
+CPPFLAGS += -I$(_libdmmpdir)
 LDFLAGS += -L$(_libdmmpdir) -ldmmp
 
 all: $(TEST_EXEC) $(SPD_TEST_EXEC)
index fefeb2a..0a51925 100644 (file)
@@ -5,7 +5,8 @@ DEVLIB = libmpathvalid.so
 LIBS = $(DEVLIB).$(SONAME)
 VERSION_SCRIPT := libmpathvalid.version
 
-CFLAGS += $(LIB_CFLAGS) -I$(multipathdir) -I$(mpathcmddir)
+CPPFLAGS += -I$(multipathdir) -I$(mpathcmddir)
+CFLAGS += $(LIB_CFLAGS)
 
 LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) \
           -lmultipath -L$(mpathcmddir) -lmpathcmd -ludev
index 77e954a..fb03200 100644 (file)
@@ -8,12 +8,13 @@ DEVLIB = libmultipath.so
 LIBS = $(DEVLIB).$(SONAME)
 VERSION_SCRIPT := libmultipath.version
 
-CFLAGS += $(LIB_CFLAGS) -I$(mpathcmddir) -I$(mpathpersistdir) -I$(nvmedir)
+CPPFLAGS += -I$(mpathcmddir) -I$(mpathpersistdir) -I$(nvmedir)
+CFLAGS += $(LIB_CFLAGS)
 
 LIBDEPS += -lpthread -ldl -ldevmapper -ludev -L$(mpathcmddir) -lmpathcmd -lurcu -laio
 
 ifdef SYSTEMD
-       CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
+       CPPFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
        ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1)
                LIBDEPS += -lsystemd
        else
@@ -22,53 +23,60 @@ ifdef SYSTEMD
 endif
 
 ifneq ($(call check_func,dm_task_no_flush,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
-       CFLAGS += -DLIBDM_API_FLUSH -D_GNU_SOURCE
+       CPPFLAGS += -DLIBDM_API_FLUSH -D_GNU_SOURCE
 endif
 
 ifneq ($(call check_func,dm_task_get_errno,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
-       CFLAGS += -DLIBDM_API_GET_ERRNO
+       CPPFLAGS += -DLIBDM_API_GET_ERRNO
 endif
 
 ifneq ($(call check_func,dm_task_set_cookie,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
-       CFLAGS += -DLIBDM_API_COOKIE
+       CPPFLAGS += -DLIBDM_API_COOKIE
 endif
 
 ifneq ($(call check_func,udev_monitor_set_receive_buffer_size,$(LIBUDEV_INCDIR)/libudev.h),0)
-       CFLAGS += -DLIBUDEV_API_RECVBUF
+       CPPFLAGS += -DLIBUDEV_API_RECVBUF
 endif
 
 ifneq ($(call check_func,dm_task_deferred_remove,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
-       CFLAGS += -DLIBDM_API_DEFERRED
+       CPPFLAGS += -DLIBDM_API_DEFERRED
 endif
 
 ifneq ($(call check_func,dm_hold_control_dev,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
-       CFLAGS += -DLIBDM_API_HOLD_CONTROL
+       CPPFLAGS += -DLIBDM_API_HOLD_CONTROL
 endif
 
 ifneq ($(call check_var,ELS_DTAG_LNK_INTEGRITY,$(LINUX_HEADERS_INCDIR)/scsi/fc/fc_els.h),0)
-       CFLAGS += -DFPIN_EVENT_HANDLER
+       CPPFLAGS += -DFPIN_EVENT_HANDLER
 endif
 
+# object files referencing MULTIPATH_DIR or CONFIG_DIR
+# they need to be recompiled for unit tests
+OBJS-U := prio.o checkers.o foreign.o config.o
+OBJS-T := $(patsubst %.o,%-test.o,$(OBJS-U))
 
-OBJS = parser.o vector.o devmapper.o callout.o \
-       hwtable.o blacklist.o util.o dmparser.o config.o \
+# other object files
+OBJS-O := parser.o vector.o devmapper.o \
+       hwtable.o blacklist.o util.o dmparser.o \
        structs.o discovery.o propsel.o dict.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 \
+       log.o configure.o structs_vec.o sysfs.o \
        lock.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \
-       io_err_stat.o dm-generic.o generic.o foreign.o nvme-lib.o \
+       io_err_stat.o dm-generic.o generic.o nvme-lib.o \
        libsg.o valid.o strbuf.o
 
+OBJS := $(OBJS-O) $(OBJS-U)
+
 all:   $(DEVLIB)
 
 nvme-lib.o: nvme-lib.c nvme-ioctl.c nvme-ioctl.h
-       $(CC) $(CFLAGS) -Wno-unused-function -c -o $@ $<
+       $(CC) $(CPPFLAGS) $(CFLAGS) -Wno-unused-function -c -o $@ $<
 
 # there are lots of "unused parameters" in dict.c
 # because not all handler / snprint methods need all parameters
 dict.o:        dict.c
-       $(CC) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
+       $(CC) $(CPPFLAGS) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
 
 make_static = $(shell sed '/^static/!s/^\([a-z]\{1,\} \)/static \1/' <$1 >$2)
 
@@ -97,11 +105,16 @@ $(LIBS:%.so.$(SONAME)=%-nv.so):    $(OBJS) $(NV_VERSION_SCRIPT)
 
 abi:    $(LIBS:%.so.$(SONAME)=%-nv.abi)
 
-../tests/$(LIBS): $(OBJS) $(VERSION_SCRIPT)
+../tests/$(LIBS): $(OBJS-O) $(OBJS-T) $(VERSION_SCRIPT)
        $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=`basename $@` \
-               -o $@ $(OBJS) $(LIBDEPS)
+               -o $@ $(OBJS-O) $(OBJS-T) $(LIBDEPS)
        $(LN) $@ ${@:.so.0=.so}
 
+# This rule is invoked from tests/Makefile, overriding configdir and plugindir
+%-test.o: %.c
+       @echo building $@ because of $?
+       $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+
 test-lib:      ../tests/$(LIBS)
 
 install: all
diff --git a/libmultipath/callout.c b/libmultipath/callout.c
deleted file mode 100644 (file)
index dac088c..0000000
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * Source: copy of the udev package source file
- *
- * Copyrights of the source file apply
- * Copyright (c) 2004 Christophe Varoqui
- */
-#include <stdio.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <sys/wait.h>
-#include <errno.h>
-
-#include "checkers.h"
-#include "vector.h"
-#include "structs.h"
-#include "util.h"
-#include "callout.h"
-#include "debug.h"
-
-int execute_program(char *path, char *value, int len)
-{
-       int retval;
-       int count;
-       int status;
-       int fds[2], null_fd;
-       pid_t pid;
-       char *pos;
-       char arg[CALLOUT_MAX_SIZE];
-       int argc = sizeof(arg) / 2;
-       char *argv[argc + 1];
-       int i;
-
-       i = 0;
-
-       if (strchr(path, ' ')) {
-               strlcpy(arg, path, sizeof(arg));
-               pos = arg;
-               while (pos != NULL && i < argc) {
-                       if (pos[0] == '\'') {
-                               /* don't separate if in apostrophes */
-                               pos++;
-                               argv[i] = strsep(&pos, "\'");
-                               while (pos[0] == ' ')
-                                       pos++;
-                       } else {
-                               argv[i] = strsep(&pos, " ");
-                       }
-                       i++;
-               }
-       } else {
-               argv[i++] = path;
-       }
-       argv[i] =  NULL;
-
-       retval = pipe(fds);
-
-       if (retval != 0) {
-               condlog(0, "error creating pipe for callout: %s", strerror(errno));
-               return -1;
-       }
-
-       pid = fork();
-
-       switch(pid) {
-       case 0:
-               /* child */
-
-               /* dup write side of pipe to STDOUT */
-               if (dup2(fds[1], STDOUT_FILENO) < 0) {
-                       condlog(1, "failed to dup2 stdout: %m");
-                       return -1;
-               }
-               close(fds[0]);
-               close(fds[1]);
-
-               /* Ignore writes to stderr */
-               null_fd = open("/dev/null", O_WRONLY);
-               if (null_fd > 0) {
-                       if (dup2(null_fd, STDERR_FILENO) < 0)
-                               condlog(1, "failed to dup2 stderr: %m");
-                       close(null_fd);
-               }
-
-               retval = execv(argv[0], argv);
-               condlog(0, "error execing %s : %s", argv[0], strerror(errno));
-               exit(-1);
-       case -1:
-               condlog(0, "fork failed: %s", strerror(errno));
-               close(fds[0]);
-               close(fds[1]);
-               return -1;
-       default:
-               /* parent reads from fds[0] */
-               close(fds[1]);
-               retval = 0;
-               i = 0;
-               while (1) {
-                       count = read(fds[0], value + i, len - i-1);
-                       if (count <= 0)
-                               break;
-
-                       i += count;
-                       if (i >= len-1) {
-                               condlog(0, "not enough space for response from %s", argv[0]);
-                               retval = -1;
-                               break;
-                       }
-               }
-
-               if (count < 0) {
-                       condlog(0, "no response from %s", argv[0]);
-                       retval = -1;
-               }
-
-               if (i > 0 && value[i-1] == '\n')
-                       i--;
-               value[i] = '\0';
-
-               wait(&status);
-               close(fds[0]);
-
-               retval = -1;
-               if (WIFEXITED(status)) {
-                       status = WEXITSTATUS(status);
-                       if (status == 0)
-                               retval = 0;
-                       else
-                               condlog(0, "%s exited with %d", argv[0], status);
-               }
-               else if (WIFSIGNALED(status))
-                       condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status));
-               else
-                       condlog(0, "%s terminated abnormally", argv[0]);
-       }
-       return retval;
-}
-
-int apply_format(char * string, char * cmd, struct path * pp)
-{
-       char * pos;
-       char * dst;
-       char * p;
-       char * q;
-       int len;
-       int myfree;
-
-       if (!string)
-               return 1;
-
-       if (!cmd)
-               return 1;
-
-       dst = cmd;
-       p = dst;
-       pos = strchr(string, '%');
-       myfree = CALLOUT_MAX_SIZE;
-
-       if (!pos) {
-               strcpy(dst, string);
-               return 0;
-       }
-
-       len = (int) (pos - string) + 1;
-       myfree -= len;
-
-       if (myfree < 2)
-               return 1;
-
-       snprintf(p, len, "%s", string);
-       p += len - 1;
-       pos++;
-
-       switch (*pos) {
-       case 'n':
-               len = strlen(pp->dev) + 1;
-               myfree -= len;
-
-               if (myfree < 2)
-                       return 1;
-
-               snprintf(p, len, "%s", pp->dev);
-               for (q = p; q < p + len; q++) {
-                       if (q && *q == '!')
-                               *q = '/';
-               }
-               p += len - 1;
-               break;
-       case 'd':
-               len = strlen(pp->dev_t) + 1;
-               myfree -= len;
-
-               if (myfree < 2)
-                       return 1;
-
-               snprintf(p, len, "%s", pp->dev_t);
-               p += len - 1;
-               break;
-       default:
-               break;
-       }
-       pos++;
-
-       if (!*pos) {
-               condlog(3, "formatted callout = %s", dst);
-               return 0;
-       }
-
-       len = strlen(pos) + 1;
-       myfree -= len;
-
-       if (myfree < 2)
-               return 1;
-
-       snprintf(p, len, "%s", pos);
-       condlog(3, "reformatted callout = %s", dst);
-       return 0;
-}
diff --git a/libmultipath/callout.h b/libmultipath/callout.h
deleted file mode 100644 (file)
index ab648e8..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#ifndef _CALLOUT_H
-#define _CALLOUT_H
-
-int execute_program(char *, char *, int);
-int apply_format (char *, char *, struct path *);
-
-#endif /* _CALLOUT_H */
index 00554d6..fdb91e1 100644 (file)
@@ -12,6 +12,8 @@
 #include "vector.h"
 #include "util.h"
 
+static const char * const checker_dir = MULTIPATH_DIR;
+
 struct checker_class {
        struct list_head node;
        void *handle;
@@ -133,8 +135,7 @@ void reset_checker_classes(void)
        }
 }
 
-static struct checker_class *add_checker_class(const char *multipath_dir,
-                                              const char *name)
+static struct checker_class *add_checker_class(const char *name)
 {
        char libname[LIB_CHECKER_NAMELEN];
        struct stat stbuf;
@@ -148,10 +149,10 @@ static struct checker_class *add_checker_class(const char *multipath_dir,
        if (!strncmp(c->name, NONE, 4))
                goto done;
        snprintf(libname, LIB_CHECKER_NAMELEN, "%s/libcheck%s.so",
-                multipath_dir, name);
+                checker_dir, name);
        if (stat(libname,&stbuf) < 0) {
                condlog(0,"Checker '%s' not found in %s",
-                       name, multipath_dir);
+                       name, checker_dir);
                goto out;
        }
        condlog(3, "loading %s checker", libname);
@@ -409,8 +410,7 @@ void checker_clear_message (struct checker *c)
        c->msgid = CHECKER_MSGID_NONE;
 }
 
-void checker_get(const char *multipath_dir, struct checker *dst,
-                const char *name)
+void checker_get(struct checker *dst, const char *name)
 {
        struct checker_class *src = NULL;
 
@@ -420,7 +420,7 @@ void checker_get(const char *multipath_dir, struct checker *dst,
        if (name && strlen(name)) {
                src = checker_class_lookup(name);
                if (!src)
-                       src = add_checker_class(multipath_dir, name);
+                       src = add_checker_class(name);
        }
        dst->cls = src;
        if (!src)
@@ -429,7 +429,7 @@ void checker_get(const char *multipath_dir, struct checker *dst,
        (void)checker_class_ref(dst->cls);
 }
 
-int init_checkers(const char *multipath_dir)
+int init_checkers(void)
 {
 #ifdef LOAD_ALL_SHARED_LIBS
        static const char *const all_checkers[] = {
@@ -444,9 +444,9 @@ int init_checkers(const char *multipath_dir)
        unsigned int i;
 
        for (i = 0; i < ARRAY_SIZE(all_checkers); i++)
-               add_checker_class(multipath_dir, all_checkers[i]);
+               add_checker_class(all_checkers[i]);
 #else
-       if (!add_checker_class(multipath_dir, DEFAULT_CHECKER))
+       if (!add_checker_class(DEFAULT_CHECKER))
                return 1;
 #endif
        return 0;
index 5d25a42..ea1e8af 100644 (file)
@@ -135,7 +135,7 @@ static inline int checker_selected(const struct checker *c)
 }
 
 const char *checker_state_name(int);
-int init_checkers(const char *);
+int init_checkers(void);
 void cleanup_checkers (void);
 int checker_init (struct checker *, void **);
 int checker_mp_init(struct checker *, void **);
@@ -179,7 +179,7 @@ void reset_checker_classes(void);
  */
 const char *checker_message(const struct checker *);
 void checker_clear_message (struct checker *c);
-void checker_get(const char *, struct checker *, const char *);
+void checker_get(struct checker *, const char *);
 
 /* Prototypes for symbols exported by path checker dynamic libraries (.so) */
 int libcheck_check(struct checker *);
index 8e0ed5e..8d8e45e 100644 (file)
@@ -3,7 +3,8 @@
 #
 include ../../Makefile.inc
 
-CFLAGS += $(LIB_CFLAGS) -I..
+CPPFLAGS += -I..
+CFLAGS += $(LIB_CFLAGS)
 LDFLAGS += -L.. -lmultipath
 LIBDEPS = -lmultipath -laio -lpthread -lrt
 
index d924a9f..f7aaa30 100644 (file)
@@ -96,7 +96,7 @@ int libcheck_init (struct checker * c)
                goto out;
        }
 
-       /* get the changeble values */
+       /* get the changeable values */
        cmd[2] = 0xA + (CHANGEABLE_PAGE_CODE_VALUES << 6);
        io_hdr.dxferp = &changeable;
        memset(&changeable, 0, sizeof(struct control_mode_page));
index c93e462..1bcb757 100644 (file)
@@ -26,6 +26,7 @@
 
 #define TUR_CMD_LEN 6
 #define HEAVY_CHECK_COUNT       10
+#define MAX_NR_TIMEOUTS 1
 
 enum {
        MSG_TUR_RUNNING = CHECKER_FIRST_MSGID,
@@ -54,6 +55,7 @@ struct tur_checker_context {
        int holders; /* uatomic access only */
        int msgid;
        struct checker_context ctx;
+       unsigned int nr_timeouts;
 };
 
 int libcheck_init (struct checker * c)
@@ -358,8 +360,23 @@ int libcheck_check(struct checker * c)
                }
        } else {
                if (uatomic_read(&ct->holders) > 1) {
+                       /* The thread has been cancelled but hasn't quit. */
+                       if (ct->nr_timeouts == MAX_NR_TIMEOUTS) {
+                               condlog(2, "%d:%d : waiting for stalled tur thread to finish",
+                                       major(ct->devt), minor(ct->devt));
+                               ct->nr_timeouts++;
+                       }
                        /*
-                        * The thread has been cancelled but hasn't quit.
+                        * Don't start new threads until the last once has
+                        * finished.
+                        */
+                       if (ct->nr_timeouts > MAX_NR_TIMEOUTS) {
+                               c->msgid = MSG_TUR_TIMEOUT;
+                               return PATH_TIMEOUT;
+                       }
+                       ct->nr_timeouts++;
+                       /*
+                        * Start a new thread while the old one is stalled.
                         * We have to prevent it from interfering with the new
                         * thread. We create a new context and leave the old
                         * one with the stale thread, hoping it will clean up
@@ -375,13 +392,15 @@ int libcheck_check(struct checker * c)
                         */
                        if (libcheck_init(c) != 0)
                                return PATH_UNCHECKED;
+                       ((struct tur_checker_context *)c->context)->nr_timeouts = ct->nr_timeouts;
 
                        if (!uatomic_sub_return(&ct->holders, 1))
                                /* It did terminate, eventually */
                                cleanup_context(ct);
 
                        ct = c->context;
-               }
+               } else
+                       ct->nr_timeouts = 0;
                /* Start new TUR checker */
                pthread_mutex_lock(&ct->lock);
                tur_status = ct->state = PATH_PENDING;
index c595e76..ab8b26e 100644 (file)
@@ -237,6 +237,18 @@ const char *get_mpe_wwid(const struct _vector *mptable, const char *alias)
        return NULL;
 }
 
+static void
+free_pctable (vector pctable)
+{
+       int i;
+       struct pcentry *pce;
+
+       vector_foreach_slot(pctable, pce, i)
+               free(pce);
+
+       vector_free(pctable);
+}
+
 void
 free_hwe (struct hwentry * hwe)
 {
@@ -252,9 +264,6 @@ free_hwe (struct hwentry * hwe)
        if (hwe->revision)
                free(hwe->revision);
 
-       if (hwe->getuid)
-               free(hwe->getuid);
-
        if (hwe->uid_attribute)
                free(hwe->uid_attribute);
 
@@ -282,6 +291,9 @@ free_hwe (struct hwentry * hwe)
        if (hwe->bl_product)
                free(hwe->bl_product);
 
+       if (hwe->pctable)
+               free_pctable(hwe->pctable);
+
        free(hwe);
 }
 
@@ -312,9 +324,6 @@ free_mpe (struct mpentry * mpe)
        if (mpe->selector)
                free(mpe->selector);
 
-       if (mpe->getuid)
-               free(mpe->getuid);
-
        if (mpe->uid_attribute)
                free(mpe->uid_attribute);
 
@@ -363,6 +372,15 @@ alloc_hwe (void)
        return hwe;
 }
 
+struct pcentry *
+alloc_pce (void)
+{
+       struct pcentry *pce = (struct pcentry *)
+                               calloc(1, sizeof(struct pcentry));
+       pce->type = PCE_INVALID;
+       return pce;
+}
+
 static char *
 set_param_str(const char * str)
 {
@@ -387,24 +405,30 @@ set_param_str(const char * str)
 }
 
 #define merge_str(s) \
-       if (!dst->s && src->s) { \
-               if (!(dst->s = set_param_str(src->s))) \
-                       return 1; \
+       if (!dst->s && src->s && strlen(src->s)) { \
+               dst->s = src->s; \
+               src->s = NULL; \
        }
 
 #define merge_num(s) \
        if (!dst->s && src->s) \
                dst->s = src->s
 
+static void
+merge_pce(struct pcentry *dst, struct pcentry *src)
+{
+       merge_num(fast_io_fail);
+       merge_num(dev_loss);
+       merge_num(eh_deadline);
+}
 
-static int
+static void
 merge_hwe (struct hwentry * dst, struct hwentry * src)
 {
        char id[SCSI_VENDOR_SIZE+PATH_PRODUCT_SIZE];
        merge_str(vendor);
        merge_str(product);
        merge_str(revision);
-       merge_str(getuid);
        merge_str(uid_attribute);
        merge_str(features);
        merge_str(hwhandler);
@@ -449,18 +473,13 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)
        reconcile_features_with_options(id, &dst->features,
                                        &dst->no_path_retry,
                                        &dst->retain_hwhandler);
-       return 0;
 }
 
-static int
+static void
 merge_mpe(struct mpentry *dst, struct mpentry *src)
 {
-       if (!dst || !src)
-               return 1;
-
        merge_str(alias);
        merge_str(uid_attribute);
-       merge_str(getuid);
        merge_str(selector);
        merge_str(features);
        merge_str(prio_name);
@@ -499,8 +518,6 @@ merge_mpe(struct mpentry *dst, struct mpentry *src)
        merge_num(uid);
        merge_num(gid);
        merge_num(mode);
-
-       return 0;
 }
 
 void merge_mptable(vector mptable)
@@ -554,9 +571,6 @@ store_hwe (vector hwtable, struct hwentry * dhwe)
        if (dhwe->uid_attribute && !(hwe->uid_attribute = set_param_str(dhwe->uid_attribute)))
                goto out;
 
-       if (dhwe->getuid && !(hwe->getuid = set_param_str(dhwe->getuid)))
-               goto out;
-
        if (dhwe->features && !(hwe->features = set_param_str(dhwe->features)))
                goto out;
 
@@ -609,6 +623,51 @@ out:
 }
 
 static void
+validate_pctable(struct hwentry *ovr, int idx, const char *table_desc)
+{
+       struct pcentry *pce;
+
+       if (!ovr || !ovr->pctable)
+               return;
+
+       vector_foreach_slot_after(ovr->pctable, pce, idx) {
+               if (pce->type == PCE_INVALID) {
+                       condlog(0, "protocol section in %s missing type",
+                               table_desc);
+                       vector_del_slot(ovr->pctable, idx--);
+                       free(pce);
+               }
+       }
+
+       if (VECTOR_SIZE(ovr->pctable) == 0) {
+               vector_free(ovr->pctable);
+               ovr->pctable = NULL;
+       }
+}
+
+static void
+merge_pctable(struct hwentry *ovr)
+{
+       struct pcentry *pce1, *pce2;
+       int i, j;
+
+       if (!ovr || !ovr->pctable)
+               return;
+
+       vector_foreach_slot(ovr->pctable, pce1, i) {
+               j = i + 1;
+               vector_foreach_slot_after(ovr->pctable, pce2, j) {
+                       if (pce1->type != pce2->type)
+                               continue;
+                       merge_pce(pce2,pce1);
+                       vector_del_slot(ovr->pctable, i--);
+                       free(pce1);
+                       break;
+               }
+       }
+}
+
+static void
 factorize_hwtable (vector hw, int n, const char *table_desc)
 {
        struct hwentry *hwe1, *hwe2;
@@ -656,23 +715,22 @@ static struct config *alloc_config (void)
 
 static void _uninit_config(struct config *conf)
 {
+       void *ptr;
+       int i;
+
        if (!conf)
                conf = &__internal_config;
 
-       if (conf->multipath_dir)
-               free(conf->multipath_dir);
-
        if (conf->selector)
                free(conf->selector);
 
        if (conf->uid_attribute)
                free(conf->uid_attribute);
 
+       vector_foreach_slot(&conf->uid_attrs, ptr, i)
+               free(ptr);
        vector_reset(&conf->uid_attrs);
 
-       if (conf->getuid)
-               free(conf->getuid);
-
        if (conf->features)
                free(conf->features);
 
@@ -702,8 +760,6 @@ static void _uninit_config(struct config *conf)
        if (conf->checker_name)
                free(conf->checker_name);
 
-       if (conf->config_dir)
-               free(conf->config_dir);
        if (conf->enable_foreign)
                free(conf->enable_foreign);
 
@@ -756,6 +812,7 @@ process_config_dir(struct config *conf, char *dir)
        int i, n;
        char path[LINE_MAX];
        int old_hwtable_size;
+       int old_pctable_size = 0;
 
        if (dir[0] != '/') {
                condlog(1, "config_dir '%s' must be a fully qualified path",
@@ -782,11 +839,15 @@ process_config_dir(struct config *conf, char *dir)
                        continue;
 
                old_hwtable_size = VECTOR_SIZE(conf->hwtable);
+               old_pctable_size = conf->overrides ?
+                                  VECTOR_SIZE(conf->overrides->pctable) : 0;
                snprintf(path, LINE_MAX, "%s/%s", dir, namelist[i]->d_name);
                path[LINE_MAX-1] = '\0';
                process_file(conf, path);
                factorize_hwtable(conf->hwtable, old_hwtable_size,
                                  namelist[i]->d_name);
+               validate_pctable(conf->overrides, old_pctable_size,
+                                namelist[i]->d_name);
        }
        pthread_cleanup_pop(1);
 }
@@ -849,7 +910,6 @@ int _init_config (const char *file, struct config *conf)
        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;
        conf->checkint = CHECKINT_UNDEF;
@@ -894,13 +954,11 @@ int _init_config (const char *file, struct config *conf)
                        goto out;
                }
                factorize_hwtable(conf->hwtable, builtin_hwtable_size, file);
+               validate_pctable(conf->overrides, 0, file);
        }
 
        conf->processed_main_config = 1;
-       if (conf->config_dir == NULL)
-               conf->config_dir = set_default(DEFAULT_CONFIG_DIR);
-       if (conf->config_dir && conf->config_dir[0] != '\0')
-               process_config_dir(conf, conf->config_dir);
+       process_config_dir(conf, CONFIG_DIR);
 
        /*
         * fill the voids left in the config file
@@ -994,6 +1052,7 @@ int _init_config (const char *file, struct config *conf)
                        goto out;
        }
 
+       merge_pctable(conf->overrides);
        merge_mptable(conf->mptable);
        merge_blacklist(conf->blist_devnode);
        merge_blacklist(conf->blist_property);
@@ -1007,8 +1066,7 @@ int _init_config (const char *file, struct config *conf)
        if (conf->bindings_file == NULL)
                conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE);
 
-       if (!conf->multipath_dir || !conf->bindings_file ||
-           !conf->wwids_file || !conf->prkeys_file)
+       if (!conf->bindings_file || !conf->wwids_file || !conf->prkeys_file)
                goto out;
 
        libmp_verbosity = conf->verbosity;
@@ -1018,10 +1076,10 @@ out:
        return 1;
 }
 
-char *get_uid_attribute_by_attrs(struct config *conf,
-                                const char *path_dev)
+const char *get_uid_attribute_by_attrs(const struct config *conf,
+                                      const char *path_dev)
 {
-       vector uid_attrs = &conf->uid_attrs;
+       const struct _vector *uid_attrs = &conf->uid_attrs;
        int j;
        char *att, *col;
 
index c73389b..36d4015 100644 (file)
@@ -40,12 +40,19 @@ enum force_reload_types {
        FORCE_RELOAD_WEAK,
 };
 
+#define PCE_INVALID -1
+struct pcentry {
+       int type;
+       int fast_io_fail;
+       unsigned int dev_loss;
+       int eh_deadline;
+};
+
 struct hwentry {
        char * vendor;
        char * product;
        char * revision;
        char * uid_attribute;
-       char * getuid;
        char * features;
        char * hwhandler;
        char * selector;
@@ -85,13 +92,14 @@ struct hwentry {
        int vpd_vendor_id;
        int recheck_wwid;
        char * bl_product;
+
+       vector pctable;
 };
 
 struct mpentry {
        char * wwid;
        char * alias;
        char * uid_attribute;
-       char * getuid;
        char * selector;
        char * features;
 
@@ -189,11 +197,9 @@ struct config {
        unsigned int sequence_nr;
        int recheck_wwid;
 
-       char * multipath_dir;
        char * selector;
        struct _vector uid_attrs;
        char * uid_attribute;
-       char * getuid;
        char * features;
        char * hwhandler;
        char * bindings_file;
@@ -204,7 +210,6 @@ struct config {
        char * checker_name;
        char * alias_prefix;
        char * partition_delim;
-       char * config_dir;
        int prkey_source;
        int all_tg_pt;
        struct be64 reservation_key;
@@ -284,6 +289,7 @@ const char *get_mpe_wwid (const struct _vector *mptable, const char *alias);
 
 struct hwentry * alloc_hwe (void);
 struct mpentry * alloc_mpe (void);
+struct pcentry * alloc_pce (void);
 
 void free_hwe (struct hwentry * hwe);
 void free_hwtable (vector hwtable);
@@ -314,7 +320,7 @@ void libmp_put_multipath_config(void *);
 void put_multipath_config(void *);
 
 int parse_uid_attrs(char *uid_attrs, struct config *conf);
-char *get_uid_attribute_by_attrs(struct config *conf,
-                                const char *path_dev);
+const char *get_uid_attribute_by_attrs(const struct config *conf,
+                                      const char *path_dev);
 
 #endif
index eca11ba..09ae708 100644 (file)
@@ -329,9 +329,6 @@ int setup_map(struct multipath *mpp, char **params, struct vectors *vecs)
        select_mode(conf, mpp);
        select_uid(conf, mpp);
        select_gid(conf, mpp);
-       select_fast_io_fail(conf, mpp);
-       select_dev_loss(conf, mpp);
-       select_eh_deadline(conf, mpp);
        select_reservation_key(conf, mpp);
        select_deferred_remove(conf, mpp);
        select_marginal_path_err_sample_time(conf, mpp);
@@ -347,7 +344,7 @@ int setup_map(struct multipath *mpp, char **params, struct vectors *vecs)
        select_ghost_delay(conf, mpp);
        select_flush_on_last_del(conf, mpp);
 
-       sysfs_set_scsi_tmo(mpp, conf->checkint);
+       sysfs_set_scsi_tmo(conf, mpp);
        marginal_pathgroups = conf->marginal_pathgroups;
        pthread_cleanup_pop(1);
 
index 7d95413..7979f20 100644 (file)
@@ -11,7 +11,6 @@
 #define DEFAULT_NVME_UID_ATTRIBUTE     "ID_WWN"
 #define DEFAULT_DASD_UID_ATTRIBUTE     "ID_UID"
 #define DEFAULT_UDEVDIR                "/dev"
-#define DEFAULT_MULTIPATHDIR   "/" LIB_STRING "/multipath"
 #define DEFAULT_SELECTOR       "service-time 0"
 #define DEFAULT_ALIAS_PREFIX   "mpath"
 #define DEFAULT_FEATURES       "0"
@@ -69,7 +68,6 @@
 #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"
 #define MULTIPATH_SHM_BASE     "/dev/shm/multipath/"
 
 
index 2507f77..1748d25 100644 (file)
@@ -540,7 +540,7 @@ 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++) {
+       for (ro = mpp->force_readonly ? 1 : 0; ro <= 1; ro++) {
                int err;
 
                if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, ro,
@@ -682,8 +682,8 @@ int dm_get_map(const char *name, unsigned long long *size, char **outparams)
        r = DMP_NOT_FOUND;
        /* Fetch 1st target */
        if (dm_get_next_target(dmt, NULL, &start, &length,
-                              &target_type, &params) != NULL)
-               /* more than one target */
+                              &target_type, &params) != NULL || !params)
+               /* more than one target or not found target */
                goto out;
 
        if (size)
@@ -1715,6 +1715,16 @@ int dm_reassign_table(const char *name, char *old, char *new)
        do {
                next = dm_get_next_target(dmt, next, &start, &length,
                                          &target, &params);
+               if (!target || !params) {
+                       /*
+                        * We can't call dm_task_add_target() with
+                        * invalid parameters. But simply dropping this
+                        * target feels wrong, too. Abort and warn.
+                        */
+                       condlog(1, "%s: invalid target found in map %s",
+                               __func__, name);
+                       goto out_reload;
+               }
                buff = strdup(params);
                if (!buff) {
                        condlog(3, "%s: failed to replace target %s, "
index 2af9764..ad049cc 100644 (file)
@@ -116,32 +116,6 @@ set_str(vector strvec, void *ptr, const char *file, int line_nr)
 }
 
 static int
-set_dir(vector strvec, void *ptr, const char *file, int line_nr)
-{
-       char **str_ptr = (char **)ptr;
-       char *old_str = *str_ptr;
-       struct stat sb;
-
-       *str_ptr = set_value(strvec);
-       if (!*str_ptr) {
-               free(old_str);
-               return 1;
-       }
-       if ((*str_ptr)[0] != '/'){
-               condlog(1, "%s line %d, %s is not an absolute directory path. Ignoring", file, line_nr, *str_ptr);
-               *str_ptr = old_str;
-       } else {
-               if (stat(*str_ptr, &sb) == 0 && S_ISDIR(sb.st_mode))
-                       free(old_str);
-               else {
-                       condlog(1, "%s line %d, %s is not an existing directory. Ignoring", file, line_nr, *str_ptr);
-                       *str_ptr = old_str;
-               }
-       }
-       return 0;
-}
-
-static int
 set_path(vector strvec, void *ptr, const char *file, int line_nr)
 {
        char **str_ptr = (char **)ptr;
@@ -279,10 +253,30 @@ static int                                                                \
 def_ ## option ## _handler (struct config *conf, vector strvec,                \
                            const char *file, int line_nr)              \
 {                                                                      \
-       condlog(2, "%s line %d, \"" #option "\" is deprecated and will be disabled in a future release", file, line_nr);                                \
+       static bool warned;                                             \
+       if (!warned) {                                                  \
+               condlog(2, "%s line %d, \"" #option "\" is deprecated and will be disabled in a future release", file, line_nr); \
+               warned = true;                                          \
+       }                                                               \
        return function (strvec, &conf->option, file, line_nr);         \
 }
 
+static int deprecated_handler(struct config *conf, vector strvec, const char *file,
+                             int line_nr);
+
+#define declare_deprecated_handler(option)                             \
+static int                                                             \
+deprecated_ ## option ## _handler (struct config *conf, vector strvec, \
+                                  const char *file, int line_nr)       \
+{                                                                      \
+       static bool warned;                                             \
+       if (!warned) {                                                  \
+               condlog(1, "%s line %d: ignoring deprecated option \"" #option "\"", file, line_nr); \
+               warned = true;                                          \
+       }                                                               \
+       return deprecated_handler(conf, strvec, file, line_nr);         \
+}
+
 #define declare_def_range_handler(option, minval, maxval)                      \
 static int                                                             \
 def_ ## option ## _handler (struct config *conf, vector strvec,         \
@@ -413,6 +407,29 @@ snprint_mp_ ## option (struct config *conf, struct strbuf *buff,   \
        return function(buff, mpe->option);                             \
 }
 
+#define declare_pc_handler(option, function)                           \
+static int                                                             \
+pc_ ## option ## _handler (struct config *conf, vector strvec,         \
+                          const char *file, int line_nr)               \
+{                                                                      \
+       struct pcentry *pce;                                            \
+       if (!conf->overrides || !conf->overrides->pctable)              \
+               return 1;                                               \
+       pce = VECTOR_LAST_SLOT(conf->overrides->pctable);               \
+       if (!pce)                                                       \
+               return 1;                                               \
+       return function (strvec, &pce->option, file, line_nr);          \
+}
+
+#define declare_pc_snprint(option, function)                           \
+static int                                                             \
+snprint_pc_ ## option (struct config *conf, struct strbuf *buff,       \
+                      const void *data)                                \
+{                                                                      \
+       const struct pcentry *pce  = (const struct pcentry *)data;      \
+       return function(buff, pce->option);                             \
+}
+
 static int checkint_handler(struct config *conf, vector strvec,
                            const char *file, int line_nr)
 {
@@ -436,8 +453,7 @@ declare_def_snprint(verbosity, print_int)
 declare_def_handler(reassign_maps, set_yes_no)
 declare_def_snprint(reassign_maps, print_yes_no)
 
-declare_def_warn_handler(multipath_dir, set_dir)
-declare_def_snprint(multipath_dir, print_str)
+declare_deprecated_handler(multipath_dir)
 
 static int def_partition_delim_handler(struct config *conf, vector strvec,
                                       const char *file, int line_nr)
@@ -593,8 +609,13 @@ static int uid_attrs_handler(struct config *conf, vector strvec,
                             const char *file, int line_nr)
 {
        char *val;
+       void *ptr;
+       int i;
 
+       vector_foreach_slot(&conf->uid_attrs, ptr, i)
+               free(ptr);
        vector_reset(&conf->uid_attrs);
+
        val = set_value(strvec);
        if (!val)
                return 1;
@@ -614,13 +635,6 @@ declare_ovr_snprint(uid_attribute, print_str)
 declare_hw_handler(uid_attribute, set_str)
 declare_hw_snprint(uid_attribute, print_str)
 
-declare_def_handler(getuid, set_str)
-declare_def_snprint(getuid, print_str)
-declare_ovr_handler(getuid, set_str)
-declare_ovr_snprint(getuid, print_str)
-declare_hw_handler(getuid, set_str)
-declare_hw_snprint(getuid, print_str)
-
 declare_def_handler(prio_name, set_str)
 declare_def_snprint_defstr(prio_name, print_str, DEFAULT_PRIO)
 declare_ovr_handler(prio_name, set_str)
@@ -825,21 +839,8 @@ declare_def_handler(enable_foreign, set_str)
 declare_def_snprint_defstr(enable_foreign, print_str,
                           DEFAULT_ENABLE_FOREIGN)
 
-static int
-def_config_dir_handler(struct config *conf, vector strvec, const char *file,
-                      int line_nr)
-{
-       /* this is only valid in the main config file */
-       if (conf->processed_main_config) {
-               condlog(1, "%s line %d, config_dir option only valid in /etc/multipath.conf",
-                       file, line_nr);
-               return 0;
-       }
-       condlog(2, "%s line %d, \"config_dir\" is deprecated and will be disabled in a future release",
-               file, line_nr);
-       return set_path(strvec, &conf->config_dir, file, line_nr);
-}
-declare_def_snprint(config_dir, print_str)
+declare_deprecated_handler(config_dir)
+declare_deprecated_handler(pg_timeout)
 
 #define declare_def_attr_handler(option, function)                     \
 static int                                                             \
@@ -1037,6 +1038,8 @@ declare_ovr_handler(fast_io_fail, set_undef_off_zero)
 declare_ovr_snprint(fast_io_fail, print_undef_off_zero)
 declare_hw_handler(fast_io_fail, set_undef_off_zero)
 declare_hw_snprint(fast_io_fail, print_undef_off_zero)
+declare_pc_handler(fast_io_fail, set_undef_off_zero)
+declare_pc_snprint(fast_io_fail, print_undef_off_zero)
 
 static int
 set_dev_loss(vector strvec, void *ptr, const char *file, int line_nr)
@@ -1074,6 +1077,8 @@ declare_ovr_handler(dev_loss, set_dev_loss)
 declare_ovr_snprint(dev_loss, print_dev_loss)
 declare_hw_handler(dev_loss, set_dev_loss)
 declare_hw_snprint(dev_loss, print_dev_loss)
+declare_pc_handler(dev_loss, set_dev_loss)
+declare_pc_snprint(dev_loss, print_dev_loss)
 
 declare_def_handler(eh_deadline, set_undef_off_zero)
 declare_def_snprint(eh_deadline, print_undef_off_zero)
@@ -1081,6 +1086,8 @@ declare_ovr_handler(eh_deadline, set_undef_off_zero)
 declare_ovr_snprint(eh_deadline, print_undef_off_zero)
 declare_hw_handler(eh_deadline, set_undef_off_zero)
 declare_hw_snprint(eh_deadline, print_undef_off_zero)
+declare_pc_handler(eh_deadline, set_undef_off_zero)
+declare_pc_snprint(eh_deadline, print_undef_off_zero)
 
 static int
 set_pgpolicy(vector strvec, void *ptr, const char *file, int line_nr)
@@ -1888,6 +1895,69 @@ declare_mp_snprint(wwid, print_str)
 declare_mp_handler(alias, set_str_noslash)
 declare_mp_snprint(alias, print_str)
 
+
+static int
+protocol_handler(struct config *conf, vector strvec, const char *file,
+               int line_nr)
+{
+       struct pcentry *pce;
+
+       if (!conf->overrides)
+               return 1;
+
+       if (!conf->overrides->pctable &&
+           !(conf->overrides->pctable = vector_alloc()))
+               return 1;
+
+       if (!(pce = alloc_pce()))
+               return 1;
+
+       if (!vector_alloc_slot(conf->overrides->pctable)) {
+               free(pce);
+               return 1;
+       }
+       vector_set_slot(conf->overrides->pctable, pce);
+
+       return 0;
+}
+
+static int
+set_protocol_type(vector strvec, void *ptr, const char *file, int line_nr)
+{
+       int *int_ptr = (int *)ptr;
+       char *buff;
+       int i;
+
+       buff = set_value(strvec);
+
+       if (!buff)
+               return 1;
+
+       for (i = 0; i <= LAST_BUS_PROTOCOL_ID; i++) {
+               if (protocol_name[i] && !strcmp(buff, protocol_name[i])) {
+                       *int_ptr = i;
+                       break;
+               }
+       }
+       if (i > LAST_BUS_PROTOCOL_ID)
+               condlog(1, "%s line %d, invalid value for type: \"%s\"",
+                       file, line_nr, buff);
+
+       free(buff);
+       return 0;
+}
+
+static int
+print_protocol_type(struct strbuf *buff, int type)
+{
+       if (type < 0)
+               return 0;
+       return append_strbuf_quoted(buff, protocol_name[type]);
+}
+
+declare_pc_handler(type, set_protocol_type)
+declare_pc_snprint(type, print_protocol_type)
+
 /*
  * deprecated handlers
  */
@@ -1913,7 +1983,7 @@ snprint_deprecated (struct config *conf, struct strbuf *buff, const void * data)
        return 0;
 }
 
-#define __deprecated
+declare_deprecated_handler(getuid_callout)
 
 /*
  * If you add or remove a keyword also update multipath/multipath.conf.5
@@ -1926,12 +1996,12 @@ init_keywords(vector keywords)
        install_keyword("polling_interval", &checkint_handler, &snprint_def_checkint);
        install_keyword("max_polling_interval", &def_max_checkint_handler, &snprint_def_max_checkint);
        install_keyword("reassign_maps", &def_reassign_maps_handler, &snprint_def_reassign_maps);
-       install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir);
+       install_keyword("multipath_dir", &deprecated_multipath_dir_handler, &snprint_deprecated);
        install_keyword("path_selector", &def_selector_handler, &snprint_def_selector);
        install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy);
        install_keyword("uid_attrs", &uid_attrs_handler, &snprint_uid_attrs);
        install_keyword("uid_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute);
-       install_keyword("getuid_callout", &def_getuid_handler, &snprint_def_getuid);
+       install_keyword("getuid_callout", &deprecated_getuid_callout_handler, &snprint_deprecated);
        install_keyword("prio", &def_prio_name_handler, &snprint_def_prio_name);
        install_keyword("prio_args", &def_prio_args_handler, &snprint_def_prio_args);
        install_keyword("features", &def_features_handler, &snprint_def_features);
@@ -1947,7 +2017,7 @@ init_keywords(vector keywords)
        install_keyword("queue_without_daemon", &def_queue_without_daemon_handler, &snprint_def_queue_without_daemon);
        install_keyword("checker_timeout", &def_checker_timeout_handler, &snprint_def_checker_timeout);
        install_keyword("allow_usb_devices", &def_allow_usb_devices_handler, &snprint_def_allow_usb_devices);
-       install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated);
+       install_keyword("pg_timeout", &deprecated_pg_timeout_handler, &snprint_deprecated);
        install_keyword("flush_on_last_del", &def_flush_on_last_del_handler, &snprint_def_flush_on_last_del);
        install_keyword("user_friendly_names", &def_user_friendly_names_handler, &snprint_def_user_friendly_names);
        install_keyword("mode", &def_mode_handler, &snprint_def_mode);
@@ -1969,7 +2039,7 @@ init_keywords(vector keywords)
        install_keyword("strict_timing", &def_strict_timing_handler, &snprint_def_strict_timing);
        install_keyword("deferred_remove", &def_deferred_remove_handler, &snprint_def_deferred_remove);
        install_keyword("partition_delimiter", &def_partition_delim_handler, &snprint_def_partition_delim);
-       install_keyword("config_dir", &def_config_dir_handler, &snprint_def_config_dir);
+       install_keyword("config_dir", &deprecated_config_dir_handler, &snprint_deprecated);
        install_keyword("delay_watch_checks", &def_delay_watch_checks_handler, &snprint_def_delay_watch_checks);
        install_keyword("delay_wait_checks", &def_delay_wait_checks_handler, &snprint_def_delay_wait_checks);
        install_keyword("san_path_err_threshold", &def_san_path_err_threshold_handler, &snprint_def_san_path_err_threshold);
@@ -1997,12 +2067,6 @@ init_keywords(vector keywords)
                        &snprint_def_enable_foreign);
        install_keyword("marginal_pathgroups", &def_marginal_pathgroups_handler, &snprint_def_marginal_pathgroups);
        install_keyword("recheck_wwid", &def_recheck_wwid_handler, &snprint_def_recheck_wwid);
-       __deprecated install_keyword("default_selector", &def_selector_handler, NULL);
-       __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL);
-       __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL);
-       __deprecated install_keyword("default_getuid_callout", &def_getuid_handler, NULL);
-       __deprecated install_keyword("default_features", &def_features_handler, NULL);
-       __deprecated install_keyword("default_path_checker", &def_checker_name_handler, NULL);
 
        install_keyword_root("blacklist", &blacklist_handler);
        install_keyword_multi("devnode", &ble_blist_devnode_handler, &snprint_ble_simple);
@@ -2025,16 +2089,6 @@ init_keywords(vector keywords)
        install_keyword("product", &ble_elist_device_product_handler, &snprint_bled_product);
        install_sublevel_end();
 
-#if 0
-       __deprecated install_keyword_root("devnode_blacklist", &blacklist_handler);
-       __deprecated install_keyword("devnode", &ble_devnode_handler, &snprint_ble_simple);
-       __deprecated install_keyword("wwid", &ble_wwid_handler, &snprint_ble_simple);
-       __deprecated install_keyword("device", &ble_device_handler, NULL);
-       __deprecated install_sublevel();
-       __deprecated install_keyword("vendor", &ble_vendor_handler, &snprint_bled_vendor);
-       __deprecated install_keyword("product", &ble_product_handler, &snprint_bled_product);
-       __deprecated install_sublevel_end();
-#endif
 /*
  * If you add or remove a "device subsection" keyword also update
  * multipath/multipath.conf.5 and the TEMPLATE in libmultipath/hwtable.c
@@ -2048,7 +2102,7 @@ init_keywords(vector keywords)
        install_keyword("product_blacklist", &hw_bl_product_handler, &snprint_hw_bl_product);
        install_keyword("path_grouping_policy", &hw_pgpolicy_handler, &snprint_hw_pgpolicy);
        install_keyword("uid_attribute", &hw_uid_attribute_handler, &snprint_hw_uid_attribute);
-       install_keyword("getuid_callout", &hw_getuid_handler, &snprint_hw_getuid);
+       install_keyword("getuid_callout", &deprecated_getuid_callout_handler, &snprint_deprecated);
        install_keyword("path_selector", &hw_selector_handler, &snprint_hw_selector);
        install_keyword("path_checker", &hw_checker_name_handler, &snprint_hw_checker_name);
        install_keyword("checker", &hw_checker_name_handler, NULL);
@@ -2092,7 +2146,7 @@ init_keywords(vector keywords)
        install_keyword_root("overrides", &overrides_handler);
        install_keyword("path_grouping_policy", &ovr_pgpolicy_handler, &snprint_ovr_pgpolicy);
        install_keyword("uid_attribute", &ovr_uid_attribute_handler, &snprint_ovr_uid_attribute);
-       install_keyword("getuid_callout", &ovr_getuid_handler, &snprint_ovr_getuid);
+       install_keyword("getuid_callout", &deprecated_getuid_callout_handler, &snprint_deprecated);
        install_keyword("path_selector", &ovr_selector_handler, &snprint_ovr_selector);
        install_keyword("path_checker", &ovr_checker_name_handler, &snprint_ovr_checker_name);
        install_keyword("checker", &ovr_checker_name_handler, NULL);
@@ -2129,6 +2183,13 @@ init_keywords(vector keywords)
        install_keyword("ghost_delay", &ovr_ghost_delay_handler, &snprint_ovr_ghost_delay);
        install_keyword("all_tg_pt", &ovr_all_tg_pt_handler, &snprint_ovr_all_tg_pt);
        install_keyword("recheck_wwid", &ovr_recheck_wwid_handler, &snprint_ovr_recheck_wwid);
+       install_keyword_multi("protocol", &protocol_handler, NULL);
+       install_sublevel();
+       install_keyword("type", &pc_type_handler, &snprint_pc_type);
+       install_keyword("fast_io_fail_tmo", &pc_fast_io_fail_handler, &snprint_pc_fast_io_fail);
+       install_keyword("dev_loss_tmo", &pc_dev_loss_handler, &snprint_pc_dev_loss);
+       install_keyword("eh_deadline", &pc_eh_deadline_handler, &snprint_pc_eh_deadline);
+       install_sublevel_end();
 
        install_keyword_root("multipaths", &multipaths_handler);
        install_keyword_multi("multipath", &multipath_handler, NULL);
index b969fba..0d8a558 100644 (file)
@@ -21,7 +21,6 @@
 #include "structs.h"
 #include "config.h"
 #include "blacklist.h"
-#include "callout.h"
 #include "debug.h"
 #include "propsel.h"
 #include "sg_include.h"
@@ -598,13 +597,13 @@ sysfs_get_asymmetric_access_state(struct path *pp, char *buff, int buflen)
 }
 
 static int
-sysfs_set_eh_deadline(struct multipath *mpp, struct path *pp)
+sysfs_set_eh_deadline(struct path *pp)
 {
        struct udev_device *hostdev;
        char host_name[HOST_NAME_LEN], value[16];
        int ret, len;
 
-       if (mpp->eh_deadline == EH_DEADLINE_UNSET)
+       if (pp->eh_deadline == EH_DEADLINE_UNSET)
                return 0;
 
        sprintf(host_name, "host%d", pp->sg_id.host_no);
@@ -613,12 +612,12 @@ sysfs_set_eh_deadline(struct multipath *mpp, struct path *pp)
        if (!hostdev)
                return 1;
 
-       if (mpp->eh_deadline == EH_DEADLINE_OFF)
+       if (pp->eh_deadline == EH_DEADLINE_OFF)
                len = sprintf(value, "off");
-       else if (mpp->eh_deadline == EH_DEADLINE_ZERO)
+       else if (pp->eh_deadline == EH_DEADLINE_ZERO)
                len = sprintf(value, "0");
        else
-               len = sprintf(value, "%d", mpp->eh_deadline);
+               len = sprintf(value, "%d", pp->eh_deadline);
 
        ret = sysfs_attr_set_value(hostdev, "eh_deadline",
                                   value, len + 1);
@@ -642,8 +641,8 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
        unsigned int tmo;
        int ret;
 
-       if (mpp->dev_loss == DEV_LOSS_TMO_UNSET &&
-           mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
+       if (pp->dev_loss == DEV_LOSS_TMO_UNSET &&
+           pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
                return;
 
        sprintf(rport_id, "rport-%d:%d-%d",
@@ -685,14 +684,14 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
         * then set fast_io_fail, and _then_ set dev_loss_tmo
         * to the correct value.
         */
-       if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET &&
-           mpp->fast_io_fail != MP_FAST_IO_FAIL_ZERO &&
-           mpp->fast_io_fail != MP_FAST_IO_FAIL_OFF) {
+       if (pp->fast_io_fail != MP_FAST_IO_FAIL_UNSET &&
+           pp->fast_io_fail != MP_FAST_IO_FAIL_ZERO &&
+           pp->fast_io_fail != MP_FAST_IO_FAIL_OFF) {
                /* Check if we need to temporarily increase dev_loss_tmo */
-               if ((unsigned int)mpp->fast_io_fail >= tmo) {
+               if ((unsigned int)pp->fast_io_fail >= tmo) {
                        /* Increase dev_loss_tmo temporarily */
                        snprintf(value, sizeof(value), "%u",
-                                (unsigned int)mpp->fast_io_fail + 1);
+                                (unsigned int)pp->fast_io_fail + 1);
                        ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
                                                   value, strlen(value));
                        if (ret <= 0) {
@@ -706,20 +705,20 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
                                goto out;
                        }
                }
-       } else if (mpp->dev_loss > DEFAULT_DEV_LOSS_TMO &&
-               mpp->no_path_retry != NO_PATH_RETRY_QUEUE) {
+       } else if (pp->dev_loss > DEFAULT_DEV_LOSS_TMO &&
+                  mpp->no_path_retry != NO_PATH_RETRY_QUEUE) {
                condlog(2, "%s: limiting dev_loss_tmo to %d, since "
                        "fast_io_fail is not set",
                        rport_id, DEFAULT_DEV_LOSS_TMO);
-               mpp->dev_loss = DEFAULT_DEV_LOSS_TMO;
+               pp->dev_loss = DEFAULT_DEV_LOSS_TMO;
        }
-       if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
-               if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
+       if (pp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
+               if (pp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
                        sprintf(value, "off");
-               else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO)
+               else if (pp->fast_io_fail == MP_FAST_IO_FAIL_ZERO)
                        sprintf(value, "0");
                else
-                       snprintf(value, 16, "%u", mpp->fast_io_fail);
+                       snprintf(value, 16, "%u", pp->fast_io_fail);
                ret = sysfs_attr_set_value(rport_dev, "fast_io_fail_tmo",
                                           value, strlen(value));
                if (ret <= 0) {
@@ -730,8 +729,8 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
                                        rport_id, value, -ret);
                }
        }
-       if (mpp->dev_loss != DEV_LOSS_TMO_UNSET) {
-               snprintf(value, 16, "%u", mpp->dev_loss);
+       if (pp->dev_loss != DEV_LOSS_TMO_UNSET) {
+               snprintf(value, 16, "%u", pp->dev_loss);
                ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
                                           value, strlen(value));
                if (ret <= 0) {
@@ -747,15 +746,15 @@ out:
 }
 
 static void
-sysfs_set_session_tmo(struct multipath *mpp, struct path *pp)
+sysfs_set_session_tmo(struct path *pp)
 {
        struct udev_device *session_dev = NULL;
        char session_id[64];
        char value[11];
 
-       if (mpp->dev_loss != DEV_LOSS_TMO_UNSET)
+       if (pp->dev_loss != DEV_LOSS_TMO_UNSET)
                condlog(3, "%s: ignoring dev_loss_tmo on iSCSI", pp->dev);
-       if (mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
+       if (pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
                return;
 
        sprintf(session_id, "session%d", pp->sg_id.transport_id);
@@ -769,15 +768,15 @@ sysfs_set_session_tmo(struct multipath *mpp, struct path *pp)
        condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
                pp->sg_id.channel, pp->sg_id.scsi_id, session_id);
 
-       if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
-               if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF) {
+       if (pp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
+               if (pp->fast_io_fail == MP_FAST_IO_FAIL_OFF) {
                        condlog(3, "%s: can't switch off fast_io_fail_tmo "
                                "on iSCSI", pp->dev);
-               } else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) {
+               } else if (pp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) {
                        condlog(3, "%s: can't set fast_io_fail_tmo to '0'"
                                "on iSCSI", pp->dev);
                } else {
-                       snprintf(value, 11, "%u", mpp->fast_io_fail);
+                       snprintf(value, 11, "%u", pp->fast_io_fail);
                        if (sysfs_attr_set_value(session_dev, "recovery_tmo",
                                                 value, strlen(value)) <= 0) {
                                condlog(3, "%s: Failed to set recovery_tmo, "
@@ -790,14 +789,14 @@ sysfs_set_session_tmo(struct multipath *mpp, struct path *pp)
 }
 
 static void
-sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp)
+sysfs_set_nexus_loss_tmo(struct path *pp)
 {
        struct udev_device *parent, *sas_dev = NULL;
        const char *end_dev_id = NULL;
        char value[11];
        static const char ed_str[] = "end_device-";
 
-       if (!pp->udev || mpp->dev_loss == DEV_LOSS_TMO_UNSET)
+       if (!pp->udev || pp->dev_loss == DEV_LOSS_TMO_UNSET)
                return;
 
        for (parent = udev_device_get_parent(pp->udev);
@@ -824,8 +823,8 @@ sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp)
        condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
                pp->sg_id.channel, pp->sg_id.scsi_id, end_dev_id);
 
-       if (mpp->dev_loss != DEV_LOSS_TMO_UNSET) {
-               snprintf(value, 11, "%u", mpp->dev_loss);
+       if (pp->dev_loss != DEV_LOSS_TMO_UNSET) {
+               snprintf(value, 11, "%u", pp->dev_loss);
                if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout",
                                         value, strlen(value)) <= 0)
                        condlog(3, "%s: failed to update "
@@ -836,76 +835,98 @@ sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp)
        return;
 }
 
+static void
+scsi_tmo_error_msg(struct path *pp)
+{
+       STATIC_BITFIELD(bf, LAST_BUS_PROTOCOL_ID + 1);
+       STRBUF_ON_STACK(proto_buf);
+       unsigned int proto_id = bus_protocol_id(pp);
+
+       snprint_path_protocol(&proto_buf, pp);
+       condlog(2, "%s: setting scsi timeouts is unsupported for protocol %s",
+               pp->dev, get_strbuf_str(&proto_buf));
+       set_bit_in_bitfield(proto_id, bf);
+}
+
 int
-sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint)
+sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp)
 {
        struct path *pp;
        int i;
-       unsigned int dev_loss_tmo = mpp->dev_loss;
-       struct path *err_path = NULL;
-       STATIC_BITFIELD(bf, LAST_BUS_PROTOCOL_ID + 1);
+       unsigned int min_dev_loss = 0;
+       bool warn_dev_loss = false;
+       bool warn_fast_io_fail = false;
 
        if (mpp->no_path_retry > 0) {
                uint64_t no_path_retry_tmo =
-                       (uint64_t)mpp->no_path_retry * checkint;
+                       (uint64_t)mpp->no_path_retry * conf->checkint;
 
                if (no_path_retry_tmo > MAX_DEV_LOSS_TMO)
-                       no_path_retry_tmo = MAX_DEV_LOSS_TMO;
-               if (no_path_retry_tmo > dev_loss_tmo)
-                       dev_loss_tmo = no_path_retry_tmo;
-       } else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE) {
-               dev_loss_tmo = MAX_DEV_LOSS_TMO;
-       }
-       if (mpp->dev_loss != DEV_LOSS_TMO_UNSET &&
-           mpp->dev_loss != dev_loss_tmo) {
-               condlog(2, "%s: Using dev_loss_tmo=%u instead of %u because of no_path_retry setting",
-                       mpp->alias, dev_loss_tmo, mpp->dev_loss);
-               mpp->dev_loss = dev_loss_tmo;
-       }
-       if (mpp->dev_loss != DEV_LOSS_TMO_UNSET &&
-           mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET &&
-           (unsigned int)mpp->fast_io_fail >= mpp->dev_loss) {
-               condlog(3, "%s: turning off fast_io_fail (%d is not smaller than dev_loss_tmo)",
-                       mpp->alias, mpp->fast_io_fail);
-               mpp->fast_io_fail = MP_FAST_IO_FAIL_OFF;
-       }
-       if (mpp->dev_loss == DEV_LOSS_TMO_UNSET &&
-           mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET &&
-           mpp->eh_deadline == EH_DEADLINE_UNSET)
-               return 0;
+                       min_dev_loss = MAX_DEV_LOSS_TMO;
+               else
+                       min_dev_loss = no_path_retry_tmo;
+       } else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE)
+               min_dev_loss = MAX_DEV_LOSS_TMO;
 
        vector_foreach_slot(mpp->paths, pp, i) {
+               select_fast_io_fail(conf, pp);
+               select_dev_loss(conf, pp);
+               select_eh_deadline(conf, pp);
+
+               if (pp->dev_loss == DEV_LOSS_TMO_UNSET &&
+                   pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET &&
+                   pp->eh_deadline == EH_DEADLINE_UNSET)
+                       continue;
+
                if (pp->bus != SYSFS_BUS_SCSI) {
-                       if (!err_path)
-                               err_path = pp;
+                       scsi_tmo_error_msg(pp);
                        continue;
                }
+               sysfs_set_eh_deadline(pp);
+
+               if (pp->dev_loss == DEV_LOSS_TMO_UNSET &&
+                   pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
+                       continue;
+
+               if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP &&
+                   pp->sg_id.proto_id != SCSI_PROTOCOL_ISCSI &&
+                   pp->sg_id.proto_id != SCSI_PROTOCOL_SAS) {
+                       scsi_tmo_error_msg(pp);
+                       continue;
+               }
+
+               if (pp->dev_loss != DEV_LOSS_TMO_UNSET &&
+                   pp->dev_loss < min_dev_loss) {
+                       warn_dev_loss = true;
+                       pp->dev_loss = min_dev_loss;
+               }
+               if (pp->dev_loss != DEV_LOSS_TMO_UNSET &&
+                   pp->fast_io_fail > 0 &&
+                   (unsigned int)pp->fast_io_fail >= pp->dev_loss) {
+                       warn_fast_io_fail = true;
+                       pp->fast_io_fail = MP_FAST_IO_FAIL_OFF;
+               }
 
                switch (pp->sg_id.proto_id) {
                case SCSI_PROTOCOL_FCP:
                        sysfs_set_rport_tmo(mpp, pp);
                        break;
                case SCSI_PROTOCOL_ISCSI:
-                       sysfs_set_session_tmo(mpp, pp);
+                       sysfs_set_session_tmo(pp);
                        break;
                case SCSI_PROTOCOL_SAS:
-                       sysfs_set_nexus_loss_tmo(mpp, pp);
+                       sysfs_set_nexus_loss_tmo(pp);
                        break;
                default:
-                       if (!err_path)
-                               err_path = pp;
+                       break;
                }
-               sysfs_set_eh_deadline(mpp, pp);
-       }
-
-       if (err_path && !is_bit_set_in_bitfield(bus_protocol_id(pp), bf)) {
-               STRBUF_ON_STACK(proto_buf);
-
-               snprint_path_protocol(&proto_buf, err_path);
-               condlog(2, "%s: setting dev_loss_tmo is unsupported for protocol %s",
-                       mpp->alias, get_strbuf_str(&proto_buf));
-               set_bit_in_bitfield(bus_protocol_id(pp), bf);
        }
+       if (warn_dev_loss)
+               condlog(2, "%s: Raising dev_loss_tmo to %u because of no_path_retry setting",
+                       mpp->alias, min_dev_loss);
+       if (warn_fast_io_fail)
+               condlog(3, "%s: turning off fast_io_fail (not smaller than dev_loss_tmo)",
+                       mpp->alias);
        return 0;
 }
 
@@ -2044,7 +2065,7 @@ fix_broken_nvme_wwid(struct path *pp, const char *value, size_t size)
 }
 
 static int
-get_udev_uid(struct path * pp, char *uid_attribute, struct udev_device *udev)
+get_udev_uid(struct path * pp, const char *uid_attribute, struct udev_device *udev)
 {
        ssize_t len;
        const char *value;
@@ -2188,7 +2209,7 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev,
        int used_fallback = 0;
        size_t i;
 
-       if (!pp->uid_attribute && !pp->getuid) {
+       if (!pp->uid_attribute) {
                conf = get_multipath_config();
                pthread_cleanup_push(put_multipath_config, conf);
                select_getuid(conf, pp);
@@ -2197,24 +2218,7 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev,
        }
 
        memset(pp->wwid, 0, WWID_SIZE);
-       if (pp->getuid) {
-               char buff[CALLOUT_MAX_SIZE];
-
-               /* Use 'getuid' callout, deprecated */
-               condlog(1, "%s: using deprecated getuid callout", pp->dev);
-               if (path_state != PATH_UP) {
-                       condlog(3, "%s: path inaccessible", pp->dev);
-                       len = -EWOULDBLOCK;
-               } else if (apply_format(pp->getuid, &buff[0], pp)) {
-                       condlog(0, "error formatting uid callout command");
-                       len = -EINVAL;
-               } else if (execute_program(buff, pp->wwid, WWID_SIZE)) {
-                       condlog(3, "error calling out %s", buff);
-                       len = -EIO;
-               } else
-                       len = strlen(pp->wwid);
-               origin = "callout";
-       } else if (pp->uid_attribute) {
+       if (pp->uid_attribute) {
                /* if the uid_attribute is an empty string skip udev checking */
                bool check_uid_attr = udev && *pp->uid_attribute;
 
index 466af34..acd5179 100644 (file)
@@ -42,7 +42,7 @@ int alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice,
 int store_pathinfo (vector pathvec, struct config *conf,
                    struct udev_device *udevice, int flag,
                    struct path **pp_ptr);
-int sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint);
+int sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp);
 int sysfs_get_timeout(const struct path *pp, unsigned int *timeout);
 int sysfs_get_iscsi_ip_address(const struct path *pp, char *ip_address);
 int sysfs_get_host_adapter_name(const struct path *pp,
index e80eb3e..d01a5ef 100644 (file)
@@ -37,6 +37,7 @@
 #include "strbuf.h"
 
 static vector foreigns;
+static const char *const foreign_dir = MULTIPATH_DIR;
 
 /* This protects vector foreigns */
 static pthread_rwlock_t foreign_lock = PTHREAD_RWLOCK_INITIALIZER;
@@ -125,7 +126,7 @@ static void free_pre(void *arg)
        }
 }
 
-static int _init_foreign(const char *multipath_dir, const char *enable)
+static int _init_foreign(const char *enable)
 {
        char pathbuf[PATH_MAX];
        struct dirent **di;
@@ -153,7 +154,7 @@ static int _init_foreign(const char *multipath_dir, const char *enable)
                }
        }
 
-       r = scandir(multipath_dir, &di, select_foreign_libs, alphasort);
+       r = scandir(foreign_dir, &di, select_foreign_libs, alphasort);
 
        if (r == 0) {
                condlog(3, "%s: no foreign multipath libraries found",
@@ -208,7 +209,7 @@ static int _init_foreign(const char *multipath_dir, const char *enable)
                                        __func__, ret, fgn->name);
                }
 
-               snprintf(pathbuf, sizeof(pathbuf), "%s/%s", multipath_dir, fn);
+               snprintf(pathbuf, sizeof(pathbuf), "%s/%s", foreign_dir, fn);
                fgn->handle = dlopen(pathbuf, RTLD_NOW|RTLD_LOCAL);
                msg = dlerror();
                if (fgn->handle == NULL) {
@@ -257,7 +258,7 @@ out_free_pre:
        return r;
 }
 
-int init_foreign(const char *multipath_dir, const char *enable)
+int init_foreign(const char *enable)
 {
        int ret;
 
@@ -270,7 +271,7 @@ int init_foreign(const char *multipath_dir, const char *enable)
        }
 
        pthread_cleanup_push(unlock_foreigns, NULL);
-       ret = _init_foreign(multipath_dir, enable);
+       ret = _init_foreign(enable);
        pthread_cleanup_pop(1);
 
        return ret;
index f547c14..b9cdb36 100644 (file)
@@ -198,7 +198,7 @@ struct foreign {
  * @param enable: regex to match foreign library name ("*" above) against
  * @returns: 0 on success, negative value on failure.
  */
-int init_foreign(const char *multipath_dir, const char *enable);
+int init_foreign(const char *enable);
 
 /**
  * cleanup_foreign(dir)
index f447a1c..42cea4d 100644 (file)
@@ -4,7 +4,8 @@
 TOPDIR=../..
 include ../../Makefile.inc
 
-CFLAGS += $(LIB_CFLAGS) -I.. -I$(nvmedir)
+CPPFLAGS += -I.. -I$(nvmedir)
+CFLAGS += $(LIB_CFLAGS)
 LDFLAGS += -L..
 LIBDEPS = -lmultipath -ludev -lpthread -lrt
 
index 838e116..52ca56d 100644 (file)
@@ -23,7 +23,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdbool.h>
-#include <libudev.h>
 #include <pthread.h>
 #include <limits.h>
 #include <dirent.h>
index bd15710..513fa67 100644 (file)
@@ -90,7 +90,8 @@ static struct hwentry default_hw[] = {
                .product       = ".*",
                .uid_attribute = DEFAULT_NVME_UID_ATTRIBUTE,
                .checker_name  = NONE,
-               .retain_hwhandler = RETAIN_HWHANDLER_OFF,
+               .pgpolicy      = GROUP_BY_PRIO,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
        },
        /*
         * Apple
@@ -120,6 +121,12 @@ static struct hwentry default_hw[] = {
                .vpd_vendor_id = VPD_VP_HP3PAR,
        },
        {
+               /* Alletra 9000 NVMe */
+               .vendor        = "NVME",
+               .product       = "HPE Alletra",
+               .no_path_retry = NO_PATH_RETRY_QUEUE,
+       },
+       {
                /* RA8000 / ESA12000 */
                .vendor        = "DEC",
                .product       = "HSG80",
@@ -182,8 +189,8 @@ static struct hwentry default_hw[] = {
        },
        {
                /* MSA 1040, 1050, 1060, 2040, 2050 and 2060 families */
-               .vendor        = "HP",
-               .product       = "MSA [12]0[456]0 SA[NS]",
+               .vendor        = "(HP|HPE)",
+               .product       = "MSA [12]0[456]0 (SAN|SAS|FC|iSCSI)",
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .no_path_retry = 18,
@@ -192,7 +199,7 @@ static struct hwentry default_hw[] = {
        {
                /* SAN Virtualization Services Platform */
                .vendor        = "HP",
-               .product       = "HSVX700",
+               .product       = "(HSVX700|HSVX740)",
                .hwhandler     = "1 alua",
                .pgpolicy      = GROUP_BY_PRIO,
                .pgfailback    = -FAILBACK_IMMEDIATE,
@@ -380,12 +387,6 @@ static struct hwentry default_hw[] = {
                .no_path_retry = 30,
        },
        {
-               /* EMC PowerMax NVMe */
-               .vendor        = "NVME",
-               .product       = "^EMC PowerMax_",
-               .pgpolicy      = MULTIBUS,
-       },
-       {
                /* PowerStore */
                .vendor        = "DellEMC",
                .product       = "PowerStore",
@@ -397,9 +398,15 @@ static struct hwentry default_hw[] = {
                .fast_io_fail  = 15,
        },
        {
-               /* PowerVault ME4 */
+               /* PowerStore NVMe */
+               .vendor        = ".*",
+               .product       = "dellemc-powerstore",
+               .no_path_retry = 3,
+       },
+       {
+               /* PowerVault ME 4/5 families */
                .vendor        = "DellEMC",
-               .product       = "ME4",
+               .product       = "^ME",
                .pgpolicy      = GROUP_BY_PRIO,
                .prio_name     = PRIO_ALUA,
                .hwhandler     = "1 alua",
@@ -467,8 +474,8 @@ static struct hwentry default_hw[] = {
         * Maintainer: Matthias Rudolph <Matthias.Rudolph@hitachivantara.com>
         */
        {
-               /* USP-V, HUS VM, VSP, VSP G1X00 and VSP GX00 families / HP XP */
-               .vendor        = "(HITACHI|HP)",
+               /* USP-V, HUS VM, VSP, VSP G1X00 and VSP GX00 families / HPE XP */
+               .vendor        = "(HITACHI|HP|HPE)",
                .product       = "^OPEN-",
                .pgpolicy      = MULTIBUS,
        },
@@ -665,7 +672,7 @@ static struct hwentry default_hw[] = {
        },
        {
                // Storwize V5000 and V7000 lines / SAN Volume Controller (SVC) / Flex System V7000 /
-               // FlashSystem V840/V9000/5000/5100/5200/7200/9100/9200/9200R
+               // FlashSystem V840/V9000/5000/5100/5200/7200/7300/9100/9200/9200R/9500
                .vendor        = "IBM",
                .product       = "^2145",
                .no_path_retry = NO_PATH_RETRY_QUEUE,
@@ -674,6 +681,12 @@ static struct hwentry default_hw[] = {
                .prio_name     = PRIO_ALUA,
        },
        {
+               /* FlashSystem(Storwize/SVC) NVMe */
+               .vendor        = "NVME",
+               .product       = "IBM[ ]+2145",
+               .no_path_retry = NO_PATH_RETRY_QUEUE,
+       },
+       {
                /* PAV DASD ECKD */
                .vendor        = "IBM",
                .product       = "S/390 DASD ECKD",
@@ -727,6 +740,12 @@ static struct hwentry default_hw[] = {
                .pgpolicy      = MULTIBUS,
        },
        {
+               /* FlashSystem(RamSan) NVMe */
+               .vendor        = "NVMe",
+               .product       = "FlashSystem",
+               .no_path_retry = NO_PATH_RETRY_FAIL,
+       },
+       {
                /* (DDN) DCS9900, SONAS 2851-DR1 */
                .vendor        = "IBM",
                .product       = "^(DCS9900|2851)",
@@ -832,14 +851,9 @@ static struct hwentry default_hw[] = {
                .no_path_retry = 24,
        },
        {
-               /*
-                * NVMe-FC namespace devices: MULTIBUS, queueing preferred
-                *
-                * The hwtable is searched backwards, so place this after "Generic NVMe"
-                */
+               /* ONTAP NVMe */
                .vendor        = "NVME",
                .product       = "^NetApp ONTAP Controller",
-               .pgpolicy      = MULTIBUS,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
        },
        /*
@@ -1081,6 +1095,12 @@ static struct hwentry default_hw[] = {
                .fast_io_fail  = 10,
                .max_sectors_kb = 4096,
        },
+       {
+               /* FlashArray NVMe */
+               .vendor        = "NVME",
+               .product       = "Pure Storage FlashArray",
+               .no_path_retry = 10,
+       },
        /*
         * Huawei
         */
@@ -1094,6 +1114,13 @@ static struct hwentry default_hw[] = {
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .no_path_retry = 15,
        },
+       {
+               /* OceanStor NVMe */
+               .vendor        = "NVME",
+               .product       = "Huawei-XSG1",
+               .checker_name  = DIRECTIO,
+               .no_path_retry = 12,
+       },
        /*
         * Kove
         */
index 216f0ee..b3690ac 100644 (file)
@@ -31,7 +31,7 @@
  *   The new version inherits the previous ones.
  */
 
-LIBMULTIPATH_14.0.0 {
+LIBMULTIPATH_15.0.0 {
 global:
        /* symbols referenced by multipath and multipathd */
        add_foreign;
@@ -164,6 +164,7 @@ global:
        remember_wwid;
        remove_map;
        remove_map_by_alias;
+       remove_map_callback;
        remove_maps;
        remove_wwid;
        replace_wwids;
index ced021f..248f72b 100644 (file)
@@ -246,6 +246,35 @@ static inline void list_splice_tail_init(struct list_head *list,
 #define list_entry(ptr, type, member) \
        container_of(ptr, type, member)
 
+
+/**
+ * list_pop - unlink and return the first list element
+ * @head:      the &struct list_head pointer.
+ */
+static inline struct list_head *list_pop(struct list_head *head)
+{
+       struct list_head *tmp;
+
+       if (list_empty(head))
+               return NULL;
+       tmp = head->next;
+       list_del_init(tmp);
+       return tmp;
+}
+
+/**
+ * list_pop_entry - unlink and return the entry of the first list element
+ * @head:      the &struct list_head pointer.
+ * @type:      the type of the struct this is embedded in.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_pop_entry(head, type, member)             \
+({                                                     \
+       struct list_head *__h = list_pop(head);         \
+                                                       \
+       (__h ? container_of(__h, type, member) : NULL); \
+})
+
 /**
  * list_for_each       -       iterate over a list
  * @pos:       the &struct list_head to use as a loop counter.
@@ -335,6 +364,30 @@ static inline void list_splice_tail_init(struct list_head *list,
             pos = n, n = list_entry(n->member.prev, typeof(*n), member))
 
 /**
+ * list_for_some_entry - iterate list from the given begin node to the given end node
+ * @pos:       the type * to use as a loop counter.
+ * @from:      the begin node of the iteration.
+ * @to:                the end node of the iteration.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_some_entry(pos, from, to, member)                      \
+       for (pos = list_entry((from)->next, typeof(*pos), member);      \
+            &pos->member != (to);                                      \
+            pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_some_entry_reverse - iterate backwards list from the given begin node to the given end node
+ * @pos:       the type * to use as a loop counter.
+ * @from:      the begin node of the iteration.
+ * @to:                the end node of the iteration.
+ * @member:    the name of the list_struct within the struct.
+ */
+#define list_for_some_entry_reverse(pos, from, to, member)             \
+       for (pos = list_entry((from)->prev, typeof(*pos), member);      \
+            &pos->member != (to);                                      \
+            pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
  * list_for_some_entry_safe - iterate list from the given begin node to the given end node safe against removal of list entry
  * @pos:       the type * to use as a loop counter.
  * @n:         another type * to use as temporary storage
index bf88f30..68a793e 100644 (file)
@@ -754,23 +754,6 @@ snprint_path_failures(struct strbuf *buff, const struct path * pp)
 int
 snprint_path_protocol(struct strbuf *buff, const struct path * pp)
 {
-       static const char * const protocol_name[LAST_BUS_PROTOCOL_ID + 1] = {
-               [SYSFS_BUS_UNDEF] = "undef",
-               [SYSFS_BUS_CCW] = "ccw",
-               [SYSFS_BUS_CCISS] = "cciss",
-               [SYSFS_BUS_NVME] = "nvme",
-               [SYSFS_BUS_SCSI + SCSI_PROTOCOL_FCP] = "scsi:fcp",
-               [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SPI] = "scsi:spi",
-               [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SSA] = "scsi:ssa",
-               [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SBP] = "scsi:sbp",
-               [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SRP] = "scsi:srp",
-               [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ISCSI] = "scsi:iscsi",
-               [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SAS] = "scsi:sas",
-               [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ADT] = "scsi:adt",
-               [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ATA] = "scsi:ata",
-               [SYSFS_BUS_SCSI + SCSI_PROTOCOL_USB] = "scsi:usb",
-               [SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC] = "scsi:unspec",
-       };
        const char *pn = protocol_name[bus_protocol_id(pp)];
 
        assert(pn != NULL);
@@ -1424,6 +1407,52 @@ int snprint_multipath_topology_json (struct strbuf *buff,
 }
 
 static int
+snprint_pcentry (const struct config *conf, struct strbuf *buff,
+                const struct pcentry *pce)
+{
+       int i, rc;
+       struct keyword *kw;
+       struct keyword * rootkw;
+       size_t initial_len = get_strbuf_len(buff);
+
+       rootkw = find_keyword(conf->keywords, NULL, "overrides");
+       assert(rootkw && rootkw->sub);
+       rootkw = find_keyword(conf->keywords, rootkw->sub, "protocol");
+       assert(rootkw);
+
+       if ((rc = append_strbuf_str(buff, "\tprotocol {\n")) < 0)
+               return rc;
+
+       iterate_sub_keywords(rootkw, kw, i) {
+               if ((rc = snprint_keyword(buff, "\t\t%k %v\n", kw, pce)) < 0)
+                       return rc;
+       }
+
+       if ((rc = append_strbuf_str(buff, "\t}\n")) < 0)
+               return rc;
+       return get_strbuf_len(buff) - initial_len;
+}
+
+static int
+snprint_pctable (const struct config *conf, struct strbuf *buff,
+                const struct _vector *pctable)
+{
+       int i, rc;
+       struct pcentry *pce;
+       struct keyword * rootkw;
+       size_t initial_len = get_strbuf_len(buff);
+
+       rootkw = find_keyword(conf->keywords, NULL, "overrides");
+       assert(rootkw);
+
+       vector_foreach_slot(pctable, pce, i) {
+               if ((rc = snprint_pcentry(conf, buff, pce)) < 0)
+                       return rc;
+       }
+       return get_strbuf_len(buff) - initial_len;
+}
+
+static int
 snprint_hwentry (const struct config *conf,
                 struct strbuf *buff, const struct hwentry * hwe)
 {
@@ -1577,6 +1606,10 @@ static int snprint_overrides(const struct config *conf, struct strbuf *buff,
                if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, NULL)) < 0)
                        return rc;
        }
+
+       if (overrides->pctable &&
+           (rc = snprint_pctable(conf, buff, overrides->pctable)) < 0)
+               return rc;
 out:
        if ((rc = append_strbuf_str(buff, "}\n")) < 0)
                return rc;
index ef68cd0..cdd3752 100644 (file)
@@ -8,6 +8,7 @@
 #include "util.h"
 #include "prio.h"
 
+static const char * const prio_dir = MULTIPATH_DIR;
 static LIST_HEAD(prioritizers);
 
 unsigned int get_prio_timeout(unsigned int timeout_ms,
@@ -18,7 +19,7 @@ unsigned int get_prio_timeout(unsigned int timeout_ms,
        return default_timeout;
 }
 
-int init_prio (const char *multipath_dir)
+int init_prio(void)
 {
 #ifdef LOAD_ALL_SHARED_LIBS
        static const char *const all_prios[] = {
@@ -39,9 +40,9 @@ int init_prio (const char *multipath_dir)
        unsigned int i;
 
        for  (i = 0; i < ARRAY_SIZE(all_prios); i++)
-               add_prio(multipath_dir, all_prios[i]);
+               add_prio(all_prios[i]);
 #else
-       if (!add_prio(multipath_dir, DEFAULT_PRIO))
+       if (!add_prio(DEFAULT_PRIO))
                return 1;
 #endif
        return 0;
@@ -90,7 +91,7 @@ void cleanup_prio(void)
        }
 }
 
-static struct prio * prio_lookup (char * name)
+static struct prio *prio_lookup(const char *name)
 {
        struct prio * p;
 
@@ -109,7 +110,7 @@ int prio_set_args (struct prio * p, const char * args)
        return snprintf(p->args, PRIO_ARGS_LEN, "%s", args);
 }
 
-struct prio * add_prio (const char *multipath_dir, const char * name)
+struct prio *add_prio (const char *name)
 {
        char libname[LIB_PRIO_NAMELEN];
        struct stat stbuf;
@@ -121,10 +122,10 @@ struct prio * add_prio (const char *multipath_dir, const char * name)
                return NULL;
        snprintf(p->name, PRIO_NAME_LEN, "%s", name);
        snprintf(libname, LIB_PRIO_NAMELEN, "%s/libprio%s.so",
-                multipath_dir, name);
+                prio_dir, name);
        if (stat(libname,&stbuf) < 0) {
                condlog(0,"Prioritizer '%s' not found in %s",
-                       name, multipath_dir);
+                       name, prio_dir);
                goto out;
        }
        condlog(3, "loading %s prioritizer", libname);
@@ -170,7 +171,7 @@ const char * prio_args (const struct prio * p)
        return p->args;
 }
 
-void prio_get (char *multipath_dir, struct prio * dst, char * name, char * args)
+void prio_get(struct prio *dst, const char *name, const char *args)
 {
        struct prio * src = NULL;
 
@@ -180,7 +181,7 @@ void prio_get (char *multipath_dir, struct prio * dst, char * name, char * args)
        if (name && strlen(name)) {
                src = prio_lookup(name);
                if (!src)
-                       src = add_prio(multipath_dir, name);
+                       src = add_prio(name);
        }
        if (!src) {
                dst->getprio = NULL;
index 66c7936..184bf65 100644 (file)
@@ -54,11 +54,11 @@ struct prio {
 
 unsigned int get_prio_timeout(unsigned int checker_timeout,
                              unsigned int default_timeout);
-int init_prio (const char *);
+int init_prio(void);
 void cleanup_prio (void);
-struct prio * add_prio (const char *, const char *);
+struct prio * add_prio (const char *);
 int prio_getprio (struct prio *, struct path *, unsigned int);
-void prio_get (char *, struct prio *, char *, char *);
+void prio_get (struct prio *, const char *, const char *);
 void prio_put (struct prio *);
 int prio_selected (const struct prio *);
 const char * prio_name (const struct prio *);
index 16c6397..a5ab5e1 100644 (file)
@@ -3,7 +3,8 @@
 #
 include ../../Makefile.inc
 
-CFLAGS += $(LIB_CFLAGS) -I..
+CPPFLAGS += -I..
+CFLAGS += $(LIB_CFLAGS)
 LDFLAGS += -L..
 LIBDEPS = -lmultipath -lm -lpthread -lrt
 
@@ -25,7 +26,7 @@ LIBS = \
 
 ifneq ($(call check_file,$(LINUX_HEADERS_INCDIR)/linux/nvme_ioctl.h),0)
        LIBS += libprioana.so
-       CFLAGS += -I../nvme
+       CPPFLAGS += -I../nvme
 endif
 
 all: $(LIBS)
index e98773c..167a46b 100644 (file)
@@ -31,7 +31,7 @@
 // name: find_regex
 // @param string: string you want to search into
 // @param regex: the pattern used
-// @return result: string finded in string with regex, "none" if none
+// @return result: string found in string with regex, "none" if none
 char *find_regex(char * string, char * regex)
 {
        int err;
index 1419ec6..50d0b5c 100644 (file)
@@ -79,6 +79,8 @@ static const char conf_origin[] =
        "(setting: multipath.conf defaults/devices section)";
 static const char overrides_origin[] =
        "(setting: multipath.conf overrides section)";
+static const char overrides_pce_origin[] =
+       "(setting: multipath.conf overrides protocol section)";
 static const char cmdline_origin[] =
        "(setting: multipath command line [-p] flag)";
 static const char autodetect_origin[] =
@@ -146,6 +148,27 @@ do {                                                                       \
        }                                                               \
 } while (0)
 
+#define pp_set_ovr_pce(var)                                            \
+do {                                                                   \
+       struct pcentry *_pce;                                           \
+       int _i;                                                         \
+                                                                       \
+       if (conf->overrides) {                                          \
+               vector_foreach_slot(conf->overrides->pctable, _pce, _i) {       \
+                       if (_pce->type == (int)bus_protocol_id(pp) && _pce->var) {      \
+                               pp->var = _pce->var;                    \
+                               origin = overrides_pce_origin;          \
+                               goto out;                               \
+                       }                                               \
+               }                                                       \
+               if (conf->overrides->var) {                             \
+                       pp->var = conf->overrides->var;                 \
+                       origin = overrides_origin;                      \
+                       goto out;                                       \
+               }                                                       \
+       }                                                               \
+} while (0)
+
 int select_mode(struct config *conf, struct multipath *mp)
 {
        const char *origin;
@@ -537,7 +560,7 @@ int select_checker(struct config *conf, struct path *pp)
        do_set(checker_name, conf, ckr_name, conf_origin);
        do_default(ckr_name, DEFAULT_CHECKER);
 out:
-       checker_get(conf->multipath_dir, c, ckr_name);
+       checker_get(c, ckr_name);
        condlog(3, "%s: path_checker = %s %s", pp->dev,
                checker_name(c), origin);
        if (conf->checker_timeout) {
@@ -566,20 +589,14 @@ int select_getuid(struct config *conf, struct path *pp)
                goto out;
        }
 
-       pp_set_ovr(getuid);
        pp_set_ovr(uid_attribute);
-       pp_set_hwe(getuid);
        pp_set_hwe(uid_attribute);
-       pp_set_conf(getuid);
        pp_set_conf(uid_attribute);
        pp_set_default(uid_attribute, DEFAULT_UID_ATTRIBUTE);
 out:
        if (pp->uid_attribute)
                condlog(3, "%s: uid_attribute = %s %s", pp->dev,
                        pp->uid_attribute, origin);
-       else if (pp->getuid)
-               condlog(3, "%s: getuid = \"%s\" %s", pp->dev, pp->getuid,
-                       origin);
        return 0;
 }
 
@@ -594,7 +611,7 @@ int select_recheck_wwid(struct config *conf, struct path * pp)
        pp_set_default(recheck_wwid, DEFAULT_RECHECK_WWID);
 out:
        if (pp->recheck_wwid == RECHECK_WWID_ON &&
-           (pp->bus != SYSFS_BUS_SCSI || pp->getuid != NULL ||
+           (pp->bus != SYSFS_BUS_SCSI ||
             !has_uid_fallback(pp))) {
                pp->recheck_wwid = RECHECK_WWID_OFF;
                origin = "(setting: unsupported by device type/config)";
@@ -604,8 +621,7 @@ out:
        return 0;
 }
 
-void
-detect_prio(struct config *conf, struct path * pp)
+void detect_prio(struct path *pp)
 {
        struct prio *p = &pp->prio;
        char buff[512];
@@ -631,19 +647,19 @@ detect_prio(struct config *conf, struct path * pp)
        default:
                return;
        }
-       prio_get(conf->multipath_dir, p, default_prio, DEFAULT_PRIO_ARGS);
+       prio_get(p, default_prio, DEFAULT_PRIO_ARGS);
 }
 
-#define set_prio(dir, src, msg)                                        \
+#define set_prio(src, msg)                                             \
 do {                                                                   \
        if (src && src->prio_name) {                                    \
-               prio_get(dir, p, src->prio_name, src->prio_args);       \
+               prio_get(p, src->prio_name, src->prio_args);            \
                origin = msg;                                           \
                goto out;                                               \
        }                                                               \
 } while(0)
 
-#define set_prio_from_vec(type, dir, src, msg, p)                      \
+#define set_prio_from_vec(type, src, msg, p)                           \
 do {                                                                   \
        type *_p;                                                       \
        int i;                                                          \
@@ -656,7 +672,7 @@ do {                                                                        \
                        prio_args = _p->prio_args;                      \
        }                                                               \
        if (prio_name != NULL) {                                        \
-               prio_get(dir, p, prio_name, prio_args);                 \
+               prio_get(p, prio_name, prio_args);                      \
                origin = msg;                                           \
                goto out;                                               \
        }                                                               \
@@ -670,19 +686,18 @@ int select_prio(struct config *conf, struct path *pp)
        int log_prio = 3;
 
        if (pp->detect_prio == DETECT_PRIO_ON) {
-               detect_prio(conf, pp);
+               detect_prio(pp);
                if (prio_selected(p)) {
                        origin = autodetect_origin;
                        goto out;
                }
        }
        mpe = find_mpe(conf->mptable, pp->wwid);
-       set_prio(conf->multipath_dir, mpe, multipaths_origin);
-       set_prio(conf->multipath_dir, conf->overrides, overrides_origin);
-       set_prio_from_vec(struct hwentry, conf->multipath_dir,
-                         pp->hwe, hwe_origin, p);
-       set_prio(conf->multipath_dir, conf, conf_origin);
-       prio_get(conf->multipath_dir, p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
+       set_prio(mpe, multipaths_origin);
+       set_prio(conf->overrides, overrides_origin);
+       set_prio_from_vec(struct hwentry, pp->hwe, hwe_origin, p);
+       set_prio(conf, conf_origin);
+       prio_get(p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
        origin = default_origin;
 out:
        /*
@@ -692,8 +707,7 @@ out:
                int tpgs = path_get_tpgs(pp);
 
                if (tpgs == TPGS_NONE) {
-                       prio_get(conf->multipath_dir,
-                                p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
+                       prio_get(p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
                        origin = "(setting: emergency fallback - alua failed)";
                        log_prio = 1;
                }
@@ -769,53 +783,53 @@ int select_minio(struct config *conf, struct multipath *mp)
                return select_minio_bio(conf, mp);
 }
 
-int select_fast_io_fail(struct config *conf, struct multipath *mp)
+int select_fast_io_fail(struct config *conf, struct path *pp)
 {
        const char *origin;
        STRBUF_ON_STACK(buff);
 
-       mp_set_ovr(fast_io_fail);
-       mp_set_hwe(fast_io_fail);
-       mp_set_conf(fast_io_fail);
-       mp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL);
+       pp_set_ovr_pce(fast_io_fail);
+       pp_set_hwe(fast_io_fail);
+       pp_set_conf(fast_io_fail);
+       pp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL);
 out:
-       print_undef_off_zero(&buff, mp->fast_io_fail);
-       condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias,
+       print_undef_off_zero(&buff, pp->fast_io_fail);
+       condlog(3, "%s: fast_io_fail_tmo = %s %s", pp->dev,
                get_strbuf_str(&buff), origin);
        return 0;
 }
 
-int select_dev_loss(struct config *conf, struct multipath *mp)
+int select_dev_loss(struct config *conf, struct path *pp)
 {
        const char *origin;
        STRBUF_ON_STACK(buff);
 
-       mp_set_ovr(dev_loss);
-       mp_set_hwe(dev_loss);
-       mp_set_conf(dev_loss);
-       mp->dev_loss = DEV_LOSS_TMO_UNSET;
+       pp_set_ovr_pce(dev_loss);
+       pp_set_hwe(dev_loss);
+       pp_set_conf(dev_loss);
+       pp->dev_loss = DEV_LOSS_TMO_UNSET;
        return 0;
 out:
-       print_dev_loss(&buff, mp->dev_loss);
-       condlog(3, "%s: dev_loss_tmo = %s %s", mp->alias,
+       print_dev_loss(&buff, pp->dev_loss);
+       condlog(3, "%s: dev_loss_tmo = %s %s", pp->dev,
                get_strbuf_str(&buff), origin);
        return 0;
 }
 
-int select_eh_deadline(struct config *conf, struct multipath *mp)
+int select_eh_deadline(struct config *conf, struct path *pp)
 {
        const char *origin;
        STRBUF_ON_STACK(buff);
 
-       mp_set_ovr(eh_deadline);
-       mp_set_hwe(eh_deadline);
-       mp_set_conf(eh_deadline);
-       mp->eh_deadline = EH_DEADLINE_UNSET;
+       pp_set_ovr_pce(eh_deadline);
+       pp_set_hwe(eh_deadline);
+       pp_set_conf(eh_deadline);
+       pp->eh_deadline = EH_DEADLINE_UNSET;
        /* not changing sysfs in default cause, so don't print anything */
        return 0;
 out:
-       print_undef_off_zero(&buff, mp->eh_deadline);
-       condlog(3, "%s: eh_deadline = %s %s", mp->alias,
+       print_undef_off_zero(&buff, pp->eh_deadline);
+       condlog(3, "%s: eh_deadline = %s %s", pp->dev,
                get_strbuf_str(&buff), origin);
        return 0;
 }
index 72a7e33..152ca44 100644 (file)
@@ -16,9 +16,9 @@ int select_minio(struct config *conf, struct multipath *mp);
 int select_mode(struct config *conf, struct multipath *mp);
 int select_uid(struct config *conf, struct multipath *mp);
 int select_gid(struct config *conf, struct multipath *mp);
-int select_fast_io_fail(struct config *conf, struct multipath *mp);
-int select_dev_loss(struct config *conf, struct multipath *mp);
-int select_eh_deadline(struct config *conf, struct multipath *mp);
+int select_fast_io_fail(struct config *conf, struct path *pp);
+int select_dev_loss(struct config *conf, struct path *pp);
+int select_eh_deadline(struct config *conf, struct path *pp);
 int select_reservation_key(struct config *conf, struct multipath *mp);
 int select_retain_hwhandler (struct config *conf, struct multipath * mp);
 int select_detect_prio(struct config *conf, struct path * pp);
index 4b62da5..49621cb 100644 (file)
 #include "dm-generic.h"
 #include "devmapper.h"
 
+const char * const protocol_name[LAST_BUS_PROTOCOL_ID + 1] = {
+       [SYSFS_BUS_UNDEF] = "undef",
+       [SYSFS_BUS_CCW] = "ccw",
+       [SYSFS_BUS_CCISS] = "cciss",
+       [SYSFS_BUS_NVME] = "nvme",
+       [SYSFS_BUS_SCSI + SCSI_PROTOCOL_FCP] = "scsi:fcp",
+       [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SPI] = "scsi:spi",
+       [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SSA] = "scsi:ssa",
+       [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SBP] = "scsi:sbp",
+       [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SRP] = "scsi:srp",
+       [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ISCSI] = "scsi:iscsi",
+       [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SAS] = "scsi:sas",
+       [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ADT] = "scsi:adt",
+       [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ATA] = "scsi:ata",
+       [SYSFS_BUS_SCSI + SCSI_PROTOCOL_USB] = "scsi:usb",
+       [SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC] = "scsi:unspec",
+};
+
 struct adapter_group *
 alloc_adaptergroup(void)
 {
@@ -121,7 +139,6 @@ uninitialize_path(struct path *pp)
 
        pp->dmstate = PSTATE_UNDEF;
        pp->uid_attribute = NULL;
-       pp->getuid = NULL;
 
        if (checker_selected(&pp->checker))
                checker_put(&pp->checker);
@@ -228,7 +245,6 @@ alloc_multipath (void)
                mpp->bestpg = 1;
                mpp->mpcontext = NULL;
                mpp->no_path_retry = NO_PATH_RETRY_UNDEF;
-               mpp->fast_io_fail = MP_FAST_IO_FAIL_UNSET;
                dm_multipath_to_gen(mpp)->ops = &dm_gen_multipath_ops;
        }
        return mpp;
index d94f93a..a6a0944 100644 (file)
@@ -192,6 +192,7 @@ enum scsi_protocol {
  */
 #define LAST_BUS_PROTOCOL_ID (SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC)
 unsigned int bus_protocol_id(const struct path *pp);
+extern const char * const protocol_name[];
 
 #define SCSI_INVALID_LUN ~0ULL
 
@@ -327,8 +328,7 @@ struct path {
        int detect_prio;
        int detect_checker;
        int tpgs;
-       char * uid_attribute;
-       char * getuid;
+       const char *uid_attribute;
        struct prio prio;
        struct checker checker;
        struct multipath * mpp;
@@ -348,6 +348,9 @@ struct path {
        int marginal;
        int vpd_vendor_id;
        int recheck_wwid;
+       int fast_io_fail;
+       unsigned int dev_loss;
+       int eh_deadline;
        /* configlet pointers */
        vector hwe;
        struct gen_path generic_path;
@@ -375,7 +378,6 @@ struct multipath {
        int minio;
        int flush_on_last_del;
        int attribute_flags;
-       int fast_io_fail;
        int retain_hwhandler;
        int deferred_remove;
        bool in_recovery;
@@ -394,8 +396,6 @@ struct multipath {
        int needs_paths_uevent;
        int ghost_delay;
        int ghost_delay_tick;
-       unsigned int dev_loss;
-       int eh_deadline;
        uid_t uid;
        gid_t gid;
        mode_t mode;
index 6c23df8..a69f064 100644 (file)
@@ -336,11 +336,17 @@ void set_path_removed(struct path *pp)
        pp->initialized = INIT_REMOVED;
 }
 
+void remove_map_callback(struct multipath *mpp __attribute__((unused)))
+{
+}
+
 void
 remove_map(struct multipath *mpp, vector pathvec, vector mpvec)
 {
        int i;
 
+       remove_map_callback(mpp);
+
        free_pathvec(mpp->paths, KEEP_PATHS);
        free_pgvec(mpp->pg, KEEP_PATHS);
        mpp->paths = mpp->pg = NULL;
index 29e2557..5793af9 100644 (file)
 #include "config.h"
 #include "blacklist.h"
 #include "devmapper.h"
-
-#define MAX_ACCUMULATION_COUNT 2048
-#define MAX_ACCUMULATION_TIME 30*1000
-#define MIN_BURST_SPEED 10
+#include "strbuf.h"
 
 typedef int (uev_trigger)(struct uevent *, void * trigger_data);
 
@@ -67,6 +64,21 @@ static uev_trigger *my_uev_trigger;
 static void *my_trigger_data;
 static int servicing_uev;
 
+struct uevent_filter_state {
+       struct list_head uevq;
+       struct list_head *old_tail;
+       struct config *conf;
+       unsigned long added;
+       unsigned long discarded;
+       unsigned long filtered;
+       unsigned long merged;
+};
+
+static void reset_filter_state(struct uevent_filter_state *st)
+{
+       st->added = st->discarded = st->filtered = st->merged = 0;
+}
+
 int is_uevent_busy(void)
 {
        int empty;
@@ -162,40 +174,24 @@ int uevent_get_env_positive_int(const struct uevent *uev,
 }
 
 void
-uevent_get_wwid(struct uevent *uev)
+uevent_get_wwid(struct uevent *uev, const struct config *conf)
 {
-       char *uid_attribute;
+       const char *uid_attribute;
        const char *val;
-       struct config * conf;
 
-       conf = get_multipath_config();
-       pthread_cleanup_push(put_multipath_config, conf);
        uid_attribute = get_uid_attribute_by_attrs(conf, uev->kernel);
-       pthread_cleanup_pop(1);
-
        val = uevent_get_env_var(uev, uid_attribute);
        if (val)
                uev->wwid = val;
 }
 
-static bool uevent_need_merge(void)
+static bool uevent_need_merge(const struct config *conf)
 {
-       struct config * conf;
-       bool need_merge = false;
-
-       conf = get_multipath_config();
-       if (VECTOR_SIZE(&conf->uid_attrs) > 0)
-               need_merge = true;
-       put_multipath_config(conf);
-
-       return need_merge;
+       return VECTOR_SIZE(&conf->uid_attrs) > 0;
 }
 
-static bool uevent_can_discard(struct uevent *uev)
+static bool uevent_can_discard(struct uevent *uev, const struct config *conf)
 {
-       int invalid = 0;
-       struct config * conf;
-
        /*
         * do not filter dm devices by devnode
         */
@@ -204,15 +200,10 @@ static bool uevent_can_discard(struct uevent *uev)
        /*
         * filter paths devices by devnode
         */
-       conf = get_multipath_config();
-       pthread_cleanup_push(put_multipath_config, conf);
        if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
                           uev->kernel) > 0)
-               invalid = 1;
-       pthread_cleanup_pop(1);
-
-       if (invalid)
                return true;
+
        return false;
 }
 
@@ -220,6 +211,10 @@ static bool
 uevent_can_filter(struct uevent *earlier, struct uevent *later)
 {
 
+       if (!strncmp(later->kernel, "dm-", 3) ||
+           strcmp(earlier->kernel, later->kernel))
+               return false;
+
        /*
         * filter earlier uvents if path has removed later. Eg:
         * "add path1 |chang path1 |add path2 |remove path1"
@@ -227,11 +222,8 @@ uevent_can_filter(struct uevent *earlier, struct uevent *later)
         * "add path2 |remove path1"
         * uevents "add path1" and "chang path1" are filtered out
         */
-       if (!strcmp(earlier->kernel, later->kernel) &&
-               !strcmp(later->action, "remove") &&
-               strncmp(later->kernel, "dm-", 3)) {
+       if (!strcmp(later->action, "remove"))
                return true;
-       }
 
        /*
         * filter change uvents if add uevents exist. Eg:
@@ -240,12 +232,9 @@ uevent_can_filter(struct uevent *earlier, struct uevent *later)
         * "add path1 |add path2"
         * uevent "chang path1" is filtered out
         */
-       if (!strcmp(earlier->kernel, later->kernel) &&
-               !strcmp(earlier->action, "change") &&
-               !strcmp(later->action, "add") &&
-               strncmp(later->kernel, "dm-", 3)) {
+       if (!strcmp(earlier->action, "change") &&
+           !strcmp(later->action, "add"))
                return true;
-       }
 
        return false;
 }
@@ -278,10 +267,10 @@ merge_need_stop(struct uevent *earlier, struct uevent *later)
         * with the same wwid and different action
         * it would be better to stop merging.
         */
-       if (!strcmp(earlier->wwid, later->wwid) &&
-           strcmp(earlier->action, later->action) &&
+       if (strcmp(earlier->action, later->action) &&
            strcmp(earlier->action, "change") &&
-           strcmp(later->action, "change"))
+           strcmp(later->action, "change") &&
+           !strcmp(earlier->wwid, later->wwid))
                return true;
 
        return false;
@@ -296,106 +285,209 @@ uevent_can_merge(struct uevent *earlier, struct uevent *later)
         * and actions are addition or deletion
         */
        if (earlier->wwid && later->wwid &&
-           !strcmp(earlier->wwid, later->wwid) &&
+           strncmp(earlier->kernel, "dm-", 3) &&
            !strcmp(earlier->action, later->action) &&
-           strncmp(earlier->action, "change", 6) &&
-           strncmp(earlier->kernel, "dm-", 3)) {
+           (!strcmp(earlier->action, "add") ||
+            !strcmp(earlier->action, "remove")) &&
+           !strcmp(earlier->wwid, later->wwid))
                return true;
-       }
 
        return false;
 }
 
-static void
-uevent_prepare(struct list_head *tmpq)
+static void uevent_delete_from_list(struct uevent *to_delete,
+                                   struct uevent **previous,
+                                   struct list_head **old_tail)
+{
+       /*
+        * "old_tail" is the list_head before the last list element to which
+        * the caller iterates (the list anchor if the caller iterates over
+        * the entire list). If this element is removed (which can't happen
+        * for the anchor), "old_tail" must be moved. It can happen that
+        * "old_tail" ends up pointing at the anchor.
+        */
+       if (*old_tail == &to_delete->node)
+               *old_tail = to_delete->node.prev;
+
+       list_del_init(&to_delete->node);
+
+       /*
+        * The "to_delete" uevent has been merged with other uevents
+        * previously. Re-insert them into the list, at the point we're
+        * currently at. This must be done after the list_del_init() above,
+        * otherwise previous->next would still point to to_delete.
+        */
+       if (!list_empty(&to_delete->merge_node)) {
+               struct uevent *last = list_entry(to_delete->merge_node.prev,
+                                                typeof(*last), node);
+
+               condlog(3, "%s: deleted uevent \"%s %s\" with merged uevents",
+                       __func__, to_delete->action, to_delete->kernel);
+               list_splice(&to_delete->merge_node, &(*previous)->node);
+               *previous = last;
+       }
+       if (to_delete->udev)
+               udev_device_unref(to_delete->udev);
+
+       free(to_delete);
+}
+
+/*
+ * Use this function to delete events that are known not to
+ * be equal to old_tail, and have an empty merge_node list.
+ * For others, use uevent_delete_from_list().
+ */
+static void uevent_delete_simple(struct uevent *to_delete)
+{
+       list_del_init(&to_delete->node);
+
+       if (to_delete->udev)
+               udev_device_unref(to_delete->udev);
+
+       free(to_delete);
+}
+
+static void uevent_prepare(struct uevent_filter_state *st)
 {
        struct uevent *uev, *tmp;
 
-       list_for_each_entry_reverse_safe(uev, tmp, tmpq, node) {
-               if (uevent_can_discard(uev)) {
-                       list_del_init(&uev->node);
-                       if (uev->udev)
-                               udev_device_unref(uev->udev);
-                       free(uev);
+       list_for_some_entry_reverse_safe(uev, tmp, &st->uevq, st->old_tail, node) {
+
+               st->added++;
+               if (uevent_can_discard(uev, st->conf)) {
+                       uevent_delete_simple(uev);
+                       st->discarded++;
                        continue;
                }
 
                if (strncmp(uev->kernel, "dm-", 3) &&
-                   uevent_need_merge())
-                       uevent_get_wwid(uev);
+                   uevent_need_merge(st->conf))
+                       uevent_get_wwid(uev, st->conf);
        }
 }
 
 static void
-uevent_filter(struct uevent *later, struct list_head *tmpq)
+uevent_filter(struct uevent *later, struct uevent_filter_state *st)
 {
        struct uevent *earlier, *tmp;
 
-       list_for_some_entry_reverse_safe(earlier, tmp, &later->node, tmpq, node) {
+       list_for_some_entry_reverse_safe(earlier, tmp, &later->node, &st->uevq, node) {
                /*
                 * filter unnessary earlier uevents
                 * by the later uevent
                 */
+               if (!list_empty(&earlier->merge_node)) {
+                       struct uevent *mn, *t;
+
+                       list_for_each_entry_reverse_safe(mn, t, &earlier->merge_node, node) {
+                               if (uevent_can_filter(mn, later)) {
+                                       condlog(4, "uevent: \"%s %s\" (merged into \"%s %s\") filtered by \"%s %s\"",
+                                               mn->action, mn->kernel,
+                                               earlier->action, earlier->kernel,
+                                               later->action, later->kernel);
+                                       uevent_delete_simple(mn);
+                                       st->filtered++;
+                               }
+                       }
+               }
                if (uevent_can_filter(earlier, later)) {
-                       condlog(3, "uevent: %s-%s has filtered by uevent: %s-%s",
-                               earlier->kernel, earlier->action,
-                               later->kernel, later->action);
-
-                       list_del_init(&earlier->node);
-                       if (earlier->udev)
-                               udev_device_unref(earlier->udev);
-                       free(earlier);
+                       condlog(4, "uevent: \"%s %s\" filtered by \"%s %s\"",
+                               earlier->action, earlier->kernel,
+                               later->action, later->kernel);
+
+                       uevent_delete_from_list(earlier, &tmp, &st->old_tail);
+                       st->filtered++;
                }
        }
 }
 
-static void
-uevent_merge(struct uevent *later, struct list_head *tmpq)
+static void uevent_merge(struct uevent *later, struct uevent_filter_state *st)
 {
        struct uevent *earlier, *tmp;
 
-       list_for_some_entry_reverse_safe(earlier, tmp, &later->node, tmpq, node) {
+       list_for_some_entry_reverse_safe(earlier, tmp, &later->node, &st->uevq, node) {
                if (merge_need_stop(earlier, later))
                        break;
                /*
                 * merge earlier uevents to the later uevent
                 */
                if (uevent_can_merge(earlier, later)) {
-                       condlog(3, "merged uevent: %s-%s-%s with uevent: %s-%s-%s",
-                               earlier->action, earlier->kernel, earlier->wwid,
+                       condlog(4, "uevent: \"%s %s\" merged with \"%s %s\" for WWID %s",
+                               earlier->action, earlier->kernel,
                                later->action, later->kernel, later->wwid);
 
+                       /* See comment in uevent_delete_from_list() */
+                       if (&earlier->node == st->old_tail)
+                               st->old_tail = earlier->node.prev;
+
                        list_move(&earlier->node, &later->merge_node);
+                       list_splice_init(&earlier->merge_node,
+                                        &later->merge_node);
+                       st->merged++;
                }
        }
 }
 
-static void
-merge_uevq(struct list_head *tmpq)
+static void merge_uevq(struct uevent_filter_state *st)
 {
        struct uevent *later;
 
-       uevent_prepare(tmpq);
-       list_for_each_entry_reverse(later, tmpq, node) {
-               uevent_filter(later, tmpq);
-               if(uevent_need_merge())
-                       uevent_merge(later, tmpq);
+       uevent_prepare(st);
+
+       list_for_some_entry_reverse(later, &st->uevq, st->old_tail, node)
+               uevent_filter(later, st);
+
+       if(uevent_need_merge(st->conf))
+               list_for_some_entry_reverse(later, &st->uevq, st->old_tail, node)
+                       uevent_merge(later, st);
+}
+
+static void print_uev(struct strbuf *buf, struct uevent *uev)
+{
+       print_strbuf(buf, "\"%s %s\"", uev->action, uev->kernel);
+       if (!list_empty(&uev->merge_node)) {
+               struct uevent *u;
+
+               append_strbuf_str(buf, "[");
+               list_for_each_entry(u, &uev->merge_node, node)
+                       print_strbuf(buf, "\"%s %s \"", u->action, u->kernel);
+               append_strbuf_str(buf, "]");
        }
+       append_strbuf_str(buf, " ");
 }
 
-static void
-service_uevq(struct list_head *tmpq)
+static void print_uevq(const char *msg, struct list_head *uevq)
 {
-       struct uevent *uev, *tmp;
+       struct uevent *uev;
+       int i = 0;
+       STRBUF_ON_STACK(buf);
 
-       list_for_each_entry_safe(uev, tmp, tmpq, node) {
-               list_del_init(&uev->node);
+       if (4 > MAX_VERBOSITY || 4 > libmp_verbosity)
+               return;
 
-               pthread_cleanup_push(cleanup_uev, uev);
-               if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))
-                       condlog(0, "uevent trigger error");
-               pthread_cleanup_pop(1);
-       }
+       if (list_empty(uevq))
+               append_strbuf_str(&buf, "*empty*");
+       else
+               list_for_each_entry(uev, uevq, node) {
+                       print_strbuf(&buf, "%d:", i++);
+                       print_uev(&buf, uev);
+               }
+
+       condlog(4, "uevent queue (%s): %s", msg, steal_strbuf_str(&buf));
+}
+
+static void
+service_uevq(struct list_head *tmpq)
+{
+       struct uevent *uev = list_pop_entry(tmpq, typeof(*uev), node);
+
+       if (uev == NULL)
+               return;
+       condlog(4, "servicing uevent '%s %s'", uev->action, uev->kernel);
+       pthread_cleanup_push(cleanup_uev, uev);
+       if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))
+               condlog(0, "uevent trigger error");
+       pthread_cleanup_pop(1);
 }
 
 static void uevent_cleanup(void *arg)
@@ -426,42 +518,68 @@ static void cleanup_global_uevq(void *arg __attribute__((unused)))
        pthread_mutex_unlock(uevq_lockp);
 }
 
+static void log_filter_state(const struct uevent_filter_state *st)
+{
+       if (st->added == 0 && st->filtered == 0 && st->merged == 0)
+               return;
+
+       condlog(3, "uevents: %lu added, %lu discarded, %lu filtered, %lu merged",
+               st->added, st->discarded, st->filtered, st->merged);
+}
+
 /*
  * Service the uevent queue.
  */
 int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
                    void * trigger_data)
 {
+       struct uevent_filter_state filter_state;
+
+       INIT_LIST_HEAD(&filter_state.uevq);
        my_uev_trigger = uev_trigger;
        my_trigger_data = trigger_data;
 
        mlockall(MCL_CURRENT | MCL_FUTURE);
 
+       pthread_cleanup_push(cleanup_uevq, &filter_state.uevq);
        while (1) {
-               LIST_HEAD(uevq_tmp);
-
                pthread_cleanup_push(cleanup_mutex, uevq_lockp);
                pthread_mutex_lock(uevq_lockp);
-               servicing_uev = 0;
-               /*
-                * Condition signals are unreliable,
-                * so make sure we only wait if we have to.
-                */
-               if (list_empty(&uevq)) {
+
+               servicing_uev = !list_empty(&filter_state.uevq);
+
+               while (list_empty(&filter_state.uevq) && list_empty(&uevq)) {
+                       condlog(4, "%s: waiting for events", __func__);
                        pthread_cond_wait(uev_condp, uevq_lockp);
+                       condlog(4, "%s: waking up", __func__);
                }
+
                servicing_uev = 1;
-               list_splice_init(&uevq, &uevq_tmp);
+               /*
+                * "old_tail" is the list element towards which merge_uevq()
+                * will iterate: the last element of uevq before
+                * appending new uevents. If uveq  empty, uevq.prev
+                * equals &uevq, which is what we need.
+                */
+               filter_state.old_tail = filter_state.uevq.prev;
+               list_splice_tail_init(&uevq, &filter_state.uevq);
                pthread_cleanup_pop(1);
 
                if (!my_uev_trigger)
                        break;
 
-               pthread_cleanup_push(cleanup_uevq, &uevq_tmp);
-               merge_uevq(&uevq_tmp);
-               service_uevq(&uevq_tmp);
+               reset_filter_state(&filter_state);
+               pthread_cleanup_push(put_multipath_config, filter_state.conf);
+               print_uevq("append", &filter_state.uevq);
+               filter_state.conf = get_multipath_config();
+               merge_uevq(&filter_state);
                pthread_cleanup_pop(1);
+               log_filter_state(&filter_state);
+
+               print_uevq("merge", &filter_state.uevq);
+               service_uevq(&filter_state.uevq);
        }
+       pthread_cleanup_pop(1);
        condlog(3, "Terminating uev service queue");
        return 0;
 }
@@ -528,44 +646,43 @@ static struct uevent *uevent_from_udev_device(struct udev_device *dev)
        return uev;
 }
 
-static bool uevent_burst(struct timeval *start_time, int events)
+#define MAX_UEVENTS 1000
+static int uevent_receive_events(int fd, struct list_head *tmpq,
+                                struct udev_monitor *monitor)
 {
-       struct timeval diff_time, end_time;
-       unsigned long speed;
-       unsigned long eclipse_ms;
-
-       if(events > MAX_ACCUMULATION_COUNT) {
-               condlog(2, "burst got %u uevents, too much uevents, stopped", events);
-               return false;
-       }
+       struct pollfd ev_poll;
+       int n = 0;
 
-       gettimeofday(&end_time, NULL);
-       timersub(&end_time, start_time, &diff_time);
+       do {
+               struct uevent *uev;
+               struct udev_device *dev;
 
-       eclipse_ms = diff_time.tv_sec * 1000 + diff_time.tv_usec / 1000;
+               dev = udev_monitor_receive_device(monitor);
+               if (!dev) {
+                       condlog(0, "failed getting udev device");
+                       break;
+               }
+               uev = uevent_from_udev_device(dev);
+               if (!uev)
+                       break;
 
-       if (eclipse_ms == 0)
-               return true;
+               list_add_tail(&uev->node, tmpq);
+               n++;
+               condlog(4, "received uevent \"%s %s\"", uev->action, uev->kernel);
 
-       if (eclipse_ms > MAX_ACCUMULATION_TIME) {
-               condlog(2, "burst continued %lu ms, too long time, stopped", eclipse_ms);
-               return false;
-       }
+               ev_poll.fd = fd;
+               ev_poll.events = POLLIN;
 
-       speed = (events * 1000) / eclipse_ms;
-       if (speed > MIN_BURST_SPEED)
-               return true;
+       } while (n < MAX_UEVENTS && poll(&ev_poll, 1, 0) > 0);
 
-       return false;
+       return n;
 }
 
 int uevent_listen(struct udev *udev)
 {
        int err = 2;
        struct udev_monitor *monitor = NULL;
-       int fd, socket_flags, events;
-       struct timeval start_time;
-       int timeout = 30;
+       int fd, socket_flags;
        LIST_HEAD(uevlisten_tmp);
 
        /*
@@ -617,59 +734,30 @@ int uevent_listen(struct udev *udev)
                goto out;
        }
 
-       events = 0;
-       gettimeofday(&start_time, NULL);
        pthread_cleanup_push(cleanup_global_uevq, NULL);
        pthread_cleanup_push(cleanup_uevq, &uevlisten_tmp);
        while (1) {
-               struct uevent *uev;
-               struct udev_device *dev;
-               struct pollfd ev_poll;
-               int poll_timeout;
-               int fdcount;
+               int fdcount, events;
+               struct pollfd ev_poll = { .fd = fd, .events = POLLIN, };
 
-               memset(&ev_poll, 0, sizeof(struct pollfd));
-               ev_poll.fd = fd;
-               ev_poll.events = POLLIN;
-               poll_timeout = timeout * 1000;
-               errno = 0;
-               fdcount = poll(&ev_poll, 1, poll_timeout);
-               if (fdcount > 0 && ev_poll.revents & POLLIN) {
-                       timeout = uevent_burst(&start_time, events + 1) ? 1 : 0;
-                       dev = udev_monitor_receive_device(monitor);
-                       if (!dev) {
-                               condlog(0, "failed getting udev device");
-                               continue;
-                       }
-                       uev = uevent_from_udev_device(dev);
-                       if (!uev)
-                               continue;
-                       list_add_tail(&uev->node, &uevlisten_tmp);
-                       events++;
-                       continue;
-               }
+               fdcount = poll(&ev_poll, 1, -1);
                if (fdcount < 0) {
                        if (errno == EINTR)
                                continue;
 
-                       condlog(0, "error receiving "
-                               "uevent message: %m");
+                       condlog(0, "error receiving uevent message: %m");
                        err = -errno;
                        break;
                }
-               if (!list_empty(&uevlisten_tmp)) {
-                       /*
-                        * Queue uevents and poke service pthread.
-                        */
-                       condlog(3, "Forwarding %d uevents", events);
-                       pthread_mutex_lock(uevq_lockp);
-                       list_splice_tail_init(&uevlisten_tmp, &uevq);
-                       pthread_cond_signal(uev_condp);
-                       pthread_mutex_unlock(uevq_lockp);
-                       events = 0;
-               }
-               gettimeofday(&start_time, NULL);
-               timeout = 30;
+               events = uevent_receive_events(fd, &uevlisten_tmp, monitor);
+               if (events <= 0)
+                       continue;
+
+               condlog(4, "Forwarding %d uevents", events);
+               pthread_mutex_lock(uevq_lockp);
+               list_splice_tail_init(&uevlisten_tmp, &uevq);
+               pthread_cond_signal(uev_condp);
+               pthread_mutex_unlock(uevq_lockp);
        }
        pthread_cleanup_pop(1);
        pthread_cleanup_pop(1);
index 61ca1b5..53a7ca2 100644 (file)
@@ -10,6 +10,7 @@
 #define OBJECT_SIZE                    512
 
 struct udev;
+struct config;
 
 struct uevent {
        struct list_head node;
@@ -31,7 +32,7 @@ int uevent_listen(struct udev *udev);
 int uevent_dispatch(int (*store_uev)(struct uevent *, void * trigger_data),
                    void * trigger_data);
 bool uevent_is_mpath(const struct uevent *uev);
-void uevent_get_wwid(struct uevent *uev);
+void uevent_get_wwid(struct uevent *uev, const struct config *conf);
 
 int uevent_get_env_positive_int(const struct uevent *uev,
                                const char *attr);
index 66c6cf3..7039941 100644 (file)
@@ -20,8 +20,9 @@
 #ifndef _VERSION_H
 #define _VERSION_H
 
-#define VERSION_CODE 0x000809
-#define DATE_CODE    0x100216
+#define VERSION_CODE 0x000900
+/* MMDDYY, in hex */
+#define DATE_CODE    0x050316
 
 #define PROG    "multipath-tools"
 
index eb26970..2e4d483 100644 (file)
@@ -1,6 +1,7 @@
 include ../Makefile.inc
 
-CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir)
+CPPFLAGS += -I$(multipathdir) -I$(mpathpersistdir)
+CFLAGS += $(BIN_CFLAGS)
 LDFLAGS += $(BIN_LDFLAGS)
 
 LIBDEPS += -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath \
index c930499..bcb0453 100644 (file)
@@ -3,7 +3,8 @@
 #
 include ../Makefile.inc
 
-CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir)
+CPPFLAGS += -I$(multipathdir) -I$(mpathcmddir)
+CFLAGS += $(BIN_CFLAGS)
 LDFLAGS += $(BIN_LDFLAGS)
 LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathcmddir) -lmpathcmd \
        -lpthread -ldevmapper -ldl -ludev
index d09f62d..034dd2f 100644 (file)
@@ -988,11 +988,11 @@ main (int argc, char *argv[])
 
        libmp_udev_set_sync_support(1);
 
-       if (init_checkers(conf->multipath_dir)) {
+       if (init_checkers()) {
                condlog(0, "failed to initialize checkers");
                goto out;
        }
-       if (init_prio(conf->multipath_dir)) {
+       if (init_prio()) {
                condlog(0, "failed to initialize prioritizers");
                goto out;
        }
@@ -1001,7 +1001,7 @@ main (int argc, char *argv[])
                conf->enable_foreign = strdup("");
 
        /* Failing here is non-fatal */
-       init_foreign(conf->multipath_dir, conf->enable_foreign);
+       init_foreign(conf->enable_foreign);
        if (cmd == CMD_USABLE_PATHS) {
                r = check_usable_paths(conf, dev, dev_type) ?
                        RTVL_FAIL : RTVL_OK;
@@ -1060,6 +1060,11 @@ main (int argc, char *argv[])
        if (retries < 0)
                retries = conf->remove_retries;
        if (cmd == CMD_FLUSH_ONE) {
+               if (dm_is_mpath(dev) != 1) {
+                       condlog(0, "%s is not a multipath device", dev);
+                       r = RTVL_FAIL;
+                       goto out;
+               }
                r = dm_suspend_and_flush_map(dev, retries) ?
                    RTVL_FAIL : RTVL_OK;
                goto out;
index 605b46e..c2d34f1 100644 (file)
@@ -178,13 +178,7 @@ The default is: \fBno\fR
 .
 .TP
 .B multipath_dir
-This option is deprecated, and will be removed in a future release.
-Directory where the dynamic shared objects are stored. Defined at compile time,
-commonly \fI/lib64/multipath/\fR or \fI/lib/multipath/\fR.
-.RS
-.TP
-The default is: \fB<system dependent>\fR
-.RE
+This option is not supported any more. The value is ignored.
 .
 .
 .TP
@@ -264,6 +258,8 @@ If this option is configured and matches the device
 node name of a device, it overrides any other configured  methods for
 determining the WWID for this device.
 .PP
+This option cannot be changed during runtime with the multipathd \fBreconfigure\fR command.
+.PP
 The default is: \fB<unset>\fR. To enable uevent merging, set it e.g. to
 \(dqsd:ID_SERIAL dasd:ID_UID nvme:ID_WWN\(dq.
 .RE
@@ -287,12 +283,7 @@ The default is: \fBID_WWN\fR, for NVMe devices
 .
 .TP
 .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
+This option is not supported any more. The value is ignored.
 .
 .
 .TP
@@ -937,15 +928,7 @@ The default is: \fB<unset>\fR
 .
 .TP
 .B config_dir
-This option is deprecated, and will be removed in a future release.
-If set to anything other than "", multipath will search this directory
-alphabetically for file ending in ".conf" and it will read configuration
-information from them, just as if it was in \fI/etc/multipath.conf\fR.
-config_dir must either be "" or a fully qualified directory name.
-.RS
-.TP
-The default is: \fB/etc/multipath/conf.d/\fR
-.RE
+This option is not supported any more. The value is ignored.
 .
 .
 .TP
@@ -1507,6 +1490,18 @@ section:
 .SH "devices section"
 .\" ----------------------------------------------------------------------------
 .
+.TP 4
+.B Important:
+The built-in hardware device table of
+.I multipath-tools
+is created by members of the Linux community in the hope that it will be useful.
+The existence of an entry for a given storage product in the hardware table
+.B does not imply
+that the product vendor supports, or has tested, the product with
+.I multipath-tools
+in any way.
+.B Always consult the vendor\(aqs official documentation for support-related information.
+.PP
 \fImultipath-tools\fR have a built-in device table with reasonable defaults
 for more than 100 known multipath-capable storage devices. The devices section
 can be used to override these settings. If there are multiple matches for a
@@ -1610,8 +1605,6 @@ section:
 .TP
 .B uid_attribute
 .TP
-.B getuid_callout
-.TP
 .B path_selector
 .TP
 .B path_checker
@@ -1636,6 +1629,8 @@ section:
 .TP
 .B dev_loss_tmo
 .TP
+.B eh_deadline
+.TP
 .B flush_on_last_del
 .TP
 .B user_friendly_names
@@ -1692,8 +1687,6 @@ the values are taken from the \fIdevices\fR or \fIdefaults\fR sections:
 .TP
 .B uid_attribute
 .TP
-.B getuid_callout
-.TP
 .B path_selector
 .TP
 .B path_checker
@@ -1722,6 +1715,8 @@ the values are taken from the \fIdevices\fR or \fIdefaults\fR sections:
 .TP
 .B dev_loss_tmo
 .TP
+.B eh_deadline
+.TP
 .B user_friendly_names
 .TP
 .B retain_attached_hw_handler
@@ -1760,6 +1755,38 @@ the values are taken from the \fIdevices\fR or \fIdefaults\fR sections:
 .RE
 .PD
 .LP
+The overrides section also recognizes the optional \fIprotocol\fR subsection,
+and can contain multiple protocol subsections. Path devices are matched against
+the protocol subsection using the mandatory \fItype\fR attribute.  Attributes
+in a matching protocol subsection take precedence over attributes in the rest
+of the overrides section. If there are multiple matching protocol subsections,
+later entries take precedence.
+.TP
+.B protocol subsection
+The protocol subsection recognizes the following mandatory attribute:
+.RS
+.TP
+.B type
+The protocol string of the path device. The possible values are \fIscsi:fcp\fR,
+\fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR,
+\fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR,
+\fIscsi:unspec\fR, \fIccw\fR, \fIcciss\fR, \fInvme\fR, and \fIundef\fR. This is
+\fBnot\fR a regular expression. the path device protocol string must match
+exactly. The protocol that a path is using can be viewed by running
+\fBmultipathd show paths format "%d %P"\fR
+.LP
+The following attributes are optional; if not set, the default values are taken
+from the \fIoverrides\fR, \fIdevices\fR, or \fIdefaults\fR section:
+.sp 1
+.PD .1v
+.RS
+.TP
+.B fast_io_fail_tmo
+.TP
+.B dev_loss_tmo
+.TP
+.B eh_deadline
+.PD
 .
 .
 .\" ----------------------------------------------------------------------------
@@ -1776,18 +1803,9 @@ The WWID is generated by four methods (in the order of preference):
 The WWID is derived from udev attributes by matching the device node name; cf
 \fIuid_attrs\fR above.
 .TP
-.B getuid_callout
-Use the specified external program; cf \fIgetuid_callout\fR above.
-Care should be taken when using this method; the external program
-needs to be loaded from disk for execution, which might lead to
-deadlock situations in an all-paths-down scenario.
-.TP
 .B uid_attribute
 Use the value of the specified udev attribute; cf \fIuid_attribute\fR
-above. This method is preferred to \fIgetuid_callout\fR as multipath
-does not need to call any external programs here. However, under
-certain circumstances udev might not be able to generate the requested
-variable.
+above.
 .TP
 .B sysfs
 Try to determine the WWID from sysfs attributes.
index 9a49144..c937cd5 100644 (file)
@@ -1,30 +1,31 @@
 include ../Makefile.inc
 
 ifneq ($(call check_func,dm_task_get_errno,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
-       CFLAGS += -DLIBDM_API_GET_ERRNO
+       CPPFLAGS += -DLIBDM_API_GET_ERRNO
 endif
 
 ifneq ($(call check_var,ELS_DTAG_LNK_INTEGRITY,$(LINUX_HEADERS_INCDIR)/scsi/fc/fc_els.h),0)
-       CFLAGS += -DFPIN_EVENT_HANDLER
+       CPPFLAGS += -DFPIN_EVENT_HANDLER
        FPIN_SUPPORT = 1
 endif
 #
 # debugging stuff
 #
-#CFLAGS += -DLCKDBG
-#CFLAGS += -D_DEBUG_
-#CFLAGS += -DLOGDBG
-CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) \
-         -I$(mpathcmddir) -I$(thirdpartydir)
+#CPPFLAGS += -DLCKDBG
+#CPPFLAGS += -D_DEBUG_
+#CPPFLAGS += -DLOGDBG
+
+CPPFLAGS += -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir) -I$(thirdpartydir) \
+       $(shell $(PKGCONFIG) --modversion liburcu 2>/dev/null | \
+               awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }')
+CFLAGS += $(BIN_CFLAGS)
 LDFLAGS += $(BIN_LDFLAGS)
 LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \
           -L$(mpathcmddir) -lmpathcmd -ludev -ldl -lurcu -lpthread \
           -ldevmapper -lreadline
-CFLAGS += $(shell $(PKGCONFIG) --modversion liburcu 2>/dev/null | \
-       awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }')
 
 ifdef SYSTEMD
-       CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
+       CPPFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
        ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1)
                LIBDEPS += -lsystemd
        else
@@ -32,7 +33,7 @@ ifdef SYSTEMD
        endif
 endif
 ifeq ($(ENABLE_DMEVENTS_POLL),0)
-       CFLAGS += -DNO_DMEVENTS_POLL
+       CPPFLAGS += -DNO_DMEVENTS_POLL
 endif
 
 OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \
@@ -52,7 +53,7 @@ $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
        $(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(EXEC) $(LIBDEPS)
 
 cli_handlers.o:        cli_handlers.c
-       $(CC) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
+       $(CC) $(CPPFLAGS) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
 
 install:
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
index aaf5655..384ae31 100644 (file)
@@ -1,5 +1,6 @@
 #include <errno.h>
 #include <unistd.h>
+#include <stdint.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <libudev.h>
index f2c0b28..2f2b9d4 100644 (file)
@@ -37,6 +37,7 @@
 /*
  * libmultipath
  */
+#include "version.h"
 #include "parser.h"
 #include "vector.h"
 #include "config.h"
@@ -127,7 +128,7 @@ static int poll_dmevents = 0;
 static int poll_dmevents = 1;
 #endif
 /* Don't access this variable without holding config_lock */
-static volatile enum daemon_status running_state = DAEMON_INIT;
+static enum daemon_status running_state = DAEMON_INIT;
 /* Don't access this variable without holding config_lock */
 static bool __delayed_reconfig;
 pid_t daemon_pid;
@@ -155,16 +156,6 @@ int should_exit(void)
        return get_running_state() == DAEMON_SHUTDOWN;
 }
 
-static bool get_delayed_reconfig(void)
-{
-       bool val;
-
-       pthread_mutex_lock(&config_lock);
-       val = __delayed_reconfig;
-       pthread_mutex_unlock(&config_lock);
-       return val;
-}
-
 /*
  * global copy of vecs for use in sig handlers
  */
@@ -287,17 +278,6 @@ enum daemon_status wait_for_state_change_if(enum daemon_status oldstate,
 
 /* Don't access this variable without holding config_lock */
 static enum force_reload_types reconfigure_pending = FORCE_RELOAD_NONE;
-/* Only set while changing to DAEMON_CONFIGURE, and only access while
- * reconfiguring or scheduling a delayed reconfig in DAEMON_CONFIGURE */
-static volatile enum force_reload_types reload_type = FORCE_RELOAD_NONE;
-
-static void enable_delayed_reconfig(void)
-{
-       pthread_mutex_lock(&config_lock);
-       reconfigure_pending = reload_type;
-       __delayed_reconfig = true;
-       pthread_mutex_unlock(&config_lock);
-}
 
 /* must be called with config_lock held */
 static void __post_config_state(enum daemon_status state)
@@ -305,33 +285,11 @@ static void __post_config_state(enum daemon_status state)
        if (state != running_state && running_state != DAEMON_SHUTDOWN) {
                enum daemon_status old_state = running_state;
 
-               /*
-                * Handle a pending reconfigure request.
-                * DAEMON_IDLE is set from child() after reconfigure(),
-                * or from checkerloop() after completing checkers.
-                * In either case, child() will see DAEMON_CONFIGURE
-                * again and start another reconfigure cycle.
-                */
-               if (reconfigure_pending != FORCE_RELOAD_NONE &&
-                   state == DAEMON_IDLE &&
-                   (old_state == DAEMON_CONFIGURE ||
-                    old_state == DAEMON_RUNNING)) {
-                       /*
-                        * notify systemd of transient idle state, lest systemd
-                        * thinks the reload lasts forever.
-                        */
-                       do_sd_notify(old_state, DAEMON_IDLE);
-                       old_state = DAEMON_IDLE;
-                       state = DAEMON_CONFIGURE;
-               }
-               if (state == DAEMON_CONFIGURE) {
-                       reload_type = (reconfigure_pending == FORCE_RELOAD_YES) ? FORCE_RELOAD_YES : FORCE_RELOAD_WEAK;
-                       reconfigure_pending = FORCE_RELOAD_NONE;
-                       __delayed_reconfig = false;
-               }
                running_state = state;
                pthread_cond_broadcast(&config_cond);
                do_sd_notify(old_state, state);
+               condlog(4, "daemon state %s -> %s",
+                       daemon_status_msg[old_state], daemon_status_msg[state]);
        }
 }
 
@@ -343,6 +301,38 @@ void post_config_state(enum daemon_status state)
        pthread_cleanup_pop(1);
 }
 
+static bool unblock_reconfigure(void)
+{
+       bool was_delayed;
+
+       pthread_mutex_lock(&config_lock);
+       was_delayed = __delayed_reconfig;
+       if (was_delayed) {
+               __delayed_reconfig = false;
+               /*
+                * In IDLE state, make sure child() is woken up
+                * Otherwise it will wake up when state switches to IDLE
+                */
+               if (running_state == DAEMON_IDLE)
+                       __post_config_state(DAEMON_CONFIGURE);
+       }
+       pthread_mutex_unlock(&config_lock);
+       if (was_delayed)
+               condlog(3, "unblocked delayed reconfigure");
+       return was_delayed;
+}
+
+/*
+ * Make sure child() is woken up when a map is removed that multipathd
+ * is currently waiting for.
+ * Overrides libmultipath's weak symbol by the same name
+ */
+void remove_map_callback(struct multipath *mpp)
+{
+       if (mpp->wait_for_udev > 0)
+               unblock_reconfigure();
+}
+
 void schedule_reconfigure(enum force_reload_types requested_type)
 {
        pthread_mutex_lock(&config_lock);
@@ -825,12 +815,9 @@ ev_add_map (char * dev, const char * alias, struct vectors * vecs)
                dm_get_info(mpp->alias, &mpp->dmi);
                if (mpp->wait_for_udev) {
                        mpp->wait_for_udev = 0;
-                       if (get_delayed_reconfig() &&
-                           !need_to_delay_reconfig(vecs)) {
-                               condlog(2, "reconfigure (delayed)");
-                               schedule_reconfigure(FORCE_RELOAD_WEAK);
+                       if (!need_to_delay_reconfig(vecs) &&
+                           unblock_reconfigure())
                                return 0;
-                       }
                }
                /*
                 * Not really an error -- we generate our own uevent
@@ -1130,6 +1117,28 @@ out:
        return ret;
 }
 
+static int
+sysfs_get_ro (struct path *pp)
+{
+       int ro;
+       char buff[3]; /* Either "0\n\0" or "1\n\0" */
+
+       if (!pp->udev)
+               return -1;
+
+       if (sysfs_attr_get_value(pp->udev, "ro", buff, sizeof(buff)) <= 0) {
+               condlog(3, "%s: Cannot read ro attribute in sysfs", pp->dev);
+               return -1;
+       }
+
+       if (sscanf(buff, "%d\n", &ro) != 1 || ro < 0 || ro > 1) {
+               condlog(3, "%s: Cannot parse ro attribute", pp->dev);
+               return -1;
+       }
+
+       return ro;
+}
+
 /*
  * returns:
  * 0: added
@@ -1143,6 +1152,7 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map)
        int retries = 3;
        int start_waiter = 0;
        int ret;
+       int ro;
 
        /*
         * need path UID to go any further
@@ -1207,6 +1217,11 @@ rescan:
        /* persistent reservation check*/
        mpath_pr_event_handle(pp);
 
+       /* ro check - if new path is ro, force map to be ro as well */
+       ro = sysfs_get_ro(pp);
+       if (ro == 1)
+               mpp->force_readonly = 1;
+
        if (!need_do_map)
                return 0;
 
@@ -1446,28 +1461,6 @@ finish_path_init(struct path *pp, struct vectors * vecs)
        return -1;
 }
 
-static int
-sysfs_get_ro (struct path *pp)
-{
-       int ro;
-       char buff[3]; /* Either "0\n\0" or "1\n\0" */
-
-       if (!pp->udev)
-               return -1;
-
-       if (sysfs_attr_get_value(pp->udev, "ro", buff, sizeof(buff)) <= 0) {
-               condlog(3, "%s: Cannot read ro attribute in sysfs", pp->dev);
-               return -1;
-       }
-
-       if (sscanf(buff, "%d\n", &ro) != 1 || ro < 0 || ro > 1) {
-               condlog(3, "%s: Cannot parse ro attribute", pp->dev);
-               return -1;
-       }
-
-       return ro;
-}
-
 static bool
 needs_ro_update(struct multipath *mpp, int ro)
 {
@@ -1934,11 +1927,8 @@ missing_uev_wait_tick(struct vectors *vecs)
                }
        }
 
-       if (timed_out && get_delayed_reconfig() &&
-           !need_to_delay_reconfig(vecs)) {
-               condlog(2, "reconfigure (delayed)");
-               schedule_reconfigure(FORCE_RELOAD_WEAK);
-       }
+       if (timed_out && !need_to_delay_reconfig(vecs))
+               unblock_reconfigure();
 }
 
 static void
@@ -2577,7 +2567,6 @@ checkerloop (void *ap)
        rcu_register_thread();
        mlockall(MCL_CURRENT | MCL_FUTURE);
        vecs = (struct vectors *)ap;
-       condlog(2, "path checkers start up");
 
        /* Tweak start time for initial path check */
        get_monotonic_time(&last_time);
@@ -2714,8 +2703,8 @@ checkerloop (void *ap)
        return NULL;
 }
 
-int
-configure (struct vectors * vecs)
+static int
+configure (struct vectors * vecs, enum force_reload_types reload_type)
 {
        struct multipath * mpp;
        struct path * pp;
@@ -2846,12 +2835,59 @@ void rcu_free_config(struct rcu_head *head)
        free_config(conf);
 }
 
-int
-reconfigure (struct vectors * vecs)
+static bool reconfigure_check_uid_attrs(const struct _vector *old_attrs,
+                                       const struct _vector *new_attrs)
+{
+       int i;
+       char *old;
+
+       if (VECTOR_SIZE(old_attrs) != VECTOR_SIZE(new_attrs))
+               return true;
+
+       vector_foreach_slot(old_attrs, old, i) {
+               char *new = VECTOR_SLOT(new_attrs, i);
+
+               if (strcmp(old, new))
+                       return true;
+       }
+
+       return false;
+}
+
+static void reconfigure_check(struct config *old, struct config *new)
 {
-       struct config * old, *conf;
        int old_marginal_pathgroups;
 
+       old_marginal_pathgroups = old->marginal_pathgroups;
+       if ((old_marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) !=
+           (new->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
+               condlog(1, "multipathd must be restarted to turn %s fpin marginal paths",
+                       (old_marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)?
+                       "off" : "on");
+               new->marginal_pathgroups = old_marginal_pathgroups;
+       }
+
+       if (reconfigure_check_uid_attrs(&old->uid_attrs, &new->uid_attrs)) {
+               int i;
+               void *ptr;
+
+               condlog(1, "multipathd must be restarted to change uid_attrs, keeping old values");
+               vector_foreach_slot(&new->uid_attrs, ptr, i)
+                       free(ptr);
+               vector_reset(&new->uid_attrs);
+               new->uid_attrs = old->uid_attrs;
+
+               /* avoid uid_attrs being freed in rcu_free_config() */
+               old->uid_attrs.allocated = 0;
+               old->uid_attrs.slot = NULL;
+       }
+}
+
+static int
+reconfigure (struct vectors *vecs, enum force_reload_types reload_type)
+{
+       struct config * old, *conf;
+
        conf = load_config(DEFAULT_CONFIGFILE);
        if (!conf)
                return 1;
@@ -2859,6 +2895,7 @@ reconfigure (struct vectors * vecs)
        if (verbosity)
                libmp_verbosity = verbosity;
        setlogmask(LOG_UPTO(libmp_verbosity + 3));
+       condlog(2, "%s: setting up paths and maps", __func__);
 
        /*
         * free old map and path vectors ... they use old conf state
@@ -2880,22 +2917,15 @@ reconfigure (struct vectors * vecs)
        uxsock_timeout = conf->uxsock_timeout;
 
        old = rcu_dereference(multipath_conf);
-       old_marginal_pathgroups = old->marginal_pathgroups;
-       if ((old_marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) !=
-           (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
-               condlog(1, "multipathd must be restarted to turn %s fpin marginal paths",
-                       (old_marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)?
-                       "off" : "on");
-               conf->marginal_pathgroups = old_marginal_pathgroups;
-       }
+       reconfigure_check(old, conf);
+
        conf->sequence_nr = old->sequence_nr + 1;
        rcu_assign_pointer(multipath_conf, conf);
        call_rcu(&old->rcu, rcu_free_config);
 #ifdef FPIN_EVENT_HANDLER
        fpin_clean_marginal_dev_list(NULL);
 #endif
-       configure(vecs);
-
+       configure(vecs, reload_type);
 
        return 0;
 }
@@ -2938,18 +2968,18 @@ void
 handle_signals(bool nonfatal)
 {
        if (exit_sig) {
-               condlog(2, "exit (signal)");
+               condlog(3, "exit (signal)");
                exit_sig = 0;
                exit_daemon();
        }
        if (!nonfatal)
                return;
        if (reconfig_sig) {
-               condlog(2, "reconfigure (signal)");
+               condlog(3, "reconfigure (signal)");
                schedule_reconfigure(FORCE_RELOAD_WEAK);
        }
        if (log_reset_sig) {
-               condlog(2, "reset log (signal)");
+               condlog(3, "reset log (signal)");
                if (logsink == LOGSINK_SYSLOG)
                        log_thread_reset();
        }
@@ -3258,8 +3288,9 @@ child (__attribute__((unused)) void *param)
 
        post_config_state(DAEMON_START);
 
-       condlog(2, "--------start up--------");
-       condlog(2, "read " DEFAULT_CONFIGFILE);
+       condlog(2, "multipathd v%d.%d.%d%s: start up",
+               MULTIPATH_VERSION(VERSION_CODE), EXTRAVERSION);
+       condlog(3, "read " DEFAULT_CONFIGFILE);
 
        if (verbosity)
                libmp_verbosity = verbosity;
@@ -3277,17 +3308,17 @@ child (__attribute__((unused)) void *param)
                conf->bindings_read_only = bindings_read_only;
        uxsock_timeout = conf->uxsock_timeout;
        rcu_assign_pointer(multipath_conf, conf);
-       if (init_checkers(conf->multipath_dir)) {
+       if (init_checkers()) {
                condlog(0, "failed to initialize checkers");
                goto failed;
        }
-       if (init_prio(conf->multipath_dir)) {
+       if (init_prio()) {
                condlog(0, "failed to initialize prioritizers");
                goto failed;
        }
        /* Failing this is non-fatal */
 
-       init_foreign(conf->multipath_dir, conf->enable_foreign);
+       init_foreign(conf->enable_foreign);
 
        if (poll_dmevents)
                poll_dmevents = dmevent_poll_supported();
@@ -3317,11 +3348,10 @@ child (__attribute__((unused)) void *param)
        pthread_cleanup_push(config_cleanup, NULL);
        pthread_mutex_lock(&config_lock);
 
-       __post_config_state(DAEMON_IDLE);
        rc = pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs);
        if (!rc) {
                /* Wait for uxlsnr startup */
-               while (running_state == DAEMON_IDLE)
+               while (running_state == DAEMON_START)
                        pthread_cond_wait(&config_cond, &config_lock);
                state = running_state;
        }
@@ -3395,38 +3425,64 @@ child (__attribute__((unused)) void *param)
        pthread_attr_destroy(&misc_attr);
 
        while (1) {
+               int rc = 0;
+
                pthread_cleanup_push(config_cleanup, NULL);
                pthread_mutex_lock(&config_lock);
                while (running_state != DAEMON_CONFIGURE &&
-                      running_state != DAEMON_SHUTDOWN)
+                      running_state != DAEMON_SHUTDOWN &&
+                      /*
+                       * Check if another reconfigure request was scheduled
+                       * while we last ran reconfigure().
+                       * We have to test __delayed_reconfig here
+                       * to avoid a busy loop
+                       */
+                      (reconfigure_pending == FORCE_RELOAD_NONE
+                        || __delayed_reconfig))
                        pthread_cond_wait(&config_cond, &config_lock);
+
+               if (running_state != DAEMON_CONFIGURE &&
+                   running_state != DAEMON_SHUTDOWN)
+                       /* This sets running_state to DAEMON_CONFIGURE */
+                       __post_config_state(DAEMON_CONFIGURE);
                state = running_state;
                pthread_cleanup_pop(1);
                if (state == DAEMON_SHUTDOWN)
                        break;
-               if (state == DAEMON_CONFIGURE) {
-                       int rc = 0;
 
-                       pthread_cleanup_push(cleanup_lock, &vecs->lock);
-                       lock(&vecs->lock);
-                       pthread_testcancel();
-                       if (!need_to_delay_reconfig(vecs))
-                               rc = reconfigure(vecs);
-                       else
-                               enable_delayed_reconfig();
-                       lock_cleanup_pop(vecs->lock);
-                       if (!rc)
-                               post_config_state(DAEMON_IDLE);
-                       else {
-                               condlog(0, "fatal error applying configuration - aborting");
-                               exit_daemon();
-                       }
+               /* handle DAEMON_CONFIGURE */
+               pthread_cleanup_push(cleanup_lock, &vecs->lock);
+               lock(&vecs->lock);
+               pthread_testcancel();
+               if (!need_to_delay_reconfig(vecs)) {
+                       enum force_reload_types reload_type;
+
+                       pthread_mutex_lock(&config_lock);
+                       reload_type = reconfigure_pending == FORCE_RELOAD_YES ?
+                               FORCE_RELOAD_YES : FORCE_RELOAD_WEAK;
+                       reconfigure_pending = FORCE_RELOAD_NONE;
+                       __delayed_reconfig = false;
+                       pthread_mutex_unlock(&config_lock);
+
+                       rc = reconfigure(vecs, reload_type);
+               } else {
+                       pthread_mutex_lock(&config_lock);
+                       __delayed_reconfig = true;
+                       pthread_mutex_unlock(&config_lock);
+                       condlog(3, "delaying reconfigure()");
+               }
+               lock_cleanup_pop(vecs->lock);
+               if (!rc)
+                       post_config_state(DAEMON_IDLE);
+               else {
+                       condlog(0, "fatal error applying configuration - aborting");
+                       exit_daemon();
                }
        }
 
        exit_code = 0;
 failed:
-       condlog(2, "--------shut down-------");
+       condlog(2, "multipathd: shut down");
        /* All cleanup is done in the cleanup_child() exit handler */
        return sd_notify_exit(exit_code);
 }
index c07367f..645e356 100644 (file)
@@ -91,7 +91,6 @@ static LIST_HEAD(clients);
 static struct pollfd *polls;
 static int notify_fd = -1;
 static int idle_fd = -1;
-static char *watch_config_dir;
 
 static bool _socket_client_is_root(int fd)
 {
@@ -167,7 +166,6 @@ void uxsock_cleanup(void *arg)
 
        close(ux_sock);
        close(notify_fd);
-       free(watch_config_dir);
 
        list_for_each_entry_safe(client_loop, client_tmp, &clients, node) {
                dead_client(client_loop);
@@ -213,16 +211,7 @@ static void reset_watch(int notify_fd, struct watch_descriptors *wds,
                *sequence_nr = conf->sequence_nr;
                if (wds->conf_wd == -1)
                        conf_reset = 1;
-               if (!watch_config_dir || !conf->config_dir ||
-                   strcmp(watch_config_dir, conf->config_dir)) {
-                       dir_reset = 1;
-                       if (watch_config_dir)
-                               free(watch_config_dir);
-                       if (conf->config_dir)
-                               watch_config_dir = strdup(conf->config_dir);
-                       else
-                               watch_config_dir = NULL;
-               } else if (wds->dir_wd == -1)
+               if (wds->dir_wd == -1)
                        dir_reset = 1;
        }
        put_multipath_config(conf);
@@ -232,14 +221,12 @@ static void reset_watch(int notify_fd, struct watch_descriptors *wds,
                        inotify_rm_watch(notify_fd, wds->dir_wd);
                        wds->dir_wd = -1;
                }
-               if (watch_config_dir) {
-                       wds->dir_wd = inotify_add_watch(notify_fd,
-                                                       watch_config_dir,
-                                                       IN_CLOSE_WRITE |
-                                                       IN_DELETE | IN_ONLYDIR);
-                       if (wds->dir_wd == -1)
-                               condlog(3, "didn't set up notifications on %s: %m", watch_config_dir);
-               }
+               wds->dir_wd = inotify_add_watch(notify_fd,
+                                               CONFIG_DIR,
+                                               IN_CLOSE_WRITE |
+                                               IN_DELETE | IN_ONLYDIR);
+               if (wds->dir_wd == -1)
+                       condlog(3, "didn't set up notifications on %s: %m", CONFIG_DIR);
        }
        if (conf_reset) {
                wds->conf_wd = inotify_add_watch(notify_fd, DEFAULT_CONFIGFILE,
index 8cbc4b7..d20ef23 100644 (file)
@@ -1,5 +1,8 @@
 include ../Makefile.inc
 
+# directory where to run the tests
+TESTDIR := $(CURDIR)
+
 # Test special behavior of gcc 4.8 with nested initializers
 # gcc 4.8 compiles blacklist.c only with -Wno-missing-field-initializers
 TEST_MISSING_INITIALIZERS = $(shell \
@@ -8,8 +11,8 @@ TEST_MISSING_INITIALIZERS = $(shell \
        || echo -Wno-missing-field-initializers)
 W_MISSING_INITIALIZERS := $(call TEST_MISSING_INITIALIZERS)
 
-CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) \
-       -Wno-unused-parameter $(W_MISSING_INITIALIZERS)
+CPPFLAGS += -I$(multipathdir) -I$(mpathcmddir) -DTESTCONFDIR=\"$(TESTDIR)/conf.d\"
+CFLAGS += $(BIN_CFLAGS) -Wno-unused-parameter $(W_MISSING_INITIALIZERS)
 LIBDEPS += -L. -L$(mpathcmddir) -lmultipath -lmpathcmd -lcmocka
 
 TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
@@ -40,6 +43,9 @@ mpathvalid-test_FLAGS := -I$(mpathvaliddir)
 #    That may be necessary if functions called from the object file are wrapped
 #    (wrapping works only for symbols which are undefined after processing a
 #    linker input file).
+#    Some object files, e.g. "config.o", are compiled separately for the
+#    unit tests. Look for OBJS-U in libmultipath/Makefile. Make sure to use the
+#    unit test file, e.g. "config-test.o", in XYZ-test_OBJDEPS
 # XYZ-test_LIBDEPS: Additional libs to link for this test
 
 dmevents-test_OBJDEPS = ../libmultipath/devmapper.o
@@ -66,7 +72,7 @@ endif
 strbuf-test_OBJDEPS := ../libmultipath/strbuf.o
 
 %.o: %.c
-       $(CC) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $<
+       $(CC) $(CPPFLAGS) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $<
 
 lib/libchecktur.so:
        mkdir -p lib
@@ -91,7 +97,7 @@ valgrind_clean:
 
 clean: test_clean valgrind_clean dep_clean
        $(RM) $(TESTS:%=%-test) $(OBJS) *.o.wrap
-       $(RM) -rf lib
+       $(RM) -rf lib conf.d
 
 .SECONDARY: $(OBJS)
 
@@ -105,7 +111,7 @@ dep_clean:
                sort -u | tr '\n' ' ' >$@
 
 libmultipath.so.0:
-       $(MAKE) -C $(multipathdir) test-lib
+       make -C $(multipathdir) configdir=$(TESTDIR)/conf.d plugindir=$(TESTDIR)/lib test-lib
 
 # COLON will get expanded during second expansion below
 COLON:=:
index 9f7d388..20ccc47 100644 (file)
@@ -693,7 +693,7 @@ static void test_check_state_blksize(void **state)
        do_libcheck_reset(1);
 }
 
-/* test async checkers pending and getting resovled by another checker
+/* test async checkers pending and getting resolved by another checker
  * as well as the loops for getting multiple events */
 static void test_check_state_async(void **state)
 {
index 79bfa5f..bfaf613 100644 (file)
@@ -143,12 +143,11 @@ static int setup(void **state)
        }
        hwt->tmpname = strdup(buf);
 
-       snprintf(buf, sizeof(buf), "%s", tmplate);
-       if (mkdtemp(buf) == NULL) {
-               condlog(0, "mkdtemp (2): %s", strerror(errno));
+       hwt->dirname = strdup(TESTCONFDIR);
+       if (mkdir(hwt->dirname, 0744) != 0) {
+               condlog(0, "mkdir %s: %s", hwt->dirname, strerror(errno));
                goto err;
        }
-       hwt->dirname = strdup(buf);
 
        make_config_file_path(buf, sizeof(buf), hwt, -1);
        hwt->config_file = fopen(buf, "w+");
@@ -404,7 +403,7 @@ static const char _vendor[] = "vendor";
 static const char _product[] = "product";
 static const char _prio[] = "prio";
 static const char _checker[] = "path_checker";
-static const char _getuid[] = "getuid_callout";
+static const char _vpd_vnd[] = "vpd_vendor";
 static const char _uid_attr[] = "uid_attribute";
 static const char _bl_product[] = "product_blacklist";
 static const char _minio[] = "rr_min_io_rq";
@@ -436,7 +435,7 @@ static const struct key_value prio_emc = { _prio, "emc" };
 static const struct key_value prio_hds = { _prio, "hds" };
 static const struct key_value prio_rdac = { _prio, "rdac" };
 static const struct key_value chk_hp = { _checker, "hp_sw" };
-static const struct key_value gui_foo = { _getuid, "/tmp/foo" };
+static const struct key_value vpd_hp3par = { _vpd_vnd, "hp3par" };
 static const struct key_value uid_baz = { _uid_attr, "BAZ_ATTR" };
 static const struct key_value bl_bar = { _bl_product, "bar" };
 static const struct key_value bl_baz = { _bl_product, "baz" };
@@ -574,7 +573,7 @@ static void test_internal_nvme(const struct hwt_state *hwt)
        assert_ptr_not_equal(mp, NULL);
        TEST_PROP(checker_name(&pp->checker), NONE);
        TEST_PROP(pp->uid_attribute, DEFAULT_NVME_UID_ATTRIBUTE);
-       assert_int_equal(mp->pgpolicy, DEFAULT_PGPOLICY);
+       assert_int_equal(mp->pgpolicy, GROUP_BY_PRIO);
        assert_int_equal(mp->no_path_retry, DEFAULT_NO_PATH_RETRY);
        assert_int_equal(mp->retain_hwhandler, RETAIN_HWHANDLER_OFF);
 
@@ -587,7 +586,7 @@ static void test_internal_nvme(const struct hwt_state *hwt)
        assert_ptr_not_equal(mp, NULL);
        TEST_PROP(checker_name(&pp->checker), NONE);
        TEST_PROP(pp->uid_attribute, "ID_WWN");
-       assert_int_equal(mp->pgpolicy, MULTIBUS);
+       assert_int_equal(mp->pgpolicy, GROUP_BY_PRIO);
        assert_int_equal(mp->no_path_retry, NO_PATH_RETRY_QUEUE);
        assert_int_equal(mp->retain_hwhandler, RETAIN_HWHANDLER_OFF);
 }
@@ -756,31 +755,31 @@ static void test_regex_string_hwe(const struct hwt_state *hwt)
        /* foo:baz matches kv1 */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* boo:baz matches kv1 */
        pp = mock_path(vnd_boo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* .oo:ba. matches kv1 */
        pp = mock_path(vnd__oo.value, prd_ba_.value);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* .foo:(bar|baz|ba\.) doesn't match */
        pp = mock_path(vnd__oo.value, prd_ba_s.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
        /* foo:bar matches kv2 and kv1 */
-       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
@@ -788,7 +787,7 @@ static int setup_regex_string_hwe(void **state)
 {
        struct hwt_state *hwt = CHECK_STATE(state);
        const struct key_value kv1[] = { vnd_t_oo, prd_ba_s, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
 
        WRITE_TWO_DEVICES(hwt, kv1, kv2);
        SET_TEST_FUNC(hwt, test_regex_string_hwe);
@@ -813,39 +812,39 @@ static void test_regex_string_hwe_dir(const struct hwt_state *hwt)
        /* foo:baz matches kv1 */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* boo:baz matches kv1 */
        pp = mock_path(vnd_boo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* .oo:ba. matches kv1 */
        pp = mock_path(vnd__oo.value, prd_ba_.value);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* .oo:(bar|baz|ba\.)$ doesn't match */
        pp = mock_path(vnd__oo.value, prd_ba_s.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
        /* foo:bar matches kv2 */
-       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
        /* Later match takes prio */
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_regex_string_hwe_dir(void **state)
 {
        const struct key_value kv1[] = { vnd_t_oo, prd_ba_s, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
@@ -868,29 +867,29 @@ static void test_regex_2_strings_hwe_dir(const struct hwt_state *hwt)
        /* foo:baz matches kv1 */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* boo:baz doesn't match */
        pp = mock_path(vnd_boo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
        /* foo:bar matches kv2 and kv1 */
        pp = mock_path(vnd_foo.value, prd_bar.value);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(pp->uid_attribute, uid_baz.value);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* foo:barz matches kv3 and kv2 and kv1 */
-       pp = mock_path_flags(vnd_foo.value, prd_barz.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_barz.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_rdac.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
-       TEST_PROP(pp->uid_attribute, NULL);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
+       TEST_PROP(pp->uid_attribute, uid_baz.value);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
@@ -899,7 +898,7 @@ static int setup_regex_2_strings_hwe_dir(void **state)
        const struct key_value kv1[] = { vnd_foo, prd_ba_, prio_emc, chk_hp };
        const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, uid_baz };
        const struct key_value kv3[] = { vnd_foo, prd_barz,
-                                        prio_rdac, gui_foo };
+                                        prio_rdac, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        begin_config(hwt);
@@ -925,40 +924,40 @@ static void test_string_regex_hwe_dir(const struct hwt_state *hwt)
        struct path *pp;
 
        /* foo:bar matches kv2 and kv1 */
-       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* foo:baz matches kv1 */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* boo:baz matches kv1 */
        pp = mock_path(vnd_boo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* .oo:ba. matches kv1 */
        pp = mock_path(vnd__oo.value, prd_ba_.value);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* .oo:(bar|baz|ba\.)$ doesn't match */
        pp = mock_path(vnd__oo.value, prd_ba_s.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 }
 
 static int setup_string_regex_hwe_dir(void **state)
 {
        const struct key_value kv1[] = { vnd_t_oo, prd_ba_s, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        WRITE_TWO_DEVICES_W_DIR(hwt, kv2, kv1);
@@ -981,20 +980,20 @@ static void test_2_ident_strings_hwe(const struct hwt_state *hwt)
        /* foo:baz doesn't match */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
        /* foo:bar matches both */
-       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_strings_hwe(void **state)
 {
        const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        WRITE_TWO_DEVICES(hwt, kv1, kv2);
@@ -1016,20 +1015,20 @@ static void test_2_ident_strings_both_dir(const struct hwt_state *hwt)
        /* foo:baz doesn't match */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
        /* foo:bar matches both */
-       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_strings_both_dir(void **state)
 {
        const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        begin_config(hwt);
@@ -1056,13 +1055,13 @@ static void test_2_ident_strings_both_dir_w_prev(const struct hwt_state *hwt)
        /* foo:baz doesn't match */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
        /* foo:bar matches both */
-       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
@@ -1072,7 +1071,7 @@ static int setup_2_ident_strings_both_dir_w_prev(void **state)
 
        const struct key_value kv0[] = { vnd_foo, prd_bar };
        const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
 
        begin_config(hwt);
        begin_section_all(hwt, "devices");
@@ -1101,20 +1100,20 @@ static void test_2_ident_strings_hwe_dir(const struct hwt_state *hwt)
        /* foo:baz doesn't match */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
        /* foo:bar matches both */
-       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_strings_hwe_dir(void **state)
 {
        const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
@@ -1135,13 +1134,13 @@ static void test_3_ident_strings_hwe_dir(const struct hwt_state *hwt)
        /* foo:baz doesn't match */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
        /* foo:bar matches both */
-       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
@@ -1149,7 +1148,7 @@ static int setup_3_ident_strings_hwe_dir(void **state)
 {
        const struct key_value kv0[] = { vnd_foo, prd_bar };
        const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        begin_config(hwt);
@@ -1179,20 +1178,20 @@ static void test_2_ident_self_matching_re_hwe_dir(const struct hwt_state *hwt)
        /* foo:baz doesn't match */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
        /* foo:bar matches both */
-       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_self_matching_re_hwe_dir(void **state)
 {
        const struct key_value kv1[] = { vnd__oo, prd_bar, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd__oo, prd_bar, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd__oo, prd_bar, prio_hds, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
@@ -1214,20 +1213,20 @@ static void test_2_ident_self_matching_re_hwe(const struct hwt_state *hwt)
        /* foo:baz doesn't match */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
        /* foo:bar matches */
-       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_self_matching_re_hwe(void **state)
 {
        const struct key_value kv1[] = { vnd__oo, prd_bar, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd__oo, prd_bar, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd__oo, prd_bar, prio_hds, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        WRITE_TWO_DEVICES(hwt, kv1, kv2);
@@ -1251,20 +1250,20 @@ test_2_ident_not_self_matching_re_hwe_dir(const struct hwt_state *hwt)
        /* foo:baz doesn't match */
        pp = mock_path(vnd_foo.value, prd_baz.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
        /* foo:bar matches both */
-       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_not_self_matching_re_hwe_dir(void **state)
 {
        const struct key_value kv1[] = { vnd_t_oo, prd_bar, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd_t_oo, prd_bar, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd_t_oo, prd_bar, prio_hds, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
@@ -1288,26 +1287,26 @@ static void test_2_matching_res_hwe_dir(const struct hwt_state *hwt)
        /* foo:bar matches k1 only */
        pp = mock_path(vnd_foo.value, prd_bar.value);
        TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* foo:bay matches k1 and k2 */
-       pp = mock_path_flags(vnd_foo.value, "bay", USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, "bay", USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
        /* foo:baz matches k2 only. */
-       pp = mock_path_flags(vnd_foo.value, prd_baz.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_baz.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 }
 
 static int setup_2_matching_res_hwe_dir(void **state)
 {
        const struct key_value kv1[] = { vnd_foo, prd_barx, prio_emc, chk_hp };
-       const struct key_value kv2[] = { vnd_foo, prd_bazy, prio_hds, gui_foo };
+       const struct key_value kv2[] = { vnd_foo, prd_bazy, prio_hds, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
@@ -1329,12 +1328,12 @@ static void test_2_nonmatching_res_hwe_dir(const struct hwt_state *hwt)
        /* foo:bar doesn't match */
        pp = mock_path(vnd_foo.value, prd_bar.value);
        TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-       TEST_PROP(pp->getuid, NULL);
+       assert_int_equal(pp->vpd_vendor_id, 0);
        TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
-       pp = mock_path_flags(vnd_foo.value, prd_baz.value, USE_GETUID);
+       pp = mock_path_flags(vnd_foo.value, prd_baz.value, USE_VPD_VND);
        TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-       TEST_PROP(pp->getuid, gui_foo.value);
+       assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
        TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
@@ -1342,7 +1341,7 @@ static int setup_2_nonmatching_res_hwe_dir(void **state)
 {
        const struct key_value kv1[] = { vnd_foo, prd_bazy, prio_emc, chk_hp };
        const struct key_value kv2[] = { vnd_foo, prd_bazy1,
-                                        prio_hds, gui_foo };
+                                        prio_hds, vpd_hp3par };
        struct hwt_state *hwt = CHECK_STATE(state);
 
        WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
index cfe4bae..0230a88 100644 (file)
@@ -104,7 +104,7 @@ int __wrap_init_config(const char *file)
        int r = mock_type(int);
        struct config *conf;
 
-       assert_ptr_equal(file, DEFAULT_CONFIGFILE);
+       assert_string_equal(file, DEFAULT_CONFIGFILE);
        if (r != 0)
                return r;
 
index f5542ed..6dd3ee8 100644 (file)
@@ -4,6 +4,8 @@
 #include <cmocka.h>
 #include <libudev.h>
 #include <sys/sysmacros.h>
+#include <linux/hdreg.h>
+#include <scsi/sg.h>
 #include "debug.h"
 #include "util.h"
 #include "vector.h"
 #include "config.h"
 #include "discovery.h"
 #include "propsel.h"
+#include "unaligned.h"
 #include "test-lib.h"
 
-const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO);
+const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO|DI_SERIAL);
 const char default_devnode[] = "sdxTEST";
 const char default_wwid[] = "TEST-WWID";
 /* default_wwid should be a substring of default_wwid_1! */
@@ -47,15 +50,6 @@ int __wrap_open(const char *path, int flags, int mode)
        return __real_open(path, flags, mode);
 }
 
-int __wrap_execute_program(char *path, char *value, int len)
-{
-       char *val = mock_ptr_type(char *);
-
-       condlog(5, "%s: %s", __func__, val);
-       strlcpy(value, val, len);
-       return 0;
-}
-
 int __wrap_libmp_get_version(int which, unsigned int version[3])
 {
        unsigned int *vers = mock_ptr_type(unsigned int *);
@@ -181,6 +175,23 @@ ssize_t __wrap_sysfs_attr_get_value(struct udev_device *dev,
        return strlen(value);
 }
 
+/* mock vpd_pg80 */
+ssize_t __wrap_sysfs_bin_attr_get_value(struct udev_device *dev,
+                                       const char *attr_name,
+                                       char *buf, size_t sz)
+{
+       static const char serial[] = "mptest_serial";
+
+       assert_string_equal(attr_name, "vpd_pg80");
+       assert_in_range(sz, sizeof(serial) + 3, INT_MAX);
+       memset(buf, 0, sizeof(serial) + 3);
+       buf[1] = 0x80;
+       put_unaligned_be16(sizeof(serial) - 1, &buf[2]);
+       memcpy(&buf[4], serial, sizeof(serial) - 1);
+
+       return sizeof(serial) + 3;
+}
+
 int __wrap_checker_check(struct checker *c, int st)
 {
        condlog(5, "%s: %d", __func__, st);
@@ -195,6 +206,41 @@ int __wrap_prio_getprio(struct prio *p, struct path *pp, unsigned int tmo)
        return pr;
 }
 
+int __real_ioctl(int fd, unsigned long request, void *param);
+
+int __wrap_ioctl(int fd, unsigned long request, void *param)
+{
+       condlog(5, "%s: %lu", __func__, request);
+
+       if (request == HDIO_GETGEO) {
+               static const struct hd_geometry geom = {
+                       .heads = 4, .sectors = 31, .cylinders = 64, .start = 0
+               };
+               memcpy(param, &geom, sizeof(geom));
+               return 0;
+       } else if (request == SG_IO) {
+               /* mock hp3par special VPD */
+               struct sg_io_hdr *hdr = param;
+               static const char vpd_data[] = "VPD DATA";
+               unsigned char *buf = hdr->dxferp;
+               /* see vpd_vendor_pages in discovery.c */
+               const int HP3PAR_VPD = 0xc0;
+
+               if (hdr->interface_id == 'S' && hdr->cmdp[0] == 0x12
+                   && (hdr->cmdp[1] & 1) == 1 && hdr->cmdp[2] == HP3PAR_VPD) {
+                       assert_in_range(hdr->dxfer_len,
+                                       sizeof(vpd_data) + 3, INT_MAX);
+                       memset(buf, 0, hdr->dxfer_len);
+                       buf[1] = HP3PAR_VPD;
+                       put_unaligned_be16(sizeof(vpd_data), &buf[2]);
+                       memcpy(&buf[4], vpd_data, sizeof(vpd_data));
+                       hdr->status = 0;
+                       return 0;
+               }
+       }
+       return __real_ioctl(fd, request, param);
+}
+
 struct mocked_path *fill_mocked_path(struct mocked_path *mp,
                                     const char *vendor, const char *product,
                                     const char *rev, const char *wwid,
@@ -219,14 +265,13 @@ struct mocked_path *mocked_path_from_path(struct mocked_path *mp,
        mp->devnode = pp->dev;
        mp->flags = (prio_selected(&pp->prio) ? 0 : NEED_SELECT_PRIO) |
                (pp->fd < 0 ? NEED_FD : 0) |
-               (pp->getuid ? USE_GETUID : 0);
+               (pp->vpd_vendor_id != 0 ? USE_VPD_VND : 0);
        return mp;
 }
 
+static const char hbtl[] = "4:0:3:1";
 static void mock_sysfs_pathinfo(const struct mocked_path *mp)
 {
-       static const char hbtl[] = "4:0:3:1";
-
        will_return(__wrap_udev_device_get_subsystem, "scsi");
        will_return(__wrap_udev_device_get_sysname, hbtl);
        will_return(__wrap_udev_device_get_sysname, hbtl);
@@ -284,16 +329,18 @@ void mock_pathinfo(int mask, const struct mocked_path *mp)
        /* fake open() in pathinfo() */
        if (mp->flags & NEED_FD)
                will_return(__wrap_udev_device_get_devnode, _mocked_filename);
-       /* DI_SERIAL is unsupported */
-       assert_false(mask & DI_SERIAL);
+
+       /* scsi_ioctl_pathinfo() */
+       if (mask & DI_SERIAL) {
+               will_return(__wrap_udev_device_get_subsystem, "scsi");
+               will_return(__wrap_udev_device_get_sysname, hbtl);
+               will_return(__wrap_udev_device_get_sysname, hbtl);
+       }
 
        if (mask & DI_WWID) {
-               if (mp->flags & USE_GETUID)
-                       will_return(__wrap_execute_program, mp->wwid);
-               else
-                       /* get_udev_uid() */
-                       will_return(__wrap_udev_device_get_property_value,
-                                   mp->wwid);
+               /* get_udev_uid() */
+               will_return(__wrap_udev_device_get_property_value,
+                           mp->wwid);
        }
 
        if (mask & DI_CHECKER) {
index 7643ab6..efd03a7 100644 (file)
@@ -14,7 +14,7 @@ enum {
        BL_MASK = BL_BY_DEVNODE|BL_BY_DEVICE|BL_BY_WWID|BL_BY_PROPERTY,
        NEED_SELECT_PRIO = (1 << 8),
        NEED_FD         = (1 << 9),
-       USE_GETUID      = (1 << 10),
+       USE_VPD_VND     = (1 << 10),
        DEV_HIDDEN      = (1 << 11),
 };
 
index 7523fec..6a010ab 100644 (file)
@@ -111,7 +111,7 @@ static void test_uid_attrs(void **state)
 static void test_wwid(void **state)
 {
        struct uevent *uev = *state;
-       uevent_get_wwid(uev);
+       uevent_get_wwid(uev, &conf);
 
        assert_string_equal(uev->wwid, WWID);
 }