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: >
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
# 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
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
echo "$(2)"; \
fi)
+# "make" on some distros will fail on explicit '#' or '\#' in the program text below
+__HASH__ := \#
+# Check if _DFORTIFY_SOURCE=3 is supported.
+# On some distros (e.g. Debian Buster) it will be falsely reported as supported
+# but it doesn't seem to make a difference wrt the compilation result.
+FORTIFY_OPT := $(shell \
+ if /bin/echo -e '$(__HASH__)include <string.h>\nint main(void) { return 0; }' | \
+ $(CC) -o /dev/null -c -O2 -Werror -D_FORTIFY_SOURCE=3 -xc - 2>/dev/null; \
+ then \
+ echo "-D_FORTIFY_SOURCE=3"; \
+ else \
+ echo "-D_FORTIFY_SOURCE=2"; \
+ fi)
+
STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)
ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,)
WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered -Wno-error=clobbered,)
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
%.o: %.c
@echo building $@ because of $?
- $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
+ $(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
%.abi: %.so.0
abidw $< >$@
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
#
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 \
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;
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
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)
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
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
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)
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
+++ /dev/null
-/*
- * Source: copy of the udev package source file
- *
- * Copyrights of the source file apply
- * Copyright (c) 2004 Christophe Varoqui
- */
-#include <stdio.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <sys/wait.h>
-#include <errno.h>
-
-#include "checkers.h"
-#include "vector.h"
-#include "structs.h"
-#include "util.h"
-#include "callout.h"
-#include "debug.h"
-
-int execute_program(char *path, char *value, int len)
-{
- int retval;
- int count;
- int status;
- int fds[2], null_fd;
- pid_t pid;
- char *pos;
- char arg[CALLOUT_MAX_SIZE];
- int argc = sizeof(arg) / 2;
- char *argv[argc + 1];
- int i;
-
- i = 0;
-
- if (strchr(path, ' ')) {
- strlcpy(arg, path, sizeof(arg));
- pos = arg;
- while (pos != NULL && i < argc) {
- if (pos[0] == '\'') {
- /* don't separate if in apostrophes */
- pos++;
- argv[i] = strsep(&pos, "\'");
- while (pos[0] == ' ')
- pos++;
- } else {
- argv[i] = strsep(&pos, " ");
- }
- i++;
- }
- } else {
- argv[i++] = path;
- }
- argv[i] = NULL;
-
- retval = pipe(fds);
-
- if (retval != 0) {
- condlog(0, "error creating pipe for callout: %s", strerror(errno));
- return -1;
- }
-
- pid = fork();
-
- switch(pid) {
- case 0:
- /* child */
-
- /* dup write side of pipe to STDOUT */
- if (dup2(fds[1], STDOUT_FILENO) < 0) {
- condlog(1, "failed to dup2 stdout: %m");
- return -1;
- }
- close(fds[0]);
- close(fds[1]);
-
- /* Ignore writes to stderr */
- null_fd = open("/dev/null", O_WRONLY);
- if (null_fd > 0) {
- if (dup2(null_fd, STDERR_FILENO) < 0)
- condlog(1, "failed to dup2 stderr: %m");
- close(null_fd);
- }
-
- retval = execv(argv[0], argv);
- condlog(0, "error execing %s : %s", argv[0], strerror(errno));
- exit(-1);
- case -1:
- condlog(0, "fork failed: %s", strerror(errno));
- close(fds[0]);
- close(fds[1]);
- return -1;
- default:
- /* parent reads from fds[0] */
- close(fds[1]);
- retval = 0;
- i = 0;
- while (1) {
- count = read(fds[0], value + i, len - i-1);
- if (count <= 0)
- break;
-
- i += count;
- if (i >= len-1) {
- condlog(0, "not enough space for response from %s", argv[0]);
- retval = -1;
- break;
- }
- }
-
- if (count < 0) {
- condlog(0, "no response from %s", argv[0]);
- retval = -1;
- }
-
- if (i > 0 && value[i-1] == '\n')
- i--;
- value[i] = '\0';
-
- wait(&status);
- close(fds[0]);
-
- retval = -1;
- if (WIFEXITED(status)) {
- status = WEXITSTATUS(status);
- if (status == 0)
- retval = 0;
- else
- condlog(0, "%s exited with %d", argv[0], status);
- }
- else if (WIFSIGNALED(status))
- condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status));
- else
- condlog(0, "%s terminated abnormally", argv[0]);
- }
- return retval;
-}
-
-int apply_format(char * string, char * cmd, struct path * pp)
-{
- char * pos;
- char * dst;
- char * p;
- char * q;
- int len;
- int myfree;
-
- if (!string)
- return 1;
-
- if (!cmd)
- return 1;
-
- dst = cmd;
- p = dst;
- pos = strchr(string, '%');
- myfree = CALLOUT_MAX_SIZE;
-
- if (!pos) {
- strcpy(dst, string);
- return 0;
- }
-
- len = (int) (pos - string) + 1;
- myfree -= len;
-
- if (myfree < 2)
- return 1;
-
- snprintf(p, len, "%s", string);
- p += len - 1;
- pos++;
-
- switch (*pos) {
- case 'n':
- len = strlen(pp->dev) + 1;
- myfree -= len;
-
- if (myfree < 2)
- return 1;
-
- snprintf(p, len, "%s", pp->dev);
- for (q = p; q < p + len; q++) {
- if (q && *q == '!')
- *q = '/';
- }
- p += len - 1;
- break;
- case 'd':
- len = strlen(pp->dev_t) + 1;
- myfree -= len;
-
- if (myfree < 2)
- return 1;
-
- snprintf(p, len, "%s", pp->dev_t);
- p += len - 1;
- break;
- default:
- break;
- }
- pos++;
-
- if (!*pos) {
- condlog(3, "formatted callout = %s", dst);
- return 0;
- }
-
- len = strlen(pos) + 1;
- myfree -= len;
-
- if (myfree < 2)
- return 1;
-
- snprintf(p, len, "%s", pos);
- condlog(3, "reformatted callout = %s", dst);
- return 0;
-}
+++ /dev/null
-#ifndef _CALLOUT_H
-#define _CALLOUT_H
-
-int execute_program(char *, char *, int);
-int apply_format (char *, char *, struct path *);
-
-#endif /* _CALLOUT_H */
#include "vector.h"
#include "util.h"
+static const char * const checker_dir = MULTIPATH_DIR;
+
struct checker_class {
struct list_head node;
void *handle;
}
}
-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;
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);
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;
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)
(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[] = {
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;
}
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 **);
*/
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 *);
#
include ../../Makefile.inc
-CFLAGS += $(LIB_CFLAGS) -I..
+CPPFLAGS += -I..
+CFLAGS += $(LIB_CFLAGS)
LDFLAGS += -L.. -lmultipath
LIBDEPS = -lmultipath -laio -lpthread -lrt
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));
#define TUR_CMD_LEN 6
#define HEAVY_CHECK_COUNT 10
+#define MAX_NR_TIMEOUTS 1
enum {
MSG_TUR_RUNNING = CHECKER_FIRST_MSGID,
int holders; /* uatomic access only */
int msgid;
struct checker_context ctx;
+ unsigned int nr_timeouts;
};
int libcheck_init (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
*/
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;
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)
{
if (hwe->revision)
free(hwe->revision);
- if (hwe->getuid)
- free(hwe->getuid);
-
if (hwe->uid_attribute)
free(hwe->uid_attribute);
if (hwe->bl_product)
free(hwe->bl_product);
+ if (hwe->pctable)
+ free_pctable(hwe->pctable);
+
free(hwe);
}
if (mpe->selector)
free(mpe->selector);
- if (mpe->getuid)
- free(mpe->getuid);
-
if (mpe->uid_attribute)
free(mpe->uid_attribute);
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)
{
}
#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);
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);
merge_num(uid);
merge_num(gid);
merge_num(mode);
-
- return 0;
}
void merge_mptable(vector mptable)
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;
}
static void
+validate_pctable(struct hwentry *ovr, int idx, const char *table_desc)
+{
+ struct pcentry *pce;
+
+ if (!ovr || !ovr->pctable)
+ return;
+
+ vector_foreach_slot_after(ovr->pctable, pce, idx) {
+ if (pce->type == PCE_INVALID) {
+ condlog(0, "protocol section in %s missing type",
+ table_desc);
+ vector_del_slot(ovr->pctable, idx--);
+ free(pce);
+ }
+ }
+
+ if (VECTOR_SIZE(ovr->pctable) == 0) {
+ vector_free(ovr->pctable);
+ ovr->pctable = NULL;
+ }
+}
+
+static void
+merge_pctable(struct hwentry *ovr)
+{
+ struct pcentry *pce1, *pce2;
+ int i, j;
+
+ if (!ovr || !ovr->pctable)
+ return;
+
+ vector_foreach_slot(ovr->pctable, pce1, i) {
+ j = i + 1;
+ vector_foreach_slot_after(ovr->pctable, pce2, j) {
+ if (pce1->type != pce2->type)
+ continue;
+ merge_pce(pce2,pce1);
+ vector_del_slot(ovr->pctable, i--);
+ free(pce1);
+ break;
+ }
+ }
+}
+
+static void
factorize_hwtable (vector hw, int n, const char *table_desc)
{
struct hwentry *hwe1, *hwe2;
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);
if (conf->checker_name)
free(conf->checker_name);
- if (conf->config_dir)
- free(conf->config_dir);
if (conf->enable_foreign)
free(conf->enable_foreign);
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",
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);
}
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;
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
goto out;
}
+ merge_pctable(conf->overrides);
merge_mptable(conf->mptable);
merge_blacklist(conf->blist_devnode);
merge_blacklist(conf->blist_property);
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;
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;
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;
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;
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;
char * checker_name;
char * alias_prefix;
char * partition_delim;
- char * config_dir;
int prkey_source;
int all_tg_pt;
struct be64 reservation_key;
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);
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
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);
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);
#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"
#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/"
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,
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)
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, "
}
static int
-set_dir(vector strvec, void *ptr, const char *file, int line_nr)
-{
- char **str_ptr = (char **)ptr;
- char *old_str = *str_ptr;
- struct stat sb;
-
- *str_ptr = set_value(strvec);
- if (!*str_ptr) {
- free(old_str);
- return 1;
- }
- if ((*str_ptr)[0] != '/'){
- condlog(1, "%s line %d, %s is not an absolute directory path. Ignoring", file, line_nr, *str_ptr);
- *str_ptr = old_str;
- } else {
- if (stat(*str_ptr, &sb) == 0 && S_ISDIR(sb.st_mode))
- free(old_str);
- else {
- condlog(1, "%s line %d, %s is not an existing directory. Ignoring", file, line_nr, *str_ptr);
- *str_ptr = old_str;
- }
- }
- return 0;
-}
-
-static int
set_path(vector strvec, void *ptr, const char *file, int line_nr)
{
char **str_ptr = (char **)ptr;
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, \
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)
{
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)
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;
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)
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 \
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)
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)
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)
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
*/
return 0;
}
-#define __deprecated
+declare_deprecated_handler(getuid_callout)
/*
* If you add or remove a keyword also update multipath/multipath.conf.5
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);
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);
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);
&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);
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
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);
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);
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);
#include "structs.h"
#include "config.h"
#include "blacklist.h"
-#include "callout.h"
#include "debug.h"
#include "propsel.h"
#include "sg_include.h"
}
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);
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);
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",
* 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) {
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) {
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) {
}
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);
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, "
}
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);
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 "
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;
}
}
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;
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);
}
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;
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,
#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;
}
}
-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;
}
}
- 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",
__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) {
return r;
}
-int init_foreign(const char *multipath_dir, const char *enable)
+int init_foreign(const char *enable)
{
int ret;
}
pthread_cleanup_push(unlock_foreigns, NULL);
- ret = _init_foreign(multipath_dir, enable);
+ ret = _init_foreign(enable);
pthread_cleanup_pop(1);
return ret;
* @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)
TOPDIR=../..
include ../../Makefile.inc
-CFLAGS += $(LIB_CFLAGS) -I.. -I$(nvmedir)
+CPPFLAGS += -I.. -I$(nvmedir)
+CFLAGS += $(LIB_CFLAGS)
LDFLAGS += -L..
LIBDEPS = -lmultipath -ludev -lpthread -lrt
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
-#include <libudev.h>
#include <pthread.h>
#include <limits.h>
#include <dirent.h>
.product = ".*",
.uid_attribute = DEFAULT_NVME_UID_ATTRIBUTE,
.checker_name = NONE,
- .retain_hwhandler = RETAIN_HWHANDLER_OFF,
+ .pgpolicy = GROUP_BY_PRIO,
+ .pgfailback = -FAILBACK_IMMEDIATE,
},
/*
* Apple
.vpd_vendor_id = VPD_VP_HP3PAR,
},
{
+ /* Alletra 9000 NVMe */
+ .vendor = "NVME",
+ .product = "HPE Alletra",
+ .no_path_retry = NO_PATH_RETRY_QUEUE,
+ },
+ {
/* RA8000 / ESA12000 */
.vendor = "DEC",
.product = "HSG80",
},
{
/* 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,
{
/* SAN Virtualization Services Platform */
.vendor = "HP",
- .product = "HSVX700",
+ .product = "(HSVX700|HSVX740)",
.hwhandler = "1 alua",
.pgpolicy = GROUP_BY_PRIO,
.pgfailback = -FAILBACK_IMMEDIATE,
.no_path_retry = 30,
},
{
- /* EMC PowerMax NVMe */
- .vendor = "NVME",
- .product = "^EMC PowerMax_",
- .pgpolicy = MULTIBUS,
- },
- {
/* PowerStore */
.vendor = "DellEMC",
.product = "PowerStore",
.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",
* Maintainer: Matthias Rudolph <Matthias.Rudolph@hitachivantara.com>
*/
{
- /* USP-V, HUS VM, VSP, VSP G1X00 and VSP GX00 families / HP XP */
- .vendor = "(HITACHI|HP)",
+ /* USP-V, HUS VM, VSP, VSP G1X00 and VSP GX00 families / HPE XP */
+ .vendor = "(HITACHI|HP|HPE)",
.product = "^OPEN-",
.pgpolicy = MULTIBUS,
},
},
{
// 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,
.prio_name = PRIO_ALUA,
},
{
+ /* FlashSystem(Storwize/SVC) NVMe */
+ .vendor = "NVME",
+ .product = "IBM[ ]+2145",
+ .no_path_retry = NO_PATH_RETRY_QUEUE,
+ },
+ {
/* PAV DASD ECKD */
.vendor = "IBM",
.product = "S/390 DASD ECKD",
.pgpolicy = MULTIBUS,
},
{
+ /* FlashSystem(RamSan) NVMe */
+ .vendor = "NVMe",
+ .product = "FlashSystem",
+ .no_path_retry = NO_PATH_RETRY_FAIL,
+ },
+ {
/* (DDN) DCS9900, SONAS 2851-DR1 */
.vendor = "IBM",
.product = "^(DCS9900|2851)",
.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,
},
/*
.fast_io_fail = 10,
.max_sectors_kb = 4096,
},
+ {
+ /* FlashArray NVMe */
+ .vendor = "NVME",
+ .product = "Pure Storage FlashArray",
+ .no_path_retry = 10,
+ },
/*
* Huawei
*/
.pgfailback = -FAILBACK_IMMEDIATE,
.no_path_retry = 15,
},
+ {
+ /* OceanStor NVMe */
+ .vendor = "NVME",
+ .product = "Huawei-XSG1",
+ .checker_name = DIRECTIO,
+ .no_path_retry = 12,
+ },
/*
* Kove
*/
* The new version inherits the previous ones.
*/
-LIBMULTIPATH_14.0.0 {
+LIBMULTIPATH_15.0.0 {
global:
/* symbols referenced by multipath and multipathd */
add_foreign;
remember_wwid;
remove_map;
remove_map_by_alias;
+ remove_map_callback;
remove_maps;
remove_wwid;
replace_wwids;
#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.
pos = n, n = list_entry(n->member.prev, typeof(*n), member))
/**
+ * list_for_some_entry - iterate list from the given begin node to the given end node
+ * @pos: the type * to use as a loop counter.
+ * @from: the begin node of the iteration.
+ * @to: the end node of the iteration.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_some_entry(pos, from, to, member) \
+ for (pos = list_entry((from)->next, typeof(*pos), member); \
+ &pos->member != (to); \
+ pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_some_entry_reverse - iterate backwards list from the given begin node to the given end node
+ * @pos: the type * to use as a loop counter.
+ * @from: the begin node of the iteration.
+ * @to: the end node of the iteration.
+ * @member: the name of the list_struct within the struct.
+ */
+#define list_for_some_entry_reverse(pos, from, to, member) \
+ for (pos = list_entry((from)->prev, typeof(*pos), member); \
+ &pos->member != (to); \
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
* list_for_some_entry_safe - iterate list from the given begin node to the given end node safe against removal of list entry
* @pos: the type * to use as a loop counter.
* @n: another type * to use as temporary storage
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);
}
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)
{
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;
#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,
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[] = {
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;
}
}
-static struct prio * prio_lookup (char * name)
+static struct prio *prio_lookup(const char *name)
{
struct prio * p;
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;
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);
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;
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;
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 *);
#
include ../../Makefile.inc
-CFLAGS += $(LIB_CFLAGS) -I..
+CPPFLAGS += -I..
+CFLAGS += $(LIB_CFLAGS)
LDFLAGS += -L..
LIBDEPS = -lmultipath -lm -lpthread -lrt
ifneq ($(call check_file,$(LINUX_HEADERS_INCDIR)/linux/nvme_ioctl.h),0)
LIBS += libprioana.so
- CFLAGS += -I../nvme
+ CPPFLAGS += -I../nvme
endif
all: $(LIBS)
// 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;
"(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[] =
} \
} 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;
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) {
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;
}
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)";
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];
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; \
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; \
} \
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:
/*
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;
}
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;
}
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);
#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)
{
pp->dmstate = PSTATE_UNDEF;
pp->uid_attribute = NULL;
- pp->getuid = NULL;
if (checker_selected(&pp->checker))
checker_put(&pp->checker);
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;
*/
#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
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;
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;
int minio;
int flush_on_last_del;
int attribute_flags;
- int fast_io_fail;
int retain_hwhandler;
int deferred_remove;
bool in_recovery;
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;
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;
#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);
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;
}
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
*/
/*
* 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;
}
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"
* "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:
* "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;
}
* 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;
* 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)
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;
}
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);
/*
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);
#define OBJECT_SIZE 512
struct udev;
+struct config;
struct uevent {
struct list_head node;
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);
#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"
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 \
#
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
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;
}
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;
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;
.
.TP
.B multipath_dir
-This option is deprecated, and will be removed in a future release.
-Directory where the dynamic shared objects are stored. Defined at compile time,
-commonly \fI/lib64/multipath/\fR or \fI/lib/multipath/\fR.
-.RS
-.TP
-The default is: \fB<system dependent>\fR
-.RE
+This option is not supported any more. The value is ignored.
.
.
.TP
node name of a device, it overrides any other configured methods for
determining the WWID for this device.
.PP
+This option cannot be changed during runtime with the multipathd \fBreconfigure\fR command.
+.PP
The default is: \fB<unset>\fR. To enable uevent merging, set it e.g. to
\(dqsd:ID_SERIAL dasd:ID_UID nvme:ID_WWN\(dq.
.RE
.
.TP
.B getuid_callout
-(Superseded by \fIuid_attribute\fR) The default program and args to callout
-to obtain a unique path identifier. Should be specified with an absolute path.
-.RS
-.TP
-The default is: \fB<unset>\fR
-.RE
+This option is not supported any more. The value is ignored.
.
.
.TP
.
.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
.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
.TP
.B uid_attribute
.TP
-.B getuid_callout
-.TP
.B path_selector
.TP
.B path_checker
.TP
.B dev_loss_tmo
.TP
+.B eh_deadline
+.TP
.B flush_on_last_del
.TP
.B user_friendly_names
.TP
.B uid_attribute
.TP
-.B getuid_callout
-.TP
.B path_selector
.TP
.B path_checker
.TP
.B dev_loss_tmo
.TP
+.B eh_deadline
+.TP
.B user_friendly_names
.TP
.B retain_attached_hw_handler
.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
.
.
.\" ----------------------------------------------------------------------------
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.
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
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 \
$(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)
#include <errno.h>
#include <unistd.h>
+#include <stdint.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <libudev.h>
/*
* libmultipath
*/
+#include "version.h"
#include "parser.h"
#include "vector.h"
#include "config.h"
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;
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
*/
/* 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)
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]);
}
}
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);
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
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
int retries = 3;
int start_waiter = 0;
int ret;
+ int ro;
/*
* need path UID to go any further
/* 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;
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)
{
}
}
- 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
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);
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;
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;
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
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;
}
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();
}
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;
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();
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;
}
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);
}
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)
{
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);
*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);
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,
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 \
|| 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 \
# 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
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
clean: test_clean valgrind_clean dep_clean
$(RM) $(TESTS:%=%-test) $(OBJS) *.o.wrap
- $(RM) -rf lib
+ $(RM) -rf lib conf.d
.SECONDARY: $(OBJS)
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:=:
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)
{
}
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+");
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";
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" };
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);
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);
}
/* 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);
}
{
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);
/* 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);
/* 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);
}
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);
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);
/* 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);
/* 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);
/* 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);
}
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");
/* 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);
/* 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);
}
{
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);
/* 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);
/* 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);
/* 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);
/* 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);
/* 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);
}
{
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);
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;
#include <cmocka.h>
#include <libudev.h>
#include <sys/sysmacros.h>
+#include <linux/hdreg.h>
+#include <scsi/sg.h>
#include "debug.h"
#include "util.h"
#include "vector.h"
#include "config.h"
#include "discovery.h"
#include "propsel.h"
+#include "unaligned.h"
#include "test-lib.h"
-const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO);
+const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO|DI_SERIAL);
const char default_devnode[] = "sdxTEST";
const char default_wwid[] = "TEST-WWID";
/* default_wwid should be a substring of default_wwid_1! */
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 *);
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);
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,
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);
/* 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) {
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),
};
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);
}