diff -pruN 0.8.8-1/debian/bug-script 0.9.0-3/debian/bug-script
--- 0.8.8-1/debian/bug-script	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/debian/bug-script	2022-08-02 08:21:25.000000000 +0000
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+CONFIG_FILE=/etc/multipath.conf
+
+exec >&3
+
+if [ -e "$CONFIG_FILE" ]; then
+    printf "Contents of $CONFIG_FILE:\n"
+    grep  -Ev "^([[:space:]]*#|$)" $CONFIG_FILE
+else
+    printf "$CONFIG_FILE does not exist.\n"
+fi
+
+printf "\n"
+
diff -pruN 0.8.8-1/debian/buildconfig.build-deb 0.9.0-3/debian/buildconfig.build-deb
--- 0.8.8-1/debian/buildconfig.build-deb	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/debian/buildconfig.build-deb	2022-08-02 08:21:25.000000000 +0000
@@ -0,0 +1,3 @@
+USE_SYSTEMD=1
+SYSTEMDPATH=/lib
+SCSI_DH_MODULES_PRELOAD=scsi_dh_alua scsi_dh_emc scsi_dh_rdac
diff -pruN 0.8.8-1/debian/buildconfig.build-udeb 0.9.0-3/debian/buildconfig.build-udeb
--- 0.8.8-1/debian/buildconfig.build-udeb	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/debian/buildconfig.build-udeb	2022-08-02 08:21:25.000000000 +0000
@@ -0,0 +1,2 @@
+USE_SYSTEMD=0
+CFLAGS += -static-libgcc
diff -pruN 0.8.8-1/debian/buildconfig.common 0.9.0-3/debian/buildconfig.common
--- 0.8.8-1/debian/buildconfig.common	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/debian/buildconfig.common	2022-08-02 08:21:25.000000000 +0000
@@ -0,0 +1,2 @@
+RUN=run
+LIB=lib
diff -pruN 0.8.8-1/debian/changelog 0.9.0-3/debian/changelog
--- 0.8.8-1/debian/changelog	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/changelog	2022-08-02 08:21:25.000000000 +0000
@@ -1,3 +1,42 @@
+multipath-tools (0.9.0-3) unstable; urgency=medium
+
+  * [baa940a] Install reportbug helper using dh_bugfiles (Closes: #1016512)
+
+ -- Chris Hofstaedtler <zeha@debian.org>  Tue, 02 Aug 2022 08:21:25 +0000
+
+multipath-tools (0.9.0-2) unstable; urgency=medium
+
+  [ Athos Ribeiro ]
+  * [e3e7c47] d/t/kpartx-file-loopback: silence kpartx messages to stderr
+
+  [ Chris Hofstaedtler ]
+  * [057ee38] Apply wrap-and-sort -a
+  * [8c9d6fe] Use debhelper compat level 13
+  * [99f87ed] Use dh_missing
+  * [f7cde4c] Use dh_installsystemd
+  * [6797e0c] d/control: fix Depends/Pre-Depends mixup
+  * [c1cb1ba] Use debhelper sequencer (Closes: #801884)
+  * [eeb7a95] Remove upgrade code from versions before oldoldoldstable.
+    Specifically remove the debconf warning if scsi_id is still used (old
+    udev), and the init script bug fix. Rids us of all custom postinst/prerm
+    scripts.
+  * [8491532] Use only one way of installing manpages
+  * [32fb1aa] Fix typo in README.Debian
+  * [5be350e] Possibly fix multipath in d-i
+  * [c46f47a] Make initramfs scripts +x again
+
+ -- Chris Hofstaedtler <zeha@debian.org>  Sat, 30 Jul 2022 14:31:16 +0000
+
+multipath-tools (0.9.0-1) unstable; urgency=medium
+
+  * [6f546d1] New upstream version 0.9.0
+  * [8ff5696] Refresh patches
+  * [11da4e0] Follow manpages filename changes
+  * [bde7d75] Install new modules-load.d dropin file
+  * [1ac2df6] Avoid setting EXTRAVERSION
+
+ -- Chris Hofstaedtler <zeha@debian.org>  Fri, 29 Jul 2022 22:02:31 +0000
+
 multipath-tools (0.8.8-1) unstable; urgency=medium
 
   [ Utkarsh Gupta ]
diff -pruN 0.8.8-1/debian/control 0.9.0-3/debian/control
--- 0.8.8-1/debian/control	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/control	2022-08-02 08:21:25.000000000 +0000
@@ -2,16 +2,35 @@ Source: multipath-tools
 Section: admin
 Priority: optional
 Maintainer: Debian DM Multipath Team <team+linux-blocks@tracker.debian.org>
-Uploaders: Guido Günther <agx@sigxcpu.org>, Ritesh Raj Sarraf <rrs@debian.org>, Chris Hofstaedtler <zeha@debian.org>
-Build-Depends: debhelper-compat (= 9), po-debconf, libdevmapper-dev, libreadline-dev, libaio-dev, libudev-dev, libsystemd-dev, systemd, liburcu-dev, pkg-config, libjson-c-dev
+Uploaders: Guido Günther <agx@sigxcpu.org>,
+           Ritesh Raj Sarraf <rrs@debian.org>,
+           Chris Hofstaedtler <zeha@debian.org>
+Build-Depends: debhelper-compat (= 13),
+               libaio-dev,
+               libdevmapper-dev,
+               libjson-c-dev,
+               libreadline-dev,
+               libsystemd-dev,
+               libudev-dev,
+               liburcu-dev,
+               pkg-config,
+               systemd
 Vcs-Git: https://salsa.debian.org/linux-blocks-team/multipath-tools.git
 Vcs-Browser: https://salsa.debian.org/linux-blocks-team/multipath-tools
 Standards-Version: 3.9.8
 Homepage: http://christophe.varoqui.free.fr/
+Rules-Requires-Root: no
 
 Package: multipath-tools
 Architecture: linux-any
-Depends: ${shlibs:Depends}, ${misc:Depends}, udev, kpartx (>= ${binary:Version}), lsb-base, libaio1, sg3-utils-udev
+Depends: kpartx (>= ${binary:Version}),
+         libaio1,
+         lsb-base,
+         sg3-utils-udev,
+         udev,
+         ${misc:Depends},
+         ${shlibs:Depends}
+Pre-Depends: ${misc:Pre-Depends}
 Suggests: multipath-tools-boot
 Description: maintain multipath block device access
  These tools are in charge of maintaining the disk multipath device maps and
@@ -22,7 +41,10 @@ Description: maintain multipath block de
 
 Package: kpartx
 Architecture: linux-any
-Depends: ${shlibs:Depends}, ${misc:Depends}, udev, dmsetup
+Depends: dmsetup,
+         udev,
+         ${misc:Depends},
+         ${shlibs:Depends}
 Description: create device mappings for partitions
  Kpartx can be used to set up device mappings for the partitions of any
  partitioned block device.
@@ -32,8 +54,12 @@ Description: create device mappings for
 
 Package: multipath-tools-boot
 Architecture: all
-Depends: ${misc:Depends}, initramfs-tools | linux-initramfs-tool, lsb-base,
- multipath-tools (>= ${source:Version}), multipath-tools (<< ${source:Version}.1~)
+Depends: initramfs-tools | linux-initramfs-tool,
+         lsb-base,
+         multipath-tools (<< ${source:Version}.1~),
+         multipath-tools (>= ${source:Version}),
+         ${misc:Depends}
+Pre-Depends: ${misc:Pre-Depends}
 Description: Support booting from multipath devices
  This package contains the necessary support for booting from a multipath
  device:
@@ -47,7 +73,10 @@ Description: Support booting from multip
 Package: multipath-udeb
 Package-Type: udeb
 Architecture: linux-any
-Depends: ${shlibs:Depends}, ${misc:Depends}, multipath-modules, kpartx-udeb
+Depends: kpartx-udeb,
+         multipath-modules,
+         ${misc:Depends},
+         ${shlibs:Depends}
 Section: debian-installer
 Description: maintain multipath block device access - udeb package
  This is a udeb, or a microdeb, for the debian-installer.
@@ -58,7 +87,8 @@ Description: maintain multipath block de
 Package: kpartx-udeb
 Package-Type: udeb
 Architecture: linux-any
-Depends: ${shlibs:Depends}, ${misc:Depends}
+Depends: ${misc:Depends},
+         ${shlibs:Depends}
 Section: debian-installer
 Description: create device mappings for partitions - udeb package
  This is a udeb, or a microdeb, for the debian-installer.
diff -pruN 0.8.8-1/debian/copyright 0.9.0-3/debian/copyright
--- 0.8.8-1/debian/copyright	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/copyright	2022-08-02 08:21:25.000000000 +0000
@@ -9,4 +9,3 @@ libdevmapper is licensed under the GNU L
 On Debian systems, the complete text of the GNU General Public License
 and of the GNU Lesser General Public License can be found in
 /usr/share/common-licenses/GPL-2 and /usr/share/common-licenses/LGPL-2 .
-
diff -pruN 0.8.8-1/debian/d-i/multipath.conf 0.9.0-3/debian/d-i/multipath.conf
--- 0.8.8-1/debian/d-i/multipath.conf	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/debian/d-i/multipath.conf	2022-08-02 08:21:25.000000000 +0000
@@ -0,0 +1,4 @@
+defaults {
+    find_multipaths yes
+    user_friendly_names yes
+}
File 0.8.8-1/debian/initramfs/hooks is a regular file while file 0.9.0-3/debian/initramfs/hooks is a directory
diff -pruN 0.8.8-1/debian/initramfs/init-top 0.9.0-3/debian/initramfs/init-top
--- 0.8.8-1/debian/initramfs/init-top	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/initramfs/init-top	1970-01-01 00:00:00.000000000 +0000
@@ -1,44 +0,0 @@
-#!/bin/sh
-#
-# multipath hardware handler
-
-PREREQ=" "
-
-prereqs() { echo "$PREREQ"; }
-
-case $1 in
-prereqs)
-	prereqs
-	exit 0
-	;;
-esac
-
-. /scripts/functions
-
-verbose()
-{
-  case "$quiet" in y*|Y*|1|t*|T*)
-    return 1;;
-  *)
-    return 0;;
-  esac
-}
-
-maybe_break pre-multipath
-VERBOSITY=0
-HW_HANDLERS=""
-
-verbose && log_begin_msg "Loading multipath hardware handlers"
-for module in ${HW_HANDLERS}; do
-  if modprobe --syslog "$module"; then
-    verbose && log_success_msg "loaded module ${module}."
-  else
-    log_failure_msg "failed to load module ${module}."
-  fi
-done
-verbose && log_end_msg
-
-maybe_break post-multipath
-
-exit 0
-
diff -pruN 0.8.8-1/debian/initramfs/local-top 0.9.0-3/debian/initramfs/local-top
--- 0.8.8-1/debian/initramfs/local-top	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/initramfs/local-top	1970-01-01 00:00:00.000000000 +0000
@@ -1,60 +0,0 @@
-#!/bin/sh
-#
-# multipath discovery
-
-PREREQ="udev iscsi"
-
-prereqs() { echo "$PREREQ"; }
-
-case $1 in
-prereqs)
-	prereqs
-	exit 0
-	;;
-esac
-
-. /scripts/functions
-
-verbose()
-{
-  case "$quiet" in y*|Y*|1|t*|T*)
-    return 1;;
-  *)
-    return 0;;
-  esac
-}
-
-maybe_break pre-multipath
-VERBOSITY=0
-MP_MODULES="scsi_dh_alua scsi_dh_emc scsi_dh_rdac dm-multipath"
-
-if [ ! -e /sbin/multipath ]; then
-	exit 0
-fi
-
-verbose && log_begin_msg "Loading multipath modules"
-for module in ${MP_MODULES}; do
-  if modprobe "$module"; then
-    verbose && log_success_msg "loaded module ${module}."
-  else
-    log_failure_msg "failed to load module ${module}."
-  fi
-done
-verbose && log_end_msg
-
-verbose && log_begin_msg "Discovering multipaths"
-/sbin/multipath -v $VERBOSITY
-verbose && log_end_msg
-
-if [ -x /sbin/kpartx -a -x /sbin/dmsetup ]; then
-    /sbin/dmsetup ls --target multipath --exec "/sbin/kpartx -a -p -part" >/dev/null
-fi
-
-if [ -x /bin/udevadm ]; then
-    /bin/udevadm settle --timeout=10
-fi
-
-maybe_break post-multipath
-
-exit 0
-
diff -pruN 0.8.8-1/debian/initramfs/scripts/init-top/multipath 0.9.0-3/debian/initramfs/scripts/init-top/multipath
--- 0.8.8-1/debian/initramfs/scripts/init-top/multipath	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/debian/initramfs/scripts/init-top/multipath	2022-08-02 08:21:25.000000000 +0000
@@ -0,0 +1,44 @@
+#!/bin/sh
+#
+# multipath hardware handler
+
+PREREQ=" "
+
+prereqs() { echo "$PREREQ"; }
+
+case $1 in
+prereqs)
+	prereqs
+	exit 0
+	;;
+esac
+
+. /scripts/functions
+
+verbose()
+{
+  case "$quiet" in y*|Y*|1|t*|T*)
+    return 1;;
+  *)
+    return 0;;
+  esac
+}
+
+maybe_break pre-multipath
+VERBOSITY=0
+HW_HANDLERS=""
+
+verbose && log_begin_msg "Loading multipath hardware handlers"
+for module in ${HW_HANDLERS}; do
+  if modprobe --syslog "$module"; then
+    verbose && log_success_msg "loaded module ${module}."
+  else
+    log_failure_msg "failed to load module ${module}."
+  fi
+done
+verbose && log_end_msg
+
+maybe_break post-multipath
+
+exit 0
+
diff -pruN 0.8.8-1/debian/initramfs/scripts/local-top/multipath 0.9.0-3/debian/initramfs/scripts/local-top/multipath
--- 0.8.8-1/debian/initramfs/scripts/local-top/multipath	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/debian/initramfs/scripts/local-top/multipath	2022-08-02 08:21:25.000000000 +0000
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# multipath discovery
+
+PREREQ="udev iscsi"
+
+prereqs() { echo "$PREREQ"; }
+
+case $1 in
+prereqs)
+	prereqs
+	exit 0
+	;;
+esac
+
+. /scripts/functions
+
+verbose()
+{
+  case "$quiet" in y*|Y*|1|t*|T*)
+    return 1;;
+  *)
+    return 0;;
+  esac
+}
+
+maybe_break pre-multipath
+VERBOSITY=0
+MP_MODULES="scsi_dh_alua scsi_dh_emc scsi_dh_rdac dm-multipath"
+
+if [ ! -e /sbin/multipath ]; then
+	exit 0
+fi
+
+verbose && log_begin_msg "Loading multipath modules"
+for module in ${MP_MODULES}; do
+  if modprobe "$module"; then
+    verbose && log_success_msg "loaded module ${module}."
+  else
+    log_failure_msg "failed to load module ${module}."
+  fi
+done
+verbose && log_end_msg
+
+verbose && log_begin_msg "Discovering multipaths"
+/sbin/multipath -v $VERBOSITY
+verbose && log_end_msg
+
+if [ -x /sbin/kpartx -a -x /sbin/dmsetup ]; then
+    /sbin/dmsetup ls --target multipath --exec "/sbin/kpartx -a -p -part" >/dev/null
+fi
+
+if [ -x /bin/udevadm ]; then
+    /bin/udevadm settle --timeout=10
+fi
+
+maybe_break post-multipath
+
+exit 0
+
diff -pruN 0.8.8-1/debian/kpartx.install 0.9.0-3/debian/kpartx.install
--- 0.8.8-1/debian/kpartx.install	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/kpartx.install	2022-08-02 08:21:25.000000000 +0000
@@ -1,3 +1,3 @@
-/usr/share/man/man8/kpartx.8.gz
-/sbin/kpartx
 /lib/udev/kpartx_id
+/sbin/kpartx
+/usr/share/man/man8/kpartx.8*
diff -pruN 0.8.8-1/debian/kpartx.manpages 0.9.0-3/debian/kpartx.manpages
--- 0.8.8-1/debian/kpartx.manpages	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/kpartx.manpages	1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-kpartx/kpartx.8
diff -pruN 0.8.8-1/debian/multipath-tools-boot.config 0.9.0-3/debian/multipath-tools-boot.config
--- 0.8.8-1/debian/multipath-tools-boot.config	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/multipath-tools-boot.config	1970-01-01 00:00:00.000000000 +0000
@@ -1,13 +0,0 @@
-#!/bin/sh
-
-set -e
-
-CONF=/etc/multipath.conf
-
-# if the user still references /sbin/scsi_id warn him:
-if [ -e "$CONF" ] && grep -sq '^[[:space:]]*getuid_callout[[:space:]]*\"/sbin/scsi_id[[:space:]]\+' $CONF; then
-    . /usr/share/debconf/confmodule
-    db_input critical multipath-tools-boot/scsi_id || true
-    db_go || true
-fi
-
diff -pruN 0.8.8-1/debian/multipath-tools-boot.install 0.9.0-3/debian/multipath-tools-boot.install
--- 0.8.8-1/debian/multipath-tools-boot.install	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/debian/multipath-tools-boot.install	2022-08-02 08:21:25.000000000 +0000
@@ -0,0 +1,2 @@
+debian/initramfs/hooks usr/share/initramfs-tools/
+debian/initramfs/scripts usr/share/initramfs-tools/
diff -pruN 0.8.8-1/debian/multipath-tools-boot.postinst 0.9.0-3/debian/multipath-tools-boot.postinst
--- 0.8.8-1/debian/multipath-tools-boot.postinst	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/multipath-tools-boot.postinst	1970-01-01 00:00:00.000000000 +0000
@@ -1,34 +0,0 @@
-#!/bin/sh
-# postinst script for multipath-tools
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# Source debconf library.
-. /usr/share/debconf/confmodule
-
-case "$1" in
-    configure)
-	if [ -x /usr/sbin/update-initramfs -a -e /etc/initramfs-tools/initramfs.conf ]; then
-		update-initramfs -u
-	fi
-    ;;
-
-    abort-upgrade|abort-remove|abort-deconfigure)
-    ;;
-
-    *)
-        echo "postinst called with unknown argument \`$1'" >&2
-        exit 1
-    ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
-
-
diff -pruN 0.8.8-1/debian/multipath-tools-boot.postrm 0.9.0-3/debian/multipath-tools-boot.postrm
--- 0.8.8-1/debian/multipath-tools-boot.postrm	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/multipath-tools-boot.postrm	1970-01-01 00:00:00.000000000 +0000
@@ -1,45 +0,0 @@
-#!/bin/sh
-# postrm script for multipath-tools-boot
-#
-# see: dh_installdeb(1)
-
-set -e
-
-# summary of how this script can be called:
-#        * <postrm> `remove'
-#        * <postrm> `purge'
-#        * <old-postrm> `upgrade' <new-version>
-#        * <new-postrm> `failed-upgrade' <old-version>
-#        * <new-postrm> `abort-install'
-#        * <new-postrm> `abort-install' <old-version>
-#        * <new-postrm> `abort-upgrade' <old-version>
-#        * <disappearer's-postrm> `disappear' <overwriter>
-#          <overwriter-version>
-# for details, see http://www.debian.org/doc/debian-policy/ or
-# the debian-policy package
-
-
-case "$1" in
-    remove)
-	if [ -x /usr/sbin/update-initramfs -a -e /etc/initramfs-tools/initramfs.conf ]; then
-		update-initramfs -u
-	fi
-    ;;
-
-    purge|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
-    ;;
-
-    *)
-        echo "postrm called with unknown argument \`$1'" >&2
-        exit 1
-    ;;
-esac
-
-# dh_installdeb will replace this with shell code automatically
-# generated by other debhelper scripts.
-
-#DEBHELPER#
-
-exit 0
-
-
diff -pruN 0.8.8-1/debian/multipath-tools-boot.templates 0.9.0-3/debian/multipath-tools-boot.templates
--- 0.8.8-1/debian/multipath-tools-boot.templates	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/multipath-tools-boot.templates	1970-01-01 00:00:00.000000000 +0000
@@ -1,10 +0,0 @@
-Template: multipath-tools-boot/scsi_id
-Type: note
-_Description: The location of the getuid callout has changed
- Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/scsi_id
- but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. Please update
- your configuration. This is best done by removing the getuid_callout option
- entirely.
- .
- Don't forget to update your initramfs after these changes. Otherwise your
- system might not boot from multipath.
diff -pruN 0.8.8-1/debian/multipath-tools.install 0.9.0-3/debian/multipath-tools.install
--- 0.8.8-1/debian/multipath-tools.install	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/multipath-tools.install	2022-08-02 08:21:25.000000000 +0000
@@ -1,16 +1,17 @@
-/sbin/multi*
-/sbin/mpathpersist
 /lib/libdmmp.so*
-/lib/pkgconfig/libdmmp.pc
-/lib/multipath/*
-/lib/libmultipath.so*
 /lib/libmpathcmd.so*
 /lib/libmpathpersist.so*
-/usr/share/man/man*/multi*gz
-/usr/share/man/man*/mpath*gz
-/usr/share/man/man*/*dmmp*gz
-/usr/include/mpath_cmd.h
-/usr/include/mpath_persist.h
-/usr/include/libdmmp/libdmmp.h
+/lib/libmultipath.so*
+/lib/modules-load.d/*.conf
+/lib/multipath/*
+/lib/pkgconfig/libdmmp.pc
 /lib/systemd/system/multipathd.service
 /lib/systemd/system/multipathd.socket
+/sbin/mpathpersist
+/sbin/multi*
+/usr/include/libdmmp/libdmmp.h
+/usr/include/mpath_cmd.h
+/usr/include/mpath_persist.h
+/usr/share/man/man*/*dmmp*
+/usr/share/man/man*/mpath*
+/usr/share/man/man*/multi*
diff -pruN 0.8.8-1/debian/multipath-tools.links 0.9.0-3/debian/multipath-tools.links
--- 0.8.8-1/debian/multipath-tools.links	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/multipath-tools.links	2022-08-02 08:21:25.000000000 +0000
@@ -1,2 +1,2 @@
-/lib/libmpathpersist.so.0 /lib/libmpathpersist.so
 /dev/null /lib/systemd/system/multipath-tools-boot.service
+/lib/libmpathpersist.so.0 /lib/libmpathpersist.so
diff -pruN 0.8.8-1/debian/multipath-tools.prerm 0.9.0-3/debian/multipath-tools.prerm
--- 0.8.8-1/debian/multipath-tools.prerm	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/multipath-tools.prerm	1970-01-01 00:00:00.000000000 +0000
@@ -1,22 +0,0 @@
-#!/bin/sh
-#
-# multipath-tools prerm script
-set -e
-
-FIXED=0.4.8-1
-# fix upgrades from versions << $FIXED where the init script would return an
-# error on stop when the daemon isn't running:
-if [ "$1" = "failed-upgrade" ] && dpkg --compare-versions "$2" lt "$FIXED"; then
-    if [ -x "/etc/init.d/multipath-tools" ]; then
-	echo "Ignoring broken stop target in versions << $FIXED..."
-	if [ -x "`which invoke-rc.d 2>/dev/null`" ]; then
-		invoke-rc.d multipath-tools stop || true
-	else
-		/etc/init.d/multipath-tools stop || true
-	fi
-	exit 0
-    fi
-fi
-
-#DEBHELPER#
-
diff -pruN 0.8.8-1/debian/multipath-tools.README.Debian 0.9.0-3/debian/multipath-tools.README.Debian
--- 0.8.8-1/debian/multipath-tools.README.Debian	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/multipath-tools.README.Debian	2022-08-02 08:21:25.000000000 +0000
@@ -4,7 +4,7 @@ Additional information for users of mult
 LVM over DM-Multipath
 =====================
 
-Debigu Bug #1001710
+Debian Bug #1001710
 
 On a setup that involves both, LVM and DM-Multipath, it is important to set the
 right filter in lvm.conf file to ensure that LVM does not acquire the bare SCSI devices
@@ -59,9 +59,9 @@ guidance on appropriate values for this
 
 
 
-
 What are these weird numbers in /dev/mapper?
 ============================================
+
 Modern Fibre-Channel connected storage devices are identified by
 World-Wide-Numbers. If multipath-tools detects one of these devices,
 it uses that as the default identifier in /dev/mapper.
@@ -101,6 +101,7 @@ For that, please follow the following st
 
 Where did my FC-connected filesystem go?
 ========================================
+
 If you were previously mounting a device connected to your system by
 Fibre Channel and then installed multipath-tools, you need to change
 the way you mount the device. The device must now be accessed using
@@ -125,6 +126,7 @@ See the output of 'multipath -l' to conf
 
 Preventing multipath-tools mapping any devices
 ==============================================
+
 You may wish to prevent multipath-tools mapping any of your storage devices.
 To do this replace the contents of /etc/multipath.conf with:
 
@@ -134,6 +136,7 @@ To do this replace the contents of /etc/
 
 Booting from multipathed devices
 ================================
+
 To enable booting from multipathed devices install the multipath-tools-boot
 package. Be sure to update your bootloader configuration (e.g.
 /boot/grub/menu.lst) to point to the multipathed root device afterwards.
@@ -154,4 +157,3 @@ updated version of the configuration get
 
 -- 
 [1] https://bugzilla.redhat.com/show_bug.cgi?id=445268
-
diff -pruN 0.8.8-1/debian/multipath-udeb.install 0.9.0-3/debian/multipath-udeb.install
--- 0.8.8-1/debian/multipath-udeb.install	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/multipath-udeb.install	2022-08-02 08:21:25.000000000 +0000
@@ -1,4 +1,8 @@
-/sbin/multipath
-/lib/multipath/
-/lib/libmultipath.so.*
-/lib/libmpathcmd.so*
+debian/tmp-multipath-udeb/lib/libmpathcmd.so* lib/
+debian/tmp-multipath-udeb/lib/libmultipath.so.* lib/
+debian/tmp-multipath-udeb/lib/multipath/ lib/
+debian/tmp-multipath-udeb/sbin/multipath sbin/
+# maybe enough to make multipath find devices
+debian/d-i/multipath.conf etc/
+lib/udev/rules.d/11-dm-mpath.rules
+lib/udev/rules.d/56-multipath.rules
diff -pruN 0.8.8-1/debian/not-installed 0.9.0-3/debian/not-installed
--- 0.8.8-1/debian/not-installed	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/debian/not-installed	2022-08-02 08:21:25.000000000 +0000
@@ -0,0 +1,8 @@
+lib/udev/rules.d/11-dm-mpath.rules
+lib/udev/rules.d/11-dm-parts.rules
+lib/udev/rules.d/56-multipath.rules
+lib/udev/rules.d/66-kpartx.rules
+lib/udev/rules.d/68-del-part-nodes.rules
+debian/tmp-multipath-udeb/usr/lib/modules-load.d/multipath.conf
+lib/libmpathvalid.so*
+usr/include/mpath_valid.h
diff -pruN 0.8.8-1/debian/patches/0002-Update-build-flags.patch 0.9.0-3/debian/patches/0002-Update-build-flags.patch
--- 0.8.8-1/debian/patches/0002-Update-build-flags.patch	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/patches/0002-Update-build-flags.patch	2022-08-02 08:21:25.000000000 +0000
@@ -2,21 +2,47 @@ From: Debian DM Multipath Team <team+lin
 Date: Wed, 23 Dec 2020 22:52:02 +0000
 Subject: Update build flags
 
-We have two missions:
+We have three missions:
 
 1) ensure we build using the environment-supplied flags, especially
    LDFLAGS, CFLAGS, CPPFLAGS
 
 2) disable -Werror, as this can just break with newer GCC versions.
+
+3) remove EXTRAVERSION, as our git revision is not interesting.
 ---
- Makefile.inc | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
+ Makefile.inc | 14 +++-----------
+ 1 file changed, 3 insertions(+), 11 deletions(-)
 
 diff --git a/Makefile.inc b/Makefile.inc
-index b340f2a..24c0c3a 100644
+index bcd2212..118e3ef 100644
 --- a/Makefile.inc
 +++ b/Makefile.inc
-@@ -99,10 +99,10 @@ WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered -Wno-error=clobbered,)
+@@ -13,7 +13,7 @@
+ # SCSI_DH_MODULES_PRELOAD := scsi_dh_alua scsi_dh_rdac
+ SCSI_DH_MODULES_PRELOAD :=
+ 
+-EXTRAVERSION := $(shell rev=$$(git rev-parse --short=7 HEAD 2>/dev/null); echo $${rev:+-g$$rev})
++EXTRAVERSION :=
+ 
+ PKGCONFIG	?= pkg-config
+ 
+@@ -122,14 +122,6 @@ __HASH__ := \#
+ # Check if _DFORTIFY_SOURCE=3 is supported.
+ # On some distros (e.g. Debian Buster) it will be falsely reported as supported
+ # but it doesn't seem to make a difference wrt the compilation result.
+-FORTIFY_OPT := $(shell \
+-	if /bin/echo -e '$(__HASH__)include <string.h>\nint main(void) { return 0; }' | \
+-		$(CC) -o /dev/null -c -O2 -Werror -D_FORTIFY_SOURCE=3 -xc - 2>/dev/null; \
+-	then \
+-		echo "-D_FORTIFY_SOURCE=3"; \
+-	else \
+-		echo "-D_FORTIFY_SOURCE=2"; \
+-	fi)
+ 
+ STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)
+ ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,)
+@@ -137,10 +129,10 @@ WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered -Wno-error=clobbered,)
  WFORMATOVERFLOW := $(call TEST_CC_OPTION,-Wformat-overflow=2,)
  
  OPTFLAGS	:= -O2 -g $(STACKPROT) --param=ssp-buffer-size=4
@@ -24,8 +50,8 @@ index b340f2a..24c0c3a 100644
 +WARNFLAGS	:= -Wall -Wextra -Wformat=2 $(WFORMATOVERFLOW) -Werror=implicit-int \
  		  -Werror=implicit-function-declaration -Werror=format-security \
  		  $(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS)
--CPPFLAGS	:= -Wp,-D_FORTIFY_SOURCE=2
-+CPPFLAGS	:= $(shell dpkg-buildflags --get CPPFLAGS) -Wp,-D_FORTIFY_SOURCE=2
- CFLAGS		:= --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe \
- 		   -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \
- 		   -MMD -MP
+-CPPFLAGS	:= $(FORTIFY_OPT) \
++CPPFLAGS	:= $(CPPFLAGS) \
+ 		   -DBIN_DIR=\"$(bindir)\" -DMULTIPATH_DIR=\"$(plugindir)\" -DRUN_DIR=\"${RUN}\" \
+ 		   -DCONFIG_DIR=\"$(configdir)\" -DEXTRAVERSION=\"$(EXTRAVERSION)\" -MMD -MP
+ CFLAGS		:= --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe
diff -pruN 0.8.8-1/debian/patches/0004-systemd-alias-multipath-service.patch 0.9.0-3/debian/patches/0004-systemd-alias-multipath-service.patch
--- 0.8.8-1/debian/patches/0004-systemd-alias-multipath-service.patch	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/patches/0004-systemd-alias-multipath-service.patch	2022-08-02 08:21:25.000000000 +0000
@@ -8,7 +8,7 @@ Add Alias of multipath-tools because our
  1 file changed, 1 insertion(+)
 
 diff --git a/multipathd/multipathd.service b/multipathd/multipathd.service
-index 1919b38..0e60fb8 100644
+index aec62db..a4a3265 100644
 --- a/multipathd/multipathd.service
 +++ b/multipathd/multipathd.service
 @@ -23,3 +23,4 @@ TasksMax=infinity
diff -pruN 0.8.8-1/debian/patches/0005-udebs-disable-systemd.patch 0.9.0-3/debian/patches/0005-udebs-disable-systemd.patch
--- 0.8.8-1/debian/patches/0005-udebs-disable-systemd.patch	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/patches/0005-udebs-disable-systemd.patch	2022-08-02 08:21:25.000000000 +0000
@@ -8,7 +8,7 @@ D-I does not support systemd. So our ude
  1 file changed, 3 insertions(+), 1 deletion(-)
 
 diff --git a/Makefile.inc b/Makefile.inc
-index 24c0c3a..04370d7 100644
+index 118e3ef..7a46dbf 100644
 --- a/Makefile.inc
 +++ b/Makefile.inc
 @@ -37,7 +37,8 @@ ifndef RUN
diff -pruN 0.8.8-1/debian/patches/0008-Bug-916521-FTCBFS-uses-the-wrong-pkg-config.patch 0.9.0-3/debian/patches/0008-Bug-916521-FTCBFS-uses-the-wrong-pkg-config.patch
--- 0.8.8-1/debian/patches/0008-Bug-916521-FTCBFS-uses-the-wrong-pkg-config.patch	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/patches/0008-Bug-916521-FTCBFS-uses-the-wrong-pkg-config.patch	2022-08-02 08:21:25.000000000 +0000
@@ -11,12 +11,12 @@ dh_auto_build and cross builds successfu
  1 file changed, 2 insertions(+), 1 deletion(-)
 
 diff --git a/Makefile.inc b/Makefile.inc
-index 04370d7..a9cebb8 100644
+index 7a46dbf..cb8b18a 100644
 --- a/Makefile.inc
 +++ b/Makefile.inc
-@@ -15,7 +15,8 @@
- # Uncomment to disable dmevents polling support
- # ENABLE_DMEVENTS_POLL = 0
+@@ -15,7 +15,8 @@ SCSI_DH_MODULES_PRELOAD :=
+ 
+ EXTRAVERSION :=
  
 -PKGCONFIG	?= pkg-config
 +PKG_CONFIG	?= pkg-config
diff -pruN 0.8.8-1/debian/po/cs.po 0.9.0-3/debian/po/cs.po
--- 0.8.8-1/debian/po/cs.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/cs.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,49 +0,0 @@
-# Translation of multipath-tools debconf templates to Czech
-# Copyright (C) 2009 Debian Czech l10n team <debian-l10n-czech@lists.debian.org>
-# This file is distributed under the same license as the multipath-tools package.
-#
-# Translators:
-# Tomas Fidler <tomas.fidler@tiscali.cz>, 2009.
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools 0.4.8-15\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2009-06-17 14:00+0200\n"
-"Last-Translator: Tomas Fidler <tomas.fidler@tiscali.cz>\n"
-"Language-Team: Czech <debian-l10n-czech@lists.debian.org>\n"
-"Language: cs\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "Změnilo se umístění getuid callout"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"Soubor /etc/multipath.conf v parametru  „getuid_callout“ odkazuje na  /sbin/"
-"scsi_id. Tento soubor se v udev verze 0.113 a vyšší přesunul do /lib/udev/"
-"scsi_id. Prosím aktualizujte Váš konfigurační soubor. Nejjednodušší cestou "
-"pro aktualizaci je odstranění parametru getuid_callout ze souboru /etc/"
-"multipath.conf."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"Nezapomeňte po provedené změně aktualizovat initramfs. V opačném případě se "
-"nemusí podařit start systému z multipath zařízení."
diff -pruN 0.8.8-1/debian/po/da.po 0.9.0-3/debian/po/da.po
--- 0.8.8-1/debian/po/da.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/da.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,47 +0,0 @@
-# Danish translation multipath-tools.
-# Copyright (C) multipath-tools & nedenstående oversættere.
-# This file is distributed under the same license as the multipath-tools package.
-# Joe Hansen (joedalton2@yahoo.dk), 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2011-05-28 18:30+01:00\n"
-"Last-Translator: Joe Hansen <joedalton2@yahoo.dk>\n"
-"Language-Team: Danish <debian-l10n-danish@lists.debian.org>\n"
-"Language: da\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "Lokationen af getuid callout er blevet ændret"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"Din /etc/multipath.conf har stadig en getuid_callout, der peger på /sbin/"
-"scsi_id men den binære fil er flyttet til /lib/udev/scsi_id i udev 0.113-1. "
-"Opdater venligst din konfiguration. Dette gøres bedst ved at fjerne "
-"indstillingen getuid_callout fuldstændig."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"Glem ikke at opdatere din initramfs efter disse ændringer. Ellers vil dit "
-"system måske ikke starte op fra multipath."
diff -pruN 0.8.8-1/debian/po/de.po 0.9.0-3/debian/po/de.po
--- 0.8.8-1/debian/po/de.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/de.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,46 +0,0 @@
-# Translation of multipath-tools debconf templates to German
-# Copyright (C) Helge Kreutzmann <debian@helgefjell.de>, 2008.
-# This file is distributed under the same license as the multipath-tools package.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools 0.4.8-9\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2008-05-20 20:37+0200\n"
-"Last-Translator: Helge Kreutzmann <debian@helgefjell.de>\n"
-"Language-Team: de <debian-l10n-german@lists.debian.org>\n"
-"Language: de\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "Der Ort des Getuid-Callouts ist geändert worden"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"In Ihrer /etc/multipath.conf weist ein getuid_callout auf /sbin/scsi_id, das "
-"Programm wurde aber in Udev 0.113-1 nach /lib/udev/scsi_id verschoben. Bitte "
-"aktualisieren Sie Ihre Konfiguration. Dies erfolgt am besten durch das "
-"komplette Entfernen der Option getuid_callout."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"Vergessen Sie nicht, nach dieser Änderung Ihr Initramfs zu aktualisieren. "
-"Andernfalls könnte Ihr System nicht vom Multipath aus starten."
diff -pruN 0.8.8-1/debian/po/es.po 0.9.0-3/debian/po/es.po
--- 0.8.8-1/debian/po/es.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/es.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,75 +0,0 @@
-# multipath-tools po-debconf translation to Spanish.
-# Copyright (C) 2009 Software in the Public Interest.
-# This file is distributed under the same license as the multipath-tools package.
-#
-#  Changes:
-# - Initial translation
-#       Fernando González de Requena <fgrequena@gmail.com>, 2009.
-#
-#
-#  Traductores, si no conoce el formato PO, merece la pena leer la
-#  documentación de gettext, especialmente las secciones dedicadas a este
-#  formato, por ejemplo ejecutando:
-#         info -n '(gettext)PO Files'
-#         info -n '(gettext)Header Entry'
-#
-# Equipo de traducción al español, por favor lean antes de traducir
-# los siguientes documentos:
-#
-# - El proyecto de traducción de Debian al español
-#   http://www.debian.org/intl/spanish/
-#   especialmente las notas y normas de traducción en
-#   http://www.debian.org/intl/spanish/notas
-#
-# - La guía de traducción de po's de debconf:
-#   /usr/share/doc/po-debconf/README-trans
-#   o http://www.debian.org/intl/l10n/po-debconf/README-trans
-#
-# Si tiene dudas o consultas sobre esta traducción consulte con el último
-# traductor (campo Last-Translator) y ponga en copia a la lista de
-# traducción de Debian al español (<debian-l10n-spanish@lists.debian.org>)
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools 0.4.8-15\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2009-05-04 18:22+0200\n"
-"Last-Translator: Fernando González de Requena <fgrequena@gmail.com>\n"
-"Language-Team: Spanish <debian-l10n-spanish@lists.debian.org>\n"
-"Language: es\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: KBabel 1.11.4\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "Ha cambiado el lugar del «getuid callout»"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"Su archivo «/etc/multipath.conf» todavía tiene un campo «getuid_callout» "
-"apuntando a «/sbin/scsi_id», pero en udev 0.113-1 el binario se ha "
-"trasladado a «/lib/udev/scsi_id». Actualice su configuración. La mejor mejor "
-"manera de hacerlo es eliminar completamente la opción «getuid_callout»."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"No olvide actualizar su initramfs tras estos cambios. De otro modo su "
-"sistema podría no arrancar desde multipath."
diff -pruN 0.8.8-1/debian/po/fr.po 0.9.0-3/debian/po/fr.po
--- 0.8.8-1/debian/po/fr.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/fr.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,51 +0,0 @@
-# translation of multipath-tools debconf templates to French
-# Copyright (C) Jean Guillou <guillou.jean@free.fr>
-# This file is distributed under the same license as the multipath-tools package
-#
-# Jean Guillou <guillou.jean@free.fr>, 2008.
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2008-05-19 11:20+0200\n"
-"Last-Translator: Jean Guillou <guillou.jean@free.fr>\n"
-"Language-Team: French <debian-l10n-french@lists.debian.org>\n"
-"Language: fr\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: KBabel 1.11.4\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "Modification de la valeur du champ « getuid_callout »"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"Dans le fichier /etc/multipath.conf la valeur actuelle du champ « "
-"getuid_callout » est toujours /sbin/scsi_id mais l'adresse de l'exécutable "
-"est /lib/udev/scsi_id dans udev 0.113-1. Il est impératif de modifier le "
-"fichier de configuration. La solution suggérée est la suppression de cette "
-"option."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"Veuillez noter que vous devrez mettre à jour le système de fichiers initial "
-"en mémoire (« initramfs ») après ces changements. En l'absence de cette "
-"action, le système pourrait refuser de démarrer depuis un périphérique "
-"multichemins (« multipath »)."
diff -pruN 0.8.8-1/debian/po/it.po 0.9.0-3/debian/po/it.po
--- 0.8.8-1/debian/po/it.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/it.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,48 +0,0 @@
-# Italian translation of multipath-tools debconf messages
-# Copyright (C) 2013, multipath-tools package copyright holder
-# This file is distributed under the same license as the multipath-tools package.
-# Beatrice Torracca <beatricet@libero.it>, 2013.
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2013-11-17 14:22+0200\n"
-"Last-Translator: Beatrice Torracca <beatricet@libero.it>\n"
-"Language-Team: Italian <debian-l10n-italian@lists.debian.org>\n"
-"Language: it\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Virtaal 0.7.1\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "La posizione di getuid_callout è cambiata"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"Il file /etc/multipath.conf nel sistema ha ancora un getuid_callout che "
-"punta a /sbin/scsi_id, ma il binario è stato spostato in /lib/udev/scsi_id "
-"con udev 0.113-1. Aggiornare la propria configurazione; il modo migliore per "
-"farlo è quello di rimuovere del tutto l'opzione getuid_callout."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"Non dimenticarsi di aggiornare il proprio initramfs dopo queste modifiche, "
-"altrimenti il sistema potrebbe non avviarsi da multipath."
diff -pruN 0.8.8-1/debian/po/ja.po 0.9.0-3/debian/po/ja.po
--- 0.8.8-1/debian/po/ja.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/ja.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,45 +0,0 @@
-# Copyright (C) 2008-2009 Debian LVM Team <pkg-lvm-maintainers@lists.alioth.debian.org>
-# This file is distributed under the same license as the multipath-tools package.
-# Hideki Yamane (Debian-JP) <henrich@debian.or.jp>, 2009.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools 0.4.8-13\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2009-01-06 06:26+0900\n"
-"Last-Translator: Hideki Yamane (Debian-JP) <henrich@debian.or.jp>\n"
-"Language-Team: Japanese <debian-japanese@lists.debian.org>\n"
-"Language: ja\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "getuid callout の位置が変更されました"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"/etc/multipath.conf には /sbin/scsi_id をさしている getuid_callout があります"
-"が、バイナリは udev 0.113-1 で /lib/udeb/scsi_id に移動しました。設定を更新し"
-"てください。完全に getuid_callout オプションを削除するのが一番良い方法です。"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"この変更を行った後で initramfs をアップデートするのを忘れないようにしてくださ"
-"い。そうしないと、システムはマルチパスから起動しなくなります。"
diff -pruN 0.8.8-1/debian/po/nl.po 0.9.0-3/debian/po/nl.po
--- 0.8.8-1/debian/po/nl.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/nl.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,47 +0,0 @@
-# Dutch translation of multipath-tools debconf templates.
-# Copyright (C) 2012 THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the multipath-tools package.
-# Jeroen Schot <schot@a-eskwadraat.nl>, 2012.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools 0.4.9-3\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2012-01-11 12:21+0100\n"
-"Last-Translator: Jeroen Schot <schot@a-eskwadraat.nl>\n"
-"Language-Team: Debian l10n Dutch <debian-l10n-dutch@lists.debian.org>\n"
-"Language: nl\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "De locatie van de getuid-callout is gewijzigd."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"In uw /etc/multipath.conf staat nog steeds een getuid_callout die verwijst "
-"naar /sbin/scsi_id, maar het programma is verplaatst naar /lib/udev/scsi_id "
-"in udev 0.113-1. U dient uw configuratie bij te werken. U kunt het best de "
-"optie getuid_callout helemaal weghalen."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"Vergeet niet om uw initramfs bij te werken na deze wijzigingen. Anders kan "
-"uw computer mogelijk niet vanaf multipath opstarten."
diff -pruN 0.8.8-1/debian/po/POTFILES.in 0.9.0-3/debian/po/POTFILES.in
--- 0.8.8-1/debian/po/POTFILES.in	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/POTFILES.in	1970-01-01 00:00:00.000000000 +0000
@@ -1 +0,0 @@
-[type: gettext/rfc822deb] multipath-tools-boot.templates
diff -pruN 0.8.8-1/debian/po/pt_BR.po 0.9.0-3/debian/po/pt_BR.po
--- 0.8.8-1/debian/po/pt_BR.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/pt_BR.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,49 +0,0 @@
-# multipath-tools Brazilian Portuguese translation
-# Copyright (C) 2011 THE multipath-tools COPYRIGHT HOLDER
-# This file is distributed under the same license as the multipath-tools package.
-# Flamarion Jorge <jorge.flamarion@gmail.com>, 2011.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools 0.4.9-2\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2011-06-03 21:07-0300\n"
-"Last-Translator: Flamarion Jorge <jorge.flamarion@gmail.com>\n"
-"Language-Team: Brazilian Portuguese <debian-l10n-portuguese@lists.debian."
-"org>\n"
-"Language: pt_BR\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n > 1);\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "A localização da chamada getuid foi modificada"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"Seu /etc/multipath.conf ainda tem uma opção getuid_callout apontando para "
-"/sbin/scsi_id, mas o binário foi movido para /lib/udev/scsi_id no "
-"udev 0.113-1. Por favor, atualize sua configuração. Isso é feito da melhor "
-"forma removendo a opção getuid_callout inteiramente."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"Não se esqueça de atualizar seu initramfs depois dessas mudanças. Do "
-"contrário seu sistema pode não inicializar pelo multipath."
diff -pruN 0.8.8-1/debian/po/pt.po 0.9.0-3/debian/po/pt.po
--- 0.8.8-1/debian/po/pt.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/pt.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,48 +0,0 @@
-# translation of multipath-tools debconf to Portuguese
-# Copyright (C) 2008 Américo Monteiro
-# This file is distributed under the same license as the multipath-tools package.
-#
-# Américo Monteiro <a_monteiro@netcabo.pt>, 2008.
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools 0.4.8-9\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2008-05-19 19:16+0100\n"
-"Last-Translator: Américo Monteiro <a_monteiro@netcabo.pt>\n"
-"Language-Team: Portuguese <traduz@debianpt.org>\n"
-"Language: pt\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: KBabel 1.11.4\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "A localização de 'getuid callout' mudou"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"O seu /etc/multipath.conf ainda tem o getuid_callout a apontar para /sbin/"
-"scsi_id mas o binário foi movido para /lib/udev/scsi_id no udev 0.113-1. Por "
-"favor actualize a sua configuração. Isto fica melhor se remover "
-"completamente a opção getuid_callout."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"Não se esqueça de actualizar o seu initramfs após estas alterações. De outra "
-"maneira o seu sistema pode não arrancar a partir do multipath."
diff -pruN 0.8.8-1/debian/po/ru.po 0.9.0-3/debian/po/ru.po
--- 0.8.8-1/debian/po/ru.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/ru.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,50 +0,0 @@
-# translation of ru.po to Russian
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-#
-# Yuri Kozlov <kozlov.y@gmail.com>, 2008.
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools 0.4.8-9\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2008-06-15 19:17+0400\n"
-"Last-Translator: Yuri Kozlov <kozlov.y@gmail.com>\n"
-"Language-Team: Russian <debian-l10n-russian@lists.debian.org>\n"
-"Language: ru\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: KBabel 1.11.4\n"
-"Plural-Forms:  nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
-"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "Изменилось расположение getuid callout"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"В имеющемся файле /etc/multipath.conf параметр getuid_callout указывает на /"
-"sbin/scsi_id, но этот двоичный файл был перемещён в /lib/udev/scsi_id (пакет "
-"udev, версия 0.113-1). Обновите вашу настройку. Лучше всего вообще удалить "
-"параметр getuid_callout."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"Не забудьте обновить initramfs после изменения. Иначе система может не "
-"загрузиться с помощью многоканального доступа."
diff -pruN 0.8.8-1/debian/po/sv.po 0.9.0-3/debian/po/sv.po
--- 0.8.8-1/debian/po/sv.po	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/sv.po	1970-01-01 00:00:00.000000000 +0000
@@ -1,49 +0,0 @@
-# translation of multipath-tools_0.4.8-10_sv.po to Swedish
-# Copyright (C) 2008
-# This file is distributed under the same license as the multipath-tools package.
-#
-# Martin Ågren <martin.agren@gmail.com>, 2008.
-msgid ""
-msgstr ""
-"Project-Id-Version: multipath-tools_0.4.8-10_sv\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: 2008-07-23 20:58+0200\n"
-"Last-Translator: Martin Ågren <martin.agren@gmail.com>\n"
-"Language-Team: Swedish <debian-l10n-swedish@lists.debian.org>\n"
-"Language: sv\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"X-Generator: KBabel 1.11.4\n"
-"Plural-Forms:  nplurals=2; plural=(n != 1);\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr "Sökvägen till getuid-utanropet har ändrats"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-"Din /etc/multipath.conf har fortfarande en getuid_callout som pekar på /sbin/"
-"scsi_id men binären har flyttats till /lib/udev/scsi_id i udev 0.113-1. "
-"Uppdatera din konfiguration. Detta görs bäst genom att ta bort "
-"getuid_callout-valet helt och hållet."
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
-"Glöm inte att uppdatera din initramfs efter dessa ändringar. Annars kanske "
-"inte ditt system startar upp från multipath."
diff -pruN 0.8.8-1/debian/po/templates.pot 0.9.0-3/debian/po/templates.pot
--- 0.8.8-1/debian/po/templates.pot	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/po/templates.pot	1970-01-01 00:00:00.000000000 +0000
@@ -1,41 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: multipath-tools@packages.debian.org\n"
-"POT-Creation-Date: 2008-05-17 14:36+0200\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid "The location of the getuid callout has changed"
-msgstr ""
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Your /etc/multipath.conf still has a getuid_callout pointing to /sbin/"
-"scsi_id but the binary has moved to /lib/udev/scsi_id in udev 0.113-1. "
-"Please update your configuration. This is best done by removing the "
-"getuid_callout option entirely."
-msgstr ""
-
-#. Type: note
-#. Description
-#: ../multipath-tools-boot.templates:1001
-msgid ""
-"Don't forget to update your initramfs after these changes. Otherwise your "
-"system might not boot from multipath."
-msgstr ""
diff -pruN 0.8.8-1/debian/reportbug/script 0.9.0-3/debian/reportbug/script
--- 0.8.8-1/debian/reportbug/script	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/reportbug/script	1970-01-01 00:00:00.000000000 +0000
@@ -1,15 +0,0 @@
-#!/bin/sh
-
-CONFIG_FILE=/etc/multipath.conf
-
-exec >&3
-
-if [ -e "$CONFIG_FILE" ]; then
-    printf "Contents of $CONFIG_FILE:\n"
-    grep  -Ev "^([[:space:]]*#|$)" $CONFIG_FILE
-else
-    printf "$CONFIG_FILE does not exist.\n"
-fi
-
-printf "\n"
-
diff -pruN 0.8.8-1/debian/rules 0.9.0-3/debian/rules
--- 0.8.8-1/debian/rules	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/rules	2022-08-02 08:21:25.000000000 +0000
@@ -1,147 +1,76 @@
 #!/usr/bin/make -f
 
-INITRAMFS=$(CURDIR)/debian/multipath-tools-boot/usr/share/initramfs-tools/
-
 # For Hardening
 export DEB_BUILD_MAINT_OPTIONS = hardening=+all
 DPKG_EXPORT_BUILDFLAGS = 1
 include /usr/share/dpkg/buildflags.mk
 include /usr/share/dpkg/pkg-info.mk
 export KBUILD_BUILD_TIMESTAMP = "@$(SOURCE_DATE_EPOCH)"
-#
+TREENAMES = build-deb build-udeb
+
+%:
+	dh $@
+
+execute_before_dh_clean:
+	for treename in $(TREENAMES); do \
+		rm -rf debian/$$treename ;\
+	done
+	rm -rf debian/tmp-multipath-udeb
+
+execute_before_dh_auto_configure:
+	# no out of tree build support, prepare two source trees
+	for treename in $(TREENAMES); do \
+		mkdir debian/$$treename ;\
+		cp -r -t debian/$$treename $$(find . -mindepth 1 -maxdepth 1 \! -name debian \! -name .git) ;\
+	done
 
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
+override_dh_auto_configure:
+	for treename in $(TREENAMES); do \
+		cat debian/buildconfig.common debian/buildconfig.$$treename Makefile.inc > debian/$$treename/Makefile.inc ;\
+	done
 
-build: build-arch build-indep
+override_dh_auto_build:
+	dh_auto_build --sourcedirectory=debian/build-deb
+	# multipath-udeb: build separately; don't reference dynamic libgcc at runtime (#779579); disable systemd
+	dh_auto_build --sourcedirectory=debian/build-udeb
 
-build-arch: build-multipath-udeb-stamp build-stamp
-build-indep:
-	# Nothing to do here.
-	touch $@
+override_dh_auto_test:
 
-build-stamp: clean-tree
-	dh_testdir
-	
+override_dh_auto_install:
 	[ ! -f kpartx/del-part-nodes.rules ] || cp kpartx/del-part-nodes.rules debian/kpartx.del-part-nodes.udev
 	[ ! -f kpartx/dm-parts.rules ] || cp kpartx/dm-parts.rules debian/kpartx.dm-parts.udev
 	[ ! -f kpartx/kpartx.rules ] || cp kpartx/kpartx.rules debian/kpartx.udev
 	[ ! -f multipath/multipath.rules ] || cp multipath/multipath.rules debian/multipath.udev
 	[ ! -f multipath/11-dm-mpath.rules ] || cp multipath/11-dm-mpath.rules debian/dm-mpath.udev
 
-	dh_auto_build --parallel -- $(OPTFLAGS) LIB=/lib SYSTEMDPATH=/lib USE_SYSTEMD=1
+	dh_auto_install --no-parallel --sourcedirectory=debian/build-deb -- DESTDIR=$(CURDIR)/debian/tmp
+	dh_auto_install --no-parallel --sourcedirectory=debian/build-udeb -- DESTDIR=$(CURDIR)/debian/tmp-multipath-udeb
 
-	touch $@
+execute_after_dh_install:
+	if strings debian/multipath-udeb/lib/libmultipath.so.0 | grep libsystemd.so ; then \
+		echo ERROR: udeb linked to libsystemd ;\
+		exit 1 ;\
+	fi
 
-# multipath-udeb: build separately; don't reference dynamic libgcc at runtime (#779579)
-build-multipath-udeb-stamp:
-	dh_testdir
-
-	DEB_CFLAGS_MAINT_APPEND="-static-libgcc" dh_auto_build --parallel -- $(OPTFLAGS) LIB=/lib USE_SYSTEMD=0
-	
-	# store files for install target
-	mkdir -p $(CURDIR)/debian/tmp-multipath-udeb/sbin
-	$(MAKE) -j1 install DESTDIR=$(CURDIR)/debian/tmp-multipath-udeb LIB=/lib USE_SYSTEMD=0
-	rm -f debian/tmp-multipath-udeb/lib/libmpathvalid.so* debian/tmp-multipath-udeb/usr/include/mpath_valid.h
-
-	touch $@
-
-clean-tree:
-	$(MAKE) clean
-
-clean: clean-tree
-	dh_testdir
-	rm -f build-stamp build-multipath-udeb-stamp
-	dh_prep
-	debconf-updatepo
-	rm -rf debian/*.debhelper.log
-	rm -rf debian/tmp-multipath-udeb
-	dh_clean
-
-install-indep:
-	dh_testdir
-	dh_testroot
-	dh_prep
-	dh_installdirs
-
-	# initramfs stuff:
-	install -D -m 755 debian/initramfs/hooks $(INITRAMFS)/hooks/multipath
-	install -D -m 755 debian/initramfs/local-top \
-		$(INITRAMFS)/scripts/local-top/multipath
-	install -D -m 755 debian/initramfs/init-top \
-		$(INITRAMFS)/scripts/init-top/multipath
-
-	# reportbug:
-	for pkg in "multipath-tools" "multipath-tools-boot"; do \
-	    install -D -m 755 debian/reportbug/script debian/$${pkg}/usr/share/bug/$${pkg}/script; \
-	done
+override_dh_bugfiles:
+	dh_bugfiles -A -pmultipath-tools -pmultipath-tools-boot
 
-	# lintian override
-	install -D -m 644 debian/multipath-tools-boot.lintian-overrides \
-	    debian/multipath-tools-boot/usr/share/lintian/overrides/multipath-tools-boot
-
-
-install: install-indep
-	dh_testdir
-	dh_testroot
-	dh_installdirs
-
-	mkdir -p $(CURDIR)/debian/tmp/sbin
-	$(MAKE) -j1 install DESTDIR=$(CURDIR)/debian/tmp LIB=/lib SYSTEMDPATH=/lib USE_SYSTEMD=1
-	rm -f debian/tmp/lib/libmpathvalid.so* debian/tmp/usr/include/mpath_valid.h
-	dh_install -X.rules --fail-missing -Nmultipath-udeb --sourcedir=$(CURDIR)/debian/tmp
-	dh_install -Xkpartx_id -X.rules -X.service -X.socket --fail-missing -pmultipath-udeb --sourcedir=$(CURDIR)/debian/tmp-multipath-udeb
-
-binary-indep: build-indep install-indep
-	dh_testdir -i
-	dh_testroot -i
-	dh_installdirs -i
-	dh_installchangelogs -i
-	dh_installdocs -i
-	dh_installexamples -i
+override_dh_installinit:
 	dh_installinit -pmultipath-tools-boot --name=multipath-tools-boot --no-start -- start 21 S .
 	dh_installinit -pmultipath-tools-boot
-	dh_installdebconf -pmultipath-tools-boot
-	dh_installman -i
-	dh_compress -i
-	dh_fixperms -i
-	dh_installdeb -i
-	dh_gencontrol -i
-	dh_md5sums -i
-	dh_builddeb -i
-
-# Build architecture-dependent files here.
-binary-arch: build install
-	dh_testdir -a
-	dh_testroot -a
-	dh_installchangelogs -a
-	dh_installdocs -a
-	dh_installexamples -a
-	dh_lintian -a
-	dh_systemd_enable -pmultipath-tools multipathd.service
 	dh_installinit -pmultipath-tools
+
+override_dh_installudev:
 	dh_installudev -pkpartx
 	dh_installudev -pkpartx --name=dm-parts --priority=56
 	dh_installudev -pkpartx --name=del-part-nodes --priority=68
 	dh_installudev -pmultipath-tools --name=multipath
 	dh_installudev -pmultipath-tools --name=dm-mpath --priority=56
+
+override_dh_installsystemd:
 	dh_link -a lib/systemd/system/multipathd.service \
 		lib/systemd/system/multipath-tools.service
-	dh_systemd_start -pmultipath-tools multipathd.service
-	dh_installman -a
-	dh_makeshlibs -a --add-udeb=multipath-udeb
-	dh_link -a
-	dh_strip -a
-	dh_compress -a
-	dh_fixperms -a
-	dh_installdeb -a
-	dh_shlibdeps -a
-	dh_gencontrol -a
-	dh_md5sums -a
-	dh_builddeb -a
+	dh_installsystemd -pmultipath-tools multipathd.service
 
-binary:	binary-indep binary-arch
-.PHONY: binary binary-arch binary-indep unpack configure build clean
-
-# We build the same tree twice, this cannot happen in parallel.
-.NOTPARALLEL:
+override_dh_makeshlibs:
+	dh_makeshlibs -a --add-udeb=multipath-udeb
diff -pruN 0.8.8-1/debian/tests/control 0.9.0-3/debian/tests/control
--- 0.8.8-1/debian/tests/control	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/tests/control	2022-08-02 08:21:25.000000000 +0000
@@ -1,8 +1,13 @@
-
 Tests: kpartx-file-loopback
-Depends: kpartx, qemu-utils, gdisk
+Depends: gdisk,
+         kpartx,
+         qemu-utils
 Restrictions: needs-root, isolation-machine
 
 Tests: tgtbasedmpaths
-Depends: multipath-tools, tgt, open-iscsi, fio, lsscsi
+Depends: fio,
+         lsscsi,
+         multipath-tools,
+         open-iscsi,
+         tgt
 Restrictions: needs-root, isolation-machine, allow-stderr
diff -pruN 0.8.8-1/debian/tests/kpartx-file-loopback 0.9.0-3/debian/tests/kpartx-file-loopback
--- 0.8.8-1/debian/tests/kpartx-file-loopback	2022-01-16 22:57:28.000000000 +0000
+++ 0.9.0-3/debian/tests/kpartx-file-loopback	2022-08-02 08:21:25.000000000 +0000
@@ -17,7 +17,7 @@ udevadm settle
 loopdev=$(kpartx -lv "$DATAFILE" | awk '{ print $5; }')
 if losetup "$loopdev" 2>/dev/null | grep -qc "$loopdev"; then
 	echo "standard_filename: OK"
-	kpartx -dsv "$DATAFILE"
+	kpartx -dsv "$DATAFILE" 2>/dev/null
 	losetup
 	dmsetup ls --tree -o blkdevname
 	(losetup -j "$DATAFILE" | grep -qc $loopdev ) \
@@ -37,7 +37,7 @@ udevadm settle
 loopdev=$(kpartx -lv "$DATAFILE" | awk '{ print $5; }')
 if losetup "$loopdev" 2>/dev/null | grep -qc "$loopdev"; then
 	echo "filename_with_spaces: OK"
-	kpartx -dsv "$DATAFILE"
+	kpartx -dsv "$DATAFILE" 2>/dev/null
 	losetup
 	dmsetup ls --tree -o blkdevname
 	(losetup -j "$DATAFILE" | grep -qc $loopdev ) \
diff -pruN 0.8.8-1/.github/workflows/foreign.yaml 0.9.0-3/.github/workflows/foreign.yaml
--- 0.8.8-1/.github/workflows/foreign.yaml	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/.github/workflows/foreign.yaml	2022-06-10 16:47:26.000000000 +0000
@@ -24,7 +24,8 @@ jobs:
         run: make test
       - name: build
         if: ${{ matrix.arch != '' && matrix.arch != '-i386' }}
-        run: make test-progs
+        # The build path is different between builder and runner
+        run: make TESTDIR=${{ github.workspace }}/tests test-progs
       - name: archive
         if: ${{ matrix.arch != '' && matrix.arch != '-i386' }}
         run: >
@@ -61,6 +62,8 @@ jobs:
         uses: mosteo-actions/docker-run@v1
         with:
           image: mwilck/multipath-run-${{ matrix.os }}-${{ matrix.arch }}
-          # The runner is an image that has "make" as entrypoint
+          # The runner is an image that has "make" as entrypoint and uses
+          # github.workspace as both host dir and guest volume by default.
+          # See https://github.com/mosteo-actions/docker-run/blob/v1/action.yml
           # So run "make -C tests" here
           command: -C tests
diff -pruN 0.8.8-1/kpartx/crc32.c 0.9.0-3/kpartx/crc32.c
--- 0.8.8-1/kpartx/crc32.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/kpartx/crc32.c	2022-06-10 16:47:26.000000000 +0000
@@ -342,7 +342,7 @@ uint32_t attribute((pure)) crc32_be(uint
  * but again the multiple of the polynomial to subtract depends only on
  * the high bits, the high 8 bits in this case.
  *
- * The multile we need in that case is the low 32 bits of a 40-bit
+ * The multiple we need in that case is the low 32 bits of a 40-bit
  * value whose high 8 bits are given, and which is a multiple of the
  * generator polynomial.  This is simply the CRC-32 of the given
  * one-byte message.
diff -pruN 0.8.8-1/kpartx/devmapper.c 0.9.0-3/kpartx/devmapper.c
--- 0.8.8-1/kpartx/devmapper.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/kpartx/devmapper.c	2022-06-10 16:47:26.000000000 +0000
@@ -18,6 +18,12 @@
 #define MAX_PREFIX_LEN (_UUID_PREFIX_LEN + 4)
 #define PARAMS_SIZE 1024
 
+#ifdef LIBDM_API_COOKIE
+#    define __DM_API_COOKIE_UNUSED__ /* empty */
+#else
+#    define __DM_API_COOKIE_UNUSED__ __attribute__((unused))
+#endif
+
 int dm_prereq(char * str, uint32_t x, uint32_t y, uint32_t z)
 {
 	int r = 1;
@@ -55,12 +61,12 @@ out:
 	return r;
 }
 
-int dm_simplecmd(int task, const char *name, int no_flush, uint16_t udev_flags)
+int dm_simplecmd(int task, const char *name, int no_flush, __DM_API_COOKIE_UNUSED__ uint16_t udev_flags)
 {
 	int r = 0;
+#ifdef LIBDM_API_COOKIE
 	int udev_wait_flag = (task == DM_DEVICE_RESUME ||
 			      task == DM_DEVICE_REMOVE);
-#ifdef LIBDM_API_COOKIE
 	uint32_t cookie = 0;
 #endif
 	struct dm_task *dmt;
@@ -406,8 +412,10 @@ dm_get_map(const char *mapname, char * o
 		goto out;
 
 	/* Fetch 1st target */
-	dm_get_next_target(dmt, NULL, &start, &length,
-			   &target_type, &params);
+	if (dm_get_next_target(dmt, NULL, &start, &length,
+			       &target_type, &params) != NULL || !params)
+		/* more than one target or not found target */
+		goto out;
 
 	if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE)
 		r = 0;
diff -pruN 0.8.8-1/kpartx/gpt.c 0.9.0-3/kpartx/gpt.c
--- 0.8.8-1/kpartx/gpt.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/kpartx/gpt.c	2022-06-10 16:47:26.000000000 +0000
@@ -94,7 +94,7 @@ efi_crc32(const void *buf, unsigned long
  *
  * Description: Returns 1 if PMBR is valid, 0 otherwise.
  * Validity depends on two things:
- *  1) MSDOS signature is in the last two bytes of the MBR
+ *  1) MS-DOS signature is in the last two bytes of the MBR
  *  2) One partition of type 0xEE is found
  */
 static int
diff -pruN 0.8.8-1/kpartx/kpartx.rules 0.9.0-3/kpartx/kpartx.rules
--- 0.8.8-1/kpartx/kpartx.rules	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/kpartx/kpartx.rules	2022-06-10 16:47:26.000000000 +0000
@@ -12,6 +12,9 @@ ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}=="
 # Create dm tables for partitions on multipath devices.
 ENV{DM_UUID}!="mpath-?*", GOTO="mpath_kpartx_end"
 
+# Ignore RAID members
+ENV{ID_FS_TYPE}=="linux_raid_member|isw_raid_member|ddf_raid_member", GOTO="mpath_kpartx_end"
+
 # DM_SUBSYSTEM_UDEV_FLAG1 is the "skip_kpartx" flag.
 # For events not generated by libdevmapper, we need to fetch it from db:
 # - "change" events with DM_ACTIVATION!="1" (e.g. partition table changes)
diff -pruN 0.8.8-1/kpartx/Makefile 0.9.0-3/kpartx/Makefile
--- 0.8.8-1/kpartx/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/kpartx/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -3,13 +3,14 @@
 #
 include ../Makefile.inc
 
-CFLAGS += $(BIN_CFLAGS) -I. -I$(multipathdir) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+CPPFLAGS += -I. -I$(multipathdir) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64
+CFLAGS += $(BIN_CFLAGS)
 LDFLAGS += $(BIN_LDFLAGS)
 
 LIBDEPS += -ldevmapper
 
-ifneq ($(call check_func,dm_task_set_cookie,/usr/include/libdevmapper.h),0)
-	CFLAGS += -DLIBDM_API_COOKIE
+ifneq ($(call check_func,dm_task_set_cookie,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
+	CPPFLAGS += -DLIBDM_API_COOKIE
 endif
 
 OBJS = bsd.o dos.o kpartx.o solaris.o unixware.o dasd.o sun.o \
@@ -21,7 +22,6 @@ all: $(EXEC)
 
 $(EXEC): $(OBJS)
 	$(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(LIBDEPS)
-	$(GZIP) $(EXEC).8 > $(EXEC).8.gz
 
 install: $(EXEC) $(EXEC).8
 	$(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
@@ -33,11 +33,11 @@ install: $(EXEC) $(EXEC).8
 	$(INSTALL_PROGRAM) -m 644 kpartx.rules $(DESTDIR)$(libudevdir)/rules.d/66-kpartx.rules
 	$(INSTALL_PROGRAM) -m 644 del-part-nodes.rules $(DESTDIR)$(libudevdir)/rules.d/68-del-part-nodes.rules
 	$(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
-	$(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir)
+	$(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(man8dir)
 
 uninstall:
 	$(RM) $(DESTDIR)$(bindir)/$(EXEC)
-	$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
+	$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8
 	$(RM) $(DESTDIR)$(libudevdir)/kpartx_id
 	$(RM) $(DESTDIR)$(libudevdir)/rules.d/11-dm-parts.rules
 	$(RM) $(DESTDIR)$(libudevdir)/rules.d/66-kpartx.rules
@@ -45,7 +45,7 @@ uninstall:
 	$(RM) $(DESTDIR)$(libudevdir)/rules.d/68-del-part-nodes.rules
 
 clean: dep_clean
-	$(RM) core *.o $(EXEC) *.gz
+	$(RM) core *.o $(EXEC)
 
 include $(wildcard $(OBJS:.o=.d))
 
diff -pruN 0.8.8-1/libdmmp/libdmmp.c 0.9.0-3/libdmmp/libdmmp.c
--- 0.8.8-1/libdmmp/libdmmp.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libdmmp/libdmmp.c	2022-06-10 16:47:26.000000000 +0000
@@ -26,7 +26,6 @@
 #include <libudev.h>
 #include <errno.h>
 #include <libdevmapper.h>
-#include <stdbool.h>
 #include <unistd.h>
 #include <assert.h>
 #include <json.h>
@@ -189,7 +188,7 @@ int dmmp_mpath_array_get(struct dmmp_con
 	j_token = json_tokener_new();
 	if (j_token == NULL) {
 		rc = DMMP_ERR_BUG;
-		_error(ctx, "BUG: json_tokener_new() retuned NULL");
+		_error(ctx, "BUG: json_tokener_new() returned NULL");
 		goto out;
 	}
 	j_obj = json_tokener_parse_ex(j_token, j_str, strlen(j_str) + 1);
diff -pruN 0.8.8-1/libdmmp/Makefile 0.9.0-3/libdmmp/Makefile
--- 0.8.8-1/libdmmp/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libdmmp/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -15,13 +15,13 @@ HEADERS = libdmmp/libdmmp.h
 
 OBJS = libdmmp.o libdmmp_mp.o libdmmp_pg.o libdmmp_path.o libdmmp_misc.o
 
-CFLAGS += $(LIB_CFLAGS) -fvisibility=hidden -I$(libdmmpdir) -I$(mpathcmddir) \
-	  $(shell $(PKGCONFIG) --cflags json-c)
+CPPFLAGS += -I$(libdmmpdir) -I$(mpathcmddir) $(shell $(PKGCONFIG) --cflags json-c)
+CFLAGS += $(LIB_CFLAGS) -fvisibility=hidden
 
 LIBDEPS += $(shell $(PKGCONFIG) --libs json-c) -L$(mpathcmddir) -lmpathcmd -lpthread
 
 all: $(LIBS) doc
-.PHONY:	doc doc.gz clean install uninstall check speed_test dep_clean
+.PHONY:	doc clean install uninstall check speed_test dep_clean
 
 $(LIBS): $(OBJS)
 	$(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ -o $@ $(OBJS) $(LIBDEPS)
@@ -31,7 +31,7 @@ $(DEVLIB): $(LIBS)
 
 abi:    $(DEVLIB:%.so=%.abi)
 
-install:	doc.gz
+install:
 	mkdir -p $(DESTDIR)$(usrlibdir)
 	$(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(usrlibdir)/$(LIBS)
 	$(INSTALL_PROGRAM) -m 644 -D \
@@ -45,7 +45,7 @@ install:	doc.gz
 		$(DESTDIR)$(pkgconfdir)/$(PKGFILE)
 	perl -i -pe 's|__INCLUDEDIR__|$(includedir)|g' \
 		$(DESTDIR)$(pkgconfdir)/$(PKGFILE)
-	$(INSTALL_PROGRAM) -m 644 -t $(DESTDIR)$(man3dir) docs/man/*.3.gz
+	$(INSTALL_PROGRAM) -m 644 -t $(DESTDIR)$(man3dir) docs/man/*.3
 
 uninstall:
 	$(RM) $(DESTDIR)$(usrlibdir)/$(LIBS)
@@ -58,8 +58,7 @@ uninstall:
 	$(RM) $(DESTDIR)$(pkgconfdir)/$(PKGFILE)
 
 clean: dep_clean
-	$(RM) core *.a *.o *.gz *.so *.so.* *.abi $(NV_VERSION_SCRIPT)
-	$(RM) docs/man/*.gz
+	$(RM) core *.a *.o *.so *.so.* *.abi $(NV_VERSION_SCRIPT)
 	$(MAKE) -C test clean
 
 include $(wildcard $(OBJS:.o=.d))
@@ -70,13 +69,8 @@ check: all
 speed_test: all
 	$(MAKE) -C test speed_test
 
-doc.gz:	doc $(patsubst %,%.gz,$(wildcard docs/man/*.3))
-
 doc: docs/man/dmmp_strerror.3
 
-docs/man/%.3.gz:	docs/man/%.3
-	gzip -c $< >$@
-
 docs/man/dmmp_strerror.3:	$(HEADERS)
 	TEMPFILE=$(shell mktemp); \
 	cat $^ | perl docs/doc-preclean.pl >$$TEMPFILE; \
diff -pruN 0.8.8-1/libdmmp/test/Makefile 0.9.0-3/libdmmp/test/Makefile
--- 0.8.8-1/libdmmp/test/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libdmmp/test/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -9,7 +9,7 @@ _mpathcmddir=../$(mpathcmddir)
 
 TEST_EXEC = libdmmp_test
 SPD_TEST_EXEC = libdmmp_speed_test
-CFLAGS += -I$(_libdmmpdir)
+CPPFLAGS += -I$(_libdmmpdir)
 LDFLAGS += -L$(_libdmmpdir) -ldmmp
 
 all: $(TEST_EXEC) $(SPD_TEST_EXEC)
diff -pruN 0.8.8-1/libmpathcmd/Makefile 0.9.0-3/libmpathcmd/Makefile
--- 0.8.8-1/libmpathcmd/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmpathcmd/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -42,7 +42,7 @@ uninstall:
 	$(RM) $(DESTDIR)$(includedir)/mpath_cmd.h
 
 clean: dep_clean
-	$(RM) core *.a *.o *.so *.so.* *.gz *.abi $(NV_VERSION_SCRIPT)
+	$(RM) core *.a *.o *.so *.so.* *.abi $(NV_VERSION_SCRIPT)
 
 include $(wildcard $(OBJS:.o=.d))
 
diff -pruN 0.8.8-1/libmpathcmd/mpath_cmd.h 0.9.0-3/libmpathcmd/mpath_cmd.h
--- 0.8.8-1/libmpathcmd/mpath_cmd.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmpathcmd/mpath_cmd.h	2022-06-10 16:47:26.000000000 +0000
@@ -80,7 +80,7 @@ int mpath_disconnect(int fd);
  *	mpath_recv_reply()
  *
  * RETURNS:
- *	0 on successs, and reply will either be NULL (if there was no
+ *	0 on success, and reply will either be NULL (if there was no
  *	reply data), or point to the reply string, which must be freed by
  *	the caller. -1 on failure (with errno set).
  */
diff -pruN 0.8.8-1/libmpathpersist/libmpathpersist.version 0.9.0-3/libmpathpersist/libmpathpersist.version
--- 0.8.8-1/libmpathpersist/libmpathpersist.version	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmpathpersist/libmpathpersist.version	2022-06-10 16:47:26.000000000 +0000
@@ -10,27 +10,28 @@
  *
  * See libmultipath.version for general policy about version numbers.
  */
-LIBMPATHPERSIST_2.0.0 {
+LIBMPATHPERSIST_2.1.0 {
 global:
-
-	__mpath_persistent_reserve_in;
-	__mpath_persistent_reserve_out;
-	dumpHex;
-	mpath_alloc_prin_response;
+	/* public API as defined in mpath_persist.h */
+	libmpathpersist_exit;
+	libmpathpersist_init;
 	mpath_lib_exit;
 	mpath_lib_init;
 	mpath_mx_alloc_len;
+	mpath_persistent_reserve_free_vecs;
+	__mpath_persistent_reserve_in;
 	mpath_persistent_reserve_in;
 	mpath_persistent_reserve_init_vecs;
+	__mpath_persistent_reserve_out;
 	mpath_persistent_reserve_out;
-	mpath_persistent_reserve_free_vecs;
+local: *;
+};
+
+__LIBMPATHPERSIST_INT_1.0.0 {
+	/* Internal use by multipath-tools */
+	dumpHex;
+	mpath_alloc_prin_response;
 	prin_do_scsi_ioctl;
 	prout_do_scsi_ioctl;
 	update_map_pr;
-
-	/* added in 1.1.0 */
-	libmpathpersist_init;
-	libmpathpersist_exit;
-
-local: *;
 };
diff -pruN 0.8.8-1/libmpathpersist/Makefile 0.9.0-3/libmpathpersist/Makefile
--- 0.8.8-1/libmpathpersist/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmpathpersist/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -10,9 +10,9 @@ LDFLAGS += -L$(multipathdir) -L$(mpathcm
 
 LIBDEPS += -lmultipath -lmpathcmd -ldevmapper -lpthread -ldl
 
-OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o
+OBJS = mpath_persist.o mpath_updatepr.o mpath_pr_ioctl.o mpath_persist_int.o
 
-all: $(DEVLIB) man
+all: $(DEVLIB)
 
 $(LIBS): $(OBJS) $(VERSION_SCRIPT)
 	$(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=$@ \
@@ -32,10 +32,6 @@ abi:    $(LIBS:%.so.$(SONAME)=%-nv.abi)
 $(DEVLIB): $(LIBS)
 	$(LN) $(LIBS) $@
 
-man:
-	$(GZIP) mpath_persistent_reserve_in.3 > mpath_persistent_reserve_in.3.gz
-	$(GZIP) mpath_persistent_reserve_out.3 > mpath_persistent_reserve_out.3.gz
-
 install: all
 	$(INSTALL_PROGRAM) -d $(DESTDIR)$(syslibdir)
 	$(INSTALL_PROGRAM) -m 755 $(LIBS) $(DESTDIR)$(syslibdir)/$(LIBS)
@@ -43,19 +39,19 @@ install: all
 	$(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(man3dir)
 	$(INSTALL_PROGRAM) -m 755 -d $(DESTDIR)$(includedir)
 	$(LN) $(LIBS) $(DESTDIR)$(syslibdir)/$(DEVLIB)
-	$(INSTALL_PROGRAM) -m 644 mpath_persistent_reserve_in.3.gz $(DESTDIR)$(man3dir)
-	$(INSTALL_PROGRAM) -m 644 mpath_persistent_reserve_out.3.gz $(DESTDIR)$(man3dir)
+	$(INSTALL_PROGRAM) -m 644 mpath_persistent_reserve_in.3 $(DESTDIR)$(man3dir)
+	$(INSTALL_PROGRAM) -m 644 mpath_persistent_reserve_out.3 $(DESTDIR)$(man3dir)
 	$(INSTALL_PROGRAM) -m 644 mpath_persist.h $(DESTDIR)$(includedir)
 
 uninstall:
 	$(RM) $(DESTDIR)$(syslibdir)/$(LIBS)
-	$(RM) $(DESTDIR)$(man3dir)/mpath_persistent_reserve_in.3.gz
-	$(RM) $(DESTDIR)$(man3dir)/mpath_persistent_reserve_out.3.gz
+	$(RM) $(DESTDIR)$(man3dir)/mpath_persistent_reserve_in.3
+	$(RM) $(DESTDIR)$(man3dir)/mpath_persistent_reserve_out.3
 	$(RM) $(DESTDIR)$(includedir)/mpath_persist.h
 	$(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB)
 
 clean: dep_clean
-	$(RM) core *.a *.o *.so *.so.* *.gz *.abi $(NV_VERSION_SCRIPT)
+	$(RM) core *.a *.o *.so *.so.* *.abi $(NV_VERSION_SCRIPT)
 
 include $(wildcard $(OBJS:.o=.d))
 
diff -pruN 0.8.8-1/libmpathpersist/mpath_persist.c 0.9.0-3/libmpathpersist/mpath_persist.c
--- 0.8.8-1/libmpathpersist/mpath_persist.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmpathpersist/mpath_persist.c	2022-06-10 16:47:26.000000000 +0000
@@ -1,39 +1,13 @@
 #include <libdevmapper.h>
-#include "defaults.h"
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <fcntl.h>
-#include "vector.h"
-#include "checkers.h"
-#include "structs.h"
-#include "structs_vec.h"
-#include <libudev.h>
 
-#include "prio.h"
-#include <unistd.h>
-#include "devmapper.h"
-#include "debug.h"
-#include "config.h"
-#include "switchgroup.h"
-#include "discovery.h"
-#include "configure.h"
-#include "dmparser.h"
-#include <ctype.h>
-#include "propsel.h"
 #include "util.h"
-#include "unaligned.h"
+#include "vector.h"
+#include "config.h"
+#include "debug.h"
+#include "devmapper.h"
 
 #include "mpath_persist.h"
-#include "mpathpr.h"
-#include "mpath_pr_ioctl.h"
-
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-
-#define __STDC_FORMAT_MACROS 1
+#include "mpath_persist_int.h"
 
 extern struct udev *udev;
 
@@ -97,42 +71,6 @@ int libmpathpersist_exit(void)
 	return 0;
 }
 
-int
-mpath_prin_activepath (struct multipath *mpp, int rq_servact,
-	struct prin_resp * resp, int noisy)
-{
-	int i,j, ret = MPATH_PR_DMMP_ERROR;
-	struct pathgroup *pgp = NULL;
-	struct path *pp = NULL;
-
-	vector_foreach_slot (mpp->pg, pgp, j){
-		vector_foreach_slot (pgp->paths, pp, i){
-			if (!((pp->state == PATH_UP) ||
-			      (pp->state == PATH_GHOST))){
-				condlog(2, "%s: %s not available. Skip.",
-					mpp->wwid, pp->dev);
-				condlog(3, "%s: status = %d.",
-					mpp->wwid, pp->state);
-				continue;
-			}
-
-			condlog(3, "%s: sending pr in command to %s ",
-				mpp->wwid, pp->dev);
-			ret = mpath_send_prin_activepath(pp->dev, rq_servact,
-							 resp, noisy);
-			switch(ret)
-			{
-				case MPATH_PR_SUCCESS:
-				case MPATH_PR_SENSE_INVALID_OP:
-					return ret;
-				default:
-					continue;
-			}
-		}
-	}
-	return ret;
-}
-
 static vector curmp;
 static vector pathvec;
 
@@ -182,83 +120,6 @@ int mpath_persistent_reserve_init_vecs(i
 	return __mpath_persistent_reserve_init_vecs(&curmp, &pathvec, verbose);
 }
 
-static int mpath_get_map(vector curmp, vector pathvec, int fd, char **palias,
-			 struct multipath **pmpp)
-{
-	int ret = MPATH_PR_DMMP_ERROR;
-	struct stat info;
-	int major, minor;
-	char *alias;
-	struct multipath *mpp;
-
-	if (fstat(fd, &info) != 0){
-		condlog(0, "stat error fd=%d", fd);
-		return MPATH_PR_FILE_ERROR;
-	}
-	if(!S_ISBLK(info.st_mode)){
-		condlog(3, "Failed to get major:minor. fd=%d", fd);
-		return MPATH_PR_FILE_ERROR;
-	}
-
-	major = major(info.st_rdev);
-	minor = minor(info.st_rdev);
-	condlog(4, "Device  %d:%d", major, minor);
-
-	/* get alias from major:minor*/
-	alias = dm_mapname(major, minor);
-	if (!alias){
-		condlog(0, "%d:%d failed to get device alias.", major, minor);
-		return MPATH_PR_DMMP_ERROR;
-	}
-
-	condlog(3, "alias = %s", alias);
-
-	if (dm_map_present(alias) && dm_is_mpath(alias) != 1){
-		condlog(3, "%s: not a multipath device.", alias);
-		goto out;
-	}
-
-	/* get info of all paths from the dm device     */
-	if (get_mpvec(curmp, pathvec, alias)){
-		condlog(0, "%s: failed to get device info.", alias);
-		goto out;
-	}
-
-	mpp = find_mp_by_alias(curmp, alias);
-
-	if (!mpp) {
-		condlog(0, "%s: devmap not registered.", alias);
-		goto out;
-	}
-
-	ret = MPATH_PR_SUCCESS;
-	if (pmpp)
-		*pmpp = mpp;
-	if (palias) {
-		*palias = alias;
-		alias = NULL;
-	}
-out:
-	free(alias);
-	return ret;
-}
-
-static int do_mpath_persistent_reserve_in (vector curmp, vector pathvec,
-	int fd, int rq_servact, struct prin_resp *resp, int noisy)
-{
-	struct multipath *mpp;
-	int ret;
-
-	ret = mpath_get_map(curmp, pathvec, fd, NULL, &mpp);
-	if (ret != MPATH_PR_SUCCESS)
-		return ret;
-
-	ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy);
-
-	return ret;
-}
-
-
 int __mpath_persistent_reserve_in (int fd, int rq_servact,
 	struct prin_resp *resp, int noisy)
 {
@@ -266,86 +127,6 @@ int __mpath_persistent_reserve_in (int f
 					      resp, noisy);
 }
 
-static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
-	int rq_servact, int rq_scope, unsigned int rq_type,
-	struct prout_param_descriptor *paramp, int noisy)
-{
-	struct multipath *mpp;
-	char *alias;
-	int ret;
-	uint64_t prkey;
-	struct config *conf;
-
-	ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
-	if (ret != MPATH_PR_SUCCESS)
-		return ret;
-
-	conf = get_multipath_config();
-	select_reservation_key(conf, mpp);
-	select_all_tg_pt(conf, mpp);
-	put_multipath_config(conf);
-
-	memcpy(&prkey, paramp->sa_key, 8);
-	if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey &&
-	    (rq_servact == MPATH_PROUT_REG_IGN_SA ||
-	     (rq_servact == MPATH_PROUT_REG_SA &&
-	      (!get_be64(mpp->reservation_key) ||
-	       memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) {
-		memcpy(&mpp->reservation_key, paramp->sa_key, 8);
-		if (update_prkey_flags(alias, get_be64(mpp->reservation_key),
-				       paramp->sa_flags)) {
-			condlog(0, "%s: failed to set prkey for multipathd.",
-				alias);
-			ret = MPATH_PR_DMMP_ERROR;
-			goto out1;
-		}
-	}
-
-	if (memcmp(paramp->key, &mpp->reservation_key, 8) &&
-	    memcmp(paramp->sa_key, &mpp->reservation_key, 8) &&
-	    (prkey || rq_servact != MPATH_PROUT_REG_IGN_SA)) {
-		condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64, alias, get_be64(mpp->reservation_key));
-		ret = MPATH_PR_SYNTAX_ERROR;
-		goto out1;
-	}
-
-	switch(rq_servact)
-	{
-	case MPATH_PROUT_REG_SA:
-	case MPATH_PROUT_REG_IGN_SA:
-		ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
-		break;
-	case MPATH_PROUT_RES_SA :
-	case MPATH_PROUT_PREE_SA :
-	case MPATH_PROUT_PREE_AB_SA :
-	case MPATH_PROUT_CLEAR_SA:
-		ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
-		break;
-	case MPATH_PROUT_REL_SA:
-		ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
-		break;
-	default:
-		ret = MPATH_PR_OTHER;
-		goto out1;
-	}
-
-	if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) ||
-				(rq_servact ==  MPATH_PROUT_REG_IGN_SA)))
-	{
-		if (prkey == 0) {
-			update_prflag(alias, 0);
-			update_prkey(alias, 0);
-		} else
-			update_prflag(alias, 1);
-	} else if ((ret == MPATH_PR_SUCCESS) && (rq_servact == MPATH_PROUT_CLEAR_SA)) {
-		update_prflag(alias, 0);
-		update_prkey(alias, 0);
-	}
-out1:
-	free(alias);
-	return ret;
-}
-
 
 int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
 	unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy)
@@ -384,550 +165,3 @@ int mpath_persistent_reserve_out ( int f
 	__mpath_persistent_reserve_free_vecs(curmp, pathvec);
 	return ret;
 }
-
-int
-get_mpvec (vector curmp, vector pathvec, char * refwwid)
-{
-	int i;
-	struct multipath *mpp;
-
-	vector_foreach_slot (curmp, mpp, i){
-		/*
-		 * discard out of scope maps
-		 */
-		if (!mpp->alias) {
-			condlog(0, "%s: map with empty alias!", __func__);
-			continue;
-		}
-
-		if (mpp->pg != NULL)
-			/* Already seen this one */
-			continue;
-
-		if (refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE - 1))
-			continue;
-
-		if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
-		    update_mpp_paths(mpp, pathvec)) {
-			condlog(1, "error parsing map %s", mpp->wwid);
-			remove_map(mpp, pathvec, curmp);
-			i--;
-		} else
-			extract_hwe_from_path(mpp);
-	}
-	return MPATH_PR_SUCCESS ;
-}
-
-int mpath_send_prin_activepath (char * dev, int rq_servact,
-				struct prin_resp * resp, int noisy)
-{
-
-	int rc;
-
-	rc = prin_do_scsi_ioctl(dev, rq_servact, resp,  noisy);
-
-	return (rc);
-}
-
-int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
-	unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
-{
-
-	int i, j, k;
-	struct pathgroup *pgp = NULL;
-	struct path *pp = NULL;
-	int rollback = 0;
-	int active_pathcount=0;
-	int rc;
-	int count=0;
-	int status = MPATH_PR_SUCCESS;
-	int all_tg_pt;
-	uint64_t sa_key = 0;
-
-	if (!mpp)
-		return MPATH_PR_DMMP_ERROR;
-
-	all_tg_pt = (mpp->all_tg_pt == ALL_TG_PT_ON ||
-		     paramp->sa_flags & MPATH_F_ALL_TG_PT_MASK);
-	active_pathcount = count_active_paths(mpp);
-
-	if (active_pathcount == 0) {
-		condlog (0, "%s: no path available", mpp->wwid);
-		return MPATH_PR_DMMP_ERROR;
-	}
-
-	struct threadinfo thread[active_pathcount];
-	int hosts[active_pathcount];
-
-	memset(thread, 0, sizeof(thread));
-
-	/* init thread parameter */
-	for (i =0; i< active_pathcount; i++){
-		hosts[i] = -1;
-		thread[i].param.rq_servact = rq_servact;
-		thread[i].param.rq_scope = rq_scope;
-		thread[i].param.rq_type = rq_type;
-		thread[i].param.paramp = paramp;
-		thread[i].param.noisy = noisy;
-		thread[i].param.status = MPATH_PR_SKIP;
-
-		condlog (3, "THREAD ID [%d] INFO]", i);
-		condlog (3, "rq_servact=%d ", thread[i].param.rq_servact);
-		condlog (3, "rq_scope=%d ", thread[i].param.rq_scope);
-		condlog (3, "rq_type=%d ", thread[i].param.rq_type);
-		condlog (3, "rkey=");
-		condlog (3, "paramp->sa_flags =%02x ",
-			 thread[i].param.paramp->sa_flags);
-		condlog (3, "noisy=%d ", thread[i].param.noisy);
-		condlog (3, "status=%d ", thread[i].param.status);
-	}
-
-	pthread_attr_t attr;
-	pthread_attr_init(&attr);
-	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
-	vector_foreach_slot (mpp->pg, pgp, j){
-		vector_foreach_slot (pgp->paths, pp, i){
-			if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
-				condlog (1, "%s: %s path not up. Skip.", mpp->wwid, pp->dev);
-				continue;
-			}
-			if (all_tg_pt && pp->sg_id.host_no != -1) {
-				for (k = 0; k < count; k++) {
-					if (pp->sg_id.host_no == hosts[k]) {
-						condlog(3, "%s: %s host %d matches skip.", pp->wwid, pp->dev, pp->sg_id.host_no);
-						break;
-					}
-				}
-				if (k < count)
-					continue;
-			}
-			strlcpy(thread[count].param.dev, pp->dev,
-				FILE_NAME_SIZE);
-
-			if (count && (thread[count].param.paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK)){
-				/*
-				 * Clearing SPEC_I_PT as transportids are already registered by now.
-				 */
-				thread[count].param.paramp->sa_flags &= (~MPATH_F_SPEC_I_PT_MASK);
-			}
-
-			condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
-
-			rc = pthread_create(&thread[count].id, &attr, mpath_prout_pthread_fn, (void *)(&thread[count].param));
-			if (rc){
-				condlog (0, "%s: failed to create thread %d", mpp->wwid, rc);
-				thread[count].param.status = MPATH_PR_THREAD_ERROR;
-			}
-			else
-				hosts[count] = pp->sg_id.host_no;
-			count = count + 1;
-		}
-	}
-	for( i=0; i < count ; i++){
-		if (thread[i].param.status != MPATH_PR_THREAD_ERROR) {
-			rc = pthread_join(thread[i].id, NULL);
-			if (rc){
-				condlog (0, "%s: Thread[%d] failed to join thread %d", mpp->wwid, i, rc);
-			}
-		}
-		if (!rollback && (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)){
-			rollback = 1;
-			sa_key = get_unaligned_be64(&paramp->sa_key[0]);
-			status = MPATH_PR_RESERV_CONFLICT ;
-		}
-		if (!rollback && (status == MPATH_PR_SUCCESS)){
-			status = thread[i].param.status;
-		}
-	}
-	if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){
-		condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid);
-		memcpy(&paramp->key, &paramp->sa_key, 8);
-		memset(&paramp->sa_key, 0, 8);
-		for( i=0 ; i < count ; i++){
-			if(thread[i].param.status == MPATH_PR_SUCCESS) {
-				rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn,
-						(void *)(&thread[i].param));
-				if (rc){
-					condlog (0, "%s: failed to create thread for rollback. %d",  mpp->wwid, rc);
-					thread[i].param.status = MPATH_PR_THREAD_ERROR;
-				}
-			} else
-				thread[i].param.status = MPATH_PR_SKIP;
-		}
-		for(i=0; i < count ; i++){
-			if (thread[i].param.status != MPATH_PR_SKIP &&
-			    thread[i].param.status != MPATH_PR_THREAD_ERROR) {
-				rc = pthread_join(thread[i].id, NULL);
-				if (rc){
-					condlog (3, "%s: failed to join thread while rolling back %d",
-						 mpp->wwid, i);
-				}
-			}
-		}
-	}
-
-	pthread_attr_destroy(&attr);
-	return (status);
-}
-
-void * mpath_prout_pthread_fn(void *p)
-{
-	int ret;
-	struct prout_param * param = (struct prout_param *)p;
-
-	ret = prout_do_scsi_ioctl( param->dev,param->rq_servact, param->rq_scope,
-			param->rq_type, param->paramp, param->noisy);
-	param->status = ret;
-	pthread_exit(NULL);
-}
-
-int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
-	unsigned int rq_type, struct prout_param_descriptor* paramp, int noisy)
-{
-	int i,j, ret;
-	struct pathgroup *pgp = NULL;
-	struct path *pp = NULL;
-
-	vector_foreach_slot (mpp->pg, pgp, j){
-		vector_foreach_slot (pgp->paths, pp, i){
-			if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
-				condlog (1, "%s: %s path not up. Skip",
-					 mpp->wwid, pp->dev);
-				continue;
-			}
-
-			condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
-			ret = send_prout_activepath(pp->dev, rq_servact,
-						    rq_scope, rq_type,
-						    paramp, noisy);
-			return ret ;
-		}
-	}
-	condlog (0, "%s: no path available", mpp->wwid);
-	return MPATH_PR_DMMP_ERROR;
-}
-
-int send_prout_activepath(char * dev, int rq_servact, int rq_scope,
-	unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
-{
-	struct prout_param param;
-	param.rq_servact = rq_servact;
-	param.rq_scope  = rq_scope;
-	param.rq_type   = rq_type;
-	param.paramp    = paramp;
-	param.noisy = noisy;
-	param.status = -1;
-
-	pthread_t thread;
-	pthread_attr_t attr;
-	int rc;
-
-	memset(&thread, 0, sizeof(thread));
-	strlcpy(param.dev, dev, FILE_NAME_SIZE);
-	/* Initialize and set thread joinable attribute */
-	pthread_attr_init(&attr);
-	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
-	rc = pthread_create(&thread, &attr, mpath_prout_pthread_fn, (void *)(&param));
-	if (rc){
-		condlog (3, "%s: failed to create thread %d", dev, rc);
-		return MPATH_PR_THREAD_ERROR;
-	}
-	/* Free attribute and wait for the other threads */
-	pthread_attr_destroy(&attr);
-	rc = pthread_join(thread, NULL);
-
-	return (param.status);
-}
-
-int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
-	unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
-{
-	int i, j;
-	int num = 0;
-	struct pathgroup *pgp = NULL;
-	struct path *pp = NULL;
-	int active_pathcount = 0;
-	pthread_attr_t attr;
-	int rc, found = 0;
-	int count = 0;
-	int status = MPATH_PR_SUCCESS;
-	struct prin_resp resp;
-	struct prout_param_descriptor *pamp;
-	struct prin_resp *pr_buff;
-	int length;
-	struct transportid *pptr;
-
-	if (!mpp)
-		return MPATH_PR_DMMP_ERROR;
-
-	active_pathcount = count_active_paths(mpp);
-
-	if (active_pathcount == 0) {
-		condlog (0, "%s: no path available", mpp->wwid);
-		return MPATH_PR_DMMP_ERROR;
-	}
-
-	struct threadinfo thread[active_pathcount];
-	memset(thread, 0, sizeof(thread));
-	for (i = 0; i < active_pathcount; i++){
-		thread[i].param.rq_servact = rq_servact;
-		thread[i].param.rq_scope = rq_scope;
-		thread[i].param.rq_type = rq_type;
-		thread[i].param.paramp = paramp;
-		thread[i].param.noisy = noisy;
-		thread[i].param.status = MPATH_PR_SKIP;
-
-		condlog (3, " path count = %d", i);
-		condlog (3, "rq_servact=%d ", thread[i].param.rq_servact);
-		condlog (3, "rq_scope=%d ", thread[i].param.rq_scope);
-		condlog (3, "rq_type=%d ", thread[i].param.rq_type);
-		condlog (3, "noisy=%d ", thread[i].param.noisy);
-		condlog (3, "status=%d ", thread[i].param.status);
-	}
-
-	pthread_attr_init (&attr);
-	pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE);
-
-	vector_foreach_slot (mpp->pg, pgp, j){
-		vector_foreach_slot (pgp->paths, pp, i){
-			if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
-				condlog (1, "%s: %s path not up.", mpp->wwid, pp->dev);
-				continue;
-			}
-
-			strlcpy(thread[count].param.dev, pp->dev,
-				FILE_NAME_SIZE);
-			condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
-			rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn,
-					(void *) (&thread[count].param));
-			if (rc) {
-				condlog (0, "%s: failed to create thread. %d",  mpp->wwid, rc);
-				thread[count].param.status = MPATH_PR_THREAD_ERROR;
-			}
-			count = count + 1;
-		}
-	}
-	pthread_attr_destroy (&attr);
-	for (i = 0; i < count; i++){
-		if (thread[i].param.status != MPATH_PR_THREAD_ERROR) {
-			rc = pthread_join (thread[i].id, NULL);
-			if (rc){
-				condlog (1, "%s: failed to join thread.  %d",  mpp->wwid,  rc);
-			}
-		}
-	}
-
-	for (i = 0; i < count; i++){
-		/*  check thread status here and return the status */
-
-		if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
-			status = MPATH_PR_RESERV_CONFLICT;
-		else if (status == MPATH_PR_SUCCESS
-				&& thread[i].param.status != MPATH_PR_RESERV_CONFLICT)
-			status = thread[i].param.status;
-	}
-
-	status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
-	if (status != MPATH_PR_SUCCESS){
-		condlog (0, "%s: pr in read reservation command failed.", mpp->wwid);
-		return MPATH_PR_OTHER;
-	}
-
-	num = resp.prin_descriptor.prin_readresv.additional_length / 8;
-	if (num == 0){
-		condlog (2, "%s: Path holding reservation is released.", mpp->wwid);
-		return MPATH_PR_SUCCESS;
-	}
-	condlog (2, "%s: Path holding reservation is not avialable.", mpp->wwid);
-
-	pr_buff =  mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA);
-	if (!pr_buff){
-		condlog (0, "%s: failed to  alloc pr in response buffer.", mpp->wwid);
-		return MPATH_PR_OTHER;
-	}
-
-	status = mpath_prin_activepath (mpp, MPATH_PRIN_RFSTAT_SA, pr_buff, noisy);
-
-	if (status != MPATH_PR_SUCCESS){
-		condlog (0,  "%s: pr in read full status command failed.",  mpp->wwid);
-		goto out;
-	}
-
-	num = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor;
-	if (0 == num){
-		goto out;
-	}
-	length = sizeof (struct prout_param_descriptor) + (sizeof (struct transportid *));
-
-	pamp = (struct prout_param_descriptor *)malloc (length);
-	if (!pamp){
-		condlog (0, "%s: failed to alloc pr out parameter.", mpp->wwid);
-		goto out1;
-	}
-
-	memset(pamp, 0, length);
-
-	pamp->trnptid_list[0] = (struct transportid *) malloc (sizeof (struct transportid));
-	if (!pamp->trnptid_list[0]){
-		condlog (0, "%s: failed to alloc pr out transportid.", mpp->wwid);
-		goto out1;
-	}
-
-	if (get_be64(mpp->reservation_key)){
-		memcpy (pamp->key, &mpp->reservation_key, 8);
-		condlog (3, "%s: reservation key set.", mpp->wwid);
-	}
-
-	status = mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA,
-				     rq_scope, rq_type, pamp, noisy);
-
-	if (status) {
-		condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid);
-		goto out1;
-	}
-
-	pamp->num_transportid = 1;
-	pptr=pamp->trnptid_list[0];
-
-	for (i = 0; i < num; i++){
-		if (get_be64(mpp->reservation_key) &&
-			memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key,
-			       &mpp->reservation_key, 8)){
-			/*register with tarnsport id*/
-			memset(pamp, 0, length);
-			pamp->trnptid_list[0] = pptr;
-			memset (pamp->trnptid_list[0], 0, sizeof (struct transportid));
-			memcpy (pamp->sa_key,
-					pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
-			pamp->sa_flags = MPATH_F_SPEC_I_PT_MASK;
-			pamp->num_transportid = 1;
-
-			memcpy (pamp->trnptid_list[0],
-					&pr_buff->prin_descriptor.prin_readfd.descriptors[i]->trnptid,
-					sizeof (struct transportid));
-			status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
-					pamp, noisy);
-
-			pamp->sa_flags = 0;
-			memcpy (pamp->key, pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
-			memset (pamp->sa_key, 0, 8);
-			pamp->num_transportid = 0;
-			status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
-					pamp, noisy);
-		}
-		else
-		{
-			if (get_be64(mpp->reservation_key))
-				found = 1;
-		}
-
-
-	}
-
-	if (found){
-		memset (pamp, 0, length);
-		memcpy (pamp->sa_key, &mpp->reservation_key, 8);
-		memset (pamp->key, 0, 8);
-		status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
-	}
-
-
-	free(pptr);
-out1:
-	free (pamp);
-out:
-	free (pr_buff);
-	return (status);
-}
-
-void * mpath_alloc_prin_response(int prin_sa)
-{
-	void * ptr = NULL;
-	int size=0;
-	switch (prin_sa)
-	{
-		case MPATH_PRIN_RKEY_SA:
-			size = sizeof(struct prin_readdescr);
-			break;
-		case MPATH_PRIN_RRES_SA:
-			size = sizeof(struct prin_resvdescr);
-			break;
-		case MPATH_PRIN_RCAP_SA:
-			size=sizeof(struct prin_capdescr);
-			break;
-		case MPATH_PRIN_RFSTAT_SA:
-			size = sizeof(struct print_fulldescr_list) +
-				sizeof(struct prin_fulldescr *)*MPATH_MX_TIDS;
-			break;
-	}
-	if (size > 0)
-	{
-		ptr = calloc(size, 1);
-	}
-	return ptr;
-}
-
-int update_map_pr(struct multipath *mpp)
-{
-	int noisy=0;
-	struct prin_resp *resp;
-	unsigned int i;
-	int ret, isFound;
-
-	if (!get_be64(mpp->reservation_key))
-	{
-		/* Nothing to do. Assuming pr mgmt feature is disabled*/
-		condlog(4, "%s: reservation_key not set in multipath.conf",
-			mpp->alias);
-		return MPATH_PR_SUCCESS;
-	}
-
-	resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
-	if (!resp)
-	{
-		condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
-		return MPATH_PR_OTHER;
-	}
-	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;
-	}
-
-	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;
-	}
-
-	condlog(2, "%s: Multipath  reservation_key: 0x%" PRIx64 " ", mpp->alias,
-		get_be64(mpp->reservation_key));
-
-	isFound =0;
-	for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ )
-	{
-		condlog(2, "%s: PR IN READKEYS[%d]  reservation key:", mpp->alias, i);
-		dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , 1);
-
-		if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
-		{
-			condlog(2, "%s: reservation key found in pr in readkeys response", mpp->alias);
-			isFound =1;
-		}
-	}
-
-	if (isFound)
-	{
-		mpp->prflag = 1;
-		condlog(2, "%s: prflag flag set.", mpp->alias );
-	}
-
-	free(resp);
-	return MPATH_PR_SUCCESS;
-}
diff -pruN 0.8.8-1/libmpathpersist/mpath_persist.h 0.9.0-3/libmpathpersist/mpath_persist.h
--- 0.8.8-1/libmpathpersist/mpath_persist.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmpathpersist/mpath_persist.h	2022-06-10 16:47:26.000000000 +0000
@@ -77,7 +77,7 @@ extern "C" {
 #define MPATH_PROTOCOL_ID_SAS		0x06
 
 
-/*Transport ID FORMATE CODE */
+/*Transport ID FORMAT CODE */
 #define MPATH_WWUI_DEVICE_NAME		0x00	/* World wide unique initiator device name */
 #define MPATH_WWUI_PORT_IDENTIFIER	0x40	/* World wide unique initiator port identifier	*/
 
diff -pruN 0.8.8-1/libmpathpersist/mpath_persist_int.c 0.9.0-3/libmpathpersist/mpath_persist_int.c
--- 0.8.8-1/libmpathpersist/mpath_persist_int.c	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/libmpathpersist/mpath_persist_int.c	2022-06-10 16:47:26.000000000 +0000
@@ -0,0 +1,792 @@
+#include <libdevmapper.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <libudev.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include "vector.h"
+#include "defaults.h"
+#include "checkers.h"
+#include "structs.h"
+#include "structs_vec.h"
+#include "prio.h"
+#include "devmapper.h"
+#include "debug.h"
+#include "config.h"
+#include "switchgroup.h"
+#include "discovery.h"
+#include "configure.h"
+#include "dmparser.h"
+#include "propsel.h"
+#include "util.h"
+#include "unaligned.h"
+
+#include "mpath_persist.h"
+#include "mpath_persist_int.h"
+#include "mpathpr.h"
+#include "mpath_pr_ioctl.h"
+
+struct prout_param {
+	char dev[FILE_NAME_SIZE];
+	int rq_servact;
+	int rq_scope;
+	unsigned int rq_type;
+	struct prout_param_descriptor  *paramp;
+	int noisy;
+	int status;
+};
+
+struct threadinfo {
+	int status;
+	pthread_t id;
+	struct prout_param param;
+};
+
+static int mpath_send_prin_activepath (char * dev, int rq_servact,
+				struct prin_resp * resp, int noisy)
+{
+
+	int rc;
+
+	rc = prin_do_scsi_ioctl(dev, rq_servact, resp,  noisy);
+
+	return (rc);
+}
+
+static int mpath_prin_activepath (struct multipath *mpp, int rq_servact,
+	struct prin_resp * resp, int noisy)
+{
+	int i,j, ret = MPATH_PR_DMMP_ERROR;
+	struct pathgroup *pgp = NULL;
+	struct path *pp = NULL;
+
+	vector_foreach_slot (mpp->pg, pgp, j){
+		vector_foreach_slot (pgp->paths, pp, i){
+			if (!((pp->state == PATH_UP) ||
+			      (pp->state == PATH_GHOST))){
+				condlog(2, "%s: %s not available. Skip.",
+					mpp->wwid, pp->dev);
+				condlog(3, "%s: status = %d.",
+					mpp->wwid, pp->state);
+				continue;
+			}
+
+			condlog(3, "%s: sending pr in command to %s ",
+				mpp->wwid, pp->dev);
+			ret = mpath_send_prin_activepath(pp->dev, rq_servact,
+							 resp, noisy);
+			switch(ret)
+			{
+				case MPATH_PR_SUCCESS:
+				case MPATH_PR_SENSE_INVALID_OP:
+					return ret;
+				default:
+					continue;
+			}
+		}
+	}
+	return ret;
+}
+
+void *mpath_alloc_prin_response(int prin_sa)
+{
+	void * ptr = NULL;
+	int size=0;
+	switch (prin_sa)
+	{
+		case MPATH_PRIN_RKEY_SA:
+			size = sizeof(struct prin_readdescr);
+			break;
+		case MPATH_PRIN_RRES_SA:
+			size = sizeof(struct prin_resvdescr);
+			break;
+		case MPATH_PRIN_RCAP_SA:
+			size=sizeof(struct prin_capdescr);
+			break;
+		case MPATH_PRIN_RFSTAT_SA:
+			size = sizeof(struct print_fulldescr_list) +
+				sizeof(struct prin_fulldescr *)*MPATH_MX_TIDS;
+			break;
+	}
+	if (size > 0)
+	{
+		ptr = calloc(size, 1);
+	}
+	return ptr;
+}
+
+static int get_mpvec(vector curmp, vector pathvec, char *refwwid)
+{
+	int i;
+	struct multipath *mpp;
+
+	vector_foreach_slot (curmp, mpp, i){
+		/*
+		 * discard out of scope maps
+		 */
+		if (!mpp->alias) {
+			condlog(0, "%s: map with empty alias!", __func__);
+			continue;
+		}
+
+		if (mpp->pg != NULL)
+			/* Already seen this one */
+			continue;
+
+		if (refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE - 1))
+			continue;
+
+		if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
+		    update_mpp_paths(mpp, pathvec)) {
+			condlog(1, "error parsing map %s", mpp->wwid);
+			remove_map(mpp, pathvec, curmp);
+			i--;
+		} else
+			extract_hwe_from_path(mpp);
+	}
+	return MPATH_PR_SUCCESS ;
+}
+
+static int mpath_get_map(vector curmp, vector pathvec, int fd, char **palias,
+			 struct multipath **pmpp)
+{
+	int ret = MPATH_PR_DMMP_ERROR;
+	struct stat info;
+	int major, minor;
+	char *alias;
+	struct multipath *mpp;
+
+	if (fstat(fd, &info) != 0){
+		condlog(0, "stat error fd=%d", fd);
+		return MPATH_PR_FILE_ERROR;
+	}
+	if(!S_ISBLK(info.st_mode)){
+		condlog(3, "Failed to get major:minor. fd=%d", fd);
+		return MPATH_PR_FILE_ERROR;
+	}
+
+	major = major(info.st_rdev);
+	minor = minor(info.st_rdev);
+	condlog(4, "Device  %d:%d", major, minor);
+
+	/* get alias from major:minor*/
+	alias = dm_mapname(major, minor);
+	if (!alias){
+		condlog(0, "%d:%d failed to get device alias.", major, minor);
+		return MPATH_PR_DMMP_ERROR;
+	}
+
+	condlog(3, "alias = %s", alias);
+
+	if (dm_map_present(alias) && dm_is_mpath(alias) != 1){
+		condlog(3, "%s: not a multipath device.", alias);
+		goto out;
+	}
+
+	/* get info of all paths from the dm device     */
+	if (get_mpvec(curmp, pathvec, alias)){
+		condlog(0, "%s: failed to get device info.", alias);
+		goto out;
+	}
+
+	mpp = find_mp_by_alias(curmp, alias);
+
+	if (!mpp) {
+		condlog(0, "%s: devmap not registered.", alias);
+		goto out;
+	}
+
+	ret = MPATH_PR_SUCCESS;
+	if (pmpp)
+		*pmpp = mpp;
+	if (palias) {
+		*palias = alias;
+		alias = NULL;
+	}
+out:
+	free(alias);
+	return ret;
+}
+
+int do_mpath_persistent_reserve_in(vector curmp, vector pathvec,
+				   int fd, int rq_servact,
+				   struct prin_resp *resp, int noisy)
+{
+	struct multipath *mpp;
+	int ret;
+
+	ret = mpath_get_map(curmp, pathvec, fd, NULL, &mpp);
+	if (ret != MPATH_PR_SUCCESS)
+		return ret;
+
+	ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy);
+
+	return ret;
+}
+
+static void *mpath_prout_pthread_fn(void *p)
+{
+	int ret;
+	struct prout_param * param = (struct prout_param *)p;
+
+	ret = prout_do_scsi_ioctl( param->dev,param->rq_servact, param->rq_scope,
+			param->rq_type, param->paramp, param->noisy);
+	param->status = ret;
+	pthread_exit(NULL);
+}
+
+static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
+			   unsigned int rq_type,
+			   struct prout_param_descriptor * paramp, int noisy)
+{
+
+	int i, j, k;
+	struct pathgroup *pgp = NULL;
+	struct path *pp = NULL;
+	int rollback = 0;
+	int active_pathcount=0;
+	int rc;
+	int count=0;
+	int status = MPATH_PR_SUCCESS;
+	int all_tg_pt;
+	uint64_t sa_key = 0;
+
+	if (!mpp)
+		return MPATH_PR_DMMP_ERROR;
+
+	all_tg_pt = (mpp->all_tg_pt == ALL_TG_PT_ON ||
+		     paramp->sa_flags & MPATH_F_ALL_TG_PT_MASK);
+	active_pathcount = count_active_paths(mpp);
+
+	if (active_pathcount == 0) {
+		condlog (0, "%s: no path available", mpp->wwid);
+		return MPATH_PR_DMMP_ERROR;
+	}
+
+	struct threadinfo thread[active_pathcount];
+	int hosts[active_pathcount];
+
+	memset(thread, 0, sizeof(thread));
+
+	/* init thread parameter */
+	for (i =0; i< active_pathcount; i++){
+		hosts[i] = -1;
+		thread[i].param.rq_servact = rq_servact;
+		thread[i].param.rq_scope = rq_scope;
+		thread[i].param.rq_type = rq_type;
+		thread[i].param.paramp = paramp;
+		thread[i].param.noisy = noisy;
+		thread[i].param.status = MPATH_PR_SKIP;
+
+		condlog (3, "THREAD ID [%d] INFO]", i);
+		condlog (3, "rq_servact=%d ", thread[i].param.rq_servact);
+		condlog (3, "rq_scope=%d ", thread[i].param.rq_scope);
+		condlog (3, "rq_type=%d ", thread[i].param.rq_type);
+		condlog (3, "rkey=");
+		condlog (3, "paramp->sa_flags =%02x ",
+			 thread[i].param.paramp->sa_flags);
+		condlog (3, "noisy=%d ", thread[i].param.noisy);
+		condlog (3, "status=%d ", thread[i].param.status);
+	}
+
+	pthread_attr_t attr;
+	pthread_attr_init(&attr);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+	vector_foreach_slot (mpp->pg, pgp, j){
+		vector_foreach_slot (pgp->paths, pp, i){
+			if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
+				condlog (1, "%s: %s path not up. Skip.", mpp->wwid, pp->dev);
+				continue;
+			}
+			if (all_tg_pt && pp->sg_id.host_no != -1) {
+				for (k = 0; k < count; k++) {
+					if (pp->sg_id.host_no == hosts[k]) {
+						condlog(3, "%s: %s host %d matches skip.", pp->wwid, pp->dev, pp->sg_id.host_no);
+						break;
+					}
+				}
+				if (k < count)
+					continue;
+			}
+			strlcpy(thread[count].param.dev, pp->dev,
+				FILE_NAME_SIZE);
+
+			if (count && (thread[count].param.paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK)){
+				/*
+				 * Clearing SPEC_I_PT as transportids are already registered by now.
+				 */
+				thread[count].param.paramp->sa_flags &= (~MPATH_F_SPEC_I_PT_MASK);
+			}
+
+			condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
+
+			rc = pthread_create(&thread[count].id, &attr, mpath_prout_pthread_fn, (void *)(&thread[count].param));
+			if (rc){
+				condlog (0, "%s: failed to create thread %d", mpp->wwid, rc);
+				thread[count].param.status = MPATH_PR_THREAD_ERROR;
+			}
+			else
+				hosts[count] = pp->sg_id.host_no;
+			count = count + 1;
+		}
+	}
+	for( i=0; i < count ; i++){
+		if (thread[i].param.status != MPATH_PR_THREAD_ERROR) {
+			rc = pthread_join(thread[i].id, NULL);
+			if (rc){
+				condlog (0, "%s: Thread[%d] failed to join thread %d", mpp->wwid, i, rc);
+			}
+		}
+		if (!rollback && (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)){
+			rollback = 1;
+			sa_key = get_unaligned_be64(&paramp->sa_key[0]);
+			status = MPATH_PR_RESERV_CONFLICT ;
+		}
+		if (!rollback && (status == MPATH_PR_SUCCESS)){
+			status = thread[i].param.status;
+		}
+	}
+	if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){
+		condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid);
+		memcpy(&paramp->key, &paramp->sa_key, 8);
+		memset(&paramp->sa_key, 0, 8);
+		for( i=0 ; i < count ; i++){
+			if(thread[i].param.status == MPATH_PR_SUCCESS) {
+				rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn,
+						(void *)(&thread[i].param));
+				if (rc){
+					condlog (0, "%s: failed to create thread for rollback. %d",  mpp->wwid, rc);
+					thread[i].param.status = MPATH_PR_THREAD_ERROR;
+				}
+			} else
+				thread[i].param.status = MPATH_PR_SKIP;
+		}
+		for(i=0; i < count ; i++){
+			if (thread[i].param.status != MPATH_PR_SKIP &&
+			    thread[i].param.status != MPATH_PR_THREAD_ERROR) {
+				rc = pthread_join(thread[i].id, NULL);
+				if (rc){
+					condlog (3, "%s: failed to join thread while rolling back %d",
+						 mpp->wwid, i);
+				}
+			}
+		}
+	}
+
+	pthread_attr_destroy(&attr);
+	return (status);
+}
+
+static int send_prout_activepath(char *dev, int rq_servact, int rq_scope,
+				 unsigned int rq_type,
+				 struct prout_param_descriptor * paramp, int noisy)
+{
+	struct prout_param param;
+	param.rq_servact = rq_servact;
+	param.rq_scope  = rq_scope;
+	param.rq_type   = rq_type;
+	param.paramp    = paramp;
+	param.noisy = noisy;
+	param.status = -1;
+
+	pthread_t thread;
+	pthread_attr_t attr;
+	int rc;
+
+	memset(&thread, 0, sizeof(thread));
+	strlcpy(param.dev, dev, FILE_NAME_SIZE);
+	/* Initialize and set thread joinable attribute */
+	pthread_attr_init(&attr);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+	rc = pthread_create(&thread, &attr, mpath_prout_pthread_fn, (void *)(&param));
+	if (rc){
+		condlog (3, "%s: failed to create thread %d", dev, rc);
+		return MPATH_PR_THREAD_ERROR;
+	}
+	/* Free attribute and wait for the other threads */
+	pthread_attr_destroy(&attr);
+	rc = pthread_join(thread, NULL);
+
+	return (param.status);
+}
+
+static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
+			      unsigned int rq_type,
+			      struct prout_param_descriptor* paramp, int noisy)
+{
+	int i,j, ret;
+	struct pathgroup *pgp = NULL;
+	struct path *pp = NULL;
+
+	vector_foreach_slot (mpp->pg, pgp, j){
+		vector_foreach_slot (pgp->paths, pp, i){
+			if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
+				condlog (1, "%s: %s path not up. Skip",
+					 mpp->wwid, pp->dev);
+				continue;
+			}
+
+			condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
+			ret = send_prout_activepath(pp->dev, rq_servact,
+						    rq_scope, rq_type,
+						    paramp, noisy);
+			return ret ;
+		}
+	}
+	condlog (0, "%s: no path available", mpp->wwid);
+	return MPATH_PR_DMMP_ERROR;
+}
+
+static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
+			   unsigned int rq_type,
+			   struct prout_param_descriptor * paramp, int noisy)
+{
+	int i, j;
+	int num = 0;
+	struct pathgroup *pgp = NULL;
+	struct path *pp = NULL;
+	int active_pathcount = 0;
+	pthread_attr_t attr;
+	int rc, found = 0;
+	int count = 0;
+	int status = MPATH_PR_SUCCESS;
+	struct prin_resp resp;
+	struct prout_param_descriptor *pamp;
+	struct prin_resp *pr_buff;
+	int length;
+	struct transportid *pptr;
+
+	if (!mpp)
+		return MPATH_PR_DMMP_ERROR;
+
+	active_pathcount = count_active_paths(mpp);
+
+	if (active_pathcount == 0) {
+		condlog (0, "%s: no path available", mpp->wwid);
+		return MPATH_PR_DMMP_ERROR;
+	}
+
+	struct threadinfo thread[active_pathcount];
+	memset(thread, 0, sizeof(thread));
+	for (i = 0; i < active_pathcount; i++){
+		thread[i].param.rq_servact = rq_servact;
+		thread[i].param.rq_scope = rq_scope;
+		thread[i].param.rq_type = rq_type;
+		thread[i].param.paramp = paramp;
+		thread[i].param.noisy = noisy;
+		thread[i].param.status = MPATH_PR_SKIP;
+
+		condlog (3, " path count = %d", i);
+		condlog (3, "rq_servact=%d ", thread[i].param.rq_servact);
+		condlog (3, "rq_scope=%d ", thread[i].param.rq_scope);
+		condlog (3, "rq_type=%d ", thread[i].param.rq_type);
+		condlog (3, "noisy=%d ", thread[i].param.noisy);
+		condlog (3, "status=%d ", thread[i].param.status);
+	}
+
+	pthread_attr_init (&attr);
+	pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_JOINABLE);
+
+	vector_foreach_slot (mpp->pg, pgp, j){
+		vector_foreach_slot (pgp->paths, pp, i){
+			if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
+				condlog (1, "%s: %s path not up.", mpp->wwid, pp->dev);
+				continue;
+			}
+
+			strlcpy(thread[count].param.dev, pp->dev,
+				FILE_NAME_SIZE);
+			condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
+			rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn,
+					(void *) (&thread[count].param));
+			if (rc) {
+				condlog (0, "%s: failed to create thread. %d",  mpp->wwid, rc);
+				thread[count].param.status = MPATH_PR_THREAD_ERROR;
+			}
+			count = count + 1;
+		}
+	}
+	pthread_attr_destroy (&attr);
+	for (i = 0; i < count; i++){
+		if (thread[i].param.status != MPATH_PR_THREAD_ERROR) {
+			rc = pthread_join (thread[i].id, NULL);
+			if (rc){
+				condlog (1, "%s: failed to join thread.  %d",  mpp->wwid,  rc);
+			}
+		}
+	}
+
+	for (i = 0; i < count; i++){
+		/*  check thread status here and return the status */
+
+		if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
+			status = MPATH_PR_RESERV_CONFLICT;
+		else if (status == MPATH_PR_SUCCESS
+				&& thread[i].param.status != MPATH_PR_RESERV_CONFLICT)
+			status = thread[i].param.status;
+	}
+
+	status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
+	if (status != MPATH_PR_SUCCESS){
+		condlog (0, "%s: pr in read reservation command failed.", mpp->wwid);
+		return MPATH_PR_OTHER;
+	}
+
+	num = resp.prin_descriptor.prin_readresv.additional_length / 8;
+	if (num == 0){
+		condlog (2, "%s: Path holding reservation is released.", mpp->wwid);
+		return MPATH_PR_SUCCESS;
+	}
+	condlog (2, "%s: Path holding reservation is not available.", mpp->wwid);
+
+	pr_buff =  mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA);
+	if (!pr_buff){
+		condlog (0, "%s: failed to  alloc pr in response buffer.", mpp->wwid);
+		return MPATH_PR_OTHER;
+	}
+
+	status = mpath_prin_activepath (mpp, MPATH_PRIN_RFSTAT_SA, pr_buff, noisy);
+
+	if (status != MPATH_PR_SUCCESS){
+		condlog (0,  "%s: pr in read full status command failed.",  mpp->wwid);
+		goto out;
+	}
+
+	num = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor;
+	if (0 == num){
+		goto out;
+	}
+	length = sizeof (struct prout_param_descriptor) + (sizeof (struct transportid *));
+
+	pamp = (struct prout_param_descriptor *)malloc (length);
+	if (!pamp){
+		condlog (0, "%s: failed to alloc pr out parameter.", mpp->wwid);
+		goto out1;
+	}
+
+	memset(pamp, 0, length);
+
+	pamp->trnptid_list[0] = (struct transportid *) malloc (sizeof (struct transportid));
+	if (!pamp->trnptid_list[0]){
+		condlog (0, "%s: failed to alloc pr out transportid.", mpp->wwid);
+		goto out1;
+	}
+
+	if (get_be64(mpp->reservation_key)){
+		memcpy (pamp->key, &mpp->reservation_key, 8);
+		condlog (3, "%s: reservation key set.", mpp->wwid);
+	}
+
+	status = mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA,
+				     rq_scope, rq_type, pamp, noisy);
+
+	if (status) {
+		condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid);
+		goto out1;
+	}
+
+	pamp->num_transportid = 1;
+	pptr=pamp->trnptid_list[0];
+
+	for (i = 0; i < num; i++){
+		if (get_be64(mpp->reservation_key) &&
+			memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key,
+			       &mpp->reservation_key, 8)){
+			/*register with tarnsport id*/
+			memset(pamp, 0, length);
+			pamp->trnptid_list[0] = pptr;
+			memset (pamp->trnptid_list[0], 0, sizeof (struct transportid));
+			memcpy (pamp->sa_key,
+					pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
+			pamp->sa_flags = MPATH_F_SPEC_I_PT_MASK;
+			pamp->num_transportid = 1;
+
+			memcpy (pamp->trnptid_list[0],
+					&pr_buff->prin_descriptor.prin_readfd.descriptors[i]->trnptid,
+					sizeof (struct transportid));
+			status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
+					pamp, noisy);
+
+			pamp->sa_flags = 0;
+			memcpy (pamp->key, pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
+			memset (pamp->sa_key, 0, 8);
+			pamp->num_transportid = 0;
+			status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
+					pamp, noisy);
+		}
+		else
+		{
+			if (get_be64(mpp->reservation_key))
+				found = 1;
+		}
+
+
+	}
+
+	if (found){
+		memset (pamp, 0, length);
+		memcpy (pamp->sa_key, &mpp->reservation_key, 8);
+		memset (pamp->key, 0, 8);
+		status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
+	}
+
+
+	free(pptr);
+out1:
+	free (pamp);
+out:
+	free (pr_buff);
+	return (status);
+}
+
+int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
+				    int rq_servact, int rq_scope, unsigned int rq_type,
+				    struct prout_param_descriptor *paramp, int noisy)
+{
+	struct multipath *mpp;
+	char *alias;
+	int ret;
+	uint64_t prkey;
+	struct config *conf;
+
+	ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
+	if (ret != MPATH_PR_SUCCESS)
+		return ret;
+
+	conf = get_multipath_config();
+	select_reservation_key(conf, mpp);
+	select_all_tg_pt(conf, mpp);
+	put_multipath_config(conf);
+
+	memcpy(&prkey, paramp->sa_key, 8);
+	if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey &&
+	    (rq_servact == MPATH_PROUT_REG_IGN_SA ||
+	     (rq_servact == MPATH_PROUT_REG_SA &&
+	      (!get_be64(mpp->reservation_key) ||
+	       memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) {
+		memcpy(&mpp->reservation_key, paramp->sa_key, 8);
+		if (update_prkey_flags(alias, get_be64(mpp->reservation_key),
+				       paramp->sa_flags)) {
+			condlog(0, "%s: failed to set prkey for multipathd.",
+				alias);
+			ret = MPATH_PR_DMMP_ERROR;
+			goto out1;
+		}
+	}
+
+	if (memcmp(paramp->key, &mpp->reservation_key, 8) &&
+	    memcmp(paramp->sa_key, &mpp->reservation_key, 8) &&
+	    (prkey || rq_servact != MPATH_PROUT_REG_IGN_SA)) {
+		condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64, alias, get_be64(mpp->reservation_key));
+		ret = MPATH_PR_SYNTAX_ERROR;
+		goto out1;
+	}
+
+	switch(rq_servact)
+	{
+	case MPATH_PROUT_REG_SA:
+	case MPATH_PROUT_REG_IGN_SA:
+		ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+		break;
+	case MPATH_PROUT_RES_SA :
+	case MPATH_PROUT_PREE_SA :
+	case MPATH_PROUT_PREE_AB_SA :
+	case MPATH_PROUT_CLEAR_SA:
+		ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+		break;
+	case MPATH_PROUT_REL_SA:
+		ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+		break;
+	default:
+		ret = MPATH_PR_OTHER;
+		goto out1;
+	}
+
+	if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) ||
+				(rq_servact ==  MPATH_PROUT_REG_IGN_SA)))
+	{
+		if (prkey == 0) {
+			update_prflag(alias, 0);
+			update_prkey(alias, 0);
+		} else
+			update_prflag(alias, 1);
+	} else if ((ret == MPATH_PR_SUCCESS) && (rq_servact == MPATH_PROUT_CLEAR_SA)) {
+		update_prflag(alias, 0);
+		update_prkey(alias, 0);
+	}
+out1:
+	free(alias);
+	return ret;
+}
+
+int update_map_pr(struct multipath *mpp)
+{
+	int noisy=0;
+	struct prin_resp *resp;
+	unsigned int i;
+	int ret, isFound;
+
+	if (!get_be64(mpp->reservation_key))
+	{
+		/* Nothing to do. Assuming pr mgmt feature is disabled*/
+		condlog(4, "%s: reservation_key not set in multipath.conf",
+			mpp->alias);
+		return MPATH_PR_SUCCESS;
+	}
+
+	resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
+	if (!resp)
+	{
+		condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
+		return MPATH_PR_OTHER;
+	}
+	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;
+	}
+
+	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;
+	}
+
+	condlog(2, "%s: Multipath  reservation_key: 0x%" PRIx64 " ", mpp->alias,
+		get_be64(mpp->reservation_key));
+
+	isFound =0;
+	for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ )
+	{
+		condlog(2, "%s: PR IN READKEYS[%d]  reservation key:", mpp->alias, i);
+		dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , 1);
+
+		if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
+		{
+			condlog(2, "%s: reservation key found in pr in readkeys response", mpp->alias);
+			isFound =1;
+		}
+	}
+
+	if (isFound)
+	{
+		mpp->prflag = 1;
+		condlog(2, "%s: prflag flag set.", mpp->alias );
+	}
+
+	free(resp);
+	return MPATH_PR_SUCCESS;
+}
diff -pruN 0.8.8-1/libmpathpersist/mpath_persist_int.h 0.9.0-3/libmpathpersist/mpath_persist_int.h
--- 0.8.8-1/libmpathpersist/mpath_persist_int.h	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/libmpathpersist/mpath_persist_int.h	2022-06-10 16:47:26.000000000 +0000
@@ -0,0 +1,25 @@
+#ifndef _MPATH_PERSIST_INT_H
+#define _MPATH_PERSIST_INT_H
+
+/*
+ * This header file contains symbols that are used by multipath-tools
+ * but aren't part of the public libmpathpersist API.
+ */
+
+void * mpath_alloc_prin_response(int prin_sa);
+int do_mpath_persistent_reserve_in(vector curmp, vector pathvec,
+				   int fd, int rq_servact,
+				   struct prin_resp *resp, int noisy);
+void *mpath_alloc_prin_response(int prin_sa);
+int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
+				    int rq_servact, int rq_scope,
+				    unsigned int rq_type,
+				    struct prout_param_descriptor *paramp,
+				    int noisy);
+int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int noisy);
+int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope,
+			 unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy);
+void dumpHex(const char* , int len, int no_ascii);
+int update_map_pr(struct multipath *mpp);
+
+#endif /* _MPATH_PERSIST_INT_H */
diff -pruN 0.8.8-1/libmpathpersist/mpathpr.h 0.9.0-3/libmpathpersist/mpathpr.h
--- 0.8.8-1/libmpathpersist/mpathpr.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmpathpersist/mpathpr.h	2022-06-10 16:47:26.000000000 +0000
@@ -1,54 +1,13 @@
 #ifndef MPATHPR_H
 #define MPATHPR_H
 
-#include "structs.h" /* FILE_NAME_SIZE */
-
-struct prin_param {
-	char dev[FILE_NAME_SIZE];
-	int rq_servact;
-	struct prin_resp *resp;
-	int noisy;
-	int status;
-};
-
-struct prout_param {
-	char dev[FILE_NAME_SIZE];
-	int rq_servact;
-	int rq_scope;
-	unsigned int rq_type;
-	struct prout_param_descriptor  *paramp;
-	int noisy;
-	int status;
-};
-
-struct threadinfo {
-	int status;
-	pthread_t id;
-	struct prout_param param;
-};
-
-int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int noisy);
-int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope,
-		unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy);
-void * _mpath_pr_update (void *arg);
-int mpath_send_prin_activepath (char * dev, int rq_servact, struct prin_resp * resp, int noisy);
-int get_mpvec (vector curmp, vector pathvec, char * refwwid);
-void * mpath_prout_pthread_fn(void *p);
-void dumpHex(const char* , int len, int no_ascii);
-
-int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
-	unsigned int rq_type,  struct prout_param_descriptor * paramp, int noisy);
-int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
-	unsigned int rq_type,  struct prout_param_descriptor * paramp, int noisy);
-int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
-	unsigned int rq_type,  struct prout_param_descriptor * paramp, int noisy);
-int send_prout_activepath(char * dev, int rq_servact, int rq_scope,
-	unsigned int rq_type,   struct prout_param_descriptor * paramp, int noisy);
+/*
+ * This header file contains symbols that are only used by
+ * libmpathpersist internally.
+ */
 
 int update_prflag(char *mapname, int set);
 int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags);
 #define update_prkey(mapname, prkey) update_prkey_flags(mapname, prkey, 0)
-void * mpath_alloc_prin_response(int prin_sa);
-int update_map_pr(struct multipath *mpp);
 
 #endif
diff -pruN 0.8.8-1/libmpathpersist/mpath_pr_ioctl.c 0.9.0-3/libmpathpersist/mpath_pr_ioctl.c
--- 0.8.8-1/libmpathpersist/mpath_pr_ioctl.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmpathpersist/mpath_pr_ioctl.c	2022-06-10 16:47:26.000000000 +0000
@@ -10,15 +10,13 @@
 #include <unistd.h>
 #include <string.h>
 #include <sys/ioctl.h>
-#include <unistd.h>
 #include <libudev.h>
 #include "mpath_pr_ioctl.h"
 #include "mpath_persist.h"
 #include "unaligned.h"
 
 #include "debug.h"
-
-#define FILE_NAME_SIZE          256
+#include "structs.h" /* FILE_NAME_SIZE */
 
 #define TIMEOUT 2000
 #define MAXRETRY 5
diff -pruN 0.8.8-1/libmpathpersist/mpath_updatepr.c 0.9.0-3/libmpathpersist/mpath_updatepr.c
--- 0.8.8-1/libmpathpersist/mpath_updatepr.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmpathpersist/mpath_updatepr.c	2022-06-10 16:47:26.000000000 +0000
@@ -1,6 +1,7 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <string.h>
 #include <stdarg.h>
 #include <fcntl.h>
 #include <sys/ioctl.h>
diff -pruN 0.8.8-1/libmpathvalid/Makefile 0.9.0-3/libmpathvalid/Makefile
--- 0.8.8-1/libmpathvalid/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmpathvalid/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -5,7 +5,8 @@ DEVLIB = libmpathvalid.so
 LIBS = $(DEVLIB).$(SONAME)
 VERSION_SCRIPT := libmpathvalid.version
 
-CFLAGS += $(LIB_CFLAGS) -I$(multipathdir) -I$(mpathcmddir)
+CPPFLAGS += -I$(multipathdir) -I$(mpathcmddir)
+CFLAGS += $(LIB_CFLAGS)
 
 LIBDEPS += -lpthread -ldevmapper -ldl -L$(multipathdir) \
 	   -lmultipath -L$(mpathcmddir) -lmpathcmd -ludev
@@ -43,7 +44,7 @@ uninstall:
 	$(RM) $(DESTDIR)$(includedir)/mpath_valid.h
 
 clean: dep_clean
-	$(RM) core *.a *.o *.so *.so.* *.gz *.abi $(NV_VERSION_SCRIPT)
+	$(RM) core *.a *.o *.so *.so.* *.abi $(NV_VERSION_SCRIPT)
 
 include $(wildcard $(OBJS:.o=.d))
 
diff -pruN 0.8.8-1/libmpathvalid/mpath_valid.h 0.9.0-3/libmpathvalid/mpath_valid.h
--- 0.8.8-1/libmpathvalid/mpath_valid.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmpathvalid/mpath_valid.h	2022-06-10 16:47:26.000000000 +0000
@@ -83,7 +83,7 @@ int mpathvalid_init(int verbosity, int l
 
 /*
  * DESCRIPTION:
- * 	Reread the multipath configuration files and reinitalize
+ * 	Reread the multipath configuration files and reinitialize
  * 	the device mapper multipath configuration. This function can
  * 	be called as many times as necessary.
  *
@@ -95,8 +95,8 @@ int mpathvalid_reload_config(void);
 /*
  * DESCRIPTION:
  * 	Release the device mapper multipath configuration. This
- * 	function must be called to cleanup resoures allocated by
- * 	mpathvalid_init(). After calling this function, no futher
+ * 	function must be called to cleanup resources allocated by
+ * 	mpathvalid_init(). After calling this function, no further
  * 	libmpathvalid functions may be called.
  *
  * RETURNS: 0 = Success, -1 = Failure
diff -pruN 0.8.8-1/libmultipath/alias.c 0.9.0-3/libmultipath/alias.c
--- 0.8.8-1/libmultipath/alias.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/alias.c	2022-06-10 16:47:26.000000000 +0000
@@ -4,7 +4,6 @@
  */
 #include <stdlib.h>
 #include <errno.h>
-#include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <limits.h>
@@ -19,8 +18,6 @@
 #include "checkers.h"
 #include "structs.h"
 #include "config.h"
-#include "util.h"
-#include "errno.h"
 #include "devmapper.h"
 #include "strbuf.h"
 
diff -pruN 0.8.8-1/libmultipath/callout.c 0.9.0-3/libmultipath/callout.c
--- 0.8.8-1/libmultipath/callout.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/callout.c	1970-01-01 00:00:00.000000000 +0000
@@ -1,221 +0,0 @@
-/*
- * Source: copy of the udev package source file
- *
- * Copyrights of the source file apply
- * Copyright (c) 2004 Christophe Varoqui
- */
-#include <stdio.h>
-#include <sys/stat.h>
-#include <string.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <stdlib.h>
-#include <fcntl.h>
-#include <sys/wait.h>
-#include <errno.h>
-
-#include "checkers.h"
-#include "vector.h"
-#include "structs.h"
-#include "util.h"
-#include "callout.h"
-#include "debug.h"
-
-int execute_program(char *path, char *value, int len)
-{
-	int retval;
-	int count;
-	int status;
-	int fds[2], null_fd;
-	pid_t pid;
-	char *pos;
-	char arg[CALLOUT_MAX_SIZE];
-	int argc = sizeof(arg) / 2;
-	char *argv[argc + 1];
-	int i;
-
-	i = 0;
-
-	if (strchr(path, ' ')) {
-		strlcpy(arg, path, sizeof(arg));
-		pos = arg;
-		while (pos != NULL && i < argc) {
-			if (pos[0] == '\'') {
-				/* don't separate if in apostrophes */
-				pos++;
-				argv[i] = strsep(&pos, "\'");
-				while (pos[0] == ' ')
-					pos++;
-			} else {
-				argv[i] = strsep(&pos, " ");
-			}
-			i++;
-		}
-	} else {
-		argv[i++] = path;
-	}
-	argv[i] =  NULL;
-
-	retval = pipe(fds);
-
-	if (retval != 0) {
-		condlog(0, "error creating pipe for callout: %s", strerror(errno));
-		return -1;
-	}
-
-	pid = fork();
-
-	switch(pid) {
-	case 0:
-		/* child */
-
-		/* dup write side of pipe to STDOUT */
-		if (dup2(fds[1], STDOUT_FILENO) < 0) {
-			condlog(1, "failed to dup2 stdout: %m");
-			return -1;
-		}
-		close(fds[0]);
-		close(fds[1]);
-
-		/* Ignore writes to stderr */
-		null_fd = open("/dev/null", O_WRONLY);
-		if (null_fd > 0) {
-			if (dup2(null_fd, STDERR_FILENO) < 0)
-				condlog(1, "failed to dup2 stderr: %m");
-			close(null_fd);
-		}
-
-		retval = execv(argv[0], argv);
-		condlog(0, "error execing %s : %s", argv[0], strerror(errno));
-		exit(-1);
-	case -1:
-		condlog(0, "fork failed: %s", strerror(errno));
-		close(fds[0]);
-		close(fds[1]);
-		return -1;
-	default:
-		/* parent reads from fds[0] */
-		close(fds[1]);
-		retval = 0;
-		i = 0;
-		while (1) {
-			count = read(fds[0], value + i, len - i-1);
-			if (count <= 0)
-				break;
-
-			i += count;
-			if (i >= len-1) {
-				condlog(0, "not enough space for response from %s", argv[0]);
-				retval = -1;
-				break;
-			}
-		}
-
-		if (count < 0) {
-			condlog(0, "no response from %s", argv[0]);
-			retval = -1;
-		}
-
-		if (i > 0 && value[i-1] == '\n')
-			i--;
-		value[i] = '\0';
-
-		wait(&status);
-		close(fds[0]);
-
-		retval = -1;
-		if (WIFEXITED(status)) {
-			status = WEXITSTATUS(status);
-			if (status == 0)
-				retval = 0;
-			else
-				condlog(0, "%s exited with %d", argv[0], status);
-		}
-		else if (WIFSIGNALED(status))
-			condlog(0, "%s was terminated by signal %d", argv[0], WTERMSIG(status));
-		else
-			condlog(0, "%s terminated abnormally", argv[0]);
-	}
-	return retval;
-}
-
-int apply_format(char * string, char * cmd, struct path * pp)
-{
-	char * pos;
-	char * dst;
-	char * p;
-	char * q;
-	int len;
-	int myfree;
-
-	if (!string)
-		return 1;
-
-	if (!cmd)
-		return 1;
-
-	dst = cmd;
-	p = dst;
-	pos = strchr(string, '%');
-	myfree = CALLOUT_MAX_SIZE;
-
-	if (!pos) {
-		strcpy(dst, string);
-		return 0;
-	}
-
-	len = (int) (pos - string) + 1;
-	myfree -= len;
-
-	if (myfree < 2)
-		return 1;
-
-	snprintf(p, len, "%s", string);
-	p += len - 1;
-	pos++;
-
-	switch (*pos) {
-	case 'n':
-		len = strlen(pp->dev) + 1;
-		myfree -= len;
-
-		if (myfree < 2)
-			return 1;
-
-		snprintf(p, len, "%s", pp->dev);
-		for (q = p; q < p + len; q++) {
-			if (q && *q == '!')
-				*q = '/';
-		}
-		p += len - 1;
-		break;
-	case 'd':
-		len = strlen(pp->dev_t) + 1;
-		myfree -= len;
-
-		if (myfree < 2)
-			return 1;
-
-		snprintf(p, len, "%s", pp->dev_t);
-		p += len - 1;
-		break;
-	default:
-		break;
-	}
-	pos++;
-
-	if (!*pos) {
-		condlog(3, "formatted callout = %s", dst);
-		return 0;
-	}
-
-	len = strlen(pos) + 1;
-	myfree -= len;
-
-	if (myfree < 2)
-		return 1;
-
-	snprintf(p, len, "%s", pos);
-	condlog(3, "reformatted callout = %s", dst);
-	return 0;
-}
diff -pruN 0.8.8-1/libmultipath/callout.h 0.9.0-3/libmultipath/callout.h
--- 0.8.8-1/libmultipath/callout.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/callout.h	1970-01-01 00:00:00.000000000 +0000
@@ -1,7 +0,0 @@
-#ifndef _CALLOUT_H
-#define _CALLOUT_H
-
-int execute_program(char *, char *, int);
-int apply_format (char *, char *, struct path *);
-
-#endif /* _CALLOUT_H */
diff -pruN 0.8.8-1/libmultipath/checkers/directio.c 0.9.0-3/libmultipath/checkers/directio.c
--- 0.8.8-1/libmultipath/checkers/directio.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/checkers/directio.c	2022-06-10 16:47:26.000000000 +0000
@@ -12,7 +12,6 @@
 #include <sys/ioctl.h>
 #include <linux/fs.h>
 #include <errno.h>
-#include <unistd.h>
 #include <libaio.h>
 
 #include "checkers.h"
@@ -23,7 +22,7 @@
 
 /* Note: This checker type relies on the fact that only one checker can be run
  * at a time, since multiple checkers share the same aio_group, and must be
- * able to modify other checker's async_reqs. If multple checkers become able
+ * able to modify other checker's async_reqs. If multiple checkers become able
  * to be run at the same time, this checker will need to add locking, and
  * probably polling on event fds, to deal with that */
 
diff -pruN 0.8.8-1/libmultipath/checkers/Makefile 0.9.0-3/libmultipath/checkers/Makefile
--- 0.8.8-1/libmultipath/checkers/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/checkers/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -3,7 +3,8 @@
 #
 include ../../Makefile.inc
 
-CFLAGS += $(LIB_CFLAGS) -I..
+CPPFLAGS += -I..
+CFLAGS += $(LIB_CFLAGS)
 LDFLAGS += -L.. -lmultipath
 LIBDEPS = -lmultipath -laio -lpthread -lrt
 
diff -pruN 0.8.8-1/libmultipath/checkers/rdac.c 0.9.0-3/libmultipath/checkers/rdac.c
--- 0.8.8-1/libmultipath/checkers/rdac.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/checkers/rdac.c	2022-06-10 16:47:26.000000000 +0000
@@ -96,7 +96,7 @@ int libcheck_init (struct checker * c)
 		goto out;
 	}
 
-	/* get the changeble values */
+	/* get the changeable values */
 	cmd[2] = 0xA + (CHANGEABLE_PAGE_CODE_VALUES << 6);
 	io_hdr.dxferp = &changeable;
 	memset(&changeable, 0, sizeof(struct control_mode_page));
diff -pruN 0.8.8-1/libmultipath/checkers/tur.c 0.9.0-3/libmultipath/checkers/tur.c
--- 0.8.8-1/libmultipath/checkers/tur.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/checkers/tur.c	2022-06-10 16:47:26.000000000 +0000
@@ -23,10 +23,10 @@
 #include "../libmultipath/sg_include.h"
 #include "../libmultipath/util.h"
 #include "../libmultipath/time-util.h"
-#include "../libmultipath/util.h"
 
 #define TUR_CMD_LEN 6
 #define HEAVY_CHECK_COUNT       10
+#define MAX_NR_TIMEOUTS 1
 
 enum {
 	MSG_TUR_RUNNING = CHECKER_FIRST_MSGID,
@@ -55,6 +55,7 @@ struct tur_checker_context {
 	int holders; /* uatomic access only */
 	int msgid;
 	struct checker_context ctx;
+	unsigned int nr_timeouts;
 };
 
 int libcheck_init (struct checker * c)
@@ -359,8 +360,23 @@ int libcheck_check(struct checker * c)
 		}
 	} else {
 		if (uatomic_read(&ct->holders) > 1) {
+			/* The thread has been cancelled but hasn't quit. */
+			if (ct->nr_timeouts == MAX_NR_TIMEOUTS) {
+				condlog(2, "%d:%d : waiting for stalled tur thread to finish",
+					major(ct->devt), minor(ct->devt));
+				ct->nr_timeouts++;
+			}
+			/*
+			 * Don't start new threads until the last once has
+			 * finished.
+			 */
+			if (ct->nr_timeouts > MAX_NR_TIMEOUTS) {
+				c->msgid = MSG_TUR_TIMEOUT;
+				return PATH_TIMEOUT;
+			}
+			ct->nr_timeouts++;
 			/*
-			 * The thread has been cancelled but hasn't quit.
+			 * Start a new thread while the old one is stalled.
 			 * We have to prevent it from interfering with the new
 			 * thread. We create a new context and leave the old
 			 * one with the stale thread, hoping it will clean up
@@ -376,13 +392,15 @@ int libcheck_check(struct checker * c)
 			 */
 			if (libcheck_init(c) != 0)
 				return PATH_UNCHECKED;
+			((struct tur_checker_context *)c->context)->nr_timeouts = ct->nr_timeouts;
 
 			if (!uatomic_sub_return(&ct->holders, 1))
 				/* It did terminate, eventually */
 				cleanup_context(ct);
 
 			ct = c->context;
-		}
+		} else
+			ct->nr_timeouts = 0;
 		/* Start new TUR checker */
 		pthread_mutex_lock(&ct->lock);
 		tur_status = ct->state = PATH_PENDING;
diff -pruN 0.8.8-1/libmultipath/checkers.c 0.9.0-3/libmultipath/checkers.c
--- 0.8.8-1/libmultipath/checkers.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/checkers.c	2022-06-10 16:47:26.000000000 +0000
@@ -5,12 +5,15 @@
 #include <sys/stat.h>
 #include <urcu.h>
 #include <urcu/uatomic.h>
+#include <assert.h>
 
 #include "debug.h"
 #include "checkers.h"
 #include "vector.h"
 #include "util.h"
 
+static const char * const checker_dir = MULTIPATH_DIR;
+
 struct checker_class {
 	struct list_head node;
 	void *handle;
@@ -132,8 +135,7 @@ void reset_checker_classes(void)
 	}
 }
 
-static struct checker_class *add_checker_class(const char *multipath_dir,
-					       const char *name)
+static struct checker_class *add_checker_class(const char *name)
 {
 	char libname[LIB_CHECKER_NAMELEN];
 	struct stat stbuf;
@@ -147,10 +149,10 @@ static struct checker_class *add_checker
 	if (!strncmp(c->name, NONE, 4))
 		goto done;
 	snprintf(libname, LIB_CHECKER_NAMELEN, "%s/libcheck%s.so",
-		 multipath_dir, name);
+		 checker_dir, name);
 	if (stat(libname,&stbuf) < 0) {
 		condlog(0,"Checker '%s' not found in %s",
-			name, multipath_dir);
+			name, checker_dir);
 		goto out;
 	}
 	condlog(3, "loading %s checker", libname);
@@ -408,8 +410,7 @@ void checker_clear_message (struct check
 	c->msgid = CHECKER_MSGID_NONE;
 }
 
-void checker_get(const char *multipath_dir, struct checker *dst,
-		 const char *name)
+void checker_get(struct checker *dst, const char *name)
 {
 	struct checker_class *src = NULL;
 
@@ -419,7 +420,7 @@ void checker_get(const char *multipath_d
 	if (name && strlen(name)) {
 		src = checker_class_lookup(name);
 		if (!src)
-			src = add_checker_class(multipath_dir, name);
+			src = add_checker_class(name);
 	}
 	dst->cls = src;
 	if (!src)
@@ -428,7 +429,7 @@ void checker_get(const char *multipath_d
 	(void)checker_class_ref(dst->cls);
 }
 
-int init_checkers(const char *multipath_dir)
+int init_checkers(void)
 {
 #ifdef LOAD_ALL_SHARED_LIBS
 	static const char *const all_checkers[] = {
@@ -443,9 +444,9 @@ int init_checkers(const char *multipath_
 	unsigned int i;
 
 	for (i = 0; i < ARRAY_SIZE(all_checkers); i++)
-		add_checker_class(multipath_dir, all_checkers[i]);
+		add_checker_class(all_checkers[i]);
 #else
-	if (!add_checker_class(multipath_dir, DEFAULT_CHECKER))
+	if (!add_checker_class(DEFAULT_CHECKER))
 		return 1;
 #endif
 	return 0;
diff -pruN 0.8.8-1/libmultipath/checkers.h 0.9.0-3/libmultipath/checkers.h
--- 0.8.8-1/libmultipath/checkers.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/checkers.h	2022-06-10 16:47:26.000000000 +0000
@@ -135,7 +135,7 @@ static inline int checker_selected(const
 }
 
 const char *checker_state_name(int);
-int init_checkers(const char *);
+int init_checkers(void);
 void cleanup_checkers (void);
 int checker_init (struct checker *, void **);
 int checker_mp_init(struct checker *, void **);
@@ -156,7 +156,7 @@ void checker_disable (struct checker *);
  *
  * Rationale:
  * Path checkers that do I/O may hang forever. To avoid blocking, some
- * checkers therefore use asyncronous, detached threads for checking
+ * checkers therefore use asynchronous, detached threads for checking
  * the paths. These threads may continue hanging if multipathd is stopped.
  * In this case, we can't unload the checker DSO at exit. In order to
  * avoid race conditions and crashes, the entry point of the thread
@@ -179,7 +179,7 @@ void reset_checker_classes(void);
  */
 const char *checker_message(const struct checker *);
 void checker_clear_message (struct checker *c);
-void checker_get(const char *, struct checker *, const char *);
+void checker_get(struct checker *, const char *);
 
 /* Prototypes for symbols exported by path checker dynamic libraries (.so) */
 int libcheck_check(struct checker *);
diff -pruN 0.8.8-1/libmultipath/config.c 0.9.0-3/libmultipath/config.c
--- 0.8.8-1/libmultipath/config.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/config.c	2022-06-10 16:47:26.000000000 +0000
@@ -237,6 +237,18 @@ const char *get_mpe_wwid(const struct _v
 	return NULL;
 }
 
+static void
+free_pctable (vector pctable)
+{
+	int i;
+	struct pcentry *pce;
+
+	vector_foreach_slot(pctable, pce, i)
+		free(pce);
+
+	vector_free(pctable);
+}
+
 void
 free_hwe (struct hwentry * hwe)
 {
@@ -252,9 +264,6 @@ free_hwe (struct hwentry * hwe)
 	if (hwe->revision)
 		free(hwe->revision);
 
-	if (hwe->getuid)
-		free(hwe->getuid);
-
 	if (hwe->uid_attribute)
 		free(hwe->uid_attribute);
 
@@ -282,6 +291,9 @@ free_hwe (struct hwentry * hwe)
 	if (hwe->bl_product)
 		free(hwe->bl_product);
 
+	if (hwe->pctable)
+		free_pctable(hwe->pctable);
+
 	free(hwe);
 }
 
@@ -312,9 +324,6 @@ free_mpe (struct mpentry * mpe)
 	if (mpe->selector)
 		free(mpe->selector);
 
-	if (mpe->getuid)
-		free(mpe->getuid);
-
 	if (mpe->uid_attribute)
 		free(mpe->uid_attribute);
 
@@ -363,6 +372,15 @@ alloc_hwe (void)
 	return hwe;
 }
 
+struct pcentry *
+alloc_pce (void)
+{
+	struct pcentry *pce = (struct pcentry *)
+				calloc(1, sizeof(struct pcentry));
+	pce->type = PCE_INVALID;
+	return pce;
+}
+
 static char *
 set_param_str(const char * str)
 {
@@ -387,24 +405,30 @@ set_param_str(const char * str)
 }
 
 #define merge_str(s) \
-	if (!dst->s && src->s) { \
-		if (!(dst->s = set_param_str(src->s))) \
-			return 1; \
+	if (!dst->s && src->s && strlen(src->s)) { \
+		dst->s = src->s; \
+		src->s = NULL; \
 	}
 
 #define merge_num(s) \
 	if (!dst->s && src->s) \
 		dst->s = src->s
 
+static void
+merge_pce(struct pcentry *dst, struct pcentry *src)
+{
+	merge_num(fast_io_fail);
+	merge_num(dev_loss);
+	merge_num(eh_deadline);
+}
 
-static int
+static void
 merge_hwe (struct hwentry * dst, struct hwentry * src)
 {
 	char id[SCSI_VENDOR_SIZE+PATH_PRODUCT_SIZE];
 	merge_str(vendor);
 	merge_str(product);
 	merge_str(revision);
-	merge_str(getuid);
 	merge_str(uid_attribute);
 	merge_str(features);
 	merge_str(hwhandler);
@@ -449,18 +473,13 @@ merge_hwe (struct hwentry * dst, struct
 	reconcile_features_with_options(id, &dst->features,
 					&dst->no_path_retry,
 					&dst->retain_hwhandler);
-	return 0;
 }
 
-static int
+static void
 merge_mpe(struct mpentry *dst, struct mpentry *src)
 {
-	if (!dst || !src)
-		return 1;
-
 	merge_str(alias);
 	merge_str(uid_attribute);
-	merge_str(getuid);
 	merge_str(selector);
 	merge_str(features);
 	merge_str(prio_name);
@@ -499,8 +518,6 @@ merge_mpe(struct mpentry *dst, struct mp
 	merge_num(uid);
 	merge_num(gid);
 	merge_num(mode);
-
-	return 0;
 }
 
 void merge_mptable(vector mptable)
@@ -554,9 +571,6 @@ store_hwe (vector hwtable, struct hwentr
 	if (dhwe->uid_attribute && !(hwe->uid_attribute = set_param_str(dhwe->uid_attribute)))
 		goto out;
 
-	if (dhwe->getuid && !(hwe->getuid = set_param_str(dhwe->getuid)))
-		goto out;
-
 	if (dhwe->features && !(hwe->features = set_param_str(dhwe->features)))
 		goto out;
 
@@ -609,6 +623,51 @@ out:
 }
 
 static void
+validate_pctable(struct hwentry *ovr, int idx, const char *table_desc)
+{
+	struct pcentry *pce;
+
+	if (!ovr || !ovr->pctable)
+		return;
+
+	vector_foreach_slot_after(ovr->pctable, pce, idx) {
+		if (pce->type == PCE_INVALID) {
+			condlog(0, "protocol section in %s missing type",
+				table_desc);
+			vector_del_slot(ovr->pctable, idx--);
+			free(pce);
+		}
+	}
+
+	if (VECTOR_SIZE(ovr->pctable) == 0) {
+		vector_free(ovr->pctable);
+		ovr->pctable = NULL;
+	}
+}
+
+static void
+merge_pctable(struct hwentry *ovr)
+{
+	struct pcentry *pce1, *pce2;
+	int i, j;
+
+	if (!ovr || !ovr->pctable)
+		return;
+
+	vector_foreach_slot(ovr->pctable, pce1, i) {
+		j = i + 1;
+		vector_foreach_slot_after(ovr->pctable, pce2, j) {
+			if (pce1->type != pce2->type)
+				continue;
+			merge_pce(pce2,pce1);
+			vector_del_slot(ovr->pctable, i--);
+			free(pce1);
+			break;
+		}
+	}
+}
+
+static void
 factorize_hwtable (vector hw, int n, const char *table_desc)
 {
 	struct hwentry *hwe1, *hwe2;
@@ -656,23 +715,22 @@ static struct config *alloc_config (void
 
 static void _uninit_config(struct config *conf)
 {
+	void *ptr;
+	int i;
+
 	if (!conf)
 		conf = &__internal_config;
 
-	if (conf->multipath_dir)
-		free(conf->multipath_dir);
-
 	if (conf->selector)
 		free(conf->selector);
 
 	if (conf->uid_attribute)
 		free(conf->uid_attribute);
 
+	vector_foreach_slot(&conf->uid_attrs, ptr, i)
+		free(ptr);
 	vector_reset(&conf->uid_attrs);
 
-	if (conf->getuid)
-		free(conf->getuid);
-
 	if (conf->features)
 		free(conf->features);
 
@@ -702,8 +760,6 @@ static void _uninit_config(struct config
 	if (conf->checker_name)
 		free(conf->checker_name);
 
-	if (conf->config_dir)
-		free(conf->config_dir);
 	if (conf->enable_foreign)
 		free(conf->enable_foreign);
 
@@ -756,6 +812,7 @@ process_config_dir(struct config *conf,
 	int i, n;
 	char path[LINE_MAX];
 	int old_hwtable_size;
+	int old_pctable_size = 0;
 
 	if (dir[0] != '/') {
 		condlog(1, "config_dir '%s' must be a fully qualified path",
@@ -782,11 +839,15 @@ process_config_dir(struct config *conf,
 			continue;
 
 		old_hwtable_size = VECTOR_SIZE(conf->hwtable);
+		old_pctable_size = conf->overrides ?
+				   VECTOR_SIZE(conf->overrides->pctable) : 0;
 		snprintf(path, LINE_MAX, "%s/%s", dir, namelist[i]->d_name);
 		path[LINE_MAX-1] = '\0';
 		process_file(conf, path);
 		factorize_hwtable(conf->hwtable, old_hwtable_size,
 				  namelist[i]->d_name);
+		validate_pctable(conf->overrides, old_pctable_size,
+				 namelist[i]->d_name);
 	}
 	pthread_cleanup_pop(1);
 }
@@ -849,7 +910,6 @@ int _init_config (const char *file, stru
 	conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE);
 	conf->wwids_file = set_default(DEFAULT_WWIDS_FILE);
 	conf->prkeys_file = set_default(DEFAULT_PRKEYS_FILE);
-	conf->multipath_dir = set_default(DEFAULT_MULTIPATHDIR);
 	conf->attribute_flags = 0;
 	conf->reassign_maps = DEFAULT_REASSIGN_MAPS;
 	conf->checkint = CHECKINT_UNDEF;
@@ -894,13 +954,11 @@ int _init_config (const char *file, stru
 			goto out;
 		}
 		factorize_hwtable(conf->hwtable, builtin_hwtable_size, file);
+		validate_pctable(conf->overrides, 0, file);
 	}
 
 	conf->processed_main_config = 1;
-	if (conf->config_dir == NULL)
-		conf->config_dir = set_default(DEFAULT_CONFIG_DIR);
-	if (conf->config_dir && conf->config_dir[0] != '\0')
-		process_config_dir(conf, conf->config_dir);
+	process_config_dir(conf, CONFIG_DIR);
 
 	/*
 	 * fill the voids left in the config file
@@ -994,6 +1052,7 @@ int _init_config (const char *file, stru
 			goto out;
 	}
 
+	merge_pctable(conf->overrides);
 	merge_mptable(conf->mptable);
 	merge_blacklist(conf->blist_devnode);
 	merge_blacklist(conf->blist_property);
@@ -1007,8 +1066,7 @@ int _init_config (const char *file, stru
 	if (conf->bindings_file == NULL)
 		conf->bindings_file = set_default(DEFAULT_BINDINGS_FILE);
 
-	if (!conf->multipath_dir || !conf->bindings_file ||
-	    !conf->wwids_file || !conf->prkeys_file)
+	if (!conf->bindings_file || !conf->wwids_file || !conf->prkeys_file)
 		goto out;
 
 	libmp_verbosity = conf->verbosity;
@@ -1018,10 +1076,10 @@ out:
 	return 1;
 }
 
-char *get_uid_attribute_by_attrs(struct config *conf,
-				 const char *path_dev)
+const char *get_uid_attribute_by_attrs(const struct config *conf,
+				       const char *path_dev)
 {
-	vector uid_attrs = &conf->uid_attrs;
+	const struct _vector *uid_attrs = &conf->uid_attrs;
 	int j;
 	char *att, *col;
 
diff -pruN 0.8.8-1/libmultipath/config.h 0.9.0-3/libmultipath/config.h
--- 0.8.8-1/libmultipath/config.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/config.h	2022-06-10 16:47:26.000000000 +0000
@@ -40,12 +40,19 @@ enum force_reload_types {
 	FORCE_RELOAD_WEAK,
 };
 
+#define PCE_INVALID -1
+struct pcentry {
+	int type;
+	int fast_io_fail;
+	unsigned int dev_loss;
+	int eh_deadline;
+};
+
 struct hwentry {
 	char * vendor;
 	char * product;
 	char * revision;
 	char * uid_attribute;
-	char * getuid;
 	char * features;
 	char * hwhandler;
 	char * selector;
@@ -85,13 +92,14 @@ struct hwentry {
 	int vpd_vendor_id;
 	int recheck_wwid;
 	char * bl_product;
+
+	vector pctable;
 };
 
 struct mpentry {
 	char * wwid;
 	char * alias;
 	char * uid_attribute;
-	char * getuid;
 	char * selector;
 	char * features;
 
@@ -189,11 +197,9 @@ struct config {
 	unsigned int sequence_nr;
 	int recheck_wwid;
 
-	char * multipath_dir;
 	char * selector;
 	struct _vector uid_attrs;
 	char * uid_attribute;
-	char * getuid;
 	char * features;
 	char * hwhandler;
 	char * bindings_file;
@@ -204,7 +210,6 @@ struct config {
 	char * checker_name;
 	char * alias_prefix;
 	char * partition_delim;
-	char * config_dir;
 	int prkey_source;
 	int all_tg_pt;
 	struct be64 reservation_key;
@@ -284,6 +289,7 @@ const char *get_mpe_wwid (const struct _
 
 struct hwentry * alloc_hwe (void);
 struct mpentry * alloc_mpe (void);
+struct pcentry * alloc_pce (void);
 
 void free_hwe (struct hwentry * hwe);
 void free_hwtable (vector hwtable);
@@ -314,7 +320,7 @@ void libmp_put_multipath_config(void *);
 void put_multipath_config(void *);
 
 int parse_uid_attrs(char *uid_attrs, struct config *conf);
-char *get_uid_attribute_by_attrs(struct config *conf,
-				 const char *path_dev);
+const char *get_uid_attribute_by_attrs(const struct config *conf,
+				       const char *path_dev);
 
 #endif
diff -pruN 0.8.8-1/libmultipath/configure.c 0.9.0-3/libmultipath/configure.c
--- 0.8.8-1/libmultipath/configure.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/configure.c	2022-06-10 16:47:26.000000000 +0000
@@ -329,9 +329,6 @@ int setup_map(struct multipath *mpp, cha
 	select_mode(conf, mpp);
 	select_uid(conf, mpp);
 	select_gid(conf, mpp);
-	select_fast_io_fail(conf, mpp);
-	select_dev_loss(conf, mpp);
-	select_eh_deadline(conf, mpp);
 	select_reservation_key(conf, mpp);
 	select_deferred_remove(conf, mpp);
 	select_marginal_path_err_sample_time(conf, mpp);
@@ -347,7 +344,7 @@ int setup_map(struct multipath *mpp, cha
 	select_ghost_delay(conf, mpp);
 	select_flush_on_last_del(conf, mpp);
 
-	sysfs_set_scsi_tmo(mpp, conf->checkint);
+	sysfs_set_scsi_tmo(conf, mpp);
 	marginal_pathgroups = conf->marginal_pathgroups;
 	pthread_cleanup_pop(1);
 
@@ -450,12 +447,12 @@ get_udev_for_mpp(const struct multipath
 	dev_t devnum;
 	struct udev_device *udd;
 
-	if (!mpp || !mpp->dmi) {
+	if (!mpp || !has_dm_info(mpp)) {
 		condlog(1, "%s called with empty mpp", __func__);
 		return NULL;
 	}
 
-	devnum = makedev(mpp->dmi->major, mpp->dmi->minor);
+	devnum = makedev(mpp->dmi.major, mpp->dmi.minor);
 	udd = udev_device_new_from_devnum(udev, 'b', devnum);
 	if (!udd) {
 		condlog(1, "failed to get udev device for %s", mpp->alias);
@@ -503,11 +500,8 @@ unref:
 }
 
 void
-trigger_paths_udev_change(struct multipath *mpp, bool is_mpath)
+trigger_path_udev_change(struct path *pp, bool is_mpath)
 {
-	struct pathgroup *pgp;
-	struct path *pp;
-	int i, j;
 	/*
 	 * If a path changes from multipath to non-multipath, we must
 	 * synthesize an artificial "add" event, otherwise the LVM2 rules
@@ -515,6 +509,45 @@ trigger_paths_udev_change(struct multipa
 	 * irritate ourselves with an "add", so use "change".
 	 */
 	const char *action = is_mpath ? "change" : "add";
+	const char *env;
+
+	if (!pp->udev)
+		return;
+	/*
+	 * Paths that are already classified as multipath
+	 * members don't need another uevent.
+	 */
+	env = udev_device_get_property_value(
+		pp->udev, "DM_MULTIPATH_DEVICE_PATH");
+
+	if (is_mpath && env != NULL && !strcmp(env, "1")) {
+		/*
+		 * If FIND_MULTIPATHS_WAIT_UNTIL is not "0",
+		 * path is in "maybe" state and timer is running
+		 * Send uevent now (see multipath.rules).
+		 */
+		env = udev_device_get_property_value(
+			pp->udev, "FIND_MULTIPATHS_WAIT_UNTIL");
+		if (env == NULL || !strcmp(env, "0"))
+			return;
+	} else if (!is_mpath &&
+		   (env == NULL || !strcmp(env, "0")))
+		return;
+
+	condlog(3, "triggering %s uevent for %s (is %smultipath member)",
+		action, pp->dev, is_mpath ? "" : "no ");
+	sysfs_attr_set_value(pp->udev, "uevent",
+			     action, strlen(action));
+	trigger_partitions_udev_change(pp->udev, action,
+				       strlen(action));
+}
+
+void
+trigger_paths_udev_change(struct multipath *mpp, bool is_mpath)
+{
+	struct pathgroup *pgp;
+	struct path *pp;
+	int i, j;
 
 	if (!mpp || !mpp->pg)
 		return;
@@ -522,39 +555,8 @@ trigger_paths_udev_change(struct multipa
 	vector_foreach_slot (mpp->pg, pgp, i) {
 		if (!pgp->paths)
 			continue;
-		vector_foreach_slot(pgp->paths, pp, j) {
-			const char *env;
-
-			if (!pp->udev)
-				continue;
-			/*
-			 * Paths that are already classified as multipath
-			 * members don't need another uevent.
-			 */
-			env = udev_device_get_property_value(
-				pp->udev, "DM_MULTIPATH_DEVICE_PATH");
-
-			if (is_mpath && env != NULL && !strcmp(env, "1")) {
-				/*
-				 * If FIND_MULTIPATHS_WAIT_UNTIL is not "0",
-				 * path is in "maybe" state and timer is running
-				 * Send uevent now (see multipath.rules).
-				 */
-				env = udev_device_get_property_value(
-					pp->udev, "FIND_MULTIPATHS_WAIT_UNTIL");
-				if (env == NULL || !strcmp(env, "0"))
-					continue;
-			} else if (!is_mpath &&
-				   (env == NULL || !strcmp(env, "0")))
-				continue;
-
-			condlog(3, "triggering %s uevent for %s (is %smultipath member)",
-				action, pp->dev, is_mpath ? "" : "no ");
-			sysfs_attr_set_value(pp->udev, "uevent",
-					     action, strlen(action));
-			trigger_partitions_udev_change(pp->udev, action,
-						       strlen(action));
-		}
+		vector_foreach_slot(pgp->paths, pp, j)
+			trigger_path_udev_change(pp, is_mpath);
 	}
 
 	mpp->needs_paths_uevent = 0;
@@ -574,7 +576,8 @@ sysfs_set_max_sectors_kb(struct multipat
 		return 0;
 	max_sectors_kb = mpp->max_sectors_kb;
 	if (is_reload) {
-		if (!mpp->dmi && dm_get_info(mpp->alias, &mpp->dmi) != 0) {
+		if (!has_dm_info(mpp) &&
+		    dm_get_info(mpp->alias, &mpp->dmi) != 0) {
 			condlog(1, "failed to get dm info for %s", mpp->alias);
 			return 1;
 		}
diff -pruN 0.8.8-1/libmultipath/configure.h 0.9.0-3/libmultipath/configure.h
--- 0.8.8-1/libmultipath/configure.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/configure.h	2022-06-10 16:47:26.000000000 +0000
@@ -56,6 +56,7 @@ int coalesce_paths (struct vectors *vecs
 int get_refwwid (enum mpath_cmds cmd, const char *dev, enum devtypes dev_type,
 		 vector pathvec, char **wwid);
 struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type);
+void trigger_path_udev_change(struct path *pp, bool is_mpath);
 void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath);
 void trigger_partitions_udev_change(struct udev_device *dev, const char *action,
 				    int len);
diff -pruN 0.8.8-1/libmultipath/defaults.h 0.9.0-3/libmultipath/defaults.h
--- 0.8.8-1/libmultipath/defaults.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/defaults.h	2022-06-10 16:47:26.000000000 +0000
@@ -11,7 +11,6 @@
 #define DEFAULT_NVME_UID_ATTRIBUTE	"ID_WWN"
 #define DEFAULT_DASD_UID_ATTRIBUTE	"ID_UID"
 #define DEFAULT_UDEVDIR		"/dev"
-#define DEFAULT_MULTIPATHDIR	"/" LIB_STRING "/multipath"
 #define DEFAULT_SELECTOR	"service-time 0"
 #define DEFAULT_ALIAS_PREFIX	"mpath"
 #define DEFAULT_FEATURES	"0"
@@ -69,7 +68,6 @@
 #define DEFAULT_BINDINGS_FILE	"/etc/multipath/bindings"
 #define DEFAULT_WWIDS_FILE	"/etc/multipath/wwids"
 #define DEFAULT_PRKEYS_FILE    "/etc/multipath/prkeys"
-#define DEFAULT_CONFIG_DIR	"/etc/multipath/conf.d"
 #define MULTIPATH_SHM_BASE	"/dev/shm/multipath/"
 
 
diff -pruN 0.8.8-1/libmultipath/devmapper.c 0.9.0-3/libmultipath/devmapper.c
--- 0.8.8-1/libmultipath/devmapper.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/devmapper.c	2022-06-10 16:47:26.000000000 +0000
@@ -473,14 +473,11 @@ dm_addmap (int task, const char *target,
 		dm_task_set_ro(dmt);
 
 	if (task == DM_DEVICE_CREATE) {
-		prefixed_uuid = calloc(1, UUID_PREFIX_LEN +
-				       strlen(mpp->wwid) + 1);
-		if (!prefixed_uuid) {
+		if (asprintf(&prefixed_uuid, UUID_PREFIX "%s", mpp->wwid) < 0) {
 			condlog(0, "cannot create prefixed uuid : %s",
 				strerror(errno));
 			goto addout;
 		}
-		sprintf(prefixed_uuid, UUID_PREFIX "%s", mpp->wwid);
 		if (!dm_task_set_uuid(dmt, prefixed_uuid))
 			goto freeout;
 		dm_task_skip_lockfs(dmt);
@@ -543,7 +540,7 @@ int dm_addmap_create (struct multipath *
 	int ro;
 	uint16_t udev_flags = build_udev_flags(mpp, 0);
 
-	for (ro = 0; ro <= 1; ro++) {
+	for (ro = mpp->force_readonly ? 1 : 0; ro <= 1; ro++) {
 		int err;
 
 		if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, ro,
@@ -611,12 +608,21 @@ int dm_addmap_reload(struct multipath *m
 	return 0;
 }
 
-static int
-do_get_info(const char *name, struct dm_info *info)
+bool
+has_dm_info(const struct multipath *mpp)
+{
+	return (mpp && mpp->dmi.exists != 0);
+}
+
+int
+dm_get_info(const char *name, struct dm_info *info)
 {
 	int r = -1;
 	struct dm_task *dmt;
 
+	if (!name || !info)
+		return r;
+
 	if (!(dmt = libmp_dm_task_create(DM_DEVICE_INFO)))
 		return r;
 
@@ -646,7 +652,7 @@ int dm_map_present(const char * str)
 {
 	struct dm_info info;
 
-	return (do_get_info(str, &info) == 0);
+	return (dm_get_info(str, &info) == 0);
 }
 
 int dm_get_map(const char *name, unsigned long long *size, char **outparams)
@@ -676,8 +682,8 @@ int dm_get_map(const char *name, unsigne
 	r = DMP_NOT_FOUND;
 	/* Fetch 1st target */
 	if (dm_get_next_target(dmt, NULL, &start, &length,
-			       &target_type, &params) != NULL)
-		/* more than one target */
+			       &target_type, &params) != NULL || !params)
+		/* more than one target or not found target */
 		goto out;
 
 	if (size)
@@ -969,7 +975,7 @@ dm_dev_t (const char * mapname, char * d
 {
 	struct dm_info info;
 
-	if (do_get_info(mapname, &info) != 0)
+	if (dm_get_info(mapname, &info) != 0)
 		return 1;
 
 	if (snprintf(dev_t, len, "%i:%i", info.major, info.minor) > len)
@@ -1013,7 +1019,7 @@ dm_get_major_minor(const char *name, int
 {
 	struct dm_info info;
 
-	if (do_get_info(name, &info) != 0)
+	if (dm_get_info(name, &info) != 0)
 		return -1;
 
 	*major = info.major;
@@ -1367,7 +1373,7 @@ dm_geteventnr (const char *name)
 {
 	struct dm_info info;
 
-	if (do_get_info(name, &info) != 0)
+	if (dm_get_info(name, &info) != 0)
 		return -1;
 
 	return info.event_nr;
@@ -1378,7 +1384,7 @@ dm_is_suspended(const char *name)
 {
 	struct dm_info info;
 
-	if (do_get_info(name, &info) != 0)
+	if (dm_get_info(name, &info) != 0)
 		return -1;
 
 	return info.suspended;
@@ -1542,7 +1548,7 @@ dm_get_deferred_remove (const char * map
 {
 	struct dm_info info;
 
-	if (do_get_info(mapname, &info) != 0)
+	if (dm_get_info(mapname, &info) != 0)
 		return -1;
 
 	return info.deferred_remove;
@@ -1583,32 +1589,6 @@ dm_cancel_deferred_remove (struct multip
 
 #endif
 
-static struct dm_info *
-alloc_dminfo (void)
-{
-	return calloc(1, sizeof(struct dm_info));
-}
-
-int
-dm_get_info (const char * mapname, struct dm_info ** dmi)
-{
-	if (!mapname)
-		return 1;
-
-	if (!*dmi)
-		*dmi = alloc_dminfo();
-
-	if (!*dmi)
-		return 1;
-
-	if (do_get_info(mapname, *dmi) != 0) {
-		free(*dmi);
-		*dmi = NULL;
-		return 1;
-	}
-	return 0;
-}
-
 struct rename_data {
 	const char *old;
 	char *new;
@@ -1735,6 +1715,16 @@ int dm_reassign_table(const char *name,
 	do {
 		next = dm_get_next_target(dmt, next, &start, &length,
 					  &target, &params);
+		if (!target || !params) {
+			/*
+			 * We can't call dm_task_add_target() with
+			 * invalid parameters. But simply dropping this
+			 * target feels wrong, too. Abort and warn.
+			 */
+			condlog(1, "%s: invalid target found in map %s",
+				__func__, name);
+			goto out_reload;
+		}
 		buff = strdup(params);
 		if (!buff) {
 			condlog(3, "%s: failed to replace target %s, "
diff -pruN 0.8.8-1/libmultipath/devmapper.h 0.9.0-3/libmultipath/devmapper.h
--- 0.8.8-1/libmultipath/devmapper.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/devmapper.h	2022-06-10 16:47:26.000000000 +0000
@@ -70,7 +70,8 @@ char * dm_mapname(int major, int minor);
 int dm_remove_partmaps (const char * mapname, int need_sync,
 			int deferred_remove);
 int dm_get_uuid(const char *name, char *uuid, int uuid_len);
-int dm_get_info (const char * mapname, struct dm_info ** dmi);
+bool has_dm_info(const struct multipath *mpp);
+int dm_get_info (const char * mapname, struct dm_info *dmi);
 int dm_rename (const char * old, char * new, char * delim, int skip_kpartx);
 int dm_reassign(const char * mapname);
 int dm_reassign_table(const char *name, char *old, char *new);
diff -pruN 0.8.8-1/libmultipath/dict.c 0.9.0-3/libmultipath/dict.c
--- 0.8.8-1/libmultipath/dict.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/dict.c	2022-06-10 16:47:26.000000000 +0000
@@ -116,32 +116,6 @@ set_str(vector strvec, void *ptr, const
 }
 
 static int
-set_dir(vector strvec, void *ptr, const char *file, int line_nr)
-{
-	char **str_ptr = (char **)ptr;
-	char *old_str = *str_ptr;
-	struct stat sb;
-
-	*str_ptr = set_value(strvec);
-	if (!*str_ptr) {
-		free(old_str);
-		return 1;
-	}
-	if ((*str_ptr)[0] != '/'){
-		condlog(1, "%s line %d, %s is not an absolute directory path. Ignoring", file, line_nr, *str_ptr);
-		*str_ptr = old_str;
-	} else {
-		if (stat(*str_ptr, &sb) == 0 && S_ISDIR(sb.st_mode))
-			free(old_str);
-		else {
-			condlog(1, "%s line %d, %s is not an existing directory. Ignoring", file, line_nr, *str_ptr);
-			*str_ptr = old_str;
-		}
-	}
-	return 0;
-}
-
-static int
 set_path(vector strvec, void *ptr, const char *file, int line_nr)
 {
 	char **str_ptr = (char **)ptr;
@@ -279,10 +253,30 @@ static int								\
 def_ ## option ## _handler (struct config *conf, vector strvec,		\
 			    const char *file, int line_nr)		\
 {									\
-	condlog(2, "%s line %d, \"" #option "\" is deprecated and will be disabled in a future release", file, line_nr);				\
+	static bool warned;						\
+	if (!warned) {							\
+		condlog(2, "%s line %d, \"" #option "\" is deprecated and will be disabled in a future release", file, line_nr); \
+		warned = true;						\
+	}								\
 	return function (strvec, &conf->option, file, line_nr);		\
 }
 
+static int deprecated_handler(struct config *conf, vector strvec, const char *file,
+			      int line_nr);
+
+#define declare_deprecated_handler(option)				\
+static int								\
+deprecated_ ## option ## _handler (struct config *conf, vector strvec,	\
+				   const char *file, int line_nr)	\
+{									\
+	static bool warned;						\
+	if (!warned) {							\
+		condlog(1, "%s line %d: ignoring deprecated option \"" #option "\"", file, line_nr); \
+		warned = true;						\
+	}								\
+	return deprecated_handler(conf, strvec, file, line_nr);		\
+}
+
 #define declare_def_range_handler(option, minval, maxval)			\
 static int								\
 def_ ## option ## _handler (struct config *conf, vector strvec,         \
@@ -413,6 +407,29 @@ snprint_mp_ ## option (struct config *co
 	return function(buff, mpe->option);				\
 }
 
+#define declare_pc_handler(option, function)				\
+static int								\
+pc_ ## option ## _handler (struct config *conf, vector strvec,		\
+			   const char *file, int line_nr)		\
+{									\
+	struct pcentry *pce;						\
+	if (!conf->overrides || !conf->overrides->pctable)		\
+		return 1;						\
+	pce = VECTOR_LAST_SLOT(conf->overrides->pctable);		\
+	if (!pce)							\
+		return 1;						\
+	return function (strvec, &pce->option, file, line_nr);		\
+}
+
+#define declare_pc_snprint(option, function)				\
+static int								\
+snprint_pc_ ## option (struct config *conf, struct strbuf *buff,	\
+		       const void *data)				\
+{									\
+	const struct pcentry *pce  = (const struct pcentry *)data;	\
+	return function(buff, pce->option);				\
+}
+
 static int checkint_handler(struct config *conf, vector strvec,
 			    const char *file, int line_nr)
 {
@@ -436,8 +453,7 @@ declare_def_snprint(verbosity, print_int
 declare_def_handler(reassign_maps, set_yes_no)
 declare_def_snprint(reassign_maps, print_yes_no)
 
-declare_def_warn_handler(multipath_dir, set_dir)
-declare_def_snprint(multipath_dir, print_str)
+declare_deprecated_handler(multipath_dir)
 
 static int def_partition_delim_handler(struct config *conf, vector strvec,
 				       const char *file, int line_nr)
@@ -512,6 +528,59 @@ snprint_def_find_multipaths(struct confi
 			 find_multipaths_optvals[conf->find_multipaths]);
 }
 
+static const char * const marginal_pathgroups_optvals[] = {
+	[MARGINAL_PATHGROUP_OFF] = "off",
+	[MARGINAL_PATHGROUP_ON] = "on",
+#ifdef FPIN_EVENT_HANDLER
+	[MARGINAL_PATHGROUP_FPIN] = "fpin",
+#endif
+};
+
+static int
+def_marginal_pathgroups_handler(struct config *conf, vector strvec,
+			    const char *file, int line_nr)
+{
+	char *buff;
+	unsigned int i;
+
+	buff = set_value(strvec);
+	if (!buff)
+		return 1;
+	for (i = MARGINAL_PATHGROUP_OFF;
+	     i < ARRAY_SIZE(marginal_pathgroups_optvals); i++) {
+		if (marginal_pathgroups_optvals[i] != NULL &&
+		    !strcmp(buff, marginal_pathgroups_optvals[i])) {
+			conf->marginal_pathgroups = i;
+			break;
+		}
+	}
+
+	if (i >= ARRAY_SIZE(marginal_pathgroups_optvals)) {
+		if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0)
+			conf->marginal_pathgroups = MARGINAL_PATHGROUP_OFF;
+		else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0)
+			conf->marginal_pathgroups = MARGINAL_PATHGROUP_ON;
+		/* This can only be true if FPIN_EVENT_HANDLER isn't defined,
+		 * otherwise this check will have already happened above */
+		else if (strcmp(buff, "fpin") == 0)
+			condlog(1, "%s line %d, support for \"fpin\" is not compiled in for marginal_pathgroups", file, line_nr);
+		else
+			condlog(1, "%s line %d, invalid value for marginal_pathgroups: \"%s\"",
+				file, line_nr, buff);
+	}
+	free(buff);
+	return 0;
+}
+
+static int
+snprint_def_marginal_pathgroups(struct config *conf, struct strbuf *buff,
+			    const void *data)
+{
+	return append_strbuf_quoted(buff,
+			 marginal_pathgroups_optvals[conf->marginal_pathgroups]);
+}
+
+
 declare_def_handler(selector, set_str)
 declare_def_snprint_defstr(selector, print_str, DEFAULT_SELECTOR)
 declare_hw_handler(selector, set_str)
@@ -540,8 +609,13 @@ static int uid_attrs_handler(struct conf
 			     const char *file, int line_nr)
 {
 	char *val;
+	void *ptr;
+	int i;
 
+	vector_foreach_slot(&conf->uid_attrs, ptr, i)
+		free(ptr);
 	vector_reset(&conf->uid_attrs);
+
 	val = set_value(strvec);
 	if (!val)
 		return 1;
@@ -561,13 +635,6 @@ declare_ovr_snprint(uid_attribute, print
 declare_hw_handler(uid_attribute, set_str)
 declare_hw_snprint(uid_attribute, print_str)
 
-declare_def_handler(getuid, set_str)
-declare_def_snprint(getuid, print_str)
-declare_ovr_handler(getuid, set_str)
-declare_ovr_snprint(getuid, print_str)
-declare_hw_handler(getuid, set_str)
-declare_hw_snprint(getuid, print_str)
-
 declare_def_handler(prio_name, set_str)
 declare_def_snprint_defstr(prio_name, print_str, DEFAULT_PRIO)
 declare_ovr_handler(prio_name, set_str)
@@ -772,21 +839,8 @@ declare_def_handler(enable_foreign, set_
 declare_def_snprint_defstr(enable_foreign, print_str,
 			   DEFAULT_ENABLE_FOREIGN)
 
-static int
-def_config_dir_handler(struct config *conf, vector strvec, const char *file,
-		       int line_nr)
-{
-	/* this is only valid in the main config file */
-	if (conf->processed_main_config) {
-		condlog(1, "%s line %d, config_dir option only valid in /etc/multipath.conf",
-			file, line_nr);
-		return 0;
-	}
-	condlog(2, "%s line %d, \"config_dir\" is deprecated and will be disabled in a future release",
-		file, line_nr);
-	return set_path(strvec, &conf->config_dir, file, line_nr);
-}
-declare_def_snprint(config_dir, print_str)
+declare_deprecated_handler(config_dir)
+declare_deprecated_handler(pg_timeout)
 
 #define declare_def_attr_handler(option, function)			\
 static int								\
@@ -984,6 +1038,8 @@ declare_ovr_handler(fast_io_fail, set_un
 declare_ovr_snprint(fast_io_fail, print_undef_off_zero)
 declare_hw_handler(fast_io_fail, set_undef_off_zero)
 declare_hw_snprint(fast_io_fail, print_undef_off_zero)
+declare_pc_handler(fast_io_fail, set_undef_off_zero)
+declare_pc_snprint(fast_io_fail, print_undef_off_zero)
 
 static int
 set_dev_loss(vector strvec, void *ptr, const char *file, int line_nr)
@@ -1021,6 +1077,8 @@ declare_ovr_handler(dev_loss, set_dev_lo
 declare_ovr_snprint(dev_loss, print_dev_loss)
 declare_hw_handler(dev_loss, set_dev_loss)
 declare_hw_snprint(dev_loss, print_dev_loss)
+declare_pc_handler(dev_loss, set_dev_loss)
+declare_pc_snprint(dev_loss, print_dev_loss)
 
 declare_def_handler(eh_deadline, set_undef_off_zero)
 declare_def_snprint(eh_deadline, print_undef_off_zero)
@@ -1028,6 +1086,8 @@ declare_ovr_handler(eh_deadline, set_und
 declare_ovr_snprint(eh_deadline, print_undef_off_zero)
 declare_hw_handler(eh_deadline, set_undef_off_zero)
 declare_hw_snprint(eh_deadline, print_undef_off_zero)
+declare_pc_handler(eh_deadline, set_undef_off_zero)
+declare_pc_snprint(eh_deadline, print_undef_off_zero)
 
 static int
 set_pgpolicy(vector strvec, void *ptr, const char *file, int line_nr)
@@ -1526,9 +1586,6 @@ declare_ovr_snprint(all_tg_pt, print_yes
 declare_hw_handler(all_tg_pt, set_yes_no_undef)
 declare_hw_snprint(all_tg_pt, print_yes_no_undef)
 
-declare_def_handler(marginal_pathgroups, set_yes_no)
-declare_def_snprint(marginal_pathgroups, print_yes_no)
-
 declare_def_handler(recheck_wwid, set_yes_no_undef)
 declare_def_snprint_defint(recheck_wwid, print_yes_no_undef, DEFAULT_RECHECK_WWID)
 declare_ovr_handler(recheck_wwid, set_yes_no_undef)
@@ -1838,6 +1895,69 @@ declare_mp_snprint(wwid, print_str)
 declare_mp_handler(alias, set_str_noslash)
 declare_mp_snprint(alias, print_str)
 
+
+static int
+protocol_handler(struct config *conf, vector strvec, const char *file,
+               int line_nr)
+{
+	struct pcentry *pce;
+
+	if (!conf->overrides)
+		return 1;
+
+	if (!conf->overrides->pctable &&
+	    !(conf->overrides->pctable = vector_alloc()))
+		return 1;
+
+	if (!(pce = alloc_pce()))
+		return 1;
+
+	if (!vector_alloc_slot(conf->overrides->pctable)) {
+		free(pce);
+		return 1;
+	}
+	vector_set_slot(conf->overrides->pctable, pce);
+
+	return 0;
+}
+
+static int
+set_protocol_type(vector strvec, void *ptr, const char *file, int line_nr)
+{
+	int *int_ptr = (int *)ptr;
+	char *buff;
+	int i;
+
+	buff = set_value(strvec);
+
+	if (!buff)
+		return 1;
+
+	for (i = 0; i <= LAST_BUS_PROTOCOL_ID; i++) {
+		if (protocol_name[i] && !strcmp(buff, protocol_name[i])) {
+			*int_ptr = i;
+			break;
+		}
+	}
+	if (i > LAST_BUS_PROTOCOL_ID)
+		condlog(1, "%s line %d, invalid value for type: \"%s\"",
+			file, line_nr, buff);
+
+	free(buff);
+	return 0;
+}
+
+static int
+print_protocol_type(struct strbuf *buff, int type)
+{
+	if (type < 0)
+		return 0;
+	return append_strbuf_quoted(buff, protocol_name[type]);
+}
+
+declare_pc_handler(type, set_protocol_type)
+declare_pc_snprint(type, print_protocol_type)
+
 /*
  * deprecated handlers
  */
@@ -1863,7 +1983,7 @@ snprint_deprecated (struct config *conf,
 	return 0;
 }
 
-#define __deprecated
+declare_deprecated_handler(getuid_callout)
 
 /*
  * If you add or remove a keyword also update multipath/multipath.conf.5
@@ -1876,12 +1996,12 @@ init_keywords(vector keywords)
 	install_keyword("polling_interval", &checkint_handler, &snprint_def_checkint);
 	install_keyword("max_polling_interval", &def_max_checkint_handler, &snprint_def_max_checkint);
 	install_keyword("reassign_maps", &def_reassign_maps_handler, &snprint_def_reassign_maps);
-	install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir);
+	install_keyword("multipath_dir", &deprecated_multipath_dir_handler, &snprint_deprecated);
 	install_keyword("path_selector", &def_selector_handler, &snprint_def_selector);
 	install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy);
 	install_keyword("uid_attrs", &uid_attrs_handler, &snprint_uid_attrs);
 	install_keyword("uid_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute);
-	install_keyword("getuid_callout", &def_getuid_handler, &snprint_def_getuid);
+	install_keyword("getuid_callout", &deprecated_getuid_callout_handler, &snprint_deprecated);
 	install_keyword("prio", &def_prio_name_handler, &snprint_def_prio_name);
 	install_keyword("prio_args", &def_prio_args_handler, &snprint_def_prio_args);
 	install_keyword("features", &def_features_handler, &snprint_def_features);
@@ -1897,7 +2017,7 @@ init_keywords(vector keywords)
 	install_keyword("queue_without_daemon", &def_queue_without_daemon_handler, &snprint_def_queue_without_daemon);
 	install_keyword("checker_timeout", &def_checker_timeout_handler, &snprint_def_checker_timeout);
 	install_keyword("allow_usb_devices", &def_allow_usb_devices_handler, &snprint_def_allow_usb_devices);
-	install_keyword("pg_timeout", &deprecated_handler, &snprint_deprecated);
+	install_keyword("pg_timeout", &deprecated_pg_timeout_handler, &snprint_deprecated);
 	install_keyword("flush_on_last_del", &def_flush_on_last_del_handler, &snprint_def_flush_on_last_del);
 	install_keyword("user_friendly_names", &def_user_friendly_names_handler, &snprint_def_user_friendly_names);
 	install_keyword("mode", &def_mode_handler, &snprint_def_mode);
@@ -1919,7 +2039,7 @@ init_keywords(vector keywords)
 	install_keyword("strict_timing", &def_strict_timing_handler, &snprint_def_strict_timing);
 	install_keyword("deferred_remove", &def_deferred_remove_handler, &snprint_def_deferred_remove);
 	install_keyword("partition_delimiter", &def_partition_delim_handler, &snprint_def_partition_delim);
-	install_keyword("config_dir", &def_config_dir_handler, &snprint_def_config_dir);
+	install_keyword("config_dir", &deprecated_config_dir_handler, &snprint_deprecated);
 	install_keyword("delay_watch_checks", &def_delay_watch_checks_handler, &snprint_def_delay_watch_checks);
 	install_keyword("delay_wait_checks", &def_delay_wait_checks_handler, &snprint_def_delay_wait_checks);
 	install_keyword("san_path_err_threshold", &def_san_path_err_threshold_handler, &snprint_def_san_path_err_threshold);
@@ -1947,12 +2067,6 @@ init_keywords(vector keywords)
 			&snprint_def_enable_foreign);
 	install_keyword("marginal_pathgroups", &def_marginal_pathgroups_handler, &snprint_def_marginal_pathgroups);
 	install_keyword("recheck_wwid", &def_recheck_wwid_handler, &snprint_def_recheck_wwid);
-	__deprecated install_keyword("default_selector", &def_selector_handler, NULL);
-	__deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL);
-	__deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL);
-	__deprecated install_keyword("default_getuid_callout", &def_getuid_handler, NULL);
-	__deprecated install_keyword("default_features", &def_features_handler, NULL);
-	__deprecated install_keyword("default_path_checker", &def_checker_name_handler, NULL);
 
 	install_keyword_root("blacklist", &blacklist_handler);
 	install_keyword_multi("devnode", &ble_blist_devnode_handler, &snprint_ble_simple);
@@ -1975,16 +2089,6 @@ init_keywords(vector keywords)
 	install_keyword("product", &ble_elist_device_product_handler, &snprint_bled_product);
 	install_sublevel_end();
 
-#if 0
-	__deprecated install_keyword_root("devnode_blacklist", &blacklist_handler);
-	__deprecated install_keyword("devnode", &ble_devnode_handler, &snprint_ble_simple);
-	__deprecated install_keyword("wwid", &ble_wwid_handler, &snprint_ble_simple);
-	__deprecated install_keyword("device", &ble_device_handler, NULL);
-	__deprecated install_sublevel();
-	__deprecated install_keyword("vendor", &ble_vendor_handler, &snprint_bled_vendor);
-	__deprecated install_keyword("product", &ble_product_handler, &snprint_bled_product);
-	__deprecated install_sublevel_end();
-#endif
 /*
  * If you add or remove a "device subsection" keyword also update
  * multipath/multipath.conf.5 and the TEMPLATE in libmultipath/hwtable.c
@@ -1998,7 +2102,7 @@ init_keywords(vector keywords)
 	install_keyword("product_blacklist", &hw_bl_product_handler, &snprint_hw_bl_product);
 	install_keyword("path_grouping_policy", &hw_pgpolicy_handler, &snprint_hw_pgpolicy);
 	install_keyword("uid_attribute", &hw_uid_attribute_handler, &snprint_hw_uid_attribute);
-	install_keyword("getuid_callout", &hw_getuid_handler, &snprint_hw_getuid);
+	install_keyword("getuid_callout", &deprecated_getuid_callout_handler, &snprint_deprecated);
 	install_keyword("path_selector", &hw_selector_handler, &snprint_hw_selector);
 	install_keyword("path_checker", &hw_checker_name_handler, &snprint_hw_checker_name);
 	install_keyword("checker", &hw_checker_name_handler, NULL);
@@ -2042,7 +2146,7 @@ init_keywords(vector keywords)
 	install_keyword_root("overrides", &overrides_handler);
 	install_keyword("path_grouping_policy", &ovr_pgpolicy_handler, &snprint_ovr_pgpolicy);
 	install_keyword("uid_attribute", &ovr_uid_attribute_handler, &snprint_ovr_uid_attribute);
-	install_keyword("getuid_callout", &ovr_getuid_handler, &snprint_ovr_getuid);
+	install_keyword("getuid_callout", &deprecated_getuid_callout_handler, &snprint_deprecated);
 	install_keyword("path_selector", &ovr_selector_handler, &snprint_ovr_selector);
 	install_keyword("path_checker", &ovr_checker_name_handler, &snprint_ovr_checker_name);
 	install_keyword("checker", &ovr_checker_name_handler, NULL);
@@ -2079,6 +2183,13 @@ init_keywords(vector keywords)
 	install_keyword("ghost_delay", &ovr_ghost_delay_handler, &snprint_ovr_ghost_delay);
 	install_keyword("all_tg_pt", &ovr_all_tg_pt_handler, &snprint_ovr_all_tg_pt);
 	install_keyword("recheck_wwid", &ovr_recheck_wwid_handler, &snprint_ovr_recheck_wwid);
+	install_keyword_multi("protocol", &protocol_handler, NULL);
+	install_sublevel();
+	install_keyword("type", &pc_type_handler, &snprint_pc_type);
+	install_keyword("fast_io_fail_tmo", &pc_fast_io_fail_handler, &snprint_pc_fast_io_fail);
+	install_keyword("dev_loss_tmo", &pc_dev_loss_handler, &snprint_pc_dev_loss);
+	install_keyword("eh_deadline", &pc_eh_deadline_handler, &snprint_pc_eh_deadline);
+	install_sublevel_end();
 
 	install_keyword_root("multipaths", &multipaths_handler);
 	install_keyword_multi("multipath", &multipath_handler, NULL);
diff -pruN 0.8.8-1/libmultipath/discovery.c 0.9.0-3/libmultipath/discovery.c
--- 0.8.8-1/libmultipath/discovery.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/discovery.c	2022-06-10 16:47:26.000000000 +0000
@@ -21,7 +21,6 @@
 #include "structs.h"
 #include "config.h"
 #include "blacklist.h"
-#include "callout.h"
 #include "debug.h"
 #include "propsel.h"
 #include "sg_include.h"
@@ -475,60 +474,33 @@ sysfs_get_tgt_nodename(struct path *pp,
 	return 0;
 }
 
-int sysfs_get_host_adapter_name(const struct path *pp, char *adapter_name)
-{
-	int proto_id;
-
-	if (!pp || !adapter_name)
-		return 1;
-
-	proto_id = pp->sg_id.proto_id;
-
-	if (proto_id != SCSI_PROTOCOL_FCP &&
-	    proto_id != SCSI_PROTOCOL_SAS &&
-	    proto_id != SCSI_PROTOCOL_ISCSI &&
-	    proto_id != SCSI_PROTOCOL_SRP) {
-		return 1;
-	}
-	/* iscsi doesn't have adapter info in sysfs
-	 * get ip_address for grouping paths
-	 */
-	if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI)
-		return sysfs_get_iscsi_ip_address(pp, adapter_name);
-
-	/* fetch adapter pci name for other protocols
-	 */
-	return sysfs_get_host_pci_name(pp, adapter_name);
-}
-
-int sysfs_get_host_pci_name(const struct path *pp, char *pci_name)
+static int sysfs_get_host_bus_id(const struct path *pp, char *bus_id)
 {
 	struct udev_device *hostdev, *parent;
 	char host_name[HOST_NAME_LEN];
-	const char *driver_name, *value;
+	const char *driver_name, *subsystem_name, *value;
 
-	if (!pp || !pci_name)
+	if (!pp || !bus_id)
 		return 1;
 
-	sprintf(host_name, "host%d", pp->sg_id.host_no);
+	snprintf(host_name, sizeof(host_name), "host%d", pp->sg_id.host_no);
 	hostdev = udev_device_new_from_subsystem_sysname(udev,
 			"scsi_host", host_name);
 	if (!hostdev)
 		return 1;
 
-	parent = udev_device_get_parent(hostdev);
-	while (parent) {
+	for (parent = udev_device_get_parent(hostdev);
+	     parent;
+	     parent = udev_device_get_parent(parent)) {
 		driver_name = udev_device_get_driver(parent);
-		if (!driver_name) {
-			parent = udev_device_get_parent(parent);
-			continue;
-		}
-		if (!strcmp(driver_name, "pcieport"))
+		subsystem_name = udev_device_get_subsystem(parent);
+		if (driver_name && !strcmp(driver_name, "pcieport"))
+			break;
+		if (subsystem_name && !strcmp(subsystem_name, "ccw"))
 			break;
-		parent = udev_device_get_parent(parent);
 	}
 	if (parent) {
-		/* pci_device found
+		/* pci_device or ccw fcp device found
 		 */
 		value = udev_device_get_sysname(parent);
 
@@ -537,7 +509,7 @@ int sysfs_get_host_pci_name(const struct
 			return 1;
 		}
 
-		strncpy(pci_name, value, SLOT_NAME_SIZE);
+		strlcpy(bus_id, value, SLOT_NAME_SIZE);
 		udev_device_unref(hostdev);
 		return 0;
 	}
@@ -545,6 +517,32 @@ int sysfs_get_host_pci_name(const struct
 	return 1;
 }
 
+int sysfs_get_host_adapter_name(const struct path *pp, char *adapter_name)
+{
+	int proto_id;
+
+	if (!pp || !adapter_name)
+		return 1;
+
+	proto_id = pp->sg_id.proto_id;
+
+	if (proto_id != SCSI_PROTOCOL_FCP &&
+	    proto_id != SCSI_PROTOCOL_SAS &&
+	    proto_id != SCSI_PROTOCOL_ISCSI &&
+	    proto_id != SCSI_PROTOCOL_SRP) {
+		return 1;
+	}
+	/* iscsi doesn't have adapter info in sysfs
+	 * get ip_address for grouping paths
+	 */
+	if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI)
+		return sysfs_get_iscsi_ip_address(pp, adapter_name);
+
+	/* fetch adapter bus-ID for other protocols
+	 */
+	return sysfs_get_host_bus_id(pp, adapter_name);
+}
+
 int sysfs_get_iscsi_ip_address(const struct path *pp, char *ip_address)
 {
 	struct udev_device *hostdev;
@@ -599,13 +597,13 @@ sysfs_get_asymmetric_access_state(struct
 }
 
 static int
-sysfs_set_eh_deadline(struct multipath *mpp, struct path *pp)
+sysfs_set_eh_deadline(struct path *pp)
 {
 	struct udev_device *hostdev;
 	char host_name[HOST_NAME_LEN], value[16];
 	int ret, len;
 
-	if (mpp->eh_deadline == EH_DEADLINE_UNSET)
+	if (pp->eh_deadline == EH_DEADLINE_UNSET)
 		return 0;
 
 	sprintf(host_name, "host%d", pp->sg_id.host_no);
@@ -614,12 +612,12 @@ sysfs_set_eh_deadline(struct multipath *
 	if (!hostdev)
 		return 1;
 
-	if (mpp->eh_deadline == EH_DEADLINE_OFF)
+	if (pp->eh_deadline == EH_DEADLINE_OFF)
 		len = sprintf(value, "off");
-	else if (mpp->eh_deadline == EH_DEADLINE_ZERO)
+	else if (pp->eh_deadline == EH_DEADLINE_ZERO)
 		len = sprintf(value, "0");
 	else
-		len = sprintf(value, "%d", mpp->eh_deadline);
+		len = sprintf(value, "%d", pp->eh_deadline);
 
 	ret = sysfs_attr_set_value(hostdev, "eh_deadline",
 				   value, len + 1);
@@ -643,8 +641,8 @@ sysfs_set_rport_tmo(struct multipath *mp
 	unsigned int tmo;
 	int ret;
 
-	if (mpp->dev_loss == DEV_LOSS_TMO_UNSET &&
-	    mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
+	if (pp->dev_loss == DEV_LOSS_TMO_UNSET &&
+	    pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
 		return;
 
 	sprintf(rport_id, "rport-%d:%d-%d",
@@ -686,14 +684,14 @@ sysfs_set_rport_tmo(struct multipath *mp
 	 * then set fast_io_fail, and _then_ set dev_loss_tmo
 	 * to the correct value.
 	 */
-	if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET &&
-	    mpp->fast_io_fail != MP_FAST_IO_FAIL_ZERO &&
-	    mpp->fast_io_fail != MP_FAST_IO_FAIL_OFF) {
+	if (pp->fast_io_fail != MP_FAST_IO_FAIL_UNSET &&
+	    pp->fast_io_fail != MP_FAST_IO_FAIL_ZERO &&
+	    pp->fast_io_fail != MP_FAST_IO_FAIL_OFF) {
 		/* Check if we need to temporarily increase dev_loss_tmo */
-		if ((unsigned int)mpp->fast_io_fail >= tmo) {
+		if ((unsigned int)pp->fast_io_fail >= tmo) {
 			/* Increase dev_loss_tmo temporarily */
 			snprintf(value, sizeof(value), "%u",
-				 (unsigned int)mpp->fast_io_fail + 1);
+				 (unsigned int)pp->fast_io_fail + 1);
 			ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
 						   value, strlen(value));
 			if (ret <= 0) {
@@ -707,20 +705,20 @@ sysfs_set_rport_tmo(struct multipath *mp
 				goto out;
 			}
 		}
-	} else if (mpp->dev_loss > DEFAULT_DEV_LOSS_TMO &&
-		mpp->no_path_retry != NO_PATH_RETRY_QUEUE) {
+	} else if (pp->dev_loss > DEFAULT_DEV_LOSS_TMO &&
+		   mpp->no_path_retry != NO_PATH_RETRY_QUEUE) {
 		condlog(2, "%s: limiting dev_loss_tmo to %d, since "
 			"fast_io_fail is not set",
 			rport_id, DEFAULT_DEV_LOSS_TMO);
-		mpp->dev_loss = DEFAULT_DEV_LOSS_TMO;
+		pp->dev_loss = DEFAULT_DEV_LOSS_TMO;
 	}
-	if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
-		if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
+	if (pp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
+		if (pp->fast_io_fail == MP_FAST_IO_FAIL_OFF)
 			sprintf(value, "off");
-		else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO)
+		else if (pp->fast_io_fail == MP_FAST_IO_FAIL_ZERO)
 			sprintf(value, "0");
 		else
-			snprintf(value, 16, "%u", mpp->fast_io_fail);
+			snprintf(value, 16, "%u", pp->fast_io_fail);
 		ret = sysfs_attr_set_value(rport_dev, "fast_io_fail_tmo",
 					   value, strlen(value));
 		if (ret <= 0) {
@@ -731,8 +729,8 @@ sysfs_set_rport_tmo(struct multipath *mp
 					rport_id, value, -ret);
 		}
 	}
-	if (mpp->dev_loss != DEV_LOSS_TMO_UNSET) {
-		snprintf(value, 16, "%u", mpp->dev_loss);
+	if (pp->dev_loss != DEV_LOSS_TMO_UNSET) {
+		snprintf(value, 16, "%u", pp->dev_loss);
 		ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo",
 					   value, strlen(value));
 		if (ret <= 0) {
@@ -748,15 +746,15 @@ out:
 }
 
 static void
-sysfs_set_session_tmo(struct multipath *mpp, struct path *pp)
+sysfs_set_session_tmo(struct path *pp)
 {
 	struct udev_device *session_dev = NULL;
 	char session_id[64];
 	char value[11];
 
-	if (mpp->dev_loss != DEV_LOSS_TMO_UNSET)
+	if (pp->dev_loss != DEV_LOSS_TMO_UNSET)
 		condlog(3, "%s: ignoring dev_loss_tmo on iSCSI", pp->dev);
-	if (mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
+	if (pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
 		return;
 
 	sprintf(session_id, "session%d", pp->sg_id.transport_id);
@@ -770,15 +768,15 @@ sysfs_set_session_tmo(struct multipath *
 	condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
 		pp->sg_id.channel, pp->sg_id.scsi_id, session_id);
 
-	if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
-		if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF) {
+	if (pp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) {
+		if (pp->fast_io_fail == MP_FAST_IO_FAIL_OFF) {
 			condlog(3, "%s: can't switch off fast_io_fail_tmo "
 				"on iSCSI", pp->dev);
-		} else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) {
+		} else if (pp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) {
 			condlog(3, "%s: can't set fast_io_fail_tmo to '0'"
 				"on iSCSI", pp->dev);
 		} else {
-			snprintf(value, 11, "%u", mpp->fast_io_fail);
+			snprintf(value, 11, "%u", pp->fast_io_fail);
 			if (sysfs_attr_set_value(session_dev, "recovery_tmo",
 						 value, strlen(value)) <= 0) {
 				condlog(3, "%s: Failed to set recovery_tmo, "
@@ -791,14 +789,14 @@ sysfs_set_session_tmo(struct multipath *
 }
 
 static void
-sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp)
+sysfs_set_nexus_loss_tmo(struct path *pp)
 {
 	struct udev_device *parent, *sas_dev = NULL;
 	const char *end_dev_id = NULL;
 	char value[11];
 	static const char ed_str[] = "end_device-";
 
-	if (!pp->udev || mpp->dev_loss == DEV_LOSS_TMO_UNSET)
+	if (!pp->udev || pp->dev_loss == DEV_LOSS_TMO_UNSET)
 		return;
 
 	for (parent = udev_device_get_parent(pp->udev);
@@ -825,8 +823,8 @@ sysfs_set_nexus_loss_tmo(struct multipat
 	condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no,
 		pp->sg_id.channel, pp->sg_id.scsi_id, end_dev_id);
 
-	if (mpp->dev_loss != DEV_LOSS_TMO_UNSET) {
-		snprintf(value, 11, "%u", mpp->dev_loss);
+	if (pp->dev_loss != DEV_LOSS_TMO_UNSET) {
+		snprintf(value, 11, "%u", pp->dev_loss);
 		if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout",
 					 value, strlen(value)) <= 0)
 			condlog(3, "%s: failed to update "
@@ -837,74 +835,98 @@ sysfs_set_nexus_loss_tmo(struct multipat
 	return;
 }
 
+static void
+scsi_tmo_error_msg(struct path *pp)
+{
+	STATIC_BITFIELD(bf, LAST_BUS_PROTOCOL_ID + 1);
+	STRBUF_ON_STACK(proto_buf);
+	unsigned int proto_id = bus_protocol_id(pp);
+
+	snprint_path_protocol(&proto_buf, pp);
+	condlog(2, "%s: setting scsi timeouts is unsupported for protocol %s",
+		pp->dev, get_strbuf_str(&proto_buf));
+	set_bit_in_bitfield(proto_id, bf);
+}
+
 int
-sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint)
+sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp)
 {
 	struct path *pp;
 	int i;
-	unsigned int dev_loss_tmo = mpp->dev_loss;
-	struct path *err_path = NULL;
+	unsigned int min_dev_loss = 0;
+	bool warn_dev_loss = false;
+	bool warn_fast_io_fail = false;
 
 	if (mpp->no_path_retry > 0) {
 		uint64_t no_path_retry_tmo =
-			(uint64_t)mpp->no_path_retry * checkint;
+			(uint64_t)mpp->no_path_retry * conf->checkint;
 
 		if (no_path_retry_tmo > MAX_DEV_LOSS_TMO)
-			no_path_retry_tmo = MAX_DEV_LOSS_TMO;
-		if (no_path_retry_tmo > dev_loss_tmo)
-			dev_loss_tmo = no_path_retry_tmo;
-	} else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE) {
-		dev_loss_tmo = MAX_DEV_LOSS_TMO;
-	}
-	if (mpp->dev_loss != DEV_LOSS_TMO_UNSET &&
-	    mpp->dev_loss != dev_loss_tmo) {
-		condlog(2, "%s: Using dev_loss_tmo=%u instead of %u because of no_path_retry setting",
-			mpp->alias, dev_loss_tmo, mpp->dev_loss);
-		mpp->dev_loss = dev_loss_tmo;
-	}
-	if (mpp->dev_loss != DEV_LOSS_TMO_UNSET &&
-	    mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET &&
-	    (unsigned int)mpp->fast_io_fail >= mpp->dev_loss) {
-		condlog(3, "%s: turning off fast_io_fail (%d is not smaller than dev_loss_tmo)",
-			mpp->alias, mpp->fast_io_fail);
-		mpp->fast_io_fail = MP_FAST_IO_FAIL_OFF;
-	}
-	if (mpp->dev_loss == DEV_LOSS_TMO_UNSET &&
-	    mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET &&
-	    mpp->eh_deadline == EH_DEADLINE_UNSET)
-		return 0;
+			min_dev_loss = MAX_DEV_LOSS_TMO;
+		else
+			min_dev_loss = no_path_retry_tmo;
+	} else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE)
+		min_dev_loss = MAX_DEV_LOSS_TMO;
 
 	vector_foreach_slot(mpp->paths, pp, i) {
+		select_fast_io_fail(conf, pp);
+		select_dev_loss(conf, pp);
+		select_eh_deadline(conf, pp);
+
+		if (pp->dev_loss == DEV_LOSS_TMO_UNSET &&
+		    pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET &&
+		    pp->eh_deadline == EH_DEADLINE_UNSET)
+			continue;
+
 		if (pp->bus != SYSFS_BUS_SCSI) {
-			if (!err_path)
-				err_path = pp;
+			scsi_tmo_error_msg(pp);
 			continue;
 		}
+		sysfs_set_eh_deadline(pp);
+
+		if (pp->dev_loss == DEV_LOSS_TMO_UNSET &&
+		    pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET)
+			continue;
+
+		if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP &&
+		    pp->sg_id.proto_id != SCSI_PROTOCOL_ISCSI &&
+		    pp->sg_id.proto_id != SCSI_PROTOCOL_SAS) {
+			scsi_tmo_error_msg(pp);
+			continue;
+		}
+
+		if (pp->dev_loss != DEV_LOSS_TMO_UNSET &&
+		    pp->dev_loss < min_dev_loss) {
+			warn_dev_loss = true;
+			pp->dev_loss = min_dev_loss;
+		}
+		if (pp->dev_loss != DEV_LOSS_TMO_UNSET &&
+		    pp->fast_io_fail > 0 &&
+		    (unsigned int)pp->fast_io_fail >= pp->dev_loss) {
+			warn_fast_io_fail = true;
+			pp->fast_io_fail = MP_FAST_IO_FAIL_OFF;
+		}
 
 		switch (pp->sg_id.proto_id) {
 		case SCSI_PROTOCOL_FCP:
 			sysfs_set_rport_tmo(mpp, pp);
 			break;
 		case SCSI_PROTOCOL_ISCSI:
-			sysfs_set_session_tmo(mpp, pp);
+			sysfs_set_session_tmo(pp);
 			break;
 		case SCSI_PROTOCOL_SAS:
-			sysfs_set_nexus_loss_tmo(mpp, pp);
+			sysfs_set_nexus_loss_tmo(pp);
 			break;
 		default:
-			if (!err_path)
-				err_path = pp;
+			break;
 		}
-		sysfs_set_eh_deadline(mpp, pp);
-	}
-
-	if (err_path) {
-		STRBUF_ON_STACK(proto_buf);
-
-		snprint_path_protocol(&proto_buf, err_path);
-		condlog(2, "%s: setting dev_loss_tmo is unsupported for protocol %s",
-			mpp->alias, get_strbuf_str(&proto_buf));
 	}
+	if (warn_dev_loss)
+		condlog(2, "%s: Raising dev_loss_tmo to %u because of no_path_retry setting",
+			mpp->alias, min_dev_loss);
+	if (warn_fast_io_fail)
+		condlog(3, "%s: turning off fast_io_fail (not smaller than dev_loss_tmo)",
+			mpp->alias);
 	return 0;
 }
 
@@ -2043,7 +2065,7 @@ fix_broken_nvme_wwid(struct path *pp, co
 }
 
 static int
-get_udev_uid(struct path * pp, char *uid_attribute, struct udev_device *udev)
+get_udev_uid(struct path * pp, const char *uid_attribute, struct udev_device *udev)
 {
 	ssize_t len;
 	const char *value;
@@ -2187,7 +2209,7 @@ get_uid (struct path * pp, int path_stat
 	int used_fallback = 0;
 	size_t i;
 
-	if (!pp->uid_attribute && !pp->getuid) {
+	if (!pp->uid_attribute) {
 		conf = get_multipath_config();
 		pthread_cleanup_push(put_multipath_config, conf);
 		select_getuid(conf, pp);
@@ -2196,24 +2218,7 @@ get_uid (struct path * pp, int path_stat
 	}
 
 	memset(pp->wwid, 0, WWID_SIZE);
-	if (pp->getuid) {
-		char buff[CALLOUT_MAX_SIZE];
-
-		/* Use 'getuid' callout, deprecated */
-		condlog(1, "%s: using deprecated getuid callout", pp->dev);
-		if (path_state != PATH_UP) {
-			condlog(3, "%s: path inaccessible", pp->dev);
-			len = -EWOULDBLOCK;
-		} else if (apply_format(pp->getuid, &buff[0], pp)) {
-			condlog(0, "error formatting uid callout command");
-			len = -EINVAL;
-		} else if (execute_program(buff, pp->wwid, WWID_SIZE)) {
-			condlog(3, "error calling out %s", buff);
-			len = -EIO;
-		} else
-			len = strlen(pp->wwid);
-		origin = "callout";
-	} else if (pp->uid_attribute) {
+	if (pp->uid_attribute) {
 		/* if the uid_attribute is an empty string skip udev checking */
 		bool check_uid_attr = udev && *pp->uid_attribute;
 
@@ -2345,7 +2350,10 @@ int pathinfo(struct path *pp, struct con
 			 * Avoid any IO on the device itself.
 			 * simply use the path_offline() return as its state
 			 */
-			pp->chkrstate = pp->state = path_state;
+			if (path_state != PATH_PENDING ||
+			    pp->state == PATH_UNCHECKED ||
+			    pp->state == PATH_WILD)
+				pp->chkrstate = pp->state = path_state;
 		return PATHINFO_OK;
 	}
 
diff -pruN 0.8.8-1/libmultipath/discovery.h 0.9.0-3/libmultipath/discovery.h
--- 0.8.8-1/libmultipath/discovery.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/discovery.h	2022-06-10 16:47:26.000000000 +0000
@@ -42,9 +42,8 @@ int alloc_path_with_pathinfo (struct con
 int store_pathinfo (vector pathvec, struct config *conf,
 		    struct udev_device *udevice, int flag,
 		    struct path **pp_ptr);
-int sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint);
+int sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp);
 int sysfs_get_timeout(const struct path *pp, unsigned int *timeout);
-int sysfs_get_host_pci_name(const struct path *pp, char *pci_name);
 int sysfs_get_iscsi_ip_address(const struct path *pp, char *ip_address);
 int sysfs_get_host_adapter_name(const struct path *pp,
 				char *adapter_name);
diff -pruN 0.8.8-1/libmultipath/dmparser.c 0.9.0-3/libmultipath/dmparser.c
--- 0.8.8-1/libmultipath/dmparser.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/dmparser.c	2022-06-10 16:47:26.000000000 +0000
@@ -102,7 +102,7 @@ err:
 }
 
 /*
- * Caution callers: If this function encounters yet unkown path devices, it
+ * Caution callers: If this function encounters yet unknown path devices, it
  * adds them uninitialized to the mpp.
  * Call update_pathvec_from_dm() after this function to make sure
  * all data structures are in a sane state.
@@ -436,9 +436,19 @@ int disassemble_status(const char *param
 		free(word);
 
 		/*
-		 * PG Status (discarded, would be '0' anyway)
+		 * Path Selector Group Arguments
 		 */
-		p += get_word(p, NULL);
+		p += get_word(p, &word);
+
+		if (!word)
+			return 1;
+
+		num_pg_args = atoi(word);
+		free(word);
+
+		/* Ignore ps group arguments */
+		for (j = 0; j < num_pg_args; j++)
+			p += get_word(p, NULL);
 
 		p += get_word(p, &word);
 
diff -pruN 0.8.8-1/libmultipath/foreign/Makefile 0.9.0-3/libmultipath/foreign/Makefile
--- 0.8.8-1/libmultipath/foreign/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/foreign/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -4,7 +4,8 @@
 TOPDIR=../..
 include ../../Makefile.inc
 
-CFLAGS += $(LIB_CFLAGS) -I.. -I$(nvmedir)
+CPPFLAGS += -I.. -I$(nvmedir)
+CFLAGS += $(LIB_CFLAGS)
 LDFLAGS += -L..
 LIBDEPS = -lmultipath -ludev -lpthread -lrt
 
diff -pruN 0.8.8-1/libmultipath/foreign/nvme.c 0.9.0-3/libmultipath/foreign/nvme.c
--- 0.8.8-1/libmultipath/foreign/nvme.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/foreign/nvme.c	2022-06-10 16:47:26.000000000 +0000
@@ -23,7 +23,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdbool.h>
-#include <libudev.h>
 #include <pthread.h>
 #include <limits.h>
 #include <dirent.h>
@@ -337,7 +336,7 @@ static int snprint_nvme_pg(const struct
 static int nvme_style(__attribute__((unused)) const struct gen_multipath* gm,
 		      struct strbuf *buf, __attribute__((unused)) int verbosity)
 {
-	return append_strbuf_str(buf, "%%w [%%G]:%%d %%s");
+	return append_strbuf_str(buf, "%w [%G]:%d %s");
 }
 
 static const struct gen_multipath_ops nvme_map_ops = {
diff -pruN 0.8.8-1/libmultipath/foreign.c 0.9.0-3/libmultipath/foreign.c
--- 0.8.8-1/libmultipath/foreign.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/foreign.c	2022-06-10 16:47:26.000000000 +0000
@@ -37,6 +37,7 @@
 #include "strbuf.h"
 
 static vector foreigns;
+static const char *const foreign_dir = MULTIPATH_DIR;
 
 /* This protects vector foreigns */
 static pthread_rwlock_t foreign_lock = PTHREAD_RWLOCK_INITIALIZER;
@@ -125,7 +126,7 @@ static void free_pre(void *arg)
 	}
 }
 
-static int _init_foreign(const char *multipath_dir, const char *enable)
+static int _init_foreign(const char *enable)
 {
 	char pathbuf[PATH_MAX];
 	struct dirent **di;
@@ -153,7 +154,7 @@ static int _init_foreign(const char *mul
 		}
 	}
 
-	r = scandir(multipath_dir, &di, select_foreign_libs, alphasort);
+	r = scandir(foreign_dir, &di, select_foreign_libs, alphasort);
 
 	if (r == 0) {
 		condlog(3, "%s: no foreign multipath libraries found",
@@ -208,7 +209,7 @@ static int _init_foreign(const char *mul
 					__func__, ret, fgn->name);
 		}
 
-		snprintf(pathbuf, sizeof(pathbuf), "%s/%s", multipath_dir, fn);
+		snprintf(pathbuf, sizeof(pathbuf), "%s/%s", foreign_dir, fn);
 		fgn->handle = dlopen(pathbuf, RTLD_NOW|RTLD_LOCAL);
 		msg = dlerror();
 		if (fgn->handle == NULL) {
@@ -257,7 +258,7 @@ out_free_pre:
 	return r;
 }
 
-int init_foreign(const char *multipath_dir, const char *enable)
+int init_foreign(const char *enable)
 {
 	int ret;
 
@@ -270,7 +271,7 @@ int init_foreign(const char *multipath_d
 	}
 
 	pthread_cleanup_push(unlock_foreigns, NULL);
-	ret = _init_foreign(multipath_dir, enable);
+	ret = _init_foreign(enable);
 	pthread_cleanup_pop(1);
 
 	return ret;
diff -pruN 0.8.8-1/libmultipath/foreign.h 0.9.0-3/libmultipath/foreign.h
--- 0.8.8-1/libmultipath/foreign.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/foreign.h	2022-06-10 16:47:26.000000000 +0000
@@ -42,7 +42,7 @@ struct foreign {
 	/**
 	 * method: init(api, name)
 	 * Initialize foreign library, and check API compatibility
-	 * return pointer to opaque internal data strucure if successful,
+	 * return pointer to opaque internal data structure if successful,
 	 * NULL otherwise.
 	 *
 	 * @param[in] api: API version
@@ -198,7 +198,7 @@ struct foreign {
  * @param enable: regex to match foreign library name ("*" above) against
  * @returns: 0 on success, negative value on failure.
  */
-int init_foreign(const char *multipath_dir, const char *enable);
+int init_foreign(const char *enable);
 
 /**
  * cleanup_foreign(dir)
diff -pruN 0.8.8-1/libmultipath/hwtable.c 0.9.0-3/libmultipath/hwtable.c
--- 0.8.8-1/libmultipath/hwtable.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/hwtable.c	2022-06-10 16:47:26.000000000 +0000
@@ -90,7 +90,8 @@ static struct hwentry default_hw[] = {
 		.product       = ".*",
 		.uid_attribute = DEFAULT_NVME_UID_ATTRIBUTE,
 		.checker_name  = NONE,
-		.retain_hwhandler = RETAIN_HWHANDLER_OFF,
+		.pgpolicy      = GROUP_BY_PRIO,
+		.pgfailback    = -FAILBACK_IMMEDIATE,
 	},
 	/*
 	 * Apple
@@ -120,6 +121,12 @@ static struct hwentry default_hw[] = {
 		.vpd_vendor_id = VPD_VP_HP3PAR,
 	},
 	{
+		/* Alletra 9000 NVMe */
+		.vendor        = "NVME",
+		.product       = "HPE Alletra",
+		.no_path_retry = NO_PATH_RETRY_QUEUE,
+	},
+	{
 		/* RA8000 / ESA12000 */
 		.vendor        = "DEC",
 		.product       = "HSG80",
@@ -182,8 +189,8 @@ static struct hwentry default_hw[] = {
 	},
 	{
 		/* MSA 1040, 1050, 1060, 2040, 2050 and 2060 families */
-		.vendor        = "HP",
-		.product       = "MSA [12]0[456]0 SA[NS]",
+		.vendor        = "(HP|HPE)",
+		.product       = "MSA [12]0[456]0 (SAN|SAS|FC|iSCSI)",
 		.pgpolicy      = GROUP_BY_PRIO,
 		.pgfailback    = -FAILBACK_IMMEDIATE,
 		.no_path_retry = 18,
@@ -192,7 +199,7 @@ static struct hwentry default_hw[] = {
 	{
 		/* SAN Virtualization Services Platform */
 		.vendor        = "HP",
-		.product       = "HSVX700",
+		.product       = "(HSVX700|HSVX740)",
 		.hwhandler     = "1 alua",
 		.pgpolicy      = GROUP_BY_PRIO,
 		.pgfailback    = -FAILBACK_IMMEDIATE,
@@ -380,12 +387,6 @@ static struct hwentry default_hw[] = {
 		.no_path_retry = 30,
 	},
 	{
-		/* EMC PowerMax NVMe */
-		.vendor        = "NVME",
-		.product       = "^EMC PowerMax_",
-		.pgpolicy      = MULTIBUS,
-	},
-	{
 		/* PowerStore */
 		.vendor        = "DellEMC",
 		.product       = "PowerStore",
@@ -397,9 +398,15 @@ static struct hwentry default_hw[] = {
 		.fast_io_fail  = 15,
 	},
 	{
-		/* PowerVault ME4 */
+		/* PowerStore NVMe */
+		.vendor        = ".*",
+		.product       = "dellemc-powerstore",
+		.no_path_retry = 3,
+	},
+	{
+		/* PowerVault ME 4/5 families */
 		.vendor        = "DellEMC",
-		.product       = "ME4",
+		.product       = "^ME",
 		.pgpolicy      = GROUP_BY_PRIO,
 		.prio_name     = PRIO_ALUA,
 		.hwhandler     = "1 alua",
@@ -467,8 +474,8 @@ static struct hwentry default_hw[] = {
 	 * Maintainer: Matthias Rudolph <Matthias.Rudolph@hitachivantara.com>
 	 */
 	{
-		/* USP-V, HUS VM, VSP, VSP G1X00 and VSP GX00 families / HP XP */
-		.vendor        = "(HITACHI|HP)",
+		/* USP-V, HUS VM, VSP, VSP G1X00 and VSP GX00 families / HPE XP */
+		.vendor        = "(HITACHI|HP|HPE)",
 		.product       = "^OPEN-",
 		.pgpolicy      = MULTIBUS,
 	},
@@ -665,7 +672,7 @@ static struct hwentry default_hw[] = {
 	},
 	{
 		// Storwize V5000 and V7000 lines / SAN Volume Controller (SVC) / Flex System V7000 /
-		// FlashSystem V840/V9000/5000/5100/5200/7200/9100/9200/9200R
+		// FlashSystem V840/V9000/5000/5100/5200/7200/7300/9100/9200/9200R/9500
 		.vendor        = "IBM",
 		.product       = "^2145",
 		.no_path_retry = NO_PATH_RETRY_QUEUE,
@@ -674,6 +681,12 @@ static struct hwentry default_hw[] = {
 		.prio_name     = PRIO_ALUA,
 	},
 	{
+		/* FlashSystem(Storwize/SVC) NVMe */
+		.vendor        = "NVME",
+		.product       = "IBM[ ]+2145",
+		.no_path_retry = NO_PATH_RETRY_QUEUE,
+	},
+	{
 		/* PAV DASD ECKD */
 		.vendor        = "IBM",
 		.product       = "S/390 DASD ECKD",
@@ -727,6 +740,12 @@ static struct hwentry default_hw[] = {
 		.pgpolicy      = MULTIBUS,
 	},
 	{
+		/* FlashSystem(RamSan) NVMe */
+		.vendor        = "NVMe",
+		.product       = "FlashSystem",
+		.no_path_retry = NO_PATH_RETRY_FAIL,
+	},
+	{
 		/* (DDN) DCS9900, SONAS 2851-DR1 */
 		.vendor        = "IBM",
 		.product       = "^(DCS9900|2851)",
@@ -832,14 +851,9 @@ static struct hwentry default_hw[] = {
 		.no_path_retry = 24,
 	},
 	{
-		/*
-		 * NVMe-FC namespace devices: MULTIBUS, queueing preferred
-		 *
-		 * The hwtable is searched backwards, so place this after "Generic NVMe"
-		 */
+		/* ONTAP NVMe */
 		.vendor        = "NVME",
 		.product       = "^NetApp ONTAP Controller",
-		.pgpolicy      = MULTIBUS,
 		.no_path_retry = NO_PATH_RETRY_QUEUE,
 	},
 	/*
@@ -1039,7 +1053,7 @@ static struct hwentry default_hw[] = {
 	{
 		/* Linux-IO Target */
 		.vendor        = "(LIO-ORG|SUSE)",
-		.product       = "RBD",
+		.product       = ".",
 		.hwhandler     = "1 alua",
 		.pgpolicy      = GROUP_BY_PRIO,
 		.pgfailback    = -FAILBACK_IMMEDIATE,
@@ -1081,6 +1095,12 @@ static struct hwentry default_hw[] = {
 		.fast_io_fail  = 10,
 		.max_sectors_kb = 4096,
 	},
+	{
+		/* FlashArray NVMe */
+		.vendor        = "NVME",
+		.product       = "Pure Storage FlashArray",
+		.no_path_retry = 10,
+	},
 	/*
 	 * Huawei
 	 */
@@ -1094,6 +1114,13 @@ static struct hwentry default_hw[] = {
 		.pgfailback    = -FAILBACK_IMMEDIATE,
 		.no_path_retry = 15,
 	},
+	{
+		/* OceanStor NVMe */
+		.vendor        = "NVME",
+		.product       = "Huawei-XSG1",
+		.checker_name  = DIRECTIO,
+		.no_path_retry = 12,
+	},
 	/*
 	 * Kove
 	 */
diff -pruN 0.8.8-1/libmultipath/libmultipath.version 0.9.0-3/libmultipath/libmultipath.version
--- 0.8.8-1/libmultipath/libmultipath.version	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/libmultipath.version	2022-06-10 16:47:26.000000000 +0000
@@ -31,7 +31,7 @@
  *   The new version inherits the previous ones.
  */
 
-LIBMULTIPATH_13.0.0 {
+LIBMULTIPATH_15.0.0 {
 global:
 	/* symbols referenced by multipath and multipathd */
 	add_foreign;
@@ -56,6 +56,7 @@ global:
 	check_foreign;
 	cleanup_charp;
 	cleanup_lock;
+	cleanup_mutex;
 	cleanup_ucharp;
 	close_fd;
 	coalesce_paths;
@@ -121,6 +122,7 @@ global:
 	get_used_hwes;
 	get_vpd_sgio;
 	group_by_prio;
+	has_dm_info;
 	init_checkers;
 	init_config;
 	init_foreign;
@@ -162,6 +164,7 @@ global:
 	remember_wwid;
 	remove_map;
 	remove_map_by_alias;
+	remove_map_callback;
 	remove_maps;
 	remove_wwid;
 	replace_wwids;
@@ -175,7 +178,7 @@ global:
 	select_reservation_key;
 	send_packet;
 	set_max_fds;
-	__set_no_path_retry;
+	set_no_path_retry;
 	set_path_removed;
 	set_prkey;
 	setup_map;
@@ -209,6 +212,7 @@ global:
 	sysfs_is_multipathed;
 	timespeccmp;
 	timespecsub;
+	trigger_path_udev_change;
 	trigger_paths_udev_change;
 	truncate_strbuf;
 	udev;
diff -pruN 0.8.8-1/libmultipath/list.h 0.9.0-3/libmultipath/list.h
--- 0.8.8-1/libmultipath/list.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/list.h	2022-06-10 16:47:26.000000000 +0000
@@ -246,6 +246,35 @@ static inline void list_splice_tail_init
 #define list_entry(ptr, type, member) \
 	container_of(ptr, type, member)
 
+
+/**
+ * list_pop - unlink and return the first list element
+ * @head:	the &struct list_head pointer.
+ */
+static inline struct list_head *list_pop(struct list_head *head)
+{
+	struct list_head *tmp;
+
+	if (list_empty(head))
+		return NULL;
+	tmp = head->next;
+	list_del_init(tmp);
+	return tmp;
+}
+
+/**
+ * list_pop_entry - unlink and return the entry of the first list element
+ * @head:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_pop_entry(head, type, member)		\
+({							\
+	struct list_head *__h = list_pop(head);		\
+							\
+	(__h ? container_of(__h, type, member) : NULL);	\
+})
+
 /**
  * list_for_each	-	iterate over a list
  * @pos:	the &struct list_head to use as a loop counter.
@@ -335,6 +364,30 @@ static inline void list_splice_tail_init
 	     pos = n, n = list_entry(n->member.prev, typeof(*n), member))
 
 /**
+ * list_for_some_entry - iterate list from the given begin node to the given end node
+ * @pos:	the type * to use as a loop counter.
+ * @from:	the begin node of the iteration.
+ * @to:		the end node of the iteration.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_some_entry(pos, from, to, member)                      \
+	for (pos = list_entry((from)->next, typeof(*pos), member);      \
+	     &pos->member != (to);                                      \
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_some_entry_reverse - iterate backwards list from the given begin node to the given end node
+ * @pos:	the type * to use as a loop counter.
+ * @from:	the begin node of the iteration.
+ * @to:		the end node of the iteration.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_some_entry_reverse(pos, from, to, member)		\
+	for (pos = list_entry((from)->prev, typeof(*pos), member);      \
+	     &pos->member != (to);                                      \
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
  * list_for_some_entry_safe - iterate list from the given begin node to the given end node safe against removal of list entry
  * @pos:	the type * to use as a loop counter.
  * @n:		another type * to use as temporary storage
diff -pruN 0.8.8-1/libmultipath/Makefile 0.9.0-3/libmultipath/Makefile
--- 0.8.8-1/libmultipath/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -8,12 +8,13 @@ DEVLIB = libmultipath.so
 LIBS = $(DEVLIB).$(SONAME)
 VERSION_SCRIPT := libmultipath.version
 
-CFLAGS += $(LIB_CFLAGS) -I$(mpathcmddir) -I$(mpathpersistdir) -I$(nvmedir)
+CPPFLAGS += -I$(mpathcmddir) -I$(mpathpersistdir) -I$(nvmedir)
+CFLAGS += $(LIB_CFLAGS)
 
 LIBDEPS += -lpthread -ldl -ldevmapper -ludev -L$(mpathcmddir) -lmpathcmd -lurcu -laio
 
 ifdef SYSTEMD
-	CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
+	CPPFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
 	ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1)
 		LIBDEPS += -lsystemd
 	else
@@ -21,49 +22,61 @@ ifdef SYSTEMD
 	endif
 endif
 
-ifneq ($(call check_func,dm_task_no_flush,/usr/include/libdevmapper.h),0)
-	CFLAGS += -DLIBDM_API_FLUSH -D_GNU_SOURCE
+ifneq ($(call check_func,dm_task_no_flush,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
+	CPPFLAGS += -DLIBDM_API_FLUSH -D_GNU_SOURCE
 endif
 
-ifneq ($(call check_func,dm_task_get_errno,/usr/include/libdevmapper.h),0)
-	CFLAGS += -DLIBDM_API_GET_ERRNO
+ifneq ($(call check_func,dm_task_get_errno,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
+	CPPFLAGS += -DLIBDM_API_GET_ERRNO
 endif
 
-ifneq ($(call check_func,dm_task_set_cookie,/usr/include/libdevmapper.h),0)
-	CFLAGS += -DLIBDM_API_COOKIE
+ifneq ($(call check_func,dm_task_set_cookie,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
+	CPPFLAGS += -DLIBDM_API_COOKIE
 endif
 
-ifneq ($(call check_func,udev_monitor_set_receive_buffer_size,/usr/include/libudev.h),0)
-	CFLAGS += -DLIBUDEV_API_RECVBUF
+ifneq ($(call check_func,udev_monitor_set_receive_buffer_size,$(LIBUDEV_INCDIR)/libudev.h),0)
+	CPPFLAGS += -DLIBUDEV_API_RECVBUF
 endif
 
-ifneq ($(call check_func,dm_task_deferred_remove,/usr/include/libdevmapper.h),0)
-	CFLAGS += -DLIBDM_API_DEFERRED
+ifneq ($(call check_func,dm_task_deferred_remove,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
+	CPPFLAGS += -DLIBDM_API_DEFERRED
 endif
 
-ifneq ($(call check_func,dm_hold_control_dev,/usr/include/libdevmapper.h),0)
-	CFLAGS += -DLIBDM_API_HOLD_CONTROL
+ifneq ($(call check_func,dm_hold_control_dev,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
+	CPPFLAGS += -DLIBDM_API_HOLD_CONTROL
 endif
 
-OBJS = parser.o vector.o devmapper.o callout.o \
-	hwtable.o blacklist.o util.o dmparser.o config.o \
+ifneq ($(call check_var,ELS_DTAG_LNK_INTEGRITY,$(LINUX_HEADERS_INCDIR)/scsi/fc/fc_els.h),0)
+	CPPFLAGS += -DFPIN_EVENT_HANDLER
+endif
+
+# object files referencing MULTIPATH_DIR or CONFIG_DIR
+# they need to be recompiled for unit tests
+OBJS-U := prio.o checkers.o foreign.o config.o
+OBJS-T := $(patsubst %.o,%-test.o,$(OBJS-U))
+
+# other object files
+OBJS-O := parser.o vector.o devmapper.o \
+	hwtable.o blacklist.o util.o dmparser.o \
 	structs.o discovery.o propsel.o dict.o \
 	pgpolicies.o debug.o defaults.o uevent.o time-util.o \
 	switchgroup.o uxsock.o print.o alias.o log_pthread.o \
-	log.o configure.o structs_vec.o sysfs.o prio.o checkers.o \
+	log.o configure.o structs_vec.o sysfs.o \
 	lock.o file.o wwids.o prioritizers/alua_rtpg.o prkey.o \
-	io_err_stat.o dm-generic.o generic.o foreign.o nvme-lib.o \
+	io_err_stat.o dm-generic.o generic.o nvme-lib.o \
 	libsg.o valid.o strbuf.o
 
+OBJS := $(OBJS-O) $(OBJS-U)
+
 all:	$(DEVLIB)
 
 nvme-lib.o: nvme-lib.c nvme-ioctl.c nvme-ioctl.h
-	$(CC) $(CFLAGS) -Wno-unused-function -c -o $@ $<
+	$(CC) $(CPPFLAGS) $(CFLAGS) -Wno-unused-function -c -o $@ $<
 
 # there are lots of "unused parameters" in dict.c
-# because not all handler / snprint methods nees all parameters
+# because not all handler / snprint methods need all parameters
 dict.o:	dict.c
-	$(CC) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
+	$(CC) $(CPPFLAGS) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
 
 make_static = $(shell sed '/^static/!s/^\([a-z]\{1,\} \)/static \1/' <$1 >$2)
 
@@ -92,11 +105,16 @@ $(LIBS:%.so.$(SONAME)=%-nv.so):	$(OBJS)
 
 abi:    $(LIBS:%.so.$(SONAME)=%-nv.abi)
 
-../tests/$(LIBS): $(OBJS) $(VERSION_SCRIPT)
+../tests/$(LIBS): $(OBJS-O) $(OBJS-T) $(VERSION_SCRIPT)
 	$(CC) $(LDFLAGS) $(SHARED_FLAGS) -Wl,-soname=`basename $@` \
-		-o $@ $(OBJS) $(LIBDEPS)
+		-o $@ $(OBJS-O) $(OBJS-T) $(LIBDEPS)
 	$(LN) $@ ${@:.so.0=.so}
 
+# This rule is invoked from tests/Makefile, overriding configdir and plugindir
+%-test.o: %.c
+	@echo building $@ because of $?
+	$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
+
 test-lib:	../tests/$(LIBS)
 
 install: all
@@ -110,7 +128,7 @@ uninstall:
 	$(RM) $(DESTDIR)$(syslibdir)/$(DEVLIB)
 
 clean: dep_clean
-	$(RM) core *.a *.o *.so *.so.* *.gz *.abi nvme-ioctl.c nvme-ioctl.h $(NV_VERSION_SCRIPT)
+	$(RM) core *.a *.o *.so *.so.* *.abi nvme-ioctl.c nvme-ioctl.h $(NV_VERSION_SCRIPT)
 
 include $(wildcard $(OBJS:.o=.d))
 
diff -pruN 0.8.8-1/libmultipath/print.c 0.9.0-3/libmultipath/print.c
--- 0.8.8-1/libmultipath/print.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/print.c	2022-06-10 16:47:26.000000000 +0000
@@ -8,7 +8,6 @@
 #include <sys/stat.h>
 #include <dirent.h>
 #include <unistd.h>
-#include <string.h>
 #include <errno.h>
 #include <assert.h>
 #include <libudev.h>
@@ -96,6 +95,7 @@
 			     "            \"host_wwpn\" : \"%R\",\n" \
 			     "            \"target_wwpn\" : \"%r\",\n" \
 			     "            \"host_adapter\" : \"%a\",\n" \
+			     "            \"lun_hex\" : \"%L\",\n" \
 			     "            \"marginal_st\" : \"%M\""
 
 #define PROGRESS_LEN  10
@@ -171,8 +171,8 @@ snprint_name (struct strbuf *buff, const
 static int
 snprint_sysfs (struct strbuf *buff, const struct multipath * mpp)
 {
-	if (mpp->dmi)
-		return print_strbuf(buff, "dm-%i", mpp->dmi->minor);
+	if (has_dm_info(mpp))
+		return print_strbuf(buff, "dm-%i", mpp->dmi.minor);
 	else
 		return append_strbuf_str(buff, "undef");
 }
@@ -180,9 +180,9 @@ snprint_sysfs (struct strbuf *buff, cons
 static int
 snprint_ro (struct strbuf *buff, const struct multipath * mpp)
 {
-	if (!mpp->dmi)
+	if (!has_dm_info(mpp))
 		return append_strbuf_str(buff, "undef");
-	if (mpp->dmi->read_only)
+	if (mpp->dmi.read_only)
 		return append_strbuf_str(buff, "ro");
 	else
 		return append_strbuf_str(buff, "rw");
@@ -256,7 +256,9 @@ snprint_nb_paths (struct strbuf *buff, c
 static int
 snprint_dm_map_state (struct strbuf *buff, const struct multipath * mpp)
 {
-	if (mpp->dmi && mpp->dmi->suspended)
+	if (!has_dm_info(mpp))
+		return append_strbuf_str(buff, "undef");
+	else if (mpp->dmi.suspended)
 		return append_strbuf_str(buff, "suspend");
 	else
 		return append_strbuf_str(buff, "active");
@@ -451,6 +453,24 @@ snprint_hcil (struct strbuf *buff, const
 			pp->sg_id.lun);
 }
 
+
+static int
+snprint_path_lunhex (struct strbuf *buff, const struct path * pp)
+{
+	uint64_t lunhex = SCSI_INVALID_LUN, scsilun;
+
+	if (!pp || pp->sg_id.host_no < 0)
+		return print_strbuf(buff, "0x%016" PRIx64, lunhex);
+
+	scsilun = pp->sg_id.lun;
+	/* cf. Linux kernel function int_to_scsilun() */
+	lunhex = ((scsilun & 0x000000000000ffffULL) << 48) |
+		((scsilun & 0x00000000ffff0000ULL) << 16) |
+		((scsilun & 0x0000ffff00000000ULL) >> 16) |
+		((scsilun & 0xffff000000000000ULL) >> 48);
+	return print_strbuf(buff, "0x%016" PRIx64, lunhex);
+}
+
 static int
 snprint_dev (struct strbuf *buff, const struct path * pp)
 {
@@ -734,43 +754,10 @@ snprint_path_failures(struct strbuf *buf
 int
 snprint_path_protocol(struct strbuf *buff, const struct path * pp)
 {
-	switch (pp->bus) {
-	case SYSFS_BUS_SCSI:
-		switch (pp->sg_id.proto_id) {
-		case SCSI_PROTOCOL_FCP:
-			return append_strbuf_str(buff, "scsi:fcp");
-		case SCSI_PROTOCOL_SPI:
-			return append_strbuf_str(buff, "scsi:spi");
-		case SCSI_PROTOCOL_SSA:
-			return append_strbuf_str(buff, "scsi:ssa");
-		case SCSI_PROTOCOL_SBP:
-			return append_strbuf_str(buff, "scsi:sbp");
-		case SCSI_PROTOCOL_SRP:
-			return append_strbuf_str(buff, "scsi:srp");
-		case SCSI_PROTOCOL_ISCSI:
-			return append_strbuf_str(buff, "scsi:iscsi");
-		case SCSI_PROTOCOL_SAS:
-			return append_strbuf_str(buff, "scsi:sas");
-		case SCSI_PROTOCOL_ADT:
-			return append_strbuf_str(buff, "scsi:adt");
-		case SCSI_PROTOCOL_ATA:
-			return append_strbuf_str(buff, "scsi:ata");
-		case SCSI_PROTOCOL_USB:
-			return append_strbuf_str(buff, "scsi:usb");
-		case SCSI_PROTOCOL_UNSPEC:
-		default:
-			return append_strbuf_str(buff, "scsi:unspec");
-		}
-	case SYSFS_BUS_CCW:
-		return append_strbuf_str(buff, "ccw");
-	case SYSFS_BUS_CCISS:
-		return append_strbuf_str(buff, "cciss");
-	case SYSFS_BUS_NVME:
-		return append_strbuf_str(buff, "nvme");
-	case SYSFS_BUS_UNDEF:
-	default:
-		return append_strbuf_str(buff, "undef");
-	}
+	const char *pn = protocol_name[bus_protocol_id(pp)];
+
+	assert(pn != NULL);
+	return append_strbuf_str(buff, pn);
 }
 
 static int
@@ -842,6 +829,7 @@ static const struct path_data pd[] = {
 	{'0', "failures",      snprint_path_failures},
 	{'P', "protocol",      snprint_path_protocol},
 	{'I', "init_st",       snprint_initialized},
+	{'L', "LUN hex",       snprint_path_lunhex},
 };
 
 static const struct pathgroup_data pgd[] = {
@@ -1419,6 +1407,52 @@ int snprint_multipath_topology_json (str
 }
 
 static int
+snprint_pcentry (const struct config *conf, struct strbuf *buff,
+		 const struct pcentry *pce)
+{
+	int i, rc;
+	struct keyword *kw;
+	struct keyword * rootkw;
+	size_t initial_len = get_strbuf_len(buff);
+
+	rootkw = find_keyword(conf->keywords, NULL, "overrides");
+	assert(rootkw && rootkw->sub);
+	rootkw = find_keyword(conf->keywords, rootkw->sub, "protocol");
+	assert(rootkw);
+
+	if ((rc = append_strbuf_str(buff, "\tprotocol {\n")) < 0)
+		return rc;
+
+	iterate_sub_keywords(rootkw, kw, i) {
+		if ((rc = snprint_keyword(buff, "\t\t%k %v\n", kw, pce)) < 0)
+			return rc;
+	}
+
+	if ((rc = append_strbuf_str(buff, "\t}\n")) < 0)
+		return rc;
+	return get_strbuf_len(buff) - initial_len;
+}
+
+static int
+snprint_pctable (const struct config *conf, struct strbuf *buff,
+		 const struct _vector *pctable)
+{
+	int i, rc;
+	struct pcentry *pce;
+	struct keyword * rootkw;
+	size_t initial_len = get_strbuf_len(buff);
+
+	rootkw = find_keyword(conf->keywords, NULL, "overrides");
+	assert(rootkw);
+
+	vector_foreach_slot(pctable, pce, i) {
+		if ((rc = snprint_pcentry(conf, buff, pce)) < 0)
+			return rc;
+	}
+	return get_strbuf_len(buff) - initial_len;
+}
+
+static int
 snprint_hwentry (const struct config *conf,
 		 struct strbuf *buff, const struct hwentry * hwe)
 {
@@ -1572,6 +1606,10 @@ static int snprint_overrides(const struc
 		if ((rc = snprint_keyword(buff, "\t%k %v\n", kw, NULL)) < 0)
 			return rc;
 	}
+
+	if (overrides->pctable &&
+	    (rc = snprint_pctable(conf, buff, overrides->pctable)) < 0)
+		return rc;
 out:
 	if ((rc = append_strbuf_str(buff, "}\n")) < 0)
 		return rc;
diff -pruN 0.8.8-1/libmultipath/prio.c 0.9.0-3/libmultipath/prio.c
--- 0.8.8-1/libmultipath/prio.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/prio.c	2022-06-10 16:47:26.000000000 +0000
@@ -8,6 +8,7 @@
 #include "util.h"
 #include "prio.h"
 
+static const char * const prio_dir = MULTIPATH_DIR;
 static LIST_HEAD(prioritizers);
 
 unsigned int get_prio_timeout(unsigned int timeout_ms,
@@ -18,7 +19,7 @@ unsigned int get_prio_timeout(unsigned i
 	return default_timeout;
 }
 
-int init_prio (const char *multipath_dir)
+int init_prio(void)
 {
 #ifdef LOAD_ALL_SHARED_LIBS
 	static const char *const all_prios[] = {
@@ -39,9 +40,9 @@ int init_prio (const char *multipath_dir
 	unsigned int i;
 
 	for  (i = 0; i < ARRAY_SIZE(all_prios); i++)
-		add_prio(multipath_dir, all_prios[i]);
+		add_prio(all_prios[i]);
 #else
-	if (!add_prio(multipath_dir, DEFAULT_PRIO))
+	if (!add_prio(DEFAULT_PRIO))
 		return 1;
 #endif
 	return 0;
@@ -90,7 +91,7 @@ void cleanup_prio(void)
 	}
 }
 
-static struct prio * prio_lookup (char * name)
+static struct prio *prio_lookup(const char *name)
 {
 	struct prio * p;
 
@@ -109,7 +110,7 @@ int prio_set_args (struct prio * p, cons
 	return snprintf(p->args, PRIO_ARGS_LEN, "%s", args);
 }
 
-struct prio * add_prio (const char *multipath_dir, const char * name)
+struct prio *add_prio (const char *name)
 {
 	char libname[LIB_PRIO_NAMELEN];
 	struct stat stbuf;
@@ -121,10 +122,10 @@ struct prio * add_prio (const char *mult
 		return NULL;
 	snprintf(p->name, PRIO_NAME_LEN, "%s", name);
 	snprintf(libname, LIB_PRIO_NAMELEN, "%s/libprio%s.so",
-		 multipath_dir, name);
+		 prio_dir, name);
 	if (stat(libname,&stbuf) < 0) {
 		condlog(0,"Prioritizer '%s' not found in %s",
-			name, multipath_dir);
+			name, prio_dir);
 		goto out;
 	}
 	condlog(3, "loading %s prioritizer", libname);
@@ -170,7 +171,7 @@ const char * prio_args (const struct pri
 	return p->args;
 }
 
-void prio_get (char *multipath_dir, struct prio * dst, char * name, char * args)
+void prio_get(struct prio *dst, const char *name, const char *args)
 {
 	struct prio * src = NULL;
 
@@ -180,7 +181,7 @@ void prio_get (char *multipath_dir, stru
 	if (name && strlen(name)) {
 		src = prio_lookup(name);
 		if (!src)
-			src = add_prio(multipath_dir, name);
+			src = add_prio(name);
 	}
 	if (!src) {
 		dst->getprio = NULL;
diff -pruN 0.8.8-1/libmultipath/prio.h 0.9.0-3/libmultipath/prio.h
--- 0.8.8-1/libmultipath/prio.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/prio.h	2022-06-10 16:47:26.000000000 +0000
@@ -54,11 +54,11 @@ struct prio {
 
 unsigned int get_prio_timeout(unsigned int checker_timeout,
 			      unsigned int default_timeout);
-int init_prio (const char *);
+int init_prio(void);
 void cleanup_prio (void);
-struct prio * add_prio (const char *, const char *);
+struct prio * add_prio (const char *);
 int prio_getprio (struct prio *, struct path *, unsigned int);
-void prio_get (char *, struct prio *, char *, char *);
+void prio_get (struct prio *, const char *, const char *);
 void prio_put (struct prio *);
 int prio_selected (const struct prio *);
 const char * prio_name (const struct prio *);
diff -pruN 0.8.8-1/libmultipath/prioritizers/alua_spc3.h 0.9.0-3/libmultipath/prioritizers/alua_spc3.h
--- 0.8.8-1/libmultipath/prioritizers/alua_spc3.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/prioritizers/alua_spc3.h	2022-06-10 16:47:26.000000000 +0000
@@ -286,7 +286,7 @@ struct rtpg_tp_dscr {
 struct rtpg_tpg_dscr {
 	unsigned char	b0;		/* x....... = pref(ered) port        */
 					/* .xxx.... = reserved               */
-					/* ....xxxx = asymetric access state */
+					/* ....xxxx = asymmetric access state*/
 	unsigned char	b1;		/* xxx..... = reserved               */
 					/* ...x.... = LBA dependent support  */
 					/* ....x... = unavailable support    */
diff -pruN 0.8.8-1/libmultipath/prioritizers/iet.c 0.9.0-3/libmultipath/prioritizers/iet.c
--- 0.8.8-1/libmultipath/prioritizers/iet.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/prioritizers/iet.c	2022-06-10 16:47:26.000000000 +0000
@@ -31,7 +31,7 @@
 // name: find_regex
 // @param string: string you want to search into
 // @param regex: the pattern used
-// @return result: string finded in string with regex, "none" if none
+// @return result: string found in string with regex, "none" if none
 char *find_regex(char * string, char * regex)
 {
 	int err;
diff -pruN 0.8.8-1/libmultipath/prioritizers/Makefile 0.9.0-3/libmultipath/prioritizers/Makefile
--- 0.8.8-1/libmultipath/prioritizers/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/prioritizers/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -3,7 +3,8 @@
 #
 include ../../Makefile.inc
 
-CFLAGS += $(LIB_CFLAGS) -I..
+CPPFLAGS += -I..
+CFLAGS += $(LIB_CFLAGS)
 LDFLAGS += -L..
 LIBDEPS = -lmultipath -lm -lpthread -lrt
 
@@ -23,9 +24,9 @@ LIBS = \
 	libpriopath_latency.so \
 	libpriosysfs.so
 
-ifneq ($(call check_file,/usr/include/linux/nvme_ioctl.h),0)
+ifneq ($(call check_file,$(LINUX_HEADERS_INCDIR)/linux/nvme_ioctl.h),0)
 	LIBS += libprioana.so
-	CFLAGS += -I../nvme
+	CPPFLAGS += -I../nvme
 endif
 
 all: $(LIBS)
diff -pruN 0.8.8-1/libmultipath/prioritizers/path_latency.c 0.9.0-3/libmultipath/prioritizers/path_latency.c
--- 0.8.8-1/libmultipath/prioritizers/path_latency.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/prioritizers/path_latency.c	2022-06-10 16:47:26.000000000 +0000
@@ -284,7 +284,7 @@ int getprio(struct path *pp, char *args,
 
 	if (lg_avglatency > lg_maxavglatency) {
 		pp_pl_log(2,
-			  "%s: average latency (%lld us) is outside the thresold (%lld us)",
+			  "%s: average latency (%lld us) is outside the threshold (%lld us)",
 			  pp->dev, (long long)pow(base_num, lg_avglatency),
 			  (long long)MAX_AVG_LATENCY);
 		return DEFAULT_PRIORITY;
diff -pruN 0.8.8-1/libmultipath/propsel.c 0.9.0-3/libmultipath/propsel.c
--- 0.8.8-1/libmultipath/propsel.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/propsel.c	2022-06-10 16:47:26.000000000 +0000
@@ -79,10 +79,14 @@ static const char conf_origin[] =
 	"(setting: multipath.conf defaults/devices section)";
 static const char overrides_origin[] =
 	"(setting: multipath.conf overrides section)";
+static const char overrides_pce_origin[] =
+	"(setting: multipath.conf overrides protocol section)";
 static const char cmdline_origin[] =
 	"(setting: multipath command line [-p] flag)";
 static const char autodetect_origin[] =
 	"(setting: storage device autodetected)";
+static const char fpin_marginal_path_origin[] =
+	"(setting: overridden by marginal_path_fpin)";
 static const char marginal_path_origin[] =
 	"(setting: implied by marginal_path check)";
 static const char delay_watch_origin[] =
@@ -144,6 +148,27 @@ do {									\
 	}								\
 } while (0)
 
+#define pp_set_ovr_pce(var)						\
+do {									\
+	struct pcentry *_pce;						\
+	int _i;								\
+									\
+	if (conf->overrides) {						\
+		vector_foreach_slot(conf->overrides->pctable, _pce, _i) {	\
+			if (_pce->type == (int)bus_protocol_id(pp) && _pce->var) {	\
+				pp->var = _pce->var;			\
+				origin = overrides_pce_origin;		\
+				goto out;				\
+			}						\
+		}							\
+		if (conf->overrides->var) {				\
+			pp->var = conf->overrides->var;			\
+			origin = overrides_origin;			\
+			goto out;					\
+		}							\
+	}								\
+} while (0)
+
 int select_mode(struct config *conf, struct multipath *mp)
 {
 	const char *origin;
@@ -535,7 +560,7 @@ int select_checker(struct config *conf,
 	do_set(checker_name, conf, ckr_name, conf_origin);
 	do_default(ckr_name, DEFAULT_CHECKER);
 out:
-	checker_get(conf->multipath_dir, c, ckr_name);
+	checker_get(c, ckr_name);
 	condlog(3, "%s: path_checker = %s %s", pp->dev,
 		checker_name(c), origin);
 	if (conf->checker_timeout) {
@@ -564,20 +589,14 @@ int select_getuid(struct config *conf, s
 		goto out;
 	}
 
-	pp_set_ovr(getuid);
 	pp_set_ovr(uid_attribute);
-	pp_set_hwe(getuid);
 	pp_set_hwe(uid_attribute);
-	pp_set_conf(getuid);
 	pp_set_conf(uid_attribute);
 	pp_set_default(uid_attribute, DEFAULT_UID_ATTRIBUTE);
 out:
 	if (pp->uid_attribute)
 		condlog(3, "%s: uid_attribute = %s %s", pp->dev,
 			pp->uid_attribute, origin);
-	else if (pp->getuid)
-		condlog(3, "%s: getuid = \"%s\" %s", pp->dev, pp->getuid,
-			origin);
 	return 0;
 }
 
@@ -592,7 +611,7 @@ int select_recheck_wwid(struct config *c
 	pp_set_default(recheck_wwid, DEFAULT_RECHECK_WWID);
 out:
 	if (pp->recheck_wwid == RECHECK_WWID_ON &&
-	    (pp->bus != SYSFS_BUS_SCSI || pp->getuid != NULL ||
+	    (pp->bus != SYSFS_BUS_SCSI ||
 	     !has_uid_fallback(pp))) {
 		pp->recheck_wwid = RECHECK_WWID_OFF;
 		origin = "(setting: unsupported by device type/config)";
@@ -602,8 +621,7 @@ out:
 	return 0;
 }
 
-void
-detect_prio(struct config *conf, struct path * pp)
+void detect_prio(struct path *pp)
 {
 	struct prio *p = &pp->prio;
 	char buff[512];
@@ -629,19 +647,19 @@ detect_prio(struct config *conf, struct
 	default:
 		return;
 	}
-	prio_get(conf->multipath_dir, p, default_prio, DEFAULT_PRIO_ARGS);
+	prio_get(p, default_prio, DEFAULT_PRIO_ARGS);
 }
 
-#define set_prio(dir, src, msg)					\
+#define set_prio(src, msg)						\
 do {									\
 	if (src && src->prio_name) {					\
-		prio_get(dir, p, src->prio_name, src->prio_args);	\
+		prio_get(p, src->prio_name, src->prio_args);		\
 		origin = msg;						\
 		goto out;						\
 	}								\
 } while(0)
 
-#define set_prio_from_vec(type, dir, src, msg, p)			\
+#define set_prio_from_vec(type, src, msg, p)				\
 do {									\
 	type *_p;							\
 	int i;								\
@@ -654,7 +672,7 @@ do {									\
 			prio_args = _p->prio_args;			\
 	}								\
 	if (prio_name != NULL) {					\
-		prio_get(dir, p, prio_name, prio_args);			\
+		prio_get(p, prio_name, prio_args);			\
 		origin = msg;						\
 		goto out;						\
 	}								\
@@ -668,19 +686,18 @@ int select_prio(struct config *conf, str
 	int log_prio = 3;
 
 	if (pp->detect_prio == DETECT_PRIO_ON) {
-		detect_prio(conf, pp);
+		detect_prio(pp);
 		if (prio_selected(p)) {
 			origin = autodetect_origin;
 			goto out;
 		}
 	}
 	mpe = find_mpe(conf->mptable, pp->wwid);
-	set_prio(conf->multipath_dir, mpe, multipaths_origin);
-	set_prio(conf->multipath_dir, conf->overrides, overrides_origin);
-	set_prio_from_vec(struct hwentry, conf->multipath_dir,
-			  pp->hwe, hwe_origin, p);
-	set_prio(conf->multipath_dir, conf, conf_origin);
-	prio_get(conf->multipath_dir, p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
+	set_prio(mpe, multipaths_origin);
+	set_prio(conf->overrides, overrides_origin);
+	set_prio_from_vec(struct hwentry, pp->hwe, hwe_origin, p);
+	set_prio(conf, conf_origin);
+	prio_get(p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
 	origin = default_origin;
 out:
 	/*
@@ -690,8 +707,7 @@ out:
 		int tpgs = path_get_tpgs(pp);
 
 		if (tpgs == TPGS_NONE) {
-			prio_get(conf->multipath_dir,
-				 p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
+			prio_get(p, DEFAULT_PRIO, DEFAULT_PRIO_ARGS);
 			origin = "(setting: emergency fallback - alua failed)";
 			log_prio = 1;
 		}
@@ -767,53 +783,53 @@ int select_minio(struct config *conf, st
 		return select_minio_bio(conf, mp);
 }
 
-int select_fast_io_fail(struct config *conf, struct multipath *mp)
+int select_fast_io_fail(struct config *conf, struct path *pp)
 {
 	const char *origin;
 	STRBUF_ON_STACK(buff);
 
-	mp_set_ovr(fast_io_fail);
-	mp_set_hwe(fast_io_fail);
-	mp_set_conf(fast_io_fail);
-	mp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL);
+	pp_set_ovr_pce(fast_io_fail);
+	pp_set_hwe(fast_io_fail);
+	pp_set_conf(fast_io_fail);
+	pp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL);
 out:
-	print_undef_off_zero(&buff, mp->fast_io_fail);
-	condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias,
+	print_undef_off_zero(&buff, pp->fast_io_fail);
+	condlog(3, "%s: fast_io_fail_tmo = %s %s", pp->dev,
 		get_strbuf_str(&buff), origin);
 	return 0;
 }
 
-int select_dev_loss(struct config *conf, struct multipath *mp)
+int select_dev_loss(struct config *conf, struct path *pp)
 {
 	const char *origin;
 	STRBUF_ON_STACK(buff);
 
-	mp_set_ovr(dev_loss);
-	mp_set_hwe(dev_loss);
-	mp_set_conf(dev_loss);
-	mp->dev_loss = DEV_LOSS_TMO_UNSET;
+	pp_set_ovr_pce(dev_loss);
+	pp_set_hwe(dev_loss);
+	pp_set_conf(dev_loss);
+	pp->dev_loss = DEV_LOSS_TMO_UNSET;
 	return 0;
 out:
-	print_dev_loss(&buff, mp->dev_loss);
-	condlog(3, "%s: dev_loss_tmo = %s %s", mp->alias,
+	print_dev_loss(&buff, pp->dev_loss);
+	condlog(3, "%s: dev_loss_tmo = %s %s", pp->dev,
 		get_strbuf_str(&buff), origin);
 	return 0;
 }
 
-int select_eh_deadline(struct config *conf, struct multipath *mp)
+int select_eh_deadline(struct config *conf, struct path *pp)
 {
 	const char *origin;
 	STRBUF_ON_STACK(buff);
 
-	mp_set_ovr(eh_deadline);
-	mp_set_hwe(eh_deadline);
-	mp_set_conf(eh_deadline);
-	mp->eh_deadline = EH_DEADLINE_UNSET;
+	pp_set_ovr_pce(eh_deadline);
+	pp_set_hwe(eh_deadline);
+	pp_set_conf(eh_deadline);
+	pp->eh_deadline = EH_DEADLINE_UNSET;
 	/* not changing sysfs in default cause, so don't print anything */
 	return 0;
 out:
-	print_undef_off_zero(&buff, mp->eh_deadline);
-	condlog(3, "%s: eh_deadline = %s %s", mp->alias,
+	print_undef_off_zero(&buff, pp->eh_deadline);
+	condlog(3, "%s: eh_deadline = %s %s", pp->dev,
 		get_strbuf_str(&buff), origin);
 	return 0;
 }
@@ -1035,9 +1051,12 @@ int select_san_path_err_threshold(struct
 	const char *origin;
 	STRBUF_ON_STACK(buff);
 
-	if (marginal_path_check_enabled(mp)) {
+	if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
 		mp->san_path_err_threshold = NU_NO;
-		origin = marginal_path_origin;
+		if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
+			origin = fpin_marginal_path_origin;
+		else
+			origin = marginal_path_origin;
 		goto out;
 	}
 	mp_set_mpe(san_path_err_threshold);
@@ -1058,9 +1077,12 @@ int select_san_path_err_forget_rate(stru
 	const char *origin;
 	STRBUF_ON_STACK(buff);
 
-	if (marginal_path_check_enabled(mp)) {
+	if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
 		mp->san_path_err_forget_rate = NU_NO;
-		origin = marginal_path_origin;
+		if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
+			origin = fpin_marginal_path_origin;
+		else
+			origin = marginal_path_origin;
 		goto out;
 	}
 	mp_set_mpe(san_path_err_forget_rate);
@@ -1082,9 +1104,12 @@ int select_san_path_err_recovery_time(st
 	const char *origin;
 	STRBUF_ON_STACK(buff);
 
-	if (marginal_path_check_enabled(mp)) {
+	if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
 		mp->san_path_err_recovery_time = NU_NO;
-		origin = marginal_path_origin;
+		if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
+			origin = fpin_marginal_path_origin;
+		else
+			origin = marginal_path_origin;
 		goto out;
 	}
 	mp_set_mpe(san_path_err_recovery_time);
@@ -1106,6 +1131,12 @@ int select_marginal_path_err_sample_time
 	const char *origin;
 	STRBUF_ON_STACK(buff);
 
+	if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
+		mp->marginal_path_err_sample_time = NU_NO;
+		origin = fpin_marginal_path_origin;
+		goto out;
+	}
+
 	mp_set_mpe(marginal_path_err_sample_time);
 	mp_set_ovr(marginal_path_err_sample_time);
 	mp_set_hwe(marginal_path_err_sample_time);
@@ -1129,6 +1160,12 @@ int select_marginal_path_err_rate_thresh
 	const char *origin;
 	STRBUF_ON_STACK(buff);
 
+	if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
+		mp->marginal_path_err_rate_threshold = NU_NO;
+		origin = fpin_marginal_path_origin;
+		goto out;
+	}
+
 	mp_set_mpe(marginal_path_err_rate_threshold);
 	mp_set_ovr(marginal_path_err_rate_threshold);
 	mp_set_hwe(marginal_path_err_rate_threshold);
@@ -1146,6 +1183,12 @@ int select_marginal_path_err_recheck_gap
 	const char *origin;
 	STRBUF_ON_STACK(buff);
 
+	if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
+		mp->marginal_path_err_recheck_gap_time = NU_NO;
+		origin = fpin_marginal_path_origin;
+		goto out;
+	}
+
 	mp_set_mpe(marginal_path_err_recheck_gap_time);
 	mp_set_ovr(marginal_path_err_recheck_gap_time);
 	mp_set_hwe(marginal_path_err_recheck_gap_time);
@@ -1164,6 +1207,12 @@ int select_marginal_path_double_failed_t
 	const char *origin;
 	STRBUF_ON_STACK(buff);
 
+	if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) {
+		mp->marginal_path_double_failed_time = NU_NO;
+		origin = fpin_marginal_path_origin;
+		goto out;
+	}
+
 	mp_set_mpe(marginal_path_double_failed_time);
 	mp_set_ovr(marginal_path_double_failed_time);
 	mp_set_hwe(marginal_path_double_failed_time);
diff -pruN 0.8.8-1/libmultipath/propsel.h 0.9.0-3/libmultipath/propsel.h
--- 0.8.8-1/libmultipath/propsel.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/propsel.h	2022-06-10 16:47:26.000000000 +0000
@@ -16,9 +16,9 @@ int select_minio(struct config *conf, st
 int select_mode(struct config *conf, struct multipath *mp);
 int select_uid(struct config *conf, struct multipath *mp);
 int select_gid(struct config *conf, struct multipath *mp);
-int select_fast_io_fail(struct config *conf, struct multipath *mp);
-int select_dev_loss(struct config *conf, struct multipath *mp);
-int select_eh_deadline(struct config *conf, struct multipath *mp);
+int select_fast_io_fail(struct config *conf, struct path *pp);
+int select_dev_loss(struct config *conf, struct path *pp);
+int select_eh_deadline(struct config *conf, struct path *pp);
 int select_reservation_key(struct config *conf, struct multipath *mp);
 int select_retain_hwhandler (struct config *conf, struct multipath * mp);
 int select_detect_prio(struct config *conf, struct path * pp);
diff -pruN 0.8.8-1/libmultipath/structs.c 0.9.0-3/libmultipath/structs.c
--- 0.8.8-1/libmultipath/structs.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/structs.c	2022-06-10 16:47:26.000000000 +0000
@@ -18,6 +18,25 @@
 #include "prio.h"
 #include "prioritizers/alua_spc3.h"
 #include "dm-generic.h"
+#include "devmapper.h"
+
+const char * const protocol_name[LAST_BUS_PROTOCOL_ID + 1] = {
+	[SYSFS_BUS_UNDEF] = "undef",
+	[SYSFS_BUS_CCW] = "ccw",
+	[SYSFS_BUS_CCISS] = "cciss",
+	[SYSFS_BUS_NVME] = "nvme",
+	[SYSFS_BUS_SCSI + SCSI_PROTOCOL_FCP] = "scsi:fcp",
+	[SYSFS_BUS_SCSI + SCSI_PROTOCOL_SPI] = "scsi:spi",
+	[SYSFS_BUS_SCSI + SCSI_PROTOCOL_SSA] = "scsi:ssa",
+	[SYSFS_BUS_SCSI + SCSI_PROTOCOL_SBP] = "scsi:sbp",
+	[SYSFS_BUS_SCSI + SCSI_PROTOCOL_SRP] = "scsi:srp",
+	[SYSFS_BUS_SCSI + SCSI_PROTOCOL_ISCSI] = "scsi:iscsi",
+	[SYSFS_BUS_SCSI + SCSI_PROTOCOL_SAS] = "scsi:sas",
+	[SYSFS_BUS_SCSI + SCSI_PROTOCOL_ADT] = "scsi:adt",
+	[SYSFS_BUS_SCSI + SCSI_PROTOCOL_ATA] = "scsi:ata",
+	[SYSFS_BUS_SCSI + SCSI_PROTOCOL_USB] = "scsi:usb",
+	[SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC] = "scsi:unspec",
+};
 
 struct adapter_group *
 alloc_adaptergroup(void)
@@ -120,7 +139,6 @@ uninitialize_path(struct path *pp)
 
 	pp->dmstate = PSTATE_UNDEF;
 	pp->uid_attribute = NULL;
-	pp->getuid = NULL;
 
 	if (checker_selected(&pp->checker))
 		checker_put(&pp->checker);
@@ -227,7 +245,6 @@ alloc_multipath (void)
 		mpp->bestpg = 1;
 		mpp->mpcontext = NULL;
 		mpp->no_path_retry = NO_PATH_RETRY_UNDEF;
-		mpp->fast_io_fail = MP_FAST_IO_FAIL_UNSET;
 		dm_multipath_to_gen(mpp)->ops = &dm_gen_multipath_ops;
 	}
 	return mpp;
@@ -278,11 +295,6 @@ free_multipath (struct multipath * mpp,
 		mpp->alias = NULL;
 	}
 
-	if (mpp->dmi) {
-		free(mpp->dmi);
-		mpp->dmi = NULL;
-	}
-
 	if (!free_paths && mpp->pg) {
 		struct pathgroup *pgp;
 		struct path *pp;
@@ -407,10 +419,10 @@ find_mp_by_minor (const struct _vector *
 		return NULL;
 
 	vector_foreach_slot (mpvec, mpp, i) {
-		if (!mpp->dmi)
+		if (!has_dm_info(mpp))
 			continue;
 
-		if (mpp->dmi->minor == minor)
+		if (mpp->dmi.minor == minor)
 			return mpp;
 	}
 	return NULL;
@@ -757,3 +769,13 @@ out:
 
 	return 0;
 }
+
+unsigned int bus_protocol_id(const struct path *pp) {
+	if (!pp || pp->bus < 0 || pp->bus > SYSFS_BUS_SCSI)
+		return SYSFS_BUS_UNDEF;
+	if (pp->bus != SYSFS_BUS_SCSI)
+		return pp->bus;
+	if ((int)pp->sg_id.proto_id < 0 || pp->sg_id.proto_id > SCSI_PROTOCOL_UNSPEC)
+		return SYSFS_BUS_UNDEF;
+	return SYSFS_BUS_SCSI + pp->sg_id.proto_id;
+}
diff -pruN 0.8.8-1/libmultipath/structs.h 0.9.0-3/libmultipath/structs.h
--- 0.8.8-1/libmultipath/structs.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/structs.h	2022-06-10 16:47:26.000000000 +0000
@@ -4,6 +4,7 @@
 #include <sys/types.h>
 #include <inttypes.h>
 #include <stdbool.h>
+#include <libdevmapper.h>
 
 #include "prio.h"
 #include "byteorder.h"
@@ -56,12 +57,13 @@ enum failback_mode {
 	FAILBACK_FOLLOWOVER
 };
 
+/* SYSFS_BUS_SCSI should be last, see bus_protocol_id() */
 enum sysfs_buses {
 	SYSFS_BUS_UNDEF,
-	SYSFS_BUS_SCSI,
 	SYSFS_BUS_CCW,
 	SYSFS_BUS_CCISS,
 	SYSFS_BUS_NVME,
+	SYSFS_BUS_SCSI,
 };
 
 enum pathstates {
@@ -110,6 +112,12 @@ enum find_multipaths_states {
 	__FIND_MULTIPATHS_LAST,
 };
 
+enum marginal_pathgroups_mode {
+	MARGINAL_PATHGROUP_OFF = YN_NO,
+	MARGINAL_PATHGROUP_ON = YN_YES,
+	MARGINAL_PATHGROUP_FPIN,
+};
+
 enum flush_states {
 	FLUSH_UNDEF = YNU_UNDEF,
 	FLUSH_DISABLED = YNU_NO,
@@ -174,9 +182,18 @@ enum scsi_protocol {
 	SCSI_PROTOCOL_ADT = 7,	/* Media Changers */
 	SCSI_PROTOCOL_ATA = 8,
 	SCSI_PROTOCOL_USB = 9,  /* USB Attached SCSI (UAS), and others */
-	SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */
+	SCSI_PROTOCOL_UNSPEC = 0xa, /* No specific protocol */
 };
 
+/*
+ * Linear ordering of bus/protocol
+ * This assumes that SYSFS_BUS_SCSI is last in enum sysfs_buses
+ * SCSI is the only bus type for which we distinguish protocols.
+ */
+#define LAST_BUS_PROTOCOL_ID (SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC)
+unsigned int bus_protocol_id(const struct path *pp);
+extern const char * const protocol_name[];
+
 #define SCSI_INVALID_LUN ~0ULL
 
 enum no_undef_states {
@@ -311,8 +328,7 @@ struct path {
 	int detect_prio;
 	int detect_checker;
 	int tpgs;
-	char * uid_attribute;
-	char * getuid;
+	const char *uid_attribute;
 	struct prio prio;
 	struct checker checker;
 	struct multipath * mpp;
@@ -332,6 +348,9 @@ struct path {
 	int marginal;
 	int vpd_vendor_id;
 	int recheck_wwid;
+	int fast_io_fail;
+	unsigned int dev_loss;
+	int eh_deadline;
 	/* configlet pointers */
 	vector hwe;
 	struct gen_path generic_path;
@@ -359,7 +378,6 @@ struct multipath {
 	int minio;
 	int flush_on_last_del;
 	int attribute_flags;
-	int fast_io_fail;
 	int retain_hwhandler;
 	int deferred_remove;
 	bool in_recovery;
@@ -378,15 +396,13 @@ struct multipath {
 	int needs_paths_uevent;
 	int ghost_delay;
 	int ghost_delay_tick;
-	unsigned int dev_loss;
-	int eh_deadline;
 	uid_t uid;
 	gid_t gid;
 	mode_t mode;
 	unsigned long long size;
 	vector paths;
 	vector pg;
-	struct dm_info * dmi;
+	struct dm_info dmi;
 
 	/* configlet pointers */
 	char * alias;
@@ -418,6 +434,7 @@ struct multipath {
 	unsigned char prflag;
 	int all_tg_pt;
 	struct gen_multipath generic_mp;
+	bool fpin_must_reload;
 };
 
 static inline int marginal_path_check_enabled(const struct multipath *mpp)
diff -pruN 0.8.8-1/libmultipath/structs_vec.c 0.9.0-3/libmultipath/structs_vec.c
--- 0.8.8-1/libmultipath/structs_vec.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/structs_vec.c	2022-06-10 16:47:26.000000000 +0000
@@ -336,11 +336,17 @@ void set_path_removed(struct path *pp)
 	pp->initialized = INIT_REMOVED;
 }
 
+void remove_map_callback(struct multipath *mpp __attribute__((unused)))
+{
+}
+
 void
 remove_map(struct multipath *mpp, vector pathvec, vector mpvec)
 {
 	int i;
 
+	remove_map_callback(mpp);
+
 	free_pathvec(mpp->paths, KEEP_PATHS);
 	free_pgvec(mpp->pg, KEEP_PATHS);
 	mpp->paths = mpp->pg = NULL;
@@ -588,23 +594,22 @@ static void leave_recovery_mode(struct m
 	}
 }
 
-void __set_no_path_retry(struct multipath *mpp, bool check_features)
+void set_no_path_retry(struct multipath *mpp)
 {
 	bool is_queueing = false; /* assign a value to make gcc happy */
 
-	check_features = check_features && mpp->features != NULL;
-	if (check_features)
+	if (mpp->features)
 		is_queueing = strstr(mpp->features, "queue_if_no_path");
 
 	switch (mpp->no_path_retry) {
 	case NO_PATH_RETRY_UNDEF:
 		break;
 	case NO_PATH_RETRY_FAIL:
-		if (!check_features || is_queueing)
+		if (!mpp->features || is_queueing)
 			dm_queue_if_no_path(mpp->alias, 0);
 		break;
 	case NO_PATH_RETRY_QUEUE:
-		if (!check_features || !is_queueing)
+		if (!mpp->features || !is_queueing)
 			dm_queue_if_no_path(mpp->alias, 1);
 		break;
 	default:
@@ -613,11 +618,11 @@ void __set_no_path_retry(struct multipat
 			 * If in_recovery is set, leave_recovery_mode() takes
 			 * care of dm_queue_if_no_path. Otherwise, do it here.
 			 */
-			if ((!check_features || !is_queueing) &&
+			if ((!mpp->features || !is_queueing) &&
 			    !mpp->in_recovery)
 				dm_queue_if_no_path(mpp->alias, 1);
 			leave_recovery_mode(mpp);
-		} else
+		} else if (pathcount(mpp, PATH_PENDING) == 0)
 			enter_recovery_mode(mpp);
 		break;
 	}
diff -pruN 0.8.8-1/libmultipath/structs_vec.h 0.9.0-3/libmultipath/structs_vec.h
--- 0.8.8-1/libmultipath/structs_vec.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/structs_vec.h	2022-06-10 16:47:26.000000000 +0000
@@ -11,8 +11,7 @@ struct vectors {
 	struct mutex_lock lock; /* defined in lock.h */
 };
 
-void __set_no_path_retry(struct multipath *mpp, bool check_features);
-#define set_no_path_retry(mpp) __set_no_path_retry(mpp, true)
+void set_no_path_retry(struct multipath *mpp);
 
 int adopt_paths (vector pathvec, struct multipath * mpp);
 void orphan_path (struct path * pp, const char *reason);
diff -pruN 0.8.8-1/libmultipath/uevent.c 0.9.0-3/libmultipath/uevent.c
--- 0.8.8-1/libmultipath/uevent.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/uevent.c	2022-06-10 16:47:26.000000000 +0000
@@ -41,7 +41,6 @@
 #include <sys/mman.h>
 #include <sys/time.h>
 #include <libudev.h>
-#include <errno.h>
 
 #include "debug.h"
 #include "list.h"
@@ -52,10 +51,7 @@
 #include "config.h"
 #include "blacklist.h"
 #include "devmapper.h"
-
-#define MAX_ACCUMULATION_COUNT 2048
-#define MAX_ACCUMULATION_TIME 30*1000
-#define MIN_BURST_SPEED 10
+#include "strbuf.h"
 
 typedef int (uev_trigger)(struct uevent *, void * trigger_data);
 
@@ -68,6 +64,21 @@ static uev_trigger *my_uev_trigger;
 static void *my_trigger_data;
 static int servicing_uev;
 
+struct uevent_filter_state {
+	struct list_head uevq;
+	struct list_head *old_tail;
+	struct config *conf;
+	unsigned long added;
+	unsigned long discarded;
+	unsigned long filtered;
+	unsigned long merged;
+};
+
+static void reset_filter_state(struct uevent_filter_state *st)
+{
+	st->added = st->discarded = st->filtered = st->merged = 0;
+}
+
 int is_uevent_busy(void)
 {
 	int empty;
@@ -163,40 +174,24 @@ int uevent_get_env_positive_int(const st
 }
 
 void
-uevent_get_wwid(struct uevent *uev)
+uevent_get_wwid(struct uevent *uev, const struct config *conf)
 {
-	char *uid_attribute;
+	const char *uid_attribute;
 	const char *val;
-	struct config * conf;
 
-	conf = get_multipath_config();
-	pthread_cleanup_push(put_multipath_config, conf);
 	uid_attribute = get_uid_attribute_by_attrs(conf, uev->kernel);
-	pthread_cleanup_pop(1);
-
 	val = uevent_get_env_var(uev, uid_attribute);
 	if (val)
 		uev->wwid = val;
 }
 
-static bool uevent_need_merge(void)
+static bool uevent_need_merge(const struct config *conf)
 {
-	struct config * conf;
-	bool need_merge = false;
-
-	conf = get_multipath_config();
-	if (VECTOR_SIZE(&conf->uid_attrs) > 0)
-		need_merge = true;
-	put_multipath_config(conf);
-
-	return need_merge;
+	return VECTOR_SIZE(&conf->uid_attrs) > 0;
 }
 
-static bool uevent_can_discard(struct uevent *uev)
+static bool uevent_can_discard(struct uevent *uev, const struct config *conf)
 {
-	int invalid = 0;
-	struct config * conf;
-
 	/*
 	 * do not filter dm devices by devnode
 	 */
@@ -205,15 +200,10 @@ static bool uevent_can_discard(struct ue
 	/*
 	 * filter paths devices by devnode
 	 */
-	conf = get_multipath_config();
-	pthread_cleanup_push(put_multipath_config, conf);
 	if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
 			   uev->kernel) > 0)
-		invalid = 1;
-	pthread_cleanup_pop(1);
-
-	if (invalid)
 		return true;
+
 	return false;
 }
 
@@ -221,6 +211,10 @@ static bool
 uevent_can_filter(struct uevent *earlier, struct uevent *later)
 {
 
+	if (!strncmp(later->kernel, "dm-", 3) ||
+	    strcmp(earlier->kernel, later->kernel))
+		return false;
+
 	/*
 	 * filter earlier uvents if path has removed later. Eg:
 	 * "add path1 |chang path1 |add path2 |remove path1"
@@ -228,11 +222,8 @@ uevent_can_filter(struct uevent *earlier
 	 * "add path2 |remove path1"
 	 * uevents "add path1" and "chang path1" are filtered out
 	 */
-	if (!strcmp(earlier->kernel, later->kernel) &&
-		!strcmp(later->action, "remove") &&
-		strncmp(later->kernel, "dm-", 3)) {
+	if (!strcmp(later->action, "remove"))
 		return true;
-	}
 
 	/*
 	 * filter change uvents if add uevents exist. Eg:
@@ -241,12 +232,9 @@ uevent_can_filter(struct uevent *earlier
 	 * "add path1 |add path2"
 	 * uevent "chang path1" is filtered out
 	 */
-	if (!strcmp(earlier->kernel, later->kernel) &&
-		!strcmp(earlier->action, "change") &&
-		!strcmp(later->action, "add") &&
-		strncmp(later->kernel, "dm-", 3)) {
+	if (!strcmp(earlier->action, "change") &&
+	    !strcmp(later->action, "add"))
 		return true;
-	}
 
 	return false;
 }
@@ -279,10 +267,10 @@ merge_need_stop(struct uevent *earlier,
 	 * with the same wwid and different action
 	 * it would be better to stop merging.
 	 */
-	if (!strcmp(earlier->wwid, later->wwid) &&
-	    strcmp(earlier->action, later->action) &&
+	if (strcmp(earlier->action, later->action) &&
 	    strcmp(earlier->action, "change") &&
-	    strcmp(later->action, "change"))
+	    strcmp(later->action, "change") &&
+	    !strcmp(earlier->wwid, later->wwid))
 		return true;
 
 	return false;
@@ -292,111 +280,214 @@ static bool
 uevent_can_merge(struct uevent *earlier, struct uevent *later)
 {
 	/* merge paths uevents
-	 * whose wwids exsit and are same
+	 * whose wwids exist and are same
 	 * and actions are same,
 	 * and actions are addition or deletion
 	 */
 	if (earlier->wwid && later->wwid &&
-	    !strcmp(earlier->wwid, later->wwid) &&
+	    strncmp(earlier->kernel, "dm-", 3) &&
 	    !strcmp(earlier->action, later->action) &&
-	    strncmp(earlier->action, "change", 6) &&
-	    strncmp(earlier->kernel, "dm-", 3)) {
+	    (!strcmp(earlier->action, "add") ||
+	     !strcmp(earlier->action, "remove")) &&
+	    !strcmp(earlier->wwid, later->wwid))
 		return true;
-	}
 
 	return false;
 }
 
-static void
-uevent_prepare(struct list_head *tmpq)
+static void uevent_delete_from_list(struct uevent *to_delete,
+				    struct uevent **previous,
+				    struct list_head **old_tail)
+{
+	/*
+	 * "old_tail" is the list_head before the last list element to which
+	 * the caller iterates (the list anchor if the caller iterates over
+	 * the entire list). If this element is removed (which can't happen
+	 * for the anchor), "old_tail" must be moved. It can happen that
+	 * "old_tail" ends up pointing at the anchor.
+	 */
+	if (*old_tail == &to_delete->node)
+		*old_tail = to_delete->node.prev;
+
+	list_del_init(&to_delete->node);
+
+	/*
+	 * The "to_delete" uevent has been merged with other uevents
+	 * previously. Re-insert them into the list, at the point we're
+	 * currently at. This must be done after the list_del_init() above,
+	 * otherwise previous->next would still point to to_delete.
+	 */
+	if (!list_empty(&to_delete->merge_node)) {
+		struct uevent *last = list_entry(to_delete->merge_node.prev,
+						 typeof(*last), node);
+
+		condlog(3, "%s: deleted uevent \"%s %s\" with merged uevents",
+			__func__, to_delete->action, to_delete->kernel);
+		list_splice(&to_delete->merge_node, &(*previous)->node);
+		*previous = last;
+	}
+	if (to_delete->udev)
+		udev_device_unref(to_delete->udev);
+
+	free(to_delete);
+}
+
+/*
+ * Use this function to delete events that are known not to
+ * be equal to old_tail, and have an empty merge_node list.
+ * For others, use uevent_delete_from_list().
+ */
+static void uevent_delete_simple(struct uevent *to_delete)
+{
+	list_del_init(&to_delete->node);
+
+	if (to_delete->udev)
+		udev_device_unref(to_delete->udev);
+
+	free(to_delete);
+}
+
+static void uevent_prepare(struct uevent_filter_state *st)
 {
 	struct uevent *uev, *tmp;
 
-	list_for_each_entry_reverse_safe(uev, tmp, tmpq, node) {
-		if (uevent_can_discard(uev)) {
-			list_del_init(&uev->node);
-			if (uev->udev)
-				udev_device_unref(uev->udev);
-			free(uev);
+	list_for_some_entry_reverse_safe(uev, tmp, &st->uevq, st->old_tail, node) {
+
+		st->added++;
+		if (uevent_can_discard(uev, st->conf)) {
+			uevent_delete_simple(uev);
+			st->discarded++;
 			continue;
 		}
 
 		if (strncmp(uev->kernel, "dm-", 3) &&
-		    uevent_need_merge())
-			uevent_get_wwid(uev);
+		    uevent_need_merge(st->conf))
+			uevent_get_wwid(uev, st->conf);
 	}
 }
 
 static void
-uevent_filter(struct uevent *later, struct list_head *tmpq)
+uevent_filter(struct uevent *later, struct uevent_filter_state *st)
 {
 	struct uevent *earlier, *tmp;
 
-	list_for_some_entry_reverse_safe(earlier, tmp, &later->node, tmpq, node) {
+	list_for_some_entry_reverse_safe(earlier, tmp, &later->node, &st->uevq, node) {
 		/*
 		 * filter unnessary earlier uevents
 		 * by the later uevent
 		 */
+		if (!list_empty(&earlier->merge_node)) {
+			struct uevent *mn, *t;
+
+			list_for_each_entry_reverse_safe(mn, t, &earlier->merge_node, node) {
+				if (uevent_can_filter(mn, later)) {
+					condlog(4, "uevent: \"%s %s\" (merged into \"%s %s\") filtered by \"%s %s\"",
+						mn->action, mn->kernel,
+						earlier->action, earlier->kernel,
+						later->action, later->kernel);
+					uevent_delete_simple(mn);
+					st->filtered++;
+				}
+			}
+		}
 		if (uevent_can_filter(earlier, later)) {
-			condlog(3, "uevent: %s-%s has filtered by uevent: %s-%s",
-				earlier->kernel, earlier->action,
-				later->kernel, later->action);
-
-			list_del_init(&earlier->node);
-			if (earlier->udev)
-				udev_device_unref(earlier->udev);
-			free(earlier);
+			condlog(4, "uevent: \"%s %s\" filtered by \"%s %s\"",
+				earlier->action, earlier->kernel,
+				later->action, later->kernel);
+
+			uevent_delete_from_list(earlier, &tmp, &st->old_tail);
+			st->filtered++;
 		}
 	}
 }
 
-static void
-uevent_merge(struct uevent *later, struct list_head *tmpq)
+static void uevent_merge(struct uevent *later, struct uevent_filter_state *st)
 {
 	struct uevent *earlier, *tmp;
 
-	list_for_some_entry_reverse_safe(earlier, tmp, &later->node, tmpq, node) {
+	list_for_some_entry_reverse_safe(earlier, tmp, &later->node, &st->uevq, node) {
 		if (merge_need_stop(earlier, later))
 			break;
 		/*
 		 * merge earlier uevents to the later uevent
 		 */
 		if (uevent_can_merge(earlier, later)) {
-			condlog(3, "merged uevent: %s-%s-%s with uevent: %s-%s-%s",
-				earlier->action, earlier->kernel, earlier->wwid,
+			condlog(4, "uevent: \"%s %s\" merged with \"%s %s\" for WWID %s",
+				earlier->action, earlier->kernel,
 				later->action, later->kernel, later->wwid);
 
+			/* See comment in uevent_delete_from_list() */
+			if (&earlier->node == st->old_tail)
+				st->old_tail = earlier->node.prev;
+
 			list_move(&earlier->node, &later->merge_node);
+			list_splice_init(&earlier->merge_node,
+					 &later->merge_node);
+			st->merged++;
 		}
 	}
 }
 
-static void
-merge_uevq(struct list_head *tmpq)
+static void merge_uevq(struct uevent_filter_state *st)
 {
 	struct uevent *later;
 
-	uevent_prepare(tmpq);
-	list_for_each_entry_reverse(later, tmpq, node) {
-		uevent_filter(later, tmpq);
-		if(uevent_need_merge())
-			uevent_merge(later, tmpq);
+	uevent_prepare(st);
+
+	list_for_some_entry_reverse(later, &st->uevq, st->old_tail, node)
+		uevent_filter(later, st);
+
+	if(uevent_need_merge(st->conf))
+		list_for_some_entry_reverse(later, &st->uevq, st->old_tail, node)
+			uevent_merge(later, st);
+}
+
+static void print_uev(struct strbuf *buf, struct uevent *uev)
+{
+	print_strbuf(buf, "\"%s %s\"", uev->action, uev->kernel);
+	if (!list_empty(&uev->merge_node)) {
+		struct uevent *u;
+
+		append_strbuf_str(buf, "[");
+		list_for_each_entry(u, &uev->merge_node, node)
+			print_strbuf(buf, "\"%s %s \"", u->action, u->kernel);
+		append_strbuf_str(buf, "]");
 	}
+	append_strbuf_str(buf, " ");
+}
+
+static void print_uevq(const char *msg, struct list_head *uevq)
+{
+	struct uevent *uev;
+	int i = 0;
+	STRBUF_ON_STACK(buf);
+
+	if (4 > MAX_VERBOSITY || 4 > libmp_verbosity)
+		return;
+
+	if (list_empty(uevq))
+		append_strbuf_str(&buf, "*empty*");
+	else
+		list_for_each_entry(uev, uevq, node) {
+			print_strbuf(&buf, "%d:", i++);
+			print_uev(&buf, uev);
+		}
+
+	condlog(4, "uevent queue (%s): %s", msg, steal_strbuf_str(&buf));
 }
 
 static void
 service_uevq(struct list_head *tmpq)
 {
-	struct uevent *uev, *tmp;
-
-	list_for_each_entry_safe(uev, tmp, tmpq, node) {
-		list_del_init(&uev->node);
+	struct uevent *uev = list_pop_entry(tmpq, typeof(*uev), node);
 
-		pthread_cleanup_push(cleanup_uev, uev);
-		if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))
-			condlog(0, "uevent trigger error");
-		pthread_cleanup_pop(1);
-	}
+	if (uev == NULL)
+		return;
+	condlog(4, "servicing uevent '%s %s'", uev->action, uev->kernel);
+	pthread_cleanup_push(cleanup_uev, uev);
+	if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))
+		condlog(0, "uevent trigger error");
+	pthread_cleanup_pop(1);
 }
 
 static void uevent_cleanup(void *arg)
@@ -427,42 +518,68 @@ static void cleanup_global_uevq(void *ar
 	pthread_mutex_unlock(uevq_lockp);
 }
 
+static void log_filter_state(const struct uevent_filter_state *st)
+{
+	if (st->added == 0 && st->filtered == 0 && st->merged == 0)
+		return;
+
+	condlog(3, "uevents: %lu added, %lu discarded, %lu filtered, %lu merged",
+		st->added, st->discarded, st->filtered, st->merged);
+}
+
 /*
  * Service the uevent queue.
  */
 int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
 		    void * trigger_data)
 {
+	struct uevent_filter_state filter_state;
+
+	INIT_LIST_HEAD(&filter_state.uevq);
 	my_uev_trigger = uev_trigger;
 	my_trigger_data = trigger_data;
 
 	mlockall(MCL_CURRENT | MCL_FUTURE);
 
+	pthread_cleanup_push(cleanup_uevq, &filter_state.uevq);
 	while (1) {
-		LIST_HEAD(uevq_tmp);
-
 		pthread_cleanup_push(cleanup_mutex, uevq_lockp);
 		pthread_mutex_lock(uevq_lockp);
-		servicing_uev = 0;
-		/*
-		 * Condition signals are unreliable,
-		 * so make sure we only wait if we have to.
-		 */
-		if (list_empty(&uevq)) {
+
+		servicing_uev = !list_empty(&filter_state.uevq);
+
+		while (list_empty(&filter_state.uevq) && list_empty(&uevq)) {
+			condlog(4, "%s: waiting for events", __func__);
 			pthread_cond_wait(uev_condp, uevq_lockp);
+			condlog(4, "%s: waking up", __func__);
 		}
+
 		servicing_uev = 1;
-		list_splice_init(&uevq, &uevq_tmp);
+		/*
+		 * "old_tail" is the list element towards which merge_uevq()
+		 * will iterate: the last element of uevq before
+		 * appending new uevents. If uveq  empty, uevq.prev
+		 * equals &uevq, which is what we need.
+		 */
+		filter_state.old_tail = filter_state.uevq.prev;
+		list_splice_tail_init(&uevq, &filter_state.uevq);
 		pthread_cleanup_pop(1);
 
 		if (!my_uev_trigger)
 			break;
 
-		pthread_cleanup_push(cleanup_uevq, &uevq_tmp);
-		merge_uevq(&uevq_tmp);
-		service_uevq(&uevq_tmp);
+		reset_filter_state(&filter_state);
+		pthread_cleanup_push(put_multipath_config, filter_state.conf);
+		print_uevq("append", &filter_state.uevq);
+		filter_state.conf = get_multipath_config();
+		merge_uevq(&filter_state);
 		pthread_cleanup_pop(1);
+		log_filter_state(&filter_state);
+
+		print_uevq("merge", &filter_state.uevq);
+		service_uevq(&filter_state.uevq);
 	}
+	pthread_cleanup_pop(1);
 	condlog(3, "Terminating uev service queue");
 	return 0;
 }
@@ -529,44 +646,43 @@ static struct uevent *uevent_from_udev_d
 	return uev;
 }
 
-static bool uevent_burst(struct timeval *start_time, int events)
+#define MAX_UEVENTS 1000
+static int uevent_receive_events(int fd, struct list_head *tmpq,
+				 struct udev_monitor *monitor)
 {
-	struct timeval diff_time, end_time;
-	unsigned long speed;
-	unsigned long eclipse_ms;
+	struct pollfd ev_poll;
+	int n = 0;
 
-	if(events > MAX_ACCUMULATION_COUNT) {
-		condlog(2, "burst got %u uevents, too much uevents, stopped", events);
-		return false;
-	}
-
-	gettimeofday(&end_time, NULL);
-	timersub(&end_time, start_time, &diff_time);
+	do {
+		struct uevent *uev;
+		struct udev_device *dev;
 
-	eclipse_ms = diff_time.tv_sec * 1000 + diff_time.tv_usec / 1000;
+		dev = udev_monitor_receive_device(monitor);
+		if (!dev) {
+			condlog(0, "failed getting udev device");
+			break;
+		}
+		uev = uevent_from_udev_device(dev);
+		if (!uev)
+			break;
 
-	if (eclipse_ms == 0)
-		return true;
+		list_add_tail(&uev->node, tmpq);
+		n++;
+		condlog(4, "received uevent \"%s %s\"", uev->action, uev->kernel);
 
-	if (eclipse_ms > MAX_ACCUMULATION_TIME) {
-		condlog(2, "burst continued %lu ms, too long time, stopped", eclipse_ms);
-		return false;
-	}
+		ev_poll.fd = fd;
+		ev_poll.events = POLLIN;
 
-	speed = (events * 1000) / eclipse_ms;
-	if (speed > MIN_BURST_SPEED)
-		return true;
+	} while (n < MAX_UEVENTS && poll(&ev_poll, 1, 0) > 0);
 
-	return false;
+	return n;
 }
 
 int uevent_listen(struct udev *udev)
 {
 	int err = 2;
 	struct udev_monitor *monitor = NULL;
-	int fd, socket_flags, events;
-	struct timeval start_time;
-	int timeout = 30;
+	int fd, socket_flags;
 	LIST_HEAD(uevlisten_tmp);
 
 	/*
@@ -618,59 +734,30 @@ int uevent_listen(struct udev *udev)
 		goto out;
 	}
 
-	events = 0;
-	gettimeofday(&start_time, NULL);
 	pthread_cleanup_push(cleanup_global_uevq, NULL);
 	pthread_cleanup_push(cleanup_uevq, &uevlisten_tmp);
 	while (1) {
-		struct uevent *uev;
-		struct udev_device *dev;
-		struct pollfd ev_poll;
-		int poll_timeout;
-		int fdcount;
+		int fdcount, events;
+		struct pollfd ev_poll = { .fd = fd, .events = POLLIN, };
 
-		memset(&ev_poll, 0, sizeof(struct pollfd));
-		ev_poll.fd = fd;
-		ev_poll.events = POLLIN;
-		poll_timeout = timeout * 1000;
-		errno = 0;
-		fdcount = poll(&ev_poll, 1, poll_timeout);
-		if (fdcount > 0 && ev_poll.revents & POLLIN) {
-			timeout = uevent_burst(&start_time, events + 1) ? 1 : 0;
-			dev = udev_monitor_receive_device(monitor);
-			if (!dev) {
-				condlog(0, "failed getting udev device");
-				continue;
-			}
-			uev = uevent_from_udev_device(dev);
-			if (!uev)
-				continue;
-			list_add_tail(&uev->node, &uevlisten_tmp);
-			events++;
-			continue;
-		}
+		fdcount = poll(&ev_poll, 1, -1);
 		if (fdcount < 0) {
 			if (errno == EINTR)
 				continue;
 
-			condlog(0, "error receiving "
-				"uevent message: %m");
+			condlog(0, "error receiving uevent message: %m");
 			err = -errno;
 			break;
 		}
-		if (!list_empty(&uevlisten_tmp)) {
-			/*
-			 * Queue uevents and poke service pthread.
-			 */
-			condlog(3, "Forwarding %d uevents", events);
-			pthread_mutex_lock(uevq_lockp);
-			list_splice_tail_init(&uevlisten_tmp, &uevq);
-			pthread_cond_signal(uev_condp);
-			pthread_mutex_unlock(uevq_lockp);
-			events = 0;
-		}
-		gettimeofday(&start_time, NULL);
-		timeout = 30;
+		events = uevent_receive_events(fd, &uevlisten_tmp, monitor);
+		if (events <= 0)
+			continue;
+
+		condlog(4, "Forwarding %d uevents", events);
+		pthread_mutex_lock(uevq_lockp);
+		list_splice_tail_init(&uevlisten_tmp, &uevq);
+		pthread_cond_signal(uev_condp);
+		pthread_mutex_unlock(uevq_lockp);
 	}
 	pthread_cleanup_pop(1);
 	pthread_cleanup_pop(1);
diff -pruN 0.8.8-1/libmultipath/uevent.h 0.9.0-3/libmultipath/uevent.h
--- 0.8.8-1/libmultipath/uevent.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/uevent.h	2022-06-10 16:47:26.000000000 +0000
@@ -10,6 +10,7 @@
 #define OBJECT_SIZE			512
 
 struct udev;
+struct config;
 
 struct uevent {
 	struct list_head node;
@@ -31,7 +32,7 @@ int uevent_listen(struct udev *udev);
 int uevent_dispatch(int (*store_uev)(struct uevent *, void * trigger_data),
 		    void * trigger_data);
 bool uevent_is_mpath(const struct uevent *uev);
-void uevent_get_wwid(struct uevent *uev);
+void uevent_get_wwid(struct uevent *uev, const struct config *conf);
 
 int uevent_get_env_positive_int(const struct uevent *uev,
 				const char *attr);
diff -pruN 0.8.8-1/libmultipath/util.h 0.9.0-3/libmultipath/util.h
--- 0.8.8-1/libmultipath/util.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/util.h	2022-06-10 16:47:26.000000000 +0000
@@ -82,6 +82,16 @@ struct bitfield {
 	bitfield_t bits[];
 };
 
+#define STATIC_BITFIELD(name, length)					\
+	static struct {							\
+		unsigned int len;					\
+		bitfield_t bits[((length) - 1) / bits_per_slot + 1];	\
+	} __static__ ## name = {					\
+		.len = (length),					\
+		.bits = { 0, },						\
+	}; \
+	struct bitfield *name = (struct bitfield *)& __static__ ## name
+
 struct bitfield *alloc_bitfield(unsigned int maxbit);
 
 void _log_bitfield_overflow(const char *f, unsigned int bit, unsigned int len);
diff -pruN 0.8.8-1/libmultipath/version.h 0.9.0-3/libmultipath/version.h
--- 0.8.8-1/libmultipath/version.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/libmultipath/version.h	2022-06-10 16:47:26.000000000 +0000
@@ -20,8 +20,9 @@
 #ifndef _VERSION_H
 #define _VERSION_H
 
-#define VERSION_CODE 0x000808
-#define DATE_CODE    0x030c15
+#define VERSION_CODE 0x000900
+/* MMDDYY, in hex */
+#define DATE_CODE    0x050316
 
 #define PROG    "multipath-tools"
 
diff -pruN 0.8.8-1/Makefile.inc 0.9.0-3/Makefile.inc
--- 0.8.8-1/Makefile.inc	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/Makefile.inc	2022-06-10 16:47:26.000000000 +0000
@@ -3,18 +3,18 @@
 #
 
 #
-# Allow to force some libraries to be used statically. (Uncomment one of the
-# following lines or define the values when calling make.)
-#
-# WITH_LOCAL_LIBDM	= 1
-# WITH_LOCAL_LIBSYSFS	= 1
-#
 # Uncomment to disable libdmmp support
 # ENABLE_LIBDMMP = 0
 #
 # Uncomment to disable dmevents polling support
 # ENABLE_DMEVENTS_POLL = 0
 
+# List of scsi device handler modules to load on boot, e.g.
+# SCSI_DH_MODULES_PRELOAD := scsi_dh_alua scsi_dh_rdac
+SCSI_DH_MODULES_PRELOAD :=
+
+EXTRAVERSION := $(shell rev=$$(git rev-parse --short=7 HEAD 2>/dev/null); echo $${rev:+-g$$rev})
+
 PKGCONFIG	?= pkg-config
 
 ifeq ($(TOPDIR),)
@@ -52,12 +52,34 @@ ifndef SYSTEMDPATH
 	SYSTEMDPATH=usr/lib
 endif
 
+ifndef DEVMAPPER_INCDIR
+	ifeq ($(shell $(PKGCONFIG) --modversion devmapper >/dev/null 2>&1 && echo 1), 1)
+		DEVMAPPER_INCDIR = $(shell $(PKGCONFIG) --variable=includedir devmapper)
+	else
+		DEVMAPPER_INCDIR = /usr/include
+	endif
+endif
+
+ifndef LIBUDEV_INCDIR
+	ifeq ($(shell $(PKGCONFIG) --modversion libudev >/dev/null 2>&1 && echo 1), 1)
+		LIBUDEV_INCDIR = $(shell $(PKGCONFIG) --variable=includedir libudev)
+	else
+		LIBUDEV_INCDIR = /usr/include
+	endif
+endif
+
+# Allow user to override default location.
+ifndef LINUX_HEADERS_INCDIR
+	LINUX_HEADERS_INCDIR = /usr/include
+endif
+
 prefix		=
 exec_prefix	= $(prefix)
 usr_prefix	= $(prefix)
 bindir		= $(exec_prefix)/sbin
 libudevdir	= $(prefix)/$(SYSTEMDPATH)/udev
 udevrulesdir	= $(libudevdir)/rules.d
+modulesloaddir  = $(prefix)/$(SYSTEMDPATH)/modules-load.d
 multipathdir	= $(TOPDIR)/libmultipath
 man8dir		= $(prefix)/usr/share/man/man8
 man5dir		= $(prefix)/usr/share/man/man5
@@ -74,8 +96,10 @@ libdmmpdir	= $(TOPDIR)/libdmmp
 nvmedir		= $(TOPDIR)/libmultipath/nvme
 includedir	= $(prefix)/usr/include
 pkgconfdir	= $(usrlibdir)/pkgconfig
+plugindir       := $(prefix)/$(LIB)/multipath
+configdir       := $(prefix)/etc/multipath/conf.d
 
-GZIP		= gzip -9 -c
+GZIP_PROG	= gzip -9 -c
 RM		= rm -f
 LN		= ln -sf
 INSTALL_PROGRAM	= install
@@ -93,6 +117,20 @@ TEST_CC_OPTION = $(shell \
 		echo "$(2)"; \
 	fi)
 
+# "make" on some distros will fail on explicit '#' or '\#' in the program text below
+__HASH__ := \#
+# Check if _DFORTIFY_SOURCE=3 is supported.
+# On some distros (e.g. Debian Buster) it will be falsely reported as supported
+# but it doesn't seem to make a difference wrt the compilation result.
+FORTIFY_OPT := $(shell \
+	if /bin/echo -e '$(__HASH__)include <string.h>\nint main(void) { return 0; }' | \
+		$(CC) -o /dev/null -c -O2 -Werror -D_FORTIFY_SOURCE=3 -xc - 2>/dev/null; \
+	then \
+		echo "-D_FORTIFY_SOURCE=3"; \
+	else \
+		echo "-D_FORTIFY_SOURCE=2"; \
+	fi)
+
 STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)
 ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,)
 WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered -Wno-error=clobbered,)
@@ -102,10 +140,10 @@ OPTFLAGS	:= -O2 -g $(STACKPROT) --param=
 WARNFLAGS	:= -Werror -Wall -Wextra -Wformat=2 $(WFORMATOVERFLOW) -Werror=implicit-int \
 		  -Werror=implicit-function-declaration -Werror=format-security \
 		  $(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS)
-CPPFLAGS	:= -Wp,-D_FORTIFY_SOURCE=2
-CFLAGS		:= --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe \
-		   -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \
-		   -MMD -MP
+CPPFLAGS	:= $(FORTIFY_OPT) \
+		   -DBIN_DIR=\"$(bindir)\" -DMULTIPATH_DIR=\"$(plugindir)\" -DRUN_DIR=\"${RUN}\" \
+		   -DCONFIG_DIR=\"$(configdir)\" -DEXTRAVERSION=\"$(EXTRAVERSION)\" -MMD -MP
+CFLAGS		:= --std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe
 BIN_CFLAGS	= -fPIE -DPIE
 LIB_CFLAGS	= -fPIC
 SHARED_FLAGS	= -shared
@@ -138,9 +176,22 @@ check_file = $(shell \
 	echo "$$found" \
 	)
 
+# Check whether a file contains a variable with name $1 in header file $2
+check_var = $(shell \
+	if grep -Eq "(^|[[:blank:]])$1([[:blank:]]|=|$$)" "$2"; then \
+                found=1; \
+                status="yes"; \
+        else \
+                found=0; \
+                status="no"; \
+        fi; \
+        echo 1>&2 "Checking for ..  $1 in $2 ... $$status"; \
+        echo "$$found" \
+        )
+
 %.o:	%.c
 	@echo building $@ because of $?
-	$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
+	$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
 
 %.abi:  %.so.0
 	abidw $< >$@
diff -pruN 0.8.8-1/mpathpersist/main.c 0.9.0-3/mpathpersist/main.c
--- 0.8.8-1/mpathpersist/main.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/mpathpersist/main.c	2022-06-10 16:47:26.000000000 +0000
@@ -10,6 +10,7 @@
 #include <getopt.h>
 #include <libudev.h>
 #include "mpath_persist.h"
+#include "mpath_persist_int.h"
 #include "main.h"
 #include "debug.h"
 #include <pthread.h>
diff -pruN 0.8.8-1/mpathpersist/Makefile 0.9.0-3/mpathpersist/Makefile
--- 0.8.8-1/mpathpersist/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/mpathpersist/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -1,6 +1,7 @@
 include ../Makefile.inc
 
-CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir)
+CPPFLAGS += -I$(multipathdir) -I$(mpathpersistdir)
+CFLAGS += $(BIN_CFLAGS)
 LDFLAGS += $(BIN_LDFLAGS)
 
 LIBDEPS += -L$(mpathpersistdir) -lmpathpersist -L$(multipathdir) -lmultipath \
@@ -14,22 +15,21 @@ all: $(EXEC)
 
 $(EXEC): $(OBJS)
 	$(CC) $(OBJS) -o $(EXEC) $(LDFLAGS) $(CFLAGS) $(LIBDEPS)
-	$(GZIP) $(EXEC).8 > $(EXEC).8.gz
 
 install:
 	$(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
 	$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
 	$(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
-	$(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir)
+	$(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(man8dir)
 
 clean: dep_clean
-	$(RM) core *.o $(EXEC) *.gz
+	$(RM) core *.o $(EXEC)
 
 include $(wildcard $(OBJS:.o=.d))
 
 uninstall:
 	$(RM) $(DESTDIR)$(bindir)/$(EXEC)
-	$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
+	$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8
 
 dep_clean:
 	$(RM) $(OBJS:.o=.d)
diff -pruN 0.8.8-1/multipath/main.c 0.9.0-3/multipath/main.c
--- 0.8.8-1/multipath/main.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/multipath/main.c	2022-06-10 16:47:26.000000000 +0000
@@ -63,7 +63,6 @@
 #include "time-util.h"
 #include "file.h"
 #include "valid.h"
-#include "alias.h"
 
 /*
  * Return values of configure(), check_path_valid(), and main().
@@ -989,11 +988,11 @@ main (int argc, char *argv[])
 
 	libmp_udev_set_sync_support(1);
 
-	if (init_checkers(conf->multipath_dir)) {
+	if (init_checkers()) {
 		condlog(0, "failed to initialize checkers");
 		goto out;
 	}
-	if (init_prio(conf->multipath_dir)) {
+	if (init_prio()) {
 		condlog(0, "failed to initialize prioritizers");
 		goto out;
 	}
@@ -1002,7 +1001,7 @@ main (int argc, char *argv[])
 		conf->enable_foreign = strdup("");
 
 	/* Failing here is non-fatal */
-	init_foreign(conf->multipath_dir, conf->enable_foreign);
+	init_foreign(conf->enable_foreign);
 	if (cmd == CMD_USABLE_PATHS) {
 		r = check_usable_paths(conf, dev, dev_type) ?
 			RTVL_FAIL : RTVL_OK;
@@ -1061,6 +1060,11 @@ main (int argc, char *argv[])
 	if (retries < 0)
 		retries = conf->remove_retries;
 	if (cmd == CMD_FLUSH_ONE) {
+		if (dm_is_mpath(dev) != 1) {
+			condlog(0, "%s is not a multipath device", dev);
+			r = RTVL_FAIL;
+			goto out;
+		}
 		r = dm_suspend_and_flush_map(dev, retries) ?
 		    RTVL_FAIL : RTVL_OK;
 		goto out;
diff -pruN 0.8.8-1/multipath/Makefile 0.9.0-3/multipath/Makefile
--- 0.8.8-1/multipath/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/multipath/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -3,7 +3,8 @@
 #
 include ../Makefile.inc
 
-CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir)
+CPPFLAGS += -I$(multipathdir) -I$(mpathcmddir)
+CFLAGS += $(BIN_CFLAGS)
 LDFLAGS += $(BIN_LDFLAGS)
 LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathcmddir) -lmpathcmd \
 	-lpthread -ldevmapper -ldl -ludev
@@ -16,29 +17,36 @@ all: $(EXEC)
 
 $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
 	$(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(LIBDEPS)
-	$(GZIP) $(EXEC).8 > $(EXEC).8.gz
-	$(GZIP) $(EXEC).conf.5 > $(EXEC).conf.5.gz
 
 install:
 	$(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
 	$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
 	$(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir)
 	$(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir)
-	$(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules
+	$(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(udevrulesdir)/56-multipath.rules
+	$(INSTALL_PROGRAM) -d $(DESTDIR)$(modulesloaddir)
+	$(INSTALL_PROGRAM) -m 644 modules-load.conf $(DESTDIR)$(modulesloaddir)/multipath.conf
 	$(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
-	$(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir)
+	$(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(man8dir)
 	$(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir)
-	$(INSTALL_PROGRAM) -m 644 $(EXEC).conf.5.gz $(DESTDIR)$(man5dir)
+	$(INSTALL_PROGRAM) -m 644 $(EXEC).conf.5 $(DESTDIR)$(man5dir)
+ifneq ($(SCSI_DH_MODULES_PRELOAD),)
+	$(INSTALL_PROGRAM) -m 644 scsi_dh.conf $(DESTDIR)$(modulesloaddir)/scsi_dh.conf
+	for _x in $(SCSI_DH_MODULES_PRELOAD); do echo "$$_x"; done \
+	    >>$(DESTDIR)$(modulesloaddir)/scsi_dh.conf
+endif
 
 uninstall:
 	$(RM) $(DESTDIR)$(bindir)/$(EXEC)
 	$(RM) $(DESTDIR)$(udevrulesdir)/11-dm-mpath.rules
+	$(RM) $(DESTDIR)$(modulesloaddir)/multipath.conf
+	$(RM) $(DESTDIR)$(modulesloaddir)/scsi_dh.conf
 	$(RM) $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules
-	$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
-	$(RM) $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz
+	$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8
+	$(RM) $(DESTDIR)$(man5dir)/$(EXEC).conf.5
 
 clean: dep_clean
-	$(RM) core *.o $(EXEC) *.gz
+	$(RM) core *.o $(EXEC)
 
 include $(wildcard $(OBJS:.o=.d))
 
diff -pruN 0.8.8-1/multipath/modules-load.conf 0.9.0-3/multipath/modules-load.conf
--- 0.8.8-1/multipath/modules-load.conf	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/multipath/modules-load.conf	2022-06-10 16:47:26.000000000 +0000
@@ -0,0 +1,3 @@
+# load dm-multipath early, both multipathd and multipath depend on it
+# (note that multipath may be called from udev rules!)
+dm-multipath
diff -pruN 0.8.8-1/multipath/multipath.conf.5 0.9.0-3/multipath/multipath.conf.5
--- 0.8.8-1/multipath/multipath.conf.5	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/multipath/multipath.conf.5	2022-06-10 16:47:26.000000000 +0000
@@ -178,13 +178,7 @@ The default is: \fBno\fR
 .
 .TP
 .B multipath_dir
-This option is deprecated, and will be removed in a future release.
-Directory where the dynamic shared objects are stored. Defined at compile time,
-commonly \fI/lib64/multipath/\fR or \fI/lib/multipath/\fR.
-.RS
-.TP
-The default is: \fB<system dependent>\fR
-.RE
+This option is not supported any more. The value is ignored.
 .
 .
 .TP
@@ -251,7 +245,7 @@ The default is: \fBfailover\fR
 .B uid_attrs
 .
 Setting this option activates \fBmerging uevents\fR by WWID, which may improve
-uevent processing effiency. Moreover, it's an alternative method to configure
+uevent processing efficiency. Moreover, it's an alternative method to configure
 the udev properties to use for determining unique path identifiers (WWIDs).
 .RS
 .PP
@@ -264,6 +258,8 @@ If this option is configured and matches
 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
@@ -287,12 +283,7 @@ The default is: \fBID_WWN\fR, for NVMe d
 .
 .TP
 .B getuid_callout
-(Superseded by \fIuid_attribute\fR) The default program and args to callout
-to obtain a unique path identifier. Should be specified with an absolute path.
-.RS
-.TP
-The default is: \fB<unset>\fR
-.RE
+This option is not supported any more. The value is ignored.
 .
 .
 .TP
@@ -587,7 +578,7 @@ and multipathd. This is equivalent to ul
 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 greated than 1024.
+if that number is greater than 1024.
 .RS
 .TP
 The default is: \fBmax\fR
@@ -830,7 +821,7 @@ The default is: \fBno\fR
 .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
-mutipath.conf. If the SCSI layer has not attached a hardware handler,
+multipath.conf. If the SCSI layer has not attached a hardware handler,
 multipath will continue to use its configured hardware handler.
 .RS
 .PP
@@ -937,15 +928,7 @@ The default is: \fB<unset>\fR
 .
 .TP
 .B config_dir
-This option is deprecated, and will be removed in a future release.
-If set to anything other than "", multipath will search this directory
-alphabetically for file ending in ".conf" and it will read configuration
-information from them, just as if it was in \fI/etc/multipath.conf\fR.
-config_dir must either be "" or a fully qualified directory name.
-.RS
-.TP
-The default is: \fB/etc/multipath/conf.d/\fR
-.RE
+This option is not supported any more. The value is ignored.
 .
 .
 .TP
@@ -1088,23 +1071,32 @@ The default is: \fBno\fR
 .
 .TP
 .B marginal_pathgroups
-If set to \fIno\fR, the \fIdelay_*_checks\fR, \fImarginal_path_*\fR, and
+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 \fIyes\fR, when one of the marginal path detecting methods
+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
-seperate pathgroup that will only be used after all the non-marginal pathgroups
-have been tried first. This prevents the possibility of IO errors occuring
+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. See "Shaky paths detection" below for more information.
+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: \fBno\fR
+The default is: \fBoff\fR
 .RE
 .
 .
@@ -1140,7 +1132,7 @@ device path.
 .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 encounted in udev rules, it is
+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
@@ -1420,7 +1412,7 @@ from later entries take precedence.
 The \fImultipath\fR subsection recognizes the following attributes:
 .TP 17
 .B wwid
-(Mandatory) World Wide Identifier. Detected multipath maps are matched agains this attribute.
+(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.
@@ -1498,6 +1490,18 @@ section:
 .SH "devices section"
 .\" ----------------------------------------------------------------------------
 .
+.TP 4
+.B Important:
+The built-in hardware device table of
+.I multipath-tools
+is created by members of the Linux community in the hope that it will be useful.
+The existence of an entry for a given storage product in the hardware table
+.B does not imply
+that the product vendor supports, or has tested, the product with
+.I multipath-tools
+in any way.
+.B Always consult the vendor\(aqs official documentation for support-related information.
+.PP
 \fImultipath-tools\fR have a built-in device table with reasonable defaults
 for more than 100 known multipath-capable storage devices. The devices section
 can be used to override these settings. If there are multiple matches for a
@@ -1547,7 +1551,7 @@ device type, instead of the default "mpa
 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 wilcard for the \fImultipathd show maps format\fR
+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
@@ -1601,8 +1605,6 @@ section:
 .TP
 .B uid_attribute
 .TP
-.B getuid_callout
-.TP
 .B path_selector
 .TP
 .B path_checker
@@ -1627,6 +1629,8 @@ section:
 .TP
 .B dev_loss_tmo
 .TP
+.B eh_deadline
+.TP
 .B flush_on_last_del
 .TP
 .B user_friendly_names
@@ -1683,8 +1687,6 @@ the values are taken from the \fIdevices
 .TP
 .B uid_attribute
 .TP
-.B getuid_callout
-.TP
 .B path_selector
 .TP
 .B path_checker
@@ -1713,6 +1715,8 @@ the values are taken from the \fIdevices
 .TP
 .B dev_loss_tmo
 .TP
+.B eh_deadline
+.TP
 .B user_friendly_names
 .TP
 .B retain_attached_hw_handler
@@ -1751,6 +1755,38 @@ the values are taken from the \fIdevices
 .RE
 .PD
 .LP
+The overrides section also recognizes the optional \fIprotocol\fR subsection,
+and can contain multiple protocol subsections. Path devices are matched against
+the protocol subsection using the mandatory \fItype\fR attribute.  Attributes
+in a matching protocol subsection take precedence over attributes in the rest
+of the overrides section. If there are multiple matching protocol subsections,
+later entries take precedence.
+.TP
+.B protocol subsection
+The protocol subsection recognizes the following mandatory attribute:
+.RS
+.TP
+.B type
+The protocol string of the path device. The possible values are \fIscsi:fcp\fR,
+\fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR,
+\fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR,
+\fIscsi:unspec\fR, \fIccw\fR, \fIcciss\fR, \fInvme\fR, and \fIundef\fR. This is
+\fBnot\fR a regular expression. the path device protocol string must match
+exactly. The protocol that a path is using can be viewed by running
+\fBmultipathd show paths format "%d %P"\fR
+.LP
+The following attributes are optional; if not set, the default values are taken
+from the \fIoverrides\fR, \fIdevices\fR, or \fIdefaults\fR section:
+.sp 1
+.PD .1v
+.RS
+.TP
+.B fast_io_fail_tmo
+.TP
+.B dev_loss_tmo
+.TP
+.B eh_deadline
+.PD
 .
 .
 .\" ----------------------------------------------------------------------------
@@ -1767,18 +1803,9 @@ The WWID is generated by four methods (i
 The WWID is derived from udev attributes by matching the device node name; cf
 \fIuid_attrs\fR above.
 .TP
-.B getuid_callout
-Use the specified external program; cf \fIgetuid_callout\fR above.
-Care should be taken when using this method; the external program
-needs to be loaded from disk for execution, which might lead to
-deadlock situations in an all-paths-down scenario.
-.TP
 .B uid_attribute
 Use the value of the specified udev attribute; cf \fIuid_attribute\fR
-above. This method is preferred to \fIgetuid_callout\fR as multipath
-does not need to call any external programs here. However, under
-certain circumstances udev might not be able to generate the requested
-variable.
+above.
 .TP
 .B sysfs
 Try to determine the WWID from sysfs attributes.
@@ -1795,7 +1822,7 @@ in most scenarios. Users who want to ena
 .SH "Shaky paths detection"
 .\" ----------------------------------------------------------------------------
 .
-A common problem in SAN setups is the occurence of intermittent errors: a
+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
@@ -1841,6 +1868,13 @@ increase and the threshold is never reac
 path checks by multipathd, which is variable and controlled by the
 \fIpolling_interval\fR and \fImax_polling_interval\fR parameters.
 .
+.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.
+.
 .RS 8
 .LP
 This method is \fBdeprecated\fR in favor of the \(dqmarginal_path\(dq failure
diff -pruN 0.8.8-1/multipath/scsi_dh.conf 0.9.0-3/multipath/scsi_dh.conf
--- 0.8.8-1/multipath/scsi_dh.conf	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/multipath/scsi_dh.conf	2022-06-10 16:47:26.000000000 +0000
@@ -0,0 +1,2 @@
+# Load SCSI device handler modules for multipath early
+# This file may be empty
diff -pruN 0.8.8-1/multipathd/dmevents.c 0.9.0-3/multipathd/dmevents.c
--- 0.8.8-1/multipathd/dmevents.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/multipathd/dmevents.c	2022-06-10 16:47:26.000000000 +0000
@@ -385,7 +385,7 @@ void *wait_dmevents (__attribute__((unus
 
 
 	if (!waiter) {
-		condlog(0, "dmevents waiter not intialized");
+		condlog(0, "dmevents waiter not initialized");
 		return NULL;
 	}
 
diff -pruN 0.8.8-1/multipathd/fpin.h 0.9.0-3/multipathd/fpin.h
--- 0.8.8-1/multipathd/fpin.h	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/multipathd/fpin.h	2022-06-10 16:47:26.000000000 +0000
@@ -0,0 +1,20 @@
+#ifndef __FPIN_H__
+#define __FPIN_H__
+
+#ifdef FPIN_EVENT_HANDLER
+void *fpin_fabric_notification_receiver(void *unused);
+void *fpin_els_li_consumer(void *data);
+void fpin_clean_marginal_dev_list(__attribute__((unused)) void *arg);
+#else
+static void *fpin_fabric_notification_receiver(__attribute__((unused))void *unused)
+{
+	return NULL;
+}
+static void *fpin_els_li_consumer(__attribute__((unused))void *data)
+{
+	return NULL;
+}
+/* fpin_clean_marginal_dev_list() is never called */
+#endif
+
+#endif
diff -pruN 0.8.8-1/multipathd/fpin_handlers.c 0.9.0-3/multipathd/fpin_handlers.c
--- 0.8.8-1/multipathd/fpin_handlers.c	1970-01-01 00:00:00.000000000 +0000
+++ 0.9.0-3/multipathd/fpin_handlers.c	2022-06-10 16:47:26.000000000 +0000
@@ -0,0 +1,541 @@
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <libudev.h>
+#include <scsi/scsi_netlink_fc.h>
+#include <scsi/fc/fc_els.h>
+
+#include "parser.h"
+#include "vector.h"
+#include "structs.h"
+#include "structs_vec.h"
+#include "main.h"
+#include "debug.h"
+#include "util.h"
+#include "sysfs.h"
+
+#include "fpin.h"
+#include "devmapper.h"
+
+static pthread_cond_t fpin_li_cond = PTHREAD_COND_INITIALIZER;
+static pthread_mutex_t fpin_li_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t fpin_li_marginal_dev_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+static LIST_HEAD(els_marginal_list_head);
+static LIST_HEAD(fpin_li_marginal_dev_list_head);
+
+
+#define DEF_RX_BUF_SIZE	4096
+#define DEV_NAME_LEN	128
+#define FCH_EVT_LINKUP 0x2
+#define FCH_EVT_LINK_FPIN 0x501
+#define FCH_EVT_RSCN 0x5
+
+#define list_first_entry(ptr, type, member) \
+	list_entry((ptr)->next, type, member)
+
+/* max ELS frame Size */
+#define FC_PAYLOAD_MAXLEN   2048
+
+struct els_marginal_list {
+	uint32_t event_code;
+	uint16_t host_num;
+	uint16_t length;
+	char payload[FC_PAYLOAD_MAXLEN];
+	struct list_head node;
+};
+/* Structure to store the marginal devices info */
+struct marginal_dev_list {
+	char dev_t[BLK_DEV_SIZE];
+	uint32_t host_num;
+	struct list_head node;
+};
+
+static void _udev_device_unref(void *p)
+{
+	udev_device_unref(p);
+}
+
+
+/*set/unset the path state to marginal*/
+static int 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);
+	pp->marginal = set;
+	pp->mpp->fpin_must_reload = true;
+	return 0;
+}
+
+/* This will unset marginal state of a device*/
+static void fpin_path_unsetmarginal(char *devname, struct vectors *vecs)
+{
+	struct path *pp;
+
+	pp = find_path_by_dev(vecs->pathvec, devname);
+	if (!pp)
+		pp = find_path_by_devt(vecs->pathvec, devname);
+
+	fpin_set_pathstate(pp, false);
+}
+
+/*This will set the marginal state of a device*/
+static int fpin_path_setmarginal(struct path *pp)
+{
+	return fpin_set_pathstate(pp, true);
+}
+
+/* Unsets all the devices in the list from marginal state */
+static void
+fpin_unset_marginal_dev(uint32_t host_num, struct vectors *vecs)
+{
+	struct marginal_dev_list *tmp_marg = NULL;
+	struct marginal_dev_list *marg = NULL;
+	struct multipath *mpp;
+	int ret = 0;
+	int i;
+
+	pthread_cleanup_push(cleanup_lock, &vecs->lock);
+	lock(&vecs->lock);
+	pthread_testcancel();
+
+	pthread_mutex_lock(&fpin_li_marginal_dev_mutex);
+	pthread_cleanup_push(cleanup_mutex, &fpin_li_marginal_dev_mutex);
+	pthread_testcancel();
+	if (list_empty(&fpin_li_marginal_dev_list_head)) {
+		condlog(4, "Marginal List is empty\n");
+		goto empty;
+	}
+	list_for_each_entry_safe(marg, tmp_marg, &fpin_li_marginal_dev_list_head, node) {
+		if (marg->host_num != host_num)
+			continue;
+		condlog(4, " unsetting marginal dev: is %s %d\n",
+				tmp_marg->dev_t, tmp_marg->host_num);
+		fpin_path_unsetmarginal(marg->dev_t, vecs);
+		list_del(&marg->node);
+		free(marg);
+	}
+empty:
+	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);
+			if (ret == 2)
+				condlog(2, "map removed during reload");
+			else
+				mpp->fpin_must_reload = false;
+		}
+	}
+	pthread_cleanup_pop(1);
+}
+
+/*
+ * On Receiving the frame from HBA driver, insert the frame into link
+ * integrity frame list which will be picked up later by consumer thread for
+ * processing.
+ */
+static int
+fpin_els_add_li_frame(struct fc_nl_event *fc_event)
+{
+	struct els_marginal_list *els_mrg = NULL;
+	int ret = 0;
+
+	if (fc_event->event_datalen > FC_PAYLOAD_MAXLEN)
+		return -EINVAL;
+
+	pthread_mutex_lock(&fpin_li_mutex);
+	pthread_cleanup_push(cleanup_mutex, &fpin_li_mutex);
+	pthread_testcancel();
+	els_mrg = calloc(1, sizeof(struct els_marginal_list));
+	if (els_mrg != NULL) {
+		els_mrg->host_num = fc_event->host_no;
+		els_mrg->event_code = fc_event->event_code;
+		els_mrg->length = fc_event->event_datalen;
+		memcpy(els_mrg->payload, &(fc_event->event_data), fc_event->event_datalen);
+		list_add_tail(&els_mrg->node, &els_marginal_list_head);
+		pthread_cond_signal(&fpin_li_cond);
+	} else
+		ret = -ENOMEM;
+	pthread_cleanup_pop(1);
+	return ret;
+
+}
+
+/*Sets the rport port_state to marginal*/
+static void fpin_set_rport_marginal(struct udev_device *rport_dev)
+{
+	sysfs_attr_set_value(rport_dev, "port_state",
+				"Marginal", strlen("Marginal"));
+}
+
+/*Add the marginal devices info into the list*/
+static void
+fpin_add_marginal_dev_info(uint32_t host_num, char *devname)
+{
+	struct marginal_dev_list *newdev = NULL;
+
+	newdev = calloc(1, sizeof(struct marginal_dev_list));
+	if (newdev != NULL) {
+		newdev->host_num = host_num;
+		strlcpy(newdev->dev_t, devname, BLK_DEV_SIZE);
+		condlog(4, "\n%s hostno %d devname %s\n", __func__,
+				host_num, newdev->dev_t);
+		pthread_mutex_lock(&fpin_li_marginal_dev_mutex);
+		list_add_tail(&(newdev->node),
+				&fpin_li_marginal_dev_list_head);
+		pthread_mutex_unlock(&fpin_li_marginal_dev_mutex);
+	}
+}
+
+/*
+ * 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
+ * Marginal
+ */
+static int  fpin_chk_wwn_setpath_marginal(uint16_t host_num,  struct vectors *vecs,
+		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;
+
+	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->sg_id.proto_id != SCSI_PROTOCOL_FCP || host_num !=  pp->sg_id.host_no)
+			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);
+		}
+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);
+			if (ret == 2)
+				condlog(2, "map removed during reload");
+			else
+				mpp->fpin_must_reload = false;
+		}
+	}
+	pthread_cleanup_pop(1);
+	return ret;
+}
+
+/*
+ * This function loops around all the impacted wwns received as part of els
+ * frame and sets the associated path and port states to marginal.
+ */
+static int
+fpin_parse_li_els_setpath_marginal(uint16_t host_num, struct fc_tlv_desc *tlv,
+		struct vectors *vecs)
+{
+	uint32_t wwn_count = 0, iter = 0;
+	uint64_t wwpn;
+	struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv;
+	int count = 0;
+	int ret = 0;
+
+	/* Update the wwn to list */
+	wwn_count = be32_to_cpu(li_desc->pname_count);
+	condlog(4, "Got wwn count as %d\n", wwn_count);
+
+	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);
+		if (ret < 0)
+			condlog(2, "failed to set the path marginal associated with wwpn: 0x%" PRIx64 "\n", wwpn);
+
+		count++;
+	}
+	return count;
+}
+
+/*
+ * This function process the ELS frame received from HBA driver,
+ * and sets the path associated with the port wwn to marginal
+ * and also set the port state to marginal.
+ */
+static int
+fpin_process_els_frame(uint16_t host_num, char *fc_payload, struct vectors *vecs)
+{
+
+	int count = -1;
+	struct fc_els_fpin *fpin = (struct fc_els_fpin *)fc_payload;
+	struct fc_tlv_desc *tlv;
+
+	tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
+
+	/*
+	 * Parse the els frame and set the affected paths and port
+	 * state to marginal
+	 */
+	count = fpin_parse_li_els_setpath_marginal(host_num, tlv, vecs);
+	if (count <= 0)
+		condlog(4, "Could not find any WWNs, ret = %d\n",
+					count);
+	return count;
+}
+
+/*
+ * This function process the FPIN ELS frame received from HBA driver,
+ * and push the frame to appropriate frame list. Currently we have only FPIN
+ * LI frame list.
+ */
+static int
+fpin_handle_els_frame(struct fc_nl_event *fc_event)
+{
+	int ret = -1;
+	uint32_t els_cmd;
+	struct fc_els_fpin *fpin = (struct fc_els_fpin *)&fc_event->event_data;
+	struct fc_tlv_desc *tlv;
+	uint32_t dtag;
+
+	els_cmd = (uint32_t)fc_event->event_data;
+	tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0];
+	dtag = be32_to_cpu(tlv->desc_tag);
+	condlog(4, "Got CMD in add as 0x%x fpin_cmd 0x%x dtag 0x%x\n",
+			els_cmd, fpin->fpin_cmd, dtag);
+
+	if ((fc_event->event_code == FCH_EVT_LINK_FPIN) ||
+			(fc_event->event_code == FCH_EVT_LINKUP) ||
+			(fc_event->event_code == FCH_EVT_RSCN)) {
+
+		if (els_cmd == ELS_FPIN) {
+			/*
+			 * Check the type of fpin by checking the tag info
+			 * At present we are supporting only LI events
+			 */
+			if (dtag == ELS_DTAG_LNK_INTEGRITY) {
+				/*Push the Payload to FPIN frame queue. */
+				ret = fpin_els_add_li_frame(fc_event);
+				if (ret != 0)
+					condlog(0, "Failed to process LI frame with error %d\n",
+							ret);
+			} else {
+				condlog(4, "Unsupported FPIN received 0x%x\n", dtag);
+				return ret;
+			}
+		} else {
+			/*Push the Payload to FPIN frame queue. */
+			ret = fpin_els_add_li_frame(fc_event);
+			if (ret != 0)
+				condlog(0, "Failed to process Linkup/RSCN event with error %d evnt %d\n",
+						ret, fc_event->event_code);
+		}
+	} else
+		condlog(4, "Invalid command received: 0x%x\n", els_cmd);
+	return ret;
+}
+
+/*cleans the global marginal dev list*/
+void fpin_clean_marginal_dev_list(__attribute__((unused)) void *arg)
+{
+	struct marginal_dev_list *tmp_marg = NULL;
+
+	pthread_mutex_lock(&fpin_li_marginal_dev_mutex);
+	while (!list_empty(&fpin_li_marginal_dev_list_head)) {
+		tmp_marg  = list_first_entry(&fpin_li_marginal_dev_list_head,
+				struct marginal_dev_list, node);
+		list_del(&tmp_marg->node);
+		free(tmp_marg);
+	}
+	pthread_mutex_unlock(&fpin_li_marginal_dev_mutex);
+}
+
+/* Cleans the global els  marginal list */
+static void fpin_clean_els_marginal_list(void *arg)
+{
+	struct list_head *head = (struct list_head *)arg;
+	struct els_marginal_list *els_marg;
+
+	while (!list_empty(head)) {
+		els_marg  = list_first_entry(head, struct els_marginal_list,
+					     node);
+		list_del(&els_marg->node);
+		free(els_marg);
+	}
+}
+
+static void rcu_unregister(__attribute__((unused)) void *param)
+{
+	rcu_unregister_thread();
+}
+/*
+ * This is the FPIN ELS consumer thread. The thread sleeps on pthread cond
+ * variable unless notified by fpin_fabric_notification_receiver thread.
+ * This thread is only to process FPIN-LI ELS frames. A new thread and frame
+ * list will be added if any more ELS frames types are to be supported.
+ */
+void *fpin_els_li_consumer(void *data)
+{
+	struct list_head marginal_list_head;
+	int ret = 0;
+	uint16_t host_num;
+	struct els_marginal_list *els_marg;
+	uint32_t event_code;
+	struct vectors *vecs = (struct vectors *)data;
+
+	pthread_cleanup_push(rcu_unregister, NULL);
+	rcu_register_thread();
+	pthread_cleanup_push(fpin_clean_marginal_dev_list, NULL);
+	INIT_LIST_HEAD(&marginal_list_head);
+	pthread_cleanup_push(fpin_clean_els_marginal_list,
+				(void *)&marginal_list_head);
+	for ( ; ; ) {
+		pthread_mutex_lock(&fpin_li_mutex);
+		pthread_cleanup_push(cleanup_mutex, &fpin_li_mutex);
+		pthread_testcancel();
+		while (list_empty(&els_marginal_list_head))
+			pthread_cond_wait(&fpin_li_cond, &fpin_li_mutex);
+
+		if (!list_empty(&els_marginal_list_head)) {
+			condlog(4, "Invoke List splice tail\n");
+			list_splice_tail_init(&els_marginal_list_head, &marginal_list_head);
+		}
+		pthread_cleanup_pop(1);
+
+		while (!list_empty(&marginal_list_head)) {
+			els_marg  = list_first_entry(&marginal_list_head,
+							struct els_marginal_list, node);
+			host_num = els_marg->host_num;
+			event_code = els_marg->event_code;
+			/* Now finally process FPIN LI ELS Frame */
+			condlog(4, "Got a new Payload buffer, processing it\n");
+			if ((event_code ==  FCH_EVT_LINKUP) || (event_code == FCH_EVT_RSCN))
+				 fpin_unset_marginal_dev(host_num, vecs);
+			else {
+				ret = fpin_process_els_frame(host_num, els_marg->payload, vecs);
+				if (ret <= 0)
+					condlog(0, "ELS frame processing failed with ret %d\n", ret);
+			}
+			list_del(&els_marg->node);
+			free(els_marg);
+
+		}
+	}
+
+	pthread_cleanup_pop(1);
+	pthread_cleanup_pop(1);
+	pthread_cleanup_pop(1);
+	return NULL;
+}
+
+static void receiver_cleanup_list(__attribute__((unused)) void *arg)
+{
+	pthread_mutex_lock(&fpin_li_mutex);
+	fpin_clean_els_marginal_list(&els_marginal_list_head);
+	pthread_mutex_unlock(&fpin_li_mutex);
+}
+
+/*
+ * Listen for ELS frames from driver. on receiving the frame payload,
+ * push the payload to a list, and notify the fpin_els_li_consumer thread to
+ * process it. Once consumer thread is notified, return to listen for more ELS
+ * frames from driver.
+ */
+void *fpin_fabric_notification_receiver(__attribute__((unused))void *unused)
+{
+	int ret;
+	long fd;
+	uint32_t els_cmd;
+	struct fc_nl_event *fc_event = NULL;
+	struct sockaddr_nl fc_local;
+	unsigned char buf[DEF_RX_BUF_SIZE] __attribute__((aligned(sizeof(uint64_t))));
+	size_t plen = 0;
+
+	pthread_cleanup_push(rcu_unregister, NULL);
+	rcu_register_thread();
+
+	pthread_cleanup_push(receiver_cleanup_list, NULL);
+	fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_SCSITRANSPORT);
+	if (fd < 0) {
+		condlog(0, "fc socket error %ld", fd);
+		return NULL;
+	}
+
+	pthread_cleanup_push(close_fd, (void *)fd);
+	memset(&fc_local, 0, sizeof(fc_local));
+	fc_local.nl_family = AF_NETLINK;
+	fc_local.nl_groups = ~0;
+	fc_local.nl_pid = getpid();
+	ret = bind(fd, (struct sockaddr *)&fc_local, sizeof(fc_local));
+	if (ret == -1) {
+		condlog(0, "fc socket bind error %d\n", ret);
+		goto out;
+	}
+	for ( ; ; ) {
+		condlog(4, "Waiting for ELS...\n");
+		ret = read(fd, buf, DEF_RX_BUF_SIZE);
+		if (ret < 0) {
+			condlog(0, "failed to read the els frame (%d)", ret);
+			continue;
+		}
+		condlog(4, "Got a new request %d\n", ret);
+		if (!NLMSG_OK((struct nlmsghdr *)buf, (unsigned int)ret)) {
+			condlog(0, "bad els frame read (%d)", ret);
+			continue;
+		}
+		/* Push the frame to appropriate frame list */
+		plen = NLMSG_PAYLOAD((struct nlmsghdr *)buf, 0);
+		fc_event = (struct fc_nl_event *)NLMSG_DATA(buf);
+		if (plen < sizeof(*fc_event)) {
+			condlog(0, "too short (%d) to be an FC event", ret);
+			continue;
+		}
+		els_cmd = (uint32_t)fc_event->event_data;
+		condlog(4, "Got host no as %d, event 0x%x, len %d evntnum %d evntcode %d\n",
+				fc_event->host_no, els_cmd, fc_event->event_datalen,
+				fc_event->event_num, fc_event->event_code);
+		fpin_handle_els_frame(fc_event);
+	}
+out:
+	pthread_cleanup_pop(1);
+	pthread_cleanup_pop(1);
+	pthread_cleanup_pop(1);
+	return NULL;
+}
diff -pruN 0.8.8-1/multipathd/main.c 0.9.0-3/multipathd/main.c
--- 0.8.8-1/multipathd/main.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/multipathd/main.c	2022-06-10 16:47:26.000000000 +0000
@@ -16,6 +16,7 @@
 #include <linux/oom.h>
 #include <libudev.h>
 #include <urcu.h>
+#include "fpin.h"
 #ifdef USE_SYSTEMD
 #include <systemd/sd-daemon.h>
 #endif
@@ -36,6 +37,7 @@
 /*
  * libmultipath
  */
+#include "version.h"
 #include "parser.h"
 #include "vector.h"
 #include "config.h"
@@ -59,13 +61,13 @@
 #include "prio.h"
 #include "wwids.h"
 #include "pgpolicies.h"
-#include "uevent.h"
 #include "log.h"
 #include "uxsock.h"
 #include "alias.h"
 
 #include "mpath_cmd.h"
 #include "mpath_persist.h"
+#include "mpath_persist_int.h"
 
 #include "prioritizers/alua_rtpg.h"
 
@@ -79,15 +81,16 @@
 #include "waiter.h"
 #include "dmevents.h"
 #include "io_err_stat.h"
-#include "wwids.h"
 #include "foreign.h"
 #include "../third-party/valgrind/drd.h"
 #include "init_unwinder.h"
 
-#define FILE_NAME_SIZE 256
 #define CMDSIZE 160
 #define MSG_SIZE 32
 
+int mpath_pr_event_handle(struct path *pp);
+void * mpath_pr_event_handler_fn (void * );
+
 #define LOG_MSG(lvl, pp)					\
 do {								\
 	if (pp->mpp && checker_selected(&pp->checker) &&	\
@@ -125,15 +128,17 @@ static int poll_dmevents = 0;
 static int poll_dmevents = 1;
 #endif
 /* Don't access this variable without holding config_lock */
-static volatile enum daemon_status running_state = DAEMON_INIT;
+static enum daemon_status running_state = DAEMON_INIT;
 /* Don't access this variable without holding config_lock */
 static bool __delayed_reconfig;
 pid_t daemon_pid;
 static pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_cond_t config_cond;
-static pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr;
+static pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr,
+	fpin_thr, fpin_consumer_thr;
 static bool check_thr_started, uevent_thr_started, uxlsnr_thr_started,
-	uevq_thr_started, dmevent_thr_started;
+	uevq_thr_started, dmevent_thr_started, fpin_thr_started,
+	fpin_consumer_thr_started;
 static int pid_fd = -1;
 
 static inline enum daemon_status get_running_state(void)
@@ -151,16 +156,6 @@ int should_exit(void)
 	return get_running_state() == DAEMON_SHUTDOWN;
 }
 
-static bool get_delayed_reconfig(void)
-{
-	bool val;
-
-	pthread_mutex_lock(&config_lock);
-	val = __delayed_reconfig;
-	pthread_mutex_unlock(&config_lock);
-	return val;
-}
-
 /*
  * global copy of vecs for use in sig handlers
  */
@@ -283,17 +278,6 @@ enum daemon_status wait_for_state_change
 
 /* Don't access this variable without holding config_lock */
 static enum force_reload_types reconfigure_pending = FORCE_RELOAD_NONE;
-/* Only set while changing to DAEMON_CONFIGURE, and only access while
- * reconfiguring or scheduling a delayed reconfig in DAEMON_CONFIGURE */
-static volatile enum force_reload_types reload_type = FORCE_RELOAD_NONE;
-
-static void enable_delayed_reconfig(void)
-{
-	pthread_mutex_lock(&config_lock);
-	reconfigure_pending = reload_type;
-	__delayed_reconfig = true;
-	pthread_mutex_unlock(&config_lock);
-}
 
 /* must be called with config_lock held */
 static void __post_config_state(enum daemon_status state)
@@ -301,33 +285,11 @@ static void __post_config_state(enum dae
 	if (state != running_state && running_state != DAEMON_SHUTDOWN) {
 		enum daemon_status old_state = running_state;
 
-		/*
-		 * Handle a pending reconfigure request.
-		 * DAEMON_IDLE is set from child() after reconfigure(),
-		 * or from checkerloop() after completing checkers.
-		 * In either case, child() will see DAEMON_CONFIGURE
-		 * again and start another reconfigure cycle.
-		 */
-		if (reconfigure_pending != FORCE_RELOAD_NONE &&
-		    state == DAEMON_IDLE &&
-		    (old_state == DAEMON_CONFIGURE ||
-		     old_state == DAEMON_RUNNING)) {
-			/*
-			 * notify systemd of transient idle state, lest systemd
-			 * thinks the reload lasts forever.
-			 */
-			do_sd_notify(old_state, DAEMON_IDLE);
-			old_state = DAEMON_IDLE;
-			state = DAEMON_CONFIGURE;
-		}
-		if (state == DAEMON_CONFIGURE) {
-			reload_type = (reconfigure_pending == FORCE_RELOAD_YES) ? FORCE_RELOAD_YES : FORCE_RELOAD_WEAK;
-			reconfigure_pending = FORCE_RELOAD_NONE;
-			__delayed_reconfig = false;
-		}
 		running_state = state;
 		pthread_cond_broadcast(&config_cond);
 		do_sd_notify(old_state, state);
+		condlog(4, "daemon state %s -> %s",
+			daemon_status_msg[old_state], daemon_status_msg[state]);
 	}
 }
 
@@ -339,6 +301,38 @@ void post_config_state(enum daemon_statu
 	pthread_cleanup_pop(1);
 }
 
+static bool unblock_reconfigure(void)
+{
+	bool was_delayed;
+
+	pthread_mutex_lock(&config_lock);
+	was_delayed = __delayed_reconfig;
+	if (was_delayed) {
+		__delayed_reconfig = false;
+		/*
+		 * In IDLE state, make sure child() is woken up
+		 * Otherwise it will wake up when state switches to IDLE
+		 */
+		if (running_state == DAEMON_IDLE)
+			__post_config_state(DAEMON_CONFIGURE);
+	}
+	pthread_mutex_unlock(&config_lock);
+	if (was_delayed)
+		condlog(3, "unblocked delayed reconfigure");
+	return was_delayed;
+}
+
+/*
+ * Make sure child() is woken up when a map is removed that multipathd
+ * is currently waiting for.
+ * Overrides libmultipath's weak symbol by the same name
+ */
+void remove_map_callback(struct multipath *mpp)
+{
+	if (mpp->wait_for_udev > 0)
+		unblock_reconfigure();
+}
+
 void schedule_reconfigure(enum force_reload_types requested_type)
 {
 	pthread_mutex_lock(&config_lock);
@@ -818,14 +812,12 @@ ev_add_map (char * dev, const char * ali
 		conf = get_multipath_config();
 		reassign_maps = conf->reassign_maps;
 		put_multipath_config(conf);
+		dm_get_info(mpp->alias, &mpp->dmi);
 		if (mpp->wait_for_udev) {
 			mpp->wait_for_udev = 0;
-			if (get_delayed_reconfig() &&
-			    !need_to_delay_reconfig(vecs)) {
-				condlog(2, "reconfigure (delayed)");
-				schedule_reconfigure(FORCE_RELOAD_WEAK);
+			if (!need_to_delay_reconfig(vecs) &&
+			    unblock_reconfigure())
 				return 0;
-			}
 		}
 		/*
 		 * Not really an error -- we generate our own uevent
@@ -908,7 +900,7 @@ ev_remove_map (char * devname, char * al
 	}
 	if (strcmp(mpp->alias, alias)) {
 		condlog(2, "%s: minor number mismatch (map %d, event %d)",
-			mpp->alias, mpp->dmi->minor, minor);
+			mpp->alias, mpp->dmi.minor, minor);
 		return 1;
 	}
 	return flush_map(mpp, vecs, 0);
@@ -1125,6 +1117,28 @@ out:
 	return ret;
 }
 
+static int
+sysfs_get_ro (struct path *pp)
+{
+	int ro;
+	char buff[3]; /* Either "0\n\0" or "1\n\0" */
+
+	if (!pp->udev)
+		return -1;
+
+	if (sysfs_attr_get_value(pp->udev, "ro", buff, sizeof(buff)) <= 0) {
+		condlog(3, "%s: Cannot read ro attribute in sysfs", pp->dev);
+		return -1;
+	}
+
+	if (sscanf(buff, "%d\n", &ro) != 1 || ro < 0 || ro > 1) {
+		condlog(3, "%s: Cannot parse ro attribute", pp->dev);
+		return -1;
+	}
+
+	return ro;
+}
+
 /*
  * returns:
  * 0: added
@@ -1138,6 +1152,7 @@ ev_add_path (struct path * pp, struct ve
 	int retries = 3;
 	int start_waiter = 0;
 	int ret;
+	int ro;
 
 	/*
 	 * need path UID to go any further
@@ -1155,6 +1170,8 @@ ev_add_path (struct path * pp, struct ve
 		free_path(pp);
 		return 1;
 	}
+	if (mpp)
+		trigger_path_udev_change(pp, true);
 	if (mpp && mpp->wait_for_udev &&
 	    (pathcount(mpp, PATH_UP) > 0 ||
 	     (pathcount(mpp, PATH_GHOST) > 0 &&
@@ -1200,6 +1217,11 @@ rescan:
 	/* persistent reservation check*/
 	mpath_pr_event_handle(pp);
 
+	/* ro check - if new path is ro, force map to be ro as well */
+	ro = sysfs_get_ro(pp);
+	if (ro == 1)
+		mpp->force_readonly = 1;
+
 	if (!need_do_map)
 		return 0;
 
@@ -1439,6 +1461,30 @@ finish_path_init(struct path *pp, struct
 	return -1;
 }
 
+static bool
+needs_ro_update(struct multipath *mpp, int ro)
+{
+	struct pathgroup * pgp;
+	struct path * pp;
+	unsigned int i, j;
+
+	if (!mpp || ro < 0)
+		return false;
+	if (!has_dm_info(mpp))
+		return true;
+	if (mpp->dmi.read_only == ro)
+		return false;
+	if (ro == 1)
+		return true;
+	vector_foreach_slot (mpp->pg, pgp, i) {
+		vector_foreach_slot (pgp->paths, pp, j) {
+			if (sysfs_get_ro(pp) == 1)
+				return false;
+		}
+	}
+	return true;
+}
+
 static int
 uev_update_path (struct uevent *uev, struct vectors * vecs)
 {
@@ -1511,7 +1557,7 @@ uev_update_path (struct uevent *uev, str
 		}
 
 		ro = uevent_get_disk_ro(uev);
-		if (mpp && ro >= 0) {
+		if (needs_ro_update(mpp, ro)) {
 			condlog(2, "%s: update path write_protect to '%d' (uevent)", uev->kernel, ro);
 
 			if (mpp->wait_for_udev)
@@ -1549,7 +1595,7 @@ out:
 
 		condlog(0, "%s: spurious uevent, path not found", uev->kernel);
 	}
-	/* pp->initalized must not be INIT_PARTIAL if needs_reinit is set */
+	/* pp->initialized must not be INIT_PARTIAL if needs_reinit is set */
 	if (needs_reinit)
 		retval = uev_add_path(uev, vecs, 1);
 	return retval;
@@ -1741,7 +1787,7 @@ uxlsnrloop (void * ap)
 
 	/*
 	 * Wait for initial reconfiguration to finish, while
-	 * hadling signals
+	 * handling signals
 	 */
 	while (wait_for_state_change_if(DAEMON_CONFIGURE, 50)
 	       == DAEMON_CONFIGURE)
@@ -1881,11 +1927,8 @@ missing_uev_wait_tick(struct vectors *ve
 		}
 	}
 
-	if (timed_out && get_delayed_reconfig() &&
-	    !need_to_delay_reconfig(vecs)) {
-		condlog(2, "reconfigure (delayed)");
-		schedule_reconfigure(FORCE_RELOAD_WEAK);
-	}
+	if (timed_out && !need_to_delay_reconfig(vecs))
+		unblock_reconfigure();
 }
 
 static void
@@ -2057,7 +2100,7 @@ static int check_path_reinstate_state(st
 	/*
 	 * This function is only called when the path state changes
 	 * from "bad" to "good". pp->state reflects the *previous* state.
-	 * If this was "bad", we know that a failure must have occured
+	 * If this was "bad", we know that a failure must have occurred
 	 * beforehand, and count that.
 	 * Note that we count path state _changes_ this way. If a path
 	 * remains in "bad" state, failure count is not increased.
@@ -2227,7 +2270,7 @@ check_path (struct vectors * vecs, struc
 
 	/*
 	 * provision a next check soonest,
-	 * in case we exit abnormaly from here
+	 * in case we exit abnormally from here
 	 */
 	pp->tick = checkint;
 
@@ -2524,7 +2567,6 @@ checkerloop (void *ap)
 	rcu_register_thread();
 	mlockall(MCL_CURRENT | MCL_FUTURE);
 	vecs = (struct vectors *)ap;
-	condlog(2, "path checkers start up");
 
 	/* Tweak start time for initial path check */
 	get_monotonic_time(&last_time);
@@ -2661,8 +2703,8 @@ checkerloop (void *ap)
 	return NULL;
 }
 
-int
-configure (struct vectors * vecs)
+static int
+configure (struct vectors * vecs, enum force_reload_types reload_type)
 {
 	struct multipath * mpp;
 	struct path * pp;
@@ -2793,8 +2835,56 @@ void rcu_free_config(struct rcu_head *he
 	free_config(conf);
 }
 
-int
-reconfigure (struct vectors * vecs)
+static bool reconfigure_check_uid_attrs(const struct _vector *old_attrs,
+					const struct _vector *new_attrs)
+{
+	int i;
+	char *old;
+
+	if (VECTOR_SIZE(old_attrs) != VECTOR_SIZE(new_attrs))
+		return true;
+
+	vector_foreach_slot(old_attrs, old, i) {
+		char *new = VECTOR_SLOT(new_attrs, i);
+
+		if (strcmp(old, new))
+			return true;
+	}
+
+	return false;
+}
+
+static void reconfigure_check(struct config *old, struct config *new)
+{
+	int old_marginal_pathgroups;
+
+	old_marginal_pathgroups = old->marginal_pathgroups;
+	if ((old_marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) !=
+	    (new->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) {
+		condlog(1, "multipathd must be restarted to turn %s fpin marginal paths",
+			(old_marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)?
+			"off" : "on");
+		new->marginal_pathgroups = old_marginal_pathgroups;
+	}
+
+	if (reconfigure_check_uid_attrs(&old->uid_attrs, &new->uid_attrs)) {
+		int i;
+		void *ptr;
+
+		condlog(1, "multipathd must be restarted to change uid_attrs, keeping old values");
+		vector_foreach_slot(&new->uid_attrs, ptr, i)
+			free(ptr);
+		vector_reset(&new->uid_attrs);
+		new->uid_attrs = old->uid_attrs;
+
+		/* avoid uid_attrs being freed in rcu_free_config() */
+		old->uid_attrs.allocated = 0;
+		old->uid_attrs.slot = NULL;
+	}
+}
+
+static int
+reconfigure (struct vectors *vecs, enum force_reload_types reload_type)
 {
 	struct config * old, *conf;
 
@@ -2805,6 +2895,7 @@ reconfigure (struct vectors * vecs)
 	if (verbosity)
 		libmp_verbosity = verbosity;
 	setlogmask(LOG_UPTO(libmp_verbosity + 3));
+	condlog(2, "%s: setting up paths and maps", __func__);
 
 	/*
 	 * free old map and path vectors ... they use old conf state
@@ -2826,12 +2917,15 @@ reconfigure (struct vectors * vecs)
 	uxsock_timeout = conf->uxsock_timeout;
 
 	old = rcu_dereference(multipath_conf);
+	reconfigure_check(old, conf);
+
 	conf->sequence_nr = old->sequence_nr + 1;
 	rcu_assign_pointer(multipath_conf, conf);
 	call_rcu(&old->rcu, rcu_free_config);
-
-	configure(vecs);
-
+#ifdef FPIN_EVENT_HANDLER
+	fpin_clean_marginal_dev_list(NULL);
+#endif
+	configure(vecs, reload_type);
 
 	return 0;
 }
@@ -2874,18 +2968,18 @@ void
 handle_signals(bool nonfatal)
 {
 	if (exit_sig) {
-		condlog(2, "exit (signal)");
+		condlog(3, "exit (signal)");
 		exit_sig = 0;
 		exit_daemon();
 	}
 	if (!nonfatal)
 		return;
 	if (reconfig_sig) {
-		condlog(2, "reconfigure (signal)");
+		condlog(3, "reconfigure (signal)");
 		schedule_reconfigure(FORCE_RELOAD_WEAK);
 	}
 	if (log_reset_sig) {
-		condlog(2, "reset log (signal)");
+		condlog(3, "reset log (signal)");
 		if (logsink == LOGSINK_SYSLOG)
 			log_thread_reset();
 	}
@@ -3048,6 +3142,11 @@ static void cleanup_threads(void)
 		pthread_cancel(uevq_thr);
 	if (dmevent_thr_started)
 		pthread_cancel(dmevent_thr);
+	if (fpin_thr_started)
+		pthread_cancel(fpin_thr);
+	if (fpin_consumer_thr_started)
+		pthread_cancel(fpin_consumer_thr);
+
 
 	if (check_thr_started)
 		pthread_join(check_thr, NULL);
@@ -3059,6 +3158,11 @@ static void cleanup_threads(void)
 		pthread_join(uevq_thr, NULL);
 	if (dmevent_thr_started)
 		pthread_join(dmevent_thr, NULL);
+	if (fpin_thr_started)
+		pthread_join(fpin_thr, NULL);
+	if (fpin_consumer_thr_started)
+		pthread_join(fpin_consumer_thr, NULL);
+
 
 	/*
 	 * As all threads are joined now, and we're in DAEMON_SHUTDOWN
@@ -3152,6 +3256,7 @@ child (__attribute__((unused)) void *par
 	char *envp;
 	enum daemon_status state;
 	int exit_code = 1;
+	int fpin_marginal_paths = 0;
 
 	init_unwinder();
 	mlockall(MCL_CURRENT | MCL_FUTURE);
@@ -3183,8 +3288,9 @@ child (__attribute__((unused)) void *par
 
 	post_config_state(DAEMON_START);
 
-	condlog(2, "--------start up--------");
-	condlog(2, "read " DEFAULT_CONFIGFILE);
+	condlog(2, "multipathd v%d.%d.%d%s: start up",
+		MULTIPATH_VERSION(VERSION_CODE), EXTRAVERSION);
+	condlog(3, "read " DEFAULT_CONFIGFILE);
 
 	if (verbosity)
 		libmp_verbosity = verbosity;
@@ -3202,17 +3308,17 @@ child (__attribute__((unused)) void *par
 		conf->bindings_read_only = bindings_read_only;
 	uxsock_timeout = conf->uxsock_timeout;
 	rcu_assign_pointer(multipath_conf, conf);
-	if (init_checkers(conf->multipath_dir)) {
+	if (init_checkers()) {
 		condlog(0, "failed to initialize checkers");
 		goto failed;
 	}
-	if (init_prio(conf->multipath_dir)) {
+	if (init_prio()) {
 		condlog(0, "failed to initialize prioritizers");
 		goto failed;
 	}
 	/* Failing this is non-fatal */
 
-	init_foreign(conf->multipath_dir, conf->enable_foreign);
+	init_foreign(conf->enable_foreign);
 
 	if (poll_dmevents)
 		poll_dmevents = dmevent_poll_supported();
@@ -3230,7 +3336,10 @@ child (__attribute__((unused)) void *par
 
 	setscheduler();
 	set_oom_adj();
-
+#ifdef FPIN_EVENT_HANDLER
+	if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)
+		fpin_marginal_paths = 1;
+#endif
 	/*
 	 * Startup done, invalidate configuration
 	 */
@@ -3239,11 +3348,10 @@ child (__attribute__((unused)) void *par
 	pthread_cleanup_push(config_cleanup, NULL);
 	pthread_mutex_lock(&config_lock);
 
-	__post_config_state(DAEMON_IDLE);
 	rc = pthread_create(&uxlsnr_thr, &misc_attr, uxlsnrloop, vecs);
 	if (!rc) {
 		/* Wait for uxlsnr startup */
-		while (running_state == DAEMON_IDLE)
+		while (running_state == DAEMON_START)
 			pthread_cond_wait(&config_cond, &config_lock);
 		state = running_state;
 	}
@@ -3298,41 +3406,83 @@ child (__attribute__((unused)) void *par
 		goto failed;
 	} else
 		uevq_thr_started = true;
+
+	if (fpin_marginal_paths) {
+		if ((rc = pthread_create(&fpin_thr, &misc_attr,
+			fpin_fabric_notification_receiver, NULL))) {
+			condlog(0, "failed to create the fpin receiver thread: %d", rc);
+			goto failed;
+		} else
+			fpin_thr_started = true;
+
+		if ((rc = pthread_create(&fpin_consumer_thr,
+			&misc_attr, fpin_els_li_consumer, vecs))) {
+			condlog(0, "failed to create the fpin consumer thread thread: %d", rc);
+			goto failed;
+		} else
+			fpin_consumer_thr_started = true;
+	}
 	pthread_attr_destroy(&misc_attr);
 
 	while (1) {
+		int rc = 0;
+
 		pthread_cleanup_push(config_cleanup, NULL);
 		pthread_mutex_lock(&config_lock);
 		while (running_state != DAEMON_CONFIGURE &&
-		       running_state != DAEMON_SHUTDOWN)
+		       running_state != DAEMON_SHUTDOWN &&
+		       /*
+			* Check if another reconfigure request was scheduled
+			* while we last ran reconfigure().
+			* We have to test __delayed_reconfig here
+			* to avoid a busy loop
+			*/
+		       (reconfigure_pending == FORCE_RELOAD_NONE
+			 || __delayed_reconfig))
 			pthread_cond_wait(&config_cond, &config_lock);
+
+		if (running_state != DAEMON_CONFIGURE &&
+		    running_state != DAEMON_SHUTDOWN)
+			/* This sets running_state to DAEMON_CONFIGURE */
+			__post_config_state(DAEMON_CONFIGURE);
 		state = running_state;
 		pthread_cleanup_pop(1);
 		if (state == DAEMON_SHUTDOWN)
 			break;
-		if (state == DAEMON_CONFIGURE) {
-			int rc = 0;
 
-			pthread_cleanup_push(cleanup_lock, &vecs->lock);
-			lock(&vecs->lock);
-			pthread_testcancel();
-			if (!need_to_delay_reconfig(vecs))
-				rc = reconfigure(vecs);
-			else
-				enable_delayed_reconfig();
-			lock_cleanup_pop(vecs->lock);
-			if (!rc)
-				post_config_state(DAEMON_IDLE);
-			else {
-				condlog(0, "fatal error applying configuration - aborting");
-				exit_daemon();
-			}
+		/* handle DAEMON_CONFIGURE */
+		pthread_cleanup_push(cleanup_lock, &vecs->lock);
+		lock(&vecs->lock);
+		pthread_testcancel();
+		if (!need_to_delay_reconfig(vecs)) {
+			enum force_reload_types reload_type;
+
+			pthread_mutex_lock(&config_lock);
+			reload_type = reconfigure_pending == FORCE_RELOAD_YES ?
+				FORCE_RELOAD_YES : FORCE_RELOAD_WEAK;
+			reconfigure_pending = FORCE_RELOAD_NONE;
+			__delayed_reconfig = false;
+			pthread_mutex_unlock(&config_lock);
+
+			rc = reconfigure(vecs, reload_type);
+		} else {
+			pthread_mutex_lock(&config_lock);
+			__delayed_reconfig = true;
+			pthread_mutex_unlock(&config_lock);
+			condlog(3, "delaying reconfigure()");
+		}
+		lock_cleanup_pop(vecs->lock);
+		if (!rc)
+			post_config_state(DAEMON_IDLE);
+		else {
+			condlog(0, "fatal error applying configuration - aborting");
+			exit_daemon();
 		}
 	}
 
 	exit_code = 0;
 failed:
-	condlog(2, "--------shut down-------");
+	condlog(2, "multipathd: shut down");
 	/* All cleanup is done in the cleanup_child() exit handler */
 	return sd_notify_exit(exit_code);
 }
diff -pruN 0.8.8-1/multipathd/main.h 0.9.0-3/multipathd/main.h
--- 0.8.8-1/multipathd/main.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/multipathd/main.h	2022-06-10 16:47:26.000000000 +0000
@@ -17,7 +17,7 @@ enum remove_path_result {
 	REMOVE_PATH_FAILURE = 0x0, /* path could not be removed. It is still
 				    * part of the kernel map, but its state
 				    * is set to INIT_REMOVED, and it will be
-				    * removed at the next possible occassion */
+				    * removed at the next possible occasion */
 	REMOVE_PATH_SUCCESS = 0x1, /* path was removed */
 	REMOVE_PATH_DELAY = 0x2, /* path is set to be removed later. it
 			          * currently still exists and is part of the
@@ -27,9 +27,6 @@ enum remove_path_result {
 				      * because the path was also removed */
 };
 
-struct prout_param_descriptor;
-struct prin_resp;
-
 extern pid_t daemon_pid;
 extern int uxsock_timeout;
 
@@ -44,17 +41,7 @@ int ev_remove_path (struct path *, struc
 int ev_add_map (char *, const char *, struct vectors *);
 int ev_remove_map (char *, char *, int, struct vectors *);
 int flush_map(struct multipath *, struct vectors *, int);
-void * mpath_alloc_prin_response(int prin_sa);
-int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp,
-		       int noisy);
-void dumpHex(const char * , int len, int no_ascii);
-int prout_do_scsi_ioctl(char * , int rq_servact, int rq_scope,
-			unsigned int rq_type,
-			struct prout_param_descriptor *param, int noisy);
-int mpath_pr_event_handle(struct path *pp);
-void * mpath_pr_event_handler_fn (void * );
-int update_map_pr(struct multipath *mpp);
-void * mpath_pr_event_handler_fn (void * pathp );
+
 void handle_signals(bool);
 int __setup_multipath (struct vectors * vecs, struct multipath * mpp,
 		       int reset);
diff -pruN 0.8.8-1/multipathd/Makefile 0.9.0-3/multipathd/Makefile
--- 0.8.8-1/multipathd/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/multipathd/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -1,26 +1,31 @@
 include ../Makefile.inc
 
-ifneq ($(call check_func,dm_task_get_errno,/usr/include/libdevmapper.h),0)
-	CFLAGS += -DLIBDM_API_GET_ERRNO
+ifneq ($(call check_func,dm_task_get_errno,$(DEVMAPPER_INCDIR)/libdevmapper.h),0)
+	CPPFLAGS += -DLIBDM_API_GET_ERRNO
 endif
 
+ifneq ($(call check_var,ELS_DTAG_LNK_INTEGRITY,$(LINUX_HEADERS_INCDIR)/scsi/fc/fc_els.h),0)
+	CPPFLAGS += -DFPIN_EVENT_HANDLER
+	FPIN_SUPPORT = 1
+endif
 #
 # debugging stuff
 #
-#CFLAGS += -DLCKDBG
-#CFLAGS += -D_DEBUG_
-#CFLAGS += -DLOGDBG
-CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) \
-	  -I$(mpathcmddir) -I$(thirdpartydir)
+#CPPFLAGS += -DLCKDBG
+#CPPFLAGS += -D_DEBUG_
+#CPPFLAGS += -DLOGDBG
+
+CPPFLAGS += -I$(multipathdir) -I$(mpathpersistdir) -I$(mpathcmddir) -I$(thirdpartydir) \
+	$(shell $(PKGCONFIG) --modversion liburcu 2>/dev/null | \
+		awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }')
+CFLAGS += $(BIN_CFLAGS)
 LDFLAGS += $(BIN_LDFLAGS)
 LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \
 	   -L$(mpathcmddir) -lmpathcmd -ludev -ldl -lurcu -lpthread \
 	   -ldevmapper -lreadline
-CFLAGS += $(shell $(PKGCONFIG) --modversion liburcu 2>/dev/null | \
-	awk -F. '{ printf("-DURCU_VERSION=0x%06x", 256 * ( 256 * $$1 + $$2) + $$3); }')
 
 ifdef SYSTEMD
-	CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
+	CPPFLAGS += -DUSE_SYSTEMD=$(SYSTEMD)
 	ifeq ($(shell test $(SYSTEMD) -gt 209 && echo 1), 1)
 		LIBDEPS += -lsystemd
 	else
@@ -28,22 +33,27 @@ ifdef SYSTEMD
 	endif
 endif
 ifeq ($(ENABLE_DMEVENTS_POLL),0)
-	CFLAGS += -DNO_DMEVENTS_POLL
+	CPPFLAGS += -DNO_DMEVENTS_POLL
 endif
 
 OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \
        dmevents.o init_unwinder.o
 
+ifeq ($(FPIN_SUPPORT),1)
+OBJS += fpin_handlers.o
+endif
+
+
+
 EXEC = multipathd
 
 all : $(EXEC)
 
 $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
 	$(CC) $(CFLAGS) $(OBJS) $(LDFLAGS) -o $(EXEC) $(LIBDEPS)
-	$(GZIP) $(EXEC).8 > $(EXEC).8.gz
 
 cli_handlers.o:	cli_handlers.c
-	$(CC) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
+	$(CC) $(CPPFLAGS) $(CFLAGS) -Wno-unused-parameter -c -o $@ $<
 
 install:
 	$(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
@@ -54,16 +64,16 @@ ifdef SYSTEMD
 	$(INSTALL_PROGRAM) -m 644 $(EXEC).socket $(DESTDIR)$(unitdir)
 endif
 	$(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
-	$(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir)
+	$(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(man8dir)
 
 uninstall:
 	$(RM) $(DESTDIR)$(bindir)/$(EXEC)
-	$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
+	$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8
 	$(RM) $(DESTDIR)$(unitdir)/$(EXEC).service
 	$(RM) $(DESTDIR)$(unitdir)/$(EXEC).socket
 
 clean: dep_clean
-	$(RM) core *.o $(EXEC) *.gz
+	$(RM) core *.o $(EXEC)
 
 include $(wildcard $(OBJS:.o=.d))
 
diff -pruN 0.8.8-1/multipathd/multipathd.8 0.9.0-3/multipathd/multipathd.8
--- 0.8.8-1/multipathd/multipathd.8	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/multipathd/multipathd.8	2022-06-10 16:47:26.000000000 +0000
@@ -82,7 +82,7 @@ multipathd. See
 .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 seperate thread for each device.
+waiting method, based on creating a separate thread for each device.
 .
 .
 .
diff -pruN 0.8.8-1/multipathd/multipathd.service 0.9.0-3/multipathd/multipathd.service
--- 0.8.8-1/multipathd/multipathd.service	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/multipathd/multipathd.service	2022-06-10 16:47:26.000000000 +0000
@@ -1,12 +1,14 @@
 [Unit]
 Description=Device-Mapper Multipath Device Controller
-Before=iscsi.service iscsid.service lvm2-activation-early.service
+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
@@ -14,8 +16,6 @@ ConditionVirtualization=!container
 [Service]
 Type=notify
 NotifyAccess=main
-LimitCORE=infinity
-ExecStartPre=-/sbin/modprobe -a scsi_dh_alua scsi_dh_emc scsi_dh_rdac dm-multipath
 ExecStart=/sbin/multipathd -d -s
 ExecReload=/sbin/multipathd reconfigure
 TasksMax=infinity
diff -pruN 0.8.8-1/multipathd/uxlsnr.c 0.9.0-3/multipathd/uxlsnr.c
--- 0.8.8-1/multipathd/uxlsnr.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/multipathd/uxlsnr.c	2022-06-10 16:47:26.000000000 +0000
@@ -75,7 +75,7 @@ enum {
 };
 
 #define POLLFD_CHUNK (4096 / sizeof(struct pollfd))
-/* Minimum mumber of pollfds to reserve for clients */
+/* Minimum number of pollfds to reserve for clients */
 #define MIN_POLLS (POLLFD_CHUNK - POLLFDS_BASE)
 /*
  * Max number of client connections allowed
@@ -91,7 +91,6 @@ static LIST_HEAD(clients);
 static struct pollfd *polls;
 static int notify_fd = -1;
 static int idle_fd = -1;
-static char *watch_config_dir;
 
 static bool _socket_client_is_root(int fd)
 {
@@ -167,7 +166,6 @@ void uxsock_cleanup(void *arg)
 
 	close(ux_sock);
 	close(notify_fd);
-	free(watch_config_dir);
 
 	list_for_each_entry_safe(client_loop, client_tmp, &clients, node) {
 		dead_client(client_loop);
@@ -213,16 +211,7 @@ static void reset_watch(int notify_fd, s
 		*sequence_nr = conf->sequence_nr;
 		if (wds->conf_wd == -1)
 			conf_reset = 1;
-		if (!watch_config_dir || !conf->config_dir ||
-		    strcmp(watch_config_dir, conf->config_dir)) {
-			dir_reset = 1;
-			if (watch_config_dir)
-				free(watch_config_dir);
-			if (conf->config_dir)
-				watch_config_dir = strdup(conf->config_dir);
-			else
-				watch_config_dir = NULL;
-		} else if (wds->dir_wd == -1)
+		if (wds->dir_wd == -1)
 			dir_reset = 1;
 	}
 	put_multipath_config(conf);
@@ -232,14 +221,12 @@ static void reset_watch(int notify_fd, s
 			inotify_rm_watch(notify_fd, wds->dir_wd);
 			wds->dir_wd = -1;
 		}
-		if (watch_config_dir) {
-			wds->dir_wd = inotify_add_watch(notify_fd,
-							watch_config_dir,
-							IN_CLOSE_WRITE |
-							IN_DELETE | IN_ONLYDIR);
-			if (wds->dir_wd == -1)
-				condlog(3, "didn't set up notifications on %s: %m", watch_config_dir);
-		}
+		wds->dir_wd = inotify_add_watch(notify_fd,
+						CONFIG_DIR,
+						IN_CLOSE_WRITE |
+						IN_DELETE | IN_ONLYDIR);
+		if (wds->dir_wd == -1)
+			condlog(3, "didn't set up notifications on %s: %m", CONFIG_DIR);
 	}
 	if (conf_reset) {
 		wds->conf_wd = inotify_add_watch(notify_fd, DEFAULT_CONFIGFILE,
diff -pruN 0.8.8-1/README.md 0.9.0-3/README.md
--- 0.8.8-1/README.md	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/README.md	2022-06-10 16:47:26.000000000 +0000
@@ -52,6 +52,68 @@ To get latest devel code:
 Github page: https://github.com/opensvc/multipath-tools
 
 
+Building multipath-tools
+========================
+
+Prerequisites: development packages of for `libdevmapper`, `libreadline`,
+`libaio`, `libudev`, `libjson-c`, `liburcu`, and `libsystemd`.
+
+To build multipath-tools, type:
+
+    make
+	make DESTDIR="/my/target/dir" install
+
+To uninstall, type:
+
+    make uninstall
+
+Customizing the build
+---------------------
+
+The following variables can be passed to the `make` command line:
+
+ * `plugindir="/some/path"`: directory where libmultipath plugins (path
+   checkers, prioritizers, and foreign multipath support) will be looked up.
+   This used to be the run-time option `multipath_dir` in earlier versions.
+ * `configdir="/some/path"` : directory to search for configuration files.
+    This used to be the run-time option `config_dir` in earlier versions.
+	The default is `/etc/multipath/conf.d`.
+ * `ENABLE_LIBDMMP=0`: disable building libdmmp
+ * `ENABLE_DMEVENTS_POLL=0`: disable support for the device-mapper event
+   polling API. For use with pre-5.0 kernels that don't support dmevent polling
+   (but even if you don't use this option, multipath-tools will work with
+   these kernels).
+ * `SCSI_DH_MODULES_PRELOAD="(list)"`: specify a space-separated list of SCSI
+   device handler kernel modules to load early during boot. Some
+   multipath-tools functionality depends on these modules being loaded
+   early. This option causes a *modules-load.d(5)* configuration file to be
+   created, thus it depends on functionality provided by *systemd*.
+   This variable only matters for `make install`.
+
+Note: The usefulness of the preload list depends on the kernel configuration.
+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.
+
+See `Makefile.inc` for additional variables to customize paths and compiler
+flags.
+
+Special Makefile targets
+------------------------
+
+The following targets are intended for developers only.
+
+ * `make test` to build and run the unit tests
+ * `make valgrind-test` to run the unit tests under valgrind
+ * `make abi` to create an XML representation of the ABI of the libraries in
+   the `abi/` subdirectory
+ * `make abi-test` to compare the ABI of a different multipath-tools version,
+   which must be stored in the `reference-abi/` subdirectory. If this test
+   fails, the ABI has changed wrt the reference.
+ * `make compile-commands.json` to create input for [clangd](https://clangd.llvm.org/).
+
+
 Add storage devices
 ===================
 
diff -pruN 0.8.8-1/tests/directio.c 0.9.0-3/tests/directio.c
--- 0.8.8-1/tests/directio.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/tests/directio.c	2022-06-10 16:47:26.000000000 +0000
@@ -693,7 +693,7 @@ static void test_check_state_blksize(voi
 	do_libcheck_reset(1);
 }
 
-/* test async checkers pending and getting resovled by another checker
+/* test async checkers pending and getting resolved by another checker
  * as well as the loops for getting multiple events */
 static void test_check_state_async(void **state)
 {
diff -pruN 0.8.8-1/tests/hwtable.c 0.9.0-3/tests/hwtable.c
--- 0.8.8-1/tests/hwtable.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/tests/hwtable.c	2022-06-10 16:47:26.000000000 +0000
@@ -143,12 +143,11 @@ static int setup(void **state)
 	}
 	hwt->tmpname = strdup(buf);
 
-	snprintf(buf, sizeof(buf), "%s", tmplate);
-	if (mkdtemp(buf) == NULL) {
-		condlog(0, "mkdtemp (2): %s", strerror(errno));
+	hwt->dirname = strdup(TESTCONFDIR);
+	if (mkdir(hwt->dirname, 0744) != 0) {
+		condlog(0, "mkdir %s: %s", hwt->dirname, strerror(errno));
 		goto err;
 	}
-	hwt->dirname = strdup(buf);
 
 	make_config_file_path(buf, sizeof(buf), hwt, -1);
 	hwt->config_file = fopen(buf, "w+");
@@ -404,7 +403,7 @@ static const char _vendor[] = "vendor";
 static const char _product[] = "product";
 static const char _prio[] = "prio";
 static const char _checker[] = "path_checker";
-static const char _getuid[] = "getuid_callout";
+static const char _vpd_vnd[] = "vpd_vendor";
 static const char _uid_attr[] = "uid_attribute";
 static const char _bl_product[] = "product_blacklist";
 static const char _minio[] = "rr_min_io_rq";
@@ -421,7 +420,7 @@ static const struct key_value vnd_boo =
 static const struct key_value prd_baz = { _product, "baz" };
 static const struct key_value wwid_test = { _wwid, default_wwid };
 
-/* Regular expresssions */
+/* Regular expressions */
 static const struct key_value vnd__oo = { _vendor, ".oo" };
 static const struct key_value vnd_t_oo = { _vendor, "^.oo" };
 static const struct key_value prd_ba_ = { _product, "ba." };
@@ -436,7 +435,7 @@ static const struct key_value prio_emc =
 static const struct key_value prio_hds = { _prio, "hds" };
 static const struct key_value prio_rdac = { _prio, "rdac" };
 static const struct key_value chk_hp = { _checker, "hp_sw" };
-static const struct key_value gui_foo = { _getuid, "/tmp/foo" };
+static const struct key_value vpd_hp3par = { _vpd_vnd, "hp3par" };
 static const struct key_value uid_baz = { _uid_attr, "BAZ_ATTR" };
 static const struct key_value bl_bar = { _bl_product, "bar" };
 static const struct key_value bl_baz = { _bl_product, "baz" };
@@ -574,7 +573,7 @@ static void test_internal_nvme(const str
 	assert_ptr_not_equal(mp, NULL);
 	TEST_PROP(checker_name(&pp->checker), NONE);
 	TEST_PROP(pp->uid_attribute, DEFAULT_NVME_UID_ATTRIBUTE);
-	assert_int_equal(mp->pgpolicy, DEFAULT_PGPOLICY);
+	assert_int_equal(mp->pgpolicy, GROUP_BY_PRIO);
 	assert_int_equal(mp->no_path_retry, DEFAULT_NO_PATH_RETRY);
 	assert_int_equal(mp->retain_hwhandler, RETAIN_HWHANDLER_OFF);
 
@@ -587,7 +586,7 @@ static void test_internal_nvme(const str
 	assert_ptr_not_equal(mp, NULL);
 	TEST_PROP(checker_name(&pp->checker), NONE);
 	TEST_PROP(pp->uid_attribute, "ID_WWN");
-	assert_int_equal(mp->pgpolicy, MULTIBUS);
+	assert_int_equal(mp->pgpolicy, GROUP_BY_PRIO);
 	assert_int_equal(mp->no_path_retry, NO_PATH_RETRY_QUEUE);
 	assert_int_equal(mp->retain_hwhandler, RETAIN_HWHANDLER_OFF);
 }
@@ -603,7 +602,7 @@ static int setup_internal_nvme(void **st
 }
 
 /*
- * Device section with a simple entry qith double quotes ('foo:"bar"')
+ * Device section with a simple entry with double quotes ('foo:"bar"')
  */
 static void test_quoted_hwe(const struct hwt_state *hwt)
 {
@@ -756,31 +755,31 @@ static void test_regex_string_hwe(const
 	/* foo:baz matches kv1 */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* boo:baz matches kv1 */
 	pp = mock_path(vnd_boo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* .oo:ba. matches kv1 */
 	pp = mock_path(vnd__oo.value, prd_ba_.value);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* .foo:(bar|baz|ba\.) doesn't match */
 	pp = mock_path(vnd__oo.value, prd_ba_s.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
 	/* foo:bar matches kv2 and kv1 */
-	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
@@ -788,7 +787,7 @@ static int setup_regex_string_hwe(void *
 {
 	struct hwt_state *hwt = CHECK_STATE(state);
 	const struct key_value kv1[] = { vnd_t_oo, prd_ba_s, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
 
 	WRITE_TWO_DEVICES(hwt, kv1, kv2);
 	SET_TEST_FUNC(hwt, test_regex_string_hwe);
@@ -813,39 +812,39 @@ static void test_regex_string_hwe_dir(co
 	/* foo:baz matches kv1 */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* boo:baz matches kv1 */
 	pp = mock_path(vnd_boo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* .oo:ba. matches kv1 */
 	pp = mock_path(vnd__oo.value, prd_ba_.value);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* .oo:(bar|baz|ba\.)$ doesn't match */
 	pp = mock_path(vnd__oo.value, prd_ba_s.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
 	/* foo:bar matches kv2 */
-	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
 	/* Later match takes prio */
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_regex_string_hwe_dir(void **state)
 {
 	const struct key_value kv1[] = { vnd_t_oo, prd_ba_s, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
@@ -868,29 +867,29 @@ static void test_regex_2_strings_hwe_dir
 	/* foo:baz matches kv1 */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* boo:baz doesn't match */
 	pp = mock_path(vnd_boo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
 	/* foo:bar matches kv2 and kv1 */
 	pp = mock_path(vnd_foo.value, prd_bar.value);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(pp->uid_attribute, uid_baz.value);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* foo:barz matches kv3 and kv2 and kv1 */
-	pp = mock_path_flags(vnd_foo.value, prd_barz.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_barz.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_rdac.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
-	TEST_PROP(pp->uid_attribute, NULL);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
+	TEST_PROP(pp->uid_attribute, uid_baz.value);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
@@ -899,7 +898,7 @@ static int setup_regex_2_strings_hwe_dir
 	const struct key_value kv1[] = { vnd_foo, prd_ba_, prio_emc, chk_hp };
 	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, uid_baz };
 	const struct key_value kv3[] = { vnd_foo, prd_barz,
-					 prio_rdac, gui_foo };
+					 prio_rdac, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	begin_config(hwt);
@@ -925,40 +924,40 @@ static void test_string_regex_hwe_dir(co
 	struct path *pp;
 
 	/* foo:bar matches kv2 and kv1 */
-	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* foo:baz matches kv1 */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* boo:baz matches kv1 */
 	pp = mock_path(vnd_boo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* .oo:ba. matches kv1 */
 	pp = mock_path(vnd__oo.value, prd_ba_.value);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* .oo:(bar|baz|ba\.)$ doesn't match */
 	pp = mock_path(vnd__oo.value, prd_ba_s.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 }
 
 static int setup_string_regex_hwe_dir(void **state)
 {
 	const struct key_value kv1[] = { vnd_t_oo, prd_ba_s, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	WRITE_TWO_DEVICES_W_DIR(hwt, kv2, kv1);
@@ -967,7 +966,7 @@ static int setup_string_regex_hwe_dir(vo
 }
 
 /*
- * Two identical device entries kv1 and kv2, trival regex ("string").
+ * Two identical device entries kv1 and kv2, trivial regex ("string").
  * Both are added to the main config file.
  * These entries are NOT merged.
  * This could happen in a large multipath.conf file.
@@ -981,20 +980,20 @@ static void test_2_ident_strings_hwe(con
 	/* foo:baz doesn't match */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
 	/* foo:bar matches both */
-	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_strings_hwe(void **state)
 {
 	const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	WRITE_TWO_DEVICES(hwt, kv1, kv2);
@@ -1003,7 +1002,7 @@ static int setup_2_ident_strings_hwe(voi
 }
 
 /*
- * Two identical device entries kv1 and kv2, trival regex ("string").
+ * Two identical device entries kv1 and kv2, trivial regex ("string").
  * Both are added to an extra config file.
  * This could happen in a large multipath.conf file.
  *
@@ -1016,20 +1015,20 @@ static void test_2_ident_strings_both_di
 	/* foo:baz doesn't match */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
 	/* foo:bar matches both */
-	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_strings_both_dir(void **state)
 {
 	const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	begin_config(hwt);
@@ -1043,7 +1042,7 @@ static int setup_2_ident_strings_both_di
 }
 
 /*
- * Two identical device entries kv1 and kv2, trival regex ("string").
+ * Two identical device entries kv1 and kv2, trivial regex ("string").
  * Both are added to an extra config file.
  * An empty entry kv0 with the same string exists in the main config file.
  *
@@ -1056,13 +1055,13 @@ static void test_2_ident_strings_both_di
 	/* foo:baz doesn't match */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
 	/* foo:bar matches both */
-	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
@@ -1072,7 +1071,7 @@ static int setup_2_ident_strings_both_di
 
 	const struct key_value kv0[] = { vnd_foo, prd_bar };
 	const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
 
 	begin_config(hwt);
 	begin_section_all(hwt, "devices");
@@ -1086,7 +1085,7 @@ static int setup_2_ident_strings_both_di
 }
 
 /*
- * Two identical device entries kv1 and kv2, trival regex ("string").
+ * Two identical device entries kv1 and kv2, trivial regex ("string").
  * kv1 is added to the main config file, kv2 to a config_dir file.
  * These entries are merged.
  * This case is more important as you may think, because it's equivalent
@@ -1101,20 +1100,20 @@ static void test_2_ident_strings_hwe_dir
 	/* foo:baz doesn't match */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
 	/* foo:bar matches both */
-	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_strings_hwe_dir(void **state)
 {
 	const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
@@ -1135,13 +1134,13 @@ static void test_3_ident_strings_hwe_dir
 	/* foo:baz doesn't match */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
 	/* foo:bar matches both */
-	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
@@ -1149,7 +1148,7 @@ static int setup_3_ident_strings_hwe_dir
 {
 	const struct key_value kv0[] = { vnd_foo, prd_bar };
 	const struct key_value kv1[] = { vnd_foo, prd_bar, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd_foo, prd_bar, prio_hds, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	begin_config(hwt);
@@ -1179,20 +1178,20 @@ static void test_2_ident_self_matching_r
 	/* foo:baz doesn't match */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
 	/* foo:bar matches both */
-	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_self_matching_re_hwe_dir(void **state)
 {
 	const struct key_value kv1[] = { vnd__oo, prd_bar, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd__oo, prd_bar, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd__oo, prd_bar, prio_hds, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
@@ -1214,20 +1213,20 @@ static void test_2_ident_self_matching_r
 	/* foo:baz doesn't match */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
 	/* foo:bar matches */
-	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_self_matching_re_hwe(void **state)
 {
 	const struct key_value kv1[] = { vnd__oo, prd_bar, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd__oo, prd_bar, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd__oo, prd_bar, prio_hds, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	WRITE_TWO_DEVICES(hwt, kv1, kv2);
@@ -1251,20 +1250,20 @@ test_2_ident_not_self_matching_re_hwe_di
 	/* foo:baz doesn't match */
 	pp = mock_path(vnd_foo.value, prd_baz.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
 	/* foo:bar matches both */
-	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_bar.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
 static int setup_2_ident_not_self_matching_re_hwe_dir(void **state)
 {
 	const struct key_value kv1[] = { vnd_t_oo, prd_bar, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd_t_oo, prd_bar, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd_t_oo, prd_bar, prio_hds, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
@@ -1288,26 +1287,26 @@ static void test_2_matching_res_hwe_dir(
 	/* foo:bar matches k1 only */
 	pp = mock_path(vnd_foo.value, prd_bar.value);
 	TEST_PROP(prio_name(&pp->prio), prio_emc.value);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* foo:bay matches k1 and k2 */
-	pp = mock_path_flags(vnd_foo.value, "bay", USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, "bay", USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 
 	/* foo:baz matches k2 only. */
-	pp = mock_path_flags(vnd_foo.value, prd_baz.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_baz.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 }
 
 static int setup_2_matching_res_hwe_dir(void **state)
 {
 	const struct key_value kv1[] = { vnd_foo, prd_barx, prio_emc, chk_hp };
-	const struct key_value kv2[] = { vnd_foo, prd_bazy, prio_hds, gui_foo };
+	const struct key_value kv2[] = { vnd_foo, prd_bazy, prio_hds, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
@@ -1329,12 +1328,12 @@ static void test_2_nonmatching_res_hwe_d
 	/* foo:bar doesn't match */
 	pp = mock_path(vnd_foo.value, prd_bar.value);
 	TEST_PROP(prio_name(&pp->prio), DEFAULT_PRIO);
-	TEST_PROP(pp->getuid, NULL);
+	assert_int_equal(pp->vpd_vendor_id, 0);
 	TEST_PROP(checker_name(&pp->checker), DEFAULT_CHECKER);
 
-	pp = mock_path_flags(vnd_foo.value, prd_baz.value, USE_GETUID);
+	pp = mock_path_flags(vnd_foo.value, prd_baz.value, USE_VPD_VND);
 	TEST_PROP(prio_name(&pp->prio), prio_hds.value);
-	TEST_PROP(pp->getuid, gui_foo.value);
+	assert_int_equal(pp->vpd_vendor_id, VPD_VP_HP3PAR);
 	TEST_PROP(checker_name(&pp->checker), chk_hp.value);
 }
 
@@ -1342,7 +1341,7 @@ static int setup_2_nonmatching_res_hwe_d
 {
 	const struct key_value kv1[] = { vnd_foo, prd_bazy, prio_emc, chk_hp };
 	const struct key_value kv2[] = { vnd_foo, prd_bazy1,
-					 prio_hds, gui_foo };
+					 prio_hds, vpd_hp3par };
 	struct hwt_state *hwt = CHECK_STATE(state);
 
 	WRITE_TWO_DEVICES_W_DIR(hwt, kv1, kv2);
diff -pruN 0.8.8-1/tests/Makefile 0.9.0-3/tests/Makefile
--- 0.8.8-1/tests/Makefile	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/tests/Makefile	2022-06-10 16:47:26.000000000 +0000
@@ -1,5 +1,8 @@
 include ../Makefile.inc
 
+# directory where to run the tests
+TESTDIR := $(CURDIR)
+
 # Test special behavior of gcc 4.8 with nested initializers
 # gcc 4.8 compiles blacklist.c only with -Wno-missing-field-initializers
 TEST_MISSING_INITIALIZERS = $(shell \
@@ -8,8 +11,8 @@ TEST_MISSING_INITIALIZERS = $(shell \
 	|| echo -Wno-missing-field-initializers)
 W_MISSING_INITIALIZERS := $(call TEST_MISSING_INITIALIZERS)
 
-CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) \
-	-Wno-unused-parameter $(W_MISSING_INITIALIZERS)
+CPPFLAGS += -I$(multipathdir) -I$(mpathcmddir) -DTESTCONFDIR=\"$(TESTDIR)/conf.d\"
+CFLAGS += $(BIN_CFLAGS) -Wno-unused-parameter $(W_MISSING_INITIALIZERS)
 LIBDEPS += -L. -L$(mpathcmddir) -lmultipath -lmpathcmd -lcmocka
 
 TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \
@@ -40,6 +43,9 @@ mpathvalid-test_FLAGS := -I$(mpathvalidd
 #    That may be necessary if functions called from the object file are wrapped
 #    (wrapping works only for symbols which are undefined after processing a
 #    linker input file).
+#    Some object files, e.g. "config.o", are compiled separately for the
+#    unit tests. Look for OBJS-U in libmultipath/Makefile. Make sure to use the
+#    unit test file, e.g. "config-test.o", in XYZ-test_OBJDEPS
 # XYZ-test_LIBDEPS: Additional libs to link for this test
 
 dmevents-test_OBJDEPS = ../libmultipath/devmapper.o
@@ -66,7 +72,7 @@ endif
 strbuf-test_OBJDEPS := ../libmultipath/strbuf.o
 
 %.o: %.c
-	$(CC) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $<
+	$(CC) $(CPPFLAGS) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $<
 
 lib/libchecktur.so:
 	mkdir -p lib
@@ -91,7 +97,7 @@ valgrind_clean:
 
 clean: test_clean valgrind_clean dep_clean
 	$(RM) $(TESTS:%=%-test) $(OBJS) *.o.wrap
-	$(RM) -rf lib
+	$(RM) -rf lib conf.d
 
 .SECONDARY: $(OBJS)
 
@@ -105,7 +111,7 @@ dep_clean:
 		sort -u | tr '\n' ' ' >$@
 
 libmultipath.so.0:
-	$(MAKE) -C $(multipathdir) test-lib
+	make -C $(multipathdir) configdir=$(TESTDIR)/conf.d plugindir=$(TESTDIR)/lib test-lib
 
 # COLON will get expanded during second expansion below
 COLON:=:
diff -pruN 0.8.8-1/tests/mpathvalid.c 0.9.0-3/tests/mpathvalid.c
--- 0.8.8-1/tests/mpathvalid.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/tests/mpathvalid.c	2022-06-10 16:47:26.000000000 +0000
@@ -104,7 +104,7 @@ int __wrap_init_config(const char *file)
 	int r = mock_type(int);
 	struct config *conf;
 
-	assert_ptr_equal(file, DEFAULT_CONFIGFILE);
+	assert_string_equal(file, DEFAULT_CONFIGFILE);
 	if (r != 0)
 		return r;
 
diff -pruN 0.8.8-1/tests/README.md 0.9.0-3/tests/README.md
--- 0.8.8-1/tests/README.md	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/tests/README.md	2022-06-10 16:47:26.000000000 +0000
@@ -38,7 +38,7 @@ device will be opened in read-only mode;
 loss. However, the user needs to specify a device to be used. Set the
 environment variable `DIO_TEST_DEV` to the path of the device.
 Alternatively, create a file `directio_test_dev` under
-the `tests` directory containting a single line that sets this environment
+the `tests` directory containing a single line that sets this environment
 variable in Bourne Shell syntax, like this:
 
     DIO_TEST_DEV=/dev/sdc3
diff -pruN 0.8.8-1/tests/strbuf.c 0.9.0-3/tests/strbuf.c
--- 0.8.8-1/tests/strbuf.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/tests/strbuf.c	2022-06-10 16:47:26.000000000 +0000
@@ -9,7 +9,6 @@
 #include <stddef.h>
 #include <setjmp.h>
 #include <stdlib.h>
-#include <stdbool.h>
 #include <cmocka.h>
 #include <errno.h>
 #include "strbuf.h"
diff -pruN 0.8.8-1/tests/test-lib.c 0.9.0-3/tests/test-lib.c
--- 0.8.8-1/tests/test-lib.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/tests/test-lib.c	2022-06-10 16:47:26.000000000 +0000
@@ -4,6 +4,8 @@
 #include <cmocka.h>
 #include <libudev.h>
 #include <sys/sysmacros.h>
+#include <linux/hdreg.h>
+#include <scsi/sg.h>
 #include "debug.h"
 #include "util.h"
 #include "vector.h"
@@ -12,9 +14,10 @@
 #include "config.h"
 #include "discovery.h"
 #include "propsel.h"
+#include "unaligned.h"
 #include "test-lib.h"
 
-const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO);
+const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO|DI_SERIAL);
 const char default_devnode[] = "sdxTEST";
 const char default_wwid[] = "TEST-WWID";
 /* default_wwid should be a substring of default_wwid_1! */
@@ -47,15 +50,6 @@ int __wrap_open(const char *path, int fl
 	return __real_open(path, flags, mode);
 }
 
-int __wrap_execute_program(char *path, char *value, int len)
-{
-	char *val = mock_ptr_type(char *);
-
-	condlog(5, "%s: %s", __func__, val);
-	strlcpy(value, val, len);
-	return 0;
-}
-
 int __wrap_libmp_get_version(int which, unsigned int version[3])
 {
 	unsigned int *vers = mock_ptr_type(unsigned int *);
@@ -181,6 +175,23 @@ ssize_t __wrap_sysfs_attr_get_value(stru
 	return strlen(value);
 }
 
+/* mock vpd_pg80 */
+ssize_t __wrap_sysfs_bin_attr_get_value(struct udev_device *dev,
+					const char *attr_name,
+					char *buf, size_t sz)
+{
+	static const char serial[] = "mptest_serial";
+
+	assert_string_equal(attr_name, "vpd_pg80");
+	assert_in_range(sz, sizeof(serial) + 3, INT_MAX);
+	memset(buf, 0, sizeof(serial) + 3);
+	buf[1] = 0x80;
+	put_unaligned_be16(sizeof(serial) - 1, &buf[2]);
+	memcpy(&buf[4], serial, sizeof(serial) - 1);
+
+	return sizeof(serial) + 3;
+}
+
 int __wrap_checker_check(struct checker *c, int st)
 {
 	condlog(5, "%s: %d", __func__, st);
@@ -195,6 +206,41 @@ int __wrap_prio_getprio(struct prio *p,
 	return pr;
 }
 
+int __real_ioctl(int fd, unsigned long request, void *param);
+
+int __wrap_ioctl(int fd, unsigned long request, void *param)
+{
+	condlog(5, "%s: %lu", __func__, request);
+
+	if (request == HDIO_GETGEO) {
+		static const struct hd_geometry geom = {
+			.heads = 4, .sectors = 31, .cylinders = 64, .start = 0
+		};
+		memcpy(param, &geom, sizeof(geom));
+		return 0;
+	} else if (request == SG_IO) {
+		/* mock hp3par special VPD */
+		struct sg_io_hdr *hdr = param;
+		static const char vpd_data[] = "VPD DATA";
+		unsigned char *buf = hdr->dxferp;
+		/* see vpd_vendor_pages in discovery.c */
+		const int HP3PAR_VPD = 0xc0;
+
+		if (hdr->interface_id == 'S' && hdr->cmdp[0] == 0x12
+		    && (hdr->cmdp[1] & 1) == 1 && hdr->cmdp[2] == HP3PAR_VPD) {
+			assert_in_range(hdr->dxfer_len,
+					sizeof(vpd_data) + 3, INT_MAX);
+			memset(buf, 0, hdr->dxfer_len);
+			buf[1] = HP3PAR_VPD;
+			put_unaligned_be16(sizeof(vpd_data), &buf[2]);
+			memcpy(&buf[4], vpd_data, sizeof(vpd_data));
+			hdr->status = 0;
+			return 0;
+		}
+	}
+	return __real_ioctl(fd, request, param);
+}
+
 struct mocked_path *fill_mocked_path(struct mocked_path *mp,
 				     const char *vendor, const char *product,
 				     const char *rev, const char *wwid,
@@ -219,14 +265,13 @@ struct mocked_path *mocked_path_from_pat
 	mp->devnode = pp->dev;
 	mp->flags = (prio_selected(&pp->prio) ? 0 : NEED_SELECT_PRIO) |
 		(pp->fd < 0 ? NEED_FD : 0) |
-		(pp->getuid ? USE_GETUID : 0);
+		(pp->vpd_vendor_id != 0 ? USE_VPD_VND : 0);
 	return mp;
 }
 
+static const char hbtl[] = "4:0:3:1";
 static void mock_sysfs_pathinfo(const struct mocked_path *mp)
 {
-	static const char hbtl[] = "4:0:3:1";
-
 	will_return(__wrap_udev_device_get_subsystem, "scsi");
 	will_return(__wrap_udev_device_get_sysname, hbtl);
 	will_return(__wrap_udev_device_get_sysname, hbtl);
@@ -284,16 +329,18 @@ void mock_pathinfo(int mask, const struc
 	/* fake open() in pathinfo() */
 	if (mp->flags & NEED_FD)
 		will_return(__wrap_udev_device_get_devnode, _mocked_filename);
-	/* DI_SERIAL is unsupported */
-	assert_false(mask & DI_SERIAL);
+
+	/* scsi_ioctl_pathinfo() */
+	if (mask & DI_SERIAL) {
+		will_return(__wrap_udev_device_get_subsystem, "scsi");
+		will_return(__wrap_udev_device_get_sysname, hbtl);
+		will_return(__wrap_udev_device_get_sysname, hbtl);
+	}
 
 	if (mask & DI_WWID) {
-		if (mp->flags & USE_GETUID)
-			will_return(__wrap_execute_program, mp->wwid);
-		else
-			/* get_udev_uid() */
-			will_return(__wrap_udev_device_get_property_value,
-				    mp->wwid);
+		/* get_udev_uid() */
+		will_return(__wrap_udev_device_get_property_value,
+			    mp->wwid);
 	}
 
 	if (mask & DI_CHECKER) {
diff -pruN 0.8.8-1/tests/test-lib.h 0.9.0-3/tests/test-lib.h
--- 0.8.8-1/tests/test-lib.h	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/tests/test-lib.h	2022-06-10 16:47:26.000000000 +0000
@@ -14,7 +14,7 @@ enum {
 	BL_MASK = BL_BY_DEVNODE|BL_BY_DEVICE|BL_BY_WWID|BL_BY_PROPERTY,
 	NEED_SELECT_PRIO = (1 << 8),
 	NEED_FD		= (1 << 9),
-	USE_GETUID	= (1 << 10),
+	USE_VPD_VND	= (1 << 10),
 	DEV_HIDDEN	= (1 << 11),
 };
 
diff -pruN 0.8.8-1/tests/uevent.c 0.9.0-3/tests/uevent.c
--- 0.8.8-1/tests/uevent.c	2021-12-06 06:42:30.000000000 +0000
+++ 0.9.0-3/tests/uevent.c	2022-06-10 16:47:26.000000000 +0000
@@ -111,7 +111,7 @@ static void test_uid_attrs(void **state)
 static void test_wwid(void **state)
 {
 	struct uevent *uev = *state;
-	uevent_get_wwid(uev);
+	uevent_get_wwid(uev, &conf);
 
 	assert_string_equal(uev->wwid, WWID);
 }
