diff --git a/SOURCES/0001-Revert-new-udev-autoactivation.patch b/SOURCES/0001-Revert-new-udev-autoactivation.patch
index 2b096a7..971fc3a 100644
--- a/SOURCES/0001-Revert-new-udev-autoactivation.patch
+++ b/SOURCES/0001-Revert-new-udev-autoactivation.patch
@@ -1,7 +1,7 @@
 From 63c4458aaf67d114c677baf657a7e9e43440f349 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Mon, 20 Dec 2021 14:22:02 -0600
-Subject: [PATCH 01/23] Revert "new udev autoactivation"
+Subject: [PATCH 01/54] Revert "new udev autoactivation"
 
 This reverts commit 67722b312390cdab29c076c912e14bd739c5c0f6.
 ---
@@ -541,5 +541,5 @@ index e777dda16..e32cba921 100644
  DM_DIR=$(shell $(GREP) "\#define DM_DIR" $(top_srcdir)/libdm/misc/dm-ioctl.h | $(AWK) '{print $$3}')
  
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0002-Revert-pvscan-only-add-device-args-to-dev-cache.patch b/SOURCES/0002-Revert-pvscan-only-add-device-args-to-dev-cache.patch
index 8e73676..9f309a3 100644
--- a/SOURCES/0002-Revert-pvscan-only-add-device-args-to-dev-cache.patch
+++ b/SOURCES/0002-Revert-pvscan-only-add-device-args-to-dev-cache.patch
@@ -1,7 +1,7 @@
 From 2091305b796d5552fd991c527a0359a0b4d8fde0 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Mon, 20 Dec 2021 13:38:23 -0600
-Subject: [PATCH 02/23] Revert "pvscan: only add device args to dev cache"
+Subject: [PATCH 02/54] Revert "pvscan: only add device args to dev cache"
 
 This reverts commit 33e47182f773c1a902b533580b63a803906de55d.
 ---
@@ -446,5 +446,5 @@ index 95d593d57..8e2611361 100644
  	if (cmd->enable_devices_file && device_ids_use_devname(cmd)) {
  		relax_deviceid_filter = 1;
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0003-pvscan-fix-messages-from-coverity-changes.patch b/SOURCES/0003-pvscan-fix-messages-from-coverity-changes.patch
index 5ff1c9d..7756188 100644
--- a/SOURCES/0003-pvscan-fix-messages-from-coverity-changes.patch
+++ b/SOURCES/0003-pvscan-fix-messages-from-coverity-changes.patch
@@ -1,7 +1,7 @@
 From a5a2d5fa1ec47a5a548db4cf435dc84de7ce7c31 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Wed, 20 Oct 2021 16:12:41 -0500
-Subject: [PATCH 03/23] pvscan: fix messages from coverity changes
+Subject: [PATCH 03/54] pvscan: fix messages from coverity changes
 
 ---
  tools/pvscan.c | 4 ++--
@@ -30,5 +30,5 @@ index 8e2611361..f60c4a2ca 100644
  			continue;
  		}
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0004-vgimportdevices-skip-lvmlockd-locking.patch b/SOURCES/0004-vgimportdevices-skip-lvmlockd-locking.patch
index d84d9d9..450ead9 100644
--- a/SOURCES/0004-vgimportdevices-skip-lvmlockd-locking.patch
+++ b/SOURCES/0004-vgimportdevices-skip-lvmlockd-locking.patch
@@ -1,7 +1,7 @@
 From 074fce5c73c55e7a1547d5efff65a9f96e6db3b1 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Mon, 25 Oct 2021 12:11:17 -0500
-Subject: [PATCH 04/23] vgimportdevices: skip lvmlockd locking
+Subject: [PATCH 04/54] vgimportdevices: skip lvmlockd locking
 
 Help bootstrapping existing shared vgs into the devices file.
 Reading the vg in vgimportdevices would require locking to be
@@ -35,5 +35,5 @@ index 3f315f98f..2580613c4 100644
  	 * For each VG:
  	 * device_id_add() each PV in the VG
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0005-hints-remove-the-cmd-hints-list.patch b/SOURCES/0005-hints-remove-the-cmd-hints-list.patch
index 09ef5d4..ea26c2a 100644
--- a/SOURCES/0005-hints-remove-the-cmd-hints-list.patch
+++ b/SOURCES/0005-hints-remove-the-cmd-hints-list.patch
@@ -1,7 +1,7 @@
 From 00ebabfe6e1ebfceffcef335d44a6156a1c15418 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Mon, 1 Nov 2021 16:01:09 -0500
-Subject: [PATCH 05/23] hints: remove the cmd hints list
+Subject: [PATCH 05/54] hints: remove the cmd hints list
 
 which is no longer used after commit
 "toollib: remove all devices list from process_each_pv"
@@ -91,5 +91,5 @@ index 3cd912270..479a5037a 100644
  	 * Check if the devices_file content is up to date and
  	 * if not update it.
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0006-filter-sysfs-skip-when-device-id-is-set.patch b/SOURCES/0006-filter-sysfs-skip-when-device-id-is-set.patch
index 61d8fe7..7afc6a9 100644
--- a/SOURCES/0006-filter-sysfs-skip-when-device-id-is-set.patch
+++ b/SOURCES/0006-filter-sysfs-skip-when-device-id-is-set.patch
@@ -1,7 +1,7 @@
 From f73be4480a5dd104a77e3ef84d7dcc80b834e593 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Tue, 2 Nov 2021 15:42:26 -0500
-Subject: [PATCH 06/23] filter-sysfs: skip when device id is set
+Subject: [PATCH 06/54] filter-sysfs: skip when device id is set
 
 When a device id is set for a device, using an idtype other
 than devname, it means that sysfs has been used with the device
@@ -418,5 +418,5 @@ index 32ac324dd..672211057 100644
  }
  
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0007-lvmdevices-increase-open-file-limit.patch b/SOURCES/0007-lvmdevices-increase-open-file-limit.patch
index 6a586d7..435d851 100644
--- a/SOURCES/0007-lvmdevices-increase-open-file-limit.patch
+++ b/SOURCES/0007-lvmdevices-increase-open-file-limit.patch
@@ -1,7 +1,7 @@
 From f732f3d53faee3732d0f4a666c378709e6c2f5e9 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Fri, 29 Oct 2021 14:49:36 -0500
-Subject: [PATCH 07/23] lvmdevices: increase open file limit
+Subject: [PATCH 07/54] lvmdevices: increase open file limit
 
 ---
  lib/label/label.c  | 4 ++--
@@ -57,5 +57,5 @@ index 8d9634848..3f104f7de 100644
  	device_ids_match(cmd);
  
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0008-filter-sysfs-support-old-kernels-without-sys-dev-blo.patch b/SOURCES/0008-filter-sysfs-support-old-kernels-without-sys-dev-blo.patch
index cd4a4e3..d334f92 100644
--- a/SOURCES/0008-filter-sysfs-support-old-kernels-without-sys-dev-blo.patch
+++ b/SOURCES/0008-filter-sysfs-support-old-kernels-without-sys-dev-blo.patch
@@ -1,7 +1,7 @@
 From fad2b3dc8c44ba1222508ee78b5f161994efe638 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Tue, 9 Nov 2021 11:54:48 -0600
-Subject: [PATCH 08/23] filter-sysfs: support old kernels without sys/dev/block
+Subject: [PATCH 08/54] filter-sysfs: support old kernels without sys/dev/block
 
 rhel5 for example doesn't have /sys/dev/block
 ---
@@ -69,5 +69,5 @@ index 672211057..d8de7940b 100644
  		goto_bad;
  
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0009-device_id-match-different-dm-device-names.patch b/SOURCES/0009-device_id-match-different-dm-device-names.patch
index 94ec305..f1d6c1c 100644
--- a/SOURCES/0009-device_id-match-different-dm-device-names.patch
+++ b/SOURCES/0009-device_id-match-different-dm-device-names.patch
@@ -1,7 +1,7 @@
 From 459d931a9bfe4c9adcbbf2e76fdf35fda5b13c61 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Fri, 12 Nov 2021 16:42:51 -0600
-Subject: [PATCH 09/23] device_id: match different dm device names
+Subject: [PATCH 09/54] device_id: match different dm device names
 
 If a devices file entry for a dm device is using the devname
 for the device id, then recognize different dm names as matching.
@@ -151,5 +151,5 @@ index eb06109ff..dea739fc4 100644
  	return 0;
  }
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0010-device_id-fix-search-on-filtered-device.patch b/SOURCES/0010-device_id-fix-search-on-filtered-device.patch
index edc2d3e..b8c9ed4 100644
--- a/SOURCES/0010-device_id-fix-search-on-filtered-device.patch
+++ b/SOURCES/0010-device_id-fix-search-on-filtered-device.patch
@@ -1,7 +1,7 @@
 From 5533cd7bf4c1edc5d8fb0e95d2f83b2b2d446339 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Tue, 16 Nov 2021 09:29:24 -0600
-Subject: [PATCH 10/23] device_id: fix search on filtered device
+Subject: [PATCH 10/54] device_id: fix search on filtered device
 
 When devnames are used as device ids and devnames change,
 then new devices need to be located for the PVs.  If the old
@@ -130,5 +130,5 @@ index f95be52b1..a99fe3e9a 100644
 +
  vgremove -ff $vg1
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0011-device_id-searched_devnames-improvements.patch b/SOURCES/0011-device_id-searched_devnames-improvements.patch
index d8a658e..7c3f423 100644
--- a/SOURCES/0011-device_id-searched_devnames-improvements.patch
+++ b/SOURCES/0011-device_id-searched_devnames-improvements.patch
@@ -1,7 +1,7 @@
 From 39adf3e513ac7b1cbbbf0189f973573ade3c8939 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Tue, 16 Nov 2021 11:26:41 -0600
-Subject: [PATCH 11/23] device_id: searched_devnames improvements
+Subject: [PATCH 11/54] device_id: searched_devnames improvements
 
 Remove the searched_devnames file in a couple more places:
 . When hints need refreshing it's possible that a missing
@@ -98,5 +98,5 @@ index e444a0c82..3ce9634f2 100644
  			return 0;
  
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0012-tests-pv-ext-flags-work-with-devices-file.patch b/SOURCES/0012-tests-pv-ext-flags-work-with-devices-file.patch
index 1e421c0..dcf9507 100644
--- a/SOURCES/0012-tests-pv-ext-flags-work-with-devices-file.patch
+++ b/SOURCES/0012-tests-pv-ext-flags-work-with-devices-file.patch
@@ -1,7 +1,7 @@
 From 9c9bf13186d387d807f279c112745768c8b32513 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Tue, 16 Nov 2021 14:21:07 -0600
-Subject: [PATCH 12/23] tests pv-ext-flags: work with devices file
+Subject: [PATCH 12/54] tests pv-ext-flags: work with devices file
 
 ---
  test/shell/pv-ext-flags.sh | 4 ++++
@@ -36,5 +36,5 @@ index 3e6bcff76..ae4d6b7ff 100644
  # prepare a VG with $dev1 and $dev both having 1 MDA
  aux enable_dev "$dev2"
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0013-display-ignore-reportformat.patch b/SOURCES/0013-display-ignore-reportformat.patch
index f7dc091..33b7e28 100644
--- a/SOURCES/0013-display-ignore-reportformat.patch
+++ b/SOURCES/0013-display-ignore-reportformat.patch
@@ -1,7 +1,7 @@
 From 594c1fec1644fdf291aa0ff23de20db65c4cfadf Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Wed, 17 Nov 2021 10:40:27 -0600
-Subject: [PATCH 13/23] display: ignore --reportformat
+Subject: [PATCH 13/54] display: ignore --reportformat
 
 Using the option would do nothing useful but would
 print extraneous braces.
@@ -87,5 +87,5 @@ index 1e12bedca..1727ba089 100644
  	 * Remaining position args after command name and --options are removed.
  	 */
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0014-fix-spelling-of-pruning.patch b/SOURCES/0014-fix-spelling-of-pruning.patch
index e24450a..61afdb4 100644
--- a/SOURCES/0014-fix-spelling-of-pruning.patch
+++ b/SOURCES/0014-fix-spelling-of-pruning.patch
@@ -1,7 +1,7 @@
 From 7ac0b3c119b1cbb8e0b4969ece0b279637ace8c3 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Fri, 19 Nov 2021 12:02:35 -0600
-Subject: [PATCH 14/23] fix spelling of pruning
+Subject: [PATCH 14/54] fix spelling of pruning
 
 ---
  lib/format_text/archive.c | 2 +-
@@ -21,5 +21,5 @@ index 09a472b4c..2e8792a93 100644
  }
  
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0015-man-lvmautoactivation.patch b/SOURCES/0015-man-lvmautoactivation.patch
index 0128098..6abef88 100644
--- a/SOURCES/0015-man-lvmautoactivation.patch
+++ b/SOURCES/0015-man-lvmautoactivation.patch
@@ -1,7 +1,7 @@
 From 25dbe3dd825a629ff7f67cb43342cc345071d3f7 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Wed, 24 Nov 2021 16:03:39 -0600
-Subject: [PATCH 15/23] man: lvmautoactivation
+Subject: [PATCH 15/54] man: lvmautoactivation
 
 new topical man page describing autoactivation
 ---
@@ -412,5 +412,5 @@ index b20b987da..4c5929955 100644
 +.BR lvmautoactivation (7)
 +for more information about how pvscan is used for autoactivation.
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0017-tests-devicesfile-devname.sh-drop-mdadm-chunk.patch b/SOURCES/0017-tests-devicesfile-devname.sh-drop-mdadm-chunk.patch
index ef22830..a4147e6 100644
--- a/SOURCES/0017-tests-devicesfile-devname.sh-drop-mdadm-chunk.patch
+++ b/SOURCES/0017-tests-devicesfile-devname.sh-drop-mdadm-chunk.patch
@@ -1,7 +1,7 @@
 From 10a4478e9b778dd8d4ff9737a503474b00ce9510 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Wed, 1 Dec 2021 08:56:05 -0600
-Subject: [PATCH 17/23] tests devicesfile-devname.sh drop mdadm chunk
+Subject: [PATCH 17/54] tests devicesfile-devname.sh drop mdadm chunk
 
 ---
  test/shell/devicesfile-devname.sh | 2 +-
@@ -21,5 +21,5 @@ index a99fe3e9a..338637275 100644
  
  sed -e "s|DEVNAME=$dev1|DEVNAME=$dev3|" "$ORIG" > tmp1.devices
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0018-devices-file-don-t-write-in-test-mode.patch b/SOURCES/0018-devices-file-don-t-write-in-test-mode.patch
index feb3bf7..3e79410 100644
--- a/SOURCES/0018-devices-file-don-t-write-in-test-mode.patch
+++ b/SOURCES/0018-devices-file-don-t-write-in-test-mode.patch
@@ -1,7 +1,7 @@
 From 04770589b49effdb064c9b3790e8dd2fee2c3547 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Wed, 1 Dec 2021 10:08:08 -0600
-Subject: [PATCH 18/23] devices file: don't write in test mode
+Subject: [PATCH 18/54] devices file: don't write in test mode
 
 ---
  lib/device/device_id.c | 3 +++
@@ -22,5 +22,5 @@ index ce7ded154..4c2b5a3dd 100644
  		if (sscanf(_devices_file_version, "%u.%u.%u", &df_major, &df_minor, &df_counter) != 3) {
  			/* don't update a file we can't parse */
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0019-print-warning-about-unrecognized-journal-option-valu.patch b/SOURCES/0019-print-warning-about-unrecognized-journal-option-valu.patch
index 5fc92af..177f936 100644
--- a/SOURCES/0019-print-warning-about-unrecognized-journal-option-valu.patch
+++ b/SOURCES/0019-print-warning-about-unrecognized-journal-option-valu.patch
@@ -1,7 +1,7 @@
 From 604fd528fb4f00a9f77e084a1b22eff2aeef0259 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Thu, 2 Dec 2021 12:40:52 -0600
-Subject: [PATCH 19/23] print warning about unrecognized journal option value
+Subject: [PATCH 19/54] print warning about unrecognized journal option value
 
 ---
  lib/log/log.c | 1 +
@@ -20,5 +20,5 @@ index 7b4d537b3..5771a1d01 100644
  }
  
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0020-device_id-handle-wwid-with-spaces-or-control-charact.patch b/SOURCES/0020-device_id-handle-wwid-with-spaces-or-control-charact.patch
index e254e36..007cd75 100644
--- a/SOURCES/0020-device_id-handle-wwid-with-spaces-or-control-charact.patch
+++ b/SOURCES/0020-device_id-handle-wwid-with-spaces-or-control-charact.patch
@@ -1,7 +1,7 @@
 From 357a807e81bbd1430b045eb2601a64b17d588400 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Thu, 2 Dec 2021 13:30:36 -0600
-Subject: [PATCH 20/23] device_id: handle wwid with spaces or control
+Subject: [PATCH 20/54] device_id: handle wwid with spaces or control
  characters
 
 non-standard wwid can be reported from sysfs with spaces/etc.
@@ -46,5 +46,5 @@ index 4c2b5a3dd..0621bc858 100644
  		goto_bad;
  
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0021-man-add-section-about-static-autoactivation.patch b/SOURCES/0021-man-add-section-about-static-autoactivation.patch
index a6d10cf..f5494dc 100644
--- a/SOURCES/0021-man-add-section-about-static-autoactivation.patch
+++ b/SOURCES/0021-man-add-section-about-static-autoactivation.patch
@@ -1,7 +1,7 @@
 From 7631c5b826b5a3eddfcd22db9b80574b249794c1 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Mon, 6 Dec 2021 13:20:32 -0600
-Subject: [PATCH 21/23] man: add section about static autoactivation
+Subject: [PATCH 21/54] man: add section about static autoactivation
 
 ---
  man/lvmautoactivation.7_main | 48 ++++++++++++++++++++++++++++++------
@@ -92,5 +92,5 @@ index 87c15a3d1..bf885991d 100644
  .P
  VG "vg" contains two PVs:
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0022-lvcreate-include-recent-options.patch b/SOURCES/0022-lvcreate-include-recent-options.patch
index 38a729d..3ad1a54 100644
--- a/SOURCES/0022-lvcreate-include-recent-options.patch
+++ b/SOURCES/0022-lvcreate-include-recent-options.patch
@@ -1,7 +1,7 @@
 From af4bfa1f1f84194000bc50f43ddc906c0cd4b104 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Mon, 13 Dec 2021 08:59:31 -0600
-Subject: [PATCH 22/23] lvcreate: include recent options
+Subject: [PATCH 22/54] lvcreate: include recent options
 
 The permitted option list in lvcreate has not kept
 up with command-lines.in.
@@ -31,5 +31,5 @@ index 0121c09a8..79af42685 100644
  	permission_ARG,\
  	persistent_ARG,\
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0023-man-lvmautoactivation-replace-systemctl-with-journal.patch b/SOURCES/0023-man-lvmautoactivation-replace-systemctl-with-journal.patch
index 16b08c4..c5af575 100644
--- a/SOURCES/0023-man-lvmautoactivation-replace-systemctl-with-journal.patch
+++ b/SOURCES/0023-man-lvmautoactivation-replace-systemctl-with-journal.patch
@@ -1,7 +1,7 @@
 From 61833dd5b6117e8ace84289cff656d1dfb0ed123 Mon Sep 17 00:00:00 2001
 From: David Teigland <teigland@redhat.com>
 Date: Tue, 14 Dec 2021 12:02:08 -0600
-Subject: [PATCH 23/23] man lvmautoactivation: replace systemctl with
+Subject: [PATCH 23/54] man lvmautoactivation: replace systemctl with
  journalctl
 
 ---
@@ -22,5 +22,5 @@ index bf885991d..54dab718b 100644
  .
  .SS pvscan options
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0024-make-generate.patch b/SOURCES/0024-make-generate.patch
index f5a15ad..54cc1cf 100644
--- a/SOURCES/0024-make-generate.patch
+++ b/SOURCES/0024-make-generate.patch
@@ -1,7 +1,7 @@
 From 4b26fb3543049f3d179b620ff937c44e922ada58 Mon Sep 17 00:00:00 2001
 From: Marian Csontos <mcsontos@redhat.com>
 Date: Tue, 4 Jan 2022 17:15:56 +0100
-Subject: [PATCH] make: generate
+Subject: [PATCH 24/54] make: generate
 
 ---
  man/lvdisplay.8_pregen | 12 --------
@@ -192,5 +192,5 @@ index 9c694921d..0a12b3c39 100644
  .br
  Select objects for processing and reporting based on specified criteria.
 -- 
-2.31.1
+2.34.3
 
diff --git a/SOURCES/0025-filter-mpath-use-multipath-blacklist.patch b/SOURCES/0025-filter-mpath-use-multipath-blacklist.patch
deleted file mode 100644
index b8bc200..0000000
--- a/SOURCES/0025-filter-mpath-use-multipath-blacklist.patch
+++ /dev/null
@@ -1,521 +0,0 @@
-From 1199fd6af6d57c45e857bff218c817d8d0cac986 Mon Sep 17 00:00:00 2001
-From: David Teigland <teigland@redhat.com>
-Date: Thu, 21 Apr 2022 13:45:01 -0500
-Subject: [PATCH 1/2] filter-mpath: use multipath blacklist
-
-Explicit wwid's from these sections control whether the
-same wwid in /etc/multipath/wwids is recognized as a
-multipath component.  Other non-wwid keywords are not
-used, and may require disabling the use of the multipath
-wwids file in lvm.conf.
-
-(cherry picked from commit 5d40b91bd4aa8580ee1f40d467b848f7847f39e3)
----
- lib/device/dev-mpath.c                | 181 ++++++++++++++++++++++++--
- test/shell/duplicate-pvs-multipath.sh |  67 ++++++++++
- test/shell/multipath-config.sh        | 171 ++++++++++++++++++++++++
- 3 files changed, 408 insertions(+), 11 deletions(-)
- create mode 100644 test/shell/duplicate-pvs-multipath.sh
- create mode 100644 test/shell/multipath-config.sh
-
-diff --git a/lib/device/dev-mpath.c b/lib/device/dev-mpath.c
-index ba7bf9740..580ab31a5 100644
---- a/lib/device/dev-mpath.c
-+++ b/lib/device/dev-mpath.c
-@@ -17,12 +17,14 @@
- #include "lib/activate/activate.h"
- #include "lib/commands/toolcontext.h"
- #include "lib/device/device_id.h"
-+#include "lib/datastruct/str_list.h"
- #ifdef UDEV_SYNC_SUPPORT
- #include <libudev.h>
- #include "lib/device/dev-ext-udev-constants.h"
- #endif
- 
- #include <dirent.h>
-+#include <ctype.h>
- 
- #define MPATH_PREFIX "mpath-"
- 
-@@ -35,15 +37,167 @@
-  * If dm-3 is not an mpath device, then the constant "1" is stored in
-  * the hash table with the key of the dm minor number.
-  */
--static struct dm_pool *_hash_mem;
-+static struct dm_pool *_wwid_mem;
- static struct dm_hash_table *_minor_hash_tab;
- static struct dm_hash_table *_wwid_hash_tab;
-+static struct dm_list _ignored;
-+static struct dm_list _ignored_exceptions;
- 
- #define MAX_WWID_LINE 512
- 
--/*
-- * do we need to check the multipath.conf blacklist?
-- */
-+static void _read_blacklist_file(const char *path)
-+{
-+	FILE *fp;
-+	char line[MAX_WWID_LINE];
-+	char wwid[MAX_WWID_LINE];
-+	char *word, *p;
-+	int section_black = 0;
-+	int section_exceptions = 0;
-+	int found_quote;
-+	int found_three;
-+	int i, j;
-+
-+	if (!(fp = fopen(path, "r")))
-+		return;
-+
-+	while (fgets(line, sizeof(line), fp)) {
-+		word = NULL;
-+
-+		/* skip initial white space on the line */
-+		for (i = 0; i < MAX_WWID_LINE; i++) {
-+			if ((line[i] == '\n') || (line[i] == '\0'))
-+				break;
-+			if (isspace(line[i]))
-+				continue;
-+			word = &line[i];
-+			break;
-+		}
-+
-+		if (!word || word[0] == '#')
-+			continue;
-+
-+		/* identify the start of the section we want to read */
-+		if (strchr(word, '{')) {
-+			if (!strncmp(word, "blacklist_exceptions", 20))
-+				section_exceptions = 1;
-+			else if (!strncmp(word, "blacklist", 9))
-+				section_black = 1;
-+			continue;
-+		}
-+		/* identify the end of the section we've been reading */
-+		if (strchr(word, '}')) {
-+			section_exceptions = 0;
-+			section_black = 0;
-+			continue;
-+		}
-+		/* skip lines that are not in a section we want */
-+		if (!section_black && !section_exceptions)
-+			continue;
-+
-+		/*
-+		 * read a wwid from the blacklist{_exceptions} section.
-+		 * does not recognize other non-wwid entries in the
-+		 * section, and skips those (should the entire mp
-+		 * config filtering be disabled if non-wwids are seen?
-+		 */
-+		if (!(p = strstr(word, "wwid")))
-+			continue;
-+
-+		i += 4; /* skip "wwid" */
-+
-+		/*
-+		 * copy wwid value from the line.
-+		 * the wwids copied here need to match the
-+		 * wwids read from /etc/multipath/wwids,
-+		 * which are matched to wwids from sysfs.
-+		 */
-+
-+		memset(wwid, 0, sizeof(wwid));
-+		found_quote = 0;
-+		found_three = 0;
-+		j = 0;
-+
-+		for (; i < MAX_WWID_LINE; i++) {
-+			if ((line[i] == '\n') || (line[i] == '\0'))
-+				break;
-+			if (!j && isspace(line[i]))
-+				continue;
-+			if (isspace(line[i]))
-+				break;
-+			/* quotes around wwid are optional */
-+			if ((line[i] == '"') && !found_quote) {
-+				found_quote = 1;
-+				continue;
-+			}
-+			/* second quote is end of wwid */
-+			if ((line[i] == '"') && found_quote)
-+				break;
-+			/* ignore first "3" in wwid */
-+			if ((line[i] == '3') && !found_three) {
-+				found_three = 1;
-+				continue;
-+			}
-+
-+			wwid[j] = line[i];
-+			j++;
-+		}
-+
-+		if (j < 8)
-+			continue;
-+
-+		log_debug("multipath wwid %s in %s %s",
-+			  wwid, section_exceptions ? "blacklist_exceptions" : "blacklist", path);
-+
-+		if (section_exceptions) {
-+			if (!str_list_add(_wwid_mem, &_ignored_exceptions, dm_pool_strdup(_wwid_mem, wwid)))
-+				stack;
-+		} else {
-+			if (!str_list_add(_wwid_mem, &_ignored, dm_pool_strdup(_wwid_mem, wwid)))
-+				stack;
-+		}
-+	}
-+
-+	if (fclose(fp))
-+		stack;
-+}
-+
-+static void _read_wwid_exclusions(void)
-+{
-+	char path[PATH_MAX] = { 0 };
-+	DIR *dir;
-+	struct dirent *de;
-+	struct dm_str_list *sl, *sl2;
-+	int rem_count = 0;
-+
-+	_read_blacklist_file("/etc/multipath.conf");
-+
-+	if ((dir = opendir("/etc/multipath/conf.d"))) {
-+		while ((de = readdir(dir))) {
-+			if (de->d_name[0] == '.')
-+				continue;
-+			snprintf(path, PATH_MAX-1, "/etc/multipath/conf.d/%s", de->d_name);
-+			_read_blacklist_file(path);
-+		}
-+		closedir(dir);
-+	}
-+
-+	/* for each wwid in ignored_exceptions, remove it from ignored */
-+
-+	dm_list_iterate_items_safe(sl, sl2, &_ignored) {
-+		if (str_list_match_item(&_ignored_exceptions, sl->str))
-+			str_list_del(&_ignored, sl->str);
-+	}
-+
-+	/* for each wwid in ignored, remove it from wwid_hash */
-+
-+	dm_list_iterate_items(sl, &_ignored) {
-+		dm_hash_remove_binary(_wwid_hash_tab, sl->str, strlen(sl->str));
-+		rem_count++;
-+	}
-+
-+	if (rem_count)
-+		log_debug("multipath config ignored %d wwids", rem_count);
-+}
- 
- static void _read_wwid_file(const char *config_wwids_file)
- {
-@@ -93,6 +247,9 @@ int dev_mpath_init(const char *config_wwids_file)
- 	struct dm_hash_table *minor_tab;
- 	struct dm_hash_table *wwid_tab;
- 
-+	dm_list_init(&_ignored);
-+	dm_list_init(&_ignored_exceptions);
-+
- 	if (!(mem = dm_pool_create("mpath", 256))) {
- 		log_error("mpath pool creation failed.");
- 		return 0;
-@@ -104,7 +261,7 @@ int dev_mpath_init(const char *config_wwids_file)
- 		return 0;
- 	}
- 
--	_hash_mem = mem;
-+	_wwid_mem = mem;
- 	_minor_hash_tab = minor_tab;
- 
- 	/* multipath_wwids_file="" disables the use of the file */
-@@ -116,16 +273,18 @@ int dev_mpath_init(const char *config_wwids_file)
- 	if (!(wwid_tab = dm_hash_create(110))) {
- 		log_error("mpath hash table creation failed.");
- 		dm_hash_destroy(_minor_hash_tab);
--		dm_pool_destroy(_hash_mem);
-+		dm_pool_destroy(_wwid_mem);
- 		_minor_hash_tab = NULL;
--		_hash_mem = NULL;
-+		_wwid_mem = NULL;
- 		return 0;
- 	}
- 
- 	_wwid_hash_tab = wwid_tab;
- 
--	if (config_wwids_file)
-+	if (config_wwids_file) {
- 		_read_wwid_file(config_wwids_file);
-+		_read_wwid_exclusions();
-+	}
- 
- 	return 1;
- }
-@@ -136,12 +295,12 @@ void dev_mpath_exit(void)
- 		dm_hash_destroy(_minor_hash_tab);
- 	if (_wwid_hash_tab)
- 		dm_hash_destroy(_wwid_hash_tab);
--	if (_hash_mem)
--		dm_pool_destroy(_hash_mem);
-+	if (_wwid_mem)
-+		dm_pool_destroy(_wwid_mem);
- 
- 	_minor_hash_tab = NULL;
- 	_wwid_hash_tab = NULL;
--	_hash_mem = NULL;
-+	_wwid_mem = NULL;
- }
- 
- 
-diff --git a/test/shell/duplicate-pvs-multipath.sh b/test/shell/duplicate-pvs-multipath.sh
-new file mode 100644
-index 000000000..59c15b0d4
---- /dev/null
-+++ b/test/shell/duplicate-pvs-multipath.sh
-@@ -0,0 +1,67 @@
-+#!/usr/bin/env bash
-+
-+# Copyright (C) 2021 Red Hat, Inc. All rights reserved.
-+#
-+# This copyrighted material is made available to anyone wishing to use,
-+# modify, copy, or redistribute it subject to the terms and conditions
-+# of the GNU General Public License v.2.
-+#
-+# You should have received a copy of the GNU General Public License
-+# along with this program; if not, write to the Free Software Foundation,
-+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-+
-+test_description='duplicate pv detection of mpath components using wwid'
-+
-+SKIP_WITH_LVMPOLLD=1
-+SKIP_WITH_LVMLOCKD=1
-+
-+. lib/inittest
-+
-+# FIXME: skip until mpath/scsi_debug cleanup works after a failure
-+skip
-+
-+modprobe --dry-run scsi_debug || skip
-+multipath -l || skip
-+multipath -l | grep scsi_debug && skip
-+
-+# Turn off multipath_component_detection so that the duplicate
-+# resolution of mpath components is used.
-+aux lvmconf 'devices/multipath_component_detection = 0'
-+# Prevent wwids from being used for filtering.
-+aux lvmconf 'devices/multipath_wwids_file = "/dev/null"'
-+# Need to use /dev/mapper/mpath
-+aux lvmconf 'devices/dir = "/dev"'
-+aux lvmconf 'devices/scan = "/dev"'
-+# Could set filter to $MP and the component /dev/sd devs
-+aux lvmconf "devices/filter = [ \"a|.*|\" ]"
-+aux lvmconf "devices/global_filter = [ \"a|.*|\" ]"
-+
-+modprobe scsi_debug dev_size_mb=100 num_tgts=1 vpd_use_hostno=0 add_host=4 delay=20 max_luns=2 no_lun_0=1
-+sleep 2
-+
-+multipath -r
-+sleep 2
-+
-+MPB=$(multipath -l | grep scsi_debug | cut -f1 -d ' ')
-+echo $MPB
-+MP=/dev/mapper/$MPB
-+echo $MP
-+
-+pvcreate $MP
-+vgcreate $vg1 $MP
-+lvcreate -l1 $vg1
-+vgchange -an $vg1
-+
-+pvs |tee out
-+grep $MP out
-+for i in $(grep -H scsi_debug /sys/block/sd*/device/model | cut -f4 -d /); do
-+	not grep /dev/$i out;
-+done
-+
-+vgchange -an $vg1
-+vgremove -y $vg1
-+
-+sleep 2
-+multipath -f $MP
-+sleep 1
-+rmmod scsi_debug
-diff --git a/test/shell/multipath-config.sh b/test/shell/multipath-config.sh
-new file mode 100644
-index 000000000..ffb7d632a
---- /dev/null
-+++ b/test/shell/multipath-config.sh
-@@ -0,0 +1,171 @@
-+#!/usr/bin/env bash
-+
-+# Copyright (C) 2021 Red Hat, Inc. All rights reserved.
-+#
-+# This copyrighted material is made available to anyone wishing to use,
-+# modify, copy, or redistribute it subject to the terms and conditions
-+# of the GNU General Public License v.2.
-+#
-+# You should have received a copy of the GNU General Public License
-+# along with this program; if not, write to the Free Software Foundation,
-+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-+
-+test_description='using multipath blacklist'
-+
-+SKIP_WITH_LVMPOLLD=1
-+SKIP_WITH_LVMLOCKD=1
-+
-+. lib/inittest
-+
-+# FIXME: don't run this test by default because it destroys the
-+# local multipath config, the timing of multipath/dm/lvm interactions
-+# is fragile, and there's insufficient cleanup after a test fails.
-+skip
-+
-+systemctl stop multipathd
-+multipath -F || true
-+rm /etc/multipath/wwids || true
-+rmmod scsi_debug || true
-+rm /etc/multipath/conf.d/lvmtest.conf || true
-+
-+modprobe --dry-run scsi_debug || skip
-+multipath -l || skip
-+multipath -l | grep scsi_debug && skip
-+ls /etc/multipath/wwids && skip
-+
-+# Need to use /dev/mapper/mpath
-+aux lvmconf 'devices/dir = "/dev"'
-+aux lvmconf 'devices/scan = "/dev"'
-+# Could set filter to $MP and the component /dev/sd devs
-+aux lvmconf "devices/filter = [ \"a|.*|\" ]"
-+aux lvmconf "devices/global_filter = [ \"a|.*|\" ]"
-+
-+modprobe scsi_debug dev_size_mb=16 num_tgts=1
-+sleep 2
-+
-+# Get scsi device name created by scsi_debug.
-+# SD = sdh
-+# SD_DEV = /dev/sdh
-+
-+SD=$(grep -H scsi_debug /sys/block/sd*/device/model | cut -f4 -d /);
-+echo $SD
-+SD_DEV=/dev/$SD
-+echo $SD_DEV
-+
-+# if multipath claimed SD, then io will fail
-+#dd if=$SD_DEV of=/dev/null bs=4k count=1 iflag=direct
-+#dd if=/dev/zero of=$SD_DEV bs=4k count=1 oflag=direct
-+
-+# check if multipathd claimed the scsi dev when it appears and create mp dm device
-+sleep 2
-+multipath -l
-+# create the mp dm device
-+multipath $SD_DEV
-+
-+# Get mpath device name created by multipath.
-+# MP = mpatha
-+# MP_DEV = /dev/maper/mpatha
-+
-+MP=$(multipath -l | grep scsi_debug | cut -f1 -d ' ')
-+echo $MP
-+MP_DEV=/dev/mapper/$MP
-+echo $MP_DEV
-+
-+dd if=$MP_DEV of=/dev/null bs=4k count=1 iflag=direct
-+dd if=/dev/zero of=$MP_DEV bs=4k count=1 oflag=direct
-+
-+# Get wwid for the mp and sd dev.
-+WWID=$(multipath -l $MP_DEV | head -1 | awk '{print $2}' | tr -d ')' | tr -d '(')
-+echo $WWID
-+
-+grep $WWID /etc/multipath/wwids
-+
-+pvcreate $MP_DEV
-+vgcreate $vg1 $MP_DEV
-+
-+not pvs $SD_DEV
-+pvs $MP_DEV
-+
-+# remove mpath dm device then check that SD_DEV is
-+# filtered based on /etc/multipath/wwids instead of
-+# based on sysfs holder
-+multipath -f $MP
-+sleep 2
-+not pvs $SD_DEV
-+multipath $SD_DEV
-+sleep 2
-+multipath -l | grep $SD
-+
-+#
-+# Add the wwid to the blacklist, then restart multipath
-+# so the sd dev should no longer be used by multipath,
-+# but the sd dev wwid is still in /etc/multipath/wwids.
-+#
-+
-+mkdir /etc/multipath/conf.d/ || true
-+rm -f /etc/multipath/conf.d/lvmtest.conf
-+
-+cat <<EOF > "/etc/multipath/conf.d/lvmtest.conf"
-+blacklist {
-+	wwid $WWID
-+}
-+EOF
-+
-+cat /etc/multipath/conf.d/lvmtest.conf
-+
-+multipath -r
-+sleep 2
-+
-+grep $WWID /etc/multipath/wwids
-+
-+multipath -l |tee out
-+not grep $SD out
-+not grep $MP out
-+not grep $WWID out
-+
-+not pvs $MP_DEV
-+pvs $SD_DEV
-+vgs $vg1
-+
-+#
-+# Add the wwid to the blacklist_exceptions, in addition
-+# to the blacklist, then restart multipath so the
-+# sd dev should again be used by multipath.
-+#
-+
-+rm -f /etc/multipath/conf.d/lvmtest.conf
-+
-+cat <<EOF > "/etc/multipath/conf.d/lvmtest.conf"
-+blacklist {
-+wwid $WWID
-+}
-+blacklist_exceptions {
-+wwid $WWID
-+}
-+EOF
-+
-+cat /etc/multipath/conf.d/lvmtest.conf
-+
-+multipath -r
-+sleep 2
-+
-+grep $WWID /etc/multipath/wwids
-+
-+multipath -l |tee out
-+grep $SD out
-+grep $MP out
-+grep $WWID out
-+
-+pvs $MP_DEV
-+not pvs $SD_DEV
-+vgs $vg1
-+lvs $vg1
-+
-+sleep 2
-+vgremove -ff $vg1
-+sleep 2
-+multipath -f $MP
-+rm /etc/multipath/conf.d/lvmtest.conf
-+rm /etc/multipath/wwids
-+sleep 1
-+rmmod scsi_debug
--- 
-2.34.3
-
diff --git a/SOURCES/0025-pvcreate-overwrite-partition-header-with-f.patch b/SOURCES/0025-pvcreate-overwrite-partition-header-with-f.patch
new file mode 100644
index 0000000..e3a9f54
--- /dev/null
+++ b/SOURCES/0025-pvcreate-overwrite-partition-header-with-f.patch
@@ -0,0 +1,141 @@
+From a5c37afdca97d6565ea02bc4bc7d52f360823cd3 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Wed, 8 Sep 2021 16:30:11 -0500
+Subject: [PATCH 25/54] pvcreate: overwrite partition header with -f
+
+$ pvcreate /dev/sdc
+  Cannot use /dev/sdc: device is partitioned
+$ pvcreate -f /dev/sdc
+  Physical volume "/dev/sdc" successfully created.
+---
+ lib/commands/toolcontext.h       |  1 +
+ lib/filters/filter-partitioned.c |  3 +++
+ man/pvcreate.8_des               |  7 ++++---
+ test/shell/test-partition.sh     | 12 ++++++++++--
+ tools/toollib.c                  | 10 ++++++++++
+ 5 files changed, 28 insertions(+), 5 deletions(-)
+
+diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
+index 356c79f8a..b83883fb8 100644
+--- a/lib/commands/toolcontext.h
++++ b/lib/commands/toolcontext.h
+@@ -201,6 +201,7 @@ struct cmd_context {
+ 	unsigned ignore_device_name_mismatch:1; /* skip updating devices file names */
+ 	unsigned backup_disabled:1;		/* skip repeated debug message */
+ 	unsigned event_activation:1;		/* whether event_activation is set */
++	unsigned filter_partitioned_skip:1;	/* don't use filter-partitioned */
+ 
+ 	/*
+ 	 * Devices and filtering.
+diff --git a/lib/filters/filter-partitioned.c b/lib/filters/filter-partitioned.c
+index 642553ef2..8f468a567 100644
+--- a/lib/filters/filter-partitioned.c
++++ b/lib/filters/filter-partitioned.c
+@@ -27,6 +27,9 @@ static int _passes_partitioned_filter(struct cmd_context *cmd, struct dev_filter
+ 	if (cmd->filter_nodata_only)
+ 		return 1;
+ 
++	if (cmd->filter_partitioned_skip)
++		return 1;
++
+ 	dev->filtered_flags &= ~DEV_FILTERED_PARTITIONED;
+ 
+ 	ret = dev_is_partitioned(cmd, dev);
+diff --git a/man/pvcreate.8_des b/man/pvcreate.8_des
+index 69bd133aa..4048eb71c 100644
+--- a/man/pvcreate.8_des
++++ b/man/pvcreate.8_des
+@@ -7,9 +7,10 @@ Use \fBvgcreate\fP(8) to create a new VG on the PV, or \fBvgextend\fP(8)
+ to add the PV to an existing VG.  Use \fBpvremove\fP(8) to remove the LVM
+ disk label from the device.
+ .P
+-The force option will create a PV without confirmation.  Repeating the
+-force option (\fB-ff\fP) will forcibly create a PV, overriding checks that
+-normally prevent it, e.g. if the PV is already in a VG.
++The force option will create a PV without confirmation, and will overwrite
++partition headers.  Repeating the force option (\fB-ff\fP) will override other
++checks that would normally prevent a pvcreate, e.g. if the PV is already in a
++VG.
+ .P
+ .B Metadata location, size, and alignment
+ .P
+diff --git a/test/shell/test-partition.sh b/test/shell/test-partition.sh
+index 0e92f00db..3a45f9089 100644
+--- a/test/shell/test-partition.sh
++++ b/test/shell/test-partition.sh
+@@ -16,7 +16,6 @@
+ #
+ 
+ 
+-
+ SKIP_WITH_LVMPOLLD=1
+ 
+ LVM_TEST_CONFIG_DEVICES="types = [\"device-mapper\", 142]"
+@@ -25,7 +24,7 @@ LVM_TEST_CONFIG_DEVICES="types = [\"device-mapper\", 142]"
+ 
+ which sfdisk || skip
+ 
+-aux prepare_pvs 1 30
++aux prepare_pvs 2 30
+ 
+ pvs "$dev1"
+ 
+@@ -33,3 +32,12 @@ pvs "$dev1"
+ echo "1 2" | sfdisk --force "$dev1"
+ 
+ not pvs "$dev1"
++
++wipefs -a "$dev2"
++echo "1 2" | sfdisk --force "$dev2"
++partprobe
++not pvcreate "$dev2"
++pvcreate -f "$dev2"
++pvs "$dev2"
++pvremove "$dev2"
++
+diff --git a/tools/toollib.c b/tools/toollib.c
+index d6f48aad2..80d3de57c 100644
+--- a/tools/toollib.c
++++ b/tools/toollib.c
+@@ -5243,6 +5243,10 @@ int pvcreate_each_device(struct cmd_context *cmd,
+ 	if (cmd->enable_devices_file && !pp->is_remove)
+ 		cmd->filter_deviceid_skip = 1;
+ 
++	/* pvcreate -f overwrites partitions */
++	if (pp->force && !pp->is_remove)
++		cmd->filter_partitioned_skip = 1;
++
+ 	log_debug("Scanning and filtering device args (%u).", dm_list_size(&scan_devs));
+ 	label_scan_devs(cmd, cmd->filter, &scan_devs);
+ 
+@@ -5257,6 +5261,7 @@ int pvcreate_each_device(struct cmd_context *cmd,
+ 		}
+ 	}
+ 	cmd->filter_deviceid_skip = 0;
++	cmd->filter_partitioned_skip = 0;
+ 
+ 	/*
+ 	 * Can the command continue if some specified devices were not found?
+@@ -5469,6 +5474,9 @@ do_command:
+ 	if (cmd->enable_devices_file && !pp->is_remove)
+ 		cmd->filter_deviceid_skip = 1;
+ 
++	if (pp->force && !pp->is_remove)
++		cmd->filter_partitioned_skip = 1;
++
+ 	log_debug("Rescanning and filtering device args with exclusive open");
+ 	if (!label_scan_devs_excl(cmd, cmd->filter, &rescan_devs)) {
+ 		log_debug("Failed to rescan devs excl");
+@@ -5482,7 +5490,9 @@ do_command:
+ 			dm_list_add(&pp->arg_fail, &pd->list);
+ 		}
+ 	}
++
+ 	cmd->filter_deviceid_skip = 0;
++	cmd->filter_partitioned_skip = 0;
+ 
+ 	if (dm_list_empty(&pp->arg_process) && dm_list_empty(&remove_duplicates)) {
+ 		log_debug("No devices to process.");
+-- 
+2.34.3
+
diff --git a/SOURCES/0026-filter-mpath-handle-other-wwid-types-in-blacklist.patch b/SOURCES/0026-filter-mpath-handle-other-wwid-types-in-blacklist.patch
deleted file mode 100644
index 5dc8ccc..0000000
--- a/SOURCES/0026-filter-mpath-handle-other-wwid-types-in-blacklist.patch
+++ /dev/null
@@ -1,54 +0,0 @@
-From 1f3ce86bf26bc7173dfde516ab1ace71e0c8074b Mon Sep 17 00:00:00 2001
-From: David Teigland <teigland@redhat.com>
-Date: Mon, 6 Jun 2022 11:39:02 -0500
-Subject: [PATCH 2/2] filter-mpath: handle other wwid types in blacklist
-
-Fixes commit 494372b4eed0c8f6040e3357939eb7511ac25745
-  "filter-mpath: use multipath blacklist"
-to handle wwids with initial type digits 1 and 2 used
-for t10 and eui ids.  Originally recognized type 3 naa.
-
-(cherry picked from commit 25abb5730f4d8f79df69f0817881ffb9eed195a9)
----
- lib/device/dev-mpath.c | 11 ++++++-----
- 1 file changed, 6 insertions(+), 5 deletions(-)
-
-diff --git a/lib/device/dev-mpath.c b/lib/device/dev-mpath.c
-index 580ab31a5..3d0626fe3 100644
---- a/lib/device/dev-mpath.c
-+++ b/lib/device/dev-mpath.c
-@@ -54,7 +54,7 @@ static void _read_blacklist_file(const char *path)
- 	int section_black = 0;
- 	int section_exceptions = 0;
- 	int found_quote;
--	int found_three;
-+	int found_type;
- 	int i, j;
- 
- 	if (!(fp = fopen(path, "r")))
-@@ -114,7 +114,7 @@ static void _read_blacklist_file(const char *path)
- 
- 		memset(wwid, 0, sizeof(wwid));
- 		found_quote = 0;
--		found_three = 0;
-+		found_type = 0;
- 		j = 0;
- 
- 		for (; i < MAX_WWID_LINE; i++) {
-@@ -132,9 +132,10 @@ static void _read_blacklist_file(const char *path)
- 			/* second quote is end of wwid */
- 			if ((line[i] == '"') && found_quote)
- 				break;
--			/* ignore first "3" in wwid */
--			if ((line[i] == '3') && !found_three) {
--				found_three = 1;
-+			/* exclude initial 3/2/1 for naa/eui/t10 */
-+			if (!j && !found_type &&
-+			    ((line[i] == '3') || (line[i] == '2') || (line[i] == '1'))) {
-+				found_type = 1;
- 				continue;
- 			}
- 
--- 
-2.34.3
-
diff --git a/SOURCES/0026-lvmdevices-check-error-exit-if-update-is-needed.patch b/SOURCES/0026-lvmdevices-check-error-exit-if-update-is-needed.patch
new file mode 100644
index 0000000..485b697
--- /dev/null
+++ b/SOURCES/0026-lvmdevices-check-error-exit-if-update-is-needed.patch
@@ -0,0 +1,166 @@
+From bb477d63e336a10e5959962a9f26a028ea9e55eb Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Thu, 13 Jan 2022 14:52:54 -0600
+Subject: [PATCH 26/54] lvmdevices check: error exit if update is needed
+
+. error exit means that lvmdevices --update would make a change.
+
+. remove check of PART field from --check because it isn't used.
+
+. unlink searched_devnames file to ensure check|update will search
+---
+ lib/device/device_id.c             |  3 ++-
+ test/shell/devicesfile-realdevs.sh |  8 +++----
+ tools/args.h                       |  3 ++-
+ tools/lvmdevices.c                 | 37 +++++++++++++-----------------
+ 4 files changed, 24 insertions(+), 27 deletions(-)
+
+diff --git a/lib/device/device_id.c b/lib/device/device_id.c
+index 0621bc858..a33dcebe0 100644
+--- a/lib/device/device_id.c
++++ b/lib/device/device_id.c
+@@ -2271,7 +2271,8 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
+ 			continue;
+ 		}
+ 
+-		log_warn("Devices file PVID %s updating IDNAME to %s.", dev->pvid, devname);
++		if (!noupdate)
++			log_warn("Devices file PVID %s updating IDNAME to %s.", dev->pvid, devname);
+ 
+ 		free(du->idname);
+ 		free(du->devname);
+diff --git a/test/shell/devicesfile-realdevs.sh b/test/shell/devicesfile-realdevs.sh
+index 8d4aa3e67..23d4bedb4 100644
+--- a/test/shell/devicesfile-realdevs.sh
++++ b/test/shell/devicesfile-realdevs.sh
+@@ -423,7 +423,7 @@ sed "s/$pvid1/badpvid/" "$DF.orig" |tee $DF
+ not grep $pvid1 $DF
+ grep $did1 $DF
+ 
+-lvmdevices --check 2>&1|tee out
++not lvmdevices --check 2>&1|tee out
+ grep $dev1 out
+ grep badpvid out
+ grep $pvid1 out
+@@ -493,7 +493,7 @@ rm $DF
+ d1=$(basename $dev1)
+ d3=$(basename $dev3)
+ sed "s/$d1/$d3/" "$DF.orig" |tee $DF
+-lvmdevices --check 2>&1 |tee out
++not lvmdevices --check 2>&1 |tee out
+ grep $dev1 out
+ 
+ lvmdevices --update
+@@ -515,7 +515,7 @@ sed "s/$d1/tmp/" "$DF.orig" |tee ${DF}_1
+ sed "s/$d2/$d1/" "${DF}_1" |tee ${DF}_2
+ sed "s/tmp/$d2/" "${DF}_2" |tee $DF
+ rm ${DF}_1 ${DF}_2
+-lvmdevices --check 2>&1 |tee out
++not lvmdevices --check 2>&1 |tee out
+ grep $dev1 out
+ grep $dev2 out
+ 
+@@ -536,7 +536,7 @@ rm $DF
+ d1=$(basename $dev1)
+ d3=$(basename $dev3)
+ sed "s/$d1/$d3/" "$DF.orig" |tee $DF
+-lvmdevices --check 2>&1 |tee out
++not lvmdevices --check 2>&1 |tee out
+ grep $dev1 out
+ 
+ pvs -o+uuid,deviceid | grep $vg |tee out
+diff --git a/tools/args.h b/tools/args.h
+index 774ce33f4..9a7bf81b2 100644
+--- a/tools/args.h
++++ b/tools/args.h
+@@ -153,7 +153,8 @@ arg(cachesize_ARG, '\0', "cachesize", sizemb_VAL, 0, 0,
+     "The size of cache to use.\n")
+ 
+ arg(check_ARG, '\0', "check", 0, 0, 0,
+-    "Check the content of the devices file.\n")
++    "Checks the content of the devices file.\n"
++    "Reports incorrect device names or PVIDs for entries.\n")
+ 
+ arg(commandprofile_ARG, '\0', "commandprofile", string_VAL, 0, 0,
+     "The command profile to use for command configuration.\n"
+diff --git a/tools/lvmdevices.c b/tools/lvmdevices.c
+index 3f104f7de..c50c09f90 100644
+--- a/tools/lvmdevices.c
++++ b/tools/lvmdevices.c
+@@ -128,7 +128,6 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
+ 	struct device *dev;
+ 	struct dev_use *du, *du2;
+ 	const char *deviceidtype;
+-	int changes = 0;
+ 
+ 	dm_list_init(&search_pvids);
+ 	dm_list_init(&found_devs);
+@@ -184,8 +183,11 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
+ 
+ 	if (arg_is_set(cmd, check_ARG) || arg_is_set(cmd, update_ARG)) {
+ 		int search_count = 0;
++		int update_needed = 0;
+ 		int invalid = 0;
+ 
++		unlink_searched_devnames(cmd);
++
+ 		label_scan_setup_bcache();
+ 
+ 		dm_list_iterate_items(du, &cmd->use_devices) {
+@@ -225,6 +227,8 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
+ 		 * run just above.
+ 		 */
+ 		device_ids_validate(cmd, NULL, &invalid, 1);
++		if (invalid)
++			update_needed = 1;
+ 
+ 		/*
+ 		 * Find and fix any devname entries that have moved to a
+@@ -240,33 +244,24 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
+ 				label_scan_invalidate(du->dev);
+ 		}
+ 
+-		/*
+-		 * check du->part
+-		 */
+-		dm_list_iterate_items(du, &cmd->use_devices) {
+-			int part = 0;
+-			if (!du->dev)
+-				continue;
+-			dev = du->dev;
+-
+-			dev_get_partition_number(dev, &part);
+-
+-			if (part != du->part) {
+-				log_warn("Device %s partition %u has incorrect PART in devices file (%u)",
+-					 dev_name(dev), part, du->part);
+-				du->part = part;
+-				changes++;
+-			}
+-		}
+-
+ 		if (arg_is_set(cmd, update_ARG)) {
+-			if (invalid || !dm_list_empty(&found_devs)) {
++			if (update_needed || !dm_list_empty(&found_devs)) {
+ 				if (!device_ids_write(cmd))
+ 					goto_bad;
+ 				log_print("Updated devices file to version %s", devices_file_version());
+ 			} else {
+ 				log_print("No update for devices file is needed.");
+ 			}
++		} else {
++			/*
++			 * --check exits with an error if the devices file
++			 * needs updates, i.e. running --update would make
++			 * changes.
++			 */
++			if (update_needed) {
++				log_error("Updates needed for devices file.");
++				goto bad;
++			}
+ 		}
+ 		goto out;
+ 	}
+-- 
+2.34.3
+
diff --git a/SOURCES/0027-Revert-pvcreate-overwrite-partition-header-with-f.patch b/SOURCES/0027-Revert-pvcreate-overwrite-partition-header-with-f.patch
new file mode 100644
index 0000000..337a61d
--- /dev/null
+++ b/SOURCES/0027-Revert-pvcreate-overwrite-partition-header-with-f.patch
@@ -0,0 +1,140 @@
+From 9375aebad1db72267dd67e3ed768aa3b0e698d52 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Tue, 18 Jan 2022 12:16:52 -0600
+Subject: [PATCH 27/54] Revert "pvcreate: overwrite partition header with -f"
+
+This reverts commit a5c37afdca97d6565ea02bc4bc7d52f360823cd3.
+
+This commit did not properly recognize GPT cases.
+---
+ lib/commands/toolcontext.h       |  1 -
+ lib/filters/filter-partitioned.c |  3 ---
+ man/pvcreate.8_des               |  7 +++----
+ test/shell/test-partition.sh     | 12 ++----------
+ tools/toollib.c                  | 10 ----------
+ 5 files changed, 5 insertions(+), 28 deletions(-)
+
+diff --git a/lib/commands/toolcontext.h b/lib/commands/toolcontext.h
+index b83883fb8..356c79f8a 100644
+--- a/lib/commands/toolcontext.h
++++ b/lib/commands/toolcontext.h
+@@ -201,7 +201,6 @@ struct cmd_context {
+ 	unsigned ignore_device_name_mismatch:1; /* skip updating devices file names */
+ 	unsigned backup_disabled:1;		/* skip repeated debug message */
+ 	unsigned event_activation:1;		/* whether event_activation is set */
+-	unsigned filter_partitioned_skip:1;	/* don't use filter-partitioned */
+ 
+ 	/*
+ 	 * Devices and filtering.
+diff --git a/lib/filters/filter-partitioned.c b/lib/filters/filter-partitioned.c
+index 8f468a567..642553ef2 100644
+--- a/lib/filters/filter-partitioned.c
++++ b/lib/filters/filter-partitioned.c
+@@ -27,9 +27,6 @@ static int _passes_partitioned_filter(struct cmd_context *cmd, struct dev_filter
+ 	if (cmd->filter_nodata_only)
+ 		return 1;
+ 
+-	if (cmd->filter_partitioned_skip)
+-		return 1;
+-
+ 	dev->filtered_flags &= ~DEV_FILTERED_PARTITIONED;
+ 
+ 	ret = dev_is_partitioned(cmd, dev);
+diff --git a/man/pvcreate.8_des b/man/pvcreate.8_des
+index 4048eb71c..69bd133aa 100644
+--- a/man/pvcreate.8_des
++++ b/man/pvcreate.8_des
+@@ -7,10 +7,9 @@ Use \fBvgcreate\fP(8) to create a new VG on the PV, or \fBvgextend\fP(8)
+ to add the PV to an existing VG.  Use \fBpvremove\fP(8) to remove the LVM
+ disk label from the device.
+ .P
+-The force option will create a PV without confirmation, and will overwrite
+-partition headers.  Repeating the force option (\fB-ff\fP) will override other
+-checks that would normally prevent a pvcreate, e.g. if the PV is already in a
+-VG.
++The force option will create a PV without confirmation.  Repeating the
++force option (\fB-ff\fP) will forcibly create a PV, overriding checks that
++normally prevent it, e.g. if the PV is already in a VG.
+ .P
+ .B Metadata location, size, and alignment
+ .P
+diff --git a/test/shell/test-partition.sh b/test/shell/test-partition.sh
+index 3a45f9089..0e92f00db 100644
+--- a/test/shell/test-partition.sh
++++ b/test/shell/test-partition.sh
+@@ -16,6 +16,7 @@
+ #
+ 
+ 
++
+ SKIP_WITH_LVMPOLLD=1
+ 
+ LVM_TEST_CONFIG_DEVICES="types = [\"device-mapper\", 142]"
+@@ -24,7 +25,7 @@ LVM_TEST_CONFIG_DEVICES="types = [\"device-mapper\", 142]"
+ 
+ which sfdisk || skip
+ 
+-aux prepare_pvs 2 30
++aux prepare_pvs 1 30
+ 
+ pvs "$dev1"
+ 
+@@ -32,12 +33,3 @@ pvs "$dev1"
+ echo "1 2" | sfdisk --force "$dev1"
+ 
+ not pvs "$dev1"
+-
+-wipefs -a "$dev2"
+-echo "1 2" | sfdisk --force "$dev2"
+-partprobe
+-not pvcreate "$dev2"
+-pvcreate -f "$dev2"
+-pvs "$dev2"
+-pvremove "$dev2"
+-
+diff --git a/tools/toollib.c b/tools/toollib.c
+index 80d3de57c..d6f48aad2 100644
+--- a/tools/toollib.c
++++ b/tools/toollib.c
+@@ -5243,10 +5243,6 @@ int pvcreate_each_device(struct cmd_context *cmd,
+ 	if (cmd->enable_devices_file && !pp->is_remove)
+ 		cmd->filter_deviceid_skip = 1;
+ 
+-	/* pvcreate -f overwrites partitions */
+-	if (pp->force && !pp->is_remove)
+-		cmd->filter_partitioned_skip = 1;
+-
+ 	log_debug("Scanning and filtering device args (%u).", dm_list_size(&scan_devs));
+ 	label_scan_devs(cmd, cmd->filter, &scan_devs);
+ 
+@@ -5261,7 +5257,6 @@ int pvcreate_each_device(struct cmd_context *cmd,
+ 		}
+ 	}
+ 	cmd->filter_deviceid_skip = 0;
+-	cmd->filter_partitioned_skip = 0;
+ 
+ 	/*
+ 	 * Can the command continue if some specified devices were not found?
+@@ -5474,9 +5469,6 @@ do_command:
+ 	if (cmd->enable_devices_file && !pp->is_remove)
+ 		cmd->filter_deviceid_skip = 1;
+ 
+-	if (pp->force && !pp->is_remove)
+-		cmd->filter_partitioned_skip = 1;
+-
+ 	log_debug("Rescanning and filtering device args with exclusive open");
+ 	if (!label_scan_devs_excl(cmd, cmd->filter, &rescan_devs)) {
+ 		log_debug("Failed to rescan devs excl");
+@@ -5490,9 +5482,7 @@ do_command:
+ 			dm_list_add(&pp->arg_fail, &pd->list);
+ 		}
+ 	}
+-
+ 	cmd->filter_deviceid_skip = 0;
+-	cmd->filter_partitioned_skip = 0;
+ 
+ 	if (dm_list_empty(&pp->arg_process) && dm_list_empty(&remove_duplicates)) {
+ 		log_debug("No devices to process.");
+-- 
+2.34.3
+
diff --git a/SOURCES/0028-devices-exclude-multipath-components-based-on-matchi.patch b/SOURCES/0028-devices-exclude-multipath-components-based-on-matchi.patch
new file mode 100644
index 0000000..8646d04
--- /dev/null
+++ b/SOURCES/0028-devices-exclude-multipath-components-based-on-matchi.patch
@@ -0,0 +1,462 @@
+From 5403a6f05987b21addb50c9b056e36567d631df7 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Wed, 17 Nov 2021 17:10:45 -0600
+Subject: [PATCH 28/54] devices: exclude multipath components based on matching
+ wwid
+
+If multipath component devices get through the filter and
+cause lvm to see duplicate PVs, then check the wwid of the
+devs and drop the component devices as if they had been
+filtered.  If a dm mpath device was found among the duplicates
+then use that as the PV, otherwise do not use any of the
+components as the PV.
+
+"duplicate PVs" associated with multipath configs will no
+longer stop commands from working.
+---
+ lib/cache/lvmcache.c                  | 186 +++++++++++++++++++++++++-
+ lib/device/dev-mpath.c                |  71 ++++++++++
+ lib/device/dev-type.h                 |   2 +
+ lib/device/device_id.c                |   4 +-
+ lib/device/device_id.h                |   2 +
+ test/shell/duplicate-pvs-multipath.sh |  67 ++++++++++
+ 6 files changed, 323 insertions(+), 9 deletions(-)
+ create mode 100644 test/shell/duplicate-pvs-multipath.sh
+
+diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
+index bee63ebb4..a0811d4ea 100644
+--- a/lib/cache/lvmcache.c
++++ b/lib/cache/lvmcache.c
+@@ -625,6 +625,102 @@ static void _warn_unused_duplicates(struct cmd_context *cmd)
+ 	}
+ }
+ 
++static int _all_multipath_components(struct cmd_context *cmd, struct lvmcache_info *info, const char *pvid,
++				     struct dm_list *altdevs, struct device **dev_mpath)
++{
++	struct device_list *devl;
++	struct device *dev_mp = NULL;
++	struct device *dev1 = NULL;
++	struct device *dev;
++	const char *wwid1 = NULL;
++	const char *wwid;
++	int diff_wwid = 0;
++	int same_wwid = 0;
++	int dev_is_mp;
++
++	*dev_mpath = NULL;
++
++	/* This function only makes sense with more than one dev. */
++	if ((info && dm_list_empty(altdevs)) || (!info && (dm_list_size(altdevs) == 1))) {
++		log_debug("Skip multipath component checks with single device for PVID %s", pvid);
++		return 0;
++	}
++
++	log_debug("Checking for multipath components for duplicate PVID %s", pvid);
++
++	if (info) {
++		dev = info->dev;
++		dev_is_mp = (cmd->dev_types->device_mapper_major == MAJOR(dev->dev)) && dev_has_mpath_uuid(cmd, dev, NULL);
++
++		if (dev_is_mp) {
++			if ((wwid1 = dev_mpath_component_wwid(cmd, dev))) {
++				dev_mp = dev;
++				dev1 = dev;
++			}
++		} else {
++			if ((wwid1 = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_WWID)))
++				dev1 = dev;
++		}
++	}
++
++	dm_list_iterate_items(devl, altdevs) {
++		dev = devl->dev;
++		dev_is_mp = (cmd->dev_types->device_mapper_major == MAJOR(dev->dev)) && dev_has_mpath_uuid(cmd, dev, NULL);
++
++		if (dev_is_mp)
++			wwid = dev_mpath_component_wwid(cmd, dev);
++		else
++			wwid = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_WWID);
++
++		if (!wwid && wwid1) {
++			log_print("Different wwids for duplicate PVs %s %s %s none",
++				  dev_name(dev1), wwid1, dev_name(dev));
++			diff_wwid++;
++			continue;
++		}
++
++		if (!wwid)
++			continue;
++
++		if (!wwid1) {
++			wwid1 = wwid;
++			dev1 = dev;
++			continue;
++		}
++
++		/* Different wwids indicates these are not multipath components. */
++		if (strcmp(wwid1, wwid)) {
++			log_print("Different wwids for duplicate PVs %s %s %s %s",
++				  dev_name(dev1), wwid1, dev_name(dev), wwid);
++			diff_wwid++;
++			continue;
++		}
++
++		/* Different mpath devs with the same wwid shouldn't happen. */
++		if (dev_is_mp && dev_mp) {
++			log_print("Found multiple multipath devices for PVID %s WWID %s: %s %s",
++				   pvid, wwid1, dev_name(dev_mp), dev_name(dev));
++			continue;
++		}
++
++		log_debug("Same wwids for duplicate PVs %s %s", dev_name(dev1), dev_name(dev));
++		same_wwid++;
++
++		/* Save the mpath device so it can be used as the PV. */
++		if (dev_is_mp)
++			dev_mp = dev;
++	}
++
++	if (diff_wwid || !same_wwid)
++		return 0;
++
++	if (dev_mp)
++		log_debug("Found multipath device %s for PVID %s WWID %s.", dev_name(dev_mp), pvid, wwid1);
++
++	*dev_mpath = dev_mp;
++	return 1;
++}
++
+ /*
+  * If we've found devices with the same PVID, decide which one
+  * to use.
+@@ -680,6 +776,8 @@ static void _choose_duplicates(struct cmd_context *cmd,
+ 	struct device_list *devl, *devl_safe, *devl_add, *devl_del;
+ 	struct lvmcache_info *info;
+ 	struct device *dev1, *dev2;
++	struct device *dev_mpath;
++	struct device *dev_drop;
+ 	const char *device_id = NULL, *device_id_type = NULL;
+ 	const char *idname1 = NULL, *idname2 = NULL;
+ 	uint32_t dev1_major, dev1_minor, dev2_major, dev2_minor;
+@@ -702,6 +800,7 @@ static void _choose_duplicates(struct cmd_context *cmd,
+ next:
+ 	dm_list_init(&altdevs);
+ 	pvid = NULL;
++	dev_mpath = NULL;
+ 
+ 	dm_list_iterate_items_safe(devl, devl_safe, &_initial_duplicates) {
+ 		if (!pvid) {
+@@ -720,23 +819,97 @@ next:
+ 		return;
+ 	}
+ 
++	info = lvmcache_info_from_pvid(pvid, NULL, 0);
++
+ 	/*
+-	 * Get rid of any md components before comparing alternatives.
+-	 * (Since an md component can never be used, it's not an
+-	 * option to use like other kinds of alternatives.)
++	 * Usually and ideally, components of md and multipath devs should have
++	 * been excluded by filters, and not scanned for a PV.  In some unusual
++	 * cases the components can get through the filters, and a PV can be
++	 * found on them.  Detecting the same PVID on both the component and
++	 * the md/mpath device gives us a last chance to drop the component.
++	 * An md/mpath component device is completely ignored, as if it had
++	 * been filtered, and not kept in the list unused duplicates.
+ 	 */
+ 
+-	info = lvmcache_info_from_pvid(pvid, NULL, 0);
++	/*
++	 * Get rid of multipath components based on matching wwids.
++	 */
++	if (_all_multipath_components(cmd, info, pvid, &altdevs, &dev_mpath)) {
++		if (info && dev_mpath && (info->dev != dev_mpath)) {
++			/*
++			 * info should be dropped from lvmcache and info->dev
++			 * should be treated as if it had been excluded by a filter.
++			 * dev_mpath should be added to lvmcache by the caller.
++			 */
++			dev_drop = info->dev;
++
++			/* Have caller add dev_mpath to lvmcache. */
++			log_debug("Using multipath device %s for PVID %s.", dev_name(dev_mpath), pvid);
++			if ((devl_add = zalloc(sizeof(*devl_add)))) {
++				devl_add->dev = dev_mpath;
++				dm_list_add(add_cache_devs, &devl_add->list);
++			}
++
++			/* Remove dev_mpath from altdevs. */
++			if ((devl = _get_devl_in_device_list(dev_mpath, &altdevs)))
++				dm_list_del(&devl->list);
++
++			/* Remove info from lvmcache that came from the component dev. */
++			log_debug("Ignoring multipath component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
++			lvmcache_del(info);
++			info = NULL;
++
++			/* Make the component dev look like it was filtered. */
++			cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
++			dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
++		}
++
++		if (info && !dev_mpath) {
++			/*
++			 * Only mpath component devs were found and no actual
++			 * multipath dev, so drop the component from lvmcache.
++			 */
++			dev_drop = info->dev;
++
++			log_debug("Ignoring multipath component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
++			lvmcache_del(info);
++			info = NULL;
++
++			/* Make the component dev look like it was filtered. */
++			cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
++			dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
++		}
++
++		dm_list_iterate_items_safe(devl, devl_safe, &altdevs) {
++			/*
++			 * The altdevs are all mpath components that should look
++			 * like they were filtered, they are not in lvmcache.
++			 */
++			dev_drop = devl->dev;
++
++			log_debug("Ignoring multipath component %s with PVID %s (dropping duplicate)", dev_name(dev_drop), pvid);
++			dm_list_del(&devl->list);
++
++			cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
++			dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
++		}
++		goto next;
++	}
++
++	/*
++	 * Get rid of any md components.
++	 * FIXME: use a function like _all_multipath_components to pick the actual md device.
++	 */
+ 	if (info && dev_is_md_component(cmd, info->dev, NULL, 1)) {
+ 		/* does not go in del_cache_devs which become unused_duplicates */
+-		log_debug_cache("PV %s drop MD component from scan selection %s", pvid, dev_name(info->dev));
++		log_debug("Ignoring md component %s with PVID %s (dropping info)", dev_name(info->dev), pvid);
+ 		lvmcache_del(info);
+ 		info = NULL;
+ 	}
+ 
+ 	dm_list_iterate_items_safe(devl, devl_safe, &altdevs) {
+ 		if (dev_is_md_component(cmd, devl->dev, NULL, 1)) {
+-			log_debug_cache("PV %s drop MD component from scan duplicates %s", pvid, dev_name(devl->dev));
++			log_debug("Ignoring md component %s with PVID %s (dropping duplicate)", dev_name(devl->dev), pvid);
+ 			dm_list_del(&devl->list);
+ 		}
+ 	}
+@@ -744,7 +917,6 @@ next:
+ 	if (dm_list_empty(&altdevs))
+ 		goto next;
+ 
+-
+ 	/*
+ 	 * Find the device for the pvid that's currently in lvmcache.
+ 	 */
+diff --git a/lib/device/dev-mpath.c b/lib/device/dev-mpath.c
+index ba7bf9740..cbbad9dc9 100644
+--- a/lib/device/dev-mpath.c
++++ b/lib/device/dev-mpath.c
+@@ -482,3 +482,74 @@ found:
+ 	return 1;
+ }
+ 
++const char *dev_mpath_component_wwid(struct cmd_context *cmd, struct device *dev)
++{
++	char slaves_path[PATH_MAX];
++	char wwid_path[PATH_MAX];
++	char sysbuf[PATH_MAX] = { 0 };
++	char *slave_name;
++	const char *wwid = NULL;
++	struct stat info;
++	DIR *dr;
++	struct dirent *de;
++
++	/* /sys/dev/block/253:7/slaves/sda/device/wwid */
++
++	if (dm_snprintf(slaves_path, sizeof(slaves_path), "%s/dev/block/%d:%d/slaves",
++			dm_sysfs_dir(), (int)MAJOR(dev->dev), (int)MINOR(dev->dev)) < 0) {
++		log_warn("Sysfs path to check mpath components is too long.");
++		return NULL;
++	}
++
++	if (stat(slaves_path, &info))
++		return NULL;
++
++	if (!S_ISDIR(info.st_mode)) {
++		log_warn("Path %s is not a directory.", slaves_path);
++		return NULL;
++	}
++
++	/* Get wwid from first component */
++
++	if (!(dr = opendir(slaves_path))) {
++		log_debug("Device %s has no slaves dir", dev_name(dev));
++		return NULL;
++	}
++
++	while ((de = readdir(dr))) {
++		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
++			continue;
++
++		/* slave_name "sda" */
++		slave_name = de->d_name;
++
++		/* read /sys/block/sda/device/wwid */
++
++		if (dm_snprintf(wwid_path, sizeof(wwid_path), "%s/block/%s/device/wwid",
++       				dm_sysfs_dir(), slave_name) < 0) {
++			log_warn("Failed to create sysfs wwid path for %s", slave_name);
++			continue;
++		}
++
++		get_sysfs_value(wwid_path, sysbuf, sizeof(sysbuf), 0);
++		if (!sysbuf[0])
++			continue;
++
++		if (strstr(sysbuf, "scsi_debug")) {
++			int i;
++			for (i = 0; i < strlen(sysbuf); i++) {
++				if (sysbuf[i] == ' ')
++					sysbuf[i] = '_';
++			}
++		}
++
++		if ((wwid = dm_pool_strdup(cmd->mem, sysbuf)))
++			break;
++	}
++	if (closedir(dr))
++		stack;
++
++	return wwid;
++}
++
++
+diff --git a/lib/device/dev-type.h b/lib/device/dev-type.h
+index f3521c6e0..36fb8f258 100644
+--- a/lib/device/dev-type.h
++++ b/lib/device/dev-type.h
+@@ -63,6 +63,8 @@ int dev_is_swap(struct cmd_context *cmd, struct device *dev, uint64_t *signature
+ int dev_is_luks(struct cmd_context *cmd, struct device *dev, uint64_t *signature, int full);
+ int dasd_is_cdl_formatted(struct device *dev);
+ 
++const char *dev_mpath_component_wwid(struct cmd_context *cmd, struct device *dev);
++
+ int dev_is_lvm1(struct device *dev, char *buf, int buflen);
+ int dev_is_pool(struct device *dev, char *buf, int buflen);
+ 
+diff --git a/lib/device/device_id.c b/lib/device/device_id.c
+index a33dcebe0..625576ec6 100644
+--- a/lib/device/device_id.c
++++ b/lib/device/device_id.c
+@@ -243,7 +243,7 @@ static int _dm_uuid_has_prefix(char *sysbuf, const char *prefix)
+ }
+ 
+ /* the dm uuid uses the wwid of the underlying dev */
+-static int _dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out)
++int dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out)
+ {
+ 	char sysbuf[PATH_MAX] = { 0 };
+ 	const char *idname;
+@@ -988,7 +988,7 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
+ 	}
+ 
+ 	if (MAJOR(dev->dev) == cmd->dev_types->device_mapper_major) {
+-		if (_dev_has_mpath_uuid(cmd, dev, &idname)) {
++		if (dev_has_mpath_uuid(cmd, dev, &idname)) {
+ 			idtype = DEV_ID_TYPE_MPATH_UUID;
+ 			goto id_done;
+ 		}
+diff --git a/lib/device/device_id.h b/lib/device/device_id.h
+index 939b3a0f4..4cf1374c8 100644
+--- a/lib/device/device_id.h
++++ b/lib/device/device_id.h
+@@ -55,4 +55,6 @@ void unlink_searched_devnames(struct cmd_context *cmd);
+ 
+ int read_sys_block(struct cmd_context *cmd, struct device *dev, const char *suffix, char *sysbuf, int sysbufsize);
+ 
++int dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out);
++
+ #endif
+diff --git a/test/shell/duplicate-pvs-multipath.sh b/test/shell/duplicate-pvs-multipath.sh
+new file mode 100644
+index 000000000..a145e4afb
+--- /dev/null
++++ b/test/shell/duplicate-pvs-multipath.sh
+@@ -0,0 +1,67 @@
++#!/usr/bin/env bash
++
++# Copyright (C) 2021 Red Hat, Inc. All rights reserved.
++#
++# This copyrighted material is made available to anyone wishing to use,
++# modify, copy, or redistribute it subject to the terms and conditions
++# of the GNU General Public License v.2.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software Foundation,
++# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++
++test_description='udev rule and systemd unit run vgchange'
++
++SKIP_WITH_LVMPOLLD=1
++SKIP_WITH_LVMLOCKD=1
++
++. lib/inittest
++
++# FIXME: skip until mpath/scsi_debug cleanup works after a failure
++skip
++
++modprobe --dry-run scsi_debug || skip
++multipath -l || skip
++multipath -l | grep scsi_debug && skip
++
++# Turn off multipath_component_detection so that the duplicate
++# resolution of mpath components is used.
++aux lvmconf 'devices/multipath_component_detection = 0'
++# Prevent wwids from being used for filtering.
++aux lvmconf 'devices/multipath_wwids_file = "/dev/null"'
++# Need to use /dev/mapper/mpath
++aux lvmconf 'devices/dir = "/dev"'
++aux lvmconf 'devices/scan = "/dev"'
++# Could set filter to $MP and the component /dev/sd devs
++aux lvmconf "devices/filter = [ \"a|.*|\" ]"
++aux lvmconf "devices/global_filter = [ \"a|.*|\" ]"
++
++modprobe scsi_debug dev_size_mb=100 num_tgts=1 vpd_use_hostno=0 add_host=4 delay=20 max_luns=2 no_lun_0=1
++sleep 2
++
++multipath -r
++sleep 2
++
++MPB=$(multipath -l | grep scsi_debug | cut -f1 -d ' ')
++echo $MPB
++MP=/dev/mapper/$MPB
++echo $MP
++
++pvcreate $MP
++vgcreate $vg1 $MP
++lvcreate -l1 $vg1
++vgchange -an $vg1
++
++pvs |tee out
++grep $MP out
++for i in $(grep -H scsi_debug /sys/block/sd*/device/model | cut -f4 -d /); do
++	not grep /dev/$i out;
++done
++
++vgchange -an $vg1
++vgremove -y $vg1
++
++sleep 2
++multipath -f $MP
++sleep 1
++rmmod scsi_debug
+-- 
+2.34.3
+
diff --git a/SOURCES/0029-devices-exclude-md-components-when-duplicate-pvs-are.patch b/SOURCES/0029-devices-exclude-md-components-when-duplicate-pvs-are.patch
new file mode 100644
index 0000000..6dbb455
--- /dev/null
+++ b/SOURCES/0029-devices-exclude-md-components-when-duplicate-pvs-are.patch
@@ -0,0 +1,268 @@
+From 7b79acc6161b2cff81a03848c160dd6993a4477b Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Mon, 22 Nov 2021 15:10:43 -0600
+Subject: [PATCH 29/54] devices: exclude md components when duplicate pvs are
+ seen
+
+Improve handling of md components that get through the
+filter, like the previous improvement for multipath.
+If md components get through the filter and trigger
+duplicate PV code, then eliminate any devs entirely
+that are not an md device.
+---
+ lib/cache/lvmcache.c | 168 ++++++++++++++++++++++++++++++++++++++-----
+ 1 file changed, 149 insertions(+), 19 deletions(-)
+
+diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
+index a0811d4ea..0e62cd267 100644
+--- a/lib/cache/lvmcache.c
++++ b/lib/cache/lvmcache.c
+@@ -673,7 +673,7 @@ static int _all_multipath_components(struct cmd_context *cmd, struct lvmcache_in
+ 			wwid = device_id_system_read(cmd, dev, DEV_ID_TYPE_SYS_WWID);
+ 
+ 		if (!wwid && wwid1) {
+-			log_print("Different wwids for duplicate PVs %s %s %s none",
++			log_debug("Different wwids for duplicate PVs %s %s %s none",
+ 				  dev_name(dev1), wwid1, dev_name(dev));
+ 			diff_wwid++;
+ 			continue;
+@@ -690,7 +690,7 @@ static int _all_multipath_components(struct cmd_context *cmd, struct lvmcache_in
+ 
+ 		/* Different wwids indicates these are not multipath components. */
+ 		if (strcmp(wwid1, wwid)) {
+-			log_print("Different wwids for duplicate PVs %s %s %s %s",
++			log_debug("Different wwids for duplicate PVs %s %s %s %s",
+ 				  dev_name(dev1), wwid1, dev_name(dev), wwid);
+ 			diff_wwid++;
+ 			continue;
+@@ -721,6 +721,52 @@ static int _all_multipath_components(struct cmd_context *cmd, struct lvmcache_in
+ 	return 1;
+ }
+ 
++static int _all_md_components(struct cmd_context *cmd, struct lvmcache_info *info, const char *pvid,
++			      struct dm_list *altdevs, struct device **dev_md_out)
++{
++	struct device_list *devl;
++	struct device *dev_md = NULL;
++	struct device *dev;
++	int real_dup = 0;
++ 
++	*dev_md_out = NULL;
++
++	/* There will often be no info struct because of the extra_md_checks function. */
++ 
++	if (info && (cmd->dev_types->md_major == MAJOR(info->dev->dev)))
++		dev_md = info->dev;
++ 
++	dm_list_iterate_items(devl, altdevs) {
++		dev = devl->dev;
++ 
++		if (cmd->dev_types->md_major == MAJOR(dev->dev)) {
++			if (dev_md) {
++				/* md devs themselves are dups */
++				log_debug("Found multiple md devices for PVID %s: %s %s",
++					  pvid, dev_name(dev_md), dev_name(dev));
++				real_dup = 1;
++				break;
++			} else
++				dev_md = dev;
++		} else {
++			if (!dev_is_md_component(cmd, dev, NULL, 1)) {
++				/* md dev copied to another device */
++				real_dup = 1;
++				break;
++			}
++		}
++	}
++ 
++	if (real_dup)
++		return 0;
++ 
++	if (dev_md)
++		log_debug("Found md device %s for PVID %s.", dev_name(dev_md), pvid);
++ 
++	*dev_md_out = dev_md;
++	return 1;
++}
++
+ /*
+  * If we've found devices with the same PVID, decide which one
+  * to use.
+@@ -776,7 +822,7 @@ static void _choose_duplicates(struct cmd_context *cmd,
+ 	struct device_list *devl, *devl_safe, *devl_add, *devl_del;
+ 	struct lvmcache_info *info;
+ 	struct device *dev1, *dev2;
+-	struct device *dev_mpath;
++	struct device *dev_mpath, *dev_md;
+ 	struct device *dev_drop;
+ 	const char *device_id = NULL, *device_id_type = NULL;
+ 	const char *idname1 = NULL, *idname2 = NULL;
+@@ -801,6 +847,7 @@ next:
+ 	dm_list_init(&altdevs);
+ 	pvid = NULL;
+ 	dev_mpath = NULL;
++	dev_md = NULL;
+ 
+ 	dm_list_iterate_items_safe(devl, devl_safe, &_initial_duplicates) {
+ 		if (!pvid) {
+@@ -829,6 +876,11 @@ next:
+ 	 * the md/mpath device gives us a last chance to drop the component.
+ 	 * An md/mpath component device is completely ignored, as if it had
+ 	 * been filtered, and not kept in the list unused duplicates.
++	 *
++	 * One issue related to eliminating mpath/md duplicate PVs here is
++	 * that it occurs after label_scan, and hints are created based
++	 * on what label_scan finds, so hints are disabled due to duplicate
++	 * PVs that are later resolved here.
+ 	 */
+ 
+ 	/*
+@@ -898,24 +950,89 @@ next:
+ 
+ 	/*
+ 	 * Get rid of any md components.
+-	 * FIXME: use a function like _all_multipath_components to pick the actual md device.
+ 	 */
+-	if (info && dev_is_md_component(cmd, info->dev, NULL, 1)) {
+-		/* does not go in del_cache_devs which become unused_duplicates */
+-		log_debug("Ignoring md component %s with PVID %s (dropping info)", dev_name(info->dev), pvid);
+-		lvmcache_del(info);
+-		info = NULL;
+-	}
++	if (_all_md_components(cmd, info, pvid, &altdevs, &dev_md)) {
++		if (info && dev_md && (info->dev != dev_md)) {
++			/*
++			 * info should be dropped from lvmcache and info->dev
++			 * should be treated as if it had been excluded by a filter.
++			 * dev_md should be added to lvmcache by the caller.
++			 * Often this info struct has been removed by
++			 * lvmcache_extra_md_component_checks.
++			 */
++			dev_drop = info->dev;
+ 
+-	dm_list_iterate_items_safe(devl, devl_safe, &altdevs) {
+-		if (dev_is_md_component(cmd, devl->dev, NULL, 1)) {
+-			log_debug("Ignoring md component %s with PVID %s (dropping duplicate)", dev_name(devl->dev), pvid);
+-			dm_list_del(&devl->list);
++			/* Have caller add dev_md to lvmcache. */
++			log_debug("Using md device %s for PVID %s.", dev_name(dev_md), pvid);
++			if ((devl_add = zalloc(sizeof(*devl_add)))) {
++				devl_add->dev = dev_md;
++				dm_list_add(add_cache_devs, &devl_add->list);
++			}
++
++			/* Remove dev_md from altdevs. */
++			if ((devl = _get_devl_in_device_list(dev_md, &altdevs)))
++				dm_list_del(&devl->list);
++
++			/* Remove info from lvmcache that came from the component dev. */
++			log_debug("Ignoring md component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
++			lvmcache_del(info);
++			info = NULL;
++
++			/* Make the component dev look like it was filtered. */
++			cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
++			dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
+ 		}
+-	}
+ 
+-	if (dm_list_empty(&altdevs))
++		if (!info && dev_md) {
++			/*
++			 * The info struct was from a component and was dropped
++			 * and the actual md dev was found on initial_duplicates
++			 * and the caller should add it to lvmcache.
++			 */
++
++			/* Have caller add dev_md to lvmcache. */
++			log_debug("Using md device %s for PVID %s.", dev_name(dev_md), pvid);
++			if ((devl_add = zalloc(sizeof(*devl_add)))) {
++				devl_add->dev = dev_md;
++				dm_list_add(add_cache_devs, &devl_add->list);
++			}
++
++			/* Remove dev_md from altdevs. */
++			if ((devl = _get_devl_in_device_list(dev_md, &altdevs)))
++				dm_list_del(&devl->list);
++		}
++
++		if (info && !dev_md) {
++			/*
++			 * Only md component devs were found and no actual
++			 * md dev, so drop the component from lvmcache.
++			 */
++			dev_drop = info->dev;
++
++			log_debug("Ignoring md component %s with PVID %s (dropping info)", dev_name(dev_drop), pvid);
++			lvmcache_del(info);
++			info = NULL;
++
++			/* Make the component dev look like it was filtered. */
++			cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
++			dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
++		}
++
++		dm_list_iterate_items_safe(devl, devl_safe, &altdevs) {
++			/*
++			 * The altdevs are all md components that should look
++			 * like they were filtered, they are not in lvmcache.
++			 */
++			dev_drop = devl->dev;
++
++			log_debug("Ignoring md component %s with PVID %s (dropping duplicate)", dev_name(dev_drop), pvid);
++			dm_list_del(&devl->list);
++
++			cmd->filter->wipe(cmd, cmd->filter, dev_drop, NULL);
++			dev_drop->flags &= ~DEV_SCAN_FOUND_LABEL;
++		}
+ 		goto next;
++	}
+ 
+ 	/*
+ 	 * Find the device for the pvid that's currently in lvmcache.
+@@ -1321,6 +1438,18 @@ int lvmcache_label_reopen_vg_rw(struct cmd_context *cmd, const char *vgname, con
+  * times it can be a clue that label_scan mistakenly read the pv from an md
+  * component device instead of from the md device itself.  So for unmatching
+  * sizes, we do a full md component check on the device.
++ *
++ * It might be nice to do this checking in the filter (when passes_filter is
++ * called after the initial read), but that doesn't work because passes_filter
++ * is called before _text_read so metadata/pvsummary info is not yet available
++ * which this function uses.
++ *
++ * The unique value of this function is that it can eliminate md components
++ * without there being duplicate PVs.  But, there will often be duplicate PVs,
++ * handled by _all_md_components(), where other devs with the same pvid will be
++ * in _initial_duplicates.  One could be the md device itself which will be
++ * added to lvmcache by choose_duplicates, and other duplicates that are
++ * components will be dropped.
+  */
+ 
+ void lvmcache_extra_md_component_checks(struct cmd_context *cmd)
+@@ -1382,7 +1511,8 @@ void lvmcache_extra_md_component_checks(struct cmd_context *cmd)
+ 			 */
+ 			if (pvsize && devsize && (pvsize != devsize))
+ 				do_check_size = 1;
+-			if (device_hint && !strncmp(device_hint, "/dev/md", 7))
++			if (device_hint && !strncmp(device_hint, "/dev/md", 7) &&
++			    (MAJOR(info->dev->dev) != cmd->dev_types->md_major))
+ 				do_check_name = 1;
+ 
+ 			if (!do_check_size && !do_check_name)
+@@ -1412,11 +1542,11 @@ void lvmcache_extra_md_component_checks(struct cmd_context *cmd)
+ 				  device_hint ?: "none", dev_name(dev));
+ 
+ 			if (dev_is_md_component(cmd, dev, NULL, 1)) {
+-				log_debug("dropping PV from md component %s", dev_name(dev));
++				log_debug("Ignoring PV from md component %s with PVID %s (metadata %s %llu)",
++					  dev_name(dev), dev->pvid, device_hint ?: "none", (unsigned long long)pvsize);
+ 				dev->flags &= ~DEV_SCAN_FOUND_LABEL;
+ 				/* lvmcache_del will also delete vginfo if info was last one */
+ 				lvmcache_del(info);
+-				lvmcache_del_dev_from_duplicates(dev);
+ 				cmd->filter->wipe(cmd, cmd->filter, dev, NULL);
+ 			}
+ 		}
+-- 
+2.34.3
+
diff --git a/SOURCES/0030-lvmdevices-fix-checks-when-adding-entries.patch b/SOURCES/0030-lvmdevices-fix-checks-when-adding-entries.patch
new file mode 100644
index 0000000..75eb9b1
--- /dev/null
+++ b/SOURCES/0030-lvmdevices-fix-checks-when-adding-entries.patch
@@ -0,0 +1,246 @@
+From 4e72068216b006edc69c8bafba5198051e3ed1dd Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Tue, 25 Jan 2022 11:35:36 -0600
+Subject: [PATCH 30/54] lvmdevices: fix checks when adding entries
+
+Removes some incorrect and unnecessary checks for other entries
+when adding a new devices.  The removed checks and corrections were
+mostly redundant with what is already done by device id matching.
+Other checking is reworked so the warnings are a bit different.
+---
+ lib/device/device_id.c | 153 +++++++++++++----------------------------
+ 1 file changed, 48 insertions(+), 105 deletions(-)
+
+diff --git a/lib/device/device_id.c b/lib/device/device_id.c
+index 625576ec6..ccc5f43a1 100644
+--- a/lib/device/device_id.c
++++ b/lib/device/device_id.c
+@@ -935,6 +935,10 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
+ 	struct dev_use *du, *update_du = NULL, *du_dev, *du_pvid, *du_devname, *du_devid;
+ 	struct dev_id *id;
+ 	int found_id = 0;
++	int part = 0;
++
++	if (!dev_get_partition_number(dev, &part))
++		return_0;
+ 
+ 	/*
+ 	 * When enable_devices_file=0 and pending_devices_file=1 we let
+@@ -953,10 +957,6 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
+ 	 */
+ 	memcpy(&pvid, pvid_arg, ID_LEN);
+ 
+-	du_dev = get_du_for_dev(cmd, dev);
+-	du_pvid = get_du_for_pvid(cmd, pvid);
+-	du_devname = _get_du_for_devname(cmd, dev_name(dev));
+-
+ 	/*
+ 	 * Choose the device_id type for the device being added.
+ 	 *
+@@ -1072,6 +1072,9 @@ id_done:
+ 	idtype = 0;
+ 
+ 	/*
++	 * "dev" is the device we are adding.
++	 * "id" is the device_id it's using, set in dev->id.
++	 *
+ 	 * Update the cmd->use_devices list for the new device.  The
+ 	 * use_devices list will be used to update the devices file.
+ 	 *
+@@ -1083,23 +1086,57 @@ id_done:
+ 	 * those other entries to fix any incorrect info.
+ 	 */
+ 
++	/* Is there already an entry matched to this device? */
++	du_dev = get_du_for_dev(cmd, dev);
++
++	/* Is there already an entry matched to this device's pvid? */
++	du_pvid = get_du_for_pvid(cmd, pvid);
++
++	/* Is there already an entry using this device's name? */
++	du_devname = _get_du_for_devname(cmd, dev_name(dev));
++
++	/* Is there already an entry using the device_id for this device? */
+ 	du_devid = _get_du_for_device_id(cmd, id->idtype, id->idname);
+ 
+ 	if (du_dev)
+-		log_debug("device_id_add %s pvid %s matches du_dev %p dev %s",
++		log_debug("device_id_add %s pvid %s matches entry %p dev %s",
+ 			  dev_name(dev), pvid, du_dev, dev_name(du_dev->dev));
+ 	if (du_pvid)
+-		log_debug("device_id_add %s pvid %s matches du_pvid %p dev %s pvid %s",
++		log_debug("device_id_add %s pvid %s matches entry %p dev %s with same pvid %s",
+ 			  dev_name(dev), pvid, du_pvid, du_pvid->dev ? dev_name(du_pvid->dev) : ".",
+ 			  du_pvid->pvid);
+ 	if (du_devid)
+-		log_debug("device_id_add %s pvid %s matches du_devid %p dev %s pvid %s",
++		log_debug("device_id_add %s pvid %s matches entry %p dev %s with same device_id %d %s",
+ 			  dev_name(dev), pvid, du_devid, du_devid->dev ? dev_name(du_devid->dev) : ".",
+-			  du_devid->pvid);
++			  du_devid->idtype, du_devid->idname);
+ 	if (du_devname)
+-		log_debug("device_id_add %s pvid %s matches du_devname %p dev %s pvid %s",
++		log_debug("device_id_add %s pvid %s matches entry %p dev %s with same devname %s",
+ 			  dev_name(dev), pvid, du_devname, du_devname->dev ? dev_name(du_devname->dev) : ".",
+-			  du_devname->pvid);
++			  du_devname->devname);
++
++	if (du_pvid && (du_pvid->dev != dev))
++		log_warn("WARNING: adding device %s with PVID %s which is already used for %s.",
++			 dev_name(dev), pvid, du_pvid->dev ? dev_name(du_pvid->dev) : "missing device");
++
++	if (du_devid && (du_devid->dev != dev)) {
++		if (!du_devid->dev) {
++			log_warn("WARNING: adding device %s with idname %s which is already used for missing device.",
++				 dev_name(dev), id->idname);
++		} else {
++			int ret1, ret2;
++			dev_t devt1, devt2;
++			/* Check if both entries are partitions of the same device. */
++			ret1 = dev_get_primary_dev(cmd->dev_types, dev, &devt1);
++			ret2 = dev_get_primary_dev(cmd->dev_types, du_devid->dev, &devt2);
++			if ((ret1 == 2) && (ret2 == 2) && (devt1 == devt2)) {
++				log_debug("Using separate entries for partitions of same device %s part %d %s part %d.",
++					  dev_name(dev), part, dev_name(du_devid->dev), du_devid->part);
++			} else {
++				log_warn("WARNING: adding device %s with idname %s which is already used for %s.",
++					 dev_name(dev), id->idname, dev_name(du_devid->dev));
++			}
++		}
++	}
+ 
+ 	/*
+ 	 * If one of the existing entries (du_dev, du_pvid, du_devid, du_devname)
+@@ -1112,29 +1149,6 @@ id_done:
+ 		dm_list_del(&update_du->list);
+ 		update_matching_kind = "device";
+ 		update_matching_name = dev_name(dev);
+-
+-		if (du_devid && (du_devid != du_dev)) {
+-			log_warn("WARNING: device %s (%s) and %s (%s) have duplicate device ID.",
+-				 dev_name(dev), id->idname,
+-				 (du_pvid && du_pvid->dev) ? dev_name(du_pvid->dev) : "none",
+-				 du_pvid ? du_pvid->idname : "");
+-		}
+-
+-		if (du_pvid && (du_pvid != du_dev)) {
+-			log_warn("WARNING: device %s (%s) and %s (%s) have duplicate PVID %s",
+-				 dev_name(dev), id->idname,
+-				 du_pvid->dev ? dev_name(du_pvid->dev) : "none", du_pvid->idname,
+-				 pvid);
+-		}
+-
+-		if (du_devname && (du_devname != du_dev)) {
+-			/* clear devname in another entry with our devname */
+-			log_warn("Devices file PVID %s clearing wrong DEVNAME %s.",
+-				 du_devname->pvid, du_devname->devname);
+-			free(du_devname->devname);
+-			du_devname->devname = NULL;
+-		}
+-
+ 	} else if (du_pvid) {
+ 		/*
+ 		 * If the device_id of the existing entry for PVID is the same
+@@ -1154,11 +1168,6 @@ id_done:
+ 			update_matching_kind = "PVID";
+ 			update_matching_name = pvid;
+ 		} else {
+-			log_warn("WARNING: device %s (%s) and %s (%s) have duplicate PVID %s",
+-				 dev_name(dev), id->idname,
+-				 du_pvid->dev ? dev_name(du_pvid->dev) : "none", du_pvid->idname,
+-				 pvid);
+-
+ 			if (!cmd->current_settings.yes &&
+ 			    yes_no_prompt("Add device with duplicate PV to devices file?") == 'n') {
+ 				log_print("Device not added.");
+@@ -1166,21 +1175,6 @@ id_done:
+ 				return 1;
+ 			}
+ 		}
+-
+-		if (du_devid && (du_devid != du_pvid)) {
+-			/* warn about another entry using the same device_id */
+-			log_warn("WARNING: duplicate device_id %s for PVIDs %s %s",
+-				 du_devid->idname, du_devid->pvid, du_pvid->pvid);
+-		}
+-
+-		if (du_devname && (du_devname != du_pvid)) {
+-			/* clear devname in another entry with our devname */
+-			log_warn("Devices file PVID %s clearing wrong DEVNAME %s.",
+-				 du_devname->pvid, du_devname->devname);
+-			free(du_devname->devname);
+-			du_devname->devname = NULL;
+-		}
+-
+ 	} else if (du_devid) {
+ 		/*
+ 		 * Do we create a new du or update the existing du?
+@@ -1195,64 +1189,13 @@ id_done:
+ 		 * the same device_id (create a new du for dev.)
+ 		 * If not, then update the existing du_devid.
+ 		 */
+-		
+-		if (du_devid->dev != dev)
+-			check_idname = device_id_system_read(cmd, du_devid->dev, id->idtype);
+-
+-		if (check_idname && !strcmp(check_idname, id->idname)) {
+-			int ret1, ret2;
+-			dev_t devt1, devt2;
+-
+-			/*
+-			 * two different devices have the same device_id,
+-			 * create a new du for the device being added
+-			 */
+-
+-			/* dev_is_partitioned() the dev open to read it. */
+-			if (!label_scan_open(du_devid->dev))
+-				log_warn("Cannot open %s", dev_name(du_devid->dev));
+-
+-			if (dev_is_partitioned(cmd, du_devid->dev)) {
+-				/* Check if existing entry is whole device and new entry is a partition of it. */
+-				ret1 = dev_get_primary_dev(cmd->dev_types, dev, &devt1);
+-				if ((ret1 == 2) && (devt1 == du_devid->dev->dev))
+-					log_warn("Remove partitioned device %s from devices file.", dev_name(du_devid->dev));
+-			} else {
+-				/* Check if both entries are partitions of the same device. */
+-				ret1 = dev_get_primary_dev(cmd->dev_types, dev, &devt1);
+-				ret2 = dev_get_primary_dev(cmd->dev_types, du_devid->dev, &devt2);
+-
+-				if ((ret1 == 2) && (ret2 == 2) && (devt1 == devt2)) {
+-					log_warn("Partitions %s %s have same device_id %s",
+-						 dev_name(dev), dev_name(du_devid->dev), id->idname);
+-				} else {
+-					log_warn("Duplicate device_id %s %s for %s and %s",
+-						 idtype_to_str(id->idtype), check_idname,
+-						 dev_name(dev), dev_name(du_devid->dev));
+-				}
+-			}
+-		} else {
++		if (du_devid->dev == dev) {
+ 			/* update the existing entry with matching devid */
+ 			update_du = du_devid;
+ 			dm_list_del(&update_du->list);
+ 			update_matching_kind = "device_id";
+ 			update_matching_name = id->idname;
+ 		}
+-
+-		if (du_devname && (du_devname != du_devid)) {
+-			/* clear devname in another entry with our devname */
+-			log_warn("Devices file PVID %s clearing wrong DEVNAME %s",
+-				 du_devname->pvid, du_devname->devname);
+-			free(du_devname->devname);
+-			du_devname->devname = NULL;
+-		}
+-
+-	} else if (du_devname) {
+-		/* clear devname in another entry with our devname */
+-		log_warn("Devices file PVID %s clearing wrong DEVNAME %s",
+-			 du_devname->pvid, du_devname->devname);
+-		free(du_devname->devname);
+-		du_devname->devname = NULL;
+ 	}
+ 
+ 	free((void *)check_idname);
+-- 
+2.34.3
+
diff --git a/SOURCES/0031-lvmdevices-make-deldev-work-for-missing-device.patch b/SOURCES/0031-lvmdevices-make-deldev-work-for-missing-device.patch
new file mode 100644
index 0000000..633e1d4
--- /dev/null
+++ b/SOURCES/0031-lvmdevices-make-deldev-work-for-missing-device.patch
@@ -0,0 +1,106 @@
+From df2b1555aff71452cde156badec70117065c9e2c Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Thu, 3 Feb 2022 16:56:03 -0600
+Subject: [PATCH 31/54] lvmdevices: make deldev work for missing device
+
+---
+ lib/device/device_id.c |  6 +++---
+ lib/device/device_id.h |  1 +
+ tools/lvmdevices.c     | 33 ++++++++++++++++-----------------
+ 3 files changed, 20 insertions(+), 20 deletions(-)
+
+diff --git a/lib/device/device_id.c b/lib/device/device_id.c
+index ccc5f43a1..aeaa1ffc6 100644
+--- a/lib/device/device_id.c
++++ b/lib/device/device_id.c
+@@ -894,7 +894,7 @@ struct dev_use *get_du_for_pvid(struct cmd_context *cmd, const char *pvid)
+ 	return NULL;
+ }
+ 
+-static struct dev_use *_get_du_for_devname(struct cmd_context *cmd, const char *devname)
++struct dev_use *get_du_for_devname(struct cmd_context *cmd, const char *devname)
+ {
+ 	struct dev_use *du;
+ 
+@@ -1093,7 +1093,7 @@ id_done:
+ 	du_pvid = get_du_for_pvid(cmd, pvid);
+ 
+ 	/* Is there already an entry using this device's name? */
+-	du_devname = _get_du_for_devname(cmd, dev_name(dev));
++	du_devname = get_du_for_devname(cmd, dev_name(dev));
+ 
+ 	/* Is there already an entry using the device_id for this device? */
+ 	du_devid = _get_du_for_device_id(cmd, id->idtype, id->idname);
+@@ -1514,7 +1514,7 @@ int device_ids_match_dev(struct cmd_context *cmd, struct device *dev)
+ 	struct dev_use *du;
+ 
+ 	/* First check the du entry with matching devname since it's likely correct. */
+-	if ((du = _get_du_for_devname(cmd, dev_name(dev)))) {
++	if ((du = get_du_for_devname(cmd, dev_name(dev)))) {
+ 		if (_match_du_to_dev(cmd, du, dev))
+ 			return 1;
+ 	}
+diff --git a/lib/device/device_id.h b/lib/device/device_id.h
+index 4cf1374c8..2cd2fd7c6 100644
+--- a/lib/device/device_id.h
++++ b/lib/device/device_id.h
+@@ -40,6 +40,7 @@ void device_id_update_vg_uuid(struct cmd_context *cmd, struct volume_group *vg,
+ 
+ struct dev_use *get_du_for_dev(struct cmd_context *cmd, struct device *dev);
+ struct dev_use *get_du_for_pvid(struct cmd_context *cmd, const char *pvid);
++struct dev_use *get_du_for_devname(struct cmd_context *cmd, const char *devname);
+ 
+ char *devices_file_version(void);
+ int devices_file_exists(struct cmd_context *cmd);
+diff --git a/tools/lvmdevices.c b/tools/lvmdevices.c
+index c50c09f90..662b35f9a 100644
+--- a/tools/lvmdevices.c
++++ b/tools/lvmdevices.c
+@@ -383,28 +383,27 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
+ 		 * No filter because we always want to allow removing a device
+ 		 * by name from the devices file.
+ 		 */
+-		if (!(dev = dev_cache_get(cmd, devname, NULL))) {
+-			log_error("No device found for %s.", devname);
+-			goto bad;
+-		}
+-
+-		/*
+-		 * dev_cache_scan uses sysfs to check if an LV is using each dev
+-		 * and sets this flag is so.
+-		 */
+-		if (dev_is_used_by_active_lv(cmd, dev, NULL, NULL, NULL, NULL)) {
+-			if (!arg_count(cmd, yes_ARG) &&
+-			    yes_no_prompt("Device %s is used by an active LV, continue to remove? ", devname) == 'n') {
+-				log_error("Device not removed.");
+-				goto bad;
++		if ((dev = dev_cache_get(cmd, devname, NULL))) {
++			/*
++			 * dev_cache_scan uses sysfs to check if an LV is using each dev
++			 * and sets this flag is so.
++			 */
++			if (dev_is_used_by_active_lv(cmd, dev, NULL, NULL, NULL, NULL)) {
++				if (!arg_count(cmd, yes_ARG) &&
++			    	    yes_no_prompt("Device %s is used by an active LV, continue to remove? ", devname) == 'n') {
++					log_error("Device not removed.");
++					goto bad;
++				}
+ 			}
++			if ((du = get_du_for_dev(cmd, dev)))
++				goto dev_del;
+ 		}
+ 
+-		if (!(du = get_du_for_dev(cmd, dev))) {
+-			log_error("Device not found in devices file.");
++		if (!(du = get_du_for_devname(cmd, devname))) {
++			log_error("No devices file entry for %s.", devname);
+ 			goto bad;
+ 		}
+-
++ dev_del:
+ 		dm_list_del(&du->list);
+ 		free_du(du);
+ 		device_ids_write(cmd);
+-- 
+2.34.3
+
diff --git a/SOURCES/0032-devices-file-do-not-clear-PVID-of-unread-devices.patch b/SOURCES/0032-devices-file-do-not-clear-PVID-of-unread-devices.patch
new file mode 100644
index 0000000..ca4d5eb
--- /dev/null
+++ b/SOURCES/0032-devices-file-do-not-clear-PVID-of-unread-devices.patch
@@ -0,0 +1,144 @@
+From 08a5619a1d7a5a8dd6e0df6e4dedec47ce2533b7 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Thu, 10 Feb 2022 14:00:25 -0600
+Subject: [PATCH 32/54] devices file: do not clear PVID of unread devices
+
+In a certain disconnected state, a block device is present on
+the system, can be opened, reports a valid size, reports the
+correct device id (wwid), and matches a devices file entry.
+But, reading the device can still fail.  In this case,
+device_ids_validate() was misinterpreting the read error as
+the device having no data/label on it (and no PVID).
+The validate function would then clear the PVID from the
+devices file entry for the device, thinking that it was
+fixing the devices file (making it consistent with the on disk
+state.)  Fix this by not attempting to check and correct a
+devices file entry that cannot be read.  Also make this case
+explicit in the hints validation code (which was doing the
+right thing but indirectly.)
+---
+ lib/device/device.h    |  1 +
+ lib/device/device_id.c | 14 ++++++++++++++
+ lib/label/hints.c      | 14 ++++++++++++++
+ lib/label/label.c      |  8 ++++++++
+ 4 files changed, 37 insertions(+)
+
+diff --git a/lib/device/device.h b/lib/device/device.h
+index 9e471a9b5..8c3a8c30e 100644
+--- a/lib/device/device.h
++++ b/lib/device/device.h
+@@ -40,6 +40,7 @@
+ #define DEV_IS_NVME		0x00040000	/* set if dev is nvme */
+ #define DEV_MATCHED_USE_ID	0x00080000	/* matched an entry from cmd->use_devices */
+ #define DEV_SCAN_FOUND_NOLABEL	0x00100000	/* label_scan read, passed filters, but no lvm label */
++#define DEV_SCAN_NOT_READ	0x00200000	/* label_scan not able to read dev */
+ 
+ /*
+  * Support for external device info.
+diff --git a/lib/device/device_id.c b/lib/device/device_id.c
+index aeaa1ffc6..7fe581571 100644
+--- a/lib/device/device_id.c
++++ b/lib/device/device_id.c
+@@ -1724,6 +1724,13 @@ void device_ids_validate(struct cmd_context *cmd, struct dm_list *scanned_devs,
+ 		if (scanned_devs && !dev_in_device_list(dev, scanned_devs))
+ 			continue;
+ 
++		/*
++		 * The matched device could not be read so we do not have
++		 * the PVID from disk and cannot verify the devices file entry.
++		 */
++		if (dev->flags & DEV_SCAN_NOT_READ)
++			continue;
++
+ 		/*
+ 		 * du and dev may have been matched, but the dev could still
+ 		 * have been excluded by other filters during label scan.
+@@ -1806,6 +1813,13 @@ void device_ids_validate(struct cmd_context *cmd, struct dm_list *scanned_devs,
+ 		if (scanned_devs && !dev_in_device_list(dev, scanned_devs))
+ 			continue;
+ 
++		/*
++		 * The matched device could not be read so we do not have
++		 * the PVID from disk and cannot verify the devices file entry.
++		 */
++		if (dev->flags & DEV_SCAN_NOT_READ)
++			continue;
++
+ 		if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "persistent")) {
+ 			log_warn("Devices file %s is excluded by filter: %s.",
+ 				 dev_name(dev), dev_filtered_reason(dev));
+diff --git a/lib/label/hints.c b/lib/label/hints.c
+index 3ce9634f2..95d5d77b8 100644
+--- a/lib/label/hints.c
++++ b/lib/label/hints.c
+@@ -234,6 +234,7 @@ static int _touch_newhints(void)
+ 		return_0;
+ 	if (fclose(fp))
+ 		stack;
++	log_debug("newhints created");
+ 	return 1;
+ }
+ 
+@@ -504,6 +505,19 @@ int validate_hints(struct cmd_context *cmd, struct dm_list *hints)
+ 		if (!hint->chosen)
+ 			continue;
+ 
++		/* 
++		 * label_scan was unable to read the dev so we don't know its pvid.
++		 * Since we are unable to verify the hint is correct, it's possible
++		 * that the PVID is actually found on a different device, so don't
++		 * depend on hints. (This would also fail the following pvid check.)
++		 */
++		if (dev->flags & DEV_SCAN_NOT_READ) {
++			log_debug("Uncertain hint for unread device %d:%d %s",
++				  major(hint->devt), minor(hint->devt), dev_name(dev));
++			ret = 0;
++			continue;
++		}
++
+ 		if (strcmp(dev->pvid, hint->pvid)) {
+ 			log_debug("Invalid hint device %d:%d %s pvid %s had hint pvid %s",
+ 				  major(hint->devt), minor(hint->devt), dev_name(dev),
+diff --git a/lib/label/label.c b/lib/label/label.c
+index 9fac3e464..354ab35e2 100644
+--- a/lib/label/label.c
++++ b/lib/label/label.c
+@@ -686,6 +686,8 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
+ 
+ 	dm_list_iterate_items_safe(devl, devl2, devs) {
+ 
++		devl->dev->flags &= ~DEV_SCAN_NOT_READ;
++
+ 		/*
+ 		 * If we prefetch more devs than blocks in the cache, then the
+ 		 * cache will wait for earlier reads to complete, toss the
+@@ -701,6 +703,7 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
+ 				log_debug_devs("Scan failed to open %s.", dev_name(devl->dev));
+ 				dm_list_del(&devl->list);
+ 				dm_list_add(&reopen_devs, &devl->list);
++				devl->dev->flags |= DEV_SCAN_NOT_READ;
+ 				continue;
+ 			}
+ 		}
+@@ -724,6 +727,7 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
+ 			log_debug_devs("Scan failed to read %s.", dev_name(devl->dev));
+ 			scan_read_errors++;
+ 			scan_failed_count++;
++			devl->dev->flags |= DEV_SCAN_NOT_READ;
+ 			lvmcache_del_dev(devl->dev);
+ 			if (bb)
+ 				bcache_put(bb);
+@@ -1113,6 +1117,10 @@ int label_scan(struct cmd_context *cmd)
+ 	 * filter", and this result needs to be cleared (wiped) so that the
+ 	 * complete set of filters (including those that require data) can be
+ 	 * checked in _process_block, where headers have been read.
++	 *
++	 * FIXME: devs that are filtered with data in _process_block
++	 * are not moved to the filtered_devs list like devs filtered
++	 * here without data.  Does that have any effect?
+ 	 */
+ 	log_debug_devs("Filtering devices to scan (nodata)");
+ 
+-- 
+2.34.3
+
diff --git a/SOURCES/0033-man-lvmcache-mention-writecache-memory-usage.patch b/SOURCES/0033-man-lvmcache-mention-writecache-memory-usage.patch
new file mode 100644
index 0000000..a8314e4
--- /dev/null
+++ b/SOURCES/0033-man-lvmcache-mention-writecache-memory-usage.patch
@@ -0,0 +1,35 @@
+From cdefd8635de24200b55822fa0b6bc23a638fb87a Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Mon, 21 Feb 2022 11:35:58 -0600
+Subject: [PATCH 33/54] man lvmcache: mention writecache memory usage
+
+---
+ man/lvmcache.7_main | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+diff --git a/man/lvmcache.7_main b/man/lvmcache.7_main
+index 48cf7b492..73680235b 100644
+--- a/man/lvmcache.7_main
++++ b/man/lvmcache.7_main
+@@ -240,6 +240,18 @@ The writecache block size should be chosen to match the xfs sectsz value.
+ It is also possible to specify a sector size of 4096 to mkfs.xfs when
+ creating the file system.  In this case the writecache block size of 4096
+ can be used.
++.P
++.SS dm-writecache memory usage
++.P
++The amount of main system memory used by dm-writecache can be a factor
++when selecting the writecache cachevol size and the writecache block size.
++.P
++.IP \[bu] 2
++writecache block size 4096: each 100 GiB of writecache cachevol uses
++slighly over 2 GiB of system memory.
++.IP \[bu] 2
++writecache block size 512: each 100 GiB of writecache cachevol uses
++a little over 16 GiB of system memory.
+ .
+ .SS dm-writecache settings
+ .
+-- 
+2.34.3
+
diff --git a/SOURCES/0034-writecache-display-block-size-from-lvs.patch b/SOURCES/0034-writecache-display-block-size-from-lvs.patch
new file mode 100644
index 0000000..5167d68
--- /dev/null
+++ b/SOURCES/0034-writecache-display-block-size-from-lvs.patch
@@ -0,0 +1,102 @@
+From c047ff61f68d1b853569b153251f8bc5f88e23cd Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Mon, 21 Feb 2022 16:09:57 -0600
+Subject: [PATCH 34/54] writecache: display block size from lvs
+
+lvs was missing the ability to display writecache block size.
+now possible with lvs -o writecache_block_size
+---
+ lib/report/columns.h                     |  1 +
+ lib/report/properties.c                  |  2 ++
+ lib/report/report.c                      | 20 ++++++++++++++++++++
+ man/lvmcache.7_main                      |  4 ++++
+ test/shell/writecache-cache-blocksize.sh |  2 ++
+ 5 files changed, 29 insertions(+)
+
+diff --git a/lib/report/columns.h b/lib/report/columns.h
+index 12b78b766..7e450dace 100644
+--- a/lib/report/columns.h
++++ b/lib/report/columns.h
+@@ -108,6 +108,7 @@ FIELD(LVS, lv, TIM, "RTime", lvid, 26, lvtimeremoved, lv_time_removed, "Removal
+ FIELD(LVS, lv, STR, "Host", lvid, 10, lvhost, lv_host, "Creation host of the LV, if known.", 0)
+ FIELD(LVS, lv, STR_LIST, "Modules", lvid, 0, modules, lv_modules, "Kernel device-mapper modules required for this LV.", 0)
+ FIELD(LVS, lv, BIN, "Historical", lvid, 0, lvhistorical, lv_historical, "Set if the LV is historical.", 0)
++FIELD(LVS, lv, NUM, "WCacheBlkSize", lvid, 0, writecache_block_size, writecache_block_size, "The writecache block size", 0)
+ /*
+  * End of LVS type fields
+  */
+diff --git a/lib/report/properties.c b/lib/report/properties.c
+index 12ea890f4..6f302360f 100644
+--- a/lib/report/properties.c
++++ b/lib/report/properties.c
+@@ -353,6 +353,8 @@ GET_PV_STR_PROPERTY_FN(pv_device_id_type, pv->device_id_type)
+ #define _writecache_writeback_blocks_get prop_not_implemented_get
+ #define _writecache_error_set prop_not_implemented_set
+ #define _writecache_error_get prop_not_implemented_get
++#define _writecache_block_size_set prop_not_implemented_set
++#define _writecache_block_size_get prop_not_implemented_get
+ 
+ #define _vdo_operating_mode_set prop_not_implemented_set
+ #define _vdo_operating_mode_get prop_not_implemented_get
+diff --git a/lib/report/report.c b/lib/report/report.c
+index 60df417a4..c06b22674 100644
+--- a/lib/report/report.c
++++ b/lib/report/report.c
+@@ -3346,6 +3346,26 @@ static int _integritymismatches_disp(struct dm_report *rh __attribute__((unused)
+ 	return _field_set_value(field, "", &GET_TYPE_RESERVED_VALUE(num_undef_64));
+ }
+ 
++static int _writecache_block_size_disp(struct dm_report *rh __attribute__((unused)),
++				   struct dm_pool *mem,
++				   struct dm_report_field *field,
++				   const void *data,
++				   void *private __attribute__((unused)))
++{
++	struct logical_volume *lv = (struct logical_volume *) data;
++	uint32_t bs = 0;
++
++	if (lv_is_writecache(lv)) {
++		struct lv_segment *seg = first_seg(lv);
++		bs = seg->writecache_block_size;
++	}
++
++	if (!bs)
++		return dm_report_field_int32(rh, field, &GET_TYPE_RESERVED_VALUE(num_undef_32));
++
++	return dm_report_field_uint32(rh, field, &bs);
++}
++
+ static int _datapercent_disp(struct dm_report *rh, struct dm_pool *mem,
+ 			     struct dm_report_field *field,
+ 			     const void *data, void *private)
+diff --git a/man/lvmcache.7_main b/man/lvmcache.7_main
+index 73680235b..519e352cb 100644
+--- a/man/lvmcache.7_main
++++ b/man/lvmcache.7_main
+@@ -241,6 +241,10 @@ It is also possible to specify a sector size of 4096 to mkfs.xfs when
+ creating the file system.  In this case the writecache block size of 4096
+ can be used.
+ .P
++The writecache block size is displayed by the command:
++.br
++lvs -o writecacheblocksize VG/LV
++.P
+ .SS dm-writecache memory usage
+ .P
+ The amount of main system memory used by dm-writecache can be a factor
+diff --git a/test/shell/writecache-cache-blocksize.sh b/test/shell/writecache-cache-blocksize.sh
+index 2579ef7b7..4e17effe5 100644
+--- a/test/shell/writecache-cache-blocksize.sh
++++ b/test/shell/writecache-cache-blocksize.sh
+@@ -222,6 +222,8 @@ vgextend $vg "$dev2"
+ lvcreate -n $lv1 -l 8 -an $vg "$dev1"
+ lvcreate -n $lv2 -l 4 -an $vg "$dev2"
+ lvconvert --yes --type writecache --cachevol $lv2 --cachesettings "block_size=4096" $vg/$lv1
++lvs -o writecacheblocksize $vg/$lv1 |tee out
++grep 4096 out
+ lvchange -ay $vg/$lv1
+ mkfs.xfs -f "$DM_DEV_DIR/$vg/$lv1" |tee out
+ grep "sectsz=4096" out
+-- 
+2.34.3
+
diff --git a/SOURCES/0035-devices-simplify-dev_cache_get_by_devt.patch b/SOURCES/0035-devices-simplify-dev_cache_get_by_devt.patch
new file mode 100644
index 0000000..2f8a911
--- /dev/null
+++ b/SOURCES/0035-devices-simplify-dev_cache_get_by_devt.patch
@@ -0,0 +1,148 @@
+From 8552290efae4905fd1a942be8e752842b11f1881 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Thu, 24 Feb 2022 15:57:29 -0600
+Subject: [PATCH 35/54] devices: simplify dev_cache_get_by_devt
+
+remove unused args, and no callers need or want a
+repeated dev_cache_scan if there is no dev from the
+lookup.
+---
+ lib/device/dev-cache.c | 60 ++++--------------------------------------
+ lib/device/dev-cache.h |  2 +-
+ lib/label/label.c      |  2 +-
+ tools/pvscan.c         |  6 ++---
+ 4 files changed, 10 insertions(+), 60 deletions(-)
+
+diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c
+index c6e5f68cf..cc1af7c7a 100644
+--- a/lib/device/dev-cache.c
++++ b/lib/device/dev-cache.c
+@@ -1577,63 +1577,13 @@ struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct d
+ 	return dev;
+ }
+ 
+-struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t dev, struct dev_filter *f, int *filtered)
++struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t devt)
+ {
+-	char path[PATH_MAX];
+-	const char *sysfs_dir;
+-	struct stat info;
+-	struct device *d = (struct device *) btree_lookup(_cache.devices, (uint32_t) dev);
+-	int ret;
+-
+-	if (filtered)
+-		*filtered = 0;
+-
+-	if (!d) {
+-		sysfs_dir = dm_sysfs_dir();
+-		if (sysfs_dir && *sysfs_dir) {
+-			/* First check if dev is sysfs to avoid useless scan */
+-			if (dm_snprintf(path, sizeof(path), "%sdev/block/%d:%d",
+-					sysfs_dir, (int)MAJOR(dev), (int)MINOR(dev)) < 0) {
+-				log_error("dm_snprintf partition failed.");
+-				return NULL;
+-			}
+-
+-			if (lstat(path, &info)) {
+-				log_debug("No sysfs entry for %d:%d errno %d at %s.",
+-					  (int)MAJOR(dev), (int)MINOR(dev), errno, path);
+-				return NULL;
+-			}
+-		}
+-
+-		log_debug_devs("Device num not found in dev_cache repeat dev_cache_scan for %d:%d",
+-				(int)MAJOR(dev), (int)MINOR(dev));
+-		dev_cache_scan(cmd);
+-		d = (struct device *) btree_lookup(_cache.devices, (uint32_t) dev);
+-
+-		if (!d)
+-			return NULL;
+-	}
+-
+-	if (d->flags & DEV_REGULAR)
+-		return d;
+-
+-	if (!f)
+-		return d;
+-
+-	ret = f->passes_filter(cmd, f, d, NULL);
+-
+-	if (ret == -EAGAIN) {
+-		log_debug_devs("get device by number defer filter %s", dev_name(d));
+-		d->flags |= DEV_FILTER_AFTER_SCAN;
+-		ret = 1;
+-	}
+-
+-	if (ret)
+-		return d;
+-
+-	if (filtered)
+-		*filtered = 1;
++	struct device *dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) devt);
+ 
++	if (dev)
++		return dev;
++	log_debug_devs("No devno %d:%d in dev cache.", (int)MAJOR(devt), (int)MINOR(devt));
+ 	return NULL;
+ }
+ 
+diff --git a/lib/device/dev-cache.h b/lib/device/dev-cache.h
+index 635dc4fc9..7305eeb0e 100644
+--- a/lib/device/dev-cache.h
++++ b/lib/device/dev-cache.h
+@@ -54,7 +54,7 @@ int dev_cache_has_scanned(void);
+ int dev_cache_add_dir(const char *path);
+ struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct dev_filter *f);
+ 
+-struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t device, struct dev_filter *f, int *filtered);
++struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t devt);
+ 
+ struct device *dev_hash_get(const char *name);
+ 
+diff --git a/lib/label/label.c b/lib/label/label.c
+index 354ab35e2..ffe925254 100644
+--- a/lib/label/label.c
++++ b/lib/label/label.c
+@@ -1443,7 +1443,7 @@ void label_scan_invalidate_lv(struct cmd_context *cmd, struct logical_volume *lv
+ 	if (lv_info(cmd, lv, 0, &lvinfo, 0, 0) && lvinfo.exists) {
+ 		/* FIXME: Still unclear what is it supposed to find */
+ 		devt = MKDEV(lvinfo.major, lvinfo.minor);
+-		if ((dev = dev_cache_get_by_devt(cmd, devt, NULL, NULL)))
++		if ((dev = dev_cache_get_by_devt(cmd, devt)))
+ 			label_scan_invalidate(dev);
+ 	}
+ }
+diff --git a/tools/pvscan.c b/tools/pvscan.c
+index f60c4a2ca..160a2c9a0 100644
+--- a/tools/pvscan.c
++++ b/tools/pvscan.c
+@@ -857,7 +857,7 @@ static int _get_devs_from_saved_vg(struct cmd_context *cmd, const char *vgname,
+ 
+ 		devno = MKDEV(file_major, file_minor);
+ 
+-		if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
++		if (!(dev = dev_cache_get_by_devt(cmd, devno))) {
+ 			log_error_pvscan(cmd, "No device found for %d:%d PVID %s", file_major, file_minor, pvid);
+ 			goto bad;
+ 		}
+@@ -1195,7 +1195,7 @@ static int _get_args_devs(struct cmd_context *cmd, struct dm_list *pvscan_args,
+ 		if (arg->devname)
+ 			arg->dev = dev_cache_get(cmd, arg->devname, NULL);
+ 		else if (arg->devno)
+-			arg->dev = dev_cache_get_by_devt(cmd, arg->devno, NULL, NULL);
++			arg->dev = dev_cache_get_by_devt(cmd, arg->devno);
+ 		else
+ 			return_0;
+ 	}
+@@ -1257,7 +1257,7 @@ static void _set_pv_devices_online(struct cmd_context *cmd, struct volume_group
+ 
+ 		devno = MKDEV(major, minor);
+ 
+-		if (!(dev = dev_cache_get_by_devt(cmd, devno, NULL, NULL))) {
++		if (!(dev = dev_cache_get_by_devt(cmd, devno))) {
+ 			log_print_pvscan(cmd, "VG %s PV %s no device found for %d:%d",
+ 					 vg->name, pvid, major, minor);
+ 			pvl->pv->status |= MISSING_PV;
+-- 
+2.34.3
+
diff --git a/SOURCES/0036-devices-drop-incorrect-paths-from-aliases-list.patch b/SOURCES/0036-devices-drop-incorrect-paths-from-aliases-list.patch
new file mode 100644
index 0000000..010f2f3
--- /dev/null
+++ b/SOURCES/0036-devices-drop-incorrect-paths-from-aliases-list.patch
@@ -0,0 +1,467 @@
+From 8ba6259b24cd4b99e061f2610c5cd0bcde890039 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Thu, 24 Feb 2022 16:03:21 -0600
+Subject: [PATCH 36/54] devices: drop incorrect paths from aliases list
+
+along with some basic checks for cases when a device
+has no aliases.
+
+lvm itself creates many situations where a struct device
+has no valid paths, when it activates and opens an LV,
+does something with it, e.g. zeroing, and then closes
+and deactivates it.  (dev-cache is intended for PVs, and
+the use of LVs should be moved out of dev-cache in a
+future patch.)
+---
+ lib/device/dev-cache.c | 223 ++++++++++++++++++++++++++---------------
+ lib/device/dev-cache.h |   2 +-
+ lib/device/dev-io.c    |  34 ++++---
+ lib/device/device.h    |   3 -
+ 4 files changed, 164 insertions(+), 98 deletions(-)
+
+diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c
+index cc1af7c7a..58e67e130 100644
+--- a/lib/device/dev-cache.c
++++ b/lib/device/dev-cache.c
+@@ -351,7 +351,7 @@ static int _add_alias(struct device *dev, const char *path, enum add_hash hash)
+ 			goto out;
+ 		}
+ 
+-	if (!(path = dm_pool_strdup(_cache.mem, path)) ||
++	if (!(path = _strdup(path)) ||
+ 	    !(sl = _zalloc(sizeof(*sl)))) {
+ 		log_error("Failed to add allias to dev cache.");
+ 		return 0;
+@@ -1162,6 +1162,17 @@ static int _insert(const char *path, const struct stat *info,
+ 	return 1;
+ }
+ 
++static void _drop_all_aliases(struct device *dev)
++{
++	struct dm_str_list *strl, *strl2;
++
++	dm_list_iterate_items_safe(strl, strl2, &dev->aliases) {
++		log_debug("Drop alias for %d:%d %s.", (int)MAJOR(dev->dev), (int)MINOR(dev->dev), strl->str);
++		dm_hash_remove(_cache.names, strl->str);
++		dm_list_del(&strl->list);
++	}
++}
++
+ void dev_cache_scan(struct cmd_context *cmd)
+ {
+ 	log_debug_devs("Creating list of system devices.");
+@@ -1371,59 +1382,6 @@ int dev_cache_add_dir(const char *path)
+ 	return 1;
+ }
+ 
+-/* Check cached device name is still valid before returning it */
+-/* This should be a rare occurrence */
+-/* set quiet if the cache is expected to be out-of-date */
+-/* FIXME Make rest of code pass/cache struct device instead of dev_name */
+-const char *dev_name_confirmed(struct device *dev, int quiet)
+-{
+-	struct stat buf;
+-	const char *name;
+-	int r;
+-
+-	if ((dev->flags & DEV_REGULAR))
+-		return dev_name(dev);
+-
+-	while ((r = stat(name = dm_list_item(dev->aliases.n,
+-					  struct dm_str_list)->str, &buf)) ||
+-	       (buf.st_rdev != dev->dev)) {
+-		if (r < 0) {
+-			if (quiet)
+-				log_sys_debug("stat", name);
+-			else
+-				log_sys_error("stat", name);
+-		}
+-		if (quiet)
+-			log_debug_devs("Path %s no longer valid for device(%d,%d)",
+-				       name, (int) MAJOR(dev->dev),
+-				       (int) MINOR(dev->dev));
+-		else
+-			log_warn("Path %s no longer valid for device(%d,%d)",
+-				 name, (int) MAJOR(dev->dev),
+-				 (int) MINOR(dev->dev));
+-
+-		/* Remove the incorrect hash entry */
+-		dm_hash_remove(_cache.names, name);
+-
+-		/* Leave list alone if there isn't an alternative name */
+-		/* so dev_name will always find something to return. */
+-		/* Otherwise add the name to the correct device. */
+-		if (dm_list_size(&dev->aliases) > 1) {
+-			dm_list_del(dev->aliases.n);
+-			if (!r)
+-				_insert(name, &buf, 0, obtain_device_list_from_udev());
+-			continue;
+-		}
+-
+-		/* Scanning issues this inappropriately sometimes. */
+-		log_debug_devs("Aborting - please provide new pathname for what "
+-			       "used to be %s", name);
+-		return NULL;
+-	}
+-
+-	return dev_name(dev);
+-}
+-
+ struct device *dev_hash_get(const char *name)
+ {
+ 	return (struct device *) dm_hash_lookup(_cache.names, name);
+@@ -1452,26 +1410,23 @@ static void _remove_alias(struct device *dev, const char *name)
+  * deactivated LV.  Those old paths are all invalid and are dropped here.
+  */
+ 
+-static void _verify_aliases(struct device *dev, const char *newname)
++static void _verify_aliases(struct device *dev)
+ {
+ 	struct dm_str_list *strl, *strl2;
+ 	struct stat st;
+ 
+ 	dm_list_iterate_items_safe(strl, strl2, &dev->aliases) {
+-		/* newname was just stat'd and added by caller */
+-		if (newname && !strcmp(strl->str, newname))
+-			continue;
+-
+ 		if (stat(strl->str, &st) || (st.st_rdev != dev->dev)) {
+-			log_debug("Drop invalid path %s for %d:%d (new path %s).",
+-				  strl->str, (int)MAJOR(dev->dev), (int)MINOR(dev->dev), newname ?: "");
++			log_debug("Drop alias for %d:%d invalid path %s %d:%d.",
++				  (int)MAJOR(dev->dev), (int)MINOR(dev->dev), strl->str,
++				  (int)MAJOR(st.st_rdev), (int)MINOR(st.st_rdev));
+ 			dm_hash_remove(_cache.names, strl->str);
+ 			dm_list_del(&strl->list);
+ 		}
+ 	}
+ }
+ 
+-struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct dev_filter *f)
++static struct device *_dev_cache_get(struct cmd_context *cmd, const char *name, struct dev_filter *f, int existing)
+ {
+ 	struct device *dev = (struct device *) dm_hash_lookup(_cache.names, name);
+ 	struct stat st;
+@@ -1485,13 +1440,18 @@ struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct d
+ 	if (dev && (dev->flags & DEV_REGULAR))
+ 		return dev;
+ 
++	if (dev && dm_list_empty(&dev->aliases)) {
++		/* shouldn't happen */
++		log_warn("Ignoring dev with no valid paths for %s.", name);
++		return NULL;
++	}
++
+ 	/*
+-	 * The requested path is invalid, remove any dev-cache
+-	 * info for it.
++	 * The requested path is invalid, remove any dev-cache info for it.
+ 	 */
+ 	if (stat(name, &st)) {
+ 		if (dev) {
+-			log_print("Device path %s is invalid for %d:%d %s.",
++			log_debug("Device path %s is invalid for %d:%d %s.",
+ 				  name, (int)MAJOR(dev->dev), (int)MINOR(dev->dev), dev_name(dev));
+ 
+ 			dm_hash_remove(_cache.names, name);
+@@ -1499,11 +1459,17 @@ struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct d
+ 			_remove_alias(dev, name);
+ 
+ 			/* Remove any other names in dev->aliases that are incorrect. */
+-			_verify_aliases(dev, NULL);
++			_verify_aliases(dev);
+ 		}
+ 		return NULL;
+ 	}
+ 
++	if (dev && dm_list_empty(&dev->aliases)) {
++		/* shouldn't happen */
++		log_warn("Ignoring dev with no valid paths for %s.", name);
++		return NULL;
++	}
++
+ 	if (!S_ISBLK(st.st_mode)) {
+ 		log_debug("Not a block device %s.", name);
+ 		return NULL;
+@@ -1514,26 +1480,110 @@ struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct d
+ 	 * Remove incorrect info and then add new dev-cache entry.
+ 	 */
+ 	if (dev && (st.st_rdev != dev->dev)) {
+-		log_debug("Device path %s does not match %d:%d %s.",
+-			  name, (int)MAJOR(dev->dev), (int)MINOR(dev->dev), dev_name(dev));
++		struct device *dev_by_devt = (struct device *) btree_lookup(_cache.devices, (uint32_t) st.st_rdev);
++
++		/*
++		 * lvm commands create this condition when they
++		 * activate/deactivate LVs combined with creating new LVs.
++		 * The command does not purge dev structs when deactivating
++		 * an LV (which it probably should do), but the better
++		 * approach would be not using dev-cache at all for LVs.
++		 */
+ 
+-		dm_hash_remove(_cache.names, name);
++		log_debug("Dropping aliases for device entry %d:%d %s for new device %d:%d %s.",
++			  (int)MAJOR(dev->dev), (int)MINOR(dev->dev), dev_name(dev),
++			  (int)MAJOR(st.st_rdev), (int)MINOR(st.st_rdev), name);
+ 
+-		_remove_alias(dev, name);
++		_drop_all_aliases(dev);
+ 
+-		/* Remove any other names in dev->aliases that are incorrect. */
+-		_verify_aliases(dev, NULL);
++		if (dev_by_devt) {
++			log_debug("Dropping aliases for device entry %d:%d %s for new device %d:%d %s.",
++				   (int)MAJOR(dev_by_devt->dev), (int)MINOR(dev_by_devt->dev), dev_name(dev_by_devt),
++				   (int)MAJOR(st.st_rdev), (int)MINOR(st.st_rdev), name);
+ 
+-		/* Add new dev-cache entry next. */
+-		dev = NULL;
++			_drop_all_aliases(dev_by_devt);
++		}
++
++#if 0
++		/*
++		 * I think only lvm's own dm devs should be added here, so use
++		 * a warning to look for any other unknown cases.
++		 */
++		if (MAJOR(st.st_rdev) != cmd->dev_types->device_mapper_major) {
++			log_warn("WARNING: new device appeared %d:%d %s",
++				  (int)MAJOR(st.st_rdev), (int)(MINOR(st.st_rdev)), name);
++		}
++#endif
++
++		if (!_insert_dev(name, st.st_rdev))
++			return_NULL;
++
++		/* Get the struct dev that was just added. */
++		dev = (struct device *) dm_hash_lookup(_cache.names, name);
++
++		if (!dev) {
++			log_error("Failed to get device %s", name);
++			return NULL;
++		}
++
++		goto out;
+ 	}
+ 
++	if (dev && dm_list_empty(&dev->aliases)) {
++		/* shouldn't happen */
++		log_warn("Ignoring dev with no valid paths for %s.", name);
++		return NULL;
++	}
++
++	if (!dev && existing)
++		return_NULL;
++
+ 	/*
+-	 * Either add a new struct dev for st_rdev and name,
+-	 * or add name as a new alias for an existing struct dev
+-	 * for st_rdev.
++	 * This case should never be hit for a PV. It should only
++	 * happen when the command is opening a new LV it has created.
++	 * Add an arg to all callers indicating when the arg should be
++	 * new (for an LV) and not existing.
++	 * FIXME: fix this further by not using dev-cache struct devs
++	 * at all for new dm devs (LVs) that lvm uses.  Make the
++	 * dev-cache contain only devs for PVs.
++	 * Places to fix that use a dev for LVs include:
++	 * . lv_resize opening lv to discard
++	 * . wipe_lv opening lv to zero it
++	 * . _extend_sanlock_lv opening lv to extend it
++	 * . _write_log_header opening lv to write header
++	 * Also, io to LVs should not go through bcache.
++	 * bcache should contain only labels and metadata
++	 * scanned from PVs.
+ 	 */
+ 	if (!dev) {
++		/*
++		 * This case should only be used for new devices created by this
++		 * command (opening LVs it's created), so if a dev exists for the
++		 * dev_t referenced by the name, then drop all aliases for before
++		 * _insert_dev adds the new name.  lvm commands actually hit this
++		 * fairly often when it uses some LV, deactivates the LV, then
++		 * creates some new LV which ends up with the same major:minor.
++		 * Without dropping the aliases, it's plausible that lvm commands
++		 * could end up using the wrong dm device.
++		 */
++		struct device *dev_by_devt = (struct device *) btree_lookup(_cache.devices, (uint32_t) st.st_rdev);
++		if (dev_by_devt) {
++			log_debug("Dropping aliases for %d:%d before adding new path %s.",
++				  (int)MAJOR(st.st_rdev), (int)(MINOR(st.st_rdev)), name);
++			_drop_all_aliases(dev_by_devt);
++		}
++
++#if 0
++		/*
++		 * I think only lvm's own dm devs should be added here, so use
++		 * a warning to look for any other unknown cases.
++		 */
++		if (MAJOR(st.st_rdev) != cmd->dev_types->device_mapper_major) {
++			log_warn("WARNING: new device appeared %d:%d %s",
++				  (int)MAJOR(st.st_rdev), (int)(MINOR(st.st_rdev)), name);
++		}
++#endif
++
+ 		if (!_insert_dev(name, st.st_rdev))
+ 			return_NULL;
+ 
+@@ -1544,10 +1594,9 @@ struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct d
+ 			log_error("Failed to get device %s", name);
+ 			return NULL;
+ 		}
+-
+-		_verify_aliases(dev, name);
+ 	}
+ 
++ out:
+ 	/*
+ 	 * The caller passed a filter if they only want the dev if it
+ 	 * passes filters.
+@@ -1577,6 +1626,16 @@ struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct d
+ 	return dev;
+ }
+ 
++struct device *dev_cache_get_existing(struct cmd_context *cmd, const char *name, struct dev_filter *f)
++{
++	return _dev_cache_get(cmd, name, f, 1);
++}
++
++struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct dev_filter *f)
++{
++	return _dev_cache_get(cmd, name, f, 0);
++}
++
+ struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t devt)
+ {
+ 	struct device *dev = (struct device *) btree_lookup(_cache.devices, (uint32_t) devt);
+@@ -1653,8 +1712,10 @@ int dev_fd(struct device *dev)
+ 
+ const char *dev_name(const struct device *dev)
+ {
+-	return (dev && dev->aliases.n) ? dm_list_item(dev->aliases.n, struct dm_str_list)->str :
+-	    unknown_device_name();
++	if (dev && dev->aliases.n && !dm_list_empty(&dev->aliases))
++		return dm_list_item(dev->aliases.n, struct dm_str_list)->str;
++	else
++		return unknown_device_name();
+ }
+ 
+ bool dev_cache_has_md_with_end_superblock(struct dev_types *dt)
+diff --git a/lib/device/dev-cache.h b/lib/device/dev-cache.h
+index 7305eeb0e..51c3fc6c3 100644
+--- a/lib/device/dev-cache.h
++++ b/lib/device/dev-cache.h
+@@ -53,7 +53,7 @@ int dev_cache_has_scanned(void);
+ 
+ int dev_cache_add_dir(const char *path);
+ struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct dev_filter *f);
+-
++struct device *dev_cache_get_existing(struct cmd_context *cmd, const char *name, struct dev_filter *f);
+ struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t devt);
+ 
+ struct device *dev_hash_get(const char *name);
+diff --git a/lib/device/dev-io.c b/lib/device/dev-io.c
+index b4f1930b1..811ad8978 100644
+--- a/lib/device/dev-io.c
++++ b/lib/device/dev-io.c
+@@ -58,6 +58,9 @@ static int _dev_get_size_file(struct device *dev, uint64_t *size)
+ 	const char *name = dev_name(dev);
+ 	struct stat info;
+ 
++	if (dm_list_empty(&dev->aliases))
++		return_0;
++
+ 	if (dev->size_seqno == _dev_size_seqno) {
+ 		log_very_verbose("%s: using cached size %" PRIu64 " sectors",
+ 				 name, dev->size);
+@@ -87,7 +90,7 @@ static int _dev_get_size_dev(struct device *dev, uint64_t *size)
+ 	int do_close = 0;
+ 
+ 	if (dm_list_empty(&dev->aliases))
+-		return 0;
++		return_0;
+ 
+ 	if (dev->size_seqno == _dev_size_seqno) {
+ 		log_very_verbose("%s: using cached size %" PRIu64 " sectors",
+@@ -305,6 +308,13 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
+ 	if ((flags & O_EXCL))
+ 		need_excl = 1;
+ 
++	if (dm_list_empty(&dev->aliases)) {
++		/* shouldn't happen */
++		log_print("Cannot open device %d:%d with no valid paths.", (int)MAJOR(dev->dev), (int)MINOR(dev->dev));
++		return 0;
++	}
++	name = dev_name(dev);
++
+ 	if (dev->fd >= 0) {
+ 		if (((dev->flags & DEV_OPENED_RW) || !need_rw) &&
+ 		    ((dev->flags & DEV_OPENED_EXCL) || !need_excl)) {
+@@ -314,7 +324,7 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
+ 
+ 		if (dev->open_count && !need_excl)
+ 			log_debug_devs("%s: Already opened read-only. Upgrading "
+-				       "to read-write.", dev_name(dev));
++				       "to read-write.", name);
+ 
+ 		/* dev_close_immediate will decrement this */
+ 		dev->open_count++;
+@@ -327,11 +337,7 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
+ 
+ 	if (critical_section())
+ 		/* FIXME Make this log_error */
+-		log_verbose("dev_open(%s) called while suspended",
+-			    dev_name(dev));
+-
+-	if (!(name = dev_name_confirmed(dev, quiet)))
+-		return_0;
++		log_verbose("dev_open(%s) called while suspended", name);
+ 
+ #ifdef O_DIRECT_SUPPORT
+ 	if (direct) {
+@@ -372,9 +378,9 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
+ 		}
+ #endif
+ 		if (quiet)
+-			log_sys_debug("open", name);
++			log_debug("Failed to open device path %s (%d).", name, errno);
+ 		else
+-			log_sys_error("open", name);
++			log_error("Failed to open device path %s (%d).", name, errno);
+ 
+ 		dev->flags |= DEV_OPEN_FAILURE;
+ 		return 0;
+@@ -415,10 +421,12 @@ int dev_open_flags(struct device *dev, int flags, int direct, int quiet)
+ 	if ((flags & O_CREAT) && !(flags & O_TRUNC))
+ 		dev->end = lseek(dev->fd, (off_t) 0, SEEK_END);
+ 
+-	log_debug_devs("Opened %s %s%s%s", dev_name(dev),
+-		       dev->flags & DEV_OPENED_RW ? "RW" : "RO",
+-		       dev->flags & DEV_OPENED_EXCL ? " O_EXCL" : "",
+-		       dev->flags & DEV_O_DIRECT ? " O_DIRECT" : "");
++	if (!quiet) {
++		log_debug_devs("Opened %s %s%s%s", name,
++				dev->flags & DEV_OPENED_RW ? "RW" : "RO",
++				dev->flags & DEV_OPENED_EXCL ? " O_EXCL" : "",
++				dev->flags & DEV_O_DIRECT ? " O_DIRECT" : "");
++	}
+ 
+ 	dev->flags &= ~DEV_OPEN_FAILURE;
+ 	return 1;
+diff --git a/lib/device/device.h b/lib/device/device.h
+index 8c3a8c30e..572994bb9 100644
+--- a/lib/device/device.h
++++ b/lib/device/device.h
+@@ -204,9 +204,6 @@ struct device *dev_create_file(const char *filename, struct device *dev,
+ 			       struct dm_str_list *alias, int use_malloc);
+ void dev_destroy_file(struct device *dev);
+ 
+-/* Return a valid device name from the alias list; NULL otherwise */
+-const char *dev_name_confirmed(struct device *dev, int quiet);
+-
+ int dev_mpath_init(const char *config_wwids_file);
+ void dev_mpath_exit(void);
+ 
+-- 
+2.34.3
+
diff --git a/SOURCES/0037-devices-initial-use-of-existing-option.patch b/SOURCES/0037-devices-initial-use-of-existing-option.patch
new file mode 100644
index 0000000..79fe5f3
--- /dev/null
+++ b/SOURCES/0037-devices-initial-use-of-existing-option.patch
@@ -0,0 +1,70 @@
+From 7dc7ab8e99005da29aba22df2bb67e58e19a50f0 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Thu, 24 Feb 2022 16:10:37 -0600
+Subject: [PATCH 37/54] devices: initial use of existing option
+
+Use dev_cache_get_existing() in a few common, high level
+locations where it's obvious that only existing dev-cache
+entries are wanted.  This can be expanded and used in more
+locations (or dev_cache_get can stop creating new entries.)
+---
+ lib/device/device_id.c | 4 ++--
+ tools/toollib.c        | 6 +++---
+ 2 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/lib/device/device_id.c b/lib/device/device_id.c
+index 7fe581571..bcb2e6bcf 100644
+--- a/lib/device/device_id.c
++++ b/lib/device/device_id.c
+@@ -1565,7 +1565,7 @@ void device_ids_match(struct cmd_context *cmd)
+ 		dm_list_iterate_items(du, &cmd->use_devices) {
+ 			if (du->dev)
+ 				continue;
+-			if (!(du->dev = dev_cache_get(cmd, du->devname, NULL))) {
++			if (!(du->dev = dev_cache_get_existing(cmd, du->devname, NULL))) {
+ 				log_warn("Device not found for %s.", du->devname);
+ 			} else {
+ 				/* Should we set dev->id?  Which idtype?  Use --deviceidtype? */
+@@ -1603,7 +1603,7 @@ void device_ids_match(struct cmd_context *cmd)
+ 		 * the du/dev pairs in preparation for using the filters.
+ 		 */
+ 		if (du->devname &&
+-		    (dev = dev_cache_get(cmd, du->devname, NULL))) {
++		    (dev = dev_cache_get_existing(cmd, du->devname, NULL))) {
+ 			/* On successful match, du, dev, and id are linked. */
+ 			if (_match_du_to_dev(cmd, du, dev))
+ 				continue;
+diff --git a/tools/toollib.c b/tools/toollib.c
+index d6f48aad2..16be336d4 100644
+--- a/tools/toollib.c
++++ b/tools/toollib.c
+@@ -1434,7 +1434,7 @@ int process_each_label(struct cmd_context *cmd, int argc, char **argv,
+ 				goto out;
+ 			}
+ 
+-			if (!(dev = dev_cache_get(cmd, argv[opt], cmd->filter))) {
++			if (!(dev = dev_cache_get_existing(cmd, argv[opt], cmd->filter))) {
+ 				log_error("Failed to find device "
+ 					  "\"%s\".", argv[opt]);
+ 				ret_max = ECMD_FAILED;
+@@ -3870,7 +3870,7 @@ static int _get_arg_devices(struct cmd_context *cmd,
+ 			return ECMD_FAILED;
+ 		}
+ 
+-		if (!(dil->dev = dev_cache_get(cmd, sl->str, cmd->filter))) {
++		if (!(dil->dev = dev_cache_get_existing(cmd, sl->str, cmd->filter))) {
+ 			log_error("Cannot use %s: %s", sl->str, devname_error_reason(sl->str));
+ 			ret_max = EINIT_FAILED;
+ 		} else {
+@@ -5206,7 +5206,7 @@ int pvcreate_each_device(struct cmd_context *cmd,
+ 		struct device *dev;
+ 
+ 		/* No filter used here */
+-		if (!(dev = dev_cache_get(cmd, pd->name, NULL))) {
++		if (!(dev = dev_cache_get_existing(cmd, pd->name, NULL))) {
+ 			log_error("No device found for %s.", pd->name);
+ 			dm_list_del(&pd->list);
+ 			dm_list_add(&pp->arg_fail, &pd->list);
+-- 
+2.34.3
+
diff --git a/SOURCES/0038-devices-fix-dev_name-assumptions.patch b/SOURCES/0038-devices-fix-dev_name-assumptions.patch
new file mode 100644
index 0000000..4608b4c
--- /dev/null
+++ b/SOURCES/0038-devices-fix-dev_name-assumptions.patch
@@ -0,0 +1,270 @@
+From 591b5f006fca7e06bfbf0d5512da3ae5b0f6bbdd Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Tue, 22 Feb 2022 15:03:11 -0600
+Subject: [PATCH 38/54] devices: fix dev_name assumptions
+
+dev_name(dev) returns "[unknown]" if there are no names
+on dev->aliases.  It's meant mainly for log messages.
+
+Many places assume a valid path name is returned, and
+use it directly.  A caller that wants to use the path
+from dev_name() must first check if the dev has any
+paths with dm_list_empty(&dev->aliases).
+---
+ lib/activate/dev_manager.c     |  9 ++++++++-
+ lib/device/dev-type.c          |  3 +++
+ lib/device/device_id.c         | 13 +++++++++++--
+ lib/label/hints.c              |  2 ++
+ lib/label/label.c              | 16 +++++++++++++++-
+ lib/locking/lvmlockd.c         |  4 ++++
+ lib/metadata/mirror.c          | 17 +++++++++++++----
+ lib/metadata/pv_list.c         |  5 +++++
+ lib/metadata/vg.c              |  5 +++++
+ test/shell/losetup-partscan.sh |  2 ++
+ 10 files changed, 68 insertions(+), 8 deletions(-)
+
+diff --git a/lib/activate/dev_manager.c b/lib/activate/dev_manager.c
+index a73a556b2..284254d68 100644
+--- a/lib/activate/dev_manager.c
++++ b/lib/activate/dev_manager.c
+@@ -2875,6 +2875,10 @@ int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
+ 
+ 	/* FIXME Avoid repeating identical stat in dm_tree_node_add_target_area */
+ 	for (s = start_area; s < areas; s++) {
++
++		/* FIXME: dev_name() does not return NULL!  It needs to check if dm_list_empty(&dev->aliases)
++		   but this knot of logic is too complex to pull apart without careful deconstruction. */
++
+ 		if ((seg_type(seg, s) == AREA_PV &&
+ 		     (!seg_pvseg(seg, s) || !seg_pv(seg, s) || !seg_dev(seg, s) ||
+ 		       !(name = dev_name(seg_dev(seg, s))) || !*name ||
+@@ -2893,7 +2897,10 @@ int add_areas_line(struct dev_manager *dm, struct lv_segment *seg,
+ 				return_0;
+ 			num_error_areas++;
+ 		} else if (seg_type(seg, s) == AREA_PV) {
+-			if (!dm_tree_node_add_target_area(node, dev_name(seg_dev(seg, s)), NULL,
++			struct device *dev = seg_dev(seg, s);
++			name = dm_list_empty(&dev->aliases) ? NULL : dev_name(dev);
++
++			if (!dm_tree_node_add_target_area(node, name, NULL,
+ 				    (seg_pv(seg, s)->pe_start + (extent_size * seg_pe(seg, s)))))
+ 				return_0;
+ 			num_existing_areas++;
+diff --git a/lib/device/dev-type.c b/lib/device/dev-type.c
+index 0e77a009d..c67a86fa3 100644
+--- a/lib/device/dev-type.c
++++ b/lib/device/dev-type.c
+@@ -966,6 +966,9 @@ static int _wipe_known_signatures_with_blkid(struct device *dev, const char *nam
+ 
+ 	/* TODO: Should we check for valid dev - _dev_is_valid(dev)? */
+ 
++	if (dm_list_empty(&dev->aliases))
++		goto_out;
++
+ 	if (!(probe = blkid_new_probe_from_filename(dev_name(dev)))) {
+ 		log_error("Failed to create a new blkid probe for device %s.", dev_name(dev));
+ 		goto out;
+diff --git a/lib/device/device_id.c b/lib/device/device_id.c
+index bcb2e6bcf..82db6e4a5 100644
+--- a/lib/device/device_id.c
++++ b/lib/device/device_id.c
+@@ -347,6 +347,8 @@ const char *device_id_system_read(struct cmd_context *cmd, struct device *dev, u
+ 	}
+ 
+ 	else if (idtype == DEV_ID_TYPE_DEVNAME) {
++		if (dm_list_empty(&dev->aliases))
++			goto_bad;
+ 		if (!(idname = strdup(dev_name(dev))))
+ 			goto_bad;
+ 		return idname;
+@@ -940,6 +942,10 @@ int device_id_add(struct cmd_context *cmd, struct device *dev, const char *pvid_
+ 	if (!dev_get_partition_number(dev, &part))
+ 		return_0;
+ 
++	/* Ensure valid dev_name(dev) below. */
++	if (dm_list_empty(&dev->aliases))
++		return_0;
++
+ 	/*
+ 	 * When enable_devices_file=0 and pending_devices_file=1 we let
+ 	 * pvcreate/vgcreate add new du's to cmd->use_devices.  These du's may
+@@ -1820,6 +1826,9 @@ void device_ids_validate(struct cmd_context *cmd, struct dm_list *scanned_devs,
+ 		if (dev->flags & DEV_SCAN_NOT_READ)
+ 			continue;
+ 
++		if (dm_list_empty(&dev->aliases))
++			continue;
++
+ 		if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "persistent")) {
+ 			log_warn("Devices file %s is excluded by filter: %s.",
+ 				 dev_name(dev), dev_filtered_reason(dev));
+@@ -2197,14 +2206,14 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
+ 	dm_list_iterate_items(dil, &search_pvids) {
+ 		char *dup_devname1, *dup_devname2, *dup_devname3;
+ 
+-		if (!dil->dev) {
++		if (!dil->dev || dm_list_empty(&dil->dev->aliases)) {
+ 			not_found++;
+ 			continue;
+ 		}
+-		found++;
+ 
+ 		dev = dil->dev;
+ 		devname = dev_name(dev);
++		found++;
+ 
+ 		if (!(du = get_du_for_pvid(cmd, dil->pvid))) {
+ 			/* shouldn't happen */
+diff --git a/lib/label/hints.c b/lib/label/hints.c
+index 95d5d77b8..2a7b3dca7 100644
+--- a/lib/label/hints.c
++++ b/lib/label/hints.c
+@@ -498,6 +498,8 @@ int validate_hints(struct cmd_context *cmd, struct dm_list *hints)
+ 	if (!(iter = dev_iter_create(NULL, 0)))
+ 		return 0;
+ 	while ((dev = dev_iter_get(cmd, iter))) {
++		if (dm_list_empty(&dev->aliases))
++			continue;
+ 		if (!(hint = _find_hint_name(hints, dev_name(dev))))
+ 			continue;
+ 
+diff --git a/lib/label/label.c b/lib/label/label.c
+index ffe925254..cf707f7a3 100644
+--- a/lib/label/label.c
++++ b/lib/label/label.c
+@@ -1565,10 +1565,24 @@ int label_scan_open_rw(struct device *dev)
+ 
+ int label_scan_reopen_rw(struct device *dev)
+ {
++	const char *name;
+ 	int flags = 0;
+ 	int prev_fd = dev->bcache_fd;
+ 	int fd;
+ 
++	if (dm_list_empty(&dev->aliases)) {
++		log_error("Cannot reopen rw device %d:%d with no valid paths di %d fd %d.",
++			  (int)MAJOR(dev->dev), (int)MINOR(dev->dev), dev->bcache_di, dev->bcache_fd);
++		return 0;
++	}
++
++	name = dev_name(dev);
++	if (!name || name[0] != '/') {
++		log_error("Cannot reopen rw device %d:%d with no valid name di %d fd %d.",
++			  (int)MAJOR(dev->dev), (int)MINOR(dev->dev), dev->bcache_di, dev->bcache_fd);
++		return 0;
++	}
++
+ 	if (!(dev->flags & DEV_IN_BCACHE)) {
+ 		if ((dev->bcache_fd != -1) || (dev->bcache_di != -1)) {
+ 			/* shouldn't happen */
+@@ -1598,7 +1612,7 @@ int label_scan_reopen_rw(struct device *dev)
+ 	flags |= O_NOATIME;
+ 	flags |= O_RDWR;
+ 
+-	fd = open(dev_name(dev), flags, 0777);
++	fd = open(name, flags, 0777);
+ 	if (fd < 0) {
+ 		log_error("Failed to open rw %s errno %d di %d fd %d.",
+ 			  dev_name(dev), errno, dev->bcache_di, dev->bcache_fd);
+diff --git a/lib/locking/lvmlockd.c b/lib/locking/lvmlockd.c
+index b598df3d6..60c80f1b1 100644
+--- a/lib/locking/lvmlockd.c
++++ b/lib/locking/lvmlockd.c
+@@ -272,6 +272,8 @@ static void _lockd_retrive_vg_pv_list(struct volume_group *vg,
+ 
+ 	i = 0;
+ 	dm_list_iterate_items(pvl, &vg->pvs) {
++		if (!pvl->pv->dev || dm_list_empty(&pvl->pv->dev->aliases))
++			continue;
+ 		lock_pvs->path[i] = strdup(pv_dev_name(pvl->pv));
+ 		if (!lock_pvs->path[i]) {
+ 			log_error("Fail to allocate PV path for VG %s", vg->name);
+@@ -341,6 +343,8 @@ static void _lockd_retrive_lv_pv_list(struct volume_group *vg,
+ 
+ 	dm_list_iterate_items(pvl, &vg->pvs) {
+ 		if (lv_is_on_pv(lv, pvl->pv)) {
++			if (!pvl->pv->dev || dm_list_empty(&pvl->pv->dev->aliases))
++				continue;
+ 			lock_pvs->path[i] = strdup(pv_dev_name(pvl->pv));
+ 			if (!lock_pvs->path[i]) {
+ 				log_error("Fail to allocate PV path for LV %s/%s",
+diff --git a/lib/metadata/mirror.c b/lib/metadata/mirror.c
+index e2bf191a1..46da57948 100644
+--- a/lib/metadata/mirror.c
++++ b/lib/metadata/mirror.c
+@@ -1231,14 +1231,23 @@ int remove_mirrors_from_segments(struct logical_volume *lv,
+ const char *get_pvmove_pvname_from_lv_mirr(const struct logical_volume *lv_mirr)
+ {
+ 	struct lv_segment *seg;
++	struct device *dev;
+ 
+ 	dm_list_iterate_items(seg, &lv_mirr->segments) {
+ 		if (!seg_is_mirrored(seg))
+ 			continue;
+-		if (seg_type(seg, 0) == AREA_PV)
+-			return dev_name(seg_dev(seg, 0));
+-		if (seg_type(seg, 0) == AREA_LV)
+-			return dev_name(seg_dev(first_seg(seg_lv(seg, 0)), 0));
++		if (seg_type(seg, 0) == AREA_PV) {
++			dev = seg_dev(seg, 0);
++			if (!dev || dm_list_empty(&dev->aliases))
++				return NULL;
++			return dev_name(dev);
++		}
++		if (seg_type(seg, 0) == AREA_LV) {
++			dev = seg_dev(first_seg(seg_lv(seg, 0)), 0);
++			if (!dev || dm_list_empty(&dev->aliases))
++				return NULL;
++			return dev_name(dev);
++		}
+ 	}
+ 
+ 	return NULL;
+diff --git a/lib/metadata/pv_list.c b/lib/metadata/pv_list.c
+index 813e8e525..fc3667db0 100644
+--- a/lib/metadata/pv_list.c
++++ b/lib/metadata/pv_list.c
+@@ -152,6 +152,11 @@ static int _create_pv_entry(struct dm_pool *mem, struct pv_list *pvl,
+ 	struct pv_list *new_pvl = NULL, *pvl2;
+ 	struct dm_list *pe_ranges;
+ 
++	if (!pvl->pv->dev || dm_list_empty(&pvl->pv->dev->aliases)) {
++		log_error("Failed to create PV entry for missing device.");
++		return 0;
++	}
++
+ 	pvname = pv_dev_name(pvl->pv);
+ 	if (allocatable_only && !(pvl->pv->status & ALLOCATABLE_PV)) {
+ 		log_warn("WARNING: Physical volume %s not allocatable.", pvname);
+diff --git a/lib/metadata/vg.c b/lib/metadata/vg.c
+index 85482552a..adc954bab 100644
+--- a/lib/metadata/vg.c
++++ b/lib/metadata/vg.c
+@@ -679,6 +679,11 @@ int vgreduce_single(struct cmd_context *cmd, struct volume_group *vg,
+ 		return r;
+ 	}
+ 
++	if (!pv->dev || dm_list_empty(&pv->dev->aliases)) {
++		log_error("No device found for PV.");
++		return r;
++	}
++
+ 	log_debug("vgreduce_single VG %s PV %s", vg->name, pv_dev_name(pv));
+ 
+ 	if (pv_pe_alloc_count(pv)) {
+diff --git a/test/shell/losetup-partscan.sh b/test/shell/losetup-partscan.sh
+index 99f552ad1..670568945 100644
+--- a/test/shell/losetup-partscan.sh
++++ b/test/shell/losetup-partscan.sh
+@@ -33,6 +33,8 @@ aux udev_wait
+ ls -la "${LOOP}"*
+ test -e "${LOOP}p1"
+ 
++aux lvmconf 'devices/scan = "/dev"'
++
+ aux extend_filter "a|$LOOP|"
+ aux extend_devices "$LOOP"
+ 
+-- 
+2.34.3
+
diff --git a/SOURCES/0039-devices-use-dev-cache-aliases-handling-from-label-sc.patch b/SOURCES/0039-devices-use-dev-cache-aliases-handling-from-label-sc.patch
new file mode 100644
index 0000000..49a63fe
--- /dev/null
+++ b/SOURCES/0039-devices-use-dev-cache-aliases-handling-from-label-sc.patch
@@ -0,0 +1,272 @@
+From 932b9720bb074e49ac920642b3fe9c3d84019787 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Mon, 28 Feb 2022 17:37:12 -0600
+Subject: [PATCH 39/54] devices: use dev-cache aliases handling from label scan
+ functions
+
+The label scan functions where doing some device alias validation
+which is now better handled by the dev-cache layer, so just use
+that.
+---
+ lib/device/dev-cache.c |   4 +-
+ lib/device/dev-cache.h |   1 +
+ lib/label/label.c      | 143 ++++++++++-------------------------------
+ 3 files changed, 36 insertions(+), 112 deletions(-)
+
+diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c
+index 58e67e130..b0759b06c 100644
+--- a/lib/device/dev-cache.c
++++ b/lib/device/dev-cache.c
+@@ -1410,7 +1410,7 @@ static void _remove_alias(struct device *dev, const char *name)
+  * deactivated LV.  Those old paths are all invalid and are dropped here.
+  */
+ 
+-static void _verify_aliases(struct device *dev)
++void dev_cache_verify_aliases(struct device *dev)
+ {
+ 	struct dm_str_list *strl, *strl2;
+ 	struct stat st;
+@@ -1459,7 +1459,7 @@ static struct device *_dev_cache_get(struct cmd_context *cmd, const char *name,
+ 			_remove_alias(dev, name);
+ 
+ 			/* Remove any other names in dev->aliases that are incorrect. */
+-			_verify_aliases(dev);
++			dev_cache_verify_aliases(dev);
+ 		}
+ 		return NULL;
+ 	}
+diff --git a/lib/device/dev-cache.h b/lib/device/dev-cache.h
+index 51c3fc6c3..321a56d7b 100644
+--- a/lib/device/dev-cache.h
++++ b/lib/device/dev-cache.h
+@@ -55,6 +55,7 @@ int dev_cache_add_dir(const char *path);
+ struct device *dev_cache_get(struct cmd_context *cmd, const char *name, struct dev_filter *f);
+ struct device *dev_cache_get_existing(struct cmd_context *cmd, const char *name, struct dev_filter *f);
+ struct device *dev_cache_get_by_devt(struct cmd_context *cmd, dev_t devt);
++void dev_cache_verify_aliases(struct device *dev);
+ 
+ struct device *dev_hash_get(const char *name);
+ 
+diff --git a/lib/label/label.c b/lib/label/label.c
+index cf707f7a3..06958b502 100644
+--- a/lib/label/label.c
++++ b/lib/label/label.c
+@@ -458,7 +458,6 @@ static int _scan_dev_open(struct device *dev)
+ 	const char *name;
+ 	const char *modestr;
+ 	struct stat sbuf;
+-	int retried = 0;
+ 	int flags = 0;
+ 	int fd, di;
+ 
+@@ -478,14 +477,23 @@ static int _scan_dev_open(struct device *dev)
+ 		return 0;
+ 	}
+ 
++ next_name:
+ 	/*
+ 	 * All the names for this device (major:minor) are kept on
+ 	 * dev->aliases, the first one is the primary/preferred name.
++	 *
++	 * The default name preferences in dev-cache mean that the first
++	 * name in dev->aliases is not a symlink for scsi devices, but is
++	 * the /dev/mapper/ symlink for mpath devices.
++	 *
++	 * If preferred names are set to symlinks, should this
++	 * first attempt to open using a non-symlink?
++	 *
++	 * dm_list_first() returns NULL if the list is empty.
+ 	 */
+ 	if (!(name_list = dm_list_first(&dev->aliases))) {
+-		/* Shouldn't happen */
+-		log_error("Device open %s %d:%d has no path names.",
+-			  dev_name(dev), (int)MAJOR(dev->dev), (int)MINOR(dev->dev));
++		log_error("Device open %d:%d has no path names.",
++			  (int)MAJOR(dev->dev), (int)MINOR(dev->dev));
+ 		return 0;
+ 	}
+ 	name_sl = dm_list_item(name_list, struct dm_str_list);
+@@ -513,50 +521,34 @@ static int _scan_dev_open(struct device *dev)
+ 		modestr = "ro";
+ 	}
+ 
+-retry_open:
+-
+ 	fd = open(name, flags, 0777);
+-
+ 	if (fd < 0) {
+ 		if ((errno == EBUSY) && (flags & O_EXCL)) {
+ 			log_error("Can't open %s exclusively.  Mounted filesystem?",
+ 				  dev_name(dev));
++			return 0;
+ 		} else {
+-			int major, minor;
+-
+ 			/*
+-			 * Shouldn't happen, if it does, print stat info to help figure
+-			 * out what's wrong.
++			 * drop name from dev->aliases and use verify_aliases to
++			 * drop any other invalid aliases before retrying open with
++			 * any remaining valid paths.
+ 			 */
+-
+-			major = (int)MAJOR(dev->dev);
+-			minor = (int)MINOR(dev->dev);
+-
+-			log_error("Device open %s %d:%d failed errno %d", name, major, minor, errno);
+-
+-			if (stat(name, &sbuf)) {
+-				log_debug_devs("Device open %s %d:%d stat failed errno %d",
+-					       name, major, minor, errno);
+-			} else if (sbuf.st_rdev != dev->dev) {
+-				log_debug_devs("Device open %s %d:%d stat %d:%d does not match.",
+-					       name, major, minor,
+-					       (int)MAJOR(sbuf.st_rdev), (int)MINOR(sbuf.st_rdev));
+-			}
+-
+-			if (!retried) {
+-				/*
+-				 * FIXME: remove this, the theory for this retry is that
+-				 * there may be a udev race that we can sometimes mask by
+-				 * retrying.  This is here until we can figure out if it's
+-				 * needed and if so fix the real problem.
+-				 */
+-				usleep(5000);
+-				log_debug_devs("Device open %s retry", dev_name(dev));
+-				retried = 1;
+-				goto retry_open;
+-			}
++			log_debug("Drop alias for %d:%d failed open %s (%d)",
++				  (int)MAJOR(dev->dev), (int)MINOR(dev->dev), name, errno);
++			dev_cache_failed_path(dev, name);
++			dev_cache_verify_aliases(dev);
++			goto next_name;
+ 		}
+-		return 0;
++	}
++
++	/* Verify that major:minor from the path still match dev. */
++	if ((fstat(fd, &sbuf) < 0) || (sbuf.st_rdev != dev->dev)) {
++		log_warn("Invalid path %s for device %d:%d, trying different path.",
++			 name, (int)MAJOR(dev->dev), (int)MINOR(dev->dev));
++		(void)close(fd);
++		dev_cache_failed_path(dev, name);
++		dev_cache_verify_aliases(dev);
++		goto next_name;
+ 	}
+ 
+ 	dev->flags |= DEV_IN_BCACHE;
+@@ -604,37 +596,6 @@ static int _scan_dev_close(struct device *dev)
+ 	return 1;
+ }
+ 
+-static void _drop_bad_aliases(struct device *dev)
+-{
+-	struct dm_str_list *strl, *strl2;
+-	const char *name;
+-	struct stat sbuf;
+-	int major = (int)MAJOR(dev->dev);
+-	int minor = (int)MINOR(dev->dev);
+-	int bad;
+-
+-	dm_list_iterate_items_safe(strl, strl2, &dev->aliases) {
+-		name = strl->str;
+-		bad = 0;
+-
+-		if (stat(name, &sbuf)) {
+-			bad = 1;
+-			log_debug_devs("Device path check %d:%d %s stat failed errno %d",
+-					major, minor, name, errno);
+-		} else if (sbuf.st_rdev != dev->dev) {
+-			bad = 1;
+-			log_debug_devs("Device path check %d:%d %s stat %d:%d does not match.",
+-				       major, minor, name,
+-				       (int)MAJOR(sbuf.st_rdev), (int)MINOR(sbuf.st_rdev));
+-		}
+-
+-		if (bad) {
+-			log_debug_devs("Device path check %d:%d dropping path %s.", major, minor, name);
+-			dev_cache_failed_path(dev, name);
+-		}
+-	}
+-}
+-
+ // Like bcache_invalidate, only it throws any dirty data away if the
+ // write fails.
+ static void _invalidate_di(struct bcache *cache, int di)
+@@ -662,10 +623,8 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
+ 	char headers_buf[HEADERS_BUF_SIZE];
+ 	struct dm_list wait_devs;
+ 	struct dm_list done_devs;
+-	struct dm_list reopen_devs;
+ 	struct device_list *devl, *devl2;
+ 	struct block *bb;
+-	int retried_open = 0;
+ 	int scan_read_errors = 0;
+ 	int scan_process_errors = 0;
+ 	int scan_failed_count = 0;
+@@ -676,7 +635,6 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
+ 
+ 	dm_list_init(&wait_devs);
+ 	dm_list_init(&done_devs);
+-	dm_list_init(&reopen_devs);
+ 
+ 	log_debug_devs("Scanning %d devices for VG info", dm_list_size(devs));
+ 
+@@ -700,9 +658,9 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
+ 
+ 		if (!_in_bcache(devl->dev)) {
+ 			if (!_scan_dev_open(devl->dev)) {
+-				log_debug_devs("Scan failed to open %s.", dev_name(devl->dev));
++				log_debug_devs("Scan failed to open %d:%d %s.",
++					       (int)MAJOR(devl->dev->dev), (int)MINOR(devl->dev->dev), dev_name(devl->dev));
+ 				dm_list_del(&devl->list);
+-				dm_list_add(&reopen_devs, &devl->list);
+ 				devl->dev->flags |= DEV_SCAN_NOT_READ;
+ 				continue;
+ 			}
+@@ -786,41 +744,6 @@ static int _scan_list(struct cmd_context *cmd, struct dev_filter *f,
+ 	if (!dm_list_empty(devs))
+ 		goto scan_more;
+ 
+-	/*
+-	 * We're done scanning all the devs.  If we failed to open any of them
+-	 * the first time through, refresh device paths and retry.  We failed
+-	 * to open the devs on the reopen_devs list.
+-	 *
+-	 * FIXME: it's not clear if or why this helps.
+-	 */
+-	if (!dm_list_empty(&reopen_devs)) {
+-		if (retried_open) {
+-			/* Don't try again. */
+-			scan_failed_count += dm_list_size(&reopen_devs);
+-			dm_list_splice(&done_devs, &reopen_devs);
+-			goto out;
+-		}
+-		retried_open = 1;
+-
+-		dm_list_iterate_items_safe(devl, devl2, &reopen_devs) {
+-			_drop_bad_aliases(devl->dev);
+-
+-			if (dm_list_empty(&devl->dev->aliases)) {
+-				log_warn("WARNING: Scan ignoring device %d:%d with no paths.",
+-					 (int)MAJOR(devl->dev->dev),
+-					 (int)MINOR(devl->dev->dev));
+-					 
+-				dm_list_del(&devl->list);
+-				lvmcache_del_dev(devl->dev);
+-				scan_failed_count++;
+-			}
+-		}
+-
+-		/* Put devs that failed to open back on the original list to retry. */
+-		dm_list_splice(devs, &reopen_devs);
+-		goto scan_more;
+-	}
+-out:
+ 	log_debug_devs("Scanned devices: read errors %d process errors %d failed %d",
+ 			scan_read_errors, scan_process_errors, scan_failed_count);
+ 
+-- 
+2.34.3
+
diff --git a/SOURCES/0040-writecache-check-memory-usage.patch b/SOURCES/0040-writecache-check-memory-usage.patch
new file mode 100644
index 0000000..774e7a7
--- /dev/null
+++ b/SOURCES/0040-writecache-check-memory-usage.patch
@@ -0,0 +1,101 @@
+From 10a598075a0fdf6d93cc2fefa73fc4a5f1d0de48 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Tue, 1 Mar 2022 14:31:39 -0600
+Subject: [PATCH 40/54] writecache: check memory usage
+
+warn if writecache neds > 50% of system memory, and
+confirm if writecache needs > 90% of system memory.
+---
+ tools/lvconvert.c | 69 +++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 69 insertions(+)
+
+diff --git a/tools/lvconvert.c b/tools/lvconvert.c
+index 34b82ea02..a90946173 100644
+--- a/tools/lvconvert.c
++++ b/tools/lvconvert.c
+@@ -6072,6 +6072,69 @@ bad:
+ 	return 0;
+ }
+ 
++static int _check_writecache_memory(struct cmd_context *cmd, struct logical_volume *lv_fast,
++				     uint32_t block_size_sectors)
++{
++	char line[128];
++	FILE *fp;
++	uint64_t cachevol_size_bytes = lv_fast->size * SECTOR_SIZE;
++	uint64_t need_mem_bytes = 0;
++	uint64_t proc_mem_bytes = 0;
++	uint64_t need_mem_gb;
++	uint64_t proc_mem_gb;
++	unsigned long long proc_mem_kb = 0;
++
++	if (!(fp = fopen("/proc/meminfo", "r")))
++		goto skip_proc;
++
++	while (fgets(line, sizeof(line), fp)) {
++		if (strncmp(line, "MemTotal:", 9))
++			continue;
++		if (sscanf(line, "%*s%llu%*s", &proc_mem_kb) != 1)
++			break;
++		break;
++	}
++	(void)fclose(fp);
++
++	proc_mem_bytes = proc_mem_kb * 1024;
++
++ skip_proc:
++	/* dm-writecache memory consumption per block is 88 bytes */
++	if (block_size_sectors == 8) {
++		need_mem_bytes = cachevol_size_bytes * 88 / 4096;
++	} else if (block_size_sectors == 1) {
++		need_mem_bytes = cachevol_size_bytes * 88 / 512;
++	} else {
++		/* shouldn't happen */
++		log_warn("Unknown memory usage for unknown writecache block_size_sectors %u", block_size_sectors);
++		return 1;
++	}
++
++	need_mem_gb = need_mem_bytes / 1073741824;
++	proc_mem_gb = proc_mem_bytes / 1073741824;
++
++	/*
++	 * warn if writecache needs > 50% of main memory, and
++	 * confirm if writecache needs > 90% of main memory.
++	 */
++	if (need_mem_bytes >= (proc_mem_bytes / 2)) {
++		log_warn("WARNING: writecache size %s will use %llu GiB of system memory (%llu GiB).",
++			  display_size(cmd, lv_fast->size),
++			  (unsigned long long)need_mem_gb,
++			  (unsigned long long)proc_mem_gb);
++
++		if (need_mem_gb >= (proc_mem_gb * 9 / 10)) {
++			if (!arg_is_set(cmd, yes_ARG) &&
++			    yes_no_prompt("Continue adding writecache? [y/n]: ") == 'n') {
++				log_error("Conversion aborted.");
++				return 0;
++			}
++		}
++	}
++
++	return 1;
++}
++
+ int lvconvert_writecache_attach_single(struct cmd_context *cmd,
+ 					struct logical_volume *lv,
+ 					struct processing_handle *handle)
+@@ -6160,6 +6223,12 @@ int lvconvert_writecache_attach_single(struct cmd_context *cmd,
+ 		goto_bad;
+ 	}
+ 
++	if (!_check_writecache_memory(cmd, lv_fast, block_size_sectors)) {
++		if (!is_active && !deactivate_lv(cmd, lv))
++			stack;
++		goto_bad;
++	}
++
+ 	if (!is_active) {
+ 		if (!deactivate_lv(cmd, lv)) {
+ 			log_error("Failed to deactivate LV after checking block size %s", display_lvname(lv));
+-- 
+2.34.3
+
diff --git a/SOURCES/0041-change-messages-about-filtered-devices.patch b/SOURCES/0041-change-messages-about-filtered-devices.patch
new file mode 100644
index 0000000..e1b7aa0
--- /dev/null
+++ b/SOURCES/0041-change-messages-about-filtered-devices.patch
@@ -0,0 +1,108 @@
+From 090dc0c320f0abee8ab79f4eaea6561c195b5009 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Thu, 31 Mar 2022 11:38:08 -0500
+Subject: [PATCH 41/54] change messages about filtered devices
+
+Change messages that refer to devices being "excluded by filters"
+to say just "excluded".  This will avoid mistaking the word
+"filters" with the lvm.conf filter setting.
+---
+ lib/device/device_id.c | 6 +++---
+ tools/lvmdevices.c     | 4 ++--
+ tools/pvscan.c         | 4 ++--
+ tools/vgimportclone.c  | 4 ++--
+ 4 files changed, 9 insertions(+), 9 deletions(-)
+
+diff --git a/lib/device/device_id.c b/lib/device/device_id.c
+index 82db6e4a5..6133e700a 100644
+--- a/lib/device/device_id.c
++++ b/lib/device/device_id.c
+@@ -1744,7 +1744,7 @@ void device_ids_validate(struct cmd_context *cmd, struct dm_list *scanned_devs,
+ 		 * probably wants to do something about it.
+ 		 */
+ 		if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "persistent")) {
+-			log_warn("Devices file %s is excluded by filter: %s.",
++			log_warn("Devices file %s is excluded: %s.",
+ 				 dev_name(dev), dev_filtered_reason(dev));
+ 			continue;
+ 		}
+@@ -1830,7 +1830,7 @@ void device_ids_validate(struct cmd_context *cmd, struct dm_list *scanned_devs,
+ 			continue;
+ 
+ 		if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, "persistent")) {
+-			log_warn("Devices file %s is excluded by filter: %s.",
++			log_warn("Devices file %s is excluded: %s.",
+ 				 dev_name(dev), dev_filtered_reason(dev));
+ 			/* FIXME: what if this dev is wrongly matched and should be checked below? */
+ 			continue;
+@@ -2266,7 +2266,7 @@ void device_ids_find_renamed_devs(struct cmd_context *cmd, struct dm_list *dev_l
+ 
+ 		if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, NULL)) {
+ 			/* I don't think this would happen */
+-			log_warn("WARNING: new device %s for PVID %s does not pass filter %s.",
++			log_warn("WARNING: new device %s for PVID %s is excluded: %s.",
+ 				 dev_name(dev), dil->pvid, dev_filtered_reason(dev));
+ 			if (du) /* Should not happen 'du' is NULL */
+ 				du->dev = NULL;
+diff --git a/tools/lvmdevices.c b/tools/lvmdevices.c
+index 662b35f9a..8521b89ea 100644
+--- a/tools/lvmdevices.c
++++ b/tools/lvmdevices.c
+@@ -112,7 +112,7 @@ static void _search_devs_for_pvids(struct cmd_context *cmd, struct dm_list *sear
+ 		dev = devl->dev;
+ 		cmd->filter->wipe(cmd, cmd->filter, dev, NULL);
+ 		if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, NULL)) {
+-			log_warn("WARNING: PVID %s found on %s which is excluded by filter: %s",
++			log_warn("WARNING: PVID %s found on %s which is excluded: %s",
+ 			 	  dev->pvid, dev_name(dev), dev_filtered_reason(dev));
+ 			dm_list_del(&devl->list);
+ 		}
+@@ -310,7 +310,7 @@ int lvmdevices(struct cmd_context *cmd, int argc, char **argv)
+ 		cmd->filter_deviceid_skip = 1;
+ 
+ 		if (!cmd->filter->passes_filter(cmd, cmd->filter, dev, NULL)) {
+-			log_warn("WARNING: adding device %s that is excluded by filter: %s.",
++			log_warn("WARNING: adding device %s that is excluded: %s.",
+ 				 dev_name(dev), dev_filtered_reason(dev));
+ 		}
+ 
+diff --git a/tools/pvscan.c b/tools/pvscan.c
+index 160a2c9a0..50d46051a 100644
+--- a/tools/pvscan.c
++++ b/tools/pvscan.c
+@@ -1756,7 +1756,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
+ 
+ 	dm_list_iterate_items_safe(devl, devl2, &pvscan_devs) {
+ 		if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
+-			log_print_pvscan(cmd, "%s excluded by filters: %s.",
++			log_print_pvscan(cmd, "%s excluded: %s.",
+ 					 dev_name(devl->dev), dev_filtered_reason(devl->dev));
+ 			dm_list_del(&devl->list);
+ 		}
+@@ -1813,7 +1813,7 @@ static int _pvscan_cache_args(struct cmd_context *cmd, int argc, char **argv,
+ 
+ 		/* Applies all filters, including those that need data from dev. */
+ 		if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, NULL)) {
+-			log_print_pvscan(cmd, "%s excluded by filters: %s.",
++			log_print_pvscan(cmd, "%s excluded: %s.",
+ 					 dev_name(devl->dev), dev_filtered_reason(devl->dev));
+ 			dm_list_del(&devl->list);
+ 		}
+diff --git a/tools/vgimportclone.c b/tools/vgimportclone.c
+index 23bb6271f..cab501619 100644
+--- a/tools/vgimportclone.c
++++ b/tools/vgimportclone.c
+@@ -311,8 +311,8 @@ int vgimportclone(struct cmd_context *cmd, int argc, char **argv)
+ 	 */
+ 	dm_list_iterate_items(devl, &vp.new_devs) {
+ 		if (!cmd->filter->passes_filter(cmd, cmd->filter, devl->dev, "persistent")) {
+-			/* FIXME: print which filter */
+-			log_error("Device %s was excluded by filters.", dev_name(devl->dev));
++			log_error("Device %s is excluded: %s.",
++				  dev_name(devl->dev), dev_filtered_reason(devl->dev));
+ 			goto out;
+ 		}
+ 	}
+-- 
+2.34.3
+
diff --git a/SOURCES/0042-vgimportdevices-fix-incorrect-deviceidtype-usage.patch b/SOURCES/0042-vgimportdevices-fix-incorrect-deviceidtype-usage.patch
new file mode 100644
index 0000000..fbf6ee7
--- /dev/null
+++ b/SOURCES/0042-vgimportdevices-fix-incorrect-deviceidtype-usage.patch
@@ -0,0 +1,31 @@
+From 4aa92f3e18cb49470ee9b5d928abe6b4c86f3074 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Wed, 6 Apr 2022 12:20:26 -0500
+Subject: [PATCH 42/54] vgimportdevices: fix incorrect deviceidtype usage
+
+When a VG has PVs with different device id types,
+it would try to use the idtype of the previous PV
+in the loop.  This would produce an unncessary warning,
+or could lead to using the devname idtype when a better
+idtype is available.
+---
+ tools/vgimportdevices.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+diff --git a/tools/vgimportdevices.c b/tools/vgimportdevices.c
+index 2580613c4..ea205d941 100644
+--- a/tools/vgimportdevices.c
++++ b/tools/vgimportdevices.c
+@@ -57,8 +57,7 @@ static int _vgimportdevices_single(struct cmd_context *cmd,
+ 	dm_list_iterate_items(pvl, &vg->pvs) {
+ 		pv = pvl->pv;
+ 
+-		if (!idtypestr && pv->device_id_type)
+-			idtypestr = pv->device_id_type;
++		idtypestr = pv->device_id_type;
+ 
+ 		memcpy(pvid, &pvl->pv->id.uuid, ID_LEN);
+ 		device_id_add(cmd, pv->dev, pvid, idtypestr, NULL);
+-- 
+2.34.3
+
diff --git a/SOURCES/0043-lvmlockd-return-error-from-vgcreate-init_vg_sanlock.patch b/SOURCES/0043-lvmlockd-return-error-from-vgcreate-init_vg_sanlock.patch
new file mode 100644
index 0000000..f67d1a5
--- /dev/null
+++ b/SOURCES/0043-lvmlockd-return-error-from-vgcreate-init_vg_sanlock.patch
@@ -0,0 +1,32 @@
+From 6de2a6a378a7673168fad34aebe8ddcb564a5911 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Fri, 8 Apr 2022 11:28:53 -0500
+Subject: [PATCH 43/54] lvmlockd: return error from vgcreate init_vg_sanlock
+
+in vgcreate for shared sanlock vg, if sanlock_write_resource
+returns an unexpected error, then make init_vg_sanlock fail
+which will cause the vgcreate to fail.
+---
+ daemons/lvmlockd/lvmlockd-sanlock.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/daemons/lvmlockd/lvmlockd-sanlock.c b/daemons/lvmlockd/lvmlockd-sanlock.c
+index e595eeffd..d87d1093b 100644
+--- a/daemons/lvmlockd/lvmlockd-sanlock.c
++++ b/daemons/lvmlockd/lvmlockd-sanlock.c
+@@ -684,10 +684,10 @@ int lm_init_vg_sanlock(char *ls_name, char *vg_name, uint32_t flags, char *vg_ar
+ 			break;
+ 		}
+ 
+-		if (rv) {
++		if (rv < 0) {
+ 			log_error("clear lv resource area %llu error %d",
+ 				  (unsigned long long)offset, rv);
+-			break;
++			return rv;
+ 		}
+ 		offset += align_size;
+ 	}
+-- 
+2.34.3
+
diff --git a/SOURCES/0044-devices-file-remove-extraneous-unlock-in-vgchange-u.patch b/SOURCES/0044-devices-file-remove-extraneous-unlock-in-vgchange-u.patch
new file mode 100644
index 0000000..8009d2f
--- /dev/null
+++ b/SOURCES/0044-devices-file-remove-extraneous-unlock-in-vgchange-u.patch
@@ -0,0 +1,45 @@
+From d96432835532fbcd8c72694c6ed68fca3ce98d5c Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Wed, 13 Apr 2022 12:16:57 -0500
+Subject: [PATCH 44/54] devices file: remove extraneous unlock in vgchange -u
+
+vgchange -u exit path was unlocking the devices file in cases
+when it wasn't needed, which produced an warning.
+---
+ lib/device/device_id.c | 7 +++----
+ 1 file changed, 3 insertions(+), 4 deletions(-)
+
+diff --git a/lib/device/device_id.c b/lib/device/device_id.c
+index 6133e700a..20901ab90 100644
+--- a/lib/device/device_id.c
++++ b/lib/device/device_id.c
+@@ -1272,15 +1272,15 @@ void device_id_update_vg_uuid(struct cmd_context *cmd, struct volume_group *vg,
+ 	int update = 0;
+ 
+ 	if (!cmd->enable_devices_file)
+-		goto out;
++		return;
+ 
+ 	/* Without this setting there is no stacking LVs on PVs. */
+ 	if (!cmd->scan_lvs)
+-		goto out;
++		return;
+ 
+ 	/* Check if any devices file entries are stacked on LVs. */
+ 	if (!_device_ids_use_lvmlv(cmd))
+-		goto out;
++		return;
+ 
+ 	memcpy(old_vgid, old_vg_id, ID_LEN);
+ 	memcpy(new_vgid, &vg->id, ID_LEN);
+@@ -1310,7 +1310,6 @@ void device_id_update_vg_uuid(struct cmd_context *cmd, struct volume_group *vg,
+ 	if (update &&
+ 	    !device_ids_write(cmd))
+ 		stack;
+- out:
+ 	unlock_devices_file(cmd);
+ }
+ 
+-- 
+2.34.3
+
diff --git a/SOURCES/0045-filter-mpath-use-multipath-blacklist.patch b/SOURCES/0045-filter-mpath-use-multipath-blacklist.patch
new file mode 100644
index 0000000..c2f921a
--- /dev/null
+++ b/SOURCES/0045-filter-mpath-use-multipath-blacklist.patch
@@ -0,0 +1,458 @@
+From 5d40b91bd4aa8580ee1f40d467b848f7847f39e3 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Thu, 21 Apr 2022 13:45:01 -0500
+Subject: [PATCH 45/54] filter-mpath: use multipath blacklist
+
+Explicit wwid's from these sections control whether the
+same wwid in /etc/multipath/wwids is recognized as a
+multipath component.  Other non-wwid keywords are not
+used, and may require disabling the use of the multipath
+wwids file in lvm.conf.
+---
+ lib/device/dev-mpath.c                | 181 ++++++++++++++++++++++++--
+ test/shell/duplicate-pvs-multipath.sh |   2 +-
+ test/shell/multipath-config.sh        | 171 ++++++++++++++++++++++++
+ 3 files changed, 342 insertions(+), 12 deletions(-)
+ create mode 100644 test/shell/multipath-config.sh
+
+diff --git a/lib/device/dev-mpath.c b/lib/device/dev-mpath.c
+index cbbad9dc9..6eed03c5b 100644
+--- a/lib/device/dev-mpath.c
++++ b/lib/device/dev-mpath.c
+@@ -17,12 +17,14 @@
+ #include "lib/activate/activate.h"
+ #include "lib/commands/toolcontext.h"
+ #include "lib/device/device_id.h"
++#include "lib/datastruct/str_list.h"
+ #ifdef UDEV_SYNC_SUPPORT
+ #include <libudev.h>
+ #include "lib/device/dev-ext-udev-constants.h"
+ #endif
+ 
+ #include <dirent.h>
++#include <ctype.h>
+ 
+ #define MPATH_PREFIX "mpath-"
+ 
+@@ -35,15 +37,167 @@
+  * If dm-3 is not an mpath device, then the constant "1" is stored in
+  * the hash table with the key of the dm minor number.
+  */
+-static struct dm_pool *_hash_mem;
++static struct dm_pool *_wwid_mem;
+ static struct dm_hash_table *_minor_hash_tab;
+ static struct dm_hash_table *_wwid_hash_tab;
++static struct dm_list _ignored;
++static struct dm_list _ignored_exceptions;
+ 
+ #define MAX_WWID_LINE 512
+ 
+-/*
+- * do we need to check the multipath.conf blacklist?
+- */
++static void _read_blacklist_file(const char *path)
++{
++	FILE *fp;
++	char line[MAX_WWID_LINE];
++	char wwid[MAX_WWID_LINE];
++	char *word, *p;
++	int section_black = 0;
++	int section_exceptions = 0;
++	int found_quote;
++	int found_three;
++	int i, j;
++
++	if (!(fp = fopen(path, "r")))
++		return;
++
++	while (fgets(line, sizeof(line), fp)) {
++		word = NULL;
++
++		/* skip initial white space on the line */
++		for (i = 0; i < MAX_WWID_LINE; i++) {
++			if ((line[i] == '\n') || (line[i] == '\0'))
++				break;
++			if (isspace(line[i]))
++				continue;
++			word = &line[i];
++			break;
++		}
++
++		if (!word || word[0] == '#')
++			continue;
++
++		/* identify the start of the section we want to read */
++		if (strchr(word, '{')) {
++			if (!strncmp(word, "blacklist_exceptions", 20))
++				section_exceptions = 1;
++			else if (!strncmp(word, "blacklist", 9))
++				section_black = 1;
++			continue;
++		}
++		/* identify the end of the section we've been reading */
++		if (strchr(word, '}')) {
++			section_exceptions = 0;
++			section_black = 0;
++			continue;
++		}
++		/* skip lines that are not in a section we want */
++		if (!section_black && !section_exceptions)
++			continue;
++
++		/*
++		 * read a wwid from the blacklist{_exceptions} section.
++		 * does not recognize other non-wwid entries in the
++		 * section, and skips those (should the entire mp
++		 * config filtering be disabled if non-wwids are seen?
++		 */
++		if (!(p = strstr(word, "wwid")))
++			continue;
++
++		i += 4; /* skip "wwid" */
++
++		/*
++		 * copy wwid value from the line.
++		 * the wwids copied here need to match the
++		 * wwids read from /etc/multipath/wwids,
++		 * which are matched to wwids from sysfs.
++		 */
++
++		memset(wwid, 0, sizeof(wwid));
++		found_quote = 0;
++		found_three = 0;
++		j = 0;
++
++		for (; i < MAX_WWID_LINE; i++) {
++			if ((line[i] == '\n') || (line[i] == '\0'))
++				break;
++			if (!j && isspace(line[i]))
++				continue;
++			if (isspace(line[i]))
++				break;
++			/* quotes around wwid are optional */
++			if ((line[i] == '"') && !found_quote) {
++				found_quote = 1;
++				continue;
++			}
++			/* second quote is end of wwid */
++			if ((line[i] == '"') && found_quote)
++				break;
++			/* ignore first "3" in wwid */
++			if ((line[i] == '3') && !found_three) {
++				found_three = 1;
++				continue;
++			}
++
++			wwid[j] = line[i];
++			j++;
++		}
++
++		if (j < 8)
++			continue;
++
++		log_debug("multipath wwid %s in %s %s",
++			  wwid, section_exceptions ? "blacklist_exceptions" : "blacklist", path);
++
++		if (section_exceptions) {
++			if (!str_list_add(_wwid_mem, &_ignored_exceptions, dm_pool_strdup(_wwid_mem, wwid)))
++				stack;
++		} else {
++			if (!str_list_add(_wwid_mem, &_ignored, dm_pool_strdup(_wwid_mem, wwid)))
++				stack;
++		}
++	}
++
++	if (fclose(fp))
++		stack;
++}
++
++static void _read_wwid_exclusions(void)
++{
++	char path[PATH_MAX] = { 0 };
++	DIR *dir;
++	struct dirent *de;
++	struct dm_str_list *sl, *sl2;
++	int rem_count = 0;
++
++	_read_blacklist_file("/etc/multipath.conf");
++
++	if ((dir = opendir("/etc/multipath/conf.d"))) {
++		while ((de = readdir(dir))) {
++			if (de->d_name[0] == '.')
++				continue;
++			snprintf(path, PATH_MAX-1, "/etc/multipath/conf.d/%s", de->d_name);
++			_read_blacklist_file(path);
++		}
++		closedir(dir);
++	}
++
++	/* for each wwid in ignored_exceptions, remove it from ignored */
++
++	dm_list_iterate_items_safe(sl, sl2, &_ignored) {
++		if (str_list_match_item(&_ignored_exceptions, sl->str))
++			str_list_del(&_ignored, sl->str);
++	}
++
++	/* for each wwid in ignored, remove it from wwid_hash */
++
++	dm_list_iterate_items(sl, &_ignored) {
++		dm_hash_remove_binary(_wwid_hash_tab, sl->str, strlen(sl->str));
++		rem_count++;
++	}
++
++	if (rem_count)
++		log_debug("multipath config ignored %d wwids", rem_count);
++}
+ 
+ static void _read_wwid_file(const char *config_wwids_file)
+ {
+@@ -93,6 +247,9 @@ int dev_mpath_init(const char *config_wwids_file)
+ 	struct dm_hash_table *minor_tab;
+ 	struct dm_hash_table *wwid_tab;
+ 
++	dm_list_init(&_ignored);
++	dm_list_init(&_ignored_exceptions);
++
+ 	if (!(mem = dm_pool_create("mpath", 256))) {
+ 		log_error("mpath pool creation failed.");
+ 		return 0;
+@@ -104,7 +261,7 @@ int dev_mpath_init(const char *config_wwids_file)
+ 		return 0;
+ 	}
+ 
+-	_hash_mem = mem;
++	_wwid_mem = mem;
+ 	_minor_hash_tab = minor_tab;
+ 
+ 	/* multipath_wwids_file="" disables the use of the file */
+@@ -116,16 +273,18 @@ int dev_mpath_init(const char *config_wwids_file)
+ 	if (!(wwid_tab = dm_hash_create(110))) {
+ 		log_error("mpath hash table creation failed.");
+ 		dm_hash_destroy(_minor_hash_tab);
+-		dm_pool_destroy(_hash_mem);
++		dm_pool_destroy(_wwid_mem);
+ 		_minor_hash_tab = NULL;
+-		_hash_mem = NULL;
++		_wwid_mem = NULL;
+ 		return 0;
+ 	}
+ 
+ 	_wwid_hash_tab = wwid_tab;
+ 
+-	if (config_wwids_file)
++	if (config_wwids_file) {
+ 		_read_wwid_file(config_wwids_file);
++		_read_wwid_exclusions();
++	}
+ 
+ 	return 1;
+ }
+@@ -136,12 +295,12 @@ void dev_mpath_exit(void)
+ 		dm_hash_destroy(_minor_hash_tab);
+ 	if (_wwid_hash_tab)
+ 		dm_hash_destroy(_wwid_hash_tab);
+-	if (_hash_mem)
+-		dm_pool_destroy(_hash_mem);
++	if (_wwid_mem)
++		dm_pool_destroy(_wwid_mem);
+ 
+ 	_minor_hash_tab = NULL;
+ 	_wwid_hash_tab = NULL;
+-	_hash_mem = NULL;
++	_wwid_mem = NULL;
+ }
+ 
+ 
+diff --git a/test/shell/duplicate-pvs-multipath.sh b/test/shell/duplicate-pvs-multipath.sh
+index a145e4afb..59c15b0d4 100644
+--- a/test/shell/duplicate-pvs-multipath.sh
++++ b/test/shell/duplicate-pvs-multipath.sh
+@@ -10,7 +10,7 @@
+ # along with this program; if not, write to the Free Software Foundation,
+ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ 
+-test_description='udev rule and systemd unit run vgchange'
++test_description='duplicate pv detection of mpath components using wwid'
+ 
+ SKIP_WITH_LVMPOLLD=1
+ SKIP_WITH_LVMLOCKD=1
+diff --git a/test/shell/multipath-config.sh b/test/shell/multipath-config.sh
+new file mode 100644
+index 000000000..ffb7d632a
+--- /dev/null
++++ b/test/shell/multipath-config.sh
+@@ -0,0 +1,171 @@
++#!/usr/bin/env bash
++
++# Copyright (C) 2021 Red Hat, Inc. All rights reserved.
++#
++# This copyrighted material is made available to anyone wishing to use,
++# modify, copy, or redistribute it subject to the terms and conditions
++# of the GNU General Public License v.2.
++#
++# You should have received a copy of the GNU General Public License
++# along with this program; if not, write to the Free Software Foundation,
++# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++
++test_description='using multipath blacklist'
++
++SKIP_WITH_LVMPOLLD=1
++SKIP_WITH_LVMLOCKD=1
++
++. lib/inittest
++
++# FIXME: don't run this test by default because it destroys the
++# local multipath config, the timing of multipath/dm/lvm interactions
++# is fragile, and there's insufficient cleanup after a test fails.
++skip
++
++systemctl stop multipathd
++multipath -F || true
++rm /etc/multipath/wwids || true
++rmmod scsi_debug || true
++rm /etc/multipath/conf.d/lvmtest.conf || true
++
++modprobe --dry-run scsi_debug || skip
++multipath -l || skip
++multipath -l | grep scsi_debug && skip
++ls /etc/multipath/wwids && skip
++
++# Need to use /dev/mapper/mpath
++aux lvmconf 'devices/dir = "/dev"'
++aux lvmconf 'devices/scan = "/dev"'
++# Could set filter to $MP and the component /dev/sd devs
++aux lvmconf "devices/filter = [ \"a|.*|\" ]"
++aux lvmconf "devices/global_filter = [ \"a|.*|\" ]"
++
++modprobe scsi_debug dev_size_mb=16 num_tgts=1
++sleep 2
++
++# Get scsi device name created by scsi_debug.
++# SD = sdh
++# SD_DEV = /dev/sdh
++
++SD=$(grep -H scsi_debug /sys/block/sd*/device/model | cut -f4 -d /);
++echo $SD
++SD_DEV=/dev/$SD
++echo $SD_DEV
++
++# if multipath claimed SD, then io will fail
++#dd if=$SD_DEV of=/dev/null bs=4k count=1 iflag=direct
++#dd if=/dev/zero of=$SD_DEV bs=4k count=1 oflag=direct
++
++# check if multipathd claimed the scsi dev when it appears and create mp dm device
++sleep 2
++multipath -l
++# create the mp dm device
++multipath $SD_DEV
++
++# Get mpath device name created by multipath.
++# MP = mpatha
++# MP_DEV = /dev/maper/mpatha
++
++MP=$(multipath -l | grep scsi_debug | cut -f1 -d ' ')
++echo $MP
++MP_DEV=/dev/mapper/$MP
++echo $MP_DEV
++
++dd if=$MP_DEV of=/dev/null bs=4k count=1 iflag=direct
++dd if=/dev/zero of=$MP_DEV bs=4k count=1 oflag=direct
++
++# Get wwid for the mp and sd dev.
++WWID=$(multipath -l $MP_DEV | head -1 | awk '{print $2}' | tr -d ')' | tr -d '(')
++echo $WWID
++
++grep $WWID /etc/multipath/wwids
++
++pvcreate $MP_DEV
++vgcreate $vg1 $MP_DEV
++
++not pvs $SD_DEV
++pvs $MP_DEV
++
++# remove mpath dm device then check that SD_DEV is
++# filtered based on /etc/multipath/wwids instead of
++# based on sysfs holder
++multipath -f $MP
++sleep 2
++not pvs $SD_DEV
++multipath $SD_DEV
++sleep 2
++multipath -l | grep $SD
++
++#
++# Add the wwid to the blacklist, then restart multipath
++# so the sd dev should no longer be used by multipath,
++# but the sd dev wwid is still in /etc/multipath/wwids.
++#
++
++mkdir /etc/multipath/conf.d/ || true
++rm -f /etc/multipath/conf.d/lvmtest.conf
++
++cat <<EOF > "/etc/multipath/conf.d/lvmtest.conf"
++blacklist {
++	wwid $WWID
++}
++EOF
++
++cat /etc/multipath/conf.d/lvmtest.conf
++
++multipath -r
++sleep 2
++
++grep $WWID /etc/multipath/wwids
++
++multipath -l |tee out
++not grep $SD out
++not grep $MP out
++not grep $WWID out
++
++not pvs $MP_DEV
++pvs $SD_DEV
++vgs $vg1
++
++#
++# Add the wwid to the blacklist_exceptions, in addition
++# to the blacklist, then restart multipath so the
++# sd dev should again be used by multipath.
++#
++
++rm -f /etc/multipath/conf.d/lvmtest.conf
++
++cat <<EOF > "/etc/multipath/conf.d/lvmtest.conf"
++blacklist {
++wwid $WWID
++}
++blacklist_exceptions {
++wwid $WWID
++}
++EOF
++
++cat /etc/multipath/conf.d/lvmtest.conf
++
++multipath -r
++sleep 2
++
++grep $WWID /etc/multipath/wwids
++
++multipath -l |tee out
++grep $SD out
++grep $MP out
++grep $WWID out
++
++pvs $MP_DEV
++not pvs $SD_DEV
++vgs $vg1
++lvs $vg1
++
++sleep 2
++vgremove -ff $vg1
++sleep 2
++multipath -f $MP
++rm /etc/multipath/conf.d/lvmtest.conf
++rm /etc/multipath/wwids
++sleep 1
++rmmod scsi_debug
+-- 
+2.34.3
+
diff --git a/SOURCES/0046-improve-description-of-devices-option.patch b/SOURCES/0046-improve-description-of-devices-option.patch
new file mode 100644
index 0000000..3ec4a9e
--- /dev/null
+++ b/SOURCES/0046-improve-description-of-devices-option.patch
@@ -0,0 +1,28 @@
+From e027f4da9bc7b4ed9b225af75089e3443595bf81 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Mon, 2 May 2022 09:46:28 -0500
+Subject: [PATCH 46/54] improve description of devices option
+
+---
+ tools/args.h | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/tools/args.h b/tools/args.h
+index 9a7bf81b2..00a2ec817 100644
+--- a/tools/args.h
++++ b/tools/args.h
+@@ -235,8 +235,9 @@ arg(deviceidtype_ARG, '\0', "deviceidtype", string_VAL, 0, 0,
+     "then it will override the default type that lvm would use.\n")
+ 
+ arg(devices_ARG, '\0', "devices", pv_VAL, ARG_GROUPABLE, 0,
+-    "Devices that the command can use. This option can be repeated\n"
+-    "or accepts a comma separated list of devices. This overrides\n"
++    "Restricts the devices that are visible and accessible to the command.\n"
++    "Devices not listed will appear to be missing. This option can be\n"
++    "repeated, or accepts a comma separated list of devices. This overrides\n"
+     "the devices file.\n")
+ 
+ arg(devicesfile_ARG, '\0', "devicesfile", string_VAL, 0, 0,
+-- 
+2.34.3
+
diff --git a/SOURCES/0047-vdo-support-vdosettings.patch b/SOURCES/0047-vdo-support-vdosettings.patch
new file mode 100644
index 0000000..e6d91f3
--- /dev/null
+++ b/SOURCES/0047-vdo-support-vdosettings.patch
@@ -0,0 +1,576 @@
+From 9c6954bc61b22ca03df8897d88eb9618e65fc3c6 Mon Sep 17 00:00:00 2001
+From: Zdenek Kabelac <zkabelac@redhat.com>
+Date: Wed, 13 Apr 2022 15:09:08 +0200
+Subject: [PATCH 47/54] vdo: support --vdosettings
+
+Allow to use --vdosettings with lvcreate,lvconvert,lvchange.
+Support settings currenly only configurable via lvm.conf.
+With lvchange we require inactivate LV for changes to be applied.
+
+Settings block_map_era_length has supported alias block_map_period.
+---
+ device_mapper/vdo/target.h  |   6 +-
+ man/lvmvdo.7_main           |  39 ++++++--
+ test/shell/lvchange-vdo.sh  |   8 ++
+ test/shell/lvconvert-vdo.sh |   8 +-
+ test/shell/lvcreate-vdo.sh  |   6 +-
+ tools/args.h                |   9 ++
+ tools/command-lines.in      |   6 +-
+ tools/lvchange.c            |  43 ++++++++-
+ tools/lvconvert.c           |   9 +-
+ tools/lvcreate.c            |  34 ++++---
+ tools/toollib.c             | 177 ++++++++++++++++++++++++++++++++++++
+ tools/toollib.h             |   6 ++
+ 12 files changed, 312 insertions(+), 39 deletions(-)
+
+diff --git a/device_mapper/vdo/target.h b/device_mapper/vdo/target.h
+index 51dde3f4d..60c5bff56 100644
+--- a/device_mapper/vdo/target.h
++++ b/device_mapper/vdo/target.h
+@@ -77,8 +77,10 @@ enum dm_vdo_write_policy {
+ struct dm_vdo_target_params {
+ 	uint32_t minimum_io_size;       // in sectors
+ 	uint32_t block_map_cache_size_mb;
+-	uint32_t block_map_era_length;	// format period
+-
++	union {
++		uint32_t block_map_era_length;	// format period
++		uint32_t block_map_period;      // supported alias
++	};
+ 	uint32_t check_point_frequency;
+ 	uint32_t index_memory_size_mb;  // format
+ 
+diff --git a/man/lvmvdo.7_main b/man/lvmvdo.7_main
+index 3b77173c4..14bd640b5 100644
+--- a/man/lvmvdo.7_main
++++ b/man/lvmvdo.7_main
+@@ -132,6 +132,19 @@ that can keep 100% incompressible data there.
+ # lvconvert --type vdo-pool -n vdo0 -V10G vg/ExistingLV
+ .fi
+ .
++.SS \n+[step]. Change the compression and deduplication of a VDOPoolLV
++.
++Disable or enable the compression and deduplication for VDOPoolLV
++(the volume that maintains all VDO LV(s) associated with it).
++.P
++.B lvchange --compression y|n --deduplication y|n VG/VDOPoolLV
++.P
++.I Example
++.nf
++# lvchange --compression n  vg/vdopool0
++# lvchange --deduplication y vg/vdopool1
++.fi
++.
+ .SS \n+[step]. Change the default settings used for creating a VDOPoolLV
+ .
+ VDO allows to set a large variety of options. Lots of these settings
+@@ -173,17 +186,27 @@ EOF
+ # lvcreate --vdo -L10G --config 'allocation/vdo_cpu_threads=4' vg/vdopool1
+ .fi
+ .
+-.SS \n+[step]. Change the compression and deduplication of a VDOPoolLV
+-.
+-Disable or enable the compression and deduplication for VDOPoolLV
+-(the volume that maintains all VDO LV(s) associated with it).
+-.P
+-.B lvchange --compression y|n --deduplication y|n VG/VDOPoolLV
++.SS \n+[step]. Set or change VDO settings with option --vdosettings
++.
++Use the form 'option=value' or 'option1=value option2=value',
++or repeat --vdosettings for each option being set.
++Options are listed in the Example section above, for the full description see
++.BR lvm.conf (5).
++Options can omit 'vdo_' and 'vdo_use_' prefixes and all its underscores.
++So i.e.  vdo_use_metadata_hints=1  and  metadatahints=1 are equivalent.
++To change the option for an already existing VDOPoolLV use
++.BR lvchange (8)
++command. However not all option can be changed.
++Only compression and deduplication options can be also changed for an active VDO LV.
++Lowest priority options are specified with configuration file,
++then with --vdosettings and highest are expliction option --compression
++and --deduplication.
+ .P
+ .I Example
++.P
+ .nf
+-# lvchange --compression n  vg/vdopool0
+-# lvchange --deduplication y vg/vdopool1
++# lvcreate --vdo -L10G --vdosettings 'ack_threads=1 hash_zone_threads=2' vg/vdopool0
++# lvchange --vdosettings 'bio_threads=2 deduplication=1' vg/vdopool0
+ .fi
+ .
+ .SS \n+[step]. Checking the usage of VDOPoolLV
+diff --git a/test/shell/lvchange-vdo.sh b/test/shell/lvchange-vdo.sh
+index 461b7821f..7cc44d6bc 100644
+--- a/test/shell/lvchange-vdo.sh
++++ b/test/shell/lvchange-vdo.sh
+@@ -48,9 +48,17 @@ check grep_dmsetup status $vg-vdopool-vpool " online online "
+ lvchange --compression n --deduplication n $vg/vdopool
+ check grep_dmsetup status $vg-vdopool-vpool " offline offline "
+ 
++# --vdosettings needs inactive LV
++not lvchange --vdosettings 'ack_threads=8' $vg/vdopool
+ 
+ lvchange -an $vg/$lv1
+ 
++# With inactive vdo-pool changes are applied
++# explicit option --compression has highest priority
++lvchange --vdosettings 'ack_threads=5 compression=0' --compression y $vg/vdopool
++check lv_field $vg/$lv1 vdo_ack_threads "5"
++check lv_field $vg/$lv1 vdo_compression "enabled"
++
+ # Test activation
+ lvchange -aly $vg/$lv1
+ check active $vg $lv1
+diff --git a/test/shell/lvconvert-vdo.sh b/test/shell/lvconvert-vdo.sh
+index 529f325bd..c42d8f25a 100644
+--- a/test/shell/lvconvert-vdo.sh
++++ b/test/shell/lvconvert-vdo.sh
+@@ -28,12 +28,12 @@ lvcreate -L5G -n $lv1 $vg
+ not lvconvert --type vdo-pool $vg/$lv1 |& tee out
+ grep "WARNING" out
+ 
+-
+-lvconvert -y --type vdo-pool $vg/$lv1
++# Check --vdosettings is also applied to converted vdo-pool
++lvconvert -y --type vdo-pool --vdosettings 'ack_threads=5' $vg/$lv1
++check lv_field $vg/$lv1 vdo_ack_threads "5"
+ lvremove -f $vg
+ 
+-
+-# 
++#
+ lvcreate -L5G -n $lv1 $vg
+ lvconvert -y --vdopool $vg/$lv1
+ lvremove -f $vg
+diff --git a/test/shell/lvcreate-vdo.sh b/test/shell/lvcreate-vdo.sh
+index 44f8bf094..3e807ac94 100644
+--- a/test/shell/lvcreate-vdo.sh
++++ b/test/shell/lvcreate-vdo.sh
+@@ -79,8 +79,12 @@ not fsck -n "$DM_DEV_DIR/mapper/$vg-${lv2}"
+ 
+ lvremove -ff $vg
+ 
++# Unknown settings does not pass
++# TODO: try to catch this in parser and 'fail'
++not lvcreate --type vdo --vdosettings 'ack_Xthreads=4' -L10G -V1T -ky -n $lv1 $vg
+ 
+-lvcreate --type vdo -L10G -V1T -ky -n $lv1 $vg
++lvcreate --type vdo --vdosettings 'ack_threads=4' -L10G -V1T -ky -n $lv1 $vg
++check lv_field $vg/$lv1 vdo_ack_threads "4"
+ lvs -a $vg
+ lvremove -ff $vg
+ 
+diff --git a/tools/args.h b/tools/args.h
+index 00a2ec817..bfd848ce9 100644
+--- a/tools/args.h
++++ b/tools/args.h
+@@ -900,6 +900,15 @@ arg(vdopool_ARG, '\0', "vdopool", lv_VAL, 0, 0,
+     "The name of a VDO pool LV.\n"
+     "See \\fBlvmvdo\\fP(7) for more information about VDO usage.\n")
+ 
++arg(vdosettings_ARG, '\0', "vdosettings", string_VAL, ARG_GROUPABLE, 0,
++    "Specifies tunable VDO options for VDO LVs.\n"
++    "Use the form 'option=value' or 'option1=value option2=value', or\n"
++    "repeat --vdosettings for each option being set.\n"
++    "These settings override the default VDO behaviors.\n"
++    "To remove vdosettings and revert to the default\n"
++    "VDO behaviors, use --vdosettings 'default'.\n"
++    "See \\fBlvmvdo\\fP(7) for more information.\n")
++
+ arg(version_ARG, '\0', "version", 0, 0, 0,
+     "Display version information.\n")
+ 
+diff --git a/tools/command-lines.in b/tools/command-lines.in
+index 00ac08934..08302b34f 100644
+--- a/tools/command-lines.in
++++ b/tools/command-lines.in
+@@ -243,6 +243,7 @@ OO_LVCHANGE_META: --addtag Tag, --deltag Tag,
+ --setautoactivation Bool, --errorwhenfull Bool, --discards Discards, --zero Bool,
+ --cachemode CacheMode, --cachepolicy String, --cachesettings String,
+ --minrecoveryrate SizeKB, --maxrecoveryrate SizeKB,
++--vdosettings String,
+ --writebehind Number, --writemostly WriteMostlyPV, --persistent n
+ 
+ # It's unfortunate that activate needs to be optionally allowed here;
+@@ -341,7 +342,8 @@ OO_LVCONVERT_CACHE: --cachemetadataformat CacheMetadataFormat,
+ --cachesettings String, --zero Bool
+ 
+ OO_LVCONVERT_VDO: --metadataprofile String, --readahead Readahead,
+---compression Bool, --deduplication Bool, --zero Bool
++--compression Bool, --deduplication Bool, --vdosettings String,
++--zero Bool
+ 
+ OO_LVCONVERT: --alloc Alloc, --background, --force, --noudevsync
+ 
+@@ -839,7 +841,7 @@ OO_LVCREATE_POOL: --poolmetadatasize SizeMB, --poolmetadataspare Bool, --chunksi
+ 
+ OO_LVCREATE_THINPOOL: --discards Discards, --errorwhenfull Bool
+ 
+-OO_LVCREATE_VDO: --compression Bool, --deduplication Bool
++OO_LVCREATE_VDO: --compression Bool, --deduplication Bool, --vdosettings String
+ ---
+ 
+ lvcreate --type error --size SizeMB VG
+diff --git a/tools/lvchange.c b/tools/lvchange.c
+index 0525bc53c..dc51786d7 100644
+--- a/tools/lvchange.c
++++ b/tools/lvchange.c
+@@ -755,6 +755,43 @@ out:
+ 	return r;
+ }
+ 
++static int _lvchange_vdo(struct cmd_context *cmd,
++			 struct logical_volume *lv,
++			 uint32_t *mr)
++{
++	struct lv_segment *seg;
++	int updated = 0;
++
++	seg = first_seg(lv);
++
++	// With VDO LV given flip to VDO pool
++	if (seg_is_vdo(seg))
++		seg = first_seg(seg_lv(seg, 0));
++
++	if (!get_vdo_settings(cmd, &seg->vdo_params, &updated))
++		return_0;
++
++	if ((updated & VDO_CHANGE_OFFLINE) &&
++	    lv_info(cmd, seg->lv, 1, NULL, 0, 0)) {
++		log_error("Cannot change VDO settings for active VDO pool %s.",
++			  display_lvname(seg->lv));
++		// TODO maybe add --force support with prompt here
++		log_print_unless_silent("VDO pool %s with all its LVs needs to be deactivated.",
++					display_lvname(seg->lv));
++		return 0;
++	}
++
++	if (updated) {
++		if (!dm_vdo_validate_target_params(&seg->vdo_params, 0 /* vdo_size */))
++			return_0;
++
++		/* Request caller to commit and reload metadata */
++		*mr |= MR_RELOAD;
++	}
++
++	return 1;
++}
++
+ static int _lvchange_tag(struct cmd_context *cmd, struct logical_volume *lv,
+ 			 int arg, uint32_t *mr)
+ {
+@@ -1154,6 +1191,7 @@ static int _option_requires_direct_commit(int opt_enum)
+ 		cachemode_ARG,
+ 		cachepolicy_ARG,
+ 		cachesettings_ARG,
++		vdosettings_ARG,
+ 		-1
+ 	};
+ 
+@@ -1354,7 +1392,10 @@ static int _lvchange_properties_single(struct cmd_context *cmd,
+ 			docmds++;
+ 			doit += _lvchange_cache(cmd, lv, &mr);
+ 			break;
+-
++		case vdosettings_ARG:
++			docmds++;
++			doit += _lvchange_vdo(cmd, lv, &mr);
++			break;
+ 		default:
+ 			log_error(INTERNAL_ERROR "Failed to check for option %s",
+ 				  arg_long_option_name(i));
+diff --git a/tools/lvconvert.c b/tools/lvconvert.c
+index a90946173..3d4b24fe3 100644
+--- a/tools/lvconvert.c
++++ b/tools/lvconvert.c
+@@ -5456,13 +5456,8 @@ static int _lvconvert_to_vdopool_single(struct cmd_context *cmd,
+ 	if (!fill_vdo_target_params(cmd, &vdo_params, &vdo_pool_header_size, vg->profile))
+ 		goto_out;
+ 
+-	if (arg_is_set(cmd, compression_ARG))
+-		vdo_params.use_compression =
+-			arg_int_value(cmd, compression_ARG, 0);
+-
+-	if (arg_is_set(cmd, deduplication_ARG))
+-		vdo_params.use_deduplication =
+-			arg_int_value(cmd, deduplication_ARG, 0);
++	if (!get_vdo_settings(cmd, &vdo_params, NULL))
++		return_0;
+ 
+ 	if (!activate_lv(cmd, lv)) {
+ 		log_error("Cannot activate %s.", display_lvname(lv));
+diff --git a/tools/lvcreate.c b/tools/lvcreate.c
+index 79af42685..8de6f3408 100644
+--- a/tools/lvcreate.c
++++ b/tools/lvcreate.c
+@@ -698,6 +698,23 @@ static int _read_cache_params(struct cmd_context *cmd,
+ 	return 1;
+ }
+ 
++static int _read_vdo_params(struct cmd_context *cmd,
++			    struct lvcreate_params *lp)
++{
++	if (!seg_is_vdo(lp))
++		return 1;
++
++	// prefiling settings here
++	if (!fill_vdo_target_params(cmd, &lp->vdo_params,  &lp->vdo_pool_header_size, NULL))
++		return_0;
++
++	// override with optional vdo settings
++	if (!get_vdo_settings(cmd, &lp->vdo_params, NULL))
++		return_0;
++
++	return 1;
++}
++
+ static int _read_activation_params(struct cmd_context *cmd,
+ 				   struct volume_group *vg,
+ 				   struct lvcreate_params *lp)
+@@ -888,7 +905,8 @@ static int _lvcreate_params(struct cmd_context *cmd,
+ #define VDO_POOL_ARGS \
+ 	vdopool_ARG,\
+ 	compression_ARG,\
+-	deduplication_ARG
++	deduplication_ARG,\
++	vdosettings_ARG
+ 
+ 	/* Cache and cache-pool segment type */
+ 	if (seg_is_cache(lp)) {
+@@ -1098,19 +1116,6 @@ static int _lvcreate_params(struct cmd_context *cmd,
+ 						zero_ARG,
+ 						-1))
+ 			return_0;
+-
+-		// FIXME: prefiling here - this is wrong place
+-		// but will work for this moment
+-		if (!fill_vdo_target_params(cmd, &lp->vdo_params, &lp->vdo_pool_header_size, NULL))
+-			return_0;
+-
+-		if (arg_is_set(cmd, compression_ARG))
+-			lp->vdo_params.use_compression =
+-				arg_int_value(cmd, compression_ARG, 0);
+-
+-		if (arg_is_set(cmd, deduplication_ARG))
+-			lp->vdo_params.use_deduplication =
+-				arg_int_value(cmd, deduplication_ARG, 0);
+ 	}
+ 
+ 	/* Check options shared between more segment types */
+@@ -1198,6 +1203,7 @@ static int _lvcreate_params(struct cmd_context *cmd,
+ 			      &lp->pool_metadata_size, &lp->pool_metadata_spare,
+ 			      &lp->chunk_size, &lp->discards, &lp->zero_new_blocks)) ||
+ 	    !_read_cache_params(cmd, lp) ||
++	    !_read_vdo_params(cmd, lp) ||
+ 	    !_read_mirror_and_raid_params(cmd, lp))
+ 		return_0;
+ 
+diff --git a/tools/toollib.c b/tools/toollib.c
+index 16be336d4..697baee82 100644
+--- a/tools/toollib.c
++++ b/tools/toollib.c
+@@ -1192,6 +1192,183 @@ out:
+ 	return ok;
+ }
+ 
++/*
++ * Compare VDO option name, skip any '_' in name
++ * and also allow to use it without  vdo_[use_] prefix
++ */
++static int _compare_vdo_option(const char *b1, const char *b2)
++{
++	if (strncasecmp(b1, "vdo", 3) == 0) // skip vdo prefix
++		b1 += 3;
++
++	if ((tolower(*b1) != tolower(*b2)) &&
++	    (strncmp(b2, "use_", 4) == 0))
++		b2 += 4;  // try again with skipped prefix 'use_'
++
++	while (*b1 && *b2) {
++		if (tolower(*b1) == tolower(*b2)) {
++			++b1;
++			++b2;
++			continue;	// matching char
++		}
++
++		if (*b1 == '_')
++			++b1;           // skip to next char
++		else if (*b2 == '_')
++			++b2;           // skip to next char
++		else
++			break;          // mismatch
++	}
++
++	return (*b1 || *b2) ? 0 : 1;
++}
++
++#define CHECK_AND_SET(var, onoff) \
++	option = #var;\
++	if (_compare_vdo_option(cn->key, option)) {\
++		if (is_lvchange || !cn->v || (cn->v->type != DM_CFG_INT))\
++			goto err;\
++		if (vtp->var != cn->v->v.i) {\
++			vtp->var = cn->v->v.i;\
++			u |= onoff;\
++		}\
++		continue;\
++	}
++
++#define DO_OFFLINE(var) \
++	CHECK_AND_SET(var, VDO_CHANGE_OFFLINE)
++
++#define DO_ONLINE(var) \
++	CHECK_AND_SET(var, VDO_CHANGE_ONLINE)
++
++int get_vdo_settings(struct cmd_context *cmd,
++		     struct dm_vdo_target_params *vtp,
++		     int *updated)
++{
++	const char *str, *option = NULL;
++	struct arg_value_group_list *group;
++	struct dm_config_tree *result = NULL, *prev = NULL, *current = NULL;
++	struct dm_config_node *cn;
++	int r = 0, u = 0, is_lvchange;
++	int use_compression = vtp->use_compression;
++	int use_deduplication = vtp->use_deduplication;
++	int checked_lvchange;
++
++	if (updated)
++		*updated = 0;
++
++	// Group all --vdosettings
++	dm_list_iterate_items(group, &cmd->arg_value_groups) {
++		if (!grouped_arg_is_set(group->arg_values, vdosettings_ARG))
++			continue;
++
++		if (!(current = dm_config_create()))
++			goto_out;
++		if (prev)
++			current->cascade = prev;
++		prev = current;
++
++		if (!(str = grouped_arg_str_value(group->arg_values,
++						  vdosettings_ARG,
++						  NULL)))
++			goto_out;
++
++		if (!dm_config_parse_without_dup_node_check(current, str, str + strlen(str)))
++			goto_out;
++	}
++
++	if (current) {
++		if (!(result = dm_config_flatten(current)))
++			goto_out;
++
++		checked_lvchange = !strcmp(cmd->name, "lvchange");
++
++		/* Use all acceptable VDO options */
++		for (cn = result->root; cn; cn = cn->sib) {
++			is_lvchange = 0;
++			DO_OFFLINE(ack_threads);
++			DO_OFFLINE(bio_rotation);
++			DO_OFFLINE(bio_threads);
++			DO_OFFLINE(block_map_cache_size_mb);
++			DO_OFFLINE(block_map_era_length);
++			DO_OFFLINE(block_map_period); // alias for block_map_era_length
++			DO_OFFLINE(cpu_threads);
++			DO_OFFLINE(hash_zone_threads);
++			DO_OFFLINE(logical_threads);
++			DO_OFFLINE(max_discard);
++			DO_OFFLINE(physical_threads);
++
++			// Support also these - even when we have regular opts for them
++			DO_ONLINE(use_compression);
++			DO_ONLINE(use_deduplication);
++
++			// Settings bellow cannot be changed with lvchange command
++			is_lvchange = checked_lvchange;
++
++			DO_OFFLINE(check_point_frequency);
++			DO_OFFLINE(index_memory_size_mb);
++			DO_OFFLINE(minimum_io_size);
++			DO_OFFLINE(slab_size_mb);
++			DO_OFFLINE(use_metadata_hints);
++			DO_OFFLINE(use_sparse_index);
++
++			option = "write_policy";
++			if (_compare_vdo_option(cn->key, option)) {
++				if (is_lvchange || !cn->v || (cn->v->type != DM_CFG_STRING))
++					goto err;
++				if (!set_vdo_write_policy(&vtp->write_policy, cn->v->v.str))
++					goto_out;
++				u |= VDO_CHANGE_OFFLINE;
++				continue;
++			}
++
++			log_error("Unknown VDO setting \"%s\".", cn->key);
++			goto out;
++		}
++	}
++
++	if (arg_is_set(cmd, compression_ARG)) {
++		vtp->use_compression = arg_int_value(cmd, compression_ARG, 0);
++		if (vtp->use_compression != use_compression)
++			u |= VDO_CHANGE_ONLINE;
++	}
++
++	if (arg_is_set(cmd, deduplication_ARG)) {
++		vtp->use_deduplication = arg_int_value(cmd, deduplication_ARG, 0);
++		if (vtp->use_deduplication != use_deduplication)
++			u |= VDO_CHANGE_ONLINE;
++	}
++
++	if (updated) {
++		// validation of updated VDO option
++		if (!dm_vdo_validate_target_params(vtp, 0 /* vdo_size */)) {
++err:
++			if (is_lvchange)
++				log_error("Cannot change VDO setting \"vdo_%s\" in existing VDO pool.",
++					  option);
++			else
++				log_error("Invalid argument for VDO setting \"vdo_%s\".",
++					  option);
++			goto out;
++		}
++
++		*updated = u;
++	}
++
++	r = 1;
++out:
++	if (result)
++		dm_config_destroy(result);
++
++	while (prev) {
++		current = prev->cascade;
++		dm_config_destroy(prev);
++		prev = current;
++	}
++
++	return r;
++}
++
+ static int _get_one_writecache_setting(struct cmd_context *cmd, struct writecache_settings *settings,
+ 				       char *key, char *val, uint32_t *block_size_sectors)
+ {
+diff --git a/tools/toollib.h b/tools/toollib.h
+index f3a60fbc4..2b38e4e4f 100644
+--- a/tools/toollib.h
++++ b/tools/toollib.h
+@@ -217,6 +217,12 @@ int get_cache_params(struct cmd_context *cmd,
+ 		     const char **name,
+ 		     struct dm_config_tree **settings);
+ 
++#define VDO_CHANGE_ONLINE  1
++#define VDO_CHANGE_OFFLINE 2
++int get_vdo_settings(struct cmd_context *cmd,
++		     struct dm_vdo_target_params *vtp,
++		     int *updated);
++
+ int get_writecache_settings(struct cmd_context *cmd, struct writecache_settings *settings,
+                             uint32_t *block_size_sectors);
+ 
+-- 
+2.34.3
+
diff --git a/SOURCES/0048-toollib-fix-segfault-when-handling-selection-with-hi.patch b/SOURCES/0048-toollib-fix-segfault-when-handling-selection-with-hi.patch
new file mode 100644
index 0000000..6a0b3e0
--- /dev/null
+++ b/SOURCES/0048-toollib-fix-segfault-when-handling-selection-with-hi.patch
@@ -0,0 +1,45 @@
+From 45a2ccfa3bdf4c5b3f8b9e0cc5330fca345b0d26 Mon Sep 17 00:00:00 2001
+From: Peter Rajnoha <prajnoha@redhat.com>
+Date: Thu, 5 May 2022 11:02:32 +0200
+Subject: [PATCH 48/54] toollib: fix segfault when handling selection with
+ historical LVs
+
+When processing historical LVs inside process_each_lv_in_vg for
+selection, we need to use dummy "_historical_lv" for select_match_lv.
+
+This is because a historical LV is not an actual LV, but only a tiny
+representation with subset of original properties that we recorded
+(name, uuid...).
+
+To use the same processing functions we use for full-fledged non-historical
+LVs, we need to use the prefilled "_historical_lv" structure which has all
+the other missing properties hard-coded.
+---
+ tools/toollib.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/tools/toollib.c b/tools/toollib.c
+index 697baee82..01ba03658 100644
+--- a/tools/toollib.c
++++ b/tools/toollib.c
+@@ -3392,13 +3392,14 @@ int process_each_lv_in_vg(struct cmd_context *cmd, struct volume_group *vg,
+ 				process_lv = 1;
+ 			}
+ 
+-			process_lv = process_lv && select_match_lv(cmd, handle, vg, lvl->lv) && _select_matches(handle);
++			_historical_lv.this_glv = glvl->glv;
++			_historical_lv.name = glvl->glv->historical->name;
++
++			process_lv = process_lv && select_match_lv(cmd, handle, vg, &_historical_lv) && _select_matches(handle);
+ 
+ 			if (!process_lv)
+ 				continue;
+ 
+-			_historical_lv.this_glv = glvl->glv;
+-			_historical_lv.name = glvl->glv->historical->name;
+ 			log_very_verbose("Processing historical LV %s in VG %s.", glvl->glv->historical->name, vg->name);
+ 
+ 			ret = process_single_lv(cmd, &_historical_lv, handle);
+-- 
+2.34.3
+
diff --git a/SOURCES/0049-devices-file-move-clean-up-after-command-is-run.patch b/SOURCES/0049-devices-file-move-clean-up-after-command-is-run.patch
new file mode 100644
index 0000000..2fbfdbc
--- /dev/null
+++ b/SOURCES/0049-devices-file-move-clean-up-after-command-is-run.patch
@@ -0,0 +1,39 @@
+From eda98e4b9418568d6793d2c853aaa54db051cc9f Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Fri, 27 May 2022 12:38:43 -0500
+Subject: [PATCH 49/54] devices file: move clean up after command is run
+
+devices_file_exit wasn't being called between lvm_shell
+commands, so the file lock wouldn't be released.
+---
+ lib/commands/toolcontext.c | 1 -
+ tools/lvmcmdline.c         | 1 +
+ 2 files changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lib/commands/toolcontext.c b/lib/commands/toolcontext.c
+index a0c78ddd6..7db5e11a1 100644
+--- a/lib/commands/toolcontext.c
++++ b/lib/commands/toolcontext.c
+@@ -1912,7 +1912,6 @@ int refresh_toolcontext(struct cmd_context *cmd)
+ 	_destroy_segtypes(&cmd->segtypes);
+ 	_destroy_formats(cmd, &cmd->formats);
+ 
+-	devices_file_exit(cmd);
+ 	if (!dev_cache_exit())
+ 		stack;
+ 	_destroy_dev_types(cmd);
+diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c
+index 1727ba089..eb63fd9b5 100644
+--- a/tools/lvmcmdline.c
++++ b/tools/lvmcmdline.c
+@@ -3306,6 +3306,7 @@ int lvm_run_command(struct cmd_context *cmd, int argc, char **argv)
+ 	hints_exit(cmd);
+ 	lvmcache_destroy(cmd, 1, 1);
+ 	label_scan_destroy(cmd);
++	devices_file_exit(cmd);
+ 
+ 	if ((config_string_cft = remove_config_tree_by_source(cmd, CONFIG_STRING)))
+ 		dm_config_destroy(config_string_cft);
+-- 
+2.34.3
+
diff --git a/SOURCES/0050-devices-file-fail-if-devicesfile-filename-doesn-t-ex.patch b/SOURCES/0050-devices-file-fail-if-devicesfile-filename-doesn-t-ex.patch
new file mode 100644
index 0000000..0adf7b6
--- /dev/null
+++ b/SOURCES/0050-devices-file-fail-if-devicesfile-filename-doesn-t-ex.patch
@@ -0,0 +1,53 @@
+From bf0b3962088fb18f4a2aba00f38955e1fc6e31fe Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Fri, 27 May 2022 14:27:03 -0500
+Subject: [PATCH 50/54] devices file: fail if --devicesfile filename doesn't
+ exist
+
+A typo of the filename after --devicesfile should result in a
+command error rather than the command falling back to using no
+devices file at all.  Exception is vgcreate|pvcreate which
+create a new devices file if the file name doesn't exist.
+---
+ lib/device/dev-cache.c          | 9 +++++++++
+ test/shell/devicesfile-basic.sh | 4 ++++
+ 2 files changed, 13 insertions(+)
+
+diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c
+index b0759b06c..0eb2568b5 100644
+--- a/lib/device/dev-cache.c
++++ b/lib/device/dev-cache.c
+@@ -1882,6 +1882,15 @@ static int _setup_devices(struct cmd_context *cmd, int no_file_match)
+ 
+ 	file_exists = devices_file_exists(cmd);
+ 
++	/*
++	 * Fail if user specifies a file name that doesn't exist and
++	 * the command is not creating a new devices file.
++	 */
++	if (!file_exists && !cmd->create_edit_devices_file && cmd->devicesfile && strlen(cmd->devicesfile)) {
++		log_error("Devices file not found: %s", cmd->devices_file_path);
++		return 0;
++	}
++
+ 	/*
+ 	 * Removing the devices file is another way of disabling the use of
+ 	 * a devices file, unless the command creates the devices file.
+diff --git a/test/shell/devicesfile-basic.sh b/test/shell/devicesfile-basic.sh
+index 7ba9e2c7f..d1cfb6a35 100644
+--- a/test/shell/devicesfile-basic.sh
++++ b/test/shell/devicesfile-basic.sh
+@@ -104,6 +104,10 @@ not ls "$DFDIR/system.devices"
+ vgs --devicesfile test.devices $vg1
+ not vgs --devicesfile test.devices $vg2
+ 
++# misspelled override name fails
++not vgs --devicesfile doesnotexist $vg1
++not vgs --devicesfile doesnotexist $vg2
++
+ # devicesfile and devices cannot be used together
+ not vgs --devicesfile test.devices --devices "$dev1","$dev1" $vg1
+ 
+-- 
+2.34.3
+
diff --git a/SOURCES/0051-filter-mpath-handle-other-wwid-types-in-blacklist.patch b/SOURCES/0051-filter-mpath-handle-other-wwid-types-in-blacklist.patch
new file mode 100644
index 0000000..820ed88
--- /dev/null
+++ b/SOURCES/0051-filter-mpath-handle-other-wwid-types-in-blacklist.patch
@@ -0,0 +1,52 @@
+From 25abb5730f4d8f79df69f0817881ffb9eed195a9 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Mon, 6 Jun 2022 11:39:02 -0500
+Subject: [PATCH 51/54] filter-mpath: handle other wwid types in blacklist
+
+Fixes commit 494372b4eed0c8f6040e3357939eb7511ac25745
+  "filter-mpath: use multipath blacklist"
+to handle wwids with initial type digits 1 and 2 used
+for t10 and eui ids.  Originally recognized type 3 naa.
+---
+ lib/device/dev-mpath.c | 11 ++++++-----
+ 1 file changed, 6 insertions(+), 5 deletions(-)
+
+diff --git a/lib/device/dev-mpath.c b/lib/device/dev-mpath.c
+index 6eed03c5b..7abbfb289 100644
+--- a/lib/device/dev-mpath.c
++++ b/lib/device/dev-mpath.c
+@@ -54,7 +54,7 @@ static void _read_blacklist_file(const char *path)
+ 	int section_black = 0;
+ 	int section_exceptions = 0;
+ 	int found_quote;
+-	int found_three;
++	int found_type;
+ 	int i, j;
+ 
+ 	if (!(fp = fopen(path, "r")))
+@@ -114,7 +114,7 @@ static void _read_blacklist_file(const char *path)
+ 
+ 		memset(wwid, 0, sizeof(wwid));
+ 		found_quote = 0;
+-		found_three = 0;
++		found_type = 0;
+ 		j = 0;
+ 
+ 		for (; i < MAX_WWID_LINE; i++) {
+@@ -132,9 +132,10 @@ static void _read_blacklist_file(const char *path)
+ 			/* second quote is end of wwid */
+ 			if ((line[i] == '"') && found_quote)
+ 				break;
+-			/* ignore first "3" in wwid */
+-			if ((line[i] == '3') && !found_three) {
+-				found_three = 1;
++			/* exclude initial 3/2/1 for naa/eui/t10 */
++			if (!j && !found_type &&
++			    ((line[i] == '3') || (line[i] == '2') || (line[i] == '1'))) {
++				found_type = 1;
+ 				continue;
+ 			}
+ 
+-- 
+2.34.3
+
diff --git a/SOURCES/0052-vdo-fix-conversion-of-vdo_slab_size_mb.patch b/SOURCES/0052-vdo-fix-conversion-of-vdo_slab_size_mb.patch
new file mode 100644
index 0000000..7bb6100
--- /dev/null
+++ b/SOURCES/0052-vdo-fix-conversion-of-vdo_slab_size_mb.patch
@@ -0,0 +1,56 @@
+From 7cb63b05dad453d015bbe462b799fb031dd6952c Mon Sep 17 00:00:00 2001
+From: Zdenek Kabelac <zkabelac@redhat.com>
+Date: Tue, 31 May 2022 22:48:38 +0200
+Subject: [PATCH 52/54] vdo: fix conversion of vdo_slab_size_mb
+
+When converting VDO volume, the parameter vdo_slabSize was
+incorrectly copied as vdo_blockMapCacheSize, however this parameter
+is then no longer used for any table line creation so the wrong
+value was only stored in metadata.
+
+Also use just single get_kb_size_with_unit_ and remove it's duplicate
+functionality with get_mb_size_with_unit_.
+
+Use $VERB for vdo remove call.
+---
+ scripts/lvm_import_vdo.sh | 13 ++-----------
+ 1 file changed, 2 insertions(+), 11 deletions(-)
+
+diff --git a/scripts/lvm_import_vdo.sh b/scripts/lvm_import_vdo.sh
+index 61a82e41e..beb55dbdb 100755
+--- a/scripts/lvm_import_vdo.sh
++++ b/scripts/lvm_import_vdo.sh
+@@ -125,15 +125,6 @@ get_kb_size_with_unit_() {
+ 	esac
+ }
+ 
+-get_mb_size_with_unit_() {
+-	case "$1" in
+-	*[mM]) echo $(( ${1%[mM]} )) ;;
+-	*[gG]) echo $(( ${1%[gG]} * 1024 )) ;;
+-	*[tT]) echo $(( ${1%[tT]} * 1024 * 1024 )) ;;
+-	*[pP]) echo $(( ${1%[pP]} * 1024 * 1024 * 1024 )) ;;
+-	esac
+-}
+-
+ # Figure out largest possible extent size usable for VG
+ # $1   physical size
+ # $2   logical size
+@@ -328,12 +319,12 @@ allocation {
+ 	vdo_use_deduplication = $(get_enabled_value_ "$vdo_deduplication")
+ 	vdo_use_metadata_hints=1
+ 	vdo_minimum_io_size = $vdo_logicalBlockSize
+-	vdo_block_map_cache_size_mb = $(get_mb_size_with_unit_ "$vdo_blockMapCacheSize")
++	vdo_block_map_cache_size_mb = $(( $(get_kb_size_with_unit_ "$vdo_blockMapCacheSize") / 1024 ))
+ 	vdo_block_map_period = $vdo_blockMapPeriod
+ 	vdo_check_point_frequency = $vdo_indexCfreq
+ 	vdo_use_sparse_index = $(get_enabled_value_ "$vdo_indexSparse")
+ 	vdo_index_memory_size_mb = $(awk "BEGIN {print $vdo_indexMemory * 1024}")
+-	vdo_slab_size_mb = $(get_mb_size_with_unit_ "$vdo_blockMapCacheSize")
++	vdo_slab_size_mb = $(( $(get_kb_size_with_unit_ "$vdo_blockMapCacheSize") / 1024 ))
+ 	vdo_ack_threads = $vdo_ackThreads
+ 	vdo_bio_threads = $vdo_bioThreads
+ 	vdo_bio_rotation = $vdo_bioRotationInterval
+-- 
+2.34.3
+
diff --git a/SOURCES/0053-filter-mpath-get-wwids-from-sysfs-vpd_pg83.patch b/SOURCES/0053-filter-mpath-get-wwids-from-sysfs-vpd_pg83.patch
new file mode 100644
index 0000000..3c98ac4
--- /dev/null
+++ b/SOURCES/0053-filter-mpath-get-wwids-from-sysfs-vpd_pg83.patch
@@ -0,0 +1,741 @@
+From e36b180a6983c4fa07d6714a0bf81e6935487359 Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Mon, 6 Jun 2022 14:04:20 -0500
+Subject: [PATCH 53/54] filter-mpath: get wwids from sysfs vpd_pg83
+
+to compare with wwids in /etc/multipath/wwids when
+excluding multipath components.  The wwid printed
+from the sysfs wwid file may not be the wwid used
+in multipath wwids.  Save the wwids found for each
+device on dev->wwids to avoid repeating reading
+and parsing the sysfs files.
+---
+ lib/Makefile.in        |   1 +
+ lib/device/dev-cache.c |  18 ++++
+ lib/device/dev-cache.h |   1 +
+ lib/device/dev-mpath.c | 232 ++++++++++++++++++++++++++++++++++-------
+ lib/device/device.h    |  13 +++
+ lib/device/device_id.c |  31 +++++-
+ lib/device/device_id.h |   2 +
+ lib/device/parse_vpd.c | 199 +++++++++++++++++++++++++++++++++++
+ 8 files changed, 454 insertions(+), 43 deletions(-)
+ create mode 100644 lib/device/parse_vpd.c
+
+diff --git a/lib/Makefile.in b/lib/Makefile.in
+index 8b3eac60a..3077825d2 100644
+--- a/lib/Makefile.in
++++ b/lib/Makefile.in
+@@ -40,6 +40,7 @@ SOURCES =\
+ 	device/dev-luks.c \
+ 	device/dev-dasd.c \
+ 	device/dev-lvm1-pool.c \
++	device/parse_vpd.c \
+ 	display/display.c \
+ 	error/errseg.c \
+ 	unknown/unknown.c \
+diff --git a/lib/device/dev-cache.c b/lib/device/dev-cache.c
+index 0eb2568b5..65e1cb138 100644
+--- a/lib/device/dev-cache.c
++++ b/lib/device/dev-cache.c
+@@ -80,6 +80,7 @@ static void _dev_init(struct device *dev)
+ 
+ 	dm_list_init(&dev->aliases);
+ 	dm_list_init(&dev->ids);
++	dm_list_init(&dev->wwids);
+ }
+ 
+ void dev_destroy_file(struct device *dev)
+@@ -383,6 +384,22 @@ out:
+ 	return 1;
+ }
+ 
++int get_sysfs_binary(const char *path, char *buf, size_t buf_size, int *retlen)
++{
++	int ret;
++	int fd;
++
++	fd = open(path, O_RDONLY);
++	if (fd < 0)
++		return 0;
++	ret = read(fd, buf, buf_size);
++	close(fd);
++	if (ret <= 0)
++		return 0;
++	*retlen = ret;
++	return 1;
++}
++
+ int get_sysfs_value(const char *path, char *buf, size_t buf_size, int error_if_no_value)
+ {
+ 	FILE *fp;
+@@ -1336,6 +1353,7 @@ int dev_cache_exit(void)
+ 		dm_hash_iterate(n, _cache.names) {
+ 			dev = (struct device *) dm_hash_get_data(_cache.names, n);
+ 			free_dids(&dev->ids);
++			free_wwids(&dev->wwids);
+ 		}
+ 	}
+ 
+diff --git a/lib/device/dev-cache.h b/lib/device/dev-cache.h
+index 321a56d7b..c49e6265d 100644
+--- a/lib/device/dev-cache.h
++++ b/lib/device/dev-cache.h
+@@ -74,6 +74,7 @@ void dev_cache_failed_path(struct device *dev, const char *path);
+ bool dev_cache_has_md_with_end_superblock(struct dev_types *dt);
+ 
+ int get_sysfs_value(const char *path, char *buf, size_t buf_size, int error_if_no_value);
++int get_sysfs_binary(const char *path, char *buf, size_t buf_size, int *retlen);
+ int get_dm_uuid_from_sysfs(char *buf, size_t buf_size, int major, int minor);
+ 
+ int setup_devices_file(struct cmd_context *cmd);
+diff --git a/lib/device/dev-mpath.c b/lib/device/dev-mpath.c
+index 7abbfb289..3795c992d 100644
+--- a/lib/device/dev-mpath.c
++++ b/lib/device/dev-mpath.c
+@@ -200,11 +200,12 @@ static void _read_wwid_exclusions(void)
+ 		log_debug("multipath config ignored %d wwids", rem_count);
+ }
+ 
+-static void _read_wwid_file(const char *config_wwids_file)
++static void _read_wwid_file(const char *config_wwids_file, int *entries)
+ {
+ 	FILE *fp;
+ 	char line[MAX_WWID_LINE];
+ 	char *wwid, *p;
++	char typestr[2] = { 0 };
+ 	int count = 0;
+ 
+ 	if (config_wwids_file[0] != '/') {
+@@ -226,8 +227,17 @@ static void _read_wwid_file(const char *config_wwids_file)
+ 		if (line[0] == '/')
+ 			wwid++;
+ 
+-		/* skip the initial '3' */
+-		wwid++;
++
++		/*
++		 * the initial character is the id type,
++		 * 1 is t10, 2 is eui, 3 is naa, 8 is scsi name.
++		 * wwids are stored in the hash table without the type charater.
++		 * It seems that sometimes multipath does not include
++		 * the type charater (seen with t10 scsi_debug devs).
++		 */
++		typestr[0] = *wwid;
++		if (typestr[0] == '1' || typestr[0] == '2' || typestr[0] == '3')
++			wwid++;
+ 
+ 		if ((p = strchr(wwid, '/')))
+ 			*p = '\0';
+@@ -240,6 +250,7 @@ static void _read_wwid_file(const char *config_wwids_file)
+ 		stack;
+ 
+ 	log_debug("multipath wwids read %d from %s", count, config_wwids_file);
++	*entries = count;
+ }
+ 
+ int dev_mpath_init(const char *config_wwids_file)
+@@ -247,6 +258,7 @@ int dev_mpath_init(const char *config_wwids_file)
+ 	struct dm_pool *mem;
+ 	struct dm_hash_table *minor_tab;
+ 	struct dm_hash_table *wwid_tab;
++	int entries = 0;
+ 
+ 	dm_list_init(&_ignored);
+ 	dm_list_init(&_ignored_exceptions);
+@@ -283,10 +295,16 @@ int dev_mpath_init(const char *config_wwids_file)
+ 	_wwid_hash_tab = wwid_tab;
+ 
+ 	if (config_wwids_file) {
+-		_read_wwid_file(config_wwids_file);
++		_read_wwid_file(config_wwids_file, &entries);
+ 		_read_wwid_exclusions();
+ 	}
+ 
++	if (!entries) {
++		/* reading dev wwids is skipped with null wwid_hash_tab */
++		dm_hash_destroy(_wwid_hash_tab);
++		_wwid_hash_tab = NULL;
++	}
++
+ 	return 1;
+ }
+ 
+@@ -432,10 +450,10 @@ static int _dev_is_mpath_component_udev(struct device *dev)
+ }
+ #endif
+ 
+-static int _dev_is_mpath_component_sysfs(struct cmd_context *cmd, struct device *dev)
++static int _dev_is_mpath_component_sysfs(struct cmd_context *cmd, struct device *dev,
++					 int primary_result, dev_t primary_dev)
+ {
+ 	struct dev_types *dt = cmd->dev_types;
+-	const char *part_name;
+ 	const char *name;               /* e.g. "sda" for "/dev/sda" */
+ 	char link_path[PATH_MAX];       /* some obscure, unpredictable sysfs path */
+ 	char holders_path[PATH_MAX];    /* e.g. "/sys/block/sda/holders/" */
+@@ -449,25 +467,15 @@ static int _dev_is_mpath_component_sysfs(struct cmd_context *cmd, struct device
+ 	int dm_dev_major;
+ 	int dm_dev_minor;
+ 	struct stat info;
+-	dev_t primary_dev;
+ 	int is_mpath_component = 0;
+ 
+-	/* multipathing is only known to exist for SCSI or NVME devices */
+-	if (!major_is_scsi_device(dt, dev_major) && !dev_is_nvme(dt, dev))
+-		return 0;
+-
+-	switch (dev_get_primary_dev(dt, dev, &primary_dev)) {
++	switch (primary_result) {
+ 
+ 	case 2: /* The dev is partition. */
+-		part_name = dev_name(dev); /* name of original dev for log_debug msg */
+ 
+ 		/* gets "foo" for "/dev/foo" where "/dev/foo" comes from major:minor */
+ 		if (!(name = _get_sysfs_name_by_devt(sysfs_dir, primary_dev, link_path, sizeof(link_path))))
+ 			return_0;
+-
+-		log_debug_devs("%s: Device is a partition, using primary "
+-			       "device %s for mpath component detection",
+-			       part_name, name);
+ 		break;
+ 
+ 	case 1: /* The dev is already a primary dev. Just continue with the dev. */
+@@ -589,47 +597,189 @@ static int _dev_is_mpath_component_sysfs(struct cmd_context *cmd, struct device
+ 	return is_mpath_component;
+ }
+ 
+-static int _dev_in_wwid_file(struct cmd_context *cmd, struct device *dev)
++static int _read_sys_wwid(struct cmd_context *cmd, struct device *dev,
++			  char *idbuf, int idbufsize)
+ {
+-	char sysbuf[PATH_MAX] = { 0 };
+-	char *wwid;
+-	long look;
++	char idtmp[DEV_WWID_SIZE];
+ 
+-	if (!_wwid_hash_tab)
++	if (!read_sys_block(cmd, dev, "device/wwid", idbuf, idbufsize)) {
++		/* the wwid file is not under device for nvme devs */
++		if (!read_sys_block(cmd, dev, "wwid", idbuf, idbufsize))
++			return 0;
++	}
++	if (!idbuf[0])
+ 		return 0;
+ 
+-	if (!read_sys_block(cmd, dev, "device/wwid", sysbuf, sizeof(sysbuf)))
++	/* in t10 id, replace series of spaces with one _ like multipath */
++	if (!strncmp(idbuf, "t10.", 4) && strchr(idbuf, ' ')) {
++		if (idbufsize < DEV_WWID_SIZE)
++			return 0;
++		memcpy(idtmp, idbuf, DEV_WWID_SIZE);
++		memset(idbuf, 0, idbufsize);
++		format_t10_id((const unsigned char *)idtmp, DEV_WWID_SIZE, (unsigned char *)idbuf, idbufsize);
++	}
++	return 1;
++}
++
++#define VPD_SIZE 4096
++
++static int _read_sys_vpd_wwids(struct cmd_context *cmd, struct device *dev,
++			       struct dm_list *ids)
++{
++	unsigned char vpd_data[VPD_SIZE] = { 0 };
++	int vpd_datalen = 0;
++
++	if (!read_sys_block_binary(cmd, dev, "device/vpd_pg83", (char *)vpd_data, VPD_SIZE, &vpd_datalen))
++		return 0;
++	if (!vpd_datalen)
+ 		return 0;
+ 
+-	if (!sysbuf[0])
++	/* adds dev_wwid entry to dev->wwids for each id in vpd data */
++	parse_vpd_ids(vpd_data, vpd_datalen, ids);
++	return 1;
++}
++
++void free_wwids(struct dm_list *ids)
++{
++	struct dev_wwid *dw, *safe;
++
++	dm_list_iterate_items_safe(dw, safe, ids) {
++		dm_list_del(&dw->list);
++		free(dw);
++	}
++}
++
++static int _wwid_type_num(char *id)
++{
++	if (!strncmp(id, "naa.", 4))
++		return 3;
++	else if (!strncmp(id, "eui.", 4))
++		return 2;
++	else if (!strncmp(id, "t10.", 4))
++		return 1;
++	else
++		return -1;
++}
++
++/*
++ * TODO: if each of the different wwid types (naa/eui/t10) were
++ * represented by different DEV_ID_TYPE_FOO values, and used
++ * as device_id types, then we could drop struct dev_wwid and
++ * drop dev->wwids, and just use dev->ids for each of the
++ * different wwids found in vpd_pg83.  This would also require
++ * the ability to handle both the original method of replacing
++ * every space in the id string with _ and the new/multipath
++ * format_t10_id replacing series of spaces with one _.
++ */
++struct dev_wwid *add_wwid(char *id, int id_type, struct dm_list *ids)
++{
++	struct dev_wwid *dw;
++	int len;
++
++	if (!id_type) {
++		id_type = _wwid_type_num(id);
++		if (id_type == -1)
++			log_debug("unknown wwid type %s", id);
++	}
++
++	if (!(dw = zalloc(sizeof(struct dev_wwid))))
++		return NULL;
++	len = strlen(id);
++	if (len >= DEV_WWID_SIZE)
++		len = DEV_WWID_SIZE - 1;
++	memcpy(dw->id, id, len);
++	dw->type = id_type;
++	dm_list_add(ids, &dw->list);
++	return dw;
++}
++
++/*
++ * we save ids with format: naa.<value>, eui.<value>, t10.<value>.
++ * multipath wwids file uses format: 3<value>, 2<value>, 1<value>.
++ * The values are saved in wwid_hash_tab without the type prefix.
++ */
++
++static int _dev_in_wwid_file(struct cmd_context *cmd, struct device *dev,
++			     int primary_result, dev_t primary_dev)
++{
++	char idbuf[DEV_WWID_SIZE] = { 0 };
++	struct dev_wwid *dw;
++	char *wwid;
++
++	if (!_wwid_hash_tab)
+ 		return 0;
+ 
+ 	/*
+-	 * sysfs prints wwid as <typestr>.<value>
+-	 * multipath wwid uses '3'<value>
+-	 * does "<typestr>." always correspond to "3"?
++	 * Check the primary device, not the partition.
+ 	 */
+-	if (!(wwid = strchr(sysbuf, '.')))
+-		return 0;
++	if (primary_result == 2) {
++		if (!(dev = dev_cache_get_by_devt(cmd, primary_dev))) {
++			log_debug("dev_is_mpath_component %s no primary dev", dev_name(dev));
++			return 0;
++		}
++	}
+ 
+-	/* skip the type and dot, just as '3' was skipped from wwids entry */
+-	wwid++;
+-	
+-	look = (long) dm_hash_lookup_binary(_wwid_hash_tab, wwid, strlen(wwid));
++	/*
++	 * This function may be called multiple times for the same device, in
++	 * particular if partitioned for each partition.
++	 */
++	if (!dm_list_empty(&dev->wwids))
++		goto lookup;
+ 
+-	if (look) {
+-		log_debug_devs("dev_is_mpath_component %s multipath wwid %s", dev_name(dev), wwid);
+-		return 1;
++	/*
++	 * Get all the ids for the device from vpd_pg83 and check if any of
++	 * those are in /etc/multipath/wwids.  These ids should include the
++	 * value printed from the sysfs wwid file.
++	 */
++	_read_sys_vpd_wwids(cmd, dev, &dev->wwids);
++	if (!dm_list_empty(&dev->wwids))
++		goto lookup;
++
++	/*
++	 * This will read the sysfs wwid file, nvme devices in particular have
++	 * a wwid file but not a vpd_pg83 file.
++	 */
++	if (_read_sys_wwid(cmd, dev, idbuf, sizeof(idbuf)))
++		add_wwid(idbuf, 0, &dev->wwids);
++
++ lookup:
++	dm_list_iterate_items(dw, &dev->wwids) {
++		if (dw->type == 1 || dw->type == 2 || dw->type == 3)
++			wwid = &dw->id[4];
++		else
++			wwid = dw->id;
++
++		if (dm_hash_lookup_binary(_wwid_hash_tab, wwid, strlen(wwid))) {
++			log_debug_devs("dev_is_mpath_component %s %s in wwids file", dev_name(dev), dw->id);
++			return 1;
++		}
+ 	}
++
+ 	return 0;
+ }
+ 
+ int dev_is_mpath_component(struct cmd_context *cmd, struct device *dev)
+ {
+-	if (_dev_is_mpath_component_sysfs(cmd, dev) == 1)
++	struct dev_types *dt = cmd->dev_types;
++	int primary_result;
++	dev_t primary_dev;
++
++	/*
++	 * multipath only uses SCSI or NVME devices
++	 */
++	if (!major_is_scsi_device(dt, MAJOR(dev->dev)) && !dev_is_nvme(dt, dev))
++		return 0;
++
++	/*
++	 * primary_result 2: dev is a partition, primary_dev is the whole device
++	 * primary_result 1: dev is a whole device
++	 */
++	primary_result = dev_get_primary_dev(dt, dev, &primary_dev);
++
++	if (_dev_is_mpath_component_sysfs(cmd, dev, primary_result, primary_dev) == 1)
+ 		goto found;
+ 
+-	if (_dev_in_wwid_file(cmd, dev))
++	if (_dev_in_wwid_file(cmd, dev, primary_result, primary_dev))
+ 		goto found;
+ 
+ 	if (external_device_info_source() == DEV_EXT_UDEV) {
+@@ -637,6 +787,12 @@ int dev_is_mpath_component(struct cmd_context *cmd, struct device *dev)
+ 			goto found;
+ 	}
+ 
++	/*
++	 * TODO: save the result of this function in dev->flags and use those
++	 * flags on repeated calls to avoid repeating the work multiple times
++	 * for the same device when there are partitions on the device.
++	 */
++
+ 	return 0;
+ found:
+ 	return 1;
+diff --git a/lib/device/device.h b/lib/device/device.h
+index 572994bb9..1c85f37a9 100644
+--- a/lib/device/device.h
++++ b/lib/device/device.h
+@@ -59,6 +59,14 @@ struct dev_ext {
+ 	void *handle;
+ };
+ 
++#define DEV_WWID_SIZE 128
++
++struct dev_wwid {
++	struct dm_list list;
++	int type;
++	char id[DEV_WWID_SIZE];
++};
++
+ #define DEV_ID_TYPE_SYS_WWID   0x0001
+ #define DEV_ID_TYPE_SYS_SERIAL 0x0002
+ #define DEV_ID_TYPE_MPATH_UUID 0x0003
+@@ -105,6 +113,7 @@ struct dev_use {
+  */
+ struct device {
+ 	struct dm_list aliases;	/* struct dm_str_list */
++	struct dm_list wwids; /* struct dev_wwid, used for multipath component detection */
+ 	struct dm_list ids; /* struct dev_id, different entries for different idtypes */
+ 	struct dev_id *id; /* points to the the ids entry being used for this dev */
+ 	dev_t dev;
+@@ -206,5 +215,9 @@ void dev_destroy_file(struct device *dev);
+ 
+ int dev_mpath_init(const char *config_wwids_file);
+ void dev_mpath_exit(void);
++struct dev_wwid *add_wwid(char *id, int id_type, struct dm_list *ids);
++void free_wwids(struct dm_list *ids);
++int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list *ids);
++int format_t10_id(const unsigned char *in, int in_bytes, unsigned char *out, int out_bytes);
+ 
+ #endif
+diff --git a/lib/device/device_id.c b/lib/device/device_id.c
+index 20901ab90..4d8fa5c9c 100644
+--- a/lib/device/device_id.c
++++ b/lib/device/device_id.c
+@@ -182,7 +182,9 @@ void free_dids(struct dm_list *ids)
+ 	}
+ }
+ 
+-int read_sys_block(struct cmd_context *cmd, struct device *dev, const char *suffix, char *sysbuf, int sysbufsize)
++static int _read_sys_block(struct cmd_context *cmd, struct device *dev,
++			   const char *suffix, char *sysbuf, int sysbufsize,
++			   int binary, int *retlen)
+ {
+ 	char path[PATH_MAX];
+ 	dev_t devt = dev->dev;
+@@ -196,11 +198,17 @@ int read_sys_block(struct cmd_context *cmd, struct device *dev, const char *suff
+ 		return 0;
+ 	}
+ 
+-	get_sysfs_value(path, sysbuf, sysbufsize, 0);
++	if (binary) {
++		ret = get_sysfs_binary(path, sysbuf, sysbufsize, retlen);
++		if (ret && !*retlen)
++			ret = 0;
++	} else {
++		ret = get_sysfs_value(path, sysbuf, sysbufsize, 0);
++		if (ret && !sysbuf[0])
++			ret = 0;
++	}
+ 
+-	if (sysbuf[0]) {
+-		if (prim)
+-			log_debug("Using primary device_id for partition %s.", dev_name(dev));
++	if (ret) {
+ 		sysbuf[sysbufsize - 1] = '\0';
+ 		return 1;
+ 	}
+@@ -220,6 +228,19 @@ int read_sys_block(struct cmd_context *cmd, struct device *dev, const char *suff
+ 	return 0;
+ }
+ 
++int read_sys_block(struct cmd_context *cmd, struct device *dev,
++		   const char *suffix, char *sysbuf, int sysbufsize)
++{
++	return _read_sys_block(cmd, dev, suffix, sysbuf, sysbufsize, 0, NULL);
++}
++
++int read_sys_block_binary(struct cmd_context *cmd, struct device *dev,
++			  const char *suffix, char *sysbuf, int sysbufsize,
++			  int *retlen)
++{
++	return _read_sys_block(cmd, dev, suffix, sysbuf, sysbufsize, 1, retlen);
++}
++
+ static int _dm_uuid_has_prefix(char *sysbuf, const char *prefix)
+ {
+ 	if (!strncmp(sysbuf, prefix, strlen(prefix)))
+diff --git a/lib/device/device_id.h b/lib/device/device_id.h
+index 2cd2fd7c6..e049e2333 100644
+--- a/lib/device/device_id.h
++++ b/lib/device/device_id.h
+@@ -55,6 +55,8 @@ void devices_file_exit(struct cmd_context *cmd);
+ void unlink_searched_devnames(struct cmd_context *cmd);
+ 
+ int read_sys_block(struct cmd_context *cmd, struct device *dev, const char *suffix, char *sysbuf, int sysbufsize);
++int read_sys_block_binary(struct cmd_context *cmd, struct device *dev,
++			  const char *suffix, char *sysbuf, int sysbufsize, int *retlen);
+ 
+ int dev_has_mpath_uuid(struct cmd_context *cmd, struct device *dev, const char **idname_out);
+ 
+diff --git a/lib/device/parse_vpd.c b/lib/device/parse_vpd.c
+new file mode 100644
+index 000000000..4bafa7b9e
+--- /dev/null
++++ b/lib/device/parse_vpd.c
+@@ -0,0 +1,199 @@
++/*
++ * Copyright (C) 2022 Red Hat, Inc. All rights reserved.
++ *
++ * This file is part of LVM2.
++ *
++ * This copyrighted material is made available to anyone wishing to use,
++ * modify, copy, or redistribute it subject to the terms and conditions
++ * of the GNU Lesser General Public License v.2.1.
++ *
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this program; if not, write to the Free Software Foundation,
++ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
++ */
++
++#include "base/memory/zalloc.h"
++#include "lib/misc/lib.h"
++#include "lib/device/device.h"
++
++#include <stdio.h>
++#include <unistd.h>
++#include <stdint.h>
++#include <stdlib.h>
++#include <stdarg.h>
++#include <string.h>
++#include <inttypes.h>
++#include <sys/types.h>
++#include <sys/ioctl.h>
++#include <sys/stat.h>
++#include <fcntl.h>
++#include <ctype.h>
++#include <limits.h>
++#include <dirent.h>
++#include <errno.h>
++#include <stdbool.h>
++#include <assert.h>
++
++/*
++ * Replace series of spaces with a single _.
++ */
++int format_t10_id(const unsigned char *in, int in_bytes, unsigned char *out, int out_bytes)
++{
++	int in_space = 0;
++	int retlen = 0;
++	int j = 0;
++	int i;
++
++	for (i = 0; i < in_bytes; i++) {
++		if (!in[i])
++			break;
++		if (j >= (out_bytes - 2))
++			break;
++		/* skip leading spaces */
++		if (!retlen && (in[i] == ' '))
++			continue;
++		/* replace one or more spaces with _ */
++		if (in[i] == ' ') {
++			in_space = 1;
++			continue;
++		}
++		/* spaces are finished so insert _ */
++		if (in_space) {
++			out[j++] = '_';
++			in_space = 0;
++			retlen++;
++		}
++		out[j++] = in[i];
++		retlen++;
++	}
++	return retlen;
++}
++
++static int _to_hex(const unsigned char *in, int in_bytes, unsigned char *out, int out_bytes)
++{
++	int off = 0;
++	int num;
++	int i;
++
++	for (i = 0; i < in_bytes; i++) {
++		num = sprintf((char *)out + off, "%02x", in[i]);
++		if (num < 0)
++			break;
++		off += num;
++		if (off + 2 >= out_bytes)
++			break;
++	}
++	return off;
++}
++
++#define ID_BUFSIZE 1024
++
++/*
++ * based on linux kernel function
++ */
++int parse_vpd_ids(const unsigned char *vpd_data, int vpd_datalen, struct dm_list *ids)
++{
++	char id[ID_BUFSIZE];
++	unsigned char tmp_str[ID_BUFSIZE];
++	const unsigned char *d, *cur_id_str;
++	size_t id_len = ID_BUFSIZE;
++	int id_size = -1;
++	uint8_t cur_id_size = 0;
++
++	memset(id, 0, ID_BUFSIZE);
++	for (d = vpd_data + 4;
++	     d < vpd_data + vpd_datalen;
++	     d += d[3] + 4) {
++		memset(tmp_str, 0, sizeof(tmp_str));
++
++		switch (d[1] & 0xf) {
++		case 0x1:
++			/* T10 Vendor ID */
++			cur_id_size = d[3];
++			if (cur_id_size + 4 > id_len)
++				cur_id_size = id_len - 4;
++			cur_id_str = d + 4;
++			format_t10_id(cur_id_str, cur_id_size, tmp_str, sizeof(tmp_str));
++			id_size = snprintf(id, ID_BUFSIZE, "t10.%s", tmp_str);
++			if (id_size < 0)
++				break;
++			if (id_size >= ID_BUFSIZE)
++				id_size = ID_BUFSIZE - 1;
++			add_wwid(id, 1, ids);
++			break;
++		case 0x2:
++			/* EUI-64 */
++			cur_id_size = d[3];
++			cur_id_str = d + 4;
++			switch (cur_id_size) {
++			case 8:
++				_to_hex(cur_id_str, 8, tmp_str, sizeof(tmp_str));
++				id_size = snprintf(id, ID_BUFSIZE, "eui.%s", tmp_str);
++				break;
++			case 12:
++				_to_hex(cur_id_str, 12, tmp_str, sizeof(tmp_str));
++				id_size = snprintf(id, ID_BUFSIZE, "eui.%s", tmp_str);
++				break;
++			case 16:
++				_to_hex(cur_id_str, 16, tmp_str, sizeof(tmp_str));
++				id_size = snprintf(id, ID_BUFSIZE, "eui.%s", tmp_str);
++				break;
++			default:
++				break;
++			}
++			if (id_size < 0)
++				break;
++			if (id_size >= ID_BUFSIZE)
++				id_size = ID_BUFSIZE - 1;
++			add_wwid(id, 2, ids);
++			break;
++		case 0x3:
++			/* NAA */
++			cur_id_size = d[3];
++			cur_id_str = d + 4;
++			switch (cur_id_size) {
++			case 8:
++				_to_hex(cur_id_str, 8, tmp_str, sizeof(tmp_str));
++				id_size = snprintf(id, ID_BUFSIZE, "naa.%s", tmp_str);
++				break;
++			case 16:
++				_to_hex(cur_id_str, 16, tmp_str, sizeof(tmp_str));
++				id_size = snprintf(id, ID_BUFSIZE, "naa.%s", tmp_str);
++				break;
++			default:
++				break;
++			}
++			if (id_size < 0)
++				break;
++			if (id_size >= ID_BUFSIZE)
++				id_size = ID_BUFSIZE - 1;
++			add_wwid(id, 3, ids);
++			break;
++		case 0x8:
++			/* SCSI name string */
++			cur_id_size = d[3];
++			cur_id_str = d + 4;
++			if (cur_id_size >= id_len)
++				cur_id_size = id_len - 1;
++			memcpy(id, cur_id_str, cur_id_size);
++			id_size = cur_id_size;
++
++			/*
++			 * Not in the kernel version, copying multipath code,
++			 * which checks if this string begins with naa or eui
++			 * and if so does tolower() on the chars.
++			 */
++			if (!strncmp(id, "naa.", 4) || !strncmp(id, "eui.", 4)) {
++				int i;
++				for (i = 0; i < id_size; i++)
++					id[i] = tolower(id[i]);
++			}
++			add_wwid(id, 8, ids);
++			break;
++		default:
++			break;
++		}
++	}
++
++	return id_size;
++}
+-- 
+2.34.3
+
diff --git a/SOURCES/0054-build-Fix-make-rpm-with-VERSION_DM-without-dash.patch b/SOURCES/0054-build-Fix-make-rpm-with-VERSION_DM-without-dash.patch
new file mode 100644
index 0000000..c1ce567
--- /dev/null
+++ b/SOURCES/0054-build-Fix-make-rpm-with-VERSION_DM-without-dash.patch
@@ -0,0 +1,29 @@
+From e60d7ce8e748cb6d51552879c162d01aafa17160 Mon Sep 17 00:00:00 2001
+From: Marian Csontos <mcsontos@redhat.com>
+Date: Wed, 15 Jun 2022 11:53:51 +0200
+Subject: [PATCH 54/54] build: Fix make rpm with VERSION_DM without dash
+
+When building RPM from a branch based on a release tag the expected -git
+suffix is missing breaking the script producing error like following one:
+
+    error: line 215: Unterminated rich dependency: (2021-53.ge36b180a6.el9: Requires: device-mapper-devel >= 1.02.181 (2021-53.ge36b180a6.el9
+---
+ Makefile.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/Makefile.in b/Makefile.in
+index 3b7e0ecaa..f7a46269a 100644
+--- a/Makefile.in
++++ b/Makefile.in
+@@ -110,7 +110,7 @@ rpm: dist
+ 	$(LN_S) -f $(abs_top_srcdir)/spec/build.inc $(rpmbuilddir)/SOURCES
+ 	$(LN_S) -f $(abs_top_srcdir)/spec/macros.inc $(rpmbuilddir)/SOURCES
+ 	$(LN_S) -f $(abs_top_srcdir)/spec/packages.inc $(rpmbuilddir)/SOURCES
+-	DM_VER=$$(cut -d- -f1 $(top_srcdir)/VERSION_DM);\
++	DM_VER=$$(cut -d' ' -f1 $(top_srcdir)/VERSION_DM | cut -d- -f1);\
+ 	GIT_VER=$$(cd $(top_srcdir); git describe | cut -d- --output-delimiter=. -f2,3 || echo 0);\
+ 	$(SED) -e "s,\(device_mapper_version\) [0-9.]*$$,\1 $$DM_VER," \
+ 	    -e "s,^\(Version:[^0-9%]*\)[0-9.]*$$,\1 $(LVM_VER)," \
+-- 
+2.34.3
+
diff --git a/SOURCES/0055-make-generate.patch b/SOURCES/0055-make-generate.patch
new file mode 100644
index 0000000..03629e4
--- /dev/null
+++ b/SOURCES/0055-make-generate.patch
@@ -0,0 +1,921 @@
+From 13b8c209f39ab018f34b3ccd369590a89fe71a57 Mon Sep 17 00:00:00 2001
+From: Marian Csontos <mcsontos@redhat.com>
+Date: Wed, 15 Jun 2022 19:58:18 +0200
+Subject: [PATCH] make: generate
+
+---
+ man/lvchange.8_pregen        | 20 ++++++++++++++++++--
+ man/lvconvert.8_pregen       | 22 ++++++++++++++++++++--
+ man/lvcreate.8_pregen        | 24 ++++++++++++++++++++++--
+ man/lvdisplay.8_pregen       |  5 +++--
+ man/lvextend.8_pregen        |  5 +++--
+ man/lvm-fullreport.8_pregen  |  5 +++--
+ man/lvm-lvpoll.8_pregen      |  5 +++--
+ man/lvmconfig.8_pregen       |  5 +++--
+ man/lvmdevices.8_pregen      |  8 +++++---
+ man/lvmdiskscan.8_pregen     |  5 +++--
+ man/lvreduce.8_pregen        |  5 +++--
+ man/lvremove.8_pregen        |  5 +++--
+ man/lvrename.8_pregen        |  5 +++--
+ man/lvresize.8_pregen        |  5 +++--
+ man/lvs.8_pregen             |  5 +++--
+ man/lvscan.8_pregen          |  5 +++--
+ man/pvchange.8_pregen        |  5 +++--
+ man/pvck.8_pregen            |  5 +++--
+ man/pvcreate.8_pregen        |  5 +++--
+ man/pvdisplay.8_pregen       |  5 +++--
+ man/pvmove.8_pregen          |  5 +++--
+ man/pvremove.8_pregen        |  5 +++--
+ man/pvresize.8_pregen        |  5 +++--
+ man/pvs.8_pregen             |  5 +++--
+ man/pvscan.8_pregen          |  5 +++--
+ man/vgcfgbackup.8_pregen     |  5 +++--
+ man/vgcfgrestore.8_pregen    |  5 +++--
+ man/vgchange.8_pregen        |  5 +++--
+ man/vgck.8_pregen            |  5 +++--
+ man/vgconvert.8_pregen       |  5 +++--
+ man/vgcreate.8_pregen        |  5 +++--
+ man/vgdisplay.8_pregen       |  5 +++--
+ man/vgexport.8_pregen        |  5 +++--
+ man/vgextend.8_pregen        |  5 +++--
+ man/vgimport.8_pregen        |  5 +++--
+ man/vgimportclone.8_pregen   |  5 +++--
+ man/vgimportdevices.8_pregen |  5 +++--
+ man/vgmerge.8_pregen         |  5 +++--
+ man/vgmknodes.8_pregen       |  5 +++--
+ man/vgreduce.8_pregen        |  5 +++--
+ man/vgremove.8_pregen        |  5 +++--
+ man/vgrename.8_pregen        |  5 +++--
+ man/vgs.8_pregen             |  5 +++--
+ man/vgscan.8_pregen          |  5 +++--
+ man/vgsplit.8_pregen         |  5 +++--
+ 45 files changed, 188 insertions(+), 91 deletions(-)
+
+diff --git a/man/lvchange.8_pregen b/man/lvchange.8_pregen
+index b559c89c9..27bee0f14 100644
+--- a/man/lvchange.8_pregen
++++ b/man/lvchange.8_pregen
+@@ -126,6 +126,8 @@ lvchange \(em Change the attributes of logical volume(s)
+     \fB--sysinit\fP
+ .br
+  \fB-t\fP|\fB--test\fP
++.br
++    \fB--vdosettings\fP \fIString\fP
+ .br
+  \fB-v\fP|\fB--verbose\fP
+ .br
+@@ -202,6 +204,8 @@ required, after which the others are optional.
+      \fB--\fP[\fBraid\fP]\fBminrecoveryrate\fP \fISize\fP[k|UNIT]
+ .br
+      \fB--\fP[\fBraid\fP]\fBmaxrecoveryrate\fP \fISize\fP[k|UNIT]
++.br
++     \fB--vdosettings\fP \fIString\fP
+ .br
+      \fB--\fP[\fBraid\fP]\fBwritebehind\fP \fINumber\fP
+ .br
+@@ -609,8 +613,9 @@ See \fBlvm.conf\fP(5) for more information about profiles.
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+@@ -927,6 +932,17 @@ error messages in multi-stage operations if a tool relies on reading
+ back metadata it believes has changed but hasn't.
+ .
+ .HP
++\fB--vdosettings\fP \fIString\fP
++.br
++Specifies tunable VDO options for VDO LVs.
++Use the form 'option=value' or 'option1=value option2=value', or
++repeat --vdosettings for each option being set.
++These settings override the default VDO behaviors.
++To remove vdosettings and revert to the default
++VDO behaviors, use --vdosettings 'default'.
++See \fBlvmvdo\fP(7) for more information.
++.
++.HP
+ \fB-v\fP|\fB--verbose\fP ...
+ .br
+ Set verbose level. Repeat from 1 to 4 times to increase the detail
+diff --git a/man/lvconvert.8_pregen b/man/lvconvert.8_pregen
+index 679519303..fa52da55c 100644
+--- a/man/lvconvert.8_pregen
++++ b/man/lvconvert.8_pregen
+@@ -155,6 +155,8 @@ lvconvert \(em Change logical volume layout
+     \fB--usepolicies\fP
+ .br
+     \fB--vdopool\fP \fILV\fP
++.br
++    \fB--vdosettings\fP \fIString\fP
+ .br
+  \fB-v\fP|\fB--verbose\fP
+ .br
+@@ -742,6 +744,8 @@ Convert LV to type vdopool.
+ .br
+ [    \fB--deduplication\fP \fBy\fP|\fBn\fP ]
+ .br
++[    \fB--vdosettings\fP \fIString\fP ]
++.br
+ [ COMMON_OPTIONS ]
+ .ad b
+ .RE
+@@ -1131,8 +1135,9 @@ See \fBlvmvdo\fP(7) for more information about VDO usage.
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+@@ -1535,6 +1540,17 @@ The name of a VDO pool LV.
+ See \fBlvmvdo\fP(7) for more information about VDO usage.
+ .
+ .HP
++\fB--vdosettings\fP \fIString\fP
++.br
++Specifies tunable VDO options for VDO LVs.
++Use the form 'option=value' or 'option1=value option2=value', or
++repeat --vdosettings for each option being set.
++These settings override the default VDO behaviors.
++To remove vdosettings and revert to the default
++VDO behaviors, use --vdosettings 'default'.
++See \fBlvmvdo\fP(7) for more information.
++.
++.HP
+ \fB-v\fP|\fB--verbose\fP ...
+ .br
+ Set verbose level. Repeat from 1 to 4 times to increase the detail
+@@ -1808,6 +1824,8 @@ Convert LV to type vdopool.
+ .br
+ [    \fB--deduplication\fP \fBy\fP|\fBn\fP ]
+ .br
++[    \fB--vdosettings\fP \fIString\fP ]
++.br
+ [ COMMON_OPTIONS ]
+ .ad b
+ .RE
+diff --git a/man/lvcreate.8_pregen b/man/lvcreate.8_pregen
+index 2cccbbe56..0ffe92a94 100644
+--- a/man/lvcreate.8_pregen
++++ b/man/lvcreate.8_pregen
+@@ -157,6 +157,8 @@ lvcreate \(em Create a logical volume
+     \fB--vdo\fP
+ .br
+     \fB--vdopool\fP \fILV\fP
++.br
++    \fB--vdosettings\fP \fIString\fP
+ .br
+  \fB-v\fP|\fB--verbose\fP
+ .br
+@@ -537,6 +539,8 @@ Create a LV that returns VDO when used.
+ .br
+ [    \fB--deduplication\fP \fBy\fP|\fBn\fP ]
+ .br
++[    \fB--vdosettings\fP \fIString\fP ]
++.br
+ [ COMMON_OPTIONS ]
+ .ad b
+ .RE
+@@ -1003,8 +1007,9 @@ See \fBlvmvdo\fP(7) for more information about VDO usage.
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+@@ -1438,6 +1443,17 @@ The name of a VDO pool LV.
+ See \fBlvmvdo\fP(7) for more information about VDO usage.
+ .
+ .HP
++\fB--vdosettings\fP \fIString\fP
++.br
++Specifies tunable VDO options for VDO LVs.
++Use the form 'option=value' or 'option1=value option2=value', or
++repeat --vdosettings for each option being set.
++These settings override the default VDO behaviors.
++To remove vdosettings and revert to the default
++VDO behaviors, use --vdosettings 'default'.
++See \fBlvmvdo\fP(7) for more information.
++.
++.HP
+ \fB-v\fP|\fB--verbose\fP ...
+ .br
+ Set verbose level. Repeat from 1 to 4 times to increase the detail
+@@ -1966,6 +1982,8 @@ Create a VDO LV with VDO pool.
+ .br
+ [    \fB--deduplication\fP \fBy\fP|\fBn\fP ]
+ .br
++[    \fB--vdosettings\fP \fIString\fP ]
++.br
+ [ COMMON_OPTIONS ]
+ .ad b
+ .RE
+@@ -1996,6 +2014,8 @@ Create a VDO LV with VDO pool.
+ .br
+ [    \fB--deduplication\fP \fBy\fP|\fBn\fP ]
+ .br
++[    \fB--vdosettings\fP \fIString\fP ]
++.br
+ [ COMMON_OPTIONS ]
+ .ad b
+ .RE
+diff --git a/man/lvdisplay.8_pregen b/man/lvdisplay.8_pregen
+index 04aab4c09..387a7d30d 100644
+--- a/man/lvdisplay.8_pregen
++++ b/man/lvdisplay.8_pregen
+@@ -186,8 +186,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvextend.8_pregen b/man/lvextend.8_pregen
+index be6992e94..8a3e1ea4e 100644
+--- a/man/lvextend.8_pregen
++++ b/man/lvextend.8_pregen
+@@ -328,8 +328,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvm-fullreport.8_pregen b/man/lvm-fullreport.8_pregen
+index 02b38ef40..edae0efe3 100644
+--- a/man/lvm-fullreport.8_pregen
++++ b/man/lvm-fullreport.8_pregen
+@@ -169,8 +169,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvm-lvpoll.8_pregen b/man/lvm-lvpoll.8_pregen
+index 7f45f6eb3..fa8027f8e 100644
+--- a/man/lvm-lvpoll.8_pregen
++++ b/man/lvm-lvpoll.8_pregen
+@@ -115,8 +115,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvmconfig.8_pregen b/man/lvmconfig.8_pregen
+index 51946e1ec..e6762f989 100644
+--- a/man/lvmconfig.8_pregen
++++ b/man/lvmconfig.8_pregen
+@@ -156,8 +156,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvmdevices.8_pregen b/man/lvmdevices.8_pregen
+index d64c3a31a..a2397e50f 100644
+--- a/man/lvmdevices.8_pregen
++++ b/man/lvmdevices.8_pregen
+@@ -322,7 +322,8 @@ Find a device with the PVID and add the device to the devices file.
+ .HP
+ \fB--check\fP
+ .br
+-Check the content of the devices file.
++Checks the content of the devices file.
++Reports incorrect device names or PVIDs for entries.
+ .
+ .HP
+ \fB--commandprofile\fP \fIString\fP
+@@ -364,8 +365,9 @@ then it will override the default type that lvm would use.
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvmdiskscan.8_pregen b/man/lvmdiskscan.8_pregen
+index 7fd3d941d..ac0931c88 100644
+--- a/man/lvmdiskscan.8_pregen
++++ b/man/lvmdiskscan.8_pregen
+@@ -102,8 +102,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvreduce.8_pregen b/man/lvreduce.8_pregen
+index ea960eb53..cd2e38e5b 100644
+--- a/man/lvreduce.8_pregen
++++ b/man/lvreduce.8_pregen
+@@ -130,8 +130,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvremove.8_pregen b/man/lvremove.8_pregen
+index 2bd7997a3..8a4afbdff 100644
+--- a/man/lvremove.8_pregen
++++ b/man/lvremove.8_pregen
+@@ -136,8 +136,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvrename.8_pregen b/man/lvrename.8_pregen
+index d41a4c241..27ce2caeb 100644
+--- a/man/lvrename.8_pregen
++++ b/man/lvrename.8_pregen
+@@ -120,8 +120,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvresize.8_pregen b/man/lvresize.8_pregen
+index f3ea2536c..10e7dda7c 100644
+--- a/man/lvresize.8_pregen
++++ b/man/lvresize.8_pregen
+@@ -286,8 +286,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvs.8_pregen b/man/lvs.8_pregen
+index 2b2c5f193..94a74f9dd 100644
+--- a/man/lvs.8_pregen
++++ b/man/lvs.8_pregen
+@@ -172,8 +172,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/lvscan.8_pregen b/man/lvscan.8_pregen
+index f459ab35b..8d79f22dd 100644
+--- a/man/lvscan.8_pregen
++++ b/man/lvscan.8_pregen
+@@ -119,8 +119,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/pvchange.8_pregen b/man/pvchange.8_pregen
+index 4add9ca69..010845274 100644
+--- a/man/pvchange.8_pregen
++++ b/man/pvchange.8_pregen
+@@ -179,8 +179,9 @@ multiple tags at once. See \fBlvm\fP(8) for information about tags.
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/pvck.8_pregen b/man/pvck.8_pregen
+index 88200f21e..7d6652342 100644
+--- a/man/pvck.8_pregen
++++ b/man/pvck.8_pregen
+@@ -351,8 +351,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/pvcreate.8_pregen b/man/pvcreate.8_pregen
+index a74a5ec2a..6ffd596fe 100644
+--- a/man/pvcreate.8_pregen
++++ b/man/pvcreate.8_pregen
+@@ -229,8 +229,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/pvdisplay.8_pregen b/man/pvdisplay.8_pregen
+index 2f26a8727..59628bedd 100644
+--- a/man/pvdisplay.8_pregen
++++ b/man/pvdisplay.8_pregen
+@@ -183,8 +183,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/pvmove.8_pregen b/man/pvmove.8_pregen
+index 0f70497a2..f633b97d2 100644
+--- a/man/pvmove.8_pregen
++++ b/man/pvmove.8_pregen
+@@ -206,8 +206,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/pvremove.8_pregen b/man/pvremove.8_pregen
+index 3d4a86c09..51c589fd7 100644
+--- a/man/pvremove.8_pregen
++++ b/man/pvremove.8_pregen
+@@ -103,8 +103,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/pvresize.8_pregen b/man/pvresize.8_pregen
+index 87d87c8ce..9ce57e325 100644
+--- a/man/pvresize.8_pregen
++++ b/man/pvresize.8_pregen
+@@ -98,8 +98,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/pvs.8_pregen b/man/pvs.8_pregen
+index 32c28e4d1..955b3f887 100644
+--- a/man/pvs.8_pregen
++++ b/man/pvs.8_pregen
+@@ -169,8 +169,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/pvscan.8_pregen b/man/pvscan.8_pregen
+index 1c96d5aab..1e1cc11ab 100644
+--- a/man/pvscan.8_pregen
++++ b/man/pvscan.8_pregen
+@@ -369,8 +369,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgcfgbackup.8_pregen b/man/vgcfgbackup.8_pregen
+index 5e658093a..cf984b04b 100644
+--- a/man/vgcfgbackup.8_pregen
++++ b/man/vgcfgbackup.8_pregen
+@@ -123,8 +123,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgcfgrestore.8_pregen b/man/vgcfgrestore.8_pregen
+index 695e05582..6165cd36e 100644
+--- a/man/vgcfgrestore.8_pregen
++++ b/man/vgcfgrestore.8_pregen
+@@ -208,8 +208,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgchange.8_pregen b/man/vgchange.8_pregen
+index 05c67aead..7c7030c0d 100644
+--- a/man/vgchange.8_pregen
++++ b/man/vgchange.8_pregen
+@@ -556,8 +556,9 @@ See \fBlvm.conf\fP(5) for more information about profiles.
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgck.8_pregen b/man/vgck.8_pregen
+index a8fa33f4b..cfb828ff9 100644
+--- a/man/vgck.8_pregen
++++ b/man/vgck.8_pregen
+@@ -114,8 +114,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgconvert.8_pregen b/man/vgconvert.8_pregen
+index 4d54c2b27..b99de39b8 100644
+--- a/man/vgconvert.8_pregen
++++ b/man/vgconvert.8_pregen
+@@ -124,8 +124,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgcreate.8_pregen b/man/vgcreate.8_pregen
+index 9bb8d3868..d5316aa1c 100644
+--- a/man/vgcreate.8_pregen
++++ b/man/vgcreate.8_pregen
+@@ -206,8 +206,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgdisplay.8_pregen b/man/vgdisplay.8_pregen
+index 0a12b3c39..b6cd294d5 100644
+--- a/man/vgdisplay.8_pregen
++++ b/man/vgdisplay.8_pregen
+@@ -180,8 +180,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgexport.8_pregen b/man/vgexport.8_pregen
+index a87058946..6af64b82a 100644
+--- a/man/vgexport.8_pregen
++++ b/man/vgexport.8_pregen
+@@ -139,8 +139,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgextend.8_pregen b/man/vgextend.8_pregen
+index 2b2650527..e55e0a110 100644
+--- a/man/vgextend.8_pregen
++++ b/man/vgextend.8_pregen
+@@ -147,8 +147,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgimport.8_pregen b/man/vgimport.8_pregen
+index 5cd8fab84..9f8614205 100644
+--- a/man/vgimport.8_pregen
++++ b/man/vgimport.8_pregen
+@@ -128,8 +128,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgimportclone.8_pregen b/man/vgimportclone.8_pregen
+index bf0af5841..80da5454b 100644
+--- a/man/vgimportclone.8_pregen
++++ b/man/vgimportclone.8_pregen
+@@ -113,8 +113,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgimportdevices.8_pregen b/man/vgimportdevices.8_pregen
+index 5897e29ad..44e5fc663 100644
+--- a/man/vgimportdevices.8_pregen
++++ b/man/vgimportdevices.8_pregen
+@@ -132,8 +132,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgmerge.8_pregen b/man/vgmerge.8_pregen
+index a36e0c7bd..dfe8e2f0f 100644
+--- a/man/vgmerge.8_pregen
++++ b/man/vgmerge.8_pregen
+@@ -107,8 +107,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgmknodes.8_pregen b/man/vgmknodes.8_pregen
+index 0a03e1582..d7cd722a4 100644
+--- a/man/vgmknodes.8_pregen
++++ b/man/vgmknodes.8_pregen
+@@ -108,8 +108,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgreduce.8_pregen b/man/vgreduce.8_pregen
+index f3178a618..63b0a20f3 100644
+--- a/man/vgreduce.8_pregen
++++ b/man/vgreduce.8_pregen
+@@ -199,8 +199,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgremove.8_pregen b/man/vgremove.8_pregen
+index 779c0f13e..661ada673 100644
+--- a/man/vgremove.8_pregen
++++ b/man/vgremove.8_pregen
+@@ -109,8 +109,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgrename.8_pregen b/man/vgrename.8_pregen
+index d3e776ca4..2b849d180 100644
+--- a/man/vgrename.8_pregen
++++ b/man/vgrename.8_pregen
+@@ -133,8 +133,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgs.8_pregen b/man/vgs.8_pregen
+index ee5083a93..2ca98b0b0 100644
+--- a/man/vgs.8_pregen
++++ b/man/vgs.8_pregen
+@@ -166,8 +166,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgscan.8_pregen b/man/vgscan.8_pregen
+index 824e7f673..a8da70d99 100644
+--- a/man/vgscan.8_pregen
++++ b/man/vgscan.8_pregen
+@@ -101,8 +101,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+diff --git a/man/vgsplit.8_pregen b/man/vgsplit.8_pregen
+index 065c8b52a..99938a4e3 100644
+--- a/man/vgsplit.8_pregen
++++ b/man/vgsplit.8_pregen
+@@ -175,8 +175,9 @@ messages sent to the log file and/or syslog (if configured).
+ .HP
+ \fB--devices\fP \fIPV\fP
+ .br
+-Devices that the command can use. This option can be repeated
+-or accepts a comma separated list of devices. This overrides
++Restricts the devices that are visible and accessible to the command.
++Devices not listed will appear to be missing. This option can be
++repeated, or accepts a comma separated list of devices. This overrides
+ the devices file.
+ .
+ .HP
+-- 
+2.34.3
+
diff --git a/SOURCES/0056-exit-with-error-when-devicesfile-name-doesn-t-exist.patch b/SOURCES/0056-exit-with-error-when-devicesfile-name-doesn-t-exist.patch
new file mode 100644
index 0000000..710790e
--- /dev/null
+++ b/SOURCES/0056-exit-with-error-when-devicesfile-name-doesn-t-exist.patch
@@ -0,0 +1,246 @@
+From 73b9a2805ca2f2c70f6f631b405f8fea3f72f23b Mon Sep 17 00:00:00 2001
+From: David Teigland <teigland@redhat.com>
+Date: Tue, 5 Jul 2022 17:08:00 -0500
+Subject: [PATCH] exit with error when --devicesfile name doesn't exist
+
+---
+ lib/cache/lvmcache.c            |  3 ++-
+ lib/label/label.c               |  4 ++--
+ test/shell/devicesfile-basic.sh |  1 +
+ tools/pvcreate.c                |  3 ++-
+ tools/pvremove.c                |  3 ++-
+ tools/pvscan.c                  |  3 ++-
+ tools/toollib.c                 | 27 +++++++++++++++++++++------
+ tools/vgcfgrestore.c            |  5 ++++-
+ tools/vgcreate.c                |  5 ++++-
+ tools/vgextend.c                |  3 ++-
+ tools/vgmerge.c                 |  3 ++-
+ tools/vgsplit.c                 |  3 ++-
+ 12 files changed, 46 insertions(+), 17 deletions(-)
+
+diff --git a/lib/cache/lvmcache.c b/lib/cache/lvmcache.c
+index 0e62cd267..61a2fee6d 100644
+--- a/lib/cache/lvmcache.c
++++ b/lib/cache/lvmcache.c
+@@ -1600,7 +1600,8 @@ int lvmcache_label_scan(struct cmd_context *cmd)
+ 	 * with infos/vginfos based on reading headers from
+ 	 * each device, and a vg summary from each mda.
+ 	 */
+-	label_scan(cmd);
++	if (!label_scan(cmd))
++		return_0;
+ 
+ 	/*
+ 	 * When devnames are used as device ids (which is dispreferred),
+diff --git a/lib/label/label.c b/lib/label/label.c
+index 06958b502..00ede2b76 100644
+--- a/lib/label/label.c
++++ b/lib/label/label.c
+@@ -800,7 +800,7 @@ static int _setup_bcache(void)
+ 	}
+ 
+ 	if (!(scan_bcache = bcache_create(BCACHE_BLOCK_SIZE_IN_SECTORS, cache_blocks, ioe))) {
+-		log_error("Failed to create bcache with %d cache blocks.", cache_blocks);
++		log_error("Failed to set up io layer with %d blocks.", cache_blocks);
+ 		return 0;
+ 	}
+ 
+@@ -1015,7 +1015,7 @@ int label_scan(struct cmd_context *cmd)
+ 	 * data to invalidate.)
+ 	 */
+ 	if (!(iter = dev_iter_create(NULL, 0))) {
+-		log_error("Scanning failed to get devices.");
++		log_error("Failed to get device list.");
+ 		return 0;
+ 	}
+ 	while ((dev = dev_iter_get(cmd, iter))) {
+diff --git a/test/shell/devicesfile-basic.sh b/test/shell/devicesfile-basic.sh
+index d1cfb6a35..2d197a73a 100644
+--- a/test/shell/devicesfile-basic.sh
++++ b/test/shell/devicesfile-basic.sh
+@@ -107,6 +107,7 @@ not vgs --devicesfile test.devices $vg2
+ # misspelled override name fails
+ not vgs --devicesfile doesnotexist $vg1
+ not vgs --devicesfile doesnotexist $vg2
++not vgs --devicesfile doesnotexist
+ 
+ # devicesfile and devices cannot be used together
+ not vgs --devicesfile test.devices --devices "$dev1","$dev1" $vg1
+diff --git a/tools/pvcreate.c b/tools/pvcreate.c
+index 71eb060a3..a1ef0e9e1 100644
+--- a/tools/pvcreate.c
++++ b/tools/pvcreate.c
+@@ -144,7 +144,8 @@ int pvcreate(struct cmd_context *cmd, int argc, char **argv)
+ 
+ 	cmd->create_edit_devices_file = 1;
+ 
+-	lvmcache_label_scan(cmd);
++	if (!lvmcache_label_scan(cmd))
++		return_ECMD_FAILED;
+ 
+ 	if (!(handle = init_processing_handle(cmd, NULL))) {
+ 		log_error("Failed to initialize processing handle.");
+diff --git a/tools/pvremove.c b/tools/pvremove.c
+index 2dfdbd016..5c39ee0c7 100644
+--- a/tools/pvremove.c
++++ b/tools/pvremove.c
+@@ -45,7 +45,8 @@ int pvremove(struct cmd_context *cmd, int argc, char **argv)
+ 
+ 	clear_hint_file(cmd);
+ 
+-	lvmcache_label_scan(cmd);
++	if (!lvmcache_label_scan(cmd))
++		return_ECMD_FAILED;
+ 
+ 	/* When forcibly clearing a PV we don't care about a VG lock. */
+ 	if (pp.force == DONT_PROMPT_OVERRIDE)
+diff --git a/tools/pvscan.c b/tools/pvscan.c
+index 50d46051a..bce1fbb40 100644
+--- a/tools/pvscan.c
++++ b/tools/pvscan.c
+@@ -1626,7 +1626,8 @@ static int _pvscan_cache_all(struct cmd_context *cmd, int argc, char **argv,
+ 	 * which we want 'pvscan --cache' to do, and that uses
+ 	 * info from lvmcache, e.g. duplicate pv info.
+ 	 */
+-	lvmcache_label_scan(cmd);
++	if (!lvmcache_label_scan(cmd))
++		return_0;
+ 
+ 	cmd->pvscan_recreate_hints = 0;
+ 	cmd->use_hints = 0;
+diff --git a/tools/toollib.c b/tools/toollib.c
+index 01ba03658..210b3dca5 100644
+--- a/tools/toollib.c
++++ b/tools/toollib.c
+@@ -1601,7 +1601,10 @@ int process_each_label(struct cmd_context *cmd, int argc, char **argv,
+ 
+ 	log_set_report_object_type(LOG_REPORT_OBJECT_TYPE_LABEL);
+ 
+-	lvmcache_label_scan(cmd);
++	if (!lvmcache_label_scan(cmd)) {
++		ret_max = ECMD_FAILED;
++		goto_out;
++	}
+ 
+ 	if (argc) {
+ 		for (; opt < argc; opt++) {
+@@ -2381,8 +2384,13 @@ int process_each_vg(struct cmd_context *cmd,
+ 	 * Scan all devices to populate lvmcache with initial
+ 	 * list of PVs and VGs.
+ 	 */
+-	if (!(read_flags & PROCESS_SKIP_SCAN))
+-		lvmcache_label_scan(cmd);
++	if (!(read_flags & PROCESS_SKIP_SCAN)) {
++		if (!lvmcache_label_scan(cmd)) {
++			ret_max = ECMD_FAILED;
++			goto_out;
++		}
++	}
++
+ 
+ 	/*
+ 	 * A list of all VGs on the system is needed when:
+@@ -3932,7 +3940,10 @@ int process_each_lv(struct cmd_context *cmd,
+ 	 * Scan all devices to populate lvmcache with initial
+ 	 * list of PVs and VGs.
+ 	 */
+-	lvmcache_label_scan(cmd);
++	if (!lvmcache_label_scan(cmd)) {
++		ret_max = ECMD_FAILED;
++		goto_out;
++	}
+ 
+ 	/*
+ 	 * A list of all VGs on the system is needed when:
+@@ -4568,8 +4579,12 @@ int process_each_pv(struct cmd_context *cmd,
+ 		goto_out;
+ 	}
+ 
+-	if (!(read_flags & PROCESS_SKIP_SCAN))
+-		lvmcache_label_scan(cmd);
++	if (!(read_flags & PROCESS_SKIP_SCAN)) {
++		if (!lvmcache_label_scan(cmd)) {
++			ret_max = ECMD_FAILED;
++			goto_out;
++		}
++	}
+ 
+ 	if (!lvmcache_get_vgnameids(cmd, &all_vgnameids, only_this_vgname, 1)) {
+ 		ret_max = ret;
+diff --git a/tools/vgcfgrestore.c b/tools/vgcfgrestore.c
+index e49313d14..9fcba89d4 100644
+--- a/tools/vgcfgrestore.c
++++ b/tools/vgcfgrestore.c
+@@ -132,7 +132,10 @@ int vgcfgrestore(struct cmd_context *cmd, int argc, char **argv)
+ 
+ 	clear_hint_file(cmd);
+ 
+-	lvmcache_label_scan(cmd);
++	if (!lvmcache_label_scan(cmd)) {
++		unlock_vg(cmd, NULL, vg_name);
++		return_ECMD_FAILED;
++	}
+ 
+ 	cmd->handles_unknown_segments = 1;
+ 
+diff --git a/tools/vgcreate.c b/tools/vgcreate.c
+index dde3f1eac..14608777f 100644
+--- a/tools/vgcreate.c
++++ b/tools/vgcreate.c
+@@ -84,7 +84,10 @@ int vgcreate(struct cmd_context *cmd, int argc, char **argv)
+ 
+ 	cmd->create_edit_devices_file = 1;
+ 
+-	lvmcache_label_scan(cmd);
++	if (!lvmcache_label_scan(cmd)) {
++		unlock_vg(cmd, NULL, vp_new.vg_name);
++		return_ECMD_FAILED;
++	}
+ 
+ 	if (lvmcache_vginfo_from_vgname(vp_new.vg_name, NULL)) {
+ 		unlock_vg(cmd, NULL, vp_new.vg_name);
+diff --git a/tools/vgextend.c b/tools/vgextend.c
+index 0856b4c78..fecd6bdd5 100644
+--- a/tools/vgextend.c
++++ b/tools/vgextend.c
+@@ -160,7 +160,8 @@ int vgextend(struct cmd_context *cmd, int argc, char **argv)
+ 
+ 	cmd->edit_devices_file = 1;
+ 
+-	lvmcache_label_scan(cmd);
++	if (!lvmcache_label_scan(cmd))
++		return_ECMD_FAILED;
+ 
+ 	if (!(handle = init_processing_handle(cmd, NULL))) {
+ 		log_error("Failed to initialize processing handle.");
+diff --git a/tools/vgmerge.c b/tools/vgmerge.c
+index 08615cd62..4ed4a8f0b 100644
+--- a/tools/vgmerge.c
++++ b/tools/vgmerge.c
+@@ -72,7 +72,8 @@ static int _vgmerge_single(struct cmd_context *cmd, const char *vg_name_to,
+ 		return ECMD_FAILED;
+ 	}
+ 
+-	lvmcache_label_scan(cmd);
++	if (!lvmcache_label_scan(cmd))
++		return_ECMD_FAILED;
+ 
+ 	if (strcmp(vg_name_to, vg_name_from) > 0)
+ 		lock_vg_from_first = 1;
+diff --git a/tools/vgsplit.c b/tools/vgsplit.c
+index a085ac2ba..9d6534e89 100644
+--- a/tools/vgsplit.c
++++ b/tools/vgsplit.c
+@@ -559,7 +559,8 @@ int vgsplit(struct cmd_context *cmd, int argc, char **argv)
+ 		return ECMD_FAILED;
+ 	}
+ 
+-	lvmcache_label_scan(cmd);
++	if (!lvmcache_label_scan(cmd))
++		return_ECMD_FAILED;
+ 
+ 	if (!(vginfo_to = lvmcache_vginfo_from_vgname(vg_name_to, NULL))) {
+ 		if (!validate_name(vg_name_to)) {
+-- 
+2.34.3
+
diff --git a/SPECS/lvm2.spec b/SPECS/lvm2.spec
index 07615e8..3d7b748 100644
--- a/SPECS/lvm2.spec
+++ b/SPECS/lvm2.spec
@@ -55,7 +55,7 @@
 %global commit 4dc5d4ac7e7a9457ccc46ff04796b347e58bf4da
 %global shortcommit %(c=%{commit}; echo ${c:0:7})
 %endif
-%global rel_suffix .2
+#%%global rel_suffix .test
 
 # Do not reset Release to 1 unless both lvm2 and device-mapper
 # versions are increased together.
@@ -68,7 +68,7 @@ Version: 2.03.14
 %if 0%{?from_snapshot}
 Release: 0.1.20210426git%{shortcommit}%{?dist}%{?rel_suffix}
 %else
-Release: 3%{?dist}%{?rel_suffix}
+Release: 5%{?dist}%{?rel_suffix}
 %endif
 License: GPLv2
 URL: http://sourceware.org/lvm2
@@ -103,9 +103,39 @@ Patch22: 0021-man-add-section-about-static-autoactivation.patch
 Patch23: 0022-lvcreate-include-recent-options.patch
 Patch24: 0023-man-lvmautoactivation-replace-systemctl-with-journal.patch
 Patch25: 0024-make-generate.patch
-# BZ 2100133:
-Patch26: 0025-filter-mpath-use-multipath-blacklist.patch
-Patch27: 0026-filter-mpath-handle-other-wwid-types-in-blacklist.patch
+Patch26: 0025-pvcreate-overwrite-partition-header-with-f.patch
+Patch27: 0026-lvmdevices-check-error-exit-if-update-is-needed.patch
+Patch28: 0027-Revert-pvcreate-overwrite-partition-header-with-f.patch
+Patch29: 0028-devices-exclude-multipath-components-based-on-matchi.patch
+Patch30: 0029-devices-exclude-md-components-when-duplicate-pvs-are.patch
+Patch31: 0030-lvmdevices-fix-checks-when-adding-entries.patch
+Patch32: 0031-lvmdevices-make-deldev-work-for-missing-device.patch
+Patch33: 0032-devices-file-do-not-clear-PVID-of-unread-devices.patch
+Patch34: 0033-man-lvmcache-mention-writecache-memory-usage.patch
+Patch35: 0034-writecache-display-block-size-from-lvs.patch
+Patch36: 0035-devices-simplify-dev_cache_get_by_devt.patch
+Patch37: 0036-devices-drop-incorrect-paths-from-aliases-list.patch
+Patch38: 0037-devices-initial-use-of-existing-option.patch
+Patch39: 0038-devices-fix-dev_name-assumptions.patch
+Patch40: 0039-devices-use-dev-cache-aliases-handling-from-label-sc.patch
+Patch41: 0040-writecache-check-memory-usage.patch
+Patch42: 0041-change-messages-about-filtered-devices.patch
+Patch43: 0042-vgimportdevices-fix-incorrect-deviceidtype-usage.patch
+Patch44: 0043-lvmlockd-return-error-from-vgcreate-init_vg_sanlock.patch
+Patch45: 0044-devices-file-remove-extraneous-unlock-in-vgchange-u.patch
+Patch46: 0045-filter-mpath-use-multipath-blacklist.patch
+Patch47: 0046-improve-description-of-devices-option.patch
+Patch48: 0047-vdo-support-vdosettings.patch
+Patch49: 0048-toollib-fix-segfault-when-handling-selection-with-hi.patch
+Patch50: 0049-devices-file-move-clean-up-after-command-is-run.patch
+Patch51: 0050-devices-file-fail-if-devicesfile-filename-doesn-t-ex.patch
+Patch52: 0051-filter-mpath-handle-other-wwid-types-in-blacklist.patch
+Patch53: 0052-vdo-fix-conversion-of-vdo_slab_size_mb.patch
+Patch54: 0053-filter-mpath-get-wwids-from-sysfs-vpd_pg83.patch
+Patch55: 0054-build-Fix-make-rpm-with-VERSION_DM-without-dash.patch
+Patch56: 0055-make-generate.patch
+# BZ 2090949:
+Patch57: 0056-exit-with-error-when-devicesfile-name-doesn-t-exist.patch
 
 BuildRequires: gcc
 %if %{enable_testsuite}
@@ -193,6 +223,36 @@ or more physical volumes and creating one or more logical volumes
 %patch25 -p1 -b .backup25
 %patch26 -p1 -b .backup26
 %patch27 -p1 -b .backup27
+%patch28 -p1 -b .backup28
+%patch29 -p1 -b .backup29
+%patch30 -p1 -b .backup30
+%patch31 -p1 -b .backup31
+%patch32 -p1 -b .backup32
+%patch33 -p1 -b .backup33
+%patch34 -p1 -b .backup34
+%patch35 -p1 -b .backup35
+%patch36 -p1 -b .backup36
+%patch37 -p1 -b .backup37
+%patch38 -p1 -b .backup38
+%patch39 -p1 -b .backup39
+%patch40 -p1 -b .backup40
+%patch41 -p1 -b .backup41
+%patch42 -p1 -b .backup42
+%patch43 -p1 -b .backup43
+%patch44 -p1 -b .backup44
+%patch45 -p1 -b .backup45
+%patch46 -p1 -b .backup46
+%patch47 -p1 -b .backup47
+%patch48 -p1 -b .backup48
+%patch49 -p1 -b .backup49
+%patch50 -p1 -b .backup50
+%patch51 -p1 -b .backup51
+%patch52 -p1 -b .backup52
+%patch53 -p1 -b .backup53
+%patch54 -p1 -b .backup54
+%patch55 -p1 -b .backup55
+%patch56 -p1 -b .backup56
+%patch57 -p1 -b .backup57
 
 %build
 %global _default_pid_dir /run
@@ -811,11 +871,11 @@ An extensive functional testsuite for LVM2.
 %endif
 
 %changelog
-* Tue Jul 12 2022 Marian Csontos <mcsontos@redhat.com> - 2.03.14-3.el8_6.2
-- Fix packaging.
+* Thu Jul 14 2022 Marian Csontos <mcsontos@redhat.com> - 2.03.14-5
+- Exit with error when --devicesfile used does not exist.
 
-* Tue Jun 28 2022 Marian Csontos <mcsontos@redhat.com> - 2.03.14-3.el8_6.1
-- Fix filter not respecting blacklisted multipath devices.
+* Wed Jun 15 2022 Marian Csontos <mcsontos@redhat.com> - 2.03.14-4
+- Additional important fixes from upstream up to commit 3b0f9ce.
 
 * Tue Jan 04 2022 Marian Csontos <mcsontos@redhat.com> - 2.03.14-3
 - Fix devices file and autoactivation related issues.