Imported Upstream version 0.8.6 upstream/0.8.6
authorDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:20 +0000 (13:50 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Fri, 14 Jan 2022 04:50:20 +0000 (13:50 +0900)
128 files changed:
.github/workflows/build-and-unittest.yaml [new file with mode: 0644]
.github/workflows/foreign.yaml [new file with mode: 0644]
.github/workflows/native.yaml [new file with mode: 0644]
Makefile
Makefile.inc
README.alua
README.md
kpartx/kpartx.c
libdmmp/Makefile
libdmmp/docs/man/dmmp_context_free.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_context_log_func_set.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_context_log_priority_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_context_log_priority_set.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_context_new.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_context_timeout_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_context_timeout_set.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_context_userdata_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_context_userdata_set.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_flush_mpath.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_last_error_msg.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_log_priority_str.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_mpath_array_free.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_mpath_array_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_mpath_kdev_name_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_mpath_name_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_mpath_wwid_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_path_array_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_path_blk_name_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_path_group_array_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_path_group_id_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_path_group_priority_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_path_group_selector_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_path_group_status_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_path_group_status_str.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_path_status_get.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_path_status_str.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_reconfig.3 [new file with mode: 0644]
libdmmp/docs/man/dmmp_strerror.3 [new file with mode: 0644]
libdmmp/docs/man/libdmmp.h.3 [moved from libdmmp/docs/libdmmp.h.3 with 100% similarity]
libdmmp/test/libdmmp_speed_test.c
libdmmp/test/libdmmp_test.c
libmpathcmd/Makefile
libmpathcmd/libmpathcmd.version [new file with mode: 0644]
libmpathpersist/Makefile
libmpathpersist/libmpathpersist.version [new file with mode: 0644]
libmpathpersist/mpath_persist.c
libmpathpersist/mpath_persist.h
libmpathvalid/Makefile [new file with mode: 0644]
libmpathvalid/libmpathvalid.version [new file with mode: 0644]
libmpathvalid/mpath_valid.c [new file with mode: 0644]
libmpathvalid/mpath_valid.h [new file with mode: 0644]
libmultipath/Makefile
libmultipath/alias.c
libmultipath/checkers.c
libmultipath/checkers.h
libmultipath/checkers/Makefile
libmultipath/checkers/tur.c
libmultipath/config.c
libmultipath/config.h
libmultipath/configure.c
libmultipath/configure.h
libmultipath/debug.c
libmultipath/debug.h
libmultipath/defaults.h
libmultipath/devmapper.c
libmultipath/devmapper.h
libmultipath/dict.c
libmultipath/dict.h
libmultipath/discovery.c
libmultipath/discovery.h
libmultipath/foreign/Makefile
libmultipath/hwtable.c
libmultipath/io_err_stat.c
libmultipath/libmultipath.version [new file with mode: 0644]
libmultipath/log.c
libmultipath/log.h
libmultipath/log_pthread.c
libmultipath/parser.c
libmultipath/parser.h
libmultipath/print.c
libmultipath/prio.c
libmultipath/prio.h
libmultipath/prioritizers/Makefile
libmultipath/propsel.c
libmultipath/propsel.h
libmultipath/structs.c
libmultipath/structs.h
libmultipath/structs_vec.c
libmultipath/sysfs.c
libmultipath/util.c
libmultipath/util.h
libmultipath/valid.c
libmultipath/version.h
mpathpersist/main.c
multipath/11-dm-mpath.rules
multipath/main.c
multipath/multipath.conf.5
multipathd/Makefile
multipathd/cli_handlers.c
multipathd/dmevents.c
multipathd/init_unwinder.c [new file with mode: 0644]
multipathd/init_unwinder.h [new file with mode: 0644]
multipathd/main.c
multipathd/main.h
multipathd/multipathd.service
multipathd/uxlsnr.c
multipathd/waiter.c
tests/Makefile
tests/README.md
tests/alias.c
tests/blacklist.c
tests/devt.c
tests/directio.c
tests/dmevents.c
tests/globals.c
tests/hwtable.c
tests/mpathvalid.c [new file with mode: 0644]
tests/parser.c
tests/pgpolicy.c
tests/test-lib.c
tests/test-log.c
tests/test-log.h
tests/uevent.c
tests/unaligned.c
tests/util.c
tests/valid.c
tests/vpd.c
third-party/valgrind/mpath-tools.supp [new file with mode: 0644]

diff --git a/.github/workflows/build-and-unittest.yaml b/.github/workflows/build-and-unittest.yaml
new file mode 100644 (file)
index 0000000..7ff4584
--- /dev/null
@@ -0,0 +1,102 @@
+name: basic-build-and-ci
+on:
+  push:
+    branches:
+      - master
+      - queue
+      - tip
+  pull_request:
+
+jobs:
+  bionic:
+    runs-on: ubuntu-18.04
+    steps:
+      - uses: actions/checkout@v2
+      - name: mpath
+        run: sudo modprobe dm_multipath
+      - name: zram
+        run: sudo modprobe zram num_devices=0
+      - name: zram-device
+        run: echo ZRAM=$(sudo cat /sys/class/zram-control/hot_add) >> $GITHUB_ENV
+      - name: set-zram-size
+        run: echo 1G | sudo tee /sys/block/zram$ZRAM/disksize
+      - name: update
+        run: sudo apt-get update
+      - name: dependencies
+        run: >
+          sudo apt-get install --yes gcc
+          make perl-base pkg-config valgrind
+          libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev
+          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev
+      - name: build
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo)
+      - name: test
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo) test
+      - name: valgrind-test
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo) valgrind-test
+      - name: valgrind-results
+        run: cat tests/*.vgr
+      - name: clean-nonroot-artifacts
+        run: rm -f tests/dmevents.out tests/directio.out
+      - name: root-test
+        run: sudo make DIO_TEST_DEV=/dev/zram$ZRAM test
+  focal-gcc10:
+    runs-on: ubuntu-20.04
+    steps:
+      - uses: actions/checkout@v2
+      - name: mpath
+        run: sudo modprobe dm_multipath
+      - name: brd
+        run: sudo modprobe brd rd_nr=1 rd_size=65536
+      - name: update
+        run: sudo apt-get update
+      - name: dependencies
+        run: >
+          sudo apt-get install --yes gcc-10
+          make perl-base pkg-config valgrind
+          libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev
+          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev
+      - name: set CC
+        run: echo CC=gcc-10 >> $GITHUB_ENV
+      - name: build
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo)
+      - name: test
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo) test
+      - name: valgrind-test
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo) valgrind-test
+      - name: valgrind-results
+        run: cat tests/*.vgr
+      - name: clean-nonroot-artifacts
+        run: rm -f tests/dmevents.out tests/directio.out
+      - name: root-test
+        run: sudo make DIO_TEST_DEV=/dev/ram0 test
+  focal-clang10:
+    runs-on: ubuntu-20.04
+    steps:
+      - uses: actions/checkout@v2
+      - name: mpath
+        run: sudo modprobe dm_multipath
+      - name: brd
+        run: sudo modprobe brd rd_nr=1 rd_size=65536
+      - name: update
+        run: sudo apt-get update
+      - name: dependencies
+        run: >
+          sudo apt-get install --yes clang
+          make perl-base pkg-config valgrind
+          libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev
+          libudev-dev libjson-c-dev liburcu-dev libcmocka-dev
+      - name: set CC
+        run: echo CC=clang >> $GITHUB_ENV
+      - name: build
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo)
+      - name: test
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo) test
+      - name: valgrind-test
+        run: make -O -j$(grep -c ^processor /proc/cpuinfo) valgrind-test
+      - name: valgrind-results
+        run: cat tests/*.vgr
+      - name: clean-nonroot-artifacts
+        run: rm -f tests/dmevents.out tests/directio.out
+      - name: root-test
+        run: sudo make DIO_TEST_DEV=/dev/ram0 test
diff --git a/.github/workflows/foreign.yaml b/.github/workflows/foreign.yaml
new file mode 100644 (file)
index 0000000..c164cb3
--- /dev/null
@@ -0,0 +1,66 @@
+name: compile and unit test on foreign arch
+on:
+  push:
+    branches:
+      - master
+      - queue
+      - tip
+  pull_request:
+
+jobs:
+
+  build:
+    runs-on: ubuntu-20.04
+    strategy:
+      matrix:
+        os: [buster]
+        arch: ['ppc64le', 'aarch64', 's390x']
+    container: mwilck/multipath-build-${{ matrix.os }}-${{ matrix.arch }}
+    steps:
+      - name: checkout
+        uses: actions/checkout@v1
+      - name: build and test
+        if: ${{ matrix.arch == '' || matrix.arch == '-i386' }}
+        run: make test
+      - name: build
+        if: ${{ matrix.arch != '' && matrix.arch != '-i386' }}
+        run: make test-progs
+      - name: archive
+        if: ${{ matrix.arch != '' && matrix.arch != '-i386' }}
+        run: >
+          tar cfv binaries.tar
+          Makefile*
+          libmpathcmd/*.so* libmultipath/*.so*
+          tests/lib tests/*-test tests/Makefile tests/*.so*
+      - uses: actions/upload-artifact@v1
+        if: ${{ matrix.arch != '' && matrix.arch != '-i386' }}
+        with:
+          name: multipath-${{ matrix.os }}-${{ matrix.arch }}
+          path: binaries.tar
+
+  test:
+    runs-on: ubuntu-20.04
+    needs: build
+    strategy:
+      matrix:
+        os: [buster]
+        arch: ['ppc64le', 'aarch64', 's390x']
+    steps:
+      - name: get binaries
+        uses: actions/download-artifact@v1
+        with:
+          name: multipath-${{ matrix.os }}-${{ matrix.arch }}
+      - name: unpack
+        run: tar xfv multipath-${{ matrix.os }}-${{ matrix.arch }}/binaries.tar
+      - name: enable foreign arch
+        run: sudo docker run --rm --privileged multiarch/qemu-user-static:register --reset
+      - name: run tests
+        # Github actions doesn't support referencing docker images with
+        # context variables. Workaround: use mosteo-actions/docker-run action
+        # See https://github.community/t/expressions-in-docker-uri/16271
+        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
+          # So run "make -C tests" here
+          command: -C tests
diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml
new file mode 100644 (file)
index 0000000..2bb1886
--- /dev/null
@@ -0,0 +1,32 @@
+name: compile and unit test on native arch
+on:
+  push:
+    branches:
+      - master
+      - queue
+      - tip
+  pull_request:
+
+jobs:
+  build-and-test:
+    runs-on: ubuntu-20.04
+    strategy:
+      matrix:
+        os: [buster, jessie, sid, alpine, fedora-34]
+        arch: ['', '-i386']
+        exclude:
+          - os: fedora-34
+            arch: '-i386'
+    container: mwilck/multipath-build-${{ matrix.os }}${{ matrix.arch }}
+    steps:
+      - name: checkout
+        uses: actions/checkout@v1
+      - name: build and test
+        run: make test
+      - name: clean
+        run: make clean
+      - name: clang
+        env:
+          CC: clang
+        run: make test
+
index 4a3491d..7f21db8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -9,6 +9,7 @@ BUILDDIRS := \
        libmultipath/checkers \
        libmultipath/foreign \
        libmpathpersist \
+       libmpathvalid \
        multipath \
        multipathd \
        mpathpersist \
@@ -29,7 +30,8 @@ $(BUILDDIRS):
        $(MAKE) -C $@
 
 libmultipath libdmmp: libmpathcmd
-libmpathpersist multipath multipathd: libmultipath
+libmpathpersist libmpathvalid multipath multipathd: libmultipath
+libmultipath/prioritizers libmultipath/checkers libmultipath/foreign: libmultipath
 mpathpersist multipathd:  libmpathpersist
 
 libmultipath/checkers.install \
@@ -46,11 +48,14 @@ $(BUILDDIRS:=.uninstall):
        $(MAKE) -C ${@:.uninstall=} uninstall
 
 clean: $(BUILDDIRS.clean)
-install: $(BUILDDIRS:=.install)
+install: all $(BUILDDIRS:=.install)
 uninstall: $(BUILDDIRS:=.uninstall)
 
-test:  all
-       $(MAKE) -C tests
+test-progs:    all
+       $(MAKE) -C tests progs
+
+test:  test-progs
+       $(MAKE) -C tests all
 
 valgrind-test: all
        $(MAKE) -C tests valgrind
index e05f3a9..f1e2313 100644 (file)
@@ -15,6 +15,8 @@
 # Uncomment to disable dmevents polling support
 # ENABLE_DMEVENTS_POLL = 0
 
+PKGCONFIG      ?= pkg-config
+
 ifeq ($(TOPDIR),)
        TOPDIR  = ..
 endif
@@ -36,8 +38,8 @@ ifndef RUN
 endif
 
 ifndef SYSTEMD
-       ifeq ($(shell pkg-config --modversion libsystemd >/dev/null 2>&1 && echo 1), 1)
-               SYSTEMD = $(shell pkg-config --modversion libsystemd | awk '{print $$1}')
+       ifeq ($(shell $(PKGCONFIG) --modversion libsystemd >/dev/null 2>&1 && echo 1), 1)
+               SYSTEMD = $(shell $(PKGCONFIG) --modversion libsystemd | awk '{print $$1}')
        else
                ifeq ($(shell systemctl --version >/dev/null 2>&1 && echo 1), 1)
                        SYSTEMD = $(shell systemctl --version 2> /dev/null | \
@@ -66,6 +68,7 @@ libdir                = $(prefix)/$(LIB)/multipath
 unitdir                = $(prefix)/$(SYSTEMDPATH)/systemd/system
 mpathpersistdir        = $(TOPDIR)/libmpathpersist
 mpathcmddir    = $(TOPDIR)/libmpathcmd
+mpathvaliddir  = $(TOPDIR)/libmpathvalid
 thirdpartydir  = $(TOPDIR)/third-party
 libdmmpdir     = $(TOPDIR)/libdmmp
 nvmedir                = $(TOPDIR)/libmultipath/nvme
@@ -104,7 +107,7 @@ CFLAGS              := --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe \
 BIN_CFLAGS     = -fPIE -DPIE
 LIB_CFLAGS     = -fPIC
 SHARED_FLAGS   = -shared
-LDFLAGS                := $(LDFLAGS) -Wl,-z,relro -Wl,-z,now
+LDFLAGS                := $(LDFLAGS) -Wl,-z,relro -Wl,-z,now -Wl,-z,defs
 BIN_LDFLAGS    = -pie
 
 # Check whether a function with name $1 has been declared in header file $2.
index 340ccba..b15eb48 100644 (file)
@@ -4,7 +4,7 @@ ALUA is supported in some devices, but usually it's disabled by default.
 To enable ALUA, the following options should be changed:
 
 - EMC CLARiiON/VNX:
-   "Failover Mode" should be changed to "4".
+   "Failover Mode" should be changed to "4" or "Active-Active mode(ALUA)-failover mode 4"
 
 - HPE 3PAR:
    "Host:" should be changed to "Generic-ALUA Persona 2 (UARepLun, SESLun, ALUA)".
@@ -19,3 +19,6 @@ To enable ALUA, the following options should be changed:
 - NetApp ONTAP:
    To check ALUA state: "igroup show -v <igroup_name>", and to enable ALUA:
    "igroup set <igroup_name> alua yes".
+
+- Huawei OceanStor:
+   "Host Access Mode" should be changed to "Asymmetric".
index 3e3ef67..b15c265 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,5 +1,8 @@
+[![basic-build-and-ci](https://github.com/openSUSE/multipath-tools/actions/workflows/build-and-unittest.yaml/badge.svg)](https://github.com/openSUSE/multipath-tools/actions/workflows/build-and-unittest.yaml) [![compile and unit test on native arch](https://github.com/openSUSE/multipath-tools/actions/workflows/native.yaml/badge.svg)](https://github.com/openSUSE/multipath-tools/actions/workflows/native.yaml) [![compile and unit test on foreign arch](https://github.com/openSUSE/multipath-tools/actions/workflows/foreign.yaml/badge.svg)](https://github.com/openSUSE/multipath-tools/actions/workflows/foreign.yaml)
+
 multipath-tools for Linux
-*************************
+=========================
+
 
 https://github.com/opensvc/multipath-tools
 
index 4a0aae9..8ff116b 100644 (file)
@@ -424,7 +424,7 @@ main(int argc, char **argv){
                                        fprintf(stderr, "can't del loop : %s\n",
                                               loopdev);
                                r = 1;
-                       } else
+                       } else if (verbose)
                                fprintf(stderr, "loop deleted : %s\n", loopdev);
                }
                goto end;
@@ -668,20 +668,20 @@ main(int argc, char **argv){
                if (n > 0)
                        break;
        }
-       if (what == LIST && loopcreated && S_ISREG (buf.st_mode)) {
+       if (what == LIST && loopcreated) {
                if (fd != -1)
                        close(fd);
                if (del_loop(device)) {
                        if (verbose)
-                               printf("can't del loop : %s\n",
+                               fprintf(stderr, "can't del loop : %s\n",
                                        device);
                        exit(1);
                }
-               printf("loop deleted : %s\n", device);
+               if (verbose)
+                       fprintf(stderr, "loop deleted : %s\n", device);
        }
 
 end:
-       dm_lib_release();
        dm_lib_exit();
 
        return r;
index 1dd3f34..764a0bc 100644 (file)
@@ -16,17 +16,18 @@ HEADERS = libdmmp/libdmmp.h
 OBJS = libdmmp.o libdmmp_mp.o libdmmp_pg.o libdmmp_path.o libdmmp_misc.o
 
 CFLAGS += $(LIB_CFLAGS) -fvisibility=hidden -I$(libdmmpdir) -I$(mpathcmddir) \
-         $(shell pkg-config --cflags json-c)
+         $(shell $(PKGCONFIG) --cflags json-c)
 
-LIBDEPS += $(shell pkg-config --libs json-c) -L$(mpathcmddir) -lmpathcmd -lpthread
+LIBDEPS += $(shell $(PKGCONFIG) --libs json-c) -L$(mpathcmddir) -lmpathcmd -lpthread
 
 all: $(LIBS) doc
+.PHONY:        doc doc.gz clean install uninstall check speed_test dep_clean
 
 $(LIBS): $(OBJS)
        $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS)
        $(LN) $@ $(DEVLIB)
 
-install:
+install:       doc.gz
        mkdir -p $(DESTDIR)$(usrlibdir)
        $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(usrlibdir)/$(LIBS)
        $(INSTALL_PROGRAM) -m 644 -D \
@@ -40,11 +41,7 @@ install:
                $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
        perl -i -pe 's|__INCLUDEDIR__|$(includedir)|g' \
                $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
-       @for file in docs/man/*.3.gz; do \
-               $(INSTALL_PROGRAM) -m 644 -D \
-                       $$file \
-                       $(DESTDIR)$(man3dir)/ || exit $?; \
-       done
+       $(INSTALL_PROGRAM) -m 644 -t $(DESTDIR)$(man3dir) docs/man/*.3.gz
 
 uninstall:
        $(RM) $(DESTDIR)$(usrlibdir)/$(LIBS)
@@ -58,7 +55,7 @@ uninstall:
 
 clean: dep_clean
        $(RM) core *.a *.o *.gz *.so *.so.*
-       $(RM) -r docs/man
+       $(RM) docs/man/*.gz
        $(MAKE) -C test clean
 
 include $(wildcard $(OBJS:.o=.d))
@@ -69,23 +66,19 @@ check: all
 speed_test: all
        $(MAKE) -C test speed_test
 
-doc: docs/man/$(EXTRA_MAN_FILES).gz
+doc.gz:        doc $(patsubst %,%.gz,$(wildcard docs/man/*.3))
 
-TEMPFILE := $(shell mktemp)
+doc: docs/man/dmmp_strerror.3
 
-docs/man/$(EXTRA_MAN_FILES).gz: $(HEADERS)
-       @for file in $(EXTRA_MAN_FILES); do \
-               $(INSTALL_PROGRAM) -v -m 644 -D docs/$$file docs/man/$$file; \
-       done
-       cat $(HEADERS) | \
-           perl docs/doc-preclean.pl > "$(TEMPFILE)"
-       perl docs/kernel-doc -man "$(TEMPFILE)" | \
-           perl docs/split-man.pl docs/man
-       -rm -f "$(TEMPFILE)"
-       @for file in docs/man/*.3; do \
-               gzip -f $$file; \
-       done
-       find docs/man -type f -name \*[0-9].gz
+docs/man/%.3.gz:       docs/man/%.3
+       gzip -c $< >$@
+
+docs/man/dmmp_strerror.3:      $(HEADERS)
+       TEMPFILE=$(shell mktemp); \
+       cat $^ | perl docs/doc-preclean.pl >$$TEMPFILE; \
+       perl docs/kernel-doc -man $$TEMPFILE | \
+           perl docs/split-man.pl docs/man; \
+       rm -f $$TEMPFILE
 
 dep_clean:
        $(RM) $(OBJS:.o=.d)
diff --git a/libdmmp/docs/man/dmmp_context_free.3 b/libdmmp/docs/man/dmmp_context_free.3
new file mode 100644 (file)
index 0000000..0d26f42
--- /dev/null
@@ -0,0 +1,15 @@
+.TH "dmmp_context_free" 3 "dmmp_context_free" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_context_free \- Release the memory of struct dmmp_context.
+.SH SYNOPSIS
+.B "void" dmmp_context_free
+.BI "(struct dmmp_context *" ctx ");"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+.SH "DESCRIPTION"
+
+Release the memory of struct dmmp_context, but the userdata memory defined
+via \fBdmmp_context_userdata_set\fP will not be touched.
+.SH "RETURN"
+void
diff --git a/libdmmp/docs/man/dmmp_context_log_func_set.3 b/libdmmp/docs/man/dmmp_context_log_func_set.3
new file mode 100644 (file)
index 0000000..986793d
--- /dev/null
@@ -0,0 +1,21 @@
+.TH "dmmp_context_log_func_set" 3 "dmmp_context_log_func_set" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_context_log_func_set \- Set log handler function.
+.SH SYNOPSIS
+.B "void" dmmp_context_log_func_set
+.BI "(struct dmmp_context *" ctx ","
+.BI "void (*" log_func ") (struct dmmp_context *ctx, int priority, const char *file, int line, const char *func_name, const char *format, va_list args));"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+If this pointer is NULL, your program will be terminated by assert.
+.IP "log_func" 12
+Pointer of log handler function. If set to NULL, all log will be
+ignored.
+.SH "DESCRIPTION"
+
+Set custom log handler. The log handler will be invoked when log message
+is equal or more important(less value) than log priority setting.
+Please check manpage libdmmp.h(3) for detail usage.
+.SH "RETURN"
+void
diff --git a/libdmmp/docs/man/dmmp_context_log_priority_get.3 b/libdmmp/docs/man/dmmp_context_log_priority_get.3
new file mode 100644 (file)
index 0000000..9a273a2
--- /dev/null
@@ -0,0 +1,23 @@
+.TH "dmmp_context_log_priority_get" 3 "dmmp_context_log_priority_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_context_log_priority_get \- Get log priority.
+.SH SYNOPSIS
+.B "int" dmmp_context_log_priority_get
+.BI "(struct dmmp_context *" ctx ");"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Retrieve current log priority. Valid log priority values are:
+
+* DMMP_LOG_PRIORITY_ERROR -- 3
+
+* DMMP_LOG_PRIORITY_WARNING -- 4
+
+* DMMP_LOG_PRIORITY_INFO -- 5
+
+* DMMP_LOG_PRIORITY_DEBUG -- 7
+.SH "RETURN"
+int, log priority.
diff --git a/libdmmp/docs/man/dmmp_context_log_priority_set.3 b/libdmmp/docs/man/dmmp_context_log_priority_set.3
new file mode 100644 (file)
index 0000000..469c5a4
--- /dev/null
@@ -0,0 +1,29 @@
+.TH "dmmp_context_log_priority_set" 3 "dmmp_context_log_priority_set" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_context_log_priority_set \- Set log priority.
+.SH SYNOPSIS
+.B "void" dmmp_context_log_priority_set
+.BI "(struct dmmp_context *" ctx ","
+.BI "int " priority ");"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+If this pointer is NULL, your program will be terminated by assert.
+.IP "priority" 12
+int, log priority.
+.SH "DESCRIPTION"
+
+
+When library generates log message, only equal or more important(less value)
+message will be forwarded to log handler function. Valid log priority values
+are:
+
+* DMMP_LOG_PRIORITY_ERROR -- 3
+
+* DMMP_LOG_PRIORITY_WARNING -- 4
+
+* DMMP_LOG_PRIORITY_INFO -- 5
+
+* DMMP_LOG_PRIORITY_DEBUG -- 7
+.SH "RETURN"
+void
diff --git a/libdmmp/docs/man/dmmp_context_new.3 b/libdmmp/docs/man/dmmp_context_new.3
new file mode 100644 (file)
index 0000000..0eaeb00
--- /dev/null
@@ -0,0 +1,19 @@
+.TH "dmmp_context_new" 3 "dmmp_context_new" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_context_new \- Create struct dmmp_context.
+.SH SYNOPSIS
+.B "struct dmmp_context *" dmmp_context_new
+.BI "(" void ");"
+.SH ARGUMENTS
+.IP "void" 12
+no arguments
+.SH "DESCRIPTION"
+
+The default logging level (DMMP_LOG_PRIORITY_DEFAULT) is
+DMMP_LOG_PRIORITY_WARNING which means only warning and error message will be
+forward to log handler function.  The default log handler function will print
+log message to STDERR, to change so, please use \fBdmmp_context_log_func_set\fP
+to set your own log handler, check manpage libdmmp.h(3) for detail.
+.SH "RETURN"
+Pointer of 'struct dmmp_context'. Should be freed by
+\fBdmmp_context_free\fP.
diff --git a/libdmmp/docs/man/dmmp_context_timeout_get.3 b/libdmmp/docs/man/dmmp_context_timeout_get.3
new file mode 100644 (file)
index 0000000..1df2793
--- /dev/null
@@ -0,0 +1,15 @@
+.TH "dmmp_context_timeout_get" 3 "dmmp_context_timeout_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_context_timeout_get \- Get IPC timeout.
+.SH SYNOPSIS
+.B "unsigned int" dmmp_context_timeout_get
+.BI "(struct dmmp_context *" ctx ");"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Retrieve timeout value of IPC connection to multipathd daemon.
+.SH "RETURN"
+unsigned int. Timeout in milliseconds.
diff --git a/libdmmp/docs/man/dmmp_context_timeout_set.3 b/libdmmp/docs/man/dmmp_context_timeout_set.3
new file mode 100644 (file)
index 0000000..f3d7709
--- /dev/null
@@ -0,0 +1,19 @@
+.TH "dmmp_context_timeout_set" 3 "dmmp_context_timeout_set" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_context_timeout_set \- Set IPC timeout.
+.SH SYNOPSIS
+.B "void" dmmp_context_timeout_set
+.BI "(struct dmmp_context *" ctx ","
+.BI "unsigned int " tmo ");"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+If this pointer is NULL, your program will be terminated by assert.
+.IP "tmo" 12
+Timeout in milliseconds(1 seconds equal 1000 milliseconds).
+0 means infinite, function only return when error or pass.
+.SH "DESCRIPTION"
+
+By default, the IPC to multipathd daemon will timeout after 60 seconds.
+.SH "RETURN"
+void
diff --git a/libdmmp/docs/man/dmmp_context_userdata_get.3 b/libdmmp/docs/man/dmmp_context_userdata_get.3
new file mode 100644 (file)
index 0000000..fb713d5
--- /dev/null
@@ -0,0 +1,15 @@
+.TH "dmmp_context_userdata_get" 3 "dmmp_context_userdata_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_context_userdata_get \- Get user data pointer.
+.SH SYNOPSIS
+.B "void *" dmmp_context_userdata_get
+.BI "(struct dmmp_context *" ctx ");"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Retrieve user data pointer from 'struct dmmp_context'.
+.SH "RETURN"
+void *. Pointer of user defined data.
diff --git a/libdmmp/docs/man/dmmp_context_userdata_set.3 b/libdmmp/docs/man/dmmp_context_userdata_set.3
new file mode 100644 (file)
index 0000000..c5bf63f
--- /dev/null
@@ -0,0 +1,18 @@
+.TH "dmmp_context_userdata_set" 3 "dmmp_context_userdata_set" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_context_userdata_set \- Set user data pointer.
+.SH SYNOPSIS
+.B "void" dmmp_context_userdata_set
+.BI "(struct dmmp_context *" ctx ","
+.BI "void *" userdata ");"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+If this pointer is NULL, your program will be terminated by assert.
+.IP "userdata" 12
+Pointer of user defined data.
+.SH "DESCRIPTION"
+
+Store user data pointer into 'struct dmmp_context'.
+.SH "RETURN"
+void
diff --git a/libdmmp/docs/man/dmmp_flush_mpath.3 b/libdmmp/docs/man/dmmp_flush_mpath.3
new file mode 100644 (file)
index 0000000..cdfd526
--- /dev/null
@@ -0,0 +1,36 @@
+.TH "dmmp_flush_mpath" 3 "dmmp_flush_mpath" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_flush_mpath \- Flush specified multipath device map if unused.
+.SH SYNOPSIS
+.B "int" dmmp_flush_mpath
+.BI "(struct dmmp_context *" ctx ","
+.BI "const char *" mpath_name ");"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+If this pointer is NULL, your program will be terminated by assert.
+.IP "mpath_name" 12
+const char *. The name of multipath device map.
+.SH "DESCRIPTION"
+
+Flush a multipath device map specified as parameter, if unused.
+.SH "RETURN"
+int. Valid error codes are:
+
+* DMMP_OK
+
+* DMMP_ERR_BUG
+
+* DMMP_ERR_NO_MEMORY
+
+* DMMP_ERR_NO_DAEMON
+
+* DMMP_ERR_MPATH_BUSY
+
+* DMMP_ERR_MPATH_NOT_FOUND
+
+* DMMP_ERR_INVALID_ARGUMENT
+
+* DMMP_ERR_PERMISSION_DENY
+
+Error number could be converted to string by \fBdmmp_strerror\fP.
diff --git a/libdmmp/docs/man/dmmp_last_error_msg.3 b/libdmmp/docs/man/dmmp_last_error_msg.3
new file mode 100644 (file)
index 0000000..20acbc6
--- /dev/null
@@ -0,0 +1,16 @@
+.TH "dmmp_last_error_msg" 3 "dmmp_last_error_msg" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_last_error_msg \- Retrieves the last error message.
+.SH SYNOPSIS
+.B "const char *" dmmp_last_error_msg
+.BI "(struct dmmp_context *" ctx ");"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Retrieves the last error message.
+.SH "RETURN"
+const char *. No need to free this memory, the resources will get
+freed when \fBdmmp_context_free\fP.
diff --git a/libdmmp/docs/man/dmmp_log_priority_str.3 b/libdmmp/docs/man/dmmp_log_priority_str.3
new file mode 100644 (file)
index 0000000..3b5f828
--- /dev/null
@@ -0,0 +1,24 @@
+.TH "dmmp_log_priority_str" 3 "dmmp_log_priority_str" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_log_priority_str \- Convert log priority to string.
+.SH SYNOPSIS
+.B "const char *" dmmp_log_priority_str
+.BI "(int " priority ");"
+.SH ARGUMENTS
+.IP "priority" 12
+int. Log priority.
+.SH "DESCRIPTION"
+
+Convert log priority to string (const char *).
+.SH "RETURN"
+const char *. Valid string are:
+
+* "ERROR" for DMMP_LOG_PRIORITY_ERROR
+
+* "WARN " for DMMP_LOG_PRIORITY_WARNING
+
+* "INFO " for DMMP_LOG_PRIORITY_INFO
+
+* "DEBUG" for DMMP_LOG_PRIORITY_DEBUG
+
+* "Invalid argument" for invalid log priority.
diff --git a/libdmmp/docs/man/dmmp_mpath_array_free.3 b/libdmmp/docs/man/dmmp_mpath_array_free.3
new file mode 100644 (file)
index 0000000..8c294e0
--- /dev/null
@@ -0,0 +1,18 @@
+.TH "dmmp_mpath_array_free" 3 "dmmp_mpath_array_free" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_mpath_array_free \- Free 'struct dmmp_mpath' pointer array.
+.SH SYNOPSIS
+.B "void" dmmp_mpath_array_free
+.BI "(struct dmmp_mpath **" dmmp_mps ","
+.BI "uint32_t " dmmp_mp_count ");"
+.SH ARGUMENTS
+.IP "dmmp_mps" 12
+Pointer of 'struct dmmp_mpath' array.
+.IP "dmmp_mp_count" 12
+uint32_t, the size of 'dmmp_mps' pointer array.
+.SH "DESCRIPTION"
+
+Free the 'dmmp_mps' pointer array generated by \fBdmmp_mpath_array_get\fP.
+If provided 'dmmp_mps' pointer is NULL or dmmp_mp_count == 0, do nothing.
+.SH "RETURN"
+void
diff --git a/libdmmp/docs/man/dmmp_mpath_array_get.3 b/libdmmp/docs/man/dmmp_mpath_array_get.3
new file mode 100644 (file)
index 0000000..e211db4
--- /dev/null
@@ -0,0 +1,36 @@
+.TH "dmmp_mpath_array_get" 3 "dmmp_mpath_array_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_mpath_array_get \- Query all existing multipath devices.
+.SH SYNOPSIS
+.B "int" dmmp_mpath_array_get
+.BI "(struct dmmp_context *" ctx ","
+.BI "struct dmmp_mpath ***" dmmp_mps ","
+.BI "uint32_t *" dmmp_mp_count ");"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+If this pointer is NULL, your program will be terminated by assert.
+.IP "dmmp_mps" 12
+Output pointer array of 'struct dmmp_mpath'.
+If this pointer is NULL, your program will be terminated by assert.
+.IP "dmmp_mp_count" 12
+Output pointer of uint32_t. Hold the size of 'dmmp_mps' pointer array.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Query all existing multipath devices and store them into a pointer array.
+The memory of 'dmmp_mps' should be freed via \fBdmmp_mpath_array_free\fP.
+.SH "RETURN"
+int. Valid error codes are:
+
+* DMMP_OK
+
+* DMMP_ERR_BUG
+
+* DMMP_ERR_NO_MEMORY
+
+* DMMP_ERR_NO_DAEMON
+
+* DMMP_ERR_INCONSISTENT_DATA
+
+Error number could be converted to string by \fBdmmp_strerror\fP.
diff --git a/libdmmp/docs/man/dmmp_mpath_kdev_name_get.3 b/libdmmp/docs/man/dmmp_mpath_kdev_name_get.3
new file mode 100644 (file)
index 0000000..e802fe6
--- /dev/null
@@ -0,0 +1,17 @@
+.TH "dmmp_mpath_kdev_name_get" 3 "dmmp_mpath_kdev_name_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_mpath_kdev_name_get \- Retrieve kernel DEVNAME of certain mpath.
+.SH SYNOPSIS
+.B "const char *" dmmp_mpath_kdev_name_get
+.BI "(struct dmmp_mpath *" dmmp_mp ");"
+.SH ARGUMENTS
+.IP "dmmp_mp" 12
+Pointer of 'struct dmmp_mpath'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Retrieve DEVNAME name used by kernel uevent of specified mpath.
+For example: 'dm-1'.
+.SH "RETURN"
+const char *. No need to free this memory, the resources will get
+freed when \fBdmmp_mpath_array_free\fP.
diff --git a/libdmmp/docs/man/dmmp_mpath_name_get.3 b/libdmmp/docs/man/dmmp_mpath_name_get.3
new file mode 100644 (file)
index 0000000..d70579e
--- /dev/null
@@ -0,0 +1,18 @@
+.TH "dmmp_mpath_name_get" 3 "dmmp_mpath_name_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_mpath_name_get \- Retrieve name(alias) of certain mpath.
+.SH SYNOPSIS
+.B "const char *" dmmp_mpath_name_get
+.BI "(struct dmmp_mpath *" dmmp_mp ");"
+.SH ARGUMENTS
+.IP "dmmp_mp" 12
+Pointer of 'struct dmmp_mpath'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Retrieve the name (also known as alias) of certain mpath.
+When the config 'user_friendly_names' been set 'no', the name will be
+identical to WWID retrieved by \fBdmmp_mpath_wwid_get\fP.
+.SH "RETURN"
+const char *. No need to free this memory, the resources will get
+freed when \fBdmmp_mpath_array_free\fP.
diff --git a/libdmmp/docs/man/dmmp_mpath_wwid_get.3 b/libdmmp/docs/man/dmmp_mpath_wwid_get.3
new file mode 100644 (file)
index 0000000..3d060e9
--- /dev/null
@@ -0,0 +1,13 @@
+.TH "dmmp_mpath_wwid_get" 3 "dmmp_mpath_wwid_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_mpath_wwid_get \- Retrieve WWID of certain mpath.
+.SH SYNOPSIS
+.B "const char *" dmmp_mpath_wwid_get
+.BI "(struct dmmp_mpath *" dmmp_mp ");"
+.SH ARGUMENTS
+.IP "dmmp_mp" 12
+Pointer of 'struct dmmp_mpath'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "RETURN"
+const char *. No need to free this memory, the resources will get
+freed when \fBdmmp_mpath_array_free\fP.
diff --git a/libdmmp/docs/man/dmmp_path_array_get.3 b/libdmmp/docs/man/dmmp_path_array_get.3
new file mode 100644 (file)
index 0000000..53340b3
--- /dev/null
@@ -0,0 +1,25 @@
+.TH "dmmp_path_array_get" 3 "dmmp_path_array_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_path_array_get \- Retrieve path pointer array.
+.SH SYNOPSIS
+.B "void" dmmp_path_array_get
+.BI "(struct dmmp_path_group *" dmmp_pg ","
+.BI "struct dmmp_path ***" dmmp_ps ","
+.BI "uint32_t *" dmmp_p_count ");"
+.SH ARGUMENTS
+.IP "dmmp_pg" 12
+Pointer of 'struct dmmp_path_group'.
+If this pointer is NULL, your program will be terminated by assert.
+.IP "dmmp_ps" 12
+Output pointer of 'struct dmmp_path' pointer array.
+If this pointer is NULL, your program will be terminated by assert.
+.IP "dmmp_p_count" 12
+Output pointer of uint32_t. Hold the size of 'dmmp_ps' pointer array.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+The memory of output pointer array is hold by 'struct dmmp_mpath', no
+need to free this memory, the resources will got freed when
+\fBdmmp_mpath_array_free\fP.
+.SH "RETURN"
+void
diff --git a/libdmmp/docs/man/dmmp_path_blk_name_get.3 b/libdmmp/docs/man/dmmp_path_blk_name_get.3
new file mode 100644 (file)
index 0000000..da5f9f0
--- /dev/null
@@ -0,0 +1,17 @@
+.TH "dmmp_path_blk_name_get" 3 "dmmp_path_blk_name_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_path_blk_name_get \- Retrieve block name.
+.SH SYNOPSIS
+.B "const char *" dmmp_path_blk_name_get
+.BI "(struct dmmp_path *" dmmp_p ");"
+.SH ARGUMENTS
+.IP "dmmp_p" 12
+Pointer of 'struct dmmp_path'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Retrieve block name of certain path. The example of block names are "sda",
+"nvme0n1".
+.SH "RETURN"
+const char *. No need to free this memory, the resources will get
+freed when \fBdmmp_mpath_array_free\fP.
diff --git a/libdmmp/docs/man/dmmp_path_group_array_get.3 b/libdmmp/docs/man/dmmp_path_group_array_get.3
new file mode 100644 (file)
index 0000000..6eee4a2
--- /dev/null
@@ -0,0 +1,27 @@
+.TH "dmmp_path_group_array_get" 3 "dmmp_path_group_array_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_path_group_array_get \- Retrieve path groups pointer array.
+.SH SYNOPSIS
+.B "void" dmmp_path_group_array_get
+.BI "(struct dmmp_mpath *" dmmp_mp ","
+.BI "struct dmmp_path_group ***" dmmp_pgs ","
+.BI "uint32_t *" dmmp_pg_count ");"
+.SH ARGUMENTS
+.IP "dmmp_mp" 12
+Pointer of 'struct dmmp_mpath'.
+If this pointer is NULL, your program will be terminated by assert.
+.IP "dmmp_pgs" 12
+Output pointer of 'struct dmmp_path_group' pointer array.
+If this pointer is NULL, your program will be terminated by assert.
+.IP "dmmp_pg_count" 12
+Output pointer of uint32_t. Hold the size of 'dmmp_pgs' pointer array.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Retrieve the path groups of certain mpath.
+
+The memory of output pointer array is hold by 'struct dmmp_mpath', no
+need to free this memory, the resources will got freed when
+\fBdmmp_mpath_array_free\fP.
+.SH "RETURN"
+void
diff --git a/libdmmp/docs/man/dmmp_path_group_id_get.3 b/libdmmp/docs/man/dmmp_path_group_id_get.3
new file mode 100644 (file)
index 0000000..4f07b53
--- /dev/null
@@ -0,0 +1,18 @@
+.TH "dmmp_path_group_id_get" 3 "dmmp_path_group_id_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_path_group_id_get \- Retrieve path group ID.
+.SH SYNOPSIS
+.B "uint32_t" dmmp_path_group_id_get
+.BI "(struct dmmp_path_group *" dmmp_pg ");"
+.SH ARGUMENTS
+.IP "dmmp_pg" 12
+Pointer of 'struct dmmp_path_group'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Retrieve the path group ID which could be used to switch active path group
+via command:
+
+multipathd -k'switch multipath mpathb group $id'
+.SH "RETURN"
+uint32_t.
diff --git a/libdmmp/docs/man/dmmp_path_group_priority_get.3 b/libdmmp/docs/man/dmmp_path_group_priority_get.3
new file mode 100644 (file)
index 0000000..a48b270
--- /dev/null
@@ -0,0 +1,16 @@
+.TH "dmmp_path_group_priority_get" 3 "dmmp_path_group_priority_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_path_group_priority_get \- Retrieve path group priority.
+.SH SYNOPSIS
+.B "uint32_t" dmmp_path_group_priority_get
+.BI "(struct dmmp_path_group *" dmmp_pg ");"
+.SH ARGUMENTS
+.IP "dmmp_pg" 12
+Pointer of 'struct dmmp_path_group'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+The enabled path group with highest priority will be next active path group
+if active path group down.
+.SH "RETURN"
+uint32_t.
diff --git a/libdmmp/docs/man/dmmp_path_group_selector_get.3 b/libdmmp/docs/man/dmmp_path_group_selector_get.3
new file mode 100644 (file)
index 0000000..407b3f4
--- /dev/null
@@ -0,0 +1,16 @@
+.TH "dmmp_path_group_selector_get" 3 "dmmp_path_group_selector_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_path_group_selector_get \- Retrieve path group selector.
+.SH SYNOPSIS
+.B "const char *" dmmp_path_group_selector_get
+.BI "(struct dmmp_path_group *" dmmp_pg ");"
+.SH ARGUMENTS
+.IP "dmmp_pg" 12
+Pointer of 'struct dmmp_path_group'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Path group selector determine which path in active path group will be
+use to next I/O.
+.SH "RETURN"
+const char *.
diff --git a/libdmmp/docs/man/dmmp_path_group_status_get.3 b/libdmmp/docs/man/dmmp_path_group_status_get.3
new file mode 100644 (file)
index 0000000..a81aeb3
--- /dev/null
@@ -0,0 +1,23 @@
+.TH "dmmp_path_group_status_get" 3 "dmmp_path_group_status_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_path_group_status_get \- Retrieve path group status.
+.SH SYNOPSIS
+.B "uint32_t" dmmp_path_group_status_get
+.BI "(struct dmmp_path_group *" dmmp_pg ");"
+.SH ARGUMENTS
+.IP "dmmp_pg" 12
+Pointer of 'struct dmmp_path_group'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+The valid path group statuses are:
+
+* DMMP_PATH_GROUP_STATUS_UNKNOWN
+
+* DMMP_PATH_GROUP_STATUS_ENABLED  -- standby to be active
+
+* DMMP_PATH_GROUP_STATUS_DISABLED -- disabled due to all path down
+
+* DMMP_PATH_GROUP_STATUS_ACTIVE -- selected to handle I/O
+.SH "RETURN"
+uint32_t.
diff --git a/libdmmp/docs/man/dmmp_path_group_status_str.3 b/libdmmp/docs/man/dmmp_path_group_status_str.3
new file mode 100644 (file)
index 0000000..e4a9f74
--- /dev/null
@@ -0,0 +1,26 @@
+.TH "dmmp_path_group_status_str" 3 "dmmp_path_group_status_str" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_path_group_status_str \- Convert path group status to string.
+.SH SYNOPSIS
+.B "const char *" dmmp_path_group_status_str
+.BI "(uint32_t " pg_status ");"
+.SH ARGUMENTS
+.IP "pg_status" 12
+uint32_t. Path group status.
+When provided value is not a valid path group status, return "Invalid
+argument".
+.SH "DESCRIPTION"
+
+Convert path group status uint32_t to string (const char *).
+.SH "RETURN"
+const char *. Valid string are:
+
+* "Invalid argument"
+
+* "undef"
+
+* "enabled"
+
+* "disabled"
+
+* "active"
diff --git a/libdmmp/docs/man/dmmp_path_status_get.3 b/libdmmp/docs/man/dmmp_path_status_get.3
new file mode 100644 (file)
index 0000000..025cfee
--- /dev/null
@@ -0,0 +1,54 @@
+.TH "dmmp_path_status_get" 3 "dmmp_path_status_get" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_path_status_get \- Retrieve the path status.
+.SH SYNOPSIS
+.B "uint32_t" dmmp_path_status_get
+.BI "(struct dmmp_path *" dmmp_p ");"
+.SH ARGUMENTS
+.IP "dmmp_p" 12
+Pointer of 'struct dmmp_path'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+The valid path statuses are:
+
+* DMMP_PATH_STATUS_UNKNOWN
+
+* DMMP_PATH_STATUS_DOWN
+
+Path is down and you shouldn't try to send commands to it.
+
+* DMMP_PATH_STATUS_UP
+
+Path is up and I/O can be sent to it.
+
+* DMMP_PATH_STATUS_SHAKY
+
+Only emc_clariion checker when path not available for "normal"
+operations.
+
+* DMMP_PATH_STATUS_GHOST
+
+Only hp_sw and rdac checkers.  Indicates a "passive/standby"
+path on active/passive HP arrays. These paths will return valid
+answers to certain SCSI commands (tur, read_capacity, inquiry,
+start_stop), but will fail I/O commands.  The path needs an
+initialization command to be sent to it in order for I/Os to
+succeed.
+
+* DMMP_PATH_STATUS_PENDING
+
+Available for all async checkers when a check IO is in flight.
+
+* DMMP_PATH_STATUS_TIMEOUT
+
+Only tur checker when command timed out.
+
+* DMMP_PATH_STATUS_DELAYED
+
+If a path fails after being up for less than delay_watch_checks checks,
+when it comes back up again, it will not be marked as up until it has
+been up for delay_wait_checks checks. During this time, it is marked as
+"delayed".
+.SH "RETURN"
+uint32_t.
diff --git a/libdmmp/docs/man/dmmp_path_status_str.3 b/libdmmp/docs/man/dmmp_path_status_str.3
new file mode 100644 (file)
index 0000000..3944d39
--- /dev/null
@@ -0,0 +1,34 @@
+.TH "dmmp_path_status_str" 3 "dmmp_path_status_str" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_path_status_str \- Convert path status to string.
+.SH SYNOPSIS
+.B "const char *" dmmp_path_status_str
+.BI "(uint32_t " path_status ");"
+.SH ARGUMENTS
+.IP "path_status" 12
+uint32_t. Path status.
+When provided value is not a valid path status, return
+"Invalid argument".
+.SH "DESCRIPTION"
+
+Convert path status uint32_t to string (const char *):
+
+* DMMP_PATH_STATUS_UNKNOWN -- "undef"
+
+* DMMP_PATH_STATUS_DOWN -- "faulty"
+
+* DMMP_PATH_STATUS_UP -- "ready"
+
+* DMMP_PATH_STATUS_SHAKY -- "shaky"
+
+* DMMP_PATH_STATUS_GHOST -- "ghost"
+
+* DMMP_PATH_STATUS_PENDING -- "pending"
+
+* DMMP_PATH_STATUS_TIMEOUT -- "timeout"
+
+* DMMP_PATH_STATUS_REMOVED -- "removed"
+
+* DMMP_PATH_STATUS_DELAYED -- "delayed"
+.SH "RETURN"
+const char *. The meaning of status value.
diff --git a/libdmmp/docs/man/dmmp_reconfig.3 b/libdmmp/docs/man/dmmp_reconfig.3
new file mode 100644 (file)
index 0000000..a743e30
--- /dev/null
@@ -0,0 +1,27 @@
+.TH "dmmp_reconfig" 3 "dmmp_reconfig" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_reconfig \- Instruct multipathd daemon to do reconfiguration.
+.SH SYNOPSIS
+.B "int" dmmp_reconfig
+.BI "(struct dmmp_context *" ctx ");"
+.SH ARGUMENTS
+.IP "ctx" 12
+Pointer of 'struct dmmp_context'.
+If this pointer is NULL, your program will be terminated by assert.
+.SH "DESCRIPTION"
+
+Instruct multipathd daemon to do reconfiguration.
+.SH "RETURN"
+int. Valid error codes are:
+
+* DMMP_OK
+
+* DMMP_ERR_BUG
+
+* DMMP_ERR_NO_MEMORY
+
+* DMMP_ERR_NO_DAEMON
+
+* DMMP_ERR_PERMISSION_DENY
+
+Error number could be converted to string by \fBdmmp_strerror\fP.
diff --git a/libdmmp/docs/man/dmmp_strerror.3 b/libdmmp/docs/man/dmmp_strerror.3
new file mode 100644 (file)
index 0000000..4d753d3
--- /dev/null
@@ -0,0 +1,33 @@
+.TH "dmmp_strerror" 3 "dmmp_strerror" "March 2021" "Device Mapper Multipath API - libdmmp Manual" 
+.SH NAME
+dmmp_strerror \- Convert error code to string.
+.SH SYNOPSIS
+.B "const char *" dmmp_strerror
+.BI "(int " rc ");"
+.SH ARGUMENTS
+.IP "rc" 12
+int. Return code by libdmmp functions. When provided error code is not a
+valid error code, return "Invalid argument".
+.SH "DESCRIPTION"
+
+Convert error code (int) to string (const char *):
+
+* DMMP_OK -- "OK"
+
+* DMMP_ERR_BUG -- "BUG of libdmmp library"
+
+* DMMP_ERR_NO_MEMORY -- "Out of memory"
+
+* DMMP_ERR_IPC_TIMEOUT -- "Timeout when communicate with multipathd,
+try to set bigger timeout value via dmmp_context_timeout_set ()"
+
+* DMMP_ERR_IPC_ERROR -- "Error when communicate with multipathd daemon"
+
+* DMMP_ERR_NO_DAEMON -- "The multipathd daemon not started"
+
+* DMMP_ERR_INCOMPATIBLE -- "The multipathd daemon version is not
+compatible with current library"
+
+* Other invalid error number -- "Invalid argument"
+.SH "RETURN"
+const char *. The meaning of provided error code.
index 372cd39..d91ba50 100644 (file)
@@ -27,7 +27,7 @@
 
 #include <libdmmp/libdmmp.h>
 
-int main(int argc, char *argv[])
+int main(void)
 {
        struct dmmp_context *ctx = NULL;
        struct dmmp_mpath **dmmp_mps = NULL;
index d944e1e..a940b57 100644 (file)
@@ -102,7 +102,7 @@ out:
        return rc;
 }
 
-int main(int argc, char *argv[])
+int main(void)
 {
        struct dmmp_context *ctx = NULL;
        struct dmmp_mpath **dmmp_mps = NULL;
index 0f6b816..2591019 100644 (file)
@@ -3,18 +3,22 @@ include ../Makefile.inc
 SONAME = 0
 DEVLIB = libmpathcmd.so
 LIBS = $(DEVLIB).$(SONAME)
+VERSION_SCRIPT := libmpathcmd.version
 
 CFLAGS += $(LIB_CFLAGS)
 
 OBJS = mpath_cmd.o
 
-all: $(LIBS)
+all:   $(DEVLIB)
 
-$(LIBS): $(OBJS)
-       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS)
-       $(LN) $@ $(DEVLIB)
+$(LIBS): $(OBJS) $(VERSION_SCRIPT)
+       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ \
+               -Wl,--version-script=$(VERSION_SCRIPT) -o $@ $(OBJS) $(LIBDEPS)
 
-install: $(LIBS)
+$(DEVLIB): $(LIBS)
+       $(LN) $(LIBS) $@
+
+install: all
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir)
        $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)
        $(LN) $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB)
diff --git a/libmpathcmd/libmpathcmd.version b/libmpathcmd/libmpathcmd.version
new file mode 100644 (file)
index 0000000..f100628
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2020 SUSE LLC
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * libmpathcmd ABI
+ *
+ * The ABI of libmpathcmd is supposed to remain stable. Removing symbols
+ * or altering existing symbols' semantics is not allowed. When changing a
+ * a symbol, either use a new name, or explicit symver directives.
+ *
+ * See libmultipath.version for general policy about version numbers.
+ */
+LIBMPATHCMD_1.0.0 {
+global:
+       __mpath_connect;
+       mpath_connect;
+       mpath_disconnect;
+       mpath_process_cmd;
+       mpath_recv_reply;
+       mpath_recv_reply_len;
+       mpath_recv_reply_data;
+       mpath_send_cmd;
+local:
+       *;
+};
index 21fdad8..57103e5 100644 (file)
@@ -3,23 +3,29 @@ include ../Makefile.inc
 SONAME = 0
 DEVLIB = libmpathpersist.so
 LIBS = $(DEVLIB).$(SONAME)
+VERSION_SCRIPT := libmpathpersist.version
 
 CFLAGS += $(LIB_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir)
+LDFLAGS += -L$(multipathdir) -L$(mpathcmddir)
 
-LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) -lmultipath \
-          -L$(mpathcmddir) -lmpathcmd
+LIBDEPS += -lmultipath -lmpathcmd -ldevmapper -lpthread -ldl
 
 OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o
 
-all: $(LIBS)
+all: $(DEVLIB) man
 
-$(LIBS): $(OBJS)
-       $(CC) $(LDFLAGS) $(SHARED_FLAGS) $(LIBDEPS) -Wl,-soname=$@ -o $@ $(OBJS)
-       $(LN) $(LIBS) $(DEVLIB)
+$(LIBS): $(OBJS) $(VERSION_SCRIPT)
+       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ \
+               -Wl,--version-script=$(VERSION_SCRIPT) -o $@ $(OBJS) $(LIBDEPS)
+
+$(DEVLIB): $(LIBS)
+       $(LN) $(LIBS) $@
+
+man:
        $(GZIP) mpath_persistent_reserve_in.3 > mpath_persistent_reserve_in.3.gz
        $(GZIP) mpath_persistent_reserve_out.3 > mpath_persistent_reserve_out.3.gz
 
-install: $(LIBS)
+install: all
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir)
        $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)
        $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(syslibdir)
diff --git a/libmpathpersist/libmpathpersist.version b/libmpathpersist/libmpathpersist.version
new file mode 100644 (file)
index 0000000..e074813
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 SUSE LLC
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * libmpathpersist ABI
+ *
+ * The ABI of libmpathpersist is supposed to remain stable. Removing symbols
+ * or altering existing symbols' semantics is not allowed. When changing a
+ * a symbol, either use a new name, or explicit symver directives.
+ *
+ * See libmultipath.version for general policy about version numbers.
+ */
+LIBMPATHPERSIST_1.0.0 {
+global:
+
+       __mpath_persistent_reserve_in;
+       __mpath_persistent_reserve_out;
+       dumpHex;
+       mpath_alloc_prin_response;
+       mpath_lib_exit;
+       mpath_lib_init;
+       mpath_mx_alloc_len;
+       mpath_persistent_reserve_in;
+       mpath_persistent_reserve_init_vecs;
+       mpath_persistent_reserve_out;
+       mpath_persistent_reserve_free_vecs;
+       prin_do_scsi_ioctl;
+       prout_do_scsi_ioctl;
+       update_map_pr;
+
+local: *;
+};
+
+LIBMPATHPERSIST_1.1.0 {
+global:
+       libmpathpersist_init;
+       libmpathpersist_exit;
+} LIBMPATHPERSIST_1.0.0;
index 1f9817e..190e970 100644 (file)
 
 extern struct udev *udev;
 
+static void adapt_config(struct config *conf)
+{
+       conf->force_sync = 1;
+       set_max_fds(conf->max_fds);
+}
+
+int libmpathpersist_init(void)
+{
+       struct config *conf;
+       int rc = 0;
+
+       if (libmultipath_init()) {
+               condlog(0, "Failed to initialize libmultipath.");
+               return 1;
+       }
+       if (init_config(DEFAULT_CONFIGFILE)) {
+               condlog(0, "Failed to initialize multipath config.");
+               return 1;
+       }
+       conf = libmp_get_multipath_config();
+       adapt_config(conf);
+       libmp_put_multipath_config(conf);
+       return rc;
+}
+
 struct config *
 mpath_lib_init (void)
 {
@@ -47,21 +72,28 @@ mpath_lib_init (void)
                condlog(0, "Failed to initialize multipath config.");
                return NULL;
        }
-       conf->force_sync = 1;
-       set_max_fds(conf->max_fds);
-
+       adapt_config(conf);
        return conf;
 }
 
+static void libmpathpersist_cleanup(void)
+{
+       libmultipath_exit();
+       dm_lib_exit();
+}
+
 int
 mpath_lib_exit (struct config *conf)
 {
-       dm_lib_release();
-       dm_lib_exit();
-       cleanup_prio();
-       cleanup_checkers();
        free_config(conf);
-       conf = NULL;
+       libmpathpersist_cleanup();
+       return 0;
+}
+
+int libmpathpersist_exit(void)
+{
+       uninit_config();
+       libmpathpersist_cleanup();
        return 0;
 }
 
@@ -101,72 +133,57 @@ mpath_prin_activepath (struct multipath *mpp, int rq_servact,
        return ret;
 }
 
-int mpath_persistent_reserve_in (int fd, int rq_servact,
-       struct prin_resp *resp, int noisy, int verbose)
-{
-       int ret = mpath_persistent_reserve_init_vecs(verbose);
-
-       if (ret != MPATH_PR_SUCCESS)
-               return ret;
-       ret = __mpath_persistent_reserve_in(fd, rq_servact, resp, noisy);
-       mpath_persistent_reserve_free_vecs();
-       return ret;
-}
-
-int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
-       unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose)
-{
-       int ret = mpath_persistent_reserve_init_vecs(verbose);
-
-       if (ret != MPATH_PR_SUCCESS)
-               return ret;
-       ret = __mpath_persistent_reserve_out(fd, rq_servact, rq_scope, rq_type,
-                                            paramp, noisy);
-       mpath_persistent_reserve_free_vecs();
-       return ret;
-}
-
 static vector curmp;
 static vector pathvec;
 
-void mpath_persistent_reserve_free_vecs(void)
+static void __mpath_persistent_reserve_free_vecs(vector curmp, vector pathvec)
 {
        free_multipathvec(curmp, KEEP_PATHS);
        free_pathvec(pathvec, FREE_PATHS);
-       curmp = pathvec = NULL;
 }
 
-int mpath_persistent_reserve_init_vecs(int verbose)
+void mpath_persistent_reserve_free_vecs(void)
 {
-       struct config *conf = get_multipath_config();
+       __mpath_persistent_reserve_free_vecs(curmp, pathvec);
+       curmp = pathvec = NULL;
+}
 
-       conf->verbosity = verbose;
-       put_multipath_config(conf);
+static int __mpath_persistent_reserve_init_vecs(vector *curmp_p,
+                                               vector *pathvec_p, int verbose)
+{
+       libmp_verbosity = verbose;
 
-       if (curmp)
+       if (*curmp_p)
                return MPATH_PR_SUCCESS;
        /*
         * allocate core vectors to store paths and multipaths
         */
-       curmp = vector_alloc ();
-       pathvec = vector_alloc ();
+       *curmp_p = vector_alloc ();
+       *pathvec_p = vector_alloc ();
 
-       if (!curmp || !pathvec){
+       if (!*curmp_p || !*pathvec_p){
                condlog (0, "vector allocation failed.");
                goto err;
        }
 
-       if (dm_get_maps(curmp))
+       if (dm_get_maps(*curmp_p))
                goto err;
 
        return MPATH_PR_SUCCESS;
 
 err:
-       mpath_persistent_reserve_free_vecs();
+       __mpath_persistent_reserve_free_vecs(*curmp_p, *pathvec_p);
+       *curmp_p = *pathvec_p = NULL;
        return MPATH_PR_DMMP_ERROR;
 }
 
-static int mpath_get_map(int fd, char **palias, struct multipath **pmpp)
+int mpath_persistent_reserve_init_vecs(int verbose)
+{
+       return __mpath_persistent_reserve_init_vecs(&curmp, &pathvec, verbose);
+}
+
+static int mpath_get_map(vector curmp, vector pathvec, int fd, char **palias,
+                        struct multipath **pmpp)
 {
        int ret = MPATH_PR_DMMP_ERROR;
        struct stat info;
@@ -226,13 +243,13 @@ out:
        return ret;
 }
 
-int __mpath_persistent_reserve_in (int fd, int rq_servact,
-       struct prin_resp *resp, int noisy)
+static int do_mpath_persistent_reserve_in (vector curmp, vector pathvec,
+       int fd, int rq_servact, struct prin_resp *resp, int noisy)
 {
        struct multipath *mpp;
        int ret;
 
-       ret = mpath_get_map(fd, NULL, &mpp);
+       ret = mpath_get_map(curmp, pathvec, fd, NULL, &mpp);
        if (ret != MPATH_PR_SUCCESS)
                return ret;
 
@@ -241,8 +258,17 @@ int __mpath_persistent_reserve_in (int fd, int rq_servact,
        return ret;
 }
 
-int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
-       unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy)
+
+int __mpath_persistent_reserve_in (int fd, int rq_servact,
+       struct prin_resp *resp, int noisy)
+{
+       return do_mpath_persistent_reserve_in(curmp, pathvec, fd, rq_servact,
+                                             resp, noisy);
+}
+
+static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
+       int rq_servact, int rq_scope, unsigned int rq_type,
+       struct prout_param_descriptor *paramp, int noisy)
 {
        struct multipath *mpp;
        char *alias;
@@ -250,7 +276,7 @@ int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
        uint64_t prkey;
        struct config *conf;
 
-       ret = mpath_get_map(fd, &alias, &mpp);
+       ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
        if (ret != MPATH_PR_SUCCESS)
                return ret;
 
@@ -261,9 +287,10 @@ int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
 
        memcpy(&prkey, paramp->sa_key, 8);
        if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey &&
-           ((!get_be64(mpp->reservation_key) &&
-             rq_servact == MPATH_PROUT_REG_SA) ||
-            rq_servact == MPATH_PROUT_REG_IGN_SA)) {
+           (rq_servact == MPATH_PROUT_REG_IGN_SA ||
+            (rq_servact == MPATH_PROUT_REG_SA &&
+             (!get_be64(mpp->reservation_key) ||
+              memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) {
                memcpy(&mpp->reservation_key, paramp->sa_key, 8);
                if (update_prkey_flags(alias, get_be64(mpp->reservation_key),
                                       paramp->sa_flags)) {
@@ -275,7 +302,8 @@ int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
        }
 
        if (memcmp(paramp->key, &mpp->reservation_key, 8) &&
-           memcmp(paramp->sa_key, &mpp->reservation_key, 8)) {
+           memcmp(paramp->sa_key, &mpp->reservation_key, 8) &&
+           (prkey || rq_servact != MPATH_PROUT_REG_IGN_SA)) {
                condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64, alias, get_be64(mpp->reservation_key));
                ret = MPATH_PR_SYNTAX_ERROR;
                goto out1;
@@ -318,6 +346,45 @@ out1:
        return ret;
 }
 
+
+int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
+       unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy)
+{
+       return do_mpath_persistent_reserve_out(curmp, pathvec, fd, rq_servact,
+                                              rq_scope, rq_type, paramp,
+                                              noisy);
+}
+
+int mpath_persistent_reserve_in (int fd, int rq_servact,
+       struct prin_resp *resp, int noisy, int verbose)
+{
+       vector curmp = NULL, pathvec;
+       int ret = __mpath_persistent_reserve_init_vecs(&curmp, &pathvec,
+                                                      verbose);
+
+       if (ret != MPATH_PR_SUCCESS)
+               return ret;
+       ret = do_mpath_persistent_reserve_in(curmp, pathvec, fd, rq_servact,
+                                            resp, noisy);
+       __mpath_persistent_reserve_free_vecs(curmp, pathvec);
+       return ret;
+}
+
+int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
+       unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose)
+{
+       vector curmp = NULL, pathvec;
+       int ret = __mpath_persistent_reserve_init_vecs(&curmp, &pathvec,
+                                                      verbose);
+
+       if (ret != MPATH_PR_SUCCESS)
+               return ret;
+       ret = do_mpath_persistent_reserve_out(curmp, pathvec, fd, rq_servact,
+                                             rq_scope, rq_type, paramp, noisy);
+       __mpath_persistent_reserve_free_vecs(curmp, pathvec);
+       return ret;
+}
+
 int
 get_mpvec (vector curmp, vector pathvec, char * refwwid)
 {
@@ -341,11 +408,12 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid)
                        continue;
 
                if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
-                   update_multipath_status(mpp) != DMP_OK) {
+                   update_mpp_paths(mpp, pathvec)) {
                        condlog(1, "error parsing map %s", mpp->wwid);
                        remove_map(mpp, pathvec, curmp, PURGE_VEC);
                        i--;
-               }
+               } else
+                       extract_hwe_from_path(mpp);
        }
        return MPATH_PR_SUCCESS ;
 }
index 7cf4faf..9e9c0a8 100644 (file)
@@ -175,6 +175,24 @@ struct prout_param_descriptor {            /* PROUT parameter descriptor */
  * DESCRIPTION :
  *     Initialize device mapper multipath configuration. This function must be invoked first
  *     before performing reservation management functions.
+ *     Either this function or mpath_lib_init() may be used.
+ *     Use this function to work with libmultipath's internal "struct config"
+ *     and "struct udev". The latter will be initialized automatically.
+ *     Call libmpathpersist_exit() for cleanup.
+ * RESTRICTIONS:
+ *
+ * RETURNS: 0->Success, 1->Failed.
+ */
+extern int libmpathpersist_init (void);
+
+/*
+ * DESCRIPTION :
+ *     Initialize device mapper multipath configuration. This function must be invoked first
+ *     before performing reservation management functions.
+ *     Either this function or libmpathpersist_init() may be used.
+ *     Use this function to work with an application-specific "struct config"
+ *     and "struct udev". The latter must be initialized by the application.
+ *     Call mpath_lib_exit() for cleanup.
  * RESTRICTIONS:
  *
  * RETURNS: struct config ->Success, NULL->Failed.
@@ -186,12 +204,25 @@ extern struct config * mpath_lib_init (void);
  * DESCRIPTION :
  *     Release device mapper multipath configuration. This function must be invoked after
  *     performing reservation management functions.
+ *     Use this after initialization with mpath_lib_init().
  * RESTRICTIONS:
  *
  * RETURNS: 0->Success, 1->Failed.
  */
 extern int mpath_lib_exit (struct config *conf);
 
+/*
+ * DESCRIPTION :
+ *     Release device mapper multipath configuration a. This function must be invoked after
+ *     performing reservation management functions.
+ *     Use this after initialization with libmpathpersist_init().
+ *     Calling libmpathpersist_init() after libmpathpersist_exit() will fail.
+ * RESTRICTIONS:
+ *
+ * RETURNS: 0->Success, 1->Failed.
+ */
+extern int libmpathpersist_exit (void);
+
 
 /*
  * DESCRIPTION :
@@ -215,9 +246,13 @@ extern int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp
 
 /*
  * DESCRIPTION :
- * This function is like mpath_persistent_reserve_in(), except that it doesn't call
- * mpath_persistent_reserve_init_vecs() and mpath_persistent_reserve_free_vecs()
- * before and after the actual PR call.
+ * This function is like mpath_persistent_reserve_in(), except that it
+ * requires mpath_persistent_reserve_init_vecs() to be called before the
+ * PR call to set up internal variables. These must later be cleanup up
+ * by calling mpath_persistent_reserve_free_vecs().
+ *
+ * RESTRICTIONS:
+ * This function uses static internal variables, and is not thread-safe.
  */
 extern int __mpath_persistent_reserve_in(int fd, int rq_servact,
                struct prin_resp *resp, int noisy);
@@ -249,9 +284,13 @@ extern int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
                int verbose);
 /*
  * DESCRIPTION :
- * This function is like mpath_persistent_reserve_out(), except that it doesn't call
- * mpath_persistent_reserve_init_vecs() and mpath_persistent_reserve_free_vecs()
- * before and after the actual PR call.
+ * This function is like mpath_persistent_reserve_out(), except that it
+ * requires mpath_persistent_reserve_init_vecs() to be called before the
+ * PR call to set up internal variables. These must later be cleanup up
+ * by calling mpath_persistent_reserve_free_vecs().
+ *
+ * RESTRICTIONS:
+ * This function uses static internal variables, and is not thread-safe.
  */
 extern int __mpath_persistent_reserve_out( int fd, int rq_servact, int rq_scope,
                unsigned int rq_type, struct prout_param_descriptor *paramp,
@@ -265,6 +304,7 @@ extern int __mpath_persistent_reserve_out( int fd, int rq_servact, int rq_scope,
  * @verbose: Set verbosity level. Input argument. value:0 to 3. 0->disabled, 3->Max verbose
  *
  * RESTRICTIONS:
+ * This function uses static internal variables, and is not thread-safe.
  *
  * RETURNS: MPATH_PR_SUCCESS if successful else returns any of the status specified
  *       above in RETURN_STATUS.
@@ -275,6 +315,9 @@ int mpath_persistent_reserve_init_vecs(int verbose);
  * DESCRIPTION :
  * This function frees data structures allocated by
  * mpath_persistent_reserve_init_vecs().
+ *
+ * RESTRICTIONS:
+ * This function uses static internal variables, and is not thread-safe.
  */
 void mpath_persistent_reserve_free_vecs(void);
 
diff --git a/libmpathvalid/Makefile b/libmpathvalid/Makefile
new file mode 100644 (file)
index 0000000..6bea4bc
--- /dev/null
@@ -0,0 +1,39 @@
+include ../Makefile.inc
+
+SONAME = 0
+DEVLIB = libmpathvalid.so
+LIBS = $(DEVLIB).$(SONAME)
+VERSION_SCRIPT := libmpathvalid.version
+
+CFLAGS += $(LIB_CFLAGS) -I$(multipathdir) -I$(mpathcmddir)
+
+LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) \
+          -lmultipath -L$(mpathcmddir) -lmpathcmd -ludev
+
+OBJS = mpath_valid.o
+
+all: $(LIBS)
+
+$(LIBS): $(OBJS) $(VERSION_SCRIPT)
+       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS) -Wl,--version-script=libmpathvalid.version
+       $(LN) $(LIBS) $(DEVLIB)
+
+install: $(LIBS)
+       $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(syslibdir)
+       $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)
+       $(LN) $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB)
+       $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(includedir)
+       $(INSTALL_PROGRAM) -m 644 mpath_valid.h $(DESTDIR)$(includedir)
+
+uninstall:
+       $(RM) $(DESTDIR)$(syslibdir)/$(LIBS)
+       $(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB)
+       $(RM) $(DESTDIR)$(includedir)/mpath_valid.h
+
+clean: dep_clean
+       $(RM) core *.a *.o *.so *.so.* *.gz
+
+include $(wildcard $(OBJS:.o=.d))
+
+dep_clean:
+       $(RM) $(OBJS:.o=.d)
diff --git a/libmpathvalid/libmpathvalid.version b/libmpathvalid/libmpathvalid.version
new file mode 100644 (file)
index 0000000..3bd0d3c
--- /dev/null
@@ -0,0 +1,10 @@
+MPATH_1.0 {
+       global:
+               mpathvalid_init;
+               mpathvalid_reload_config;
+               mpathvalid_exit;
+               mpathvalid_is_path;
+               mpathvalid_get_mode;
+       local:
+               *;
+};
diff --git a/libmpathvalid/mpath_valid.c b/libmpathvalid/mpath_valid.c
new file mode 100644 (file)
index 0000000..7073d17
--- /dev/null
@@ -0,0 +1,202 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <libdevmapper.h>
+#include <libudev.h>
+#include <errno.h>
+
+#include "devmapper.h"
+#include "structs.h"
+#include "util.h"
+#include "config.h"
+#include "discovery.h"
+#include "wwids.h"
+#include "sysfs.h"
+#include "mpath_cmd.h"
+#include "valid.h"
+#include "mpath_valid.h"
+#include "debug.h"
+
+static unsigned int
+get_conf_mode(struct config *conf)
+{
+       if (conf->find_multipaths == FIND_MULTIPATHS_SMART)
+               return MPATH_SMART;
+       if (conf->find_multipaths == FIND_MULTIPATHS_GREEDY)
+               return MPATH_GREEDY;
+       return MPATH_STRICT;
+}
+
+static void
+set_conf_mode(struct config *conf, unsigned int mode)
+{
+       if (mode == MPATH_SMART)
+               conf->find_multipaths = FIND_MULTIPATHS_SMART;
+       else if (mode == MPATH_GREEDY)
+               conf->find_multipaths = FIND_MULTIPATHS_GREEDY;
+       else
+               conf->find_multipaths = FIND_MULTIPATHS_STRICT;
+}
+
+unsigned int
+mpathvalid_get_mode(void)
+{
+       int mode;
+       struct config *conf;
+
+       conf = get_multipath_config();
+       if (!conf)
+               mode = MPATH_MODE_ERROR;
+       else
+               mode = get_conf_mode(conf);
+       put_multipath_config(conf);
+       return mode;
+}
+
+static int
+convert_result(int result) {
+       switch (result) {
+       case PATH_IS_ERROR:
+               return MPATH_IS_ERROR;
+       case PATH_IS_NOT_VALID:
+               return MPATH_IS_NOT_VALID;
+       case PATH_IS_VALID:
+               return MPATH_IS_VALID;
+       case PATH_IS_VALID_NO_CHECK:
+               return MPATH_IS_VALID_NO_CHECK;
+       case PATH_IS_MAYBE_VALID:
+               return MPATH_IS_MAYBE_VALID;
+       }
+       return MPATH_IS_ERROR;
+}
+
+static void
+set_log_style(int log_style)
+{
+       /*
+        * convert MPATH_LOG_* to LOGSINK_*
+        * currently there is no work to do here.
+        */
+       logsink = log_style;
+}
+
+static int
+load_default_config(int verbosity)
+{
+       /* need to set verbosity here to control logging during init_config() */
+       libmp_verbosity = verbosity;
+       if (init_config(DEFAULT_CONFIGFILE))
+               return -1;
+       /* Need to override verbosity from init_config() */
+       libmp_verbosity = verbosity;
+
+       return 0;
+}
+
+int
+mpathvalid_init(int verbosity, int log_style)
+{
+       unsigned int version[3];
+
+       set_log_style(log_style);
+       if (libmultipath_init())
+               return -1;
+
+       skip_libmp_dm_init();
+       if (load_default_config(verbosity))
+               goto fail;
+
+       if (dm_prereq(version))
+               goto fail_config;
+
+       return 0;
+
+fail_config:
+       uninit_config();
+fail:
+       libmultipath_exit();
+       return -1;
+}
+
+int
+mpathvalid_reload_config(void)
+{
+       uninit_config();
+       return load_default_config(libmp_verbosity);
+}
+
+int
+mpathvalid_exit(void)
+{
+       uninit_config();
+       libmultipath_exit();
+       return 0;
+}
+
+/*
+ * name: name of path to check
+ * mode: mode to use for determination. MPATH_DEFAULT uses configured mode
+ * info: on success, contains the path wwid
+ * paths: array of the returned mpath_info from other claimed paths
+ * nr_paths: the size of the paths array
+ */
+int
+mpathvalid_is_path(const char *name, unsigned int mode, char **wwid,
+                  const char **path_wwids, unsigned int nr_paths)
+{
+       struct config *conf;
+       int find_multipaths_saved, r = MPATH_IS_ERROR;
+       unsigned int i;
+       struct path *pp;
+
+       if (!name || mode >= MPATH_MODE_ERROR)
+               return r;
+       if (nr_paths > 0 && !path_wwids)
+               return r;
+       if (!udev)
+               return r;
+
+       pp = alloc_path();
+       if (!pp)
+               return r;
+
+       if (wwid) {
+               *wwid = (char *)malloc(WWID_SIZE);
+               if (!*wwid)
+                       goto out;
+       }
+
+       conf = get_multipath_config();
+       if (!conf)
+               goto out_wwid;
+       find_multipaths_saved = conf->find_multipaths;
+       if (mode != MPATH_DEFAULT)
+               set_conf_mode(conf, mode);
+       r = convert_result(is_path_valid(name, conf, pp, true));
+       conf->find_multipaths = find_multipaths_saved;
+       put_multipath_config(conf);
+
+       if (r == MPATH_IS_MAYBE_VALID) {
+               for (i = 0; i < nr_paths; i++) {
+                       if (path_wwids[i] &&
+                           strncmp(path_wwids[i], pp->wwid, WWID_SIZE) == 0) {
+                               r = MPATH_IS_VALID;
+                               break;
+                       }
+               }
+       }
+
+out_wwid:
+       if (wwid) {
+               if (r == MPATH_IS_VALID || r == MPATH_IS_VALID_NO_CHECK ||
+                   r == MPATH_IS_MAYBE_VALID)
+                       strlcpy(*wwid, pp->wwid, WWID_SIZE);
+               else {
+                       free(*wwid);
+                       *wwid = NULL;
+               }
+       }
+out:
+       free_path(pp);
+       return r;
+}
diff --git a/libmpathvalid/mpath_valid.h b/libmpathvalid/mpath_valid.h
new file mode 100644 (file)
index 0000000..63de4e1
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc.
+ *
+ * This file is part of the device-mapper multipath userspace tools.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef LIB_MPATH_VALID_H
+#define LIB_MPATH_VALID_H
+
+#ifdef __cpluscplus
+extern "C" {
+#endif
+
+enum mpath_valid_mode {
+       MPATH_DEFAULT,
+       MPATH_STRICT,
+       MPATH_SMART,
+       MPATH_GREEDY,
+       MPATH_MODE_ERROR,
+};
+
+/*
+ * MPATH_IS_VALID_NO_CHECK is used to indicate that it is safe to skip
+ * checks to see if the device has already been released to the system
+ * for use by things other that multipath.
+ * MPATH_IS_MAYBE_VALID is used to indicate that this device would
+ * be a valid multipath path device if another device with the same
+ * wwid existed */
+enum mpath_valid_result {
+       MPATH_IS_ERROR = -1,
+       MPATH_IS_NOT_VALID,
+       MPATH_IS_VALID,
+       MPATH_IS_VALID_NO_CHECK,
+       MPATH_IS_MAYBE_VALID,
+};
+
+enum mpath_valid_log_style {
+       MPATH_LOG_STDERR = -1,          /* log to STDERR */
+       MPATH_LOG_STDERR_TIMESTAMP,     /* log to STDERR, with timestamps */
+       MPATH_LOG_SYSLOG,               /* log to system log */
+};
+
+enum mpath_valid_verbosity {
+       MPATH_LOG_PRIO_NOLOG = -1,      /* log nothing */
+       MPATH_LOG_PRIO_ERR,
+       MPATH_LOG_PRIO_WARN,
+       MPATH_LOG_PRIO_NOTICE,
+       MPATH_LOG_PRIO_INFO,
+       MPATH_LOG_PRIO_DEBUG,
+};
+
+/* Function declarations */
+
+/*
+ * DESCRIPTION:
+ *     Initialize the device mapper multipath configuration. This
+ *     function must be invoked before calling any other
+ *     libmpathvalid functions. Call mpathvalid_exit() to cleanup.
+ * @verbosity: the logging level (mpath_valid_verbosity)
+ * @log_style: the logging style (mpath_valid_log_style)
+ *
+ * RESTRICTIONS:
+ *     Calling mpathvalid_init() after calling mpathvalid_exit() has no
+ *     effect.
+ *
+ * RETURNS: 0 = Success, -1 = Failure
+ */
+int mpathvalid_init(int verbosity, int log_style);
+
+
+/*
+ * DESCRIPTION:
+ *     Reread the multipath configuration files and reinitalize
+ *     the device mapper multipath configuration. This function can
+ *     be called as many times as necessary.
+ *
+ * RETURNS: 0 = Success, -1 = Failure
+ */
+int mpathvalid_reload_config(void);
+
+
+/*
+ * DESCRIPTION:
+ *     Release the device mapper multipath configuration. This
+ *     function must be called to cleanup resoures allocated by
+ *     mpathvalid_init(). After calling this function, no futher
+ *     libmpathvalid functions may be called.
+ *
+ * RETURNS: 0 = Success, -1 = Failure
+ */
+int mpathvalid_exit(void);
+
+/*
+ * DESCRIPTION:
+ *     Return the configured find_multipaths claim mode, using the
+ *     configuration from either mpathvalid_init() or
+ *     mpathvalid_reload_config()
+ *
+ * RETURNS:
+ *     MPATH_STRICT, MPATH_SMART, MPATH_GREEDY, or MPATH_MODE_ERROR
+ *
+ *     MPATH_STRICT     = find_multiapths (yes|on|no|off)
+ *     MPATH_SMART      = find_multipaths smart
+ *     MPATH_GREEDY     = find_multipaths greedy
+ *     MPATH_MODE_ERROR = multipath configuration not initialized
+ */
+unsigned int mpathvalid_get_mode(void);
+/*
+ * DESCRIPTION:
+ *     Return whether device-mapper multipath claims a path device,
+ *     using the configuration read from either mpathvalid_init() or
+ *     mpathvalid_reload_config(). If the device is either claimed or
+ *     potentially claimed (MPATH_IS_VALID, MPATH_IS_VALID_NO_CHECK,
+ *     or MPATH_IS_MAYBE_VALID) and wwid is not NULL, then *wiid will
+ *     be set to point to the wwid of device. If set, *wwid must be
+ *     freed by the caller. path_wwids is an obptional parameter that
+ *     points to an array of wwids, that were returned from previous
+ *     calls to mpathvalid_is_path(). These are wwids of existing
+ *     devices that are or potentially are claimed by device-mapper
+ *     multipath. path_wwids is used with the MPATH_SMART claim mode,
+ *     to claim devices when another device with the same wwid exists.
+ *     nr_paths must either be set to the number of elements of
+ *     path_wwids, or 0, if path_wwids is NULL.
+ * @name: The kernel name of the device. input argument
+ * @mode: the find_multipaths claim mode (mpath_valid_mode). input argument
+ * @wwid: address of a pointer to the path wwid, or NULL. Output argument.
+ *       Set if path is/may be claimed. If set, must be freed by caller
+ * @path_wwids: Array of pointers to path wwids, or NULL. input argument
+ * @nr_paths: number of elements in path_wwids array. input argument.
+ *
+ * RETURNS: device claim result (mpath_valid_result)
+ *         Also sets *wwid if wwid is not NULL, and the claim result is
+ *         MPATH_IS_VALID, MPATH_IS_VALID_NO_CHECK, or
+ *         MPATH_IS_MAYBE_VALID
+ */
+int mpathvalid_is_path(const char *name, unsigned int mode, char **wwid,
+                      const char **path_wwids, unsigned int nr_paths);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* LIB_PATH_VALID_H */
index 62ba16e..e7254f3 100644 (file)
@@ -6,6 +6,7 @@ include ../Makefile.inc
 SONAME = 0
 DEVLIB = libmultipath.so
 LIBS = $(DEVLIB).$(SONAME)
+VERSION_SCRIPT := libmultipath.version
 
 CFLAGS += $(LIB_CFLAGS) -I$(mpathcmddir) -I$(mpathpersistdir) -I$(nvmedir)
 
@@ -54,7 +55,7 @@ OBJS = memory.o parser.o vector.o devmapper.o callout.o \
        io_err_stat.o dm-generic.o generic.o foreign.o nvme-lib.o \
        libsg.o valid.o
 
-all: $(LIBS)
+all:   $(DEVLIB)
 
 nvme-lib.o: nvme-lib.c nvme-ioctl.c nvme-ioctl.h
        $(CC) $(CFLAGS) -Wno-unused-function -c -o $@ $<
@@ -72,11 +73,22 @@ nvme-ioctl.c: nvme/nvme-ioctl.c
 nvme-ioctl.h: nvme/nvme-ioctl.h
        $(call make_static,$<,$@)
 
-$(LIBS): $(OBJS)
-       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS)
-       $(LN) $@ $(DEVLIB)
 
-install:
+$(LIBS): $(OBJS) $(VERSION_SCRIPT)
+       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ \
+               -Wl,--version-script=$(VERSION_SCRIPT) -o $@ $(OBJS) $(LIBDEPS)
+
+$(DEVLIB): $(LIBS)
+       $(LN) $(LIBS) $@
+
+../tests/$(LIBS): $(OBJS) $(VERSION_SCRIPT)
+       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=`basename $@` \
+               -o $@ $(OBJS) $(LIBDEPS)
+       $(LN) $@ ${@:.so.0=.so}
+
+test-lib:      ../tests/$(LIBS)
+
+install: all
        $(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir)
        $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)
        $(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(libdir)
index a7ba485..02bc9d6 100644 (file)
@@ -21,6 +21,7 @@
 #include "config.h"
 #include "util.h"
 #include "errno.h"
+#include "devmapper.h"
 
 
 /*
@@ -119,6 +120,28 @@ scan_devname(const char *alias, const char *prefix)
        return n;
 }
 
+static int
+id_already_taken(int id, const char *prefix, const char *map_wwid)
+{
+       char alias[LINE_MAX];
+
+       if (format_devname(alias, id, LINE_MAX, prefix) < 0)
+               return 0;
+
+       if (dm_map_present(alias)) {
+               char wwid[WWID_SIZE];
+
+               /* If both the name and the wwid match, then it's fine.*/
+               if (dm_get_uuid(alias, wwid, sizeof(wwid)) == 0 &&
+                   strncmp(map_wwid, wwid, sizeof(wwid)) == 0)
+                       return 0;
+               condlog(3, "%s: alias '%s' already taken, but not in bindings file. reselecting alias", map_wwid, alias);
+               return 1;
+       }
+       return 0;
+}
+
+
 /*
  * Returns: 0   if matching entry in WWIDs file found
  *         -1   if an error occurs
@@ -128,7 +151,7 @@ scan_devname(const char *alias, const char *prefix)
  */
 static int
 lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
-              const char *prefix)
+              const char *prefix, int check_if_taken)
 {
        char buf[LINE_MAX];
        unsigned int line_nr = 0;
@@ -183,12 +206,31 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
                        return 0;
                }
        }
+       if (!prefix && check_if_taken)
+               id = -1;
        if (id >= smallest_bigger_id) {
                if (biggest_id < INT_MAX)
                        id = biggest_id + 1;
                else
                        id = -1;
        }
+       if (id > 0 && check_if_taken) {
+               while(id_already_taken(id, prefix, map_wwid)) {
+                       if (id == INT_MAX) {
+                               id = -1;
+                               break;
+                       }
+                       id++;
+                       if (id == smallest_bigger_id) {
+                               if (biggest_id == INT_MAX) {
+                                       id = -1;
+                                       break;
+                               }
+                               if (biggest_id >= smallest_bigger_id)
+                                       id = biggest_id + 1;
+                       }
+               }
+       }
        if (id < 0) {
                condlog(0, "no more available user_friendly_names");
                return -1;
@@ -331,7 +373,7 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old,
                goto out;
        }
 
-       id = lookup_binding(f, wwid, &alias, NULL);
+       id = lookup_binding(f, wwid, &alias, NULL, 0);
        if (alias) {
                condlog(3, "Use existing binding [%s] for WWID [%s]",
                        alias, wwid);
@@ -388,7 +430,7 @@ get_user_friendly_alias(const char *wwid, const char *file, const char *prefix,
                return NULL;
        }
 
-       id = lookup_binding(f, wwid, &alias, prefix);
+       id = lookup_binding(f, wwid, &alias, prefix, 1);
        if (id < 0) {
                fclose(f);
                return NULL;
index f7ddd53..2dd9915 100644 (file)
@@ -3,10 +3,13 @@
 #include <stddef.h>
 #include <dlfcn.h>
 #include <sys/stat.h>
+#include <urcu.h>
+#include <urcu/uatomic.h>
 
 #include "debug.h"
 #include "checkers.h"
 #include "vector.h"
+#include "util.h"
 
 struct checker_class {
        struct list_head node;
@@ -19,6 +22,7 @@ struct checker_class {
        int (*mp_init)(struct checker *);    /* to allocate the mpcontext */
        void (*free)(struct checker *);      /* to free the context */
        void (*reset)(void);                 /* to reset the global variables */
+       void *(*thread)(void *);             /* async thread entry point */
        const char **msgtable;
        short msgtable_size;
 };
@@ -54,19 +58,32 @@ static struct checker_class *alloc_checker_class(void)
        c = MALLOC(sizeof(struct checker_class));
        if (c) {
                INIT_LIST_HEAD(&c->node);
-               c->refcount = 1;
+               uatomic_set(&c->refcount, 1);
        }
        return c;
 }
 
+/* Use uatomic_{sub,add}_return() to ensure proper memory barriers */
+static int checker_class_ref(struct checker_class *cls)
+{
+       return uatomic_add_return(&cls->refcount, 1);
+}
+
+static int checker_class_unref(struct checker_class *cls)
+{
+       return uatomic_sub_return(&cls->refcount, 1);
+}
+
 void free_checker_class(struct checker_class *c)
 {
+       int cnt;
+
        if (!c)
                return;
-       c->refcount--;
-       if (c->refcount) {
-               condlog(4, "%s checker refcount %d",
-                       c->name, c->refcount);
+       cnt = checker_class_unref(c);
+       if (cnt != 0) {
+               condlog(cnt < 0 ? 1 : 4, "%s checker refcount %d",
+                       c->name, cnt);
                return;
        }
        condlog(3, "unloading %s checker", c->name);
@@ -160,7 +177,8 @@ static struct checker_class *add_checker_class(const char *multipath_dir,
 
        c->mp_init = (int (*)(struct checker *)) dlsym(c->handle, "libcheck_mp_init");
        c->reset = (void (*)(void)) dlsym(c->handle, "libcheck_reset");
-       /* These 2 functions can be NULL. call dlerror() to clear out any
+       c->thread = (void *(*)(void*)) dlsym(c->handle, "libcheck_thread");
+       /* These 3 functions can be NULL. call dlerror() to clear out any
         * error string */
        dlerror();
 
@@ -346,6 +364,43 @@ bad_id:
        return generic_msg[CHECKER_MSGID_NONE];
 }
 
+static void checker_cleanup_thread(void *arg)
+{
+       struct checker_class *cls = arg;
+
+       (void)checker_class_unref(cls);
+       rcu_unregister_thread();
+}
+
+static void *checker_thread_entry(void *arg)
+{
+       struct checker_context *ctx = arg;
+       void *rv;
+
+       rcu_register_thread();
+       pthread_cleanup_push(checker_cleanup_thread, ctx->cls);
+       rv = ctx->cls->thread(ctx);
+       pthread_cleanup_pop(1);
+       return rv;
+}
+
+int start_checker_thread(pthread_t *thread, const pthread_attr_t *attr,
+                        struct checker_context *ctx)
+{
+       int rv;
+
+       assert(ctx && ctx->cls && ctx->cls->thread);
+       /* Take a ref here, lest the class be freed before the thread starts */
+       (void)checker_class_ref(ctx->cls);
+       rv = pthread_create(thread, attr, checker_thread_entry, ctx);
+       if (rv != 0) {
+               condlog(1, "failed to start checker thread for %s: %m",
+                       ctx->cls->name);
+               checker_class_unref(ctx->cls);
+       }
+       return rv;
+}
+
 void checker_clear_message (struct checker *c)
 {
        if (!c)
@@ -370,12 +425,28 @@ void checker_get(const char *multipath_dir, struct checker *dst,
        if (!src)
                return;
 
-       src->refcount++;
+       (void)checker_class_ref(dst->cls);
 }
 
 int init_checkers(const char *multipath_dir)
 {
+#ifdef LOAD_ALL_SHARED_LIBS
+       static const char *const all_checkers[] = {
+               DIRECTIO,
+               TUR,
+               HP_SW,
+               RDAC,
+               EMC_CLARIION,
+               READSECTOR0,
+               CCISS_TUR,
+       };
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(all_checkers); i++)
+               add_checker_class(multipath_dir, all_checkers[i]);
+#else
        if (!add_checker_class(multipath_dir, DEFAULT_CHECKER))
                return 1;
+#endif
        return 0;
 }
index 9d5f90b..2fd1d1c 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _CHECKERS_H
 #define _CHECKERS_H
 
+#include <pthread.h>
 #include "list.h"
 #include "memory.h"
 #include "defaults.h"
@@ -148,6 +149,28 @@ void checker_set_async (struct checker *);
 void checker_set_fd (struct checker *, int);
 void checker_enable (struct checker *);
 void checker_disable (struct checker *);
+/*
+ * start_checker_thread(): start async path checker thread
+ *
+ * This function provides a wrapper around pthread_create().
+ * The created thread will call the DSO's "libcheck_thread" function with the
+ * checker context as argument.
+ *
+ * Rationale:
+ * Path checkers that do I/O may hang forever. To avoid blocking, some
+ * checkers therefore use asyncronous, detached threads for checking
+ * the paths. These threads may continue hanging if multipathd is stopped.
+ * In this case, we can't unload the checker DSO at exit. In order to
+ * avoid race conditions and crashes, the entry point of the thread
+ * needs to be in libmultipath, not in the DSO itself.
+ *
+ * @param arg: pointer to struct checker_context.
+ */
+struct checker_context {
+       struct checker_class *cls;
+};
+int start_checker_thread (pthread_t *thread, const pthread_attr_t *attr,
+                         struct checker_context *ctx);
 int checker_check (struct checker *, int);
 int checker_is_sync(const struct checker *);
 const char *checker_name (const struct checker *);
@@ -164,6 +187,8 @@ void checker_get(const char *, struct checker *, const char *);
 int libcheck_check(struct checker *);
 int libcheck_init(struct checker *);
 void libcheck_free(struct checker *);
+void *libcheck_thread(struct checker_context *ctx);
+
 /*
  * msgid => message map.
  *
index 01c0451..8e0ed5e 100644 (file)
@@ -4,6 +4,8 @@
 include ../../Makefile.inc
 
 CFLAGS += $(LIB_CFLAGS) -I..
+LDFLAGS += -L.. -lmultipath
+LIBDEPS = -lmultipath -laio -lpthread -lrt
 
 # If you add or remove a checker also update multipath/multipath.conf.5
 LIBS= \
@@ -17,11 +19,8 @@ LIBS= \
 
 all: $(LIBS)
 
-libcheckdirectio.so: directio.o
-       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -laio
-
 libcheck%.so: %.o
-       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^
+       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ $(LIBDEPS)
 
 install:
        $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(libdir)
index e886fcf..a4b4a21 100644 (file)
@@ -15,7 +15,6 @@
 #include <errno.h>
 #include <sys/time.h>
 #include <pthread.h>
-#include <urcu.h>
 #include <urcu/uatomic.h>
 
 #include "checkers.h"
@@ -55,6 +54,7 @@ struct tur_checker_context {
        pthread_cond_t active;
        int holders; /* uatomic access only */
        int msgid;
+       struct checker_context ctx;
 };
 
 int libcheck_init (struct checker * c)
@@ -74,6 +74,7 @@ int libcheck_init (struct checker * c)
        pthread_mutex_init(&ct->lock, NULL);
        if (fstat(c->fd, &sb) == 0)
                ct->devt = sb.st_rdev;
+       ct->ctx.cls = c->cls;
        c->context = ct;
 
        return 0;
@@ -204,7 +205,6 @@ static void cleanup_func(void *data)
        holders = uatomic_sub_return(&ct->holders, 1);
        if (!holders)
                cleanup_context(ct);
-       rcu_unregister_thread();
 }
 
 /*
@@ -251,15 +251,15 @@ static void tur_deep_sleep(const struct tur_checker_context *ct)
 #define tur_deep_sleep(x) do {} while (0)
 #endif /* TUR_TEST_MAJOR */
 
-static void *tur_thread(void *ctx)
+void *libcheck_thread(struct checker_context *ctx)
 {
-       struct tur_checker_context *ct = ctx;
+       struct tur_checker_context *ct =
+               container_of(ctx, struct tur_checker_context, ctx);
        int state, running;
        short msgid;
 
        /* This thread can be canceled, so setup clean up */
        tur_thread_cleanup_push(ct);
-       rcu_register_thread();
 
        condlog(4, "%d:%d : tur checker starting up", major(ct->devt),
                minor(ct->devt));
@@ -394,7 +394,7 @@ int libcheck_check(struct checker * c)
                uatomic_set(&ct->running, 1);
                tur_set_async_timeout(c);
                setup_thread_attr(&attr, 32 * 1024, 1);
-               r = pthread_create(&ct->thread, &attr, tur_thread, ct);
+               r = start_checker_thread(&ct->thread, &attr, &ct->ctx);
                pthread_attr_destroy(&attr);
                if (r) {
                        uatomic_sub(&ct->holders, 1);
index b9bdbdb..30046a1 100644 (file)
 #include "devmapper.h"
 #include "mpath_cmd.h"
 #include "propsel.h"
+#include "foreign.h"
+
+/*
+ * We don't support re-initialization after
+ * libmultipath_exit().
+ */
+static bool libmultipath_exit_called;
+static pthread_once_t _init_once = PTHREAD_ONCE_INIT;
+static pthread_once_t _exit_once = PTHREAD_ONCE_INIT;
+struct udev *udev;
+
+static void _udev_init(void)
+{
+       if (udev)
+               udev_ref(udev);
+       else
+               udev = udev_new();
+       if (!udev)
+               condlog(0, "%s: failed to initialize udev", __func__);
+}
+
+static bool _is_libmultipath_initialized(void)
+{
+       return !libmultipath_exit_called && !!udev;
+}
+
+int libmultipath_init(void)
+{
+       pthread_once(&_init_once, _udev_init);
+       return !_is_libmultipath_initialized();
+}
+
+static void _libmultipath_exit(void)
+{
+       libmultipath_exit_called = true;
+       cleanup_foreign();
+       cleanup_checkers();
+       cleanup_prio();
+       libmp_dm_exit();
+       udev_unref(udev);
+}
+
+void libmultipath_exit(void)
+{
+       pthread_once(&_exit_once, _libmultipath_exit);
+}
+
+static struct config __internal_config;
+struct config *libmp_get_multipath_config(void)
+{
+       if (!__internal_config.hwtable)
+               /* not initialized */
+               return NULL;
+       return &__internal_config;
+}
+
+struct config *get_multipath_config(void)
+       __attribute__((weak, alias("libmp_get_multipath_config")));
+
+void libmp_put_multipath_config(void *conf __attribute__((unused)))
+{
+       /* empty */
+}
+
+void put_multipath_config(void *conf)
+       __attribute__((weak, alias("libmp_put_multipath_config")));
 
 static int
 hwe_strmatch (const struct hwentry *hwe1, const struct hwentry *hwe2)
@@ -147,7 +213,7 @@ struct mpentry *find_mpe(vector mptable, char *wwid)
        int i;
        struct mpentry * mpe;
 
-       if (!wwid)
+       if (!wwid || !*wwid)
                return NULL;
 
        vector_foreach_slot (mptable, mpe, i)
@@ -358,6 +424,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)
        merge_num(flush_on_last_del);
        merge_num(fast_io_fail);
        merge_num(dev_loss);
+       merge_num(eh_deadline);
        merge_num(user_friendly_names);
        merge_num(retain_hwhandler);
        merge_num(detect_prio);
@@ -369,6 +436,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)
        merge_num(max_sectors_kb);
        merge_num(ghost_delay);
        merge_num(all_tg_pt);
+       merge_num(recheck_wwid);
        merge_num(vpd_vendor_id);
        merge_num(san_path_err_threshold);
        merge_num(san_path_err_forget_rate);
@@ -442,9 +510,16 @@ void merge_mptable(vector mptable)
        int i, j;
 
        vector_foreach_slot(mptable, mp1, i) {
+               /* drop invalid multipath configs */
+               if (!mp1->wwid) {
+                       condlog(0, "multipaths config section missing wwid");
+                       vector_del_slot(mptable, i--);
+                       free_mpe(mp1);
+                       continue;
+               }
                j = i + 1;
                vector_foreach_slot_after(mptable, mp2, j) {
-                       if (strcmp(mp1->wwid, mp2->wwid))
+                       if (!mp2->wwid || strcmp(mp1->wwid, mp2->wwid))
                                continue;
                        condlog(1, "%s: duplicate multipath config section for %s",
                                __func__, mp1->wwid);
@@ -513,6 +588,7 @@ store_hwe (vector hwtable, struct hwentry * dhwe)
        hwe->flush_on_last_del = dhwe->flush_on_last_del;
        hwe->fast_io_fail = dhwe->fast_io_fail;
        hwe->dev_loss = dhwe->dev_loss;
+       hwe->eh_deadline = dhwe->eh_deadline;
        hwe->user_friendly_names = dhwe->user_friendly_names;
        hwe->retain_hwhandler = dhwe->retain_hwhandler;
        hwe->detect_prio = dhwe->detect_prio;
@@ -574,17 +650,15 @@ restart:
        return;
 }
 
-struct config *
-alloc_config (void)
+static struct config *alloc_config (void)
 {
        return (struct config *)MALLOC(sizeof(struct config));
 }
 
-void
-free_config (struct config * conf)
+static void _uninit_config(struct config *conf)
 {
        if (!conf)
-               return;
+               conf = &__internal_config;
 
        if (conf->multipath_dir)
                FREE(conf->multipath_dir);
@@ -650,7 +724,27 @@ free_config (struct config * conf)
        free_hwtable(conf->hwtable);
        free_hwe(conf->overrides);
        free_keywords(conf->keywords);
-       FREE(conf);
+
+       memset(conf, 0, sizeof(*conf));
+}
+
+void uninit_config(void)
+{
+       _uninit_config(&__internal_config);
+}
+
+void free_config(struct config *conf)
+{
+       if (!conf)
+               return;
+       else if (conf == &__internal_config) {
+               condlog(0, "ERROR: %s called for internal config. Use uninit_config() instead",
+                       __func__);
+               return;
+       }
+
+       _uninit_config(conf);
+       free(conf);
 }
 
 /* if multipath fails to process the config directory, it should continue,
@@ -719,19 +813,39 @@ static void set_max_checkint_from_watchdog(struct config *conf)
 }
 #endif
 
-struct config *
-load_config (char * file)
+static int _init_config (const char *file, struct config *conf);
+
+int init_config(const char *file)
+{
+       return _init_config(file, &__internal_config);
+}
+
+struct config *load_config(const char *file)
 {
        struct config *conf = alloc_config();
 
+       if (conf && !_init_config(file, conf))
+               return conf;
+
+       free(conf);
+       return NULL;
+}
+
+int _init_config (const char *file, struct config *conf)
+{
+
        if (!conf)
-               return NULL;
+               conf = &__internal_config;
 
        /*
-        * internal defaults
+        * Processing the config file will overwrite conf->verbosity if set
+        * When we return, we'll copy the config value back
         */
-       conf->verbosity = DEFAULT_VERBOSITY;
+       conf->verbosity = libmp_verbosity;
 
+       /*
+        * internal defaults
+        */
        get_sys_max_fds(&conf->max_fds);
        conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE);
        conf->wwids_file = set_default(DEFAULT_WWIDS_FILE);
@@ -754,6 +868,7 @@ load_config (char * file)
        conf->remove_retries = 0;
        conf->ghost_delay = DEFAULT_GHOST_DELAY;
        conf->all_tg_pt = DEFAULT_ALL_TG_PT;
+       conf->recheck_wwid = DEFAULT_RECHECK_WWID;
        /*
         * preload default hwtable
         */
@@ -897,10 +1012,11 @@ load_config (char * file)
            !conf->wwids_file || !conf->prkeys_file)
                goto out;
 
-       return conf;
+       libmp_verbosity = conf->verbosity;
+       return 0;
 out:
-       free_config(conf);
-       return NULL;
+       _uninit_config(conf);
+       return 1;
 }
 
 char *get_uid_attribute_by_attrs(struct config *conf,
index 290aea5..933fe0d 100644 (file)
 #define ORIGIN_DEFAULT 0
 #define ORIGIN_CONFIG  1
 
-/*
- * In kernel, fast_io_fail == 0 means immediate failure on rport delete.
- * OTOH '0' means not-configured in various places in multipath-tools.
- */
-#define MP_FAST_IO_FAIL_UNSET (0)
-#define MP_FAST_IO_FAIL_OFF (-1)
-#define MP_FAST_IO_FAIL_ZERO (-2)
-
 enum devtypes {
        DEV_NONE,
        DEV_DEVT,
@@ -71,6 +63,7 @@ struct hwentry {
        int flush_on_last_del;
        int fast_io_fail;
        unsigned int dev_loss;
+       int eh_deadline;
        int user_friendly_names;
        int retain_hwhandler;
        int detect_prio;
@@ -90,6 +83,7 @@ struct hwentry {
        int ghost_delay;
        int all_tg_pt;
        int vpd_vendor_id;
+       int recheck_wwid;
        char * bl_product;
 };
 
@@ -156,6 +150,7 @@ struct config {
        int attribute_flags;
        int fast_io_fail;
        unsigned int dev_loss;
+       int eh_deadline;
        int log_checker_err;
        int allow_queueing;
        int allow_usb_devices;
@@ -192,8 +187,8 @@ struct config {
        int find_multipaths_timeout;
        int marginal_pathgroups;
        int skip_delegate;
-       unsigned int version[3];
        unsigned int sequence_nr;
+       int recheck_wwid;
 
        char * multipath_dir;
        char * selector;
@@ -234,7 +229,53 @@ struct config {
        char *enable_foreign;
 };
 
-extern struct udev * udev;
+/**
+ * extern variable: udev
+ *
+ * A &struct udev instance used by libmultipath. libmultipath expects
+ * a valid, initialized &struct udev in this variable.
+ * An application can define this variable itself, in which case
+ * the applications's instance will take precedence.
+ * The application can initialize and destroy this variable by
+ * calling libmultipath_init() and libmultipath_exit(), respectively,
+ * whether or not it defines the variable itself.
+ * An application can initialize udev with udev_new() before calling
+ * libmultipath_init(), e.g. if it has to make libudev calls before
+ * libmultipath calls. If an application wants to keep using the
+ * udev variable after calling libmultipath_exit(), it should have taken
+ * an additional reference on it beforehand. This is the case e.g.
+ * after initiazing udev with udev_new().
+ */
+extern struct udev *udev;
+
+/**
+ * libmultipath_init() - library initialization
+ *
+ * This function initializes libmultipath data structures.
+ * It is light-weight; some other initializations, like device-mapper
+ * initialization, are done lazily when the respective functionality
+ * is required.
+ *
+ * Clean up by libmultipath_exit() when the program terminates.
+ * It is an error to call libmultipath_init() after libmultipath_exit().
+ * Return: 0 on success, 1 on failure.
+ */
+int libmultipath_init(void);
+
+/**
+ * libmultipath_exit() - library un-initialization
+ *
+ * This function un-initializes libmultipath data structures.
+ * It is recommended to call this function at program exit.
+ * If the application also calls dm_lib_exit(), it should do so
+ * after libmultipath_exit().
+ *
+ * Calls to libmultipath_init() after libmultipath_exit() will fail
+ * (in other words, libmultipath can't be re-initialized).
+ * Any other libmultipath calls after libmultipath_exit() may cause
+ * undefined behavior.
+ */
+void libmultipath_exit(void);
 
 int find_hwe (const struct _vector *hwtable,
              const char * vendor, const char * product, const char *revision,
@@ -252,11 +293,26 @@ void free_mptable (vector mptable);
 
 int store_hwe (vector hwtable, struct hwentry *);
 
-struct config *load_config (char * file);
-struct config * alloc_config (void);
+struct config *load_config (const char *file);
 void free_config (struct config * conf);
-extern struct config *get_multipath_config(void);
-extern void put_multipath_config(void *);
+int init_config(const char *file);
+void uninit_config(void);
+
+/*
+ * libmultipath provides default implementations of
+ * get_multipath_config() and put_multipath_config().
+ * Applications using these should use init_config(file, NULL)
+ * to load the configuration, rather than load_config(file).
+ * Likewise, uninit_config() should be used for teardown, but
+ * using free_config() for that is supported, too.
+ * Applications can define their own {get,put}_multipath_config()
+ * functions, which override the library-internal ones, but
+ * could still call libmp_{get,put}_multipath_config().
+ */
+struct config *libmp_get_multipath_config(void);
+struct config *get_multipath_config(void);
+void libmp_put_multipath_config(void *);
+void put_multipath_config(void *);
 
 int parse_uid_attrs(char *uid_attrs, struct config *conf);
 char *get_uid_attribute_by_attrs(struct config *conf,
index 6fb477f..6ca1f4b 100644 (file)
@@ -312,6 +312,13 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
                mpp->disable_queueing = 0;
 
        /*
+        * If this map was created with add_map_without_path(),
+        * mpp->hwe might not be set yet.
+        */
+       if (!mpp->hwe)
+               extract_hwe_from_path(mpp);
+
+       /*
         * properties selectors
         *
         * Ordering matters for some properties:
@@ -361,6 +368,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size,
        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);
@@ -519,20 +527,8 @@ get_udev_for_mpp(const struct multipath *mpp)
        return udd;
 }
 
-static void
-trigger_udev_change(const struct multipath *mpp)
-{
-       static const char change[] = "change";
-       struct udev_device *udd = get_udev_for_mpp(mpp);
-       if (!udd)
-               return;
-       condlog(3, "triggering %s uevent for %s", change, mpp->alias);
-       sysfs_attr_set_value(udd, "uevent", change, sizeof(change)-1);
-       udev_device_unref(udd);
-}
-
-static void trigger_partitions_udev_change(struct udev_device *dev,
-                                          const char *action, int len)
+void trigger_partitions_udev_change(struct udev_device *dev,
+                                   const char *action, int len)
 {
        struct udev_enumerate *part_enum;
        struct udev_list_entry *item;
@@ -628,15 +624,6 @@ trigger_paths_udev_change(struct multipath *mpp, bool is_mpath)
 }
 
 static int
-is_mpp_known_to_udev(const struct multipath *mpp)
-{
-       struct udev_device *udd = get_udev_for_mpp(mpp);
-       int ret = (udd != NULL);
-       udev_device_unref(udd);
-       return ret;
-}
-
-static int
 sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload)
 {
        struct pathgroup * pgp;
@@ -688,12 +675,11 @@ sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload)
        return err;
 }
 
-static void
-select_reload_action(struct multipath *mpp, const struct multipath *cmpp,
-                    const char *reason)
+static bool is_udev_ready(struct multipath *cmpp)
 {
        struct udev_device *mpp_ud;
        const char *env;
+       bool rc;
 
        /*
         * MPATH_DEVICE_READY != 1 can mean two things:
@@ -705,14 +691,20 @@ select_reload_action(struct multipath *mpp, const struct multipath *cmpp,
         */
 
        mpp_ud = get_udev_for_mpp(cmpp);
+       if (!mpp_ud)
+               return true;
        env = udev_device_get_property_value(mpp_ud, "MPATH_DEVICE_READY");
-       if ((!env || strcmp(env, "1")) && count_active_paths(mpp) > 0)
-               mpp->force_udev_reload = 1;
+       rc = (env != NULL && !strcmp(env, "1"));
        udev_device_unref(mpp_ud);
+       condlog(4, "%s: %s: \"%s\" -> %d\n", __func__, cmpp->alias, env, rc);
+       return rc;
+}
+
+static void
+select_reload_action(struct multipath *mpp, const char *reason)
+{
        mpp->action = ACT_RELOAD;
-       condlog(3, "%s: set ACT_RELOAD (%s%s)", mpp->alias,
-               mpp->force_udev_reload ? "forced, " : "",
-               reason);
+       condlog(3, "%s: set ACT_RELOAD (%s)", mpp->alias, reason);
 }
 
 void select_action (struct multipath *mpp, const struct _vector *curmp,
@@ -781,10 +773,18 @@ void select_action (struct multipath *mpp, const struct _vector *curmp,
                return;
        }
 
+       if (!is_udev_ready(cmpp) && count_active_paths(mpp) > 0) {
+               mpp->force_udev_reload = 1;
+               mpp->action = ACT_RELOAD;
+               condlog(3, "%s: set ACT_RELOAD (udev incomplete)",
+                       mpp->alias);
+               return;
+       }
+
        if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF &&
            !!strstr(mpp->features, "queue_if_no_path") !=
            !!strstr(cmpp->features, "queue_if_no_path")) {
-               select_reload_action(mpp, cmpp, "no_path_retry change");
+               select_reload_action(mpp, "no_path_retry change");
                return;
        }
        if ((mpp->retain_hwhandler != RETAIN_HWHANDLER_ON ||
@@ -792,7 +792,7 @@ void select_action (struct multipath *mpp, const struct _vector *curmp,
            (strlen(cmpp->hwhandler) != strlen(mpp->hwhandler) ||
             strncmp(cmpp->hwhandler, mpp->hwhandler,
                    strlen(mpp->hwhandler)))) {
-               select_reload_action(mpp, cmpp, "hwhandler change");
+               select_reload_action(mpp, "hwhandler change");
                return;
        }
 
@@ -800,7 +800,7 @@ void select_action (struct multipath *mpp, const struct _vector *curmp,
            !!strstr(mpp->features, "retain_attached_hw_handler") !=
            !!strstr(cmpp->features, "retain_attached_hw_handler") &&
            get_linux_version_code() < KERNEL_VERSION(4, 3, 0)) {
-               select_reload_action(mpp, cmpp, "retain_hwhandler change");
+               select_reload_action(mpp, "retain_hwhandler change");
                return;
        }
 
@@ -812,7 +812,7 @@ void select_action (struct multipath *mpp, const struct _vector *curmp,
                remove_feature(&cmpp_feat, "queue_if_no_path");
                remove_feature(&cmpp_feat, "retain_attached_hw_handler");
                if (strncmp(mpp_feat, cmpp_feat, PARAMS_SIZE)) {
-                       select_reload_action(mpp, cmpp, "features change");
+                       select_reload_action(mpp, "features change");
                        FREE(cmpp_feat);
                        FREE(mpp_feat);
                        return;
@@ -823,19 +823,19 @@ void select_action (struct multipath *mpp, const struct _vector *curmp,
 
        if (!cmpp->selector || strncmp(cmpp->selector, mpp->selector,
                    strlen(mpp->selector))) {
-               select_reload_action(mpp, cmpp, "selector change");
+               select_reload_action(mpp, "selector change");
                return;
        }
        if (cmpp->minio != mpp->minio) {
-               select_reload_action(mpp, cmpp, "minio change");
+               select_reload_action(mpp, "minio change");
                return;
        }
        if (!cmpp->pg || VECTOR_SIZE(cmpp->pg) != VECTOR_SIZE(mpp->pg)) {
-               select_reload_action(mpp, cmpp, "path group number change");
+               select_reload_action(mpp, "path group number change");
                return;
        }
        if (pgcmp(mpp, cmpp)) {
-               select_reload_action(mpp, cmpp, "path group topology change");
+               select_reload_action(mpp, "path group topology change");
                return;
        }
        if (cmpp->nextpg != mpp->bestpg) {
@@ -844,12 +844,6 @@ void select_action (struct multipath *mpp, const struct _vector *curmp,
                        mpp->alias);
                return;
        }
-       if (!is_mpp_known_to_udev(cmpp)) {
-               mpp->action = ACT_RELOAD;
-               condlog(3, "%s: set ACT_RELOAD (udev device not initialized)",
-                       mpp->alias);
-               return;
-       }
        mpp->action = ACT_NOTHING;
        condlog(3, "%s: set ACT_NOTHING (map unchanged)",
                mpp->alias);
@@ -927,16 +921,12 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
 {
        int r = DOMAP_FAIL;
        struct config *conf;
-       int verbosity;
 
        /*
         * last chance to quit before touching the devmaps
         */
        if (mpp->action == ACT_DRY_RUN) {
-               conf = get_multipath_config();
-               verbosity = conf->verbosity;
-               put_multipath_config(conf);
-               print_multipath_topology(mpp, verbosity);
+               print_multipath_topology(mpp, libmp_verbosity);
                return DOMAP_DRY;
        }
 
@@ -1132,7 +1122,7 @@ out:
  * FORCE_RELOAD_WEAK: existing maps are compared to the current conf and only
  * reloaded in DM if there's a difference. This is useful during startup.
  */
-int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
+int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
                    int force_reload, enum mpath_cmds cmd)
 {
        int ret = CP_FAIL;
@@ -1144,6 +1134,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
        struct path * pp2;
        vector curmp = vecs->mpvec;
        vector pathvec = vecs->pathvec;
+       vector newmp;
        struct config *conf;
        int allow_queueing;
        struct bitfield *size_mismatch_seen;
@@ -1164,8 +1155,23 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
        if (size_mismatch_seen == NULL)
                return CP_FAIL;
 
+       if (mpvec)
+               newmp = mpvec;
+       else
+               newmp = vector_alloc();
+       if (!newmp) {
+               condlog(0, "can not allocate newmp");
+               goto out;
+       }
+
        vector_foreach_slot (pathvec, pp1, k) {
                int invalid;
+
+               if (should_exit()) {
+                       ret = CP_FAIL;
+                       goto out;
+               }
+
                /* skip this path for some reason */
 
                /* 1. if path has no unique id or wwid blacklisted */
@@ -1270,20 +1276,14 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
                                goto out;
                        }
                }
-               if (r == DOMAP_DRY)
+               if (r == DOMAP_DRY) {
+                       if (!vector_alloc_slot(newmp)) {
+                               remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
+                               goto out;
+                       }
+                       vector_set_slot(newmp, mpp);
                        continue;
-
-               if (r == DOMAP_EXIST && mpp->action == ACT_NOTHING &&
-                   force_reload == FORCE_RELOAD_WEAK)
-                       /*
-                        * First time we're called, and no changes applied.
-                        * domap() was a noop. But we can't be sure that
-                        * udev has already finished setting up this device
-                        * (udev in initrd may have been shut down while
-                        * processing this device or its children).
-                        * Trigger a change event, just in case.
-                        */
-                       trigger_udev_change(find_mp_by_wwid(curmp, mpp->wwid));
+               }
 
                conf = get_multipath_config();
                allow_queueing = conf->allow_queueing;
@@ -1298,31 +1298,25 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
                                               "queue_if_no_path");
                }
 
-               if (!is_daemon && mpp->action != ACT_NOTHING) {
-                       int verbosity;
-
-                       conf = get_multipath_config();
-                       verbosity = conf->verbosity;
-                       put_multipath_config(conf);
-                       print_multipath_topology(mpp, verbosity);
-               }
+               if (!is_daemon && mpp->action != ACT_NOTHING)
+                       print_multipath_topology(mpp, libmp_verbosity);
 
-               if (newmp) {
-                       if (mpp->action != ACT_REJECT) {
-                               if (!vector_alloc_slot(newmp))
-                                       goto out;
-                               vector_set_slot(newmp, mpp);
+               if (mpp->action != ACT_REJECT) {
+                       if (!vector_alloc_slot(newmp)) {
+                               remove_map(mpp, vecs->pathvec, vecs->mpvec, KEEP_VEC);
+                               goto out;
                        }
-                       else
-                               remove_map(mpp, vecs->pathvec, vecs->mpvec,
-                                          KEEP_VEC);
+                       vector_set_slot(newmp, mpp);
                }
+               else
+                       remove_map(mpp, vecs->pathvec, vecs->mpvec,
+                                  KEEP_VEC);
        }
        /*
         * Flush maps with only dead paths (ie not in sysfs)
         * Keep maps with only failed paths
         */
-       if (newmp) {
+       if (mpvec) {
                vector_foreach_slot (newmp, mpp, i) {
                        char alias[WWID_SIZE];
 
@@ -1345,6 +1339,8 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid,
        ret = CP_OK;
 out:
        free(size_mismatch_seen);
+       if (!mpvec)
+               free_multipathvec(newmp, KEEP_PATHS);
        return ret;
 }
 
@@ -1445,7 +1441,7 @@ static int _get_refwwid(enum mpath_cmds cmd, const char *dev,
                                return ret;
                        }
                }
-               if (pp->udev && pp->uid_attribute &&
+               if (flags & DI_BLACKLIST &&
                    filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
                        return PATHINFO_SKIPPED;
                refwwid = pp->wwid;
@@ -1470,7 +1466,7 @@ static int _get_refwwid(enum mpath_cmds cmd, const char *dev,
                                refwwid = dev;
                }
 
-               if (refwwid && strlen(refwwid) &&
+               if (flags & DI_BLACKLIST && refwwid && strlen(refwwid) &&
                    filter_wwid(conf->blist_wwid, conf->elist_wwid, refwwid,
                                NULL) > 0)
                        return PATHINFO_SKIPPED;
index 6b23ccb..70cf77a 100644 (file)
@@ -58,3 +58,5 @@ int get_refwwid (enum mpath_cmds cmd, const char *dev, enum devtypes dev_type,
                 vector pathvec, char **wwid);
 struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type);
 void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath);
+void trigger_partitions_udev_change(struct udev_device *dev, const char *action,
+                                   int len);
index 4128cb9..510e15e 100644 (file)
 #include "config.h"
 #include "defaults.h"
 #include "debug.h"
+#include "time-util.h"
+#include "util.h"
 
-void dlog (int sink, int prio, const char * fmt, ...)
+int logsink;
+int libmp_verbosity = DEFAULT_VERBOSITY;
+
+void dlog(int prio, const char * fmt, ...)
 {
        va_list ap;
-       int thres;
-       struct config *conf;
 
        va_start(ap, fmt);
-       conf = get_multipath_config();
-       ANNOTATE_IGNORE_READS_BEGIN();
-       thres = (conf) ? conf->verbosity : DEFAULT_VERBOSITY;
-       ANNOTATE_IGNORE_READS_END();
-       put_multipath_config(conf);
-
-       if (prio <= thres) {
-               if (sink < 1) {
-                       if (sink == 0) {
-                               time_t t = time(NULL);
-                               struct tm *tb = localtime(&t);
-                               char buff[16];
-
-                               strftime(buff, sizeof(buff),
-                                        "%b %d %H:%M:%S", tb);
-                               buff[sizeof(buff)-1] = '\0';
+       if (logsink != LOGSINK_SYSLOG) {
+               if (logsink == LOGSINK_STDERR_WITH_TIME) {
+                       struct timespec ts;
+                       char buff[32];
 
-                               fprintf(stderr, "%s | ", buff);
-                       }
-                       vfprintf(stderr, fmt, ap);
+                       get_monotonic_time(&ts);
+                       safe_sprintf(buff, "%ld.%06ld",
+                                    (long)ts.tv_sec,
+                                    ts.tv_nsec/1000);
+                       fprintf(stderr, "%s | ", buff);
                }
-               else
-                       log_safe(prio + 3, fmt, ap);
+               vfprintf(stderr, fmt, ap);
        }
+       else
+               log_safe(prio + 3, fmt, ap);
        va_end(ap);
 }
index c6120c1..705a5d7 100644 (file)
@@ -1,5 +1,7 @@
-void dlog (int sink, int prio, const char * fmt, ...)
-       __attribute__((format(printf, 3, 4)));
+#ifndef _DEBUG_H
+#define _DEBUG_H
+void dlog (int prio, const char *fmt, ...)
+       __attribute__((format(printf, 2, 3)));
 
 
 #include <pthread.h>
@@ -8,6 +10,23 @@ void dlog (int sink, int prio, const char * fmt, ...)
 #include "log_pthread.h"
 
 extern int logsink;
+extern int libmp_verbosity;
 
-#define condlog(prio, fmt, args...) \
-       dlog(logsink, prio, fmt "\n", ##args)
+#ifndef MAX_VERBOSITY
+#define MAX_VERBOSITY 4
+#endif
+
+enum {
+       LOGSINK_STDERR_WITH_TIME = 0,
+       LOGSINK_STDERR_WITHOUT_TIME = -1,
+       LOGSINK_SYSLOG = 1,
+};
+
+#define condlog(prio, fmt, args...)                                    \
+       do {                                                            \
+               int __p = (prio);                                       \
+                                                                       \
+               if (__p <= MAX_VERBOSITY && __p <= libmp_verbosity)     \
+                       dlog(__p, fmt "\n", ##args);                    \
+       } while (0)
+#endif /* _DEBUG_H */
index 39a5e41..c27946c 100644 (file)
@@ -8,6 +8,7 @@
  */
 #define DEFAULT_UID_ATTRIBUTE  "ID_SERIAL"
 #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"
@@ -52,6 +53,7 @@
 #define DEFAULT_FIND_MULTIPATHS_TIMEOUT -10
 #define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1
 #define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF
+#define DEFAULT_RECHECK_WWID RECHECK_WWID_OFF
 /* Enable no foreign libraries by default */
 #define DEFAULT_ENABLE_FOREIGN "NONE"
 
index 7f09361..095cbc0 100644 (file)
@@ -26,6 +26,8 @@
 #include "sysfs.h"
 #include "config.h"
 #include "wwids.h"
+#include "version.h"
+#include "time-util.h"
 
 #include "log_pthread.h"
 #include <sys/types.h>
 #define MAX_WAIT 5
 #define LOOPS_PER_SEC 5
 
+#define INVALID_VERSION ~0U
+static unsigned int dm_library_version[3] = { INVALID_VERSION, };
+static unsigned int dm_kernel_version[3] = { INVALID_VERSION, };
+static unsigned int dm_mpath_target_version[3] = { INVALID_VERSION, };
+
 static pthread_once_t dm_initialized = PTHREAD_ONCE_INIT;
+static pthread_once_t versions_initialized = PTHREAD_ONCE_INIT;
+static pthread_mutex_t libmp_dm_lock = PTHREAD_MUTEX_INITIALIZER;
 
 static int dm_conf_verbosity;
 
@@ -52,16 +61,34 @@ static inline int dm_task_set_cookie(struct dm_task *dmt, uint32_t *c, int a)
        return 1;
 }
 
-void dm_udev_wait(unsigned int c)
+static void libmp_udev_wait(unsigned int c)
 {
 }
 
-void dm_udev_set_sync_support(int c)
+static void dm_udev_set_sync_support(int c)
 {
 }
-
+#else
+static void libmp_udev_wait(unsigned int c)
+{
+       pthread_mutex_lock(&libmp_dm_lock);
+       pthread_cleanup_push(cleanup_mutex, &libmp_dm_lock);
+       dm_udev_wait(c);
+       pthread_cleanup_pop(1);
+}
 #endif
 
+int libmp_dm_task_run(struct dm_task *dmt)
+{
+       int r;
+
+       pthread_mutex_lock(&libmp_dm_lock);
+       pthread_cleanup_push(cleanup_mutex, &libmp_dm_lock);
+       r = dm_task_run(dmt);
+       pthread_cleanup_pop(1);
+       return r;
+}
+
 __attribute__((format(printf, 4, 5))) static void
 dm_write_log (int level, const char *file, int line, const char *f, ...)
 {
@@ -78,15 +105,14 @@ dm_write_log (int level, const char *file, int line, const char *f, ...)
                return;
 
        va_start(ap, f);
-       if (logsink < 1) {
-               if (logsink == 0) {
-                       time_t t = time(NULL);
-                       struct tm *tb = localtime(&t);
-                       char buff[16];
-
-                       strftime(buff, sizeof(buff), "%b %d %H:%M:%S", tb);
-                       buff[sizeof(buff)-1] = '\0';
-
+       if (logsink != LOGSINK_SYSLOG) {
+               if (logsink == LOGSINK_STDERR_WITH_TIME) {
+                       struct timespec ts;
+                       char buff[32];
+
+                       get_monotonic_time(&ts);
+                       safe_sprintf(buff, "%ld.%06ld",
+                                    (long)ts.tv_sec, ts.tv_nsec/1000);
                        fprintf(stderr, "%s | ", buff);
                }
                fprintf(stderr, "libdevmapper: %s(%i): ", file, line);
@@ -102,7 +128,7 @@ dm_write_log (int level, const char *file, int line, const char *f, ...)
        return;
 }
 
-void dm_init(int v)
+static void dm_init(int v)
 {
        /*
         * This maps libdm's standard loglevel _LOG_WARN (= 4), which is rather
@@ -112,61 +138,68 @@ void dm_init(int v)
        dm_log_init(&dm_write_log);
 }
 
+static void init_dm_library_version(void)
+{
+       char version[64];
+       unsigned int v[3];
+
+       dm_get_library_version(version, sizeof(version));
+       if (sscanf(version, "%u.%u.%u ", &v[0], &v[1], &v[2]) != 3) {
+               condlog(0, "invalid libdevmapper version %s", version);
+               return;
+       }
+       memcpy(dm_library_version, v, sizeof(dm_library_version));
+       condlog(3, "libdevmapper version %u.%.2u.%.2u",
+               dm_library_version[0], dm_library_version[1],
+               dm_library_version[2]);
+}
+
 static int
 dm_lib_prereq (void)
 {
-       char version[64];
-       int v[3];
+
 #if defined(LIBDM_API_HOLD_CONTROL)
-       int minv[3] = {1, 2, 111};
+       unsigned int minv[3] = {1, 2, 111};
 #elif defined(LIBDM_API_GET_ERRNO)
-       int minv[3] = {1, 2, 99};
+       unsigned int minv[3] = {1, 2, 99};
 #elif defined(LIBDM_API_DEFERRED)
-       int minv[3] = {1, 2, 89};
+       unsigned int minv[3] = {1, 2, 89};
 #elif defined(DM_SUBSYSTEM_UDEV_FLAG0)
-       int minv[3] = {1, 2, 82};
+       unsigned int minv[3] = {1, 2, 82};
 #elif defined(LIBDM_API_COOKIE)
-       int minv[3] = {1, 2, 38};
+       unsigned int minv[3] = {1, 2, 38};
 #else
-       int minv[3] = {1, 2, 8};
+       unsigned int minv[3] = {1, 2, 8};
 #endif
 
-       dm_get_library_version(version, sizeof(version));
-       condlog(3, "libdevmapper version %s", version);
-       if (sscanf(version, "%d.%d.%d ", &v[0], &v[1], &v[2]) != 3) {
-               condlog(0, "invalid libdevmapper version %s", version);
-               return 1;
-       }
-
-       if VERSION_GE(v, minv)
+       if (VERSION_GE(dm_library_version, minv))
                return 0;
-       condlog(0, "libdevmapper version must be >= %d.%.2d.%.2d",
+       condlog(0, "libdevmapper version must be >= %u.%.2u.%.2u",
                minv[0], minv[1], minv[2]);
        return 1;
 }
 
-int
-dm_drv_version(unsigned int *v)
+static void init_dm_drv_version(void)
 {
        char buff[64];
-
-       v[0] = 0;
-       v[1] = 0;
-       v[2] = 0;
+       unsigned int v[3];
 
        if (!dm_driver_version(buff, sizeof(buff))) {
                condlog(0, "cannot get kernel dm version");
-               return 1;
+               return;
        }
        if (sscanf(buff, "%u.%u.%u ", &v[0], &v[1], &v[2]) != 3) {
                condlog(0, "invalid kernel dm version '%s'", buff);
-               return 1;
+               return;
        }
-       return 0;
+       memcpy(dm_kernel_version, v, sizeof(dm_library_version));
+       condlog(3, "kernel device mapper v%u.%u.%u",
+               dm_kernel_version[0],
+               dm_kernel_version[1],
+               dm_kernel_version[2]);
 }
 
-int
-dm_tgt_version (unsigned int * version, char * str)
+static int dm_tgt_version (unsigned int *version, char *str)
 {
        int r = 2;
        struct dm_task *dmt;
@@ -174,16 +207,17 @@ dm_tgt_version (unsigned int * version, char * str)
        struct dm_versions *last_target;
        unsigned int *v;
 
-       version[0] = 0;
-       version[1] = 0;
-       version[2] = 0;
-
+       /*
+        * We have to call dm_task_create() and not libmp_dm_task_create()
+        * here to avoid a recursive invocation of
+        * pthread_once(&dm_initialized), which would cause a deadlock.
+        */
        if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS)))
                return 1;
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(2, DM_DEVICE_LIST_VERSIONS, dmt);
                condlog(0, "Can not communicate with kernel DM");
                goto out;
@@ -213,26 +247,25 @@ out:
        return r;
 }
 
-static int
-dm_tgt_prereq (unsigned int *ver)
+static void init_dm_mpath_version(void)
 {
-       unsigned int minv[3] = {1, 0, 3};
-       unsigned int version[3] = {0, 0, 0};
-       unsigned int * v = version;
-
-       if (dm_tgt_version(v, TGT_MPATH)) {
-               /* in doubt return not capable */
-               return 1;
-       }
+       if (!dm_tgt_version(dm_mpath_target_version, TGT_MPATH))
+               condlog(3, "DM multipath kernel driver v%u.%u.%u",
+                       dm_mpath_target_version[0],
+                       dm_mpath_target_version[1],
+                       dm_mpath_target_version[2]);
+}
 
-       /* test request based multipath capability */
-       condlog(3, "DM multipath kernel driver v%u.%u.%u",
-               v[0], v[1], v[2]);
+static int dm_tgt_prereq (unsigned int *ver)
+{
+       unsigned int minv[3] = {1, 0, 3};
 
-       if (VERSION_GE(v, minv)) {
-               ver[0] = v[0];
-               ver[1] = v[1];
-               ver[2] = v[2];
+       if (VERSION_GE(dm_mpath_target_version, minv)) {
+               if (ver) {
+                       ver[0] = dm_mpath_target_version[0];
+                       ver[1] = dm_mpath_target_version[1];
+                       ver[2] = dm_mpath_target_version[2];
+               }
                return 0;
        }
 
@@ -241,13 +274,62 @@ dm_tgt_prereq (unsigned int *ver)
        return 1;
 }
 
+static void _init_versions(void)
+{
+       /* Can't use condlog here because of how VERSION_STRING is defined */
+       if (3 <= libmp_verbosity)
+               dlog(3, VERSION_STRING);
+       init_dm_library_version();
+       init_dm_drv_version();
+       init_dm_mpath_version();
+}
+
+static int init_versions(void) {
+       pthread_once(&versions_initialized, _init_versions);
+       return (dm_library_version[0] == INVALID_VERSION ||
+               dm_kernel_version[0] == INVALID_VERSION ||
+               dm_mpath_target_version[0] == INVALID_VERSION);
+}
+
 int dm_prereq(unsigned int *v)
 {
+       if (init_versions())
+               return 1;
        if (dm_lib_prereq())
                return 1;
        return dm_tgt_prereq(v);
 }
 
+int libmp_get_version(int which, unsigned int version[3])
+{
+       unsigned int *src_version;
+
+       init_versions();
+       switch (which) {
+       case DM_LIBRARY_VERSION:
+               src_version = dm_library_version;
+               break;
+       case DM_KERNEL_VERSION:
+               src_version = dm_kernel_version;
+               break;
+       case DM_MPATH_TARGET_VERSION:
+               src_version = dm_mpath_target_version;
+               break;
+       case MULTIPATH_VERSION:
+               version[0] = (VERSION_CODE >> 16) & 0xff;
+               version[1] = (VERSION_CODE >> 8) & 0xff;
+               version[2] = VERSION_CODE & 0xff;
+               return 0;
+       default:
+               condlog(0, "%s: invalid value for 'which'", __func__);
+               return 1;
+       }
+       if (src_version[0] == INVALID_VERSION)
+               return 1;
+       memcpy(version, src_version, 3 * sizeof(*version));
+       return 0;
+}
+
 static int libmp_dm_udev_sync = 0;
 
 void libmp_udev_set_sync_support(int on)
@@ -255,23 +337,32 @@ void libmp_udev_set_sync_support(int on)
        libmp_dm_udev_sync = !!on;
 }
 
+static bool libmp_dm_init_called;
+void libmp_dm_exit(void)
+{
+       if (!libmp_dm_init_called)
+               return;
+
+       /* switch back to default libdm logging */
+       dm_log_init(NULL);
+#ifdef LIBDM_API_HOLD_CONTROL
+       /* make sure control fd is closed in dm_lib_release() */
+       dm_hold_control_dev(0);
+#endif
+}
+
 static void libmp_dm_init(void)
 {
-       struct config *conf;
-       int verbosity;
        unsigned int version[3];
 
        if (dm_prereq(version))
                exit(1);
-       conf = get_multipath_config();
-       verbosity = conf->verbosity;
-       memcpy(conf->version, version, sizeof(version));
-       put_multipath_config(conf);
-       dm_init(verbosity);
+       dm_init(libmp_verbosity);
 #ifdef LIBDM_API_HOLD_CONTROL
        dm_hold_control_dev(1);
 #endif
        dm_udev_set_sync_support(libmp_dm_udev_sync);
+       libmp_dm_init_called = true;
 }
 
 static void _do_skip_libmp_dm_init(void)
@@ -322,12 +413,12 @@ dm_simplecmd (int task, const char *name, int no_flush, int need_sync, uint16_t
                                DM_UDEV_DISABLE_LIBRARY_FALLBACK | udev_flags))
                goto out;
 
-       r = dm_task_run (dmt);
+       r = libmp_dm_task_run (dmt);
        if (!r)
                dm_log_error(2, task, dmt);
 
        if (udev_wait_flag)
-                       dm_udev_wait(cookie);
+                       libmp_udev_wait(cookie);
 out:
        dm_task_destroy (dmt);
        return r;
@@ -414,12 +505,12 @@ dm_addmap (int task, const char *target, struct multipath *mpp,
            !dm_task_set_cookie(dmt, &cookie, udev_flags))
                goto freeout;
 
-       r = dm_task_run (dmt);
+       r = libmp_dm_task_run (dmt);
        if (!r)
                dm_log_error(2, task, dmt);
 
        if (task == DM_DEVICE_CREATE)
-                       dm_udev_wait(cookie);
+                       libmp_udev_wait(cookie);
 freeout:
        if (prefixed_uuid)
                FREE(prefixed_uuid);
@@ -529,7 +620,7 @@ do_get_info(const char *name, struct dm_info *info)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_INFO, dmt);
                goto out;
        }
@@ -570,7 +661,7 @@ int dm_get_map(const char *name, unsigned long long *size, char *outparams)
        dm_task_no_open_count(dmt);
 
        errno = 0;
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_TABLE, dmt);
                if (dm_task_get_errno(dmt) == ENXIO)
                        r = DMP_NOT_FOUND;
@@ -612,7 +703,7 @@ dm_get_prefixed_uuid(const char *name, char *uuid, int uuid_len)
        if (!dm_task_set_name (dmt, name))
                goto uuidout;
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_INFO, dmt);
                goto uuidout;
        }
@@ -683,7 +774,7 @@ int dm_get_status(const char *name, char *outstatus)
        dm_task_no_open_count(dmt);
 
        errno = 0;
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_STATUS, dmt);
                if (dm_task_get_errno(dmt) == ENXIO)
                        r = DMP_NOT_FOUND;
@@ -736,7 +827,7 @@ int dm_type(const char *name, char *type)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_TABLE, dmt);
                goto out;
        }
@@ -780,7 +871,7 @@ int dm_is_mpath(const char *name)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_TABLE, dmt);
                goto out_task;
        }
@@ -836,7 +927,7 @@ dm_map_present_by_uuid(const char *uuid)
        if (safe_sprintf(prefixed_uuid, UUID_PREFIX "%s", uuid))
                goto out;
 
-       if (!(dmt = dm_task_create(DM_DEVICE_INFO)))
+       if (!(dmt = libmp_dm_task_create(DM_DEVICE_INFO)))
                goto out;
 
        dm_task_no_open_count(dmt);
@@ -844,7 +935,7 @@ dm_map_present_by_uuid(const char *uuid)
        if (!dm_task_set_uuid(dmt, prefixed_uuid))
                goto out_task;
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_INFO, dmt);
                goto out_task;
        }
@@ -890,7 +981,7 @@ dm_get_opencount (const char * mapname)
        if (!dm_task_set_name(dmt, mapname))
                goto out;
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_INFO, dmt);
                goto out;
        }
@@ -1050,7 +1141,7 @@ int dm_flush_maps (int need_suspend, int retries)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run (dmt)) {
+       if (!libmp_dm_task_run (dmt)) {
                dm_log_error(3, DM_DEVICE_LIST, dmt);
                goto out;
        }
@@ -1096,7 +1187,7 @@ dm_message(const char * mapname, char * message)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(2, DM_DEVICE_TARGET_MSG, dmt);
                goto out;
        }
@@ -1216,7 +1307,7 @@ dm_get_maps (vector mp)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_LIST, dmt);
                goto out;
        }
@@ -1301,7 +1392,7 @@ dm_mapname(int major, int minor)
         * daemon uev_trigger -> uev_add_map
         */
        while (--loop) {
-               r = dm_task_run(dmt);
+               r = libmp_dm_task_run(dmt);
 
                if (r)
                        break;
@@ -1346,7 +1437,7 @@ do_foreach_partmaps (const char * mapname,
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_LIST, dmt);
                goto out;
        }
@@ -1581,11 +1672,11 @@ dm_rename (const char * old, char * new, char *delim, int skip_kpartx)
 
        if (!dm_task_set_cookie(dmt, &cookie, udev_flags))
                goto out;
-       r = dm_task_run(dmt);
+       r = libmp_dm_task_run(dmt);
        if (!r)
                dm_log_error(2, DM_DEVICE_RENAME, dmt);
 
-       dm_udev_wait(cookie);
+       libmp_udev_wait(cookie);
 
 out:
        dm_task_destroy(dmt);
@@ -1627,7 +1718,7 @@ int dm_reassign_table(const char *name, char *old, char *new)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_TABLE, dmt);
                goto out;
        }
@@ -1660,7 +1751,7 @@ int dm_reassign_table(const char *name, char *old, char *new)
        if (modified) {
                dm_task_no_open_count(reload_dmt);
 
-               if (!dm_task_run(reload_dmt)) {
+               if (!libmp_dm_task_run(reload_dmt)) {
                        dm_log_error(3, DM_DEVICE_RELOAD, reload_dmt);
                        condlog(3, "%s: failed to reassign targets", name);
                        goto out_reload;
@@ -1707,7 +1798,7 @@ int dm_reassign(const char *mapname)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_DEPS, dmt);
                goto out;
        }
@@ -1775,7 +1866,7 @@ int dm_setgeometry(struct multipath *mpp)
                goto out;
        }
 
-       r = dm_task_run(dmt);
+       r = libmp_dm_task_run(dmt);
        if (!r)
                dm_log_error(3, DM_DEVICE_SET_GEOMETRY, dmt);
 out:
index f469c98..e29b4d4 100644 (file)
@@ -33,13 +33,11 @@ enum {
        DMP_NOT_FOUND,
 };
 
-void dm_init(int verbosity);
 int dm_prereq(unsigned int *v);
 void skip_libmp_dm_init(void);
+void libmp_dm_exit(void);
 void libmp_udev_set_sync_support(int on);
 struct dm_task *libmp_dm_task_create(int task);
-int dm_drv_version (unsigned int * version);
-int dm_tgt_version (unsigned int * version, char * str);
 int dm_simplecmd_flush (int, const char *, uint16_t);
 int dm_simplecmd_noflush (int, const char *, uint16_t);
 int dm_addmap_create (struct multipath *mpp, char *params);
@@ -89,6 +87,15 @@ struct multipath *dm_get_multipath(const char *name);
 #include <errno.h>
 #define dm_task_get_errno(x) errno
 #endif
+enum {
+       DM_LIBRARY_VERSION,
+       DM_KERNEL_VERSION,
+       DM_MPATH_TARGET_VERSION,
+       MULTIPATH_VERSION
+};
+int libmp_get_version(int which, unsigned int version[3]);
+struct dm_task;
+int libmp_dm_task_run(struct dm_task *dmt);
 
 #define dm_log_error(lvl, cmd, dmt)                          \
        condlog(lvl, "%s: libdm task=%d error: %s", __func__, \
index f12c2e5..dd08abf 100644 (file)
@@ -822,7 +822,7 @@ declare_mp_attr_handler(gid, set_gid)
 declare_mp_attr_snprint(gid, print_gid)
 
 static int
-set_fast_io_fail(vector strvec, void *ptr)
+set_undef_off_zero(vector strvec, void *ptr)
 {
        char * buff;
        int *int_ptr = (int *)ptr;
@@ -832,36 +832,36 @@ set_fast_io_fail(vector strvec, void *ptr)
                return 1;
 
        if (strcmp(buff, "off") == 0)
-               *int_ptr = MP_FAST_IO_FAIL_OFF;
+               *int_ptr = UOZ_OFF;
        else if (sscanf(buff, "%d", int_ptr) != 1 ||
-                *int_ptr < MP_FAST_IO_FAIL_ZERO)
-               *int_ptr = MP_FAST_IO_FAIL_UNSET;
+                *int_ptr < UOZ_ZERO)
+               *int_ptr = UOZ_UNDEF;
        else if (*int_ptr == 0)
-               *int_ptr = MP_FAST_IO_FAIL_ZERO;
+               *int_ptr = UOZ_ZERO;
 
        FREE(buff);
        return 0;
 }
 
 int
-print_fast_io_fail(char * buff, int len, long v)
+print_undef_off_zero(char * buff, int len, long v)
 {
-       if (v == MP_FAST_IO_FAIL_UNSET)
+       if (v == UOZ_UNDEF)
                return 0;
-       if (v == MP_FAST_IO_FAIL_OFF)
+       if (v == UOZ_OFF)
                return snprintf(buff, len, "\"off\"");
-       if (v == MP_FAST_IO_FAIL_ZERO)
+       if (v == UOZ_ZERO)
                return snprintf(buff, len, "0");
        return snprintf(buff, len, "%ld", v);
 }
 
-declare_def_handler(fast_io_fail, set_fast_io_fail)
-declare_def_snprint_defint(fast_io_fail, print_fast_io_fail,
+declare_def_handler(fast_io_fail, set_undef_off_zero)
+declare_def_snprint_defint(fast_io_fail, print_undef_off_zero,
                           DEFAULT_FAST_IO_FAIL)
-declare_ovr_handler(fast_io_fail, set_fast_io_fail)
-declare_ovr_snprint(fast_io_fail, print_fast_io_fail)
-declare_hw_handler(fast_io_fail, set_fast_io_fail)
-declare_hw_snprint(fast_io_fail, print_fast_io_fail)
+declare_ovr_handler(fast_io_fail, set_undef_off_zero)
+declare_ovr_snprint(fast_io_fail, print_undef_off_zero)
+declare_hw_handler(fast_io_fail, set_undef_off_zero)
+declare_hw_snprint(fast_io_fail, print_undef_off_zero)
 
 static int
 set_dev_loss(vector strvec, void *ptr)
@@ -899,6 +899,13 @@ 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_def_handler(eh_deadline, set_undef_off_zero)
+declare_def_snprint(eh_deadline, print_undef_off_zero)
+declare_ovr_handler(eh_deadline, set_undef_off_zero)
+declare_ovr_snprint(eh_deadline, print_undef_off_zero)
+declare_hw_handler(eh_deadline, set_undef_off_zero)
+declare_hw_snprint(eh_deadline, print_undef_off_zero)
+
 static int
 set_pgpolicy(vector strvec, void *ptr)
 {
@@ -1394,6 +1401,14 @@ declare_hw_snprint(all_tg_pt, print_yes_no_undef)
 declare_def_handler(marginal_pathgroups, set_yes_no)
 declare_def_snprint(marginal_pathgroups, print_yes_no)
 
+declare_def_handler(recheck_wwid, set_yes_no_undef)
+declare_def_snprint_defint(recheck_wwid, print_yes_no_undef, DEFAULT_RECHECK_WWID)
+declare_ovr_handler(recheck_wwid, set_yes_no_undef)
+declare_ovr_snprint(recheck_wwid, print_yes_no_undef)
+declare_hw_handler(recheck_wwid, set_yes_no_undef)
+declare_hw_snprint(recheck_wwid, print_yes_no_undef)
+
+
 static int
 def_uxsock_timeout_handler(struct config *conf, vector strvec)
 {
@@ -1771,6 +1786,7 @@ init_keywords(vector keywords)
        install_keyword("gid", &def_gid_handler, &snprint_def_gid);
        install_keyword("fast_io_fail_tmo", &def_fast_io_fail_handler, &snprint_def_fast_io_fail);
        install_keyword("dev_loss_tmo", &def_dev_loss_handler, &snprint_def_dev_loss);
+       install_keyword("eh_deadline", &def_eh_deadline_handler, &snprint_def_eh_deadline);
        install_keyword("bindings_file", &def_bindings_file_handler, &snprint_def_bindings_file);
        install_keyword("wwids_file", &def_wwids_file_handler, &snprint_def_wwids_file);
        install_keyword("prkeys_file", &def_prkeys_file_handler, &snprint_def_prkeys_file);
@@ -1811,6 +1827,7 @@ init_keywords(vector keywords)
        install_keyword("enable_foreign", &def_enable_foreign_handler,
                        &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);
@@ -1880,6 +1897,7 @@ init_keywords(vector keywords)
        install_keyword("flush_on_last_del", &hw_flush_on_last_del_handler, &snprint_hw_flush_on_last_del);
        install_keyword("fast_io_fail_tmo", &hw_fast_io_fail_handler, &snprint_hw_fast_io_fail);
        install_keyword("dev_loss_tmo", &hw_dev_loss_handler, &snprint_hw_dev_loss);
+       install_keyword("eh_deadline", &hw_eh_deadline_handler, &snprint_hw_eh_deadline);
        install_keyword("user_friendly_names", &hw_user_friendly_names_handler, &snprint_hw_user_friendly_names);
        install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler);
        install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_hw_detect_prio);
@@ -1899,6 +1917,7 @@ init_keywords(vector keywords)
        install_keyword("ghost_delay", &hw_ghost_delay_handler, &snprint_hw_ghost_delay);
        install_keyword("all_tg_pt", &hw_all_tg_pt_handler, &snprint_hw_all_tg_pt);
        install_keyword("vpd_vendor", &hw_vpd_vendor_handler, &snprint_hw_vpd_vendor);
+       install_keyword("recheck_wwid", &hw_recheck_wwid_handler, &snprint_hw_recheck_wwid);
        install_sublevel_end();
 
        install_keyword_root("overrides", &overrides_handler);
@@ -1920,6 +1939,7 @@ init_keywords(vector keywords)
        install_keyword("flush_on_last_del", &ovr_flush_on_last_del_handler, &snprint_ovr_flush_on_last_del);
        install_keyword("fast_io_fail_tmo", &ovr_fast_io_fail_handler, &snprint_ovr_fast_io_fail);
        install_keyword("dev_loss_tmo", &ovr_dev_loss_handler, &snprint_ovr_dev_loss);
+       install_keyword("eh_deadline", &ovr_eh_deadline_handler, &snprint_ovr_eh_deadline);
        install_keyword("user_friendly_names", &ovr_user_friendly_names_handler, &snprint_ovr_user_friendly_names);
        install_keyword("retain_attached_hw_handler", &ovr_retain_hwhandler_handler, &snprint_ovr_retain_hwhandler);
        install_keyword("detect_prio", &ovr_detect_prio_handler, &snprint_ovr_detect_prio);
@@ -1939,6 +1959,7 @@ init_keywords(vector keywords)
        install_keyword("max_sectors_kb", &ovr_max_sectors_kb_handler, &snprint_ovr_max_sectors_kb);
        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_root("multipaths", &multipaths_handler);
        install_keyword_multi("multipath", &multipath_handler, NULL);
index a40ac66..a917e1c 100644 (file)
@@ -13,7 +13,7 @@ int print_rr_weight(char *buff, int len, long v);
 int print_pgfailback(char *buff, int len, long v);
 int print_pgpolicy(char *buff, int len, long v);
 int print_no_path_retry(char *buff, int len, long v);
-int print_fast_io_fail(char *buff, int len, long v);
+int print_undef_off_zero(char *buff, int len, long v);
 int print_dev_loss(char *buff, int len, unsigned long v);
 int print_reservation_key(char * buff, int len, struct be64 key, uint8_t
                          flags, int source);
index c2e1754..ec99a7a 100644 (file)
@@ -200,6 +200,9 @@ path_discovery (vector pathvec, int flag)
                const char *devtype;
                const char *devpath;
 
+               if (should_exit())
+                       break;
+
                devpath = udev_list_entry_get_name(entry);
                condlog(4, "Discover device %s", devpath);
                udevice = udev_device_new_from_syspath(udev, devpath);
@@ -355,10 +358,17 @@ sysfs_get_tgt_nodename(struct path *pp, char *node)
        if (value) {
                tgtdev = udev_device_get_parent(parent);
                while (tgtdev) {
+                       char c;
+
                        tgtname = udev_device_get_sysname(tgtdev);
-                       if (tgtname && sscanf(tgtname, "end_device-%d:%d",
-                                  &host, &tgtid) == 2)
-                               break;
+                       if (tgtname) {
+                               if (sscanf(tgtname, "end_device-%d:%d:%d%c",
+                                          &host, &channel, &tgtid, &c) == 3)
+                                       break;
+                               if (sscanf(tgtname, "end_device-%d:%d%c",
+                                          &host, &tgtid, &c) == 2)
+                                       break;
+                       }
                        tgtdev = udev_device_get_parent(tgtdev);
                        tgtid = -1;
                }
@@ -584,6 +594,42 @@ sysfs_get_asymmetric_access_state(struct path *pp, char *buff, int buflen)
        return !!preferred;
 }
 
+static int
+sysfs_set_eh_deadline(struct multipath *mpp, 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)
+               return 0;
+
+       sprintf(host_name, "host%d", pp->sg_id.host_no);
+       hostdev = udev_device_new_from_subsystem_sysname(udev,
+                       "scsi_host", host_name);
+       if (!hostdev)
+               return 1;
+
+       if (mpp->eh_deadline == EH_DEADLINE_OFF)
+               len = sprintf(value, "off");
+       else if (mpp->eh_deadline == EH_DEADLINE_ZERO)
+               len = sprintf(value, "0");
+       else
+               len = sprintf(value, "%d", mpp->eh_deadline);
+
+       ret = sysfs_attr_set_value(hostdev, "eh_deadline",
+                                  value, len + 1);
+       /*
+        * not all scsi drivers support setting eh_deadline, so failing
+        * is totally reasonable
+        */
+       if (ret <= 0)
+               condlog(3, "%s: failed to set eh_deadline to %s, error %d", udev_device_get_sysname(hostdev), value, -ret);
+
+       udev_device_unref(hostdev);
+       return (ret <= 0);
+}
+
 static void
 sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
 {
@@ -593,6 +639,10 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
        unsigned int tmo;
        int ret;
 
+       if (mpp->dev_loss == DEV_LOSS_TMO_UNSET &&
+           mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
+               return;
+
        sprintf(rport_id, "rport-%d:%d-%d",
                pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id);
        rport_dev = udev_device_new_from_subsystem_sysname(udev,
@@ -700,6 +750,11 @@ sysfs_set_session_tmo(struct multipath *mpp, struct path *pp)
        char session_id[64];
        char value[11];
 
+       if (mpp->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)
+               return;
+
        sprintf(session_id, "session%d", pp->sg_id.transport_id);
        session_dev = udev_device_new_from_subsystem_sysname(udev,
                                "iscsi_session", session_id);
@@ -711,9 +766,6 @@ sysfs_set_session_tmo(struct multipath *mpp, struct path *pp)
        condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
                pp->sg_id.channel, pp->sg_id.scsi_id, session_id);
 
-       if (mpp->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 (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF) {
                        condlog(3, "%s: can't switch off fast_io_fail_tmo "
@@ -737,12 +789,28 @@ sysfs_set_session_tmo(struct multipath *mpp, struct path *pp)
 static void
 sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp)
 {
-       struct udev_device *sas_dev = NULL;
-       char end_dev_id[64];
+       struct udev_device *parent, *sas_dev = NULL;
+       const char *end_dev_id = NULL;
        char value[11];
+       static const char ed_str[] = "end_device-";
 
-       sprintf(end_dev_id, "end_device-%d:%d",
-               pp->sg_id.host_no, pp->sg_id.transport_id);
+       if (!pp->udev || mpp->dev_loss == DEV_LOSS_TMO_UNSET)
+               return;
+
+       for (parent = udev_device_get_parent(pp->udev);
+            parent;
+            parent = udev_device_get_parent(parent)) {
+               const char *ed = udev_device_get_sysname(parent);
+
+               if (!strncmp(ed, ed_str, sizeof(ed_str) - 1)) {
+                       end_dev_id = ed;
+                       break;
+               }
+       }
+       if (!end_dev_id) {
+               condlog(1, "%s: No SAS end device", pp->dev);
+               return;
+       }
        sas_dev = udev_device_new_from_subsystem_sysname(udev,
                                "sas_end_device", end_dev_id);
        if (!sas_dev) {
@@ -798,7 +866,8 @@ sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint)
                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->fast_io_fail == MP_FAST_IO_FAIL_UNSET &&
+           mpp->eh_deadline == EH_DEADLINE_UNSET)
                return 0;
 
        vector_foreach_slot(mpp->paths, pp, i) {
@@ -811,17 +880,18 @@ sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint)
                switch (pp->sg_id.proto_id) {
                case SCSI_PROTOCOL_FCP:
                        sysfs_set_rport_tmo(mpp, pp);
-                       continue;
+                       break;
                case SCSI_PROTOCOL_ISCSI:
                        sysfs_set_session_tmo(mpp, pp);
-                       continue;
+                       break;
                case SCSI_PROTOCOL_SAS:
                        sysfs_set_nexus_loss_tmo(mpp, pp);
-                       continue;
+                       break;
                default:
                        if (!err_path)
                                err_path = pp;
                }
+               sysfs_set_eh_deadline(mpp, pp);
        }
 
        if (err_path) {
@@ -1082,19 +1152,19 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                                vpd = d;
                        }
                        break;
-               case 0x8:
-                       /* SCSI Name: Prio 4 */
-                       if (memcmp(d + 4, "eui.", 4) &&
-                           memcmp(d + 4, "naa.", 4) &&
-                           memcmp(d + 4, "iqn.", 4))
-                               continue;
+               case 0x2:
+                       /* EUI-64: Prio 4 */
                        if (prio < 4) {
                                prio = 4;
                                vpd = d;
                        }
                        break;
-               case 0x2:
-                       /* EUI-64: Prio 3 */
+               case 0x8:
+                       /* SCSI Name: Prio 3 */
+                       if (memcmp(d + 4, "eui.", 4) &&
+                           memcmp(d + 4, "naa.", 4) &&
+                           memcmp(d + 4, "iqn.", 4))
+                               break;
                        if (prio < 3) {
                                prio = 3;
                                vpd = d;
@@ -1272,14 +1342,13 @@ get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen)
        return len;
 }
 
-int
-get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen)
+static int
+fetch_vpd_page(int fd, int pg, unsigned char *buff, int maxlen)
 {
-       int len, buff_len;
-       unsigned char buff[4096];
+       int buff_len;
 
-       memset(buff, 0x0, 4096);
-       if (sgio_get_vpd(buff, 4096, fd, pg) < 0) {
+       memset(buff, 0x0, maxlen);
+       if (sgio_get_vpd(buff, maxlen, fd, pg) < 0) {
                int lvl = pg == 0x80 || pg == 0x83 ? 3 : 4;
 
                condlog(lvl, "failed to issue vpd inquiry for pg%02x",
@@ -1293,10 +1362,39 @@ get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen)
                return -ENODATA;
        }
        buff_len = get_unaligned_be16(&buff[2]) + 4;
-       if (buff_len > 4096) {
+       if (buff_len > maxlen) {
                condlog(3, "vpd pg%02x page truncated", pg);
-               buff_len = 4096;
+               buff_len = maxlen;
        }
+       return buff_len;
+}
+
+/* based on sg_inq.c from sg3_utils */
+bool
+is_vpd_page_supported(int fd, int pg)
+{
+       int i, len;
+       unsigned char buff[4096];
+
+       len = fetch_vpd_page(fd, 0x00, buff, sizeof(buff));
+       if (len < 0)
+               return false;
+
+       for (i = 4; i < len; ++i)
+               if (buff[i] == pg)
+                       return true;
+       return false;
+}
+
+int
+get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen)
+{
+       int len, buff_len;
+       unsigned char buff[4096];
+
+       buff_len = fetch_vpd_page(fd, pg, buff, sizeof(buff));
+       if (buff_len < 0)
+               return buff_len;
        if (pg == 0x80)
                len = parse_vpd_pg80(buff, str, maxlen);
        else if (pg == 0x83)
@@ -1561,6 +1659,9 @@ common_sysfs_pathinfo (struct path * pp)
                return PATHINFO_FAILED;
        }
        devt = udev_device_get_devnum(pp->udev);
+       if (major(devt) == 0 && minor(devt) == 0)
+               return PATHINFO_FAILED;
+
        snprintf(pp->dev_t, BLK_DEV_SIZE, "%d:%d", major(devt), minor(devt));
 
        condlog(4, "%s: dev_t = %s", pp->dev, pp->dev_t);
@@ -1956,12 +2057,44 @@ get_vpd_uid(struct path * pp)
        return get_vpd_sysfs(parent, 0x83, pp->wwid, WWID_SIZE);
 }
 
+/* based on code from s390-tools/dasdinfo/dasdinfo.c */
+static ssize_t dasd_get_uid(struct path *pp)
+{
+       struct udev_device *parent;
+       char value[80];
+       char *p;
+       int i;
+
+       parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, "ccw",
+                                                              NULL);
+       if (!parent)
+               return -1;
+
+       if (sysfs_attr_get_value(parent, "uid", value, 80) < 0)
+               return -1;
+
+       p = value - 1;
+       /* look for the 4th '.' and cut there */
+       for (i = 0; i < 4; i++) {
+               p = index(p + 1, '.');
+               if (!p)
+                       break;
+       }
+       if (p)
+               *p = '\0';
+
+       return strlcpy(pp->wwid, value, WWID_SIZE);
+}
+
 static ssize_t uid_fallback(struct path *pp, int path_state,
                            const char **origin)
 {
        ssize_t len = -1;
 
-       if (pp->bus == SYSFS_BUS_SCSI) {
+       if (pp->bus == SYSFS_BUS_CCW) {
+               len = dasd_get_uid(pp);
+               *origin = "sysfs";
+       } else if (pp->bus == SYSFS_BUS_SCSI) {
                len = get_vpd_uid(pp);
                *origin = "sysfs";
                if (len < 0 && path_state == PATH_UP) {
@@ -1994,7 +2127,7 @@ static ssize_t uid_fallback(struct path *pp, int path_state,
        return len;
 }
 
-static bool has_uid_fallback(struct path *pp)
+bool has_uid_fallback(struct path *pp)
 {
        /*
         * Falling back to direct WWID determination is dangerous
@@ -2009,6 +2142,9 @@ static bool has_uid_fallback(struct path *pp)
                  !strcmp(pp->uid_attribute, ""))) ||
                (pp->bus == SYSFS_BUS_NVME &&
                 (!strcmp(pp->uid_attribute, DEFAULT_NVME_UID_ATTRIBUTE) ||
+                 !strcmp(pp->uid_attribute, ""))) ||
+               (pp->bus == SYSFS_BUS_CCW &&
+                (!strcmp(pp->uid_attribute, DEFAULT_DASD_UID_ATTRIBUTE) ||
                  !strcmp(pp->uid_attribute, ""))));
 }
 
@@ -2016,16 +2152,17 @@ int
 get_uid (struct path * pp, int path_state, struct udev_device *udev,
         int allow_fallback)
 {
-       char *c;
        const char *origin = "unknown";
        ssize_t len = 0;
        struct config *conf;
        int used_fallback = 0;
+       size_t i;
 
        if (!pp->uid_attribute && !pp->getuid) {
                conf = get_multipath_config();
                pthread_cleanup_push(put_multipath_config, conf);
                select_getuid(conf, pp);
+               select_recheck_wwid(conf, pp);
                pthread_cleanup_pop(1);
        }
 
@@ -2047,19 +2184,22 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev,
                } else
                        len = strlen(pp->wwid);
                origin = "callout";
-       } else {
-               bool udev_available = udev && pp->uid_attribute
-                       && *pp->uid_attribute;
+       } else if (pp->uid_attribute) {
+               /* if the uid_attribute is an empty string skip udev checking */
+               bool check_uid_attr = udev && *pp->uid_attribute;
 
-               if (udev_available) {
+               if (check_uid_attr) {
                        len = get_udev_uid(pp, pp->uid_attribute, udev);
                        origin = "udev";
                        if (len == 0)
                                condlog(1, "%s: empty udev uid", pp->dev);
                }
-               if ((!udev_available || (len <= 0 && allow_fallback))
+               if ((!check_uid_attr || (len <= 0 && allow_fallback))
                    && has_uid_fallback(pp)) {
-                       used_fallback = 1;
+                       /* if udev wasn't set or we failed in get_udev_uid()
+                        * log at a higher priority */
+                       if (!udev || check_uid_attr)
+                               used_fallback = 1;
                        len = uid_fallback(pp, path_state, &origin);
                }
        }
@@ -2070,12 +2210,9 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev,
                return 1;
        } else {
                /* Strip any trailing blanks */
-               c = strchr(pp->wwid, '\0');
-               c--;
-               while (c && c >= pp->wwid && *c == ' ') {
-                       *c = '\0';
-                       c--;
-               }
+               for (i = strlen(pp->wwid); i > 0 && pp->wwid[i-1] == ' '; i--);
+                       /* no-op */
+               pp->wwid[i] = '\0';
        }
        condlog((used_fallback)? 1 : 3, "%s: uid = %s (%s)", pp->dev,
                *pp->wwid == '\0' ? "<empty>" : pp->wwid, origin);
@@ -2107,9 +2244,17 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
                        condlog(4, "%s: hidden", pp->dev);
                        return PATHINFO_SKIPPED;
                }
-               if (is_claimed_by_foreign(pp->udev) ||
-                   filter_property(conf, pp->udev, 4, pp->uid_attribute) > 0)
+
+               if (is_claimed_by_foreign(pp->udev))
                        return PATHINFO_SKIPPED;
+
+               /*
+                * uid_attribute is required for filter_property below,
+                * and needs access to pp->hwe.
+                */
+               if (!(mask & DI_SYSFS) && (mask & DI_BLACKLIST) &&
+                   !pp->uid_attribute && VECTOR_SIZE(pp->hwe) == 0)
+                       mask |= DI_SYSFS;
        }
 
        if (strlen(pp->dev) != 0 && filter_devnode(conf->blist_devnode,
@@ -2148,7 +2293,14 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
        }
 
        if (mask & DI_BLACKLIST && mask & DI_SYSFS) {
-               if (filter_device(conf->blist_device, conf->elist_device,
+               /* uid_attribute is required for filter_property() */
+               if (pp->udev && !pp->uid_attribute) {
+                       select_getuid(conf, pp);
+                       select_recheck_wwid(conf, pp);
+               }
+
+               if (filter_property(conf, pp->udev, 4, pp->uid_attribute) > 0 ||
+                   filter_device(conf->blist_device, conf->elist_device,
                                  pp->vendor_id, pp->product_id, pp->dev) > 0 ||
                    filter_protocol(conf->blist_protocol, conf->elist_protocol,
                                    pp) > 0)
@@ -2218,6 +2370,22 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
                        if (pp->initialized != INIT_FAILED) {
                                pp->initialized = INIT_MISSING_UDEV;
                                pp->tick = conf->retrigger_delay;
+                       } else if (pp->retriggers >= conf->retrigger_tries &&
+                                  (pp->state == PATH_UP || pp->state == PATH_GHOST)) {
+                               /*
+                                * We have failed to read udev info for this path
+                                * repeatedly. We used the fallback in get_uid()
+                                * if there was any, and still got no WWID,
+                                * although the path is allegedly up.
+                                * It's likely that this path is not fit for
+                                * multipath use.
+                                */
+                               char buf[16];
+
+                               snprint_path(buf, sizeof(buf), "%T", pp, 0);
+                               condlog(1, "%s: no WWID in state \"%s\", giving up",
+                                       pp->dev, buf);
+                               return PATHINFO_SKIPPED;
                        }
                        return PATHINFO_OK;
                }
index 6444887..a5446b4 100644 (file)
@@ -54,8 +54,10 @@ ssize_t sysfs_get_inquiry(struct udev_device *udev,
                          unsigned char *buff, size_t len);
 int sysfs_get_asymmetric_access_state(struct path *pp,
                                      char *buff, int buflen);
+bool has_uid_fallback(struct path *pp);
 int get_uid(struct path * pp, int path_state, struct udev_device *udev,
            int allow_fallback);
+bool is_vpd_page_supported(int fd, int pg);
 
 /*
  * discovery bitmask
index fae58a0..f447a1c 100644 (file)
@@ -5,13 +5,15 @@ TOPDIR=../..
 include ../../Makefile.inc
 
 CFLAGS += $(LIB_CFLAGS) -I.. -I$(nvmedir)
+LDFLAGS += -L..
+LIBDEPS = -lmultipath -ludev -lpthread -lrt
 
 LIBS = libforeign-nvme.so
 
 all: $(LIBS)
 
 libforeign-%.so: %.o
-       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^
+       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ $(LIBDEPS)
 
 install:
        $(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(libdir)
index cd65afc..58fa738 100644 (file)
@@ -388,6 +388,17 @@ static struct hwentry default_hw[] = {
                .product       = "^EMC PowerMax_",
                .pgpolicy      = MULTIBUS,
        },
+       {
+               /* PowerStore */
+               .vendor        = "DellEMC",
+               .product       = "PowerStore",
+               .pgpolicy      = GROUP_BY_PRIO,
+               .prio_name     = PRIO_ALUA,
+               .hwhandler     = "1 alua",
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .no_path_retry = 3,
+               .fast_io_fail  = 15,
+       },
        /*
         * Fujitsu
         */
@@ -819,7 +830,7 @@ static struct hwentry default_hw[] = {
                 *
                 * The hwtable is searched backwards, so place this after "Generic NVMe"
                 */
-               .vendor        = "NVME",
+               .vendor        = "NVME",
                .product       = "^NetApp ONTAP Controller",
                .pgpolicy      = MULTIBUS,
                .no_path_retry = NO_PATH_RETRY_QUEUE,
@@ -1113,8 +1124,9 @@ static struct hwentry default_hw[] = {
                .pgpolicy      = MULTIBUS,
        },
        /*
-        * Imation/Nexsan
+        * StorCentric
         */
+               /* Nexsan */
        {
                /* E-Series */
                .vendor        = "NEXSAN",
@@ -1143,9 +1155,7 @@ static struct hwentry default_hw[] = {
                .prio_name     = PRIO_ALUA,
                .no_path_retry = 30,
        },
-       /*
-        * Violin Systems
-        */
+               /* Violin Systems */
        {
                /* 3000 / 6000 Series */
                .vendor        = "VIOLIN",
@@ -1192,6 +1202,14 @@ static struct hwentry default_hw[] = {
                .pgpolicy      = MULTIBUS,
                .no_path_retry = 30,
        },
+               /* Vexata */
+       {
+               /* VX */
+               .vendor        = "Vexata",
+               .product       = "VX",
+               .pgpolicy      = MULTIBUS,
+               .no_path_retry = 30,
+       },
        /*
         * Promise Technology
         */
index 58bc1dd..d8d91f6 100644 (file)
@@ -34,6 +34,7 @@
 #include "lock.h"
 #include "time-util.h"
 #include "io_err_stat.h"
+#include "util.h"
 
 #define TIMEOUT_NO_IO_NSEC             10000000 /*10ms = 10000000ns*/
 #define FLAKY_PATHFAIL_THRESHOLD       2
 #define io_err_stat_log(prio, fmt, args...) \
        condlog(prio, "io error statistic: " fmt, ##args)
 
-
-struct io_err_stat_pathvec {
-       pthread_mutex_t mutex;
-       vector          pathvec;
-};
-
 struct dio_ctx {
        struct timespec io_starttime;
        unsigned int    blksize;
@@ -70,14 +65,14 @@ struct io_err_stat_path {
        int             err_rate_threshold;
 };
 
-pthread_t              io_err_stat_thr;
-pthread_attr_t         io_err_stat_attr;
+static pthread_t       io_err_stat_thr;
 
 static pthread_mutex_t io_err_thread_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t io_err_thread_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t io_err_pathvec_lock = PTHREAD_MUTEX_INITIALIZER;
 static int io_err_thread_running = 0;
 
-static struct io_err_stat_pathvec *paths;
+static vector io_err_pathvec;
 struct vectors *vecs;
 io_context_t   ioctx;
 
@@ -88,7 +83,7 @@ static void rcu_unregister(__attribute__((unused)) void *param)
        rcu_unregister_thread();
 }
 
-struct io_err_stat_path *find_err_path_by_dev(vector pathvec, char *dev)
+static struct io_err_stat_path *find_err_path_by_dev(vector pathvec, char *dev)
 {
        int i;
        struct io_err_stat_path *pp;
@@ -166,12 +161,15 @@ fail_close:
        return 1;
 }
 
-static void destroy_directio_ctx(struct io_err_stat_path *p)
+static void free_io_err_stat_path(struct io_err_stat_path *p)
 {
        int i;
 
-       if (!p || !p->dio_ctx_array)
+       if (!p)
                return;
+       if (!p->dio_ctx_array)
+               goto free_path;
+
        cancel_inflight_io(p);
 
        for (i = 0; i < CONCUR_NR_EVENT; i++)
@@ -180,6 +178,8 @@ static void destroy_directio_ctx(struct io_err_stat_path *p)
 
        if (p->fd > 0)
                close(p->fd);
+free_path:
+       FREE(p);
 }
 
 static struct io_err_stat_path *alloc_io_err_stat_path(void)
@@ -202,51 +202,21 @@ static struct io_err_stat_path *alloc_io_err_stat_path(void)
        return p;
 }
 
-static void free_io_err_stat_path(struct io_err_stat_path *p)
-{
-       FREE(p);
-}
-
-static struct io_err_stat_pathvec *alloc_pathvec(void)
-{
-       struct io_err_stat_pathvec *p;
-       int r;
-
-       p = (struct io_err_stat_pathvec *)MALLOC(sizeof(*p));
-       if (!p)
-               return NULL;
-       p->pathvec = vector_alloc();
-       if (!p->pathvec)
-               goto out_free_struct_pathvec;
-       r = pthread_mutex_init(&p->mutex, NULL);
-       if (r)
-               goto out_free_member_pathvec;
-
-       return p;
-
-out_free_member_pathvec:
-       vector_free(p->pathvec);
-out_free_struct_pathvec:
-       FREE(p);
-       return NULL;
-}
-
-static void free_io_err_pathvec(struct io_err_stat_pathvec *p)
+static void free_io_err_pathvec(void)
 {
        struct io_err_stat_path *path;
        int i;
 
-       if (!p)
-               return;
-       pthread_mutex_destroy(&p->mutex);
-       if (!p->pathvec) {
-               vector_foreach_slot(p->pathvec, path, i) {
-                       destroy_directio_ctx(path);
-                       free_io_err_stat_path(path);
-               }
-               vector_free(p->pathvec);
-       }
-       FREE(p);
+       pthread_mutex_lock(&io_err_pathvec_lock);
+       pthread_cleanup_push(cleanup_mutex, &io_err_pathvec_lock);
+       if (!io_err_pathvec)
+               goto out;
+       vector_foreach_slot(io_err_pathvec, path, i)
+               free_io_err_stat_path(path);
+       vector_free(io_err_pathvec);
+       io_err_pathvec = NULL;
+out:
+       pthread_cleanup_pop(1);
 }
 
 /*
@@ -258,13 +228,13 @@ static int enqueue_io_err_stat_by_path(struct path *path)
 {
        struct io_err_stat_path *p;
 
-       pthread_mutex_lock(&paths->mutex);
-       p = find_err_path_by_dev(paths->pathvec, path->dev);
+       pthread_mutex_lock(&io_err_pathvec_lock);
+       p = find_err_path_by_dev(io_err_pathvec, path->dev);
        if (p) {
-               pthread_mutex_unlock(&paths->mutex);
+               pthread_mutex_unlock(&io_err_pathvec_lock);
                return 0;
        }
-       pthread_mutex_unlock(&paths->mutex);
+       pthread_mutex_unlock(&io_err_pathvec_lock);
 
        p = alloc_io_err_stat_path();
        if (!p)
@@ -276,19 +246,18 @@ static int enqueue_io_err_stat_by_path(struct path *path)
 
        if (setup_directio_ctx(p))
                goto free_ioerr_path;
-       pthread_mutex_lock(&paths->mutex);
-       if (!vector_alloc_slot(paths->pathvec))
-               goto unlock_destroy;
-       vector_set_slot(paths->pathvec, p);
-       pthread_mutex_unlock(&paths->mutex);
+       pthread_mutex_lock(&io_err_pathvec_lock);
+       if (!vector_alloc_slot(io_err_pathvec))
+               goto unlock_pathvec;
+       vector_set_slot(io_err_pathvec, p);
+       pthread_mutex_unlock(&io_err_pathvec_lock);
 
-       io_err_stat_log(2, "%s: enqueue path %s to check",
+       io_err_stat_log(3, "%s: enqueue path %s to check",
                        path->mpp->alias, path->dev);
        return 0;
 
-unlock_destroy:
-       pthread_mutex_unlock(&paths->mutex);
-       destroy_directio_ctx(p);
+unlock_pathvec:
+       pthread_mutex_unlock(&io_err_pathvec_lock);
 free_ioerr_path:
        free_io_err_stat_path(p);
 
@@ -323,8 +292,7 @@ int io_err_stat_handle_pathfail(struct path *path)
         * the repeated count threshold and time frame, we assume a path
         * which fails at least twice within 60 seconds is flaky.
         */
-       if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0)
-               return 1;
+       get_monotonic_time(&curr_time);
        if (path->io_err_pathfail_cnt == 0) {
                path->io_err_pathfail_cnt++;
                path->io_err_pathfail_starttime = curr_time.tv_sec;
@@ -375,14 +343,14 @@ int need_io_err_check(struct path *pp)
        if (uatomic_read(&io_err_thread_running) == 0)
                return 0;
        if (count_active_paths(pp->mpp) <= 0) {
-               io_err_stat_log(2, "%s: recover path early", pp->dev);
+               io_err_stat_log(2, "%s: no paths. recovering early", pp->dev);
                goto recover;
        }
        if (pp->io_err_pathfail_cnt != PATH_IO_ERR_WAITING_TO_CHECK)
                return 1;
-       if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0 ||
-           (curr_time.tv_sec - pp->io_err_dis_reinstate_time) >
-                       pp->mpp->marginal_path_err_recheck_gap_time) {
+       get_monotonic_time(&curr_time);
+       if ((curr_time.tv_sec - pp->io_err_dis_reinstate_time) >
+           pp->mpp->marginal_path_err_recheck_gap_time) {
                io_err_stat_log(4, "%s: reschedule checking after %d seconds",
                                pp->dev,
                                pp->mpp->marginal_path_err_recheck_gap_time);
@@ -393,8 +361,7 @@ int need_io_err_check(struct path *pp)
                 * Or else,  return 1 to set path state to PATH_SHAKY
                 */
                if (r == 1) {
-                       io_err_stat_log(3, "%s: enqueue fails, to recover",
-                                       pp->dev);
+                       io_err_stat_log(2, "%s: enqueue failed. recovering early", pp->dev);
                        goto recover;
                } else
                        pp->io_err_pathfail_cnt = PATH_IO_ERR_IN_CHECKING;
@@ -408,20 +375,6 @@ recover:
        return 0;
 }
 
-static int delete_io_err_stat_by_addr(struct io_err_stat_path *p)
-{
-       int i;
-
-       i = find_slot(paths->pathvec, p);
-       if (i != -1)
-               vector_del_slot(paths->pathvec, i);
-
-       destroy_directio_ctx(p);
-       free_io_err_stat_path(p);
-
-       return 0;
-}
-
 static void account_async_io_state(struct io_err_stat_path *pp, int rc)
 {
        switch (rc) {
@@ -438,17 +391,24 @@ static void account_async_io_state(struct io_err_stat_path *pp, int rc)
        }
 }
 
-static int poll_io_err_stat(struct vectors *vecs, struct io_err_stat_path *pp)
+static int io_err_stat_time_up(struct io_err_stat_path *pp)
 {
        struct timespec currtime, difftime;
-       struct path *path;
-       double err_rate;
 
-       if (clock_gettime(CLOCK_MONOTONIC, &currtime) != 0)
-               return 1;
+       get_monotonic_time(&currtime);
        timespecsub(&currtime, &pp->start_time, &difftime);
        if (difftime.tv_sec < pp->total_time)
                return 0;
+       return 1;
+}
+
+static void end_io_err_stat(struct io_err_stat_path *pp)
+{
+       struct timespec currtime;
+       struct path *path;
+       double err_rate;
+
+       get_monotonic_time(&currtime);
 
        io_err_stat_log(4, "%s: check end", pp->devname);
 
@@ -487,10 +447,6 @@ static int poll_io_err_stat(struct vectors *vecs, struct io_err_stat_path *pp)
                                pp->devname);
        }
        lock_cleanup_pop(vecs->lock);
-
-       delete_io_err_stat_by_addr(pp);
-
-       return 0;
 }
 
 static int send_each_async_io(struct dio_ctx *ct, int fd, char *dev)
@@ -501,11 +457,7 @@ static int send_each_async_io(struct dio_ctx *ct, int fd, char *dev)
                        ct->io_starttime.tv_sec == 0) {
                struct iocb *ios[1] = { &ct->io };
 
-               if (clock_gettime(CLOCK_MONOTONIC, &ct->io_starttime) != 0) {
-                       ct->io_starttime.tv_sec = 0;
-                       ct->io_starttime.tv_nsec = 0;
-                       return rc;
-               }
+               get_monotonic_time(&ct->io_starttime);
                io_prep_pread(&ct->io, fd, ct->buf, ct->blksize, 0);
                if (io_submit(ioctx, 1, ios) != 1) {
                        io_err_stat_log(5, "%s: io_submit error %i",
@@ -524,8 +476,7 @@ static void send_batch_async_ios(struct io_err_stat_path *pp)
        struct dio_ctx *ct;
        struct timespec currtime, difftime;
 
-       if (clock_gettime(CLOCK_MONOTONIC, &currtime) != 0)
-               return;
+       get_monotonic_time(&currtime);
        /*
         * Give a free time for all IO to complete or timeout
         */
@@ -540,11 +491,8 @@ static void send_batch_async_ios(struct io_err_stat_path *pp)
                if (!send_each_async_io(ct, pp->fd, pp->devname))
                        pp->io_nr++;
        }
-       if (pp->start_time.tv_sec == 0 && pp->start_time.tv_nsec == 0 &&
-               clock_gettime(CLOCK_MONOTONIC, &pp->start_time)) {
-               pp->start_time.tv_sec = 0;
-               pp->start_time.tv_nsec = 0;
-       }
+       if (pp->start_time.tv_sec == 0 && pp->start_time.tv_nsec == 0)
+               get_monotonic_time(&pp->start_time);
 }
 
 static int try_to_cancel_timeout_io(struct dio_ctx *ct, struct timespec *t,
@@ -583,9 +531,8 @@ static void poll_async_io_timeout(void)
        int             rc = PATH_UNCHECKED;
        int             i, j;
 
-       if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0)
-               return;
-       vector_foreach_slot(paths->pathvec, pp, i) {
+       get_monotonic_time(&curr_time);
+       vector_foreach_slot(io_err_pathvec, pp, i) {
                for (j = 0; j < CONCUR_NR_EVENT; j++) {
                        rc = try_to_cancel_timeout_io(pp->dio_ctx_array + j,
                                        &curr_time, pp->devname);
@@ -631,7 +578,7 @@ static void handle_async_io_done_event(struct io_event *io_evt)
        int rc = PATH_UNCHECKED;
        int i, j;
 
-       vector_foreach_slot(paths->pathvec, pp, i) {
+       vector_foreach_slot(io_err_pathvec, pp, i) {
                for (j = 0; j < CONCUR_NR_EVENT; j++) {
                        ct = pp->dio_ctx_array + j;
                        if (&ct->io == io_evt->obj) {
@@ -650,6 +597,7 @@ static void process_async_ios_event(int timeout_nsecs, char *dev)
        struct timespec timeout = { .tv_nsec = timeout_nsecs };
 
        errno = 0;
+       pthread_testcancel();
        n = io_getevents(ioctx, 1L, CONCUR_NR_EVENT, events, &timeout);
        if (n < 0) {
                io_err_stat_log(3, "%s: async io events returned %d (errno=%s)",
@@ -662,22 +610,32 @@ static void process_async_ios_event(int timeout_nsecs, char *dev)
 
 static void service_paths(void)
 {
+       struct _vector _pathvec = { .allocated = 0 };
+       /* avoid gcc warnings that &_pathvec will never be NULL in vector ops */
+       struct _vector * const tmp_pathvec = &_pathvec;
        struct io_err_stat_path *pp;
        int i;
 
-       pthread_mutex_lock(&paths->mutex);
-       vector_foreach_slot(paths->pathvec, pp, i) {
+       pthread_mutex_lock(&io_err_pathvec_lock);
+       pthread_cleanup_push(cleanup_mutex, &io_err_pathvec_lock);
+       vector_foreach_slot(io_err_pathvec, pp, i) {
                send_batch_async_ios(pp);
                process_async_ios_event(TIMEOUT_NO_IO_NSEC, pp->devname);
                poll_async_io_timeout();
-               poll_io_err_stat(vecs, pp);
+               if (io_err_stat_time_up(pp)) {
+                       if (!vector_alloc_slot(tmp_pathvec))
+                               continue;
+                       vector_del_slot(io_err_pathvec, i--);
+                       vector_set_slot(tmp_pathvec, pp);
+               }
        }
-       pthread_mutex_unlock(&paths->mutex);
-}
-
-static void cleanup_unlock(void *arg)
-{
-       pthread_mutex_unlock((pthread_mutex_t*) arg);
+       pthread_cleanup_pop(1);
+       vector_foreach_slot_backwards(tmp_pathvec, pp, i) {
+               end_io_err_stat(pp);
+               vector_del_slot(tmp_pathvec, i);
+               free_io_err_stat_path(pp);
+       }
+       vector_reset(tmp_pathvec);
 }
 
 static void cleanup_exited(__attribute__((unused)) void *arg)
@@ -727,6 +685,7 @@ static void *io_err_stat_loop(void *data)
 int start_io_err_stat_thread(void *data)
 {
        int ret;
+       pthread_attr_t io_err_stat_attr;
 
        if (uatomic_read(&io_err_thread_running) == 1)
                return 0;
@@ -735,12 +694,18 @@ int start_io_err_stat_thread(void *data)
                io_err_stat_log(4, "io_setup failed");
                return 1;
        }
-       paths = alloc_pathvec();
-       if (!paths)
+
+       pthread_mutex_lock(&io_err_pathvec_lock);
+       io_err_pathvec = vector_alloc();
+       if (!io_err_pathvec) {
+               pthread_mutex_unlock(&io_err_pathvec_lock);
                goto destroy_ctx;
+       }
+       pthread_mutex_unlock(&io_err_pathvec_lock);
 
+       setup_thread_attr(&io_err_stat_attr, 32 * 1024, 0);
        pthread_mutex_lock(&io_err_thread_lock);
-       pthread_cleanup_push(cleanup_unlock, &io_err_thread_lock);
+       pthread_cleanup_push(cleanup_mutex, &io_err_thread_lock);
 
        ret = pthread_create(&io_err_stat_thr, &io_err_stat_attr,
                             io_err_stat_loop, data);
@@ -750,6 +715,7 @@ int start_io_err_stat_thread(void *data)
                                 &io_err_thread_lock) == 0);
 
        pthread_cleanup_pop(1);
+       pthread_attr_destroy(&io_err_stat_attr);
 
        if (ret) {
                io_err_stat_log(0, "cannot create io_error statistic thread");
@@ -760,7 +726,10 @@ int start_io_err_stat_thread(void *data)
        return 0;
 
 out_free:
-       free_io_err_pathvec(paths);
+       pthread_mutex_lock(&io_err_pathvec_lock);
+       vector_free(io_err_pathvec);
+       io_err_pathvec = NULL;
+       pthread_mutex_unlock(&io_err_pathvec_lock);
 destroy_ctx:
        io_destroy(ioctx);
        io_err_stat_log(0, "failed to start io_error statistic thread");
@@ -776,6 +745,6 @@ void stop_io_err_stat_thread(void)
                pthread_cancel(io_err_stat_thr);
 
        pthread_join(io_err_stat_thr, NULL);
-       free_io_err_pathvec(paths);
+       free_io_err_pathvec();
        io_destroy(ioctx);
 }
diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
new file mode 100644 (file)
index 0000000..0cff311
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 2020 SUSE LLC
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * libmultipath ABI
+ *
+ * libmultipath doesn't have a stable ABI in the usual sense. In particular,
+ * the library does not attempt to ship different versions of the same symbol
+ * for backward compatibility.
+ *
+ * The ABI versioning only serves to avoid linking with a non-matching ABI, to
+ * cut down the set of exported symbols, and to describe it.
+ * The version string is LIBMULTIPATH_$MAJOR.$MINOR.$REL.
+ *
+ * Policy:
+ *
+ * * Bump $MAJOR for incompatible changes, like:
+ *   - symbols removed
+ *   - parameter list or return values changed for existing functions
+ *   - externally visible data structures changed in incompatible ways
+ *     (like offsets of previously existing struct members)
+ *   In this case, the new version doesn't inherit the previous versions,
+ *   because the new library doesn't provide the full previous ABI any more.
+ *   All predecessors are merged into the new version.
+ *
+ * * Bump $MINOR for compatible changes, like adding symbols.
+ *   The new version inherits the previous ones.
+ *
+ * * Bump $REL to describe deviations from upstream, e.g. in
+ *   multipath-tools packages shipped by distributions.
+ *   The new version inherits the previous ones.
+ */
+
+LIBMULTIPATH_5.0.0 {
+global:
+       /* symbols referenced by multipath and multipathd */
+       add_foreign;
+       add_map_with_path;
+       adopt_paths;
+       alloc_multipath;
+       alloc_path;
+       alloc_path_with_pathinfo;
+       alloc_strvec;
+       change_foreign;
+       check_alias_settings;
+       checker_clear_message;
+       checker_disable;
+       checker_enable;
+       checker_is_sync;
+       checker_message;
+       checker_name;
+       checker_state_name;
+       check_foreign;
+       cleanup_lock;
+       close_fd;
+       coalesce_paths;
+       convert_dev;
+       count_active_paths;
+       delete_all_foreign;
+       delete_foreign;
+       disassemble_map;
+       disassemble_status;
+       dlog;
+       dm_cancel_deferred_remove;
+       dm_enablegroup;
+       dm_fail_path;
+       _dm_flush_map;
+       dm_flush_map_nopaths;
+       dm_flush_maps;
+       dm_geteventnr;
+       dm_get_info;
+       dm_get_major_minor;
+       dm_get_map;
+       dm_get_maps;
+       dm_get_multipath;
+       dm_get_status;
+       dm_get_uuid;
+       dm_is_mpath;
+       dm_mapname;
+       dm_map_present;
+       dm_queue_if_no_path;
+       dm_reassign;
+       dm_reinstate_path;
+       dm_simplecmd_noflush;
+       dm_switchgroup;
+       domap;
+       ensure_directories_exist;
+       extract_hwe_from_path;
+       filter_devnode;
+       filter_path;
+       filter_wwid;
+       find_mp_by_alias;
+       find_mp_by_minor;
+       find_mp_by_str;
+       find_mp_by_wwid;
+       find_mpe;
+       find_path_by_dev;
+       find_path_by_devt;
+       find_slot;
+       foreign_multipath_layout;
+       foreign_path_layout;
+       free_config;
+       free_multipath;
+       free_multipathvec;
+       free_path;
+       free_pathvec;
+       free_strvec;
+       get_monotonic_time;
+       get_multipath_layout;
+       get_path_layout;
+       get_pgpolicy_id;
+       get_refwwid;
+       get_state;
+       get_udev_device;
+       get_uid;
+       get_used_hwes;
+       group_by_prio;
+       init_checkers;
+       init_foreign;
+       init_prio;
+       io_err_stat_handle_pathfail;
+       is_path_valid;
+       is_quote;
+       libmp_dm_task_create;
+       libmp_get_version;
+       libmp_udev_set_sync_support;
+       load_config;
+       log_thread_reset;
+       log_thread_start;
+       log_thread_stop;
+       need_io_err_check;
+       normalize_timespec;
+       orphan_path;
+       orphan_paths;
+       parse_prkey_flags;
+       pathcount;
+       path_discovery;
+       path_get_tpgs;
+       pathinfo;
+       path_offline;
+       print_all_paths;
+       print_foreign_topology;
+       _print_multipath_topology;
+       pthread_cond_init_mono;
+       recv_packet;
+       recv_packet_from_client;
+       reinstate_paths;
+       remember_wwid;
+       remove_map;
+       remove_map_by_alias;
+       remove_maps;
+       remove_wwid;
+       replace_wwids;
+       reset_checker_classes;
+       select_all_tg_pt;
+       select_action;
+       select_find_multipaths_timeout;
+       select_no_path_retry;
+       select_path_group;
+       select_reservation_key;
+       send_packet;
+       set_max_fds;
+       __set_no_path_retry;
+       set_path_removed;
+       set_prkey;
+       setup_map;
+       setup_thread_attr;
+       should_multipath;
+       snprint_blacklist_report;
+       snprint_config;
+       snprint_devices;
+       snprint_foreign_multipaths;
+       snprint_foreign_paths;
+       snprint_foreign_topology;
+       _snprint_multipath;
+       snprint_multipath_header;
+       snprint_multipath_map_json;
+       _snprint_multipath_topology;
+       snprint_multipath_topology_json;
+       _snprint_path;
+       snprint_path_header;
+       snprint_status;
+       snprint_wildcards;
+       stop_io_err_stat_thread;
+       store_path;
+       store_pathinfo;
+       strchop;
+       strlcpy;
+       sync_map_state;
+       sysfs_attr_set_value;
+       sysfs_get_size;
+       sysfs_is_multipathed;
+       timespecsub;
+       trigger_paths_udev_change;
+       uevent_dispatch;
+       uevent_get_dm_str;
+       uevent_get_env_positive_int;
+       uevent_is_mpath;
+       uevent_listen;
+       update_mpp_paths;
+       update_multipath_strings;
+       update_multipath_table;
+       update_pathvec_from_dm;
+       update_queue_mode_add_path;
+       update_queue_mode_del_path;
+       ux_socket_listen;
+       valid_alias;
+       vector_alloc;
+       vector_alloc_slot;
+       vector_del_slot;
+       vector_free;
+       vector_reset;
+       vector_set_slot;
+       verify_paths;
+
+       /* checkers */
+       sg_read;
+
+       /* prioritizers */
+       get_asymmetric_access_state;
+       get_prio_timeout;
+       get_target_port_group;
+       get_target_port_group_support;
+       libmp_nvme_ana_log;
+       libmp_nvme_get_nsid;
+       libmp_nvme_identify_ns;
+       log_nvme_errcode;
+       nvme_id_ctrl_ana;
+       snprint_host_wwnn;
+       snprint_host_wwpn;
+       snprint_path_serial;
+       snprint_tgt_wwnn;
+       snprint_tgt_wwpn;
+       sysfs_get_asymmetric_access_state;
+
+       /* foreign */
+       free_scandir_result;
+       sysfs_attr_get_value;
+
+       /* added in 2.1.0 */
+       libmp_dm_task_run;
+       cleanup_mutex;
+
+       /* added in 2.2.0 */
+       libmp_get_multipath_config;
+       get_multipath_config;
+       libmp_put_multipath_config;
+       put_multipath_config;
+       init_config;
+       uninit_config;
+
+       /* added in 2.3.0 */
+       udev;
+       logsink;
+       libmultipath_init;
+       libmultipath_exit;
+
+       /* added in 4.1.0 */
+       libmp_verbosity;
+
+       /* added in 4.2.0 */
+       dm_prereq;
+       skip_libmp_dm_init;
+
+       /* added in 4.3.0 */
+       start_checker_thread;
+
+       /* added in 4.4.0 */
+       get_next_string;
+
+       /* added in 4.5.0 */
+       get_vpd_sgio;
+       trigger_partitions_udev_change;
+local:
+       *;
+};
index debd36d..10fa32c 100644 (file)
@@ -9,13 +9,16 @@
 #include <string.h>
 #include <syslog.h>
 #include <time.h>
+#include <pthread.h>
 
 #include "memory.h"
 #include "log.h"
+#include "util.h"
 
 #define ALIGN(len, s) (((len)+(s)-1)/(s)*(s))
 
 struct logarea* la;
+static pthread_mutex_t logq_lock = PTHREAD_MUTEX_INITIALIZER;
 
 #if LOGDBG
 static void dump_logarea (void)
@@ -74,16 +77,23 @@ static int logarea_init (int size)
 
 int log_init(char *program_name, int size)
 {
+       int ret = 1;
+
        logdbg(stderr,"enter log_init\n");
+
+       pthread_mutex_lock(&logq_lock);
+       pthread_cleanup_push(cleanup_mutex, &logq_lock);
+
        openlog(program_name, 0, LOG_DAEMON);
+       if (!la)
+               ret = logarea_init(size);
 
-       if (logarea_init(size))
-               return 1;
+       pthread_cleanup_pop(1);
 
-       return 0;
+       return ret;
 }
 
-void free_logarea (void)
+static void free_logarea (void)
 {
        FREE(la->start);
        FREE(la->buff);
@@ -93,20 +103,30 @@ void free_logarea (void)
 
 void log_close (void)
 {
-       free_logarea();
+       pthread_mutex_lock(&logq_lock);
+       pthread_cleanup_push(cleanup_mutex, &logq_lock);
+
+       if (la)
+               free_logarea();
        closelog();
 
+       pthread_cleanup_pop(1);
        return;
 }
 
 void log_reset (char *program_name)
 {
+       pthread_mutex_lock(&logq_lock);
+       pthread_cleanup_push(cleanup_mutex, &logq_lock);
+
        closelog();
-       tzset();
        openlog(program_name, 0, LOG_DAEMON);
+
+       pthread_cleanup_pop(1);
 }
 
-int log_enqueue (int prio, const char * fmt, va_list ap)
+__attribute__((format(printf, 2, 0)))
+static int _log_enqueue(int prio, const char * fmt, va_list ap)
 {
        int len, fwd;
        char buff[MAX_MSG_SIZE];
@@ -165,7 +185,19 @@ int log_enqueue (int prio, const char * fmt, va_list ap)
        return 0;
 }
 
-int log_dequeue (void * buff)
+int log_enqueue(int prio, const char *fmt, va_list ap)
+{
+       int ret = 1;
+
+       pthread_mutex_lock(&logq_lock);
+       pthread_cleanup_push(cleanup_mutex, &logq_lock);
+       if (la)
+               ret = _log_enqueue(prio, fmt, ap);
+       pthread_cleanup_pop(1);
+       return ret;
+}
+
+static int _log_dequeue(void *buff)
 {
        struct logmsg * src = (struct logmsg *)la->head;
        struct logmsg * dst = (struct logmsg *)buff;
@@ -194,6 +226,18 @@ int log_dequeue (void * buff)
        return 0;
 }
 
+int log_dequeue(void *buff)
+{
+       int ret = 1;
+
+       pthread_mutex_lock(&logq_lock);
+       pthread_cleanup_push(cleanup_mutex, &logq_lock);
+       if (la)
+               ret = _log_dequeue(buff);
+       pthread_cleanup_pop(1);
+       return ret;
+}
+
 /*
  * this one can block under memory pressure
  */
index d2448f6..fa224e4 100644 (file)
@@ -39,6 +39,5 @@ int log_enqueue (int prio, const char * fmt, va_list ap)
 int log_dequeue (void *);
 void log_syslog (void *);
 void dump_logmsg (void *);
-void free_logarea (void);
 
 #endif /* LOG_H */
index 15baef8..6599210 100644 (file)
 #include "log_pthread.h"
 #include "log.h"
 #include "lock.h"
+#include "util.h"
 
 static pthread_t log_thr;
 
-static pthread_mutex_t logq_lock;
-static pthread_mutex_t logev_lock;
-static pthread_cond_t logev_cond;
+/* logev_lock must not be taken with logq_lock held */
+static pthread_mutex_t logev_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t logev_cond = PTHREAD_COND_INITIALIZER;
 
 static int logq_running;
 static int log_messages_pending;
 
 void log_safe (int prio, const char * fmt, va_list ap)
 {
+       bool running;
+
        if (prio > LOG_DEBUG)
                prio = LOG_DEBUG;
 
-       if (log_thr == (pthread_t)0) {
-               vsyslog(prio, fmt, ap);
-               return;
-       }
+       /*
+        * logev_lock protects logq_running. By holding it, we avoid a race
+        * with log_thread_stop() -> log_close(), which would free the logarea.
+        */
+       pthread_mutex_lock(&logev_lock);
+       pthread_cleanup_push(cleanup_mutex, &logev_lock);
+       running = logq_running;
 
-       pthread_mutex_lock(&logq_lock);
-       log_enqueue(prio, fmt, ap);
-       pthread_mutex_unlock(&logq_lock);
+       if (running) {
+               log_enqueue(prio, fmt, ap);
 
-       pthread_mutex_lock(&logev_lock);
-       log_messages_pending = 1;
-       pthread_cond_signal(&logev_cond);
-       pthread_mutex_unlock(&logev_lock);
+               log_messages_pending = 1;
+               pthread_cond_signal(&logev_cond);
+       }
+       pthread_cleanup_pop(1);
+
+       if (!running)
+               vsyslog(prio, fmt, ap);
 }
 
 static void flush_logqueue (void)
@@ -48,52 +56,72 @@ static void flush_logqueue (void)
        int empty;
 
        do {
-               pthread_mutex_lock(&logq_lock);
                empty = log_dequeue(la->buff);
-               pthread_mutex_unlock(&logq_lock);
                if (!empty)
                        log_syslog(la->buff);
        } while (empty == 0);
 }
 
+static void cleanup_log_thread(__attribute((unused)) void *arg)
+{
+       logdbg(stderr, "log thread exiting");
+       pthread_mutex_lock(&logev_lock);
+       logq_running = 0;
+       pthread_mutex_unlock(&logev_lock);
+}
+
 static void * log_thread (__attribute__((unused)) void * et)
 {
        int running;
 
        pthread_mutex_lock(&logev_lock);
-       logq_running = 1;
+       running = logq_running;
+       if (!running)
+               logq_running = 1;
+       pthread_cond_signal(&logev_cond);
        pthread_mutex_unlock(&logev_lock);
+       if (running)
+               /* already started */
+               return NULL;
+       pthread_cleanup_push(cleanup_log_thread, NULL);
 
        mlockall(MCL_CURRENT | MCL_FUTURE);
        logdbg(stderr,"enter log_thread\n");
 
        while (1) {
                pthread_mutex_lock(&logev_lock);
-               if (logq_running && !log_messages_pending)
+               pthread_cleanup_push(cleanup_mutex, &logev_lock);
+               while (!log_messages_pending)
+                       /* this is a cancellation point */
                        pthread_cond_wait(&logev_cond, &logev_lock);
                log_messages_pending = 0;
-               running = logq_running;
-               pthread_mutex_unlock(&logev_lock);
-               if (!running)
-                       break;
+               pthread_cleanup_pop(1);
+
                flush_logqueue();
        }
+       pthread_cleanup_pop(1);
        return NULL;
 }
 
 void log_thread_start (pthread_attr_t *attr)
 {
-       logdbg(stderr,"enter log_thread_start\n");
+       int running = 0;
 
-       pthread_mutex_init(&logq_lock, NULL);
-       pthread_mutex_init(&logev_lock, NULL);
-       pthread_cond_init(&logev_cond, NULL);
+       logdbg(stderr,"enter log_thread_start\n");
 
        if (log_init("multipathd", 0)) {
                fprintf(stderr,"can't initialize log buffer\n");
                exit(1);
        }
-       if (pthread_create(&log_thr, attr, log_thread, NULL)) {
+
+       pthread_mutex_lock(&logev_lock);
+       pthread_cleanup_push(cleanup_mutex, &logev_lock);
+       if (!pthread_create(&log_thr, attr, log_thread, NULL))
+               while (!(running = logq_running))
+                       pthread_cond_wait(&logev_cond, &logev_lock);
+       pthread_cleanup_pop(1);
+
+       if (!running) {
                fprintf(stderr,"can't start log thread\n");
                exit(1);
        }
@@ -104,32 +132,30 @@ void log_thread_start (pthread_attr_t *attr)
 void log_thread_reset (void)
 {
        logdbg(stderr,"resetting log\n");
-
-       pthread_mutex_lock(&logq_lock);
        log_reset("multipathd");
-       pthread_mutex_unlock(&logq_lock);
 }
 
 void log_thread_stop (void)
 {
+       int running;
+
+       if (!la)
+               return;
+
        logdbg(stderr,"enter log_thread_stop\n");
 
        pthread_mutex_lock(&logev_lock);
-       logq_running = 0;
-       pthread_cond_signal(&logev_cond);
-       pthread_mutex_unlock(&logev_lock);
+       pthread_cleanup_push(cleanup_mutex, &logev_lock);
+       running = logq_running;
+       if (running) {
+               pthread_cancel(log_thr);
+               pthread_cond_signal(&logev_cond);
+       }
+       pthread_cleanup_pop(1);
 
-       pthread_mutex_lock(&logq_lock);
-       pthread_cancel(log_thr);
-       pthread_mutex_unlock(&logq_lock);
-       pthread_join(log_thr, NULL);
-       log_thr = (pthread_t)0;
+       if (running)
+               pthread_join(log_thr, NULL);
 
        flush_logqueue();
-
-       pthread_mutex_destroy(&logq_lock);
-       pthread_mutex_destroy(&logev_lock);
-       pthread_cond_destroy(&logev_cond);
-
        log_close();
 }
index ed6d5d6..c70243c 100644 (file)
@@ -390,7 +390,7 @@ oom:
 /* non-recursive configuration stream handler */
 static int kw_level = 0;
 
-int warn_on_duplicates(vector uniques, char *str, char *file)
+int warn_on_duplicates(vector uniques, char *str, const char *file)
 {
        char *tmp;
        int i;
@@ -434,7 +434,7 @@ is_sublevel_keyword(char *str)
 }
 
 int
-validate_config_strvec(vector strvec, char *file)
+validate_config_strvec(vector strvec, const char *file)
 {
        char *str = NULL;
        int i;
@@ -499,7 +499,8 @@ validate_config_strvec(vector strvec, char *file)
 }
 
 static int
-process_stream(struct config *conf, FILE *stream, vector keywords, char *file)
+process_stream(struct config *conf, FILE *stream, vector keywords,
+              const char *file)
 {
        int i;
        int r = 0, t;
@@ -536,7 +537,7 @@ process_stream(struct config *conf, FILE *stream, vector keywords, char *file)
                if (!strcmp(str, EOB)) {
                        if (kw_level > 0) {
                                free_strvec(strvec);
-                               break;
+                               goto out;
                        }
                        condlog(0, "unmatched '%s' at line %d of %s",
                                EOB, line_nr, file);
@@ -575,7 +576,8 @@ process_stream(struct config *conf, FILE *stream, vector keywords, char *file)
 
                free_strvec(strvec);
        }
-
+       if (kw_level == 1)
+               condlog(1, "missing '%s' at end of %s", EOB, file);
 out:
        FREE(buf);
        free_uniques(uniques);
@@ -584,7 +586,7 @@ out:
 
 /* Data initialization */
 int
-process_file(struct config *conf, char *file)
+process_file(struct config *conf, const char *file)
 {
        int r;
        FILE *stream;
index 62906e9..06666cc 100644 (file)
@@ -77,7 +77,7 @@ extern void dump_keywords(vector keydump, int level);
 extern void free_keywords(vector keywords);
 extern vector alloc_strvec(char *string);
 extern void *set_value(vector strvec);
-extern int process_file(struct config *conf, char *conf_file);
+extern int process_file(struct config *conf, const char *conf_file);
 extern struct keyword * find_keyword(vector keywords, vector v, char * name);
 int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
                    const void *data);
index 19de2c7..8151e11 100644 (file)
@@ -2055,8 +2055,16 @@ int snprint_devices(struct config *conf, char *buff, size_t len,
                struct udev_device *u_dev;
 
                path = udev_list_entry_get_name(item);
+               if (!path)
+                       continue;
                u_dev = udev_device_new_from_syspath(udev, path);
+               if (!u_dev)
+                       continue;
                devname = udev_device_get_sysname(u_dev);
+               if (!devname) {
+                       udev_device_unref(u_dev);
+                       continue;
+               }
 
                fwd += snprintf(buff + fwd, len - fwd, "    %s", devname);
                if (fwd >= len)
index 194563c..c92bde7 100644 (file)
@@ -18,10 +18,32 @@ unsigned int get_prio_timeout(unsigned int timeout_ms,
        return default_timeout;
 }
 
-int init_prio (char *multipath_dir)
+int init_prio (const char *multipath_dir)
 {
+#ifdef LOAD_ALL_SHARED_LIBS
+       static const char *const all_prios[] = {
+               PRIO_ALUA,
+               PRIO_CONST,
+               PRIO_DATACORE,
+               PRIO_EMC,
+               PRIO_HDS,
+               PRIO_HP_SW,
+               PRIO_ONTAP,
+               PRIO_RANDOM,
+               PRIO_RDAC,
+               PRIO_WEIGHTED_PATH,
+               PRIO_SYSFS,
+               PRIO_PATH_LATENCY,
+               PRIO_ANA,
+       };
+       unsigned int i;
+
+       for  (i = 0; i < ARRAY_SIZE(all_prios); i++)
+               add_prio(multipath_dir, all_prios[i]);
+#else
        if (!add_prio(multipath_dir, DEFAULT_PRIO))
                return 1;
+#endif
        return 0;
 }
 
@@ -87,7 +109,7 @@ int prio_set_args (struct prio * p, const char * args)
        return snprintf(p->args, PRIO_ARGS_LEN, "%s", args);
 }
 
-struct prio * add_prio (char *multipath_dir, char * name)
+struct prio * add_prio (const char *multipath_dir, const char * name)
 {
        char libname[LIB_PRIO_NAMELEN];
        struct stat stbuf;
index 599d1d8..26754f7 100644 (file)
@@ -55,9 +55,9 @@ struct prio {
 
 unsigned int get_prio_timeout(unsigned int checker_timeout,
                              unsigned int default_timeout);
-int init_prio (char *);
+int init_prio (const char *);
 void cleanup_prio (void);
-struct prio * add_prio (char *, char *);
+struct prio * add_prio (const char *, const char *);
 int prio_getprio (struct prio *, struct path *, unsigned int);
 void prio_get (char *, struct prio *, char *, char *);
 void prio_put (struct prio *);
index fc6e0e0..8d34ae3 100644 (file)
@@ -4,6 +4,8 @@
 include ../../Makefile.inc
 
 CFLAGS += $(LIB_CFLAGS) -I..
+LDFLAGS += -L..
+LIBDEPS = -lmultipath -lm -lpthread -lrt
 
 # If you add or remove a prioritizer also update multipath/multipath.conf.5
 LIBS = \
@@ -28,11 +30,8 @@ endif
 
 all: $(LIBS)
 
-libpriopath_latency.so: path_latency.o
-       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ -lm
-
 libprio%.so: %.o
-       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^
+       $(CC) $(LDFLAGS) $(SHARED_FLAGS) -o $@ $^ $(LIBDEPS)
 
 install: $(LIBS)
        $(INSTALL_PROGRAM) -m 755 libprio*.so $(DESTDIR)$(libdir)
index 7e6e0d6..b7b3379 100644 (file)
@@ -65,9 +65,7 @@ do {                                                                  \
        __do_set_from_vec(struct hwentry, var, (src)->hwe, dest)
 
 #define do_set_from_hwe(var, src, dest, msg)                           \
-       if (!src->hwe) {                                                \
-               condlog(0, "BUG: do_set_from_hwe called with hwe == NULL"); \
-       } else if (__do_set_from_hwe(var, src, dest)) {                 \
+       if (src->hwe && __do_set_from_hwe(var, src, dest)) {            \
                origin = msg;                                           \
                goto out;                                               \
        }
@@ -498,13 +496,15 @@ check_rdac(struct path * pp)
 {
        int len;
        char buff[44];
-       const char *checker_name;
+       const char *checker_name = NULL;
 
        if (pp->bus != SYSFS_BUS_SCSI)
                return 0;
-       /* Avoid ioctl if this is likely not an RDAC array */
-       if (__do_set_from_hwe(checker_name, pp, checker_name) &&
-           strcmp(checker_name, RDAC))
+       /* Avoid checking 0xc9 if this is likely not an RDAC array */
+       if (!__do_set_from_hwe(checker_name, pp, checker_name) &&
+           !is_vpd_page_supported(pp->fd, 0xC9))
+               return 0;
+       if (checker_name && strcmp(checker_name, RDAC))
                return 0;
        len = get_vpd_sgio(pp->fd, 0xC9, 0, buff, 44);
        if (len <= 0)
@@ -581,6 +581,27 @@ out:
        return 0;
 }
 
+/* must be called after select_getuid */
+int select_recheck_wwid(struct config *conf, struct path * pp)
+{
+       const char *origin;
+
+       pp_set_ovr(recheck_wwid);
+       pp_set_hwe(recheck_wwid);
+       pp_set_conf(recheck_wwid);
+       pp_set_default(recheck_wwid, DEFAULT_RECHECK_WWID);
+out:
+       if (pp->recheck_wwid == RECHECK_WWID_ON &&
+           (pp->bus != SYSFS_BUS_SCSI || pp->getuid != NULL ||
+            !has_uid_fallback(pp))) {
+               pp->recheck_wwid = RECHECK_WWID_OFF;
+               origin = "(setting: unsupported by device type/config)";
+       }
+       condlog(3, "%s: recheck_wwid = %i %s", pp->dev, pp->recheck_wwid,
+               origin);
+       return 0;
+}
+
 void
 detect_prio(struct config *conf, struct path * pp)
 {
@@ -737,9 +758,10 @@ out:
 
 int select_minio(struct config *conf, struct multipath *mp)
 {
-       unsigned int minv_dmrq[3] = {1, 1, 0};
+       unsigned int minv_dmrq[3] = {1, 1, 0}, version[3];
 
-       if (VERSION_GE(conf->version, minv_dmrq))
+       if (!libmp_get_version(DM_MPATH_TARGET_VERSION, version)
+           && VERSION_GE(version, minv_dmrq))
                return select_minio_rq(conf, mp);
        else
                return select_minio_bio(conf, mp);
@@ -755,7 +777,7 @@ int select_fast_io_fail(struct config *conf, struct multipath *mp)
        mp_set_conf(fast_io_fail);
        mp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL);
 out:
-       print_fast_io_fail(buff, 12, mp->fast_io_fail);
+       print_undef_off_zero(buff, 12, mp->fast_io_fail);
        condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias, buff, origin);
        return 0;
 }
@@ -776,6 +798,23 @@ out:
        return 0;
 }
 
+int select_eh_deadline(struct config *conf, struct multipath *mp)
+{
+       const char *origin;
+       char buff[12];
+
+       mp_set_ovr(eh_deadline);
+       mp_set_hwe(eh_deadline);
+       mp_set_conf(eh_deadline);
+       mp->eh_deadline = EH_DEADLINE_UNSET;
+       /* not changing sysfs in default cause, so don't print anything */
+       return 0;
+out:
+       print_undef_off_zero(buff, 12, mp->eh_deadline);
+       condlog(3, "%s: eh_deadline = %s %s", mp->alias, buff, origin);
+       return 0;
+}
+
 int select_flush_on_last_del(struct config *conf, struct multipath *mp)
 {
        const char *origin;
@@ -822,9 +861,10 @@ out:
 int select_retain_hwhandler(struct config *conf, struct multipath *mp)
 {
        const char *origin;
-       unsigned int minv_dm_retain[3] = {1, 5, 0};
+       unsigned int minv_dm_retain[3] = {1, 5, 0}, version[3];
 
-       if (!VERSION_GE(conf->version, minv_dm_retain)) {
+       if (!libmp_get_version(DM_MPATH_TARGET_VERSION, version) &&
+           !VERSION_GE(version, minv_dm_retain)) {
                mp->retain_hwhandler = RETAIN_HWHANDLER_OFF;
                origin = "(setting: WARNING, requires kernel dm-mpath version >= 1.5.0)";
                goto out;
index 3d6edd8..72a7e33 100644 (file)
@@ -7,6 +7,7 @@ int select_features (struct config *conf, struct multipath * mp);
 int select_hwhandler (struct config *conf, struct multipath * mp);
 int select_checker(struct config *conf, struct path *pp);
 int select_getuid (struct config *conf, struct path * pp);
+int select_recheck_wwid(struct config *conf, struct path * pp);
 int select_prio (struct config *conf, struct path * pp);
 int select_find_multipaths_timeout(struct config *conf, struct path *pp);
 int select_no_path_retry(struct config *conf, struct multipath *mp);
@@ -17,6 +18,7 @@ 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_reservation_key(struct config *conf, struct multipath *mp);
 int select_retain_hwhandler (struct config *conf, struct multipath * mp);
 int select_detect_prio(struct config *conf, struct path * pp);
index 464596f..8751fc2 100644 (file)
@@ -234,6 +234,17 @@ alloc_multipath (void)
        return mpp;
 }
 
+void *set_mpp_hwe(struct multipath *mpp, const struct path *pp)
+{
+       if (!mpp || !pp || !pp->hwe)
+               return NULL;
+       if (mpp->hwe)
+               return mpp->hwe;
+       mpp->hwe = vector_convert(NULL, pp->hwe,
+                                 struct hwentry, identity);
+       return mpp->hwe;
+}
+
 void free_multipath_attributes(struct multipath *mpp)
 {
        if (!mpp)
@@ -290,6 +301,10 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths)
 
        free_pathvec(mpp->paths, free_paths);
        free_pgvec(mpp->pg, free_paths);
+       if (mpp->hwe) {
+               vector_free(mpp->hwe);
+               mpp->hwe = NULL;
+       }
        FREE_PTR(mpp->mpcontext);
        FREE(mpp);
 }
@@ -453,12 +468,12 @@ find_mp_by_str (const struct _vector *mpvec, const char * str)
 }
 
 struct path *
-find_path_by_dev (const struct _vector *pathvec, const char * dev)
+find_path_by_dev (const struct _vector *pathvec, const char *dev)
 {
        int i;
        struct path * pp;
 
-       if (!pathvec)
+       if (!pathvec || !dev)
                return NULL;
 
        vector_foreach_slot (pathvec, pp, i)
index 7de93d6..c8447e5 100644 (file)
@@ -219,6 +219,35 @@ enum vpd_vendor_ids {
        VPD_VP_ARRAY_SIZE, /* This must remain the last entry */
 };
 
+/*
+ * Multipath treats 0 as undefined for optional config parameters.
+ * Use this for cases where 0 is a valid option for systems multipath
+ * is communicating with
+ */
+enum undefined_off_zero {
+       UOZ_UNDEF = 0,
+       UOZ_OFF = -1,
+       UOZ_ZERO = -2,
+};
+
+enum fast_io_fail_states {
+       MP_FAST_IO_FAIL_UNSET = UOZ_UNDEF,
+       MP_FAST_IO_FAIL_OFF = UOZ_OFF,
+       MP_FAST_IO_FAIL_ZERO = UOZ_ZERO,
+};
+
+enum eh_deadline_states {
+       EH_DEADLINE_UNSET = UOZ_UNDEF,
+       EH_DEADLINE_OFF = UOZ_OFF,
+       EH_DEADLINE_ZERO = UOZ_ZERO,
+};
+
+enum recheck_wwid_states {
+       RECHECK_WWID_UNDEF = YNU_UNDEF,
+       RECHECK_WWID_OFF = YNU_NO,
+       RECHECK_WWID_ON = YNU_YES,
+};
+
 struct vpd_vendor_page {
        int pg;
        const char *name;
@@ -293,6 +322,7 @@ struct path {
        int find_multipaths_timeout;
        int marginal;
        int vpd_vendor_id;
+       int recheck_wwid;
        /* configlet pointers */
        vector hwe;
        struct gen_path generic_path;
@@ -339,6 +369,7 @@ struct multipath {
        int ghost_delay;
        int ghost_delay_tick;
        unsigned int dev_loss;
+       int eh_deadline;
        uid_t uid;
        gid_t gid;
        mode_t mode;
@@ -422,6 +453,7 @@ struct host_group {
 struct path * alloc_path (void);
 struct pathgroup * alloc_pathgroup (void);
 struct multipath * alloc_multipath (void);
+void *set_mpp_hwe(struct multipath *mpp, const struct path *pp);
 void uninitialize_path(struct path *pp);
 void free_path (struct path *);
 void free_pathvec (vector vec, enum free_path_mode free_paths);
index 8895fa7..d242c06 100644 (file)
@@ -294,11 +294,6 @@ err:
 void orphan_path(struct path *pp, const char *reason)
 {
        condlog(3, "%s: orphan path, %s", pp->dev, reason);
-       if (pp->mpp && pp->hwe && pp->mpp->hwe == pp->hwe) {
-               condlog(0, "BUG: orphaning path %s that holds hwe of %s",
-                       pp->dev, pp->mpp->alias);
-               pp->mpp->hwe = NULL;
-       }
        pp->mpp = NULL;
        uninitialize_path(pp);
 }
@@ -308,8 +303,6 @@ void orphan_paths(vector pathvec, struct multipath *mpp, const char *reason)
        int i;
        struct path * pp;
 
-       /* Avoid BUG message from orphan_path() */
-       mpp->hwe = NULL;
        vector_foreach_slot (pathvec, pp, i) {
                if (pp->mpp == mpp) {
                        if (pp->initialized == INIT_REMOVED) {
@@ -397,24 +390,26 @@ extract_hwe_from_path(struct multipath * mpp)
        if (mpp->hwe || !mpp->paths)
                return;
 
-       condlog(3, "%s: searching paths for valid hwe", mpp->alias);
+       condlog(4, "%s: searching paths for valid hwe", mpp->alias);
        /* doing this in two passes seems like paranoia to me */
        vector_foreach_slot(mpp->paths, pp, i) {
-               if (pp->state != PATH_UP)
-                       continue;
-               if (pp->hwe) {
-                       mpp->hwe = pp->hwe;
-                       return;
-               }
+               if (pp->state == PATH_UP &&
+                   pp->initialized != INIT_REMOVED && pp->hwe)
+                       goto done;
        }
        vector_foreach_slot(mpp->paths, pp, i) {
-               if (pp->state == PATH_UP)
-                       continue;
-               if (pp->hwe) {
-                       mpp->hwe = pp->hwe;
-                       return;
-               }
+               if (pp->state != PATH_UP &&
+                   pp->initialized != INIT_REMOVED && pp->hwe)
+                       goto done;
        }
+done:
+       if (i < VECTOR_SIZE(mpp->paths))
+               (void)set_mpp_hwe(mpp, pp);
+
+       if (mpp->hwe)
+               condlog(3, "%s: got hwe from path %s", mpp->alias, pp->dev);
+       else
+               condlog(2, "%s: no hwe found", mpp->alias);
 }
 
 int
@@ -428,44 +423,27 @@ update_multipath_table (struct multipath *mpp, vector pathvec, int flags)
 
        r = dm_get_map(mpp->alias, &mpp->size, params);
        if (r != DMP_OK) {
-               condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting table" : "map not present");
+               condlog(2, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting table" : "map not present");
                return r;
        }
 
        if (disassemble_map(pathvec, params, mpp)) {
-               condlog(3, "%s: cannot disassemble map", mpp->alias);
+               condlog(2, "%s: cannot disassemble map", mpp->alias);
                return DMP_ERR;
        }
 
+       *params = '\0';
+       if (dm_get_status(mpp->alias, params) != DMP_OK)
+               condlog(2, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting status" : "map not present");
+       else if (disassemble_status(params, mpp))
+               condlog(2, "%s: cannot disassemble status", mpp->alias);
+
        /* FIXME: we should deal with the return value here */
        update_pathvec_from_dm(pathvec, mpp, flags);
 
        return DMP_OK;
 }
 
-int
-update_multipath_status (struct multipath *mpp)
-{
-       int r = DMP_ERR;
-       char status[PARAMS_SIZE] = {0};
-
-       if (!mpp)
-               return r;
-
-       r = dm_get_status(mpp->alias, status);
-       if (r != DMP_OK) {
-               condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting status" : "map not present");
-               return r;
-       }
-
-       if (disassemble_status(status, mpp)) {
-               condlog(3, "%s: cannot disassemble status", mpp->alias);
-               return DMP_ERR;
-       }
-
-       return DMP_OK;
-}
-
 static struct path *find_devt_in_pathgroups(const struct multipath *mpp,
                                            const char *dev_t)
 {
@@ -514,8 +492,6 @@ void sync_paths(struct multipath *mpp, vector pathvec)
                }
                if (!found) {
                        condlog(3, "%s dropped path %s", mpp->alias, pp->dev);
-                       if (mpp->hwe == pp->hwe)
-                               mpp->hwe = NULL;
                        vector_del_slot(mpp->paths, i--);
                        orphan_path(pp, "path removed externally");
                }
@@ -524,8 +500,6 @@ void sync_paths(struct multipath *mpp, vector pathvec)
        update_mpp_paths(mpp, pathvec);
        vector_foreach_slot (mpp->paths, pp, i)
                pp->mpp = mpp;
-       if (mpp->hwe == NULL)
-               extract_hwe_from_path(mpp);
 }
 
 int
@@ -547,11 +521,8 @@ update_multipath_strings(struct multipath *mpp, vector pathvec)
        r = update_multipath_table(mpp, pathvec, 0);
        if (r != DMP_OK)
                return r;
-       sync_paths(mpp, pathvec);
 
-       r = update_multipath_status(mpp);
-       if (r != DMP_OK)
-               return r;
+       sync_paths(mpp, pathvec);
 
        vector_foreach_slot(mpp->pg, pgp, i)
                if (pgp->paths)
@@ -701,17 +672,23 @@ struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp,
 
        conf = get_multipath_config();
        mpp->mpe = find_mpe(conf->mptable, pp->wwid);
-       mpp->hwe = pp->hwe;
        put_multipath_config(conf);
 
+       /*
+        * We need to call this before select_alias(),
+        * because that accesses hwe properties.
+        */
+       if (pp->hwe && !set_mpp_hwe(mpp, pp))
+               goto out;
+
        strcpy(mpp->wwid, pp->wwid);
        find_existing_alias(mpp, vecs);
        if (select_alias(conf, mpp))
                goto out;
        mpp->size = pp->size;
 
-       if (adopt_paths(vecs->pathvec, mpp) ||
-           find_slot(vecs->pathvec, pp) == -1)
+       if (adopt_paths(vecs->pathvec, mpp) || pp->mpp != mpp ||
+           find_slot(mpp->paths, pp) == -1)
                goto out;
 
        if (add_vec) {
@@ -754,12 +731,6 @@ int verify_paths(struct multipath *mpp)
                        vector_del_slot(mpp->paths, i);
                        i--;
 
-                       /* Make sure mpp->hwe doesn't point to freed memory.
-                        * We call extract_hwe_from_path() below to restore
-                        * mpp->hwe
-                        */
-                       if (mpp->hwe == pp->hwe)
-                               mpp->hwe = NULL;
                        /*
                         * Don't delete path from pathvec yet. We'll do this
                         * after the path has been removed from the map, in
@@ -771,7 +742,6 @@ int verify_paths(struct multipath *mpp)
                                mpp->alias, pp->dev, pp->dev_t);
                }
        }
-       extract_hwe_from_path(mpp);
        return count;
 }
 
index 5390de6..7a2af1e 100644 (file)
@@ -344,24 +344,23 @@ bool sysfs_is_multipathed(struct path *pp, bool set_wwid)
                pthread_cleanup_push(close_fd, (void *)fd);
                nr = read(fd, uuid, sizeof(uuid));
                if (nr > (int)UUID_PREFIX_LEN &&
-                   !memcmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN))
+                   !memcmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN)) {
                        found = true;
-               else if (nr < 0) {
+                       if (set_wwid) {
+                               nr -= UUID_PREFIX_LEN;
+                               memcpy(pp->wwid, uuid + UUID_PREFIX_LEN, nr);
+                               if (nr == WWID_SIZE) {
+                                       condlog(4, "%s: overflow while reading from %s",
+                                               __func__, pathbuf);
+                                       pp->wwid[0] = '\0';
+                               } else {
+                                       pp->wwid[nr] = '\0';
+                                       strchop(pp->wwid);
+                               }
+                       }
+                } else if (nr < 0)
                        condlog(1, "%s: error reading from %s: %m",
                                __func__, pathbuf);
-               }
-               if (found && set_wwid) {
-                       nr -= UUID_PREFIX_LEN;
-                       memcpy(pp->wwid, uuid + UUID_PREFIX_LEN, nr);
-                       if (nr == WWID_SIZE) {
-                               condlog(4, "%s: overflow while reading from %s",
-                                       __func__, pathbuf);
-                               pp->wwid[0] = '\0';
-                       } else {
-                               pp->wwid[nr] = '\0';
-                               strchop(pp->wwid);
-                       }
-                }
 
                pthread_cleanup_pop(1);
        }
index 1748eaf..0e37f3f 100644 (file)
@@ -424,6 +424,11 @@ void cleanup_free_ptr(void *arg)
                free(*p);
 }
 
+void cleanup_mutex(void *arg)
+{
+       pthread_mutex_unlock(arg);
+}
+
 struct bitfield *alloc_bitfield(unsigned int maxbit)
 {
        unsigned int n;
@@ -445,3 +450,8 @@ void _log_bitfield_overflow(const char *f, unsigned int bit, unsigned int len)
 {
        condlog(0, "%s: bitfield overflow: %u >= %u", f, bit, len);
 }
+
+int should_exit(void)
+{
+       return 0;
+}
index 2b9703a..e9b48f9 100644 (file)
@@ -27,6 +27,7 @@ int parse_prkey(const char *ptr, uint64_t *prkey);
 int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags);
 int safe_write(int fd, const void *buf, size_t count);
 void set_max_fds(rlim_t max_fds);
+int should_exit(void);
 
 #define KERNEL_VERSION(maj, min, ptc) ((((maj) * 256) + (min)) * 256 + (ptc))
 #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
@@ -48,6 +49,7 @@ void set_max_fds(rlim_t max_fds);
 
 void close_fd(void *arg);
 void cleanup_free_ptr(void *arg);
+void cleanup_mutex(void *arg);
 
 struct scandir_result {
        struct dirent **di;
index 456b1f6..a6aa921 100644 (file)
@@ -89,10 +89,6 @@ is_path_valid(const char *name, struct config *conf, struct path *pp,
        if (pp->wwid[0] == '\0')
                return PATH_IS_NOT_VALID;
 
-       if (pp->udev && pp->uid_attribute &&
-           filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0)
-               return PATH_IS_NOT_VALID;
-
        r = is_failed_wwid(pp->wwid);
        if (r != WWID_IS_NOT_FAILED) {
                if (r == WWID_IS_FAILED)
index 6ceed53..6e68199 100644 (file)
@@ -20,8 +20,8 @@
 #ifndef _VERSION_H
 #define _VERSION_H
 
-#define VERSION_CODE 0x000805
-#define DATE_CODE    0x0b0914
+#define VERSION_CODE 0x000806
+#define DATE_CODE    0x040115
 
 #define PROG    "multipath-tools"
 
index a6a3bcf..14245cc 100644 (file)
@@ -42,24 +42,10 @@ void * mpath_alloc_prin_response(int prin_sa);
 void mpath_print_transport_id(struct prin_fulldescr *fdesc);
 int construct_transportid(const char * inp, struct transportid transid[], int num_transportids);
 
-int logsink;
-struct config *multipath_conf;
-
-struct config *get_multipath_config(void)
-{
-       return multipath_conf;
-}
-
-void put_multipath_config(__attribute__((unused)) void * arg)
-{
-       /* Noop for now */
-}
-
 void rcu_register_thread_memb(void) {}
 
 void rcu_unregister_thread_memb(void) {}
 
-struct udev *udev;
 
 static int verbose, loglevel, noisy;
 
@@ -652,18 +638,13 @@ int main(int argc, char *argv[])
                exit (1);
        }
 
-       udev = udev_new();
-       multipath_conf = mpath_lib_init();
-       if(!multipath_conf) {
-               udev_unref(udev);
+       if (libmpathpersist_init()) {
                exit(1);
        }
+       if (atexit((void(*)(void))libmpathpersist_exit))
+               fprintf(stderr, "failed to register cleanup handler for libmpathpersist: %m");
 
        ret = handle_args(argc, argv, 0);
-
-       mpath_lib_exit(multipath_conf);
-       udev_unref(udev);
-
        return (ret >= 0) ? ret : MPATH_PR_OTHER;
 }
 
index cd522e8..d191ae8 100644 (file)
@@ -32,7 +32,7 @@ ACTION=="add", ENV{.MPATH_DEVICE_READY_OLD}=="1", GOTO="paths_ok"
 
 # Check the map state directly with multipath -U.
 # This doesn't attempt I/O on the device.
-PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -U %k", GOTO="paths_ok"
+PROGRAM=="$env{MPATH_SBIN_PATH}/multipath -U -v1 %k", GOTO="paths_ok"
 ENV{MPATH_DEVICE_READY}="0", GOTO="mpath_action"
 LABEL="paths_ok"
 
index 9e920d8..ef89c7c 100644 (file)
 #include "valid.h"
 #include "alias.h"
 
-int logsink;
-struct udev *udev;
-struct config *multipath_conf;
-
 /*
  * Return values of configure(), check_path_valid(), and main().
  */
@@ -79,16 +75,6 @@ enum {
        RTVL_RETRY, /* returned by configure(), not by main() */
 };
 
-struct config *get_multipath_config(void)
-{
-       return multipath_conf;
-}
-
-void put_multipath_config(__attribute__((unused)) void *arg)
-{
-       /* Noop for now */
-}
-
 static int
 dump_config (struct config *conf, vector hwes, vector mpvec)
 {
@@ -107,7 +93,7 @@ void rcu_register_thread_memb(void) {}
 void rcu_unregister_thread_memb(void) {}
 
 static int
-filter_pathvec (vector pathvec, char * refwwid)
+filter_pathvec (vector pathvec, const char *refwwid)
 {
        int i;
        struct path * pp;
@@ -210,8 +196,7 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
                        continue;
                }
 
-               if (update_multipath_table(mpp, pathvec, flags) != DMP_OK ||
-                   update_multipath_status(mpp) != DMP_OK) {
+               if (update_multipath_table(mpp, pathvec, flags) != DMP_OK) {
                        condlog(1, "error parsing map %s", mpp->wwid);
                        remove_map(mpp, pathvec, curmp, PURGE_VEC);
                        i--;
@@ -222,22 +207,15 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid)
                        mpp->bestpg = select_path_group(mpp);
 
                if (cmd == CMD_LIST_SHORT ||
-                   cmd == CMD_LIST_LONG) {
-                       struct config *conf = get_multipath_config();
-                       print_multipath_topology(mpp, conf->verbosity);
-                       put_multipath_config(conf);
-               }
+                   cmd == CMD_LIST_LONG)
+                       print_multipath_topology(mpp, libmp_verbosity);
 
                if (cmd == CMD_CREATE)
                        reinstate_paths(mpp);
        }
 
-       if (cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG) {
-               struct config *conf = get_multipath_config();
-
-               print_foreign_topology(conf->verbosity);
-               put_multipath_config(conf);
-       }
+       if (cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG)
+               print_foreign_topology(libmp_verbosity);
 
        return 0;
 }
@@ -284,8 +262,7 @@ static int check_usable_paths(struct config *conf,
        if (mpp == NULL)
                goto free;
 
-       if (update_multipath_table(mpp, pathvec, 0) != DMP_OK ||
-                   update_multipath_status(mpp) != DMP_OK)
+       if (update_multipath_table(mpp, pathvec, 0) != DMP_OK)
                    goto free;
 
        vector_foreach_slot (mpp->pg, pg, i) {
@@ -438,7 +415,7 @@ static int print_cmd_valid(int k, const vector pathvec,
                wait = find_multipaths_check_timeout(pp, 0, &until);
        if (wait == FIND_MULTIPATHS_WAITING)
                printf("FIND_MULTIPATHS_WAIT_UNTIL=\"%ld.%06ld\"\n",
-                              until.tv_sec, until.tv_nsec/1000);
+                      (long)until.tv_sec, until.tv_nsec/1000);
        else if (wait == FIND_MULTIPATHS_WAIT_DONE)
                printf("FIND_MULTIPATHS_WAIT_UNTIL=\"0\"\n");
        printf("DM_MULTIPATH_DEVICE_PATH=\"%d\"\n",
@@ -466,13 +443,19 @@ static bool released_to_systemd(void)
        return ret;
 }
 
+static struct vectors vecs;
+static void cleanup_vecs(void)
+{
+       free_multipathvec(vecs.mpvec, KEEP_PATHS);
+       free_pathvec(vecs.pathvec, FREE_PATHS);
+}
+
 static int
 configure (struct config *conf, enum mpath_cmds cmd,
           enum devtypes dev_type, char *devpath)
 {
        vector curmp = NULL;
        vector pathvec = NULL;
-       struct vectors vecs;
        int r = RTVL_FAIL, rc;
        int di_flag = 0;
        char * refwwid = NULL;
@@ -483,6 +466,7 @@ configure (struct config *conf, enum mpath_cmds cmd,
         */
        curmp = vector_alloc();
        pathvec = vector_alloc();
+       atexit(cleanup_vecs);
 
        if (!curmp || !pathvec) {
                condlog(0, "can not allocate memory");
@@ -559,7 +543,7 @@ configure (struct config *conf, enum mpath_cmds cmd,
        if (path_discovery(pathvec, di_flag) < 0)
                goto out;
 
-       if (conf->verbosity > 2)
+       if (libmp_verbosity > 2)
                print_all_paths(pathvec, 1);
 
        get_path_layout(pathvec, 0);
@@ -594,9 +578,6 @@ out:
        if (refwwid)
                FREE(refwwid);
 
-       free_multipathvec(curmp, KEEP_PATHS);
-       free_pathvec(pathvec, FREE_PATHS);
-
        return r;
 }
 
@@ -604,8 +585,9 @@ static int
 check_path_valid(const char *name, struct config *conf, bool is_uevent)
 {
        int fd, r = PATH_IS_ERROR;
-       struct path *pp = NULL;
+       struct path *pp;
        vector pathvec = NULL;
+       const char *wwid;
 
        pp = alloc_path();
        if (!pp)
@@ -674,14 +656,19 @@ check_path_valid(const char *name, struct config *conf, bool is_uevent)
 
        if (store_path(pathvec, pp) != 0) {
                free_path(pp);
+               pp = NULL;
                goto fail;
+       } else {
+               /* make sure path isn't freed twice */
+               wwid = pp->wwid;
+               pp = NULL;
        }
 
        /* For find_multipaths = SMART, if there is more than one path
         * matching the refwwid, then the path is valid */
        if (path_discovery(pathvec, DI_SYSFS | DI_WWID) < 0)
                goto fail;
-       filter_pathvec(pathvec, pp->wwid);
+       filter_pathvec(pathvec, wwid);
        if (VECTOR_SIZE(pathvec) > 1)
                r = PATH_IS_VALID;
        else
@@ -689,21 +676,25 @@ check_path_valid(const char *name, struct config *conf, bool is_uevent)
 
 out:
        r = print_cmd_valid(r, pathvec, conf);
-       free_pathvec(pathvec, FREE_PATHS);
        /*
         * multipath -u must exit with status 0, otherwise udev won't
         * import its output.
         */
        if (!is_uevent && r == PATH_IS_NOT_VALID)
-               return RTVL_FAIL;
-       return RTVL_OK;
+               r = RTVL_FAIL;
+       else
+               r = RTVL_OK;
+       goto cleanup;
 
 fail:
-       if (pathvec)
-               free_pathvec(pathvec, FREE_PATHS);
-       else
+       r = RTVL_FAIL;
+
+cleanup:
+       if (pp != NULL)
                free_path(pp);
-       return RTVL_FAIL;
+       if (pathvec != NULL)
+               free_pathvec(pathvec, FREE_PATHS);
+       return r;
 }
 
 static int
@@ -821,12 +812,15 @@ main (int argc, char *argv[])
        int retries = -1;
        bool enable_foreign = false;
 
-       udev = udev_new();
-       logsink = 0;
-       conf = load_config(DEFAULT_CONFIGFILE);
-       if (!conf)
+       libmultipath_init();
+       if (atexit(dm_lib_exit) || atexit(libmultipath_exit))
+               condlog(1, "failed to register cleanup handler for libmultipath: %m");
+       logsink = LOGSINK_STDERR_WITH_TIME;
+       if (init_config(DEFAULT_CONFIGFILE))
                exit(RTVL_FAIL);
-       multipath_conf = conf;
+       if (atexit(uninit_config))
+               condlog(1, "failed to register cleanup handler for config: %m");
+       conf = get_multipath_config();
        conf->retrigger_tries = 0;
        conf->force_sync = 1;
        while ((arg = getopt(argc, argv, ":adDcChl::eFfM:v:p:b:BrR:itTquUwW")) != EOF ) {
@@ -840,7 +834,7 @@ main (int argc, char *argv[])
                                exit(RTVL_FAIL);
                        }
 
-                       conf->verbosity = atoi(optarg);
+                       libmp_verbosity = atoi(optarg);
                        break;
                case 'b':
                        conf->bindings_file = strdup(optarg);
@@ -902,7 +896,7 @@ main (int argc, char *argv[])
                        break;
                case 't':
                        r = dump_config(conf, NULL, NULL) ? RTVL_FAIL : RTVL_OK;
-                       goto out_free_config;
+                       goto out;
                case 'T':
                        cmd = CMD_DUMP_CONFIG;
                        break;
@@ -971,8 +965,8 @@ main (int argc, char *argv[])
        }
        if (dev_type == DEV_UEVENT) {
                openlog("multipath", 0, LOG_DAEMON);
-               setlogmask(LOG_UPTO(conf->verbosity + 3));
-               logsink = 1;
+               setlogmask(LOG_UPTO(libmp_verbosity + 3));
+               logsink = LOGSINK_SYSLOG;
        }
 
        set_max_fds(conf->max_fds);
@@ -1063,27 +1057,13 @@ main (int argc, char *argv[])
                condlog(3, "restart multipath configuration process");
 
 out:
-       dm_lib_release();
-       dm_lib_exit();
-
-       cleanup_foreign();
-       cleanup_prio();
-       cleanup_checkers();
+       put_multipath_config(conf);
+       if (dev)
+               FREE(dev);
 
        if (dev_type == DEV_UEVENT)
                closelog();
 
-out_free_config:
-       /*
-        * Freeing config must be done after dm_lib_exit(), because
-        * the logging function (dm_write_log()), which is called there,
-        * references the config.
-        */
-       free_config(conf);
-       conf = NULL;
-       udev_unref(udev);
-       if (dev)
-               FREE(dev);
 #ifdef _DEBUG_
        dbg_free_final(NULL);
 #endif
index d2101ed..064e482 100644 (file)
@@ -472,8 +472,12 @@ The default is: \fB<unset>\fR
 .
 .TP
 .B path_checker
-The default method used to determine the paths state. Possible values
-are:
+The default method used to determine the path's state. The synchronous
+checkers (all except \fItur\fR and \fIdirectio\fR) will cause multipathd to
+pause most activity, waiting up to \fIchecker_timeout\fR seconds for the path
+to respond. The asynchronous checkers (\fItur\fR and \fIdirectio\fR) will not
+pause multipathd. Instead, multipathd will check for a response once per
+second, until \fIchecker_timeout\fR seconds have elapsed. Possible values are:
 .RS
 .TP 12
 .I readsector0
@@ -499,10 +503,8 @@ Check the path state for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF
 Series, and OEM arrays from IBM DELL SGI STK and SUN.
 .TP
 .I directio
-(Deprecated) Read the first sector with direct I/O. If you have a large number
-of paths, or many AIO users on a system, you may need to use sysctl to
-increase fs.aio-max-nr. This checker is being deprecated, it could cause
-spurious path failures under high load. Please use \fItur\fR instead.
+Read the first sector with direct I/O. This checker could cause spurious path
+failures under high load. Increasing \fIchecker_timeout\fR can help with this.
 .TP
 .I cciss_tur
 (Hardware-dependent)
@@ -639,11 +641,13 @@ The default is: \fBno\fR
 .
 .TP
 .B checker_timeout
-Specify the timeout to use for path checkers and prioritizers that issue SCSI
-commands with an explicit timeout, in seconds.
+Specify the timeout to use for path checkers and prioritizers, in seconds.
+Only prioritizers that issue scsi commands use checker_timeout.  If a path
+does not respond to the checker command after \fIchecker_timeout\fR
+seconds have elapsed, it is considered down.
 .RS
 .TP
-The default is: in \fB/sys/block/sd<x>/device/timeout\fR
+The default is: in \fB/sys/block/<dev>/device/timeout\fR
 .RE
 .
 .
@@ -717,6 +721,22 @@ The default is: \fB600\fR
 .
 .
 .TP
+.B eh_deadline
+Specify the maximum number of seconds the SCSI layer will spend doing error
+handling when scsi devices fail. After this timeout the scsi layer will perform
+a full HBA reset. Setting this may be necessary in cases where the rport is
+never lost, so \fIfast_io_fail_tmo\fR and \fIdev_loss_tmo\fR will never
+trigger, but (frequently do to load) scsi commands still hang. \fBNote:\fR when
+the scsi error handler performs the HBA reset, all target paths on that HBA
+will be affected. eh_deadline should only be set in cases where all targets on
+the affected HBAs are multipathed.
+.RS
+.TP
+The default is: \fB<unset>\fR
+.RE
+.
+.
+.TP
 .B bindings_file
 The full pathname of the binding file to be used when the user_friendly_names
 option is set.
@@ -1075,7 +1095,7 @@ for the configured time, and is declared healthy, it will be returned to its
 normal pathgroup. See "Shaky paths detection" below for more information.
 .RS
 .TP
-The default  is: \fBno\fR
+The default is: \fBno\fR
 .RE
 .
 .
@@ -1153,7 +1173,7 @@ In these cases it is recommended to increase the CLI timeout to avoid
 those issues.
 .RS
 .TP
-The default is: \fB1000\fR
+The default is: \fB4000\fR
 .RE
 .
 .
@@ -1222,7 +1242,7 @@ Sets the max_sectors_kb device parameter on all path devices and the multipath
 device to the specified value.
 .RS
 .TP
-The default is: \fB<device dependent>\fR
+The default is: in \fB/sys/block/<dev>/queue/max_sectors_kb\fR
 .RE
 .
 .
@@ -1253,6 +1273,20 @@ The default is: \fB\(dqNONE\(dq\fR
 .RE
 .
 .
+.TP
+.B recheck_wwid
+If set to \fIyes\fR, when a failed path is restored, its wwid is rechecked. If
+the wwid has changed, the path is removed from the current multipath device,
+and re-added as a new path. Multipathd will also recheck a path's wwid if it is
+manually re-added. This option only works for SCSI devices that are configured
+to use the default uid_attribute, \fIID_SERIAL\fR, or sysfs for getting their
+wwid.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
 
 .
 .\" ----------------------------------------------------------------------------
index 632b82b..d053c1e 100644 (file)
@@ -30,7 +30,7 @@ ifeq ($(ENABLE_DMEVENTS_POLL),0)
 endif
 
 OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \
-       dmevents.o
+       dmevents.o init_unwinder.o
 
 EXEC = multipathd
 
index 235e2a2..1de6ad8 100644 (file)
@@ -715,6 +715,15 @@ cli_add_path (void * v, char ** reply, int * len, void * data)
        pp = find_path_by_dev(vecs->pathvec, param);
        if (pp && pp->initialized != INIT_REMOVED) {
                condlog(2, "%s: path already in pathvec", param);
+
+               if (pp->recheck_wwid == RECHECK_WWID_ON &&
+                   check_path_wwid_change(pp)) {
+                       condlog(0, "%s: wwid changed. Removing device",
+                               pp->dev);
+                       handle_path_wwid_change(pp, vecs);
+                       return 1;
+               }
+
                if (pp->mpp)
                        return 0;
        } else if (pp) {
@@ -843,14 +852,15 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
        }
        do {
                if (dm_get_major_minor(param, &major, &minor) < 0)
-                       condlog(2, "%s: not a device mapper table", param);
+                       condlog(count ? 2 : 3,
+                               "%s: not a device mapper table", param);
                else {
                        sprintf(dev_path, "dm-%d", minor);
                        alias = dm_mapname(major, minor);
                }
                /*if there is no mapname found, we first create the device*/
                if (!alias && !count) {
-                       condlog(2, "%s: mapname not found for %d:%d",
+                       condlog(3, "%s: mapname not found for %d:%d",
                                param, major, minor);
                        get_refwwid(CMD_NONE, param, DEV_DEVMAP,
                                    vecs->pathvec, &refwwid);
@@ -860,7 +870,6 @@ cli_add_map (void * v, char ** reply, int * len, void * data)
                                    != CP_OK)
                                        condlog(2, "%s: coalesce_paths failed",
                                                                        param);
-                               dm_lib_release();
                                FREE(refwwid);
                        }
                } /*we attempt to create device only once*/
@@ -1032,7 +1041,6 @@ cli_resize(void *v, char **reply, int *len, void *data)
        if (resize_map(mpp, size, vecs) != 0)
                return 1;
 
-       dm_lib_release();
        if (setup_multipath(vecs, mpp) != 0)
                return 1;
        sync_map_state(mpp);
index 5f2d210..f52f597 100644 (file)
@@ -60,7 +60,7 @@ int dmevent_poll_supported(void)
 {
        unsigned int v[3];
 
-       if (dm_drv_version(v))
+       if (libmp_get_version(DM_KERNEL_VERSION, v))
                return 0;
 
        if (VERSION_GE(v, DM_VERSION_FOR_ARM_POLL))
@@ -156,7 +156,7 @@ static int dm_get_events(void)
 
        dm_task_no_open_count(dmt);
 
-       if (!dm_task_run(dmt)) {
+       if (!libmp_dm_task_run(dmt)) {
                dm_log_error(3, DM_DEVICE_LIST, dmt);
                goto fail;
        }
@@ -257,6 +257,8 @@ void unwatch_all_dmevents(void)
        struct dev_event *dev_evt;
        int i;
 
+       if (!waiter)
+               return;
        pthread_mutex_lock(&waiter->events_lock);
        vector_foreach_slot(waiter->events, dev_evt, i)
                free(dev_evt);
diff --git a/multipathd/init_unwinder.c b/multipathd/init_unwinder.c
new file mode 100644 (file)
index 0000000..14467f3
--- /dev/null
@@ -0,0 +1,34 @@
+#include <pthread.h>
+#include <unistd.h>
+#include "init_unwinder.h"
+
+static pthread_mutex_t dummy_mtx = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t dummy_cond = PTHREAD_COND_INITIALIZER;
+
+static void *dummy_thread(void *arg __attribute__((unused)))
+{
+       pthread_mutex_lock(&dummy_mtx);
+       pthread_cond_broadcast(&dummy_cond);
+       pthread_mutex_unlock(&dummy_mtx);
+       pause();
+       return NULL;
+}
+
+int init_unwinder(void)
+{
+       pthread_t dummy;
+       int rc;
+
+       pthread_mutex_lock(&dummy_mtx);
+
+       rc = pthread_create(&dummy, NULL, dummy_thread, NULL);
+       if (rc != 0) {
+               pthread_mutex_unlock(&dummy_mtx);
+               return rc;
+       }
+
+       pthread_cond_wait(&dummy_cond, &dummy_mtx);
+       pthread_mutex_unlock(&dummy_mtx);
+
+       return pthread_cancel(dummy);
+}
diff --git a/multipathd/init_unwinder.h b/multipathd/init_unwinder.h
new file mode 100644 (file)
index 0000000..ada09f8
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef _INIT_UNWINDER_H
+#define _INIT_UNWINDER_H 1
+
+/*
+ * init_unwinder(): make sure unwinder symbols are loaded
+ *
+ * libc's implementation of pthread_cancel() loads symbols from
+ * libgcc_s.so using dlopen() when pthread_cancel() is called
+ * for the first time. This happens even with LD_BIND_NOW=1.
+ * This may imply the need for file system access when a thread is
+ * cancelled, which in the case of multipath-tools might be in a
+ * dangerous situation where multipathd must avoid blocking.
+ *
+ * Call load_unwinder() during startup to make sure the dynamic
+ * linker has all necessary symbols resolved early on.
+ *
+ * Return: 0 if successful, an error number otherwise.
+ */
+int init_unwinder(void);
+
+#endif
index a4abbb2..102946b 100644 (file)
 #include "wwids.h"
 #include "foreign.h"
 #include "../third-party/valgrind/drd.h"
+#include "init_unwinder.h"
 
 #define FILE_NAME_SIZE 256
 #define CMDSIZE 160
 #define MSG_SIZE 32
 
-#define LOG_MSG(lvl, verb, pp)                                 \
+#define LOG_MSG(lvl, pp)                                       \
 do {                                                           \
        if (pp->mpp && checker_selected(&pp->checker) &&        \
-           lvl <= verb) {                                      \
+           lvl <= libmp_verbosity) {                                   \
                if (pp->offline)                                \
                        condlog(lvl, "%s: %s - path offline",   \
                                pp->mpp->alias, pp->dev);       \
@@ -115,21 +116,24 @@ struct mpath_event_param
        struct multipath *mpp;
 };
 
-int logsink;
 int uxsock_timeout;
-int verbosity;
-int bindings_read_only;
+static int verbosity;
+static int bindings_read_only;
 int ignore_new_devs;
 #ifdef NO_DMEVENTS_POLL
-int poll_dmevents = 0;
+static int poll_dmevents = 0;
 #else
-int poll_dmevents = 1;
+static int poll_dmevents = 1;
 #endif
 /* Don't access this variable without holding config_lock */
-enum daemon_status running_state = DAEMON_INIT;
+static volatile enum daemon_status running_state = DAEMON_INIT;
 pid_t daemon_pid;
-pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER;
-pthread_cond_t config_cond;
+static pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t config_cond;
+static pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr;
+static bool check_thr_started, uevent_thr_started, uxlsnr_thr_started,
+       uevq_thr_started, dmevent_thr_started;
+static int pid_fd = -1;
 
 static inline enum daemon_status get_running_state(void)
 {
@@ -141,12 +145,15 @@ static inline enum daemon_status get_running_state(void)
        return st;
 }
 
+int should_exit(void)
+{
+       return get_running_state() == DAEMON_SHUTDOWN;
+}
+
 /*
  * global copy of vecs for use in sig handlers
  */
-struct vectors * gvecs;
-
-struct udev * udev;
+static struct vectors * gvecs;
 
 struct config *multipath_conf;
 
@@ -184,6 +191,8 @@ static void do_sd_notify(enum daemon_status old_state,
 {
        char notify_msg[MSG_SIZE];
        const char *msg;
+       static bool startup_done = false;
+
        /*
         * Checkerloop switches back and forth between idle and running state.
         * No need to tell systemd each time.
@@ -200,6 +209,14 @@ static void do_sd_notify(enum daemon_status old_state,
 
        if (msg && !safe_sprintf(notify_msg, "STATUS=%s", msg))
                sd_notify(0, notify_msg);
+
+       if (new_state == DAEMON_SHUTDOWN)
+               sd_notify(0, "STOPPING=1");
+       else if (new_state == DAEMON_IDLE && old_state == DAEMON_CONFIGURE) {
+               sd_notify(0, "READY=1");
+               startup_done = true;
+       } else if (new_state == DAEMON_CONFIGURE && startup_done)
+               sd_notify(0, "RELOADING=1");
 }
 #endif
 
@@ -208,6 +225,23 @@ static void config_cleanup(__attribute__((unused)) void *arg)
        pthread_mutex_unlock(&config_lock);
 }
 
+#define __wait_for_state_change(condition, ms)                         \
+       ({                                                              \
+               struct timespec tmo;                                    \
+               int rc = 0;                                             \
+                                                                       \
+               if (condition) {                                        \
+                       get_monotonic_time(&tmo);                       \
+                       tmo.tv_nsec += (ms) * 1000 * 1000;              \
+                       normalize_timespec(&tmo);                       \
+                       do                                              \
+                               rc = pthread_cond_timedwait(            \
+                                       &config_cond, &config_lock, &tmo); \
+                       while (rc == 0 && (condition));                 \
+               }                                                       \
+               rc;                                                     \
+       })
+
 /*
  * If the current status is @oldstate, wait for at most @ms milliseconds
  * for the state to change, and return the new state, which may still be
@@ -217,20 +251,14 @@ enum daemon_status wait_for_state_change_if(enum daemon_status oldstate,
                                            unsigned long ms)
 {
        enum daemon_status st;
-       struct timespec tmo;
 
        if (oldstate == DAEMON_SHUTDOWN)
                return DAEMON_SHUTDOWN;
 
        pthread_mutex_lock(&config_lock);
        pthread_cleanup_push(config_cleanup, NULL);
+       __wait_for_state_change(running_state == oldstate, ms);
        st = running_state;
-       if (st == oldstate && clock_gettime(CLOCK_MONOTONIC, &tmo) == 0) {
-               tmo.tv_nsec += ms * 1000 * 1000;
-               normalize_timespec(&tmo);
-               (void)pthread_cond_timedwait(&config_cond, &config_lock, &tmo);
-               st = running_state;
-       }
        pthread_cleanup_pop(1);
        return st;
 }
@@ -266,27 +294,14 @@ int set_config_state(enum daemon_status state)
        pthread_cleanup_push(config_cleanup, NULL);
        pthread_mutex_lock(&config_lock);
        if (running_state != state) {
-#ifdef USE_SYSTEMD
-               enum daemon_status old_state = running_state;
-#endif
 
                if (running_state == DAEMON_SHUTDOWN)
                        rc = EINVAL;
-               else if (running_state != DAEMON_IDLE) {
-                       struct timespec ts;
-
-                       get_monotonic_time(&ts);
-                       ts.tv_sec += 1;
-                       rc = pthread_cond_timedwait(&config_cond,
-                                                   &config_lock, &ts);
-               }
-               if (!rc && (running_state != DAEMON_SHUTDOWN)) {
-                       running_state = state;
-                       pthread_cond_broadcast(&config_cond);
-#ifdef USE_SYSTEMD
-                       do_sd_notify(old_state, state);
-#endif
-               }
+               else
+                       rc = __wait_for_state_change(
+                               running_state != DAEMON_IDLE, 1000);
+               if (!rc)
+                       __post_config_state(state);
        }
        pthread_cleanup_pop(1);
        return rc;
@@ -497,7 +512,6 @@ retry:
                sleep(1);
                goto retry;
        }
-       dm_lib_release();
 
 fail:
        if (new_map && (retries < 0 || wait_for_events(mpp, vecs))) {
@@ -545,8 +559,6 @@ add_map_without_path (struct vectors *vecs, const char *alias)
 
        if (update_multipath_table(mpp, vecs->pathvec, 0) != DMP_OK)
                goto out;
-       if (update_multipath_status(mpp) != DMP_OK)
-               goto out;
 
        if (!vector_alloc_slot(vecs->mpvec))
                goto out;
@@ -598,10 +610,8 @@ coalesce_maps(struct vectors *vecs, vector nmpv)
                                vector_del_slot(ompv, i);
                                i--;
                        }
-                       else {
-                               dm_lib_release();
+                       else
                                condlog(2, "%s devmap removed", ompp->alias);
-                       }
                } else if (reassign_maps) {
                        condlog(3, "%s: Reassign existing device-mapper"
                                " devices", ompp->alias);
@@ -647,10 +657,8 @@ flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths)
                }
                return r;
        }
-       else {
-               dm_lib_release();
+       else
                condlog(2, "%s: map flushed", mpp->alias);
-       }
 
        orphan_paths(vecs->pathvec, mpp, "map flushed");
        remove_map_and_stop_waiter(mpp, vecs);
@@ -813,6 +821,68 @@ ev_remove_map (char * devname, char * alias, int minor, struct vectors * vecs)
        return flush_map(mpp, vecs, 0);
 }
 
+static void
+rescan_path(struct udev_device *ud)
+{
+       ud = udev_device_get_parent_with_subsystem_devtype(ud, "scsi",
+                                                          "scsi_device");
+       if (ud)
+               sysfs_attr_set_value(ud, "rescan", "1", strlen("1"));
+}
+
+void
+handle_path_wwid_change(struct path *pp, struct vectors *vecs)
+{
+       struct udev_device *udd;
+
+       if (!pp || !pp->udev)
+               return;
+
+       udd = udev_device_ref(pp->udev);
+       if (ev_remove_path(pp, vecs, 1) != 0 && pp->mpp) {
+               pp->dmstate = PSTATE_FAILED;
+               dm_fail_path(pp->mpp->alias, pp->dev_t);
+       }
+       rescan_path(udd);
+       sysfs_attr_set_value(udd, "uevent", "add", strlen("add"));
+       udev_device_unref(udd);
+}
+
+bool
+check_path_wwid_change(struct path *pp)
+{
+       char wwid[WWID_SIZE];
+       int len = 0;
+       size_t i;
+
+       if (!strlen(pp->wwid))
+               return false;
+
+       /* Get the real fresh device wwid by sgio. sysfs still has old
+        * data, so only get_vpd_sgio will work to get the new wwid */
+       len = get_vpd_sgio(pp->fd, 0x83, 0, wwid, WWID_SIZE);
+
+       if (len <= 0) {
+               condlog(2, "%s: failed to check wwid by sgio: len = %d",
+                       pp->dev, len);
+               return false;
+       }
+
+       /*Strip any trailing blanks */
+       for (i = strlen(pp->wwid); i > 0 && pp->wwid[i-1] == ' '; i--);
+               /* no-op */
+       pp->wwid[i] = '\0';
+       condlog(4, "%s: Got wwid %s by sgio", pp->dev, wwid);
+
+       if (strncmp(wwid, pp->wwid, WWID_SIZE)) {
+               condlog(0, "%s: wwid '%s' doesn't match wwid '%s' from device",
+                       pp->dev, pp->wwid, wwid);
+               return true;
+       }
+
+       return false;
+}
+
 static int
 uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
 {
@@ -880,13 +950,7 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
                                 */
                                pp->mpp = prev_mpp;
                                ret = ev_remove_path(pp, vecs, true);
-                               if (r == PATHINFO_OK && !ret)
-                                       /*
-                                        * Path successfully freed, move on to
-                                        * "new path" code path below
-                                        */
-                                       pp = NULL;
-                               else {
+                               if (ret != 0) {
                                        /*
                                         * Failure in ev_remove_path will keep
                                         * path in pathvec in INIT_REMOVED state
@@ -897,7 +961,12 @@ uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map)
                                        dm_fail_path(pp->mpp->alias, pp->dev_t);
                                        condlog(1, "%s: failed to re-add path still mapped in %s",
                                                pp->dev, pp->mpp->alias);
-                               }
+                               } else if (r == PATHINFO_OK)
+                                       /*
+                                        * Path successfully freed, move on to
+                                        * "new path" code path below
+                                        */
+                                       pp = NULL;
                        } else if (r == PATHINFO_SKIPPED) {
                                condlog(3, "%s: remove blacklisted path",
                                        uev->kernel);
@@ -998,8 +1067,8 @@ rescan:
        if (mpp) {
                condlog(4,"%s: adopting all paths for path %s",
                        mpp->alias, pp->dev);
-               if (adopt_paths(vecs->pathvec, mpp) ||
-                   find_slot(vecs->pathvec, pp) == -1)
+               if (adopt_paths(vecs->pathvec, mpp) || pp->mpp != mpp ||
+                   find_slot(mpp->paths, pp) == -1)
                        goto fail; /* leave path added to pathvec */
 
                verify_paths(mpp);
@@ -1018,7 +1087,7 @@ rescan:
                         */
                        start_waiter = 1;
                }
-               if (!start_waiter)
+               else
                        goto fail; /* leave path added to pathvec */
        }
 
@@ -1067,7 +1136,6 @@ rescan:
                else
                        goto fail_map;
        }
-       dm_lib_release();
 
        if ((mpp->action == ACT_CREATE ||
             (mpp->action == ACT_NOTHING && start_waiter && !mpp->waiter)) &&
@@ -1154,13 +1222,6 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
                        vector_del_slot(mpp->paths, i);
 
                /*
-                * Make sure mpp->hwe doesn't point to freed memory
-                * We call extract_hwe_from_path() below to restore mpp->hwe
-                */
-               if (mpp->hwe == pp->hwe)
-                       mpp->hwe = NULL;
-
-               /*
                 * remove the map IF removing the last path
                 */
                if (VECTOR_SIZE(mpp->paths) == 0) {
@@ -1191,9 +1252,6 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map)
                         */
                }
 
-               if (mpp->hwe == NULL)
-                       extract_hwe_from_path(mpp);
-
                if (setup_map(mpp, params, PARAMS_SIZE, vecs)) {
                        condlog(0, "%s: failed to setup map for"
                                " removal of path %s", mpp->alias, pp->dev);
@@ -1298,6 +1356,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
                        condlog(0, "%s: path wwid changed from '%s' to '%s'",
                                uev->kernel, wwid, pp->wwid);
                        ev_remove_path(pp, vecs, 1);
+                       rescan_path(uev->udev);
                        needs_reinit = 1;
                        goto out;
                } else {
@@ -1403,8 +1462,7 @@ map_discovery (struct vectors * vecs)
                return 1;
 
        vector_foreach_slot (vecs->mpvec, mpp, i)
-               if (update_multipath_table(mpp, vecs->pathvec, 0) != DMP_OK ||
-                   update_multipath_status(mpp) != DMP_OK) {
+               if (update_multipath_table(mpp, vecs->pathvec, 0) != DMP_OK) {
                        remove_map(mpp, vecs->pathvec, vecs->mpvec, PURGE_VEC);
                        i--;
                }
@@ -1501,31 +1559,6 @@ uev_trigger (struct uevent * uev, void * trigger_data)
                        uev_pathfail_check(uev, vecs);
                } else if (!strncmp(uev->action, "remove", 6)) {
                        r = uev_remove_map(uev, vecs);
-               } else if (!strncmp(uev->action, "add", 3)) {
-                       const char *ev_name;
-                       char *dm_name;
-                       int major = -1, minor = -1;
-
-                       /*
-                        * If DM_NAME is not set for a valid map, trigger a
-                        * change event. This can happen during coldplug
-                        * if udev was killed between handling the 'add' and
-                        * 'change' events before.
-                        */
-                       ev_name = uevent_get_dm_name(uev);
-                       if (!ev_name) {
-                               major = uevent_get_major(uev);
-                               minor = uevent_get_minor(uev);
-                               dm_name = dm_mapname(major, minor);
-                               if (dm_name && *dm_name) {
-                                       condlog(2, "%s: received incomplete 'add' uevent, triggering change",
-                                               dm_name);
-                                       udev_device_set_sysattr_value(uev->udev,
-                                                                     "uevent",
-                                                                     "change");
-                                       free(dm_name);
-                               }
-                       }
                }
                goto out;
        }
@@ -1944,8 +1977,6 @@ int reload_and_sync_map(struct multipath *mpp,
 {
        if (reload_map(vecs, mpp, refresh, 1))
                return 1;
-
-       dm_lib_release();
        if (setup_multipath(vecs, mpp) != 0)
                return 2;
        sync_map_state(mpp);
@@ -2074,7 +2105,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
        int chkr_new_path_up = 0;
        int disable_reinstate = 0;
        int oldchkrstate = pp->chkrstate;
-       int retrigger_tries, verbosity;
+       int retrigger_tries;
        unsigned int checkint, max_checkint;
        struct config *conf;
        int marginal_pathgroups, marginal_changed = 0;
@@ -2094,7 +2125,6 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
        retrigger_tries = conf->retrigger_tries;
        checkint = conf->checkint;
        max_checkint = conf->max_checkint;
-       verbosity = conf->verbosity;
        marginal_pathgroups = conf->marginal_pathgroups;
        put_multipath_config(conf);
 
@@ -2156,16 +2186,21 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
        if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) {
                condlog(2, "%s: unusable path (%s) - checker failed",
                        pp->dev, checker_state_name(newstate));
-               LOG_MSG(2, verbosity, pp);
+               LOG_MSG(2, pp);
                conf = get_multipath_config();
                pthread_cleanup_push(put_multipath_config, conf);
                pathinfo(pp, conf, 0);
                pthread_cleanup_pop(1);
                return 1;
-       } else if ((newstate != PATH_UP && newstate != PATH_GHOST) &&
-                       (pp->state == PATH_DELAYED)) {
+       } else if ((newstate != PATH_UP && newstate != PATH_GHOST &&
+                   newstate != PATH_PENDING) && (pp->state == PATH_DELAYED)) {
                /* If path state become failed again cancel path delay state */
                pp->state = newstate;
+               /*
+                * path state bad again should change the check interval time
+                * to the shortest delay
+                */
+               pp->checkint = checkint;
                return 1;
        }
        if (!pp->mpp) {
@@ -2183,13 +2218,13 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
                                ev_add_path(pp, vecs, 1);
                                pp->tick = 1;
                        } else {
+                               if (ret == PATHINFO_SKIPPED)
+                                       return -1;
                                /*
                                 * We failed multiple times to initialize this
                                 * path properly. Don't re-check too often.
                                 */
                                pp->checkint = max_checkint;
-                               if (ret == PATHINFO_SKIPPED)
-                                       return -1;
                        }
                }
                return 0;
@@ -2210,7 +2245,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
                if (ret == DMP_NOT_FOUND) {
                        /* multipath device missing. Likely removed */
                        condlog(1, "%s: multipath device '%s' not found",
-                               pp->dev, pp->mpp->alias);
+                               pp->dev, pp->mpp ? pp->mpp->alias : "");
                        return 0;
                } else
                        condlog(1, "%s: Couldn't synchronize with kernel state",
@@ -2222,11 +2257,22 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
                return 0;
        set_no_path_retry(pp->mpp);
 
+       if (pp->recheck_wwid == RECHECK_WWID_ON &&
+           (newstate == PATH_UP || newstate == PATH_GHOST) &&
+           ((pp->state != PATH_UP && pp->state != PATH_GHOST) ||
+            pp->dmstate == PSTATE_FAILED) &&
+           check_path_wwid_change(pp)) {
+               condlog(0, "%s: path wwid change detected. Removing", pp->dev);
+               handle_path_wwid_change(pp, vecs);
+               return 0;
+       }
+
        if ((newstate == PATH_UP || newstate == PATH_GHOST) &&
            (san_path_check_enabled(pp->mpp) ||
             marginal_path_check_enabled(pp->mpp))) {
-               int was_marginal = pp->marginal;
                if (should_skip_path(pp)) {
+                       if (!pp->marginal && pp->state != PATH_DELAYED)
+                               condlog(2, "%s: path is now marginal", pp->dev);
                        if (!marginal_pathgroups) {
                                if (marginal_path_check_enabled(pp->mpp))
                                        /* to reschedule as soon as possible,
@@ -2236,13 +2282,18 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
                                pp->state = PATH_DELAYED;
                                return 1;
                        }
-                       if (!was_marginal) {
+                       if (!pp->marginal) {
                                pp->marginal = 1;
                                marginal_changed = 1;
                        }
-               } else if (marginal_pathgroups && was_marginal) {
-                       pp->marginal = 0;
-                       marginal_changed = 1;
+               } else {
+                       if (pp->marginal || pp->state == PATH_DELAYED)
+                               condlog(2, "%s: path is no longer marginal",
+                                       pp->dev);
+                       if (marginal_pathgroups && pp->marginal) {
+                               pp->marginal = 0;
+                               marginal_changed = 1;
+                       }
                }
        }
 
@@ -2261,7 +2312,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
                int oldstate = pp->state;
                pp->state = newstate;
 
-               LOG_MSG(1, verbosity, pp);
+               LOG_MSG(1, pp);
 
                /*
                 * upon state change, reset the checkint
@@ -2325,7 +2376,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
                        /* Clear IO errors */
                        reinstate_path(pp);
                else {
-                       LOG_MSG(4, verbosity, pp);
+                       LOG_MSG(4, pp);
                        if (pp->checkint != max_checkint) {
                                /*
                                 * double the next check delay.
@@ -2353,9 +2404,9 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
                        log_checker_err = conf->log_checker_err;
                        put_multipath_config(conf);
                        if (log_checker_err == LOG_CHKR_ERR_ONCE)
-                               LOG_MSG(3, verbosity, pp);
+                               LOG_MSG(3, pp);
                        else
-                               LOG_MSG(2, verbosity, pp);
+                               LOG_MSG(2, pp);
                }
        }
 
@@ -2368,11 +2419,8 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
         */
        condlog(4, "path prio refresh");
 
-       if (marginal_changed) {
-               condlog(2, "%s: path is %s marginal", pp->dev,
-                       (pp->marginal)? "now" : "no longer");
+       if (marginal_changed)
                reload_and_sync_map(pp->mpp, vecs, 1);
-       }
        else if (update_prio(pp, new_path_up) &&
            (pp->mpp->pgpolicyfn == (pgpolicyfn *)group_by_prio) &&
             pp->mpp->pgfailback == -FAILBACK_IMMEDIATE) {
@@ -2430,8 +2478,8 @@ checkerloop (void *ap)
                get_monotonic_time(&start_time);
                if (start_time.tv_sec && last_time.tv_sec) {
                        timespecsub(&start_time, &last_time, &diff_time);
-                       condlog(4, "tick (%lu.%06lu secs)",
-                               diff_time.tv_sec, diff_time.tv_nsec / 1000);
+                       condlog(4, "tick (%ld.%06lu secs)",
+                               (long)diff_time.tv_sec, diff_time.tv_nsec / 1000);
                        last_time = start_time;
                        ticks = diff_time.tv_sec;
                } else {
@@ -2456,6 +2504,8 @@ checkerloop (void *ap)
                vector_foreach_slot (vecs->pathvec, pp, i) {
                        rc = check_path(vecs, pp, ticks);
                        if (rc < 0) {
+                               condlog(1, "%s: check_path() failed, removing",
+                                       pp->dev);
                                vector_del_slot(vecs->pathvec, i);
                                free_path(pp);
                                i--;
@@ -2492,18 +2542,18 @@ checkerloop (void *ap)
                        if (num_paths) {
                                unsigned int max_checkint;
 
-                               condlog(4, "checked %d path%s in %lu.%06lu secs",
+                               condlog(4, "checked %d path%s in %ld.%06lu secs",
                                        num_paths, num_paths > 1 ? "s" : "",
-                                       diff_time.tv_sec,
+                                       (long)diff_time.tv_sec,
                                        diff_time.tv_nsec / 1000);
                                conf = get_multipath_config();
                                max_checkint = conf->max_checkint;
                                put_multipath_config(conf);
                                if (diff_time.tv_sec > (time_t)max_checkint)
                                        condlog(1, "path checkers took longer "
-                                               "than %lu seconds, consider "
+                                               "than %ld seconds, consider "
                                                "increasing max_polling_interval",
-                                               diff_time.tv_sec);
+                                               (long)diff_time.tv_sec);
                        }
                }
 
@@ -2529,8 +2579,8 @@ checkerloop (void *ap)
                        } else
                                diff_time.tv_sec = 1;
 
-                       condlog(3, "waiting for %lu.%06lu secs",
-                               diff_time.tv_sec,
+                       condlog(3, "waiting for %ld.%06lu secs",
+                               (long)diff_time.tv_sec,
                                diff_time.tv_nsec / 1000);
                        if (nanosleep(&diff_time, NULL) != 0) {
                                condlog(3, "nanosleep failed with error %d",
@@ -2580,6 +2630,9 @@ configure (struct vectors * vecs)
                goto fail;
        }
 
+       if (should_exit())
+               goto fail;
+
        conf = get_multipath_config();
        pthread_cleanup_push(put_multipath_config, conf);
        vector_foreach_slot (vecs->pathvec, pp, i){
@@ -2596,6 +2649,9 @@ configure (struct vectors * vecs)
                goto fail;
        }
 
+       if (should_exit())
+               goto fail;
+
        /*
         * create new set of maps & push changed ones into dm
         * In the first call, use FORCE_RELOAD_WEAK to avoid making
@@ -2610,6 +2666,9 @@ configure (struct vectors * vecs)
                goto fail;
        }
 
+       if (should_exit())
+               goto fail;
+
        /*
         * may need to remove some maps which are no longer relevant
         * e.g., due to blacklist changes in conf file
@@ -2619,7 +2678,8 @@ configure (struct vectors * vecs)
                goto fail;
        }
 
-       dm_lib_release();
+       if (should_exit())
+               goto fail;
 
        sync_maps_state(mpvec);
        vector_foreach_slot(mpvec, mpp, i){
@@ -2629,14 +2689,10 @@ configure (struct vectors * vecs)
        }
 
        /*
-        * purge dm of old maps
+        * purge dm of old maps and save new set of maps formed by
+        * considering current path state
         */
        remove_maps(vecs);
-
-       /*
-        * save new set of maps formed by considering current path state
-        */
-       vector_free(vecs->mpvec);
        vecs->mpvec = mpvec;
 
        /*
@@ -2690,6 +2746,10 @@ reconfigure (struct vectors * vecs)
        if (!conf)
                return 1;
 
+       if (verbosity)
+               libmp_verbosity = verbosity;
+       setlogmask(LOG_UPTO(libmp_verbosity + 3));
+
        /*
         * free old map and path vectors ... they use old conf state
         */
@@ -2701,12 +2761,6 @@ reconfigure (struct vectors * vecs)
        delete_all_foreign();
 
        reset_checker_classes();
-       /* Re-read any timezone changes */
-       tzset();
-
-       dm_tgt_version(conf->version, TGT_MPATH);
-       if (verbosity)
-               conf->verbosity = verbosity;
        if (bindings_read_only)
                conf->bindings_read_only = bindings_read_only;
        check_alias_settings(conf);
@@ -2774,7 +2828,7 @@ handle_signals(bool nonfatal)
        }
        if (log_reset_sig) {
                condlog(2, "reset log (signal)");
-               if (logsink == 1)
+               if (logsink == LOGSINK_SYSLOG)
                        log_thread_reset();
        }
        reconfig_sig = 0;
@@ -2888,34 +2942,190 @@ set_oom_adj (void)
        condlog(0, "couldn't adjust oom score");
 }
 
+static void cleanup_pidfile(void)
+{
+       if (pid_fd >= 0)
+               close(pid_fd);
+       condlog(3, "unlink pidfile");
+       unlink(DEFAULT_PIDFILE);
+}
+
+static void cleanup_conf(void) {
+       struct config *conf;
+
+       conf = rcu_dereference(multipath_conf);
+       if (!conf)
+               return;
+       rcu_assign_pointer(multipath_conf, NULL);
+       call_rcu(&conf->rcu, rcu_free_config);
+}
+
+static void cleanup_maps(struct vectors *vecs)
+{
+       int queue_without_daemon, i;
+       struct multipath *mpp;
+       struct config *conf;
+
+       conf = get_multipath_config();
+       queue_without_daemon = conf->queue_without_daemon;
+       put_multipath_config(conf);
+       if (queue_without_daemon == QUE_NO_DAEMON_OFF)
+               vector_foreach_slot(vecs->mpvec, mpp, i)
+                       dm_queue_if_no_path(mpp->alias, 0);
+       remove_maps_and_stop_waiters(vecs);
+       vecs->mpvec = NULL;
+}
+
+static void cleanup_paths(struct vectors *vecs)
+{
+       free_pathvec(vecs->pathvec, FREE_PATHS);
+       vecs->pathvec = NULL;
+}
+
+static void cleanup_vecs(void)
+{
+       if (!gvecs)
+               return;
+       /*
+        * We can't take the vecs lock here, because exit() may
+        * have been called from the child() thread, holding the lock already.
+        * Anyway, by the time we get here, all threads that might access
+        * vecs should have been joined already (in cleanup_threads).
+        */
+       cleanup_maps(gvecs);
+       cleanup_paths(gvecs);
+       pthread_mutex_destroy(&gvecs->lock.mutex);
+       FREE(gvecs);
+}
+
+static void cleanup_threads(void)
+{
+       stop_io_err_stat_thread();
+
+       if (check_thr_started)
+               pthread_cancel(check_thr);
+       if (uevent_thr_started)
+               pthread_cancel(uevent_thr);
+       if (uxlsnr_thr_started)
+               pthread_cancel(uxlsnr_thr);
+       if (uevq_thr_started)
+               pthread_cancel(uevq_thr);
+       if (dmevent_thr_started)
+               pthread_cancel(dmevent_thr);
+
+       if (check_thr_started)
+               pthread_join(check_thr, NULL);
+       if (uevent_thr_started)
+               pthread_join(uevent_thr, NULL);
+       if (uxlsnr_thr_started)
+               pthread_join(uxlsnr_thr, NULL);
+       if (uevq_thr_started)
+               pthread_join(uevq_thr, NULL);
+       if (dmevent_thr_started)
+               pthread_join(dmevent_thr, NULL);
+
+       /*
+        * As all threads are joined now, and we're in DAEMON_SHUTDOWN
+        * state, no new waiter threads will be created any more.
+        */
+       pthread_attr_destroy(&waiter_attr);
+}
+
+/*
+ * Use a non-default call_rcu_data for child().
+ *
+ * We do this to avoid a memory leak from liburcu.
+ * liburcu never frees the default rcu handler (see comments on
+ * call_rcu_data_free() in urcu-call-rcu-impl.h), its thread
+ * can't be joined with pthread_join(), leaving a memory leak.
+ *
+ * Therefore we create our own, which can be destroyed and joined.
+ */
+static struct call_rcu_data *setup_rcu(void)
+{
+       struct call_rcu_data *crdp;
+
+       rcu_init();
+       rcu_register_thread();
+       crdp = create_call_rcu_data(0UL, -1);
+       if (crdp != NULL)
+               set_thread_call_rcu_data(crdp);
+       return crdp;
+}
+
+static struct call_rcu_data *mp_rcu_data;
+
+static void cleanup_rcu(void)
+{
+       pthread_t rcu_thread;
+
+       /* Wait for any pending RCU calls */
+       rcu_barrier();
+       if (mp_rcu_data != NULL) {
+               rcu_thread = get_call_rcu_thread(mp_rcu_data);
+               /* detach this thread from the RCU thread */
+               set_thread_call_rcu_data(NULL);
+               synchronize_rcu();
+               /* tell RCU thread to exit */
+               call_rcu_data_free(mp_rcu_data);
+               pthread_join(rcu_thread, NULL);
+       }
+       rcu_unregister_thread();
+}
+
+static void cleanup_child(void)
+{
+       cleanup_threads();
+       cleanup_vecs();
+       if (poll_dmevents)
+               cleanup_dmevent_waiter();
+
+       cleanup_pidfile();
+       if (logsink == LOGSINK_SYSLOG)
+               log_thread_stop();
+
+       cleanup_conf();
+
+#ifdef _DEBUG_
+       dbg_free_final(NULL);
+#endif
+}
+
+static int sd_notify_exit(int err)
+{
+#ifdef USE_SYSTEMD
+       char msg[24];
+
+       snprintf(msg, sizeof(msg), "ERRNO=%d", err);
+       sd_notify(0, msg);
+#endif
+       return err;
+}
+
 static int
 child (__attribute__((unused)) void *param)
 {
-       pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr;
        pthread_attr_t log_attr, misc_attr, uevent_attr;
        struct vectors * vecs;
-       struct multipath * mpp;
-       int i;
-#ifdef USE_SYSTEMD
-       int startup_done = 0;
-#endif
        int rc;
-       int pid_fd = -1;
        struct config *conf;
        char *envp;
-       int queue_without_daemon;
        enum daemon_status state;
+       int exit_code = 1;
 
+       init_unwinder();
        mlockall(MCL_CURRENT | MCL_FUTURE);
        signal_init();
-       rcu_init();
+       mp_rcu_data = setup_rcu();
+
+       if (atexit(cleanup_rcu) || atexit(cleanup_child))
+               fprintf(stderr, "failed to register cleanup handlers\n");
 
        setup_thread_attr(&misc_attr, 64 * 1024, 0);
        setup_thread_attr(&uevent_attr, DEFAULT_UEVENT_STACKSIZE * 1024, 0);
        setup_thread_attr(&waiter_attr, 32 * 1024, 1);
-       setup_thread_attr(&io_err_stat_attr, 32 * 1024, 0);
 
-       if (logsink == 1) {
+       if (logsink == LOGSINK_SYSLOG) {
                setup_thread_attr(&log_attr, 64 * 1024, 0);
                log_thread_start(&log_attr);
                pthread_attr_destroy(&log_attr);
@@ -2923,8 +3133,6 @@ child (__attribute__((unused)) void *param)
        pid_fd = pidfile_create(DEFAULT_PIDFILE, daemon_pid);
        if (pid_fd < 0) {
                condlog(1, "failed to create pidfile");
-               if (logsink == 1)
-                       log_thread_stop();
                exit(1);
        }
 
@@ -2933,12 +3141,18 @@ child (__attribute__((unused)) void *param)
        condlog(2, "--------start up--------");
        condlog(2, "read " DEFAULT_CONFIGFILE);
 
+       if (verbosity)
+               libmp_verbosity = verbosity;
        conf = load_config(DEFAULT_CONFIGFILE);
-       if (!conf)
+       if (verbosity)
+               libmp_verbosity = verbosity;
+       setlogmask(LOG_UPTO(libmp_verbosity + 3));
+
+       if (!conf) {
+               condlog(0, "failed to load configuration");
                goto failed;
+       }
 
-       if (verbosity)
-               conf->verbosity = verbosity;
        if (bindings_read_only)
                conf->bindings_read_only = bindings_read_only;
        uxsock_timeout = conf->uxsock_timeout;
@@ -2957,7 +3171,6 @@ child (__attribute__((unused)) void *param)
 
        if (poll_dmevents)
                poll_dmevents = dmevent_poll_supported();
-       setlogmask(LOG_UPTO(conf->verbosity + 3));
 
        envp = getenv("LimitNOFILE");
 
@@ -2995,9 +3208,12 @@ child (__attribute__((unused)) void *param)
                condlog(0, "failed to create cli listener: %d", rc);
                goto failed;
        }
-       else if (state != DAEMON_CONFIGURE) {
-               condlog(0, "cli listener failed to start");
-               goto failed;
+       else {
+               uxlsnr_thr_started = true;
+               if (state != DAEMON_CONFIGURE) {
+                       condlog(0, "cli listener failed to start");
+                       goto failed;
+               }
        }
 
        if (poll_dmevents) {
@@ -3010,7 +3226,8 @@ child (__attribute__((unused)) void *param)
                        condlog(0, "failed to create dmevent waiter thread: %d",
                                rc);
                        goto failed;
-               }
+               } else
+                       dmevent_thr_started = true;
        }
 
        /*
@@ -3019,7 +3236,8 @@ child (__attribute__((unused)) void *param)
        if ((rc = pthread_create(&uevent_thr, &uevent_attr, ueventloop, udev))) {
                condlog(0, "failed to create uevent thread: %d", rc);
                goto failed;
-       }
+       } else
+               uevent_thr_started = true;
        pthread_attr_destroy(&uevent_attr);
 
        /*
@@ -3028,11 +3246,13 @@ child (__attribute__((unused)) void *param)
        if ((rc = pthread_create(&check_thr, &misc_attr, checkerloop, vecs))) {
                condlog(0,"failed to create checker loop thread: %d", rc);
                goto failed;
-       }
+       } else
+               check_thr_started = true;
        if ((rc = pthread_create(&uevq_thr, &misc_attr, uevqloop, vecs))) {
                condlog(0, "failed to create uevent dispatcher: %d", rc);
                goto failed;
-       }
+       } else
+               uevq_thr_started = true;
        pthread_attr_destroy(&misc_attr);
 
        while (1) {
@@ -3058,96 +3278,14 @@ child (__attribute__((unused)) void *param)
                        }
                        lock_cleanup_pop(vecs->lock);
                        post_config_state(DAEMON_IDLE);
-#ifdef USE_SYSTEMD
-                       if (!startup_done) {
-                               sd_notify(0, "READY=1");
-                               startup_done = 1;
-                       }
-#endif
                }
        }
 
-       lock(&vecs->lock);
-       conf = get_multipath_config();
-       queue_without_daemon = conf->queue_without_daemon;
-       put_multipath_config(conf);
-       if (queue_without_daemon == QUE_NO_DAEMON_OFF)
-               vector_foreach_slot(vecs->mpvec, mpp, i)
-                       dm_queue_if_no_path(mpp->alias, 0);
-       remove_maps_and_stop_waiters(vecs);
-       unlock(&vecs->lock);
-
-       pthread_cancel(check_thr);
-       pthread_cancel(uevent_thr);
-       pthread_cancel(uxlsnr_thr);
-       pthread_cancel(uevq_thr);
-       if (poll_dmevents)
-               pthread_cancel(dmevent_thr);
-
-       pthread_join(check_thr, NULL);
-       pthread_join(uevent_thr, NULL);
-       pthread_join(uxlsnr_thr, NULL);
-       pthread_join(uevq_thr, NULL);
-       if (poll_dmevents)
-               pthread_join(dmevent_thr, NULL);
-
-       stop_io_err_stat_thread();
-
-       lock(&vecs->lock);
-       free_pathvec(vecs->pathvec, FREE_PATHS);
-       vecs->pathvec = NULL;
-       unlock(&vecs->lock);
-
-       pthread_mutex_destroy(&vecs->lock.mutex);
-       FREE(vecs);
-       vecs = NULL;
-
-       cleanup_foreign();
-       cleanup_checkers();
-       cleanup_prio();
-       if (poll_dmevents)
-               cleanup_dmevent_waiter();
-
-       dm_lib_release();
-       dm_lib_exit();
-
-       /* We're done here */
-       condlog(3, "unlink pidfile");
-       unlink(DEFAULT_PIDFILE);
-
-       condlog(2, "--------shut down-------");
-
-       if (logsink == 1)
-               log_thread_stop();
-
-       /*
-        * Freeing config must be done after condlog() and dm_lib_exit(),
-        * because logging functions like dlog() and dm_write_log()
-        * reference the config.
-        */
-       conf = rcu_dereference(multipath_conf);
-       rcu_assign_pointer(multipath_conf, NULL);
-       call_rcu(&conf->rcu, rcu_free_config);
-       udev_unref(udev);
-       udev = NULL;
-       pthread_attr_destroy(&waiter_attr);
-       pthread_attr_destroy(&io_err_stat_attr);
-#ifdef _DEBUG_
-       dbg_free_final(NULL);
-#endif
-
-#ifdef USE_SYSTEMD
-       sd_notify(0, "ERRNO=0");
-#endif
-       exit(0);
-
+       exit_code = 0;
 failed:
-#ifdef USE_SYSTEMD
-       sd_notify(0, "ERRNO=1");
-#endif
-       if (pid_fd >= 0)
-               close(pid_fd);
-       exit(1);
+       condlog(2, "--------shut down-------");
+       /* All cleanup is done in the cleanup_child() exit handler */
+       return sd_notify_exit(exit_code);
 }
 
 static int
@@ -3212,13 +3350,15 @@ main (int argc, char *argv[])
        int err;
        int foreground = 0;
        struct config *conf;
+       char *opt_k_arg = NULL;
+       bool opt_k = false;
 
        ANNOTATE_BENIGN_RACE_SIZED(&multipath_conf, sizeof(multipath_conf),
                                   "Manipulated through RCU");
        ANNOTATE_BENIGN_RACE_SIZED(&uxsock_timeout, sizeof(uxsock_timeout),
                "Suppress complaints about this scalar variable");
 
-       logsink = 1;
+       logsink = LOGSINK_SYSLOG;
 
        if (getuid() != 0) {
                fprintf(stderr, "need to be root\n");
@@ -3233,38 +3373,35 @@ main (int argc, char *argv[])
 
        pthread_cond_init_mono(&config_cond);
 
-       udev = udev_new();
+       if (atexit(dm_lib_exit))
+               condlog(3, "failed to register exit handler for libdm");
+
+       libmultipath_init();
+       if (atexit(libmultipath_exit))
+               condlog(3, "failed to register exit handler for libmultipath");
        libmp_udev_set_sync_support(0);
 
        while ((arg = getopt(argc, argv, ":dsv:k::Bniw")) != EOF ) {
                switch(arg) {
                case 'd':
                        foreground = 1;
-                       if (logsink > 0)
-                               logsink = 0;
-                       //debug=1; /* ### comment me out ### */
+                       if (logsink == LOGSINK_SYSLOG)
+                               logsink = LOGSINK_STDERR_WITH_TIME;
                        break;
                case 'v':
                        if (sizeof(optarg) > sizeof(char *) ||
                            !isdigit(optarg[0]))
                                exit(1);
 
-                       verbosity = atoi(optarg);
+                       libmp_verbosity = verbosity = atoi(optarg);
                        break;
                case 's':
-                       logsink = -1;
+                       logsink = LOGSINK_STDERR_WITHOUT_TIME;
                        break;
                case 'k':
-                       logsink = 0;
-                       conf = load_config(DEFAULT_CONFIGFILE);
-                       if (!conf)
-                               exit(1);
-                       if (verbosity)
-                               conf->verbosity = verbosity;
-                       uxsock_timeout = conf->uxsock_timeout;
-                       err = uxclnt(optarg, uxsock_timeout + 100);
-                       free_config(conf);
-                       return err;
+                       opt_k = true;
+                       opt_k_arg = optarg;
+                       break;
                case 'B':
                        bindings_read_only = 1;
                        break;
@@ -3280,27 +3417,35 @@ main (int argc, char *argv[])
                        exit(1);
                }
        }
-       if (optind < argc) {
+       if (opt_k || optind < argc) {
                char cmd[CMDSIZE];
                char * s = cmd;
                char * c = s;
 
-               logsink = 0;
+               logsink = LOGSINK_STDERR_WITH_TIME;
+               if (verbosity)
+                       libmp_verbosity = verbosity;
                conf = load_config(DEFAULT_CONFIGFILE);
                if (!conf)
                        exit(1);
                if (verbosity)
-                       conf->verbosity = verbosity;
+                       libmp_verbosity = verbosity;
                uxsock_timeout = conf->uxsock_timeout;
                memset(cmd, 0x0, CMDSIZE);
-               while (optind < argc) {
-                       if (strchr(argv[optind], ' '))
-                               c += snprintf(c, s + CMDSIZE - c, "\"%s\" ", argv[optind]);
-                       else
-                               c += snprintf(c, s + CMDSIZE - c, "%s ", argv[optind]);
-                       optind++;
+               if (opt_k)
+                       s = opt_k_arg;
+               else {
+                       while (optind < argc) {
+                               if (strchr(argv[optind], ' '))
+                                       c += snprintf(c, s + CMDSIZE - c,
+                                                     "\"%s\" ", argv[optind]);
+                               else
+                                       c += snprintf(c, s + CMDSIZE - c,
+                                                     "%s ", argv[optind]);
+                               optind++;
+                       }
+                       c += snprintf(c, s + CMDSIZE - c, "\n");
                }
-               c += snprintf(c, s + CMDSIZE - c, "\n");
                err = uxclnt(s, uxsock_timeout + 100);
                free_config(conf);
                return err;
index 5abbe97..ddd953f 100644 (file)
@@ -50,4 +50,6 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset);
 int reload_and_sync_map(struct multipath *mpp, struct vectors *vecs,
                        int refresh);
 
+void handle_path_wwid_change(struct path *pp, struct vectors *vecs);
+bool check_path_wwid_change(struct path *pp);
 #endif /* MAIN_H */
index ba24983..7d547fa 100644 (file)
@@ -2,7 +2,7 @@
 Description=Device-Mapper Multipath Device Controller
 Wants=systemd-udev-trigger.service systemd-udev-settle.service
 Before=iscsi.service iscsid.service lvm2-activation-early.service
-Before=local-fs-pre.target blk-availability.service
+Before=local-fs-pre.target blk-availability.service shutdown.target
 After=multipathd.socket systemd-udev-trigger.service systemd-udev-settle.service
 DefaultDependencies=no
 Conflicts=shutdown.target
index 1c5ce9d..dbee0d6 100644 (file)
 #include "config.h"
 #include "mpath_cmd.h"
 #include "time-util.h"
+#include "util.h"
 
 #include "main.h"
 #include "cli.h"
 #include "uxlsnr.h"
 
-static struct timespec sleep_time = {5, 0};
-
 struct client {
        struct list_head node;
        int fd;
 };
 
-#define MIN_POLLS 1023
+/* The number of fds we poll on, other than individual client connections */
+#define POLLFDS_BASE 2
+#define POLLFD_CHUNK (4096 / sizeof(struct pollfd))
+/* Minimum mumber of pollfds to reserve for clients */
+#define MIN_POLLS (POLLFD_CHUNK - POLLFDS_BASE)
+/*
+ * Max number of client connections allowed
+ * During coldplug, there may be a large number of "multipath -u"
+ * processes connecting.
+ */
+#define MAX_CLIENTS (16384 - POLLFDS_BASE)
+
+/* Compile-time error if POLLFD_CHUNK is too small */
+static __attribute__((unused)) char ___a[-(MIN_POLLS <= 0)];
 
 static LIST_HEAD(clients);
 static pthread_mutex_t client_lock = PTHREAD_MUTEX_INITIALIZER;
@@ -116,7 +128,7 @@ static void _dead_client(struct client *c)
 
 static void dead_client(struct client *c)
 {
-       pthread_cleanup_push(cleanup_lock, &client_lock);
+       pthread_cleanup_push(cleanup_mutex, &client_lock);
        pthread_mutex_lock(&client_lock);
        _dead_client(c);
        pthread_cleanup_pop(1);
@@ -142,8 +154,8 @@ static void check_timeout(struct timespec start_time, char *inbuf,
                        diff_time.tv_nsec / (1000 * 1000);
                if (msecs > timeout)
                        condlog(2, "cli cmd '%s' timeout reached "
-                               "after %lu.%06lu secs", inbuf,
-                               diff_time.tv_sec, diff_time.tv_nsec / 1000);
+                               "after %ld.%06lu secs", inbuf,
+                               (long)diff_time.tv_sec, diff_time.tv_nsec / 1000);
        }
 }
 
@@ -281,13 +293,13 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
        char *inbuf;
        char *reply;
        sigset_t mask;
-       int old_clients = MIN_POLLS;
+       int max_pfds = MIN_POLLS + POLLFDS_BASE;
        /* conf->sequence_nr will be 1 when uxsock_listen is first called */
        unsigned int sequence_nr = 0;
        struct watch_descriptors wds = { .conf_wd = -1, .dir_wd = -1 };
 
        condlog(3, "uxsock: startup listener");
-       polls = (struct pollfd *)MALLOC((MIN_POLLS + 2) * sizeof(struct pollfd));
+       polls = MALLOC(max_pfds * sizeof(*polls));
        if (!polls) {
                condlog(0, "uxsock: failed to allocate poll fds");
                exit_daemon();
@@ -302,37 +314,42 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
        sigdelset(&mask, SIGUSR1);
        while (1) {
                struct client *c, *tmp;
-               int i, poll_count, num_clients;
+               int i, n_pfds, poll_count, num_clients;
 
                /* setup for a poll */
                pthread_mutex_lock(&client_lock);
+               pthread_cleanup_push(cleanup_mutex, &client_lock);
                num_clients = 0;
                list_for_each_entry(c, &clients, node) {
                        num_clients++;
                }
-               if (num_clients != old_clients) {
+               if (num_clients + POLLFDS_BASE > max_pfds) {
                        struct pollfd *new;
-                       if (num_clients <= MIN_POLLS && old_clients > MIN_POLLS) {
-                               new = REALLOC(polls, (2 + MIN_POLLS) *
-                                               sizeof(struct pollfd));
-                       } else if (num_clients <= MIN_POLLS && old_clients <= MIN_POLLS) {
-                               new = polls;
+                       int n_new = max_pfds + POLLFD_CHUNK;
+
+                       new = REALLOC(polls, n_new * sizeof(*polls));
+                       if (new) {
+                               max_pfds = n_new;
+                               polls = new;
                        } else {
-                               new = REALLOC(polls, (2 + num_clients) *
-                                               sizeof(struct pollfd));
+                               condlog(1, "%s: realloc failure, %d clients not served",
+                                       __func__,
+                                       num_clients + POLLFDS_BASE - max_pfds);
+                               num_clients = max_pfds - POLLFDS_BASE;
                        }
-                       if (!new) {
-                               pthread_mutex_unlock(&client_lock);
-                               condlog(0, "%s: failed to realloc %d poll fds",
-                                       "uxsock", 2 + num_clients);
-                               sched_yield();
-                               continue;
-                       }
-                       old_clients = num_clients;
-                       polls = new;
                }
-               polls[0].fd = ux_sock;
-               polls[0].events = POLLIN;
+               if (num_clients < MAX_CLIENTS) {
+                       polls[0].fd = ux_sock;
+                       polls[0].events = POLLIN;
+               } else {
+                       /*
+                        * New clients can't connect, num_clients won't grow
+                        * to MAX_CLIENTS or higher
+                        */
+                       condlog(1, "%s: max client connections reached, pausing polling",
+                               __func__);
+                       polls[0].fd = -1;
+               }
 
                reset_watch(notify_fd, &wds, &sequence_nr);
                if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1))
@@ -342,16 +359,19 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
                polls[1].events = POLLIN;
 
                /* setup the clients */
-               i = 2;
+               i = POLLFDS_BASE;
                list_for_each_entry(c, &clients, node) {
                        polls[i].fd = c->fd;
                        polls[i].events = POLLIN;
                        i++;
+                       if (i >= max_pfds)
+                               break;
                }
-               pthread_mutex_unlock(&client_lock);
+               n_pfds = i;
+               pthread_cleanup_pop(1);
 
                /* most of our life is spent in this call */
-               poll_count = ppoll(polls, i, &sleep_time, &mask);
+               poll_count = ppoll(polls, n_pfds, NULL, &mask);
 
                handle_signals(false);
                if (poll_count == -1) {
@@ -384,7 +404,7 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock,
                }
 
                /* see if a client wants to speak to us */
-               for (i = 2; i < num_clients + 2; i++) {
+               for (i = POLLFDS_BASE; i < n_pfds; i++) {
                        if (polls[i].revents & POLLIN) {
                                struct timespec start_time;
 
index 3bc6980..bbe6c2a 100644 (file)
@@ -118,7 +118,7 @@ static int waiteventloop (struct event_thread *waiter)
        pthread_sigmask(SIG_UNBLOCK, &set, &oldset);
 
        pthread_testcancel();
-       r = dm_task_run(waiter->dmt);
+       r = libmp_dm_task_run(waiter->dmt);
        if (!r)
                dm_log_error(2, DM_DEVICE_WAITEVENT, waiter->dmt);
        pthread_testcancel();
index d26b3ce..e70c8ed 100644 (file)
@@ -10,16 +10,17 @@ W_MISSING_INITIALIZERS := $(call TEST_MISSING_INITIALIZERS)
 
 CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) \
        -Wno-unused-parameter $(W_MISSING_INITIALIZERS)
-LIBDEPS += -L$(multipathdir) -L$(mpathcmddir) -lmultipath -lmpathcmd -lcmocka
+LIBDEPS += -L. -L$(mpathcmddir) -lmultipath -lmpathcmd -lcmocka
 
 TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
-        alias directio valid devt
+        alias directio valid devt mpathvalid
 HELPERS := test-lib.o test-log.o
 
 .SILENT: $(TESTS:%=%.o)
 .PRECIOUS: $(TESTS:%=%-test)
 
 all:   $(TESTS:%=%.out)
+progs: $(TESTS:%=%-test) lib/libchecktur.so
 valgrind:      $(TESTS:%=%.vgr)
 
 # test-specific compiler flags
@@ -31,6 +32,7 @@ endif
 ifneq ($(DIO_TEST_DEV),)
 directio-test_FLAGS := -DDIO_TEST_DEV=\"$(DIO_TEST_DEV)\"
 endif
+mpathvalid-test_FLAGS := -I$(mpathvaliddir)
 
 # test-specific linker flags
 # XYZ-test_TESTDEPS: test libraries containing __wrap_xyz functions
@@ -40,10 +42,11 @@ endif
 #    linker input file).
 # XYZ-test_LIBDEPS: Additional libs to link for this test
 
+dmevents-test_OBJDEPS = ../libmultipath/devmapper.o
 dmevents-test_LIBDEPS = -lpthread -ldevmapper -lurcu
 hwtable-test_TESTDEPS := test-lib.o
 hwtable-test_OBJDEPS := ../libmultipath/discovery.o ../libmultipath/blacklist.o \
-       ../libmultipath/structs.o
+       ../libmultipath/structs.o ../libmultipath/propsel.o
 hwtable-test_LIBDEPS := -ludev -lpthread -ldl
 blacklist-test_TESTDEPS := test-log.o
 blacklist-test_OBJDEPS := ../libmultipath/blacklist.o
@@ -52,9 +55,11 @@ vpd-test_OBJDEPS :=  ../libmultipath/discovery.o
 vpd-test_LIBDEPS := -ludev -lpthread -ldl
 alias-test_TESTDEPS := test-log.o
 alias-test_LIBDEPS := -lpthread -ldl
-valid-test_OBJDEPS := ../libmultipath/valid.o
+valid-test_OBJDEPS := ../libmultipath/valid.o ../libmultipath/discovery.o
 valid-test_LIBDEPS := -ludev -lpthread -ldl
 devt-test_LIBDEPS := -ludev
+mpathvalid-test_LIBDEPS := -ludev -lpthread -ldl
+mpathvalid-test_OBJDEPS := ../libmpathvalid/mpath_valid.o
 ifneq ($(DIO_TEST_DEV),)
 directio-test_LIBDEPS := -laio
 endif
@@ -68,17 +73,17 @@ lib/libchecktur.so:
 
 %.out: %-test lib/libchecktur.so
        @echo == running $< ==
-       @LD_LIBRARY_PATH=$(multipathdir):$(mpathcmddir) ./$< >$@
+       @LD_LIBRARY_PATH=.:$(mpathcmddir) ./$< >$@
 
 %.vgr:  %-test lib/libchecktur.so
        @echo == running valgrind for $< ==
-       @LD_LIBRARY_PATH=$(multipathdir):$(mpathcmddir) \
+       @LD_LIBRARY_PATH=.:$(mpathcmddir) \
                valgrind --leak-check=full --error-exitcode=128 ./$< >$@ 2>&1
 
 OBJS = $(TESTS:%=%.o) $(HELPERS)
 
 test_clean:
-       $(RM) $(TESTS:%=%.out) $(TESTS:%=%.vgr)
+       $(RM) $(TESTS:%=%.out) $(TESTS:%=%.vgr) *.so*
 
 valgrind_clean:
        $(RM) $(TESTS:%=%.vgr)
@@ -98,12 +103,14 @@ dep_clean:
        @sed -n 's/^.*__wrap_\([a-zA-Z0-9_]*\).*$$/-Wl,--wrap=\1/p' $< | \
                sort -u | tr '\n' ' ' >$@
 
+libmultipath.so.0:
+       $(MAKE) -C $(multipathdir) test-lib
 
 # COLON will get expanded during second expansion below
 COLON:=:
 .SECONDEXPANSION:
 %-test:        %.o %.o.wrap $$($$@_OBJDEPS) $$($$@_TESTDEPS) $$($$@_TESTDEPS$$(COLON).o=.o.wrap) \
-               $(multipathdir)/libmultipath.so Makefile
+               libmultipath.so.0 Makefile
        $(CC) $(CFLAGS) -o $@ $(LDFLAGS) $< $($@_TESTDEPS) $($@_OBJDEPS) \
                $(LIBDEPS) $($@_LIBDEPS) \
                $(shell cat $<.wrap) $(foreach dep,$($@_TESTDEPS),$(shell cat $(dep).wrap))
index 6e7ad40..47c0f0b 100644 (file)
@@ -13,6 +13,11 @@ If valgrind detects a bad memory access or leak, the test will fail. The
 output of the test run, including valgrind output, is stored as
 `<testname>.vgr`.
 
+## Controlling verbosity for unit tests
+
+Some test programs use the environment variable `MPATHTEST_VERBOSITY` to
+control the log level during test execution.
+
 ## Notes on individual tests
 
 ### Tests that require root permissions
index 7fda679..7e7c187 100644 (file)
@@ -1,3 +1,4 @@
+#include <pthread.h>
 #include <stdint.h>
 #include <setjmp.h>
 #include <stdio.h>
@@ -61,6 +62,25 @@ int __wrap_ftruncate(int fd, off_t length)
        return __set_errno(mock_type(int));
 }
 
+int __wrap_dm_map_present(const char * str)
+{
+       check_expected(str);
+       return mock_type(int);
+}
+
+int __wrap_dm_get_uuid(const char *name, char *uuid, int uuid_len)
+{
+       int ret;
+
+       check_expected(name);
+       check_expected(uuid_len);
+       assert_non_null(uuid);
+       ret = mock_type(int);
+       if (ret == 0)
+               strcpy(uuid, mock_ptr_type(char *));
+       return ret;
+}
+
 static void fd_mpatha(void **state)
 {
        char buf[32];
@@ -349,6 +369,45 @@ static int test_scan_devname(void)
        return cmocka_run_group_tests(tests, NULL, NULL);
 }
 
+static void mock_unused_alias(const char *alias)
+{
+       expect_string(__wrap_dm_map_present, str, alias);
+       will_return(__wrap_dm_map_present, 0);
+}
+
+static void mock_self_alias(const char *alias, const char *wwid)
+{
+       expect_string(__wrap_dm_map_present, str, alias);
+       will_return(__wrap_dm_map_present, 1);
+       expect_string(__wrap_dm_get_uuid, name, alias);
+       expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);
+       will_return(__wrap_dm_get_uuid, 0);
+       will_return(__wrap_dm_get_uuid, wwid);
+}
+
+#define USED_STR(alias_str, wwid_str) wwid_str ": alias '" alias_str "' already taken, but not in bindings file. reselecting alias\n"
+
+static void mock_failed_alias(const char *alias, char *msg)
+{
+       expect_string(__wrap_dm_map_present, str, alias);
+       will_return(__wrap_dm_map_present, 1);
+       expect_string(__wrap_dm_get_uuid, name, alias);
+       expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);
+       will_return(__wrap_dm_get_uuid, 1);
+       expect_condlog(3, msg);
+}
+
+static void mock_used_alias(const char *alias, char *msg)
+{
+       expect_string(__wrap_dm_map_present, str, alias);
+       will_return(__wrap_dm_map_present, 1);
+       expect_string(__wrap_dm_get_uuid, name, alias);
+       expect_value(__wrap_dm_get_uuid, uuid_len, WWID_SIZE);
+       will_return(__wrap_dm_get_uuid, 0);
+       will_return(__wrap_dm_get_uuid, "WWID_USED");
+       expect_condlog(3, msg);
+}
+
 static void lb_empty(void **state)
 {
        int rc;
@@ -356,9 +415,68 @@ static void lb_empty(void **state)
 
        will_return(__wrap_fgets, NULL);
        expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
-       rc = lookup_binding(NULL, "WWID0", &alias, NULL);
+       rc = lookup_binding(NULL, "WWID0", &alias, NULL, 0);
+       assert_int_equal(rc, 1);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_empty_unused(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, NULL);
+       mock_unused_alias("MPATHa");
+       expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
        assert_int_equal(rc, 1);
        assert_ptr_equal(alias, NULL);
+       free(alias);
+}
+
+static void lb_empty_failed(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, NULL);
+       mock_failed_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+       mock_unused_alias("MPATHb");
+       expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
+       assert_int_equal(rc, 2);
+       assert_ptr_equal(alias, NULL);
+       free(alias);
+}
+
+static void lb_empty_1_used(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+       mock_unused_alias("MPATHb");
+       expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
+       assert_int_equal(rc, 2);
+       assert_ptr_equal(alias, NULL);
+       free(alias);
+}
+
+static void lb_empty_1_used_self(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID0"));
+       mock_self_alias("MPATHb", "WWID0");
+       expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
+       assert_int_equal(rc, 2);
+       assert_ptr_equal(alias, NULL);
+       free(alias);
 }
 
 static void lb_match_a(void **state)
@@ -369,7 +487,7 @@ static void lb_match_a(void **state)
        will_return(__wrap_fgets, "MPATHa WWID0\n");
        expect_condlog(3, "Found matching wwid [WWID0] in bindings file."
                       " Setting alias to MPATHa\n");
-       rc = lookup_binding(NULL, "WWID0", &alias, "MPATH");
+       rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 0);
        assert_int_equal(rc, 0);
        assert_ptr_not_equal(alias, NULL);
        assert_string_equal(alias, "MPATHa");
@@ -384,12 +502,57 @@ static void lb_nomatch_a(void **state)
        will_return(__wrap_fgets, "MPATHa WWID0\n");
        will_return(__wrap_fgets, NULL);
        expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n");
-       rc = lookup_binding(NULL, "WWID1", &alias, "MPATH");
+       rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 0);
        assert_int_equal(rc, 2);
        assert_ptr_equal(alias, NULL);
 }
 
-static void lb_match_c(void **state)
+static void lb_nomatch_a_bad_check(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(0, "no more available user_friendly_names\n");
+       rc = lookup_binding(NULL, "WWID1", &alias, NULL, 1);
+       assert_int_equal(rc, -1);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_a_unused(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       mock_unused_alias("MPATHb");
+       expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
+       assert_int_equal(rc, 2);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_a_3_used_failed_self(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID1"));
+       mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID1"));
+       mock_used_alias("MPATHd", USED_STR("MPATHd", "WWID1"));
+       mock_failed_alias("MPATHe", USED_STR("MPATHe", "WWID1"));
+       mock_self_alias("MPATHf", "WWID1");
+       expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
+       assert_int_equal(rc, 6);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void do_lb_match_c(void **state, int check_if_taken)
 {
        int rc;
        char *alias;
@@ -398,13 +561,23 @@ static void lb_match_c(void **state)
        will_return(__wrap_fgets, "MPATHc WWID1\n");
        expect_condlog(3, "Found matching wwid [WWID1] in bindings file."
                       " Setting alias to MPATHc\n");
-       rc = lookup_binding(NULL, "WWID1", &alias, "MPATH");
+       rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", check_if_taken);
        assert_int_equal(rc, 0);
        assert_ptr_not_equal(alias, NULL);
        assert_string_equal(alias, "MPATHc");
        free(alias);
 }
 
+static void lb_match_c(void **state)
+{
+       do_lb_match_c(state, 0);
+}
+
+static void lb_match_c_check(void **state)
+{
+       do_lb_match_c(state, 1);
+}
+
 static void lb_nomatch_a_c(void **state)
 {
        int rc;
@@ -414,11 +587,77 @@ static void lb_nomatch_a_c(void **state)
        will_return(__wrap_fgets, "MPATHc WWID1\n");
        will_return(__wrap_fgets, NULL);
        expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
-       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
+       assert_int_equal(rc, 2);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_a_d_unused(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHd WWID1\n");
+       will_return(__wrap_fgets, NULL);
+       mock_unused_alias("MPATHb");
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
        assert_int_equal(rc, 2);
        assert_ptr_equal(alias, NULL);
 }
 
+static void lb_nomatch_a_d_1_used(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHd WWID1\n");
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
+       mock_unused_alias("MPATHc");
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+       assert_int_equal(rc, 3);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_a_d_2_used(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHd WWID1\n");
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
+       mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
+       mock_unused_alias("MPATHe");
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+       assert_int_equal(rc, 5);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_a_d_3_used(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHd WWID1\n");
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
+       mock_used_alias("MPATHc", USED_STR("MPATHc", "WWID2"));
+       mock_used_alias("MPATHe", USED_STR("MPATHe", "WWID2"));
+       mock_unused_alias("MPATHf");
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+       assert_int_equal(rc, 6);
+       assert_ptr_equal(alias, NULL);
+}
+
 static void lb_nomatch_c_a(void **state)
 {
        int rc;
@@ -428,11 +667,44 @@ static void lb_nomatch_c_a(void **state)
        will_return(__wrap_fgets, "MPATHa WWID0\n");
        will_return(__wrap_fgets, NULL);
        expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
-       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
+       assert_int_equal(rc, 2);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_d_a_unused(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHc WWID1\n");
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHd WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       mock_unused_alias("MPATHb");
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
        assert_int_equal(rc, 2);
        assert_ptr_equal(alias, NULL);
 }
 
+static void lb_nomatch_d_a_1_used(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHc WWID1\n");
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHd WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATHb", USED_STR("MPATHb", "WWID2"));
+       mock_unused_alias("MPATHe");
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+       assert_int_equal(rc, 5);
+       assert_ptr_equal(alias, NULL);
+}
+
 static void lb_nomatch_a_b(void **state)
 {
        int rc;
@@ -443,7 +715,7 @@ static void lb_nomatch_a_b(void **state)
        will_return(__wrap_fgets, "MPATHb WWID1\n");
        will_return(__wrap_fgets, NULL);
        expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
-       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
        assert_int_equal(rc, 3);
        assert_ptr_equal(alias, NULL);
 }
@@ -459,7 +731,24 @@ static void lb_nomatch_a_b_bad(void **state)
        will_return(__wrap_fgets, NULL);
        expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
        expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
-       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
+       assert_int_equal(rc, 3);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_a_b_bad_self(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, "MPATHz WWID26\n");
+       will_return(__wrap_fgets, "MPATHb\n");
+       will_return(__wrap_fgets, NULL);
+       expect_condlog(3, "Ignoring malformed line 3 in bindings file\n");
+       mock_self_alias("MPATHc", "WWID2");
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
        assert_int_equal(rc, 3);
        assert_ptr_equal(alias, NULL);
 }
@@ -474,13 +763,32 @@ static void lb_nomatch_b_a(void **state)
        will_return(__wrap_fgets, "MPATHa WWID0\n");
        will_return(__wrap_fgets, NULL);
        expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
-       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
        assert_int_equal(rc, 27);
        assert_ptr_equal(alias, NULL);
 }
 
+static void lb_nomatch_b_a_3_used(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHb WWID1\n");
+       will_return(__wrap_fgets, "MPATHz WWID26\n");
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATHaa", USED_STR("MPATHaa", "WWID2"));
+       mock_used_alias("MPATHab", USED_STR("MPATHab", "WWID2"));
+       mock_used_alias("MPATHac", USED_STR("MPATHac", "WWID2"));
+       mock_unused_alias("MPATHad");
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+       assert_int_equal(rc, 30);
+       assert_ptr_equal(alias, NULL);
+}
+
 #ifdef MPATH_ID_INT_MAX
-static void lb_nomatch_int_max(void **state)
+static void do_lb_nomatch_int_max(void **state, int check_if_taken)
 {
        int rc;
        char *alias;
@@ -490,7 +798,32 @@ static void lb_nomatch_int_max(void **state)
        will_return(__wrap_fgets, "MPATHa WWID0\n");
        will_return(__wrap_fgets, NULL);
        expect_condlog(0, "no more available user_friendly_names\n");
-       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", check_if_taken);
+       assert_int_equal(rc, -1);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_int_max(void **state)
+{
+       do_lb_nomatch_int_max(state, 0);
+}
+
+static void lb_nomatch_int_max_check(void **state)
+{
+       do_lb_nomatch_int_max(state, 1);
+}
+
+static void lb_nomatch_int_max_used(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHb WWID1\n");
+       will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
+       expect_condlog(0, "no more available user_friendly_names\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
        assert_int_equal(rc, -1);
        assert_ptr_equal(alias, NULL);
 }
@@ -505,27 +838,96 @@ static void lb_nomatch_int_max_m1(void **state)
        will_return(__wrap_fgets, "MPATHa WWID0\n");
        will_return(__wrap_fgets, NULL);
        expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
-       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
+       assert_int_equal(rc, INT_MAX);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_int_max_m1_used(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHb WWID1\n");
+       will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
+       will_return(__wrap_fgets, "MPATHa WWID0\n");
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATH" MPATH_ID_INT_MAX, USED_STR("MPATH" MPATH_ID_INT_MAX, "WWID2"));
+       expect_condlog(0, "no more available user_friendly_names\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+       assert_int_equal(rc, -1);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_int_max_m1_1_used(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHb WWID1\n");
+       will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
+       mock_unused_alias("MPATH" MPATH_ID_INT_MAX);
+       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
        assert_int_equal(rc, INT_MAX);
        assert_ptr_equal(alias, NULL);
 }
+
+static void lb_nomatch_int_max_m1_2_used(void **state)
+{
+       int rc;
+       char *alias;
+
+       will_return(__wrap_fgets, "MPATHb WWID1\n");
+       will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX_m1 " WWIDMAX\n");
+       will_return(__wrap_fgets, NULL);
+       mock_used_alias("MPATHa", USED_STR("MPATHa", "WWID2"));
+       mock_used_alias("MPATH" MPATH_ID_INT_MAX, USED_STR("MPATH" MPATH_ID_INT_MAX, "WWID2"));
+       expect_condlog(0, "no more available user_friendly_names\n");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+       assert_int_equal(rc, -1);
+       assert_ptr_equal(alias, NULL);
+}
 #endif
 
 static int test_lookup_binding(void)
 {
        const struct CMUnitTest tests[] = {
                cmocka_unit_test(lb_empty),
+               cmocka_unit_test(lb_empty_unused),
+               cmocka_unit_test(lb_empty_failed),
+               cmocka_unit_test(lb_empty_1_used),
+               cmocka_unit_test(lb_empty_1_used_self),
                cmocka_unit_test(lb_match_a),
                cmocka_unit_test(lb_nomatch_a),
+               cmocka_unit_test(lb_nomatch_a_bad_check),
+               cmocka_unit_test(lb_nomatch_a_unused),
+               cmocka_unit_test(lb_nomatch_a_3_used_failed_self),
                cmocka_unit_test(lb_match_c),
+               cmocka_unit_test(lb_match_c_check),
                cmocka_unit_test(lb_nomatch_a_c),
+               cmocka_unit_test(lb_nomatch_a_d_unused),
+               cmocka_unit_test(lb_nomatch_a_d_1_used),
+               cmocka_unit_test(lb_nomatch_a_d_2_used),
+               cmocka_unit_test(lb_nomatch_a_d_3_used),
                cmocka_unit_test(lb_nomatch_c_a),
+               cmocka_unit_test(lb_nomatch_d_a_unused),
+               cmocka_unit_test(lb_nomatch_d_a_1_used),
                cmocka_unit_test(lb_nomatch_a_b),
                cmocka_unit_test(lb_nomatch_a_b_bad),
+               cmocka_unit_test(lb_nomatch_a_b_bad_self),
                cmocka_unit_test(lb_nomatch_b_a),
+               cmocka_unit_test(lb_nomatch_b_a_3_used),
 #ifdef MPATH_ID_INT_MAX
                cmocka_unit_test(lb_nomatch_int_max),
+               cmocka_unit_test(lb_nomatch_int_max_check),
+               cmocka_unit_test(lb_nomatch_int_max_used),
                cmocka_unit_test(lb_nomatch_int_max_m1),
+               cmocka_unit_test(lb_nomatch_int_max_m1_used),
+               cmocka_unit_test(lb_nomatch_int_max_m1_1_used),
+               cmocka_unit_test(lb_nomatch_int_max_m1_2_used),
 #endif
        };
 
@@ -735,6 +1137,7 @@ static int test_allocate_binding(void)
 int main(void)
 {
        int ret = 0;
+       init_test_verbosity(3);
 
        ret += test_format_devname();
        ret += test_scan_devname();
index 84a3ba2..882aa3a 100644 (file)
@@ -22,6 +22,7 @@
 #include "globals.c"
 #include "blacklist.h"
 #include "test-log.h"
+#include "debug.h"
 
 struct udev_device {
        const char *sysname;
@@ -152,6 +153,7 @@ static int setup(void **state)
            store_ble(blist_property_wwn_inv, "!ID_WWN", ORIGIN_CONFIG))
                return -1;
 
+       init_test_verbosity(4);
        return 0;
 }
 
index fd4d74a..0ad100a 100644 (file)
 #include <cmocka.h>
 #include <libudev.h>
 #include <sys/sysmacros.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dirent.h>
 #include "util.h"
 #include "debug.h"
 
 #include "globals.c"
 
+static bool sys_dev_block_exists(void)
+{
+       DIR *dir;
+       bool rc = false;
+
+       dir = opendir("/sys/dev/block");
+       if (dir != NULL) {
+               struct dirent *de;
+
+               while((de = readdir(dir)) != NULL) {
+                       if (strcmp(de->d_name, ".") &&
+                           strcmp(de->d_name, "..")) {
+                               rc = true;
+                               break;
+                       }
+               }
+               closedir(dir);
+       }
+       return rc;
+}
+
 static int get_one_devt(char *devt, size_t len)
 {
        struct udev_enumerate *enm;
@@ -71,6 +97,8 @@ static void test_devt2devname_devt_good(void **state)
 {
        char dummy[BLK_DEV_SIZE];
 
+       if (!sys_dev_block_exists())
+               skip();
        assert_int_equal(devt2devname(dummy, sizeof(dummy), *state), 0);
 }
 
@@ -137,6 +165,8 @@ static void test_devt2devname_real(void **state)
        struct udev_list_entry *first, *item;
        unsigned int i = 0;
 
+       if (!sys_dev_block_exists())
+               skip();
        enm = udev_enumerate_new(udev);
        assert_non_null(enm);
        r = udev_enumerate_add_match_subsystem(enm, "block");
@@ -187,6 +217,7 @@ int main(void)
 {
        int ret = 0;
 
+       init_test_verbosity(-1);
        ret += devt2devname_tests();
        return ret;
 }
index 9895409..9f7d388 100644 (file)
@@ -770,7 +770,7 @@ int main(void)
 {
        int ret = 0;
 
-       conf.verbosity = 2;
+       init_test_verbosity(2);
        ret += test_directio();
        return ret;
 }
index bee117a..204cf1d 100644 (file)
@@ -16,6 +16,7 @@
  *
  */
 
+#include <pthread.h>
 #include <stdint.h>
 #include <stdbool.h>
 #include <stdarg.h>
@@ -179,6 +180,8 @@ struct dm_names *build_dm_names(void)
        return names;
 }
 
+static bool setup_done;
+
 static int setup(void **state)
 {
        if (dmevent_poll_supported()) {
@@ -186,6 +189,7 @@ static int setup(void **state)
                *state = &data;
        } else
                *state = NULL;
+       setup_done = true;
        return 0;
 }
 
@@ -262,14 +266,20 @@ struct dm_task *__wrap_libmp_dm_task_create(int task)
        return mock_type(struct dm_task *);
 }
 
+int __real_dm_task_no_open_count(struct dm_task *dmt);
 int __wrap_dm_task_no_open_count(struct dm_task *dmt)
 {
+       if (!setup_done)
+               return __real_dm_task_no_open_count(dmt);
        assert_ptr_equal((struct test_data *)dmt, &data);
        return mock_type(int);
 }
 
+int __real_dm_task_run(struct dm_task *dmt);
 int __wrap_dm_task_run(struct dm_task *dmt)
 {
+       if (!setup_done)
+               return __real_dm_task_run(dmt);
        assert_ptr_equal((struct test_data *)dmt, &data);
        return mock_type(int);
 }
@@ -291,8 +301,11 @@ struct dm_names * __wrap_dm_task_get_names(struct dm_task *dmt)
        return data.names;
 }
 
+void __real_dm_task_destroy(struct dm_task *dmt);
 void __wrap_dm_task_destroy(struct dm_task *dmt)
 {
+       if (!setup_done)
+               return __real_dm_task_destroy(dmt);
        assert_ptr_equal((struct test_data *)dmt, &data);
 
        if (data.names) {
@@ -912,6 +925,7 @@ int main(void)
 {
        int ret = 0;
 
+       init_test_verbosity(-1);
        ret += test_dmevents();
        return ret;
 }
index 8add5eb..36319ed 100644 (file)
@@ -1,12 +1,12 @@
+#include <stdlib.h>
+#include <string.h>
+
+#include "defaults.h"
 #include "structs.h"
 #include "config.h"
+#include "debug.h"
 
-/* Required globals */
-struct udev *udev;
-int logsink = -1;
-struct config conf = {
-       .verbosity = 4,
-};
+struct config conf;
 
 struct config *get_multipath_config(void)
 {
@@ -15,3 +15,19 @@ struct config *get_multipath_config(void)
 
 void put_multipath_config(void *arg)
 {}
+
+static __attribute__((unused)) void init_test_verbosity(int test_verbosity)
+{
+       char *verb = getenv("MPATHTEST_VERBOSITY");
+
+       libmp_verbosity = test_verbosity >= 0 ? test_verbosity :
+               DEFAULT_VERBOSITY;
+       if (verb && *verb) {
+               char *c;
+               int vb;
+
+               vb = strtoul(verb, &c, 10);
+               if (!*c && vb >= 0 && vb <= 5)
+                       libmp_verbosity = vb;
+       }
+}
index 12660da..6f5766f 100644 (file)
@@ -30,8 +30,6 @@
 #define N_CONF_FILES 2
 
 static const char tmplate[] = "/tmp/hwtable-XXXXXX";
-/* pretend new dm, use minio_rq */
-static const unsigned int dm_tgt_version[3] = { 1, 1, 1 };
 
 struct key_value {
        const char *key;
@@ -55,7 +53,7 @@ struct hwt_state {
 
 static struct config *_conf;
 struct udev *udev;
-int logsink = -1;
+int logsink = LOGSINK_STDERR_WITHOUT_TIME;
 
 struct config *get_multipath_config(void)
 {
@@ -360,7 +358,6 @@ static void write_device(FILE *ff, int nkv, const struct key_value *kv)
        assert_ptr_not_equal(__cf, NULL);                               \
        assert_ptr_not_equal(__cf->hwtable, NULL);                      \
        __cf->verbosity = VERBOSITY;                                    \
-       memcpy(&__cf->version, dm_tgt_version, sizeof(__cf->version));  \
        __cf; })
 
 #define FREE_CONFIG(conf) do {                 \
@@ -1781,6 +1778,8 @@ int main(void)
 {
        int ret = 0;
 
+       /* We can't use init_test_verbosity in this test */
+       libmp_verbosity = VERBOSITY;
        ret += test_hwtable();
        return ret;
 }
diff --git a/tests/mpathvalid.c b/tests/mpathvalid.c
new file mode 100644 (file)
index 0000000..cfe4bae
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 2020 Benjamin Marzinski, Red Hat
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <libudev.h>
+#include <cmocka.h>
+#include "structs.h"
+#include "config.h"
+#include "mpath_valid.h"
+#include "util.h"
+#include "debug.h"
+
+const char *test_dev = "test_name";
+#define TEST_WWID "WWID_123"
+#define CONF_TEMPLATE "mpathvalid-testconf-XXXXXXXX"
+char conf_name[] = CONF_TEMPLATE;
+bool initialized;
+
+#if 0
+static int mode_to_findmp(unsigned int mode)
+{
+       switch (mode) {
+       case MPATH_SMART:
+               return FIND_MULTIPATHS_SMART;
+       case MPATH_GREEDY:
+               return FIND_MULTIPATHS_GREEDY;
+       case MPATH_STRICT:
+               return FIND_MULTIPATHS_STRICT;
+       }
+       fail_msg("invalid mode: %u", mode);
+       return FIND_MULTIPATHS_UNDEF;
+}
+#endif
+
+static unsigned int findmp_to_mode(int findmp)
+{
+       switch (findmp) {
+       case FIND_MULTIPATHS_SMART:
+               return MPATH_SMART;
+       case FIND_MULTIPATHS_GREEDY:
+               return MPATH_GREEDY;
+       case FIND_MULTIPATHS_STRICT:
+       case FIND_MULTIPATHS_OFF:
+       case FIND_MULTIPATHS_ON:
+               return MPATH_STRICT;
+       }
+       fail_msg("invalid find_multipaths value: %d", findmp);
+       return MPATH_DEFAULT;
+}
+
+int __wrap_is_path_valid(const char *name, struct config *conf, struct path *pp,
+                        bool check_multipathd)
+{
+       int r = mock_type(int);
+       int findmp = mock_type(int);
+
+       assert_ptr_equal(name, test_dev);
+       assert_ptr_not_equal(conf, NULL);
+       assert_ptr_not_equal(pp, NULL);
+       assert_true(check_multipathd);
+
+       assert_int_equal(findmp, conf->find_multipaths);        
+       if (r == MPATH_IS_ERROR || r == MPATH_IS_NOT_VALID)
+               return r;
+       
+       strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
+       return r;
+}
+
+int __wrap_libmultipath_init(void)
+{
+       int r = mock_type(int);
+
+       assert_false(initialized);
+       if (r != 0)
+               return r;
+       initialized = true;
+       return 0;
+}
+
+void __wrap_libmultipath_exit(void)
+{
+       assert_true(initialized);
+       initialized = false;
+}
+
+int __wrap_dm_prereq(unsigned int *v)
+{
+       assert_ptr_not_equal(v, NULL);
+       return mock_type(int);
+}
+
+int __real_init_config(const char *file);
+
+int __wrap_init_config(const char *file)
+{
+       int r = mock_type(int);
+       struct config *conf;
+
+       assert_ptr_equal(file, DEFAULT_CONFIGFILE);
+       if (r != 0)
+               return r;
+
+       assert_string_not_equal(conf_name, CONF_TEMPLATE);
+       r = __real_init_config(conf_name);
+       conf = get_multipath_config();
+       assert_ptr_not_equal(conf, NULL);
+       assert_int_equal(conf->find_multipaths, mock_type(int));
+       return 0;
+}
+
+static const char * const find_multipaths_optvals[] = {
+        [FIND_MULTIPATHS_OFF] = "off",
+        [FIND_MULTIPATHS_ON] = "on",
+        [FIND_MULTIPATHS_STRICT] = "strict",
+        [FIND_MULTIPATHS_GREEDY] = "greedy",
+        [FIND_MULTIPATHS_SMART] = "smart",
+};
+
+void make_config_file(int findmp)
+{
+       int r, fd;
+       char buf[64];
+
+       assert_true(findmp > FIND_MULTIPATHS_UNDEF &&
+                   findmp < __FIND_MULTIPATHS_LAST);
+
+       r = snprintf(buf, sizeof(buf), "defaults {\nfind_multipaths %s\n}\n",
+                    find_multipaths_optvals[findmp]);
+       assert_true(r > 0 && (long unsigned int)r < sizeof(buf));
+
+       memcpy(conf_name, CONF_TEMPLATE, sizeof(conf_name));
+       fd = mkstemp(conf_name);
+       assert_true(fd >= 0);
+       assert_int_equal(safe_write(fd, buf, r), 0);
+       assert_int_equal(close(fd), 0);
+}
+
+int setup(void **state)
+{
+       initialized = false;
+       udev = udev_new();
+       if (udev == NULL)
+               return -1;
+       return 0;
+}
+
+int teardown(void **state)
+{
+       struct config *conf;
+       conf = get_multipath_config();
+       put_multipath_config(conf);
+       if (conf)
+               uninit_config();
+       if (strcmp(conf_name, CONF_TEMPLATE) != 0)
+               unlink(conf_name);
+       udev_unref(udev);
+       udev = NULL;
+       return 0;
+}
+
+static void check_config(bool valid_config)
+{
+       struct config *conf;
+
+       conf = get_multipath_config();
+       put_multipath_config(conf);
+       if (valid_config)
+               assert_ptr_not_equal(conf, NULL);
+}
+
+/* libmultipath_init fails */
+static void test_mpathvalid_init_bad1(void **state)
+{
+       will_return(__wrap_libmultipath_init, 1);
+       assert_int_equal(mpathvalid_init(MPATH_LOG_PRIO_DEBUG,
+                                        MPATH_LOG_STDERR), -1);
+       assert_false(initialized);
+       check_config(false);
+}
+
+/* init_config fails */
+static void test_mpathvalid_init_bad2(void **state)
+{
+       will_return(__wrap_libmultipath_init, 0);
+       will_return(__wrap_init_config, 1);
+       assert_int_equal(mpathvalid_init(MPATH_LOG_PRIO_ERR,
+                                        MPATH_LOG_STDERR_TIMESTAMP), -1);
+       assert_false(initialized);
+       check_config(false);
+}
+
+/* dm_prereq fails */
+static void test_mpathvalid_init_bad3(void **state)
+{
+       make_config_file(FIND_MULTIPATHS_STRICT);
+       will_return(__wrap_libmultipath_init, 0);
+       will_return(__wrap_init_config, 0);
+       will_return(__wrap_init_config, FIND_MULTIPATHS_STRICT);
+       will_return(__wrap_dm_prereq, 1);
+       assert_int_equal(mpathvalid_init(MPATH_LOG_STDERR, MPATH_LOG_PRIO_ERR),
+                        -1);
+       assert_false(initialized);
+       check_config(false);
+}
+
+static void check_mpathvalid_init(int findmp, int prio, int log_style)
+{
+       make_config_file(findmp);
+       will_return(__wrap_libmultipath_init, 0);
+       will_return(__wrap_init_config, 0);
+       will_return(__wrap_init_config, findmp);
+       will_return(__wrap_dm_prereq, 0);
+       assert_int_equal(mpathvalid_init(prio, log_style), 0);  
+       assert_true(initialized);
+       check_config(true);
+       assert_int_equal(logsink, log_style);
+       assert_int_equal(libmp_verbosity, prio);
+       assert_int_equal(findmp_to_mode(findmp), mpathvalid_get_mode());
+}
+
+static void check_mpathvalid_exit(void)
+{
+       assert_int_equal(mpathvalid_exit(), 0);
+       assert_false(initialized);
+       check_config(false);
+}
+
+static void test_mpathvalid_init_good1(void **state)
+{
+       check_mpathvalid_init(FIND_MULTIPATHS_OFF, MPATH_LOG_PRIO_ERR,
+                             MPATH_LOG_STDERR_TIMESTAMP);
+}
+
+static void test_mpathvalid_init_good2(void **state)
+{
+       check_mpathvalid_init(FIND_MULTIPATHS_STRICT, MPATH_LOG_PRIO_DEBUG,
+                             MPATH_LOG_STDERR);
+}
+
+static void test_mpathvalid_init_good3(void **state)
+{
+       check_mpathvalid_init(FIND_MULTIPATHS_ON, MPATH_LOG_PRIO_NOLOG,
+                             MPATH_LOG_SYSLOG);
+}
+
+static void test_mpathvalid_exit(void **state)
+{
+       check_mpathvalid_init(FIND_MULTIPATHS_ON, MPATH_LOG_PRIO_ERR,
+                             MPATH_LOG_STDERR);
+       check_mpathvalid_exit();
+}
+
+/* fails if config hasn't been set */
+static void test_mpathvalid_get_mode_bad(void **state)
+{
+#if 1
+       assert_int_equal(mpathvalid_get_mode(), MPATH_MODE_ERROR);
+#else
+       assert_int_equal(mpathvalid_get_mode(), 1);
+#endif
+}
+
+/*fails if config hasn't been set */
+static void test_mpathvalid_reload_config_bad1(void **state)
+{
+#if 1
+       will_return(__wrap_init_config, 1);
+#endif
+       assert_int_equal(mpathvalid_reload_config(), -1);
+       check_config(false);
+}
+
+/* init_config fails */
+static void test_mpathvalid_reload_config_bad2(void **state)
+{
+       check_mpathvalid_init(FIND_MULTIPATHS_ON, MPATH_LOG_PRIO_ERR,
+                             MPATH_LOG_STDERR);
+       will_return(__wrap_init_config, 1);
+       assert_int_equal(mpathvalid_reload_config(), -1);
+       check_config(false);
+       check_mpathvalid_exit();
+}
+
+static void check_mpathvalid_reload_config(int findmp)
+{
+       assert_string_not_equal(conf_name, CONF_TEMPLATE);
+       unlink(conf_name);
+       make_config_file(findmp);
+       will_return(__wrap_init_config, 0);
+       will_return(__wrap_init_config, findmp);
+       assert_int_equal(mpathvalid_reload_config(), 0);
+       check_config(true);
+       assert_int_equal(findmp_to_mode(findmp), mpathvalid_get_mode());
+}
+
+static void test_mpathvalid_reload_config_good(void **state)
+{
+       check_mpathvalid_init(FIND_MULTIPATHS_OFF, MPATH_LOG_PRIO_ERR,
+                             MPATH_LOG_STDERR);
+       check_mpathvalid_reload_config(FIND_MULTIPATHS_ON);
+       check_mpathvalid_reload_config(FIND_MULTIPATHS_GREEDY);
+       check_mpathvalid_reload_config(FIND_MULTIPATHS_SMART);
+       check_mpathvalid_reload_config(FIND_MULTIPATHS_STRICT);
+       check_mpathvalid_exit();
+}
+
+/* NULL name */
+static void test_mpathvalid_is_path_bad1(void **state)
+{
+       assert_int_equal(mpathvalid_is_path(NULL, MPATH_STRICT, NULL, NULL, 0),
+                        MPATH_IS_ERROR);
+}
+
+/* bad mode */
+static void test_mpathvalid_is_path_bad2(void **state)
+{
+       assert_int_equal(mpathvalid_is_path(test_dev, MPATH_MODE_ERROR, NULL,
+                                           NULL, 0), MPATH_IS_ERROR);
+}
+
+/* NULL path_wwids and non-zero nr_paths */
+static void test_mpathvalid_is_path_bad3(void **state)
+{
+       assert_int_equal(mpathvalid_is_path(test_dev, MPATH_MODE_ERROR, NULL,
+                                           NULL, 1), MPATH_IS_ERROR);
+}
+
+/*fails if config hasn't been set */
+static void test_mpathvalid_is_path_bad4(void **state)
+{
+#if 0
+       will_return(__wrap_is_path_valid, MPATH_IS_ERROR);
+       will_return(__wrap_is_path_valid, FIND_MULTIPATHS_STRICT);
+#endif
+       assert_int_equal(mpathvalid_is_path(test_dev, MPATH_STRICT, NULL,
+                                           NULL, 0), MPATH_IS_ERROR);
+}
+
+/* is_path_valid fails */
+static void test_mpathvalid_is_path_bad5(void **state)
+{
+       check_mpathvalid_init(FIND_MULTIPATHS_OFF, MPATH_LOG_PRIO_ERR,
+                             MPATH_LOG_STDERR);
+       will_return(__wrap_is_path_valid, MPATH_IS_ERROR);
+       will_return(__wrap_is_path_valid, FIND_MULTIPATHS_GREEDY);
+       assert_int_equal(mpathvalid_is_path(test_dev, MPATH_GREEDY, NULL,
+                                           NULL, 0), MPATH_IS_ERROR);
+       check_mpathvalid_exit();
+}
+
+static void test_mpathvalid_is_path_good1(void **state)
+{
+       char *wwid;
+       check_mpathvalid_init(FIND_MULTIPATHS_STRICT, MPATH_LOG_PRIO_ERR,
+                             MPATH_LOG_STDERR);
+       will_return(__wrap_is_path_valid, MPATH_IS_NOT_VALID);
+       will_return(__wrap_is_path_valid, FIND_MULTIPATHS_STRICT);
+       assert_int_equal(mpathvalid_is_path(test_dev, MPATH_DEFAULT, &wwid,
+                                           NULL, 0), MPATH_IS_NOT_VALID);
+       assert_ptr_equal(wwid, NULL);
+       check_mpathvalid_exit();
+}
+
+static void test_mpathvalid_is_path_good2(void **state)
+{
+       const char *wwids[] = { "WWID_A", "WWID_B", "WWID_C", "WWID_D" };
+       char *wwid;
+       check_mpathvalid_init(FIND_MULTIPATHS_ON, MPATH_LOG_PRIO_ERR,
+                             MPATH_LOG_STDERR);
+       will_return(__wrap_is_path_valid, MPATH_IS_VALID);
+       will_return(__wrap_is_path_valid, FIND_MULTIPATHS_ON);
+       will_return(__wrap_is_path_valid, TEST_WWID);
+       assert_int_equal(mpathvalid_is_path(test_dev, MPATH_DEFAULT, &wwid,
+                                           wwids, 4), MPATH_IS_VALID);
+       assert_string_equal(wwid, TEST_WWID);
+       free(wwid);
+}
+
+static void test_mpathvalid_is_path_good3(void **state)
+{
+       const char *wwids[] = { "WWID_A", "WWID_B", "WWID_C", "WWID_D" };
+       char *wwid;
+       check_mpathvalid_init(FIND_MULTIPATHS_OFF, MPATH_LOG_PRIO_ERR,
+                             MPATH_LOG_STDERR);
+       will_return(__wrap_is_path_valid, MPATH_IS_VALID);
+       will_return(__wrap_is_path_valid, FIND_MULTIPATHS_SMART);
+       will_return(__wrap_is_path_valid, TEST_WWID);
+       assert_int_equal(mpathvalid_is_path(test_dev, MPATH_SMART, &wwid,
+                                           wwids, 4), MPATH_IS_VALID);
+       assert_string_equal(wwid, TEST_WWID);
+       free(wwid);
+}
+
+/* mabybe valid with no matching paths */
+static void test_mpathvalid_is_path_good4(void **state)
+{
+       const char *wwids[] = { "WWID_A", "WWID_B", "WWID_C", "WWID_D" };
+       char *wwid;
+       check_mpathvalid_init(FIND_MULTIPATHS_SMART, MPATH_LOG_PRIO_ERR,
+                             MPATH_LOG_STDERR);
+       will_return(__wrap_is_path_valid, MPATH_IS_MAYBE_VALID);
+       will_return(__wrap_is_path_valid, FIND_MULTIPATHS_SMART);
+       will_return(__wrap_is_path_valid, TEST_WWID);
+       assert_int_equal(mpathvalid_is_path(test_dev, MPATH_DEFAULT, &wwid,
+                                           wwids, 4), MPATH_IS_MAYBE_VALID);
+       assert_string_equal(wwid, TEST_WWID);
+       free(wwid);
+}
+
+/* maybe valid with matching paths */
+static void test_mpathvalid_is_path_good5(void **state)
+{
+       const char *wwids[] = { "WWID_A", "WWID_B", TEST_WWID, "WWID_D" };
+       char *wwid;
+       check_mpathvalid_init(FIND_MULTIPATHS_SMART, MPATH_LOG_PRIO_ERR,
+                             MPATH_LOG_STDERR);
+       will_return(__wrap_is_path_valid, MPATH_IS_MAYBE_VALID);
+       will_return(__wrap_is_path_valid, FIND_MULTIPATHS_SMART);
+       will_return(__wrap_is_path_valid, TEST_WWID);
+       assert_int_equal(mpathvalid_is_path(test_dev, MPATH_DEFAULT, &wwid,
+                                           wwids, 4), MPATH_IS_VALID);
+       assert_string_equal(wwid, TEST_WWID);
+       free(wwid);
+}
+
+#define setup_test(name) \
+       cmocka_unit_test_setup_teardown(name, setup, teardown)
+
+int test_mpathvalid(void)
+{
+       const struct CMUnitTest tests[] = {
+               setup_test(test_mpathvalid_init_bad1),
+               setup_test(test_mpathvalid_init_bad2),
+               setup_test(test_mpathvalid_init_bad3),
+               setup_test(test_mpathvalid_init_good1),
+               setup_test(test_mpathvalid_init_good2),
+               setup_test(test_mpathvalid_init_good3),
+               setup_test(test_mpathvalid_exit),
+               setup_test(test_mpathvalid_get_mode_bad),
+               setup_test(test_mpathvalid_reload_config_bad1),
+               setup_test(test_mpathvalid_reload_config_bad2),
+               setup_test(test_mpathvalid_reload_config_good),
+               setup_test(test_mpathvalid_is_path_bad1),
+               setup_test(test_mpathvalid_is_path_bad2),
+               setup_test(test_mpathvalid_is_path_bad3),
+               setup_test(test_mpathvalid_is_path_bad4),
+               setup_test(test_mpathvalid_is_path_bad5),
+               setup_test(test_mpathvalid_is_path_good1),
+               setup_test(test_mpathvalid_is_path_good2),
+               setup_test(test_mpathvalid_is_path_good3),
+               setup_test(test_mpathvalid_is_path_good4),
+               setup_test(test_mpathvalid_is_path_good5),
+       };
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+int main(void)
+{
+       int r = 0;
+
+       r += test_mpathvalid();
+       return r;
+}
index 5772391..cf96d81 100644 (file)
@@ -511,6 +511,7 @@ int main(void)
 {
        int ret = 0;
 
+       init_test_verbosity(-1);
        ret += test_config_parser();
        return ret;
 }
index 3f61b12..57ad338 100644 (file)
@@ -1031,6 +1031,7 @@ int main(void)
 {
        int ret = 0;
 
+       init_test_verbosity(-1);
        ret += test_pgpolicies();
        return ret;
 }
index b7c09cc..f5542ed 100644 (file)
@@ -56,6 +56,15 @@ int __wrap_execute_program(char *path, char *value, int len)
        return 0;
 }
 
+int __wrap_libmp_get_version(int which, unsigned int version[3])
+{
+       unsigned int *vers = mock_ptr_type(unsigned int *);
+
+       condlog(4, "%s: %d", __func__, which);
+       memcpy(version, vers, 3 * sizeof(unsigned int));
+       return 0;
+}
+
 struct udev_list_entry
 *__wrap_udev_device_get_properties_list_entry(struct udev_device *ud)
 {
@@ -248,17 +257,19 @@ void mock_pathinfo(int mask, const struct mocked_path *mp)
        } else
                will_return(__wrap_udev_device_get_sysattr_value, "0");
 
-       /* filter_property */
-       will_return(__wrap_udev_device_get_sysname, mp->devnode);
-       if (mp->flags & BL_BY_PROPERTY) {
-               will_return(__wrap_udev_list_entry_get_name, "BAZ");
-               return;
-       } else
-               will_return(__wrap_udev_list_entry_get_name,
-                           "SCSI_IDENT_LUN_NAA_EXT");
        if (mask & DI_SYSFS)
                mock_sysfs_pathinfo(mp);
 
+       if (mask & DI_BLACKLIST) {
+               will_return(__wrap_udev_device_get_sysname, mp->devnode);
+               if (mp->flags & BL_BY_PROPERTY) {
+                       will_return(__wrap_udev_list_entry_get_name, "BAZ");
+                       return;
+               } else
+                       will_return(__wrap_udev_list_entry_get_name,
+                                   "SCSI_IDENT_LUN_NAA_EXT");
+       }
+
        if (mp->flags & BL_BY_DEVICE &&
            (mask & DI_BLACKLIST && mask & DI_SYSFS))
                return;
@@ -339,6 +350,8 @@ struct multipath *__mock_multipath(struct vectors *vecs, struct path *pp)
        struct multipath *mp;
        struct config *conf;
        struct mocked_path mop;
+       /* pretend new dm, use minio_rq,  */
+       static const unsigned int fake_dm_tgt_version[3] = { 1, 1, 1 };
 
        mocked_path_from_path(&mop, pp);
        /* pathinfo() call in adopt_paths */
@@ -351,7 +364,9 @@ struct multipath *__mock_multipath(struct vectors *vecs, struct path *pp)
        conf = get_multipath_config();
        select_pgpolicy(conf, mp);
        select_no_path_retry(conf, mp);
+       will_return(__wrap_libmp_get_version, fake_dm_tgt_version);
        select_retain_hwhandler(conf, mp);
+       will_return(__wrap_libmp_get_version, fake_dm_tgt_version);
        select_minio(conf, mp);
        put_multipath_config(conf);
 
index 1c901cb..14f25b9 100644 (file)
@@ -7,8 +7,8 @@
 #include "log.h"
 #include "test-log.h"
 
-__attribute__((format(printf, 3, 0)))
-void __wrap_dlog (int sink, int prio, const char * fmt, ...)
+__attribute__((format(printf, 2, 0)))
+void __wrap_dlog (int prio, const char * fmt, ...)
 {
        char buff[MAX_MSG_SIZE];
        va_list ap;
index 2c878c6..6d22cd2 100644 (file)
@@ -1,7 +1,8 @@
 #ifndef _TEST_LOG_H
 #define _TEST_LOG_H
 
-void __wrap_dlog (int sink, int prio, const char * fmt, ...);
+__attribute__((format(printf, 2, 0)))
+void __wrap_dlog (int prio, const char * fmt, ...);
 void expect_condlog(int prio, char *string);
 
 #endif
index 9ffcd2d..648ff26 100644 (file)
@@ -322,6 +322,7 @@ int main(void)
 {
        int ret = 0;
 
+       init_test_verbosity(-1);
        ret += test_uevent_get_XXX();
        return ret;
 }
index 7ece1de..e43b64d 100644 (file)
@@ -91,6 +91,7 @@ int main(void)
 {
        int ret = 0;
 
+       init_test_verbosity(-1);
        ret += test_unaligned();
        return ret;
 }
index c3c49b6..9affb0e 100644 (file)
@@ -946,6 +946,7 @@ int main(void)
 {
        int ret = 0;
 
+       init_test_verbosity(-1);
        ret += test_basenamecpy();
        ret += test_bitmasks();
        ret += test_strlcpy();
index 693c72c..e7393a1 100644 (file)
 #include <stdlib.h>
 #include <errno.h>
 #include <cmocka.h>
+#include <sys/sysmacros.h>
+
 #include "globals.c"
 #include "util.h"
 #include "discovery.h"
 #include "wwids.h"
 #include "blacklist.h"
+#include "foreign.h"
 #include "valid.h"
 
+#define PATHINFO_REAL 9999
+
 int test_fd;
 struct udev_device {
        int unused;
@@ -78,12 +83,66 @@ struct udev_device *__wrap_udev_device_new_from_subsystem_sysname(struct udev *u
        return NULL;
 }
 
+/* For the "hidden" check in pathinfo() */
+const char *__wrap_udev_device_get_sysattr_value(struct udev_device *udev_device,
+                                        const char *sysattr)
+{
+       check_expected(sysattr);
+       return mock_ptr_type(char *);
+}
+
+/* For pathinfo() -> is_claimed_by_foreign() */
+int __wrap_add_foreign(struct udev_device *udev_device)
+{
+       return mock_type(int);
+}
+
+/* called from pathinfo() */
+int __wrap_filter_devnode(struct config *conf, const struct _vector *elist,
+                         const char *vendor, const char * product, const char *dev)
+{
+       return mock_type(int);
+}
+
+/* called from pathinfo() */
+int __wrap_filter_device(const struct _vector *blist, const struct _vector *elist,
+              const char *vendor, const char * product, const char *dev)
+{
+       return mock_type(int);
+}
+
+/* for common_sysfs_pathinfo() */
+dev_t __wrap_udev_device_get_devnum(struct udev_device *ud)
+{
+       return  mock_type(dev_t);
+}
+
+/* for common_sysfs_pathinfo() */
+int __wrap_sysfs_get_size(struct path *pp, unsigned long long * size)
+{
+       return mock_type(int);
+}
+
+/* called in pathinfo() before filter_property() */
+int __wrap_select_getuid(struct config *conf, struct path *pp)
+{
+       pp->uid_attribute = mock_ptr_type(char *);
+       return 0;
+}
+
+int __real_pathinfo(struct path *pp, struct config *conf, int mask);
+
 int __wrap_pathinfo(struct path *pp, struct config *conf, int mask)
 {
        int ret = mock_type(int);
+
        assert_string_equal(pp->dev, mock_ptr_type(char *));
        assert_int_equal(mask, DI_SYSFS | DI_WWID | DI_BLACKLIST);
-       if (ret == PATHINFO_OK) {
+       if (ret == PATHINFO_REAL) {
+               /* for test_filter_property() */
+               ret =  __real_pathinfo(pp, conf, mask);
+               return ret;
+       } else if (ret == PATHINFO_OK) {
                pp->uid_attribute = "ID_TEST";
                strlcpy(pp->wwid, mock_ptr_type(char *), WWID_SIZE);
        } else
@@ -128,6 +187,7 @@ enum {
        STAGE_IS_MULTIPATHED,
        STAGE_CHECK_MULTIPATHD,
        STAGE_GET_UDEV_DEVICE,
+       STAGE_PATHINFO_REAL,
        STAGE_PATHINFO,
        STAGE_FILTER_PROPERTY,
        STAGE_IS_FAILED,
@@ -167,12 +227,25 @@ static void setup_passing(char *name, char *wwid, unsigned int check_multipathd,
                    name);
        if (stage == STAGE_GET_UDEV_DEVICE)
                return;
+       if  (stage == STAGE_PATHINFO_REAL) {
+               /* special case for test_filter_property() */
+               will_return(__wrap_pathinfo, PATHINFO_REAL);
+               will_return(__wrap_pathinfo, name);
+               expect_string(__wrap_udev_device_get_sysattr_value,
+                             sysattr, "hidden");
+               will_return(__wrap_udev_device_get_sysattr_value, NULL);
+               will_return(__wrap_add_foreign, FOREIGN_IGNORED);
+               will_return(__wrap_filter_devnode, MATCH_NOTHING);
+               will_return(__wrap_udev_device_get_devnum, makedev(259, 0));
+               will_return(__wrap_sysfs_get_size, 0);
+               will_return(__wrap_select_getuid, "ID_TEST");
+               return;
+       }
        will_return(__wrap_pathinfo, PATHINFO_OK);
        will_return(__wrap_pathinfo, name);
        will_return(__wrap_pathinfo, wwid);
        if (stage == STAGE_PATHINFO)
                return;
-       will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_EXCEPT);
        if (stage == STAGE_FILTER_PROPERTY)
                return;
        will_return(__wrap_is_failed_wwid, WWID_IS_NOT_FAILED);
@@ -317,24 +390,24 @@ static void test_filter_property(void **state)
        /* test blacklist property */
        memset(&pp, 0, sizeof(pp));
        conf.find_multipaths = FIND_MULTIPATHS_STRICT;
-       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO_REAL);
        will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST);
        assert_int_equal(is_path_valid(name, &conf, &pp, false),
                         PATH_IS_NOT_VALID);
        assert_ptr_equal(pp.udev, &test_udev);
-       assert_string_equal(pp.wwid, wwid);
+
        /* test missing property */
        memset(&pp, 0, sizeof(pp));
-       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO_REAL);
        will_return(__wrap_filter_property, MATCH_PROPERTY_BLIST_MISSING);
        assert_int_equal(is_path_valid(name, &conf, &pp, false),
                         PATH_IS_NOT_VALID);
-       /* test MATCH_NOTHING fail on is_failed_wwid */
+
+       /* test MATCH_NOTHING fail on filter_device */
        memset(&pp, 0, sizeof(pp));
-       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO);
+       setup_passing(name, wwid, CHECK_MPATHD_SKIP, STAGE_PATHINFO_REAL);
        will_return(__wrap_filter_property, MATCH_NOTHING);
-       will_return(__wrap_is_failed_wwid, WWID_IS_FAILED);
-       will_return(__wrap_is_failed_wwid, wwid);
+       will_return(__wrap_filter_device, MATCH_DEVICE_BLIST);
        assert_int_equal(is_path_valid(name, &conf, &pp, false),
                         PATH_IS_NOT_VALID);
 }
@@ -481,6 +554,8 @@ int test_valid(void)
 int main(void)
 {
        int ret = 0;
+
+       init_test_verbosity(-1);
        ret += test_valid();
        return ret;
 }
index e2ec65e..8e730d3 100644 (file)
@@ -799,6 +799,7 @@ int main(void)
 {
        int ret = 0;
 
+       init_test_verbosity(-1);
        ret += test_vpd();
        return ret;
 }
diff --git a/third-party/valgrind/mpath-tools.supp b/third-party/valgrind/mpath-tools.supp
new file mode 100644 (file)
index 0000000..0537fd5
--- /dev/null
@@ -0,0 +1,32 @@
+{
+   glibc _dlerror_run leak: https://stackoverflow.com/questions/1542457/memory-leak-reported-by-valgrind-in-dlopen
+   Memcheck:Leak
+   match-leak-kinds: reachable
+   fun:calloc
+   fun:_dlerror_run
+   fun:dlopen*
+}
+
+{
+   systemd mempools are never freed: https://bugzilla.redhat.com/show_bug.cgi?id=1215670
+   Memcheck:Leak
+   match-leak-kinds: reachable
+   fun:malloc
+   fun:mempool_alloc_tile
+   fun:mempool_alloc0_tile
+   fun:hashmap_base_new
+   fun:hashmap_base_ensure_allocated
+}
+
+{
+   libgcrypt library initialization
+   Memcheck:Leak
+   match-leak-kinds: reachable
+   fun:malloc
+   ...
+   fun:_gcry_xmalloc
+   ...
+   fun:global_init.*
+   ...
+   fun:_dl_init
+}