From 29488c521c1468feef23566129886e868f86d0b1 Mon Sep 17 00:00:00 2001 From: JinWang An Date: Fri, 13 Jan 2023 15:40:25 +0900 Subject: [PATCH] Imported Upstream version 0.9.0 --- .github/workflows/foreign.yaml | 7 +- Makefile.inc | 27 +- README.md | 8 +- kpartx/Makefile | 5 +- kpartx/devmapper.c | 6 +- libdmmp/Makefile | 4 +- libdmmp/test/Makefile | 2 +- libmpathvalid/Makefile | 3 +- libmultipath/Makefile | 47 ++-- libmultipath/callout.c | 221 --------------- libmultipath/callout.h | 7 - libmultipath/checkers.c | 20 +- libmultipath/checkers.h | 4 +- libmultipath/checkers/Makefile | 3 +- libmultipath/checkers/rdac.c | 2 +- libmultipath/checkers/tur.c | 23 +- libmultipath/config.c | 138 +++++++--- libmultipath/config.h | 20 +- libmultipath/configure.c | 5 +- libmultipath/defaults.h | 2 - libmultipath/devmapper.c | 16 +- libmultipath/dict.c | 209 +++++++++----- libmultipath/discovery.c | 198 +++++++------- libmultipath/discovery.h | 2 +- libmultipath/foreign.c | 11 +- libmultipath/foreign.h | 2 +- libmultipath/foreign/Makefile | 3 +- libmultipath/foreign/nvme.c | 1 - libmultipath/hwtable.c | 69 +++-- libmultipath/libmultipath.version | 3 +- libmultipath/list.h | 53 ++++ libmultipath/print.c | 67 +++-- libmultipath/prio.c | 19 +- libmultipath/prio.h | 6 +- libmultipath/prioritizers/Makefile | 5 +- libmultipath/prioritizers/iet.c | 2 +- libmultipath/propsel.c | 104 ++++--- libmultipath/propsel.h | 6 +- libmultipath/structs.c | 20 +- libmultipath/structs.h | 10 +- libmultipath/structs_vec.c | 6 + libmultipath/uevent.c | 424 +++++++++++++++++------------ libmultipath/uevent.h | 3 +- libmultipath/version.h | 5 +- mpathpersist/Makefile | 3 +- multipath/Makefile | 3 +- multipath/main.c | 11 +- multipath/multipath.conf.5 | 90 +++--- multipathd/Makefile | 25 +- multipathd/fpin_handlers.c | 1 + multipathd/main.c | 300 +++++++++++--------- multipathd/uxlsnr.c | 27 +- tests/Makefile | 16 +- tests/directio.c | 2 +- tests/hwtable.c | 155 ++++++----- tests/mpathvalid.c | 2 +- tests/test-lib.c | 89 ++++-- tests/test-lib.h | 2 +- tests/uevent.c | 2 +- 59 files changed, 1430 insertions(+), 1096 deletions(-) delete mode 100644 libmultipath/callout.c delete mode 100644 libmultipath/callout.h diff --git a/.github/workflows/foreign.yaml b/.github/workflows/foreign.yaml index c164cb3..e9ffd3d 100644 --- a/.github/workflows/foreign.yaml +++ b/.github/workflows/foreign.yaml @@ -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 diff --git a/Makefile.inc b/Makefile.inc index d24da43..bcd2212 100644 --- a/Makefile.inc +++ b/Makefile.inc @@ -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 \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 $< >$@ diff --git a/README.md b/README.md index 1547862..f06f8ce 100644 --- 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 diff --git a/kpartx/Makefile b/kpartx/Makefile index e9900fb..742d3bc 100644 --- a/kpartx/Makefile +++ b/kpartx/Makefile @@ -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 \ diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index 49ffd31..bf14c78 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -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, ¶ms); + if (dm_get_next_target(dmt, NULL, &start, &length, + &target_type, ¶ms) != NULL || !params) + /* more than one target or not found target */ + goto out; if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE) r = 0; diff --git a/libdmmp/Makefile b/libdmmp/Makefile index 00fc852..2e99b3e 100644 --- a/libdmmp/Makefile +++ b/libdmmp/Makefile @@ -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 diff --git a/libdmmp/test/Makefile b/libdmmp/test/Makefile index 20b3945..76b24d6 100644 --- a/libdmmp/test/Makefile +++ b/libdmmp/test/Makefile @@ -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) diff --git a/libmpathvalid/Makefile b/libmpathvalid/Makefile index fefeb2a..0a51925 100644 --- a/libmpathvalid/Makefile +++ b/libmpathvalid/Makefile @@ -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 diff --git a/libmultipath/Makefile b/libmultipath/Makefile index 77e954a..fb03200 100644 --- a/libmultipath/Makefile +++ b/libmultipath/Makefile @@ -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 index dac088c..0000000 --- a/libmultipath/callout.c +++ /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 -#include -#include -#include -#include -#include -#include -#include -#include - -#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 index ab648e8..0000000 --- a/libmultipath/callout.h +++ /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 */ diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c index 00554d6..fdb91e1 100644 --- a/libmultipath/checkers.c +++ b/libmultipath/checkers.c @@ -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; diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h index 5d25a42..ea1e8af 100644 --- a/libmultipath/checkers.h +++ b/libmultipath/checkers.h @@ -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 *); diff --git a/libmultipath/checkers/Makefile b/libmultipath/checkers/Makefile index 8e0ed5e..8d8e45e 100644 --- a/libmultipath/checkers/Makefile +++ b/libmultipath/checkers/Makefile @@ -3,7 +3,8 @@ # include ../../Makefile.inc -CFLAGS += $(LIB_CFLAGS) -I.. +CPPFLAGS += -I.. +CFLAGS += $(LIB_CFLAGS) LDFLAGS += -L.. -lmultipath LIBDEPS = -lmultipath -laio -lpthread -lrt diff --git a/libmultipath/checkers/rdac.c b/libmultipath/checkers/rdac.c index d924a9f..f7aaa30 100644 --- a/libmultipath/checkers/rdac.c +++ b/libmultipath/checkers/rdac.c @@ -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)); diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c index c93e462..1bcb757 100644 --- a/libmultipath/checkers/tur.c +++ b/libmultipath/checkers/tur.c @@ -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; diff --git a/libmultipath/config.c b/libmultipath/config.c index c595e76..ab8b26e 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -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; @@ -608,6 +622,51 @@ out: return 1; } +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) { @@ -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; diff --git a/libmultipath/config.h b/libmultipath/config.h index c73389b..36d4015 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -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 diff --git a/libmultipath/configure.c b/libmultipath/configure.c index eca11ba..09ae708 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -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); diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index 7d95413..7979f20 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -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/" diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index 2507f77..1748d25 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -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, ¶ms) != NULL) - /* more than one target */ + &target_type, ¶ms) != 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, ¶ms); + 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, " diff --git a/libmultipath/dict.c b/libmultipath/dict.c index 2af9764..ad049cc 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -115,32 +115,6 @@ set_str(vector strvec, void *ptr, const char *file, int line_nr) return 0; } -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) { @@ -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); diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index b969fba..0d8a558 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -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; diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h index 466af34..acd5179 100644 --- a/libmultipath/discovery.h +++ b/libmultipath/discovery.h @@ -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, diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c index e80eb3e..d01a5ef 100644 --- a/libmultipath/foreign.c +++ b/libmultipath/foreign.c @@ -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; diff --git a/libmultipath/foreign.h b/libmultipath/foreign.h index f547c14..b9cdb36 100644 --- a/libmultipath/foreign.h +++ b/libmultipath/foreign.h @@ -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) diff --git a/libmultipath/foreign/Makefile b/libmultipath/foreign/Makefile index f447a1c..42cea4d 100644 --- a/libmultipath/foreign/Makefile +++ b/libmultipath/foreign/Makefile @@ -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 diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c index 838e116..52ca56d 100644 --- a/libmultipath/foreign/nvme.c +++ b/libmultipath/foreign/nvme.c @@ -23,7 +23,6 @@ #include #include #include -#include #include #include #include diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index bd15710..513fa67 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -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 @@ -119,6 +120,12 @@ static struct hwentry default_hw[] = { .dev_loss = MAX_DEV_LOSS_TMO, .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", @@ -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, @@ -379,12 +386,6 @@ static struct hwentry default_hw[] = { .pgfailback = -FAILBACK_IMMEDIATE, .no_path_retry = 30, }, - { - /* EMC PowerMax NVMe */ - .vendor = "NVME", - .product = "^EMC PowerMax_", - .pgpolicy = MULTIBUS, - }, { /* PowerStore */ .vendor = "DellEMC", @@ -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 */ { - /* 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, @@ -673,6 +680,12 @@ static struct hwentry default_hw[] = { .pgfailback = -FAILBACK_IMMEDIATE, .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", @@ -726,6 +739,12 @@ static struct hwentry default_hw[] = { .product = "(RamSan|FlashSystem)", .pgpolicy = MULTIBUS, }, + { + /* FlashSystem(RamSan) NVMe */ + .vendor = "NVMe", + .product = "FlashSystem", + .no_path_retry = NO_PATH_RETRY_FAIL, + }, { /* (DDN) DCS9900, SONAS 2851-DR1 */ .vendor = "IBM", @@ -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 */ diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version index 216f0ee..b3690ac 100644 --- a/libmultipath/libmultipath.version +++ b/libmultipath/libmultipath.version @@ -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; diff --git a/libmultipath/list.h b/libmultipath/list.h index ced021f..248f72b 100644 --- a/libmultipath/list.h +++ b/libmultipath/list.h @@ -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. @@ -334,6 +363,30 @@ static inline void list_splice_tail_init(struct list_head *list, &pos->member != (head); \ 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. diff --git a/libmultipath/print.c b/libmultipath/print.c index bf88f30..68a793e 100644 --- a/libmultipath/print.c +++ b/libmultipath/print.c @@ -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); @@ -1423,6 +1406,52 @@ int snprint_multipath_topology_json (struct strbuf *buff, return get_strbuf_len(buff) - initial_len; } +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; diff --git a/libmultipath/prio.c b/libmultipath/prio.c index ef68cd0..cdd3752 100644 --- a/libmultipath/prio.c +++ b/libmultipath/prio.c @@ -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; diff --git a/libmultipath/prio.h b/libmultipath/prio.h index 66c7936..184bf65 100644 --- a/libmultipath/prio.h +++ b/libmultipath/prio.h @@ -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 *); diff --git a/libmultipath/prioritizers/Makefile b/libmultipath/prioritizers/Makefile index 16c6397..a5ab5e1 100644 --- a/libmultipath/prioritizers/Makefile +++ b/libmultipath/prioritizers/Makefile @@ -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) diff --git a/libmultipath/prioritizers/iet.c b/libmultipath/prioritizers/iet.c index e98773c..167a46b 100644 --- a/libmultipath/prioritizers/iet.c +++ b/libmultipath/prioritizers/iet.c @@ -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; diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index 1419ec6..50d0b5c 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -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; } diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h index 72a7e33..152ca44 100644 --- a/libmultipath/propsel.h +++ b/libmultipath/propsel.h @@ -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); diff --git a/libmultipath/structs.c b/libmultipath/structs.c index 4b62da5..49621cb 100644 --- a/libmultipath/structs.c +++ b/libmultipath/structs.c @@ -20,6 +20,24 @@ #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; diff --git a/libmultipath/structs.h b/libmultipath/structs.h index d94f93a..a6a0944 100644 --- a/libmultipath/structs.h +++ b/libmultipath/structs.h @@ -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; diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index 6c23df8..a69f064 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -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; diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index 29e2557..5793af9 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -51,10 +51,7 @@ #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); diff --git a/libmultipath/uevent.h b/libmultipath/uevent.h index 61ca1b5..53a7ca2 100644 --- a/libmultipath/uevent.h +++ b/libmultipath/uevent.h @@ -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); diff --git a/libmultipath/version.h b/libmultipath/version.h index 66c6cf3..7039941 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -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" diff --git a/mpathpersist/Makefile b/mpathpersist/Makefile index eb26970..2e4d483 100644 --- a/mpathpersist/Makefile +++ b/mpathpersist/Makefile @@ -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 \ diff --git a/multipath/Makefile b/multipath/Makefile index c930499..bcb0453 100644 --- a/multipath/Makefile +++ b/multipath/Makefile @@ -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 diff --git a/multipath/main.c b/multipath/main.c index d09f62d..034dd2f 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -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; diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 index 605b46e..c2d34f1 100644 --- a/multipath/multipath.conf.5 +++ b/multipath/multipath.conf.5 @@ -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\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\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\fR -.RE +This option is not supported any more. The value is ignored. . . .TP @@ -937,15 +928,7 @@ The default is: \fB\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. diff --git a/multipathd/Makefile b/multipathd/Makefile index 9a49144..c937cd5 100644 --- a/multipathd/Makefile +++ b/multipathd/Makefile @@ -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) diff --git a/multipathd/fpin_handlers.c b/multipathd/fpin_handlers.c index aaf5655..384ae31 100644 --- a/multipathd/fpin_handlers.c +++ b/multipathd/fpin_handlers.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/multipathd/main.c b/multipathd/main.c index f2c0b28..2f2b9d4 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -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); } diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c index c07367f..645e356 100644 --- a/multipathd/uxlsnr.c +++ b/multipathd/uxlsnr.c @@ -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, diff --git a/tests/Makefile b/tests/Makefile index 8cbc4b7..d20ef23 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -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:=: diff --git a/tests/directio.c b/tests/directio.c index 9f7d388..20ccc47 100644 --- a/tests/directio.c +++ b/tests/directio.c @@ -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) { diff --git a/tests/hwtable.c b/tests/hwtable.c index 79bfa5f..bfaf613 100644 --- a/tests/hwtable.c +++ b/tests/hwtable.c @@ -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); diff --git a/tests/mpathvalid.c b/tests/mpathvalid.c index cfe4bae..0230a88 100644 --- a/tests/mpathvalid.c +++ b/tests/mpathvalid.c @@ -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; diff --git a/tests/test-lib.c b/tests/test-lib.c index f5542ed..6dd3ee8 100644 --- a/tests/test-lib.c +++ b/tests/test-lib.c @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "debug.h" #include "util.h" #include "vector.h" @@ -12,9 +14,10 @@ #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) { diff --git a/tests/test-lib.h b/tests/test-lib.h index 7643ab6..efd03a7 100644 --- a/tests/test-lib.h +++ b/tests/test-lib.h @@ -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), }; diff --git a/tests/uevent.c b/tests/uevent.c index 7523fec..6a010ab 100644 --- a/tests/uevent.c +++ b/tests/uevent.c @@ -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); } -- 2.34.1