Imported Upstream version 0.9.7 upstream upstream/0.9.7
authorTizenOpenSource <tizenopensrc@samsung.com>
Fri, 8 Dec 2023 03:28:18 +0000 (12:28 +0900)
committerTizenOpenSource <tizenopensrc@samsung.com>
Fri, 8 Dec 2023 03:28:18 +0000 (12:28 +0900)
103 files changed:
.github/workflows/build-and-unittest.yaml
.github/workflows/coverity.yaml
.github/workflows/foreign.yaml
.github/workflows/multiarch.yaml
.github/workflows/native.yaml
.gitignore
Makefile.inc
README.md
create-config.mk
kpartx/dm-parts.rules
kpartx/kpartx.8
libdmmp/Makefile
libmpathpersist/mpath_persist_int.c
libmpathpersist/mpath_persistent_reserve_in.3
libmpathpersist/mpath_persistent_reserve_out.3
libmpathpersist/mpath_updatepr.c
libmpathutil/libmpathutil.version
libmpathutil/util.c
libmpathutil/util.h
libmultipath/Makefile
libmultipath/alias.c
libmultipath/alias.h
libmultipath/blacklist.c
libmultipath/checkers/directio.c
libmultipath/checkers/tur.c
libmultipath/config.c
libmultipath/config.h
libmultipath/configure.c
libmultipath/configure.h
libmultipath/defaults.h
libmultipath/devmapper.c
libmultipath/dict.c
libmultipath/dict.h
libmultipath/discovery.c
libmultipath/hwtable.c
libmultipath/io_err_stat.c
libmultipath/libmultipath.version
libmultipath/lock.h
libmultipath/pgpolicies.c
libmultipath/pgpolicies.h
libmultipath/print.c
libmultipath/prio.c
libmultipath/prio.h
libmultipath/prioritizers/alua.c
libmultipath/prioritizers/alua_rtpg.c
libmultipath/prioritizers/alua_rtpg.h
libmultipath/prioritizers/ana.c
libmultipath/prioritizers/const.c
libmultipath/prioritizers/datacore.c
libmultipath/prioritizers/emc.c
libmultipath/prioritizers/hds.c
libmultipath/prioritizers/hp_sw.c
libmultipath/prioritizers/iet.c
libmultipath/prioritizers/ontap.c
libmultipath/prioritizers/path_latency.c
libmultipath/prioritizers/random.c
libmultipath/prioritizers/rdac.c
libmultipath/prioritizers/sysfs.c
libmultipath/prioritizers/weightedpath.c
libmultipath/prkey.c
libmultipath/prkey.h
libmultipath/propsel.c
libmultipath/propsel.h
libmultipath/structs.c
libmultipath/structs.h
libmultipath/structs_vec.c
libmultipath/switchgroup.c
libmultipath/sysfs.c
libmultipath/valid.c
libmultipath/version.h
libmultipath/wwids.c
mpathpersist/Makefile
mpathpersist/mpathpersist.8 [deleted file]
mpathpersist/mpathpersist.8.in [new file with mode: 0644]
multipath/11-dm-mpath.rules
multipath/Makefile
multipath/main.c
multipath/multipath.8 [deleted file]
multipath/multipath.8.in [new file with mode: 0644]
multipath/multipath.conf.5 [deleted file]
multipath/multipath.conf.5.in [new file with mode: 0644]
multipath/multipath.rules.in
multipathd/Makefile
multipathd/cli_handlers.c
multipathd/fpin_handlers.c
multipathd/main.c
multipathd/main.h
multipathd/multipathc.8
multipathd/multipathd.8 [deleted file]
multipathd/multipathd.8.in [new file with mode: 0644]
multipathd/multipathd.service [deleted file]
multipathd/multipathd.service.in [new file with mode: 0644]
multipathd/uxlsnr.c
tests/Makefile
tests/alias.c
tests/blacklist.c
tests/directio.c
tests/pgpolicy.c
tests/test-lib.c
tests/test-log.c
tests/util.c
tests/valid.c
tests/vpd.c

index abf17bf0529368b7b68d62cae9298cb930c5f862..61077dff246a8c67a1b3cb6eab2d675232c85528 100644 (file)
@@ -8,82 +8,68 @@ on:
   pull_request:
 
 jobs:
-  bionic:
-    runs-on: ubuntu-18.04
+  jammy:
+    runs-on: ubuntu-22.04
     strategy:
       fail-fast: false
       matrix:
         rl: ['', 'libreadline', 'libedit']
+        cc: [ gcc, clang ]
     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
+          make pkg-config valgrind
           libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev
           libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev
-      - name: build
-        run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) READLINE=${{ matrix.rl }}
-      - name: test
-        run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) test
-      - name: valgrind-test
-        run: make -Orecurse -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
-    strategy:
-      fail-fast: false
-      matrix:
-        rl: ['', 'libreadline', 'libedit']
-    steps:
-      - uses: actions/checkout@v2
+          libmount-dev linux-modules-extra-$(uname -r)
       - 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 libedit-dev
+      - 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: set CC
-        run: echo CC=gcc-10 >> $GITHUB_ENV
+        run: echo CC=${{ matrix.cc }} >> $GITHUB_ENV
+      - name: set optflags
+        # valgrind doesn't support the dwarf-5 format of clang 14
+        run: echo OPT='-O2 -gdwarf-4 -fstack-protector-strong' >> $GITHUB_ENV
+        if: ${{ matrix.cc == 'clang' }}
       - name: build
-        run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) READLINE=${{ matrix.rl }}
+        run: >
+          make -Orecurse -j$(grep -c ^processor /proc/cpuinfo)
+          READLINE=${{ matrix.rl }} OPTFLAGS="$OPT"
       - name: test
-        run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) test
+        run: >
+          make -Orecurse -j$(grep -c ^processor /proc/cpuinfo)
+          OPTFLAGS="$OPT" test
       - name: valgrind-test
-        run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) valgrind-test
+        id: valgrind
+        run: >
+          make -Orecurse -j$(grep -c ^processor /proc/cpuinfo)
+          OPTFLAGS="$OPT" valgrind-test
+        continue-on-error: true
       - name: valgrind-results
         run: cat tests/*.vgr
+      - name: fail if valgrind failed
+        run: /bin/false
+        if: steps.valgrind.outcome != 'success'
       - 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:
+        run: sudo make DIO_TEST_DEV=/dev/zram$ZRAM test
+  focal:
     runs-on: ubuntu-20.04
     strategy:
       fail-fast: false
       matrix:
         rl: ['', 'libreadline', 'libedit']
+        cc: [ gcc, clang ]
     steps:
       - uses: actions/checkout@v2
       - name: mpath
@@ -94,20 +80,25 @@ jobs:
         run: sudo apt-get update
       - name: dependencies
         run: >
-          sudo apt-get install --yes clang
-          make perl-base pkg-config valgrind
+          sudo apt-get install --yes gcc-10
+          make pkg-config valgrind
           libdevmapper-dev libreadline-dev libaio-dev libsystemd-dev
           libudev-dev libjson-c-dev liburcu-dev libcmocka-dev libedit-dev
       - name: set CC
-        run: echo CC=clang >> $GITHUB_ENV
+        run: echo CC=${{ matrix.cc }} >> $GITHUB_ENV
       - name: build
         run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) READLINE=${{ matrix.rl }}
       - name: test
         run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) test
       - name: valgrind-test
+        id: valgrind
         run: make -Orecurse -j$(grep -c ^processor /proc/cpuinfo) valgrind-test
+        continue-on-error: true
       - name: valgrind-results
         run: cat tests/*.vgr
+      - name: fail if valgrind failed
+        run: /bin/false
+        if: steps.valgrind.outcome != 'success'
       - name: clean-nonroot-artifacts
         run: rm -f tests/dmevents.out tests/directio.out
       - name: root-test
index 321b94e0a9354f297d2acce5aca1fafbe42e1acf..a1f4146b790e833f09704534fde8f818e0dc24ed 100644 (file)
@@ -6,7 +6,7 @@ on:
 
 jobs:
   upload-coverity-scan:
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
     steps:
       - name: checkout
         uses: actions/checkout@v2
index 2937b72b79be2c5054256ccd7d1d449b7427a83b..1b04292b9dcf7cdd2fb22084c90c2b0d401574d6 100644 (file)
@@ -10,11 +10,11 @@ on:
 jobs:
 
   cross-build:
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
     strategy:
       fail-fast: false
       matrix:
-        os: [bullseye, sid]
+        os: [bullseye, bookworm, sid]
         arch: [ppc64le, arm64, s390x]
     container: ghcr.io/mwilck/multipath-cross-debian_cross-${{ matrix.os }}-${{ matrix.arch }}
     steps:
@@ -37,12 +37,12 @@ jobs:
           path: binaries.tar
 
   test:
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
     needs: cross-build
     strategy:
       fail-fast: false
       matrix:
-        os: [bullseye, sid]
+        os: [bullseye, bookworm, sid]
         arch: [ppc64le, arm64, s390x]
     steps:
       - name: set container arch
index 513021b059ca4c4fe4e6d34225aaf7b8c4c5f10f..d76a01ee326dbc02d8c1a32ded614afd3bc94cc0 100644 (file)
@@ -10,7 +10,7 @@ on:
 jobs:
 
   build-current:
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
     strategy:
       fail-fast: false
       matrix:
@@ -54,7 +54,7 @@ jobs:
           pull-params: "--platform linux/${{ matrix.arch }}"
 
   build-old:
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
     strategy:
       fail-fast: false
       matrix:
index a7ad4c83874b5029e0c4609ff53e0a9d1b6e961e..5d83a5851ca997f0ac78b01531a7c51166271bec 100644 (file)
@@ -9,15 +9,16 @@ on:
 
 jobs:
   stable:
-    runs-on: ubuntu-20.04
+    runs-on: ubuntu-22.04
     strategy:
       fail-fast: false
       matrix:
         os:
-          - debian-buster
           - debian-jessie
+          - debian-buster
           - debian-bullseye
-          - fedora-36
+          - debian-bookworm
+          - fedora-37
           - opensuse-leap
     container: ghcr.io/mwilck/multipath-build-${{ matrix.os }}
     steps:
index 535353e51a823ca7b46b6253b3738032e566f47a..6890e4af2d043c926559e0335d70261face37319 100644 (file)
@@ -13,11 +13,16 @@ cscope.files
 cscope.out
 kpartx/kpartx
 multipath/multipath
+multipath/multipath.8
+multipath/multipath.conf.5
 multipath/multipath.rules
 multipath/tmpfiles.conf
 multipathd/multipathd
+multipathd/multipathd.8
 multipathd/multipathc
+multipathd/multipathd.service
 mpathpersist/mpathpersist
+mpathpersist/mpathpersist.8
 abi.tar.gz
 abi
 abi-test
index 2e25d2ead9b0dba4a4a2ba19a7aa7fbe2d4c91d7..6b4543035ddb8de72f2bfc2721a64b61ef0d2d39 100644 (file)
@@ -20,9 +20,9 @@ SCSI_DH_MODULES_PRELOAD :=
 
 EXTRAVERSION := $(shell rev=$$(git rev-parse --short=7 HEAD 2>/dev/null); echo $${rev:+-g$$rev})
 
-# PKGCONFIG must be read from the environment to enable compilation
+# PKG_CONFIG must be read from the environment to enable compilation
 # in Debian multiarch setups
-PKGCONFIG      ?= pkg-config
+PKG_CONFIG     ?= pkg-config
 
 ifeq ($(TOPDIR),)
        TOPDIR  = ..
@@ -36,28 +36,43 @@ prefix              :=
 # Prefix for binaries
 exec_prefix    := $(prefix)
 # Prefix for non-essential libraries (libdmmp)
-usr_prefix     := $(prefix)
+usr_prefix     := $(if $(prefix),$(prefix),/usr)
+# Prefix for configfuration files (multipath.conf)
+etc_prefix     := $(prefix)
 # Where to install systemd-related files. systemd is usually installed under /usr
-# Note: some systemd installations use separate "prefix" and "rootprefix".
-# In this case, override only unitdir to use systemd's "rootprefix" instead of $(systemd_prefix)
+# Note: systemd installations with "split-usr=true" use separate "prefixdir" and
+# "rootprefixdir". Our systemd_prefix corresponds to "prefixdir".
+# In this case, override only unitdir and libudevdir below to use
+# systemd's "rootprefixdir" instead of $(systemd_prefix)
 systemd_prefix  := /usr
-unitdir                := $(systemd_prefix)/lib/systemd/system
-tmpfilesdir    := $(systemd_prefix)/lib/tmpfiles.d
-modulesloaddir := $(systemd_prefix)/lib/modules-load.d
-libudevdir     := $(systemd_prefix)/lib/udev
+
+# Make sure all prefix variables end in "/"
+append-slash = $(1)$(if $(filter %/,$(1)),,/)
+override prefix          := $(call append-slash,$(prefix))
+override exec_prefix     := $(call append-slash,$(exec_prefix))
+override usr_prefix      := $(call append-slash,$(usr_prefix))
+override etc_prefix      := $(call append-slash,$(etc_prefix))
+override systemd_prefix  := $(call append-slash,$(systemd_prefix))
+
+unitdir                := $(systemd_prefix)lib/systemd/system
+tmpfilesdir    := $(systemd_prefix)lib/tmpfiles.d
+modulesloaddir := $(systemd_prefix)lib/modules-load.d
+libudevdir     := $(systemd_prefix)lib/udev
 udevrulesdir   := $(libudevdir)/rules.d
-bindir         := $(exec_prefix)/sbin
-mandir         := $(usr_prefix)/share/man
+bindir         := $(exec_prefix)sbin
+mandir         := $(usr_prefix)share/man
 LIB            := $(if $(shell test -d /lib64 && echo 1),lib64,lib)
-syslibdir      := $(prefix)/$(LIB)
-usrlibdir      := $(usr_prefix)/$(LIB)
-includedir     := $(usr_prefix)/include
+syslibdir      := $(prefix)$(LIB)
+usrlibdir      := $(usr_prefix)$(LIB)
+includedir     := $(usr_prefix)include
 pkgconfdir     := $(usrlibdir)/pkgconfig
-plugindir       := $(prefix)/$(LIB)/multipath
-configdir       := $(prefix)/etc/multipath/conf.d
+plugindir       := $(prefix)$(LIB)/multipath
+configdir       := $(etc_prefix)etc/multipath/conf.d
+configfile      := $(etc_prefix)etc/multipath.conf
+statedir        := $(etc_prefix)etc/multipath
 runtimedir      := $(if $(shell test -L /var/run -o ! -d /var/run && echo 1),/run,/var/run)
-devmapper_incdir := $(or $(shell $(PKGCONFIG) --variable=includedir devmapper),/usr/include)
-libudev_incdir := $(or $(shell $(PKGCONFIG) --variable=includedir libudev),/usr/include)
+devmapper_incdir := $(or $(shell $(PKG_CONFIG) --variable=includedir devmapper),/usr/include)
+libudev_incdir := $(or $(shell $(PKG_CONFIG) --variable=includedir libudev),/usr/include)
 kernel_incdir  := /usr/include
 
 ifeq ($(V),)
@@ -77,15 +92,18 @@ ORIG_LDFLAGS     := $(LDFLAGS)
 
 SYSTEMD_CPPFLAGS := $(if $(SYSTEMD),-DUSE_SYSTEMD=$(SYSTEMD))
 SYSTEMD_LIBDEPS := $(if $(SYSTEMD),$(if $(shell test $(SYSTEMD) -gt 209 && echo 1),-lsystemd,-lsystemd-daemon))
+MODPROBE_UNIT := $(shell test "0$(SYSTEMD)" -lt 245 2>/dev/null || \
+                       echo "modprobe@dm_multipath.service")
 
 OPTFLAGS       := -O2 -g $(STACKPROT) --param=ssp-buffer-size=4
 WARNFLAGS      := -Werror -Wall -Wextra -Wformat=2 $(WFORMATOVERFLOW) -Werror=implicit-int \
                  -Werror=implicit-function-declaration -Werror=format-security \
                  $(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) $(W_URCU_TYPE_LIMITS)
-CPPFLAGS       := $(FORTIFY_OPT) $(CPPFLAGS) \
+CPPFLAGS       := $(FORTIFY_OPT) $(CPPFLAGS) $(D_URCU_VERSION) \
                   -DBIN_DIR=\"$(bindir)\" -DMULTIPATH_DIR=\"$(plugindir)\" \
-                  -DRUNTIME_DIR=\"$(runtimedir)\" \
-                  -DCONFIG_DIR=\"$(configdir)\" -DEXTRAVERSION=\"$(EXTRAVERSION)\" -MMD -MP
+                  -DRUNTIME_DIR=\"$(runtimedir)\" -DCONFIG_DIR=\"$(configdir)\" \
+                  -DDEFAULT_CONFIGFILE=\"$(configfile)\" -DSTATE_DIR=\"$(statedir)\" \
+                  -DEXTRAVERSION=\"$(EXTRAVERSION)\" -MMD -MP
 CFLAGS         := --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe
 BIN_CFLAGS     := -fPIE -DPIE
 LIB_CFLAGS     := -fPIC
@@ -128,3 +146,6 @@ NV_VERSION_SCRIPT = $(DEVLIB:%.so=%-nv.version)
        @grep -P '^[ \t]+[a-zA-Z_][a-zA-Z0-9_]*;' $< >>$@
        @printf 'local:\n\t*;\n};\n' >>$@
 
+%:     %.in
+       @echo creating $@
+       $(Q)sed 's:@CONFIGFILE@:'$(configfile)':g;s:@CONFIGDIR@:'$(configdir)':g;s:@STATE_DIR@:'$(statedir)':g;s:@RUNTIME_DIR@:'$(runtimedir)':g;s/@MODPROBE_UNIT@/'$(MODPROBE_UNIT)'/g' $< >$@
index 5e04f5c30b8dcbe87bb8079ee1844ee4d9a4b0c0..d4f35f57c61fa86c9683e3144deaeecffe11f9a7 100644 (file)
--- a/README.md
+++ b/README.md
@@ -8,6 +8,7 @@ https://github.com/opensvc/multipath-tools
 This package provides the following binaries to drive the Device Mapper multipathing driver:
 
 * multipath - Device mapper target autoconfig.
+* multipathc - Interactive client for multipathd.
 * multipathd - Multipath daemon.
 * mpathpersist - Manages SCSI persistent reservations on dm multipath devices.
 * kpartx - Create device maps from partition tables.
@@ -41,14 +42,6 @@ Go to: https://github.com/opensvc/multipath-tools/tags
 Select a release-tag and then click on "zip" or "tar.gz".
 
 
-Devel code
-==========
-
-To get latest devel code:
-
-    git clone -b queue https://github.com/openSUSE/multipath-tools
-
-
 Building multipath-tools
 ========================
 
@@ -88,9 +81,17 @@ The following variables can be passed to the `make` command line:
  * `plugindir="/some/path"`: directory where libmultipath plugins (path
    checkers, prioritizers, and foreign multipath support) will be looked up.
    This used to be the run-time option `multipath_dir` in earlier versions.
- * `configdir="/some/path"` : directory to search for configuration files.
+   The default is `$(prefix)/$(LIB)/multipath`, where `$(LIB)` is `lib64` on
+   systems that have `/lib64`, and `lib` otherwise.
+ * `configfile="/some/path`": The path to the main configuration file.
+    The default is `$(etc_prefix)/etc/multipath.conf`.
+ * `configdir="/some/path"` : directory to search for additional configuration files.
     This used to be the run-time option `config_dir` in earlier versions.
-       The default is `/etc/multipath/conf.d`.
+       The default is `$(etc_prefix)/etc/multipath/conf.d`.
+ * `statedir="/some/path"`: The path of the directory where multipath-tools
+    stores run-time settings that need persist between reboots, such as known
+       WWIDs, user-friendly names, and persistent reservation keys.
+       The default is `$(etc_prefix)/etc/multipath`.
  * `READLINE=libedit` or `READLINE=libreadline`: enable command line history
     and TAB completion in the interactive mode *(which is entered with `multipathd -k` or `multipathc`)*.
     The respective development package will be required for building.
@@ -102,6 +103,13 @@ The following variables can be passed to the `make` command line:
    polling API. For use with pre-5.0 kernels that don't support dmevent polling
    (but even if you don't use this option, multipath-tools will work with
    these kernels).
+ * `SYSTEMD`: The version number of systemd (e.g. "244") to compile the code for.
+   The default is autodetected, assuming that the systemd version in the build
+   environment is the same as on the target system. Override the value to
+   build for a different systemd version, or set it to `""` to build for a
+   system without systemd.
+   **Caution:** multipathd without systemd has been largely untested by the
+   upstream maintainers since at least 2020.
  * `SCSI_DH_MODULES_PRELOAD="(list)"`: specify a space-separated list of SCSI
    device handler kernel modules to load early during boot. Some
    multipath-tools functionality depends on these modules being loaded
@@ -113,26 +121,35 @@ The following variables can be passed to the `make` command line:
    It's especially useful if `scsi_mod` is builtin but `scsi_dh_alua` and
    other device handler modules are built as modules. If `scsi_mod` itself is compiled
    as a module, it might make more sense to use a module softdep for the same
-   purpose.
+   purpose by creating a `modprobe.d` file like this:
+       
+        softdep scsi_mod post: scsi_dh_alua scsi_dh_rdac
 
 ### Installation Paths
 
  * `prefix`: The directory prefix for (almost) all files to be installed.
-   Distributions may want to set this to `/usr`.
-   **Note**: for multipath-tools, unlike many other packages, `prefix`
-   defaults to the empty string, which resolves to the root directory (`/`).
+   "Usr-merged" distributions[^systemd] may want to set this to `/usr`. The
+   default is empty (`""`).
  * `usr_prefix`: where to install those parts of the code that aren't necessary
-   for booting. You may want to set this to `/usr` if `prefix` is empty.
- * `systemd_prefix`: Prefix for systemd-related files. It defaults to `/usr`.
-   Some systemd installations use separate `prefix` and `rootprefix`. On such
-   a distribution, set `prefix`, and override `unitdir` to use systemd's
-   `rootprefix`.
+   for booting. The default is `/usr` if `$(prefix)` is empty, and `$(prefix)` otherwise.
+ * `systemd_prefix`: Prefix for systemd-related files[^systemd]. The default is `/usr`.
+ * `etc_prefix`: The prefix for configuration files. "usr-merged"
+   distributions with immutable `/usr`[^systemd] may want to set this to
+   `""`. The default is `$(prefix)`.
  * `LIB`: the subdirectory under `prefix` where shared libraries will be
    installed. By default, the makefile uses `/lib64` if this directory is
    found on the build system, and `/lib` otherwise.
    
-See also `configdir` and `plugindir` above. See `Makefile.inc` for more
-fine-grained control.
+The options `configdir`, `plugindir`, `configfile`, and `statedir` above can
+be used for setting individual paths where the `prefix` variables don't provide
+sufficient control. See `Makefile.inc` for even more fine-grained control.
+
+[^systemd]: systemd installations up to v254 which have been built with
+    `split-usr=true` may use separate `prefixdir` and `rootprefixdir`
+    directories, where `prefixdir` is a subdirectory of `rootprefixdir`.
+       multipath-tools' `systemd_prefix` corresponds to systemd's `prefixdir`.
+       On such distributions, override `unitdir` and `libudevdir` to use systemd's
+   `rootprefix`: `make libudevdir=/lib/udev unitdir=/lib/systemd/system`
 
 ### Compiler Options
 
@@ -162,32 +179,58 @@ The following targets are intended for developers only.
  * `make compile-commands.json` to create input for [clangd](https://clangd.llvm.org/).
 
 
-Add storage devices
-===================
-
-Follow the instructions in the `libmultipath/hwtable.c` header.
+Contributing
+============
 
+Please send patches or contributions for general discussion about
+multipath tools to the mailing list (see below). You can also create
+issues or pull requests on
+[GitHub](https://github.com/opensvc/multipath-tools).
+You will be asked to send your patches to the mailing list
+unless your patch is trivial.
 
 Mailing list
-============
+------------
+
+The mailing list for multipath-tools is `dm-devel@lists.linux.dev`.
+To subscribe, send an email to `dm-devel+subscribe@lists.linux.dev`.
+Mailing list archives are available on
+[lore.kernel.org](https://lore.kernel.org/dm-devel/) and
+[marc.info](https://marc.info/?l=dm-devel). See also the
+[lists.linux.dev home page](https://subspace.kernel.org/lists.linux.dev.html).
 
-(subscribers-only)
-To subscribe and archives: https://listman.redhat.com/mailman/listinfo/dm-devel
-Searchable: https://marc.info/?l=dm-devel
+When sending patches to the mailing list, please add a `Signed-off-by:`
+tag, and add Benjamin Marzinski <bmarzins@redhat.com> and 
+Martin Wilck <mwilck@suse.com> to the Cc list.
 
+Staging area
+------------
+
+Between releases, the latest reviewed code can be obtained from
+[the queue branch](https://github.com/openSUSE/multipath-tools/tree/queue)
+in the openSUSE/multipath-tools repository on GitHub. From there,
+pull requests for new releases in the master repository are
+created roughly every 3 months.
+
+Adding new storage devices
+--------------------------
+
+If you want to add special settings for a storage device which is
+new on the market, follow the instructions at the top of the
+file `libmultipath/hwtable.c`.
 
 Changelog
 =========
 
-pre-0.4.5: https://web.archive.org/web/20070309224034/http://christophe.varoqui.free.fr/wiki/wakka.php?wiki=ChangeLog
-post-0.4.5: https://github.com/opensvc/multipath-tools/commits/master
+pre-0.4.5: https://web.archive.org/web/20070309224034/http://christophe.varoqui.free.fr/wiki/wakka.php?wiki=ChangeLog
+post-0.4.5: https://github.com/opensvc/multipath-tools/commits/master
 
 
 Maintainer
 ==========
 
 Christophe Varoqui <christophe.varoqui@opensvc.com>
-Device-mapper development mailing list <dm-devel@redhat.com>
+Device-mapper development mailing list <dm-devel@lists.linux.dev>
 
 
 Licence
@@ -229,15 +272,25 @@ To enable ALUA, the following options should be changed:
 
 NVMe
 ====
-To use Device Mapper/multipath-tools with NVMe devices,
-if the Native NVMe Multipath subsystem is enabled
-( "Y" in `/sys/module/nvme_core/parameters/multipath` ),
-it has to be disabled:
-
-`echo "options nvme_core multipath=N" > /etc/modprobe.d/01-nvme_core-mp.conf`,
-regenerate the initramfs (`dracut -f` or `update-initramfs`) and reboot.
-
-Check that it is disabled(N) with:
-`cat /sys/module/nvme_core/parameters/multipath`
-or
-`systool -m nvme_core -A multipath`
+
+Using dm-multipath with NVMe
+----------------------------
+
+NVMe multipath is natively supported by the Linux kernel. If for some reason
+you prefer using device mapper multipath with NVMe devices,
+you need to disable native multipathing first:
+
+    echo "options nvme_core multipath=N" > /etc/modprobe.d/01-nvme_core-mp.conf
+
+Afterwards, regenerate the initramfs (`dracut -f` or `update-initramfs`) and reboot.
+
+Using multipath-tools with native NVMe multipath
+------------------------------------------------
+
+If native NVMe multipathing is enabled, you can still use multipath-tools
+for displaying the topology and some other information about native NVMe
+multipath setups. This feature is disabled by default. To enable it, set
+`enable_foreign nvme` in the `defaults` section of `multipath.conf`.
+Commands like `multipath -ll` will then display information about NVMe
+native multipath. This support is read-only; modifying the native multipath
+configuration is not supported.
index 2a95ec56772aba4e6368fa22977a293afe95f186..4d318b96435e1da9587ef41a379801c5a8040e9f 100644 (file)
@@ -23,7 +23,7 @@ check_cmd = $(shell \
 
 # Check whether a function with name $1 has been declared in header file $2.
 check_func = $(shell \
-       if grep -Eq "^[^[:blank:]]+[[:blank:]]+$1[[:blank:]]*(.*)*" "$2"; then \
+       if grep -Eq "^(extern[[:blank:]]+)?[^[:blank:]]+[[:blank:]]+$1[[:blank:]]*(.*)*" "$2"; then \
                found=1; \
                status="yes"; \
        else \
@@ -73,6 +73,10 @@ TEST_URCU_TYPE_LIMITS = $(shell \
                $(CC) -c -Werror=type-limits -o /dev/null -xc - 2>/dev/null  \
        || echo -Wno-type-limits )
 
+URCU_VERSION = $(shell \
+       $(PKG_CONFIG) --modversion liburcu 2>/dev/null | \
+                       awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }')
+
 DEFINES :=
 
 ifneq ($(call check_func,dm_task_no_flush,$(devmapper_incdir)/libdevmapper.h),0)
@@ -104,17 +108,26 @@ ifneq ($(call check_var,ELS_DTAG_LNK_INTEGRITY,$(kernel_incdir)/scsi/fc/fc_els.h
        FPIN_SUPPORT = 1
 endif
 
+libmount_h := $(shell $(PKG_CONFIG) --variable=includedir mount)/libmount/libmount.h
+ifneq ($(call check_func,mnt_unref_cache,$(libmount_h)),0)
+       DEFINES += LIBMOUNT_HAS_MNT_UNREF_CACHE
+endif
+
+ifneq ($(call check_func,mnt_table_parse_swaps,$(libmount_h)),0)
+       DEFINES += LIBMOUNT_SUPPORTS_SWAP
+endif
+
 ifneq ($(call check_file,$(kernel_incdir)/linux/nvme_ioctl.h),0)
        ANA_SUPPORT := 1
 endif
 
-ENABLE_LIBDMMP := $(call check_cmd,$(PKGCONFIG) --exists json-c)
+ENABLE_LIBDMMP := $(call check_cmd,$(PKG_CONFIG) --exists json-c)
 
 ifeq ($(ENABLE_DMEVENTS_POLL),0)
        DEFINES += -DNO_DMEVENTS_POLL
 endif
 
-SYSTEMD := $(strip $(or $(shell $(PKGCONFIG) --modversion libsystemd 2>/dev/null | awk '{print $$1}'), \
+SYSTEMD := $(strip $(or $(shell $(PKG_CONFIG) --modversion libsystemd 2>/dev/null | awk '{print $$1}'), \
                        $(shell systemctl --version 2>/dev/null | sed -n 's/systemd \([0-9]*\).*/\1/p')))
 
 
@@ -159,6 +172,7 @@ $(TOPDIR)/config.mk:        $(multipathdir)/autoconfig.h
        @echo creating $@
        @echo "FPIN_SUPPORT := $(FPIN_SUPPORT)" >$@
        @echo "FORTIFY_OPT := $(FORTIFY_OPT)" >>$@
+       @echo "D_URCU_VERSION := $(call URCU_VERSION)" >>$@
        @echo "SYSTEMD := $(SYSTEMD)" >>$@
        @echo "ANA_SUPPORT := $(ANA_SUPPORT)" >>$@
        @echo "STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)" >>$@
index b48b67c809d58fcf15cc857a909295a5cddffde9..ef5ff2e6f5b7d88e4caed0af9755a2003e2211ff 100644 (file)
@@ -31,7 +31,7 @@ ENV{DM_UDEV_LOW_PRIORITY_FLAG}!="1", OPTIONS+="link_priority=50"
 IMPORT{program}=="kpartx_id %M %m $env{DM_UUID}"
 
 # DM_TYPE only has a reasonable value for partitions on multipath.
-ENV{DM_UUID}=="*-mpath-*", ENV{DM_TYPE}=="?*", ENV{DM_SERIAL}=="?*" \
+ENV{DM_UUID}=="*-mpath-*", ENV{DM_TYPE}=="?*", ENV{DM_SERIAL}=="?*", \
        SYMLINK+="disk/by-id/$env{DM_TYPE}-$env{DM_SERIAL}-part$env{DM_PART}"
 ENV{DM_WWN}=="?*", ENV{DM_PART}=="?*", \
        SYMLINK+="disk/by-id/wwn-$env{DM_WWN}-part$env{DM_PART}"
index 2b144a7fa11af7994c58def1cc1d3f366e1c9aeb..ef8051a5060b50063ed6d02d91b493433f0dd890 100644 (file)
@@ -1,11 +1,12 @@
 .\" ----------------------------------------------------------------------------
-.\" Update the date below if you make any significant change.
 .\" Make sure there are no errors with:
 .\" groff -z -wall -b -e -t kpartx/kpartx.8
+.\" man --warnings -E UTF-8 -l -Tutf8 -Z  kpartx/kpartx.8 > /dev/null
 .\"
+.\" Update the date below if you make any significant change.
 .\" ----------------------------------------------------------------------------
 .
-.TH KPARTX 8 2019-04-27 "Linux"
+.TH KPARTX 8 2019-04-27 Linux
 .
 .
 .\" ----------------------------------------------------------------------------
index 6d28caf9ad7002bdca1c483df7657283aa265479..172ba045acb946b2b834e62af39a3e87ea05809a 100644 (file)
@@ -14,10 +14,10 @@ HEADERS := libdmmp/libdmmp.h
 
 OBJS := libdmmp.o libdmmp_mp.o libdmmp_pg.o libdmmp_path.o libdmmp_misc.o
 
-CPPFLAGS += -I$(libdmmpdir) -I$(mpathcmddir) $(shell $(PKGCONFIG) --cflags json-c)
+CPPFLAGS += -I$(libdmmpdir) -I$(mpathcmddir) $(shell $(PKG_CONFIG) --cflags json-c)
 CFLAGS += $(LIB_CFLAGS) -fvisibility=hidden
 
-LIBDEPS += $(shell $(PKGCONFIG) --libs json-c) -L$(mpathcmddir) -lmpathcmd -lpthread
+LIBDEPS += $(shell $(PKG_CONFIG) --libs json-c) -L$(mpathcmddir) -lmpathcmd -lpthread
 
 all: $(LIBS) doc
 .PHONY:        doc clean install uninstall check speed_test dep_clean
@@ -44,7 +44,7 @@ install:
                $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
        $(Q)sed -i 's|__INCLUDEDIR__|$(includedir)|g' \
                $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
-       $(Q)$(INSTALL_PROGRAM) -d 755 $(DESTDIR)$(mandir)/man3
+       $(Q)$(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(mandir)/man3
        $(Q)$(INSTALL_PROGRAM) -m 644 -t $(DESTDIR)$(mandir)/man3 docs/man/*.3
 
 uninstall:
@@ -74,8 +74,10 @@ doc: docs/man/dmmp_strerror.3
 docs/man/dmmp_strerror.3:      $(HEADERS)
        $(Q)TEMPFILE=$(shell mktemp); \
        cat $^ | perl docs/doc-preclean.pl >$$TEMPFILE; \
+       [ "$KBUILD_BUILD_TIMESTAMP" ] || \
+           KBUILD_BUILD_TIMESTAMP=`git log -n1 --pretty=%cd --date=iso -- $^`; \
+       export KBUILD_BUILD_TIMESTAMP; \
        LC_ALL=C \
-       KBUILD_BUILD_TIMESTAMP=`git log -n1 --pretty=%cd --date=iso -- $^` \
        perl docs/kernel-doc -man $$TEMPFILE | \
            perl docs/split-man.pl docs/man; \
        $(RM) -f $$TEMPFILE
index 6924b37998855f97580da0e22e4aaf1d66d52d59..178c2f540283dba1099132755794591765a46344 100644 (file)
@@ -733,11 +733,12 @@ int update_map_pr(struct multipath *mpp)
        int noisy=0;
        struct prin_resp *resp;
        unsigned int i;
-       int ret, isFound;
+       int ret = MPATH_PR_OTHER, isFound;
 
        if (!get_be64(mpp->reservation_key))
        {
                /* Nothing to do. Assuming pr mgmt feature is disabled*/
+               mpp->prflag = PRFLAG_UNSET;
                condlog(4, "%s: reservation_key not set in multipath.conf",
                        mpp->alias);
                return MPATH_PR_SUCCESS;
@@ -749,20 +750,27 @@ int update_map_pr(struct multipath *mpp)
                condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
                return MPATH_PR_OTHER;
        }
+       if (count_active_paths(mpp) == 0)
+       {
+               condlog(0,"%s: No available paths to check pr status",
+                       mpp->alias);
+               goto out;
+       }
+       mpp->prflag = PRFLAG_UNSET;
        ret = mpath_prin_activepath(mpp, MPATH_PRIN_RKEY_SA, resp, noisy);
 
        if (ret != MPATH_PR_SUCCESS )
        {
                condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
-               free(resp);
-               return  ret;
+               goto out;
        }
 
+       ret = MPATH_PR_SUCCESS;
+
        if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
        {
                condlog(3,"%s: No key found. Device may not be registered. ", mpp->alias);
-               free(resp);
-               return MPATH_PR_SUCCESS;
+               goto out;
        }
 
        condlog(2, "%s: Multipath  reservation_key: 0x%" PRIx64 " ", mpp->alias,
@@ -783,10 +791,11 @@ int update_map_pr(struct multipath *mpp)
 
        if (isFound)
        {
-               mpp->prflag = 1;
+               mpp->prflag = PRFLAG_SET;
                condlog(2, "%s: prflag flag set.", mpp->alias );
        }
 
+out:
        free(resp);
-       return MPATH_PR_SUCCESS;
+       return ret;
 }
index c168cae855faa0531024ec154b411a1901544474..2f9b8f7991c9baacb836219244d477b8217968e6 100644 (file)
@@ -1,11 +1,12 @@
 .\" ----------------------------------------------------------------------------
-.\" Update the date below if you make any significant change.
 .\" Make sure there are no errors with:
 .\" groff -z -wall -b -e -t libmpathpersist/mpath_persistent_reserve_in.3
+.\" man --warnings -E UTF-8 -l -Tutf8 -Z libmpathpersist/mpath_persistent_reserve_in.3 > /dev/null
 .\"
+.\" Update the date below if you make any significant change.
 .\" ----------------------------------------------------------------------------
 .
-.TH MPATH_PERSISTENT_RESERVE_IN 3 2018-06-15 "Linux"
+.TH MPATH_PERSISTENT_RESERVE_IN 3 2018-06-15 Linux
 .
 .
 .\" ----------------------------------------------------------------------------
index f20be31314d5b179192ddf566e0264c2a43e2f1f..d3204c56ba04fa35609651e2b77cd9f64567ebe8 100644 (file)
@@ -1,11 +1,12 @@
 .\" ----------------------------------------------------------------------------
-.\" Update the date below if you make any significant change.
 .\" Make sure there are no errors with:
 .\" groff -z -wall -b -e -t libmpathpersist/mpath_persistent_reserve_out.3
+.\" man --warnings -E UTF-8 -l -Tutf8 -Z libmpathpersist/mpath_persistent_reserve_out.3 > /dev/null
 .\"
+.\" Update the date below if you make any significant change.
 .\" ----------------------------------------------------------------------------
 .
-.TH MPATH_PERSISTENT_RESERVE_OUT 3 2018-06-15 "Linux"
+.TH MPATH_PERSISTENT_RESERVE_OUT 3 2018-06-15 Linux
 .
 .
 .\" ----------------------------------------------------------------------------
index 4529a82b23d0ef0fc41f5535a4d984f8d7c56e27..36bd777e10086ae1193a5f9bbd4a23a76dfb70ab 100644 (file)
@@ -14,6 +14,9 @@
 #include <mpath_persist.h>
 #include "debug.h"
 #include "mpath_cmd.h"
+#include "vector.h"
+#include "globals.h"
+#include "config.h"
 #include "uxsock.h"
 #include "mpathpr.h"
 
@@ -24,6 +27,12 @@ static int do_update_pr(char *alias, char *cmd, char *key)
        char str[256];
        char *reply;
        int ret = 0;
+       int timeout;
+       struct config *conf;
+
+       conf = get_multipath_config();
+       timeout = conf->uxsock_timeout;
+       put_multipath_config(conf);
 
        fd = mpath_connect();
        if (fd == -1) {
@@ -41,7 +50,7 @@ static int do_update_pr(char *alias, char *cmd, char *key)
                mpath_disconnect(fd);
                return -1;
        }
-       ret = recv_packet(fd, &reply, DEFAULT_REPLY_TIMEOUT);
+       ret = recv_packet(fd, &reply, timeout);
        if (ret < 0) {
                condlog(2, "%s: message=%s recv error=%d", alias, str, errno);
                ret = -1;
index 1238fc9380439717e501bc4d704e4f71763ff2aa..15ff467638b8d514d0405d97cd30c95885395868 100644 (file)
@@ -33,7 +33,7 @@
 
 /*
  * Symbols exported by both libmpathutil and libmultipath
- * libmpathutil exports just dummy symbols, intended to be overriden
+ * libmpathutil exports just dummy symbols, intended to be overridden
  * by those in libmultipath.
  * CAUTION - the version in libmpathutil.version and libmultipath.version
  * must be THE SAME, otherwise the overriding will fail!
@@ -93,12 +93,15 @@ local:
 };
 
 /* symbols referenced internally by libmultipath */
-LIBMPATHUTIL_1.0 {
+LIBMPATHUTIL_2.0 {
        alloc_bitfield;
        __append_strbuf_str;
        append_strbuf_quoted;
        basenamecpy;
+       cleanup_fd_ptr;
        cleanup_free_ptr;
+       cleanup_vector_free;
+       cleanup_fclose;
        filepresent;
        find_keyword;
        free_keywords;
@@ -113,7 +116,6 @@ LIBMPATHUTIL_1.0 {
        log_safe;
        msort;
        parse_devt;
-       parse_prkey;
        process_file;
        safe_write;
        set_value;
@@ -121,15 +123,9 @@ LIBMPATHUTIL_1.0 {
        snprint_keyword;
        steal_strbuf_str;
        strlcat;
-       systemd_service_enabled;
        validate_config_strvec;
        vector_find_or_add_slot;
        vector_insert_slot;
        vector_move_up;
        vector_sort;
 };
-
-LIBMPATHUTIL_1.1 {
-global:
-       cleanup_fd_ptr;
-} LIBMPATHUTIL_1.0;
index 9662e1ed63ae581afbf12497a2360a6253be3cca..9d147fca2541827e9f8c26e0ad0249d9756b3941 100644 (file)
@@ -213,64 +213,6 @@ setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached)
        }
 }
 
-int systemd_service_enabled_in(const char *dev, const char *prefix)
-{
-       static const char service[] = "multipathd.service";
-       char path[PATH_MAX], file[PATH_MAX];
-       DIR *dirfd;
-       struct dirent *d;
-       int found = 0;
-
-       if (safe_sprintf(path, "%s/systemd/system", prefix))
-               return 0;
-
-       condlog(3, "%s: checking for %s in %s", dev, service, path);
-
-       dirfd = opendir(path);
-       if (dirfd == NULL)
-               return 0;
-
-       while ((d = readdir(dirfd)) != NULL) {
-               char *p;
-               struct stat stbuf;
-
-               if ((strcmp(d->d_name,".") == 0) ||
-                   (strcmp(d->d_name,"..") == 0))
-                       continue;
-
-               if (strlen(d->d_name) < 6)
-                       continue;
-
-               p = d->d_name + strlen(d->d_name) - 6;
-               if (strcmp(p, ".wants"))
-                       continue;
-               if (!safe_sprintf(file, "%s/%s/%s",
-                                 path, d->d_name, service)
-                   && stat(file, &stbuf) == 0) {
-                       condlog(3, "%s: found %s", dev, file);
-                       found++;
-                       break;
-               }
-       }
-       closedir(dirfd);
-
-       return found;
-}
-
-int systemd_service_enabled(const char *dev)
-{
-       int found = 0;
-
-       found = systemd_service_enabled_in(dev, "/etc");
-       if (!found)
-               found = systemd_service_enabled_in(dev, "/usr/lib");
-       if (!found)
-               found = systemd_service_enabled_in(dev, "/lib");
-       if (!found)
-               found = systemd_service_enabled_in(dev, "/run");
-       return found;
-}
-
 static int _linux_version_code;
 static pthread_once_t _lvc_initialized = PTHREAD_ONCE_INIT;
 
@@ -386,6 +328,18 @@ void cleanup_mutex(void *arg)
        pthread_mutex_unlock(arg);
 }
 
+void cleanup_vector_free(void *arg)
+{
+       if  (arg)
+               vector_free((vector)arg);
+}
+
+void cleanup_fclose(void *p)
+{
+       if (p)
+               fclose(p);
+}
+
 struct bitfield *alloc_bitfield(unsigned int maxbit)
 {
        unsigned int n;
index 75e20fd83c73d934ea602de20cd875d2fa1b336a..de9fcfdd794fcc2bfd3cd3dbe214a61b4aad0a0f 100644 (file)
@@ -21,7 +21,6 @@ size_t strlcat(char * restrict dst, const char * restrict src, size_t size);
 dev_t parse_devt(const char *dev_t);
 char *convert_dev(char *dev, int is_path_device);
 void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached);
-int systemd_service_enabled(const char *dev);
 int get_linux_version_code(void);
 int safe_write(int fd, const void *buf, size_t count);
 void set_max_fds(rlim_t max_fds);
@@ -48,6 +47,8 @@ int should_exit(void);
 void cleanup_fd_ptr(void *arg);
 void cleanup_free_ptr(void *arg);
 void cleanup_mutex(void *arg);
+void cleanup_vector_free(void *arg);
+void cleanup_fclose(void *p);
 
 struct scandir_result {
        struct dirent **di;
index 3df851e246311daacaf4b91f4bf851c4f195fddc..85767ab4f13dc98a15e3efdffd12d47aa942fcae 100644 (file)
@@ -7,7 +7,7 @@ DEVLIB := libmultipath.so
 CPPFLAGS += -I$(mpathutildir) -I$(mpathcmddir) -I$(nvmedir) -D_GNU_SOURCE $(SYSTEMD_CPPFLAGS)
 CFLAGS += $(LIB_CFLAGS)
 LIBDEPS += -lpthread -ldl -ldevmapper -ludev -L$(mpathutildir) -lmpathutil -L$(mpathcmddir) -lmpathcmd \
-       -lurcu -laio $(SYSTEMD_LIBDEPS)
+       -lmount -lurcu -laio $(SYSTEMD_LIBDEPS)
 
 # object files referencing MULTIPATH_DIR or CONFIG_DIR
 # they need to be recompiled for unit tests
@@ -71,7 +71,7 @@ uninstall:
 clean: dep_clean
        $(Q)$(RM) core *.a *.o *.so *.so.* *.abi nvme-ioctl.c nvme-ioctl.h autoconfig.h $(NV_VERSION_SCRIPT)
 
-include $(wildcard $(OBJS:.o=.d))
+include $(wildcard $(OBJS:.o=.d) $(OBJS-T:.o=.d))
 
 dep_clean:
-       $(Q)$(RM) $(OBJS:.o=.d)
+       $(Q)$(RM) $(OBJS:.o=.d) $(OBJS-T:.o=.d)
index 052012248dfb226d1ea0a7c30c014f63f9dc378e..74431f3ff92826703756a5c594b3d74d833e365e 100644 (file)
@@ -8,6 +8,9 @@
 #include <string.h>
 #include <limits.h>
 #include <stdio.h>
+#include <stdbool.h>
+#include <assert.h>
+#include <sys/inotify.h>
 
 #include "debug.h"
 #include "util.h"
@@ -20,6 +23,8 @@
 #include "config.h"
 #include "devmapper.h"
 #include "strbuf.h"
+#include "time-util.h"
+#include "lock.h"
 
 /*
  * significant parts of this file were taken from iscsi-bindings.c of the
 "# alias wwid\n" \
 "#\n"
 
-static const char bindings_file_header[] = BINDINGS_FILE_HEADER;
+/* uatomic access only */
+static int bindings_file_changed = 1;
+
+static const char bindings_file_path[] = DEFAULT_BINDINGS_FILE;
+
+static pthread_mutex_t timestamp_mutex = PTHREAD_MUTEX_INITIALIZER;
+static struct timespec bindings_last_updated;
+
+struct binding {
+       char *alias;
+       char *wwid;
+};
+
+/*
+ * Perhaps one day we'll implement this more efficiently, thus use
+ * an abstract type.
+ */
+typedef struct _vector Bindings;
+
+/* Protect global_bindings */
+static pthread_mutex_t bindings_mutex = PTHREAD_MUTEX_INITIALIZER;
+static Bindings global_bindings = { .allocated = 0 };
+
+enum {
+       BINDING_EXISTS,
+       BINDING_CONFLICT,
+       BINDING_ADDED,
+       BINDING_DELETED,
+       BINDING_NOTFOUND,
+       BINDING_ERROR,
+};
+
+static void _free_binding(struct binding *bdg)
+{
+       free(bdg->wwid);
+       free(bdg->alias);
+       free(bdg);
+}
+
+static void free_bindings(Bindings *bindings)
+{
+       struct binding *bdg;
+       int i;
+
+       vector_foreach_slot(bindings, bdg, i)
+               _free_binding(bdg);
+       vector_reset(bindings);
+}
+
+static void set_global_bindings(Bindings *bindings)
+{
+       Bindings old_bindings;
+
+       pthread_mutex_lock(&bindings_mutex);
+       old_bindings = global_bindings;
+       global_bindings = *bindings;
+       pthread_mutex_unlock(&bindings_mutex);
+       free_bindings(&old_bindings);
+}
+
+static const struct binding *get_binding_for_alias(const Bindings *bindings,
+                                                  const char *alias)
+{
+       const struct binding *bdg;
+       int i;
+
+       if (!alias)
+               return NULL;
+       vector_foreach_slot(bindings, bdg, i) {
+               if (!strncmp(bdg->alias, alias, WWID_SIZE)) {
+                       condlog(3, "Found matching alias [%s] in bindings file."
+                               " Setting wwid to %s", alias, bdg->wwid);
+                       return bdg;
+               }
+       }
+
+       condlog(3, "No matching alias [%s] in bindings file.", alias);
+       return NULL;
+}
+
+static const struct binding *get_binding_for_wwid(const Bindings *bindings,
+                                                 const char *wwid)
+{
+       const struct binding *bdg;
+       int i;
+
+       if (!wwid)
+               return NULL;
+       vector_foreach_slot(bindings, bdg, i) {
+               if (!strncmp(bdg->wwid, wwid, WWID_SIZE)) {
+                       condlog(3, "Found matching wwid [%s] in bindings file."
+                               " Setting alias to %s", wwid, bdg->alias);
+                       return bdg;
+               }
+       }
+       condlog(3, "No matching wwid [%s] in bindings file.", wwid);
+       return NULL;
+}
+
+/*
+ * Sort order for aliases.
+ *
+ * The "numeric" ordering of aliases for a given prefix P is
+ * Pa, ..., Pz, Paa, ..., Paz, Pba, ... , Pzz, Paaa, ..., Pzzz, Paaaa, ...
+ * We use the fact that for equal prefix, longer strings are always
+ * higher than shorter ones. Strings of equal length are sorted alphabetically.
+ * This is achieved by sorting be length first, then using strcmp().
+ * If multiple prefixes are in use, the aliases with a given prefix will
+ * not necessarily be in a contiguous range of the vector, but they will
+ * be ordered such that for a given prefix, numercally higher aliases will
+ * always be sorted after lower ones.
+ */
+static int alias_compar(const void *p1, const void *p2)
+{
+       const char *alias1 = *((char * const *)p1);
+       const char *alias2 = *((char * const *)p2);
+
+       if (alias1 && alias2) {
+               ssize_t ldif = strlen(alias1) - strlen(alias2);
+
+               if (ldif)
+                       return ldif;
+               return strcmp(alias1, alias2);
+       } else
+               /* Move NULL alias to the end */
+               return alias1 ? -1 : alias2 ? 1 : 0;
+}
+
+static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
+{
+       struct binding *bdg;
+       int i, cmp = 0;
+
+       /*
+        * Keep the bindings array sorted by alias.
+        * Optimization: Search backwards, assuming that the bindings file is
+        * sorted already.
+        */
+       vector_foreach_slot_backwards(bindings, bdg, i) {
+               if ((cmp = alias_compar(&bdg->alias, &alias)) <= 0)
+                       break;
+       }
+
+       /* Check for exact match */
+       if (i >= 0 && cmp == 0)
+               return strcmp(bdg->wwid, wwid) ?
+                       BINDING_CONFLICT : BINDING_EXISTS;
+
+       i++;
+       bdg = calloc(1, sizeof(*bdg));
+       if (bdg) {
+               bdg->wwid = strdup(wwid);
+               bdg->alias = strdup(alias);
+               if (bdg->wwid && bdg->alias &&
+                   vector_insert_slot(bindings, i, bdg))
+                       return BINDING_ADDED;
+               else
+                       _free_binding(bdg);
+       }
+
+       return BINDING_ERROR;
+}
+
+static int delete_binding(Bindings *bindings, const char *wwid)
+{
+       struct binding *bdg;
+       int i;
+
+       vector_foreach_slot(bindings, bdg, i) {
+               if (!strncmp(bdg->wwid, wwid, WWID_SIZE)) {
+                       _free_binding(bdg);
+                       break;
+               }
+       }
+       if (i >= VECTOR_SIZE(bindings))
+               return BINDING_NOTFOUND;
+
+       vector_del_slot(bindings, i);
+       return BINDING_DELETED;
+}
+
+static int write_bindings_file(const Bindings *bindings, int fd,
+                              struct timespec *ts)
+{
+       struct binding *bnd;
+       STRBUF_ON_STACK(content);
+       int i;
+       size_t len;
+
+       if (__append_strbuf_str(&content, BINDINGS_FILE_HEADER,
+                               sizeof(BINDINGS_FILE_HEADER) - 1) == -1)
+               return -1;
+
+       vector_foreach_slot(bindings, bnd, i) {
+               if (print_strbuf(&content, "%s %s\n",
+                                       bnd->alias, bnd->wwid) < 0)
+                       return -1;
+       }
+       len = get_strbuf_len(&content);
+       while (len > 0) {
+               ssize_t n = write(fd, get_strbuf_str(&content), len);
+
+               if (n < 0)
+                       return n;
+               else if (n == 0) {
+                       condlog(2, "%s: short write", __func__);
+                       return -1;
+               }
+               len -= n;
+       }
+       fsync(fd);
+       if (ts) {
+               struct stat st;
+
+               if (fstat(fd, &st) == 0)
+                       *ts = st.st_mtim;
+               else
+                       clock_gettime(CLOCK_REALTIME_COARSE, ts);
+       }
+       return 0;
+}
+
+void handle_bindings_file_inotify(const struct inotify_event *event)
+{
+       const char *base;
+       bool changed = false;
+       struct stat st;
+       struct timespec ts = { 0, 0 };
+       int ret;
+
+       if (!(event->mask & IN_MOVED_TO))
+               return;
+
+       base = strrchr(bindings_file_path, '/');
+       changed = base && !strcmp(base + 1, event->name);
+       ret = stat(bindings_file_path, &st);
+
+       if (!changed)
+               return;
+
+       pthread_mutex_lock(&timestamp_mutex);
+       if (ret == 0) {
+               ts = st.st_mtim;
+               changed = timespeccmp(&ts, &bindings_last_updated) > 0;
+       }
+       pthread_mutex_unlock(&timestamp_mutex);
+
+       if (changed) {
+               uatomic_xchg_int(&bindings_file_changed, 1);
+               condlog(3, "%s: bindings file must be re-read, new timestamp: %ld.%06ld",
+                       __func__, (long)ts.tv_sec, (long)ts.tv_nsec / 1000);
+       } else
+               condlog(3, "%s: bindings file is up-to-date, timestamp: %ld.%06ld",
+                       __func__, (long)ts.tv_sec, (long)ts.tv_nsec / 1000);
+}
+
+static int update_bindings_file(const Bindings *bindings)
+{
+       int rc;
+       int fd = -1;
+       char tempname[PATH_MAX];
+       mode_t old_umask;
+       struct timespec ts;
+
+       if (safe_sprintf(tempname, "%s.XXXXXX", bindings_file_path))
+               return -1;
+       /* coverity: SECURE_TEMP */
+       old_umask = umask(0077);
+       if ((fd = mkstemp(tempname)) == -1) {
+               condlog(1, "%s: mkstemp: %m", __func__);
+               return -1;
+       }
+       umask(old_umask);
+       pthread_cleanup_push(cleanup_fd_ptr, &fd);
+       rc = write_bindings_file(bindings, fd, &ts);
+       pthread_cleanup_pop(1);
+       if (rc == -1) {
+               condlog(1, "failed to write new bindings file");
+               unlink(tempname);
+               return rc;
+       }
+       if ((rc = rename(tempname, bindings_file_path)) == -1)
+               condlog(0, "%s: rename: %m", __func__);
+       else {
+               pthread_mutex_lock(&timestamp_mutex);
+               bindings_last_updated = ts;
+               pthread_mutex_unlock(&timestamp_mutex);
+               condlog(1, "updated bindings file %s", bindings_file_path);
+       }
+       return rc;
+}
 
 int
 valid_alias(const char *alias)
@@ -109,178 +404,78 @@ scan_devname(const char *alias, const char *prefix)
        return n;
 }
 
-static int
-id_already_taken(int id, const char *prefix, const char *map_wwid)
+static bool alias_already_taken(const char *alias, const char *map_wwid)
+{
+
+       char wwid[WWID_SIZE];
+
+       /* If the map doesn't exist, it's fine */
+       if (dm_get_uuid(alias, wwid, sizeof(wwid)) != 0)
+               return false;
+
+       /* If both the name and the wwid match, it's fine.*/
+       if (strncmp(map_wwid, wwid, sizeof(wwid)) == 0)
+               return false;
+
+       condlog(3, "%s: alias '%s' already taken, reselecting alias",
+               map_wwid, alias);
+       return true;
+}
+
+static bool id_already_taken(int id, const char *prefix, const char *map_wwid)
 {
        STRBUF_ON_STACK(buf);
        const char *alias;
 
        if (append_strbuf_str(&buf, prefix) < 0 ||
            format_devname(&buf, id) < 0)
-               return 0;
+               return false;
 
        alias = get_strbuf_str(&buf);
-       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;
+       return alias_already_taken(alias, map_wwid);
 }
 
-
-/*
- * Returns: 0   if matching entry in WWIDs file found
- *         -1   if an error occurs
- *         >0   a free ID that could be used for the WWID at hand
- * *map_alias is set to a freshly allocated string with the matching alias if
- * the function returns 0, or to NULL otherwise.
- */
-static int
-lookup_binding(FILE *f, const char *map_wwid, char **map_alias,
-              const char *prefix, int check_if_taken)
+int get_free_id(const Bindings *bindings, const char *prefix, const char *map_wwid)
 {
-       char buf[LINE_MAX];
-       unsigned int line_nr = 0;
-       int id = 1;
-       int biggest_id = 1;
-       int smallest_bigger_id = INT_MAX;
-
-       *map_alias = NULL;
-
-       rewind(f);
-       while (fgets(buf, LINE_MAX, f)) {
-               const char *alias, *wwid;
-               char *c, *saveptr;
-               int curr_id;
-
-               line_nr++;
-               c = strpbrk(buf, "#\n\r");
-               if (c)
-                       *c = '\0';
-               alias = strtok_r(buf, " \t", &saveptr);
-               if (!alias) /* blank line */
-                       continue;
-               curr_id = scan_devname(alias, prefix);
-               if (curr_id == id) {
-                       if (id < INT_MAX)
-                               id++;
-                       else {
-                               id = -1;
-                               break;
-                       }
-               }
-               if (curr_id > biggest_id)
-                       biggest_id = curr_id;
-               if (curr_id > id && curr_id < smallest_bigger_id)
-                       smallest_bigger_id = curr_id;
-               wwid = strtok_r(NULL, " \t", &saveptr);
-               if (!wwid){
-                       condlog(3,
-                               "Ignoring malformed line %u in bindings file",
-                               line_nr);
+       const struct binding *bdg;
+       int i, id = 1;
+
+       vector_foreach_slot(bindings, bdg, i) {
+               int curr_id = scan_devname(bdg->alias, prefix);
+
+               if (curr_id == -1)
                        continue;
+               if (id > curr_id) {
+                       condlog(0, "%s: ERROR: bindings are not sorted", __func__);
+                       return -1;
                }
-               if (strcmp(wwid, map_wwid) == 0){
-                       condlog(3, "Found matching wwid [%s] in bindings file."
-                               " Setting alias to %s", wwid, alias);
-                       *map_alias = strdup(alias);
-                       if (*map_alias == NULL) {
-                               condlog(0, "Cannot copy alias from bindings "
-                                       "file: out of memory");
-                               return -1;
-                       }
-                       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;
-                       }
+               while (id < curr_id && id_already_taken(id, prefix, map_wwid))
                        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 < curr_id)
+                       return id;
+               id++;
+               if (id <= 0)
+                       break;
        }
-       if (id < 0) {
-               condlog(0, "no more available user_friendly_names");
-               return -1;
-       } else
-               condlog(3, "No matching wwid [%s] in bindings file.", map_wwid);
-       return id;
-}
 
-static int
-rlookup_binding(FILE *f, char *buff, const char *map_alias)
-{
-       char line[LINE_MAX];
-       unsigned int line_nr = 0;
-
-       buff[0] = '\0';
-
-       while (fgets(line, LINE_MAX, f)) {
-               char *c, *saveptr;
-               const char *alias, *wwid;
-
-               line_nr++;
-               c = strpbrk(line, "#\n\r");
-               if (c)
-                       *c = '\0';
-               alias = strtok_r(line, " \t", &saveptr);
-               if (!alias) /* blank line */
-                       continue;
-               wwid = strtok_r(NULL, " \t", &saveptr);
-               if (!wwid){
-                       condlog(3,
-                               "Ignoring malformed line %u in bindings file",
-                               line_nr);
-                       continue;
-               }
-               if (strlen(wwid) > WWID_SIZE - 1) {
-                       condlog(3,
-                               "Ignoring too large wwid at %u in bindings file", line_nr);
-                       continue;
-               }
-               if (strcmp(alias, map_alias) == 0){
-                       condlog(3, "Found matching alias [%s] in bindings file."
-                               " Setting wwid to %s", alias, wwid);
-                       strlcpy(buff, wwid, WWID_SIZE);
-                       return 0;
-               }
+       for (; id > 0; id++) {
+               if (!id_already_taken(id, prefix, map_wwid))
+                       break;
        }
-       condlog(3, "No matching alias [%s] in bindings file.", map_alias);
 
-       return -1;
+       if (id <= 0) {
+               id = -1;
+               condlog(0, "no more available user_friendly_names");
+       }
+       return id;
 }
 
+/* Called with binding_mutex held */
 static char *
-allocate_binding(int fd, const char *wwid, int id, const char *prefix)
+allocate_binding(const char *wwid, int id, const char *prefix)
 {
        STRBUF_ON_STACK(buf);
-       off_t offset;
-       ssize_t len;
-       char *alias, *c;
+       char *alias;
 
        if (id <= 0) {
                condlog(0, "%s: cannot allocate new binding for id %d",
@@ -292,314 +487,217 @@ allocate_binding(int fd, const char *wwid, int id, const char *prefix)
            format_devname(&buf, id) == -1)
                return NULL;
 
-       if (print_strbuf(&buf, " %s\n", wwid) < 0)
-               return NULL;
+       alias = steal_strbuf_str(&buf);
 
-       offset = lseek(fd, 0, SEEK_END);
-       if (offset < 0){
-               condlog(0, "Cannot seek to end of bindings file : %s",
-                       strerror(errno));
+       if (add_binding(&global_bindings, alias, wwid) != BINDING_ADDED) {
+               condlog(0, "%s: cannot allocate new binding %s for %s",
+                       __func__, alias, wwid);
+               free(alias);
                return NULL;
        }
 
-       len = get_strbuf_len(&buf);
-       alias = steal_strbuf_str(&buf);
-
-       if (write(fd, alias, len) != len) {
-               condlog(0, "Cannot write binding to bindings file : %s",
-                       strerror(errno));
-               /* clear partial write */
-               if (ftruncate(fd, offset))
-                       condlog(0, "Cannot truncate the header : %s",
-                               strerror(errno));
+       if (update_bindings_file(&global_bindings) == -1) {
+               condlog(1, "%s: deleting binding %s for %s", __func__, alias, wwid);
+               delete_binding(&global_bindings, wwid);
                free(alias);
                return NULL;
        }
-       c = strchr(alias, ' ');
-       if (c)
-               *c = '\0';
 
        condlog(3, "Created new binding [%s] for WWID [%s]", alias, wwid);
        return alias;
 }
 
-char *
-use_existing_alias (const char *wwid, const char *file, const char *alias_old,
-                   const char *prefix, int bindings_read_only)
+enum {
+       BINDINGS_FILE_UP2DATE,
+       BINDINGS_FILE_READ,
+       BINDINGS_FILE_ERROR,
+       BINDINGS_FILE_BAD,
+};
+
+static int _read_bindings_file(const struct config *conf, Bindings *bindings,
+                              bool force);
+
+static void read_bindings_file(void)
+{
+       struct config *conf;
+       Bindings bindings = {.allocated = 0, };
+       int rc;
+
+       conf = get_multipath_config();
+       pthread_cleanup_push(put_multipath_config, conf);
+       rc = _read_bindings_file(conf, &bindings, false);
+       pthread_cleanup_pop(1);
+       if (rc == BINDINGS_FILE_READ)
+               set_global_bindings(&bindings);
+}
+
+/*
+ * get_user_friendly_alias() action table
+ *
+ * The table shows the various cases, the actions taken, and the CI
+ * functions from tests/alias.c that represent them.
+ *
+ *  - O: old alias given
+ *  - A: old alias in table (y: yes, correct WWID; X: yes, wrong WWID)
+ *  - W: wwid in table
+ *
+ *  | No | O | A | W | action                                     | function gufa_X              |
+ *  |----+---+---+---+--------------------------------------------+------------------------------|
+ *  |  1 | n | - | n | get new alias                              | nomatch_Y                    |
+ *  |  2 | n | - | y | use alias from bindings                    | match_a_Y                    |
+ *  |  3 | y | n | n | add binding for old alias                  | old_nomatch_nowwidmatch      |
+ *  |  4 | y | n | y | use alias from bindings (avoid duplicates) | old_nomatch_wwidmatch        |
+ *  |  5 | y | y | n | [ impossible ]                             | -                            |
+ *  |  6 | y | y | y | use old alias == alias from bindings       | old_match                    |
+ *  |  7 | y | X | n | get new alias                              | old_match_other              |
+ *  |  8 | y | X | y | use alias from bindings                    | old_match_other_wwidmatch    |
+ *
+ * Notes:
+ *  - "use alias from bindings" means that the alias from the bindings file will
+ *    be tried; if it is in use, the alias selection will fail. No other
+ *    bindings will be attempted.
+ *  - "get new alias" fails if all aliases are used up, or if writing the
+ *    bindings file fails.
+ *  - if "alias_old" is set, it can't be bound to a different map. alias_old is
+ *    initialized in find_existing_alias() by scanning the mpvec. We trust
+ *    that the mpvec corrcectly represents kernel state.
+ */
+
+char *get_user_friendly_alias(const char *wwid, const char *alias_old,
+                             const char *prefix, bool bindings_read_only)
 {
        char *alias = NULL;
        int id = 0;
-       int fd, can_write;
-       char buff[WWID_SIZE];
-       FILE *f;
+       bool new_binding = false;
+       const struct binding *bdg;
 
-       fd = open_file(file, &can_write, bindings_file_header);
-       if (fd < 0)
-               return NULL;
+       read_bindings_file();
 
-       f = fdopen(fd, "r");
-       if (!f) {
-               condlog(0, "cannot fdopen on bindings file descriptor");
-               close(fd);
-               return NULL;
-       }
-       /* lookup the binding. if it exists, the wwid will be in buff
-        * either way, id contains the id for the alias
-        */
-       rlookup_binding(f, buff, alias_old);
+       pthread_mutex_lock(&bindings_mutex);
+       pthread_cleanup_push(cleanup_mutex, &bindings_mutex);
 
-       if (strlen(buff) > 0) {
-               /* if buff is our wwid, it's already
-                * allocated correctly
-                */
-               if (strcmp(buff, wwid) == 0)
+       if (!*alias_old)
+               goto new_alias;
+
+       /* See if there's a binding matching both alias_old and wwid */
+       bdg = get_binding_for_alias(&global_bindings, alias_old);
+       if (bdg) {
+               if (!strcmp(bdg->wwid, wwid)) {
                        alias = strdup(alias_old);
-               else {
-                       alias = NULL;
+                       goto out;
+               } else {
                        condlog(0, "alias %s already bound to wwid %s, cannot reuse",
-                               alias_old, buff);
+                               alias_old, bdg->wwid);
+                       goto new_alias;
                }
-               goto out;
-       }
-
-       id = lookup_binding(f, wwid, &alias, NULL, 0);
-       if (alias) {
-               condlog(3, "Use existing binding [%s] for WWID [%s]",
-                       alias, wwid);
-               goto out;
        }
 
        /* allocate the existing alias in the bindings file */
        id = scan_devname(alias_old, prefix);
-       if (id <= 0)
-               goto out;
 
-       if (fflush(f) != 0) {
-               condlog(0, "cannot fflush bindings file stream : %s",
-                       strerror(errno));
+new_alias:
+       /* Check for existing binding of WWID */
+       bdg = get_binding_for_wwid(&global_bindings, wwid);
+       if (bdg) {
+               if (!alias_already_taken(bdg->alias, wwid)) {
+                       condlog(3, "Use existing binding [%s] for WWID [%s]",
+                               bdg->alias, wwid);
+                       alias = strdup(bdg->alias);
+               }
                goto out;
        }
 
-       if (can_write && !bindings_read_only) {
-               alias = allocate_binding(fd, wwid, id, prefix);
-               condlog(0, "Allocated existing binding [%s] for WWID [%s]",
-                       alias, wwid);
-       }
-
-out:
-       pthread_cleanup_push(free, alias);
-       fclose(f);
-       pthread_cleanup_pop(0);
-       return alias;
-}
-
-char *
-get_user_friendly_alias(const char *wwid, const char *file, const char *prefix,
-                       int bindings_read_only)
-{
-       char *alias;
-       int fd, id;
-       FILE *f;
-       int can_write;
-
-       if (!wwid || *wwid == '\0') {
-               condlog(3, "Cannot find binding for empty WWID");
-               return NULL;
-       }
-
-       fd = open_file(file, &can_write, bindings_file_header);
-       if (fd < 0)
-               return NULL;
-
-       f = fdopen(fd, "r");
-       if (!f) {
-               condlog(0, "cannot fdopen on bindings file descriptor : %s",
-                       strerror(errno));
-               close(fd);
-               return NULL;
-       }
-
-       id = lookup_binding(f, wwid, &alias, prefix, 1);
-       if (id < 0) {
-               fclose(f);
-               return NULL;
+       if (id <= 0) {
+               /*
+                * no existing alias was provided, or allocating it
+                * failed. Try a new one.
+                */
+               id = get_free_id(&global_bindings, prefix, wwid);
+               if (id <= 0)
+                       goto out;
+               else
+                       new_binding = true;
        }
 
-       pthread_cleanup_push(free, alias);
+       if (!bindings_read_only && id > 0)
+               alias = allocate_binding(wwid, id, prefix);
 
-       if (fflush(f) != 0) {
-               condlog(0, "cannot fflush bindings file stream : %s",
-                       strerror(errno));
-               free(alias);
-               alias = NULL;
-       } else if (can_write && !bindings_read_only && !alias)
-               alias = allocate_binding(fd, wwid, id, prefix);
-
-       fclose(f);
+       if (alias && !new_binding)
+               condlog(2, "Allocated existing binding [%s] for WWID [%s]",
+                       alias, wwid);
 
-       pthread_cleanup_pop(0);
+out:
+       /* unlock bindings_mutex */
+       pthread_cleanup_pop(1);
        return alias;
 }
 
-int
-get_user_friendly_wwid(const char *alias, char *buff, const char *file)
+int get_user_friendly_wwid(const char *alias, char *buff)
 {
-       int fd, unused;
-       FILE *f;
+       const struct binding *bdg;
+       int rc = -1;
 
        if (!alias || *alias == '\0') {
                condlog(3, "Cannot find binding for empty alias");
                return -1;
        }
 
-       fd = open_file(file, &unused, bindings_file_header);
-       if (fd < 0)
-               return -1;
-
-       f = fdopen(fd, "r");
-       if (!f) {
-               condlog(0, "cannot fdopen on bindings file descriptor : %s",
-                       strerror(errno));
-               close(fd);
-               return -1;
-       }
+       read_bindings_file();
 
-       rlookup_binding(f, buff, alias);
-       if (!strlen(buff)) {
-               fclose(f);
-               return -1;
-       }
-
-       fclose(f);
-       return 0;
+       pthread_mutex_lock(&bindings_mutex);
+       pthread_cleanup_push(cleanup_mutex, &bindings_mutex);
+       bdg = get_binding_for_alias(&global_bindings, alias);
+       if (bdg) {
+               strlcpy(buff, bdg->wwid, WWID_SIZE);
+               rc = 0;
+       } else
+               *buff = '\0';
+       pthread_cleanup_pop(1);
+       return rc;
 }
 
-struct binding {
-       char *alias;
-       char *wwid;
-};
-
-static void _free_binding(struct binding *bdg)
+void cleanup_bindings(void)
 {
-       free(bdg->wwid);
-       free(bdg->alias);
-       free(bdg);
-}
-
-/*
- * Perhaps one day we'll implement this more efficiently, thus use
- * an abstract type.
- */
-typedef struct _vector Bindings;
-
-static void free_bindings(Bindings *bindings)
-{
-       struct binding *bdg;
-       int i;
-
-       vector_foreach_slot(bindings, bdg, i)
-               _free_binding(bdg);
-       vector_reset(bindings);
+       pthread_mutex_lock(&bindings_mutex);
+       free_bindings(&global_bindings);
+       pthread_mutex_unlock(&bindings_mutex);
 }
 
 enum {
-       BINDING_EXISTS,
-       BINDING_CONFLICT,
-       BINDING_ADDED,
-       BINDING_DELETED,
-       BINDING_NOTFOUND,
-       BINDING_ERROR,
+       READ_BINDING_OK,
+       READ_BINDING_SKIP,
 };
 
-static int add_binding(Bindings *bindings, const char *alias, const char *wwid)
-{
-       struct binding *bdg;
-       int i, cmp = 0;
-
-       /*
-        * Keep the bindings array sorted by alias.
-        * Optimization: Search backwards, assuming that the bindings file is
-        * sorted already.
-        */
-       vector_foreach_slot_backwards(bindings, bdg, i) {
-               if ((cmp = strcmp(bdg->alias, alias)) <= 0)
-                       break;
-       }
+static int read_binding(char *line, unsigned int linenr, char **alias,
+                       char **wwid) {
+       char *c, *saveptr;
 
-       /* Check for exact match */
-       if (i >= 0 && cmp == 0)
-               return strcmp(bdg->wwid, wwid) ?
-                       BINDING_CONFLICT : BINDING_EXISTS;
-
-       i++;
-       bdg = calloc(1, sizeof(*bdg));
-       if (bdg) {
-               bdg->wwid = strdup(wwid);
-               bdg->alias = strdup(alias);
-               if (bdg->wwid && bdg->alias &&
-                   vector_insert_slot(bindings, i, bdg))
-                       return BINDING_ADDED;
-               else
-                       _free_binding(bdg);
-       }
-
-       return BINDING_ERROR;
-}
-
-static int write_bindings_file(const Bindings *bindings, int fd)
-{
-       struct binding *bnd;
-       STRBUF_ON_STACK(line);
-       int i;
-
-       if (write(fd, BINDINGS_FILE_HEADER, sizeof(BINDINGS_FILE_HEADER) - 1)
-           != sizeof(BINDINGS_FILE_HEADER) - 1)
-               return -1;
-
-       vector_foreach_slot(bindings, bnd, i) {
-               int len;
-
-               if ((len = print_strbuf(&line, "%s %s\n",
-                                       bnd->alias, bnd->wwid)) < 0)
-                       return -1;
-               if (write(fd, get_strbuf_str(&line), len) != len)
-                       return -1;
-               truncate_strbuf(&line, 0);
-       }
-       return 0;
-}
+       c = strpbrk(line, "#\n\r");
+       if (c)
+               *c = '\0';
 
-static int fix_bindings_file(const struct config *conf,
-                            const Bindings *bindings)
-{
-       int rc;
-       int fd = -1;
-       char tempname[PATH_MAX];
-       mode_t old_umask;
+       *alias = strtok_r(line, " \t", &saveptr);
+       if (!*alias) /* blank line */
+               return READ_BINDING_SKIP;
 
-       if (safe_sprintf(tempname, "%s.XXXXXX", conf->bindings_file))
-               return -1;
-       /* coverity: SECURE_TEMP */
-       old_umask = umask(0077);
-       if ((fd = mkstemp(tempname)) == -1) {
-               condlog(1, "%s: mkstemp: %m", __func__);
-               return -1;
+       *wwid = strtok_r(NULL, " \t", &saveptr);
+       if (!*wwid) {
+               condlog(1, "invalid line %u in bindings file, missing WWID",
+                       linenr);
+               return READ_BINDING_SKIP;
        }
-       umask(old_umask);
-       pthread_cleanup_push(cleanup_fd_ptr, &fd);
-       rc = write_bindings_file(bindings, fd);
-       pthread_cleanup_pop(1);
-       if (rc == -1) {
-               condlog(1, "failed to write new bindings file %s",
-                       tempname);
-               unlink(tempname);
-               return rc;
+       if (strlen(*wwid) > WWID_SIZE - 1) {
+               condlog(3,
+                       "Ignoring too large wwid at %u in bindings file",
+                       linenr);
+               return READ_BINDING_SKIP;
        }
-       if ((rc = rename(tempname, conf->bindings_file)) == -1)
-               condlog(0, "%s: rename: %m", __func__);
-       else
-               condlog(1, "updated bindings file %s", conf->bindings_file);
-       return rc;
+       c = strtok_r(NULL, " \t", &saveptr);
+       if (c)
+               /* This is non-fatal */
+               condlog(1, "invalid line %d in bindings file, extra args \"%s\"",
+                       linenr, c);
+       return READ_BINDING_OK;
 }
 
 static int _check_bindings_file(const struct config *conf, FILE *file,
@@ -610,30 +708,28 @@ static int _check_bindings_file(const struct config *conf, FILE *file,
        char *line = NULL;
        size_t line_len = 0;
        ssize_t n;
-
+       char header[sizeof(BINDINGS_FILE_HEADER)];
+
+       header[sizeof(BINDINGS_FILE_HEADER) - 1] = '\0';
+       if (fread(header, sizeof(BINDINGS_FILE_HEADER) - 1, 1, file) < 1) {
+               condlog(2, "%s: failed to read header from %s", __func__,
+                       bindings_file_path);
+               fseek(file, 0, SEEK_SET);
+               rc = -1;
+       } else if (strcmp(header, BINDINGS_FILE_HEADER)) {
+               condlog(2, "%s: invalid header in %s", __func__,
+                       bindings_file_path);
+               fseek(file, 0, SEEK_SET);
+               rc = -1;
+       }
        pthread_cleanup_push(cleanup_free_ptr, &line);
        while ((n = getline(&line, &line_len, file)) >= 0) {
-               char *c, *alias, *wwid, *saveptr;
+               char *alias, *wwid;
                const char *mpe_wwid;
 
-               linenr++;
-               c = strpbrk(line, "#\n\r");
-               if (c)
-                       *c = '\0';
-               alias = strtok_r(line, " \t", &saveptr);
-               if (!alias) /* blank line */
-                       continue;
-               wwid = strtok_r(NULL, " \t", &saveptr);
-               if (!wwid) {
-                       condlog(1, "invalid line %d in bindings file, missing WWID",
-                               linenr);
+               if (read_binding(line, ++linenr, &alias, &wwid)
+                   == READ_BINDING_SKIP)
                        continue;
-               }
-               c = strtok_r(NULL, " \t", &saveptr);
-               if (c)
-                       /* This is non-fatal */
-                       condlog(1, "invalid line %d in bindings file, extra args \"%s\"",
-                               linenr, c);
 
                mpe_wwid = get_mpe_wwid(conf->mptable, alias);
                if (mpe_wwid && strcmp(mpe_wwid, wwid)) {
@@ -667,27 +763,72 @@ static int _check_bindings_file(const struct config *conf, FILE *file,
        return rc;
 }
 
-static void cleanup_fclose(void *p)
+static int mp_alias_compar(const void *p1, const void *p2)
 {
-       fclose(p);
+       return alias_compar(&((*(struct mpentry * const *)p1)->alias),
+                           &((*(struct mpentry * const *)p2)->alias));
 }
 
-static int alias_compar(const void *p1, const void *p2)
+static int _read_bindings_file(const struct config *conf, Bindings *bindings,
+                              bool force)
 {
-       const char *alias1 = (*(struct mpentry * const *)p1)->alias;
-       const char *alias2 = (*(struct mpentry * const *)p2)->alias;
+       int can_write;
+       int rc = 0, ret, fd;
+       FILE *file;
+       struct stat st;
+       int has_changed = uatomic_xchg_int(&bindings_file_changed, 0);
+
+       if (!force) {
+               if (!has_changed) {
+                       condlog(4, "%s: bindings are unchanged", __func__);
+                       return BINDINGS_FILE_UP2DATE;
+               }
+       }
 
-       if (alias1 && alias2)
-               return strcmp(alias1, alias2);
-       else
-               /* Move NULL alias to the end */
-               return alias1 ? -1 : alias2 ? 1 : 0;
-}
+       fd = open_file(bindings_file_path, &can_write, BINDINGS_FILE_HEADER);
+       if (fd == -1)
+               return BINDINGS_FILE_ERROR;
 
-static void cleanup_vector_free(void *arg)
-{
-       if  (arg)
-               vector_free((vector)arg);
+       file = fdopen(fd, "r");
+       if (file != NULL) {
+               condlog(3, "%s: reading %s", __func__, bindings_file_path);
+
+               pthread_cleanup_push(cleanup_fclose, file);
+               ret = _check_bindings_file(conf, file, bindings);
+               if (ret == 0) {
+                       struct timespec ts;
+
+                       rc = BINDINGS_FILE_READ;
+                       ret = fstat(fd, &st);
+                       if (ret == 0)
+                               ts = st.st_mtim;
+                       else {
+                               condlog(1, "%s: fstat failed (%m), using current time", __func__);
+                               clock_gettime(CLOCK_REALTIME_COARSE, &ts);
+                       }
+                       pthread_mutex_lock(&timestamp_mutex);
+                       bindings_last_updated = ts;
+                       pthread_mutex_unlock(&timestamp_mutex);
+               } else if (ret == -1 && can_write && !conf->bindings_read_only) {
+                       ret = update_bindings_file(bindings);
+                       if (ret == 0)
+                               rc = BINDINGS_FILE_READ;
+                       else
+                               rc = BINDINGS_FILE_BAD;
+               } else {
+                       condlog(0, "ERROR: bad settings in read-only bindings file %s",
+                               bindings_file_path);
+                       rc = BINDINGS_FILE_BAD;
+               }
+               pthread_cleanup_pop(1);
+       } else {
+               condlog(1, "failed to fdopen %s: %m",
+                       bindings_file_path);
+               close(fd);
+               rc = BINDINGS_FILE_ERROR;
+       }
+
+       return rc;
 }
 
 /*
@@ -708,8 +849,7 @@ static void cleanup_vector_free(void *arg)
  */
 int check_alias_settings(const struct config *conf)
 {
-       int can_write;
-       int rc = 0, i, fd;
+       int i, rc;
        Bindings bindings = {.allocated = 0, };
        vector mptable = NULL;
        struct mpentry *mpe;
@@ -721,7 +861,7 @@ int check_alias_settings(const struct config *conf)
        pthread_cleanup_push_cast(free_bindings, &bindings);
        pthread_cleanup_push(cleanup_vector_free, mptable);
 
-       vector_sort(mptable, alias_compar);
+       vector_sort(mptable, mp_alias_compar);
        vector_foreach_slot(mptable, mpe, i) {
                if (!mpe->alias)
                        /*
@@ -742,26 +882,12 @@ int check_alias_settings(const struct config *conf)
        pthread_cleanup_pop(1);
        pthread_cleanup_pop(1);
 
-       pthread_cleanup_push_cast(free_bindings, &bindings);
-       fd = open_file(conf->bindings_file, &can_write, BINDINGS_FILE_HEADER);
-       if (fd != -1) {
-               FILE *file = fdopen(fd, "r");
-
-               if (file != NULL) {
-                       pthread_cleanup_push(cleanup_fclose, file);
-                       rc = _check_bindings_file(conf, file, &bindings);
-                       pthread_cleanup_pop(1);
-                       if (rc == -1 && can_write && !conf->bindings_read_only)
-                               rc = fix_bindings_file(conf, &bindings);
-                       else if (rc == -1)
-                               condlog(0, "ERROR: bad settings in read-only bindings file %s",
-                                       conf->bindings_file);
-               } else {
-                       condlog(1, "failed to fdopen %s: %m",
-                               conf->bindings_file);
-                       close(fd);
-               }
+       rc = _read_bindings_file(conf, &bindings, true);
+
+       if (rc == BINDINGS_FILE_READ) {
+               set_global_bindings(&bindings);
+               rc = 0;
        }
-       pthread_cleanup_pop(1);
+
        return rc;
 }
index dbc950c47501472217f7542fc0650acd54a2e6a8..629e8d566777cacd3e4ce66ca1d09bc298fb4fe6 100644 (file)
@@ -2,15 +2,13 @@
 #define _ALIAS_H
 
 int valid_alias(const char *alias);
-char *get_user_friendly_alias(const char *wwid, const char *file,
-                             const char *prefix,
-                             int bindings_readonly);
-int get_user_friendly_wwid(const char *alias, char *buff, const char *file);
-char *use_existing_alias (const char *wwid, const char *file,
-                         const char *alias_old,
-                         const char *prefix, int bindings_read_only);
+int get_user_friendly_wwid(const char *alias, char *buff);
+char *get_user_friendly_alias(const char *wwid, const char *alias_old,
+                             const char *prefix, bool bindings_read_only);
 
 struct config;
 int check_alias_settings(const struct config *);
-
+void cleanup_bindings(void);
+struct inotify_event;
+void handle_bindings_file_inotify(const struct inotify_event *event);
 #endif /* _ALIAS_H */
index 8d15d2ea5b8a96e77c538367d36cdceca27bde62..75100b20784295c311972371ce1396e7167da880 100644 (file)
@@ -2,6 +2,8 @@
  * Copyright (c) 2004, 2005 Christophe Varoqui
  */
 #include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
 #include <libudev.h>
 
 #include "checkers.h"
@@ -191,6 +193,27 @@ find_blacklist_device (const struct _vector *blist, const char *vendor,
        return 0;
 }
 
+/*
+ * Test if nvme native multipath is enabled. If the sysfs file can't
+ * be accessed, multipath is either disabled at compile time, or no
+ * nvme driver is loaded at all. Thus treat errors as "no".
+ */
+static bool nvme_multipath_enabled(void)
+{
+       static const char fn[] = "/sys/module/nvme_core/parameters/multipath";
+       int fd, len;
+       char buf[2];
+
+       fd = open(fn, O_RDONLY);
+       if (fd == -1)
+               return false;
+
+       len = read(fd, buf, sizeof(buf));
+       close(fd);
+
+       return (len >= 1 && buf[0] == 'Y');
+}
+
 int
 setup_default_blist (struct config * conf)
 {
@@ -198,9 +221,15 @@ setup_default_blist (struct config * conf)
        struct hwentry *hwe;
        int i;
 
-       if (store_ble(conf->blist_devnode, "!^(sd[a-z]|dasd[a-z]|nvme[0-9])", ORIGIN_DEFAULT))
-               return 1;
-
+       if (nvme_multipath_enabled()) {
+               if (store_ble(conf->blist_devnode, "!^(sd[a-z]|dasd[a-z])",
+                             ORIGIN_DEFAULT))
+                       return 1;
+       } else {
+               if (store_ble(conf->blist_devnode, "!^(sd[a-z]|dasd[a-z]|nvme[0-9])",
+                             ORIGIN_DEFAULT))
+                       return 1;
+       }
        if (store_ble(conf->elist_property, "(SCSI_IDENT_|ID_WWN)", ORIGIN_DEFAULT))
                return 1;
 
index 2f3ece00f117d86c5e9632e4b9f46d68c7267b9a..12b8be494dcdc21286ca61621d1a0aa7c325a5e9 100644 (file)
@@ -70,6 +70,7 @@ static struct aio_group *
 add_aio_group(void)
 {
        struct aio_group *aio_grp;
+       int rc;
 
        aio_grp = malloc(sizeof(struct aio_group));
        if (!aio_grp)
@@ -77,9 +78,9 @@ add_aio_group(void)
        memset(aio_grp, 0, sizeof(struct aio_group));
        INIT_LIST_HEAD(&aio_grp->orphans);
 
-       if (io_setup(AIO_GROUP_SIZE, &aio_grp->ioctx) != 0) {
+       if ((rc = io_setup(AIO_GROUP_SIZE, &aio_grp->ioctx)) != 0) {
                LOG(1, "io_setup failed");
-               if (errno == EAGAIN)
+               if (rc == -EAGAIN)
                        LOG(1, "global number of io events too small. Increase fs.aio-max-nr with sysctl");
                free(aio_grp);
                return NULL;
@@ -232,15 +233,15 @@ void libcheck_free (struct checker * c)
                }
        }
 
-       if (ct->running &&
-           (ct->req->state != PATH_PENDING ||
-            io_cancel(ct->aio_grp->ioctx, &ct->req->io, &event) == 0))
+       if (ct->running && ct->req->state != PATH_PENDING)
                ct->running = 0;
        if (!ct->running) {
                free(ct->req->buf);
                free(ct->req);
                ct->aio_grp->holders--;
        } else {
+               /* Currently a no-op */
+               io_cancel(ct->aio_grp->ioctx, &ct->req->io, &event);
                ct->req->state = PATH_REMOVED;
                list_add(&ct->req->node, &ct->aio_grp->orphans);
                check_orphaned_group(ct->aio_grp);
@@ -259,14 +260,13 @@ get_events(struct aio_group *aio_grp, struct timespec *timeout)
        struct timespec *timep = timeout;
 
        do {
-               errno = 0;
                nr = io_getevents(aio_grp->ioctx, 1, 128, events, timep);
                got_events |= (nr > 0);
 
                for (i = 0; i < nr; i++) {
                        struct async_req *req = container_of(events[i].obj, struct async_req, io);
 
-                       LOG(3, "io finished %lu/%lu", events[i].res,
+                       LOG(4, "io finished %lu/%lu", events[i].res,
                            events[i].res2);
 
                        /* got an orphaned request */
@@ -283,8 +283,7 @@ get_events(struct aio_group *aio_grp, struct timespec *timeout)
        } while (nr == 128); /* assume there are more events and try again */
 
        if (nr < 0)
-               LOG(3, "async io getevents returned %i (errno=%s)",
-                   nr, strerror(errno));
+               LOG(4, "async io getevents returned %s", strerror(-nr));
 
        return got_events;
 }
@@ -315,13 +314,13 @@ check_state(int fd, struct directio_context *ct, int sync, int timeout_secs)
        } else {
                struct iocb *ios[1] = { &ct->req->io };
 
-               LOG(3, "starting new request");
+               LOG(4, "starting new request");
                memset(&ct->req->io, 0, sizeof(struct iocb));
                io_prep_pread(&ct->req->io, fd, ct->req->buf,
                              ct->req->blksize, 0);
                ct->req->state = PATH_PENDING;
-               if (io_submit(ct->aio_grp->ioctx, 1, ios) != 1) {
-                       LOG(3, "io_submit error %i", errno);
+               if ((rc = io_submit(ct->aio_grp->ioctx, 1, ios)) != 1) {
+                       LOG(3, "io_submit error %i", -rc);
                        return PATH_UNCHECKED;
                }
        }
@@ -351,16 +350,10 @@ check_state(int fd, struct directio_context *ct, int sync, int timeout_secs)
 
                LOG(3, "abort check on timeout");
 
-               r = io_cancel(ct->aio_grp->ioctx, &ct->req->io, &event);
-               /*
-                * Only reset ct->running if we really
-                * could abort the pending I/O
-                */
-               if (!r)
-                       ct->running = 0;
+               io_cancel(ct->aio_grp->ioctx, &ct->req->io, &event);
                rc = PATH_DOWN;
        } else {
-               LOG(3, "async io pending");
+               LOG(4, "async io pending");
                rc = PATH_PENDING;
        }
 
index 551dc4f04c20e6d149ea289d8172a30e3e22d73e..eb2fca18c287f91ba0015efd38700d78db04b664 100644 (file)
@@ -32,6 +32,7 @@ enum {
        MSG_TUR_RUNNING = CHECKER_FIRST_MSGID,
        MSG_TUR_TIMEOUT,
        MSG_TUR_FAILED,
+       MSG_TUR_TRANSITIONING,
 };
 
 #define _IDX(x) (MSG_ ## x - CHECKER_FIRST_MSGID)
@@ -39,6 +40,7 @@ const char *libcheck_msgtable[] = {
        [_IDX(TUR_RUNNING)] = " still running",
        [_IDX(TUR_TIMEOUT)] = " timed out",
        [_IDX(TUR_FAILED)] = " failed to initialize",
+       [_IDX(TUR_TRANSITIONING)] = " reports path is transitioning",
        NULL,
 };
 
@@ -179,13 +181,20 @@ retry:
                else if (key == 0x2) {
                        /* Not Ready */
                        /* Note: Other ALUA states are either UP or DOWN */
-                       if( asc == 0x04 && ascq == 0x0b){
+                       if (asc == 0x04 && ascq == 0x0b) {
                                /*
                                 * LOGICAL UNIT NOT ACCESSIBLE,
                                 * TARGET PORT IN STANDBY STATE
                                 */
                                *msgid = CHECKER_MSGID_GHOST;
                                return PATH_GHOST;
+                       } else if (asc == 0x04 && ascq == 0x0a) {
+                               /*
+                                * LOGICAL UNIT NOT ACCESSIBLE,
+                                * ASYMMETRIC ACCESS STATE TRANSITION
+                                */
+                               *msgid = MSG_TUR_TRANSITIONING;
+                               return PATH_PENDING;
                        }
                }
                *msgid = CHECKER_MSGID_DOWN;
@@ -350,6 +359,7 @@ int libcheck_check(struct checker * c)
                        condlog(3, "%d:%d : tur checker not finished",
                                major(ct->devt), minor(ct->devt));
                        tur_status = PATH_PENDING;
+                       c->msgid = MSG_TUR_RUNNING;
                } else {
                        /* TUR checker done */
                        ct->thread = 0;
@@ -390,13 +400,17 @@ int libcheck_check(struct checker * c)
                         * It fails only in OOM situations. In this case, return
                         * PATH_UNCHECKED to avoid prematurely failing the path.
                         */
-                       if (libcheck_init(c) != 0)
+                       if (libcheck_init(c) != 0) {
+                               c->msgid = MSG_TUR_FAILED;
                                return PATH_UNCHECKED;
+                       }
                        ((struct tur_checker_context *)c->context)->nr_timeouts = ct->nr_timeouts;
 
-                       if (!uatomic_sub_return(&ct->holders, 1))
+                       if (!uatomic_sub_return(&ct->holders, 1)) {
                                /* It did terminate, eventually */
                                cleanup_context(ct);
+                               ((struct tur_checker_context *)c->context)->nr_timeouts = 0;
+                       }
 
                        ct = c->context;
                } else
@@ -404,7 +418,7 @@ int libcheck_check(struct checker * c)
                /* Start new TUR checker */
                pthread_mutex_lock(&ct->lock);
                tur_status = ct->state = PATH_PENDING;
-               ct->msgid = CHECKER_MSGID_NONE;
+               c->msgid = ct->msgid = MSG_TUR_RUNNING;
                pthread_mutex_unlock(&ct->lock);
                ct->fd = c->fd;
                ct->timeout = c->timeout;
@@ -424,7 +438,7 @@ int libcheck_check(struct checker * c)
                }
                tur_timeout(&tsp);
                pthread_mutex_lock(&ct->lock);
-               if (ct->state == PATH_PENDING)
+               if (ct->state == PATH_PENDING && ct->msgid == MSG_TUR_RUNNING)
                        r = pthread_cond_timedwait(&ct->active, &ct->lock,
                                                   &tsp);
                if (!r) {
@@ -432,7 +446,7 @@ int libcheck_check(struct checker * c)
                        c->msgid = ct->msgid;
                }
                pthread_mutex_unlock(&ct->lock);
-               if (tur_status == PATH_PENDING) {
+               if (tur_status == PATH_PENDING && c->msgid == MSG_TUR_RUNNING) {
                        condlog(4, "%d:%d : tur checker still running",
                                major(ct->devt), minor(ct->devt));
                } else {
index 5c5c07267aba6f5220fa865fc5be3c54de56c38c..b7dbc6f5684308a430cf50d0971da1caa2d19c27 100644 (file)
@@ -452,6 +452,8 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)
        merge_num(retain_hwhandler);
        merge_num(detect_prio);
        merge_num(detect_checker);
+       merge_num(detect_pgpolicy);
+       merge_num(detect_pgpolicy_use_tpg);
        merge_num(deferred_remove);
        merge_num(delay_watch_checks);
        merge_num(delay_wait_checks);
@@ -617,6 +619,8 @@ store_hwe (vector hwtable, struct hwentry * dhwe)
        hwe->retain_hwhandler = dhwe->retain_hwhandler;
        hwe->detect_prio = dhwe->detect_prio;
        hwe->detect_checker = dhwe->detect_checker;
+       hwe->detect_pgpolicy = dhwe->detect_pgpolicy;
+       hwe->detect_pgpolicy_use_tpg = dhwe->detect_pgpolicy_use_tpg;
        hwe->ghost_delay = dhwe->ghost_delay;
        hwe->vpd_vendor_id = dhwe->vpd_vendor_id;
 
@@ -748,15 +752,6 @@ static void _uninit_config(struct config *conf)
        if (conf->hwhandler)
                free(conf->hwhandler);
 
-       if (conf->bindings_file)
-               free(conf->bindings_file);
-
-       if (conf->wwids_file)
-               free(conf->wwids_file);
-
-       if (conf->prkeys_file)
-               free(conf->prkeys_file);
-
        if (conf->prio_name)
                free(conf->prio_name);
 
@@ -918,9 +913,6 @@ int _init_config (const char *file, struct config *conf)
         * 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);
-       conf->prkeys_file = set_default(DEFAULT_PRKEYS_FILE);
        conf->attribute_flags = 0;
        conf->reassign_maps = DEFAULT_REASSIGN_MAPS;
        conf->checkint = CHECKINT_UNDEF;
@@ -1074,12 +1066,6 @@ int _init_config (const char *file, struct config *conf)
        merge_blacklist(conf->elist_wwid);
        merge_blacklist_device(conf->elist_device);
 
-       if (conf->bindings_file == NULL)
-               conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE);
-
-       if (!conf->bindings_file || !conf->wwids_file || !conf->prkeys_file)
-               goto out;
-
        libmp_verbosity = conf->verbosity;
        return 0;
 out:
index 879474696528821240276189db21b6952aee7470..384193ab0c3b0ea6e236119b205cd84979132efe 100644 (file)
@@ -76,6 +76,8 @@ struct hwentry {
        int retain_hwhandler;
        int detect_prio;
        int detect_checker;
+       int detect_pgpolicy;
+       int detect_pgpolicy_use_tpg;
        int deferred_remove;
        int delay_watch_checks;
        int delay_wait_checks;
@@ -160,6 +162,7 @@ struct config {
        int fast_io_fail;
        unsigned int dev_loss;
        int eh_deadline;
+       int max_retries;
        int log_checker_err;
        int allow_queueing;
        int allow_usb_devices;
@@ -171,6 +174,8 @@ struct config {
        int retain_hwhandler;
        int detect_prio;
        int detect_checker;
+       int detect_pgpolicy;
+       int detect_pgpolicy_use_tpg;
        int force_sync;
        int deferred_remove;
        int processed_main_config;
@@ -197,15 +202,13 @@ struct config {
        int skip_delegate;
        unsigned int sequence_nr;
        int recheck_wwid;
+       int auto_resize;
 
        char * selector;
        struct _vector uid_attrs;
        char * uid_attribute;
        char * features;
        char * hwhandler;
-       char * bindings_file;
-       char * wwids_file;
-       char * prkeys_file;
        char * prio_name;
        char * prio_args;
        char * checker_name;
index e551047af50dfc53af5874e11182af3eec5073d6..d80949039680e464397e9cd1a00331a9e4495e69 100644 (file)
@@ -304,6 +304,8 @@ int setup_map(struct multipath *mpp, char **params, struct vectors *vecs)
        pthread_cleanup_push(put_multipath_config, conf);
 
        select_pgfailback(conf, mpp);
+       select_detect_pgpolicy(conf, mpp);
+       select_detect_pgpolicy_use_tpg(conf, mpp);
        select_pgpolicy(conf, mpp);
 
        /*
@@ -593,11 +595,12 @@ sysfs_set_max_sectors_kb(struct multipath *mpp, int is_reload)
        ssize_t len;
        int i, j, ret, err = 0;
        struct udev_device *udd;
-       int max_sectors_kb;
+       int max_sectors_kb = mpp->max_sectors_kb;
 
-       if (mpp->max_sectors_kb == MAX_SECTORS_KB_UNDEF)
+       /* by default, do not initialize max_sectors_kb on the device */
+       if (max_sectors_kb == MAX_SECTORS_KB_UNDEF && !is_reload)
                return 0;
-       max_sectors_kb = mpp->max_sectors_kb;
+       /* on reload, re-apply the user tuning on all the path devices */
        if (is_reload) {
                if (!has_dm_info(mpp) &&
                    dm_get_info(mpp->alias, &mpp->dmi) != 0) {
@@ -670,7 +673,8 @@ static bool is_udev_ready(struct multipath *cmpp)
 static void
 select_reload_action(struct multipath *mpp, const char *reason)
 {
-       mpp->action = ACT_RELOAD;
+       mpp->action = mpp->action == ACT_RENAME ? ACT_RELOAD_RENAME :
+                     ACT_RELOAD;
        condlog(3, "%s: set ACT_RELOAD (%s)", mpp->alias, reason);
 }
 
@@ -681,42 +685,33 @@ void select_action (struct multipath *mpp, const struct _vector *curmp,
        struct multipath * cmpp_by_name;
        char * mpp_feat, * cmpp_feat;
 
+       mpp->action = ACT_NOTHING;
        cmpp = find_mp_by_wwid(curmp, mpp->wwid);
        cmpp_by_name = find_mp_by_alias(curmp, mpp->alias);
        if (mpp->need_reload || (cmpp && cmpp->need_reload))
                force_reload = 1;
 
-       if (!cmpp_by_name) {
-               if (cmpp) {
-                       condlog(2, "%s: rename %s to %s", mpp->wwid,
-                               cmpp->alias, mpp->alias);
-                       strlcpy(mpp->alias_old, cmpp->alias, WWID_SIZE);
-                       mpp->action = ACT_RENAME;
-                       if (force_reload) {
-                               mpp->force_udev_reload = 1;
-                               mpp->action = ACT_FORCERENAME;
-                       }
-                       return;
+       if (!cmpp) {
+               if (cmpp_by_name) {
+                       condlog(1, "%s: can't use alias \"%s\" used by %s, falling back to WWID",
+                               mpp->wwid, mpp->alias, cmpp_by_name->wwid);
+                       /* We can do this because wwid wasn't found */
+                       free(mpp->alias);
+                       mpp->alias = strdup(mpp->wwid);
                }
                mpp->action = ACT_CREATE;
-               condlog(3, "%s: set ACT_CREATE (map does not exist)",
-                       mpp->alias);
+               condlog(3, "%s: set ACT_CREATE (map does not exist%s)",
+                       mpp->alias, cmpp_by_name ? ", name changed" : "");
                return;
        }
 
-       if (!cmpp) {
-               condlog(1, "%s: can't use alias \"%s\" used by %s, falling back to WWID",
-                       mpp->wwid, mpp->alias, cmpp_by_name->wwid);
-               /* We can do this because wwid wasn't found */
-               free(mpp->alias);
-               mpp->alias = strdup(mpp->wwid);
-               mpp->action = ACT_CREATE;
-               condlog(3, "%s: set ACT_CREATE (map does not exist, name changed)",
+       if (!cmpp_by_name) {
+               condlog(2, "%s: rename %s to %s", mpp->wwid, cmpp->alias,
                        mpp->alias);
-               return;
-       }
-
-       if (cmpp != cmpp_by_name) {
+               strlcpy(mpp->alias_old, cmpp->alias, WWID_SIZE);
+               mpp->action = ACT_RENAME;
+               /* don't return here. Check for other needed actions */
+       } else if (cmpp != cmpp_by_name) {
                condlog(2, "%s: unable to rename %s to %s (%s is used by %s)",
                        mpp->wwid, cmpp->alias, mpp->alias,
                        mpp->alias, cmpp_by_name->wwid);
@@ -724,29 +719,27 @@ void select_action (struct multipath *mpp, const struct _vector *curmp,
                free(mpp->alias);
                mpp->alias = strdup(cmpp->alias);
                mpp->action = ACT_IMPOSSIBLE;
-               return;
+               /* don't return here. Check for other needed actions */
        }
 
-       if (force_reload) {
+       if (cmpp->size != mpp->size) {
                mpp->force_udev_reload = 1;
-               mpp->action = ACT_RELOAD;
-               condlog(3, "%s: set ACT_RELOAD (forced by user)",
+               mpp->action = mpp->action == ACT_RENAME ? ACT_RESIZE_RENAME :
+                             ACT_RESIZE;
+               condlog(3, "%s: set ACT_RESIZE (size change)",
                        mpp->alias);
                return;
        }
-       if (cmpp->size != mpp->size) {
+
+       if (force_reload) {
                mpp->force_udev_reload = 1;
-               mpp->action = ACT_RESIZE;
-               condlog(3, "%s: set ACT_RESIZE (size change)",
-                       mpp->alias);
+               select_reload_action(mpp, "forced by user");
                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);
+               select_reload_action(mpp, "udev incomplete");
                return;
        }
 
@@ -808,14 +801,14 @@ void select_action (struct multipath *mpp, const struct _vector *curmp,
                return;
        }
        if (cmpp->nextpg != mpp->bestpg) {
-               mpp->action = ACT_SWITCHPG;
+               mpp->action = mpp->action == ACT_RENAME ? ACT_SWITCHPG_RENAME :
+                             ACT_SWITCHPG;
                condlog(3, "%s: set ACT_SWITCHPG (next path group change)",
                        mpp->alias);
                return;
        }
-       mpp->action = ACT_NOTHING;
-       condlog(3, "%s: set ACT_NOTHING (map unchanged)",
-               mpp->alias);
+       if (mpp->action == ACT_NOTHING)
+               condlog(3, "%s: set ACT_NOTHING (map unchanged)", mpp->alias);
        return;
 }
 
@@ -916,6 +909,17 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
                }
        }
 
+       if (mpp->action == ACT_RENAME || mpp->action == ACT_SWITCHPG_RENAME ||
+           mpp->action == ACT_RELOAD_RENAME ||
+           mpp->action == ACT_RESIZE_RENAME) {
+               conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
+               r = dm_rename(mpp->alias_old, mpp->alias,
+                             conf->partition_delim, mpp->skip_kpartx);
+               pthread_cleanup_pop(1);
+               if (r == DOMAP_FAIL)
+                       return r;
+       }
        switch (mpp->action) {
        case ACT_REJECT:
        case ACT_NOTHING:
@@ -923,6 +927,7 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
                return DOMAP_EXIST;
 
        case ACT_SWITCHPG:
+       case ACT_SWITCHPG_RENAME:
                dm_switchgroup(mpp->alias, mpp->bestpg);
                /*
                 * we may have avoided reinstating paths because there where in
@@ -949,6 +954,7 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
                break;
 
        case ACT_RELOAD:
+       case ACT_RELOAD_RENAME:
                sysfs_set_max_sectors_kb(mpp, 1);
                if (mpp->ghost_delay_tick > 0 && pathcount(mpp, PATH_UP))
                        mpp->ghost_delay_tick = 0;
@@ -956,6 +962,7 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
                break;
 
        case ACT_RESIZE:
+       case ACT_RESIZE_RENAME:
                sysfs_set_max_sectors_kb(mpp, 1);
                if (mpp->ghost_delay_tick > 0 && pathcount(mpp, PATH_UP))
                        mpp->ghost_delay_tick = 0;
@@ -963,29 +970,10 @@ int domap(struct multipath *mpp, char *params, int is_daemon)
                break;
 
        case ACT_RENAME:
-               conf = get_multipath_config();
-               pthread_cleanup_push(put_multipath_config, conf);
-               r = dm_rename(mpp->alias_old, mpp->alias,
-                             conf->partition_delim, mpp->skip_kpartx);
-               pthread_cleanup_pop(1);
-               break;
-
-       case ACT_FORCERENAME:
-               conf = get_multipath_config();
-               pthread_cleanup_push(put_multipath_config, conf);
-               r = dm_rename(mpp->alias_old, mpp->alias,
-                             conf->partition_delim, mpp->skip_kpartx);
-               pthread_cleanup_pop(1);
-               if (r) {
-                       sysfs_set_max_sectors_kb(mpp, 1);
-                       if (mpp->ghost_delay_tick > 0 &&
-                           pathcount(mpp, PATH_UP))
-                               mpp->ghost_delay_tick = 0;
-                       r = dm_addmap_reload(mpp, params, 0);
-               }
                break;
 
        default:
+               r = DOMAP_FAIL;
                break;
        }
 
@@ -1205,13 +1193,13 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
 
                if (cmpp)
                        mpp->queue_mode = cmpp->queue_mode;
+               if (cmd == CMD_DRY_RUN && mpp->action == ACT_UNDEF)
+                       mpp->action = ACT_DRY_RUN;
                if (setup_map(mpp, &params, vecs)) {
                        remove_map(mpp, vecs->pathvec, NULL);
                        continue;
                }
 
-               if (cmd == CMD_DRY_RUN)
-                       mpp->action = ACT_DRY_RUN;
                if (mpp->action == ACT_UNDEF)
                        select_action(mpp, curmp,
                                      force_reload == FORCE_RELOAD_YES ? 1 : 0);
@@ -1273,8 +1261,11 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
        ret = CP_OK;
 out:
        free(size_mismatch_seen);
-       if (!mpvec)
-               free_multipathvec(newmp, KEEP_PATHS);
+       if (!mpvec) {
+               vector_foreach_slot (newmp, mpp, i)
+                       remove_map(mpp, vecs->pathvec, NULL);
+               vector_free(newmp);
+       }
        return ret;
 }
 
@@ -1387,8 +1378,7 @@ static int _get_refwwid(enum mpath_cmds cmd, const char *dev,
                        refwwid = tmpwwid;
 
                /* or may be a binding */
-               else if (get_user_friendly_wwid(dev, tmpwwid,
-                                               conf->bindings_file) == 0)
+               else if (get_user_friendly_wwid(dev, tmpwwid) == 0)
                        refwwid = tmpwwid;
 
                /* or may be an alias */
index 2bf73e651213c4eb301a6469c143f0652d17cf0f..9d935db3fbcd84eb24f0093517f5fb254491c617 100644 (file)
@@ -18,9 +18,11 @@ enum actions {
        ACT_RENAME,
        ACT_CREATE,
        ACT_RESIZE,
-       ACT_FORCERENAME,
+       ACT_RELOAD_RENAME,
        ACT_DRY_RUN,
        ACT_IMPOSSIBLE,
+       ACT_RESIZE_RENAME,
+       ACT_SWITCHPG_RENAME,
 };
 
 /*
index a5e9ea0c0a3eaad3b0b16004ce11780040a2c9a2..64b633f265313ef264b2a1535bad6b84a9740855 100644 (file)
@@ -29,6 +29,8 @@
 #define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_ON
 #define DEFAULT_DETECT_PRIO    DETECT_PRIO_ON
 #define DEFAULT_DETECT_CHECKER DETECT_CHECKER_ON
+#define DEFAULT_DETECT_PGPOLICY        DETECT_PGPOLICY_ON
+#define DEFAULT_DETECT_PGPOLICY_USE_TPG        DETECT_PGPOLICY_USE_TPG_OFF
 #define DEFAULT_DEFERRED_REMOVE        DEFERRED_REMOVE_OFF
 #define DEFAULT_DELAY_CHECKS   NU_NO
 #define DEFAULT_ERR_CHECKS     NU_NO
@@ -54,6 +56,7 @@
 #define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1
 #define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF
 #define DEFAULT_RECHECK_WWID RECHECK_WWID_OFF
+#define DEFAULT_AUTO_RESIZE AUTO_RESIZE_NEVER
 /* Enable no foreign libraries by default */
 #define DEFAULT_ENABLE_FOREIGN "NONE"
 
 #define MAX_DEV_LOSS_TMO       UINT_MAX
 #define DEFAULT_PIDFILE                RUNTIME_DIR "/multipathd.pid"
 #define DEFAULT_SOCKET         "/org/kernel/linux/storage/multipathd"
-#define DEFAULT_CONFIGFILE     "/etc/multipath.conf"
-#define DEFAULT_BINDINGS_FILE  "/etc/multipath/bindings"
-#define DEFAULT_WWIDS_FILE     "/etc/multipath/wwids"
-#define DEFAULT_PRKEYS_FILE    "/etc/multipath/prkeys"
+#define DEFAULT_BINDINGS_FILE  STATE_DIR "/bindings"
+#define DEFAULT_WWIDS_FILE     STATE_DIR "/wwids"
+#define DEFAULT_PRKEYS_FILE    STATE_DIR "/prkeys"
 #define MULTIPATH_SHM_BASE     RUNTIME_DIR "/multipath/"
 
 
index a49db3b09cef92f3cdb792da19ee2d79e4cf4ff2..9be82f4eec9931b1c635bb5417a9e92ab7d0be36 100644 (file)
@@ -706,12 +706,16 @@ dm_get_prefixed_uuid(const char *name, char *uuid, int uuid_len)
 {
        struct dm_task *dmt;
        const char *uuidtmp;
+       struct dm_info info;
        int r = 1;
 
        dmt = libmp_dm_task_create(DM_DEVICE_INFO);
        if (!dmt)
                return 1;
 
+       if (uuid_len > 0)
+               uuid[0] = '\0';
+
        if (!dm_task_set_name (dmt, name))
                goto uuidout;
 
@@ -720,11 +724,13 @@ dm_get_prefixed_uuid(const char *name, char *uuid, int uuid_len)
                goto uuidout;
        }
 
+       if (!dm_task_get_info(dmt, &info) ||
+           !info.exists)
+               goto uuidout;
+
        uuidtmp = dm_task_get_uuid(dmt);
        if (uuidtmp)
                strlcpy(uuid, uuidtmp, uuid_len);
-       else
-               uuid[0] = '\0';
 
        r = 0;
 uuidout:
@@ -1359,7 +1365,6 @@ dm_get_maps (vector mp)
                }
 
                vector_set_slot(mp, mpp);
-               mpp = NULL;
 next:
                next = names->next;
                names = (void *) names + next;
index 2e9b45f90afdc7e44d01328d243253ea14f74bad..0a160e925567a19f174ce5e69c854453ff7505ce 100644 (file)
@@ -168,27 +168,6 @@ fail:
        return 0;
 }
 
-static int
-set_path(vector strvec, void *ptr, const char *file, int line_nr)
-{
-       char **str_ptr = (char **)ptr;
-       char *old_str = *str_ptr;
-
-       *str_ptr = set_value(strvec);
-       if (!*str_ptr) {
-               free(old_str);
-               return 1;
-       }
-       if ((*str_ptr)[0] != '/'){
-               condlog(1, "%s line %d, %s is not an absolute path. Ignoring",
-                       file, line_nr, *str_ptr);
-               free(*str_ptr);
-               *str_ptr = old_str;
-       } else
-               free(old_str);
-       return 0;
-}
-
 static int
 set_str_noslash(vector strvec, void *ptr, const char *file, int line_nr)
 {
@@ -278,11 +257,6 @@ static int print_str(struct strbuf *buff, const char *ptr)
        return ret == -EINVAL ? 0 : ret;
 }
 
-static int print_ignored(struct strbuf *buff)
-{
-       return append_strbuf_quoted(buff, "ignored");
-}
-
 static int print_yes_no(struct strbuf *buff, long v)
 {
        return append_strbuf_quoted(buff, v == YN_NO ? "no" : "yes");
@@ -319,14 +293,16 @@ def_ ## option ## _handler (struct config *conf, vector strvec,           \
 static int deprecated_handler(struct config *conf, vector strvec, const char *file,
                              int line_nr);
 
-#define declare_deprecated_handler(option                            \
+#define declare_deprecated_handler(option, default)                            \
 static int                                                             \
 deprecated_ ## option ## _handler (struct config *conf, vector strvec, \
                                   const char *file, int line_nr)       \
 {                                                                      \
        static bool warned;                                             \
        if (!warned) {                                                  \
-               condlog(1, "%s line %d: ignoring deprecated option \"" #option "\"", file, line_nr); \
+               condlog(1, "%s line %d: ignoring deprecated option \""  \
+                       #option "\", using built-in value: \"%s\"",     \
+                       file, line_nr, default);                        \
                warned = true;                                          \
        }                                                               \
        return deprecated_handler(conf, strvec, file, line_nr);         \
@@ -548,7 +524,6 @@ declare_def_snprint(verbosity, print_int)
 declare_def_handler(reassign_maps, set_yes_no)
 declare_def_snprint(reassign_maps, print_yes_no)
 
-declare_deprecated_handler(multipath_dir)
 
 static int def_partition_delim_handler(struct config *conf, vector strvec,
                                       const char *file, int line_nr)
@@ -835,15 +810,6 @@ declare_hw_snprint(user_friendly_names, print_yes_no_undef)
 declare_mp_handler(user_friendly_names, set_yes_no_undef)
 declare_mp_snprint(user_friendly_names, print_yes_no_undef)
 
-declare_def_warn_handler(bindings_file, set_path)
-declare_def_snprint(bindings_file, print_str)
-
-declare_def_warn_handler(wwids_file, set_path)
-declare_def_snprint(wwids_file, print_str)
-
-declare_def_warn_handler(prkeys_file, set_path)
-declare_def_snprint(prkeys_file, print_str)
-
 declare_def_handler(retain_hwhandler, set_yes_no_undef)
 declare_def_snprint_defint(retain_hwhandler, print_yes_no_undef,
                           DEFAULT_RETAIN_HWHANDLER)
@@ -868,6 +834,22 @@ declare_ovr_snprint(detect_checker, print_yes_no_undef)
 declare_hw_handler(detect_checker, set_yes_no_undef)
 declare_hw_snprint(detect_checker, print_yes_no_undef)
 
+declare_def_handler(detect_pgpolicy, set_yes_no_undef)
+declare_def_snprint_defint(detect_pgpolicy, print_yes_no_undef,
+                          DEFAULT_DETECT_PGPOLICY)
+declare_ovr_handler(detect_pgpolicy, set_yes_no_undef)
+declare_ovr_snprint(detect_pgpolicy, print_yes_no_undef)
+declare_hw_handler(detect_pgpolicy, set_yes_no_undef)
+declare_hw_snprint(detect_pgpolicy, print_yes_no_undef)
+
+declare_def_handler(detect_pgpolicy_use_tpg, set_yes_no_undef)
+declare_def_snprint_defint(detect_pgpolicy_use_tpg, print_yes_no_undef,
+                          DEFAULT_DETECT_PGPOLICY_USE_TPG)
+declare_ovr_handler(detect_pgpolicy_use_tpg, set_yes_no_undef)
+declare_ovr_snprint(detect_pgpolicy_use_tpg, print_yes_no_undef)
+declare_hw_handler(detect_pgpolicy_use_tpg, set_yes_no_undef)
+declare_hw_snprint(detect_pgpolicy_use_tpg, print_yes_no_undef)
+
 declare_def_handler(force_sync, set_yes_no)
 declare_def_snprint(force_sync, print_yes_no)
 
@@ -902,17 +884,6 @@ declare_hw_handler(skip_kpartx, set_yes_no_undef)
 declare_hw_snprint(skip_kpartx, print_yes_no_undef)
 declare_mp_handler(skip_kpartx, set_yes_no_undef)
 declare_mp_snprint(skip_kpartx, print_yes_no_undef)
-static int def_disable_changed_wwids_handler(struct config *conf, vector strvec,
-                                            const char *file, int line_nr)
-{
-       return 0;
-}
-static int snprint_def_disable_changed_wwids(struct config *conf,
-                                            struct strbuf *buff,
-                                            const void *data)
-{
-       return print_ignored(buff);
-}
 
 declare_def_range_handler(remove_retries, 0, INT_MAX)
 declare_def_snprint(remove_retries, print_int)
@@ -934,9 +905,6 @@ declare_def_handler(enable_foreign, set_str)
 declare_def_snprint_defstr(enable_foreign, print_str,
                           DEFAULT_ENABLE_FOREIGN)
 
-declare_deprecated_handler(config_dir)
-declare_deprecated_handler(pg_timeout)
-
 #define declare_def_attr_handler(option, function)                     \
 static int                                                             \
 def_ ## option ## _handler (struct config *conf, vector strvec,                \
@@ -1184,6 +1152,30 @@ declare_hw_snprint(eh_deadline, print_undef_off_zero)
 declare_pc_handler(eh_deadline, set_undef_off_zero)
 declare_pc_snprint(eh_deadline, print_undef_off_zero)
 
+static int
+def_max_retries_handler(struct config *conf, vector strvec, const char *file,
+                       int line_nr)
+{
+       char * buff;
+
+       buff = set_value(strvec);
+       if (!buff)
+               return 1;
+
+       if (strcmp(buff, "off") == 0)
+               conf->max_retries = MAX_RETRIES_OFF;
+       else if (strcmp(buff, "0") == 0)
+               conf->max_retries = MAX_RETRIES_ZERO;
+       else
+               do_set_int(strvec, &conf->max_retries, 1, 5, file, line_nr,
+                          buff);
+
+       free(buff);
+       return 0;
+}
+
+declare_def_snprint(max_retries, print_undef_off_zero)
+
 static int
 set_pgpolicy(vector strvec, void *ptr, const char *file, int line_nr)
 {
@@ -1209,14 +1201,10 @@ set_pgpolicy(vector strvec, void *ptr, const char *file, int line_nr)
 int
 print_pgpolicy(struct strbuf *buff, long pgpolicy)
 {
-       char str[POLICY_NAME_SIZE];
-
        if (!pgpolicy)
                return 0;
 
-       get_pgpolicy_name(str, POLICY_NAME_SIZE, pgpolicy);
-
-       return append_strbuf_quoted(buff, str);
+       return append_strbuf_quoted(buff, get_pgpolicy_name(pgpolicy));
 }
 
 declare_def_handler(pgpolicy, set_pgpolicy)
@@ -1676,6 +1664,43 @@ declare_hw_snprint(recheck_wwid, print_yes_no_undef)
 
 declare_def_range_handler(uxsock_timeout, DEFAULT_REPLY_TIMEOUT, INT_MAX)
 
+static int
+def_auto_resize_handler(struct config *conf, vector strvec, const char *file,
+                       int line_nr)
+{
+       char * buff;
+
+       buff = set_value(strvec);
+       if (!buff)
+               return 1;
+
+       if (strcmp(buff, "never") == 0)
+               conf->auto_resize = AUTO_RESIZE_NEVER;
+       else if (strcmp(buff, "grow_only") == 0)
+               conf->auto_resize = AUTO_RESIZE_GROW_ONLY;
+       else if (strcmp(buff, "grow_shrink") == 0)
+               conf->auto_resize = AUTO_RESIZE_GROW_SHRINK;
+       else
+               condlog(1, "%s line %d, invalid value for auto_resize: \"%s\"",
+                       file, line_nr, buff);
+
+       free(buff);
+       return 0;
+}
+
+int
+print_auto_resize(struct strbuf *buff, long v)
+{
+       if (!v)
+               return 0;
+       return append_strbuf_quoted(buff,
+                       v == AUTO_RESIZE_GROW_ONLY ? "grow_only" :
+                       v == AUTO_RESIZE_GROW_SHRINK ? "grow_shrink" :
+                       "never");
+}
+
+declare_def_snprint(auto_resize, print_auto_resize)
+
 static int
 hw_vpd_vendor_handler(struct config *conf, vector strvec, const char *file,
                      int line_nr)
@@ -2064,7 +2089,15 @@ snprint_deprecated (struct config *conf, struct strbuf *buff, const void * data)
        return 0;
 }
 
-declare_deprecated_handler(getuid_callout)
+// Deprecated keywords
+declare_deprecated_handler(config_dir, CONFIG_DIR)
+declare_deprecated_handler(disable_changed_wwids, "yes")
+declare_deprecated_handler(getuid_callout, "(not set)")
+declare_deprecated_handler(multipath_dir, MULTIPATH_DIR)
+declare_deprecated_handler(pg_timeout, "(not set)")
+declare_deprecated_handler(bindings_file, DEFAULT_BINDINGS_FILE)
+declare_deprecated_handler(wwids_file, DEFAULT_WWIDS_FILE)
+declare_deprecated_handler(prkeys_file, DEFAULT_PRKEYS_FILE)
 
 /*
  * If you add or remove a keyword also update multipath/multipath.conf.5
@@ -2107,15 +2140,18 @@ init_keywords(vector keywords)
        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);
+       install_keyword("max_retries", &def_max_retries_handler, &snprint_def_max_retries);
+       install_keyword("bindings_file", &deprecated_bindings_file_handler, &snprint_deprecated);
+       install_keyword("wwids_file", &deprecated_wwids_file_handler, &snprint_deprecated);
+       install_keyword("prkeys_file", &deprecated_prkeys_file_handler, &snprint_deprecated);
        install_keyword("log_checker_err", &def_log_checker_err_handler, &snprint_def_log_checker_err);
        install_keyword("reservation_key", &def_reservation_key_handler, &snprint_def_reservation_key);
        install_keyword("all_tg_pt", &def_all_tg_pt_handler, &snprint_def_all_tg_pt);
        install_keyword("retain_attached_hw_handler", &def_retain_hwhandler_handler, &snprint_def_retain_hwhandler);
        install_keyword("detect_prio", &def_detect_prio_handler, &snprint_def_detect_prio);
        install_keyword("detect_checker", &def_detect_checker_handler, &snprint_def_detect_checker);
+       install_keyword("detect_pgpolicy", &def_detect_pgpolicy_handler, &snprint_def_detect_pgpolicy);
+       install_keyword("detect_pgpolicy_use_tpg", &def_detect_pgpolicy_use_tpg_handler, &snprint_def_detect_pgpolicy_use_tpg);
        install_keyword("force_sync", &def_force_sync_handler, &snprint_def_force_sync);
        install_keyword("strict_timing", &def_strict_timing_handler, &snprint_def_strict_timing);
        install_keyword("deferred_remove", &def_deferred_remove_handler, &snprint_def_deferred_remove);
@@ -2137,10 +2173,11 @@ init_keywords(vector keywords)
        install_keyword("retrigger_delay", &def_retrigger_delay_handler, &snprint_def_retrigger_delay);
        install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout);
        install_keyword("skip_kpartx", &def_skip_kpartx_handler, &snprint_def_skip_kpartx);
-       install_keyword("disable_changed_wwids", &def_disable_changed_wwids_handler, &snprint_def_disable_changed_wwids);
+       install_keyword("disable_changed_wwids", &deprecated_disable_changed_wwids_handler, &snprint_deprecated);
        install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries);
        install_keyword("max_sectors_kb", &def_max_sectors_kb_handler, &snprint_def_max_sectors_kb);
        install_keyword("ghost_delay", &def_ghost_delay_handler, &snprint_def_ghost_delay);
+       install_keyword("auto_resize", &def_auto_resize_handler, &snprint_def_auto_resize);
        install_keyword("find_multipaths_timeout",
                        &def_find_multipaths_timeout_handler,
                        &snprint_def_find_multipaths_timeout);
@@ -2206,6 +2243,8 @@ init_keywords(vector keywords)
        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);
        install_keyword("detect_checker", &hw_detect_checker_handler, &snprint_hw_detect_checker);
+       install_keyword("detect_pgpolicy", &hw_detect_pgpolicy_handler, &snprint_hw_detect_pgpolicy);
+       install_keyword("detect_pgpolicy_use_tpg", &hw_detect_pgpolicy_use_tpg_handler, &snprint_hw_detect_pgpolicy_use_tpg);
        install_keyword("deferred_remove", &hw_deferred_remove_handler, &snprint_hw_deferred_remove);
        install_keyword("delay_watch_checks", &hw_delay_watch_checks_handler, &snprint_hw_delay_watch_checks);
        install_keyword("delay_wait_checks", &hw_delay_wait_checks_handler, &snprint_hw_delay_wait_checks);
@@ -2248,6 +2287,8 @@ init_keywords(vector keywords)
        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);
        install_keyword("detect_checker", &ovr_detect_checker_handler, &snprint_ovr_detect_checker);
+       install_keyword("detect_pgpolicy", &ovr_detect_pgpolicy_handler, &snprint_ovr_detect_pgpolicy);
+       install_keyword("detect_pgpolicy_use_tpg", &ovr_detect_pgpolicy_use_tpg_handler, &snprint_ovr_detect_pgpolicy_use_tpg);
        install_keyword("deferred_remove", &ovr_deferred_remove_handler, &snprint_ovr_deferred_remove);
        install_keyword("delay_watch_checks", &ovr_delay_watch_checks_handler, &snprint_ovr_delay_watch_checks);
        install_keyword("delay_wait_checks", &ovr_delay_wait_checks_handler, &snprint_ovr_delay_wait_checks);
index 15d9cbac471e269d4c08f4f71763b075a86120b1..7e2dfbe0b648fb051372daacacc221ba1ebdcd76 100644 (file)
@@ -17,4 +17,5 @@ int print_no_path_retry(struct strbuf *buff, long v);
 int print_undef_off_zero(struct strbuf *buff, long v);
 int print_dev_loss(struct strbuf *buff, unsigned long v);
 int print_off_int_undef(struct strbuf *buff, long v);
+int print_auto_resize(struct strbuf *buff, long v);
 #endif /* _DICT_H */
index d9ee2cb96a753b831de96147f522aa7d2eed7eab..6fd4dabb9fe0212cbb5b8c9db672e627ede03f72 100644 (file)
@@ -614,6 +614,43 @@ sysfs_set_eh_deadline(struct path *pp)
        return (ret <= 0);
 }
 
+static int
+sysfs_set_max_retries(struct config *conf, struct path *pp)
+{
+       struct udev_device *parent;
+       char value[16];
+       STRBUF_ON_STACK(buf);
+       int ret, len;
+
+       if (conf->max_retries == MAX_RETRIES_UNSET)
+               return 0;
+
+       if (!pp->udev || pp->sg_id.host_no < 0)
+               return 1;
+
+       len = sprintf(value, "%d", (conf->max_retries == MAX_RETRIES_OFF)? -1 :
+                                  (conf->max_retries == MAX_RETRIES_ZERO)? 0 :
+                                  conf->max_retries);
+
+       parent = udev_device_get_parent_with_subsystem_devtype(pp->udev,
+                       "scsi", "scsi_device");
+       if (!parent)
+               return 1;
+
+       if (print_strbuf(&buf, "scsi_disk/%i:%i:%i:%" PRIu64 "/max_retries",
+                        pp->sg_id.host_no, pp->sg_id.channel,
+                        pp->sg_id.scsi_id, pp->sg_id.lun) < 0)
+               return 1;
+
+       ret = sysfs_attr_set_value(parent, get_strbuf_str(&buf), value, len);
+       if (len != ret)
+               log_sysfs_attr_set_value(3, ret,
+                                        "%s/%s: failed to set value to %s",
+                                        udev_device_get_sysname(parent),
+                                        get_strbuf_str(&buf), value);
+       return (len != ret);
+}
+
 static void
 sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp)
 {
@@ -857,6 +894,9 @@ sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp)
        bool warn_dev_loss = false;
        bool warn_fast_io_fail = false;
 
+       if (mpp->action == ACT_DRY_RUN || mpp->action == ACT_REJECT)
+               return 0;
+
        if (mpp->no_path_retry > 0) {
                uint64_t no_path_retry_tmo =
                        (uint64_t)mpp->no_path_retry * conf->checkint;
@@ -875,7 +915,8 @@ sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp)
 
                if (pp->dev_loss == DEV_LOSS_TMO_UNSET &&
                    pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET &&
-                   pp->eh_deadline == EH_DEADLINE_UNSET)
+                   pp->eh_deadline == EH_DEADLINE_UNSET &&
+                   conf->max_retries == MAX_RETRIES_UNSET)
                        continue;
 
                if (pp->bus != SYSFS_BUS_SCSI) {
@@ -883,6 +924,7 @@ sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp)
                        continue;
                }
                sysfs_set_eh_deadline(pp);
+               sysfs_set_max_retries(conf, pp);
 
                if (pp->dev_loss == DEV_LOSS_TMO_UNSET &&
                    pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
@@ -895,10 +937,11 @@ sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp)
                        continue;
                }
 
-               if (pp->dev_loss != DEV_LOSS_TMO_UNSET &&
-                   pp->dev_loss < min_dev_loss) {
-                       warn_dev_loss = true;
+               if (pp->dev_loss == DEV_LOSS_TMO_UNSET)
+                       pp->dev_loss = min_dev_loss;
+               else if (pp->dev_loss < min_dev_loss) {
                        pp->dev_loss = min_dev_loss;
+                       warn_dev_loss = true;
                }
                if (pp->dev_loss != DEV_LOSS_TMO_UNSET &&
                    pp->fast_io_fail > 0 &&
@@ -1014,18 +1057,13 @@ detect_alua(struct path * pp)
 {
        int ret;
        int tpgs;
-       unsigned int timeout;
-
 
        if (pp->bus != SYSFS_BUS_SCSI) {
                pp->tpgs = TPGS_NONE;
                return;
        }
 
-       if (sysfs_get_timeout(pp, &timeout) <= 0)
-               timeout = DEF_TIMEOUT;
-
-       tpgs = get_target_port_group_support(pp, timeout);
+       tpgs = get_target_port_group_support(pp);
        if (tpgs == -RTPG_INQUIRY_FAILED)
                return;
        else if (tpgs <= 0) {
@@ -1036,8 +1074,8 @@ detect_alua(struct path * pp)
        if (pp->fd == -1 || pp->offline)
                return;
 
-       ret = get_target_port_group(pp, timeout);
-       if (ret < 0 || get_asymmetric_access_state(pp, ret, timeout) < 0) {
+       ret = get_target_port_group(pp);
+       if (ret < 0 || get_asymmetric_access_state(pp, ret) < 0) {
                int state;
 
                if (ret == -RTPG_INQUIRY_FAILED)
@@ -1051,6 +1089,7 @@ detect_alua(struct path * pp)
                return;
        }
        pp->tpgs = tpgs;
+       pp->tpg_id = ret;
 }
 
 int path_get_tpgs(struct path *pp)
@@ -1211,6 +1250,17 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
                        invalid = (d[3] < 8);
                        new_prio = 2;
                        break;
+               case 0x6:
+                       /* Logical Unit Group */
+                       invalid = (d[3] != 4);
+                       break;
+               case 0x7:
+                       /* MD5 logical unit designator */
+                       invalid = (d[3] != 16);
+                       break;
+               case 0x0:
+                       /* Vendor Specific */
+                       break;
                case 0xa:
                        condlog(2, "%s: UUID identifiers not yet supported",
                                __func__);
@@ -1472,6 +1522,7 @@ scsi_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
 {
        struct udev_device *parent;
        const char *attr_path = NULL;
+       static const char unknown[] = "UNKNOWN";
 
        parent = pp->udev;
        while (parent) {
@@ -1492,19 +1543,22 @@ scsi_sysfs_pathinfo (struct path *pp, const struct _vector *hwtable)
        if (!attr_path || pp->sg_id.host_no == -1)
                return PATHINFO_FAILED;
 
-       if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0)
-               return PATHINFO_FAILED;;
-
+       if (sysfs_get_vendor(parent, pp->vendor_id, SCSI_VENDOR_SIZE) <= 0) {
+               condlog(1, "%s: broken device without vendor ID", pp->dev);
+               strlcpy(pp->vendor_id, unknown, SCSI_VENDOR_SIZE);
+       }
        condlog(3, "%s: vendor = %s", pp->dev, pp->vendor_id);
 
-       if (sysfs_get_model(parent, pp->product_id, PATH_PRODUCT_SIZE) <= 0)
-               return PATHINFO_FAILED;;
-
+       if (sysfs_get_model(parent, pp->product_id, PATH_PRODUCT_SIZE) <= 0) {
+               condlog(1, "%s: broken device without product ID", pp->dev);
+               strlcpy(pp->product_id, unknown, PATH_PRODUCT_SIZE);
+       }
        condlog(3, "%s: product = %s", pp->dev, pp->product_id);
 
-       if (sysfs_get_rev(parent, pp->rev, PATH_REV_SIZE) < 0)
-               return PATHINFO_FAILED;;
-
+       if (sysfs_get_rev(parent, pp->rev, PATH_REV_SIZE) < 0) {
+               condlog(2, "%s: broken device without revision", pp->dev);
+               strlcpy(pp->rev, unknown, PATH_REV_SIZE);
+       }
        condlog(3, "%s: rev = %s", pp->dev, pp->rev);
 
        /*
@@ -1948,9 +2002,6 @@ get_state (struct path * pp, struct config *conf, int daemon, int oldstate)
                checker_set_async(c);
        else
                checker_set_sync(c);
-       if (!conf->checker_timeout &&
-           sysfs_get_timeout(pp, &(c->timeout)) <= 0)
-               c->timeout = DEF_TIMEOUT;
        state = checker_check(c, oldstate);
        condlog(3, "%s: %s state = %s", pp->dev,
                checker_name(c), checker_state_name(state));
@@ -1962,7 +2013,7 @@ get_state (struct path * pp, struct config *conf, int daemon, int oldstate)
 }
 
 static int
-get_prio (struct path * pp, int timeout)
+get_prio (struct path * pp)
 {
        struct prio * p;
        struct config *conf;
@@ -1985,7 +2036,7 @@ get_prio (struct path * pp, int timeout)
                }
        }
        old_prio = pp->priority;
-       pp->priority = prio_getprio(p, pp, timeout);
+       pp->priority = prio_getprio(p, pp);
        if (pp->priority < 0) {
                /* this changes pp->offline, but why not */
                int state = path_offline(pp);
@@ -2089,7 +2140,7 @@ get_udev_uid(struct path * pp, const char *uid_attribute, struct udev_device *ud
        const char *value;
 
        value = udev_device_get_property_value(udev, uid_attribute);
-       if (!value || strlen(value) == 0)
+       if ((!value || strlen(value) == 0) && pp->can_use_env_uid)
                value = getenv(uid_attribute);
        if (value && strlen(value)) {
                len = strlcpy(pp->wwid, value, WWID_SIZE);
@@ -2463,8 +2514,7 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
          */
        if ((mask & DI_PRIO) && path_state == PATH_UP && strlen(pp->wwid)) {
                if (pp->state != PATH_DOWN || pp->priority == PRIO_UNDEF) {
-                       get_prio(pp, (pp->state != PATH_DOWN)?
-                                    (conf->checker_timeout * 1000) : 10);
+                       get_prio(pp);
                }
        }
 
index 3c4f866a9e6357a87f26cb7569c3afbfd0a1f288..ae6aac791ee4ecd7ee937c9852532fbf8b76b91e 100644 (file)
@@ -67,6 +67,8 @@
                .retain_hwhandler = RETAIN_HWHANDLER_ON,
                .detect_prio   = DETECT_PRIO_ON,
                .detect_checker = DETECT_CHECKER_ON,
+               .detect_pgpolicy = DETECT_PGPOLICY_ON,
+               .detect_pgpolicy_use_tpg = DETECT_PGPOLICY_USE_TPG_OFF,
                .deferred_remove = DEFERRED_REMOVE_OFF,
                .delay_watch_checks = DELAY_CHECKS_OFF,
                .delay_wait_checks = DELAY_CHECKS_OFF,
@@ -485,7 +487,9 @@ static struct hwentry default_hw[] = {
                /* USP-V, HUS VM, VSP, VSP G1X00 and VSP GX00 families / HPE XP */
                .vendor        = "(HITACHI|HP|HPE)",
                .product       = "^OPEN-",
-               .pgpolicy      = MULTIBUS,
+               .pgpolicy      = GROUP_BY_PRIO,
+               .pgfailback    = -FAILBACK_IMMEDIATE,
+               .no_path_retry = 10,
        },
        {
                /* AMS other than AMS 2000 */
@@ -1067,6 +1071,8 @@ static struct hwentry default_hw[] = {
                .pgfailback    = -FAILBACK_IMMEDIATE,
                .no_path_retry = 12,
                .prio_name     = PRIO_ALUA,
+               .checker_name  = DIRECTIO,
+               .detect_checker = DETECT_CHECKER_OFF,
        },
        /*
         * DataCore
index dc1c2521da966b7c983e15b86d5e27cd41467917..1c594451735781b0012626185764f03d91b9e267 100644 (file)
@@ -19,7 +19,6 @@
 #include <sys/ioctl.h>
 #include <linux/fs.h>
 #include <libaio.h>
-#include <errno.h>
 #include <sys/mman.h>
 #include <sys/select.h>
 
@@ -38,6 +37,7 @@
 #define TIMEOUT_NO_IO_NSEC             10000000 /*10ms = 10000000ns*/
 #define FLAKY_PATHFAIL_THRESHOLD       2
 #define CONCUR_NR_EVENT                        32
+#define NR_IOSTAT_PATHS                        32
 
 #define PATH_IO_ERR_IN_CHECKING                -1
 #define PATH_IO_ERR_WAITING_TO_CHECK   -2
@@ -111,10 +111,14 @@ static int init_each_dio_ctx(struct dio_ctx *ct, int blksize,
        return 0;
 }
 
-static void deinit_each_dio_ctx(struct dio_ctx *ct)
+static int deinit_each_dio_ctx(struct dio_ctx *ct)
 {
-       if (ct->buf)
-               free(ct->buf);
+       if (!ct->buf)
+               return 0;
+       if (ct->io_starttime.tv_sec != 0 || ct->io_starttime.tv_nsec != 0)
+               return 1;
+       free(ct->buf);
+       return 0;
 }
 
 static int setup_directio_ctx(struct io_err_stat_path *p)
@@ -164,17 +168,21 @@ fail_close:
 static void free_io_err_stat_path(struct io_err_stat_path *p)
 {
        int i;
+       int inflight = 0;
 
        if (!p)
                return;
        if (!p->dio_ctx_array)
                goto free_path;
 
-       cancel_inflight_io(p);
-
        for (i = 0; i < CONCUR_NR_EVENT; i++)
-               deinit_each_dio_ctx(p->dio_ctx_array + i);
-       free(p->dio_ctx_array);
+               inflight += deinit_each_dio_ctx(p->dio_ctx_array + i);
+
+       if (!inflight)
+               free(p->dio_ctx_array);
+       else
+               io_err_stat_log(2, "%s: can't free aio space of %s, %d IOs in flight",
+                               __func__, p->devname, inflight);
 
        if (p->fd > 0)
                close(p->fd);
@@ -211,6 +219,15 @@ static void free_io_err_pathvec(void)
        pthread_cleanup_push(cleanup_mutex, &io_err_pathvec_lock);
        if (!io_err_pathvec)
                goto out;
+
+       /* io_cancel() is a noop, but maybe in the future it won't be */
+       vector_foreach_slot(io_err_pathvec, path, i) {
+               if (path && path->dio_ctx_array)
+                       cancel_inflight_io(path);
+       }
+
+       /* This blocks until all I/O is finished */
+       io_destroy(ioctx);
        vector_foreach_slot(io_err_pathvec, path, i)
                free_io_err_stat_path(path);
        vector_free(io_err_pathvec);
@@ -451,7 +468,7 @@ static void end_io_err_stat(struct io_err_stat_path *pp)
 
 static int send_each_async_io(struct dio_ctx *ct, int fd, char *dev)
 {
-       int rc = -1;
+       int rc;
 
        if (ct->io_starttime.tv_nsec == 0 &&
                        ct->io_starttime.tv_sec == 0) {
@@ -459,15 +476,15 @@ static int send_each_async_io(struct dio_ctx *ct, int fd, char *dev)
 
                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",
-                                       dev, errno);
-                       return rc;
+               if ((rc = io_submit(ioctx, 1, ios)) != 1) {
+                       io_err_stat_log(2, "%s: io_submit error %s",
+                                       dev, strerror(-rc));
+                       return -1;
                }
-               rc = 0;
+               return 0;
        }
 
-       return rc;
+       return -1;
 }
 
 static void send_batch_async_ios(struct io_err_stat_path *pp)
@@ -503,7 +520,7 @@ static int try_to_cancel_timeout_io(struct dio_ctx *ct, struct timespec *t,
        int             rc = PATH_UNCHECKED;
        int             r;
 
-       if (ct->io_starttime.tv_sec == 0)
+       if (ct->io_starttime.tv_sec == 0 && ct->io_starttime.tv_nsec == 0)
                return rc;
        timespecsub(t, &ct->io_starttime, &difftime);
        if (difftime.tv_sec > IOTIMEOUT_SEC) {
@@ -512,10 +529,8 @@ static int try_to_cancel_timeout_io(struct dio_ctx *ct, struct timespec *t,
                io_err_stat_log(5, "%s: abort check on timeout", dev);
                r = io_cancel(ioctx, ios[0], &event);
                if (r)
-                       io_err_stat_log(5, "%s: io_cancel error %i",
-                                       dev, errno);
-               ct->io_starttime.tv_sec = 0;
-               ct->io_starttime.tv_nsec = 0;
+                       io_err_stat_log(5, "%s: io_cancel error %s",
+                                       dev, strerror(-r));
                rc = PATH_TIMEOUT;
        } else {
                rc = PATH_PENDING;
@@ -544,7 +559,7 @@ static void poll_async_io_timeout(void)
 static void cancel_inflight_io(struct io_err_stat_path *pp)
 {
        struct io_event event;
-       int i, r;
+       int i;
 
        for (i = 0; i < CONCUR_NR_EVENT; i++) {
                struct dio_ctx *ct = pp->dio_ctx_array + i;
@@ -555,12 +570,7 @@ static void cancel_inflight_io(struct io_err_stat_path *pp)
                        continue;
                io_err_stat_log(5, "%s: abort infligh io",
                                pp->devname);
-               r = io_cancel(ioctx, ios[0], &event);
-               if (r)
-                       io_err_stat_log(5, "%s: io_cancel error %d, %i",
-                                       pp->devname, r, errno);
-               ct->io_starttime.tv_sec = 0;
-               ct->io_starttime.tv_nsec = 0;
+               io_cancel(ioctx, ios[0], &event);
        }
 }
 
@@ -596,12 +606,11 @@ static void process_async_ios_event(int timeout_nsecs, char *dev)
        int             i, n;
        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)",
-                               dev, n, strerror(errno));
+               io_err_stat_log(3, "%s: io_getevents returned %s",
+                               dev, strerror(-n));
        } else {
                for (i = 0; i < n; i++)
                        handle_async_io_done_event(&events[i]);
@@ -690,8 +699,9 @@ int start_io_err_stat_thread(void *data)
        if (uatomic_read(&io_err_thread_running) == 1)
                return 0;
 
-       if (io_setup(CONCUR_NR_EVENT, &ioctx) != 0) {
-               io_err_stat_log(4, "io_setup failed");
+       if ((ret = io_setup(NR_IOSTAT_PATHS * CONCUR_NR_EVENT, &ioctx)) != 0) {
+               io_err_stat_log(1, "io_setup failed: %s, increase /proc/sys/fs/aio-nr ?",
+                               strerror(-ret));
                return 1;
        }
 
@@ -746,5 +756,4 @@ void stop_io_err_stat_thread(void)
 
        pthread_join(io_err_stat_thr, NULL);
        free_io_err_pathvec();
-       io_destroy(ioctx);
 }
index faef2a2de0072602a005d266ec9e62ff401d115b..8ab7c80215e31c366174171084042cd657c53a3e 100644 (file)
@@ -33,7 +33,7 @@
 
 /*
  * Symbols exported by both libmpathutil and libmultipath
- * libmpathutil exports just dummy symbols, intended to be overriden
+ * libmpathutil exports just dummy symbols, intended to be overridden
  * by those in libmultipath.
  * CAUTION - the version in libmpathutil.version and libmultipath.version
  * must be THE SAME, otherwise the overriding will fail!
@@ -43,7 +43,7 @@ LIBMPATHCOMMON_1.0.0 {
        put_multipath_config;
 };
 
-LIBMULTIPATH_17.0.0 {
+LIBMULTIPATH_22.0.0 {
 global:
        /* symbols referenced by multipath and multipathd */
        add_foreign;
@@ -64,6 +64,7 @@ global:
        checker_name;
        checker_state_name;
        check_foreign;
+       cleanup_bindings;
        cleanup_lock;
        coalesce_paths;
        count_active_paths;
@@ -120,6 +121,7 @@ global:
        get_used_hwes;
        get_vpd_sgio;
        group_by_prio;
+       handle_bindings_file_inotify;
        has_dm_info;
        init_checkers;
        init_config;
@@ -214,7 +216,7 @@ global:
 
        /* prioritizers */
        get_asymmetric_access_state;
-       get_prio_timeout;
+       get_prio_timeout_ms;
        get_target_port_group;
        get_target_port_group_support;
        libmp_nvme_ana_log;
index 9814be76f5cb5ea5e9edaadfebe252a4e69150f8..ac80d1d85f0b9a037d78f41b92c6a615becf3acf 100644 (file)
@@ -13,15 +13,20 @@ struct mutex_lock {
        int waiters; /* uatomic access only */
 };
 
-#if !defined(__GLIBC__) && defined(__GNUC__) && __GNUC__ == 12
+static inline void init_lock(struct mutex_lock *a)
+{
+       pthread_mutex_init(&a->mutex, NULL);
+       uatomic_set(&a->waiters, 0);
+}
+
+#if defined(__GNUC__) && __GNUC__ == 12 && URCU_VERSION < 0xe00
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Warray-bounds"
 #endif
 
-static inline void init_lock(struct mutex_lock *a)
+static inline int uatomic_xchg_int(int *ptr, int val)
 {
-       pthread_mutex_init(&a->mutex, NULL);
-       uatomic_set(&a->waiters, 0);
+       return uatomic_xchg(ptr, val);
 }
 
 static inline void lock(struct mutex_lock *a)
@@ -31,6 +36,10 @@ static inline void lock(struct mutex_lock *a)
        uatomic_dec(&a->waiters);
 }
 
+#if defined(__GNUC__) && __GNUC__ == 12 && URCU_VERSION < 0xe00
+#pragma GCC diagnostic pop
+#endif
+
 static inline int trylock(struct mutex_lock *a)
 {
        return pthread_mutex_trylock(&a->mutex);
@@ -51,10 +60,6 @@ static inline bool lock_has_waiters(struct mutex_lock *a)
        return (uatomic_read(&a->waiters) > 0);
 }
 
-#if !defined(__GLIBC__) && defined(__GNUC__) && __GNUC__ == 12
-#pragma GCC diagnostic pop
-#endif
-
 #define lock_cleanup_pop(a) pthread_cleanup_pop(1)
 
 void cleanup_lock (void * data);
index 10b44d373d1171704bb0558fb3bf1feb9b38dc4e..edc3c611f44edc240d75ad7a4e679839678f95d7 100644 (file)
@@ -25,35 +25,29 @@ int get_pgpolicy_id(char * str)
                return GROUP_BY_PRIO;
        if (0 == strncmp(str, "group_by_node_name", 18))
                return GROUP_BY_NODE_NAME;
+       if (0 == strncmp(str, "group_by_tpg", 12))
+               return GROUP_BY_TPG;
 
        return IOPOLICY_UNDEF;
 }
 
-int get_pgpolicy_name(char * buff, int len, int id)
+const char *get_pgpolicy_name(int id)
 {
-       char * s;
-
        switch (id) {
        case FAILOVER:
-               s = "failover";
-               break;
+               return "failover";
        case MULTIBUS:
-               s = "multibus";
-               break;
+               return "multibus";
        case GROUP_BY_SERIAL:
-               s = "group_by_serial";
-               break;
+               return "group_by_serial";
        case GROUP_BY_PRIO:
-               s = "group_by_prio";
-               break;
+               return "group_by_prio";
        case GROUP_BY_NODE_NAME:
-               s = "group_by_node_name";
-               break;
-       default:
-               s = "undefined";
-               break;
+               return "group_by_node_name";
+       case GROUP_BY_TPG:
+               return "group_by_tpg";
        }
-       return snprintf(buff, len, "%s", s);
+       return "undefined"; /* IOPOLICY_UNDEF */
 }
 
 
@@ -191,6 +185,12 @@ prios_match(struct path *pp1, struct path *pp2)
        return (pp1->priority == pp2->priority);
 }
 
+bool
+tpg_match(struct path *pp1, struct path *pp2)
+{
+       return (pp1->tpg_id == pp2->tpg_id);
+}
+
 int group_by_match(struct multipath * mp, vector paths,
                   bool (*path_match_fn)(struct path *, struct path *))
 {
@@ -279,6 +279,14 @@ int group_by_prio(struct multipath *mp, vector paths)
        return group_by_match(mp, paths, prios_match);
 }
 
+/*
+ * One path group per alua target port group present in the path vector
+ */
+int group_by_tpg(struct multipath *mp, vector paths)
+{
+       return group_by_match(mp, paths, tpg_match);
+}
+
 int one_path_per_group(struct multipath *mp, vector paths)
 {
        int i;
index 159276108d70f258fb5cca870d12a9314acae8b0..9e4ddda2570f856a4690e926ccdcfcf9389522c3 100644 (file)
@@ -16,11 +16,12 @@ enum iopolicies {
        MULTIBUS,
        GROUP_BY_SERIAL,
        GROUP_BY_PRIO,
-       GROUP_BY_NODE_NAME
+       GROUP_BY_NODE_NAME,
+       GROUP_BY_TPG,
 };
 
 int get_pgpolicy_id(char *);
-int get_pgpolicy_name (char *, int, int);
+const char *get_pgpolicy_name (int);
 int group_paths(struct multipath *, int);
 /*
  * policies
@@ -30,5 +31,6 @@ int one_group(struct multipath *, vector);
 int group_by_serial(struct multipath *, vector);
 int group_by_prio(struct multipath *, vector);
 int group_by_node_name(struct multipath *, vector);
+int group_by_tpg(struct multipath *, vector);
 
 #endif
index 3193dbe075c163eabe802cf12a8a86bfa3399f1d..360308d221a5191d9762fcbcbdcd073ee3b3ed30 100644 (file)
@@ -782,6 +782,14 @@ snprint_path_vpd_data(struct strbuf *buff, const struct path * pp)
        return append_strbuf_str(buff, "[undef]");
 }
 
+static int
+snprint_alua_tpg(struct strbuf *buff, const struct path * pp)
+{
+       if (pp->tpg_id < 0)
+               return append_strbuf_str(buff, "[undef]");
+       return print_strbuf(buff, "0x%04x", pp->tpg_id);
+}
+
 static const struct multipath_data mpd[] = {
        {'n', "name",          snprint_name},
        {'w', "uuid",          snprint_multipath_uuid},
@@ -836,6 +844,7 @@ static const struct path_data pd[] = {
        {'P', "protocol",      snprint_path_protocol},
        {'I', "init_st",       snprint_initialized},
        {'L', "LUN hex",       snprint_path_lunhex},
+       {'A', "TPG",           snprint_alua_tpg},
 };
 
 static const struct pathgroup_data pgd[] = {
index cdd375292c35b351a0edf1b2228978cc39ef1833..a54487f3d3cff7e6219f1d651a45a3d955f9a85a 100644 (file)
@@ -3,20 +3,25 @@
 #include <stddef.h>
 #include <dlfcn.h>
 #include <sys/stat.h>
+#include <libudev.h>
 
 #include "debug.h"
 #include "util.h"
 #include "prio.h"
+#include "structs.h"
+#include "discovery.h"
 
 static const char * const prio_dir = MULTIPATH_DIR;
 static LIST_HEAD(prioritizers);
 
-unsigned int get_prio_timeout(unsigned int timeout_ms,
-                             unsigned int default_timeout)
+unsigned int get_prio_timeout_ms(const struct path *pp)
 {
-       if (timeout_ms)
-               return timeout_ms;
-       return default_timeout;
+       if (pp->state == PATH_DOWN)
+               return 10;
+       else if (pp->checker_timeout)
+               return pp->checker_timeout * 1000;
+       else
+               return DEF_TIMEOUT;
 }
 
 int init_prio(void)
@@ -136,7 +141,7 @@ struct prio *add_prio (const char *name)
                                errstr);
                goto out;
        }
-       p->getprio = (int (*)(struct path *, char *, unsigned int)) dlsym(p->handle, "getprio");
+       p->getprio = (int (*)(struct path *, char *)) dlsym(p->handle, "getprio");
        errstr = dlerror();
        if (errstr != NULL)
                condlog(0, "A dynamic linking error occurred: (%s)", errstr);
@@ -149,9 +154,9 @@ out:
        return NULL;
 }
 
-int prio_getprio (struct prio * p, struct path * pp, unsigned int timeout)
+int prio_getprio (struct prio * p, struct path * pp)
 {
-       return p->getprio(pp, p->args, timeout);
+       return p->getprio(pp, p->args);
 }
 
 int prio_selected (const struct prio * p)
index 184bf65f2f7b5f8c72486842f6d98f9a35e2d26d..318d260886d407f7f699f00b4eda729e5a920fc8 100644 (file)
@@ -49,15 +49,14 @@ struct prio {
        struct list_head node;
        char name[PRIO_NAME_LEN];
        char args[PRIO_ARGS_LEN];
-       int (*getprio)(struct path *, char *, unsigned int);
+       int (*getprio)(struct path *, char *);
 };
 
-unsigned int get_prio_timeout(unsigned int checker_timeout,
-                             unsigned int default_timeout);
+unsigned int get_prio_timeout_ms(const struct path *);
 int init_prio(void);
 void cleanup_prio (void);
 struct prio * add_prio (const char *);
-int prio_getprio (struct prio *, struct path *, unsigned int);
+int prio_getprio (struct prio *, struct path *);
 void prio_get (struct prio *, const char *, const char *);
 void prio_put (struct prio *);
 int prio_selected (const struct prio *);
@@ -66,6 +65,6 @@ const char * prio_args (const struct prio *);
 int prio_set_args (struct prio *, const char *);
 
 /* The only function exported by prioritizer dynamic libraries (.so) */
-int getprio(struct path *, char *, unsigned int);
+int getprio(struct path *, char *);
 
 #endif /* _PRIO_H */
index 0ab06e2bcd5898e6e7f803dcc5e310d6c9bad481..ec68f37028ad3d276a75415cac9a3000b1b42fba 100644 (file)
@@ -51,22 +51,26 @@ static const char *aas_print_string(int rc)
 }
 
 int
-get_alua_info(struct path * pp, unsigned int timeout)
+get_alua_info(struct path * pp)
 {
        int     rc;
        int     tpg;
+       bool    diff_tpg;
 
-       tpg = get_target_port_group(pp, timeout);
+       tpg = get_target_port_group(pp);
        if (tpg < 0) {
-               rc = get_target_port_group_support(pp, timeout);
+               rc = get_target_port_group_support(pp);
                if (rc < 0)
                        return -ALUA_PRIO_TPGS_FAILED;
                if (rc == TPGS_NONE)
                        return -ALUA_PRIO_NOT_SUPPORTED;
                return -ALUA_PRIO_RTPG_FAILED;
        }
-       condlog(3, "%s: reported target port group is %i", pp->dev, tpg);
-       rc = get_asymmetric_access_state(pp, tpg, timeout);
+       diff_tpg = (pp->tpg_id != GROUP_ID_UNDEF && pp->tpg_id != tpg);
+       pp->tpg_id = tpg;
+       condlog((diff_tpg) ? 2 : 4, "%s: reported target port group is %i",
+               pp->dev, tpg);
+       rc = get_asymmetric_access_state(pp, tpg);
        if (rc < 0) {
                condlog(2, "%s: get_asymmetric_access_state returned %d",
                        __func__, rc);
@@ -94,7 +98,7 @@ int get_exclusive_pref_arg(char *args)
        return 1;
 }
 
-int getprio (struct path * pp, char * args, unsigned int timeout)
+int getprio (struct path * pp, char * args)
 {
        int rc;
        int aas;
@@ -105,7 +109,7 @@ int getprio (struct path * pp, char * args, unsigned int timeout)
                return -ALUA_PRIO_NO_INFORMATION;
 
        exclusive_pref = get_exclusive_pref_arg(args);
-       rc = get_alua_info(pp, timeout);
+       rc = get_alua_info(pp);
        if (rc >= 0) {
                aas = (rc & 0x0f);
                priopath = (rc & 0x80);
index 2db91536f3d624936ac394333d723db84d5fb4c4..dd2224c424d2822cfc175f2987f2a9a7f1cf478e 100644 (file)
@@ -136,7 +136,7 @@ scsi_error(struct sg_io_hdr *hdr, int opcode)
  */
 static int
 do_inquiry_sg(int fd, int evpd, unsigned int codepage,
-             void *resp, int resplen, unsigned int timeout)
+             void *resp, int resplen, unsigned int timeout_ms)
 {
        struct inquiry_command  cmd;
        struct sg_io_hdr        hdr;
@@ -162,7 +162,7 @@ retry:
        hdr.dxfer_len           = resplen;
        hdr.sbp                 = sense;
        hdr.mx_sb_len           = sizeof(sense);
-       hdr.timeout             = get_prio_timeout(timeout, SGIO_TIMEOUT);
+       hdr.timeout             = timeout_ms;
 
        if (ioctl(fd, SG_IO, &hdr) < 0) {
                PRINT_DEBUG("do_inquiry: IOCTL failed!");
@@ -185,7 +185,7 @@ retry:
 }
 
 int do_inquiry(const struct path *pp, int evpd, unsigned int codepage,
-              void *resp, int resplen, unsigned int timeout)
+              void *resp, int resplen)
 {
        struct udev_device *ud = NULL;
 
@@ -206,7 +206,8 @@ int do_inquiry(const struct path *pp, int evpd, unsigned int codepage,
                        return 0;
                }
        }
-       return do_inquiry_sg(pp->fd, evpd, codepage, resp, resplen, timeout);
+       return do_inquiry_sg(pp->fd, evpd, codepage, resp, resplen,
+                            get_prio_timeout_ms(pp));
 }
 
 /*
@@ -214,13 +215,13 @@ int do_inquiry(const struct path *pp, int evpd, unsigned int codepage,
  * data returned by the standard inquiry command.
  */
 int
-get_target_port_group_support(const struct path *pp, unsigned int timeout)
+get_target_port_group_support(const struct path *pp)
 {
        struct inquiry_data     inq;
        int                     rc;
 
        memset((unsigned char *)&inq, 0, sizeof(inq));
-       rc = do_inquiry(pp, 0, 0x00, &inq, sizeof(inq), timeout);
+       rc = do_inquiry(pp, 0, 0x00, &inq, sizeof(inq));
        if (!rc) {
                rc = inquiry_data_get_tpgs(&inq);
        }
@@ -229,7 +230,7 @@ get_target_port_group_support(const struct path *pp, unsigned int timeout)
 }
 
 int
-get_target_port_group(const struct path * pp, unsigned int timeout)
+get_target_port_group(const struct path * pp)
 {
        unsigned char           *buf;
        const struct vpd83_data *       vpd83;
@@ -246,7 +247,7 @@ get_target_port_group(const struct path * pp, unsigned int timeout)
        }
 
        memset(buf, 0, buflen);
-       rc = do_inquiry(pp, 1, 0x83, buf, buflen, timeout);
+       rc = do_inquiry(pp, 1, 0x83, buf, buflen);
        if (rc < 0)
                goto out;
 
@@ -263,7 +264,7 @@ get_target_port_group(const struct path * pp, unsigned int timeout)
                }
                buflen = scsi_buflen;
                memset(buf, 0, buflen);
-               rc = do_inquiry(pp, 1, 0x83, buf, buflen, timeout);
+               rc = do_inquiry(pp, 1, 0x83, buf, buflen);
                if (rc < 0)
                        goto out;
        }
@@ -293,7 +294,7 @@ out:
 }
 
 int
-do_rtpg(int fd, void* resp, long resplen, unsigned int timeout)
+do_rtpg(int fd, void* resp, long resplen, unsigned int timeout_ms)
 {
        struct rtpg_command     cmd;
        struct sg_io_hdr        hdr;
@@ -316,7 +317,7 @@ retry:
        hdr.dxfer_len           = resplen;
        hdr.mx_sb_len           = sizeof(sense);
        hdr.sbp                 = sense;
-       hdr.timeout             = get_prio_timeout(timeout, SGIO_TIMEOUT);
+       hdr.timeout             = timeout_ms;
 
        if (ioctl(fd, SG_IO, &hdr) < 0) {
                condlog(2, "%s: sg ioctl failed: %s",
@@ -340,8 +341,7 @@ retry:
 }
 
 int
-get_asymmetric_access_state(const struct path *pp, unsigned int tpg,
-                           unsigned int timeout)
+get_asymmetric_access_state(const struct path *pp, unsigned int tpg)
 {
        unsigned char           *buf;
        struct rtpg_data *      tpgd;
@@ -349,6 +349,7 @@ get_asymmetric_access_state(const struct path *pp, unsigned int tpg,
        int                     rc;
        unsigned int            buflen;
        uint64_t                scsi_buflen;
+       unsigned int            timeout_ms = get_prio_timeout_ms(pp);
        int fd = pp->fd;
 
        buflen = VPD_BUFLEN;
@@ -359,7 +360,7 @@ get_asymmetric_access_state(const struct path *pp, unsigned int tpg,
                return -RTPG_RTPG_FAILED;
        }
        memset(buf, 0, buflen);
-       rc = do_rtpg(fd, buf, buflen, timeout);
+       rc = do_rtpg(fd, buf, buflen, timeout_ms);
        if (rc < 0) {
                PRINT_DEBUG("%s: do_rtpg returned %d", __func__, rc);
                goto out;
@@ -377,7 +378,7 @@ get_asymmetric_access_state(const struct path *pp, unsigned int tpg,
                }
                buflen = scsi_buflen;
                memset(buf, 0, buflen);
-               rc = do_rtpg(fd, buf, buflen, timeout);
+               rc = do_rtpg(fd, buf, buflen, timeout_ms);
                if (rc < 0)
                        goto out;
        }
index 675709ff3a57b6e5cbf2b7cfbe15bc3a04d2a1cc..c5f9a8f949a9d23a26018d447778b1b701474d54 100644 (file)
@@ -22,9 +22,8 @@
 #define RTPG_RTPG_FAILED                       3
 #define RTPG_TPG_NOT_FOUND                     4
 
-int get_target_port_group_support(const struct path *pp, unsigned int timeout);
-int get_target_port_group(const struct path *pp, unsigned int timeout);
-int get_asymmetric_access_state(const struct path *pp,
-                               unsigned int tpg, unsigned int timeout);
+int get_target_port_group_support(const struct path *pp);
+int get_target_port_group(const struct path *pp);
+int get_asymmetric_access_state(const struct path *pp, unsigned int tpg);
 
 #endif /* __RTPG_H__ */
index b5c7873df081d6cce5be2998f3a04a8dbb474264..e9827dcaa3134748c7b464c59e10bd994a6e3728 100644 (file)
@@ -203,8 +203,7 @@ static int get_ana_info(struct path * pp)
  * - ALUA's LBA-dependent state has no ANA equivalent.
  */
 
-int getprio(struct path *pp, __attribute__((unused)) char *args,
-           __attribute__((unused)) unsigned int timeout)
+int getprio(struct path *pp, __attribute__((unused)) char *args)
 {
        int rc;
 
index 059d8593ca7a80384d032767553ad7fc8b0a77ce..2b07f47b66a5dedaa582544266e205c2181a0e33 100644 (file)
@@ -3,8 +3,7 @@
 #include "prio.h"
 
 int getprio(__attribute__((unused)) struct path * pp,
-           __attribute__((unused)) char * args,
-           __attribute__((unused)) unsigned int timeout)
+           __attribute__((unused)) char * args)
 {
        return 1;
 }
index d1d473d461ec3494633677df42d1cd27c564a1c5..f288306c2be5357638f835ab73de4c1905b21c9c 100644 (file)
@@ -32,7 +32,8 @@
 
 #define dc_log(prio, msg) condlog(prio, "%s: datacore prio: " msg, dev)
 
-int datacore_prio (const char *dev, int sg_fd, char * args)
+int datacore_prio (const char *dev, int sg_fd, char * args,
+                  unsigned int timeout_ms)
 {
        int k;
        char sdsname[32];
@@ -42,7 +43,6 @@ int datacore_prio (const char *dev, int sg_fd, char * args)
        unsigned char sense_buffer[32];
        sg_io_hdr_t io_hdr;
 
-       int timeout = 2000;
        char preferredsds_buff[255] = "";
        char * preferredsds = &preferredsds_buff[0];
 
@@ -52,9 +52,9 @@ int datacore_prio (const char *dev, int sg_fd, char * args)
        }
 
        if (sscanf(args, "timeout=%i preferredsds=%s",
-                  &timeout, preferredsds) == 2) {}
+                  (int *)&timeout_ms, preferredsds) == 2) {}
        else if (sscanf(args, "preferredsds=%s timeout=%i",
-                       preferredsds, &timeout) == 2) {}
+                       preferredsds, (int *)&timeout_ms) == 2) {}
        else if (sscanf(args, "preferredsds=%s",
                        preferredsds) == 1) {}
        else {
@@ -67,10 +67,6 @@ int datacore_prio (const char *dev, int sg_fd, char * args)
                dc_log(0, "prio args: preferredsds too short (1 character min)");
                return 0;
        }
-       if ((timeout < 500) || (timeout > 20000)) {
-               dc_log(0, "prio args: timeout out of bounds [500:20000]");
-               return 0;
-       }
        if ((ioctl(sg_fd, SG_GET_VERSION_NUM, &k) < 0) || (k < 30000))
                return 0;
 
@@ -83,7 +79,7 @@ int datacore_prio (const char *dev, int sg_fd, char * args)
        io_hdr.dxferp = inqBuff;
        io_hdr.cmdp = inqCmdBlk;
        io_hdr.sbp = sense_buffer;
-       io_hdr.timeout = timeout;
+       io_hdr.timeout = timeout_ms;
 
        // on error just return prio 0
        if (ioctl(sg_fd, SG_IO, &io_hdr) < 0)
@@ -98,8 +94,7 @@ int datacore_prio (const char *dev, int sg_fd, char * args)
        return 0;
 }
 
-int getprio(struct path * pp, char * args,
-           __attribute__((unused)) unsigned int timeout)
+int getprio(struct path * pp, char * args)
 {
-       return datacore_prio(pp->dev, pp->fd, args);
+       return datacore_prio(pp->dev, pp->fd, args, get_prio_timeout_ms(pp));
 }
index 3b63cca018b0e3c308dd127e905989aa512fae28..856c23d63cc81977c87e58ebeae02b331b78631d 100644 (file)
@@ -12,7 +12,7 @@
 
 #define pp_emc_log(prio, msg) condlog(prio, "%s: emc prio: " msg, dev)
 
-int emc_clariion_prio(const char *dev, int fd, unsigned int timeout)
+int emc_clariion_prio(const char *dev, int fd, unsigned int timeout_ms)
 {
        unsigned char sense_buffer[128];
        unsigned char sb[128];
@@ -31,7 +31,7 @@ int emc_clariion_prio(const char *dev, int fd, unsigned int timeout)
        io_hdr.dxferp = sense_buffer;
        io_hdr.cmdp = inqCmdBlk;
        io_hdr.sbp = sb;
-       io_hdr.timeout = get_prio_timeout(timeout, 60000);
+       io_hdr.timeout = timeout_ms;
        io_hdr.pack_id = 0;
        if (ioctl(fd, SG_IO, &io_hdr) < 0) {
                pp_emc_log(0, "sending query command failed");
@@ -81,8 +81,7 @@ out:
        return(ret);
 }
 
-int getprio (struct path *pp, __attribute__((unused)) char *args,
-            unsigned int timeout)
+int getprio (struct path *pp, __attribute__((unused)) char *args)
 {
-       return emc_clariion_prio(pp->dev, pp->fd, timeout);
+       return emc_clariion_prio(pp->dev, pp->fd, get_prio_timeout_ms(pp));
 }
index d569f2d722a70cdabae561d9f43360a4499e8ee5..212301ea933d89794fc65d15ad76a75cc3f65ebc 100644 (file)
@@ -84,7 +84,7 @@
 #define pp_hds_log(prio, fmt, args...) \
        condlog(prio, "%s: hds prio: " fmt, dev, ##args)
 
-int hds_modular_prio (const char *dev, int fd, unsigned int timeout)
+int hds_modular_prio (const char *dev, int fd, unsigned int timeout_ms)
 {
        int k;
        char vendor[9];
@@ -114,7 +114,7 @@ int hds_modular_prio (const char *dev, int fd, unsigned int timeout)
        io_hdr.dxferp = inqBuff;
        io_hdr.cmdp = inqCmdBlk;
        io_hdr.sbp = sense_buffer;
-       io_hdr.timeout = get_prio_timeout(timeout, 2000); /* TimeOut = 2 seconds */
+       io_hdr.timeout = timeout_ms;
 
        if (ioctl (fd, SG_IO, &io_hdr) < 0) {
                pp_hds_log(0, "SG_IO error");
@@ -168,8 +168,7 @@ int hds_modular_prio (const char *dev, int fd, unsigned int timeout)
        return -1;
 }
 
-int getprio (struct path * pp, __attribute__((unused)) char *args,
-            unsigned int timeout)
+int getprio (struct path * pp, __attribute__((unused)) char *args)
 {
-       return hds_modular_prio(pp->dev, pp->fd, timeout);
+       return hds_modular_prio(pp->dev, pp->fd, get_prio_timeout_ms(pp));
 }
index 5b85ad2e5fc4fb2f4277fc5dbf6c5de455507116..5570271ddab946067a357d697da0ece2b906d6df 100644 (file)
@@ -32,7 +32,7 @@
 #define pp_hp_sw_log(prio, fmt, args...) \
        condlog(prio, "%s: hp_sw prio: " fmt, dev, ##args)
 
-int hp_sw_prio(const char *dev, int fd, unsigned int timeout)
+int hp_sw_prio(const char *dev, int fd, unsigned int timeout_ms)
 {
        unsigned char turCmdBlk[TUR_CMD_LEN] = { 0x00, 0, 0, 0, 0, 0 };
        unsigned char sb[128];
@@ -46,7 +46,7 @@ int hp_sw_prio(const char *dev, int fd, unsigned int timeout)
        io_hdr.dxfer_direction = SG_DXFER_NONE;
        io_hdr.cmdp = turCmdBlk;
        io_hdr.sbp = sb;
-       io_hdr.timeout = get_prio_timeout(timeout, 60000);
+       io_hdr.timeout = timeout_ms;
        io_hdr.pack_id = 0;
 retry:
        if (ioctl(fd, SG_IO, &io_hdr) < 0) {
@@ -95,8 +95,7 @@ out:
        return(ret);
 }
 
-int getprio (struct path *pp, __attribute__((unused)) char *args,
-            unsigned int timeout)
+int getprio (struct path *pp, __attribute__((unused)) char *args)
 {
-       return hp_sw_prio(pp->dev, pp->fd, timeout);
+       return hp_sw_prio(pp->dev, pp->fd, get_prio_timeout_ms(pp));
 }
index 167a46b01bde96e517c2b982974933101538f320..f3bf64c5e7d4d2d5b309946aff61460dd85b9303 100644 (file)
@@ -138,8 +138,7 @@ int iet_prio(const char *dev, char * args)
        return 10;
 }
 
-int getprio(struct path * pp, char * args,
-           __attribute__((unused)) unsigned int timeout)
+int getprio(struct path * pp, char * args)
 {
        return iet_prio(pp->dev, args);
 }
index 262e69d2e6ff71f0d3656f28f14a5d5dd902188c..117886ea7c4c0cbf17c43194bbbb55fca57da6f7 100644 (file)
@@ -28,7 +28,6 @@
 #define INQUIRY_CMDLEN 6
 #define DEFAULT_PRIOVAL        10
 #define RESULTS_MAX    256
-#define SG_TIMEOUT     60000
 
 #define pp_ontap_log(prio, fmt, args...) \
        condlog(prio, "%s: ontap prio: " fmt, dev, ##args)
@@ -72,7 +71,7 @@ static void process_sg_error(struct sg_io_hdr *io_hdr)
  */
 static int send_gva(const char *dev, int fd, unsigned char pg,
                    unsigned char *results, int *results_size,
-                   unsigned int timeout)
+                   unsigned int timeout_ms)
 {
        unsigned char sb[128];
        unsigned char cdb[10] = {0xc0, 0, 0x1, 0xa, 0x98, 0xa,
@@ -90,7 +89,7 @@ static int send_gva(const char *dev, int fd, unsigned char pg,
        io_hdr.dxferp = results;
        io_hdr.cmdp = cdb;
        io_hdr.sbp = sb;
-       io_hdr.timeout = get_prio_timeout(timeout, SG_TIMEOUT);
+       io_hdr.timeout = timeout_ms;
        io_hdr.pack_id = 0;
        if (ioctl(fd, SG_IO, &io_hdr) < 0) {
                pp_ontap_log(0, "SG_IO ioctl failed, errno=%d", errno);
@@ -123,7 +122,7 @@ out:
  *  0: Device _not_ proxy path
  *  1: Device _is_ proxy path
  */
-static int get_proxy(const char *dev, int fd, unsigned int timeout)
+static int get_proxy(const char *dev, int fd, unsigned int timeout_ms)
 {
        unsigned char results[256];
        unsigned char sb[128];
@@ -142,7 +141,7 @@ static int get_proxy(const char *dev, int fd, unsigned int timeout)
        io_hdr.dxferp = results;
        io_hdr.cmdp = cdb;
        io_hdr.sbp = sb;
-       io_hdr.timeout = get_prio_timeout(timeout, SG_TIMEOUT);
+       io_hdr.timeout = timeout_ms;
        io_hdr.pack_id = 0;
        if (ioctl(fd, SG_IO, &io_hdr) < 0) {
                pp_ontap_log(0, "ioctl sending inquiry command failed, "
@@ -183,7 +182,7 @@ out:
  * 2: iSCSI software
  * 1: FCP proxy
  */
-static int ontap_prio(const char *dev, int fd, unsigned int timeout)
+static int ontap_prio(const char *dev, int fd, unsigned int timeout_ms)
 {
        unsigned char results[RESULTS_MAX];
        int results_size=RESULTS_MAX;
@@ -196,7 +195,7 @@ static int ontap_prio(const char *dev, int fd, unsigned int timeout)
        is_iscsi_software = is_iscsi_hardware = is_proxy = 0;
 
        memset(&results, 0, sizeof (results));
-       rc = send_gva(dev, fd, 0x41, results, &results_size, timeout);
+       rc = send_gva(dev, fd, 0x41, results, &results_size, timeout_ms);
        if (rc >= 0) {
                tot_len = get_unaligned_be32(&results[0]);
                if (tot_len <= 8) {
@@ -221,7 +220,7 @@ static int ontap_prio(const char *dev, int fd, unsigned int timeout)
        }
 
 try_fcp_proxy:
-       rc = get_proxy(dev, fd, timeout);
+       rc = get_proxy(dev, fd, timeout_ms);
        if (rc >= 0) {
                is_proxy = rc;
        }
@@ -241,8 +240,7 @@ prio_select:
        }
 }
 
-int getprio (struct path *pp, __attribute__((unused)) char *args,
-            unsigned int timeout)
+int getprio (struct path *pp, __attribute__((unused)) char *args)
 {
-       return ontap_prio(pp->dev, pp->fd, timeout);
+       return ontap_prio(pp->dev, pp->fd, get_prio_timeout_ms(pp));
 }
index 2f5be9b9ae5f37f1ff037767c0f1c004868f4f15..f4142577b424584bccd5a799d800529056e4e59e 100644 (file)
@@ -104,10 +104,10 @@ static void cleanup_directio_read(int fd, char *buf, int restore_flags)
        }
 }
 
-static int do_directio_read(int fd, unsigned int timeout, char *buf, int sz)
+static int do_directio_read(int fd, unsigned int timeout_ms, char *buf, int sz)
 {
        fd_set read_fds;
-       struct timeval tm = { .tv_sec = timeout };
+       struct timeval tm = { .tv_sec = timeout_ms / 1000};
        int ret;
        int num_read;
 
@@ -208,7 +208,7 @@ int calcPrio(double lg_avglatency, double lg_maxavglatency,
        return lg_maxavglatency - lg_avglatency;
 }
 
-int getprio(struct path *pp, char *args, unsigned int timeout)
+int getprio(struct path *pp, char *args)
 {
        int rc, temp;
        int io_num = 0;
@@ -247,7 +247,8 @@ int getprio(struct path *pp, char *args, unsigned int timeout)
 
                (void)clock_gettime(CLOCK_MONOTONIC, &tv_before);
 
-               if (do_directio_read(pp->fd, timeout, buf, blksize)) {
+               if (do_directio_read(pp->fd, get_prio_timeout_ms(pp), buf,
+                                    blksize)) {
                        pp_pl_log(0, "%s: path down", pp->dev);
                        cleanup_directio_read(pp->fd, buf, restore_flags);
                        return -1;
index b742ac2360f52190ce581c40bda7111f7bf4ff2c..148b957d9e444be4ed43f440bfad5ec57dd1cfc2 100644 (file)
@@ -6,8 +6,7 @@
 #include "prio.h"
 
 int getprio(__attribute__((unused)) struct path *pp,
-           __attribute__((unused)) char *args,
-           __attribute__((unused)) unsigned int timeout)
+           __attribute__((unused)) char *args)
 {
        struct timeval tv;
 
index 92a2fb85974ccd69625aab756fc03fd324aede6c..f40cd8413562d6a04272b1c32d53c2390dab3ea0 100644 (file)
@@ -12,7 +12,7 @@
 
 #define pp_rdac_log(prio, msg) condlog(prio, "%s: rdac prio: " msg, dev)
 
-int rdac_prio(const char *dev, int fd, unsigned int timeout)
+int rdac_prio(const char *dev, int fd, unsigned int timeout_ms)
 {
        unsigned char sense_buffer[128];
        unsigned char sb[128];
@@ -31,7 +31,7 @@ int rdac_prio(const char *dev, int fd, unsigned int timeout)
        io_hdr.dxferp = sense_buffer;
        io_hdr.cmdp = inqCmdBlk;
        io_hdr.sbp = sb;
-       io_hdr.timeout = get_prio_timeout(timeout, 60000);
+       io_hdr.timeout = timeout_ms;
        io_hdr.pack_id = 0;
        if (ioctl(fd, SG_IO, &io_hdr) < 0) {
                pp_rdac_log(0, "sending inquiry command failed");
@@ -91,8 +91,7 @@ out:
        return(ret);
 }
 
-int getprio (struct path *pp, __attribute__((unused)) char *args,
-            unsigned int timeout)
+int getprio (struct path *pp, __attribute__((unused)) char *args)
 {
-       return rdac_prio(pp->dev, pp->fd, timeout);
+       return rdac_prio(pp->dev, pp->fd, get_prio_timeout_ms(pp));
 }
index a6feb421c13312184a5be3115b49686454ec4050..5e8adc05cab26cb0504915ba12b8400147b9256c 100644 (file)
@@ -36,8 +36,7 @@ int get_exclusive_pref_arg(char *args)
        return 1;
 }
 
-int getprio (struct path * pp, char *args,
-            __attribute__((unused)) unsigned int timeout)
+int getprio (struct path * pp, char *args)
 {
        int prio = 0, rc, i;
        char buff[512];
index 561ebb48837ddcc41525380fac4cf81d847e421d..de51a9e7b3f6cb3bf137650846a13e3329362854 100644 (file)
@@ -125,8 +125,7 @@ int prio_path_weight(struct path *pp, char *prio_args)
        return priority;
 }
 
-int getprio(struct path *pp, char *args,
-           __attribute__((unused)) unsigned int timeout)
+int getprio(struct path *pp, char *args)
 {
        return prio_path_weight(pp, args);
 }
index a215499d313887f06d38f07805511d4ad005d131..c66d293b9e77db2de6534a42d88c1db1da582c87 100644 (file)
@@ -157,8 +157,7 @@ static int do_prkey(int fd, char *wwid, char *keystr, int cmd)
        return 0;
 }
 
-int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey,
-             uint8_t *sa_flags)
+int get_prkey(struct multipath *mpp, uint64_t *prkey, uint8_t *sa_flags)
 {
        int fd;
        int unused;
@@ -168,7 +167,7 @@ int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey,
        if (!strlen(mpp->wwid))
                goto out;
 
-       fd = open_file(conf->prkeys_file, &unused, PRKEYS_FILE_HEADER);
+       fd = open_file(DEFAULT_PRKEYS_FILE, &unused, PRKEYS_FILE_HEADER);
        if (fd < 0)
                goto out;
        ret = do_prkey(fd, mpp->wwid, keystr, PRKEY_READ);
@@ -201,7 +200,7 @@ int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey,
                sa_flags &= MPATH_F_APTPL_MASK;
        }
 
-       fd = open_file(conf->prkeys_file, &can_write, PRKEYS_FILE_HEADER);
+       fd = open_file(DEFAULT_PRKEYS_FILE, &can_write, PRKEYS_FILE_HEADER);
        if (fd < 0)
                goto out;
        if (!can_write) {
index a16de1065e683df646ea833dc04f9d2a8cbe18aa..43afd5e42f5fd5a93e0073704f121cb0ebb6bc62 100644 (file)
@@ -16,9 +16,8 @@
 int print_reservation_key(struct strbuf *buff,
                          struct be64 key, uint8_t flags, int source);
 int parse_prkey_flags(const char *ptr, uint64_t *prkey, uint8_t *flags);
-int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey,
-             uint8_t sa_flags);
-int get_prkey(struct config *conf, struct multipath *mpp, uint64_t *prkey,
-             uint8_t *sa_flags);
+int set_prkey(struct config *conf, struct multipath *mpp,
+             uint64_t prkey, uint8_t sa_flags);
+int get_prkey(struct multipath *mpp, uint64_t *prkey, uint8_t *sa_flags);
 
 #endif /* _PRKEY_H */
index a25cc9218284b7278613e40599e02b10cc1cfe1c..44241e2a5ce98bd609c6f7fb4e4f99a5437b644f 100644 (file)
@@ -35,7 +35,8 @@ pgpolicyfn *pgpolicies[] = {
        one_group,
        group_by_serial,
        group_by_prio,
-       group_by_node_name
+       group_by_node_name,
+       group_by_tpg,
 };
 
 #define do_set(var, src, dest, msg)                                    \
@@ -249,25 +250,85 @@ out:
        return 0;
 }
 
+static bool
+verify_alua_prio(struct multipath *mp)
+{
+       int i;
+       struct path *pp;
+
+       vector_foreach_slot(mp->paths, pp, i) {
+               const char *name = prio_name(&pp->prio);
+               if (strncmp(name, PRIO_ALUA, PRIO_NAME_LEN) &&
+                   strncmp(name, PRIO_SYSFS, PRIO_NAME_LEN))
+                        return false;
+       }
+       return true;
+}
+
+int select_detect_pgpolicy(struct config *conf, struct multipath *mp)
+{
+       const char *origin;
+
+       mp_set_ovr(detect_pgpolicy);
+       mp_set_hwe(detect_pgpolicy);
+       mp_set_conf(detect_pgpolicy);
+       mp_set_default(detect_pgpolicy, DEFAULT_DETECT_PGPOLICY);
+out:
+       condlog(3, "%s: detect_pgpolicy = %s %s", mp->alias,
+               (mp->detect_pgpolicy == DETECT_PGPOLICY_ON)? "yes" : "no",
+                origin);
+       return 0;
+}
+
+int select_detect_pgpolicy_use_tpg(struct config *conf, struct multipath *mp)
+{
+       const char *origin;
+
+       mp_set_ovr(detect_pgpolicy_use_tpg);
+       mp_set_hwe(detect_pgpolicy_use_tpg);
+       mp_set_conf(detect_pgpolicy_use_tpg);
+       mp_set_default(detect_pgpolicy_use_tpg,
+                      DEFAULT_DETECT_PGPOLICY_USE_TPG);
+out:
+       condlog(3, "%s: detect_pgpolicy_use_tpg = %s %s", mp->alias,
+               (mp->detect_pgpolicy_use_tpg == DETECT_PGPOLICY_USE_TPG_ON)?
+               "yes" : "no", origin);
+       return 0;
+}
+
 int select_pgpolicy(struct config *conf, struct multipath * mp)
 {
        const char *origin;
-       char buff[POLICY_NAME_SIZE];
+       int log_prio = 3;
 
        if (conf->pgpolicy_flag > 0) {
                mp->pgpolicy = conf->pgpolicy_flag;
                origin = cmdline_origin;
                goto out;
        }
+       if (mp->detect_pgpolicy == DETECT_PGPOLICY_ON && verify_alua_prio(mp)) {
+               if (mp->detect_pgpolicy_use_tpg == DETECT_PGPOLICY_USE_TPG_ON)
+                       mp->pgpolicy = GROUP_BY_TPG;
+               else
+                       mp->pgpolicy = GROUP_BY_PRIO;
+               origin = autodetect_origin;
+               goto out;
+       }
        mp_set_mpe(pgpolicy);
        mp_set_ovr(pgpolicy);
        mp_set_hwe(pgpolicy);
        mp_set_conf(pgpolicy);
        mp_set_default(pgpolicy, DEFAULT_PGPOLICY);
 out:
+       if (mp->pgpolicy == GROUP_BY_TPG && origin != autodetect_origin &&
+           !verify_alua_prio(mp)) {
+               mp->pgpolicy = DEFAULT_PGPOLICY;
+               origin = "(setting: emergency fallback - not all paths use alua prio)";
+               log_prio = 1;
+       }
        mp->pgpolicyfn = pgpolicies[mp->pgpolicy];
-       get_pgpolicy_name(buff, POLICY_NAME_SIZE, mp->pgpolicy);
-       condlog(3, "%s: path_grouping_policy = %s %s", mp->alias, buff, origin);
+       condlog(log_prio, "%s: path_grouping_policy = %s %s", mp->alias,
+               get_pgpolicy_name(mp->pgpolicy), origin);
        return 0;
 }
 
@@ -340,19 +401,15 @@ int select_alias(struct config *conf, struct multipath * mp)
 
        select_alias_prefix(conf, mp);
 
-       if (strlen(mp->alias_old) > 0) {
-               mp->alias = use_existing_alias(mp->wwid, conf->bindings_file,
-                               mp->alias_old, mp->alias_prefix,
-                               conf->bindings_read_only);
-               memset (mp->alias_old, 0, WWID_SIZE);
-               origin = "(setting: using existing alias)";
-       }
+       mp->alias = get_user_friendly_alias(mp->wwid, mp->alias_old, mp->alias_prefix,
+                                           conf->bindings_read_only);
 
-       if (mp->alias == NULL) {
-               mp->alias = get_user_friendly_alias(mp->wwid,
-                               conf->bindings_file, mp->alias_prefix, conf->bindings_read_only);
+       if (mp->alias && !strncmp(mp->alias, mp->alias_old, WWID_SIZE))
+               origin = "(setting: using existing alias)";
+       else if (mp->alias)
                origin = "(setting: user_friendly_name)";
-       }
+       memset (mp->alias_old, 0, WWID_SIZE);
+
 out:
        if (mp->alias == NULL) {
                mp->alias = strdup(mp->wwid);
@@ -567,6 +624,23 @@ out:
        return 0;
 }
 
+
+int select_checker_timeout(struct config *conf, struct path *pp)
+{
+       const char *origin;
+
+       pp_set_conf(checker_timeout);
+       if (sysfs_get_timeout(pp, &pp->checker_timeout) > 0) {
+               origin = "(setting: kernel sysfs)";
+               goto out;
+       }
+       pp_set_default(checker_timeout, DEF_TIMEOUT);
+out:
+       condlog(3, "%s checker timeout = %u s %s", pp->dev, pp->checker_timeout,
+               origin);
+       return 0;
+}
+
 /*
  * Current RDAC (NetApp E/EF Series) firmware relies
  * on periodic REPORT TARGET PORT GROUPS for
@@ -603,6 +677,8 @@ int select_checker(struct config *conf, struct path *pp)
        char *ckr_name;
        struct checker * c = &pp->checker;
 
+       if (!pp->checker_timeout)
+               select_checker_timeout(conf, pp);
        if (pp->detect_checker == DETECT_CHECKER_ON) {
                origin = autodetect_origin;
                if (check_rdac(pp)) {
@@ -623,19 +699,7 @@ out:
        checker_get(c, ckr_name);
        condlog(3, "%s: path_checker = %s %s", pp->dev,
                checker_name(c), origin);
-       if (conf->checker_timeout) {
-               c->timeout = conf->checker_timeout;
-               condlog(3, "%s: checker timeout = %u s %s",
-                       pp->dev, c->timeout, conf_origin);
-       }
-       else if (sysfs_get_timeout(pp, &c->timeout) > 0)
-               condlog(3, "%s: checker timeout = %u s (setting: kernel sysfs)",
-                       pp->dev, c->timeout);
-       else {
-               c->timeout = DEF_TIMEOUT;
-               condlog(3, "%s: checker timeout = %u s %s",
-                       pp->dev, c->timeout, default_origin);
-       }
+       c->timeout = pp->checker_timeout;
        return 0;
 }
 
@@ -745,6 +809,8 @@ int select_prio(struct config *conf, struct path *pp)
        struct prio * p = &pp->prio;
        int log_prio = 3;
 
+       if (!pp->checker_timeout)
+               select_checker_timeout(conf, pp);
        if (pp->detect_prio == DETECT_PRIO_ON) {
                detect_prio(pp);
                if (prio_selected(p)) {
@@ -925,7 +991,7 @@ int select_reservation_key(struct config *conf, struct multipath *mp)
 out:
        if (mp->prkey_source == PRKEY_SOURCE_FILE) {
                from_file = " (from prkeys file)";
-               if (get_prkey(conf, mp, &prkey, &mp->sa_flags) != 0)
+               if (get_prkey(mp, &prkey, &mp->sa_flags) != 0)
                        put_be64(mp->reservation_key, 0);
                else
                        put_be64(mp->reservation_key, prkey);
index 152ca44c72140757b0a18e86c3ccfc74aabf9c6d..73615c2f072eb8d46fd1a075930546db8b0233d2 100644 (file)
@@ -1,5 +1,7 @@
 int select_rr_weight (struct config *conf, struct multipath * mp);
 int select_pgfailback (struct config *conf, struct multipath * mp);
+int select_detect_pgpolicy (struct config *conf, struct multipath * mp);
+int select_detect_pgpolicy_use_tpg (struct config *conf, struct multipath * mp);
 int select_pgpolicy (struct config *conf, struct multipath * mp);
 int select_selector (struct config *conf, struct multipath * mp);
 int select_alias (struct config *conf, struct multipath * mp);
index 87e84d5d984e886a7745a9a3439a2125bb0f9c9d..1b305fd1aa93ec7036aa4cd8c0587e2b2174d01b 100644 (file)
@@ -125,6 +125,7 @@ alloc_path (void)
                pp->sg_id.proto_id = PROTOCOL_UNSET;
                pp->fd = -1;
                pp->tpgs = TPGS_UNDEF;
+               pp->tpg_id = GROUP_ID_UNDEF;
                pp->priority = PRIO_UNDEF;
                pp->checkint = CHECKINT_UNDEF;
                checker_clear(&pp->checker);
@@ -146,6 +147,7 @@ uninitialize_path(struct path *pp)
 
        pp->dmstate = PSTATE_UNDEF;
        pp->uid_attribute = NULL;
+       pp->checker_timeout = 0;
 
        if (checker_selected(&pp->checker))
                checker_put(&pp->checker);
index 9e2c1ab0debae66942d3897a244ae04452c4d15a..a1aac1b4338033572997e420c0561894e21b5bfd 100644 (file)
@@ -143,6 +143,18 @@ enum detect_checker_states {
        DETECT_CHECKER_ON = YNU_YES,
 };
 
+enum detect_pgpolicy_states {
+       DETECT_PGPOLICY_UNDEF = YNU_UNDEF,
+       DETECT_PGPOLICY_OFF = YNU_NO,
+       DETECT_PGPOLICY_ON = YNU_YES,
+};
+
+enum detect_pgpolicy_use_tpg_states {
+       DETECT_PGPOLICY_USE_TPG_UNDEF = YNU_UNDEF,
+       DETECT_PGPOLICY_USE_TPG_OFF = YNU_NO,
+       DETECT_PGPOLICY_USE_TPG_ON = YNU_YES,
+};
+
 enum deferred_remove_states {
        DEFERRED_REMOVE_UNDEF = YNU_UNDEF,
        DEFERRED_REMOVE_OFF = YNU_NO,
@@ -167,6 +179,13 @@ enum queue_mode_states {
        QUEUE_MODE_RQ,
 };
 
+enum auto_resize_state {
+       AUTO_RESIZE_UNDEF = 0,
+       AUTO_RESIZE_NEVER,
+       AUTO_RESIZE_GROW_ONLY,
+       AUTO_RESIZE_GROW_SHRINK,
+};
+
 #define PROTOCOL_UNSET -1
 
 enum scsi_protocol {
@@ -283,6 +302,12 @@ enum eh_deadline_states {
        EH_DEADLINE_ZERO = UOZ_ZERO,
 };
 
+enum max_retries_states {
+       MAX_RETRIES_UNSET = UOZ_UNDEF,
+       MAX_RETRIES_OFF = UOZ_OFF,
+       MAX_RETRIES_ZERO = UOZ_ZERO,
+};
+
 enum recheck_wwid_states {
        RECHECK_WWID_UNDEF = YNU_UNDEF,
        RECHECK_WWID_OFF = YNU_NO,
@@ -317,6 +342,8 @@ struct hd_geometry {
 };
 #endif
 
+#define GROUP_ID_UNDEF -1
+
 struct path {
        char dev[FILE_NAME_SIZE];
        char dev_t[BLK_DEV_SIZE];
@@ -368,16 +395,28 @@ struct path {
        unsigned int dev_loss;
        int eh_deadline;
        bool is_checked;
+       bool can_use_env_uid;
+       unsigned int checker_timeout;
        /* configlet pointers */
        vector hwe;
        struct gen_path generic_path;
+       int tpg_id;
 };
 
 typedef int (pgpolicyfn) (struct multipath *, vector);
 
+
+enum prflag_value {
+       PRFLAG_UNKNOWN,
+       PRFLAG_UNSET,
+       PRFLAG_SET,
+};
+
 struct multipath {
        char wwid[WWID_SIZE];
        char alias_old[WWID_SIZE];
+       int detect_pgpolicy;
+       int detect_pgpolicy_use_tpg;
        int pgpolicy;
        pgpolicyfn *pgpolicyfn;
        int nextpg;
@@ -449,7 +488,7 @@ struct multipath {
        int prkey_source;
        struct be64 reservation_key;
        uint8_t sa_flags;
-       unsigned char prflag;
+       int prflag;
        int all_tg_pt;
        struct gen_multipath generic_mp;
        bool fpin_must_reload;
index 5a618767e8dce2a8e509fe6b0aec90e1659ed06a..0e8a46e7127c58675a2b9cd1a8120a84c44180ab 100644 (file)
@@ -267,7 +267,7 @@ int adopt_paths(vector pathvec, struct multipath *mpp)
                        if (mpp->queue_mode == QUEUE_MODE_RQ &&
                            pp->bus == SYSFS_BUS_NVME &&
                            pp->sg_id.proto_id == NVME_PROTOCOL_TCP) {
-                               condlog(2, "%s: mulitpath device %s created with request queue_mode. Unable to add nvme:tcp paths",
+                               condlog(2, "%s: multipath device %s created with request queue_mode. Unable to add nvme:tcp paths",
                                        pp->dev, mpp->alias);
                                continue;
                        }
@@ -392,10 +392,8 @@ remove_maps(struct vectors * vecs)
        if (!vecs)
                return;
 
-       vector_foreach_slot (vecs->mpvec, mpp, i) {
-               remove_map(mpp, vecs->pathvec, vecs->mpvec);
-               i--;
-       }
+       vector_foreach_slot (vecs->mpvec, mpp, i)
+               remove_map(mpp, vecs->pathvec, NULL);
 
        vector_free(vecs->mpvec);
        vecs->mpvec = NULL;
index 6fdfcfa7f5d8e91f8f5d47ecb79286fbe04e020c..b1e1f39bec49aaa39bbf14f8403ba7f7b7f5c1bd 100644 (file)
@@ -12,6 +12,7 @@ void path_group_prio_update(struct pathgroup *pgp)
        int i;
        int priority = 0;
        int marginal = 0;
+       int defined_prios = 0;
        struct path * pp;
 
        pgp->enabled_paths = 0;
@@ -24,12 +25,17 @@ void path_group_prio_update(struct pathgroup *pgp)
                        marginal++;
                if (pp->state == PATH_UP ||
                    pp->state == PATH_GHOST) {
-                       priority += pp->priority;
+                       if (pp->priority != PRIO_UNDEF) {
+                               defined_prios++;
+                               priority += pp->priority;
+                       }
                        pgp->enabled_paths++;
                }
        }
-       if (pgp->enabled_paths)
-               pgp->priority = priority / pgp->enabled_paths;
+       if (defined_prios)
+               pgp->priority = priority / defined_prios;
+       else if (pgp->enabled_paths)
+               pgp->priority = PRIO_UNDEF;
        else
                pgp->priority = 0;
        if (marginal && marginal == i)
index c45296af96cd7f029fad1d567816bdee263f9f3e..ad3d6612eeeddc51943a48a0b95cc8d231e7ed9f 100644 (file)
@@ -175,7 +175,6 @@ sysfs_get_size (struct path *pp, unsigned long long * size)
 
        if (r != 1) {
                condlog(3, "%s: Cannot parse size attribute", pp->dev);
-               *size = 0;
                return 1;
        }
 
index a6aa92150457c75ee2308071f344423cb16b909b..f2237787d8e3afc5e3ed10877e395a649490d933 100644 (file)
@@ -17,6 +17,8 @@
 #include <stddef.h>
 #include <errno.h>
 #include <libudev.h>
+#include <dirent.h>
+#include <libmount/libmount.h>
 
 #include "vector.h"
 #include "config.h"
 #include "mpath_cmd.h"
 #include "valid.h"
 
+static int subdir_filter(const struct dirent *ent)
+{
+       unsigned int j;
+       static char const *const skip[] = {
+               ".",
+               "..",
+               "holders",
+               "integrity",
+               "mq",
+               "power",
+               "queue",
+               "slaves",
+               "trace",
+       };
+
+       if (ent->d_type != DT_DIR)
+               return 0;
+
+       for (j = 0; j < ARRAY_SIZE(skip); j++)
+               if (!strcmp(skip[j], ent->d_name))
+                       return 0;
+       return 1;
+}
+
+static int read_partitions(const char *syspath, vector parts)
+{
+       struct scandir_result sr = { .n = 0 };
+       char path[PATH_MAX], *last;
+       char *prop;
+       int i;
+
+       strlcpy(path, syspath, sizeof(path));
+       sr.n = scandir(path, &sr.di, subdir_filter, NULL);
+       if (sr.n == -1)
+               return -errno;
+
+       pthread_cleanup_push_cast(free_scandir_result, &sr);
+
+       /* parts[0] is the whole disk */
+       if ((prop = strdup(strrchr(path, '/') + 1)) != NULL) {
+               if (vector_alloc_slot(parts))
+                       vector_set_slot(parts, prop);
+               else
+                       free(prop);
+       }
+
+       last = path + strlen(path);
+       for (i = 0; i < sr.n; i++) {
+               struct stat st;
+
+               /* only add dirs that have the "partition" attribute */
+               snprintf(last, sizeof(path) - (last - path), "/%s/partition",
+                        sr.di[i]->d_name);
+
+               if (stat(path, &st) == 0 &&
+                   (prop = strdup(sr.di[i]->d_name)) != NULL) {
+                       if (vector_alloc_slot(parts))
+                               vector_set_slot(parts, prop);
+                       else
+                               free(prop);
+               }
+       }
+
+       pthread_cleanup_pop(1);
+       return 0;
+}
+
+static int no_dots(const struct dirent *ent)
+{
+       const char *name = ent->d_name;
+
+       if (name[0] == '.' &&
+           (name[1] == '\0' || (name[1] == '.' && name[2] == '\0')))
+               return 0;
+       return 1;
+}
+
+static int check_holders(const char *syspath)
+{
+       struct scandir_result __attribute__((cleanup(free_scandir_result)))
+               sr = { .n = 0 };
+
+       sr.n = scandir(syspath, &sr.di, no_dots, NULL);
+       if (sr.n > 0)
+               condlog(4, "%s: found holders under %s", __func__, syspath);
+       return sr.n;
+}
+
+static int check_all_holders(const struct _vector *parts)
+{
+       char syspath[PATH_MAX];
+       const char *sysname;
+       unsigned int j;
+
+       if (VECTOR_SIZE(parts) == 0)
+               return 0;
+
+       if (safe_sprintf(syspath, "/sys/class/block/%s/holders",
+                        (const char *)VECTOR_SLOT(parts, 0)))
+               return -EOVERFLOW;
+
+       if (check_holders(syspath) > 0)
+               return 1;
+
+       j = 1;
+       vector_foreach_slot_after(parts, sysname, j) {
+               if (safe_sprintf(syspath, "/sys/class/block/%s/%s/holders",
+                                (const char *)VECTOR_SLOT(parts, 0), sysname))
+                       return -EOVERFLOW;
+               if (check_holders(syspath) > 0)
+                       return 1;
+       }
+       return 0;
+}
+
+static void cleanup_table(void *arg)
+{
+       if (arg)
+               mnt_free_table((struct libmnt_table *)arg);
+}
+
+static void cleanup_cache(void *arg)
+{
+       if (arg)
+#ifdef LIBMOUNT_HAS_MNT_UNREF_CACHE
+               mnt_unref_cache((struct libmnt_cache *)arg);
+#else
+               mnt_free_cache((struct libmnt_cache *)arg);
+#endif
+}
+
+/*
+ * Passed a vector of partitions and a libmount table,
+ * check if any of the partitions in the vector is referenced in the table.
+ * Note that mnt_table_find_srcpath() also resolves mounts by symlinks.
+ */
+static int check_mnt_table(const struct _vector *parts,
+                          struct libmnt_table *tbl,
+                          const char *table_name)
+{
+       unsigned int i;
+       const char *sysname;
+       char devpath[PATH_MAX];
+
+       vector_foreach_slot(parts, sysname, i) {
+               if (!safe_sprintf(devpath, "/dev/%s", sysname) &&
+                   mnt_table_find_srcpath(tbl, devpath,
+                                          MNT_ITER_FORWARD) != NULL) {
+                       condlog(4, "%s: found %s in %s", __func__,
+                               sysname, table_name);
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static int check_mountinfo(const struct _vector *parts)
+{
+       static const char mountinfo[] = "/proc/self/mountinfo";
+       struct libmnt_table *tbl;
+       struct libmnt_cache *cache;
+       FILE *stream;
+       int used = 0, ret;
+
+       tbl = mnt_new_table();
+       if (!tbl )
+               return -errno;
+
+       pthread_cleanup_push(cleanup_table, tbl);
+       cache = mnt_new_cache();
+       if (cache) {
+               pthread_cleanup_push(cleanup_cache, cache);
+               if (mnt_table_set_cache(tbl, cache) == 0) {
+                       stream = fopen(mountinfo, "r");
+                       if (stream != NULL) {
+                               pthread_cleanup_push(cleanup_fclose, stream);
+                               ret = mnt_table_parse_stream(tbl, stream, mountinfo);
+                               pthread_cleanup_pop(1);
+
+                               if (ret == 0)
+                                       used = check_mnt_table(parts, tbl,
+                                                              "mountinfo");
+                       }
+               }
+               pthread_cleanup_pop(1);
+       }
+       pthread_cleanup_pop(1);
+       return used;
+}
+
+#ifdef LIBMOUNT_SUPPORTS_SWAP
+static int check_swaps(const struct _vector *parts)
+{
+       struct libmnt_table *tbl;
+       struct libmnt_cache *cache;
+       int used = 0, ret;
+
+       tbl = mnt_new_table();
+       if (!tbl )
+               return -errno;
+
+       pthread_cleanup_push(cleanup_table, tbl);
+       cache = mnt_new_cache();
+       if (cache) {
+               pthread_cleanup_push(cleanup_cache, cache);
+               if (mnt_table_set_cache(tbl, cache) == 0) {
+                       ret = mnt_table_parse_swaps(tbl, NULL);
+                       if (ret == 0)
+                               used = check_mnt_table(parts, tbl, "swaps");
+               }
+               pthread_cleanup_pop(1);
+       }
+       pthread_cleanup_pop(1);
+       return used;
+}
+#else
+static int check_swaps(const struct _vector *parts __attribute__((unused)))
+{
+       return 0;
+}
+#endif
+
+
+/*
+ * Given a block device, check if the device itself or any of its
+ * partitions is in use
+ * - by sysfs holders (e.g. LVM)
+ * - mounted according to /proc/self/mountinfo
+ * - used as swap
+ */
+static int is_device_in_use(struct udev_device *udevice)
+{
+       const char *syspath;
+       vector parts;
+       int used = 0, ret;
+
+       syspath = udev_device_get_syspath(udevice);
+       if (!syspath)
+               return -ENOMEM;
+
+       parts = vector_alloc();
+       if (!parts)
+               return -ENOMEM;
+
+       pthread_cleanup_push_cast(free_strvec, parts);
+       if ((ret = read_partitions(syspath, parts)) == 0)
+               used =  check_all_holders(parts) > 0 ||
+                       check_mountinfo(parts) > 0 ||
+                       check_swaps(parts) > 0;
+       pthread_cleanup_pop(1);
+
+       if (ret < 0)
+               return ret;
+
+       condlog(3, "%s: %s is %sin use", __func__, syspath, used ? "" : "not ");
+       return used;
+}
+
 int
 is_path_valid(const char *name, struct config *conf, struct path *pp,
              bool check_multipathd)
 {
        int r;
        int fd;
+       const char *prop;
 
        if (!pp || !name || !conf)
                return PATH_IS_ERROR;
@@ -53,23 +314,11 @@ is_path_valid(const char *name, struct config *conf, struct path *pp,
                return PATH_IS_VALID_NO_CHECK;
        }
 
-       /*
-        * "multipath -u" may be run before the daemon is started. In this
-        * case, systemd might own the socket but might delay multipathd
-        * startup until some other unit (udev settle!)  has finished
-        * starting. With many LUNs, the listen backlog may be exceeded, which
-        * would cause connect() to block. This causes udev workers calling
-        * "multipath -u" to hang, and thus creates a deadlock, until "udev
-        * settle" times out.  To avoid this, call connect() in non-blocking
-        * mode here, and take EAGAIN as indication for a filled-up systemd
-        * backlog.
-        */
-
        if (check_multipathd) {
                fd = __mpath_connect(1);
                if (fd < 0) {
-                       if (errno != EAGAIN && !systemd_service_enabled(name)) {
-                               condlog(3, "multipathd not running or enabled");
+                       if (errno != EAGAIN) {
+                               condlog(3, "multipathd not running");
                                return PATH_IS_NOT_VALID;
                        }
                } else
@@ -80,6 +329,10 @@ is_path_valid(const char *name, struct config *conf, struct path *pp,
        if (!pp->udev)
                return PATH_IS_ERROR;
 
+       prop = udev_device_get_property_value(pp->udev, "DEVTYPE");
+       if (prop == NULL || strcmp(prop, "disk"))
+               return PATH_IS_NOT_VALID;
+
        r = pathinfo(pp, conf, DI_SYSFS | DI_WWID | DI_BLACKLIST);
        if (r == PATHINFO_SKIPPED)
                return PATH_IS_NOT_VALID;
@@ -96,6 +349,11 @@ is_path_valid(const char *name, struct config *conf, struct path *pp,
                return PATH_IS_ERROR;
        }
 
+       if ((conf->find_multipaths == FIND_MULTIPATHS_GREEDY ||
+            conf->find_multipaths == FIND_MULTIPATHS_SMART) &&
+           is_device_in_use(pp->udev) > 0)
+               return PATH_IS_NOT_VALID;
+
        if (conf->find_multipaths == FIND_MULTIPATHS_GREEDY)
                return PATH_IS_VALID;
 
index 2da0045d4c84c90e983a632ff71ac6bfef562678..9c549c1a74dd7381da9f27d9806b4a0a040af48e 100644 (file)
@@ -20,9 +20,9 @@
 #ifndef _VERSION_H
 #define _VERSION_H
 
-#define VERSION_CODE 0x000904
+#define VERSION_CODE 0x000907
 /* MMDDYY, in hex */
-#define DATE_CODE    0x0C1316
+#define DATE_CODE    0x0B1017
 
 #define PROG    "multipath-tools"
 
index 89bb60caa96d5a29c33c3476895fa0d30f647f79..591cd09b5a1f375eefe1b3baedad672f5b0d1d2b 100644 (file)
@@ -94,12 +94,8 @@ replace_wwids(vector mp)
        struct multipath * mpp;
        size_t len;
        int ret = -1;
-       struct config *conf;
 
-       conf = get_multipath_config();
-       pthread_cleanup_push(put_multipath_config, conf);
-       fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
-       pthread_cleanup_pop(1);
+       fd = open_file(DEFAULT_WWIDS_FILE, &can_write, WWIDS_FILE_HEADER);
        if (fd < 0)
                goto out;
 
@@ -200,7 +196,6 @@ remove_wwid(char *wwid) {
        int len, can_write;
        char *str;
        int ret = -1;
-       struct config *conf;
 
        len = strlen(wwid) + 4; /* two slashes the newline and a zero byte */
        str = malloc(len);
@@ -216,10 +211,7 @@ remove_wwid(char *wwid) {
                goto out;
        }
        condlog(3, "removing line '%s' from wwids file", str);
-       conf = get_multipath_config();
-       pthread_cleanup_push(put_multipath_config, conf);
-       fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
-       pthread_cleanup_pop(1);
+       fd = open_file(DEFAULT_WWIDS_FILE, &can_write, WWIDS_FILE_HEADER);
 
        if (fd < 0) {
                ret = -1;
@@ -244,12 +236,8 @@ check_wwids_file(char *wwid, int write_wwid)
 {
        int fd, can_write, found, ret;
        FILE *f;
-       struct config *conf;
 
-       conf = get_multipath_config();
-       pthread_cleanup_push(put_multipath_config, conf);
-       fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
-       pthread_cleanup_pop(1);
+       fd = open_file(DEFAULT_WWIDS_FILE, &can_write, WWIDS_FILE_HEADER);
        if (fd < 0)
                return -1;
 
index f57c105c6861fb643b4a4e9e35ee4fc3be46f5c1..f37494674ab1eada34bb87a054a255d9364925f5 100644 (file)
@@ -8,10 +8,11 @@ LIBDEPS += -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath \
        -L$(mpathutildir) -lmpathutil -L$(mpathcmddir) -lmpathcmd -lpthread -ldevmapper -ludev
 
 EXEC = mpathpersist
+MANPAGES := mpathpersist.8
 
 OBJS = main.o
 
-all: $(EXEC)
+all: $(EXEC) $(MANPAGES)
 
 $(EXEC): $(OBJS)
        $(Q)$(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) $(CFLAGS) $(LIBDEPS)
@@ -23,7 +24,7 @@ install:
        $(Q)$(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(mandir)/man8
 
 clean: dep_clean
-       $(Q)$(RM) core *.o $(EXEC)
+       $(Q)$(RM) core *.o $(EXEC) $(MANPAGES)
 
 include $(wildcard $(OBJS:.o=.d))
 
diff --git a/mpathpersist/mpathpersist.8 b/mpathpersist/mpathpersist.8
deleted file mode 100644 (file)
index d594422..0000000
+++ /dev/null
@@ -1,300 +0,0 @@
-.\" ----------------------------------------------------------------------------
-.\" Update the date below if you make any significant change.
-.\" Make sure there are no errors with:
-.\" groff -z -wall -b -e -t mpathpersist/mpathpersist.8
-.\"
-.\" ----------------------------------------------------------------------------
-.
-.TH MPATHPERSIST 8 2021-11-12 "Linux"
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH NAME
-.\" ----------------------------------------------------------------------------
-.
-mpathpersist \- Manages SCSI persistent reservations on dm multipath devices.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH SYNOPSIS
-.\" ----------------------------------------------------------------------------
-.
-.B mpathpersist
-.RB [\| OPTIONS \|]
-.I device
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH DESCRIPTION
-.\" ----------------------------------------------------------------------------
-.
-This utility is used to manage SCSI persistent reservations on Device Mapper
-Multipath devices. To be able to use this functionality, the \fIreservation_key\fR
-attribute must be defined in the \fI/etc/multipath.conf\fR file. Otherwise the
-\fBmultipathd\fR daemon will not check for persistent reservation for newly
-discovered paths or reinstated paths.
-.
-.LP
-\fBmpathpersist\fR supports the same command-line options as the
-\fBsg_persist\fR utility.
-.
-Consult the \fBsg_persist (8)\fR manual page for an in-depth discussion of the
-various options.
-.
-.\" ----------------------------------------------------------------------------
-.SH OPTIONS
-.\" ----------------------------------------------------------------------------
-.
-.TP
-.BI \-verbose|\-v " level"
-Verbosity:
-.RS
-.TP 5
-.I 0
-Critical messages.
-.TP
-.I 1
-Error messages.
-.TP
-.I 2
-Warning messages.
-.TP
-.I 3
-Informational messages.
-.TP
-.I 4
-Informational messages with trace enabled.
-.RE
-.
-.TP
-.BI \--device=\fIDEVICE\fB|\-d " DEVICE"
-Query or change DEVICE.
-.
-.TP
-.BI \--batch-file=\fIDEVICE\fB|\-f " FILE"
-Read commands from \fIFILE\fR. See section \(dqBATCH FILES\(dq below. This
-option can be given at most once.
-.
-.TP
-.B \--help|\-h
-Output this usage message.
-.
-.TP
-.B \--hex|\-H
-Output response in hex.
-.
-.TP
-.B \--in|\-i
-Request PR In command.
-.
-.TP
-.B \--out|\-o
-Request PR Out command.
-.
-.TP
-.B \--param-alltgpt|\-Y
-PR Out parameter 'ALL_TG_PT'.
-.
-.TP
-.B \--param-aptpl|\-Z
-PR Out parameter 'APTPL'.
-.
-.TP
-.B \--read-keys|\-k
-PR In: Read Keys.
-.
-.TP
-.BI \--param-rk=\fIRK\fB|\-K " RK"
-PR Out parameter reservation key (RK is in hex, up to 8 bytes).
-.
-.TP
-.BI \--param-sark=\fISARK\fB|\-S " SARK"
-PR Out parameter service action reservation key (SARK is in hex).
-.
-.TP
-.B \--preempt|\-P
-PR Out: Preempt.
-.
-.TP
-.B \--clear|\-C
-PR Out: Clear registrations.
-.
-.TP
-.B \--preempt-abort|\-A
-PR Out: Preempt and Abort.
-.
-.TP
-.BI \--prout-type=\fITYPE\fB|\-T " TYPE"
-PR Out command type.
-.
-.TP
-.B \--read-full-status|\-s
-PR In: Read Full Status.
-.
-.TP
-.B \--read-keys|\-k
-PR In: Read Keys.
-.
-.TP
-.B \--read-reservation|\-r
-PR In: Read Reservation.
-.
-.TP
-.B \--register|\-G
-PR Out: Register.
-.
-.TP
-.B \--register-ignore|\-I
-PR Out: Register and Ignore.
-.
-.TP
-.B \--release|\-L
-PR Out: Release.
-.
-.TP
-.B \--report-capabilities|\-c
-PR In: Report Capabilities.
-.
-.TP
-.B \--reserve|\-R
-PR Out: Reserve.
-.
-.TP
-.BI \--transport-id=\fITIDS\fB|\-X " TIDS"
-TransportIDs can be mentioned in several forms.
-.
-.TP
-.BI \--alloc-length=\fILEN\fB|\-l " LEN"
-PR In: maximum allocation length. LEN is a decimal number between 0 and 8192.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH EXAMPLE
-.\" ----------------------------------------------------------------------------
-.
-.PP
-Register the key \(dq123abc\(dq for the /dev/mapper/mpath9 device:
-.RS
-\fBmpathpersist --out --register --param-sark=\fI123abc /dev/mapper/mpath9\fR
-.RE
-.PP
-Read registered reservation keys for the /dev/mapper/mpath9 device:
-.RS
-\fBmpathpersist -i -k \fI/dev/mapper/mpath9\fR
-.RE
-.PP
-Create a reservation for the /dev/mapper/mpath9 device with the given
-reservation key:
-.RS
-\fBmpathpersist --out --reserve --param-rk=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR
-.RE
-.PP
-Read the reservation status of the /dev/mapper/mpath9 device:
-.RS
-\fBmpathpersist -i -s -d \fI/dev/mapper/mpath9\fR
-.RE
-.PP
-Release the previously created reservation (note that the prout-type needs to
-be the same as above):
-.RS
-\fBmpathpersist --out --release --param-rk=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR
-.RE
-.PP
-Remove the current key registered for this host (i.e. reset it to 0):
-.RS
-\fBmpathpersist --out --register-ignore -K \fI123abc\fB -S \fI0\fB \fI/dev/mapper/mpath9\fR
-.RE
-.PP
-Remove current reservation, and unregister all registered keys from all I_T nexuses:
-.RS
-\fBmpathpersist -oCK \fI123abc \fI/dev/mapper/mpath9\fR
-.RE
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH BATCH FILES
-.\" ----------------------------------------------------------------------------
-.
-.PP
-The option \fI--batch-file\fR (\fI-f\fR) sets an input file to be processed
-by \fBmpathpersist\fR. Grouping commands in batch files can provide a speed
-improvement in particular on large installments, because \fBmpathpersist\fR
-needs to scan existing paths and maps only once during startup.
-.
-.PP
-The input file is a text file that is parsed
-line by line. Every line of the file is interpreted as a command line
-(i.e. list of options and parameters) for \fBmpathpersist\fR. Options
-and parameters are separated by one or more whitespace characters (space or TAB).
-Lines can, but do not have to, begin with the word \(dqmpathpersist\(dq.
-The \(dq#\(dq character, either at the beginning of the line or following
-some whitespace, denotes the start of a comment that lasts until the end of the
-line. Empty lines are allowed. Continuation of mpathpersist commands over
-multiple lines is not supported.
-.
-.PP
-All options listed in this man page, except \fI-f\fR and
-\fI-v\fR, are allowed in batch files. Both short and long option formats may be used.
-Using the \fI-f\fR option inside the batch file is an error. The \fI-v\fR
-option is ignored in batch files.
-.
-.PP
-The multipath map on which to act must be specified on every input line, e.g. using the \fI-d\fR option.
-Commands acting on different multipath maps may be combined in a
-batch file, and multiple commands may act on the same multipath
-map. Commands are executed one by one, so
-that commands further down in the file see status changes caused by previous
-commands.
-If \fBmpathpersist\fR encounters an error while processing a line in the
-batch file, batch file processing is \fBnot\fR aborted; subsequent commands
-are executed nonetheless. The exit status of \fBmpathpersist\fR is the status
-of the first failed command, or 0 if all commands succeeded.
-.
-.PP
-If other options and parameters are used along with
-\fI-f\fR on the \fBmpathpersist\fR command line, the command line will be executed first, followed
-by the commands from the batch file.
-.
-.PP
-Below is an example of a valid batch input file.
-.
-.PP
-.RS
-.EX
-# This is an mpathpersist input file.
-# Short and long forms of the same command
--i -k /dev/dm-1 # short form, this comment is ignored
-mpathpersist --in --read-keys --device=/dev/dm-1
-
-# Mixing of long and short options, variable white space
-  --out  --register    -S  abcde     /dev/dm-1
-
-# Mixing of commands for different maps
--ir /dev/dm-0
--ir /dev/dm-1
-
-mpathpersist --out --param-rk abcde --reserve --prout-type 5 /dev/dm-1
-# This should now show a reservation
--ir /dev/dm-1
--oCK abcde /dev/dm-1
---in --read-reservation /dev/dm-1
-.EE
-.RE
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "SEE ALSO"
-.\" ----------------------------------------------------------------------------
-.
-.BR multipath (8),
-.BR multipathd (8),
-.BR sg_persist (8).
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH AUTHORS
-.\" ----------------------------------------------------------------------------
-.
-\fImultipath-tools\fR was developed by Christophe Varoqui <christophe.varoqui@opensvc.com>
-and others.
-.\" EOF
diff --git a/mpathpersist/mpathpersist.8.in b/mpathpersist/mpathpersist.8.in
new file mode 100644 (file)
index 0000000..fecef0d
--- /dev/null
@@ -0,0 +1,301 @@
+.\" ----------------------------------------------------------------------------
+.\" Make sure there are no errors with:
+.\" groff -z -wall -b -e -t mpathpersist/mpathpersist.8
+.\" man --warnings -E UTF-8 -l -Tutf8 -Z mpathpersist/mpathpersist.8 > /dev/null
+.\"
+.\" Update the date below if you make any significant change.
+.\" ----------------------------------------------------------------------------
+.
+.TH MPATHPERSIST 8 2021-11-12 Linux
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH NAME
+.\" ----------------------------------------------------------------------------
+.
+mpathpersist \- Manages SCSI persistent reservations on dm multipath devices.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH SYNOPSIS
+.\" ----------------------------------------------------------------------------
+.
+.B mpathpersist
+.RB [\| OPTIONS \|]
+.I device
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH DESCRIPTION
+.\" ----------------------------------------------------------------------------
+.
+This utility is used to manage SCSI persistent reservations on Device Mapper
+Multipath devices. To be able to use this functionality, the \fIreservation_key\fR
+attribute must be defined in the \fI@CONFIGFILE@\fR file. Otherwise the
+\fBmultipathd\fR daemon will not check for persistent reservation for newly
+discovered paths or reinstated paths.
+.
+.LP
+\fBmpathpersist\fR supports the same command-line options as the
+\fBsg_persist\fR utility.
+.
+Consult the \fBsg_persist (8)\fR manual page for an in-depth discussion of the
+various options.
+.
+.\" ----------------------------------------------------------------------------
+.SH OPTIONS
+.\" ----------------------------------------------------------------------------
+.
+.TP
+.BI \-verbose|\-v " level"
+Verbosity:
+.RS
+.TP 5
+.I 0
+Critical messages.
+.TP
+.I 1
+Error messages.
+.TP
+.I 2
+Warning messages.
+.TP
+.I 3
+Informational messages.
+.TP
+.I 4
+Informational messages with trace enabled.
+.RE
+.
+.TP
+.BI \--device=\fIDEVICE\fB|\-d " DEVICE"
+Query or change DEVICE.
+.
+.TP
+.BI \--batch-file=\fIDEVICE\fB|\-f " FILE"
+Read commands from \fIFILE\fR. See section \(dqBATCH FILES\(dq below. This
+option can be given at most once.
+.
+.TP
+.B \--help|\-h
+Output this usage message.
+.
+.TP
+.B \--hex|\-H
+Output response in hex.
+.
+.TP
+.B \--in|\-i
+Request PR In command.
+.
+.TP
+.B \--out|\-o
+Request PR Out command.
+.
+.TP
+.B \--param-alltgpt|\-Y
+PR Out parameter 'ALL_TG_PT'.
+.
+.TP
+.B \--param-aptpl|\-Z
+PR Out parameter 'APTPL'.
+.
+.TP
+.B \--read-keys|\-k
+PR In: Read Keys.
+.
+.TP
+.BI \--param-rk=\fIRK\fB|\-K " RK"
+PR Out parameter reservation key (RK is in hex, up to 8 bytes).
+.
+.TP
+.BI \--param-sark=\fISARK\fB|\-S " SARK"
+PR Out parameter service action reservation key (SARK is in hex).
+.
+.TP
+.B \--preempt|\-P
+PR Out: Preempt.
+.
+.TP
+.B \--clear|\-C
+PR Out: Clear registrations.
+.
+.TP
+.B \--preempt-abort|\-A
+PR Out: Preempt and Abort.
+.
+.TP
+.BI \--prout-type=\fITYPE\fB|\-T " TYPE"
+PR Out command type.
+.
+.TP
+.B \--read-full-status|\-s
+PR In: Read Full Status.
+.
+.TP
+.B \--read-keys|\-k
+PR In: Read Keys.
+.
+.TP
+.B \--read-reservation|\-r
+PR In: Read Reservation.
+.
+.TP
+.B \--register|\-G
+PR Out: Register.
+.
+.TP
+.B \--register-ignore|\-I
+PR Out: Register and Ignore.
+.
+.TP
+.B \--release|\-L
+PR Out: Release.
+.
+.TP
+.B \--report-capabilities|\-c
+PR In: Report Capabilities.
+.
+.TP
+.B \--reserve|\-R
+PR Out: Reserve.
+.
+.TP
+.BI \--transport-id=\fITIDS\fB|\-X " TIDS"
+TransportIDs can be mentioned in several forms.
+.
+.TP
+.BI \--alloc-length=\fILEN\fB|\-l " LEN"
+PR In: maximum allocation length. LEN is a decimal number between 0 and 8192.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH EXAMPLE
+.\" ----------------------------------------------------------------------------
+.
+.PP
+Register the key \(dq123abc\(dq for the /dev/mapper/mpath9 device:
+.RS
+\fBmpathpersist --out --register --param-sark=\fI123abc /dev/mapper/mpath9\fR
+.RE
+.PP
+Read registered reservation keys for the /dev/mapper/mpath9 device:
+.RS
+\fBmpathpersist -i -k \fI/dev/mapper/mpath9\fR
+.RE
+.PP
+Create a reservation for the /dev/mapper/mpath9 device with the given
+reservation key:
+.RS
+\fBmpathpersist --out --reserve --param-rk=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR
+.RE
+.PP
+Read the reservation status of the /dev/mapper/mpath9 device:
+.RS
+\fBmpathpersist -i -s -d \fI/dev/mapper/mpath9\fR
+.RE
+.PP
+Release the previously created reservation (note that the prout-type needs to
+be the same as above):
+.RS
+\fBmpathpersist --out --release --param-rk=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR
+.RE
+.PP
+Remove the current key registered for this host (i.e. reset it to 0):
+.RS
+\fBmpathpersist --out --register-ignore -K \fI123abc\fB -S \fI0\fB \fI/dev/mapper/mpath9\fR
+.RE
+.PP
+Remove current reservation, and unregister all registered keys from all I_T nexuses:
+.RS
+\fBmpathpersist -oCK \fI123abc \fI/dev/mapper/mpath9\fR
+.RE
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH BATCH FILES
+.\" ----------------------------------------------------------------------------
+.
+.PP
+The option \fI--batch-file\fR (\fI-f\fR) sets an input file to be processed
+by \fBmpathpersist\fR. Grouping commands in batch files can provide a speed
+improvement in particular on large installments, because \fBmpathpersist\fR
+needs to scan existing paths and maps only once during startup.
+.
+.PP
+The input file is a text file that is parsed
+line by line. Every line of the file is interpreted as a command line
+(i.e. list of options and parameters) for \fBmpathpersist\fR. Options
+and parameters are separated by one or more whitespace characters (space or TAB).
+Lines can, but do not have to, begin with the word \(dqmpathpersist\(dq.
+The \(dq#\(dq character, either at the beginning of the line or following
+some whitespace, denotes the start of a comment that lasts until the end of the
+line. Empty lines are allowed. Continuation of mpathpersist commands over
+multiple lines is not supported.
+.
+.PP
+All options listed in this man page, except \fI-f\fR and
+\fI-v\fR, are allowed in batch files. Both short and long option formats may be used.
+Using the \fI-f\fR option inside the batch file is an error. The \fI-v\fR
+option is ignored in batch files.
+.
+.PP
+The multipath map on which to act must be specified on every input line, e.g. using the \fI-d\fR option.
+Commands acting on different multipath maps may be combined in a
+batch file, and multiple commands may act on the same multipath
+map. Commands are executed one by one, so
+that commands further down in the file see status changes caused by previous
+commands.
+If \fBmpathpersist\fR encounters an error while processing a line in the
+batch file, batch file processing is \fBnot\fR aborted; subsequent commands
+are executed nonetheless. The exit status of \fBmpathpersist\fR is the status
+of the first failed command, or 0 if all commands succeeded.
+.
+.PP
+If other options and parameters are used along with
+\fI-f\fR on the \fBmpathpersist\fR command line, the command line will be executed first, followed
+by the commands from the batch file.
+.
+.PP
+Below is an example of a valid batch input file.
+.
+.PP
+.RS
+.EX
+# This is an mpathpersist input file.
+# Short and long forms of the same command
+-i -k /dev/dm-1 # short form, this comment is ignored
+mpathpersist --in --read-keys --device=/dev/dm-1
+
+# Mixing of long and short options, variable white space
+  --out  --register    -S  abcde     /dev/dm-1
+
+# Mixing of commands for different maps
+-ir /dev/dm-0
+-ir /dev/dm-1
+
+mpathpersist --out --param-rk abcde --reserve --prout-type 5 /dev/dm-1
+# This should now show a reservation
+-ir /dev/dm-1
+-oCK abcde /dev/dm-1
+--in --read-reservation /dev/dm-1
+.EE
+.RE
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "SEE ALSO"
+.\" ----------------------------------------------------------------------------
+.
+.BR multipath (8),
+.BR multipathd (8),
+.BR sg_persist (8).
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH AUTHORS
+.\" ----------------------------------------------------------------------------
+.
+\fImultipath-tools\fR was developed by Christophe Varoqui <christophe.varoqui@opensvc.com>
+and others.
+.\" EOF
index d191ae8deebb619a444f3fcbdea67a6a87242ec3..c339f52140057dfefb998ffe02ea7ad1001d617b 100644 (file)
@@ -14,7 +14,7 @@ ENV{.MPATH_DEVICE_READY_OLD}="$env{MPATH_DEVICE_READY}"
 # multipath sets DM_SUBSYSTEM_UDEV_FLAG2 when it reloads a
 # table with no active devices. If this happens, mark the
 # device not ready
-ENV{DM_SUBSYSTEM_UDEV_FLAG2}=="1", ENV{MPATH_DEVICE_READY}="0",\
+ENV{DM_SUBSYSTEM_UDEV_FLAG2}=="1", ENV{MPATH_DEVICE_READY}="0", \
        GOTO="mpath_action"
 
 # If the last path has failed mark the device not ready
@@ -68,13 +68,13 @@ ENV{MPATH_DEVICE_READY}=="0", ENV{DM_NOSCAN}="1"
 # Also skip all foreign rules if no path is available.
 # Remember the original value of DM_DISABLE_OTHER_RULES_FLAG
 # and restore it back once we have at least one path available.
-ENV{MPATH_DEVICE_READY}=="0", ENV{.MPATH_DEVICE_READY_OLD}=="1",\
-       ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}=="",\
+ENV{MPATH_DEVICE_READY}=="0", ENV{.MPATH_DEVICE_READY_OLD}=="1", \
+       ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}=="", \
        ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="$env{DM_UDEV_DISABLE_OTHER_RULES_FLAG}"
 ENV{MPATH_DEVICE_READY}=="0", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
-ENV{MPATH_DEVICE_READY}!="0", ENV{.MPATH_DEVICE_READY_OLD}=="0",\
-       ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}",\
-       ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="",\
+ENV{MPATH_DEVICE_READY}!="0", ENV{.MPATH_DEVICE_READY_OLD}=="0", \
+       ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}", \
+       ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="", \
        ENV{DM_ACTIVATION}="1", ENV{MPATH_UNCHANGED}="0"
 
 # The code to check multipath state ends here. We need to set
index 73db991a213fdfc7fff58c983c68f0c90e08b7de..0efb9b2661fdc2150b2ac0eedebff6fd55ed8b6f 100644 (file)
@@ -3,7 +3,9 @@
 #
 include ../Makefile.inc
 
-EXEC := multipath
+EXEC      := multipath
+MANPAGES  := multipath.8 multipath.conf.5
+GENERATED := $(MANPAGES) multipath.rules tmpfiles.conf
 
 CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathcmddir)
 CFLAGS += $(BIN_CFLAGS)
@@ -13,7 +15,7 @@ LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathutildir) -lmpathutil \
 
 OBJS := main.o
 
-all: $(EXEC) multipath.rules tmpfiles.conf
+all: $(EXEC) $(GENERATED)
 
 $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
        @echo building $@ because of $?
@@ -25,14 +27,16 @@ install:
        $(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir)
        $(Q)$(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir)
        $(Q)$(INSTALL_PROGRAM) -m 644 multipath.rules $(DESTDIR)$(udevrulesdir)/56-multipath.rules
-       $(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(modulesloaddir)
-       $(Q)$(INSTALL_PROGRAM) -m 644 modules-load.conf $(DESTDIR)$(modulesloaddir)/multipath.conf
        $(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(tmpfilesdir)
        $(Q)$(INSTALL_PROGRAM) -m 644 tmpfiles.conf $(DESTDIR)$(tmpfilesdir)/multipath.conf
        $(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir)/man8
        $(Q)$(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(mandir)/man8
        $(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir)/man5
        $(Q)$(INSTALL_PROGRAM) -m 644 $(EXEC).conf.5 $(DESTDIR)$(mandir)/man5
+       $(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(modulesloaddir)
+ifeq ($(MODPROBE_UNIT),)
+       $(Q)$(INSTALL_PROGRAM) -m 644 modules-load.conf $(DESTDIR)$(modulesloaddir)/multipath.conf
+endif
 ifneq ($(SCSI_DH_MODULES_PRELOAD),)
        $(Q)$(INSTALL_PROGRAM) -m 644 scsi_dh.conf $(DESTDIR)$(modulesloaddir)/scsi_dh.conf
        $(Q)for _x in $(SCSI_DH_MODULES_PRELOAD); do echo "$$_x"; done \
@@ -47,15 +51,12 @@ uninstall:
        $(Q)$(RM) $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules
        $(Q)$(RM) $(DESTDIR)$(mandir)/man8/$(EXEC).8
        $(Q)$(RM) $(DESTDIR)$(mandir)/man5/$(EXEC).conf.5
+       $(Q)$(RM) $(DESTDIR)$(tmpfilesdir)/multipath.conf
 
 clean: dep_clean
-       $(Q)$(RM) core *.o $(EXEC) multipath.rules tmpfiles.conf
+       $(Q)$(RM) core *.o $(EXEC) $(GENERATED)
 
 include $(wildcard $(OBJS:.o=.d))
 
 dep_clean:
        $(Q)$(RM) $(OBJS:.o=.d)
-
-%:     %.in
-       @echo creating $@
-       $(Q)sed 's,@RUNTIME_DIR@,$(runtimedir),' $< >$@
index b9f360b4213c756eabdc54bb67527cad89d72b24..9e1c5052b8c11e2dde5e51dc99be9f92d4de08f6 100644 (file)
@@ -157,6 +157,7 @@ usage (char * progname)
                "          . group_by_serial     one priority group per serial\n"
                "          . group_by_prio       one priority group per priority lvl\n"
                "          . group_by_node_name  one priority group per target node\n"
+               "          . group_by_tpg        one priority group per ALUA target port group\n"
                "  -v lvl  verbosity level:\n"
                "          . 0 no output\n"
                "          . 1 print created devmap names only\n"
@@ -607,6 +608,8 @@ check_path_valid(const char *name, struct config *conf, bool is_uevent)
        pp = alloc_path();
        if (!pp)
                return RTVL_FAIL;
+       if (is_uevent)
+               pp->can_use_env_uid = true;
 
        r = is_path_valid(name, conf, pp, is_uevent);
        if (r <= PATH_IS_ERROR || r >= PATH_MAX_VALID_RESULT)
@@ -840,6 +843,8 @@ main (int argc, char *argv[])
        conf->force_sync = 1;
        if (atexit(cleanup_vecs))
                condlog(1, "failed to register cleanup handler for vecs: %m");
+       if (atexit(cleanup_bindings))
+               condlog(1, "failed to register cleanup handler for bindings: %m");
        while ((arg = getopt(argc, argv, ":adDcChl::eFfM:v:p:b:BrR:itTquUwW")) != EOF ) {
                switch(arg) {
                case 'v':
@@ -851,7 +856,7 @@ main (int argc, char *argv[])
                        libmp_verbosity = atoi(optarg);
                        break;
                case 'b':
-                       conf->bindings_file = strdup(optarg);
+                       condlog(1, "option -b ignored");
                        break;
                case 'B':
                        conf->bindings_read_only = 1;
@@ -1020,7 +1025,7 @@ main (int argc, char *argv[])
        }
 
        if (check_alias_settings(conf)) {
-               fprintf(stderr, "fatal configuration error, aborting");
+               fprintf(stderr, "fatal configuration error, aborting\n");
                exit(RTVL_FAIL);
        }
 
diff --git a/multipath/multipath.8 b/multipath/multipath.8
deleted file mode 100644 (file)
index 88149d5..0000000
+++ /dev/null
@@ -1,294 +0,0 @@
-.\" ----------------------------------------------------------------------------
-.\" Update the date below if you make any significant change.
-.\" Make sure there are no errors with:
-.\" groff -z -wall -b -e -t multipath/multipath.8
-.\"
-.\" ----------------------------------------------------------------------------
-.
-.TH MULTIPATH 8 2021-11-12 "Linux"
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH NAME
-.\" ----------------------------------------------------------------------------
-.
-multipath \- Device mapper target autoconfig.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH SYNOPSIS
-.\" ----------------------------------------------------------------------------
-.
-.B multipath
-.RB [\| \-v\ \c
-.IR level \|]
-.RB [\| \-B | \-d | \-i | \-q | \-r \|]
-.RB [\| \-b\ \c
-.IR file \|]
-.RB [\| \-p\ \c
-.IR policy \|]
-.RB [\| device \|]
-.
-.LP
-.B multipath
-.RB [\| \-v\ \c
-.IR level \|]
-.RB [\| \-R\ \c
-.IR retries \|]
-.B \-f device
-.
-.LP
-.B multipath
-.RB [\| \-v\ \c
-.IR level \|]
-.RB [\| \-R\ \c
-.IR retries \|]
-.B \-F
-.
-.LP
-.B multipath
-.RB [\| \-v\ \c
-.IR level \|]
-.RB [\| \-l | \-ll \|]
-.RB [\| device \|]
-.
-.LP
-.B multipath
-.RB [\| \-v\ \c
-.IR level \|]
-.RB [\| \-a | \-w \|]
-.B device
-.
-.LP
-.B multipath
-.RB [\| \-v\ \c
-.IR level \|]
-.B -W
-.
-.LP
-.B multipath
-.RB [\| \-v\ \c
-.IR level \|]
-.RB [\| \-i \|]
-.RB [\| \-c | \-C \|]
-.B device
-.
-.LP
-.B multipath
-.RB [\| \-v\ \c
-.IR level \|]
-.RB [\| \-i \|]
-.RB [\| \-u | \-U \|]
-.
-.LP
-.B multipath
-.RB [\| \-h | \-t | \-T \|]
-.
-.\" ----------------------------------------------------------------------------
-.SH DESCRIPTION
-.\" ----------------------------------------------------------------------------
-.
-.B multipath
-is used to detect and coalesce multiple paths to devices, for fail-over or performance reasons.
-.
-.\" ----------------------------------------------------------------------------
-.SH ARGUMENTS
-.\" ----------------------------------------------------------------------------
-.
-The \fBdevice\fR argument restricts \fBmultipath\fR's operation to devices matching the given
-expression. The argument may refer either to a multipath map or to
-its components ("paths"). The expression may be in one of the following formats:
-.
-.TP 1.4i
-.B device node
-file name of a device node, e.g. \fI/dev/dm-10\fR or \fI/dev/sda\fR. If the node refers
-to an existing device mapper device representing a multipath map, this selects
-the map or its paths, depending on the operation mode. Otherwise, it selects a path device.
-.
-.TP
-.B device ID
-kernel device number specified by major:minor numbers, e.g. \fI65:16\fR. This
-format can only be used for path devices.
-.
-.TP
-.B WWID
-a World Wide Identifier matching a multipath map or its paths. To list WWIDs of devices
-present in the system, use e.g. the command "\fImultipath -d -v3 2>/dev/null\fR".
-.
-.\" ----------------------------------------------------------------------------
-.SH OPERATION MODES
-.\" ----------------------------------------------------------------------------
-.
-The default operation mode is to detect and set up multipath maps from the devices found in
-the system.
-.
-Other operation modes are chosen by using one of the following command line switches:
-.TP
-.B \-f
-Flush (remove) a multipath device map specified as parameter, if unused. This operation is delegated to the multipathd daemon if it's running.
-.
-.TP
-.B \-F
-Flush (remove) all unused multipath device maps. This operation is delegated to the multipathd daemon if it's running.
-.
-.TP
-.B \-l
-Show ("list") the current multipath topology from information fetched in sysfs and the device mapper.
-.
-.TP
-.B \-ll
-Show ("list") the current multipath topology from all available information (sysfs, the
-device mapper, path checkers ...).
-.
-.TP
-.B \-a
-Add the WWID for the specified device to the WWIDs file.
-.
-.TP
-.B \-w
-Remove the WWID for the specified device from the WWIDs file.
-.
-.TP
-.B \-W
-Reset the WWIDs file to only include the current multipath devices.
-.
-.TP
-.B \-c
-Check if a block device should be a path in a multipath device.
-.
-.TP
-.B \-C
-Check if a multipath device has usable paths. This can be used to
-test whether or not I/O on this device is likely to succeed. The command
-itself doesn't attempt to do I/O on the device.
-.
-.TP
-.B \-u
-Check if the device specified in the program environment should be
-a path in a multipath device.
-.
-.TP
-.B \-U
-Check if the device specified in the program environment is a multipath device
-with usable paths. See \fB-C\fB.
-.
-.TP
-.B \-h
-Print usage text.
-.
-.TP
-.B \-t
-Display the currently used multipathd configuration.
-.
-.TP
-.B \-T
-Display the currently used multipathd configuration, limiting the output to
-those devices actually present in the system. This can be used a template for
-creating \fImultipath.conf\fR.
-.
-.\" ----------------------------------------------------------------------------
-.SH OPTIONS
-.\" ----------------------------------------------------------------------------
-.
-.TP
-.BI \-v " level"
-Verbosity of information printed to stdout in default and "list" operation
-modes. The default level is \fI-v 2\fR.
-.RS 1.2i
-.TP 1.2i
-.I 0
-Nothing is printed.
-.TP
-.I 1
-In default mode, Names/WWIDs of created or modified multipath maps are
-printed. In list mode, WWIDs of all multipath maps are printed.
-.TP
-.I 2
-In default mode,
-Topology of created or modified multipath maps is printed.
-In list mode, topology of all multipath maps is printed.
-.TP
-.I 3
-All detected paths and the topology of all multipath maps are printed.
-.
-.LP
-.
-The verbosity level also controls the level of log and debug messages printed to
-\fIstderr\fR. The default level corresponds to \fILOG_NOTICE\fR
-(important messages that shouldn't be missed in normal operation).
-.
-.RE
-.TP
-.B \-d
-Dry run, do not create or update devmaps.
-.
-.TP
-.B \-e
-Enable all foreign libraries. This overrides the
-.I enable_foreign
-option from \fBmultipath.conf(5)\fR.
-.
-.TP
-.B \-i
-Ignore WWIDs file when processing devices. If
-\fIfind_multipaths strict\fR or \fIfind_multipaths no\fR is set in
-\fImultipath.conf\fR, multipath only considers devices that are
-listed in the WWIDs file. This option overrides that behavior. For other values
-of \fIfind_multipaths\fR, this option has no effect. See the description of
-\fIfind_multipaths\fR in
-.BR multipath.conf (5).
-This option should only be used in rare circumstances.
-.
-.TP
-.B \-B
-Treat the bindings file as read only.
-.
-.TP
-.BI \-b " file"
-Set \fIuser_friendly_names\fR bindings file location. The default is
-\fI/etc/multipath/bindings\fR.
-.
-.TP
-.B \-q
-Don't unset the device mapper feature \fIqueue_if_no_path\fR for multipath
-maps. Normally, \fBmultipath\fR would do so if \fBmultipathd\fR is not
-running, because only a running multipath daemon guarantees that unusable
-paths are reinstated when they become usable again.
-.
-.TP
-.BI \-p " policy"
-Force new maps to use the specified policy, overriding the configuration in
-\fBmultipath.conf(5)\fR. The possible values for
-\fIpolicy\fR are the same as the values for \fIpath_grouping_policy\fR in
-\fBmultipath.conf(5)\fR. Existing maps are not modified.
-.
-.TP
-.B \-r
-Force a reload of all existing multipath maps. This command is delegated to
-the multipathd daemon if it's running. In this case, other command line
-switches of the \fImultipath\fR command have no effect.
-.
-.TP
-.BI \-R " retries"
-Number of times to retry flushing multipath devices that are in use. The default
-is \fI0\fR.
-.
-.\" ----------------------------------------------------------------------------
-.SH "SEE ALSO"
-.\" ----------------------------------------------------------------------------
-.
-.BR multipathd (8),
-.BR multipath.conf (5),
-.BR kpartx (8),
-.BR udev (8),
-.BR dmsetup (8),
-.BR hotplug (8).
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH AUTHORS
-.\" ----------------------------------------------------------------------------
-.
-\fImultipath-tools\fR was developed by Christophe Varoqui <christophe.varoqui@opensvc.com>
-and others.
-.\" EOF
diff --git a/multipath/multipath.8.in b/multipath/multipath.8.in
new file mode 100644 (file)
index 0000000..348eb22
--- /dev/null
@@ -0,0 +1,295 @@
+.\" ----------------------------------------------------------------------------
+.\" Make sure there are no errors with:
+.\" groff -z -wall -b -e -t multipath/multipath.8
+.\" man --warnings -E UTF-8 -l -Tutf8 -Z multipath/multipath.8 > /dev/null
+.\"
+.\" Update the date below if you make any significant change.
+.\" ----------------------------------------------------------------------------
+.
+.TH MULTIPATH 8 2021-11-12 Linux
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH NAME
+.\" ----------------------------------------------------------------------------
+.
+multipath \- Device mapper target autoconfig.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH SYNOPSIS
+.\" ----------------------------------------------------------------------------
+.
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-B | \-d | \-i | \-q | \-r \|]
+.RB [\| \-b\ \c
+.IR file \|]
+.RB [\| \-p\ \c
+.IR policy \|]
+.RB [\| device \|]
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-R\ \c
+.IR retries \|]
+.B \-f device
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-R\ \c
+.IR retries \|]
+.B \-F
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-l | \-ll \|]
+.RB [\| device \|]
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-a | \-w \|]
+.B device
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.B -W
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-i \|]
+.RB [\| \-c | \-C \|]
+.B device
+.
+.LP
+.B multipath
+.RB [\| \-v\ \c
+.IR level \|]
+.RB [\| \-i \|]
+.RB [\| \-u | \-U \|]
+.
+.LP
+.B multipath
+.RB [\| \-h | \-t | \-T \|]
+.
+.\" ----------------------------------------------------------------------------
+.SH DESCRIPTION
+.\" ----------------------------------------------------------------------------
+.
+.B multipath
+is used to detect and coalesce multiple paths to devices, for fail-over or performance reasons.
+.
+.\" ----------------------------------------------------------------------------
+.SH ARGUMENTS
+.\" ----------------------------------------------------------------------------
+.
+The \fBdevice\fR argument restricts \fBmultipath\fR's operation to devices matching the given
+expression. The argument may refer either to a multipath map or to
+its components ("paths"). The expression may be in one of the following formats:
+.
+.TP 1.4i
+.B device node
+file name of a device node, e.g. \fI/dev/dm-10\fR or \fI/dev/sda\fR. If the node refers
+to an existing device mapper device representing a multipath map, this selects
+the map or its paths, depending on the operation mode. Otherwise, it selects a path device.
+.
+.TP
+.B device ID
+kernel device number specified by major:minor numbers, e.g. \fI65:16\fR. This
+format can only be used for path devices.
+.
+.TP
+.B WWID
+a World Wide Identifier matching a multipath map or its paths. To list WWIDs of devices
+present in the system, use e.g. the command "\fImultipath -d -v3 2>/dev/null\fR".
+.
+.\" ----------------------------------------------------------------------------
+.SH OPERATION MODES
+.\" ----------------------------------------------------------------------------
+.
+The default operation mode is to detect and set up multipath maps from the devices found in
+the system.
+.
+Other operation modes are chosen by using one of the following command line switches:
+.TP
+.B \-f
+Flush (remove) a multipath device map specified as parameter, if unused. This operation is delegated to the multipathd daemon if it's running.
+.
+.TP
+.B \-F
+Flush (remove) all unused multipath device maps. This operation is delegated to the multipathd daemon if it's running.
+.
+.TP
+.B \-l
+Show ("list") the current multipath topology from information fetched in sysfs and the device mapper.
+.
+.TP
+.B \-ll
+Show ("list") the current multipath topology from all available information (sysfs, the
+device mapper, path checkers ...).
+.
+.TP
+.B \-a
+Add the WWID for the specified device to the WWIDs file.
+.
+.TP
+.B \-w
+Remove the WWID for the specified device from the WWIDs file.
+.
+.TP
+.B \-W
+Reset the WWIDs file to only include the current multipath devices.
+.
+.TP
+.B \-c
+Check if a block device should be a path in a multipath device.
+.
+.TP
+.B \-C
+Check if a multipath device has usable paths. This can be used to
+test whether or not I/O on this device is likely to succeed. The command
+itself doesn't attempt to do I/O on the device.
+.
+.TP
+.B \-u
+Check if the device specified in the program environment should be
+a path in a multipath device.
+.
+.TP
+.B \-U
+Check if the device specified in the program environment is a multipath device
+with usable paths. See \fB-C\fB.
+.
+.TP
+.B \-h
+Print usage text.
+.
+.TP
+.B \-t
+Display the currently used multipathd configuration.
+.
+.TP
+.B \-T
+Display the currently used multipathd configuration, limiting the output to
+those devices actually present in the system. This can be used a template for
+creating \fI@CONFIGFILE@\fR.
+.
+.\" ----------------------------------------------------------------------------
+.SH OPTIONS
+.\" ----------------------------------------------------------------------------
+.
+.TP
+.BI \-v " level"
+Verbosity of information printed to stdout in default and "list" operation
+modes. The default level is \fI-v 2\fR.
+.RS 1.2i
+.TP 1.2i
+.I 0
+Nothing is printed.
+.TP
+.I 1
+In default mode, Names/WWIDs of created or modified multipath maps are
+printed. In list mode, WWIDs of all multipath maps are printed.
+.TP
+.I 2
+In default mode,
+Topology of created or modified multipath maps is printed.
+In list mode, topology of all multipath maps is printed.
+.TP
+.I 3
+All detected paths and the topology of all multipath maps are printed.
+.
+.LP
+.
+The verbosity level also controls the level of log and debug messages printed to
+\fIstderr\fR. The default level corresponds to \fILOG_NOTICE\fR
+(important messages that shouldn't be missed in normal operation).
+.
+.RE
+.TP
+.B \-d
+Dry run, do not create or update devmaps.
+.
+.TP
+.B \-e
+Enable all foreign libraries. This overrides the
+.I enable_foreign
+option from \fBmultipath.conf(5)\fR.
+.
+.TP
+.B \-i
+Ignore WWIDs file when processing devices. If
+\fIfind_multipaths strict\fR or \fIfind_multipaths no\fR is set in
+\fI@CONFIGFILE@\fR, multipath only considers devices that are
+listed in the WWIDs file. This option overrides that behavior. For other values
+of \fIfind_multipaths\fR, this option has no effect. See the description of
+\fIfind_multipaths\fR in
+.BR @CONFIGFILE@ (5).
+This option should only be used in rare circumstances.
+.
+.TP
+.B \-B
+Treat the bindings file as read only.
+.
+.TP
+.BI \-b " file"
+(\fBdeprecated, do not use\fR) Set \fIuser_friendly_names\fR bindings file location. The default is
+\fI@STATE_DIR@/bindings\fR.
+.
+.TP
+.B \-q
+Don't unset the device mapper feature \fIqueue_if_no_path\fR for multipath
+maps. Normally, \fBmultipath\fR would do so if \fBmultipathd\fR is not
+running, because only a running multipath daemon guarantees that unusable
+paths are reinstated when they become usable again.
+.
+.TP
+.BI \-p " policy"
+Force new maps to use the specified policy, overriding the configuration in
+\fBmultipath.conf(5)\fR. The possible values for
+\fIpolicy\fR are the same as the values for \fIpath_grouping_policy\fR in
+\fBmultipath.conf(5)\fR. Existing maps are not modified.
+.
+.TP
+.B \-r
+Force a reload of all existing multipath maps. This command is delegated to
+the multipathd daemon if it's running. In this case, other command line
+switches of the \fImultipath\fR command have no effect.
+.
+.TP
+.BI \-R " retries"
+Number of times to retry flushing multipath devices that are in use. The default
+is \fI0\fR.
+.
+.\" ----------------------------------------------------------------------------
+.SH "SEE ALSO"
+.\" ----------------------------------------------------------------------------
+.
+.BR multipathd (8),
+.BR multipath.conf (5),
+.BR kpartx (8),
+.BR udev (8),
+.BR dmsetup (8),
+.BR hotplug (8).
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH AUTHORS
+.\" ----------------------------------------------------------------------------
+.
+\fImultipath-tools\fR was developed by Christophe Varoqui <christophe.varoqui@opensvc.com>
+and others.
+.\" EOF
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
deleted file mode 100644 (file)
index b4dccd1..0000000
+++ /dev/null
@@ -1,1974 +0,0 @@
-.\" ----------------------------------------------------------------------------
-.\" Make sure there are no errors with:
-.\" groff -z -wall -b -e -t multipath/multipath.conf.5
-.\" man --warnings -E UTF-8 -l -Tutf8 -Z multipath/multipath.conf.5 >/dev/null
-.\"
-.\" Update the date below if you make any significant change.
-.\" ----------------------------------------------------------------------------
-.
-.TH MULTIPATH.CONF 5 2022-10-01 Linux
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH NAME
-.\" ----------------------------------------------------------------------------
-.
-multipath.conf \- multipath daemon configuration file.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH DESCRIPTION
-.\" ----------------------------------------------------------------------------
-.
-.B "/etc/multipath.conf"
-is the configuration file for the multipath daemon. It is used to
-overwrite the built-in configuration table of \fBmultipathd\fP.
-Any line whose first non-white-space character is a '#' is considered
-a comment line. Empty lines are ignored.
-.PP
-Currently used multipathd configuration can be displayed with the \fBmultipath -t\fR
-or \fBmultipathd show config\fR command.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH SYNTAX
-.\" ----------------------------------------------------------------------------
-.
-The configuration file contains entries of the form:
-.RS
-.nf
-.ft B
-.sp
-<section> {
-.RS
-.ft B
-<attribute> <value>
-.I "..."
-.ft B
-<subsection> {
-.RS
-.ft B
-<attribute> <value>
-.I "..."
-.RE
-.ft B
-}
-.RE
-.ft B
-}
-.ft R
-.fi
-.RE
-.LP
-Each \fIsection\fP contains one or more attributes or subsections. The
-recognized keywords for attributes or subsections depend on the
-section in which they occur.
-.LP
-.
-\fB<attribute>\fR and \fB<value>\fR must be on a single line.
-\fB<attribute>\fR is one of the keywords listed in this man page.
-\fB<value>\fR is either a simple word (containing no whitespace and none of the
-characters '\(dq', '#', and '!') or \fIone\fR string enclosed in double
-quotes ("..."). Outside a quoted string, text starting with '#', and '!' is
-regarded as a comment and ignored until the end of the line. Inside a quoted
-string, '#' and '!' are normal characters, and whitespace is preserved.
-To represent a double quote character inside a double quoted string, use two
-consecutive double quotes ('""'). Thus '2.5\(dq SSD' can be written as "2.5"" SSD".
-.LP
-.
-Opening braces ('{') must follow the (sub)section name on the same line. Closing
-braces ('}') that mark the end of a (sub)section must be the only non-whitespace
-character on the line. Whitespace is ignored except inside double quotes, thus
-the indentation shown in the above example is helpful for human readers but
-not mandatory.
-.LP
-.
-.LP
-.B Note on regular expressions:
-The \fImultipath.conf\fR syntax allows many attribute values to be specified as POSIX
-Extended Regular Expressions (see \fBregex\fR(7)). These regular expressions
-are \fBcase sensitive\fR and \fBnot anchored\fR, thus the expression "bar" matches "barbie",
-"rhabarber", and "wunderbar", but not "Barbie". To avoid unwanted substring
-matches, standard regular expression syntax using the special characters "^" and "$" can be used.
-.
-.LP
-.
-The following \fIsection\fP keywords are recognized:
-.TP 17
-.B defaults
-This section defines default values for attributes which are used
-whenever no values are given in the appropriate device or multipath
-sections.
-.TP
-.B blacklist
-This section defines which devices should be excluded from the
-multipath topology discovery.
-.TP
-.B blacklist_exceptions
-This section defines which devices should be included in the
-multipath topology discovery, despite being listed in the
-\fIblacklist\fR section.
-.TP
-.B multipaths
-This section defines the multipath topologies. They are indexed by a
-\fIWorld Wide Identifier\fR(WWID). For details on the WWID generation
-see section \fIWWID generation\fR below. Attributes set in this section take
-precedence over all others.
-.TP
-.B devices
-This section defines the device-specific settings. Devices are identified by
-vendor, product, and revision.
-.TP
-.B overrides
-This section defines values for attributes that should override the
-device-specific settings for all devices.
-.RE
-.LP
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "defaults section"
-.\" ----------------------------------------------------------------------------
-.
-The \fIdefaults\fR section recognizes the following keywords:
-.
-.
-.TP 17
-.B verbosity
-Default verbosity. Higher values increase the verbosity level. Valid
-levels are between 0 and 6.
-.RS
-.TP
-The default is: \fB2\fR
-.RE
-.
-.
-.TP
-.B polling_interval
-Interval between two path checks in seconds. For properly functioning paths,
-the interval between checks will gradually increase to \fImax_polling_interval\fR.
-This value will be overridden by the \fIWatchdogSec\fR
-setting in the multipathd.service definition if systemd is used.
-.RS
-.TP
-The default is: \fB5\fR
-.RE
-.
-.
-.TP
-.B max_polling_interval
-Maximal interval between two path checks in seconds.
-.RS
-.TP
-The default is: \fB4 * polling_interval\fR
-.RE
-.
-.
-.TP
-.B reassign_maps
-Enable reassigning of device-mapper maps. With this option multipathd
-will remap existing device-mapper maps to always point to multipath
-device, not the underlying block devices. Possible values are
-\fIyes\fR and \fIno\fR.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B multipath_dir
-(Deprecated) This option is not supported any more, and the value is ignored.
-.
-.
-.TP
-.B path_selector
-The default path selector algorithm to use; they are offered by the
-kernel multipath target:
-.RS
-.TP 12
-.I "round-robin 0"
-Loop through every path in the path group, sending the same amount of I/O to
-each. Some aspects of behavior can be controlled with the attributes:
-\fIrr_min_io\fR, \fIrr_min_io_rq\fR and \fIrr_weight\fR.
-.TP
-.I "queue-length 0"
-(Since 2.6.31 kernel) Choose the path for the next bunch of I/O based on the amount
-of outstanding I/O to the path.
-.TP
-.I "service-time 0"
-(Since 2.6.31 kernel) Choose the path for the next bunch of I/O based on the amount
-of outstanding I/O to the path and its relative throughput.
-.TP
-.I "historical-service-time 0"
-(Since 5.8 kernel) Choose the path for the next bunch of I/O based on the
-estimation of future service time based on the history of previous I/O submitted
-to each path.
-.TP
-The default is: \fBservice-time 0\fR
-.RE
-.
-.
-.TP
-.B path_grouping_policy
-The default path grouping policy to apply to unspecified
-multipaths. Possible values are:
-.RS
-.TP 12
-.I failover
-One path per priority group.
-.TP
-.I multibus
-All paths in one priority group.
-.TP
-.I group_by_serial
-One priority group per serial number.
-.TP
-.I group_by_prio
-One priority group per priority value. Priorities are determined by
-callout programs specified as a global, per-controller or
-per-multipath option in the configuration file.
-.TP
-.I group_by_node_name
-One priority group per target node name. Target node names are fetched
-in \fI/sys/class/fc_transport/target*/node_name\fR.
-.TP
-The default is: \fBfailover\fR
-.RE
-.
-.
-.TP
-.B pg_timeout
-(Deprecated) This option is not supported any more, and the value is ignored.
-.
-.
-.TP
-.B uid_attrs
-.
-Setting this option activates \fBmerging uevents\fR by WWID, which may improve
-uevent processing efficiency. Moreover, it's an alternative method to configure
-the udev properties to use for determining unique path identifiers (WWIDs).
-.RS
-.PP
-The value of this option is a space separated list of records like
-\(dq\fItype:ATTR\fR\(dq, where \fItype\fR is matched against the beginning
-of the device node name (e.g. \fIsd:ATTR\fR matches \fIsda\fR), and
-\fIATTR\fR is the name of the udev property to use for matching devices.
-.PP
-If this option is configured and matches the device
-node name of a device, it overrides any other configured  methods for
-determining the WWID for this device.
-.PP
-This option cannot be changed during runtime with the multipathd \fBreconfigure\fR command.
-.PP
-The default is: \fB<unset>\fR. To enable uevent merging, set it e.g. to
-\(dqsd:ID_SERIAL dasd:ID_UID nvme:ID_WWN\(dq.
-.RE
-.
-.
-.TP
-.B uid_attribute
-The udev attribute providing a unique path identifier (WWID). If
-\fIuid_attribute\fR is set to the empty string, WWID determination is done
-using the \fIsysfs\fR method rather then using udev (not recommended in
-production; see \fBWWID generation\fR below).
-.RS
-.TP
-The default is: \fBID_SERIAL\fR, for SCSI devices
-.TP
-The default is: \fBID_UID\fR, for DASD devices
-.TP
-The default is: \fBID_WWN\fR, for NVMe devices
-.RE
-.
-.
-.TP
-.B getuid_callout
-(Deprecated) This option is not supported any more, and the value is ignored.
-.
-.
-.TP
-.B prio
-The name of the path priority routine. The specified routine
-should return a numeric value specifying the relative priority
-of this path. Higher number have a higher priority.
-\fI"none"\fR is a valid value. Currently the following path priority routines
-are implemented:
-.RS
-.TP 12
-.I const
-Return a constant priority of \fI1\fR.
-.TP
-.I sysfs
-Use the sysfs attributes \fIaccess_state\fR and \fIpreferred_path\fR to
-generate the path priority. This prioritizer accepts the optional prio_arg
-\fIexclusive_pref_bit\fR.
-.TP
-.I emc
-(Hardware-dependent)
-Generate the path priority for DGC class arrays as CLARiiON CX/AX and
-EMC VNX families with Failover Mode 1 (Passive Not Ready(PNR)).
-.TP
-.I alua
-(Hardware-dependent)
-Generate the path priority based on the SCSI-3 ALUA settings. This prioritizer
-accepts the optional prio_arg \fIexclusive_pref_bit\fR.
-.TP
-.I ontap
-(Hardware-dependent)
-Generate the path priority for NetApp ONTAP FAS/AFF Series and rebranded arrays,
-with ONTAP native mode(not ALUA).
-.TP
-.I rdac
-(Hardware-dependent)
-Generate the path priority for LSI/Engenio/NetApp RDAC class as NetApp SANtricity
-E/EF Series and rebranded arrays, with "Linux DM-MP (Kernel 3.9 or earlier)" option.
-.TP
-.I hp_sw
-(Hardware-dependent)
-Generate the path priority for HP/COMPAQ/DEC HSG80 and MSA/HSV arrays with
-Active/Standby mode exclusively.
-.TP
-.I hds
-(Hardware-dependent)
-Generate the path priority for Hitachi AMS families of arrays other than AMS 2000.
-.TP
-.I random
-Generate a random priority between 1 and 10.
-.TP
-.I weightedpath
-Generate the path priority based on the regular expression and the
-priority provided as argument. Requires prio_args keyword.
-.TP
-.I path_latency
-Generate the path priority based on a latency algorithm.
-Requires prio_args keyword.
-.TP
-.I ana
-(Hardware-dependent)
-Generate the path priority based on the NVMe ANA settings.
-.TP
-.I datacore
-(Hardware-dependent)
-Generate the path priority for some DataCore storage arrays. Requires prio_args
-keyword.
-.TP
-.I iet
-(iSCSI only)
-Generate path priority for iSCSI targets based on IP address. Requires
-prio_args keyword.
-.PP
-The default depends on the \fBdetect_prio\fR setting: If \fBdetect_prio\fR is
-\fByes\fR (default), the default priority algorithm is \fBsysfs\fR (except for
-NetAPP E/EF Series, where it is \fBalua\fR). If \fBdetect_prio\fR is
-\fBno\fR, the default priority algorithm is \fBconst\fR.
-.RE
-.
-.
-.TP
-.B prio_args
-Arguments to pass to to the prio function. This only applies to certain
-prioritizers:
-.RS
-.TP 12
-.I weighted
-Needs a value of the form
-\fI"<hbtl|devname|serial|wwn> <regex1> <prio1> <regex2> <prio2> ..."\fR
-.RS
-.TP 8
-.I hbtl
-Regex can be of SCSI H:B:T:L format. For example: 1:0:.:. , *:0:0:.
-.TP
-.I devname
-Regex can be of device name format. For example: sda , sd.e
-.TP
-.I serial
-Regex can be of serial number format. For example: .*J1FR.*324 . The serial can
-be looked up through sysfs or by running multipathd show paths format "%z". For
-example: 0395J1FR904324
-.TP
-.I wwn
-Regex can be of the form \fI"host_wwnn:host_wwpn:target_wwnn:target_wwpn"\fR
-these values can be looked up through sysfs or by running \fImultipathd show paths format
-"%N:%R:%n:%r"\fR. For example: 0x200100e08ba0aea0:0x210100e08ba0aea0:.*:.* , .*:.*:iqn.2009-10.com.redhat.msp.lab.ask-06:.*
-.RE
-.TP 12
-.I path_latency
-Needs a value of the form "io_num=\fI<20>\fR base_num=\fI<10>\fR"
-.RS
-.TP 8
-.I io_num
-The number of read IOs sent to the current path continuously, used to calculate the average path latency.
-Valid Values: Integer, [2, 200].
-.TP
-.I base_num
-The base number value of logarithmic scale, used to partition different priority ranks. Valid Values: Integer,
-[2, 10]. And Max average latency value is 100s, min average latency value is 1us.
-For example: If base_num=10, the paths will be grouped in priority groups with path latency <=1us, (1us, 10us],
-(10us, 100us], (100us, 1ms], (1ms, 10ms], (10ms, 100ms], (100ms, 1s], (1s, 10s], (10s, 100s], >100s.
-.RE
-.TP 12
-.I alua
-If \fIexclusive_pref_bit\fR is set, paths with the \fIpreferred path\fR bit
-set will always be in their own path group.
-.TP
-.I sysfs
-If \fIexclusive_pref_bit\fR is set, paths with the \fIpreferred path\fR bit
-set will always be in their own path group.
-.TP
-.I datacore
-.RS
-.TP 8
-.I preferredsds
-(Mandatory) The preferred "SDS name".
-.TP
-.I timeout
-(Optional) The timeout for the INQUIRY, in ms.
-.RE
-.TP 12
-.I iet
-.RS
-.TP 8
-.I preferredip=...
-(Mandatory) Th preferred IP address, in dotted decimal notation, for iSCSI targets.
-.RE
-.TP
-The default is: \fB<unset>\fR
-.RE
-.
-.
-.TP
-.B features
-Specify any device-mapper features to be used. Syntax is \fInum list\fR
-where \fInum\fR is the number, between 0 and 8, of features in \fIlist\fR.
-Possible values for the feature list are:
-.RS
-.TP 12
-.I queue_if_no_path
-(Deprecated, superseded by \fIno_path_retry\fR) Queue I/O if no path is active.
-Identical to the \fIno_path_retry\fR with \fIqueue\fR value. If both this
-feature and \fIno_path_retry\fR are set, the latter value takes
-precedence. See KNOWN ISSUES.
-.TP
-.I pg_init_retries <times>
-(Since kernel 2.6.24) Number of times to retry pg_init, it must be between 1 and 50.
-.TP
-.I pg_init_delay_msecs <msecs>
-(Since kernel 2.6.38) Number of msecs before pg_init retry, it must be between 0 and 60000.
-.TP
-.I queue_mode <mode>
-(Since kernel 4.8) Select the queueing mode per multipath device.
-<mode> can be \fIbio\fR, \fIrq\fR or \fImq\fR, which corresponds to
-bio-based, request-based, and block-multiqueue (blk-mq) request-based,
-respectively.
-Before kernel 4.20 The default depends on the kernel parameter
-\fBdm_mod.use_blk_mq\fR. It is \fImq\fR if the latter is set, and \fIrq\fR
-otherwise. Since kernel 4.20, \fIrq\fR and \fImq\fR both correspond to
-block-multiqueue. Once a multipath device has been created, its queue_mode
-cannot be changed. \fInvme:tcp\fR paths are only supported in multipath
-devices with queue_mode set to \fIbio\fR. multipath will automatically
-set this when creating a device with \fInvme:tcp\fR paths.
-.TP
-The default is: \fB0\fR
-.RE
-.
-.
-.TP
-.B path_checker
-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
-(Deprecated) Read the first sector of the device. This checker is being
-deprecated, please use \fItur\fR or \fIdirectio\fR instead.
-.TP
-.I tur
-Issue a \fITEST UNIT READY\fR command to the device.
-.TP
-.I emc_clariion
-(Hardware-dependent)
-Query the DGC/EMC specific EVPD page 0xC0 to determine the path state
-for CLARiiON CX/AX and EMC VNX and Unity arrays families.
-.TP
-.I hp_sw
-(Hardware-dependent)
-Check the path state for HP/COMPAQ/DEC HSG80 and MSA/HSV arrays with
-Active/Standby mode exclusively.
-.TP
-.I rdac
-(Hardware-dependent)
-Check the path state for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF
-Series, and rebranded arrays.
-.TP
-.I directio
-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)
-Check the path state for HP/COMPAQ Smart Array(CCISS) controllers.
-.TP
-.I none
-Do not check the device, fallback to use the values retrieved from sysfs
-.TP
-The default is: \fBtur\fR
-.RE
-.
-.
-.TP
-.B alias_prefix
-The \fIuser_friendly_names\fR prefix.
-.RS
-.TP
-The default is: \fBmpath\fR
-.RE
-.
-.
-.TP
-.B failback
-Tell multipathd how to manage path group failback.
-To select \fIimmediate\fR or a \fIvalue\fR, it's mandatory that the device
-has support for a working prioritizer.
-.RS
-.TP 12
-.I immediate
-Immediately failback to the highest priority pathgroup that contains
-active paths.
-.TP
-.I manual
-Do not perform automatic failback.
-.TP
-.I followover
-Used to deal with multiple computers accessing the same Active/Passive storage
-devices. Only perform automatic failback when the first path of a pathgroup
-becomes active. This keeps a cluster node from automatically failing back when
-another node requested the failover.
-.TP
-.I values > 0
-Deferred failback (time to defer in seconds).
-.TP
-The default is: \fBmanual\fR
-.RE
-.
-.
-.TP
-.B rr_min_io
-Number of I/O requests to route to a path before switching to the next in the
-same path group. This is only for \fIBlock I/O\fR(BIO) based multipath and
-only apply to \fIround-robin\fR path_selector.
-.RS
-.TP
-The default is: \fB1000\fR
-.RE
-.
-.
-.TP
-.B rr_min_io_rq
-Number of I/O requests to route to a path before switching to the next in the
-same path group. This is only for \fIRequest\fR based multipath and
-only apply to \fIround-robin\fR path_selector.
-.RS
-.TP
-The default is: \fB1\fR
-.RE
-.
-.
-.TP
-.B max_fds
-Specify the maximum number of file descriptors that can be opened by multipath
-and multipathd. This is equivalent to ulimit \-n. A value of \fImax\fR will set
-this to the system limit from \fI/proc/sys/fs/nr_open\fR. If this is not set, the
-maximum number of open fds is taken from the calling process. It is usually
-1024. To be safe, this should be set to the maximum number of paths plus 32,
-if that number is greater than 1024.
-.RS
-.TP
-The default is: \fBmax\fR
-.RE
-.
-.
-.TP
-.B rr_weight
-If set to \fIpriorities\fR the multipath configurator will assign path weights
-as "path prio * rr_min_io". Possible values are
-.I priorities
-or
-.I uniform .
-Only apply to \fIround-robin\fR path_selector.
-.RS
-.TP
-The default is: \fBuniform\fR
-.RE
-.
-.
-.TP
-.B no_path_retry
-Specify what to do when all paths are down. Possible values are:
-.RS
-.TP 12
-.I value > 0
-Number of retries until disable I/O queueing.
-.TP
-.I fail
-For immediate failure (no I/O queueing).
-.TP
-.I queue
-For never stop I/O queueing, similar to \fIqueue_if_no_path\fR. See KNOWN ISSUES.
-.TP
-The default is: \fBfail\fR
-.RE
-.
-.
-.TP
-.B queue_without_daemon
-If set to
-.I no
-, when multipathd stops, queueing will be turned off for all devices.
-This is useful for devices that set no_path_retry. If a machine is
-shut down while all paths to a device are down, it is possible to hang waiting
-for I/O to return from the device after multipathd has been stopped. Without
-multipathd running, access to the paths cannot be restored, and the kernel
-cannot be told to stop queueing I/O. Setting queue_without_daemon to
-.I no
-, avoids this problem.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B checker_timeout
-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/<dev>/device/timeout\fR
-.RE
-.
-.
-.TP
-.B allow_usb_devices
-If set to
-.I no
-, all USB devices will be skipped during path discovery. If you intend to use
-multipath on USB attached devices, set this to \fIyes\fR.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B flush_on_last_del
-If set to
-.I yes
-, multipathd will disable queueing when the last path to a device has been
-deleted.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B user_friendly_names
-If set to
-.I yes
-, using the bindings file \fI/etc/multipath/bindings\fR to assign a persistent
-and unique alias to the multipath, in the form of mpath<n>. If set to
-.I no
-use the WWID as the alias. In either case this be will
-be overridden by any specific aliases in the \fImultipaths\fR section.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B fast_io_fail_tmo
-Specify the number of seconds the SCSI layer will wait after a problem has been
-detected on a FC remote port before failing I/O to devices on that remote port.
-This should be smaller than dev_loss_tmo. Setting this to
-.I off
-will disable the timeout.
-.RS
-.TP
-The default is: \fB5\fR
-.RE
-.
-.
-.TP
-.B dev_loss_tmo
-Specify the number of seconds the SCSI layer will wait after a connection loss has
-been detected on a remote port before removing it from the system. This
-can be set to "infinity", which effectively means 136 years (2^32-1 seconds).
-This parameter is only applied to Fibre Channel and SAS devices.
-.RS
-.LP
-The value of \fIdev_loss_tmo\fR is restricted by other settings.
-If \fIfast_io_fail_tmo\fR is set to a positive value, \fBmultipathd\fR
-will make sure that the value of \fIdev_loss_tmo\fR is larger than
-\fIno_path_retry\fR * \fIpolling_interval\fR.
-If \fIfast_io_fail_tmo\fR is not set, the kernel limits the \fIdev_loss_tmo\fR
-value to 600 seconds.
-In this case, the user has to make sure that \fIno_path_retry\fR is smaller
-than \fIdev_loss_tmo / polling_interval\fR. In particular,
-\fIno_path_retry\fR must not be set to \(dq\fIqueue\fR\(dq. See KNOWN ISSUES.
-.LP
-When path devices reappear after a connection loss, it is much easier for
-the kernel to simply reactivate an inactive device than to re-add
-a previously deleted one. It is therefore recommended to set
-\fIdev_loss_tmo\fR to a large value within the restrictions mentioned above.
-.LP
-Fibre Channel and SAS devices have hardware-dependent defaults, which are left
-unchanged if \fIdev_loss_tmo\fR is not specified. For a few storage arrays,
-the multipath-tools built-in settings override the default. Run \fImultipath -T\fR
-to see the settings for your device.
-.TP
-The default is: \fB<hardware dependent>\fR
-.RE
-.
-.
-.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
-(Deprecated) This option is deprecated, and will be removed in a future release.
-The full pathname of the binding file to be used when the user_friendly_names
-option is set.
-.RS
-.TP
-The default is: \fB/etc/multipath/bindings\fR
-.RE
-.
-.
-.TP
-.B wwids_file
-(Deprecated) This option is deprecated, and will be removed in a future release.
-The full pathname of the WWIDs file, which is used by multipath to keep track
-of the WWIDs for LUNs it has created multipath devices on in the past.
-.RS
-.TP
-The default is: \fB/etc/multipath/wwids\fR
-.RE
-.
-.
-.TP
-.B prkeys_file
-(Deprecated) This option is deprecated, and will be removed in a future release.
-The full pathname of the prkeys file, which is used by multipathd to keep
-track of the persistent reservation key used for a specific WWID, when
-\fIreservation_key\fR is set to \fBfile\fR.
-.RS
-.TP
-The default is: \fB/etc/multipath/prkeys\fR
-.RE
-.
-.
-.TP
-.B log_checker_err
-If set to
-.I once
-, multipathd logs the first path checker error at logging level 2. Any later
-errors are logged at level 3 until the device is restored. If set to
-.I always
-, multipathd always logs the path checker error at logging level 2.
-.RS
-.TP
-The default is: \fBalways\fR
-.RE
-.
-.
-.TP
-.B reservation_key
-This is the service action reservation key used by mpathpersist. It must be
-set for all multipath devices using persistent reservations, and it must be
-the same as the RESERVATION KEY field of the PERSISTENT RESERVE OUT parameter
-list which contains an 8-byte value provided by the application client to the
-device server to identify the I_T nexus. If the \fI--param-aptpl\fR option is
-used when registering the key with mpathpersist, \fB:aptpl\fR must be appended
-to the end of the reservation key.
-.RS
-.PP
-Alternatively, this can be set to \fBfile\fR, which will store the RESERVATION
-KEY registered by mpathpersist in the \fIprkeys_file\fR. multipathd will then
-use this key to register additional paths as they appear.  When the
-registration is removed, the RESERVATION KEY is removed from the
-\fIprkeys_file\fR. The prkeys file will automatically keep track of whether
-the key was registered with \fI--param-aptpl\fR.
-.TP
-The default is: \fB<unset>\fR
-.RE
-.
-.
-.TP
-.B all_tg_pt
-Set the 'all targets ports' flag when registering keys with mpathpersist. Some
-arrays automatically set and clear registration keys on all target ports from a
-host, instead of per target port per host. The ALL_TG_PT flag must be set to
-successfully use mpathpersist on these arrays. Setting this option is identical
-to calling mpathpersist with \fI--param-alltgpt\fR
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B retain_attached_hw_handler
-(Obsolete for kernels >= 4.3) If set to
-.I yes
-and the SCSI layer has already attached a hardware_handler to the device,
-multipath will not force the device to use the hardware_handler specified by
-multipath.conf. If the SCSI layer has not attached a hardware handler,
-multipath will continue to use its configured hardware handler.
-.RS
-.PP
-The default is: \fByes\fR
-.PP
-\fBImportant Note:\fR Linux kernel 4.3 or newer always behaves as if
-\fB"retain_attached_hw_handler yes"\fR was set.
-.RE
-.
-.
-.TP
-.B detect_prio
-If set to
-.I yes
-, multipath will try to detect if the device supports SCSI-3 ALUA. If so, the
-device will automatically use the \fIsysfs\fR prioritizer if the required sysf
-attributes \fIaccess_state\fR and \fIpreferred_path\fR are supported, or the
-\fIalua\fR prioritizer if not. If set to
-.I no
-, the prioritizer will be selected as usual.
-.RS
-.TP
-The default is: \fByes\fR
-.RE
-.
-.
-.TP
-.B detect_checker
-if set to
-.I yes
-, multipath will try to detect if the device supports SCSI-3 ALUA. If so, the
-device will automatically use the \fItur\fR checker. If set to
-.I no
-, the checker will be selected as usual.
-.RS
-.TP
-The default is: \fByes\fR
-.RE
-.
-.
-.TP
-.B force_sync
-If set to
-.I yes
-, multipathd will call the path checkers in sync mode only.  This means that
-only one checker will run at a time.  This is useful in the case where many
-multipathd checkers running in parallel causes significant CPU pressure.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B strict_timing
-If set to
-.I yes
-, multipathd will start a new path checker loop after exactly one second,
-so that each path check will occur at exactly \fIpolling_interval\fR
-seconds. On busy systems path checks might take longer than one second;
-here the missing ticks will be accounted for on the next round.
-A warning will be printed if path checks take longer than \fIpolling_interval\fR
-seconds.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B deferred_remove
-If set to
-.I yes
-, multipathd will do a deferred remove instead of a regular remove when the
-last path device has been deleted.  This means that if the multipath device is
-still in use, it will be freed when the last user closes it.  If path is added
-to the multipath device before the last user closes it, the deferred remove
-will be canceled.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B partition_delimiter
-This parameter controls how multipath chooses the names of partition devices
-of multipath maps if a multipath map is renamed (e.g. if a map alias is added
-or changed). If this parameter is set to a string other than "/UNSET/" (even
-the empty string), multipath inserts that string between device name and
-partition number to construct the partition device name.
-Otherwise (i.e. if this parameter is unset or has the value "/UNSET/"),
-the behavior depends on the map name: if it ends in a digit, a \fI"p"\fR is
-inserted between name and partition number; otherwise, the partition number is
-simply appended.
-Distributions may use a non-null default value for this option; in this case,
-the user must set it to "/UNSET/" to obtain the original \fB<unset>\fR
-behavior. Use \fImultipath -T\fR to check the current settings.
-.RS
-.TP
-The default is: \fB<unset>\fR
-.RE
-.
-.
-.TP
-.B config_dir
-(Deprecated) This option is not supported any more, and the value is ignored.
-.
-.
-.TP
-.B san_path_err_threshold
-If set to a value greater than 0, multipathd will watch paths and check how many
-times a path has been failed due to errors.If the number of failures on a particular
-path is greater than the san_path_err_threshold, then the path will not reinstate
-till san_path_err_recovery_time. These path failures should occur within a
-san_path_err_forget_rate checks, if not we will consider the path is good enough
-to reinstantate. See "Shaky paths detection" below.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B san_path_err_forget_rate
-If set to a value greater than 0, multipathd will check whether the path failures
-has exceeded  the san_path_err_threshold within this many checks i.e
-san_path_err_forget_rate . If so we will not reinstate the path till
-san_path_err_recovery_time. See "Shaky paths detection" below.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B san_path_err_recovery_time
-If set to a value greater than 0, multipathd will make sure that when path failures
-has exceeded the san_path_err_threshold within san_path_err_forget_rate then the path
-will be placed in failed state for san_path_err_recovery_time duration.Once san_path_err_recovery_time
-has timeout  we will reinstate the failed path .
-san_path_err_recovery_time value should be in secs.
-See "Shaky paths detection" below.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B marginal_path_double_failed_time
-One of the four parameters of supporting path check based on accounting IO
-error such as intermittent error. When a path failed event occurs twice in
-\fImarginal_path_double_failed_time\fR seconds due to an IO error and all the
-other three parameters are set, multipathd will fail the path and enqueue
-this path into a queue of which members are sent a couple of continuous
-direct reading asynchronous IOs at a fixed sample rate of 10HZ to start IO
-error accounting process. See "Shaky paths detection" below.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B marginal_path_err_sample_time
-One of the four parameters of supporting path check based on accounting IO
-error such as intermittent error. If it is set to a value no less than 120,
-when a path fail event occurs twice in \fImarginal_path_double_failed_time\fR
-second due to an IO error, multipathd will fail the path and enqueue this
-path into a queue of which members are sent a couple of continuous direct
-reading asynchronous IOs at a fixed sample rate of 10HZ to start the IO
-accounting process for the path will last for
-\fImarginal_path_err_sample_time\fR.
-If the rate of IO error on a particular path is greater than the
-\fImarginal_path_err_rate_threshold\fR, then the path will not reinstate for
-\fImarginal_path_err_recheck_gap_time\fR seconds unless there is only one
-active path. After \fImarginal_path_err_recheck_gap_time\fR expires, the path
-will be requeued for rechecking. If checking result is good enough, the
-path will be reinstated. See "Shaky paths detection" below.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B marginal_path_err_rate_threshold
-The error rate threshold as a permillage (1/1000). One of the four parameters
-of supporting path check based on accounting IO error such as intermittent
-error. Refer to \fImarginal_path_err_sample_time\fR. If the rate of IO errors
-on a particular path is greater than this parameter, then the path will not
-reinstate for \fImarginal_path_err_recheck_gap_time\fR seconds unless there is
-only one active path. See "Shaky paths detection" below.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B marginal_path_err_recheck_gap_time
-One of the four parameters of supporting path check based on accounting IO
-error such as intermittent error. Refer to
-\fImarginal_path_err_sample_time\fR. If this parameter is set to a positive
-value, the failed path of  which the IO error rate is larger than
-\fImarginal_path_err_rate_threshold\fR will be kept in failed state for
-\fImarginal_path_err_recheck_gap_time\fR seconds. When
-\fImarginal_path_err_recheck_gap_time\fR seconds expires, the path will be
-requeued for checking. If checking result is good enough, the path will be
-reinstated, or else it will keep failed. See "Shaky paths detection" below.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B delay_watch_checks
-(Deprecated) This option is \fBdeprecated\fR, and mapped to \fIsan_path_err_forget_rate\fR.
-If this is set to a value greater than 0 and no \fIsan_path_err\fR options
-are set, \fIsan_path_err_forget_rate\fR will be set to the value of
-\fIdelay_watch_checks\fR and \fIsan_path_err_threshold\fR will be set to 1.
-See the \fIsan_path_err_forget_rate\fR and \fIsan_path_err_threshold\fR
-options, and "Shaky paths detection" below for more information.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B delay_wait_checks
-(Deprecated) This option is \fBdeprecated\fR, and mapped to \fIsan_path_err_recovery_time\fR.
-If this is set to a value greater than 0 and no \fIsan_path_err\fR options
-are set, \fIsan_path_err_recovery_time\fR will be set to the value of
-\fIdelay_wait_checks\fR times \fImax_polling_interval\fR. This will give
-approximately the same wait time as delay_wait_checks previously did.
-Also, \fIsan_path_err_threshold\fR will be set to 1. See the
-\fIsan_path_err_recovery_time\fR and \fIsan_path_err_threshold\fR
-options, and "Shaky paths detection" below for more information.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B marginal_pathgroups
-If set to \fIoff\fR, the \fIdelay_*_checks\fR, \fImarginal_path_*\fR, and
-\fIsan_path_err_*\fR options will keep marginal, or \(dqshaky\(dq, paths from
-being reinstated until they have been monitored for some time. This can cause
-situations where all non-marginal paths are down, and no paths are usable
-until multipathd detects this and reinstates a marginal path. If the multipath
-device is not configured to queue IO in this case, it can cause IO errors to
-occur, even though there are marginal paths available.  However, if this
-option is set to \fIon\fR, when one of the marginal path detecting methods
-determines that a path is marginal, it will be reinstated and placed in a
-separate pathgroup that will only be used after all the non-marginal pathgroups
-have been tried first. This prevents the possibility of IO errors occurring
-while marginal paths are still usable. After the path has been monitored
-for the configured time, and is declared healthy, it will be returned to its
-normal pathgroup.
-If this option is set to \fIfpin\fR, multipathd will receive fpin
-notifications, set path states to "marginal" accordingly, and regroup paths
-as described for \fIon\fR. This option can't be used in combination
-with other options for "Shaky path detection" (see below). \fBNote:\fR If this
-is set to \fIfpin\fR, the \fImarginal_path_*\fR and \fIsan_path_err_*\fR
-options are implicitly set to \fIno\fP. Also, this option cannot be switched
-either to or from \fIfpin\fR on a multipathd reconfigure. multipathd must be
-restarted for the change to take effect.
-See "Shaky paths detection" below for more information.
-.RS
-.TP
-The default is: \fBoff\fR
-.RE
-.
-.
-.TP
-.B find_multipaths
-This option controls whether multipath and multipathd try to create multipath
-maps over non-blacklisted devices they encounter. This matters a) when a device is
-encountered by \fBmultipath -u\fR during udev rule processing (a device is
-blocked from further processing by higher layers - such as LVM - if and only
-if it\'s considered a valid multipath device path), and b) when multipathd
-detects a new device. The following values are possible:
-.RS
-.TP 10
-.I strict
-Both multipath and multipathd treat only such devices as multipath devices
-which have been part of a multipath map previously, and which are therefore
-listed in the \fBwwids_file\fR. Users can manually set up multipath maps using the
-\fBmultipathd add map\fR command. Once set up manually, the map is
-remembered in the wwids file and will be set up automatically in the future.
-.TP
-.I no
-Multipath behaves like \fBstrict\fR. Multipathd behaves like \fBgreedy\fR.
-.TP
-.I yes
-Both multipathd and multipath treat a device as multipath device if the
-conditions for \fBstrict\fR are met, or if at least two non-blacklisted paths
-with the same WWID have been detected.
-.TP
-.I greedy
-Both multipathd and multipath treat every non-blacklisted device as multipath
-device path.
-.TP
-.I smart
-This differs from \fIfind_multipaths yes\fR only in
-the way it treats new devices for which only one path has been
-detected yet. When such a device is first encountered in udev rules, it is
-treated as a multipath device. multipathd waits whether additional paths with
-the same WWID appears. If that happens, it sets up a multipath map. If it
-doesn\'t happen until a
-timeout expires, or if setting up the map fails, a new uevent is triggered for
-the device; at second encounter in the udev rules, the device will be treated
-as non-multipath and passed on to upper layers.
-\fBNote:\fR this may cause delays during device detection if
-there are single-path devices which aren\'t blacklisted.
-.TP
-The default is: \fBstrict\fR
-.RE
-.
-.
-.TP
-.B find_multipaths_timeout
-Timeout, in seconds, to wait for additional paths after detecting the first
-one, if \fIfind_multipaths
-"smart"\fR (see above) is set. If the value is \fBpositive\fR, this timeout is used for all
-unknown, non-blacklisted devices encountered. If the value is \fBnegative\fR
-(recommended), it's only
-applied to "known" devices that have an entry in multipath's hardware table,
-either in the built-in table or in a \fIdevice\fR section; other ("unknown") devices will
-use a timeout of only 1 second to avoid booting delays. The value 0 means
-"use the built-in default". If \fIfind_multipath\fR has a value
-other than \fIsmart\fR, this option has no effect.
-.RS
-.TP
-The default is: \fB-10\fR (10s for known and 1s for unknown hardware)
-.RE
-.
-.
-.TP
-.B uxsock_timeout
-CLI receive timeout in milliseconds. For larger systems CLI commands
-might timeout before the multipathd lock is released and the CLI command
-can be processed. This will result in errors like
-"timeout receiving packet" to be returned from CLI commands.
-In these cases it is recommended to increase the CLI timeout to avoid
-those issues.
-.RS
-.TP
-The default is: \fB4000\fR
-.RE
-.
-.
-.TP
-.B retrigger_tries
-Sets the number of times multipathd will try to retrigger a uevent to get the
-WWID.
-.RS
-.TP
-The default is: \fB3\fR
-.RE
-.
-.
-.TP
-.B retrigger_delay
-Sets the amount of time, in seconds, to wait between retriggers.
-.RS
-.TP
-The default is: \fB10\fR
-.RE
-.
-.
-.TP
-.B missing_uev_wait_timeout
-Controls how many seconds multipathd will wait, after a new multipath device
-is created, to receive a change event from udev for the device, before
-automatically enabling device reloads. Usually multipathd will delay reloads
-on a device until it receives a change uevent from the initial table load.
-.RS
-.TP
-The default is: \fB30\fR
-.RE
-.
-.
-.TP
-.B skip_kpartx
-If set to
-.I yes
-, kpartx will not automatically create partitions on the device.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B disable_changed_wwids
-(Deprecated) This option is not supported any more, and the value is ignored.
-.RE
-.
-.
-.TP
-.B remove_retries
-This sets how may times multipath will retry removing a device that is in-use.
-Between each attempt, multipath will sleep 1 second.
-.RS
-.TP
-The default is: \fB0\fR
-.RE
-.
-.
-.TP
-.B max_sectors_kb
-Sets the max_sectors_kb device parameter on all path devices and the multipath
-device to the specified value.
-.RS
-.TP
-The default is: in \fB/sys/block/<dev>/queue/max_sectors_kb\fR
-.RE
-.
-.
-.TP
-.B ghost_delay
-Sets the number of seconds that multipath will wait after creating a device
-with only ghost paths before marking it ready for use in systemd. This gives
-the active paths time to appear before the multipath runs the hardware handler
-to switch the ghost paths to active ones. Setting this to \fI0\fR or \fIno\fR
-makes multipath immediately mark a device with only ghost paths as ready.
-.RS
-.TP
-The default is: \fBno\fR
-.RE
-.
-.
-.TP
-.B enable_foreign
-Enables or disables foreign libraries (see section
-.I FOREIGN MULTIPATH SUPPORT
-below). The value is a regular expression; foreign libraries are loaded
-if their name (e.g. \(dqnvme\(dq) matches the expression. By default,
-no foreign libraries are enabled. Set this to \(dqnvme\(dq to enable NVMe native
-multipath support, or \(dq.*\(dq to enable all foreign libraries.
-.RS
-.TP
-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
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "blacklist and blacklist_exceptions sections"
-.\" ----------------------------------------------------------------------------
-.
-The \fIblacklist\fR section is used to exclude specific devices from
-the multipath topology. It is most commonly used to exclude local disks or
-non-disk devices (such as LUNs for the storage array controller) from
-being handled by multipath-tools.
-.LP
-.
-.
-In the \fIblacklist\fR and \fIblacklist_exceptions\fR sections, starting a
-quoted value with an exclamation mark \fB"!"\fR will invert the matching
-of the rest of the regular expression. For instance, \fB"!^sd[a-z]"\fR will
-match all values that do not start with \fB"sd[a-z]"\fR. The exclamation mark
-can be escaped \fB"\\!"\fR to match a literal \fB!\fR at the start of a
-regular expression. \fBNote:\fR The exclamation mark must be inside quotes,
-otherwise it will be treated as starting a comment.
-.LP
-.
-.
-The \fIblacklist_exceptions\fR section is used to revert the actions of the
-\fIblacklist\fR section. This allows one to selectively include ("whitelist") devices which
-would normally be excluded via the \fIblacklist\fR section. A common usage is
-to blacklist "everything" using a catch-all regular expression, and create
-specific blacklist_exceptions entries for those devices that should be handled
-by multipath-tools.
-.LP
-.
-.
-The following keywords are recognized in both sections. The defaults are empty
-unless explicitly stated.
-.TP 17
-.B devnode
-Regular expression matching the device nodes to be excluded/included.
-.RS
-.PP
-The default \fIblacklist\fR consists of the regular expression
-\fB"!^(sd[a-z]|dasd[a-z]|nvme[0-9])"\fR. This causes all device types other
-than scsi, dasd, and nvme to be excluded from multipath handling by default.
-.RE
-.TP
-.B wwid
-Regular expression for the \fIWorld Wide Identifier\fR of a device to be excluded/included.
-.
-.TP
-.B device
-Subsection for the device description. This subsection recognizes the
-.B vendor
-and
-.B product
-keywords. Both are regular expressions. For a full description of these keywords please see the
-\fIdevices\fR section description.
-.TP
-.B property
-Regular expression for an udev property. All
-devices that have matching udev properties will be excluded/included.
-The handling of the \fIproperty\fR keyword is special,
-because devices \fBmust\fR have at least one whitelisted udev property;
-otherwise they're treated as blacklisted, and the message
-"\fIblacklisted, udev property missing\fR" is displayed in the logs.
-.
-.RS
-.PP
-.B Note:
-The behavior of this option has changed in \fBmultipath-tools\fR 0.8.2
-compared to previous versions.
-Blacklisting by missing properties is only applied to devices which do have the
-property specified by \fIuid_attribute\fR (e.g. \fIID_SERIAL\fR)
-set. Previously, it was applied to every device, possibly causing devices to be
-blacklisted because of temporary I/O error conditions.
-.PP
-The default \fIblacklist exception\fR is: \fB(SCSI_IDENT_|ID_WWN)\fR, causing
-well-behaved SCSI devices and devices that provide a WWN (World Wide Number)
-to be included, and all others to be excluded.
-.RE
-.TP
-.B protocol
-Regular expression for the protocol of a device to be excluded/included.
-.RS
-.PP
-The protocol strings that multipath recognizes are \fIscsi:fcp\fR,
-\fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR,
-\fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR,
-\fIscsi:unspec\fR, \fInvme:pcie\fR, \fInvme:rdma\fR, \fInvme:fc\fR,
-\fInvme:tcp\fR, \fInvme:loop\fR, \fInvme:apple-nvme\fR, \fInvme:unspec\fR,
-\fIccw\fR, \fIcciss\fR, and \fIundef\fR.
-The protocol that a path is using can be viewed by running
-\fBmultipathd show paths format "%d %P"\fR
-.RE
-.LP
-For every device, these 5 blacklist criteria are evaluated in the order
-"property, dev\%node, device, protocol, wwid". If a device turns out to be
-blacklisted by any criterion, it's excluded from handling by multipathd, and
-the later criteria aren't evaluated any more. For each
-criterion, the whitelist takes precedence over the blacklist if a device
-matches both.
-.LP
-.B
-Note:
-Besides the blacklist and whitelist, other configuration options such as
-\fIfind_multipaths\fR have an impact on
-whether or not a given device is handled by multipath-tools.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "multipaths section"
-.\" ----------------------------------------------------------------------------
-.
-The \fImultipaths\fR section allows setting attributes of multipath maps. The
-attributes that are set via the multipaths section (see list below) take
-precedence over all other configuration settings, including those from the
-\fIoverrides\fR section.
-.LP
-The only recognized attribute for the \fImultipaths\fR section is the
-\fImultipath\fR subsection. If there are multiple \fImultipath\fR subsections
-matching a given WWID, the contents of these sections are merged, and settings
-from later entries take precedence.
-.LP
-.
-.
-The \fImultipath\fR subsection recognizes the following attributes:
-.TP 17
-.B wwid
-(Mandatory) World Wide Identifier. Detected multipath maps are matched against this attribute.
-Note that, unlike the \fIwwid\fR attribute in the \fIblacklist\fR section,
-this is \fBnot\fR a regular expression or a substring; WWIDs must match
-exactly inside the multipaths section.
-.TP
-.B alias
-Symbolic name for the multipath map. This takes precedence over a an entry for
-the same WWID in the \fIbindings_file\fR.
-.LP
-.
-.
-The following attributes are optional; if not set the default values
-are taken from the \fIoverrides\fR, \fIdevices\fR, or \fIdefaults\fR
-section:
-.sp 1
-.PD .1v
-.RS
-.TP 18
-.B path_grouping_policy
-.TP
-.B path_selector
-.TP
-.B prio
-.TP
-.B prio_args
-.TP
-.B failback
-.TP
-.B rr_weight
-.TP
-.B no_path_retry
-.TP
-.B rr_min_io
-.TP
-.B rr_min_io_rq
-.TP
-.B flush_on_last_del
-.TP
-.B features
-.TP
-.B reservation_key
-.TP
-.B user_friendly_names
-.TP
-.B deferred_remove
-.TP
-.B san_path_err_threshold
-.TP
-.B san_path_err_forget_rate
-.TP
-.B san_path_err_recovery_time
-.TP
-.B marginal_path_err_sample_time
-.TP
-.B marginal_path_err_rate_threshold
-.TP
-.B marginal_path_err_recheck_gap_time
-.TP
-.B marginal_path_double_failed_time
-.TP
-.B delay_watch_checks
-.TP
-.B delay_wait_checks
-.TP
-.B skip_kpartx
-.TP
-.B max_sectors_kb
-.TP
-.B ghost_delay
-.RE
-.PD
-.LP
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "devices section"
-.\" ----------------------------------------------------------------------------
-.
-.TP 4
-.B Important:
-The built-in hardware device table of
-.I multipath-tools
-is created by members of the Linux community in the hope that it will be useful.
-The existence of an entry for a given storage product in the hardware table
-.B does not imply
-that the product vendor supports, or has tested, the product with
-.I multipath-tools
-in any way.
-.B Always consult the vendor\(aqs official documentation for support-related information.
-.PP
-\fImultipath-tools\fR have a built-in device table with reasonable defaults
-for more than 100 known multipath-capable storage devices. The devices section
-can be used to override these settings. If there are multiple matches for a
-given device, the attributes of all matching entries are applied to it.
-If an attribute is specified in several matching device subsections,
-later entries take precedence. Thus, entries in files under \fIconfig_dir\fR (in
-reverse alphabetical order) have the highest precedence, followed by entries
-in \fImultipath.conf\fR; the built-in hardware table has the lowest
-precedence. Inside a configuration file, later entries have higher precedence
-than earlier ones.
-.LP
-The only recognized attribute for the \fIdevices\fR section is the \fIdevice\fR
-subsection. Devices detected in the system are matched against the device entries
-using the \fBvendor\fR, \fBproduct\fR, and \fBrevision\fR fields, which are
-all POSIX Extended regular expressions (see \fBregex\fR(7)).
-.LP
-The vendor, product, and revision fields that multipath or multipathd detect for
-devices in a system depend on the device type. For SCSI devices, they correspond to the
-respective fields of the SCSI INQUIRY page. In general, the command '\fImultipathd show
-paths format "%d %s"\fR' command can be used to see the detected properties
-for all devices in the system.
-.LP
-.
-The \fIdevice\fR subsection recognizes the following attributes:
-.TP 17
-.B vendor
-(Mandatory) Regular expression to match the vendor name.
-.TP
-.B product
-(Mandatory) Regular expression to match the product name.
-.TP
-.B revision
-Regular expression to match the product revision. If not specified, any
-revision matches.
-.TP
-.B product_blacklist
-Products with the given \fBvendor\fR matching this string are
-blacklisted. This is equivalent to a \fBdevice\fR entry in the \fIblacklist\fR
-section with the \fIvendor\fR attribute set to this entry's \fIvendor\fR, and
-the \fIproduct\fR attribute set to the value of \fIproduct_blacklist\fR.
-.TP
-.B alias_prefix
-The user_friendly_names prefix to use for this
-device type, instead of the default "mpath".
-.TP
-.B vpd_vendor
-The vendor specific vpd page information, using the vpd page abbreviation.
-The vpd page abbreviation can be found by running \fIsg_vpd -e\fR. multipathd
-will use this information to gather device specific information that can be
-displayed with the \fI%g\fR wildcard for the \fImultipathd show maps format\fR
-and \fImultipathd show paths format\fR commands. Currently only the
-\fBhp3par\fR vpd page is supported.
-.TP
-.B hardware_handler
-The hardware handler to use for this device type.
-The following hardware handler are implemented:
-.RS
-.TP 12
-.I 1 emc
-(Hardware-dependent)
-Hardware handler for DGC class arrays as CLARiiON CX/AX and EMC VNX families
-with Failover Mode 1 (Passive Not Ready(PNR)).
-.TP
-.I 1 rdac
-(Hardware-dependent)
-Hardware handler for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF
-Series and rebranded arrays, with "Linux DM-MP (Kernel 3.9 or earlier)" option.
-.TP
-.I 1 hp_sw
-(Hardware-dependent)
-Hardware handler for HP/COMPAQ/DEC HSG80 and MSA/HSV arrays with
-Active/Standby mode exclusively.
-.TP
-.I 1 alua
-(Hardware-dependent)
-Hardware handler for SCSI-3 ALUA compatible arrays.
-.TP
-.I 1 ana
-(Hardware-dependent)
-Hardware handler for NVMe ANA compatible arrays.
-.PP
-The default is: \fB<unset>\fR
-.PP
-\fBImportant Note:\fR Linux kernels 4.3 and newer automatically attach a device
-handler to known devices (which includes all devices supporting SCSI-3 ALUA)
-and disallow changing the handler
-afterwards. Setting \fBhardware_handler\fR for such devices on these kernels
-has no effect.
-.RE
-.
-.
-.LP
-The following attributes are optional; if not set the default values
-are taken from the \fIdefaults\fR
-section:
-.sp 1
-.PD .1v
-.RS
-.TP 18
-.B path_grouping_policy
-.TP
-.B uid_attribute
-.TP
-.B path_selector
-.TP
-.B path_checker
-.TP
-.B prio
-.TP
-.B prio_args
-.TP
-.B features
-.TP
-.B failback
-.TP
-.B rr_weight
-.TP
-.B no_path_retry
-.TP
-.B rr_min_io
-.TP
-.B rr_min_io_rq
-.TP
-.B fast_io_fail_tmo
-.TP
-.B dev_loss_tmo
-.TP
-.B eh_deadline
-.TP
-.B flush_on_last_del
-.TP
-.B user_friendly_names
-.TP
-.B retain_attached_hw_handler
-.TP
-.B detect_prio
-.TP
-.B detect_checker
-.TP
-.B deferred_remove
-.TP
-.B san_path_err_threshold
-.TP
-.B san_path_err_forget_rate
-.TP
-.B san_path_err_recovery_time
-.TP
-.B marginal_path_err_sample_time
-.TP
-.B marginal_path_err_rate_threshold
-.TP
-.B marginal_path_err_recheck_gap_time
-.TP
-.B marginal_path_double_failed_time
-.TP
-.B delay_watch_checks
-.TP
-.B delay_wait_checks
-.TP
-.B skip_kpartx
-.TP
-.B max_sectors_kb
-.TP
-.B ghost_delay
-.TP
-.B all_tg_pt
-.RE
-.PD
-.LP
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "overrides section"
-.\" ----------------------------------------------------------------------------
-.
-The overrides section recognizes the following optional attributes; if not set
-the values are taken from the \fIdevices\fR or \fIdefaults\fR sections:
-.sp 1
-.PD .1v
-.RS
-.TP 18
-.B path_grouping_policy
-.TP
-.B uid_attribute
-.TP
-.B path_selector
-.TP
-.B path_checker
-.TP
-.B alias_prefix
-.TP
-.B features
-.TP
-.B prio
-.TP
-.B prio_args
-.TP
-.B failback
-.TP
-.B rr_weight
-.TP
-.B no_path_retry
-.TP
-.B rr_min_io
-.TP
-.B rr_min_io_rq
-.TP
-.B flush_on_last_del
-.TP
-.B fast_io_fail_tmo
-.TP
-.B dev_loss_tmo
-.TP
-.B eh_deadline
-.TP
-.B user_friendly_names
-.TP
-.B retain_attached_hw_handler
-.TP
-.B detect_prio
-.TP
-.B detect_checker
-.TP
-.B deferred_remove
-.TP
-.B san_path_err_threshold
-.TP
-.B san_path_err_forget_rate
-.TP
-.B san_path_err_recovery_time
-.TP
-.B marginal_path_err_sample_time
-.TP
-.B marginal_path_err_rate_threshold
-.TP
-.B marginal_path_err_recheck_gap_time
-.TP
-.B marginal_path_double_failed_time
-.TP
-.B delay_watch_checks
-.TP
-.B delay_wait_checks
-.TP
-.B skip_kpartx
-.TP
-.B max_sectors_kb
-.TP
-.B ghost_delay
-.TP
-.B all_tg_pt
-.RE
-.PD
-.LP
-The overrides section also recognizes the optional \fIprotocol\fR subsection,
-and can contain multiple protocol subsections. Path devices are matched against
-the protocol subsection using the mandatory \fItype\fR attribute.  Attributes
-in a matching protocol subsection take precedence over attributes in the rest
-of the overrides section. If there are multiple matching protocol subsections,
-later entries take precedence.
-.TP
-.B protocol subsection
-The protocol subsection recognizes the following mandatory attribute:
-.RS
-.TP
-.B type
-The protocol string of the path device. The possible values are \fIscsi:fcp\fR,
-\fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR,
-\fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR,
-\fIscsi:unspec\fR, \fInvme:pcie\fR, \fInvme:rdma\fR, \fInvme:fc\fR,
-\fInvme:tcp\fR, \fInvme:loop\fR, \fInvme:apple-nvme\fR, \fInvme:unspec\fR,
-\fIccw\fR, \fIcciss\fR, and \fIundef\fR. This is
-\fBnot\fR a regular expression. the path device protocol string must match
-exactly. The protocol that a path is using can be viewed by running
-\fBmultipathd show paths format "%d %P"\fR
-.LP
-The following attributes are optional; if not set, the default values are taken
-from the \fIoverrides\fR, \fIdevices\fR, or \fIdefaults\fR section:
-.sp 1
-.PD .1v
-.RS
-.TP
-.B fast_io_fail_tmo
-.TP
-.B dev_loss_tmo
-.TP
-.B eh_deadline
-.PD
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "WWID generation"
-.\" ----------------------------------------------------------------------------
-.
-Multipath uses a \fIWorld Wide Identification\fR (WWID) to determine
-which paths belong to the same device. Each path presenting the same
-WWID is assumed to point to the same device.
-.LP
-The WWID is generated by four methods (in the order of preference):
-.TP 17
-.B uid_attrs
-The WWID is derived from udev attributes by matching the device node name; cf
-\fIuid_attrs\fR above.
-.TP
-.B uid_attribute
-Use the value of the specified udev attribute; cf \fIuid_attribute\fR
-above.
-.TP
-.B sysfs
-Try to determine the WWID from sysfs attributes.
-For SCSI devices, this means reading the Vital Product Data (VPD) page
-\(dqDevice Identification\(dq (0x83).
-.PP
-The default settings (using udev and \fBuid_attribute\fR configured from
-the built-in hardware table) should work fine
-in most scenarios. Users who want to enable uevent merging must set
-\fBuid_attrs\fR.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "Shaky paths detection"
-.\" ----------------------------------------------------------------------------
-.
-A common problem in SAN setups is the occurrence of intermittent errors: a
-path is unreachable, then reachable again for a short time, disappears again,
-and so forth. This happens typically on unstable interconnects. It is
-undesirable to switch pathgroups unnecessarily on such frequent, unreliable
-events. \fImultipathd\fR supports three different methods for detecting this
-situation and dealing with it. All methods share the same basic mode of
-operation: If a path is found to be \(dqshaky\(dq or \(dqflipping\(dq,
-and appears to be in healthy status, it is not reinstated (put back to use)
-immediately. Instead, it is placed in the \(dqdelayed\(dq state and watched
-for some time, and only reinstated if the healthy state appears to be stable.
-If the \fImarginal_pathgroups\fR option is set, the path will reinstated
-immediately, but placed in a special pathgroup for marginal paths. Marginal
-pathgroups will not be used until all other pathgroups have been tried. At the
-time when the path would normally be reinstated, it will be returned to its
-normal pathgroup. The logic of determining \(dqshaky\(dq condition, as well as
-the logic when to reinstate, differs between the three methods.
-.TP 8
-.B \(dqdelay_checks\(dq failure tracking
-(Deprecated) This method is \fBdeprecated\fR and mapped to the \(dqsan_path_err\(dq method.
-See the \fIdelay_watch_checks\fR and \fIdelay_wait_checks\fR options above
-for more information.
-.
-.TP
-.B \(dqmarginal_path\(dq failure tracking
-If a second failure event (good->bad transition) occurs within
-\fImarginal_path_double_failed_time\fR seconds after a failure, high-frequency
-monitoring is started for the affected path: I/O is sent at a rate of 10 per
-second. This is done for \fImarginal_path_err_sample_time\fR seconds. During
-this period, the path is not reinstated. If the
-rate of errors remains below \fImarginal_path_err_rate_threshold\fR during the
-monitoring period, the path is reinstated. Otherwise, it
-is kept in failed state for \fImarginal_path_err_recheck_gap_time\fR, and
-after that, it is monitored again. For this method, time intervals are measured
-in seconds.
-.TP
-.B \(dqsan_path_err\(dq failure tracking
-multipathd counts path failures for each path. Once the number of failures
-exceeds the value given by \fIsan_path_err_threshold\fR, the path is not
-reinstated for \fIsan_path_err_recovery_time\fR seconds. While counting
-failures, multipathd \(dqforgets\(dq one past failure every
-\(dqsan_path_err_forget_rate\(dq ticks; thus if errors don't occur more
-often then once in the forget rate interval, the failure count doesn't
-increase and the threshold is never reached. Ticks are the time between
-path checks by multipathd, which is variable and controlled by the
-\fIpolling_interval\fR and \fImax_polling_interval\fR parameters.
-.
-.RS 8
-.LP
-This algorithm is superseded by the \(dqmarginal_path\(dq failure tracking,
-but remains supported for backward compatibility.
-.
-.RE
-.TP
-.B \(dqFPIN\(dq failure tracking
-Fibre channel fabrics can notify hosts about fabric-level issues such
-as integrity failures or congestion with so-called Fabric Performance
-Impact Notifications (FPINs).On receiving the fpin notifications through ELS
-multipathd will move the affected path and port states to marginal.
-.
-.RE
-.LP
-See the documentation
-of the individual options above for details.
-It is \fBstrongly discouraged\fR to use more than one of these methods for any
-given multipath map, because the two concurrent methods may interact in
-unpredictable ways. If the \(dqmarginal_path\(dq method is active, the
-\(dqsan_path_err\(dq parameters are implicitly set to 0.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "FOREIGN MULTIPATH SUPPORT"
-.\" ----------------------------------------------------------------------------
-.
-multipath and multipathd can load \(dqforeign\(dq libraries to add
-support for other multipathing technologies besides the Linux device mapper.
-Currently this support is limited to printing detected information about
-multipath setup. In topology output, the names of foreign maps are prefixed by
-the foreign library name in square brackets, as in this example:
-.
-.P
-.EX
-# multipath -ll
-uuid.fedcba98-3579-4567-8765-123456789abc [nvme]:nvme4n9 NVMe,Some NVMe controller,FFFFFFFF
-size=167772160 features='n/a' hwhandler='ANA' wp=rw
-|-+- policy='n/a' prio=50 status=optimized
-| `- 4:38:1    nvme4c38n1 0:0     n/a   optimized    live
-`-+- policy='n/a' prio=50 status=optimized
-  `- 4:39:1    nvme4c39n1 0:0     n/a   optimized    live
-.EE
-.
-.P
-The \(dqnvme\(dq foreign library provides support for NVMe native multipathing
-in the kernel. It is part of the standard multipath package.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "KNOWN ISSUES"
-.\" ----------------------------------------------------------------------------
-.
-The usage of \fIqueue_if_no_path\fR option can lead to \fID state\fR
-processes being hung and not killable in situations where all the paths to the
-LUN go offline. It is advisable to use the \fIno_path_retry\fR option instead.
-.P
-The use of \fIqueue_if_no_path\fR or \fIno_path_retry\fR might lead to a
-deadlock if the \fIdev_loss_tmo\fR setting results in a device being removed
-while I/O is still queued. The multipath daemon will update the \fIdev_loss_tmo\fR
-setting accordingly to avoid this deadlock. Hence if both values are
-specified the order of precedence is \fIno_path_retry, queue_if_no_path, dev_loss_tmo\fR.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "SEE ALSO"
-.\" ----------------------------------------------------------------------------
-.
-.BR udev (8),
-.BR dmsetup (8),
-.BR multipath (8),
-.BR multipathd (8).
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH AUTHORS
-.\" ----------------------------------------------------------------------------
-.
-\fImultipath-tools\fR was developed by Christophe Varoqui, <christophe.varoqui@opensvc.com>
-and others.
-.\" EOF
diff --git a/multipath/multipath.conf.5.in b/multipath/multipath.conf.5.in
new file mode 100644 (file)
index 0000000..683bdb7
--- /dev/null
@@ -0,0 +1,2039 @@
+.\" ----------------------------------------------------------------------------
+.\" Make sure there are no errors with:
+.\" groff -z -wall -b -e -t multipath/multipath.conf.5
+.\" man --warnings -E UTF-8 -l -Tutf8 -Z multipath/multipath.conf.5 > /dev/null
+.\"
+.\" Update the date below if you make any significant change.
+.\" ----------------------------------------------------------------------------
+.
+.TH MULTIPATH.CONF 5 2023-06-15 Linux
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH NAME
+.\" ----------------------------------------------------------------------------
+.
+@CONFIGFILE@, @CONFIGDIR@/*.conf \- multipath daemon configuration file.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH DESCRIPTION
+.\" ----------------------------------------------------------------------------
+.
+.B "@CONFIGFILE@"
+is the configuration file for the multipath daemon. It is used to
+overwrite the built-in configuration table of \fBmultipathd\fP.
+Any line whose first non-white-space character is a '#' is considered
+a comment line. Empty lines are ignored.
+.PP
+Currently used multipathd configuration can be displayed with the \fBmultipath -t\fR
+or \fBmultipathd show config\fR command.
+.
+.PP
+Additional configuration can be made in drop-in files under
+.B @CONFIGDIR@.
+Files ending in \fI.conf\fR in this directory are read
+in alphabetical order, after reading \fI@CONFIGFILE@\fR.
+They use the same syntax as \fI@CONFIGFILE@\fR itself,
+and support all sections and keywords. If a keyword occurs in the same section
+in multiple files, the last occurrence will take precedence over all others.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH SYNTAX
+.\" ----------------------------------------------------------------------------
+.
+The configuration file contains entries of the form:
+.RS
+.nf
+.ft B
+.sp
+<section> {
+.RS
+.ft B
+<attribute> <value>
+.I "..."
+.ft B
+<subsection> {
+.RS
+.ft B
+<attribute> <value>
+.I "..."
+.RE
+.ft B
+}
+.RE
+.ft B
+}
+.ft R
+.fi
+.RE
+.LP
+Each \fIsection\fP contains one or more attributes or subsections. The
+recognized keywords for attributes or subsections depend on the
+section in which they occur.
+.LP
+.
+\fB<attribute>\fR and \fB<value>\fR must be on a single line.
+\fB<attribute>\fR is one of the keywords listed in this man page.
+\fB<value>\fR is either a simple word (containing no whitespace and none of the
+characters '\(dq', '#', and '!') or \fIone\fR string enclosed in double
+quotes ("..."). Outside a quoted string, text starting with '#', and '!' is
+regarded as a comment and ignored until the end of the line. Inside a quoted
+string, '#' and '!' are normal characters, and whitespace is preserved.
+To represent a double quote character inside a double quoted string, use two
+consecutive double quotes ('""'). Thus '2.5\(dq SSD' can be written as "2.5"" SSD".
+.LP
+.
+Opening braces ('{') must follow the (sub)section name on the same line. Closing
+braces ('}') that mark the end of a (sub)section must be the only non-whitespace
+character on the line. Whitespace is ignored except inside double quotes, thus
+the indentation shown in the above example is helpful for human readers but
+not mandatory.
+.LP
+.
+.LP
+.B Note on regular expressions:
+The \fI@CONFIGFILE@\fR syntax allows many attribute values to be specified as POSIX
+Extended Regular Expressions (see \fBregex\fR(7)). These regular expressions
+are \fBcase sensitive\fR and \fBnot anchored\fR, thus the expression "bar" matches "barbie",
+"rhabarber", and "wunderbar", but not "Barbie". To avoid unwanted substring
+matches, standard regular expression syntax using the special characters "^" and "$" can be used.
+.
+.LP
+.
+The following \fIsection\fP keywords are recognized:
+.TP 17
+.B defaults
+This section defines default values for attributes which are used
+whenever no values are given in the appropriate device or multipath
+sections.
+.TP
+.B blacklist
+This section defines which devices should be excluded from the
+multipath topology discovery.
+.TP
+.B blacklist_exceptions
+This section defines which devices should be included in the
+multipath topology discovery, despite being listed in the
+\fIblacklist\fR section.
+.TP
+.B multipaths
+This section defines the multipath topologies. They are indexed by a
+\fIWorld Wide Identifier\fR(WWID). For details on the WWID generation
+see section \fIWWID generation\fR below. Attributes set in this section take
+precedence over all others.
+.TP
+.B devices
+This section defines the device-specific settings. Devices are identified by
+vendor, product, and revision.
+.TP
+.B overrides
+This section defines values for attributes that should override the
+device-specific settings for all devices.
+.RE
+.LP
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "defaults section"
+.\" ----------------------------------------------------------------------------
+.
+The \fIdefaults\fR section recognizes the following keywords:
+.
+.
+.TP 17
+.B verbosity
+Default verbosity. Higher values increase the verbosity level. Valid
+levels are between 0 and 6.
+.RS
+.TP
+The default is: \fB2\fR
+.RE
+.
+.
+.TP
+.B polling_interval
+Interval between two path checks in seconds. For properly functioning paths,
+the interval between checks will gradually increase to \fImax_polling_interval\fR.
+This value will be overridden by the \fIWatchdogSec\fR
+setting in the multipathd.service definition if systemd is used.
+.RS
+.TP
+The default is: \fB5\fR
+.RE
+.
+.
+.TP
+.B max_polling_interval
+Maximal interval between two path checks in seconds.
+.RS
+.TP
+The default is: \fB4 * polling_interval\fR
+.RE
+.
+.
+.TP
+.B reassign_maps
+Enable reassigning of device-mapper maps. With this option multipathd
+will remap existing device-mapper maps to always point to multipath
+device, not the underlying block devices. Possible values are
+\fIyes\fR and \fIno\fR.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B multipath_dir
+(Deprecated) This option is not supported any more, and the value is ignored.
+.
+.
+.TP
+.B path_selector
+The default path selector algorithm to use; they are offered by the
+kernel multipath target:
+.RS
+.TP 12
+.I "round-robin 0"
+Loop through every path in the path group, sending the same amount of I/O to
+each. Some aspects of behavior can be controlled with the attributes:
+\fIrr_min_io\fR, \fIrr_min_io_rq\fR and \fIrr_weight\fR.
+.TP
+.I "queue-length 0"
+(Since 2.6.31 kernel) Choose the path for the next bunch of I/O based on the amount
+of outstanding I/O to the path.
+.TP
+.I "service-time 0"
+(Since 2.6.31 kernel) Choose the path for the next bunch of I/O based on the amount
+of outstanding I/O to the path and its relative throughput.
+.TP
+.I "historical-service-time 0"
+(Since 5.8 kernel) Choose the path for the next bunch of I/O based on the
+estimation of future service time based on the history of previous I/O submitted
+to each path.
+.TP
+The default is: \fBservice-time 0\fR
+.RE
+.
+.
+.TP
+.B path_grouping_policy
+The default path grouping policy to apply to unspecified
+multipaths. Possible values are:
+.RS
+.TP 12
+.I failover
+One path per priority group.
+.TP
+.I multibus
+All paths in one priority group.
+.TP
+.I group_by_serial
+One priority group per serial number.
+.TP
+.I group_by_prio
+One priority group per priority value. Priorities are determined by
+callout programs specified as a global, per-controller or
+per-multipath option in the configuration file.
+.TP
+.I group_by_node_name
+One priority group per target node name. Target node names are fetched
+in \fI/sys/class/fc_transport/target*/node_name\fR.
+.TP
+.I group_by_tpg
+One priority group per ALUA target port group. In order to use this policy,
+all paths in the multipath device must have \fIprio\fR set to \fBalua\fR.
+.TP
+The default is: \fBfailover\fR
+.RE
+.
+.
+.TP
+.B detect_pgpolicy
+If set to \fIyes\fR and all path devices are configured with either the
+\fIalua\fR or \fIsysfs\fR prioritizer, the multipath device will automatically
+use the \fIgroup_by_prio\fR path_grouping_policy. If set to \fIno\fR, the
+path_grouping_policy will be selected as usual.
+.RS
+.TP
+The default is: \fByes\fR
+.RE
+.
+.
+.TP
+.B detect_pgpolicy_use_tpg
+If both this and \fIdetect_pgpolicy\fR are set to \fIyes\fR and all path
+devices are configured with either the \fIalua\fR or \fIsysfs\fR prioritizer,
+the multipath device will automatically use the \fIgroup_by_tpg\fR
+path_grouping_policy. If set to \fIno\fR, the path_grouping_policy will be
+selected by the method described for \fIdetect_pgpolicy\fR above.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B pg_timeout
+(Deprecated) This option is not supported any more, and the value is ignored.
+.
+.
+.TP
+.B uid_attrs
+.
+Setting this option activates \fBmerging uevents\fR by WWID, which may improve
+uevent processing efficiency. Moreover, it's an alternative method to configure
+the udev properties to use for determining unique path identifiers (WWIDs).
+.RS
+.PP
+The value of this option is a space separated list of records like
+\(dq\fItype:ATTR\fR\(dq, where \fItype\fR is matched against the beginning
+of the device node name (e.g. \fIsd:ATTR\fR matches \fIsda\fR), and
+\fIATTR\fR is the name of the udev property to use for matching devices.
+.PP
+If this option is configured and matches the device
+node name of a device, it overrides any other configured  methods for
+determining the WWID for this device.
+.PP
+This option cannot be changed during runtime with the multipathd \fBreconfigure\fR command.
+.PP
+The default is: \fB<unset>\fR. To enable uevent merging, set it e.g. to
+\(dqsd:ID_SERIAL dasd:ID_UID nvme:ID_WWN\(dq.
+.RE
+.
+.
+.TP
+.B uid_attribute
+The udev attribute providing a unique path identifier (WWID). If
+\fIuid_attribute\fR is set to the empty string, WWID determination is done
+using the \fIsysfs\fR method rather then using udev (not recommended in
+production; see \fBWWID generation\fR below).
+.RS
+.TP
+The default is: \fBID_SERIAL\fR, for SCSI devices
+.TP
+The default is: \fBID_UID\fR, for DASD devices
+.TP
+The default is: \fBID_WWN\fR, for NVMe devices
+.RE
+.
+.
+.TP
+.B getuid_callout
+(Deprecated) This option is not supported any more, and the value is ignored.
+.
+.
+.TP
+.B prio
+The name of the path priority routine. The specified routine
+should return a numeric value specifying the relative priority
+of this path. Higher number have a higher priority.
+\fI"none"\fR is a valid value. Currently the following path priority routines
+are implemented:
+.RS
+.TP 12
+.I const
+Return a constant priority of \fI1\fR.
+.TP
+.I sysfs
+Use the sysfs attributes \fIaccess_state\fR and \fIpreferred_path\fR to
+generate the path priority. This prioritizer accepts the optional prio_arg
+\fIexclusive_pref_bit\fR.
+.TP
+.I emc
+(Hardware-dependent)
+Generate the path priority for DGC class arrays as CLARiiON CX/AX and
+EMC VNX families with Failover Mode 1 (Passive Not Ready(PNR)).
+.TP
+.I alua
+(Hardware-dependent)
+Generate the path priority based on the SCSI-3 ALUA settings. This prioritizer
+accepts the optional prio_arg \fIexclusive_pref_bit\fR.
+.TP
+.I ontap
+(Hardware-dependent)
+Generate the path priority for NetApp ONTAP FAS/AFF Series and rebranded arrays,
+with ONTAP native mode(not ALUA).
+.TP
+.I rdac
+(Hardware-dependent)
+Generate the path priority for LSI/Engenio/NetApp RDAC class as NetApp SANtricity
+E/EF Series and rebranded arrays, with "Linux DM-MP (Kernel 3.9 or earlier)" option.
+.TP
+.I hp_sw
+(Hardware-dependent)
+Generate the path priority for HP/COMPAQ/DEC HSG80 and MSA/HSV arrays with
+Active/Standby mode exclusively.
+.TP
+.I hds
+(Hardware-dependent)
+Generate the path priority for Hitachi AMS families of arrays other than AMS 2000.
+.TP
+.I random
+Generate a random priority between 1 and 10.
+.TP
+.I weightedpath
+Generate the path priority based on the regular expression and the
+priority provided as argument. Requires prio_args keyword.
+.TP
+.I path_latency
+Generate the path priority based on a latency algorithm.
+Requires prio_args keyword.
+.TP
+.I ana
+(Hardware-dependent)
+Generate the path priority based on the NVMe ANA settings.
+.TP
+.I datacore
+(Hardware-dependent)
+Generate the path priority for some DataCore storage arrays. Requires prio_args
+keyword.
+.TP
+.I iet
+(iSCSI only)
+Generate path priority for iSCSI targets based on IP address. Requires
+prio_args keyword.
+.PP
+The default depends on the \fBdetect_prio\fR setting: If \fBdetect_prio\fR is
+\fByes\fR (default), the default priority algorithm is \fBsysfs\fR (except for
+NetAPP E/EF Series, where it is \fBalua\fR). If \fBdetect_prio\fR is
+\fBno\fR, the default priority algorithm is \fBconst\fR.
+.RE
+.
+.
+.TP
+.B prio_args
+Arguments to pass to to the prio function. This only applies to certain
+prioritizers:
+.RS
+.TP 12
+.I weighted
+Needs a value of the form
+\fI"<hbtl|devname|serial|wwn> <regex1> <prio1> <regex2> <prio2> ..."\fR
+.RS
+.TP 8
+.I hbtl
+Regex can be of SCSI H:B:T:L format. For example: 1:0:.:. , *:0:0:.
+.TP
+.I devname
+Regex can be of device name format. For example: sda , sd.e
+.TP
+.I serial
+Regex can be of serial number format. For example: .*J1FR.*324 . The serial can
+be looked up through sysfs or by running multipathd show paths format "%z". For
+example: 0395J1FR904324
+.TP
+.I wwn
+Regex can be of the form \fI"host_wwnn:host_wwpn:target_wwnn:target_wwpn"\fR
+these values can be looked up through sysfs or by running \fImultipathd show paths format
+"%N:%R:%n:%r"\fR. For example: 0x200100e08ba0aea0:0x210100e08ba0aea0:.*:.* , .*:.*:iqn.2009-10.com.redhat.msp.lab.ask-06:.*
+.RE
+.TP 12
+.I path_latency
+Needs a value of the form "io_num=\fI<20>\fR base_num=\fI<10>\fR"
+.RS
+.TP 8
+.I io_num
+The number of read IOs sent to the current path continuously, used to calculate the average path latency.
+Valid Values: Integer, [2, 200].
+.TP
+.I base_num
+The base number value of logarithmic scale, used to partition different priority ranks. Valid Values: Integer,
+[2, 10]. And Max average latency value is 100s, min average latency value is 1us.
+For example: If base_num=10, the paths will be grouped in priority groups with path latency <=1us, (1us, 10us],
+(10us, 100us], (100us, 1ms], (1ms, 10ms], (10ms, 100ms], (100ms, 1s], (1s, 10s], (10s, 100s], >100s.
+.RE
+.TP 12
+.I alua
+If \fIexclusive_pref_bit\fR is set, paths with the \fIpreferred path\fR bit
+set will always be in their own path group.
+.TP
+.I sysfs
+If \fIexclusive_pref_bit\fR is set, paths with the \fIpreferred path\fR bit
+set will always be in their own path group.
+.TP
+.I datacore
+.RS
+.TP 8
+.I preferredsds
+(Mandatory) The preferred "SDS name".
+.TP
+.I timeout
+(Optional) The timeout for the INQUIRY, in ms.
+.RE
+.TP 12
+.I iet
+.RS
+.TP 8
+.I preferredip=...
+(Mandatory) Th preferred IP address, in dotted decimal notation, for iSCSI targets.
+.RE
+.TP
+The default is: \fB<unset>\fR
+.RE
+.
+.
+.TP
+.B features
+Specify any device-mapper features to be used. Syntax is \fInum list\fR
+where \fInum\fR is the number, between 0 and 8, of features in \fIlist\fR.
+Possible values for the feature list are:
+.RS
+.TP 12
+.I queue_if_no_path
+(Deprecated, superseded by \fIno_path_retry\fR) Queue I/O if no path is active.
+Identical to the \fIno_path_retry\fR with \fIqueue\fR value. If both this
+feature and \fIno_path_retry\fR are set, the latter value takes
+precedence. See KNOWN ISSUES.
+.TP
+.I pg_init_retries <times>
+(Since kernel 2.6.24) Number of times to retry pg_init, it must be between 1 and 50.
+.TP
+.I pg_init_delay_msecs <msecs>
+(Since kernel 2.6.38) Number of msecs before pg_init retry, it must be between 0 and 60000.
+.TP
+.I queue_mode <mode>
+(Since kernel 4.8) Select the queueing mode per multipath device.
+<mode> can be \fIbio\fR, \fIrq\fR or \fImq\fR, which corresponds to
+bio-based, request-based, and block-multiqueue (blk-mq) request-based,
+respectively.
+Before kernel 4.20 The default depends on the kernel parameter
+\fBdm_mod.use_blk_mq\fR. It is \fImq\fR if the latter is set, and \fIrq\fR
+otherwise. Since kernel 4.20, \fIrq\fR and \fImq\fR both correspond to
+block-multiqueue. Once a multipath device has been created, its queue_mode
+cannot be changed. \fInvme:tcp\fR paths are only supported in multipath
+devices with queue_mode set to \fIbio\fR. multipath will automatically
+set this when creating a device with \fInvme:tcp\fR paths.
+.TP
+The default is: \fB0\fR
+.RE
+.
+.
+.TP
+.B path_checker
+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
+(Deprecated) Read the first sector of the device. This checker is being
+deprecated, please use \fItur\fR or \fIdirectio\fR instead.
+.TP
+.I tur
+Issue a \fITEST UNIT READY\fR command to the device.
+.TP
+.I emc_clariion
+(Hardware-dependent)
+Query the DGC/EMC specific EVPD page 0xC0 to determine the path state
+for CLARiiON CX/AX and EMC VNX and Unity arrays families.
+.TP
+.I hp_sw
+(Hardware-dependent)
+Check the path state for HP/COMPAQ/DEC HSG80 and MSA/HSV arrays with
+Active/Standby mode exclusively.
+.TP
+.I rdac
+(Hardware-dependent)
+Check the path state for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF
+Series, and rebranded arrays.
+.TP
+.I directio
+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)
+Check the path state for HP/COMPAQ Smart Array(CCISS) controllers.
+.TP
+.I none
+Do not check the device, fallback to use the values retrieved from sysfs
+.TP
+The default is: \fBtur\fR
+.RE
+.
+.
+.TP
+.B alias_prefix
+The \fIuser_friendly_names\fR prefix.
+.RS
+.TP
+The default is: \fBmpath\fR
+.RE
+.
+.
+.TP
+.B failback
+Tell multipathd how to manage path group failback.
+To select \fIimmediate\fR or a \fIvalue\fR, it's mandatory that the device
+has support for a working prioritizer.
+.RS
+.TP 12
+.I immediate
+Immediately failback to the highest priority pathgroup that contains
+active paths.
+.TP
+.I manual
+Do not perform automatic failback.
+.TP
+.I followover
+Used to deal with multiple computers accessing the same Active/Passive storage
+devices. Only perform automatic failback when the first path of a pathgroup
+becomes active. This keeps a cluster node from automatically failing back when
+another node requested the failover.
+.TP
+.I values > 0
+Deferred failback (time to defer in seconds).
+.TP
+The default is: \fBmanual\fR
+.RE
+.
+.
+.TP
+.B rr_min_io
+Number of I/O requests to route to a path before switching to the next in the
+same path group. This is only for \fIBlock I/O\fR(BIO) based multipath and
+only apply to \fIround-robin\fR path_selector.
+.RS
+.TP
+The default is: \fB1000\fR
+.RE
+.
+.
+.TP
+.B rr_min_io_rq
+Number of I/O requests to route to a path before switching to the next in the
+same path group. This is only for \fIRequest\fR based multipath and
+only apply to \fIround-robin\fR path_selector.
+.RS
+.TP
+The default is: \fB1\fR
+.RE
+.
+.
+.TP
+.B max_fds
+Specify the maximum number of file descriptors that can be opened by multipath
+and multipathd. This is equivalent to ulimit \-n. A value of \fImax\fR will set
+this to the system limit from \fI/proc/sys/fs/nr_open\fR. If this is not set, the
+maximum number of open fds is taken from the calling process. It is usually
+1024. To be safe, this should be set to the maximum number of paths plus 32,
+if that number is greater than 1024.
+.RS
+.TP
+The default is: \fBmax\fR
+.RE
+.
+.
+.TP
+.B rr_weight
+If set to \fIpriorities\fR the multipath configurator will assign path weights
+as "path prio * rr_min_io". Possible values are
+.I priorities
+or
+.I uniform .
+Only apply to \fIround-robin\fR path_selector.
+.RS
+.TP
+The default is: \fBuniform\fR
+.RE
+.
+.
+.TP
+.B no_path_retry
+Specify what to do when all paths are down. Possible values are:
+.RS
+.TP 12
+.I value > 0
+Number of retries until disable I/O queueing.
+.TP
+.I fail
+For immediate failure (no I/O queueing).
+.TP
+.I queue
+For never stop I/O queueing, similar to \fIqueue_if_no_path\fR. See KNOWN ISSUES.
+.TP
+The default is: \fBfail\fR
+.RE
+.
+.
+.TP
+.B queue_without_daemon
+If set to
+.I no
+, when multipathd stops, queueing will be turned off for all devices.
+This is useful for devices that set no_path_retry. If a machine is
+shut down while all paths to a device are down, it is possible to hang waiting
+for I/O to return from the device after multipathd has been stopped. Without
+multipathd running, access to the paths cannot be restored, and the kernel
+cannot be told to stop queueing I/O. Setting queue_without_daemon to
+.I no
+, avoids this problem.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B checker_timeout
+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/<dev>/device/timeout\fR
+.RE
+.
+.
+.TP
+.B allow_usb_devices
+If set to
+.I no
+, all USB devices will be skipped during path discovery. If you intend to use
+multipath on USB attached devices, set this to \fIyes\fR.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B flush_on_last_del
+If set to
+.I yes
+, multipathd will disable queueing when the last path to a device has been
+deleted.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B user_friendly_names
+If set to
+.I yes
+, using the bindings file \fI@STATE_DIR@/bindings\fR to assign a persistent
+and unique alias to the multipath, in the form of mpath<n>. If set to
+.I no
+use the WWID as the alias. In either case this be will
+be overridden by any specific aliases in the \fImultipaths\fR section.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B fast_io_fail_tmo
+Specify the number of seconds the SCSI layer will wait after a problem has been
+detected on a FC remote port before failing I/O to devices on that remote port.
+This should be smaller than dev_loss_tmo. Setting this to
+.I off
+will disable the timeout.
+.RS
+.TP
+The default is: \fB5\fR
+.RE
+.
+.
+.TP
+.B dev_loss_tmo
+Specify the number of seconds the SCSI layer will wait after a connection loss has
+been detected on a remote port before removing it from the system. This
+can be set to "infinity", which effectively means 136 years (2^32-1 seconds).
+This parameter is only applied to Fibre Channel and SAS devices.
+.RS
+.LP
+The value of \fIdev_loss_tmo\fR is restricted by other settings.
+If \fIfast_io_fail_tmo\fR is set to a positive value, \fBmultipathd\fR
+will make sure that the value of \fIdev_loss_tmo\fR is larger than
+\fIno_path_retry\fR * \fIpolling_interval\fR.
+If \fIfast_io_fail_tmo\fR is not set, the kernel limits the \fIdev_loss_tmo\fR
+value to 600 seconds.
+In this case, the user has to make sure that \fIno_path_retry\fR is smaller
+than \fIdev_loss_tmo / polling_interval\fR. In particular,
+\fIno_path_retry\fR must not be set to \(dq\fIqueue\fR\(dq. See KNOWN ISSUES.
+.LP
+When path devices reappear after a connection loss, it is much easier for
+the kernel to simply reactivate an inactive device than to re-add
+a previously deleted one. It is therefore recommended to set
+\fIdev_loss_tmo\fR to a large value within the restrictions mentioned above.
+.LP
+Fibre Channel and SAS devices have hardware-dependent defaults, which are left
+unchanged if \fIdev_loss_tmo\fR is not specified. For a few storage arrays,
+the multipath-tools built-in settings override the default. Run \fImultipath -T\fR
+to see the settings for your device.
+.TP
+The default is: \fB<hardware dependent>\fR
+.RE
+.
+.
+.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 due 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 max_retries
+Specify the maximum number of times the SCSI layer will retry IO commands for
+some types of SCSI errors before returning failure. Setting this can be helpful
+for cases where IO commands hang and timeout. By default, the SCSI layer will
+retry IOs 5 times. Reducing this value will allow multipath to retry the IO
+down another path sooner. Valid values are
+\fB0\fR through \fB5\fR.
+.RS
+.TP
+The default is: \fB<unset>\fR
+.RE
+.
+.
+.TP
+.B bindings_file
+(Deprecated) This option is not supported any more, and will be ignored.
+.RS
+.TP
+The compiled-in value is: \fB@STATE_DIR@/bindings\fR
+.RE
+.
+.
+.TP
+.B wwids_file
+(Deprecated) This option is not supported any more, and will be ignored.
+.RS
+.TP
+The compiled-in value is: \fB@STATE_DIR@/wwids\fR
+.RE
+.
+.
+.TP
+.B prkeys_file
+(Deprecated) This option is not supported any more, and will be ignored.
+.RS
+.TP
+The compiled-in value is: \fB@STATE_DIR@/prkeys\fR
+.RE
+.
+.
+.TP
+.B log_checker_err
+If set to
+.I once
+, multipathd logs the first path checker error at logging level 2. Any later
+errors are logged at level 3 until the device is restored. If set to
+.I always
+, multipathd always logs the path checker error at logging level 2.
+.RS
+.TP
+The default is: \fBalways\fR
+.RE
+.
+.
+.TP
+.B reservation_key
+This is the service action reservation key used by mpathpersist. It must be
+set for all multipath devices using persistent reservations, and it must be
+the same as the RESERVATION KEY field of the PERSISTENT RESERVE OUT parameter
+list which contains an 8-byte value provided by the application client to the
+device server to identify the I_T nexus. If the \fI--param-aptpl\fR option is
+used when registering the key with mpathpersist, \fB:aptpl\fR must be appended
+to the end of the reservation key.
+.RS
+.PP
+Alternatively, this can be set to \fBfile\fR, which will store the RESERVATION
+KEY registered by mpathpersist in the \fIprkeys_file\fR. multipathd will then
+use this key to register additional paths as they appear.  When the
+registration is removed, the RESERVATION KEY is removed from the
+\fIprkeys_file\fR. The prkeys file will automatically keep track of whether
+the key was registered with \fI--param-aptpl\fR.
+.TP
+The default is: \fB<unset>\fR
+.RE
+.
+.
+.TP
+.B all_tg_pt
+Set the 'all targets ports' flag when registering keys with mpathpersist. Some
+arrays automatically set and clear registration keys on all target ports from a
+host, instead of per target port per host. The ALL_TG_PT flag must be set to
+successfully use mpathpersist on these arrays. Setting this option is identical
+to calling mpathpersist with \fI--param-alltgpt\fR
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B retain_attached_hw_handler
+(Obsolete for kernels >= 4.3) If set to
+.I yes
+and the SCSI layer has already attached a hardware_handler to the device,
+multipath will not force the device to use the hardware_handler specified by
+@CONFIGFILE@. If the SCSI layer has not attached a hardware handler,
+multipath will continue to use its configured hardware handler.
+.RS
+.PP
+The default is: \fByes\fR
+.PP
+\fBImportant Note:\fR Linux kernel 4.3 or newer always behaves as if
+\fB"retain_attached_hw_handler yes"\fR was set.
+.RE
+.
+.
+.TP
+.B detect_prio
+If set to
+.I yes
+, multipath will try to detect if the device supports SCSI-3 ALUA. If so, the
+device will automatically use the \fIsysfs\fR prioritizer if the required sysf
+attributes \fIaccess_state\fR and \fIpreferred_path\fR are supported, or the
+\fIalua\fR prioritizer if not. If set to
+.I no
+, the prioritizer will be selected as usual.
+.RS
+.TP
+The default is: \fByes\fR
+.RE
+.
+.
+.TP
+.B detect_checker
+if set to
+.I yes
+, multipath will try to detect if the device supports SCSI-3 ALUA. If so, the
+device will automatically use the \fItur\fR checker. If set to
+.I no
+, the checker will be selected as usual.
+.RS
+.TP
+The default is: \fByes\fR
+.RE
+.
+.
+.TP
+.B force_sync
+If set to
+.I yes
+, multipathd will call the path checkers in sync mode only.  This means that
+only one checker will run at a time.  This is useful in the case where many
+multipathd checkers running in parallel causes significant CPU pressure.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B strict_timing
+If set to
+.I yes
+, multipathd will start a new path checker loop after exactly one second,
+so that each path check will occur at exactly \fIpolling_interval\fR
+seconds. On busy systems path checks might take longer than one second;
+here the missing ticks will be accounted for on the next round.
+A warning will be printed if path checks take longer than \fIpolling_interval\fR
+seconds.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B deferred_remove
+If set to
+.I yes
+, multipathd will do a deferred remove instead of a regular remove when the
+last path device has been deleted.  This means that if the multipath device is
+still in use, it will be freed when the last user closes it.  If path is added
+to the multipath device before the last user closes it, the deferred remove
+will be canceled.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B partition_delimiter
+This parameter controls how multipath chooses the names of partition devices
+of multipath maps if a multipath map is renamed (e.g. if a map alias is added
+or changed). If this parameter is set to a string other than "/UNSET/" (even
+the empty string), multipath inserts that string between device name and
+partition number to construct the partition device name.
+Otherwise (i.e. if this parameter is unset or has the value "/UNSET/"),
+the behavior depends on the map name: if it ends in a digit, a \fI"p"\fR is
+inserted between name and partition number; otherwise, the partition number is
+simply appended.
+Distributions may use a non-null default value for this option; in this case,
+the user must set it to "/UNSET/" to obtain the original \fB<unset>\fR
+behavior. Use \fImultipath -T\fR to check the current settings.
+.RS
+.TP
+The default is: \fB<unset>\fR
+.RE
+.
+.
+.TP
+.B config_dir
+(Deprecated) This option is not supported any more, and the value is ignored.
+.RS
+.TP
+The compiled-in value is: \fB@CONFIGDIR@\fR
+.RE
+.
+.
+.TP
+.B san_path_err_threshold
+If set to a value greater than 0, multipathd will watch paths and check how many
+times a path has been failed due to errors.If the number of failures on a particular
+path is greater than the san_path_err_threshold, then the path will not reinstate
+till san_path_err_recovery_time. These path failures should occur within a
+san_path_err_forget_rate checks, if not we will consider the path is good enough
+to reinstantate. See "Shaky paths detection" below.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B san_path_err_forget_rate
+If set to a value greater than 0, multipathd will check whether the path failures
+has exceeded  the san_path_err_threshold within this many checks i.e
+san_path_err_forget_rate . If so we will not reinstate the path till
+san_path_err_recovery_time. See "Shaky paths detection" below.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B san_path_err_recovery_time
+If set to a value greater than 0, multipathd will make sure that when path failures
+has exceeded the san_path_err_threshold within san_path_err_forget_rate then the path
+will be placed in failed state for san_path_err_recovery_time duration.Once san_path_err_recovery_time
+has timeout  we will reinstate the failed path .
+san_path_err_recovery_time value should be in secs.
+See "Shaky paths detection" below.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B marginal_path_double_failed_time
+One of the four parameters of supporting path check based on accounting IO
+error such as intermittent error. When a path failed event occurs twice in
+\fImarginal_path_double_failed_time\fR seconds due to an IO error and all the
+other three parameters are set, multipathd will fail the path and enqueue
+this path into a queue of which members are sent a couple of continuous
+direct reading asynchronous IOs at a fixed sample rate of 10HZ to start IO
+error accounting process. See "Shaky paths detection" below.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B marginal_path_err_sample_time
+One of the four parameters of supporting path check based on accounting IO
+error such as intermittent error. If it is set to a value no less than 120,
+when a path fail event occurs twice in \fImarginal_path_double_failed_time\fR
+second due to an IO error, multipathd will fail the path and enqueue this
+path into a queue of which members are sent a couple of continuous direct
+reading asynchronous IOs at a fixed sample rate of 10HZ to start the IO
+accounting process for the path will last for
+\fImarginal_path_err_sample_time\fR.
+If the rate of IO error on a particular path is greater than the
+\fImarginal_path_err_rate_threshold\fR, then the path will not reinstate for
+\fImarginal_path_err_recheck_gap_time\fR seconds unless there is only one
+active path. After \fImarginal_path_err_recheck_gap_time\fR expires, the path
+will be requeued for rechecking. If checking result is good enough, the
+path will be reinstated. See "Shaky paths detection" below.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B marginal_path_err_rate_threshold
+The error rate threshold as a permillage (1/1000). One of the four parameters
+of supporting path check based on accounting IO error such as intermittent
+error. Refer to \fImarginal_path_err_sample_time\fR. If the rate of IO errors
+on a particular path is greater than this parameter, then the path will not
+reinstate for \fImarginal_path_err_recheck_gap_time\fR seconds unless there is
+only one active path. See "Shaky paths detection" below.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B marginal_path_err_recheck_gap_time
+One of the four parameters of supporting path check based on accounting IO
+error such as intermittent error. Refer to
+\fImarginal_path_err_sample_time\fR. If this parameter is set to a positive
+value, the failed path of  which the IO error rate is larger than
+\fImarginal_path_err_rate_threshold\fR will be kept in failed state for
+\fImarginal_path_err_recheck_gap_time\fR seconds. When
+\fImarginal_path_err_recheck_gap_time\fR seconds expires, the path will be
+requeued for checking. If checking result is good enough, the path will be
+reinstated, or else it will keep failed. See "Shaky paths detection" below.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B delay_watch_checks
+(Deprecated) This option is \fBdeprecated\fR, and mapped to \fIsan_path_err_forget_rate\fR.
+If this is set to a value greater than 0 and no \fIsan_path_err\fR options
+are set, \fIsan_path_err_forget_rate\fR will be set to the value of
+\fIdelay_watch_checks\fR and \fIsan_path_err_threshold\fR will be set to 1.
+See the \fIsan_path_err_forget_rate\fR and \fIsan_path_err_threshold\fR
+options, and "Shaky paths detection" below for more information.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B delay_wait_checks
+(Deprecated) This option is \fBdeprecated\fR, and mapped to \fIsan_path_err_recovery_time\fR.
+If this is set to a value greater than 0 and no \fIsan_path_err\fR options
+are set, \fIsan_path_err_recovery_time\fR will be set to the value of
+\fIdelay_wait_checks\fR times \fImax_polling_interval\fR. This will give
+approximately the same wait time as delay_wait_checks previously did.
+Also, \fIsan_path_err_threshold\fR will be set to 1. See the
+\fIsan_path_err_recovery_time\fR and \fIsan_path_err_threshold\fR
+options, and "Shaky paths detection" below for more information.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B marginal_pathgroups
+If set to \fIoff\fR, the \fIdelay_*_checks\fR, \fImarginal_path_*\fR, and
+\fIsan_path_err_*\fR options will keep marginal, or \(dqshaky\(dq, paths from
+being reinstated until they have been monitored for some time. This can cause
+situations where all non-marginal paths are down, and no paths are usable
+until multipathd detects this and reinstates a marginal path. If the multipath
+device is not configured to queue IO in this case, it can cause IO errors to
+occur, even though there are marginal paths available.  However, if this
+option is set to \fIon\fR, when one of the marginal path detecting methods
+determines that a path is marginal, it will be reinstated and placed in a
+separate pathgroup that will only be used after all the non-marginal pathgroups
+have been tried first. This prevents the possibility of IO errors occurring
+while marginal paths are still usable. After the path has been monitored
+for the configured time, and is declared healthy, it will be returned to its
+normal pathgroup.
+If this option is set to \fIfpin\fR, multipathd will receive fpin
+notifications, set path states to "marginal" accordingly, and regroup paths
+as described for \fIon\fR. This option can't be used in combination
+with other options for "Shaky path detection" (see below). \fBNote:\fR If this
+is set to \fIfpin\fR, the \fImarginal_path_*\fR and \fIsan_path_err_*\fR
+options are implicitly set to \fIno\fP. Also, this option cannot be switched
+either to or from \fIfpin\fR on a multipathd reconfigure. multipathd must be
+restarted for the change to take effect.
+See "Shaky paths detection" below for more information.
+.RS
+.TP
+The default is: \fBoff\fR
+.RE
+.
+.
+.TP
+.B find_multipaths
+This option controls whether multipath and multipathd try to create multipath
+maps over non-blacklisted devices they encounter. This matters a) when a device is
+encountered by \fBmultipath -u\fR during udev rule processing (a device is
+blocked from further processing by higher layers - such as LVM - if and only
+if it\'s considered a valid multipath device path), and b) when multipathd
+detects a new device. The following values are possible:
+.RS
+.TP 10
+.I strict
+Both multipath and multipathd treat only such devices as multipath devices
+which have been part of a multipath map previously, and which are therefore
+listed in the \fBwwids_file\fR. Users can manually set up multipath maps using the
+\fBmultipathd add map\fR command. Once set up manually, the map is
+remembered in the wwids file and will be set up automatically in the future.
+.TP
+.I no
+Multipath behaves like \fBstrict\fR. Multipathd behaves like \fBgreedy\fR.
+.TP
+.I yes
+Both multipathd and multipath treat a device as multipath device if the
+conditions for \fBstrict\fR are met, or if at least two non-blacklisted paths
+with the same WWID have been detected.
+.TP
+.I greedy
+Both multipathd and multipath treat every non-blacklisted device as multipath
+device path.
+.TP
+.I smart
+This differs from \fIfind_multipaths yes\fR only in
+the way it treats new devices for which only one path has been
+detected yet. When such a device is first encountered in udev rules, it is
+treated as a multipath device. multipathd waits whether additional paths with
+the same WWID appears. If that happens, it sets up a multipath map. If it
+doesn\'t happen until a
+timeout expires, or if setting up the map fails, a new uevent is triggered for
+the device; at second encounter in the udev rules, the device will be treated
+as non-multipath and passed on to upper layers.
+\fBNote:\fR this may cause delays during device detection if
+there are single-path devices which aren\'t blacklisted.
+.TP
+The default is: \fBstrict\fR
+.RE
+.
+.
+.TP
+.B find_multipaths_timeout
+Timeout, in seconds, to wait for additional paths after detecting the first
+one, if \fIfind_multipaths
+"smart"\fR (see above) is set. If the value is \fBpositive\fR, this timeout is used for all
+unknown, non-blacklisted devices encountered. If the value is \fBnegative\fR
+(recommended), it's only
+applied to "known" devices that have an entry in multipath's hardware table,
+either in the built-in table or in a \fIdevice\fR section; other ("unknown") devices will
+use a timeout of only 1 second to avoid booting delays. The value 0 means
+"use the built-in default". If \fIfind_multipath\fR has a value
+other than \fIsmart\fR, this option has no effect.
+.RS
+.TP
+The default is: \fB-10\fR (10s for known and 1s for unknown hardware)
+.RE
+.
+.
+.TP
+.B uxsock_timeout
+CLI receive timeout in milliseconds. For larger systems CLI commands
+might timeout before the multipathd lock is released and the CLI command
+can be processed. This will result in errors like
+"timeout receiving packet" to be returned from CLI commands.
+In these cases it is recommended to increase the CLI timeout to avoid
+those issues.
+.RS
+.TP
+The default is: \fB4000\fR
+.RE
+.
+.
+.TP
+.B retrigger_tries
+Sets the number of times multipathd will try to retrigger a uevent to get the
+WWID.
+.RS
+.TP
+The default is: \fB3\fR
+.RE
+.
+.
+.TP
+.B retrigger_delay
+Sets the amount of time, in seconds, to wait between retriggers.
+.RS
+.TP
+The default is: \fB10\fR
+.RE
+.
+.
+.TP
+.B missing_uev_wait_timeout
+Controls how many seconds multipathd will wait, after a new multipath device
+is created, to receive a change event from udev for the device, before
+automatically enabling device reloads. Usually multipathd will delay reloads
+on a device until it receives a change uevent from the initial table load.
+.RS
+.TP
+The default is: \fB30\fR
+.RE
+.
+.
+.TP
+.B skip_kpartx
+If set to
+.I yes
+, kpartx will not automatically create partitions on the device.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B disable_changed_wwids
+(Deprecated) This option is not supported any more, and the value is ignored.
+.RE
+.
+.
+.TP
+.B remove_retries
+This sets how may times multipath will retry removing a device that is in-use.
+Between each attempt, multipath will sleep 1 second.
+.RS
+.TP
+The default is: \fB0\fR
+.RE
+.
+.
+.TP
+.B max_sectors_kb
+Sets the max_sectors_kb device parameter on all path devices and the multipath
+device to the specified value.
+.RS
+.TP
+The default is: in \fB/sys/block/<dev>/queue/max_sectors_kb\fR
+.RE
+.
+.
+.TP
+.B ghost_delay
+Sets the number of seconds that multipath will wait after creating a device
+with only ghost paths before marking it ready for use in systemd. This gives
+the active paths time to appear before the multipath runs the hardware handler
+to switch the ghost paths to active ones. Setting this to \fI0\fR or \fIno\fR
+makes multipath immediately mark a device with only ghost paths as ready.
+.RS
+.TP
+The default is: \fBno\fR
+.RE
+.
+.
+.TP
+.B auto_resize
+Controls when multipathd will automatically resize a multipath device.  If set
+to \fInever\fR, multipath devices must always be manually resized by either
+running \fBmultipathd resize map <name>\fR.  If set to \fIgrow_only\fR, when
+multipathd detects that all of a multipath device's paths have increased in
+size, it will automatically grow the multipath device to the new size. If set
+to \fIgrow_shrink\fR, multipathd will also automatically shrink the device
+once it detects all of its paths have decreased in size.
+.RS
+.TP
+The default is: \fBnever\fR
+.RE
+.
+.
+.TP
+.B enable_foreign
+Enables or disables foreign libraries (see section
+.I FOREIGN MULTIPATH SUPPORT
+below). The value is a regular expression; foreign libraries are loaded
+if their name (e.g. \(dqnvme\(dq) matches the expression. By default,
+no foreign libraries are enabled. Set this to \(dqnvme\(dq to enable NVMe native
+multipath support, or \(dq.*\(dq to enable all foreign libraries.
+.RS
+.TP
+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
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "blacklist and blacklist_exceptions sections"
+.\" ----------------------------------------------------------------------------
+.
+The \fIblacklist\fR section is used to exclude specific devices from
+the multipath topology. It is most commonly used to exclude local disks or
+non-disk devices (such as LUNs for the storage array controller) from
+being handled by multipath-tools.
+.LP
+.
+.
+In the \fIblacklist\fR and \fIblacklist_exceptions\fR sections, starting a
+quoted value with an exclamation mark \fB"!"\fR will invert the matching
+of the rest of the regular expression. For instance, \fB"!^sd[a-z]"\fR will
+match all values that do not start with \fB"sd[a-z]"\fR. The exclamation mark
+can be escaped \fB"\\!"\fR to match a literal \fB!\fR at the start of a
+regular expression. \fBNote:\fR The exclamation mark must be inside quotes,
+otherwise it will be treated as starting a comment.
+.LP
+.
+.
+The \fIblacklist_exceptions\fR section is used to revert the actions of the
+\fIblacklist\fR section. This allows one to selectively include ("whitelist") devices which
+would normally be excluded via the \fIblacklist\fR section. A common usage is
+to blacklist "everything" using a catch-all regular expression, and create
+specific blacklist_exceptions entries for those devices that should be handled
+by multipath-tools.
+.LP
+.
+.
+The following keywords are recognized in both sections. The defaults are empty
+unless explicitly stated.
+.TP 17
+.B devnode
+Regular expression matching the device nodes to be excluded/included.
+.RS
+.PP
+The default \fIblacklist\fR consists of the regular expression
+\fB"!^(sd[a-z]|dasd[a-z]|nvme[0-9])"\fR. This causes all device types other
+than scsi, dasd, and nvme to be excluded from multipath handling by default.
+.RE
+.TP
+.B wwid
+Regular expression for the \fIWorld Wide Identifier\fR of a device to be excluded/included.
+.
+.TP
+.B device
+Subsection for the device description. This subsection recognizes the
+.B vendor
+and
+.B product
+keywords. Both are regular expressions. For a full description of these keywords please see the
+\fIdevices\fR section description.
+.TP
+.B property
+Regular expression for an udev property. All
+devices that have matching udev properties will be excluded/included.
+The handling of the \fIproperty\fR keyword is special,
+because devices \fBmust\fR have at least one whitelisted udev property;
+otherwise they're treated as blacklisted, and the message
+"\fIblacklisted, udev property missing\fR" is displayed in the logs.
+.
+.RS
+.PP
+.B Note:
+The behavior of this option has changed in \fBmultipath-tools\fR 0.8.2
+compared to previous versions.
+Blacklisting by missing properties is only applied to devices which do have the
+property specified by \fIuid_attribute\fR (e.g. \fIID_SERIAL\fR)
+set. Previously, it was applied to every device, possibly causing devices to be
+blacklisted because of temporary I/O error conditions.
+.PP
+The default \fIblacklist exception\fR is: \fB(SCSI_IDENT_|ID_WWN)\fR, causing
+well-behaved SCSI devices and devices that provide a WWN (World Wide Number)
+to be included, and all others to be excluded.
+.RE
+.TP
+.B protocol
+Regular expression for the protocol of a device to be excluded/included.
+.RS
+.PP
+The protocol strings that multipath recognizes are \fIscsi:fcp\fR,
+\fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR,
+\fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR,
+\fIscsi:unspec\fR, \fInvme:pcie\fR, \fInvme:rdma\fR, \fInvme:fc\fR,
+\fInvme:tcp\fR, \fInvme:loop\fR, \fInvme:apple-nvme\fR, \fInvme:unspec\fR,
+\fIccw\fR, \fIcciss\fR, and \fIundef\fR.
+The protocol that a path is using can be viewed by running
+\fBmultipathd show paths format "%d %P"\fR
+.RE
+.LP
+For every device, these 5 blacklist criteria are evaluated in the order
+"property, dev\%node, device, protocol, wwid". If a device turns out to be
+blacklisted by any criterion, it's excluded from handling by multipathd, and
+the later criteria aren't evaluated any more. For each
+criterion, the whitelist takes precedence over the blacklist if a device
+matches both.
+.LP
+.B
+Note:
+Besides the blacklist and whitelist, other configuration options such as
+\fIfind_multipaths\fR have an impact on
+whether or not a given device is handled by multipath-tools.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "multipaths section"
+.\" ----------------------------------------------------------------------------
+.
+The \fImultipaths\fR section allows setting attributes of multipath maps. The
+attributes that are set via the multipaths section (see list below) take
+precedence over all other configuration settings, including those from the
+\fIoverrides\fR section.
+.LP
+The only recognized attribute for the \fImultipaths\fR section is the
+\fImultipath\fR subsection. If there are multiple \fImultipath\fR subsections
+matching a given WWID, the contents of these sections are merged, and settings
+from later entries take precedence.
+.LP
+.
+.
+The \fImultipath\fR subsection recognizes the following attributes:
+.TP 17
+.B wwid
+(Mandatory) World Wide Identifier. Detected multipath maps are matched against this attribute.
+Note that, unlike the \fIwwid\fR attribute in the \fIblacklist\fR section,
+this is \fBnot\fR a regular expression or a substring; WWIDs must match
+exactly inside the multipaths section.
+.TP
+.B alias
+Symbolic name for the multipath map. This takes precedence over a an entry for
+the same WWID in the \fIbindings_file\fR.
+.LP
+.
+.
+The following attributes are optional; if not set the default values
+are taken from the \fIoverrides\fR, \fIdevices\fR, or \fIdefaults\fR
+section:
+.sp 1
+.PD .1v
+.RS
+.TP 18
+.B path_grouping_policy
+.TP
+.B path_selector
+.TP
+.B prio
+.TP
+.B prio_args
+.TP
+.B failback
+.TP
+.B rr_weight
+.TP
+.B no_path_retry
+.TP
+.B rr_min_io
+.TP
+.B rr_min_io_rq
+.TP
+.B flush_on_last_del
+.TP
+.B features
+.TP
+.B reservation_key
+.TP
+.B user_friendly_names
+.TP
+.B deferred_remove
+.TP
+.B san_path_err_threshold
+.TP
+.B san_path_err_forget_rate
+.TP
+.B san_path_err_recovery_time
+.TP
+.B marginal_path_err_sample_time
+.TP
+.B marginal_path_err_rate_threshold
+.TP
+.B marginal_path_err_recheck_gap_time
+.TP
+.B marginal_path_double_failed_time
+.TP
+.B delay_watch_checks
+.TP
+.B delay_wait_checks
+.TP
+.B skip_kpartx
+.TP
+.B max_sectors_kb
+.TP
+.B ghost_delay
+.RE
+.PD
+.LP
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "devices section"
+.\" ----------------------------------------------------------------------------
+.
+.TP 4
+.B Important:
+The built-in hardware device table of
+.I multipath-tools
+is created by members of the Linux community in the hope that it will be useful.
+The existence of an entry for a given storage product in the hardware table
+.B does not imply
+that the product vendor supports, or has tested, the product with
+.I multipath-tools
+in any way.
+.B Always consult the vendor\(aqs official documentation for support-related information.
+.PP
+\fImultipath-tools\fR have a built-in device table with reasonable defaults
+for more than 100 known multipath-capable storage devices. The devices section
+can be used to override these settings. If there are multiple matches for a
+given device, the attributes of all matching entries are applied to it.
+If an attribute is specified in several matching device subsections,
+later entries take precedence. Thus, entries in files under \fIconfig_dir\fR (in
+reverse alphabetical order) have the highest precedence, followed by entries
+in \fI@CONFIGFILE@\fR; the built-in hardware table has the lowest
+precedence. Inside a configuration file, later entries have higher precedence
+than earlier ones.
+.LP
+The only recognized attribute for the \fIdevices\fR section is the \fIdevice\fR
+subsection. Devices detected in the system are matched against the device entries
+using the \fBvendor\fR, \fBproduct\fR, and \fBrevision\fR fields, which are
+all POSIX Extended regular expressions (see \fBregex\fR(7)).
+.LP
+The vendor, product, and revision fields that multipath or multipathd detect for
+devices in a system depend on the device type. For SCSI devices, they correspond to the
+respective fields of the SCSI INQUIRY page. In general, the command '\fImultipathd show
+paths format "%d %s"\fR' command can be used to see the detected properties
+for all devices in the system.
+.LP
+.
+The \fIdevice\fR subsection recognizes the following attributes:
+.TP 17
+.B vendor
+(Mandatory) Regular expression to match the vendor name.
+.TP
+.B product
+(Mandatory) Regular expression to match the product name.
+.TP
+.B revision
+Regular expression to match the product revision. If not specified, any
+revision matches.
+.TP
+.B product_blacklist
+Products with the given \fBvendor\fR matching this string are
+blacklisted. This is equivalent to a \fBdevice\fR entry in the \fIblacklist\fR
+section with the \fIvendor\fR attribute set to this entry's \fIvendor\fR, and
+the \fIproduct\fR attribute set to the value of \fIproduct_blacklist\fR.
+.TP
+.B alias_prefix
+The user_friendly_names prefix to use for this
+device type, instead of the default "mpath".
+.TP
+.B vpd_vendor
+The vendor specific vpd page information, using the vpd page abbreviation.
+The vpd page abbreviation can be found by running \fIsg_vpd -e\fR. multipathd
+will use this information to gather device specific information that can be
+displayed with the \fI%g\fR wildcard for the \fImultipathd show maps format\fR
+and \fImultipathd show paths format\fR commands. Currently only the
+\fBhp3par\fR vpd page is supported.
+.TP
+.B hardware_handler
+The hardware handler to use for this device type.
+The following hardware handler are implemented:
+.RS
+.TP 12
+.I 1 emc
+(Hardware-dependent)
+Hardware handler for DGC class arrays as CLARiiON CX/AX and EMC VNX families
+with Failover Mode 1 (Passive Not Ready(PNR)).
+.TP
+.I 1 rdac
+(Hardware-dependent)
+Hardware handler for LSI/Engenio/NetApp RDAC class as NetApp SANtricity E/EF
+Series and rebranded arrays, with "Linux DM-MP (Kernel 3.9 or earlier)" option.
+.TP
+.I 1 hp_sw
+(Hardware-dependent)
+Hardware handler for HP/COMPAQ/DEC HSG80 and MSA/HSV arrays with
+Active/Standby mode exclusively.
+.TP
+.I 1 alua
+(Hardware-dependent)
+Hardware handler for SCSI-3 ALUA compatible arrays.
+.TP
+.I 1 ana
+(Hardware-dependent)
+Hardware handler for NVMe ANA compatible arrays.
+.PP
+The default is: \fB<unset>\fR
+.PP
+\fBImportant Note:\fR Linux kernels 4.3 and newer automatically attach a device
+handler to known devices (which includes all devices supporting SCSI-3 ALUA)
+and disallow changing the handler
+afterwards. Setting \fBhardware_handler\fR for such devices on these kernels
+has no effect.
+.RE
+.
+.
+.LP
+The following attributes are optional; if not set the default values
+are taken from the \fIdefaults\fR
+section:
+.sp 1
+.PD .1v
+.RS
+.TP 18
+.B path_grouping_policy
+.TP
+.B uid_attribute
+.TP
+.B path_selector
+.TP
+.B path_checker
+.TP
+.B prio
+.TP
+.B prio_args
+.TP
+.B features
+.TP
+.B failback
+.TP
+.B rr_weight
+.TP
+.B no_path_retry
+.TP
+.B rr_min_io
+.TP
+.B rr_min_io_rq
+.TP
+.B fast_io_fail_tmo
+.TP
+.B dev_loss_tmo
+.TP
+.B eh_deadline
+.TP
+.B flush_on_last_del
+.TP
+.B user_friendly_names
+.TP
+.B retain_attached_hw_handler
+.TP
+.B detect_prio
+.TP
+.B detect_checker
+.TP
+.B deferred_remove
+.TP
+.B san_path_err_threshold
+.TP
+.B san_path_err_forget_rate
+.TP
+.B san_path_err_recovery_time
+.TP
+.B marginal_path_err_sample_time
+.TP
+.B marginal_path_err_rate_threshold
+.TP
+.B marginal_path_err_recheck_gap_time
+.TP
+.B marginal_path_double_failed_time
+.TP
+.B delay_watch_checks
+.TP
+.B delay_wait_checks
+.TP
+.B skip_kpartx
+.TP
+.B max_sectors_kb
+.TP
+.B ghost_delay
+.TP
+.B all_tg_pt
+.RE
+.PD
+.LP
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "overrides section"
+.\" ----------------------------------------------------------------------------
+.
+The overrides section recognizes the following optional attributes; if not set
+the values are taken from the \fIdevices\fR or \fIdefaults\fR sections:
+.sp 1
+.PD .1v
+.RS
+.TP 18
+.B path_grouping_policy
+.TP
+.B uid_attribute
+.TP
+.B path_selector
+.TP
+.B path_checker
+.TP
+.B alias_prefix
+.TP
+.B features
+.TP
+.B prio
+.TP
+.B prio_args
+.TP
+.B failback
+.TP
+.B rr_weight
+.TP
+.B no_path_retry
+.TP
+.B rr_min_io
+.TP
+.B rr_min_io_rq
+.TP
+.B flush_on_last_del
+.TP
+.B fast_io_fail_tmo
+.TP
+.B dev_loss_tmo
+.TP
+.B eh_deadline
+.TP
+.B user_friendly_names
+.TP
+.B retain_attached_hw_handler
+.TP
+.B detect_prio
+.TP
+.B detect_checker
+.TP
+.B deferred_remove
+.TP
+.B san_path_err_threshold
+.TP
+.B san_path_err_forget_rate
+.TP
+.B san_path_err_recovery_time
+.TP
+.B marginal_path_err_sample_time
+.TP
+.B marginal_path_err_rate_threshold
+.TP
+.B marginal_path_err_recheck_gap_time
+.TP
+.B marginal_path_double_failed_time
+.TP
+.B delay_watch_checks
+.TP
+.B delay_wait_checks
+.TP
+.B skip_kpartx
+.TP
+.B max_sectors_kb
+.TP
+.B ghost_delay
+.TP
+.B all_tg_pt
+.RE
+.PD
+.LP
+The overrides section also recognizes the optional \fIprotocol\fR subsection,
+and can contain multiple protocol subsections. Path devices are matched against
+the protocol subsection using the mandatory \fItype\fR attribute.  Attributes
+in a matching protocol subsection take precedence over attributes in the rest
+of the overrides section. If there are multiple matching protocol subsections,
+later entries take precedence.
+.TP
+.B protocol subsection
+The protocol subsection recognizes the following mandatory attribute:
+.RS
+.TP
+.B type
+The protocol string of the path device. The possible values are \fIscsi:fcp\fR,
+\fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR,
+\fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR,
+\fIscsi:unspec\fR, \fInvme:pcie\fR, \fInvme:rdma\fR, \fInvme:fc\fR,
+\fInvme:tcp\fR, \fInvme:loop\fR, \fInvme:apple-nvme\fR, \fInvme:unspec\fR,
+\fIccw\fR, \fIcciss\fR, and \fIundef\fR. This is
+\fBnot\fR a regular expression. the path device protocol string must match
+exactly. The protocol that a path is using can be viewed by running
+\fBmultipathd show paths format "%d %P"\fR
+.LP
+The following attributes are optional; if not set, the default values are taken
+from the \fIoverrides\fR, \fIdevices\fR, or \fIdefaults\fR section:
+.sp 1
+.PD .1v
+.RS
+.TP
+.B fast_io_fail_tmo
+.TP
+.B dev_loss_tmo
+.TP
+.B eh_deadline
+.PD
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "WWID generation"
+.\" ----------------------------------------------------------------------------
+.
+Multipath uses a \fIWorld Wide Identification\fR (WWID) to determine
+which paths belong to the same device. Each path presenting the same
+WWID is assumed to point to the same device.
+.LP
+The WWID is generated by four methods (in the order of preference):
+.TP 17
+.B uid_attrs
+The WWID is derived from udev attributes by matching the device node name; cf
+\fIuid_attrs\fR above.
+.TP
+.B uid_attribute
+Use the value of the specified udev attribute; cf \fIuid_attribute\fR
+above.
+.TP
+.B sysfs
+Try to determine the WWID from sysfs attributes.
+For SCSI devices, this means reading the Vital Product Data (VPD) page
+\(dqDevice Identification\(dq (0x83).
+.PP
+The default settings (using udev and \fBuid_attribute\fR configured from
+the built-in hardware table) should work fine
+in most scenarios. Users who want to enable uevent merging must set
+\fBuid_attrs\fR.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "Shaky paths detection"
+.\" ----------------------------------------------------------------------------
+.
+A common problem in SAN setups is the occurrence of intermittent errors: a
+path is unreachable, then reachable again for a short time, disappears again,
+and so forth. This happens typically on unstable interconnects. It is
+undesirable to switch pathgroups unnecessarily on such frequent, unreliable
+events. \fImultipathd\fR supports three different methods for detecting this
+situation and dealing with it. All methods share the same basic mode of
+operation: If a path is found to be \(dqshaky\(dq or \(dqflipping\(dq,
+and appears to be in healthy status, it is not reinstated (put back to use)
+immediately. Instead, it is placed in the \(dqdelayed\(dq state and watched
+for some time, and only reinstated if the healthy state appears to be stable.
+If the \fImarginal_pathgroups\fR option is set, the path will reinstated
+immediately, but placed in a special pathgroup for marginal paths. Marginal
+pathgroups will not be used until all other pathgroups have been tried. At the
+time when the path would normally be reinstated, it will be returned to its
+normal pathgroup. The logic of determining \(dqshaky\(dq condition, as well as
+the logic when to reinstate, differs between the three methods.
+.TP 8
+.B \(dqdelay_checks\(dq failure tracking
+(Deprecated) This method is \fBdeprecated\fR and mapped to the \(dqsan_path_err\(dq method.
+See the \fIdelay_watch_checks\fR and \fIdelay_wait_checks\fR options above
+for more information.
+.
+.TP
+.B \(dqmarginal_path\(dq failure tracking
+If a second failure event (good->bad transition) occurs within
+\fImarginal_path_double_failed_time\fR seconds after a failure, high-frequency
+monitoring is started for the affected path: I/O is sent at a rate of 10 per
+second. This is done for \fImarginal_path_err_sample_time\fR seconds. During
+this period, the path is not reinstated. If the
+rate of errors remains below \fImarginal_path_err_rate_threshold\fR during the
+monitoring period, the path is reinstated. Otherwise, it
+is kept in failed state for \fImarginal_path_err_recheck_gap_time\fR, and
+after that, it is monitored again. For this method, time intervals are measured
+in seconds.
+.TP
+.B \(dqsan_path_err\(dq failure tracking
+multipathd counts path failures for each path. Once the number of failures
+exceeds the value given by \fIsan_path_err_threshold\fR, the path is not
+reinstated for \fIsan_path_err_recovery_time\fR seconds. While counting
+failures, multipathd \(dqforgets\(dq one past failure every
+\(dqsan_path_err_forget_rate\(dq ticks; thus if errors don't occur more
+often then once in the forget rate interval, the failure count doesn't
+increase and the threshold is never reached. Ticks are the time between
+path checks by multipathd, which is variable and controlled by the
+\fIpolling_interval\fR and \fImax_polling_interval\fR parameters.
+.
+.RS 8
+.LP
+This algorithm is superseded by the \(dqmarginal_path\(dq failure tracking,
+but remains supported for backward compatibility.
+.
+.RE
+.TP
+.B \(dqFPIN\(dq failure tracking
+Fibre channel fabrics can notify hosts about fabric-level issues such
+as integrity failures or congestion with so-called Fabric Performance
+Impact Notifications (FPINs).On receiving the fpin notifications through ELS
+multipathd will move the affected path and port states to marginal.
+.
+.RE
+.LP
+See the documentation
+of the individual options above for details.
+It is \fBstrongly discouraged\fR to use more than one of these methods for any
+given multipath map, because the two concurrent methods may interact in
+unpredictable ways. If the \(dqmarginal_path\(dq method is active, the
+\(dqsan_path_err\(dq parameters are implicitly set to 0.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "FOREIGN MULTIPATH SUPPORT"
+.\" ----------------------------------------------------------------------------
+.
+multipath and multipathd can load \(dqforeign\(dq libraries to add
+support for other multipathing technologies besides the Linux device mapper.
+Currently this support is limited to printing detected information about
+multipath setup. In topology output, the names of foreign maps are prefixed by
+the foreign library name in square brackets, as in this example:
+.
+.P
+.EX
+# multipath -ll
+uuid.fedcba98-3579-4567-8765-123456789abc [nvme]:nvme4n9 NVMe,Some NVMe controller,FFFFFFFF
+size=167772160 features='n/a' hwhandler='ANA' wp=rw
+|-+- policy='n/a' prio=50 status=optimized
+| `- 4:38:1    nvme4c38n1 0:0     n/a   optimized    live
+`-+- policy='n/a' prio=50 status=optimized
+  `- 4:39:1    nvme4c39n1 0:0     n/a   optimized    live
+.EE
+.
+.P
+The \(dqnvme\(dq foreign library provides support for NVMe native multipathing
+in the kernel. It is part of the standard multipath package.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "KNOWN ISSUES"
+.\" ----------------------------------------------------------------------------
+.
+The usage of \fIqueue_if_no_path\fR option can lead to \fID state\fR
+processes being hung and not killable in situations where all the paths to the
+LUN go offline. It is advisable to use the \fIno_path_retry\fR option instead.
+.P
+The use of \fIqueue_if_no_path\fR or \fIno_path_retry\fR might lead to a
+deadlock if the \fIdev_loss_tmo\fR setting results in a device being removed
+while I/O is still queued. The multipath daemon will update the \fIdev_loss_tmo\fR
+setting accordingly to avoid this deadlock. Hence if both values are
+specified the order of precedence is \fIno_path_retry, queue_if_no_path, dev_loss_tmo\fR.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "SEE ALSO"
+.\" ----------------------------------------------------------------------------
+.
+.BR udev (8),
+.BR dmsetup (8),
+.BR multipath (8),
+.BR multipathc (8),
+.BR multipathd (8).
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH AUTHORS
+.\" ----------------------------------------------------------------------------
+.
+\fImultipath-tools\fR was developed by Christophe Varoqui, <christophe.varoqui@opensvc.com>
+and others.
+.\" EOF
index 8d3cf33a572f47bfde3332d4e53343f582c36897..6f1237600c0355d0443ccdd7f93e87b143ec961d 100644 (file)
@@ -31,7 +31,8 @@ IMPORT{db}="DM_MULTIPATH_DEVICE_PATH"
 
 # multipath -u sets DM_MULTIPATH_DEVICE_PATH and,
 # if "find_multipaths smart", also FIND_MULTIPATHS_WAIT_UNTIL.
-IMPORT{program}="$env{MPATH_SBIN_PATH}/multipath -u %k"
+IMPORT{program}=="$env{MPATH_SBIN_PATH}/multipath -u %k", \
+       ENV{.MPATH_CHECK_PASSED}="1"
 
 # case 1: this is definitely multipath
 ENV{DM_MULTIPATH_DEVICE_PATH}=="1", \
@@ -82,10 +83,19 @@ LABEL="stop_wait"
 # If timeout hasn't expired but we're not in "maybe" state any more, stop timer
 # Do this only once, and only if the timer has been started before
 IMPORT{db}="FIND_MULTIPATHS_WAIT_CANCELLED"
-ENV{FIND_MULTIPATHS_WAIT_CANCELLED}!="?*", \
-       ENV{FIND_MULTIPATHS_WAIT_UNTIL}=="?*", \
-       ENV{FIND_MULTIPATHS_WAIT_UNTIL}!="0", \
-       ENV{FIND_MULTIPATHS_WAIT_CANCELLED}="1", \
-       RUN+="/usr/bin/systemctl stop cancel-multipath-wait-$kernel.timer"
+ENV{FIND_MULTIPATHS_WAIT_CANCELLED}=="?*", GOTO="end_mpath"
+ENV{FIND_MULTIPATHS_WAIT_UNTIL}!="?*", GOTO="end_mpath"
+ENV{FIND_MULTIPATHS_WAIT_UNTIL}=="0", GOTO="end_mpath"
+
+ENV{FIND_MULTIPATHS_WAIT_CANCELLED}="1"
+RUN+="/usr/bin/systemctl stop cancel-multipath-wait-$kernel.timer"
+
+# If "multipath -u" failed, no values are imported from the program,
+# and we are still using the values for DM_MULTIPATH_DEVICE_PATH and
+# FIND_MULTIPATHS_WAIT_UNTIL that were imported from the database.
+# If we are in "smart" mode, we need to give up on the path now,
+# since this may have been the timeout event. Without the imports
+# from "multipath -u", we can't tell.
+ENV{.MPATH_CHECK_PASSED}!="?*", ENV{DM_MULTIPATH_DEVICE_PATH}="0"
 
 LABEL="end_mpath"
index 9d53132929bb319e717ccdd572129dd69a1e1987..997b40cfd12477327aa49deb53f88c9d5e5f7204 100644 (file)
@@ -1,11 +1,10 @@
 include ../Makefile.inc
 
-EXEC := multipathd
-CLI := multipathc
+EXEC     := multipathd
+CLI      := multipathc
+MANPAGES := multipathd.8
 
 CPPFLAGS += -I$(multipathdir) -I$(mpathutildir) -I$(mpathpersistdir) -I$(mpathcmddir) -I$(thirdpartydir) \
-       $(shell $(PKGCONFIG) --modversion liburcu 2>/dev/null | \
-               awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }') \
        -DBINDIR='"$(bindir)"' $(SYSTEMD_CPPFLAGS)
 
 #
@@ -42,7 +41,7 @@ ifeq ($(FPIN_SUPPORT),1)
 OBJS += fpin_handlers.o
 endif
 
-all : $(EXEC) $(CLI)
+all : $(EXEC) $(CLI) $(MANPAGES) $(EXEC).service
 
 $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
        @echo building $@ because of $?
@@ -79,9 +78,9 @@ uninstall:
        $(Q)$(RM) $(DESTDIR)$(unitdir)/$(EXEC).socket
 
 clean: dep_clean
-       $(Q)$(RM) core *.o $(EXEC) $(CLI)
+       $(Q)$(RM) core *.o $(EXEC) $(CLI) $(MANPAGES) $(EXEC).service
 
-include $(wildcard $(OBJS:.o=.d))
+include $(wildcard $(OBJS:.o=.d) $(CLI_OBJS:.o=.d))
 
 dep_clean:
-       $(Q)$(RM) $(OBJS:.o=.d)
+       $(Q)$(RM) $(OBJS:.o=.d) $(CLI_OBJS:.o=.d)
index e65fb75c5b491c1942496d2027edee51ccc144b8..420d75dfe280dbbd159e4dabd039f58a6e9114ed 100644 (file)
@@ -760,7 +760,7 @@ cli_del_map (void * v, struct strbuf *reply, void * data)
        }
        rc = ev_remove_map(param, alias, minor, vecs);
        if (rc == 2)
-               append_strbuf_str(reply, "delayed");
+               append_strbuf_str(reply, "delayed\n");
 
        free(alias);
        return rc;
@@ -810,32 +810,7 @@ cli_reload(void *v, struct strbuf *reply, void *data)
                return 1;
        }
 
-       return reload_and_sync_map(mpp, vecs, 0);
-}
-
-static int resize_map(struct multipath *mpp, unsigned long long size,
-              struct vectors * vecs)
-{
-       char *params __attribute__((cleanup(cleanup_charp))) = NULL;
-       unsigned long long orig_size = mpp->size;
-
-       mpp->size = size;
-       update_mpp_paths(mpp, vecs->pathvec);
-       if (setup_map(mpp, &params, vecs) != 0) {
-               condlog(0, "%s: failed to setup map for resize : %s",
-                       mpp->alias, strerror(errno));
-               mpp->size = orig_size;
-               return 1;
-       }
-       mpp->action = ACT_RESIZE;
-       mpp->force_udev_reload = 1;
-       if (domap(mpp, params, 1) == DOMAP_FAIL) {
-               condlog(0, "%s: failed to resize map : %s", mpp->alias,
-                       strerror(errno));
-               mpp->size = orig_size;
-               return 1;
-       }
-       return 0;
+       return reload_and_sync_map(mpp, vecs);
 }
 
 static int
@@ -845,9 +820,11 @@ cli_resize(void *v, struct strbuf *reply, void *data)
        char * mapname = get_keyparam(v, KEY_MAP);
        struct multipath *mpp;
        int minor;
-       unsigned long long size;
+       unsigned long long size = 0;
        struct pathgroup *pgp;
        struct path *pp;
+       unsigned int i, j, ret;
+       bool mismatch = false;
 
        mapname = convert_dev(mapname, 0);
        condlog(2, "%s: resize map (operator)", mapname);
@@ -867,21 +844,24 @@ cli_resize(void *v, struct strbuf *reply, void *data)
                return 1;
        }
 
-       pgp = VECTOR_SLOT(mpp->pg, 0);
-
-       if (!pgp){
-               condlog(0, "%s: couldn't get path group. cannot resize",
-                       mapname);
-               return 1;
+       vector_foreach_slot(mpp->pg, pgp, i) {
+               vector_foreach_slot (pgp->paths, pp, j) {
+                       sysfs_get_size(pp, &pp->size);
+                       if (!pp->size)
+                               continue;
+                       if (!size)
+                               size = pp->size;
+                       else if (pp->size != size)
+                               mismatch = true;
+               }
        }
-       pp = VECTOR_SLOT(pgp->paths, 0);
-
-       if (!pp){
-               condlog(0, "%s: couldn't get path. cannot resize", mapname);
+       if (!size) {
+               condlog(0, "%s: couldn't get size from sysfs. cannot resize",
+                       mapname);
                return 1;
        }
-       if (!pp->udev || sysfs_get_size(pp, &size)) {
-               condlog(0, "%s: couldn't get size for sysfs. cannot resize",
+       if (mismatch) {
+               condlog(0, "%s: path size not consistent. cannot resize",
                        mapname);
                return 1;
        }
@@ -893,14 +873,12 @@ cli_resize(void *v, struct strbuf *reply, void *data)
        condlog(3, "%s old size is %llu, new size is %llu", mapname, mpp->size,
                size);
 
-       if (resize_map(mpp, size, vecs) != 0)
-               return 1;
+       ret = resize_map(mpp, size, vecs);
 
-       if (setup_multipath(vecs, mpp) != 0)
-               return 1;
-       sync_map_state(mpp);
+       if (ret == 2)
+               condlog(0, "%s: map removed while trying to resize", mapname);
 
-       return 0;
+       return (ret != 0);
 }
 
 static int
@@ -1277,6 +1255,11 @@ cli_shutdown (void * v, struct strbuf *reply, void * data)
 static int
 cli_getprstatus (void * v, struct strbuf *reply, void * data)
 {
+       static const char * const prflag_str[] = {
+               [PRFLAG_UNKNOWN] = "unknown\n",
+               [PRFLAG_UNSET] = "unset\n",
+               [PRFLAG_SET] = "set\n",
+       };
        struct multipath * mpp;
        struct vectors * vecs = (struct vectors *)data;
        char * param = get_keyparam(v, KEY_MAP);
@@ -1287,10 +1270,7 @@ cli_getprstatus (void * v, struct strbuf *reply, void * data)
        if (!mpp)
                return 1;
 
-       condlog(3, "%s: prflag = %u", param, (unsigned int)mpp->prflag);
-
-       if (print_strbuf(reply, "%d", mpp->prflag) < 0)
-               return 1;
+       append_strbuf_str(reply, prflag_str[mpp->prflag]);
 
        condlog(3, "%s: reply = %s", param, get_strbuf_str(reply));
 
@@ -1310,8 +1290,8 @@ cli_setprstatus(void * v, struct strbuf *reply, void * data)
        if (!mpp)
                return 1;
 
-       if (!mpp->prflag) {
-               mpp->prflag = 1;
+       if (mpp->prflag != PRFLAG_SET) {
+               mpp->prflag = PRFLAG_SET;
                condlog(2, "%s: prflag set", param);
        }
 
@@ -1332,8 +1312,8 @@ cli_unsetprstatus(void * v, struct strbuf *reply, void * data)
        if (!mpp)
                return 1;
 
-       if (mpp->prflag) {
-               mpp->prflag = 0;
+       if (mpp->prflag != PRFLAG_UNSET) {
+               mpp->prflag = PRFLAG_UNSET;
                condlog(2, "%s: prflag unset", param);
        }
 
@@ -1447,7 +1427,7 @@ static int cli_set_marginal(void * v, struct strbuf *reply, void * data)
        }
        pp->marginal = 1;
 
-       return reload_and_sync_map(pp->mpp, vecs, 0);
+       return reload_and_sync_map(pp->mpp, vecs);
 }
 
 static int cli_unset_marginal(void * v, struct strbuf *reply, void * data)
@@ -1474,7 +1454,7 @@ static int cli_unset_marginal(void * v, struct strbuf *reply, void * data)
        }
        pp->marginal = 0;
 
-       return reload_and_sync_map(pp->mpp, vecs, 0);
+       return reload_and_sync_map(pp->mpp, vecs);
 }
 
 static int cli_unset_all_marginal(void * v, struct strbuf *reply, void * data)
@@ -1511,7 +1491,7 @@ static int cli_unset_all_marginal(void * v, struct strbuf *reply, void * data)
                vector_foreach_slot (pgp->paths, pp, j)
                        pp->marginal = 0;
 
-       return reload_and_sync_map(mpp, vecs, 0);
+       return reload_and_sync_map(mpp, vecs);
 }
 
 #define HANDLER(x) x
index 8f464f0495bd0210b2ac0ce7719c30b8fd68fff7..be087ca06119c5af9e40ad25d13d9df7a8a28ed3 100644 (file)
@@ -60,18 +60,15 @@ static void _udev_device_unref(void *p)
 
 
 /*set/unset the path state to marginal*/
-static int fpin_set_pathstate(struct path *pp, bool set)
+static void fpin_set_pathstate(struct path *pp, bool set)
 {
        const char *action = set ? "set" : "unset";
 
-       if (!pp || !pp->mpp || !pp->mpp->alias)
-               return -1;
-
-       condlog(3, "\n%s: %s  marginal path %s (fpin)",
-               action, pp->mpp->alias, pp->dev_t);
+       condlog(3, "%s: %s marginal path %s (fpin)",
+               pp->mpp ? pp->mpp->alias : "orphan", action, pp->dev_t);
        pp->marginal = set;
-       pp->mpp->fpin_must_reload = true;
-       return 0;
+       if (pp->mpp)
+               pp->mpp->fpin_must_reload = true;
 }
 
 /* This will unset marginal state of a device*/
@@ -82,14 +79,14 @@ static void fpin_path_unsetmarginal(char *devname, struct vectors *vecs)
        pp = find_path_by_dev(vecs->pathvec, devname);
        if (!pp)
                pp = find_path_by_devt(vecs->pathvec, devname);
-
-       fpin_set_pathstate(pp, false);
+       if (pp)
+               fpin_set_pathstate(pp, false);
 }
 
 /*This will set the marginal state of a device*/
-static int fpin_path_setmarginal(struct path *pp)
+static void  fpin_path_setmarginal(struct path *pp)
 {
-       return fpin_set_pathstate(pp, true);
+       fpin_set_pathstate(pp, true);
 }
 
 /* Unsets all the devices in the list from marginal state */
@@ -127,7 +124,7 @@ empty:
        /* walk backwards because reload_and_sync_map() can remove mpp */
        vector_foreach_slot_backwards(vecs->mpvec, mpp, i) {
                if (mpp->fpin_must_reload) {
-                       ret = reload_and_sync_map(mpp, vecs, 0);
+                       ret = reload_and_sync_map(mpp, vecs);
                        if (ret == 2)
                                condlog(2, "map removed during reload");
                        else
@@ -183,8 +180,8 @@ static void fpin_set_rport_marginal(struct udev_device *rport_dev)
                                         udev_device_get_syspath(rport_dev));
 }
 
-/*Add the marginal devices info into the list*/
-static void
+/*Add the marginal devices info into the list and return 0 on success*/
+static int
 fpin_add_marginal_dev_info(uint32_t host_num, char *devname)
 {
        struct marginal_dev_list *newdev = NULL;
@@ -199,70 +196,165 @@ fpin_add_marginal_dev_info(uint32_t host_num, char *devname)
                list_add_tail(&(newdev->node),
                                &fpin_li_marginal_dev_list_head);
                pthread_mutex_unlock(&fpin_li_marginal_dev_mutex);
-       }
+       } else
+               return -ENOMEM;
+       return 0;
 }
 
 /*
- * This function goes through the vecs->pathvec, and for
- * each path, check that the host  number,
- * the target WWPN associated with the path matches
- * with the els wwpn and sets the path and port state to
+ * This function compares Transport Address Controller Port pn,
+ * Host Transport Address Controller Port pn with the els wwpn ,attached_wwpn
+ * and return 1 (match) or 0 (no match) or a negative error code
+ */
+static int  extract_nvme_addresses_chk_path_pwwn(const char *address,
+               uint64_t els_wwpn, uint64_t els_attached_wwpn)
+
+{
+       uint64_t traddr;
+       uint64_t host_traddr;
+
+       /*
+        *  Find the position of "traddr=" and "host_traddr="
+        *  and the address will be in the below format
+        *  "traddr=nn-0x200400110dff9400:pn-0x200400110dff9400,
+        *  host_traddr=nn-0x200400110dff9400:pn-0x200400110dff9400"
+        */
+       const char *traddr_start = strstr(address, "traddr=");
+       const char *host_traddr_start = strstr(address, "host_traddr=");
+
+       if (!traddr_start || !host_traddr_start)
+               return -EINVAL;
+
+       /* Extract traddr pn */
+       if (sscanf(traddr_start, "traddr=nn-%*[^:]:pn-%" SCNx64, &traddr) != 1)
+               return -EINVAL;
+
+       /* Extract host_traddr pn*/
+       if (sscanf(host_traddr_start, "host_traddr=nn-%*[^:]:pn-%" SCNx64,
+                               &host_traddr) != 1)
+               return -EINVAL;
+       condlog(4, "traddr 0x%" PRIx64 " hosttraddr 0x%" PRIx64 " els_wwpn 0x%"
+               PRIx64" els_host_traddr 0x%" PRIx64,
+                       traddr, host_traddr,
+                       els_wwpn, els_attached_wwpn);
+       if ((host_traddr == els_attached_wwpn) && (traddr == els_wwpn))
+               return 1;
+       return 0;
+}
+
+/*
+ * This function check that the Transport Address Controller Port pn,
+ * Host Transport Address Controller Port pn associated with the path matches
+ * with the els wwpn ,attached_wwpn and sets the path state to
  * Marginal
  */
-static int  fpin_chk_wwn_setpath_marginal(uint16_t host_num,  struct vectors *vecs,
+static void fpin_check_set_nvme_path_marginal(uint16_t host_num, struct path *pp,
+               uint64_t els_wwpn, uint64_t attached_wwpn)
+{
+       struct udev_device *ctl = NULL;
+       const char *address = NULL;
+       int ret = 0;
+
+       ctl = udev_device_get_parent_with_subsystem_devtype(pp->udev, "nvme", NULL);
+       if (ctl == NULL) {
+               condlog(2, "%s: No parent device for ", pp->dev);
+               return;
+       }
+       address = udev_device_get_sysattr_value(ctl, "address");
+       if (!address) {
+               condlog(2, "%s: unable to get the address ", pp->dev);
+               return;
+       }
+       condlog(4, "\n address %s: dev :%s\n", address, pp->dev);
+       ret = extract_nvme_addresses_chk_path_pwwn(address, els_wwpn, attached_wwpn);
+       if (ret <= 0)
+               return;
+       ret = fpin_add_marginal_dev_info(host_num, pp->dev);
+       if (ret < 0)
+               return;
+       fpin_path_setmarginal(pp);
+}
+
+/*
+ * This function check the host  number, the target WWPN
+ * associated with the path matches with the els wwpn and
+ * sets the path and port state to Marginal
+ */
+static void fpin_check_set_scsi_path_marginal(uint16_t host_num, struct path *pp,
                uint64_t els_wwpn)
 {
-       struct path *pp;
-       struct multipath *mpp;
-       int i, k;
        char rport_id[42];
        const char *value = NULL;
        struct udev_device *rport_dev = NULL;
        uint64_t wwpn;
        int ret = 0;
+       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,
+                       "fc_remote_ports", rport_id);
+       if (!rport_dev) {
+               condlog(2, "%s: No fc_remote_port device for '%s'", pp->dev,
+                               rport_id);
+               return;
+       }
+       pthread_cleanup_push(_udev_device_unref, rport_dev);
+       value = udev_device_get_sysattr_value(rport_dev, "port_name");
+       if (!value)
+               goto unref;
+
+       wwpn =  strtol(value, NULL, 16);
+       /*
+        * If the port wwpn matches sets the path and port state
+        * to marginal
+        */
+       if (wwpn == els_wwpn) {
+               ret = fpin_add_marginal_dev_info(host_num, pp->dev);
+               if (ret < 0)
+                       goto unref;
+               fpin_path_setmarginal(pp);
+               fpin_set_rport_marginal(rport_dev);
+       }
+unref:
+       pthread_cleanup_pop(1);
+       return;
+
+}
+
+/*
+ * This function goes through the vecs->pathvec, and for
+ * each path, it checks and sets the path state to marginal
+ * if the path's associated port wwpn ,hostnum  matches with
+ * els wwnpn ,attached_wwpn
+ */
+static int  fpin_chk_wwn_setpath_marginal(uint16_t host_num,  struct vectors *vecs,
+               uint64_t els_wwpn, uint64_t attached_wwpn)
+{
+       struct path *pp;
+       struct multipath *mpp;
+       int i, k;
+       int ret = 0;
 
        pthread_cleanup_push(cleanup_lock, &vecs->lock);
        lock(&vecs->lock);
        pthread_testcancel();
 
        vector_foreach_slot(vecs->pathvec, pp, k) {
-               /* Checks the host number and also for the SCSI FCP */
-               if (pp->bus != SYSFS_BUS_SCSI || pp->sg_id.proto_id != SCSI_PROTOCOL_FCP || host_num !=  pp->sg_id.host_no)
+               if (!pp->mpp)
                        continue;
-               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,
-                               "fc_remote_ports", rport_id);
-               if (!rport_dev) {
-                       condlog(2, "%s: No fc_remote_port device for '%s'", pp->dev,
-                                       rport_id);
-                       continue;
-               }
-               pthread_cleanup_push(_udev_device_unref, rport_dev);
-               value = udev_device_get_sysattr_value(rport_dev, "port_name");
-               if (!value)
-                       goto unref;
-
-               if (value)
-                       wwpn =  strtol(value, NULL, 16);
-               /*
-                * If the port wwpn matches sets the path and port state
-                * to marginal
-                */
-               if (wwpn == els_wwpn) {
-                       ret = fpin_path_setmarginal(pp);
-                       if (ret < 0)
-                               goto unref;
-                       fpin_set_rport_marginal(rport_dev);
-                       fpin_add_marginal_dev_info(host_num, pp->dev);
+               /*checks if the bus type is nvme  and the protocol is FC-NVMe*/
+               if ((pp->bus == SYSFS_BUS_NVME) && (pp->sg_id.proto_id == NVME_PROTOCOL_FC)) {
+                       fpin_check_set_nvme_path_marginal(host_num, pp, els_wwpn, attached_wwpn);
+               } else if ((pp->bus == SYSFS_BUS_SCSI) &&
+                       (pp->sg_id.proto_id == SCSI_PROTOCOL_FCP) &&
+                       (host_num ==  pp->sg_id.host_no)) {
+                       /* Checks the host number and also for the SCSI FCP */
+                       fpin_check_set_scsi_path_marginal(host_num, pp, els_wwpn);
                }
-unref:
-               pthread_cleanup_pop(1);
        }
        /* walk backwards because reload_and_sync_map() can remove mpp */
        vector_foreach_slot_backwards(vecs->mpvec, mpp, i) {
                if (mpp->fpin_must_reload) {
-                       ret = reload_and_sync_map(mpp, vecs, 0);
+                       ret = reload_and_sync_map(mpp, vecs);
                        if (ret == 2)
                                condlog(2, "map removed during reload");
                        else
@@ -286,14 +378,18 @@ fpin_parse_li_els_setpath_marginal(uint16_t host_num, struct fc_tlv_desc *tlv,
        struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv;
        int count = 0;
        int ret = 0;
+       uint64_t attached_wwpn;
 
        /* Update the wwn to list */
        wwn_count = be32_to_cpu(li_desc->pname_count);
-       condlog(4, "Got wwn count as %d\n", wwn_count);
+       attached_wwpn = be64_to_cpu(li_desc->attached_wwpn);
+       condlog(4, "Got wwn count as %d detecting wwn 0x%" PRIx64
+               " attached_wwpn 0x%" PRIx64 "\n",
+                       wwn_count, be64_to_cpu(li_desc->detecting_wwpn), attached_wwpn);
 
        for (iter = 0; iter < wwn_count; iter++) {
                wwpn = be64_to_cpu(li_desc->pname_list[iter]);
-               ret = fpin_chk_wwn_setpath_marginal(host_num, vecs, wwpn);
+               ret = fpin_chk_wwn_setpath_marginal(host_num, vecs, wwpn, attached_wwpn);
                if (ret < 0)
                        condlog(2, "failed to set the path marginal associated with wwpn: 0x%" PRIx64 "\n", wwpn);
 
index 1e1b254fcda8aa56670b006c49a8689f0c973722..230c9d100b55a400fa83d7c0d1422543b9b99b9f 100644 (file)
@@ -395,34 +395,51 @@ void put_multipath_config(__attribute__((unused)) void *arg)
        rcu_read_unlock();
 }
 
+/*
+ * The path group orderings that this function finds acceptable are different
+ * from now select_path_group determines the best pathgroup. The idea here is
+ * to only trigger a kernel reload when it is obvious that the pathgroups would
+ * be out of order, even if all the paths were usable. Thus pathgroups with
+ * PRIO_UNDEF are skipped, and the number of enabled paths doesn't matter here.
+ */
+bool path_groups_in_order(struct multipath *mpp)
+{
+       int i;
+       struct pathgroup *pgp;
+       bool seen_marginal_pg = false;
+       int last_prio = INT_MAX;
+
+       if (VECTOR_SIZE(mpp->pg) < 2)
+               return true;
+
+       vector_foreach_slot(mpp->pg, pgp, i) {
+               if (seen_marginal_pg && !pgp->marginal)
+                       return false;
+               /* skip pgs with PRIO_UNDEF, since this is likely temporary */
+               if (!pgp->paths || pgp->priority == PRIO_UNDEF)
+                       continue;
+               if (pgp->marginal && !seen_marginal_pg) {
+                       seen_marginal_pg = true;
+                       last_prio = pgp->priority;
+                       continue;
+               }
+               if (pgp->priority > last_prio)
+                       return false;
+               last_prio = pgp->priority;
+       }
+       return true;
+}
+
 static int
-need_switch_pathgroup (struct multipath * mpp, int refresh)
+need_switch_pathgroup (struct multipath * mpp, bool *need_reload)
 {
-       struct pathgroup * pgp;
-       struct path * pp;
-       unsigned int i, j;
-       struct config *conf;
        int bestpg;
 
+       *need_reload = false;
        if (!mpp)
                return 0;
 
-       /*
-        * Refresh path priority values
-        */
-       if (refresh) {
-               vector_foreach_slot (mpp->pg, pgp, i) {
-                       vector_foreach_slot (pgp->paths, pp, j) {
-                               conf = get_multipath_config();
-                               pthread_cleanup_push(put_multipath_config,
-                                                    conf);
-                               pathinfo(pp, conf, DI_PRIO);
-                               pthread_cleanup_pop(1);
-                       }
-               }
-       }
-
-       if (!mpp->pg || VECTOR_SIZE(mpp->paths) == 0)
+       if (VECTOR_SIZE(mpp->pg) < 2)
                return 0;
 
        bestpg = select_path_group(mpp);
@@ -430,10 +447,9 @@ need_switch_pathgroup (struct multipath * mpp, int refresh)
                return 0;
 
        mpp->bestpg = bestpg;
-       if (mpp->bestpg != mpp->nextpg)
-               return 1;
+       *need_reload = !path_groups_in_order(mpp);
 
-       return 0;
+       return (*need_reload || mpp->bestpg != mpp->nextpg);
 }
 
 static void
@@ -586,13 +602,26 @@ flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) {
        return false;
 }
 
+static void
+pr_register_active_paths(struct multipath *mpp)
+{
+       unsigned int i, j;
+       struct path *pp;
+       struct pathgroup *pgp;
+
+       vector_foreach_slot (mpp->pg, pgp, i) {
+               vector_foreach_slot (pgp->paths, pp, j) {
+                       if ((pp->state == PATH_UP) || (pp->state == PATH_GHOST))
+                               mpath_pr_event_handle(pp);
+               }
+       }
+}
+
 static int
 update_map (struct multipath *mpp, struct vectors *vecs, int new_map)
 {
        int retries = 3;
        char *params __attribute__((cleanup(cleanup_charp))) = NULL;
-       struct path *pp;
-       int i;
 
 retry:
        condlog(4, "%s: updating new map", mpp->alias);
@@ -609,15 +638,6 @@ retry:
 
        mpp->action = ACT_RELOAD;
 
-       if (mpp->prflag) {
-               vector_foreach_slot(mpp->paths, pp, i) {
-                       if ((pp->state == PATH_UP)  || (pp->state == PATH_GHOST)) {
-                               /* persistent reservation check*/
-                               mpath_pr_event_handle(pp);
-                       }
-               }
-       }
-
        if (setup_map(mpp, &params, vecs)) {
                condlog(0, "%s: failed to setup new map in update", mpp->alias);
                retries = -1;
@@ -643,6 +663,11 @@ fail:
 
        sync_map_state(mpp);
 
+       if (mpp->prflag != PRFLAG_SET)
+               update_map_pr(mpp);
+       if (mpp->prflag == PRFLAG_SET)
+               pr_register_active_paths(mpp);
+
        if (retries < 0)
                condlog(0, "%s: failed reload in new map update", mpp->alias);
        return 0;
@@ -1191,6 +1216,7 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map)
        int start_waiter = 0;
        int ret;
        int ro;
+       unsigned char prflag = PRFLAG_UNSET;
 
        /*
         * need path UID to go any further
@@ -1234,6 +1260,8 @@ rescan:
 
                verify_paths(mpp);
                mpp->action = ACT_RELOAD;
+               prflag = mpp->prflag;
+               mpath_pr_event_handle(pp);
        } else {
                if (!should_multipath(pp, vecs->pathvec, vecs->mpvec)) {
                        orphan_path(pp, "only one path");
@@ -1252,9 +1280,6 @@ rescan:
                        goto fail; /* leave path added to pathvec */
        }
 
-       /* persistent reservation check*/
-       mpath_pr_event_handle(pp);
-
        /* ro check - if new path is ro, force map to be ro as well */
        ro = sysfs_get_ro(pp);
        if (ro == 1)
@@ -1319,6 +1344,10 @@ rescan:
        sync_map_state(mpp);
 
        if (retries >= 0) {
+               if (start_waiter)
+                       update_map_pr(mpp);
+               if (mpp->prflag == PRFLAG_SET && prflag != PRFLAG_SET)
+                               pr_register_active_paths(mpp);
                condlog(2, "%s [%s]: path added to devmap %s",
                        pp->dev, pp->dev_t, mpp->alias);
                return 0;
@@ -1501,6 +1530,35 @@ needs_ro_update(struct multipath *mpp, int ro)
        return true;
 }
 
+int resize_map(struct multipath *mpp, unsigned long long size,
+              struct vectors * vecs)
+{
+       char *params __attribute__((cleanup(cleanup_charp))) = NULL;
+       unsigned long long orig_size = mpp->size;
+
+       mpp->size = size;
+       update_mpp_paths(mpp, vecs->pathvec);
+       if (setup_map(mpp, &params, vecs) != 0) {
+               condlog(0, "%s: failed to setup map for resize : %s",
+                       mpp->alias, strerror(errno));
+               mpp->size = orig_size;
+               return 1;
+       }
+       mpp->action = ACT_RESIZE;
+       mpp->force_udev_reload = 1;
+       if (domap(mpp, params, 1) == DOMAP_FAIL) {
+               condlog(0, "%s: failed to resize map : %s", mpp->alias,
+                       strerror(errno));
+               mpp->size = orig_size;
+               return 1;
+       }
+       if (setup_multipath(vecs, mpp) != 0)
+               return 2;
+       sync_map_state(mpp);
+
+       return 0;
+}
+
 static int
 uev_update_path (struct uevent *uev, struct vectors * vecs)
 {
@@ -1532,6 +1590,11 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
        if (pp) {
                struct multipath *mpp = pp->mpp;
                char wwid[WWID_SIZE];
+               int auto_resize;
+
+               conf = get_multipath_config();
+               auto_resize = conf->auto_resize;
+               put_multipath_config(conf);
 
                if (pp->initialized == INIT_REQUESTED_UDEV) {
                        needs_reinit = 1;
@@ -1581,7 +1644,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
                        else {
                                if (ro == 1)
                                        pp->mpp->force_readonly = 1;
-                               retval = reload_and_sync_map(mpp, vecs, 0);
+                               retval = reload_and_sync_map(mpp, vecs);
                                if (retval == 2)
                                        condlog(2, "%s: map removed during reload", pp->dev);
                                else {
@@ -1590,6 +1653,29 @@ uev_update_path (struct uevent *uev, struct vectors * vecs)
                                }
                        }
                }
+               if (auto_resize != AUTO_RESIZE_NEVER &&
+                   !mpp->wait_for_udev) {
+                       struct pathgroup *pgp;
+                       struct path *pp2;
+                       unsigned int i, j;
+                       unsigned long long orig_size = mpp->size;
+
+                       if (!pp->size || pp->size == mpp->size ||
+                           (pp->size < mpp->size &&
+                            auto_resize == AUTO_RESIZE_GROW_ONLY))
+                               goto out;
+
+                       vector_foreach_slot(mpp->pg, pgp, i)
+                               vector_foreach_slot (pgp->paths, pp2, j)
+                                       if (pp2->size && pp2->size != pp->size)
+                                               goto out;
+                       retval = resize_map(mpp, pp->size, vecs);
+                       if (retval == 2)
+                               condlog(2, "%s: map removed during resize", pp->dev);
+                       else if (retval == 0)
+                               condlog(2, "%s: resized map from %llu to %llu",
+                                       mpp->alias, orig_size, pp->size);
+               }
        }
 out:
        lock_cleanup_pop(vecs->lock);
@@ -1969,20 +2055,26 @@ ghost_delay_tick(struct vectors *vecs)
 }
 
 static void
-deferred_failback_tick (vector mpvec)
+deferred_failback_tick (struct vectors *vecs)
 {
        struct multipath * mpp;
        unsigned int i;
+       bool need_reload;
 
-       vector_foreach_slot (mpvec, mpp, i) {
+       vector_foreach_slot (vecs->mpvec, mpp, i) {
                /*
                 * deferred failback getting sooner
                 */
                if (mpp->pgfailback > 0 && mpp->failback_tick > 0) {
                        mpp->failback_tick--;
 
-                       if (!mpp->failback_tick && need_switch_pathgroup(mpp, 1))
-                               switch_pathgroup(mpp);
+                       if (!mpp->failback_tick &&
+                           need_switch_pathgroup(mpp, &need_reload)) {
+                               if (need_reload)
+                                       reload_and_sync_map(mpp, vecs);
+                               else
+                                       switch_pathgroup(mpp);
+                       }
                }
        }
 }
@@ -2038,54 +2130,40 @@ int update_prio(struct path *pp, int refresh_all)
        int i, j, changed = 0;
        struct config *conf;
 
-       if (refresh_all) {
-               vector_foreach_slot (pp->mpp->pg, pgp, i) {
-                       vector_foreach_slot (pgp->paths, pp1, j) {
-                               oldpriority = pp1->priority;
-                               conf = get_multipath_config();
-                               pthread_cleanup_push(put_multipath_config,
-                                                    conf);
-                               pathinfo(pp1, conf, DI_PRIO);
-                               pthread_cleanup_pop(1);
-                               if (pp1->priority != oldpriority)
-                                       changed = 1;
-                       }
-               }
-               return changed;
-       }
        oldpriority = pp->priority;
-       conf = get_multipath_config();
-       pthread_cleanup_push(put_multipath_config, conf);
-       if (pp->state != PATH_DOWN)
+       if (pp->state != PATH_DOWN) {
+               conf = get_multipath_config();
+               pthread_cleanup_push(put_multipath_config, conf);
                pathinfo(pp, conf, DI_PRIO);
-       pthread_cleanup_pop(1);
+               pthread_cleanup_pop(1);
+       }
 
-       if (pp->priority == oldpriority)
+       if (pp->priority == oldpriority && !refresh_all)
                return 0;
-       return 1;
+
+       vector_foreach_slot (pp->mpp->pg, pgp, i) {
+               vector_foreach_slot (pgp->paths, pp1, j) {
+                       if (pp1 == pp || pp1->state == PATH_DOWN)
+                               continue;
+                       oldpriority = pp1->priority;
+                       conf = get_multipath_config();
+                       pthread_cleanup_push(put_multipath_config, conf);
+                       pathinfo(pp1, conf, DI_PRIO);
+                       pthread_cleanup_pop(1);
+                       if (pp1->priority != oldpriority)
+                               changed = 1;
+               }
+       }
+       return changed;
 }
 
-static int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
+static int reload_map(struct vectors *vecs, struct multipath *mpp,
                      int is_daemon)
 {
        char *params __attribute__((cleanup(cleanup_charp))) = NULL;
-       struct path *pp;
-       int i, r;
+       int r;
 
        update_mpp_paths(mpp, vecs->pathvec);
-       if (refresh) {
-               vector_foreach_slot (mpp->paths, pp, i) {
-                       struct config *conf = get_multipath_config();
-                       pthread_cleanup_push(put_multipath_config, conf);
-                       r = pathinfo(pp, conf, DI_PRIO);
-                       pthread_cleanup_pop(1);
-                       if (r) {
-                               condlog(2, "%s: failed to refresh pathinfo",
-                                       mpp->alias);
-                               return 1;
-                       }
-               }
-       }
        if (setup_map(mpp, &params, vecs)) {
                condlog(0, "%s: failed to setup map", mpp->alias);
                return 1;
@@ -2102,10 +2180,9 @@ static int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh,
        return 0;
 }
 
-int reload_and_sync_map(struct multipath *mpp,
-                       struct vectors *vecs, int refresh)
+int reload_and_sync_map(struct multipath *mpp, struct vectors *vecs)
 {
-       if (reload_map(vecs, mpp, refresh, 1))
+       if (reload_map(vecs, mpp, 1))
                return 1;
        if (setup_multipath(vecs, mpp) != 0)
                return 2;
@@ -2240,6 +2317,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
        struct config *conf;
        int marginal_pathgroups, marginal_changed = 0;
        int ret;
+       bool need_reload;
 
        if (((pp->initialized == INIT_OK || pp->initialized == INIT_PARTIAL ||
              pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp) ||
@@ -2479,13 +2557,17 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
                }
 
                if (newstate == PATH_UP || newstate == PATH_GHOST) {
-                       if (pp->mpp->prflag) {
+                       if (pp->mpp->prflag != PRFLAG_UNSET) {
+                               int prflag = pp->mpp->prflag;
                                /*
                                 * Check Persistent Reservation.
                                 */
                                condlog(2, "%s: checking persistent "
                                        "reservation registration", pp->dev);
                                mpath_pr_event_handle(pp);
+                               if (pp->mpp->prflag == PRFLAG_SET &&
+                                   prflag != PRFLAG_SET)
+                                       pr_register_active_paths(pp->mpp);
                        }
                }
 
@@ -2556,25 +2638,30 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
         */
        condlog(4, "path prio refresh");
 
-       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) {
+       if (marginal_changed) {
+               update_prio(pp, 1);
+               reload_and_sync_map(pp->mpp, vecs);
+       } else if (update_prio(pp, new_path_up) &&
+                  pp->mpp->pgpolicyfn == (pgpolicyfn *)group_by_prio &&
+                  pp->mpp->pgfailback == -FAILBACK_IMMEDIATE) {
                condlog(2, "%s: path priorities changed. reloading",
                        pp->mpp->alias);
-               reload_and_sync_map(pp->mpp, vecs, !new_path_up);
-       } else if (need_switch_pathgroup(pp->mpp, 0)) {
+               reload_and_sync_map(pp->mpp, vecs);
+       } else if (need_switch_pathgroup(pp->mpp, &need_reload)) {
                if (pp->mpp->pgfailback > 0 &&
                    (new_path_up || pp->mpp->failback_tick <= 0))
-                       pp->mpp->failback_tick =
-                               pp->mpp->pgfailback + 1;
+                       pp->mpp->failback_tick = pp->mpp->pgfailback + 1;
                else if (pp->mpp->pgfailback == -FAILBACK_IMMEDIATE ||
-                        (chkr_new_path_up && followover_should_failback(pp)))
-                       switch_pathgroup(pp->mpp);
+                        (chkr_new_path_up && followover_should_failback(pp))) {
+                       if (need_reload)
+                               reload_and_sync_map(pp->mpp, vecs);
+                       else
+                               switch_pathgroup(pp->mpp);
+               }
        }
        return 1;
 }
+
 enum checker_state {
        CHECKER_STARTING,
        CHECKER_RUNNING,
@@ -2696,7 +2783,7 @@ unlock:
                pthread_cleanup_push(cleanup_lock, &vecs->lock);
                lock(&vecs->lock);
                pthread_testcancel();
-               deferred_failback_tick(vecs->mpvec);
+               deferred_failback_tick(vecs);
                retry_count_tick(vecs->mpvec);
                missing_uev_wait_tick(vecs);
                ghost_delay_tick(vecs);
@@ -2852,6 +2939,8 @@ configure (struct vectors * vecs, enum force_reload_types reload_type)
                if (remember_wwid(mpp->wwid) == 1)
                        trigger_paths_udev_change(mpp, true);
                update_map_pr(mpp);
+               if (mpp->prflag == PRFLAG_SET)
+                       pr_register_active_paths(mpp);
        }
 
        /*
@@ -3293,6 +3382,7 @@ static void cleanup_child(void)
 {
        cleanup_threads();
        cleanup_vecs();
+       cleanup_bindings();
        if (poll_dmevents)
                cleanup_dmevent_waiter();
 
@@ -3773,6 +3863,7 @@ void *  mpath_pr_event_handler_fn (void * pathp )
                goto out;
        }
 
+       mpp->prflag = PRFLAG_UNSET;
        ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, 0);
        if (ret != MPATH_PR_SUCCESS )
        {
@@ -3825,7 +3916,7 @@ void *  mpath_pr_event_handler_fn (void * pathp )
        {
                condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret);
        }
-       mpp->prflag = 1;
+       mpp->prflag = PRFLAG_SET;
 
        free(param);
 out:
@@ -3843,12 +3934,12 @@ int mpath_pr_event_handle(struct path *pp)
        struct multipath * mpp;
 
        if (pp->bus != SYSFS_BUS_SCSI)
-               return 0;
+               goto no_pr;
 
        mpp = pp->mpp;
 
        if (!get_be64(mpp->reservation_key))
-               return -1;
+               goto no_pr;
 
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
@@ -3861,4 +3952,8 @@ int mpath_pr_event_handle(struct path *pp)
        pthread_attr_destroy(&attr);
        rc = pthread_join(thread, NULL);
        return 0;
+
+no_pr:
+       pp->mpp->prflag = PRFLAG_UNSET;
+       return 0;
 }
index e8bee8e6a2d69a837f5ece811b02491107dc174e..8a178c0becd5b4a2c364d87e674ada5b59eb5ccf 100644 (file)
@@ -47,10 +47,11 @@ int __setup_multipath (struct vectors * vecs, struct multipath * mpp,
                       int reset);
 #define setup_multipath(vecs, mpp) __setup_multipath(vecs, mpp, 1)
 int update_multipath (struct vectors *vecs, char *mapname, int reset);
-int reload_and_sync_map(struct multipath *mpp, struct vectors *vecs,
-                       int refresh);
+int reload_and_sync_map(struct multipath *mpp, struct vectors *vecs);
 
 void handle_path_wwid_change(struct path *pp, struct vectors *vecs);
 bool check_path_wwid_change(struct path *pp);
 int finish_path_init(struct path *pp, struct vectors * vecs);
+int resize_map(struct multipath *mpp, unsigned long long size,
+              struct vectors *vecs);
 #endif /* MAIN_H */
index 6c57c6cc1b449313f412b1e51d492c873324b977..cf7ae5be1fe2faa73b79ceed36ae228b214350cd 100644 (file)
@@ -1,8 +1,9 @@
 .\" ----------------------------------------------------------------------------
-.\" Update the date below if you make any significant change.
 .\" Make sure there are no errors with:
-.\" groff -z -wall -b -e -t multipathd/multipathd.8
+.\" groff -z -wall -b -e -t multipathd/multipathc.8
+.\" man --warnings -E UTF-8 -l -Tutf8 -Z multipathd/multipathc.8 > /dev/null
 .\"
+.\" Update the date below if you make any significant change.
 .\" ----------------------------------------------------------------------------
 .
 .TH MULTIPATHC 8 2022-09-03 Linux
diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8
deleted file mode 100644 (file)
index bdf102e..0000000
+++ /dev/null
@@ -1,378 +0,0 @@
-.\" ----------------------------------------------------------------------------
-.\" Update the date below if you make any significant change.
-.\" Make sure there are no errors with:
-.\" groff -z -wall -b -e -t multipathd/multipathd.8
-.\"
-.\" ----------------------------------------------------------------------------
-.
-.TH MULTIPATHD 8 2022-09-03 Linux
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH NAME
-.\" ----------------------------------------------------------------------------
-.
-multipathd \- Multipath daemon.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH SYNOPSIS
-.\" ----------------------------------------------------------------------------
-.
-.B multipathd
-.RB [\| \-d \|]
-.RB [\| \-s \|]
-.RB [\| \-v\ \c
-.IR verbosity \|]
-.RB [\| \-B \|]
-.RB [\| \-w \|]
-.LP
-.B multipathd
-.RB [\| \-v\ \c
-.IR verbosity \|]
-.B -k\fIcommand\fR
-.LP
-.B multipathd
-.RB [\| \-v\ \c
-.IR verbosity \|]
-.B -k
-
-.\" ----------------------------------------------------------------------------
-.SH DESCRIPTION
-.\" ----------------------------------------------------------------------------
-.
-The \fBmultipathd\fR daemon is in charge of checking for failed paths. When this
-happens, it will reconfigure the multipath map the path belongs to, so that this
-map regains its maximum performance and redundancy.
-
-With the \fB-k\fR option, \fBmultipathd\fR acts as a client utility that
-sends commands to a running instance of the multipathd daemon (see
-\fBCOMMANDS\fR below).
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH OPTIONS
-.\" ----------------------------------------------------------------------------
-.
-.TP
-.B \-d
-Foreground Mode. Don't daemonize, and print all messages to stdout and stderr.
-.
-.TP
-.B \-s
-Suppress timestamps. Do not prefix logging messages with a timestamp.
-.
-.TP
-.BI \-v " level"
-Verbosity level. Print additional information while running multipathd. A level
-of 0 means only print errors. A level of 3 or greater prints debugging information
-as well.
-.
-.TP
-.B \-B
-Read-only bindings file. multipathd will not write to the \fIuser_friendly_names\fR
-bindings file. If a \fIuser_friendly_name\fR doesn't already exist for a device, it
-will use its WWID as its alias.
-.
-.TP
-.B \-k\fIcommand\fB
-multipathd executes the given command (see \fBCOMMANDS\fR below). If the
-command contains whitespace or shell special characters, it needs to be quoted
-like in \fImultipathd -k'show topology'\fR. No whitespace is allowed between
-the \fB-k\fR and the command string.
-.
-.TP
-.B \-k
-multipathd executes the \fBmultipathc\fR interactive shell for entering
-commands (see \fBCOMMANDS\fR below).
-.
-.TP
-.B \-n
-\fBIGNORED\fR. Use the option
-\fIfind_multipaths\fR to control the treatment of newly detected devices by
-multipathd. See
-.BR multipath.conf(5).
-.
-.TP
-.B \-w
-Since kernel 4.14 a new device-mapper event polling interface is used for updating
-multipath devices on dmevents. Use this flag to force it to use the old event
-waiting method, based on creating a separate thread for each device.
-.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH COMMANDS
-.\" ----------------------------------------------------------------------------
-.
-.TP
-The following commands can be used in interactive mode:
-.
-.TP
-.B list|show paths
-Show the paths that multipathd is monitoring, and their state.
-.
-.TP
-.B list|show paths format $format
-Show the paths that multipathd is monitoring, using a format string with path
-format wildcards.
-.
-.TP
-.B list|show maps|multipaths
-Show the multipath devices that the multipathd is monitoring.
-.
-.TP
-.B list|show maps|multipaths format $format
-Show the status of all multipath devices that the multipathd is monitoring,
-using a format string with multipath format wildcards.
-.
-.TP
-.B list|show maps|multipaths status
-Show the status of all multipath devices that the multipathd is monitoring.
-.
-.TP
-.B list|show maps|multipaths stats
-Show some statistics of all multipath devices that the multipathd is monitoring.
-.
-.TP
-.B list|show maps|multipaths topology
-Show the current multipath topology. Same as '\fImultipath \-ll\fR'.
-.
-.TP
-.B list|show topology
-Show the current multipath topology. Same as '\fImultipath \-ll\fR'.
-.
-.TP
-.B list|show map|multipath $map topology
-Show topology of a single multipath device specified by $map, for example
-36005076303ffc56200000000000010aa. This map could be obtained from '\fIlist maps\fR'.
-.
-.TP
-.B list|show wildcards
-Show the format wildcards used in interactive commands taking $format.
-.
-.TP
-.B list|show config
-Show the currently used configuration, derived from default values and values
-specified within the configuration file \fI/etc/multipath.conf\fR.
-.
-.TP
-.B list|show config local
-Show the currently used configuration like \fIshow config\fR, but limiting
-the devices section to those devices that are actually present in the system.
-.
-.TP
-.B list|show blacklist
-Show the currently used blacklist rules, derived from default values and values
-specified within the configuration file \fI/etc/multipath.conf\fR.
-.
-.TP
-.B list|show devices
-Show all available block devices by name including the information if they are
-blacklisted or not.
-.
-.TP
-.B list|show status
-Show the number of path checkers in each possible state, the number of monitored
-paths, and whether multipathd is currently handling a uevent.
-.
-.TP
-.B list|show daemon
-Show the current state of the multipathd daemon.
-.
-.TP
-.B add path $path
-Add a path to the list of monitored paths. $path is as listed in /sys/block (e.g. sda).
-.
-.TP
-.B remove|del path $path
-Stop monitoring a path. $path is as listed in /sys/block (e.g. sda).
-.
-.TP
-.B add map|multipath $map
-Add a multipath device to the list of monitored devices. $map can either be a
-device-mapper device as listed in /sys/block (e.g. dm-0) or it can be the alias
-for the multipath device (e.g. mpath1) or the uid of the multipath device
-(e.g. 36005076303ffc56200000000000010aa).
-.
-.TP
-.B remove|del map|multipath $map
-Stop monitoring a multipath device.
-.
-.TP
-.B resize map|multipath $map
-Resizes map $map to the given size.
-.
-.TP
-.B switch|switchgroup map|multipath $map group $group
-Force a multipath device to switch to a specific path group. $group is the path
-group index, starting with 1.
-.
-.TP
-.B reconfigure
-Rereads the configuration, and reloads all changed multipath devices. This
-also happens at startup, when the service is reload, or when a SIGHUP is
-received.
-.
-.TP
-.B reconfigure all
-Rereads the configuration, and reloads all multipath devices regardless of
-whether or not they have changed. This also happens when \fImultipath -r\fR is
-run.
-.TP
-.B suspend map|multipath $map
-Sets map $map into suspend state.
-.
-.TP
-.B resume map|multipath $map
-Resumes map $map from suspend state.
-.
-.TP
-.B reset map|multipath $map
-Reassign existing device-mapper table(s) use the multipath device, instead
-of its path devices.
-.
-.TP
-.B reload map|multipath $map
-Reload a multipath device.
-.
-.TP
-.B fail path $path
-Sets path $path into failed state.
-.
-.TP
-.B reinstate path $path
-Resumes path $path from failed state.
-.
-.TP
-.B disablequeueing maps|multipaths
-Disable queueing on all multipath devices.
-.
-.TP
-.B restorequeueing maps|multipaths
-Restore queueing on all multipath devices.
-.
-.TP
-.B disablequeueing map|multipath $map
-Disable queuing on multipathed map $map.
-.
-.TP
-.B restorequeueing map|multipath $map
-Restore queuing on multipahted map $map.
-.
-.TP
-.B forcequeueing daemon
-Forces multipathd into queue_without_daemon mode, so that no_path_retry queueing
-will not be disabled when the daemon stops.
-.
-.TP
-.B restorequeueing daemon
-Restores configured queue_without_daemon mode.
-.
-.TP
-.B map|multipath $map setprstatus
-Enable persistent reservation management on $map.
-.
-.TP
-.B map|multipath $map unsetprstatus
-Disable persistent reservation management on $map.
-.
-.TP
-.B map|multipath $map getprstatus
-Get the current persistent reservation management status of $map.
-.
-.TP
-.B map|multipath $map getprkey
-Get the current persistent reservation key associated with $map.
-.
-.TP
-.B map|multipath $map setprkey key $key
-Set the persistent reservation key associated with $map to $key in the
-\fIprkeys_file\fR. This key will only be used by multipathd if
-\fIreservation_key\fR is set to \fBfile\fR in \fI/etc/multipath.conf\fR.
-.
-.TP
-.B map|multipath $map unsetprkey
-Remove the persistent reservation key associated with $map from the
-\fIprkeys_file\fR. This will only unset the key used by multipathd if
-\fIreservation_key\fR is set to \fBfile\fR in \fI/etc/multipath.conf\fR.
-.
-.TP
-.B path $path setmarginal
-move $path to a marginal pathgroup. The path will remain in the marginal
-path group until \fIunsetmarginal\fR is called. This command will only
-work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths
-detection method configured (see the multipath.conf man page for details).
-.
-.TP
-.B path $path unsetmarginal
-return marginal path $path to its normal pathgroup. This command will only
-work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths
-detection method configured (see the multipath.conf man page for details).
-.
-.TP
-.B map $map unsetmarginal
-return all marginal paths in $map to their normal pathgroups. This command
-will only work if \fImarginal_pathgroups\fR is enabled and there is no Shaky
-paths detection method configured (see the multipath.conf man page for details).
-.
-.TP
-.B quit|exit
-End interactive session.
-.
-.TP
-.B shutdown
-Stop multipathd.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "SYSTEMD INTEGRATION"
-.\" ----------------------------------------------------------------------------
-.
-When compiled with systemd support two systemd service files are installed,
-\fImultipathd.service\fR and \fImultipathd.socket\fR The \fImultipathd.socket\fR
-service instructs systemd to intercept the CLI command socket, so that any call
-to the CLI interface will start-up the daemon if required.
-The \fImultipathd.service\fR file carries the definitions for controlling the
-multipath daemon. The daemon itself uses the \fBsd_notify\fR(3) interface to
-communicate with systemd. The following unit keywords are recognized:
-.
-.TP
-.B WatchdogSec=
-Enables the internal watchdog from systemd. multipath will send a
-notification via \fBsd_notify\fR(3) to systemd to reset the watchdog. If
-specified the \fIpolling_interval\fR and \fImax_polling_interval\fR settings
-will be overridden by the watchdog settings.
-Please note that systemd prior to version 207 has issues which prevent
-the systemd-provided watchdog from working correctly. So the watchdog
-is not enabled per default, but has to be enabled manually by updating
-the \fImultipathd.service\fR file.
-.
-.TP
-.B OOMScoreAdjust=
-Overrides the internal OOM adjust mechanism.
-.
-.TP
-.B LimitNOFILE=
-Overrides the \fImax_fds\fR configuration setting.
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH "SEE ALSO"
-.\" ----------------------------------------------------------------------------
-.
-.BR multipathc (8),
-.BR multipath (8),
-.BR kpartx (8)
-.RE
-.BR sd_notify (3),
-.BR systemd.service (5).
-.
-.
-.\" ----------------------------------------------------------------------------
-.SH AUTHORS
-.\" ----------------------------------------------------------------------------
-.
-\fImultipath-tools\fR was developed by Christophe Varoqui <christophe.varoqui@opensvc.com>
-and others.
-.\" EOF
diff --git a/multipathd/multipathd.8.in b/multipathd/multipathd.8.in
new file mode 100644 (file)
index 0000000..e98c27f
--- /dev/null
@@ -0,0 +1,379 @@
+.\" ----------------------------------------------------------------------------
+.\" Make sure there are no errors with:
+.\" groff -z -wall -b -e -t multipathd/multipathd.8
+.\" man --warnings -E UTF-8 -l -Tutf8 -Z multipathd/multipathd.8 > /dev/null
+.\"
+.\" Update the date below if you make any significant change.
+.\" ----------------------------------------------------------------------------
+.
+.TH MULTIPATHD 8 2022-09-03 Linux
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH NAME
+.\" ----------------------------------------------------------------------------
+.
+multipathd \- Multipath daemon.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH SYNOPSIS
+.\" ----------------------------------------------------------------------------
+.
+.B multipathd
+.RB [\| \-d \|]
+.RB [\| \-s \|]
+.RB [\| \-v\ \c
+.IR verbosity \|]
+.RB [\| \-B \|]
+.RB [\| \-w \|]
+.LP
+.B multipathd
+.RB [\| \-v\ \c
+.IR verbosity \|]
+.B -k\fIcommand\fR
+.LP
+.B multipathd
+.RB [\| \-v\ \c
+.IR verbosity \|]
+.B -k
+
+.\" ----------------------------------------------------------------------------
+.SH DESCRIPTION
+.\" ----------------------------------------------------------------------------
+.
+The \fBmultipathd\fR daemon is in charge of checking for failed paths. When this
+happens, it will reconfigure the multipath map the path belongs to, so that this
+map regains its maximum performance and redundancy.
+
+With the \fB-k\fR option, \fBmultipathd\fR acts as a client utility that
+sends commands to a running instance of the multipathd daemon (see
+\fBCOMMANDS\fR below).
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH OPTIONS
+.\" ----------------------------------------------------------------------------
+.
+.TP
+.B \-d
+Foreground Mode. Don't daemonize, and print all messages to stdout and stderr.
+.
+.TP
+.B \-s
+Suppress timestamps. Do not prefix logging messages with a timestamp.
+.
+.TP
+.BI \-v " level"
+Verbosity level. Print additional information while running multipathd. A level
+of 0 means only print errors. A level of 3 or greater prints debugging information
+as well.
+.
+.TP
+.B \-B
+Read-only bindings file. multipathd will not write to the \fIuser_friendly_names\fR
+bindings file. If a \fIuser_friendly_name\fR doesn't already exist for a device, it
+will use its WWID as its alias.
+.
+.TP
+.B \-k\fIcommand\fB
+multipathd executes the given command (see \fBCOMMANDS\fR below). If the
+command contains whitespace or shell special characters, it needs to be quoted
+like in \fImultipathd -k'show topology'\fR. No whitespace is allowed between
+the \fB-k\fR and the command string.
+.
+.TP
+.B \-k
+multipathd executes the \fBmultipathc\fR interactive shell for entering
+commands (see \fBCOMMANDS\fR below).
+.
+.TP
+.B \-n
+\fBIGNORED\fR. Use the option
+\fIfind_multipaths\fR to control the treatment of newly detected devices by
+multipathd. See
+.BR multipath.conf(5).
+.
+.TP
+.B \-w
+Since kernel 4.14 a new device-mapper event polling interface is used for updating
+multipath devices on dmevents. Use this flag to force it to use the old event
+waiting method, based on creating a separate thread for each device.
+.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH COMMANDS
+.\" ----------------------------------------------------------------------------
+.
+.TP
+The following commands can be used in interactive mode:
+.
+.TP
+.B list|show paths
+Show the paths that multipathd is monitoring, and their state.
+.
+.TP
+.B list|show paths format $format
+Show the paths that multipathd is monitoring, using a format string with path
+format wildcards.
+.
+.TP
+.B list|show maps|multipaths
+Show the multipath devices that the multipathd is monitoring.
+.
+.TP
+.B list|show maps|multipaths format $format
+Show the status of all multipath devices that the multipathd is monitoring,
+using a format string with multipath format wildcards.
+.
+.TP
+.B list|show maps|multipaths status
+Show the status of all multipath devices that the multipathd is monitoring.
+.
+.TP
+.B list|show maps|multipaths stats
+Show some statistics of all multipath devices that the multipathd is monitoring.
+.
+.TP
+.B list|show maps|multipaths topology
+Show the current multipath topology. Same as '\fImultipath \-ll\fR'.
+.
+.TP
+.B list|show topology
+Show the current multipath topology. Same as '\fImultipath \-ll\fR'.
+.
+.TP
+.B list|show map|multipath $map topology
+Show topology of a single multipath device specified by $map, for example
+36005076303ffc56200000000000010aa. This map could be obtained from '\fIlist maps\fR'.
+.
+.TP
+.B list|show wildcards
+Show the format wildcards used in interactive commands taking $format.
+.
+.TP
+.B list|show config
+Show the currently used configuration, derived from default values and values
+specified within the configuration file \fI@CONFIGFILE@\fR.
+.
+.TP
+.B list|show config local
+Show the currently used configuration like \fIshow config\fR, but limiting
+the devices section to those devices that are actually present in the system.
+.
+.TP
+.B list|show blacklist
+Show the currently used blacklist rules, derived from default values and values
+specified within the configuration file \fI@CONFIGFILE@\fR.
+.
+.TP
+.B list|show devices
+Show all available block devices by name including the information if they are
+blacklisted or not.
+.
+.TP
+.B list|show status
+Show the number of path checkers in each possible state, the number of monitored
+paths, and whether multipathd is currently handling a uevent.
+.
+.TP
+.B list|show daemon
+Show the current state of the multipathd daemon.
+.
+.TP
+.B add path $path
+Add a path to the list of monitored paths. $path is as listed in /sys/block (e.g. sda).
+.
+.TP
+.B remove|del path $path
+Stop monitoring a path. $path is as listed in /sys/block (e.g. sda).
+.
+.TP
+.B add map|multipath $map
+Add a multipath device to the list of monitored devices. $map can either be a
+device-mapper device as listed in /sys/block (e.g. dm-0) or it can be the alias
+for the multipath device (e.g. mpath1) or the uid of the multipath device
+(e.g. 36005076303ffc56200000000000010aa).
+.
+.TP
+.B remove|del map|multipath $map
+Stop monitoring a multipath device.
+.
+.TP
+.B resize map|multipath $map
+Resizes map $map to the given size.
+.
+.TP
+.B switch|switchgroup map|multipath $map group $group
+Force a multipath device to switch to a specific path group. $group is the path
+group index, starting with 1.
+.
+.TP
+.B reconfigure
+Rereads the configuration, and reloads all changed multipath devices. This
+also happens at startup, when the service is reload, or when a SIGHUP is
+received.
+.
+.TP
+.B reconfigure all
+Rereads the configuration, and reloads all multipath devices regardless of
+whether or not they have changed. This also happens when \fImultipath -r\fR is
+run.
+.TP
+.B suspend map|multipath $map
+Sets map $map into suspend state.
+.
+.TP
+.B resume map|multipath $map
+Resumes map $map from suspend state.
+.
+.TP
+.B reset map|multipath $map
+Reassign existing device-mapper table(s) use the multipath device, instead
+of its path devices.
+.
+.TP
+.B reload map|multipath $map
+Reload a multipath device.
+.
+.TP
+.B fail path $path
+Sets path $path into failed state.
+.
+.TP
+.B reinstate path $path
+Resumes path $path from failed state.
+.
+.TP
+.B disablequeueing maps|multipaths
+Disable queueing on all multipath devices.
+.
+.TP
+.B restorequeueing maps|multipaths
+Restore queueing on all multipath devices.
+.
+.TP
+.B disablequeueing map|multipath $map
+Disable queuing on multipathed map $map.
+.
+.TP
+.B restorequeueing map|multipath $map
+Restore queuing on multipahted map $map.
+.
+.TP
+.B forcequeueing daemon
+Forces multipathd into queue_without_daemon mode, so that no_path_retry queueing
+will not be disabled when the daemon stops.
+.
+.TP
+.B restorequeueing daemon
+Restores configured queue_without_daemon mode.
+.
+.TP
+.B map|multipath $map setprstatus
+Enable persistent reservation management on $map.
+.
+.TP
+.B map|multipath $map unsetprstatus
+Disable persistent reservation management on $map.
+.
+.TP
+.B map|multipath $map getprstatus
+Get the current persistent reservation management status of $map.
+.
+.TP
+.B map|multipath $map getprkey
+Get the current persistent reservation key associated with $map.
+.
+.TP
+.B map|multipath $map setprkey key $key
+Set the persistent reservation key associated with $map to $key in the
+\fIprkeys_file\fR. This key will only be used by multipathd if
+\fIreservation_key\fR is set to \fBfile\fR in \fI@CONFIGFILE@\fR.
+.
+.TP
+.B map|multipath $map unsetprkey
+Remove the persistent reservation key associated with $map from the
+\fIprkeys_file\fR. This will only unset the key used by multipathd if
+\fIreservation_key\fR is set to \fBfile\fR in \fI@CONFIGFILE@\fR.
+.
+.TP
+.B path $path setmarginal
+move $path to a marginal pathgroup. The path will remain in the marginal
+path group until \fIunsetmarginal\fR is called. This command will only
+work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths
+detection method configured (see the multipath.conf man page for details).
+.
+.TP
+.B path $path unsetmarginal
+return marginal path $path to its normal pathgroup. This command will only
+work if \fImarginal_pathgroups\fR is enabled and there is no Shaky paths
+detection method configured (see the multipath.conf man page for details).
+.
+.TP
+.B map $map unsetmarginal
+return all marginal paths in $map to their normal pathgroups. This command
+will only work if \fImarginal_pathgroups\fR is enabled and there is no Shaky
+paths detection method configured (see the multipath.conf man page for details).
+.
+.TP
+.B quit|exit
+End interactive session.
+.
+.TP
+.B shutdown
+Stop multipathd.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "SYSTEMD INTEGRATION"
+.\" ----------------------------------------------------------------------------
+.
+When compiled with systemd support two systemd service files are installed,
+\fImultipathd.service\fR and \fImultipathd.socket\fR The \fImultipathd.socket\fR
+service instructs systemd to intercept the CLI command socket, so that any call
+to the CLI interface will start-up the daemon if required.
+The \fImultipathd.service\fR file carries the definitions for controlling the
+multipath daemon. The daemon itself uses the \fBsd_notify\fR(3) interface to
+communicate with systemd. The following unit keywords are recognized:
+.
+.TP
+.B WatchdogSec=
+Enables the internal watchdog from systemd. multipath will send a
+notification via \fBsd_notify\fR(3) to systemd to reset the watchdog. If
+specified the \fIpolling_interval\fR and \fImax_polling_interval\fR settings
+will be overridden by the watchdog settings.
+Please note that systemd prior to version 207 has issues which prevent
+the systemd-provided watchdog from working correctly. So the watchdog
+is not enabled per default, but has to be enabled manually by updating
+the \fImultipathd.service\fR file.
+.
+.TP
+.B OOMScoreAdjust=
+Overrides the internal OOM adjust mechanism.
+.
+.TP
+.B LimitNOFILE=
+Overrides the \fImax_fds\fR configuration setting.
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH "SEE ALSO"
+.\" ----------------------------------------------------------------------------
+.
+.BR multipathc (8),
+.BR multipath (8),
+.BR kpartx (8)
+.RE
+.BR sd_notify (3),
+.BR systemd.service (5).
+.
+.
+.\" ----------------------------------------------------------------------------
+.SH AUTHORS
+.\" ----------------------------------------------------------------------------
+.
+\fImultipath-tools\fR was developed by Christophe Varoqui <christophe.varoqui@opensvc.com>
+and others.
+.\" EOF
diff --git a/multipathd/multipathd.service b/multipathd/multipathd.service
deleted file mode 100644 (file)
index aec62db..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-[Unit]
-Description=Device-Mapper Multipath Device Controller
-Before=lvm2-activation-early.service
-Before=local-fs-pre.target blk-availability.service shutdown.target
-Wants=systemd-udevd-kernel.socket
-After=systemd-udevd-kernel.socket
-After=multipathd.socket systemd-remount-fs.service
-Before=initrd-cleanup.service
-DefaultDependencies=no
-Conflicts=shutdown.target
-Conflicts=initrd-cleanup.service
-ConditionKernelCommandLine=!nompath
-ConditionKernelCommandLine=!multipath=off
-ConditionVirtualization=!container
-
-[Service]
-Type=notify
-NotifyAccess=main
-ExecStart=/sbin/multipathd -d -s
-ExecReload=/sbin/multipathd reconfigure
-TasksMax=infinity
-
-[Install]
-WantedBy=sysinit.target
-Also=multipathd.socket
diff --git a/multipathd/multipathd.service.in b/multipathd/multipathd.service.in
new file mode 100644 (file)
index 0000000..6d03ff7
--- /dev/null
@@ -0,0 +1,24 @@
+[Unit]
+Description=Device-Mapper Multipath Device Controller
+Before=lvm2-activation-early.service
+Before=local-fs-pre.target blk-availability.service shutdown.target
+Wants=systemd-udevd-kernel.socket @MODPROBE_UNIT@
+After=systemd-udevd-kernel.socket @MODPROBE_UNIT@
+After=multipathd.socket systemd-remount-fs.service
+Before=initrd-cleanup.service
+DefaultDependencies=no
+Conflicts=shutdown.target
+Conflicts=initrd-cleanup.service
+ConditionKernelCommandLine=!nompath
+ConditionKernelCommandLine=!multipath=off
+ConditionVirtualization=!container
+
+[Service]
+Type=notify
+NotifyAccess=main
+ExecStart=/sbin/multipathd -d -s
+ExecReload=/sbin/multipathd reconfigure
+TasksMax=infinity
+
+[Install]
+WantedBy=sysinit.target
index 02e89fb489e768ca88950d0d9bad77c2861aac94..4d6f258c9f88e49dc728d720cf3b783dbc91de7d 100644 (file)
@@ -41,6 +41,7 @@
 #include "cli.h"
 #include "uxlsnr.h"
 #include "strbuf.h"
+#include "alias.h"
 
 /* state of client connection */
 enum {
@@ -190,6 +191,7 @@ void wakeup_cleanup(void *arg)
 struct watch_descriptors {
        int conf_wd;
        int dir_wd;
+       int mp_wd; /* /etc/multipath; for bindings file */
 };
 
 /* failing to set the watch descriptor is o.k. we just miss a warning
@@ -200,6 +202,7 @@ static void reset_watch(int notify_fd, struct watch_descriptors *wds,
        struct config *conf;
        int dir_reset = 0;
        int conf_reset = 0;
+       int mp_reset = 0;
 
        if (notify_fd == -1)
                return;
@@ -214,6 +217,8 @@ static void reset_watch(int notify_fd, struct watch_descriptors *wds,
                        conf_reset = 1;
                if (wds->dir_wd == -1)
                        dir_reset = 1;
+               if (wds->mp_wd == -1)
+                       mp_reset = 1;
        }
        put_multipath_config(conf);
 
@@ -235,7 +240,13 @@ static void reset_watch(int notify_fd, struct watch_descriptors *wds,
                if (wds->conf_wd == -1)
                        condlog(3, "didn't set up notifications on /etc/multipath.conf: %m");
        }
-       return;
+       if (mp_reset) {
+               wds->mp_wd = inotify_add_watch(notify_fd, STATE_DIR,
+                                              IN_MOVED_TO|IN_ONLYDIR);
+               if (wds->mp_wd == -1)
+                               condlog(3, "didn't set up notifications on %s: %m",
+                                       STATE_DIR);
+       }
 }
 
 static void handle_inotify(int fd, struct watch_descriptors *wds)
@@ -256,12 +267,13 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
                                        inotify_rm_watch(fd, wds->conf_wd);
                                if (wds->dir_wd != -1)
                                        inotify_rm_watch(fd, wds->dir_wd);
-                               wds->conf_wd = wds->dir_wd = -1;
+                               if (wds->mp_wd != -1)
+                                       inotify_rm_watch(fd, wds->mp_wd);
+                               wds->conf_wd = wds->dir_wd = wds->mp_wd = -1;
                        }
                        break;
                }
 
-               got_notify = 1;
                for (ptr = buff; ptr < buff + len;
                     ptr += sizeof(struct inotify_event) + event->len) {
                        event = (const struct inotify_event *) ptr;
@@ -273,7 +285,13 @@ static void handle_inotify(int fd, struct watch_descriptors *wds)
                                        wds->conf_wd = inotify_add_watch(notify_fd, DEFAULT_CONFIGFILE, IN_CLOSE_WRITE);
                                else if (wds->dir_wd == event->wd)
                                        wds->dir_wd = -1;
+                               else if (wds->mp_wd == event->wd)
+                                       wds->mp_wd = -1;
                        }
+                       if (wds->mp_wd != -1 && wds->mp_wd == event->wd)
+                               handle_bindings_file_inotify(event);
+                       else
+                               got_notify = 1;
                }
        }
        if (got_notify)
@@ -599,7 +617,7 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
        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 };
+       struct watch_descriptors wds = { .conf_wd = -1, .dir_wd = -1, .mp_wd = -1, };
        struct vectors *vecs = trigger_data;
 
        condlog(3, "uxsock: startup listener");
@@ -666,7 +684,8 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
 
                reset_watch(notify_fd, &wds, &sequence_nr);
                polls[POLLFD_NOTIFY].fd = notify_fd;
-               if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1))
+               if (notify_fd == -1 || (wds.conf_wd == -1 && wds.dir_wd == -1
+                                       && wds.mp_wd == -1))
                        polls[POLLFD_NOTIFY].events = 0;
                else
                        polls[POLLFD_NOTIFY].events = POLLIN;
index 860338b22e1f7a619381e3a387e38514b5e0ef15..7dac8a8f121f73a6f729b105a492e693fb3b8c44 100644 (file)
@@ -48,14 +48,14 @@ hwtable-test_OBJDEPS := $(multipathdir)/discovery.o $(multipathdir)/blacklist.o
        $(multipathdir)/structs.o $(multipathdir)/propsel.o
 hwtable-test_LIBDEPS := -ludev -lpthread -ldl
 blacklist-test_TESTDEPS := test-log.o
-blacklist-test_OBJDEPS := $(multipathdir)/blacklist.o
 blacklist-test_LIBDEPS := -ludev
 vpd-test_OBJDEPS :=  $(multipathdir)/discovery.o
 vpd-test_LIBDEPS := -ludev -lpthread -ldl
 alias-test_TESTDEPS := test-log.o
+alias-test_OBJDEPS := $(mpathutildir)/util.o
 alias-test_LIBDEPS := -lpthread -ldl
 valid-test_OBJDEPS := $(multipathdir)/valid.o $(multipathdir)/discovery.o
-valid-test_LIBDEPS := -ludev -lpthread -ldl
+valid-test_LIBDEPS := -lmount -ludev -lpthread -ldl
 devt-test_LIBDEPS := -ludev
 mpathvalid-test_LIBDEPS := -ludev -lpthread -ldl
 mpathvalid-test_OBJDEPS := $(mpathvaliddir)/mpath_valid.o
@@ -112,7 +112,7 @@ dep_clean:
 # Pass the original values of CFLAGS etc. to the sub-make, which will include
 # Makefile.in again. Otherwise, the flags would be added twice.
 libmultipath.so.0: $(multipathdir)/libmultipath.so.0
-       @CFLAGS=$(ORIG_CFLAGS) CPPFLAGS=$(ORIG_CPPFLAGS) LDFLAGS=$(ORIG_LDFLAGS) \
+       @CFLAGS="$(ORIG_CFLAGS)" CPPFLAGS="$(ORIG_CPPFLAGS)" LDFLAGS="$(ORIG_LDFLAGS)" \
        $(MAKE) -C $(multipathdir) configdir=$(TESTDIR)/conf.d plugindir=$(TESTDIR)/lib test-lib
 
 # COLON will get expanded during second expansion below
index 3ca6c28bbd7911a39f072b7617ef800ff0bf0488..f893d174011ea7a96e644499962f344e87b07d89 100644 (file)
@@ -3,14 +3,19 @@
 #include <setjmp.h>
 #include <stdio.h>
 #include <cmocka.h>
+#include "strbuf.h"
 #include "util.h"
 #include "alias.h"
 #include "test-log.h"
 #include <errno.h>
+#include <string.h>
 
 #include "globals.c"
 #include "../libmultipath/alias.c"
 
+/* For verbose printing of all aliases in the ordering tests */
+#define ALIAS_DEBUG 0
+
 #if INT_MAX == 0x7fffffff
 /* user_friendly_name for map #INT_MAX */
 #define MPATH_ID_INT_MAX "fxshrxw"
 #define MPATH_ID_INT_MAX_p1 "fxshrxx"
 #endif
 
-void __wrap_rewind(FILE *stream)
-{}
-
-char *__wrap_fgets(char *buf, int n, FILE *stream)
-{
-       char *val = mock_ptr_type(char *);
-       if (!val)
-               return NULL;
-       strlcpy(buf, val, n);
-       return buf;
-}
-
 static int __set_errno(int err)
 {
        if (err >= 0) {
@@ -43,29 +36,44 @@ static int __set_errno(int err)
        }
 }
 
-off_t __wrap_lseek(int fd, off_t offset, int whence)
-{
-       return __set_errno(mock_type(int));
-
-}
-
+/*
+ * allocate_binding -> write_bindings_file() writes the entire file, i.e. the
+ * header, any pre-existing bindings, and the new binding. The complete content
+ * depends on history and is different to predict here. Therefore we check only
+ * the newly added binding. Because add_binding() sorts entries, this new
+ * binding isn't necessarily the last one; receive it from will_return() and
+ * search for it with strstr().
+ * If the string to be written doesn't start with the bindings file
+ * header, it's a test of a partial write.
+ */
 ssize_t __wrap_write(int fd, const void *buf, size_t count)
 {
+       const char *binding, *start;
+
+#if DEBUG_WRITE
+       fprintf(stderr, "%s: %zx exp %zx\n===\n%s\n===\n", __func__, strlen(buf),
+               count, (const char *)buf);
+#endif
+       if (!strncmp((const char *)buf, BINDINGS_FILE_HEADER,
+                    sizeof(BINDINGS_FILE_HEADER) - 1))
+               start = (const char *)buf + sizeof(BINDINGS_FILE_HEADER) - 1;
+       else
+               start = buf;
+       binding = mock_ptr_type(char *);
+       start = strstr(start, binding);
        check_expected(count);
-       check_expected(buf);
+       assert_ptr_not_equal(start, NULL);
        return __set_errno(mock_type(int));
 }
 
-int __wrap_ftruncate(int fd, off_t length)
+int __wrap_rename(const char *old, const char *new)
 {
-       check_expected(length);
        return __set_errno(mock_type(int));
 }
 
-int __wrap_dm_map_present(const char * str)
+int __wrap_mkstemp(char *template)
 {
-       check_expected(str);
-       return mock_type(int);
+       return 10;
 }
 
 int __wrap_dm_get_uuid(const char *name, char *uuid, int uuid_len)
@@ -81,6 +89,50 @@ int __wrap_dm_get_uuid(const char *name, char *uuid, int uuid_len)
        return ret;
 }
 
+static int lock_errors;
+static int bindings_locked;
+static int timestamp_locked;
+int __wrap_pthread_mutex_lock(pthread_mutex_t *mutex)
+{
+       if (mutex == &bindings_mutex) {
+               if (bindings_locked) {
+                       fprintf(stderr, "%s: bindings_mutex LOCKED\n", __func__);
+                       lock_errors++;
+               }
+               bindings_locked = 1;
+       }  else if (mutex == &timestamp_mutex) {
+               if (timestamp_locked) {
+                       fprintf(stderr, "%s: timestamp_mutex LOCKED\n", __func__);
+                       lock_errors++;
+               }
+               timestamp_locked = 1;
+       } else
+                 fprintf(stderr, "%s called for unknown mutex %p\n", __func__, mutex);
+       return 0;
+}
+
+int __wrap_pthread_mutex_unlock(pthread_mutex_t *mutex)
+{
+       if (mutex == &bindings_mutex) {
+               if (!bindings_locked) {
+                       fprintf(stderr, "%s: bindings_mutex UNLOCKED\n", __func__);
+                       lock_errors++;
+               }
+               bindings_locked = 0;
+       }  else if (mutex == &timestamp_mutex) {
+               if (!timestamp_locked) {
+                       fprintf(stderr, "%s: timestamp_mutex UNLOCKED\n", __func__);
+                       lock_errors++;
+               }
+               timestamp_locked = 0;
+       } else
+                 fprintf(stderr, "%s called for unknown mutex %p\n", __func__, mutex);
+       return 0;
+}
+
+#define TEST_FDNO 1234
+#define TEST_FPTR ((FILE *) 0xaffe)
+
 /* strbuf wrapper for the old format_devname() */
 static int __format_devname(char *name, int id, size_t len, const char *prefix)
 {
@@ -384,41 +436,109 @@ static int test_scan_devname(void)
 
 static void mock_unused_alias(const char *alias)
 {
-       expect_string(__wrap_dm_map_present, str, alias);
-       will_return(__wrap_dm_map_present, 0);
+       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);
 }
 
 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"
+#define USED_STR(alias_str, wwid_str) wwid_str ": alias '" alias_str "' already taken, reselecting alias\n"
+#define NOMATCH_STR(alias_str) ("No matching alias [" alias_str "] in bindings file.\n")
+#define FOUND_STR(alias_str, wwid_str)                         \
+       "Found matching wwid [" wwid_str "] in bindings file."  \
+       " Setting alias to " alias_str "\n"
+#define FOUND_ALIAS_STR(alias_str, wwid_str)                           \
+       "Found matching alias [" alias_str "] in bindings file."        \
+       " Setting wwid to " wwid_str "\n"
+#define NOMATCH_WWID_STR(wwid_str) ("No matching wwid [" wwid_str "] in bindings file.\n")
+#define NEW_STR(alias_str, wwid_str) ("Created new binding [" alias_str "] for WWID [" wwid_str "]\n")
+#define EXISTING_STR(alias_str, wwid_str) ("Use existing binding [" alias_str "] for WWID [" wwid_str "]\n")
+#define ALLOC_STR(alias_str, wwid_str) ("Allocated existing binding [" alias_str "] for WWID [" wwid_str "]\n")
+#define BINDING_STR(alias_str, wwid_str) (alias_str " " wwid_str "\n")
+#define BOUND_STR(alias_str, wwid_str) ("alias "alias_str " already bound to wwid " wwid_str ", cannot reuse")
+#define ERR_STR(alias_str, wwid_str) ("ERROR: old alias [" alias_str "] for wwid [" wwid_str "] is used by other map\n")
+#define REUSE_STR(alias_str, wwid_str) ("alias " alias_str " already bound to wwid " wwid_str ", cannot reuse\n")
+#define NOMORE_STR "no more available user_friendly_names\n"
+
+#define mock_failed_alias(alias, wwid)                                 \
+       do {                                                            \
+               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);                     \
+       } while (0)
+
+#define mock_used_alias(alias, wwid)                                   \
+       do {                                                            \
+               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, USED_STR(alias, wwid));               \
+       } while(0)
+
+static void __mock_bindings_file(const char *content, bool conflict_ok)
+{
+       char *cnt __attribute__((cleanup(cleanup_charp))) = NULL;
+       char *token, *savep = NULL;
+       int i;
+       uintmax_t values[] = { BINDING_ADDED, BINDING_CONFLICT };
+
+       cnt = strdup(content);
+       assert_ptr_not_equal(cnt, NULL);
+
+       for (token = strtok_r(cnt, "\n", &savep), i = 0;
+            token && *token;
+            token = strtok_r(NULL, "\n", &savep), i++) {
+               char *alias, *wwid;
+               int rc;
+
+               if (read_binding(token, i + 1, &alias, &wwid)
+                   == READ_BINDING_SKIP)
+                       continue;
+
+               rc = add_binding(&global_bindings, alias, wwid);
+               assert_in_set(rc, values, conflict_ok ? 2 : 1);
+       }
+}
 
-static void mock_failed_alias(const char *alias, char *msg)
+static void mock_bindings_file(const char *content) {
+       return __mock_bindings_file(content, false);
+}
+
+static int teardown_bindings(void **state)
 {
-       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);
+       cleanup_bindings();
+       return 0;
 }
 
-static void mock_used_alias(const char *alias, char *msg)
+static int lookup_binding(FILE *dummy, const char *wwid, char **alias,
+                         const char *prefix, int check_if_taken)
 {
-       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);
+       const struct binding *bdg;
+       int id;
+
+       /*
+        * get_free_id() always checks if aliases are taken.
+        * Therefore if prefix is non-null, check_if_taken must be true.
+        */
+       assert_true(!prefix || check_if_taken);
+       *alias = NULL;
+       bdg = get_binding_for_wwid(&global_bindings, wwid);
+       if (bdg) {
+               *alias = strdup(bdg->alias);
+               return 0;
+       } else if (!prefix && check_if_taken)
+               return -1;
+
+       id = get_free_id(&global_bindings, prefix, wwid);
+       return id;
 }
 
 static void lb_empty(void **state)
@@ -426,8 +546,8 @@ static void lb_empty(void **state)
        int rc;
        char *alias;
 
-       will_return(__wrap_fgets, NULL);
-       expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+       mock_bindings_file("");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
        rc = lookup_binding(NULL, "WWID0", &alias, NULL, 0);
        assert_int_equal(rc, 1);
        assert_ptr_equal(alias, NULL);
@@ -438,9 +558,9 @@ static void lb_empty_unused(void **state)
        int rc;
        char *alias;
 
-       will_return(__wrap_fgets, NULL);
+       mock_bindings_file("");
        mock_unused_alias("MPATHa");
-       expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
        rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
        assert_int_equal(rc, 1);
        assert_ptr_equal(alias, NULL);
@@ -452,12 +572,11 @@ 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");
+       mock_bindings_file("");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+       mock_failed_alias("MPATHa", "WWID0");
        rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
-       assert_int_equal(rc, 2);
+       assert_int_equal(rc, 1);
        assert_ptr_equal(alias, NULL);
        free(alias);
 }
@@ -467,10 +586,10 @@ 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_bindings_file("");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+       mock_used_alias("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);
@@ -482,10 +601,10 @@ 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_bindings_file("");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+       mock_used_alias("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);
@@ -497,10 +616,9 @@ static void lb_match_a(void **state)
        int rc;
        char *alias;
 
-       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", 0);
+       mock_bindings_file("MPATHa WWID0\n");
+       expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
+       rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 1);
        assert_int_equal(rc, 0);
        assert_ptr_not_equal(alias, NULL);
        assert_string_equal(alias, "MPATHa");
@@ -512,10 +630,10 @@ static void lb_nomatch_a(void **state)
        int rc;
        char *alias;
 
-       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", 0);
+       mock_bindings_file("MPATHa WWID0\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
+       mock_unused_alias("MPATHb");
+       rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
        assert_int_equal(rc, 2);
        assert_ptr_equal(alias, NULL);
 }
@@ -525,9 +643,8 @@ 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");
+       mock_bindings_file("MPATHa WWID0\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
        rc = lookup_binding(NULL, "WWID1", &alias, NULL, 1);
        assert_int_equal(rc, -1);
        assert_ptr_equal(alias, NULL);
@@ -538,10 +655,9 @@ 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_bindings_file("MPATHa WWID0\n");
        mock_unused_alias("MPATHb");
-       expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
        rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
        assert_int_equal(rc, 2);
        assert_ptr_equal(alias, NULL);
@@ -552,29 +668,26 @@ 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");
+       mock_bindings_file("MPATHa WWID0\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
+       mock_used_alias("MPATHb", "WWID1");
+       mock_used_alias("MPATHc", "WWID1");
+       mock_used_alias("MPATHd", "WWID1");
+       mock_failed_alias("MPATHe", "WWID1");
        rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
-       assert_int_equal(rc, 6);
+       assert_int_equal(rc, 5);
        assert_ptr_equal(alias, NULL);
 }
 
-static void do_lb_match_c(void **state, int check_if_taken)
+static void do_lb_match_c(void **state)
 {
        int rc;
        char *alias;
 
-       will_return(__wrap_fgets, "MPATHa WWID0\n");
-       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", check_if_taken);
+       mock_bindings_file("MPATHa WWID0\n"
+                          "MPATHc WWID1");
+       expect_condlog(3, FOUND_STR("MPATHc", "WWID1"));
+       rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 1);
        assert_int_equal(rc, 0);
        assert_ptr_not_equal(alias, NULL);
        assert_string_equal(alias, "MPATHc");
@@ -583,12 +696,12 @@ static void do_lb_match_c(void **state, int check_if_taken)
 
 static void lb_match_c(void **state)
 {
-       do_lb_match_c(state, 0);
+       do_lb_match_c(state);
 }
 
 static void lb_match_c_check(void **state)
 {
-       do_lb_match_c(state, 1);
+       do_lb_match_c(state);
 }
 
 static void lb_nomatch_a_c(void **state)
@@ -596,11 +709,11 @@ static void lb_nomatch_a_c(void **state)
        int rc;
        char *alias;
 
-       will_return(__wrap_fgets, "MPATHa WWID0\n");
-       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", 0);
+       mock_bindings_file("MPATHa WWID0\n"
+                          "MPATHc WWID1");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       mock_unused_alias("MPATHb");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
        assert_int_equal(rc, 2);
        assert_ptr_equal(alias, NULL);
 }
@@ -610,11 +723,10 @@ 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_bindings_file("MPATHa WWID0\n"
+                          "MPATHd WWID1");
        mock_unused_alias("MPATHb");
-       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
        rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
        assert_int_equal(rc, 2);
        assert_ptr_equal(alias, NULL);
@@ -625,12 +737,11 @@ 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_bindings_file("MPATHa WWID0\n"
+                          "MPATHd WWID1");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       mock_used_alias("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);
@@ -641,13 +752,12 @@ 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_bindings_file("MPATHa WWID0\n"
+                          "MPATHd WWID1");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       mock_used_alias("MPATHb", "WWID2");
+       mock_used_alias("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);
@@ -658,14 +768,13 @@ 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_bindings_file("MPATHa WWID0\n"
+                          "MPATHd WWID1");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       mock_used_alias("MPATHb", "WWID2");
+       mock_used_alias("MPATHc", "WWID2");
+       mock_used_alias("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);
@@ -676,11 +785,11 @@ static void lb_nomatch_c_a(void **state)
        int rc;
        char *alias;
 
-       will_return(__wrap_fgets, "MPATHc WWID1\n");
-       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", 0);
+       mock_bindings_file("MPATHc WWID1\n"
+                          "MPATHa WWID0\n");
+       mock_unused_alias("MPATHb");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
        assert_int_equal(rc, 2);
        assert_ptr_equal(alias, NULL);
 }
@@ -690,12 +799,11 @@ 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_bindings_file("MPATHc WWID1\n"
+                          "MPATHa WWID0\n"
+                          "MPATHd WWID0\n");
        mock_unused_alias("MPATHb");
-       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
        rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
        assert_int_equal(rc, 2);
        assert_ptr_equal(alias, NULL);
@@ -706,13 +814,12 @@ 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_bindings_file("MPATHc WWID1\n"
+                          "MPATHa WWID0\n"
+                          "MPATHd WWID0\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       mock_used_alias("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);
@@ -723,12 +830,12 @@ static void lb_nomatch_a_b(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 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", 0);
+       mock_bindings_file("MPATHa WWID0\n"
+                          "MPATHz WWID26\n"
+                          "MPATHb WWID1\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       mock_unused_alias("MPATHc");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
        assert_int_equal(rc, 3);
        assert_ptr_equal(alias, NULL);
 }
@@ -738,14 +845,19 @@ static void lb_nomatch_a_b_bad(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");
-       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
-       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
-       assert_int_equal(rc, 3);
+       expect_condlog(1, "invalid line 3 in bindings file, missing WWID\n");
+       /*
+        * The broken line will be ignored when constructing the bindings vector.
+        * Thus in lookup_binding() MPATHb is never encountered,
+        * and MPATHb appears usable.
+        */
+       mock_bindings_file("MPATHa WWID0\n"
+                          "MPATHz WWID26\n"
+                          "MPATHb\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       mock_unused_alias("MPATHb");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+       assert_int_equal(rc, 2);
        assert_ptr_equal(alias, NULL);
 }
 
@@ -754,90 +866,156 @@ 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");
+       expect_condlog(1, "invalid line 3 in bindings file, missing WWID\n");
+       mock_bindings_file("MPATHa WWID0\n"
+                          "MPATHz WWID26\n"
+                          "MPATHb\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       mock_self_alias("MPATHb", "WWID2");
        rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
-       assert_int_equal(rc, 3);
+       assert_int_equal(rc, 2);
        assert_ptr_equal(alias, NULL);
 }
 
-static void lb_nomatch_b_a(void **state)
+static void lb_nomatch_b_z_a(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);
-       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
-       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
-       assert_int_equal(rc, 27);
+       mock_bindings_file("MPATHb WWID1\n"
+                          "MPATHz WWID26\n"
+                          "MPATHa WWID0\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       mock_unused_alias("MPATHc");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+       assert_int_equal(rc, 3);
        assert_ptr_equal(alias, NULL);
 }
 
-static void lb_nomatch_b_a_3_used(void **state)
+static void lb_nomatch_b_aa_a(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");
+       mock_bindings_file("MPATHb WWID1\n"
+                          "MPATHz WWID26\n"
+                          "MPATHa WWID0\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       mock_unused_alias("MPATHc");
        rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
-       assert_int_equal(rc, 30);
+       assert_int_equal(rc, 3);
        assert_ptr_equal(alias, NULL);
 }
 
-#ifdef MPATH_ID_INT_MAX
-static void do_lb_nomatch_int_max(void **state, int check_if_taken)
+static void fill_bindings(struct strbuf *buf, int start, int end)
+{
+       int i;
+
+       for (i = start; i <= end; i++) {
+               print_strbuf(buf,  "MPATH");
+               format_devname(buf, i + 1);
+               print_strbuf(buf,  " WWID%d\n", i);
+       }
+}
+
+static void lb_nomatch_b_a_aa(void **state)
 {
        int rc;
        char *alias;
+       STRBUF_ON_STACK(buf);
 
-       will_return(__wrap_fgets, "MPATHb WWID1\n");
-       will_return(__wrap_fgets, "MPATH" MPATH_ID_INT_MAX " WWIDMAX\n");
-       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", check_if_taken);
-       assert_int_equal(rc, -1);
+       fill_bindings(&buf, 0, 26);
+       mock_bindings_file(get_strbuf_str(&buf));
+       expect_condlog(3, NOMATCH_WWID_STR("WWID28"));
+       mock_unused_alias("MPATHab");
+       rc = lookup_binding(NULL, "WWID28", &alias, "MPATH", 1);
+       assert_int_equal(rc, 28);
        assert_ptr_equal(alias, NULL);
 }
 
-static void lb_nomatch_int_max(void **state)
+static void lb_nomatch_b_a_aa_zz(void **state)
 {
-       do_lb_nomatch_int_max(state, 0);
+       int rc;
+       char *alias;
+       STRBUF_ON_STACK(buf);
+
+       fill_bindings(&buf, 0, 26);
+       print_strbuf(&buf, "MPATHzz WWID676\n");
+       mock_bindings_file(get_strbuf_str(&buf));
+       expect_condlog(3, NOMATCH_WWID_STR("WWID703"));
+       mock_unused_alias("MPATHab");
+       rc = lookup_binding(NULL, "WWID703", &alias, "MPATH", 1);
+       assert_int_equal(rc, 28);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_b_a(void **state)
+{
+       int rc;
+       char *alias;
+
+       mock_bindings_file("MPATHb WWID1\n"
+                          "MPATHa WWID0\n");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID2"));
+       mock_unused_alias("MPATHc");
+       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 1);
+       assert_int_equal(rc, 3);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void lb_nomatch_b_a_3_used(void **state)
+{
+       int rc;
+       char *alias;
+       STRBUF_ON_STACK(buf);
+
+       fill_bindings(&buf, 0, 26);
+       mock_bindings_file(get_strbuf_str(&buf));
+       expect_condlog(3, NOMATCH_WWID_STR("WWID31"));
+       mock_used_alias("MPATHab", "WWID31");
+       mock_used_alias("MPATHac", "WWID31");
+       mock_used_alias("MPATHad", "WWID31");
+       mock_unused_alias("MPATHae");
+       rc = lookup_binding(NULL, "WWID31", &alias, "MPATH", 1);
+       assert_int_equal(rc, 31);
+       assert_ptr_equal(alias, NULL);
 }
 
-static void lb_nomatch_int_max_check(void **state)
+#ifdef MPATH_ID_INT_MAX
+/*
+ * The bindings will be sorted by alias. Therefore we have no chance to
+ * simulate a "full" table.
+ */
+static void lb_nomatch_int_max(void **state)
 {
-       do_lb_nomatch_int_max(state, 1);
+       int rc;
+       char *alias;
+       STRBUF_ON_STACK(buf);
+
+       fill_bindings(&buf, 0, 26);
+       print_strbuf(&buf, "MPATH%s WWIDMAX\n", MPATH_ID_INT_MAX);
+       mock_bindings_file(get_strbuf_str(&buf));
+       expect_condlog(3, NOMATCH_WWID_STR("WWIDNOMORE"));
+       mock_unused_alias("MPATHab");
+       rc = lookup_binding(NULL, "WWIDNOMORE", &alias, "MPATH", 1);
+       assert_int_equal(rc, 28);
+       assert_ptr_equal(alias, NULL);
 }
 
 static void lb_nomatch_int_max_used(void **state)
 {
        int rc;
        char *alias;
+       STRBUF_ON_STACK(buf);
 
-       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);
+       fill_bindings(&buf, 1, 26);
+       print_strbuf(&buf, "MPATH%s WWIDMAX\n", MPATH_ID_INT_MAX);
+       mock_bindings_file(get_strbuf_str(&buf));
+       expect_condlog(3, NOMATCH_WWID_STR("WWIDNOMORE"));
+       mock_used_alias("MPATHa", "WWIDNOMORE");
+       mock_unused_alias("MPATHab");
+       rc = lookup_binding(NULL, "WWIDNOMORE", &alias, "MPATH", 1);
+       assert_int_equal(rc, 28);
        assert_ptr_equal(alias, NULL);
 }
 
@@ -845,14 +1023,15 @@ static void lb_nomatch_int_max_m1(void **state)
 {
        int rc;
        char *alias;
+       STRBUF_ON_STACK(buf);
 
-       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);
-       expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n");
-       rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0);
-       assert_int_equal(rc, INT_MAX);
+       fill_bindings(&buf, 0, 26);
+       print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
+       mock_bindings_file(get_strbuf_str(&buf));
+       expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
+       mock_unused_alias("MPATHab");
+       rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
+       assert_int_equal(rc, 28);
        assert_ptr_equal(alias, NULL);
 }
 
@@ -860,15 +1039,16 @@ static void lb_nomatch_int_max_m1_used(void **state)
 {
        int rc;
        char *alias;
+       STRBUF_ON_STACK(buf);
 
-       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);
+       fill_bindings(&buf, 0, 26);
+       print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
+       mock_bindings_file(get_strbuf_str(&buf));
+       expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
+       mock_used_alias("MPATHab", "WWIDMAX");
+       mock_unused_alias("MPATHac");
+       rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
+       assert_int_equal(rc, 29);
        assert_ptr_equal(alias, NULL);
 }
 
@@ -876,15 +1056,16 @@ static void lb_nomatch_int_max_m1_1_used(void **state)
 {
        int rc;
        char *alias;
+       STRBUF_ON_STACK(buf);
 
-       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);
+       fill_bindings(&buf, 1, 26);
+       print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
+       mock_bindings_file(get_strbuf_str(&buf));
+       expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
+       mock_used_alias("MPATHa", "WWIDMAX");
+       mock_unused_alias("MPATHab");
+       rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
+       assert_int_equal(rc, 28);
        assert_ptr_equal(alias, NULL);
 }
 
@@ -892,15 +1073,18 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
 {
        int rc;
        char *alias;
+       STRBUF_ON_STACK(buf);
 
-       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);
+       fill_bindings(&buf, 1, 26);
+       print_strbuf(&buf, "MPATH%s WWIDMAXM1\n", MPATH_ID_INT_MAX_m1);
+       mock_bindings_file(get_strbuf_str(&buf));
+
+       expect_condlog(3, NOMATCH_WWID_STR("WWIDMAX"));
+       mock_used_alias("MPATHa", "WWIDMAX");
+       mock_used_alias("MPATHab", "WWIDMAX");
+       mock_unused_alias("MPATHac");
+       rc = lookup_binding(NULL, "WWIDMAX", &alias, "MPATH", 1);
+       assert_int_equal(rc, 29);
        assert_ptr_equal(alias, NULL);
 }
 #endif
@@ -908,53 +1092,68 @@ static void lb_nomatch_int_max_m1_2_used(void **state)
 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),
+               cmocka_unit_test_teardown(lb_empty, teardown_bindings),
+               cmocka_unit_test_teardown(lb_empty_unused, teardown_bindings),
+               cmocka_unit_test_teardown(lb_empty_failed, teardown_bindings),
+               cmocka_unit_test_teardown(lb_empty_1_used, teardown_bindings),
+               cmocka_unit_test_teardown(lb_empty_1_used_self, teardown_bindings),
+               cmocka_unit_test_teardown(lb_match_a, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a_bad_check, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a_unused, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a_3_used_failed_self, teardown_bindings),
+               cmocka_unit_test_teardown(lb_match_c, teardown_bindings),
+               cmocka_unit_test_teardown(lb_match_c_check, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a_c, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a_d_unused, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a_d_1_used, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a_d_2_used, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a_d_3_used, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_c_a, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_d_a_unused, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_d_a_1_used, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a_b, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a_b_bad, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_a_b_bad_self, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_b_z_a, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_b_aa_a, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_b_a_aa, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_b_a_aa_zz, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_b_a, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_b_a_3_used, teardown_bindings),
 #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),
+               cmocka_unit_test_teardown(lb_nomatch_int_max, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_int_max_used, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_int_max_m1, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_int_max_m1_used, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_int_max_m1_1_used, teardown_bindings),
+               cmocka_unit_test_teardown(lb_nomatch_int_max_m1_2_used, teardown_bindings),
 #endif
        };
 
        return cmocka_run_group_tests(tests, NULL, NULL);
 }
 
+static int rlookup_binding(FILE *dummy, char *buf, const char *alias) {
+
+       const struct binding *bdg;
+
+       bdg = get_binding_for_alias(&global_bindings, alias);
+       if (!bdg) {
+               return -1;
+       }
+       strlcpy(buf, bdg->wwid, WWID_SIZE);
+       return 0;
+}
+
 static void rl_empty(void **state)
 {
        int rc;
        char buf[WWID_SIZE];
 
        buf[0] = '\0';
-       will_return(__wrap_fgets, NULL);
-       expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
+       mock_bindings_file("");
+       expect_condlog(3, NOMATCH_STR("MPATHa"));
        rc = rlookup_binding(NULL, buf, "MPATHa");
        assert_int_equal(rc, -1);
        assert_string_equal(buf, "");
@@ -966,9 +1165,8 @@ static void rl_match_a(void **state)
        char buf[WWID_SIZE];
 
        buf[0] = '\0';
-       will_return(__wrap_fgets, "MPATHa WWID0\n");
-       expect_condlog(3, "Found matching alias [MPATHa] in bindings file. "
-                      "Setting wwid to WWID0\n");
+       mock_bindings_file("MPATHa WWID0\n");
+       expect_condlog(3, FOUND_ALIAS_STR("MPATHa", "WWID0"));
        rc = rlookup_binding(NULL, buf, "MPATHa");
        assert_int_equal(rc, 0);
        assert_string_equal(buf, "WWID0");
@@ -980,9 +1178,8 @@ static void rl_nomatch_a(void **state)
        char buf[WWID_SIZE];
 
        buf[0] = '\0';
-       will_return(__wrap_fgets, "MPATHa WWID0\n");
-       will_return(__wrap_fgets, NULL);
-       expect_condlog(3, "No matching alias [MPATHb] in bindings file.\n");
+       mock_bindings_file("MPATHa WWID0\n");
+       expect_condlog(3, NOMATCH_STR("MPATHb"));
        rc = rlookup_binding(NULL, buf, "MPATHb");
        assert_int_equal(rc, -1);
        assert_string_equal(buf, "");
@@ -994,10 +1191,9 @@ static void rl_malformed_a(void **state)
        char buf[WWID_SIZE];
 
        buf[0] = '\0';
-       will_return(__wrap_fgets, "MPATHa     \n");
-       will_return(__wrap_fgets, NULL);
-       expect_condlog(3, "Ignoring malformed line 1 in bindings file\n");
-       expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
+       expect_condlog(1, "invalid line 1 in bindings file, missing WWID\n");
+       mock_bindings_file("MPATHa     \n");
+       expect_condlog(3, NOMATCH_STR("MPATHa"));
        rc = rlookup_binding(NULL, buf, "MPATHa");
        assert_int_equal(rc, -1);
        assert_string_equal(buf, "");
@@ -1014,10 +1210,9 @@ static void rl_overlong_a(void **state)
        snprintf(line + sizeof(line) - 2, 2, "\n");
 
        buf[0] = '\0';
-       will_return(__wrap_fgets, line);
-       will_return(__wrap_fgets, NULL);
        expect_condlog(3, "Ignoring too large wwid at 1 in bindings file\n");
-       expect_condlog(3, "No matching alias [MPATHa] in bindings file.\n");
+       mock_bindings_file(line);
+       expect_condlog(3, NOMATCH_STR("MPATHa"));
        rc = rlookup_binding(NULL, buf, "MPATHa");
        assert_int_equal(rc, -1);
        assert_string_equal(buf, "");
@@ -1029,11 +1224,10 @@ static void rl_match_b(void **state)
        char buf[WWID_SIZE];
 
        buf[0] = '\0';
-       will_return(__wrap_fgets, "MPATHa WWID0\n");
-       will_return(__wrap_fgets, "MPATHz WWID26\n");
-       will_return(__wrap_fgets, "MPATHb WWID2\n");
-       expect_condlog(3, "Found matching alias [MPATHb] in bindings file. "
-                      "Setting wwid to WWID2\n");
+       mock_bindings_file("MPATHa WWID0\n"
+                          "MPATHz WWID26\n"
+                          "MPATHb WWID2\n");
+       expect_condlog(3, FOUND_ALIAS_STR("MPATHb", "WWID2"));
        rc = rlookup_binding(NULL, buf, "MPATHb");
        assert_int_equal(rc, 0);
        assert_string_equal(buf, "WWID2");
@@ -1042,31 +1236,41 @@ static void rl_match_b(void **state)
 static int test_rlookup_binding(void)
 {
        const struct CMUnitTest tests[] = {
-               cmocka_unit_test(rl_empty),
-               cmocka_unit_test(rl_match_a),
-               cmocka_unit_test(rl_nomatch_a),
-               cmocka_unit_test(rl_malformed_a),
-               cmocka_unit_test(rl_overlong_a),
-               cmocka_unit_test(rl_match_b),
+               cmocka_unit_test_teardown(rl_empty, teardown_bindings),
+               cmocka_unit_test_teardown(rl_match_a, teardown_bindings),
+               cmocka_unit_test_teardown(rl_nomatch_a, teardown_bindings),
+               cmocka_unit_test_teardown(rl_malformed_a, teardown_bindings),
+               cmocka_unit_test_teardown(rl_overlong_a, teardown_bindings),
+               cmocka_unit_test_teardown(rl_match_b, teardown_bindings),
        };
 
        return cmocka_run_group_tests(tests, NULL, NULL);
 }
 
+void check_bindings_size(int n)
+{
+       /* avoid -Waddress problem */
+       Bindings *bindings = &global_bindings;
+
+       assert_int_equal(VECTOR_SIZE(bindings), n);
+}
+
 static void al_a(void **state)
 {
        static const char ln[] = "MPATHa WWIDa\n";
        char *alias;
 
-       will_return(__wrap_lseek, 0);
-       expect_value(__wrap_write, count, strlen(ln));
-       expect_string(__wrap_write, buf, ln);
-       will_return(__wrap_write, strlen(ln));
-       expect_condlog(3, "Created new binding [MPATHa] for WWID [WWIDa]\n");
+       expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+       will_return(__wrap_write, ln);
+       will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+       will_return(__wrap_rename, 0);
+       expect_condlog(1, "updated bindings file " DEFAULT_BINDINGS_FILE);
+       expect_condlog(3, NEW_STR("MPATHa", "WWIDa"));
 
-       alias = allocate_binding(0, "WWIDa", 1, "MPATH");
+       alias = allocate_binding("WWIDa", 1, "MPATH");
        assert_ptr_not_equal(alias, NULL);
        assert_string_equal(alias, "MPATHa");
+       check_bindings_size(1);
        free(alias);
 }
 
@@ -1075,15 +1279,17 @@ static void al_zz(void **state)
        static const char ln[] = "MPATHzz WWIDzz\n";
        char *alias;
 
-       will_return(__wrap_lseek, 0);
-       expect_value(__wrap_write, count, strlen(ln));
-       expect_string(__wrap_write, buf, ln);
-       will_return(__wrap_write, strlen(ln));
-       expect_condlog(3, "Created new binding [MPATHzz] for WWID [WWIDzz]\n");
+       expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+       will_return(__wrap_write, ln);
+       will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+       will_return(__wrap_rename, 0);
+       expect_condlog(1, "updated bindings file " DEFAULT_BINDINGS_FILE);
+       expect_condlog(3, NEW_STR("MPATHzz", "WWIDzz"));
 
-       alias = allocate_binding(0, "WWIDzz", 26*26 + 26, "MPATH");
+       alias = allocate_binding("WWIDzz", 26*26 + 26, "MPATH");
        assert_ptr_not_equal(alias, NULL);
        assert_string_equal(alias, "MPATHzz");
+       check_bindings_size(1);
        free(alias);
 }
 
@@ -1092,8 +1298,9 @@ static void al_0(void **state)
        char *alias;
 
        expect_condlog(0, "allocate_binding: cannot allocate new binding for id 0\n");
-       alias = allocate_binding(0, "WWIDa", 0, "MPATH");
+       alias = allocate_binding("WWIDa", 0, "MPATH");
        assert_ptr_equal(alias, NULL);
+       check_bindings_size(0);
 }
 
 static void al_m2(void **state)
@@ -1101,47 +1308,688 @@ static void al_m2(void **state)
        char *alias;
 
        expect_condlog(0, "allocate_binding: cannot allocate new binding for id -2\n");
-       alias = allocate_binding(0, "WWIDa", -2, "MPATH");
+       alias = allocate_binding("WWIDa", -2, "MPATH");
        assert_ptr_equal(alias, NULL);
+       check_bindings_size(0);
 }
 
-static void al_lseek_err(void **state)
+static void al_write_partial(void **state)
 {
+       static const char ln[] = "MPATHa WWIDa\n";
        char *alias;
 
-       will_return(__wrap_lseek, -ENODEV);
-       expect_condlog(0, "Cannot seek to end of bindings file : No such device\n");
-       alias = allocate_binding(0, "WWIDa", 1, "MPATH");
+       expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+       will_return(__wrap_write, ln);
+       will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln) - 1);
+       expect_value(__wrap_write, count, 1);
+       will_return(__wrap_write, ln + sizeof(ln) - 2);
+       will_return(__wrap_write, 1);
+       will_return(__wrap_rename, 0);
+       expect_condlog(1, "updated bindings file " DEFAULT_BINDINGS_FILE);
+       expect_condlog(3, "Created new binding [MPATHa] for WWID [WWIDa]\n");
+
+       alias = allocate_binding("WWIDa", 1, "MPATH");
+       assert_ptr_not_equal(alias, NULL);
+       assert_string_equal(alias, "MPATHa");
+       check_bindings_size(1);
+       free(alias);
+}
+
+static void al_write_short(void **state)
+{
+       static const char ln[] = "MPATHa WWIDa\n";
+       char *alias;
+
+       expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+       will_return(__wrap_write, ln);
+       will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln) - 1);
+       expect_value(__wrap_write, count, 1);
+       will_return(__wrap_write, ln + sizeof(ln) - 2);
+       will_return(__wrap_write, 0);
+       expect_condlog(2, "write_bindings_file: short write");
+       expect_condlog(1, "failed to write new bindings file");
+       expect_condlog(1, "allocate_binding: deleting binding MPATHa for WWIDa");
+
+       alias = allocate_binding("WWIDa", 1, "MPATH");
        assert_ptr_equal(alias, NULL);
+       check_bindings_size(0);
 }
 
 static void al_write_err(void **state)
 {
        static const char ln[] = "MPATHa WWIDa\n";
-       const int offset = 20;
        char *alias;
 
-       will_return(__wrap_lseek, offset);
-       expect_value(__wrap_write, count, strlen(ln));
-       expect_string(__wrap_write, buf, ln);
-       will_return(__wrap_write, strlen(ln) - 1);
-       expect_value(__wrap_ftruncate, length, offset);
-       will_return(__wrap_ftruncate, 0);
-       expect_condlog(0, "Cannot write binding to bindings file :");
+       expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+       will_return(__wrap_write, ln);
+       will_return(__wrap_write, -EPERM);
+       expect_condlog(1, "failed to write new bindings file");
+       expect_condlog(1, "allocate_binding: deleting binding MPATHa for WWIDa");
+
+       alias = allocate_binding("WWIDa", 1, "MPATH");
+       assert_ptr_equal(alias, NULL);
+       check_bindings_size(0);
+}
+
+static void al_rename_err(void **state)
+{
+       static const char ln[] = "MPATHa WWIDa\n";
+       char *alias;
+
+       expect_value(__wrap_write, count, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+       will_return(__wrap_write, ln);
+       will_return(__wrap_write, strlen(BINDINGS_FILE_HEADER) + strlen(ln));
+       will_return(__wrap_rename, -EROFS);
 
-       alias = allocate_binding(0, "WWIDa", 1, "MPATH");
+       expect_condlog(0, "update_bindings_file: rename: Read-only file system");
+       expect_condlog(1, "allocate_binding: deleting binding MPATHa for WWIDa");
+       alias = allocate_binding("WWIDa", 1, "MPATH");
        assert_ptr_equal(alias, NULL);
+       check_bindings_size(0);
 }
 
 static int test_allocate_binding(void)
 {
        const struct CMUnitTest tests[] = {
-               cmocka_unit_test(al_a),
-               cmocka_unit_test(al_zz),
-               cmocka_unit_test(al_0),
-               cmocka_unit_test(al_m2),
-               cmocka_unit_test(al_lseek_err),
-               cmocka_unit_test(al_write_err),
+               cmocka_unit_test_teardown(al_a, teardown_bindings),
+               cmocka_unit_test_teardown(al_zz, teardown_bindings),
+               cmocka_unit_test_teardown(al_0, teardown_bindings),
+               cmocka_unit_test_teardown(al_m2, teardown_bindings),
+               cmocka_unit_test_teardown(al_write_partial, teardown_bindings),
+               cmocka_unit_test_teardown(al_write_short, teardown_bindings),
+               cmocka_unit_test_teardown(al_write_err, teardown_bindings),
+               cmocka_unit_test_teardown(al_rename_err, teardown_bindings),
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+#define mock_allocate_binding_err_len(alias, wwid, len, err, msg)      \
+       do {                                                            \
+               static const char ln[] = BINDING_STR(alias, wwid);      \
+                                                                       \
+               expect_value(__wrap_write, count,                       \
+                            strlen(BINDINGS_FILE_HEADER) + (len) + strlen(ln)); \
+               will_return(__wrap_write, ln);                          \
+               will_return(__wrap_write,                               \
+                           strlen(BINDINGS_FILE_HEADER) + (len) + strlen(ln)); \
+               will_return(__wrap_rename, err);                        \
+               if (err == 0) {                                         \
+                       expect_condlog(1, "updated bindings file " DEFAULT_BINDINGS_FILE);      \
+                       expect_condlog(3, NEW_STR(alias, wwid));        \
+               } else {                                                \
+                       expect_condlog(0, "update_bindings_file: rename: " msg "\n"); \
+                       expect_condlog(1, "allocate_binding: deleting binding " \
+                                      alias " for " wwid "\n");        \
+               }                                                       \
+       } while (0)
+
+#define mock_allocate_binding_err(alias, wwid, err, msg)       \
+       mock_allocate_binding_err_len(alias, wwid, 0, err, msg)
+
+#define mock_allocate_binding(alias, wwid)                     \
+       mock_allocate_binding_err(alias, wwid, 0, "")
+
+#define mock_allocate_binding_len(alias, wwid, len)                    \
+       mock_allocate_binding_err_len(alias, wwid, len, 0, "")
+
+static void gufa_empty_new_rw(void **state) {
+       char *alias;
+
+       mock_bindings_file("");
+       mock_unused_alias("MPATHa");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+
+       mock_allocate_binding("MPATHa", "WWID0");
+       alias = get_user_friendly_alias("WWID0", "", "MPATH", false);
+       assert_string_equal(alias, "MPATHa");
+       free(alias);
+}
+
+static void gufa_empty_new_ro_1(void **state) {
+       char *alias;
+
+       mock_bindings_file("");
+       mock_unused_alias("MPATHa");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+       mock_allocate_binding_err("MPATHa", "WWID0", -EROFS, "Read-only file system");
+
+       alias = get_user_friendly_alias("WWID0", "", "MPATH", false);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_empty_new_ro_2(void **state) {
+       char *alias;
+
+       mock_bindings_file("");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+       mock_unused_alias("MPATHa");
+
+       alias = get_user_friendly_alias("WWID0", "", "MPATH", true);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_match_a_unused(void **state) {
+       char *alias;
+
+       mock_bindings_file("MPATHa WWID0");
+       expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
+       mock_unused_alias("MPATHa");
+       expect_condlog(3, EXISTING_STR("MPATHa", "WWID0"));
+
+       alias = get_user_friendly_alias("WWID0", "", "MPATH", true);
+       assert_string_equal(alias, "MPATHa");
+       free(alias);
+}
+
+static void gufa_match_a_self(void **state) {
+       char *alias;
+
+       mock_bindings_file("MPATHa WWID0");
+       expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
+       mock_self_alias("MPATHa", "WWID0");
+       expect_condlog(3, EXISTING_STR("MPATHa", "WWID0"));
+
+       alias = get_user_friendly_alias("WWID0", "", "MPATH", true);
+       assert_string_equal(alias, "MPATHa");
+       free(alias);
+}
+
+static void gufa_match_a_used(void **state) {
+       char *alias;
+
+
+       mock_bindings_file("MPATHa WWID0");
+       expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
+       mock_used_alias("MPATHa", "WWID0");
+
+       alias = get_user_friendly_alias("WWID0", "", "MPATH", true);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_nomatch_a_c(void **state) {
+       char *alias;
+       static const char bindings[] = ("MPATHa WWID0\n"
+                                       "MPATHc WWID2\n");
+
+       mock_bindings_file(bindings);
+       mock_unused_alias("MPATHb");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
+
+       mock_allocate_binding_len("MPATHb", "WWID1", strlen(bindings));
+
+       alias = get_user_friendly_alias("WWID1", "", "MPATH", false);
+       assert_string_equal(alias, "MPATHb");
+       free(alias);
+}
+
+static void gufa_nomatch_c_a(void **state) {
+       char *alias;
+       const char bindings[] = ("MPATHc WWID2\n"
+                                "MPATHa WWID0\n");
+
+       mock_bindings_file(bindings);
+       mock_unused_alias("MPATHb");
+       expect_condlog(3, NOMATCH_WWID_STR("WWID1"));
+
+       mock_allocate_binding_len("MPATHb", "WWID1", sizeof(bindings) - 1);
+
+       alias = get_user_friendly_alias("WWID1", "", "MPATH", false);
+       assert_string_equal(alias, "MPATHb");
+       free(alias);
+}
+
+static void gufa_nomatch_c_b(void **state) {
+       char *alias;
+       const char bindings[] = ("MPATHc WWID2\n"
+                                "MPATHb WWID1\n");
+
+       mock_bindings_file(bindings);
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+       mock_unused_alias("MPATHa");
+
+       mock_allocate_binding_len("MPATHa", "WWID0", sizeof(bindings) - 1);
+
+       alias = get_user_friendly_alias("WWID0", "", "MPATH", false);
+       assert_string_equal(alias, "MPATHa");
+       free(alias);
+}
+
+static void gufa_nomatch_c_b_used(void **state) {
+       char *alias;
+       const char bindings[] = ("MPATHc WWID2\n"
+                                "MPATHb WWID1\n");
+
+       mock_bindings_file(bindings);
+       expect_condlog(3, NOMATCH_WWID_STR("WWID4"));
+       mock_used_alias("MPATHa", "WWID4");
+       mock_unused_alias("MPATHd");
+
+       mock_allocate_binding_len("MPATHd", "WWID4", sizeof(bindings) - 1);
+
+       alias = get_user_friendly_alias("WWID4", "", "MPATH", false);
+       assert_string_equal(alias, "MPATHd");
+       free(alias);
+}
+
+static void gufa_nomatch_b_f_a(void **state) {
+       char *alias;
+       const char bindings[] = ("MPATHb WWID1\n"
+                                "MPATHf WWID6\n"
+                                "MPATHa WWID0\n");
+
+       mock_bindings_file(bindings);
+       expect_condlog(3, NOMATCH_WWID_STR("WWID7"));
+       mock_unused_alias("MPATHc");
+
+       mock_allocate_binding_len("MPATHc", "WWID7", sizeof(bindings) - 1);
+
+       alias = get_user_friendly_alias("WWID7", "", "MPATH", false);
+       assert_string_equal(alias, "MPATHc");
+       free(alias);
+}
+
+static void gufa_nomatch_b_aa_a(void **state) {
+       char *alias;
+       STRBUF_ON_STACK(buf);
+
+       fill_bindings(&buf, 0, 26);
+       mock_bindings_file(get_strbuf_str(&buf));
+       expect_condlog(3, NOMATCH_WWID_STR("WWID28"));
+       mock_unused_alias("MPATHab");
+       mock_allocate_binding_len("MPATHab", "WWID28", get_strbuf_len(&buf));
+
+       alias = get_user_friendly_alias("WWID28", "", "MPATH", false);
+       assert_string_equal(alias, "MPATHab");
+       free(alias);
+}
+
+static void gufa_nomatch_b_f_a_sorted(void **state) {
+       char *alias;
+       const char bindings[] = ("MPATHb WWID1\n"
+                                "MPATHf WWID6\n"
+                                "MPATHa WWID0\n");
+
+       mock_bindings_file(bindings);
+       expect_condlog(3, NOMATCH_WWID_STR("WWID7"));
+       mock_unused_alias("MPATHc");
+
+       mock_allocate_binding_len("MPATHc", "WWID7", sizeof(bindings) - 1);
+
+       alias = get_user_friendly_alias("WWID7", "", "MPATH", false);
+       assert_string_equal(alias, "MPATHc");
+       free(alias);
+}
+
+static void gufa_old_empty(void **state) {
+       char *alias;
+
+       /* rlookup_binding for ALIAS */
+       mock_bindings_file("");
+       expect_condlog(3, NOMATCH_STR("MPATHz"));
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+
+       mock_allocate_binding("MPATHz", "WWID0");
+       expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
+
+       alias = get_user_friendly_alias("WWID0", "MPATHz", "MPATH", false);
+       assert_string_equal(alias, "MPATHz");
+       free(alias);
+}
+
+static void gufa_old_match(void **state) {
+       char *alias;
+
+       mock_bindings_file("MPATHb WWID1\n"
+                          "MPATHz WWID0");
+       expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID0"));
+
+       alias = get_user_friendly_alias("WWID0", "MPATHz", "MPATH", false);
+       assert_string_equal(alias, "MPATHz");
+       free(alias);
+}
+
+static void gufa_old_match_other(void **state) {
+       char *alias;
+       static const char bindings[] = "MPATHz WWID9\n";
+
+       mock_bindings_file(bindings);
+       expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
+       expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+       mock_unused_alias("MPATHa");
+
+       mock_allocate_binding_len("MPATHa", "WWID0", sizeof(bindings) - 1);
+
+       alias = get_user_friendly_alias("WWID0", "MPATHz", "MPATH", false);
+       assert_string_equal(alias, "MPATHa");
+       free(alias);
+}
+
+static void gufa_old_match_other_used(void **state) {
+       char *alias;
+       static const char bindings[] = "MPATHz WWID9\n";
+
+       mock_bindings_file(bindings);
+       expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
+       expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+       mock_used_alias("MPATHa", "WWID0");
+       mock_unused_alias("MPATHb");
+
+       mock_allocate_binding_len("MPATHb", "WWID0", sizeof(bindings) - 1);
+       alias = get_user_friendly_alias("WWID0", "MPATHz", "MPATH", false);
+       assert_string_equal(alias, "MPATHb");
+       free(alias);
+}
+
+static void gufa_old_match_other_wwidmatch(void **state) {
+       char *alias;
+       static const char bindings[] = ("MPATHz WWID9\n"
+                                       "MPATHc WWID2");
+
+       mock_bindings_file(bindings);
+       expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
+       expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
+       expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
+       mock_unused_alias("MPATHc");
+       expect_condlog(3, EXISTING_STR("MPATHc", "WWID2"));
+
+       alias = get_user_friendly_alias("WWID2", "MPATHz", "MPATH", false);
+       assert_string_equal(alias, "MPATHc");
+       free(alias);
+}
+
+static void gufa_old_match_other_wwidmatch_used(void **state) {
+       char *alias;
+       static const char bindings[] = ("MPATHz WWID9\n"
+                                       "MPATHc WWID2");
+
+       mock_bindings_file(bindings);
+       expect_condlog(3, FOUND_ALIAS_STR("MPATHz", "WWID9"));
+       expect_condlog(0, REUSE_STR("MPATHz", "WWID9"));
+       expect_condlog(3, FOUND_STR("MPATHc", "WWID2"));
+       mock_used_alias("MPATHc", "WWID2");
+
+       alias = get_user_friendly_alias("WWID2", "MPATHz", "MPATH", false);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_old_nomatch_wwidmatch(void **state) {
+       char *alias;
+       static const char bindings[] = "MPATHa WWID0";
+
+       mock_bindings_file(bindings);
+       expect_condlog(3, NOMATCH_STR("MPATHz"));
+       expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
+       mock_unused_alias("MPATHa");
+       expect_condlog(3, EXISTING_STR("MPATHa", "WWID0"));
+
+       alias = get_user_friendly_alias("WWID0", "MPATHz", "MPATH", false);
+       assert_string_equal(alias, "MPATHa");
+       free(alias);
+}
+
+static void gufa_old_nomatch_wwidmatch_used(void **state) {
+       char *alias;
+       static const char bindings[] = "MPATHa WWID0";
+
+       mock_bindings_file(bindings);
+       expect_condlog(3, NOMATCH_STR("MPATHz"));
+       expect_condlog(3, FOUND_STR("MPATHa", "WWID0"));
+       mock_used_alias("MPATHa", "WWID0");
+
+       alias = get_user_friendly_alias("WWID0", "MPATHz", "MPATH", false);
+       assert_ptr_equal(alias, NULL);
+}
+
+static void gufa_old_nomatch_nowwidmatch(void **state) {
+       char *alias;
+       static const char bindings[] = "MPATHb WWID1\n";
+
+       mock_bindings_file(bindings);
+       expect_condlog(3, NOMATCH_STR("MPATHz"));
+       expect_condlog(3, NOMATCH_WWID_STR("WWID0"));
+
+       mock_allocate_binding_len("MPATHz", "WWID0", sizeof(bindings) - 1);
+       expect_condlog(2, ALLOC_STR("MPATHz", "WWID0"));
+
+       alias = get_user_friendly_alias("WWID0", "MPATHz", "MPATH", false);
+       assert_string_equal(alias, "MPATHz");
+       free(alias);
+}
+
+static void gufa_check_locking(void **state) {
+       assert_int_equal(lock_errors, 0);
+}
+
+static int test_get_user_friendly_alias()
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test_teardown(gufa_empty_new_rw, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_empty_new_ro_1, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_empty_new_ro_2, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_match_a_unused, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_match_a_self, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_match_a_used, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_nomatch_a_c, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_nomatch_c_a, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_nomatch_c_b, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_nomatch_c_b_used, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_nomatch_b_f_a, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_nomatch_b_aa_a, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_nomatch_b_f_a_sorted, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_old_empty, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_old_match, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_old_match_other, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_old_match_other_used, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_old_match_other_wwidmatch, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_old_match_other_wwidmatch_used, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_old_nomatch_wwidmatch, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_old_nomatch_wwidmatch_used, teardown_bindings),
+               cmocka_unit_test_teardown(gufa_old_nomatch_nowwidmatch, teardown_bindings),
+               cmocka_unit_test(gufa_check_locking),
+       };
+
+       return cmocka_run_group_tests(tests, NULL, NULL);
+}
+
+/* Numbers 1-1000, randomly shuffled */
+static const int random_numbers[1000] = {
+       694, 977, 224, 178, 841, 818, 914, 549, 831, 942, 263, 834, 919, 800,
+       111, 517, 719, 297, 988, 98, 332, 516, 754, 772, 495, 488, 331, 529,
+       142, 747, 848, 618, 375, 624, 74, 753, 782, 944, 623, 468, 862, 997,
+       417, 258, 298, 774, 673, 904, 883, 766, 867, 400, 11, 950, 14, 784,
+       655, 155, 396, 9, 743, 93, 651, 245, 968, 306, 785, 581, 880, 486,
+       168, 631, 203, 4, 663, 294, 702, 762, 619, 684, 48, 181, 21, 443, 643,
+       863, 1000, 327, 26, 126, 382, 765, 586, 76, 49, 925, 319, 865, 797,
+       876, 693, 334, 433, 243, 419, 901, 854, 326, 985, 347, 874, 527, 282,
+       290, 380, 167, 95, 3, 257, 936, 60, 426, 227, 345, 577, 492, 467, 580,
+       967, 422, 823, 718, 610, 64, 700, 412, 163, 288, 506, 828, 432, 51,
+       356, 348, 539, 478, 17, 945, 602, 123, 450, 660, 429, 113, 310, 358,
+       512, 758, 508, 19, 542, 304, 286, 446, 918, 723, 333, 603, 731, 978,
+       230, 697, 109, 872, 175, 853, 947, 965, 121, 222, 101, 811, 117, 601,
+       191, 752, 384, 415, 938, 278, 915, 715, 240, 552, 912, 838, 150, 840,
+       627, 29, 636, 464, 861, 481, 992, 249, 934, 82, 368, 724, 807, 593,
+       157, 147, 199, 637, 41, 62, 902, 505, 621, 342, 174, 260, 729, 961,
+       219, 311, 629, 789, 81, 739, 860, 712, 223, 165, 741, 981, 485, 363,
+       346, 709, 125, 369, 279, 634, 399, 162, 193, 769, 149, 314, 868, 612,
+       524, 675, 341, 343, 476, 606, 388, 613, 850, 264, 903, 451, 908, 779,
+       453, 148, 497, 46, 132, 43, 885, 955, 269, 395, 72, 128, 767, 989,
+       929, 423, 742, 55, 13, 79, 924, 182, 295, 563, 668, 169, 974, 154,
+       970, 54, 674, 52, 437, 570, 550, 531, 554, 793, 678, 218, 367, 105,
+       197, 315, 958, 892, 86, 47, 284, 37, 561, 522, 198, 689, 817, 573,
+       877, 201, 803, 501, 881, 546, 530, 523, 780, 579, 953, 135, 23, 620,
+       84, 698, 303, 656, 357, 323, 494, 58, 131, 913, 995, 120, 70, 1, 195,
+       365, 210, 25, 898, 173, 307, 239, 77, 418, 952, 963, 92, 455, 425, 12,
+       536, 161, 328, 933, 401, 251, 735, 725, 362, 322, 557, 681, 302, 53,
+       786, 801, 391, 946, 748, 133, 717, 851, 7, 372, 993, 387, 906, 373,
+       667, 33, 670, 389, 209, 611, 896, 652, 69, 999, 344, 845, 633, 36,
+       487, 192, 180, 45, 640, 427, 707, 805, 188, 152, 905, 217, 30, 252,
+       386, 665, 299, 541, 410, 787, 5, 857, 751, 392, 44, 595, 146, 745,
+       641, 957, 866, 773, 806, 815, 659, 102, 704, 430, 106, 296, 129, 847,
+       130, 990, 669, 236, 225, 680, 159, 213, 438, 189, 447, 600, 232, 594,
+       32, 56, 390, 647, 855, 428, 330, 714, 738, 706, 666, 461, 469, 482,
+       558, 814, 559, 177, 575, 538, 309, 383, 261, 156, 420, 761, 630, 893,
+       10, 116, 940, 844, 71, 377, 662, 312, 520, 244, 143, 759, 119, 186,
+       592, 909, 864, 376, 768, 254, 265, 394, 511, 760, 574, 6, 436, 514,
+       59, 226, 644, 956, 578, 825, 548, 145, 736, 597, 378, 821, 987, 897,
+       354, 144, 722, 895, 589, 503, 826, 498, 543, 617, 763, 231, 808, 528,
+       89, 479, 607, 737, 170, 404, 371, 65, 103, 340, 283, 141, 313, 858,
+       289, 124, 971, 687, 954, 732, 39, 926, 176, 100, 267, 519, 890, 535,
+       276, 448, 27, 457, 899, 385, 184, 275, 770, 544, 614, 449, 160, 658,
+       259, 973, 108, 604, 24, 207, 562, 757, 744, 324, 444, 962, 591, 480,
+       398, 409, 998, 253, 325, 445, 979, 8, 35, 118, 73, 683, 208, 85, 190,
+       791, 408, 871, 657, 179, 18, 556, 496, 475, 20, 894, 484, 775, 889,
+       463, 241, 730, 57, 907, 551, 859, 943, 185, 416, 870, 590, 435, 471,
+       932, 268, 381, 626, 502, 565, 273, 534, 672, 778, 292, 473, 566, 104,
+       172, 285, 832, 411, 329, 628, 397, 472, 271, 910, 711, 690, 969, 585,
+       809, 941, 923, 555, 228, 685, 242, 94, 96, 211, 140, 61, 922, 795,
+       869, 34, 255, 38, 984, 676, 15, 560, 632, 434, 921, 355, 582, 351,
+       212, 200, 819, 960, 649, 852, 75, 771, 361, 996, 238, 316, 720, 671,
+       462, 112, 569, 171, 664, 625, 588, 405, 553, 270, 533, 353, 842, 114,
+       972, 83, 937, 63, 194, 237, 537, 980, 802, 916, 959, 688, 839, 350,
+       917, 650, 545, 615, 151, 352, 686, 726, 266, 509, 439, 491, 935, 608,
+       518, 653, 339, 609, 277, 635, 836, 88, 407, 440, 642, 927, 229, 727,
+       360, 477, 846, 413, 454, 616, 28, 598, 567, 540, 790, 424, 247, 317,
+       746, 911, 798, 321, 547, 248, 734, 829, 220, 138, 756, 500, 691, 196,
+       740, 930, 843, 733, 221, 827, 50, 813, 949, 525, 349, 474, 134, 875,
+       695, 513, 414, 515, 638, 99, 366, 490, 975, 246, 465, 206, 281, 583,
+       256, 587, 749, 2, 951, 679, 215, 364, 458, 402, 646, 991, 335, 982,
+       835, 300, 900, 703, 994, 983, 234, 888, 532, 804, 584, 305, 792, 442,
+       291, 964, 158, 370, 452, 250, 521, 166, 948, 812, 794, 272, 699, 205,
+       183, 507, 301, 920, 781, 233, 824, 137, 489, 833, 887, 966, 856, 78,
+       830, 153, 359, 696, 526, 216, 66, 701, 403, 891, 849, 571, 308, 483,
+       164, 293, 928, 677, 320, 837, 441, 639, 564, 510, 648, 274, 336, 661,
+       878, 777, 816, 976, 493, 810, 67, 87, 91, 187, 882, 986, 80, 22, 499,
+       90, 705, 139, 136, 122, 708, 716, 886, 572, 127, 40, 721, 764, 16,
+       379, 692, 645, 456, 710, 460, 783, 97, 776, 713, 884, 115, 466, 596,
+       374, 406, 110, 568, 68, 214, 622, 470, 107, 504, 682, 31, 421, 576,
+       654, 605, 788, 799, 280, 338, 931, 873, 204, 287, 459, 755, 939, 599,
+       431, 796, 235, 42, 750, 262, 318, 393, 202, 822, 879, 820, 728, 337,
+};
+
+static void fill_bindings_random(struct strbuf *buf, int start, int end,
+                                const char *prefix)
+{
+       int i;
+
+       for (i = start; i < end; i++) {
+               print_strbuf(buf,  "%s", prefix);
+               format_devname(buf, random_numbers[i]);
+               print_strbuf(buf,  " WWID%d\n", random_numbers[i]);
+       }
+}
+
+struct random_aliases {
+       int start;
+       int end;
+       const char *prefix;
+};
+
+static void order_test(int n, const struct random_aliases ra[], bool conflict_ok)
+{
+       STRBUF_ON_STACK(buf);
+       int i, j, prev, curr, tmp;
+       struct binding *bdg;
+       Bindings *bindings = &global_bindings;
+
+       for (j = 0; j < n; j++)
+               fill_bindings_random(&buf, ra[j].start, ra[j].end, ra[j].prefix);
+       __mock_bindings_file(get_strbuf_str(&buf), conflict_ok);
+
+       for (j = 0; j < n; j++) {
+               bdg = VECTOR_SLOT(bindings, 0);
+               if (ALIAS_DEBUG && j == 0)
+                       printf("%d: %s\n", 0, bdg->alias);
+               prev = scan_devname(bdg->alias, ra[j].prefix);
+               i = 1;
+               vector_foreach_slot_after(bindings, bdg, i) {
+                       if (ALIAS_DEBUG && j == 0)
+                               printf("%d: %s\n", i, bdg->alias);
+                       tmp = scan_devname(bdg->alias, ra[j].prefix);
+                       if (tmp == -1)
+                               continue;
+                       curr = tmp;
+                       if (prev > 0) {
+                               if (curr <= prev)
+                                       printf("ERROR: %d (%s) %d >= %d\n",
+                                              i, bdg->alias, prev, curr);
+                               assert_true(curr > prev);
+                       }
+                       prev = curr;
+               }
+       }
+}
+
+static void order_01(void **state)
+{
+       const struct random_aliases ra[] = {
+               {  0, 1000, "MPATH" },
+       };
+
+       order_test(ARRAY_SIZE(ra), ra, false);
+}
+
+static void order_02(void **state)
+{
+       const struct random_aliases ra[] = {
+               {   0, 500, "MPATH" },
+               { 200, 700, "mpath" },
+       };
+       order_test(ARRAY_SIZE(ra), ra, false);
+}
+
+static void order_03(void **state)
+{
+       const struct random_aliases ra[] = {
+               {  500, 1000, "MPTH" },
+               {    0,  500, "MPATH" },
+       };
+       order_test(ARRAY_SIZE(ra), ra, false);
+}
+
+static void order_04(void **state)
+{
+       const struct random_aliases ra[] = {
+               {   0, 500, "mpa" },
+               { 250, 750, "mp" },
+       };
+       order_test(ARRAY_SIZE(ra), ra, true);
+}
+
+static void order_05(void **state)
+{
+       const struct random_aliases ra[] = {
+               {  0, 100, "A" },
+               {  0, 100, "B" },
+               {  0, 100, "C" },
+               {  0, 100, "D" },
+       };
+       order_test(ARRAY_SIZE(ra), ra, false);
+}
+
+static void order_06(void **state)
+{
+       const struct random_aliases ra[] = {
+               {  0, 100, "" },
+               {  0, 100, "a" },
+               {  0, 100, "aa" },
+               {  0, 100, "ab" },
+               {  0, 100, "aaa" },
+       };
+       order_test(ARRAY_SIZE(ra), ra, true);
+}
+
+static int test_bindings_order()
+{
+       const struct CMUnitTest tests[] = {
+               cmocka_unit_test_teardown(order_01, teardown_bindings),
+               cmocka_unit_test_teardown(order_02, teardown_bindings),
+               cmocka_unit_test_teardown(order_03, teardown_bindings),
+               cmocka_unit_test_teardown(order_04, teardown_bindings),
+               cmocka_unit_test_teardown(order_05, teardown_bindings),
+               cmocka_unit_test_teardown(order_06, teardown_bindings),
        };
 
        return cmocka_run_group_tests(tests, NULL, NULL);
@@ -1152,11 +2000,16 @@ int main(void)
        int ret = 0;
        init_test_verbosity(3);
 
+       /* avoid open_file() call in _read_bindings_file */
+       bindings_file_changed = 0;
+
        ret += test_format_devname();
        ret += test_scan_devname();
        ret += test_lookup_binding();
        ret += test_rlookup_binding();
        ret += test_allocate_binding();
+       ret += test_get_user_friendly_alias();
+       ret += test_bindings_order();
 
        return ret;
 }
index 882aa3a152e03615eadd31d2e4996cec0362fa31..ba8dfd079d1c6b82800f327e03be61d34c9b4490 100644 (file)
@@ -24,6 +24,8 @@
 #include "test-log.h"
 #include "debug.h"
 
+#include "../libmultipath/blacklist.c"
+
 struct udev_device {
        const char *sysname;
        char *property_list[];
@@ -224,8 +226,15 @@ static void test_devnode_default(void **state)
 {
        assert_int_equal(filter_devnode(blist_devnode_default, NULL, "sdaa"),
                         MATCH_NOTHING);
-       assert_int_equal(filter_devnode(blist_devnode_default, NULL, "nvme0n1"),
-                        MATCH_NOTHING);
+       if (nvme_multipath_enabled()) {
+               expect_condlog(3, "nvme0n1: device node name blacklisted\n");
+               assert_int_equal(filter_devnode(blist_devnode_default, NULL,
+                                               "nvme0n1"),
+                                MATCH_DEVNODE_BLIST);
+       } else
+               assert_int_equal(filter_devnode(blist_devnode_default, NULL,
+                                               "nvme0n1"),
+                                MATCH_NOTHING);
        assert_int_equal(filter_devnode(blist_devnode_default, NULL, "dasda"),
                         MATCH_NOTHING);
        expect_condlog(3, "hda: device node name blacklisted\n");
index db9643e1548270a26c468df14a7a236d4884f1dc..5201d21aedb0bc7e47f13d7eda22fb9d1a81a985 100644 (file)
@@ -141,10 +141,9 @@ int __real_io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt);
 int __wrap_io_cancel(io_context_t ctx, struct iocb *iocb, struct io_event *evt)
 {
 #ifdef DIO_TEST_DEV
-       mock_type(int);
        return __real_io_cancel(ctx, iocb, evt);
 #else
-       return mock_type(int);
+       return 0;
 #endif
 }
 
@@ -439,14 +438,8 @@ static void test_check_state_timeout(void **state)
        do_libcheck_init(&c, 4096, NULL);
        aio_grp = get_aio_grp(&c);
        return_io_getevents_none();
-       will_return(__wrap_io_cancel, 0);
        do_check_state(&c, 1, 30, PATH_DOWN);
        check_aio_grp(aio_grp, 1, 0);
-#ifdef DIO_TEST_DEV
-       /* io_cancel will return negative value on timeout, so it happens again
-        * when freeing the checker */
-       will_return(__wrap_io_cancel, 0);
-#endif
        libcheck_free(&c);
        do_libcheck_reset(1);
 }
@@ -468,12 +461,8 @@ static void test_check_state_async_timeout(void **state)
        return_io_getevents_none();
        do_check_state(&c, 0, 3, PATH_PENDING);
        return_io_getevents_none();
-       will_return(__wrap_io_cancel, 0);
        do_check_state(&c, 0, 3, PATH_DOWN);
        check_aio_grp(aio_grp, 1, 0);
-#ifdef DIO_TEST_DEV
-       will_return(__wrap_io_cancel, 0);
-#endif
        libcheck_free(&c);
        do_libcheck_reset(1);
 }
@@ -501,13 +490,8 @@ static void test_free_with_pending(void **state)
        check_aio_grp(aio_grp, 2, 0);
         libcheck_free(&c[0]);
        check_aio_grp(aio_grp, 1, 0);
-        will_return(__wrap_io_cancel, 0);
         libcheck_free(&c[1]);
-#ifdef DIO_TEST_DEV
-       check_aio_grp(aio_grp, 1, 1); /* real cancel doesn't remove request */
-#else
-        check_aio_grp(aio_grp, 0, 0);
-#endif
+       check_aio_grp(aio_grp, 1, 1); /* cancel doesn't remove request */
         do_libcheck_reset(1);
 }
 
@@ -533,7 +517,6 @@ static void test_orphaned_aio_group(void **state)
        assert_int_equal(i, 1);
        for (i = 0; i < AIO_GROUP_SIZE; i++) {
                assert_true(is_checker_running(&c[i]));
-               will_return(__wrap_io_cancel, -1);
                if (i == AIO_GROUP_SIZE - 1) {
                        /* remove the orphaned group and create a new one */
                        will_return(__wrap_io_destroy, 0);
@@ -559,12 +542,10 @@ static void test_timeout_cancel_failed(void **state)
                do_libcheck_init(&c[i], 4096, &reqs[i]);
        aio_grp = get_aio_grp(c);
        return_io_getevents_none();
-       will_return(__wrap_io_cancel, -1);
        do_check_state(&c[0], 1, 30, PATH_DOWN);
        assert_true(is_checker_running(&c[0]));
        check_aio_grp(aio_grp, 2, 0);
        return_io_getevents_none();
-       will_return(__wrap_io_cancel, -1);
        do_check_state(&c[0], 1, 30, PATH_DOWN);
        assert_true(is_checker_running(&c[0]));
        return_io_getevents_nr(NULL, 1, &reqs[0], &res[0]);
@@ -600,7 +581,6 @@ static void test_async_timeout_cancel_failed(void **state)
        return_io_getevents_none();
        do_check_state(&c[1], 0, 2, PATH_PENDING);
        return_io_getevents_none();
-       will_return(__wrap_io_cancel, -1);
        do_check_state(&c[0], 0, 2, PATH_DOWN);
 #ifndef DIO_TEST_DEV
        /* can't pick which even gets returned on real devices */
@@ -608,7 +588,6 @@ static void test_async_timeout_cancel_failed(void **state)
        do_check_state(&c[1], 0, 2, PATH_UP);
 #endif
        return_io_getevents_none();
-       will_return(__wrap_io_cancel, -1);
        do_check_state(&c[0], 0, 2, PATH_DOWN);
        assert_true(is_checker_running(&c[0]));
        return_io_getevents_nr(NULL, 2, reqs, res);
@@ -637,7 +616,6 @@ static void test_orphan_checker_cleanup(void **state)
        aio_grp = get_aio_grp(c);
        return_io_getevents_none();
        do_check_state(&c[0], 0, 30, PATH_PENDING);
-       will_return(__wrap_io_cancel, -1);
        check_aio_grp(aio_grp, 2, 0);
        libcheck_free(&c[0]);
        check_aio_grp(aio_grp, 2, 1);
@@ -662,7 +640,6 @@ static void test_orphan_reset_cleanup(void **state)
        orphan_aio_grp = get_aio_grp(&c);
        return_io_getevents_none();
        do_check_state(&c, 0, 30, PATH_PENDING);
-       will_return(__wrap_io_cancel, -1);
        check_aio_grp(orphan_aio_grp, 1, 0);
        libcheck_free(&c);
        check_aio_grp(orphan_aio_grp, 1, 1);
index 43be831fb23aca447f6b27bcd2c1b8a91cf347e9..ccf29bc93e352a51350a18153c3a70ea3faae266 100644 (file)
@@ -32,6 +32,15 @@ struct multipath mp8, mp4, mp1, mp0, mp_null;
 struct path p8[8], p4[4], p1[1];
 
 
+static void set_tpg(struct path *pp, int *tpg, int size)
+{
+       int i;
+
+       for (i = 0; i < size; i++) {
+               pp[i].tpg_id = tpg[i];
+       }
+}
+
 static void set_priority(struct path *pp, int *prio, int size)
 {
        int i;
@@ -639,6 +648,227 @@ static void test_group_by_prio_mixed_one_marginal8(void **state)
        verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 7);
 }
 
+static void test_group_by_prio_mixed_undef8(void **state)
+{
+       int prio[] = {7,1,3,-1,5,2,8,2};
+       int group0[] = {6};
+       int group1[] = {0};
+       int group2[] = {4};
+       int group3[] = {2};
+       int group4[] = {5,7};
+       int group5[] = {1};
+       int group6[] = {3};
+       int *groups[] = {group0, group1, group2, group3,
+                         group4, group5, group6};
+       int group_size[] = {1,1,1,1,2,1,1};
+
+       set_priority(p8, prio, 8);
+       mp8.pgpolicyfn = group_by_prio;
+       assert_int_equal(group_paths(&mp8, 0), 0);
+       verify_pathgroups(&mp8, p8, groups, group_size, NULL, 7);
+}
+
+static void test_group_by_tpg_same8(void **state)
+{
+       int paths[] = {0,1,2,3,4,5,6,7};
+       int *groups[] = {paths};
+       int group_size[] = {8};
+
+       mp8.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp8, 0), 0);
+       verify_pathgroups(&mp8, p8, groups, group_size, NULL, 1);
+}
+
+static void test_group_by_tpg_different8(void **state)
+{
+       int prio[] = {1,2,3,4,5,6,7,8};
+       int tpg[] = {3,5,4,1,8,6,7,2};
+       int paths[] = {7,6,5,4,3,2,1,0};
+       int *groups[] = {&paths[0], &paths[1], &paths[2], &paths[3],
+                         &paths[4], &paths[5], &paths[6], &paths[7]};
+       int group_size[] = {1,1,1,1,1,1,1,1};
+
+       set_priority(p8, prio, 8);
+       set_tpg(p8, tpg, 8);
+       mp8.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp8, 0), 0);
+       verify_pathgroups(&mp8, p8, groups, group_size, NULL, 8);
+}
+
+static void test_group_by_tpg_mixed8(void **state)
+{
+       int prio[] = {7,2,3,3,5,2,8,2};
+       int tpg[] = {1,2,3,3,4,2,5,6};
+       int group0[] = {6};
+       int group1[] = {0};
+       int group2[] = {4};
+       int group3[] = {2,3};
+       int group4[] = {1,5};
+       int group5[] = {7};
+       int *groups[] = {group0, group1, group2, group3,
+                         group4, group5};
+       int group_size[] = {1,1,1,2,2,1};
+
+       set_priority(p8, prio, 8);
+       set_tpg(p8, tpg, 8);
+       mp8.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp8, 0), 0);
+       verify_pathgroups(&mp8, p8, groups, group_size, NULL, 6);
+}
+
+static void test_group_by_tpg_3_groups8(void **state)
+{
+       int prio[] = {1,2,2,1,2,1,1,2};
+       int tpg[] = {1,2,2,1,3,1,1,3};
+       int group0[] = {1,2};
+       int group1[] = {4,7};
+       int group2[] = {0,3,5,6};
+       int *groups[] = {group0, group1, group2};
+       int group_size[] = {2,2,4};
+
+       set_priority(p8, prio, 8);
+       set_tpg(p8, tpg, 8);
+       mp8.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp8, 0), 0);
+       verify_pathgroups(&mp8, p8, groups, group_size, NULL, 3);
+}
+
+static void test_group_by_tpg_2_groups4(void **state)
+{
+       int prio[] = {2,1,1,2};
+       int tpg[] = {1,2,2,1};
+       int group0[] = {0,3};
+       int group1[] = {1,2};
+       int *groups[] = {group0, group1};
+       int group_size[] = {2,2};
+
+       set_priority(p4, prio, 4);
+       set_tpg(p4, tpg, 4);
+       mp4.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp4, 0), 0);
+       verify_pathgroups(&mp4, p4, groups, group_size, NULL, 2);
+}
+
+static void test_group_by_tpg1(void **state)
+{
+       int paths[] = {0};
+       int *groups[] = {paths};
+       int group_size[] = {1};
+
+       mp1.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp1, 0), 0);
+       verify_pathgroups(&mp1, p1, groups, group_size, NULL, 1);
+}
+
+static void test_group_by_tpg0(void **state)
+{
+       mp0.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp0, 0), 0);
+       verify_pathgroups(&mp0, NULL, NULL, NULL, NULL, 0);
+}
+
+static void test_group_by_tpg_null(void **state)
+{
+       mp_null.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp_null, 0), 0);
+       verify_pathgroups(&mp_null, NULL, NULL, NULL, NULL, 0);
+}
+
+static void test_group_by_tpg_mixed_all_marginal8(void **state)
+{
+       int prio[] = {7,2,3,3,5,2,8,2};
+       int tpg[] = {1,2,3,3,4,2,5,6};
+       int marginal[] = {1,1,1,1,1,1,1,1};
+       int group0[] = {6};
+       int group1[] = {0};
+       int group2[] = {4};
+       int group3[] = {2,3};
+       int group4[] = {1,5};
+       int group5[] = {7};
+       int *groups[] = {group0, group1, group2, group3,
+                         group4, group5};
+       int group_size[] = {1,1,1,2,2,1};
+       int group_marginal[] = {1,1,1,1,1,1};
+
+       set_priority(p8, prio, 8);
+       set_tpg(p8, tpg, 8);
+       set_marginal(p8, marginal, 8);
+       mp8.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp8, 1), 0);
+       verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 6);
+}
+
+static void test_group_by_tpg_mixed_half_marginal8(void **state)
+{
+       int prio[] = {7,1,3,3,3,2,8,2};
+       int tpg[] = {1,2,3,4,5,6,7,6};
+       int marginal[] = {0,0,0,1,0,1,1,1};
+       int group0[] = {0};
+       int group1[] = {2};
+       int group2[] = {4};
+       int group3[] = {1};
+       int group4[] = {6};
+       int group5[] = {3};
+       int group6[] = {5,7};
+       int *groups[] = {group0, group1, group2, group3,
+                         group4, group5, group6};
+       int group_size[] = {1,1,1,1,1,1,2};
+       int group_marginal[] = {0,0,0,0,1,1,1};
+
+       set_priority(p8, prio, 8);
+       set_tpg(p8, tpg, 8);
+       set_marginal(p8, marginal, 8);
+       mp8.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp8, 1), 0);
+       verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 7);
+}
+
+static void test_group_by_tpg_mixed_one_marginal8(void **state)
+{
+       int prio[] = {7,1,3,3,5,2,8,2};
+       int tpg[]  = {1,2,3,3,4,5,6,5};
+       int marginal[] = {0,0,0,0,0,1,0,0};
+       int group0[] = {6};
+       int group1[] = {0};
+       int group2[] = {4};
+       int group3[] = {2,3};
+       int group4[] = {7};
+       int group5[] = {1};
+       int group6[] = {5};
+       int *groups[] = {group0, group1, group2, group3,
+                         group4, group5, group6};
+       int group_size[] = {1,1,1,2,1,1,1};
+       int group_marginal[] = {0,0,0,0,0,0,1};
+
+       set_priority(p8, prio, 8);
+       set_tpg(p8, tpg, 8);
+       set_marginal(p8, marginal, 8);
+       mp8.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp8, 1), 0);
+       verify_pathgroups(&mp8, p8, groups, group_size, group_marginal, 7);
+}
+
+static void test_group_by_tpg_mixed_undef8(void **state)
+{
+       int prio[] = {-1,2,3,-1,5,2,8,2};
+       int tpg[] = {1,2,3,3,4,2,5,6};
+       int group0[] = {6};
+       int group1[] = {4};
+       int group2[] = {2,3};
+       int group3[] = {1,5};
+       int group4[] = {7};
+       int group5[] = {0};
+       int *groups[] = {group0, group1, group2, group3,
+                         group4, group5};
+       int group_size[] = {1,1,2,2,1,1};
+
+       set_priority(p8, prio, 8);
+       set_tpg(p8, tpg, 8);
+       mp8.pgpolicyfn = group_by_tpg;
+       assert_int_equal(group_paths(&mp8, 0), 0);
+       verify_pathgroups(&mp8, p8, groups, group_size, NULL, 6);
+}
+
 static void test_group_by_node_name_same8(void **state)
 {
        char *node_name[] = {"a","a","a","a","a","a","a","a"};
@@ -1002,6 +1232,19 @@ int test_pgpolicies(void)
                setup_test(test_group_by_prio_mixed_all_marginal, 8),
                setup_test(test_group_by_prio_mixed_half_marginal, 8),
                setup_test(test_group_by_prio_mixed_one_marginal, 8),
+               setup_test(test_group_by_prio_mixed_undef, 8),
+               setup_test(test_group_by_tpg_same, 8),
+               setup_test(test_group_by_tpg_different, 8),
+               setup_test(test_group_by_tpg_mixed, 8),
+               setup_test(test_group_by_tpg_3_groups, 8),
+               setup_test(test_group_by_tpg_2_groups, 4),
+               setup_test(test_group_by_tpg, 1),
+               setup_test(test_group_by_tpg, 0),
+               setup_test(test_group_by_tpg, _null),
+               setup_test(test_group_by_tpg_mixed_all_marginal, 8),
+               setup_test(test_group_by_tpg_mixed_half_marginal, 8),
+               setup_test(test_group_by_tpg_mixed_one_marginal, 8),
+               setup_test(test_group_by_tpg_mixed_undef, 8),
                setup_test(test_group_by_node_name_same, 8),
                setup_test(test_group_by_node_name_increasing, 8),
                setup_test(test_group_by_node_name_3_groups, 8),
index 0bc49d53a67af8dc4f0587f17910b8b6360ede41..f75ea319a84dd18b2ac58232a25c9c70194166b8 100644 (file)
@@ -342,12 +342,6 @@ void mock_pathinfo(int mask, const struct mocked_path *mp)
                            mp->wwid);
        }
 
-       if (mask & DI_CHECKER) {
-               /* get_state -> sysfs_get_timeout  */
-               will_return(__wrap_udev_device_get_subsystem, "scsi");
-               will_return(__wrap_udev_device_get_sysattr_value, "180");
-       }
-
        if (mask & DI_PRIO && mp->flags & NEED_SELECT_PRIO) {
 
                /* sysfs_get_timeout, again (!?) */
index c17458723c4177590176ace07384b5d9af9340b9..635169994b2562a0cfdc0e3e726f51566586e17f 100644 (file)
@@ -16,12 +16,14 @@ void __wrap_dlog (int prio, const char * fmt, ...)
        va_list ap;
        char *expected;
 
-       check_expected(prio);
        va_start(ap, fmt);
        vsnprintf(buff, MAX_MSG_SIZE, fmt, ap);
        va_end(ap);
        fprintf(stderr, "%s(%d): %s", __func__, prio, buff);
        expected = mock_ptr_type(char *);
+       if (memcmp(expected, buff, strlen(expected)))
+               fprintf(stderr, "%s(expected): %s", __func__, expected);
+       check_expected(prio);
        assert_memory_equal(buff, expected, strlen(expected));
 }
 
index 9affb0e1c2567ee1800df8094ac64b8d63101a3f..d6083dce5c3855e8556b9ccbb0d48fec2a0c3bad 100644 (file)
@@ -193,7 +193,7 @@ static void test_bitmask_1(void **state)
        for (j = 0; j < BITARR_SZ; j++) {
                for (i = 0; i < 64; i++) {
                        b = 64 * j + i;
-                       assert(!is_bit_set_in_bitfield(b, bf));
+                       assert_false(is_bit_set_in_bitfield(b, bf));
                        set_bit_in_bitfield(b, bf);
                        for (k = 0; k < BITARR_SZ; k++) {
 #if 0
@@ -207,13 +207,13 @@ static void test_bitmask_1(void **state)
                        }
                        for (m = 0; m < 64; m++)
                                if (i == m)
-                                       assert(is_bit_set_in_bitfield(64 * j + m,
-                                                                     bf));
+                                       assert_true(is_bit_set_in_bitfield(64 * j + m,
+                                                                          bf));
                                else
-                                       assert(!is_bit_set_in_bitfield(64 * j + m,
-                                                                      bf));
+                                       assert_false(is_bit_set_in_bitfield(64 * j + m,
+                                                                          bf));
                        clear_bit_in_bitfield(b, bf);
-                       assert(!is_bit_set_in_bitfield(b, bf));
+                       assert_false(is_bit_set_in_bitfield(b, bf));
                        for (k = 0; k < BITARR_SZ; k++)
                                assert_int_equal(arr[k], 0ULL);
                }
@@ -235,16 +235,16 @@ static void test_bitmask_2(void **state)
        for (j = 0; j < BITARR_SZ; j++) {
                for (i = 0; i < 64; i++) {
                        b = 64 * j + i;
-                       assert(!is_bit_set_in_bitfield(b, bf));
+                       assert_false(is_bit_set_in_bitfield(b, bf));
                        set_bit_in_bitfield(b, bf);
                        for (m = 0; m < 64; m++)
                                if (m <= i)
-                                       assert(is_bit_set_in_bitfield(64 * j + m,
-                                                                     bf));
+                                       assert_true(is_bit_set_in_bitfield(64 * j + m,
+                                                                          bf));
                                else
-                                       assert(!is_bit_set_in_bitfield(64 * j + m,
-                                                                      bf));
-                       assert(is_bit_set_in_bitfield(b, bf));
+                                       assert_false(is_bit_set_in_bitfield(64 * j + m,
+                                                                           bf));
+                       assert_true(is_bit_set_in_bitfield(b, bf));
                        for (k = 0; k < BITARR_SZ; k++) {
                                if (k < j || (k == j && i == 63))
                                        assert_int_equal(arr[k], ~0ULL);
@@ -260,16 +260,16 @@ static void test_bitmask_2(void **state)
        for (j = 0; j < BITARR_SZ; j++) {
                for (i = 0; i < 64; i++) {
                        b = 64 * j + i;
-                       assert(is_bit_set_in_bitfield(b, bf));
+                       assert_true(is_bit_set_in_bitfield(b, bf));
                        clear_bit_in_bitfield(b, bf);
                        for (m = 0; m < 64; m++)
                                if (m <= i)
-                                       assert(!is_bit_set_in_bitfield(64 * j + m,
-                                                                      bf));
+                                       assert_false(is_bit_set_in_bitfield(64 * j + m,
+                                                                           bf));
                                else
-                                       assert(is_bit_set_in_bitfield(64 * j + m,
-                                                                     bf));
-                       assert(!is_bit_set_in_bitfield(b, bf));
+                                       assert_true(is_bit_set_in_bitfield(64 * j + m,
+                                                                          bf));
+                       assert_false(is_bit_set_in_bitfield(b, bf));
                        for (k = 0; k < BITARR_SZ; k++) {
                                if (k < j || (k == j && i == 63))
                                        assert_int_equal(arr[k], 0ULL);
@@ -316,9 +316,8 @@ static void _test_bitmask_small(unsigned int n)
        uint32_t *arr;
        unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
 
-       assert(sizeof(bitfield_t) == 4 || sizeof(bitfield_t) == 8);
-       assert(n <= 64);
-       assert(n >= 1);
+       assert_true(sizeof(bitfield_t) == 4 || sizeof(bitfield_t) == 8);
+       assert_in_range(n, 1, 64);
 
        bf = alloc_bitfield(n);
        assert_non_null(bf);
@@ -366,8 +365,7 @@ static void _test_bitmask_small_2(unsigned int n)
        uint32_t *arr;
        unsigned int size = maybe_swap_idx((n - 1) / 32) + 1, i;
 
-       assert(n <= 128);
-       assert(n >= 65);
+       assert_in_range(n, 65, 128);
 
        bf = alloc_bitfield(n);
        assert_non_null(bf);
index 398b771e39160d12f911fa291f616c1af6833da2..18a5a7bf82e715fe1c0d02cb96be54a7db9b05e0 100644 (file)
@@ -62,11 +62,6 @@ int __wrap___mpath_connect(int nonblocking)
        return -1;
 }
 
-int __wrap_systemd_service_enabled(const char *dev)
-{
-       return (int)mock_type(bool);
-}
-
 /* There's no point in checking the return value here */
 int __wrap_mpath_disconnect(int fd)
 {
@@ -83,6 +78,13 @@ struct udev_device *__wrap_udev_device_new_from_subsystem_sysname(struct udev *u
        return NULL;
 }
 
+/* For devtype check */
+const char *__wrap_udev_device_get_property_value(struct udev_device *udev_device, const char *property)
+{
+       check_expected(property);
+       return mock_ptr_type(char *);
+}
+
 /* For the "hidden" check in pathinfo() */
 const char *__wrap_udev_device_get_sysattr_value(struct udev_device *udev_device,
                                         const char *sysattr)
@@ -97,6 +99,12 @@ int __wrap_add_foreign(struct udev_device *udev_device)
        return mock_type(int);
 }
 
+/* For is_device_used() */
+const char *__wrap_udev_device_get_sysname(struct udev_device *udev_device)
+{
+       return mock_ptr_type(char *);
+}
+
 /* called from pathinfo() */
 int __wrap_filter_devnode(struct config *conf, const struct _vector *elist,
                          const char *vendor, const char * product, const char *dev)
@@ -165,6 +173,11 @@ int __wrap_is_failed_wwid(const char *wwid)
        return ret;
 }
 
+const char *__wrap_udev_device_get_syspath(struct udev_device *udevice)
+{
+       return mock_ptr_type(char *);
+}
+
 int __wrap_check_wwids_file(char *wwid, int write_wwid)
 {
        bool passed = mock_type(bool);
@@ -198,7 +211,6 @@ enum {
 enum {
        CHECK_MPATHD_RUNNING,
        CHECK_MPATHD_EAGAIN,
-       CHECK_MPATHD_ENABLED,
        CHECK_MPATHD_SKIP,
 };
 
@@ -214,17 +226,16 @@ static void setup_passing(char *name, char *wwid, unsigned int check_multipathd,
        else if (check_multipathd == CHECK_MPATHD_EAGAIN) {
                will_return(__wrap___mpath_connect, false);
                will_return(__wrap___mpath_connect, EAGAIN);
-       } else if (check_multipathd == CHECK_MPATHD_ENABLED) {
-               will_return(__wrap___mpath_connect, false);
-               will_return(__wrap___mpath_connect, ECONNREFUSED);
-               will_return(__wrap_systemd_service_enabled, true);
        }
+
        /* nothing for CHECK_MPATHD_SKIP */
        if (stage == STAGE_CHECK_MULTIPATHD)
                return;
        will_return(__wrap_udev_device_new_from_subsystem_sysname, true);
        will_return(__wrap_udev_device_new_from_subsystem_sysname,
                    name);
+       expect_string(__wrap_udev_device_get_property_value, property, "DEVTYPE");
+       will_return(__wrap_udev_device_get_property_value, "disk");
        if (stage == STAGE_GET_UDEV_DEVICE)
                return;
        if  (stage == STAGE_PATHINFO_REAL) {
@@ -250,6 +261,10 @@ static void setup_passing(char *name, char *wwid, unsigned int check_multipathd,
                return;
        will_return(__wrap_is_failed_wwid, WWID_IS_NOT_FAILED);
        will_return(__wrap_is_failed_wwid, wwid);
+       /* avoid real is_device_in_use() check */
+       if (conf.find_multipaths == FIND_MULTIPATHS_GREEDY ||
+           conf.find_multipaths == FIND_MULTIPATHS_SMART)
+               will_return(__wrap_udev_device_get_syspath, NULL);
        if (stage == STAGE_IS_FAILED)
                return;
        will_return(__wrap_check_wwids_file, false);
@@ -318,19 +333,10 @@ static void test_check_multipathd(void **state)
        will_return(__wrap_sysfs_is_multipathed, false);
        will_return(__wrap___mpath_connect, false);
        will_return(__wrap___mpath_connect, ECONNREFUSED);
-       will_return(__wrap_systemd_service_enabled, false);
+
        assert_int_equal(is_path_valid(name, &conf, &pp, true),
                         PATH_IS_NOT_VALID);
        assert_string_equal(pp.dev, name);
-       /* test pass because service is enabled. fail getting udev */
-       memset(&pp, 0, sizeof(pp));
-       setup_passing(name, NULL, CHECK_MPATHD_ENABLED, STAGE_CHECK_MULTIPATHD);
-       will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
-       will_return(__wrap_udev_device_new_from_subsystem_sysname,
-                   name);
-       assert_int_equal(is_path_valid(name, &conf, &pp, true),
-                        PATH_IS_ERROR);
-       assert_string_equal(pp.dev, name);
        /* test pass because connect returned EAGAIN. fail getting udev */
        setup_passing(name, NULL, CHECK_MPATHD_EAGAIN, STAGE_CHECK_MULTIPATHD);
        will_return(__wrap_udev_device_new_from_subsystem_sysname, false);
@@ -347,6 +353,30 @@ static void test_check_multipathd(void **state)
        assert_int_equal(is_path_valid(name, &conf, &pp, true),
                         PATH_IS_ERROR);
        assert_string_equal(pp.dev, name);
+
+       /* test pass because connect succeeded. succeed getting udev. Wrong DEVTYPE  */
+       memset(&pp, 0, sizeof(pp));
+       setup_passing(name, NULL, CHECK_MPATHD_RUNNING, STAGE_CHECK_MULTIPATHD);
+       will_return(__wrap_udev_device_new_from_subsystem_sysname, true);
+       will_return(__wrap_udev_device_new_from_subsystem_sysname,
+                   name);
+       expect_string(__wrap_udev_device_get_property_value, property, "DEVTYPE");
+       will_return(__wrap_udev_device_get_property_value, "partition");
+       assert_int_equal(is_path_valid(name, &conf, &pp, true),
+                        PATH_IS_NOT_VALID);
+       assert_string_equal(pp.dev, name);
+
+       /* test pass because connect succeeded. succeed getting udev. Bad DEVTYPE  */
+       memset(&pp, 0, sizeof(pp));
+       setup_passing(name, NULL, CHECK_MPATHD_RUNNING, STAGE_CHECK_MULTIPATHD);
+       will_return(__wrap_udev_device_new_from_subsystem_sysname, true);
+       will_return(__wrap_udev_device_new_from_subsystem_sysname,
+                   name);
+       expect_string(__wrap_udev_device_get_property_value, property, "DEVTYPE");
+       will_return(__wrap_udev_device_get_property_value, NULL);
+       assert_int_equal(is_path_valid(name, &conf, &pp, true),
+                        PATH_IS_NOT_VALID);
+       assert_string_equal(pp.dev, name);
 }
 
 static void test_pathinfo(void **state)
@@ -485,7 +515,7 @@ static void test_check_uuid_present(void **state)
 
        memset(&pp, 0, sizeof(pp));
        conf.find_multipaths = FIND_MULTIPATHS_STRICT;
-       setup_passing(name, wwid, CHECK_MPATHD_ENABLED, STAGE_CHECK_WWIDS);
+       setup_passing(name, wwid, CHECK_MPATHD_RUNNING, STAGE_CHECK_WWIDS);
        will_return(__wrap_dm_map_present_by_uuid, 1);
        will_return(__wrap_dm_map_present_by_uuid, wwid);
        assert_int_equal(is_path_valid(name, &conf, &pp, true),
index a7d2092c833367a81d825215bb3f69c915d1e5e1..1b2d62d63986c2876ce1f9086b3e813dfd62b20f 100644 (file)
@@ -119,7 +119,7 @@ static void hex2bin(unsigned char *dst, const char *src,
        const char *sc;
        unsigned char *ds;
 
-       assert(srclen % 2 == 0);
+       assert_true(srclen % 2 == 0);
        for (sc = src, ds = dst;
             sc < src + srclen &&  ds < dst + dstlen;
             sc += 2, ++ds)