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
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
jobs:
upload-coverity-scan:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
steps:
- name: checkout
uses: actions/checkout@v2
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:
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
jobs:
build-current:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
pull-params: "--platform linux/${{ matrix.arch }}"
build-old:
- runs-on: ubuntu-20.04
+ runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
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:
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
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 = ..
# 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),)
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
@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' $< >$@
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.
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
========================
* `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.
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
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
* `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
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.
# 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 \
$(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)
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')))
@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)" >>$@
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}"
.\" ----------------------------------------------------------------------------
-.\" 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
.
.
.\" ----------------------------------------------------------------------------
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
$(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:
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
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;
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,
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;
}
.\" ----------------------------------------------------------------------------
-.\" 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
.
.
.\" ----------------------------------------------------------------------------
.\" ----------------------------------------------------------------------------
-.\" 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
.
.
.\" ----------------------------------------------------------------------------
#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"
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) {
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;
/*
* 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!
};
/* 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;
log_safe;
msort;
parse_devt;
- parse_prkey;
process_file;
safe_write;
set_value;
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;
}
}
-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;
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;
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);
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;
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
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)
#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"
#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(×tamp_mutex);
+ if (ret == 0) {
+ ts = st.st_mtim;
+ changed = timespeccmp(&ts, &bindings_last_updated) > 0;
+ }
+ pthread_mutex_unlock(×tamp_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(×tamp_mutex);
+ bindings_last_updated = ts;
+ pthread_mutex_unlock(×tamp_mutex);
+ condlog(1, "updated bindings file %s", bindings_file_path);
+ }
+ return rc;
+}
int
valid_alias(const char *alias)
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",
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,
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)) {
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(×tamp_mutex);
+ bindings_last_updated = ts;
+ pthread_mutex_unlock(×tamp_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;
}
/*
*/
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;
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)
/*
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;
}
#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 */
* Copyright (c) 2004, 2005 Christophe Varoqui
*/
#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
#include <libudev.h>
#include "checkers.h"
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)
{
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;
add_aio_group(void)
{
struct aio_group *aio_grp;
+ int rc;
aio_grp = malloc(sizeof(struct aio_group));
if (!aio_grp)
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;
}
}
- 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);
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 */
} 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;
}
} 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;
}
}
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;
}
MSG_TUR_RUNNING = CHECKER_FIRST_MSGID,
MSG_TUR_TIMEOUT,
MSG_TUR_FAILED,
+ MSG_TUR_TRANSITIONING,
};
#define _IDX(x) (MSG_ ## x - CHECKER_FIRST_MSGID)
[_IDX(TUR_RUNNING)] = " still running",
[_IDX(TUR_TIMEOUT)] = " timed out",
[_IDX(TUR_FAILED)] = " failed to initialize",
+ [_IDX(TUR_TRANSITIONING)] = " reports path is transitioning",
NULL,
};
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;
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;
* 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
/* 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;
}
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) {
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 {
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);
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;
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);
* 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;
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:
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;
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;
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;
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;
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);
/*
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) {
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);
}
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);
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;
}
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;
}
}
}
+ 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:
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
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;
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;
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;
}
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, ¶ms, 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);
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;
}
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 */
ACT_RENAME,
ACT_CREATE,
ACT_RESIZE,
- ACT_FORCERENAME,
+ ACT_RELOAD_RENAME,
ACT_DRY_RUN,
ACT_IMPOSSIBLE,
+ ACT_RESIZE_RENAME,
+ ACT_SWITCHPG_RENAME,
};
/*
#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
#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/"
{
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;
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:
}
vector_set_slot(mp, mpp);
- mpp = NULL;
next:
next = names->next;
names = (void *) names + next;
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)
{
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");
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); \
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)
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)
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)
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)
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, \
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)
{
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)
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)
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
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);
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);
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);
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);
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 */
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)
{
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;
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) {
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)
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 &&
{
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) {
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)
return;
}
pp->tpgs = tpgs;
+ pp->tpg_id = ret;
}
int path_get_tpgs(struct path *pp)
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__);
{
struct udev_device *parent;
const char *attr_path = NULL;
+ static const char unknown[] = "UNKNOWN";
parent = pp->udev;
while (parent) {
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);
/*
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));
}
static int
-get_prio (struct path * pp, int timeout)
+get_prio (struct path * pp)
{
struct prio * p;
struct config *conf;
}
}
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);
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);
*/
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);
}
}
.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,
/* 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 */
.pgfailback = -FAILBACK_IMMEDIATE,
.no_path_retry = 12,
.prio_name = PRIO_ALUA,
+ .checker_name = DIRECTIO,
+ .detect_checker = DETECT_CHECKER_OFF,
},
/*
* DataCore
#include <sys/ioctl.h>
#include <linux/fs.h>
#include <libaio.h>
-#include <errno.h>
#include <sys/mman.h>
#include <sys/select.h>
#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
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)
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);
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);
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) {
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)
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) {
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;
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;
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);
}
}
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]);
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;
}
pthread_join(io_err_stat_thr, NULL);
free_io_err_pathvec();
- io_destroy(ioctx);
}
/*
* 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!
put_multipath_config;
};
-LIBMULTIPATH_17.0.0 {
+LIBMULTIPATH_22.0.0 {
global:
/* symbols referenced by multipath and multipathd */
add_foreign;
checker_name;
checker_state_name;
check_foreign;
+ cleanup_bindings;
cleanup_lock;
coalesce_paths;
count_active_paths;
get_used_hwes;
get_vpd_sgio;
group_by_prio;
+ handle_bindings_file_inotify;
has_dm_info;
init_checkers;
init_config;
/* 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;
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)
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);
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);
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 */
}
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 *))
{
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;
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
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
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},
{'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[] = {
#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)
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);
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)
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 *);
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 */
}
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);
return 1;
}
-int getprio (struct path * pp, char * args, unsigned int timeout)
+int getprio (struct path * pp, char * args)
{
int rc;
int aas;
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);
*/
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;
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!");
}
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;
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));
}
/*
* 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);
}
}
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;
}
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;
}
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;
}
}
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;
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",
}
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;
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;
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;
}
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;
}
#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__ */
* - 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;
#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;
}
#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];
unsigned char sense_buffer[32];
sg_io_hdr_t io_hdr;
- int timeout = 2000;
char preferredsds_buff[255] = "";
char * preferredsds = &preferredsds_buff[0];
}
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 {
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;
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)
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));
}
#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];
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");
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));
}
#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];
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");
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));
}
#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];
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) {
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));
}
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);
}
#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)
*/
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,
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);
* 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];
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, "
* 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;
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) {
}
try_fcp_proxy:
- rc = get_proxy(dev, fd, timeout);
+ rc = get_proxy(dev, fd, timeout_ms);
if (rc >= 0) {
is_proxy = rc;
}
}
}
-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));
}
}
}
-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;
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;
(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;
#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;
#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];
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");
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));
}
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];
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);
}
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;
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);
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) {
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 */
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) \
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;
}
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);
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
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)) {
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;
}
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)) {
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);
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);
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);
pp->dmstate = PSTATE_UNDEF;
pp->uid_attribute = NULL;
+ pp->checker_timeout = 0;
if (checker_selected(&pp->checker))
checker_put(&pp->checker);
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,
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 {
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,
};
#endif
+#define GROUP_ID_UNDEF -1
+
struct path {
char dev[FILE_NAME_SIZE];
char dev_t[BLK_DEV_SIZE];
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;
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;
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;
}
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;
int i;
int priority = 0;
int marginal = 0;
+ int defined_prios = 0;
struct path * pp;
pgp->enabled_paths = 0;
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)
if (r != 1) {
condlog(3, "%s: Cannot parse size attribute", pp->dev);
- *size = 0;
return 1;
}
#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;
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
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;
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;
#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"
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;
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);
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;
{
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;
-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)
$(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))
+++ /dev/null
-.\" ----------------------------------------------------------------------------
-.\" 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
--- /dev/null
+.\" ----------------------------------------------------------------------------
+.\" 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
# 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
# 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
#
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)
OBJS := main.o
-all: $(EXEC) multipath.rules tmpfiles.conf
+all: $(EXEC) $(GENERATED)
$(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
@echo building $@ because of $?
$(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 \
$(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),' $< >$@
" . 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"
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)
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':
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;
}
if (check_alias_settings(conf)) {
- fprintf(stderr, "fatal configuration error, aborting");
+ fprintf(stderr, "fatal configuration error, aborting\n");
exit(RTVL_FAIL);
}
+++ /dev/null
-.\" ----------------------------------------------------------------------------
-.\" 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
--- /dev/null
+.\" ----------------------------------------------------------------------------
+.\" 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
+++ /dev/null
-.\" ----------------------------------------------------------------------------
-.\" 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
--- /dev/null
+.\" ----------------------------------------------------------------------------
+.\" 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
# 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", \
# 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"
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)
#
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 $?
$(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)
}
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;
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, ¶ms, 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
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);
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;
}
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
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);
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));
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);
}
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);
}
}
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)
}
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)
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
/*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*/
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 */
/* 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
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;
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
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);
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);
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
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);
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, ¶ms, vecs)) {
condlog(0, "%s: failed to setup new map in update", mpp->alias);
retries = -1;
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;
int start_waiter = 0;
int ret;
int ro;
+ unsigned char prflag = PRFLAG_UNSET;
/*
* need path UID to go any further
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");
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)
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;
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, ¶ms, 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)
{
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;
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 {
}
}
}
+ 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);
}
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);
+ }
}
}
}
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, ¶ms, vecs)) {
condlog(0, "%s: failed to setup map", mpp->alias);
return 1;
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;
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) ||
}
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);
}
}
*/
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,
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);
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);
}
/*
{
cleanup_threads();
cleanup_vecs();
+ cleanup_bindings();
if (poll_dmevents)
cleanup_dmevent_waiter();
goto out;
}
+ mpp->prflag = PRFLAG_UNSET;
ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, 0);
if (ret != MPATH_PR_SUCCESS )
{
{
condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret);
}
- mpp->prflag = 1;
+ mpp->prflag = PRFLAG_SET;
free(param);
out:
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);
pthread_attr_destroy(&attr);
rc = pthread_join(thread, NULL);
return 0;
+
+no_pr:
+ pp->mpp->prflag = PRFLAG_UNSET;
+ return 0;
}
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 */
.\" ----------------------------------------------------------------------------
-.\" 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
+++ /dev/null
-.\" ----------------------------------------------------------------------------
-.\" 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
--- /dev/null
+.\" ----------------------------------------------------------------------------
+.\" 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
+++ /dev/null
-[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
--- /dev/null
+[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
#include "cli.h"
#include "uxlsnr.h"
#include "strbuf.h"
+#include "alias.h"
/* state of client connection */
enum {
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
struct config *conf;
int dir_reset = 0;
int conf_reset = 0;
+ int mp_reset = 0;
if (notify_fd == -1)
return;
conf_reset = 1;
if (wds->dir_wd == -1)
dir_reset = 1;
+ if (wds->mp_wd == -1)
+ mp_reset = 1;
}
put_multipath_config(conf);
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)
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;
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)
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");
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;
$(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
# 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
#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) {
}
}
-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)
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 == ×tamp_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 == ×tamp_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)
{
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)
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);
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);
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);
}
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);
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);
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");
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);
}
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);
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);
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");
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)
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);
}
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);
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);
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);
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);
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);
}
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);
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);
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);
}
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);
}
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);
}
{
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);
}
{
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);
}
{
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);
}
{
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
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, "");
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");
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, "");
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, "");
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, "");
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");
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);
}
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);
}
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)
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);
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;
}
#include "test-log.h"
#include "debug.h"
+#include "../libmultipath/blacklist.c"
+
struct udev_device {
const char *sysname;
char *property_list[];
{
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");
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
}
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);
}
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);
}
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);
}
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);
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]);
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 */
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);
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);
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);
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;
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"};
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),
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 (!?) */
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));
}
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
}
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);
}
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);
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);
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);
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);
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)
{
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)
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)
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);
enum {
CHECK_MPATHD_RUNNING,
CHECK_MPATHD_EAGAIN,
- CHECK_MPATHD_ENABLED,
CHECK_MPATHD_SKIP,
};
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) {
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);
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);
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)
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),
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)