Compare commits

...

No commits in common. "c8" and "c10s" have entirely different histories.
c8 ... c10s

266 changed files with 18023 additions and 16817 deletions

View File

@ -1 +0,0 @@
b52c2be340449664f0a122070838f6d8edd42e4a SOURCES/multipath-tools-0.8.4.tgz

1
.fmf/version Normal file
View File

@ -0,0 +1 @@
1

34
.gitignore vendored
View File

@ -1 +1,33 @@
SOURCES/multipath-tools-0.8.4.tgz
multipath-tools-091027.tar.gz
/multipath-tools-120123.tgz
/multipath-tools-120613.tgz
/multipath-tools-120821.tgz
/multipath-tools-130222.tgz
/multipath-tools-f21166a.tgz
/multipath.conf
/multipath-tools-git847cc43.tgz
/multipath-tools-0.7.3.tgz
/multipath-tools-07e7bd5.tgz
/multipath-tools-1cb704b.tgz
/multipath-tools-0.7.7.tgz
/multipath-tools-ef6d98b.tgz
/multipath-tools-1a8625a.tgz
/multipath-tools-b80318b.tgz
/multipath-tools-0.7.8.tgz
/multipath-tools-0.7.9.tgz
/multipath-tools-17a6101.tgz
/multipath-tools-2df6110.tgz
/multipath-tools-0.8.0.tgz
/multipath-tools-0.8.2.tgz
/multipath-tools-0.8.4.tgz
/multipath-tools-0.8.5.tgz
/multipath-tools-0.8.6.tgz
/multipath-tools-0.8.7.tgz
/multipath-tools-0.8.9.tgz
/multipath-tools-0.9.0.tgz
/multipath-tools-0.9.3.tgz
/multipath-tools-0.9.4.tgz
/multipath-tools-0.9.5.tgz
/multipath-tools-0.9.6.tgz
/multipath-tools-0.9.7.tgz
/multipath-tools-0.9.9.tgz

View File

@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 5 Jun 2024 19:22:48 -0400
Subject: [PATCH] multipathd: fix flush check in flush_map()
Forgot the comparison in the "if" statement.
Fixes 8a3898339 ("multipathd: sync features on flush_map failure corner case")
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
multipathd/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 09286dd0..58afe14a 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -813,7 +813,7 @@ flush_map(struct multipath * mpp, struct vectors * vecs)
{
int r = dm_suspend_and_flush_map(mpp->alias, 0);
if (r != DM_FLUSH_OK) {
- if (DM_FLUSH_FAIL_CANT_RESTORE)
+ if (r == DM_FLUSH_FAIL_CANT_RESTORE)
remove_feature(&mpp->features, "queue_if_no_path");
condlog(0, "%s: can't flush", mpp->alias);
return r;

View File

@ -0,0 +1,63 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 13 Apr 2017 07:22:23 -0500
Subject: [PATCH] RH: fixup udev rules for redhat
The multipath rules need to run after scsi_id is run. This means moving
them after 60-persistent-storage.rules for redhat. Redhat also uses a
different naming scheme for partitions than SuSE.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile.inc | 2 +-
kpartx/kpartx.rules | 2 +-
multipath/Makefile | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Makefile.inc b/Makefile.inc
index 81b86cd8..33dbb99c 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -34,7 +34,7 @@ endif
# Paths. All these can be overridden on the "make" command line.
prefix :=
# Prefix for binaries
-exec_prefix := $(prefix)
+exec_prefix := $(prefix)/usr
# Prefix for non-essential libraries (libdmmp)
usr_prefix := $(if $(prefix),$(prefix),/usr)
# Prefix for configuration files (multipath.conf)
diff --git a/kpartx/kpartx.rules b/kpartx/kpartx.rules
index 8dd3369c..7c3c7524 100644
--- a/kpartx/kpartx.rules
+++ b/kpartx/kpartx.rules
@@ -39,6 +39,6 @@ LABEL="mpath_kpartx_end"
GOTO="kpartx_end"
LABEL="run_kpartx"
-RUN+="/sbin/kpartx -un -p -part /dev/$name"
+RUN+="/sbin/kpartx -un /dev/$name"
LABEL="kpartx_end"
diff --git a/multipath/Makefile b/multipath/Makefile
index 67fb5e62..2ea9e528 100644
--- a/multipath/Makefile
+++ b/multipath/Makefile
@@ -27,7 +27,7 @@ install:
$(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir)
$(Q)$(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir)
$(Q)$(INSTALL_PROGRAM) -m 644 99-z-dm-mpath-late.rules $(DESTDIR)$(udevrulesdir)
- $(Q)$(INSTALL_PROGRAM) -m 644 multipath.rules $(DESTDIR)$(udevrulesdir)/56-multipath.rules
+ $(Q)$(INSTALL_PROGRAM) -m 644 multipath.rules $(DESTDIR)$(udevrulesdir)/62-multipath.rules
$(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(tmpfilesdir)
$(Q)$(INSTALL_PROGRAM) -m 644 tmpfiles.conf $(DESTDIR)$(tmpfilesdir)/multipath.conf
$(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir)/man8
@@ -50,7 +50,7 @@ uninstall:
$(Q)$(RM) $(DESTDIR)$(udevrulesdir)/99-z-dm-mpath-late.rules
$(Q)$(RM) $(DESTDIR)$(modulesloaddir)/multipath.conf
$(Q)$(RM) $(DESTDIR)$(modulesloaddir)/scsi_dh.conf
- $(Q)$(RM) $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules
+ $(Q)$(RM) $(DESTDIR)$(libudevdir)/rules.d/62-multipath.rules
$(Q)$(RM) $(DESTDIR)$(mandir)/man8/$(EXEC).8
$(Q)$(RM) $(DESTDIR)$(mandir)/man5/$(EXEC).conf.5
$(Q)$(RM) $(DESTDIR)$(tmpfilesdir)/multipath.conf

View File

@ -13,29 +13,25 @@ it.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/blacklist.c | 9 ++-------
multipath/multipath.conf.5 | 11 ++++++-----
tests/blacklist.c | 6 ++----
3 files changed, 10 insertions(+), 16 deletions(-)
libmultipath/blacklist.c | 5 ++---
multipath/multipath.conf.5.in | 11 ++++++-----
tests/blacklist.c | 7 ++-----
3 files changed, 10 insertions(+), 13 deletions(-)
diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c
index 00e8dbdb..d9691b17 100644
index 75100b20..0b212078 100644
--- a/libmultipath/blacklist.c
+++ b/libmultipath/blacklist.c
@@ -204,12 +204,6 @@ setup_default_blist (struct config * conf)
if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
return 1;
@@ -230,8 +230,6 @@ setup_default_blist (struct config * conf)
ORIGIN_DEFAULT))
return 1;
}
- if (store_ble(conf->elist_property, "(SCSI_IDENT_|ID_WWN)", ORIGIN_DEFAULT))
- return 1;
- str = STRDUP("(SCSI_IDENT_|ID_WWN)");
- if (!str)
- return 1;
- if (store_ble(conf->elist_property, str, ORIGIN_DEFAULT))
- return 1;
-
vector_foreach_slot (conf->hwtable, hwe, i) {
if (hwe->bl_product) {
if (find_blacklist_device(conf->blist_device,
@@ -411,7 +405,8 @@ filter_property(struct config *conf, struct udev_device *udev, int lvl,
@@ -438,7 +436,8 @@ filter_property(const struct config *conf, struct udev_device *udev,
*uid_attribute != '\0';
bool uid_attr_seen = false;
@ -45,11 +41,11 @@ index 00e8dbdb..d9691b17 100644
udev_list_entry_foreach(list_entry,
udev_device_get_properties_list_entry(udev)) {
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
index 05a5e8ff..3455b1cc 100644
--- a/multipath/multipath.conf.5
+++ b/multipath/multipath.conf.5
@@ -1286,9 +1286,14 @@ keywords. Both are regular expressions. For a full description of these keywords
diff --git a/multipath/multipath.conf.5.in b/multipath/multipath.conf.5.in
index dacb9b0e..645e8f88 100644
--- a/multipath/multipath.conf.5.in
+++ b/multipath/multipath.conf.5.in
@@ -1468,9 +1468,14 @@ keywords. Both are regular expressions. For a full description of these keywords
Regular expression for an udev property. All
devices that have matching udev properties will be excluded/included.
The handling of the \fIproperty\fR keyword is special,
@ -65,7 +61,7 @@ index 05a5e8ff..3455b1cc 100644
.
.RS
.PP
@@ -1299,10 +1304,6 @@ Blacklisting by missing properties is only applied to devices which do have the
@@ -1481,10 +1486,6 @@ Blacklisting by missing properties is only applied to devices which do have the
property specified by \fIuid_attribute\fR (e.g. \fIID_SERIAL\fR)
set. Previously, it was applied to every device, possibly causing devices to be
blacklisted because of temporary I/O error conditions.
@ -77,19 +73,21 @@ index 05a5e8ff..3455b1cc 100644
.TP
.B protocol
diff --git a/tests/blacklist.c b/tests/blacklist.c
index 6e7c1864..cc8a9a4a 100644
index ba8dfd07..693db3fa 100644
--- a/tests/blacklist.c
+++ b/tests/blacklist.c
@@ -271,7 +271,7 @@ static void test_property_missing(void **state)
@@ -384,9 +384,8 @@ static void test_property_missing(void **state)
{
static struct udev_device udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", "ID_SERIAL", NULL } };
conf.blist_property = blist_property_wwn;
expect_condlog(3, "sdb: blacklisted, udev property missing\n");
- expect_condlog(3, "sdb: blacklisted, udev property missing\n");
assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
- MATCH_PROPERTY_BLIST_MISSING);
+ MATCH_NOTHING);
assert_int_equal(filter_property(&conf, &udev, 3, "ID_BLAH"),
MATCH_NOTHING);
assert_int_equal(filter_property(&conf, &udev, 3, ""),
@@ -363,9 +363,7 @@ static void test_filter_path_missing1(void **state)
@@ -478,9 +477,7 @@ static void test_filter_path_missing1(void **state)
conf.blist_device = blist_device_foo_bar;
conf.blist_protocol = blist_protocol_fcp;
conf.blist_wwid = blist_wwid_xyzzy;
@ -100,6 +98,3 @@ index 6e7c1864..cc8a9a4a 100644
}
/* This one matches the property whitelist, to test the other missing
--
2.17.2

View File

@ -0,0 +1,137 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 15 Oct 2014 10:39:30 -0500
Subject: [PATCH] RH: don't start without a config file
If /etc/multipath.conf doesn't exist, don't start multipathd and blacklist
all devices when running multipath. A completely blank configuration file
is almost never what users want. Also, people may have the multipath
packages installed but don't want to use them. This patch provides a
simple way to disable multipath. Simply removing or renaming
/etc/multipath.conf will keep multipath from doing anything.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/config.c | 13 +++++++++++++
libmultipath/config.h | 1 +
multipath/main.c | 6 ++++++
multipath/multipath.rules.in | 1 +
multipathd/multipathd.8.in | 2 ++
multipathd/multipathd.service.in | 1 +
multipathd/multipathd.socket | 1 +
7 files changed, 25 insertions(+)
diff --git a/libmultipath/config.c b/libmultipath/config.c
index 83fa7369..002027a7 100644
--- a/libmultipath/config.c
+++ b/libmultipath/config.c
@@ -959,6 +959,19 @@ int _init_config (const char *file, struct config *conf)
}
factorize_hwtable(conf->hwtable, builtin_hwtable_size, file);
validate_pctable(conf->overrides, 0, file);
+ } else {
+ condlog(0, "/etc/multipath.conf does not exist, blacklisting all devices.");
+ if (conf->blist_devnode == NULL) {
+ conf->blist_devnode = vector_alloc();
+ if (!conf->blist_devnode) {
+ condlog(0, "cannot allocate blacklist\n");
+ goto out;
+ }
+ }
+ if (store_ble(conf->blist_devnode, ".*", ORIGIN_NO_CONFIG)) {
+ condlog(0, "cannot store default no-config blacklist\n");
+ goto out;
+ }
}
conf->processed_main_config = 1;
diff --git a/libmultipath/config.h b/libmultipath/config.h
index 384193ab..158cebf0 100644
--- a/libmultipath/config.h
+++ b/libmultipath/config.h
@@ -10,6 +10,7 @@
#define ORIGIN_DEFAULT 0
#define ORIGIN_CONFIG 1
+#define ORIGIN_NO_CONFIG 2
enum devtypes {
DEV_NONE,
diff --git a/multipath/main.c b/multipath/main.c
index ce702e7f..c21e3e0b 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -842,11 +842,14 @@ main (int argc, char *argv[])
char *dev = NULL;
struct config *conf;
bool enable_foreign = false;
+ bool have_config;
+ struct stat buf;
libmultipath_init();
if (atexit(dm_lib_exit) || atexit(libmultipath_exit))
condlog(1, "failed to register cleanup handler for libmultipath: %m");
logsink = LOGSINK_STDERR_WITH_TIME;
+ have_config = (stat(DEFAULT_CONFIGFILE, &buf) == 0);
if (init_config(DEFAULT_CONFIGFILE))
exit(RTVL_FAIL);
if (atexit(uninit_config))
@@ -1097,6 +1100,9 @@ main (int argc, char *argv[])
while ((r = configure(conf, cmd, dev_type, dev)) == RTVL_RETRY)
condlog(3, "restart multipath configuration process");
+ if (!have_config && r == RTVL_OK &&
+ (cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG))
+ r = RTVL_FAIL;
out:
put_multipath_config(conf);
if (dev)
diff --git a/multipath/multipath.rules.in b/multipath/multipath.rules.in
index 780bf852..2c518378 100644
--- a/multipath/multipath.rules.in
+++ b/multipath/multipath.rules.in
@@ -9,6 +9,7 @@ IMPORT{cmdline}="nompath"
ENV{nompath}=="?*", GOTO="end_mpath"
IMPORT{cmdline}="multipath"
ENV{multipath}=="off", GOTO="end_mpath"
+TEST!="/etc/multipath.conf", GOTO="end_mpath"
ENV{DEVTYPE}!="partition", GOTO="test_dev"
IMPORT{parent}="DM_MULTIPATH_DEVICE_PATH"
diff --git a/multipathd/multipathd.8.in b/multipathd/multipathd.8.in
index 7bc8806e..315884eb 100644
--- a/multipathd/multipathd.8.in
+++ b/multipathd/multipathd.8.in
@@ -49,6 +49,8 @@ map regains its maximum performance and redundancy.
With the \fB-k\fR option, \fBmultipathd\fR acts as a client utility that
sends commands to a running instance of the multipathd daemon (see
\fBCOMMANDS\fR below).
+
+In this Linux distribution, multipathd does not run unless a /etc/multipath.conf file exists.
.
.
.\" ----------------------------------------------------------------------------
diff --git a/multipathd/multipathd.service.in b/multipathd/multipathd.service.in
index a63ddd9a..01ceff7d 100644
--- a/multipathd/multipathd.service.in
+++ b/multipathd/multipathd.service.in
@@ -6,6 +6,7 @@ Wants=systemd-udevd-kernel.socket @MODPROBE_UNIT@
After=systemd-udevd-kernel.socket @MODPROBE_UNIT@
After=multipathd.socket systemd-remount-fs.service
Before=initrd-cleanup.service
+ConditionPathExists=/etc/multipath.conf
DefaultDependencies=no
Conflicts=shutdown.target
Conflicts=initrd-cleanup.service
diff --git a/multipathd/multipathd.socket b/multipathd/multipathd.socket
index 6a62f5fd..263b6b0c 100644
--- a/multipathd/multipathd.socket
+++ b/multipathd/multipathd.socket
@@ -1,6 +1,7 @@
[Unit]
Description=multipathd control socket
DefaultDependencies=no
+ConditionPathExists=/etc/multipath.conf
ConditionKernelCommandLine=!nompath
ConditionKernelCommandLine=!multipath=off
ConditionVirtualization=!container

View File

@ -1,7 +1,10 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jan 2019 14:54:56 -0600
Subject: [PATCH] RH: Fix nvme compilation warning
Subject: [PATCH] RH: Fix nvme function missing argument
A future patch will change the compilation options to error when
function declarations have unspecified arguments.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
@ -21,6 +24,3 @@ index adb192b6..bfd10ef8 100644
void argconfig_append_usage(const char *str);
void argconfig_print_help(const char *program_desc,
const struct argconfig_commandline_options *options);
--
2.17.2

View File

@ -0,0 +1,55 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 19 Apr 2017 06:10:01 -0500
Subject: [PATCH] RH: use rpm optflags if present
Use the passed in optflags when compiling as an RPM, and keep the
default flags as close as possible to the current fedora flags, while
still being generic.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile.inc | 22 +++++++++++++++++-----
1 file changed, 17 insertions(+), 5 deletions(-)
diff --git a/Makefile.inc b/Makefile.inc
index 33dbb99c..94e0ec85 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -95,11 +95,23 @@ SYSTEMD_LIBDEPS := $(if $(SYSTEMD),$(if $(shell test $(SYSTEMD) -gt 209 && echo
MODPROBE_UNIT := $(shell test "0$(SYSTEMD)" -lt 245 2>/dev/null || \
echo "modprobe@dm_multipath.service")
-OPTFLAGS := -O2 -g $(STACKPROT) --param=ssp-buffer-size=4
-WARNFLAGS := -Werror -Wall -Wextra -Wformat=2 $(WFORMATOVERFLOW) -Werror=implicit-int \
+ifndef RPM_OPT_FLAGS
+ OPTFLAGS := -O2 -g $(STACKPROT) --param=ssp-buffer-size=4 \
+ -Wall $(FORTIFY_OPT) -fexceptions -grecord-gcc-switches \
+ -fasynchronous-unwind-tables
+ ifeq ($(shell test -f /usr/lib/rpm/redhat/redhat-hardened-cc1 && echo 1),1)
+ OPTFLAGS += -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1
+ endif
+ ifeq ($(shell test -f /usr/lib/rpm/redhat/redhat-annobin-cc1 && echo 1),1)
+ OPTFLAGS += -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1
+ endif
+else
+ OPTFLAGS := $(RPM_OPT_FLAGS) --param=ssp-buffer-size=4
+endif
+WARNFLAGS := -Werror -Wextra -Wformat=2 $(WFORMATOVERFLOW) -Werror=implicit-int \
-Werror=implicit-function-declaration -Werror=format-security \
- $(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) $(W_URCU_TYPE_LIMITS)
-CPPFLAGS := $(FORTIFY_OPT) $(CPPFLAGS) $(D_URCU_VERSION) \
+ $(WNOCLOBBERED) -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) $(W_URCU_TYPE_LIMITS) -Wstrict-prototypes
+CPPFLAGS := $(CPPFLAGS) $(D_URCU_VERSION) \
-D_FILE_OFFSET_BITS=64 \
-DBIN_DIR=\"$(bindir)\" -DMULTIPATH_DIR=\"$(TGTDIR)$(plugindir)\" \
-DRUNTIME_DIR=\"$(runtimedir)\" -DCONFIG_DIR=\"$(TGTDIR)$(configdir)\" \
@@ -109,7 +121,7 @@ CFLAGS := -std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe
BIN_CFLAGS := -fPIE -DPIE
LIB_CFLAGS := -fPIC
SHARED_FLAGS := -shared
-LDFLAGS := $(LDFLAGS) -Wl,-z,relro -Wl,-z,now -Wl,-z,defs
+LDFLAGS := $(LDFLAGS) $(RPM_LD_FLAGS) -Wl,-z,relro -Wl,-z,now -Wl,-z,defs
BIN_LDFLAGS := -pie
# Source code directories. Don't modify.

View File

@ -10,22 +10,23 @@ command line. But, mostly it is used to get a multipath.conf file
with the OS defaults, and to enable and disable multipathing via
a single command.
Co-authored-by: Paul Donohue <git@PaulSD.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/config.c | 2 +
multipath/Makefile | 5 +
multipath/mpathconf | 555 ++++++++++++++++++++++++++++++++++++++++++
multipath/mpathconf.8 | 135 ++++++++++
4 files changed, 697 insertions(+)
multipath/Makefile | 4 +
multipath/mpathconf | 658 ++++++++++++++++++++++++++++++++++++++++++
multipath/mpathconf.8 | 151 ++++++++++
4 files changed, 815 insertions(+)
create mode 100644 multipath/mpathconf
create mode 100644 multipath/mpathconf.8
diff --git a/libmultipath/config.c b/libmultipath/config.c
index b36778b0..26f8e050 100644
index 002027a7..3d5943d3 100644
--- a/libmultipath/config.c
+++ b/libmultipath/config.c
@@ -781,6 +781,8 @@ load_config (char * file)
factorize_hwtable(conf->hwtable, builtin_hwtable_size, file);
@@ -961,6 +961,8 @@ int _init_config (const char *file, struct config *conf)
validate_pctable(conf->overrides, 0, file);
} else {
condlog(0, "/etc/multipath.conf does not exist, blacklisting all devices.");
+ condlog(0, "You can run \"/sbin/mpathconf --enable\" to create");
@ -34,45 +35,46 @@ index b36778b0..26f8e050 100644
conf->blist_devnode = vector_alloc();
if (!conf->blist_devnode) {
diff --git a/multipath/Makefile b/multipath/Makefile
index b9bbb3cf..e720c7f6 100644
index 2ea9e528..3dc241cc 100644
--- a/multipath/Makefile
+++ b/multipath/Makefile
@@ -18,10 +18,12 @@ $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
$(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(LIBDEPS)
$(GZIP) $(EXEC).8 > $(EXEC).8.gz
$(GZIP) $(EXEC).conf.5 > $(EXEC).conf.5.gz
+ $(GZIP) mpathconf.8 > mpathconf.8.gz
@@ -24,6 +24,7 @@ $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
install:
$(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
+ $(INSTALL_PROGRAM) -m 755 mpathconf $(DESTDIR)$(bindir)/
$(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir)
$(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir)
$(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(libudevdir)/rules.d/62-multipath.rules
@@ -29,13 +31,16 @@ install:
$(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir)
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir)
$(INSTALL_PROGRAM) -m 644 $(EXEC).conf.5.gz $(DESTDIR)$(man5dir)
+ $(INSTALL_PROGRAM) -m 644 mpathconf.8.gz $(DESTDIR)$(man8dir)
$(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
$(Q)$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
+ $(Q)$(INSTALL_PROGRAM) -m 755 mpathconf $(DESTDIR)$(bindir)/
$(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir)
$(Q)$(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir)
$(Q)$(INSTALL_PROGRAM) -m 644 99-z-dm-mpath-late.rules $(DESTDIR)$(udevrulesdir)
@@ -32,6 +33,7 @@ install:
$(Q)$(INSTALL_PROGRAM) -m 644 tmpfiles.conf $(DESTDIR)$(tmpfilesdir)/multipath.conf
$(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir)/man8
$(Q)$(INSTALL_PROGRAM) -m 644 $(EXEC).8 $(DESTDIR)$(mandir)/man8
+ $(Q)$(INSTALL_PROGRAM) -m 644 mpathconf.8 $(DESTDIR)$(mandir)/man8
$(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(mandir)/man5
$(Q)$(INSTALL_PROGRAM) -m 644 $(EXEC).conf.5 $(DESTDIR)$(mandir)/man5
$(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(modulesloaddir)
@@ -46,12 +48,14 @@ endif
uninstall:
$(RM) $(DESTDIR)$(bindir)/$(EXEC)
$(RM) $(DESTDIR)$(udevrulesdir)/11-dm-mpath.rules
$(RM) $(DESTDIR)$(libudevdir)/rules.d/62-multipath.rules
+ $(RM) $(DESTDIR)$(bindir)/mpathconf
$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
$(RM) $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz
+ $(RM) $(DESTDIR)$(man8dir)/mpathconf.8.gz
$(Q)$(RM) $(DESTDIR)$(bindir)/$(EXEC)
+ $(Q)$(RM) $(DESTDIR)$(bindir)/mpathconf
$(Q)$(RM) $(DESTDIR)$(udevrulesdir)/11-dm-mpath.rules
$(Q)$(RM) $(DESTDIR)$(udevrulesdir)/99-z-dm-mpath-late.rules
$(Q)$(RM) $(DESTDIR)$(modulesloaddir)/multipath.conf
$(Q)$(RM) $(DESTDIR)$(modulesloaddir)/scsi_dh.conf
$(Q)$(RM) $(DESTDIR)$(libudevdir)/rules.d/62-multipath.rules
$(Q)$(RM) $(DESTDIR)$(mandir)/man8/$(EXEC).8
+ $(Q)$(RM) $(DESTDIR)$(mandir)/man8/mpathconf.8
$(Q)$(RM) $(DESTDIR)$(mandir)/man5/$(EXEC).conf.5
$(Q)$(RM) $(DESTDIR)$(tmpfilesdir)/multipath.conf
clean: dep_clean
$(RM) core *.o $(EXEC) *.gz
diff --git a/multipath/mpathconf b/multipath/mpathconf
new file mode 100644
index 00000000..f34003c9
index 00000000..ce430075
--- /dev/null
+++ b/multipath/mpathconf
@@ -0,0 +1,555 @@
@@ -0,0 +1,658 @@
+#!/bin/bash
+#
+# Copyright (C) 2010 Red Hat, Inc. All rights reserved.
@ -92,7 +94,7 @@ index 00000000..f34003c9
+# This program was largely ripped off from lvmconf
+#
+
+unset ENABLE FIND FRIENDLY PROPERTY FOREIGN MODULE MULTIPATHD HAVE_DISABLE HAVE_WWID_DISABLE HAVE_FIND HAVE_BLACKLIST HAVE_EXCEPTIONS HAVE_DEFAULTS HAVE_FRIENDLY HAVE_PROPERTY HAVE_FOREIGN HAVE_MULTIPATHD HAVE_MODULE HAVE_OUTFILE SHOW_STATUS CHANGED_CONFIG WWID_LIST
+unset ENABLE FIND FRIENDLY PROPERTY FOREIGN MODULE MULTIPATHD HAVE_DISABLE HAVE_WWID_DISABLE HAVE_FIND HAVE_BLACKLIST HAVE_EXCEPTIONS HAVE_DEFAULTS HAVE_FRIENDLY HAVE_PROPERTY HAVE_FOREIGN HAVE_MULTIPATHD HAVE_MODULE HAVE_OUTFILE SHOW_STATUS CHANGED_CONFIG WWID_LIST HAVE_OPTION OPTION_NAME OPTION_VALUE HAVE_RECHECK_WWID RECHECK_WWID
+
+DEFAULT_CONFIG="# device-mapper-multipath configuration file
+
@ -106,12 +108,7 @@ index 00000000..f34003c9
+
+defaults {
+ user_friendly_names yes
+ find_multipaths yes
+ enable_foreign \"^$\"
+}
+
+blacklist_exceptions {
+ property \"(SCSI_IDENT_|ID_WWN)\"
+ find_multipaths on
+}"
+
+CONFIGFILE="/etc/multipath.conf"
@ -129,9 +126,11 @@ index 00000000..f34003c9
+ echo "Disable: --disable"
+ echo "Only allow certain wwids (instead of enable): --allow <WWID>"
+ echo "Set user_friendly_names (Default y): --user_friendly_names <y|n>"
+ echo "Set find_multipaths (Default y): --find_multipaths <y|n>"
+ echo "Set default property blacklist (Default y): --property_blacklist <y|n>"
+ echo "Set find_multipaths (Default on): --find_multipaths <on|yes|y|off|no|n|strict|greedy|smart>"
+ echo "Set default property blacklist (Default n): --property_blacklist <y|n>"
+ echo "Set enable_foreign to show foreign devices (Default n): --enable_foreign <y|n>"
+ echo "Set recheck_wwid (Defaut n): --recheck_wwid <y|n>"
+ echo "Add/Change/Remove option in defaults section: --option <option_name>:<value>"
+ echo "Load the dm-multipath modules on enable (Default y): --with_module <y|n>"
+ echo "start/stop/reload multipathd (Default n): --with_multipathd <y|n>"
+ echo "select output file (Default /etc/multipath.conf): --outfile <FILE>"
@ -224,6 +223,15 @@ index 00000000..f34003c9
+ exit 1
+ fi
+ ;;
+ --recheck_wwid)
+ if [ -n "$2" ]; then
+ RECHECK_WWID=$2
+ shift 2
+ else
+ usage
+ exit 1
+ fi
+ ;;
+ --find_multipaths)
+ if [ -n "$2" ]; then
+ FIND=$2
@ -242,6 +250,20 @@ index 00000000..f34003c9
+ exit 1
+ fi
+ ;;
+ --option)
+ if [ -n "$2" ]; then
+ OPTION_NAME=$(echo $2 | cut -s -f1 -d:)
+ OPTION_VALUE=$(echo $2 | cut -s -f2 -d:)
+ if [ -z "$OPTION_NAME" ]; then
+ usage
+ exit 1
+ fi
+ shift 2
+ else
+ usage
+ exit 1
+ fi
+ ;;
+ --enable_foreign)
+ if [ -n "$2" ]; then
+ FOREIGN=$2
@ -288,19 +310,31 @@ index 00000000..f34003c9
+
+function validate_args
+{
+ if [ "$ENABLE" = "0" ] && [ -n "$FRIENDLY" -o -n "$FIND" -o -n "$PROPERTY" -o -n "$MODULE" ]; then
+ if [ "$ENABLE" = "0" ] && [ -n "$FRIENDLY" -o -n "$FIND" -o -n "$PROPERTY" -o -n "$MODULE" -o -n "$FOREIGN" -o -n "$OPTION_NAME" -o -n "$RECHECK_WWID" ]; then
+ echo "ignoring extra parameters on disable"
+ FRIENDLY=""
+ FIND=""
+ PROPERTY=""
+ MODULE=""
+ FOREIGN=""
+ OPTION_NAME=""
+ OPTION_VALUE=""
+ RECHECK_WWID=""
+ fi
+ if [ -n "$FRIENDLY" ] && [ "$FRIENDLY" != "y" -a "$FRIENDLY" != "n" ]; then
+ echo "--user_friendly_names must be either 'y' or 'n'"
+ exit 1
+ fi
+ if [ -n "$FIND" ] && [ "$FIND" != "y" -a "$FIND" != "n" ]; then
+ echo "--find_multipaths must be either 'y' or 'n'"
+ if [ -n "$RECHECK_WWID" ] && [ "$RECHECK_WWID" != "y" -a "$RECHECK_WWID" != "n" ]; then
+ echo "--recheck_wwid must be either 'y' or 'n'"
+ exit 1
+ fi
+ if [ "$FIND" = "y" ]; then
+ FIND="on"
+ elif [ "$FIND" = "n" ]; then
+ FIND="off"
+ elif [ -n "$FIND" ] && [ "$FIND" != "on" -a "$FIND" != "yes" -a "$FIND" != "off" -a "$FIND" != "no" -a "$FIND" != "strict" -a "$FIND" != "greedy" -a "$FIND" != "smart" ]; then
+ echo "--find_multipaths must be one of 'on' 'yes' 'y' 'off' 'no' 'n' 'strict' 'greedy' or 'smart'"
+ exit 1
+ fi
+ if [ -n "$PROPERTY" ] && [ "$PROPERTY" != "y" -a "$PROPERTY" != "n" ]; then
@ -311,7 +345,19 @@ index 00000000..f34003c9
+ echo "--enable_foreign must be either 'y' or 'n'"
+ exit 1
+ fi
+ if [ -z "$ENABLE" -a -z "$FIND" -a -z "$FRIENDLY" -a -z "$PROPERTY" ]; then
+ if [ -n "$OPTION_NAME" ]; then
+ if [[ $OPTION_NAME =~ [[:space:]]|#|\"|!|\{|\} ]]; then
+ echo "--option name \"$OPTION_NAME\" is invalid"
+ exit 1
+ elif [[ $OPTION_VALUE =~ \"|#|!|\{|\} ]]; then
+ echo "--option value \"$OPTION_VALUE\" is invalid"
+ exit 1
+ fi
+ if [[ $OPTION_VALUE =~ [[:space:]] ]]; then
+ OPTION_VALUE=\"$OPTION_VALUE\"
+ fi
+ fi
+ if [ -z "$ENABLE" -a -z "$FIND" -a -z "$FRIENDLY" -a -z "$PROPERTY" -a -z "$FOREIGN" -a -z "$OPTION_NAME" -a -z "$RECHECK_WWID" ]; then
+ SHOW_STATUS=1
+ fi
+ if [ -n "$MODULE" ] && [ "$MODULE" != "y" -a "$MODULE" != "n" ]; then
@ -386,45 +432,62 @@ index 00000000..f34003c9
+fi
+
+if [ "$HAVE_BLACKLIST" = "1" ]; then
+ if sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*devnode \"\.\?\*\"" ; then
+ if sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*devnode[[:space:]][[:space:]]*\"\.\?\*\"" ; then
+ HAVE_DISABLE=1
+ elif sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*#[[:space:]]*devnode \"\.\?\*\"" ; then
+ elif sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*#[[:space:]]*devnode[[:space:]][[:space:]]*\"\.\?\*\"" ; then
+ HAVE_DISABLE=0
+ fi
+fi
+
+if [ "$HAVE_BLACKLIST" = "1" ]; then
+ if sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*wwid \"\.\?\*\"" ; then
+ if sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*wwid[[:space:]][[:space:]]*\"\.\?\*\"" ; then
+ HAVE_WWID_DISABLE=1
+ elif sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*#[[:space:]]*wwid \"\.\?\*\"" ; then
+ elif sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*#[[:space:]]*wwid[[:space:]][[:space:]]*\"\.\?\*\"" ; then
+ HAVE_WWID_DISABLE=0
+ fi
+fi
+
+if [ "$HAVE_DEFAULTS" = "1" ]; then
+ if sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*find_multipaths[[:space:]]*\(yes\|1\)" ; then
+ HAVE_FIND=1
+ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*find_multipaths[[:space:]]*\(no\|0\)" ; then
+ HAVE_FIND=0
+ HAVE_FIND=`sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | sed -n 's/^[[:blank:]]*find_multipaths[[:blank:]][[:blank:]]*\([^[:blank:]]*\).*$/\1/p' | sed -n 1p`
+ if [ "$HAVE_FIND" = "1" ]; then
+ HAVE_FIND="yes"
+ elif [ "$HAVE_FIND" = "0" ]; then
+ HAVE_FIND="no"
+ fi
+ if sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*user_friendly_names[[:space:]]*\(yes\|1\)" ; then
+ if sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*user_friendly_names[[:space:]][[:space:]]*\(yes\|1\)" ; then
+ HAVE_FRIENDLY=1
+ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*user_friendly_names[[:space:]]*\(no\|0\)" ; then
+ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*user_friendly_names[[:space:]][[:space:]]*\(no\|0\)" ; then
+ HAVE_FRIENDLY=0
+ fi
+ if sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*recheck_wwid[[:space:]][[:space:]]*\(yes\|1\)" ; then
+ HAVE_RECHECK_WWID=1
+ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*recheck_wwid[[:space:]][[:space:]]*\(no\|0\)" ; then
+ HAVE_RECHECK_WWID=0
+ fi
+ if sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*#[[:space:]]*enable_foreign" ; then
+ HAVE_FOREIGN=0
+ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*enable_foreign[[:space:]]*\"\^\$\"" ; then
+ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*enable_foreign[[:space:]][[:space:]]*\"\.\*\"" ; then
+ HAVE_FOREIGN=1
+ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*enable_foreign" ; then
+ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*enable_foreign[[:space:]][[:space:]]*\"\^\$\"" ; then
+ HAVE_FOREIGN=2
+ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*enable_foreign[[:space:]][[:space:]]*\"NONE\"" ; then
+ HAVE_FOREIGN=2
+ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*enable_foreign" ; then
+ HAVE_FOREIGN=3
+ fi
+ if [ -n "$OPTION_NAME" ]; then
+ if sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q '^[[:space:]]*'"$OPTION_NAME"'[[:space:]][[:space:]]*'"$OPTION_VALUE" ; then
+ HAVE_OPTION=1
+ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q '^[[:space:]]*'"$OPTION_NAME"'\([[:space:]].*\)\?$' ; then
+ HAVE_OPTION=0
+ fi
+ fi
+fi
+
+if [ "$HAVE_EXCEPTIONS" = "1" ]; then
+ if sed -n '/^blacklist_exceptions[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*property[[:space:]]*\"(SCSI_IDENT_|ID_WWN)\"" ; then
+ if sed -n '/^blacklist_exceptions[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*property[[:space:]][[:space:]]*\"(SCSI_IDENT_|ID_WWN)\"" ; then
+ HAVE_PROPERTY=1
+ elif sed -n '/^blacklist_exceptions[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*#[[:space:]]*property[[:space:]]*\"(SCSI_IDENT_|ID_WWN)\"" ; then
+ elif sed -n '/^blacklist_exceptions[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*#[[:space:]]*property[[:space:]][[:space:]]*\"(SCSI_IDENT_|ID_WWN)\"" ; then
+ HAVE_PROPERTY=0
+ fi
+fi
@ -435,24 +498,31 @@ index 00000000..f34003c9
+ else
+ echo "multipath is disabled"
+ fi
+ if [ -z "$HAVE_FIND" -o "$HAVE_FIND" = 0 ]; then
+ echo "find_multipaths is disabled"
+ if [ -z "$HAVE_FIND" ]; then
+ echo "find_multipaths is off"
+ else
+ echo "find_multipaths is enabled"
+ echo "find_multipaths is $HAVE_FIND"
+ fi
+ if [ -z "$HAVE_FRIENDLY" -o "$HAVE_FRIENDLY" = 0 ]; then
+ echo "user_friendly_names is disabled"
+ else
+ echo "user_friendly_names is enabled"
+ fi
+ if [ -z "$HAVE_RECHECK_WWID" -o "$HAVE_RECHECK_WWID" = 0 ]; then
+ echo "recheck_wwid is disabled"
+ else
+ echo "recheck_wwid is enabled"
+ fi
+ if [ -z "$HAVE_PROPERTY" -o "$HAVE_PROPERTY" = 0 ]; then
+ echo "default property blacklist is disabled"
+ else
+ echo "default property blacklist is enabled"
+ fi
+ if [ -z "$HAVE_FOREIGN" -o "$HAVE_FOREIGN" = 0 ]; then
+ echo "enable_foreign is not set (all foreign multipath devices will be shown)"
+ echo "enable_foreign is not set (no foreign multipath devices will be shown)"
+ elif [ "$HAVE_FOREIGN" = 1 ]; then
+ echo "enable_foreign is set (all foreign multipath devices will be shown)"
+ elif [ "$HAVE_FOREIGN" = 2 ]; then
+ echo "enable_foreign is set (no foreign multipath devices will be shown)"
+ else
+ echo "enable_foreign is set (foreign multipath devices may not be shown)"
@ -497,14 +567,14 @@ index 00000000..f34003c9
+
+if [ "$ENABLE" = 2 ]; then
+ if [ "$HAVE_DISABLE" = 1 ]; then
+ sed -i '/^blacklist[[:space:]]*{/,/^}/ s/^[[:space:]]*devnode \"\.\?\*\"/# devnode ".*"/' $TMPFILE
+ sed -i '/^blacklist[[:space:]]*{/,/^}/ s/^[[:space:]]*devnode[[:space:]][[:space:]]*\"\.\?\*\"/# devnode ".*"/' $TMPFILE
+ fi
+ if [ -z "$HAVE_WWID_DISABLE" ]; then
+ sed -i '/^blacklist[[:space:]]*{/ a\
+ wwid ".*"
+' $TMPFILE
+ elif [ "$HAVE_WWID_DISABLE" = 0 ]; then
+ sed -i '/^blacklist[[:space:]]*{/,/^}/ s/^[[:space:]]*#[[:space:]]*wwid \"\.\?\*\"/ wwid ".*"/' $TMPFILE
+ sed -i '/^blacklist[[:space:]]*{/,/^}/ s/^[[:space:]]*#[[:space:]]*wwid[[:space:]][[:space:]]*\"\.\?\*\"/ wwid ".*"/' $TMPFILE
+ fi
+ if [ "$HAVE_EXCEPTIONS" = 1 ]; then
+ sed -i '/^blacklist_exceptions[[:space:]]*{/,/^}/ {/^[[:space:]]*wwid/ d}' $TMPFILE
@ -518,7 +588,7 @@ index 00000000..f34003c9
+ add_blacklist_exceptions
+elif [ "$ENABLE" = 1 ]; then
+ if [ "$HAVE_DISABLE" = 1 ]; then
+ sed -i '/^blacklist[[:space:]]*{/,/^}/ s/^[[:space:]]*devnode \"\.\?\*\"/# devnode ".*"/' $TMPFILE
+ sed -i '/^blacklist[[:space:]]*{/,/^}/ s/^[[:space:]]*devnode[[:space:]][[:space:]]*\"\.\?\*\"/# devnode ".*"/' $TMPFILE
+ fi
+elif [ "$ENABLE" = 0 ]; then
+ if [ -z "$HAVE_DISABLE" ]; then
@ -526,30 +596,25 @@ index 00000000..f34003c9
+ devnode ".*"
+' $TMPFILE
+ elif [ "$HAVE_DISABLE" = 0 ]; then
+ sed -i '/^blacklist[[:space:]]*{/,/^}/ s/^[[:space:]]*#[[:space:]]*devnode \"\.\?\*\"/ devnode ".*"/' $TMPFILE
+ sed -i '/^blacklist[[:space:]]*{/,/^}/ s/^[[:space:]]*#[[:space:]]*devnode[[:space:]][[:space:]]*\"\.\?\*\"/ devnode ".*"/' $TMPFILE
+ fi
+fi
+
+if [ "$FIND" = "n" ]; then
+ if [ "$HAVE_FIND" = 1 ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*find_multipaths[[:space:]]*\(yes\|1\)/ find_multipaths no/' $TMPFILE
+ CHANGED_CONFIG=1
+ fi
+elif [ "$FIND" = "y" ]; then
+if [ -n "$FIND" ]; then
+ if [ -z "$HAVE_FIND" ]; then
+ sed -i '/^defaults[[:space:]]*{/ a\
+ find_multipaths yes
+ find_multipaths '"$FIND"'
+' $TMPFILE
+ CHANGED_CONFIG=1
+ elif [ "$HAVE_FIND" = 0 ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*find_multipaths[[:space:]]*\(no\|0\)/ find_multipaths yes/' $TMPFILE
+ elif [ "$FIND" != "$HAVE_FIND" ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:blank:]]*find_multipaths[[:blank:]][[:blank:]]*[^[:blank:]]*/ find_multipaths '"$FIND"'/' $TMPFILE
+ CHANGED_CONFIG=1
+ fi
+fi
+
+if [ "$FRIENDLY" = "n" ]; then
+ if [ "$HAVE_FRIENDLY" = 1 ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*user_friendly_names[[:space:]]*\(yes\|1\)/ user_friendly_names no/' $TMPFILE
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*user_friendly_names[[:space:]][[:space:]]*\(yes\|1\)/ user_friendly_names no/' $TMPFILE
+ CHANGED_CONFIG=1
+ fi
+elif [ "$FRIENDLY" = "y" ]; then
@ -559,45 +624,85 @@ index 00000000..f34003c9
+' $TMPFILE
+ CHANGED_CONFIG=1
+ elif [ "$HAVE_FRIENDLY" = 0 ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*user_friendly_names[[:space:]]*\(no\|0\)/ user_friendly_names yes/' $TMPFILE
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*user_friendly_names[[:space:]][[:space:]]*\(no\|0\)/ user_friendly_names yes/' $TMPFILE
+ CHANGED_CONFIG=1
+ fi
+fi
+
+if [ "$RECHECK_WWID" = "n" ]; then
+ if [ "$HAVE_RECHECK_WWID" = 1 ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*recheck_wwid[[:space:]][[:space:]]*\(yes\|1\)/ recheck_wwid no/' $TMPFILE
+ CHANGED_CONFIG=1
+ fi
+elif [ "$RECHECK_WWID" = "y" ]; then
+ if [ -z "$HAVE_RECHECK_WWID" ]; then
+ sed -i '/^defaults[[:space:]]*{/ a\
+ recheck_wwid yes
+' $TMPFILE
+ CHANGED_CONFIG=1
+ elif [ "$HAVE_RECHECK_WWID" = 0 ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*recheck_wwid[[:space:]][[:space:]]*\(no\|0\)/ recheck_wwid yes/' $TMPFILE
+ CHANGED_CONFIG=1
+ fi
+fi
+
+if [ "$PROPERTY" = "n" ]; then
+ if [ "$HAVE_PROPERTY" = 1 ]; then
+ sed -i '/^blacklist_exceptions[[:space:]]*{/,/^}/ s/^[[:space:]]*property[[:space:]]*\"(SCSI_IDENT_|ID_WWN)\"/# property \"(SCSI_IDENT_|ID_WWN)\"/' $TMPFILE
+ sed -i '/^blacklist_exceptions[[:space:]]*{/,/^}/ s/^[[:space:]]*property[[:space:]][[:space:]]*\"(SCSI_IDENT_|ID_WWN)\"/# property \"(SCSI_IDENT_|ID_WWN)\"/' $TMPFILE
+ CHANGED_CONFIG=1
+ fi
+elif [ "$PROPERTY" = "y" ]; then
+ if [ -z "$HAVE_PROPERTY" ]; then
+ if [ -z "$HAVE_PROPERTY" -a -z "$HAVE_EXCEPTIONS" ]; then
+ cat >> $TMPFILE << _EOF_
+
+blacklist_exceptions {
+ property "(SCSI_IDENT_|ID_WWN)"
+}
+_EOF_
+ CHANGED_CONFIG=1
+ elif [ -z "$HAVE_PROPERTY" ]; then
+ sed -i '/^blacklist_exceptions[[:space:]]*{/ a\
+ property "(SCSI_IDENT_|ID_WWN)"
+' $TMPFILE
+ CHANGED_CONFIG=1
+ elif [ "$HAVE_PROPERTY" = 0 ]; then
+ sed -i '/^blacklist_exceptions[[:space:]]*{/,/^}/ s/^[[:space:]]*#[[:space:]]*property[[:space:]]*\"(SCSI_IDENT_|ID_WWN)\"/ property \"(SCSI_IDENT_|ID_WWN)\"/' $TMPFILE
+ sed -i '/^blacklist_exceptions[[:space:]]*{/,/^}/ s/^[[:space:]]*#[[:space:]]*property[[:space:]][[:space:]]*\"(SCSI_IDENT_|ID_WWN)\"/ property \"(SCSI_IDENT_|ID_WWN)\"/' $TMPFILE
+ CHANGED_CONFIG=1
+ fi
+fi
+
+if [ "$FOREIGN" = "y" ]; then
+ if [ "$HAVE_FOREIGN" = 1 -o "$HAVE_FOREIGN" = 2 ]; then
+if [ "$FOREIGN" = "n" ]; then
+ if [ "$HAVE_FOREIGN" = 1 -o "$HAVE_FOREIGN" = 3 ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*enable_foreign/# enable_foreign/' $TMPFILE
+ CHANGED_CONFIG=1
+ fi
+elif [ "$FOREIGN" = "n" ]; then
+elif [ "$FOREIGN" = "y" ]; then
+ if [ -z "$HAVE_FOREIGN" ]; then
+ sed -i '/^defaults[[:space:]]*{/ a\
+ enable_foreign "^$"
+ enable_foreign ".*"
+' $TMPFILE
+ CHANGED_CONFIG=1
+ elif [ "$HAVE_FOREIGN" = 0 -o "$HAVE_FOREIGN" = 2 ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*#\?[[:space:]]*enable_foreign.*$/ enable_foreign "^$"/' $TMPFILE
+ elif [ "$HAVE_FOREIGN" = 0 -o "$HAVE_FOREIGN" = 2 -o "$HAVE_FOREIGN" = 3 ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*#\?[[:space:]]*enable_foreign.*$/ enable_foreign ".*"/' $TMPFILE
+ CHANGED_CONFIG=1
+ fi
+fi
+
+if [ -n "$OPTION_NAME" -a -n "$OPTION_VALUE" ]; then
+ if [ -z "$HAVE_OPTION" ]; then
+ sed -i '/^defaults[[:space:]]*{/ a\
+ '"$OPTION_NAME"' '"$OPTION_VALUE"'
+' $TMPFILE
+ CHANGED_CONFIG=1
+ elif [ "$HAVE_OPTION" = 0 ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*'"$OPTION_NAME"'\([[:space:]].*\)\?$/ '"$OPTION_NAME"' '"$OPTION_VALUE"'/' $TMPFILE
+ CHANGED_CONFIG=1
+ fi
+elif [ -n "$OPTION_NAME" -a -n "$HAVE_OPTION" ]; then
+ sed -i '/^defaults[[:space:]]*{/,/^}/{/^[[:space:]]*'"$OPTION_NAME"'\([[:space:]].*\)\?$/d}' $TMPFILE
+ CHANGED_CONFIG=1
+fi
+
+if [ -f "$OUTPUTFILE" ]; then
+ cp $OUTPUTFILE $OUTPUTFILE.old
+ if [ $? != 0 ]; then
@ -630,10 +735,10 @@ index 00000000..f34003c9
+fi
diff --git a/multipath/mpathconf.8 b/multipath/mpathconf.8
new file mode 100644
index 00000000..b82961d6
index 00000000..ea025f31
--- /dev/null
+++ b/multipath/mpathconf.8
@@ -0,0 +1,135 @@
@@ -0,0 +1,151 @@
+.TH MPATHCONF 8 "June 2010" "" "Linux Administrator's Manual"
+.SH NAME
+mpathconf - A tool for configuring device-mapper-multipath
@ -674,12 +779,12 @@ index 00000000..b82961d6
+already exists, mpathconf will edit it. If it does not exist, mpathconf will
+create a default file with
+.B user_friendly_names
+and
+set and
+.B find_multipaths
+set. To disable these, use the
+set to \fBon\fP. To disable these, use the
+.B --user_friendly_names n
+and
+.B --find_multipaths n
+.B --find_multipaths off
+options
+.SH COMMANDS
+.TP
@ -713,13 +818,22 @@ index 00000000..b82961d6
+defaults section. If set to \fBn\fP, this removes the line, if present. This
+command can be used along with any other command.
+.TP
+.B --find_multipaths\fP { \fBy\fP | \fBn\fP }
+.B --recheck_wwid \fP { \fBy\fP | \fBn\fP }
+If set to \fBy\fP, this adds the line
+.B find_multipaths yes
+.B recheck_wwid yes
+to the
+.B /etc/multipath.conf
+defaults section. If set to \fBn\fP, this removes the line, if present. This
+command can be used along with any other command.
+defaults section, or sets an existing line to \fByes\fP. If set to \fBn\fP, this
+sets an existing \fBrecheck_wwid\fP line to \fBno\fP. This command can be used
+along with any other command.
+.TP
+.B --find_multipaths\fP { \fBon\fP | \fByes\fP | \fBy\fP | \fBoff\fP | \fBno\fP | \fBn\fP | \fBstrict\fP | \fBgreedy\fP | \fBsmart\fP }
+If set to \fB<value>\fP, this adds the line
+.B find_multipaths <value>
+to the
+.B /etc/multipath.conf
+defaults section. This command can be used along with any other command.
+\fBy\fP and \fBn\fP can be used instead of \fByes\fP and \fBno\fP.
+.TP
+.B --property_blacklist \fP { \fBy\fP | \fBn\fP }
+If set to \fBy\fP, this adds the line
@ -730,11 +844,18 @@ index 00000000..b82961d6
+present. This command can be used along with any other command.
+.TP
+.B --enable_foreign\fP { \fBy\fP | \fBn\fP }
+If set to \fBn\fP, this adds the line
+.B enable_foreign "^$"
+If set to \fBy\fP, this adds the line
+.B enable_foreign ".*"
+to the
+.B /etc/multipath.conf
+defaults section. if set to \fBy\fP, this removes the line, if present. This
+defaults section. if set to \fBn\fP, this removes the line, if present. This
+command can be used along with any other command.
+.TP
+.B --option \fB<option_name>:[<value>]\fP
+Sets the defaults section option \fB<option_name>\fP to \fB<value>\fP. If the
+option was not previously set in the defaults section, it is added. If it was
+set, its value is changed to \fB<value>\fP. If \fB<value>\fP is left blank,
+then the option is removed from the defaults section, if was set there. This
+command can be used along with any other command.
+.TP
+.B --outfile \fB<filename>\fP
@ -769,6 +890,3 @@ index 00000000..b82961d6
+.BR service (8),
+.SH AUTHOR
+Benjamin Marzinski <bmarzins@redhat.com>
--
2.17.2

View File

@ -14,23 +14,38 @@ multipathd.service
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/wwids.c | 44 +++++++++++++++++++++++++++++++++++
libmultipath/wwids.h | 1 +
multipath/main.c | 10 ++++++--
multipath/multipath.8 | 7 +++++-
multipathd/multipathd.service | 1 +
5 files changed, 60 insertions(+), 3 deletions(-)
multipath/main.c | 54 ++++++++++++++++++++++++++++++--
multipath/multipath.8.in | 7 ++++-
multipathd/multipathd.service.in | 1 +
3 files changed, 59 insertions(+), 3 deletions(-)
diff --git a/libmultipath/wwids.c b/libmultipath/wwids.c
index 28a2150d..fab6fc8f 100644
--- a/libmultipath/wwids.c
+++ b/libmultipath/wwids.c
@@ -454,3 +454,47 @@ int op ## _wwid(const char *wwid) \
declare_failed_wwid_op(is_failed, false)
declare_failed_wwid_op(mark_failed, true)
declare_failed_wwid_op(unmark_failed, true)
+
+int remember_cmdline_wwid(void)
diff --git a/multipath/main.c b/multipath/main.c
index c21e3e0b..3f3ac9fb 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -120,7 +120,7 @@ usage (char * progname)
fprintf (stderr, " %s [-v level] [-R retries] -F\n", progname);
fprintf (stderr, " %s [-v level] [-l|-ll] [device]\n", progname);
fprintf (stderr, " %s [-v level] [-a|-w] device\n", progname);
- fprintf (stderr, " %s [-v level] -W\n", progname);
+ fprintf (stderr, " %s [-v level] [-A|-W]\n", progname);
fprintf (stderr, " %s [-v level] [-i] [-c|-C] device\n", progname);
fprintf (stderr, " %s [-v level] [-i] [-u|-U]\n", progname);
fprintf (stderr, " %s [-h|-t|-T]\n", progname);
@@ -134,6 +134,8 @@ usage (char * progname)
" -f flush a multipath device map\n"
" -F flush all multipath device maps\n"
" -a add a device wwid to the wwids file\n"
+ " -A add devices from kernel command line mpath.wwids\n"
+ " parameters to wwids file\n"
" -c check if a device should be a path in a multipath device\n"
" -C check if a multipath device has usable paths\n"
" -q allow queue_if_no_path when multipathd is not running\n"
@@ -448,6 +450,50 @@ static void cleanup_vecs(void)
free_pathvec(vecs.pathvec, FREE_PATHS);
}
+static int remember_cmdline_wwid(void)
+{
+ FILE *f = NULL;
+ char buf[LINE_MAX], *next, *ptr;
@ -73,50 +88,20 @@ index 28a2150d..fab6fc8f 100644
+ }
+ return ret;
+}
diff --git a/libmultipath/wwids.h b/libmultipath/wwids.h
index 0c6ee54d..e32a0b0e 100644
--- a/libmultipath/wwids.h
+++ b/libmultipath/wwids.h
@@ -17,6 +17,7 @@ int remember_wwid(char *wwid);
int check_wwids_file(char *wwid, int write_wwid);
int remove_wwid(char *wwid);
int replace_wwids(vector mp);
+int remember_cmdline_wwid(void);
enum {
WWID_IS_NOT_FAILED = 0,
diff --git a/multipath/main.c b/multipath/main.c
index cf9d2a28..78822ee1 100644
--- a/multipath/main.c
+++ b/multipath/main.c
@@ -138,7 +138,7 @@ usage (char * progname)
fprintf (stderr, " %s [-v level] [-R retries] -F\n", progname);
fprintf (stderr, " %s [-v level] [-l|-ll] [device]\n", progname);
fprintf (stderr, " %s [-v level] [-a|-w] device\n", progname);
- fprintf (stderr, " %s [-v level] -W\n", progname);
+ fprintf (stderr, " %s [-v level] [-A|-W]\n", progname);
fprintf (stderr, " %s [-v level] [-i] [-c|-C] device\n", progname);
fprintf (stderr, " %s [-v level] [-i] [-u|-U]\n", progname);
fprintf (stderr, " %s [-h|-t|-T]\n", progname);
@@ -151,6 +151,8 @@ usage (char * progname)
" -f flush a multipath device map\n"
" -F flush all multipath device maps\n"
" -a add a device wwid to the wwids file\n"
+ " -A add devices from kernel command line mpath.wwids\n"
+ " parameters to wwids file\n"
" -c check if a device should be a path in a multipath device\n"
" -C check if a multipath device has usable paths\n"
" -q allow queue_if_no_path when multipathd is not running\n"
@@ -907,7 +909,7 @@ main (int argc, char *argv[])
multipath_conf = conf;
conf->retrigger_tries = 0;
conf->force_sync = 1;
- while ((arg = getopt(argc, argv, ":adcChl::FfM:v:p:b:BrR:itTquUwW")) != EOF ) {
+ while ((arg = getopt(argc, argv, ":aAdcChl::FfM:v:p:b:BrR:itTquUwW")) != EOF ) {
+
static int
configure (struct config *conf, enum mpath_cmds cmd,
enum devtypes dev_type, char *devpath)
@@ -861,7 +907,7 @@ main (int argc, char *argv[])
condlog(1, "failed to register cleanup handler for vecs: %m");
if (atexit(cleanup_bindings))
condlog(1, "failed to register cleanup handler for bindings: %m");
- while ((arg = getopt(argc, argv, ":adDcChl::eFfM:v:p:b:BrR:itTquUwW")) != EOF ) {
+ while ((arg = getopt(argc, argv, ":aAdDcChl::eFfM:v:p:b:BrR:itTquUwW")) != EOF ) {
switch(arg) {
case 1: printf("optarg : %s\n",optarg);
break;
@@ -977,6 +979,10 @@ main (int argc, char *argv[])
case 'v':
if (!isdigit(optarg[0])) {
@@ -932,6 +978,10 @@ main (int argc, char *argv[])
case 'T':
cmd = CMD_DUMP_CONFIG;
break;
@ -127,11 +112,11 @@ index cf9d2a28..78822ee1 100644
case 'h':
usage(argv[0]);
exit(RTVL_OK);
diff --git a/multipath/multipath.8 b/multipath/multipath.8
index 9cdd05a3..8befc45a 100644
--- a/multipath/multipath.8
+++ b/multipath/multipath.8
@@ -63,7 +63,7 @@ multipath \- Device mapper target autoconfig.
diff --git a/multipath/multipath.8.in b/multipath/multipath.8.in
index b88e9a4c..edd742aa 100644
--- a/multipath/multipath.8.in
+++ b/multipath/multipath.8.in
@@ -64,7 +64,7 @@ multipath \- Device mapper target autoconfig.
.B multipath
.RB [\| \-v\ \c
.IR level \|]
@ -140,7 +125,7 @@ index 9cdd05a3..8befc45a 100644
.
.LP
.B multipath
@@ -145,6 +145,11 @@ device mapper, path checkers ...).
@@ -146,6 +146,11 @@ device mapper, path checkers ...).
Add the WWID for the specified device to the WWIDs file.
.
.TP
@ -152,18 +137,15 @@ index 9cdd05a3..8befc45a 100644
.B \-w
Remove the WWID for the specified device from the WWIDs file.
.
diff --git a/multipathd/multipathd.service b/multipathd/multipathd.service
index 17434cef..0fbcc46b 100644
--- a/multipathd/multipathd.service
+++ b/multipathd/multipathd.service
@@ -15,6 +15,7 @@ Type=notify
diff --git a/multipathd/multipathd.service.in b/multipathd/multipathd.service.in
index 01ceff7d..e0c2011b 100644
--- a/multipathd/multipathd.service.in
+++ b/multipathd/multipathd.service.in
@@ -17,6 +17,7 @@ ConditionVirtualization=!container
[Service]
Type=notify
NotifyAccess=main
LimitCORE=infinity
ExecStartPre=-/sbin/modprobe -a scsi_dh_alua scsi_dh_emc scsi_dh_rdac dm-multipath
+ExecStartPre=-/sbin/multipath -A
ExecStart=/sbin/multipathd -d -s
ExecReload=/sbin/multipathd reconfigure
TasksMax=infinity
--
2.17.2

View File

@ -6,16 +6,18 @@ Subject: [PATCH] RH: reset default find_mutipaths value to off
Upstream has changed to default find_multipaths to "strict". For now
Redhat will retain the previous default of "off".
Co-authored-by: Paul Donohue <git@PaulSD.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/defaults.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
libmultipath/defaults.h | 2 +-
multipath/multipath.conf.5.in | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h
index e5ee6afe..52fe05b9 100644
index ed08c251..4b033ff9 100644
--- a/libmultipath/defaults.h
+++ b/libmultipath/defaults.h
@@ -22,7 +22,7 @@
@@ -23,7 +23,7 @@
#define DEFAULT_NO_PATH_RETRY NO_PATH_RETRY_UNDEF
#define DEFAULT_VERBOSITY 2
#define DEFAULT_REASSIGN_MAPS 0
@ -24,6 +26,16 @@ index e5ee6afe..52fe05b9 100644
#define DEFAULT_FAST_IO_FAIL 5
#define DEFAULT_DEV_LOSS_TMO 600
#define DEFAULT_RETAIN_HWHANDLER RETAIN_HWHANDLER_ON
--
2.17.2
diff --git a/multipath/multipath.conf.5.in b/multipath/multipath.conf.5.in
index 645e8f88..a7543939 100644
--- a/multipath/multipath.conf.5.in
+++ b/multipath/multipath.conf.5.in
@@ -1225,7 +1225,7 @@ as non-multipath and passed on to upper layers.
\fBNote:\fR this may cause delays during device detection if
there are single-path devices which aren\'t blacklisted.
.TP
-The default is: \fBstrict\fR
+The default is: \fBoff\fR
.RE
.
.

View File

@ -13,7 +13,7 @@ Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
1 file changed, 29 insertions(+), 2 deletions(-)
diff --git a/libmultipath/prioritizers/ana.c b/libmultipath/prioritizers/ana.c
index b5c7873d..e139360c 100644
index e9827dca..80a32aa3 100644
--- a/libmultipath/prioritizers/ana.c
+++ b/libmultipath/prioritizers/ana.c
@@ -24,6 +24,7 @@
@ -68,7 +68,7 @@ index b5c7873d..e139360c 100644
static int get_ana_info(struct path * pp)
{
int rc;
@@ -210,8 +234,11 @@ int getprio(struct path *pp, __attribute__((unused)) char *args,
@@ -209,8 +233,11 @@ int getprio(struct path *pp, __attribute__((unused)) char *args)
if (pp->fd < 0)
rc = -ANA_ERR_NO_INFORMATION;
@ -82,6 +82,3 @@ index b5c7873d..e139360c 100644
switch (rc) {
case NVME_ANA_OPTIMIZED:
--
2.17.2

View File

@ -0,0 +1,96 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 25 Mar 2021 13:05:10 -0500
Subject: [PATCH] RH: make parse_vpd_pg83 match scsi_id output
Red Hat sets ID_SERIAL based on the result of scsi_id, instead of using
the result of sg_inq and 55-scsi-sg3_id.rules. Make parse_vpd_pg83 match
that.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/discovery.c | 12 ++----------
tests/vpd.c | 6 ++++++
2 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index e2052422..3bcd94ce 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -1221,13 +1221,9 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
good_len = 8;
break;
case 2:
- /* IEEE Extended: Prio 6 */
- new_prio = 6;
- good_len = 8;
- break;
case 3:
- /* IEEE Locally assigned: Prio 1 */
- new_prio = 1;
+ /* IEEE Extended or Locally assigned: Prio 6 */
+ new_prio = 6;
good_len = 8;
break;
default:
@@ -1245,10 +1241,6 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
break;
case 0x8:
/* SCSI Name: Prio 3 */
- invalid = (d[3] < 4 || (memcmp(d + 4, "eui.", 4) &&
- memcmp(d + 4, "naa.", 4) &&
- memcmp(d + 4, "iqn.", 4)));
- new_prio = 3;
break;
case 0x1:
/* T-10 Vendor ID: Prio 2 */
diff --git a/tests/vpd.c b/tests/vpd.c
index e3212e61..cdb111bb 100644
--- a/tests/vpd.c
+++ b/tests/vpd.c
@@ -232,11 +232,13 @@ static const char * const str_prefix[] = {
[STR_IQN] = "iqn.",
};
+#if 0
static const char byte0[] = {
[STR_EUI] = '2',
[STR_NAA] = '3',
[STR_IQN] = '8',
};
+#endif
/**
* create_scsi_string_desc() - create a SCSI name string descriptor.
@@ -767,6 +769,7 @@ make_test_vpd_naa(2, 18);
make_test_vpd_naa(2, 17);
make_test_vpd_naa(2, 16);
+#if 0
/* SCSI Name string: EUI64, WWID size: 17 */
make_test_vpd_str(0, 20, 18)
make_test_vpd_str(0, 20, 17)
@@ -802,6 +805,7 @@ make_test_vpd_str(18, 20, 18)
make_test_vpd_str(18, 20, 17)
make_test_vpd_str(18, 20, 16)
make_test_vpd_str(18, 20, 15)
+#endif
static int test_vpd(void)
{
@@ -910,6 +914,7 @@ static int test_vpd(void)
cmocka_unit_test(test_vpd_naa_2_18),
cmocka_unit_test(test_vpd_naa_2_17),
cmocka_unit_test(test_vpd_naa_2_16),
+/*
cmocka_unit_test(test_vpd_str_0_20_18),
cmocka_unit_test(test_vpd_str_0_20_17),
cmocka_unit_test(test_vpd_str_0_20_16),
@@ -934,6 +939,7 @@ static int test_vpd(void)
cmocka_unit_test(test_vpd_str_18_20_17),
cmocka_unit_test(test_vpd_str_18_20_16),
cmocka_unit_test(test_vpd_str_18_20_15),
+*/
};
return cmocka_run_group_tests(tests, setup, teardown);
}

View File

@ -0,0 +1,25 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Mar 2022 18:12:06 -0500
Subject: [PATCH] RH: add scsi device handlers to modules-load.d
Make scsi_dh_alua scsi_dh_emc and scsi_dh_rdac get loaded in early boot.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile.inc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile.inc b/Makefile.inc
index 94e0ec85..49514b06 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -16,7 +16,7 @@ READLINE :=
# List of scsi device handler modules to load on boot, e.g.
# SCSI_DH_MODULES_PRELOAD := scsi_dh_alua scsi_dh_rdac
-SCSI_DH_MODULES_PRELOAD :=
+SCSI_DH_MODULES_PRELOAD := scsi_dh_alua scsi_dh_emc scsi_dh_rdac
EXTRAVERSION := $(shell rev=$$(git rev-parse --short=7 HEAD 2>/dev/null); echo $${rev:+-g$$rev})

View File

@ -0,0 +1,26 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 15 Nov 2022 18:03:33 -0600
Subject: [PATCH] RH: compile with libreadline support
Since the license issue has been resolved, and there are problems with
the command completion with libedit, use libreadline.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile.inc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile.inc b/Makefile.inc
index 49514b06..a3ed9f28 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -12,7 +12,7 @@
# Readline library to use, libedit, libreadline, or empty
# Caution: Using libreadline may make the multipathd binary undistributable,
# see https://github.com/opensvc/multipath-tools/issues/36
-READLINE :=
+READLINE := libreadline
# List of scsi device handler modules to load on boot, e.g.
# SCSI_DH_MODULES_PRELOAD := scsi_dh_alua scsi_dh_rdac

View File

@ -0,0 +1,186 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 7 Jul 2023 15:25:59 -0500
Subject: [PATCH] RH: Add mpathcleanup
mpathcleanup is a program that will remove a multipath device as well as
all of the scsi path devices that make it up.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipath/Makefile | 2 +
multipath/mpathcleanup | 145 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 147 insertions(+)
create mode 100755 multipath/mpathcleanup
diff --git a/multipath/Makefile b/multipath/Makefile
index 3dc241cc..47e82234 100644
--- a/multipath/Makefile
+++ b/multipath/Makefile
@@ -25,6 +25,7 @@ install:
$(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir)
$(Q)$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
$(Q)$(INSTALL_PROGRAM) -m 755 mpathconf $(DESTDIR)$(bindir)/
+ $(Q)$(INSTALL_PROGRAM) -m 755 mpathcleanup $(DESTDIR)$(bindir)/
$(Q)$(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir)
$(Q)$(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir)
$(Q)$(INSTALL_PROGRAM) -m 644 99-z-dm-mpath-late.rules $(DESTDIR)$(udevrulesdir)
@@ -49,6 +50,7 @@ endif
uninstall:
$(Q)$(RM) $(DESTDIR)$(bindir)/$(EXEC)
$(Q)$(RM) $(DESTDIR)$(bindir)/mpathconf
+ $(Q)$(RM) $(DESTDIR)$(bindir)/mpathcleanup
$(Q)$(RM) $(DESTDIR)$(udevrulesdir)/11-dm-mpath.rules
$(Q)$(RM) $(DESTDIR)$(udevrulesdir)/99-z-dm-mpath-late.rules
$(Q)$(RM) $(DESTDIR)$(modulesloaddir)/multipath.conf
diff --git a/multipath/mpathcleanup b/multipath/mpathcleanup
new file mode 100755
index 00000000..6fd921e4
--- /dev/null
+++ b/multipath/mpathcleanup
@@ -0,0 +1,145 @@
+#!/bin/bash
+#
+# Copyright (C) 2023 Red Hat, Inc. All rights reserved.
+#
+# This file is part of the device-mapper-multipath package.
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+unset PROGRAM FLUSH DEVICE DEVNAME MAJOR MINOR PATHDEVS PATHDEV HAVE_MULTIPATHD QUEUEING
+
+function usage
+{
+ echo "usage: $PROGRAM [-h] [--flush] <device>"
+ echo ""
+ echo "remove a multipath device and its scsi path devices"
+ echo ""
+ echo "options:"
+ echo " -h, --help show this help message and exit"
+ echo " --flush disable queuing on the multipath device and"
+ echo " flush the path devices before removing"
+}
+
+function parse_args
+{
+ while [ -n "$1" ]; do
+ case $1 in
+ --flush)
+ FLUSH=1
+ shift
+ ;;
+ --help | -h)
+ usage
+ exit 1
+ ;;
+ *)
+ if [ -n "$DEVICE" ]; then
+ usage
+ exit 1
+ fi
+ DEVICE=$1
+ shift
+ ;;
+ esac
+ done
+}
+
+function validate_device
+{
+ if [ -z "$DEVICE" ]; then
+ usage
+ exit 1
+ fi
+ if [[ "$DEVICE" =~ ^[[:digit:]]+:[[:digit:]]+$ ]]; then
+ MAJOR=${DEVICE%%:*}
+ MINOR=${DEVICE##*:}
+ DEVNAME=`dmsetup ls --target multipath | grep "($MAJOR, $MINOR)$" | awk '{print $1}'`
+ else
+ DEVNAME=`dmsetup ls --target multipath | awk '{print $1}' | grep "^$DEVICE$"`
+ fi
+ if [ -z "$DEVNAME" ]; then
+ DEVNAME=`multipath -v 1 -l $DEVICE 2>/dev/null`
+ if [ -z "$DEVNAME" ]; then
+ echo "$DEVICE is not a multipath device"
+ exit 1
+ fi
+ # verify that this is not a native nvme multipath device
+ dmsetup ls --target multipath | awk '{print $1}' | grep -q "^$DEVNAME$"
+ if test $? -eq 1; then
+ echo "$DEVICE is not a device-mapper multipath device"
+ exit 1
+ fi
+ fi
+ if [ -z "$MINOR" ]; then
+ MINOR=`dmsetup info -c --noheadings -o minor $DEVNAME`
+ fi
+}
+
+function get_paths
+{
+ PATHDEVS=`ls /sys/block/dm-$MINOR/slaves`
+ for PATHDEV in $PATHDEVS; do
+ if [[ ! "$PATHDEV" =~ ^sd[a-z]+$ ]]; then
+ echo "$PATHDEV is not a scsi device. $PROGRAM only works with scsi devices"
+ exit 1
+ fi
+ done
+}
+
+function remove_devs
+{
+ pidof multipathd > /dev/null
+ HAVE_MULTIPATHD=$?
+ multipath -v2 -l "$DEVNAME" | grep features | grep -q queue_if_no_path
+ QUEUEING=$?
+ if [ -n "$FLUSH" ] && [ "$QUEUEING" -eq 0 ]; then
+ if test $HAVE_MULTIPATHD -eq 0; then
+ multipathd disablequeueing map "$DEVNAME" > /dev/null
+ else
+ dmsetup message "$DEVNAME" 0 fail_if_no_path
+ fi
+ sleep 1
+ fi
+ if test $HAVE_MULTIPATHD -eq 0; then
+ multipath -f "$DEVNAME"
+ else
+ multipathd -Df "$DEVNAME"
+ fi
+ if test $? -eq 1; then
+ echo "$DEVICE cannot be removed"
+ exit 1
+ fi
+ for PATHDEV in $PATHDEVS; do
+ if [ -n "$FLUSH" ]; then
+ blockdev --flushbufs /dev/"$PATHDEV"
+ fi
+ echo 1 > /sys/block/"$PATHDEV"/device/delete
+ done
+}
+
+function verify_removal
+{
+ multipath -v 1 -d $DEVNAME | grep -q "^$DEVNAME$"
+ if test $? -eq 0; then
+ echo "$DEVICE removed but path devices still exist"
+ exit 1
+ fi
+ multipath -v 1 -l $DEVNAME | grep -q "^$DEVNAME$"
+ if test $? -eq 0; then
+ echo "$DEVICE removal succeeded, but device still exists"
+ exit 1
+ fi
+}
+
+PROGRAM="$0"
+parse_args "$@"
+validate_device
+get_paths
+remove_devs
+verify_removal

View File

@ -0,0 +1,77 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 7 Aug 2024 18:59:10 -0400
Subject: [PATCH] libmultipath: fix ontap prioritizer snprintf limits
The ontap prioritizer functions dump_cdb() and process_sg_error() both
incorrectly set the snprintf() limits larger than the available space.
Instead of multiplying the number of elements to print by the size of an
element to calculate the limit, they multiplied the number of elements
to print by the maximum number of elements that the buffer could hold.
Fix this by making these functions use strbufs instead.
mwilck: removed log message in print_strbuf() failure case.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Martin Wilck <mwilck@suse.com>
---
libmultipath/prioritizers/ontap.c | 24 +++++++++++-------------
1 file changed, 11 insertions(+), 13 deletions(-)
diff --git a/libmultipath/prioritizers/ontap.c b/libmultipath/prioritizers/ontap.c
index 117886ea..90eaf274 100644
--- a/libmultipath/prioritizers/ontap.c
+++ b/libmultipath/prioritizers/ontap.c
@@ -23,6 +23,7 @@
#include "prio.h"
#include "structs.h"
#include "unaligned.h"
+#include "strbuf.h"
#define INQUIRY_CMD 0x12
#define INQUIRY_CMDLEN 6
@@ -35,32 +36,29 @@
static void dump_cdb(unsigned char *cdb, int size)
{
int i;
- char buf[10*5+1];
- char * p = &buf[0];
+ STRBUF_ON_STACK(buf);
- condlog(0, "- SCSI CDB: ");
- for (i=0; i<size; i++) {
- p += snprintf(p, 10*(size-i), "0x%02x ", cdb[i]);
+ for (i = 0; i < size; i++) {
+ if (print_strbuf(&buf, "0x%02x ", cdb[i]) < 0)
+ return;
}
- condlog(0, "%s", buf);
+ condlog(0, "- SCSI CDB: %s", get_strbuf_str(&buf));
}
static void process_sg_error(struct sg_io_hdr *io_hdr)
{
int i;
- char buf[128*5+1];
- char * p = &buf[0];
+ STRBUF_ON_STACK(buf);
condlog(0, "- masked_status=0x%02x, host_status=0x%02x, "
"driver_status=0x%02x", io_hdr->masked_status,
io_hdr->host_status, io_hdr->driver_status);
if (io_hdr->sb_len_wr > 0) {
- condlog(0, "- SCSI sense data: ");
- for (i=0; i<io_hdr->sb_len_wr; i++) {
- p += snprintf(p, 128*(io_hdr->sb_len_wr-i), "0x%02x ",
- io_hdr->sbp[i]);
+ for (i = 0; i < io_hdr->sb_len_wr; i++) {
+ if (print_strbuf(&buf, "0x%02x ", io_hdr->sbp[i]) < 0)
+ return;
}
- condlog(0, "%s", buf);
+ condlog(0, "- SCSI sense data: %s", get_strbuf_str(&buf));
}
}

View File

@ -0,0 +1,38 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 16 Sep 2024 19:29:13 -0400
Subject: [PATCH] multipathd: checker port_state before setting it.
If the rport port_state is already Marginal, trying to set it to
Marginal causes an error like the following:
multipathd[365376]: /sys/devices/pci0000:c0/0000:c0:01.1/0000:c4:00.0/host0/rport-0:0-5/fc_remote_ports/rport-0:0-5: failed to set port_state to marginal: Invalid argument
To avoid causing this confusing error message, check if the port_state
is already marginal before trying to set it.
Cc: Muneendra Kumar <muneendra.kumar@broadcom.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/fpin_handlers.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/multipathd/fpin_handlers.c b/multipathd/fpin_handlers.c
index be087ca0..6b56f9b7 100644
--- a/multipathd/fpin_handlers.c
+++ b/multipathd/fpin_handlers.c
@@ -169,9 +169,14 @@ fpin_els_add_li_frame(struct fc_nl_event *fc_event)
/*Sets the rport port_state to marginal*/
static void fpin_set_rport_marginal(struct udev_device *rport_dev)
{
+ char old_value[20]; /* match kernel show_fc_rport_port_state() size */
static const char marginal[] = "Marginal";
ssize_t ret;
+ ret = sysfs_attr_get_value(rport_dev, "port_state",
+ old_value, sizeof(old_value));
+ if (ret == sizeof(marginal) - 1 && strcmp(old_value, marginal) == 0)
+ return;
ret = sysfs_attr_set_value(rport_dev, "port_state",
marginal, sizeof(marginal) - 1);
if (ret != sizeof(marginal) - 1)

View File

@ -12,10 +12,10 @@ Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c
index 5feb1e95..1d48b08d 100644
index 6f2d8800..235306ff 100644
--- a/libmultipath/foreign/nvme.c
+++ b/libmultipath/foreign/nvme.c
@@ -697,6 +697,7 @@ static void _find_controllers(struct context *ctx, struct nvme_map *map)
@@ -708,6 +708,7 @@ static void _find_controllers(struct context *ctx, struct nvme_map *map)
path = _find_path_by_syspath(map,
udev_device_get_syspath(udev));
if (path != NULL) {
@ -23,7 +23,7 @@ index 5feb1e95..1d48b08d 100644
path->seen = true;
condlog(4, "%s: %s already known",
__func__, fn);
@@ -704,8 +705,10 @@ static void _find_controllers(struct context *ctx, struct nvme_map *map)
@@ -715,8 +716,10 @@ static void _find_controllers(struct context *ctx, struct nvme_map *map)
}
path = calloc(1, sizeof(*path));

View File

@ -0,0 +1,65 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 11 Feb 2025 16:51:59 +0100
Subject: [PATCH] multipath-tools: move DEFAULT_SOCKET definition into
Makefile.inc
This enables configuring the socket name. Follow up patches will
add more flexibility for configuring the sockets.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile.inc | 5 ++++-
libmpathcmd/mpath_cmd.h | 1 -
libmultipath/defaults.h | 1 -
3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/Makefile.inc b/Makefile.inc
index a3ed9f28..80de8ecb 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -75,6 +75,8 @@ devmapper_incdir := $(or $(shell $(PKG_CONFIG) --variable=includedir devmapper),
libudev_incdir := $(or $(shell $(PKG_CONFIG) --variable=includedir libudev),/usr/include)
kernel_incdir := /usr/include
+abstract_socket := /org/kernel/linux/storage/multipathd
+
ifeq ($(V),)
Q := @
# make's "Entering directory" messages are confusing in parallel mode
@@ -116,7 +118,8 @@ CPPFLAGS := $(CPPFLAGS) $(D_URCU_VERSION) \
-DBIN_DIR=\"$(bindir)\" -DMULTIPATH_DIR=\"$(TGTDIR)$(plugindir)\" \
-DRUNTIME_DIR=\"$(runtimedir)\" -DCONFIG_DIR=\"$(TGTDIR)$(configdir)\" \
-DDEFAULT_CONFIGFILE=\"$(TGTDIR)$(configfile)\" -DSTATE_DIR=\"$(TGTDIR)$(statedir)\" \
- -DEXTRAVERSION=\"$(EXTRAVERSION)\" -MMD -MP
+ -DEXTRAVERSION=\"$(EXTRAVERSION)\" \
+ -DDEFAULT_SOCKET=\"$(abstract_socket)\" -MMD -MP
CFLAGS := -std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe
BIN_CFLAGS := -fPIE -DPIE
LIB_CFLAGS := -fPIC
diff --git a/libmpathcmd/mpath_cmd.h b/libmpathcmd/mpath_cmd.h
index 0c293c71..5ab5a6e5 100644
--- a/libmpathcmd/mpath_cmd.h
+++ b/libmpathcmd/mpath_cmd.h
@@ -30,7 +30,6 @@
extern "C" {
#endif
-#define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd"
#define DEFAULT_REPLY_TIMEOUT 4000
diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h
index 4b033ff9..3602636c 100644
--- a/libmultipath/defaults.h
+++ b/libmultipath/defaults.h
@@ -66,7 +66,6 @@
#define DEV_LOSS_TMO_UNSET 0U
#define MAX_DEV_LOSS_TMO UINT_MAX
#define DEFAULT_PIDFILE RUNTIME_DIR "/multipathd.pid"
-#define DEFAULT_SOCKET "/org/kernel/linux/storage/multipathd"
#define DEFAULT_BINDINGS_FILE STATE_DIR "/bindings"
#define DEFAULT_WWIDS_FILE STATE_DIR "/wwids"
#define DEFAULT_PRKEYS_FILE STATE_DIR "/prkeys"

View File

@ -0,0 +1,243 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Fri, 14 Feb 2025 21:57:02 +0100
Subject: [PATCH] multipath-tools: add helper mpath_fill_sockaddr__()
Create a static new helper function which is used by both libmpathcmd
and libmpathutil and fills in the socket address name from the compile-time
default. The function is able to handle both abstract and pathname sockets,
but more changes are needed to make the latter actually work.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
.gitignore | 1 +
Makefile.inc | 8 +++--
create-config.mk | 1 +
libmpathcmd/mpath_cmd.c | 11 ++-----
libmpathcmd/mpath_fill_sockaddr.c | 32 +++++++++++++++++++
libmpathutil/uxsock.c | 15 ++++-----
multipathd/Makefile | 4 +--
...multipathd.socket => multipathd.socket.in} | 2 +-
8 files changed, 51 insertions(+), 23 deletions(-)
create mode 100644 libmpathcmd/mpath_fill_sockaddr.c
rename multipathd/{multipathd.socket => multipathd.socket.in} (88%)
diff --git a/.gitignore b/.gitignore
index 049ffe88..87446d9f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,7 @@ multipathd/multipathd
multipathd/multipathd.8
multipathd/multipathc
multipathd/multipathd.service
+multipathd/multipathd.socket
mpathpersist/mpathpersist
mpathpersist/mpathpersist.8
abi.tar.gz
diff --git a/Makefile.inc b/Makefile.inc
index 80de8ecb..1ef3f7f8 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -75,7 +75,7 @@ devmapper_incdir := $(or $(shell $(PKG_CONFIG) --variable=includedir devmapper),
libudev_incdir := $(or $(shell $(PKG_CONFIG) --variable=includedir libudev),/usr/include)
kernel_incdir := /usr/include
-abstract_socket := /org/kernel/linux/storage/multipathd
+abstract_socket := @/org/kernel/linux/storage/multipathd
ifeq ($(V),)
Q := @
@@ -119,7 +119,9 @@ CPPFLAGS := $(CPPFLAGS) $(D_URCU_VERSION) \
-DRUNTIME_DIR=\"$(runtimedir)\" -DCONFIG_DIR=\"$(TGTDIR)$(configdir)\" \
-DDEFAULT_CONFIGFILE=\"$(TGTDIR)$(configfile)\" -DSTATE_DIR=\"$(TGTDIR)$(statedir)\" \
-DEXTRAVERSION=\"$(EXTRAVERSION)\" \
- -DDEFAULT_SOCKET=\"$(abstract_socket)\" -MMD -MP
+ -DDEFAULT_SOCKET=\"$(abstract_socket)\" \
+ -DWSTRINGOP_TRUNCATION=$(if $(WSTRINGOP_TRUNCATION),1,0) \
+ -MMD -MP
CFLAGS := -std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe
BIN_CFLAGS := -fPIE -DPIE
LIB_CFLAGS := -fPIC
@@ -164,4 +166,4 @@ NV_VERSION_SCRIPT = $(DEVLIB:%.so=%-nv.version)
%: %.in
@echo creating $@
- $(Q)sed 's:@CONFIGFILE@:'$(TGTDIR)$(configfile)':g;s:@CONFIGDIR@:'$(TGTDIR)$(configdir)':g;s:@STATE_DIR@:'$(TGTDIR)$(statedir)':g;s:@RUNTIME_DIR@:'$(runtimedir)':g;s/@MODPROBE_UNIT@/'$(MODPROBE_UNIT)'/g;s:@BINDIR@:'$(bindir)':g' $< >$@
+ $(Q)sed 's:@CONFIGFILE@:'$(TGTDIR)$(configfile)':g;s:@CONFIGDIR@:'$(TGTDIR)$(configdir)':g;s:@STATE_DIR@:'$(TGTDIR)$(statedir)':g;s:@RUNTIME_DIR@:'$(runtimedir)':g;s/@MODPROBE_UNIT@/'$(MODPROBE_UNIT)'/g;s:@BINDIR@:'$(bindir)':g;s,@MPATH_SOCKET@,'$(abstract_socket)',g' $< >$@
diff --git a/create-config.mk b/create-config.mk
index 4d318b96..5cdff69a 100644
--- a/create-config.mk
+++ b/create-config.mk
@@ -179,6 +179,7 @@ $(TOPDIR)/config.mk: $(multipathdir)/autoconfig.h
@echo "ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,)" >>$@
@echo "WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered -Wno-error=clobbered,)" >>$@
@echo "WFORMATOVERFLOW := $(call TEST_CC_OPTION,-Wformat-overflow=2,)" >>$@
+ @echo "WSTRINGOP_TRUNCATION := $(call TEST_CC_OPTION,-Wstringop-truncation)" >>$@
@echo "W_MISSING_INITIALIZERS := $(call TEST_MISSING_INITIALIZERS)" >>$@
@echo "W_URCU_TYPE_LIMITS := $(call TEST_URCU_TYPE_LIMITS)" >>$@
@echo "ENABLE_LIBDMMP := $(ENABLE_LIBDMMP)" >>$@
diff --git a/libmpathcmd/mpath_cmd.c b/libmpathcmd/mpath_cmd.c
index 60b2d965..146e790d 100644
--- a/libmpathcmd/mpath_cmd.c
+++ b/libmpathcmd/mpath_cmd.c
@@ -24,11 +24,13 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <poll.h>
+#include <stddef.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include "mpath_cmd.h"
+#include "mpath_fill_sockaddr.c"
/*
* keep reading until its all read
@@ -101,14 +103,6 @@ int __mpath_connect(int nonblocking)
struct sockaddr_un addr;
int flags = 0;
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_LOCAL;
- addr.sun_path[0] = '\0';
- strncpy(&addr.sun_path[1], DEFAULT_SOCKET, sizeof(addr.sun_path) - 1);
- len = strlen(DEFAULT_SOCKET) + 1 + sizeof(sa_family_t);
- if (len > sizeof(struct sockaddr_un))
- len = sizeof(struct sockaddr_un);
-
fd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (fd == -1)
return -1;
@@ -119,6 +113,7 @@ int __mpath_connect(int nonblocking)
(void)fcntl(fd, F_SETFL, flags|O_NONBLOCK);
}
+ len = mpath_fill_sockaddr__(&addr, DEFAULT_SOCKET);
if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
int err = errno;
diff --git a/libmpathcmd/mpath_fill_sockaddr.c b/libmpathcmd/mpath_fill_sockaddr.c
new file mode 100644
index 00000000..750ef3ee
--- /dev/null
+++ b/libmpathcmd/mpath_fill_sockaddr.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2025 SUSE LLC
+ * SPDX-license-identifier: LGPL-2.1-or-newer
+ */
+
+static size_t mpath_fill_sockaddr__(struct sockaddr_un *addr, const char *name)
+{
+ size_t len;
+
+ addr->sun_family = AF_LOCAL;
+
+ if (name[0] != '@') {
+ /* Pathname socket. This should be NULL-terminated. */
+ strncpy(&addr->sun_path[0], name, sizeof(addr->sun_path) - 1);
+ addr->sun_path[sizeof(addr->sun_path) - 1] = '\0';
+ len = offsetof(struct sockaddr_un, sun_path) + strlen(name) + 1;
+ } else {
+ addr->sun_path[0] = '\0';
+ /*
+ * The abstract socket's name doesn't need to be NULL terminated.
+ * Actually, a trailing NULL would be considered part of the socket name.
+ */
+#pragma GCC diagnostic push
+#if WSTRINGOP_TRUNCATION
+#pragma GCC diagnostic ignored "-Wstringop-truncation"
+#endif
+ strncpy(&addr->sun_path[1], &name[1], sizeof(addr->sun_path) - 1);
+#pragma GCC diagnostic pop
+ len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
+ }
+ return len > sizeof(*addr) ? sizeof(*addr) : len;
+}
diff --git a/libmpathutil/uxsock.c b/libmpathutil/uxsock.c
index 2135476d..12c46084 100644
--- a/libmpathutil/uxsock.c
+++ b/libmpathutil/uxsock.c
@@ -6,12 +6,15 @@
*/
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include <unistd.h>
#include <stdarg.h>
+#include <stddef.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
+#include <sys/stat.h>
#include <sys/un.h>
#include <poll.h>
#include <signal.h>
@@ -33,6 +36,8 @@
static int _recv_packet(int fd, char **buf, unsigned int timeout,
ssize_t limit);
+#include "../libmpathcmd/mpath_fill_sockaddr.c"
+
/*
* create a unix domain socket and start listening on it
* return a file descriptor open on the socket
@@ -63,15 +68,7 @@ int ux_socket_listen(const char *name)
return -1;
}
- memset(&addr, 0, sizeof(addr));
- addr.sun_family = AF_LOCAL;
- addr.sun_path[0] = '\0';
- len = strlen(name) + 1;
- if (len >= sizeof(addr.sun_path))
- len = sizeof(addr.sun_path) - 1;
- memcpy(&addr.sun_path[1], name, len);
-
- len += sizeof(sa_family_t);
+ len = mpath_fill_sockaddr__(&addr, name);
if (bind(fd, (struct sockaddr *)&addr, len) == -1) {
condlog(3, "Couldn't bind to ux_socket, error %d", errno);
close(fd);
diff --git a/multipathd/Makefile b/multipathd/Makefile
index 997b40cf..61cf1af6 100644
--- a/multipathd/Makefile
+++ b/multipathd/Makefile
@@ -41,7 +41,7 @@ ifeq ($(FPIN_SUPPORT),1)
OBJS += fpin_handlers.o
endif
-all : $(EXEC) $(CLI) $(MANPAGES) $(EXEC).service
+all : $(EXEC) $(CLI) $(MANPAGES) $(EXEC).service $(EXEC).socket
$(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so
@echo building $@ because of $?
@@ -78,7 +78,7 @@ uninstall:
$(Q)$(RM) $(DESTDIR)$(unitdir)/$(EXEC).socket
clean: dep_clean
- $(Q)$(RM) core *.o $(EXEC) $(CLI) $(MANPAGES) $(EXEC).service
+ $(Q)$(RM) core *.o $(EXEC) $(CLI) $(MANPAGES) $(EXEC).service $(EXEC).socket
include $(wildcard $(OBJS:.o=.d) $(CLI_OBJS:.o=.d))
diff --git a/multipathd/multipathd.socket b/multipathd/multipathd.socket.in
similarity index 88%
rename from multipathd/multipathd.socket
rename to multipathd/multipathd.socket.in
index 263b6b0c..4ed9c1ff 100644
--- a/multipathd/multipathd.socket
+++ b/multipathd/multipathd.socket.in
@@ -8,7 +8,7 @@ ConditionVirtualization=!container
Before=sockets.target
[Socket]
-ListenStream=@/org/kernel/linux/storage/multipathd
+ListenStream=@MPATH_SOCKET@
[Install]
# Socket activation for multipathd is disabled by default.

View File

@ -0,0 +1,49 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Fri, 14 Feb 2025 22:00:24 +0100
Subject: [PATCH] libmpathutil: add support for Unix pathname sockets
Pathname sockets need to be world read/writable in order to allow regular
users to read information from multipathd. Our SO_PEERCRED permission check
will make sure that they can't make configuration changes. Also, SO_REUSEADDR
doesn't work for pathname sockets as it does for abstract Unix sockets. A
possibly pre-existing socket file must be removed before trying to recreate it.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathutil/uxsock.c | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/libmpathutil/uxsock.c b/libmpathutil/uxsock.c
index 12c46084..889d7a17 100644
--- a/libmpathutil/uxsock.c
+++ b/libmpathutil/uxsock.c
@@ -62,6 +62,11 @@ int ux_socket_listen(const char *name)
return fd;
}
#endif
+
+ /* This is after the PID check, so unlinking should be fine */
+ if (name[0] != '@' && unlink(name) == -1 && errno != ENOENT)
+ condlog(1, "Failed to unlink %s", name);
+
fd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (fd == -1) {
condlog(3, "Couldn't create ux_socket, error %d", errno);
@@ -75,6 +80,14 @@ int ux_socket_listen(const char *name)
return -1;
}
+ /*
+ * Socket needs to have rw permissions for everone.
+ * SO_PEERCRED makes sure that only root can modify things.
+ */
+ if (name[0] != '@' &&
+ chmod(name, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH) == -1)
+ condlog(3, "failed to set permissions on %s: %s", name, strerror(errno));
+
if (listen(fd, 10) == -1) {
condlog(3, "Couldn't listen to ux_socket, error %d", errno);
close(fd);

View File

@ -0,0 +1,91 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Wed, 12 Feb 2025 19:12:35 +0100
Subject: [PATCH] libmpathutil: move systemd_listen_fds() support into
multipathd
This feature is only used by multipathd.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathutil/uxsock.c | 15 ---------------
multipathd/main.c | 28 +++++++++++++++++++++++++++-
2 files changed, 27 insertions(+), 16 deletions(-)
diff --git a/libmpathutil/uxsock.c b/libmpathutil/uxsock.c
index 889d7a17..59c47170 100644
--- a/libmpathutil/uxsock.c
+++ b/libmpathutil/uxsock.c
@@ -46,23 +46,8 @@ int ux_socket_listen(const char *name)
{
int fd;
size_t len;
-#ifdef USE_SYSTEMD
- int num;
-#endif
struct sockaddr_un addr;
-#ifdef USE_SYSTEMD
- num = sd_listen_fds(0);
- if (num > 1) {
- condlog(3, "sd_listen_fds returned %d fds", num);
- return -1;
- } else if (num == 1) {
- fd = SD_LISTEN_FDS_START + 0;
- condlog(3, "using fd %d from sd_listen_fds", fd);
- return fd;
- }
-#endif
-
/* This is after the PID check, so unlinking should be fine */
if (name[0] != '@' && unlink(name) == -1 && errno != ENOENT)
condlog(1, "Failed to unlink %s", name);
diff --git a/multipathd/main.c b/multipathd/main.c
index 58afe14a..24048408 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -1859,15 +1859,41 @@ uevqloop (void * ap)
pthread_cleanup_pop(1);
return NULL;
}
+
+#ifdef USE_SYSTEMD
+static int get_systemd_sockets(long *ux_sock)
+{
+ int num = sd_listen_fds(0);
+
+ if (num > 1) {
+ condlog(3, "sd_listen_fds returned %d fds", num);
+ return -1;
+ } else if (num == 1) {
+ ux_sock[0] = SD_LISTEN_FDS_START + 0;
+ condlog(3, "using fd %ld from sd_listen_fds", ux_sock[0]);
+ }
+ return num;
+}
+#else
+static int get_systemd_sockets(long *ux_sock __attribute__((unused)))
+{
+ return 0;
+}
+#endif
+
+
static void *
uxlsnrloop (void * ap)
{
long ux_sock;
+ int num;
pthread_cleanup_push(rcu_unregister, NULL);
rcu_register_thread();
- ux_sock = ux_socket_listen(DEFAULT_SOCKET);
+ num = get_systemd_sockets(&ux_sock);
+ if (num < 1)
+ ux_sock = ux_socket_listen(DEFAULT_SOCKET);
if (ux_sock == -1) {
condlog(1, "could not create uxsock: %d", errno);
exit_daemon();

View File

@ -0,0 +1,85 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Wed, 12 Feb 2025 20:08:28 +0100
Subject: [PATCH] multipathd: make uxsock_listen() take a pointer to fd
This prepares being able to pass multiple socket fds.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/main.c | 2 +-
multipathd/uxlsnr.c | 11 ++++++++---
multipathd/uxlsnr.h | 3 +--
3 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 24048408..9f15d2f7 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -1920,7 +1920,7 @@ uxlsnrloop (void * ap)
== DAEMON_CONFIGURE)
handle_signals(false);
- uxsock_listen(ux_sock, ap);
+ uxsock_listen(1, &ux_sock, ap);
out_sock:
pthread_cleanup_pop(1); /* uxsock_cleanup */
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 185e0a0a..51b5e51d 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -614,7 +614,7 @@ static void handle_client(struct client *c, struct vectors *vecs, short revents)
/*
* entry point
*/
-void *uxsock_listen(long ux_sock, void *trigger_data)
+void *uxsock_listen(int n_socks, long *ux_sock, void *trigger_data)
{
sigset_t mask;
int max_pfds = MIN_POLLS + POLLFDS_BASE;
@@ -623,6 +623,11 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
struct watch_descriptors wds = { .conf_wd = -1, .dir_wd = -1, .mp_wd = -1, };
struct vectors *vecs = trigger_data;
+ if (n_socks != 1) {
+ condlog(0, "uxsock: no socket fds");
+ exit_daemon();
+ return NULL;
+ }
condlog(3, "uxsock: startup listener");
polls = calloc(1, max_pfds * sizeof(*polls));
if (!polls) {
@@ -673,7 +678,7 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
}
}
if (num_clients < MAX_CLIENTS) {
- polls[POLLFD_UX].fd = ux_sock;
+ polls[POLLFD_UX].fd = ux_sock[0];
polls[POLLFD_UX].events = POLLIN;
} else {
/*
@@ -767,7 +772,7 @@ void *uxsock_listen(long ux_sock, void *trigger_data)
/* see if we got a new client */
if (polls[POLLFD_UX].revents & POLLIN) {
- new_client(ux_sock);
+ new_client(ux_sock[0]);
}
/* handle inotify events on config files */
diff --git a/multipathd/uxlsnr.h b/multipathd/uxlsnr.h
index 3e45930b..f07b1f8b 100644
--- a/multipathd/uxlsnr.h
+++ b/multipathd/uxlsnr.h
@@ -5,7 +5,6 @@
bool waiting_clients(void);
void uxsock_cleanup(void *arg);
-void *uxsock_listen(long ux_sock,
- void * trigger_data);
+void *uxsock_listen(int n_socks, long *ux_sock, void *trigger_data);
#endif

View File

@ -0,0 +1,194 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Wed, 12 Feb 2025 20:27:18 +0100
Subject: [PATCH] multipathd: allow receiving two socket fds from systemd
Add another ListenStream directive in multipathd.socket for a Unix pathname
socket. In multipathd, read both socket fds from systemd, and open both
when they are defined.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile.inc | 3 ++-
multipathd/main.c | 18 ++++++++++++------
multipathd/multipathd.socket.in | 3 ++-
multipathd/uxlsnr.c | 33 ++++++++++++++++++++++-----------
4 files changed, 38 insertions(+), 19 deletions(-)
diff --git a/Makefile.inc b/Makefile.inc
index 1ef3f7f8..07c0ae80 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -76,6 +76,7 @@ libudev_incdir := $(or $(shell $(PKG_CONFIG) --variable=includedir libudev),/usr
kernel_incdir := /usr/include
abstract_socket := @/org/kernel/linux/storage/multipathd
+pathname_socket := /run/multipathd.socket
ifeq ($(V),)
Q := @
@@ -166,4 +167,4 @@ NV_VERSION_SCRIPT = $(DEVLIB:%.so=%-nv.version)
%: %.in
@echo creating $@
- $(Q)sed 's:@CONFIGFILE@:'$(TGTDIR)$(configfile)':g;s:@CONFIGDIR@:'$(TGTDIR)$(configdir)':g;s:@STATE_DIR@:'$(TGTDIR)$(statedir)':g;s:@RUNTIME_DIR@:'$(runtimedir)':g;s/@MODPROBE_UNIT@/'$(MODPROBE_UNIT)'/g;s:@BINDIR@:'$(bindir)':g;s,@MPATH_SOCKET@,'$(abstract_socket)',g' $< >$@
+ $(Q)sed 's:@CONFIGFILE@:'$(TGTDIR)$(configfile)':g;s:@CONFIGDIR@:'$(TGTDIR)$(configdir)':g;s:@STATE_DIR@:'$(TGTDIR)$(statedir)':g;s:@RUNTIME_DIR@:'$(runtimedir)':g;s/@MODPROBE_UNIT@/'$(MODPROBE_UNIT)'/g;s:@BINDIR@:'$(bindir)':g;s,@ABSTRACT_SOCKET@,'$(abstract_socket)',g;s,@PATHNAME_SOCKET@,'$(pathname_socket)',g' $< >$@
diff --git a/multipathd/main.c b/multipathd/main.c
index 9f15d2f7..2fef0c64 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -1865,9 +1865,13 @@ static int get_systemd_sockets(long *ux_sock)
{
int num = sd_listen_fds(0);
- if (num > 1) {
+ if (num > 2) {
condlog(3, "sd_listen_fds returned %d fds", num);
return -1;
+ } else if (num == 2) {
+ ux_sock[0] = SD_LISTEN_FDS_START + 0;
+ ux_sock[1] = SD_LISTEN_FDS_START + 1;
+ condlog(3, "using fd %ld and %ld from sd_listen_fds", ux_sock[0], ux_sock[1]);
} else if (num == 1) {
ux_sock[0] = SD_LISTEN_FDS_START + 0;
condlog(3, "using fd %ld from sd_listen_fds", ux_sock[0]);
@@ -1885,16 +1889,18 @@ static int get_systemd_sockets(long *ux_sock __attribute__((unused)))
static void *
uxlsnrloop (void * ap)
{
- long ux_sock;
+ long ux_sock[2] = {-1, -1};
int num;
pthread_cleanup_push(rcu_unregister, NULL);
rcu_register_thread();
num = get_systemd_sockets(&ux_sock);
- if (num < 1)
- ux_sock = ux_socket_listen(DEFAULT_SOCKET);
- if (ux_sock == -1) {
+ if (num < 1) {
+ ux_sock[0] = ux_socket_listen(DEFAULT_SOCKET);
+ num = 1;
+ }
+ if (ux_sock[0] == -1) {
condlog(1, "could not create uxsock: %d", errno);
exit_daemon();
goto out;
@@ -1920,7 +1926,7 @@ uxlsnrloop (void * ap)
== DAEMON_CONFIGURE)
handle_signals(false);
- uxsock_listen(1, &ux_sock, ap);
+ uxsock_listen(num, ux_sock, ap);
out_sock:
pthread_cleanup_pop(1); /* uxsock_cleanup */
diff --git a/multipathd/multipathd.socket.in b/multipathd/multipathd.socket.in
index 4ed9c1ff..5ed24757 100644
--- a/multipathd/multipathd.socket.in
+++ b/multipathd/multipathd.socket.in
@@ -8,7 +8,8 @@ ConditionVirtualization=!container
Before=sockets.target
[Socket]
-ListenStream=@MPATH_SOCKET@
+ListenStream=@ABSTRACT_SOCKET@
+ListenStream=@PATHNAME_SOCKET@
[Install]
# Socket activation for multipathd is disabled by default.
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index 51b5e51d..bbf891ba 100644
--- a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -69,7 +69,8 @@ struct client {
/* Indices for array of poll fds */
enum {
- POLLFD_UX = 0,
+ POLLFD_UX1 = 0,
+ POLLFD_UX2,
POLLFD_NOTIFY,
POLLFD_IDLE,
POLLFDS_BASE,
@@ -164,9 +165,10 @@ void uxsock_cleanup(void *arg)
{
struct client *client_loop;
struct client *client_tmp;
- long ux_sock = (long)arg;
+ long *ux_sock = (long *)arg;
- close(ux_sock);
+ close(ux_sock[0]);
+ close(ux_sock[1]);
close(notify_fd);
list_for_each_entry_safe(client_loop, client_tmp, &clients, node) {
@@ -614,20 +616,24 @@ static void handle_client(struct client *c, struct vectors *vecs, short revents)
/*
* entry point
*/
-void *uxsock_listen(int n_socks, long *ux_sock, void *trigger_data)
+void *uxsock_listen(int n_socks, long *ux_sock_in, void *trigger_data)
{
sigset_t mask;
int max_pfds = MIN_POLLS + POLLFDS_BASE;
+ long ux_sock[2] = {-1, -1};
/* conf->sequence_nr will be 1 when uxsock_listen is first called */
unsigned int sequence_nr = 0;
struct watch_descriptors wds = { .conf_wd = -1, .dir_wd = -1, .mp_wd = -1, };
struct vectors *vecs = trigger_data;
- if (n_socks != 1) {
- condlog(0, "uxsock: no socket fds");
+ if (n_socks < 1 || n_socks > 2) {
+ condlog(0, "uxsock: unsupported number of socket fds");
exit_daemon();
return NULL;
- }
+ } else if (n_socks == 2)
+ ux_sock[1] = ux_sock_in[1];
+ ux_sock[0] = ux_sock_in[0];
+
condlog(3, "uxsock: startup listener");
polls = calloc(1, max_pfds * sizeof(*polls));
if (!polls) {
@@ -678,8 +684,10 @@ void *uxsock_listen(int n_socks, long *ux_sock, void *trigger_data)
}
}
if (num_clients < MAX_CLIENTS) {
- polls[POLLFD_UX].fd = ux_sock[0];
- polls[POLLFD_UX].events = POLLIN;
+ polls[POLLFD_UX1].fd = ux_sock[0];
+ polls[POLLFD_UX1].events = POLLIN;
+ polls[POLLFD_UX2].fd = ux_sock[1];
+ polls[POLLFD_UX2].events = POLLIN;
} else {
/*
* New clients can't connect, num_clients won't grow
@@ -687,7 +695,7 @@ void *uxsock_listen(int n_socks, long *ux_sock, void *trigger_data)
*/
condlog(1, "%s: max client connections reached, pausing polling",
__func__);
- polls[POLLFD_UX].fd = -1;
+ polls[POLLFD_UX1].fd = polls[POLLFD_UX2].fd = -1;
}
reset_watch(notify_fd, &wds, &sequence_nr);
@@ -771,9 +779,12 @@ void *uxsock_listen(int n_socks, long *ux_sock, void *trigger_data)
handle_signals(true);
/* see if we got a new client */
- if (polls[POLLFD_UX].revents & POLLIN) {
+ if (polls[POLLFD_UX1].revents & POLLIN) {
new_client(ux_sock[0]);
}
+ if (polls[POLLFD_UX2].revents & POLLIN) {
+ new_client(ux_sock[1]);
+ }
/* handle inotify events on config files */
if (polls[POLLFD_NOTIFY].revents & POLLIN)

View File

@ -0,0 +1,67 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Wed, 12 Feb 2025 20:35:36 +0100
Subject: [PATCH] multipathd: listen on pathname and abstract socket by default
Pass both ABSTRACT_SOCKET and PATHNAME_SOCKET to the compiler at
build time, and listen on both sockets by default.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile.inc | 2 +-
libmpathcmd/mpath_cmd.c | 2 +-
multipathd/main.c | 11 ++++++-----
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/Makefile.inc b/Makefile.inc
index 07c0ae80..69a0d64c 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -120,7 +120,7 @@ CPPFLAGS := $(CPPFLAGS) $(D_URCU_VERSION) \
-DRUNTIME_DIR=\"$(runtimedir)\" -DCONFIG_DIR=\"$(TGTDIR)$(configdir)\" \
-DDEFAULT_CONFIGFILE=\"$(TGTDIR)$(configfile)\" -DSTATE_DIR=\"$(TGTDIR)$(statedir)\" \
-DEXTRAVERSION=\"$(EXTRAVERSION)\" \
- -DDEFAULT_SOCKET=\"$(abstract_socket)\" \
+ -DABSTRACT_SOCKET=\"$(abstract_socket)\" -DPATHNAME_SOCKET=\"$(pathname_socket)\" \
-DWSTRINGOP_TRUNCATION=$(if $(WSTRINGOP_TRUNCATION),1,0) \
-MMD -MP
CFLAGS := -std=gnu99 $(CFLAGS) $(OPTFLAGS) $(WARNFLAGS) -pipe
diff --git a/libmpathcmd/mpath_cmd.c b/libmpathcmd/mpath_cmd.c
index 146e790d..54143fb1 100644
--- a/libmpathcmd/mpath_cmd.c
+++ b/libmpathcmd/mpath_cmd.c
@@ -113,7 +113,7 @@ int __mpath_connect(int nonblocking)
(void)fcntl(fd, F_SETFL, flags|O_NONBLOCK);
}
- len = mpath_fill_sockaddr__(&addr, DEFAULT_SOCKET);
+ len = mpath_fill_sockaddr__(&addr, ABSTRACT_SOCKET);
if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
int err = errno;
diff --git a/multipathd/main.c b/multipathd/main.c
index 2fef0c64..bdbcea49 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -1895,13 +1895,14 @@ uxlsnrloop (void * ap)
pthread_cleanup_push(rcu_unregister, NULL);
rcu_register_thread();
- num = get_systemd_sockets(&ux_sock);
+ num = get_systemd_sockets(ux_sock);
if (num < 1) {
- ux_sock[0] = ux_socket_listen(DEFAULT_SOCKET);
- num = 1;
+ ux_sock[0] = ux_socket_listen(ABSTRACT_SOCKET);
+ ux_sock[1] = ux_socket_listen(PATHNAME_SOCKET);
+ num = 2;
}
- if (ux_sock[0] == -1) {
- condlog(1, "could not create uxsock: %d", errno);
+ if (ux_sock[0] == -1 && ux_sock[1] == -1) {
+ condlog(1, "could not create sockets: %d", errno);
exit_daemon();
goto out;
}

View File

@ -0,0 +1,52 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Wed, 12 Feb 2025 20:45:11 +0100
Subject: [PATCH] libmpathcmd: try both abstract and pathname sockets
When connecting to the multipathd socket, try the pathname socket
first, then the abstract socket. Fail only if both connection attempts
fail.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathcmd/mpath_cmd.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/libmpathcmd/mpath_cmd.c b/libmpathcmd/mpath_cmd.c
index 54143fb1..267e3dd7 100644
--- a/libmpathcmd/mpath_cmd.c
+++ b/libmpathcmd/mpath_cmd.c
@@ -102,7 +102,10 @@ int __mpath_connect(int nonblocking)
size_t len;
struct sockaddr_un addr;
int flags = 0;
+ const char *const names[2] = {PATHNAME_SOCKET, ABSTRACT_SOCKET};
+ int name_idx = 0;
+retry:
fd = socket(AF_LOCAL, SOCK_STREAM, 0);
if (fd == -1)
return -1;
@@ -113,13 +116,17 @@ int __mpath_connect(int nonblocking)
(void)fcntl(fd, F_SETFL, flags|O_NONBLOCK);
}
- len = mpath_fill_sockaddr__(&addr, ABSTRACT_SOCKET);
+ len = mpath_fill_sockaddr__(&addr, names[name_idx]);
if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
int err = errno;
close(fd);
- errno = err;
- return -1;
+ if (++name_idx == 1)
+ goto retry;
+ else {
+ errno = err;
+ return -1;
+ }
}
if (nonblocking && flags != -1)

View File

@ -0,0 +1,53 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Wed, 12 Feb 2025 21:17:41 +0100
Subject: [PATCH] libmpathcmd: honor MULTIPATH_SOCKET_NAME environment variable
In systemd installments, users can already override the socket names
that systemd listens on. With this patch, clients using libmpathcmd
can be customized to use a non-standard socket by setting an environment
variable.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathcmd/mpath_cmd.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/libmpathcmd/mpath_cmd.c b/libmpathcmd/mpath_cmd.c
index 267e3dd7..7f805abb 100644
--- a/libmpathcmd/mpath_cmd.c
+++ b/libmpathcmd/mpath_cmd.c
@@ -20,6 +20,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
+#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -104,6 +105,7 @@ int __mpath_connect(int nonblocking)
int flags = 0;
const char *const names[2] = {PATHNAME_SOCKET, ABSTRACT_SOCKET};
int name_idx = 0;
+ const char *env_name = getenv("MULTIPATH_SOCKET_NAME"), *name;
retry:
fd = socket(AF_LOCAL, SOCK_STREAM, 0);
@@ -116,12 +118,13 @@ retry:
(void)fcntl(fd, F_SETFL, flags|O_NONBLOCK);
}
- len = mpath_fill_sockaddr__(&addr, names[name_idx]);
+ name = env_name ? env_name : names[name_idx];
+ len = mpath_fill_sockaddr__(&addr, name);
if (connect(fd, (struct sockaddr *)&addr, len) == -1) {
int err = errno;
close(fd);
- if (++name_idx == 1)
+ if (name != env_name && ++name_idx == 1)
goto retry;
else {
errno = err;

View File

@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Fri, 14 Feb 2025 22:18:06 +0100
Subject: [PATCH] multipathd: honor MULTIPATH_SOCKET_NAME environment variable
If multipathd is started via socket activation, it will obtain
sockets from systemd. The names of these sockets, and whether
the abstract and / or pathname socket is created, is configurable
in the systemd unit file.
Add support for passing a socket name via the environment, so that
it's possible to configure the socket name at runtime even without
socket activation. In this case, only this single socket will be created.
If creating the socket fails, multipathd startup will fail, too.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/main.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/multipathd/main.c b/multipathd/main.c
index bdbcea49..0fa4a404 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -1891,11 +1891,16 @@ uxlsnrloop (void * ap)
{
long ux_sock[2] = {-1, -1};
int num;
+ const char *env_name = getenv("MULTIPATH_SOCKET_NAME");
pthread_cleanup_push(rcu_unregister, NULL);
rcu_register_thread();
num = get_systemd_sockets(ux_sock);
+ if (num < 1 && env_name != NULL) {
+ ux_sock[0] = ux_socket_listen(env_name);
+ num = 1;
+ }
if (num < 1) {
ux_sock[0] = ux_socket_listen(ABSTRACT_SOCKET);
ux_sock[1] = ux_socket_listen(PATHNAME_SOCKET);

View File

@ -0,0 +1,25 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 6 Mar 2025 16:56:34 -0500
Subject: [PATCH] multipath: clean up find_multipaths documentation
The preferred term is "on" instead of "yes".
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipath/multipath.conf.5.in | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/multipath/multipath.conf.5.in b/multipath/multipath.conf.5.in
index a7543939..31740a1f 100644
--- a/multipath/multipath.conf.5.in
+++ b/multipath/multipath.conf.5.in
@@ -1213,7 +1213,7 @@ Both multipathd and multipath treat every non-blacklisted device as multipath
device path.
.TP
.I smart
-This differs from \fIfind_multipaths yes\fR only in
+This differs from \fIfind_multipaths on\fR only in
the way it treats new devices for which only one path has been
detected yet. When such a device is first encountered in udev rules, it is
treated as a multipath device. multipathd waits whether additional paths with

View File

@ -0,0 +1,63 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 6 Mar 2025 18:52:02 -0500
Subject: [PATCH] multipathd: Add multipathd man page section about sockets
Add a section with information about how to communicate with the
multipathd daemon to the man page. Also mention that multipathd
CLI commands can be run without the -k option.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/multipathd.8.in | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/multipathd/multipathd.8.in b/multipathd/multipathd.8.in
index 315884eb..5b1aeb42 100644
--- a/multipathd/multipathd.8.in
+++ b/multipathd/multipathd.8.in
@@ -83,6 +83,11 @@ multipathd executes the given command (see \fBCOMMANDS\fR below). If the
command contains whitespace or shell special characters, it needs to be quoted
like in \fImultipathd -k'show topology'\fR. No whitespace is allowed between
the \fB-k\fR and the command string.
+
+Commands can also be issued without using \fB-k\fR. In this case, the command
+string should not be quoted. Command arguments that contain whitespace or
+special characters still need to be quoted, like in \fImultipathd show paths
+format "%n %w"\fR
.
.TP
.B \-k
@@ -103,6 +108,32 @@ multipath devices on dmevents. Use this flag to force it to use the old event
waiting method, based on creating a separate thread for each device.
.
.
+.\" ----------------------------------------------------------------------------
+.SH COMMUNICATING WITH MULTIPATHD
+.\" ----------------------------------------------------------------------------
+
+In addition to the multipathd CLI, the \fBlibmpathcmd\fR library can be used to
+send commands (see \fBCOMMANDS\fR below) to the multipathd daemon from other
+programs. By default, multipathd listens on both the \fI@ABSTRACT_SOCKET@\fR
+abstract namespace socket and the \fI@PATHNAME_SOCKET@\fR socket file.
+libmpathcmd will use either of these sockets to connect to multipathd. The
+socket file can be useful to communicate with multipathd from different
+namespaces since it can be bind mounted in them, unlike the abstract namespace
+socket. Multipathd will accept \fBlist|show\fR commands from any user. All
+other commands must be issued by root.
+
+It is possible to change the sockets that multipathd listens on. If
+\fImultipathd.socket\fR is running, multipathd will use the sockets it listens
+on. A maximum of two sockets can be defined by \fImultipathd.socket\fR, and by
+default it listens on \fI@ABSTRACT_SOCKET@\fR and \fI@PATHNAME_SOCKET@\fR. If
+\fImultipathd.socket\fR is not running, a single socket can be configured for
+listening on by setting the \fIMULTIPATH_SOCKET_NAME\fR environment variable
+when starting multipathd. This environment variable must also be set to make
+multipathd CLI commands (or any other program using libmpathcmd) connect to the
+multipathd daemon using a non-default socket, regardless of whether that socket
+was set for the daemon using \fImultipathd.socket\fR or the
+\fIMULTIPATH_SOCKET_NAME\fR environment variable.
+.
.
.\" ----------------------------------------------------------------------------
.SH COMMANDS

View File

@ -0,0 +1,45 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 2 Apr 2025 19:13:19 -0400
Subject: [PATCH] multipathd: monitor new multipath dev even if we can't update
it
If a multipath device was created by the multipath command, multipathd
might not agree with how the device was created. ev_add_map() can reload
the device with a different table by calling add_map_without_path() ->
update_map(). If this reloading of the map failed, multipathd was simply
ignoring the multipath device, even though it still existed.
One way that reloading can fail is if a path that multipathd already has
initialized goes offline. If a multipath device is created by the
multipath command while the path is offline, it will not use the offline
path, since multipath won't be able to get the necessary pathinfo.
However, multipathd will already have the pathinfo for the path, and may
not even know that it's offline, since the path is an orphan. When it
tries to reload the device, it will include the offline path, and the
reload will fail.
Instead of ignoring the device if it can't reload it, multipathd should
just montior it as it is. When the path device is no longer offline, it
can be added back to the multipath device by calling
"multipathd reconfigure" or "multipathd add path <path>".
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
multipathd/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 0fa4a404..8ce36a3c 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -686,7 +686,7 @@ retry:
}
fail:
- if (new_map && (retries < 0 || wait_for_events(mpp, vecs))) {
+ if (new_map && wait_for_events(mpp, vecs)) {
condlog(0, "%s: failed to create new map", mpp->alias);
remove_map(mpp, vecs->pathvec, vecs->mpvec);
return 1;

View File

@ -0,0 +1,74 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 2 Apr 2025 19:13:20 -0400
Subject: [PATCH] libmultipath: add helper function check_path_wwid_change
Wrap some code from select_recheck_wwid() in a helper function. A future
patch will call this code from a different function.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmultipath/discovery.c | 12 +++++++++++-
libmultipath/discovery.h | 2 +-
libmultipath/propsel.c | 4 +---
3 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index 3bcd94ce..a1284e73 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -2244,7 +2244,7 @@ static ssize_t uid_fallback(struct path *pp, int path_state,
return len;
}
-bool has_uid_fallback(struct path *pp)
+static bool has_uid_fallback(const struct path *pp)
{
/*
* Falling back to direct WWID determination is dangerous
@@ -2265,6 +2265,16 @@ bool has_uid_fallback(struct path *pp)
!strcmp(pp->uid_attribute, ""))));
}
+bool can_recheck_wwid(const struct path *pp)
+{
+ /*
+ * check_path_wwid_change() only works for scsi devices, and it
+ * is only guaranteed to give the same WWID if the path uses
+ * the default uid_attribute
+ */
+ return (pp->bus == SYSFS_BUS_SCSI && has_uid_fallback(pp));
+}
+
int
get_uid (struct path * pp, int path_state, struct udev_device *udev,
int allow_fallback)
diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h
index acd51792..2298abac 100644
--- a/libmultipath/discovery.h
+++ b/libmultipath/discovery.h
@@ -53,7 +53,7 @@ ssize_t sysfs_get_inquiry(struct udev_device *udev,
unsigned char *buff, size_t len);
int sysfs_get_asymmetric_access_state(struct path *pp,
char *buff, int buflen);
-bool has_uid_fallback(struct path *pp);
+bool can_recheck_wwid(const struct path *pp);
int get_uid(struct path * pp, int path_state, struct udev_device *udev,
int allow_fallback);
bool is_vpd_page_supported(int fd, int pg);
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
index e2dcb316..5ad0b78c 100644
--- a/libmultipath/propsel.c
+++ b/libmultipath/propsel.c
@@ -734,9 +734,7 @@ int select_recheck_wwid(struct config *conf, struct path * pp)
pp_set_conf(recheck_wwid);
pp_set_default(recheck_wwid, DEFAULT_RECHECK_WWID);
out:
- if (pp->recheck_wwid == RECHECK_WWID_ON &&
- (pp->bus != SYSFS_BUS_SCSI ||
- !has_uid_fallback(pp))) {
+ if (pp->recheck_wwid == RECHECK_WWID_ON && !can_recheck_wwid(pp)) {
pp->recheck_wwid = RECHECK_WWID_OFF;
origin = "(setting: unsupported by device type/config)";
}

View File

@ -0,0 +1,202 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 2 Apr 2025 19:13:21 -0400
Subject: [PATCH] multipathd: re-add paths skipped because they were offline
When a new device is added by the multipath command, multipathd may know
of other paths that cannot be added to the device because they are
currently offline. Instead of ignoring these paths, multipathd will now
re-add them when they come back online. To do this, it multipathd needs
a new path variable add_when_online, to track devices that could not be
added to an existing multipath device because they were offline. These
paths are handled along with the other uninitialized paths.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmultipath/libmultipath.version | 5 +++
libmultipath/print.c | 5 ++-
libmultipath/structs.h | 1 +
libmultipath/structs_vec.c | 5 +++
multipathd/main.c | 53 ++++++++++++++++++++++++++++++-
multipathd/multipathd.8.in | 5 +--
6 files changed, 70 insertions(+), 4 deletions(-)
diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index eb511749..0b7a1a2b 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -241,3 +241,8 @@ global:
local:
*;
};
+
+LIBMULTIPATH_24.0.1 {
+global:
+ can_recheck_wwid;
+} LIBMULTIPATH_24.0.0;
diff --git a/libmultipath/print.c b/libmultipath/print.c
index d592001d..fbed2dd5 100644
--- a/libmultipath/print.c
+++ b/libmultipath/print.c
@@ -664,8 +664,11 @@ snprint_path_serial (struct strbuf *buff, const struct path * pp)
static int
snprint_path_mpp (struct strbuf *buff, const struct path * pp)
{
- if (!pp->mpp)
+ if (!pp->mpp) {
+ if (pp->add_when_online)
+ return append_strbuf_str(buff, "[offline]");
return append_strbuf_str(buff, "[orphan]");
+ }
if (!pp->mpp->alias)
return append_strbuf_str(buff, "[unknown]");
return snprint_str(buff, pp->mpp->alias);
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index dbaf4d43..5dc00fbc 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -396,6 +396,7 @@ struct path {
int eh_deadline;
bool is_checked;
bool can_use_env_uid;
+ bool add_when_online;
unsigned int checker_timeout;
/* configlet pointers */
vector hwe;
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index ccc4efc7..23e135ce 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -374,6 +374,9 @@ static void orphan_paths(vector pathvec, struct multipath *mpp, const char *reas
free_path(pp);
} else
orphan_path(pp, reason);
+ } else if (pp->add_when_online &&
+ strncmp(mpp->wwid, pp->wwid, WWID_SIZE) == 0) {
+ pp->add_when_online = false;
}
}
}
@@ -560,6 +563,8 @@ void sync_paths(struct multipath *mpp, vector pathvec)
found = 0;
vector_foreach_slot(mpp->pg, pgp, j) {
if (find_slot(pgp->paths, (void *)pp) != -1) {
+ if (pp->add_when_online)
+ pp->add_when_online = false;
found = 1;
break;
}
diff --git a/multipathd/main.c b/multipathd/main.c
index 8ce36a3c..a565ade5 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -651,11 +651,44 @@ pr_register_active_paths(struct multipath *mpp)
}
}
+static void
+save_offline_paths(const struct multipath *mpp, vector offline_paths)
+{
+ unsigned int i, j;
+ struct path *pp;
+ struct pathgroup *pgp;
+
+ vector_foreach_slot (mpp->pg, pgp, i)
+ vector_foreach_slot (pgp->paths, pp, j)
+ if (pp->initialized == INIT_OK && pp->offline)
+ /* ignore failures storing the paths. */
+ store_path(offline_paths, pp);
+}
+
+static void
+handle_orphaned_offline_paths(vector offline_paths)
+{
+ unsigned int i;
+ struct path *pp;
+
+ vector_foreach_slot (offline_paths, pp, i)
+ if (pp->mpp == NULL)
+ pp->add_when_online = true;
+}
+
+static void
+cleanup_reset_vec(struct _vector **v)
+{
+ vector_reset(*v);
+}
+
static int
update_map (struct multipath *mpp, struct vectors *vecs, int new_map)
{
int retries = 3;
char *params __attribute__((cleanup(cleanup_charp))) = NULL;
+ struct _vector offline_paths_vec = { .allocated = 0 };
+ vector offline_paths __attribute__((cleanup(cleanup_reset_vec))) = &offline_paths_vec;
retry:
condlog(4, "%s: updating new map", mpp->alias);
@@ -692,6 +725,9 @@ fail:
return 1;
}
+ if (new_map && retries < 0)
+ save_offline_paths(mpp, offline_paths);
+
if (setup_multipath(vecs, mpp))
return 1;
@@ -702,6 +738,9 @@ fail:
if (mpp->prflag == PRFLAG_SET)
pr_register_active_paths(mpp);
+ if (VECTOR_SIZE(offline_paths) != 0)
+ handle_orphaned_offline_paths(offline_paths);
+
if (retries < 0)
condlog(0, "%s: failed reload in new map update", mpp->alias);
return 0;
@@ -2360,7 +2399,8 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
bool need_reload;
if (((pp->initialized == INIT_OK || pp->initialized == INIT_PARTIAL ||
- pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp) ||
+ pp->initialized == INIT_REQUESTED_UDEV) && !pp->mpp &&
+ !pp->add_when_online) ||
pp->initialized == INIT_REMOVED)
return 0;
@@ -2481,6 +2521,17 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
*/
pp->checkint = max_checkint;
}
+ } else if (pp->initialized == INIT_OK && pp->add_when_online &&
+ (newstate == PATH_UP || newstate == PATH_GHOST)) {
+ pp->add_when_online = false;
+ if (can_recheck_wwid(pp) &&
+ check_path_wwid_change(pp)) {
+ condlog(0, "%s: path wwid change detected. Removing", pp->dev);
+ handle_path_wwid_change(pp, vecs);
+ return 0;
+ }
+ ev_add_path(pp, vecs, 1);
+ pp->tick = 1;
}
return 0;
}
diff --git a/multipathd/multipathd.8.in b/multipathd/multipathd.8.in
index 5b1aeb42..342e363e 100644
--- a/multipathd/multipathd.8.in
+++ b/multipathd/multipathd.8.in
@@ -597,8 +597,9 @@ The device serial number.
The device marginal state, either \fImarginal\fR or \fInormal\fR.
.TP
.B %m
-The multipath device that this device is a path of, or \fI[orphan]\fR if
-it is not part of any multipath device.
+The multipath device that this device is a path of, or \fI[offline]\fR
+if this device could not be added to a device because it is offline or
+\fI[orphan]\fR if it is not part of any multipath device.
.TP
.B %N
The host World Wide Node Name (WWNN) of the device, if any.

View File

@ -0,0 +1,37 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Xose Vazquez Perez <xose.vazquez@gmail.com>
Date: Thu, 5 Dec 2024 23:49:16 +0100
Subject: [PATCH] multipath-tools: add HPE MSA Gen7 (2070/2072) to hwtable
https://support.hpe.com/connect/s/product?kmpmoid=1014856412
Just guessing, confirmation from manufacturer is needed.
Cc: Jon Paul <Jon.Paul@hpe.com>
Cc: Martin Wilck <mwilck@suse.com>
Cc: Benjamin Marzinski <bmarzins@redhat.com>
Cc: Christophe Varoqui <christophe.varoqui@opensvc.com>
Cc: DM-DEVEL ML <dm-devel@lists.linux.dev>
Signed-off-by: Xose Vazquez Perez <xose.vazquez@gmail.com>
Acked-by: Jon Paul <jon.paul@hpe.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/hwtable.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c
index 9e008df6..17ba5b45 100644
--- a/libmultipath/hwtable.c
+++ b/libmultipath/hwtable.c
@@ -190,9 +190,9 @@ static struct hwentry default_hw[] = {
.prio_name = PRIO_ALUA,
},
{
- /* MSA 1040, 1050, 1060, 2040, 2050 and 2060 families */
+ /* MSA 1040, 1050, 1060, 2040, 2050, 2060 and 2070 families */
.vendor = "(HP|HPE)",
- .product = "MSA [12]0[456]0 (SAN|SAS|FC|iSCSI)",
+ .product = "MSA [12]0[4567]0 (SAN|SAS|FC|iSCSI)",
.pgpolicy = GROUP_BY_PRIO,
.pgfailback = -FAILBACK_IMMEDIATE,
.no_path_retry = 18,

View File

@ -0,0 +1,84 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 20 Aug 2025 18:25:03 -0400
Subject: [PATCH] libmultipath: fix crash in print_foreign_topology
print_foreign_topology() called get_paths() to get a vector of
(struct gen_path *) items and then called get_multipath_layout__(),
which expects a vector of (struct gen_multipath *) items, with the path
vector. This can easily end badly. Fix it to correctly call
get_path_layout__(), and rename width to p_width in the functions that
end up calling snprint_multipath_topology__(), which is expecting to get
passed the path field widths.
Signed-off-by: Lin Li <lilin@redhat.com>
---
libmultipath/foreign.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libmultipath/foreign.c b/libmultipath/foreign.c
index d01a5ef0..b91e3898 100644
--- a/libmultipath/foreign.c
+++ b/libmultipath/foreign.c
@@ -500,7 +500,7 @@ void foreign_multipath_layout(fieldwidth_t *width)
}
static int __snprint_foreign_topology(struct strbuf *buf, int verbosity,
- const fieldwidth_t *width)
+ const fieldwidth_t *p_width)
{
struct foreign *fgn;
int i;
@@ -518,7 +518,7 @@ static int __snprint_foreign_topology(struct strbuf *buf, int verbosity,
if (vec != NULL) {
vector_foreach_slot(vec, gm, j) {
if (_snprint_multipath_topology(
- gm, buf, verbosity, width) < 0)
+ gm, buf, verbosity, p_width) < 0)
break;
}
}
@@ -530,7 +530,7 @@ static int __snprint_foreign_topology(struct strbuf *buf, int verbosity,
}
int snprint_foreign_topology(struct strbuf *buf, int verbosity,
- const fieldwidth_t *width)
+ const fieldwidth_t *p_width)
{
int rc;
@@ -540,7 +540,7 @@ int snprint_foreign_topology(struct strbuf *buf, int verbosity,
return 0;
}
pthread_cleanup_push(unlock_foreigns, NULL);
- rc = __snprint_foreign_topology(buf, verbosity, width);
+ rc = __snprint_foreign_topology(buf, verbosity, p_width);
pthread_cleanup_pop(1);
return rc;
}
@@ -550,9 +550,9 @@ void print_foreign_topology(int verbosity)
STRBUF_ON_STACK(buf);
struct foreign *fgn;
int i;
- fieldwidth_t *width __attribute__((cleanup(cleanup_ucharp))) = NULL;
+ fieldwidth_t *p_width __attribute__((cleanup(cleanup_ucharp))) = NULL;
- if ((width = alloc_path_layout()) == NULL)
+ if ((p_width = alloc_path_layout()) == NULL)
return;
rdlock_foreigns();
if (foreigns == NULL) {
@@ -566,11 +566,11 @@ void print_foreign_topology(int verbosity)
fgn->lock(fgn->context);
pthread_cleanup_push(fgn->unlock, fgn->context);
vec = fgn->get_paths(fgn->context);
- _get_multipath_layout(vec, LAYOUT_RESET_NOT, width);
+ _get_path_layout(vec, LAYOUT_RESET_NOT, p_width);
fgn->release_paths(fgn->context, vec);
pthread_cleanup_pop(1);
}
- __snprint_foreign_topology(&buf, verbosity, width);
+ __snprint_foreign_topology(&buf, verbosity, p_width);
pthread_cleanup_pop(1);
printf("%s", get_strbuf_str(&buf));
}

View File

@ -0,0 +1,72 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Mon, 5 May 2025 12:47:47 +0200
Subject: [PATCH] libmpathpersist: fix memory leak in mpath_prout_rel()
Found by Fedora's static analysis [1].
[1] https://openscanhub.fedoraproject.org/task/51915/log/device-mapper-multipath-0.11.1-1.fc43/scan-results.html#def44
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 178c2f54..7df74fb7 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -460,10 +460,10 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
int count = 0;
int status = MPATH_PR_SUCCESS;
struct prin_resp resp;
- struct prout_param_descriptor *pamp;
+ struct prout_param_descriptor *pamp = NULL;
struct prin_resp *pr_buff;
int length;
- struct transportid *pptr;
+ struct transportid *pptr = NULL;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
@@ -570,7 +570,7 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
pamp = (struct prout_param_descriptor *)malloc (length);
if (!pamp){
condlog (0, "%s: failed to alloc pr out parameter.", mpp->wwid);
- goto out1;
+ goto out;
}
memset(pamp, 0, length);
@@ -580,6 +580,7 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
condlog (0, "%s: failed to alloc pr out transportid.", mpp->wwid);
goto out1;
}
+ pptr = pamp->trnptid_list[0];
if (get_be64(mpp->reservation_key)){
memcpy (pamp->key, &mpp->reservation_key, 8);
@@ -591,11 +592,10 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
if (status) {
condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid);
- goto out1;
+ goto out2;
}
pamp->num_transportid = 1;
- pptr=pamp->trnptid_list[0];
for (i = 0; i < num; i++){
if (get_be64(mpp->reservation_key) &&
@@ -639,7 +639,7 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
}
-
+out2:
free(pptr);
out1:
free (pamp);

View File

@ -0,0 +1,164 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 3 Jun 2025 23:35:03 -0400
Subject: [PATCH] libmpathpersist: retry commands on other paths in
mpath_prout_common
mpath_prout_common() will only try sending the prout command to one
path. If that fails, it will give up. There are a number of cases where
it is reasonable to assume that sending the command down another path
could succeed. Keep trying other available paths in these cases.
Do do this, this patch adds a new error code, MPATH_PR_RETRYABLE_ERROR.
libmpathpersist will not return this error to users. It will change it
to MPATH_PR_OTHER if it fails trying on all the paths.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
.github/actions/spelling/expect.txt | 1 +
libmpathpersist/mpath_persist.h | 3 +++
libmpathpersist/mpath_persist_int.c | 13 +++++++++----
libmpathpersist/mpath_pr_ioctl.c | 25 ++++++++++++++-----------
4 files changed, 27 insertions(+), 15 deletions(-)
diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt
index def43a52..e2d4acf9 100644
--- a/.github/actions/spelling/expect.txt
+++ b/.github/actions/spelling/expect.txt
@@ -169,6 +169,7 @@ reconfig
redhat
restorequeueing
retrigger
+RETRYABLE
rhabarber
rootprefix
rootprefixdir
diff --git a/libmpathpersist/mpath_persist.h b/libmpathpersist/mpath_persist.h
index 0046f120..b83fc3bd 100644
--- a/libmpathpersist/mpath_persist.h
+++ b/libmpathpersist/mpath_persist.h
@@ -63,6 +63,9 @@ extern "C" {
#define MPATH_PR_THREAD_ERROR 14 /* pthreads error (e.g. unable to create new thread) */
#define MPATH_PR_OTHER 15 /*other error/warning has occurred(transport
or driver error) */
+#define MPATH_PR_RETRYABLE_ERROR 16 /* error that might be succeed
+ down another path. Internal
+ only. */
/* PR MASK */
#define MPATH_F_APTPL_MASK 0x01 /* APTPL MASK*/
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 7df74fb7..db105c6b 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -92,7 +92,7 @@ static int mpath_prin_activepath (struct multipath *mpp, int rq_servact,
}
}
}
- return ret;
+ return (ret == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : ret;
}
void *mpath_alloc_prin_response(int prin_sa)
@@ -382,7 +382,7 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
}
pthread_attr_destroy(&attr);
- return (status);
+ return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
}
static int send_prout_activepath(char *dev, int rq_servact, int rq_scope,
@@ -426,6 +426,7 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
int i,j, ret;
struct pathgroup *pgp = NULL;
struct path *pp = NULL;
+ bool found = false;
vector_foreach_slot (mpp->pg, pgp, j){
vector_foreach_slot (pgp->paths, pp, i){
@@ -436,12 +437,16 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
}
condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
+ found = true;
ret = send_prout_activepath(pp->dev, rq_servact,
rq_scope, rq_type,
paramp, noisy);
- return ret ;
+ if (ret != MPATH_PR_RETRYABLE_ERROR)
+ return ret;
}
}
+ if (found)
+ return MPATH_PR_OTHER;
condlog (0, "%s: no path available", mpp->wwid);
return MPATH_PR_DMMP_ERROR;
}
@@ -645,7 +650,7 @@ out1:
free (pamp);
out:
free (pr_buff);
- return (status);
+ return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
}
int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c
index 093ec71b..7e1d2896 100644
--- a/libmpathpersist/mpath_pr_ioctl.c
+++ b/libmpathpersist/mpath_pr_ioctl.c
@@ -52,7 +52,7 @@ int prout_do_scsi_ioctl(char * dev, int rq_servact, int rq_scope,
fd = open(devname, O_RDONLY);
if(fd < 0){
condlog (1, "%s: unable to open device.", dev);
- return MPATH_PR_FILE_ERROR;
+ return MPATH_PR_RETRYABLE_ERROR;
}
unsigned char cdb[MPATH_PROUT_CMDLEN] =
@@ -123,14 +123,17 @@ retry :
goto retry;
}
- if (((status == MPATH_PR_SENSE_NOT_READY )&& (Sensedata.ASC == 0x04)&&
- (Sensedata.ASCQ == 0x07))&& (retry > 0))
- {
- usleep(1000);
- --retry;
- condlog(3, "%s: retrying for sense 02/04/07."
- " Remaining retries = %d", dev, retry);
- goto retry;
+ if (status == MPATH_PR_SENSE_NOT_READY) {
+ if (Sensedata.ASC == 0x04 && Sensedata.ASCQ == 0x07 && retry > 0) {
+ usleep(1000);
+ --retry;
+ condlog(3,
+ "%s: retrying for sense 02/04/07."
+ " Remaining retries = %d",
+ dev, retry);
+ goto retry;
+ } else
+ status = MPATH_PR_RETRYABLE_ERROR;
}
close(fd);
@@ -342,7 +345,7 @@ int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int
fd = open(devname, O_RDONLY);
if(fd < 0){
condlog(0, "%s: Unable to open device ", dev);
- return MPATH_PR_FILE_ERROR;
+ return MPATH_PR_RETRYABLE_ERROR;
}
if (mpath_mx_alloc_len)
@@ -488,7 +491,7 @@ int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr,
case DID_OK :
break;
default :
- return MPATH_PR_OTHER;
+ return MPATH_PR_RETRYABLE_ERROR;
}
switch(io_hdr.driver_status)
{

View File

@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 3 Jun 2025 23:35:04 -0400
Subject: [PATCH] libmpathpersist: check released key against the reservation
key
According to the SCSI Spec, if a persistent reservation RELEASE is
issued using the correct key for the I_T_L Nexus that it is issued on
but the reservation is held by a different key, the command should
return Success and do nothing. When libmpathpersist tried to release a
reservation that was held by a different key, it ended up using the
failback code designed to release a reservation held by a failed path to
release it anyways. This means that any node could release a scsi
persistent reservation held by another node. Fix this to follow the SCSI
Spec.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index db105c6b..128671e5 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -551,6 +551,12 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
condlog (2, "%s: Path holding reservation is released.", mpp->wwid);
return MPATH_PR_SUCCESS;
}
+ if (!get_be64(mpp->reservation_key) ||
+ memcmp(&mpp->reservation_key, resp.prin_descriptor.prin_readresv.key, 8)) {
+ condlog(2, "%s: Releasing key not holding reservation.", mpp->wwid);
+ return MPATH_PR_SUCCESS;
+ }
+
condlog (2, "%s: Path holding reservation is not available.", mpp->wwid);
pr_buff = mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA);

View File

@ -0,0 +1,98 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:46 -0400
Subject: [PATCH] multipathd: remove thread from mpath_pr_event_handle
mpath_pr_event_handle() creates a separate thread to do the persistent
reservation work, but it doesn't take any advantage of the work being
done in another thread. Merge mpath_pr_event_handle() and
mpath_pr_event_handler_fn() into a single function with no separate
thread.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
multipathd/main.c | 50 +++++++++++------------------------------------
1 file changed, 11 insertions(+), 39 deletions(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index a565ade5..04ca47d1 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -89,8 +89,7 @@
#define CMDSIZE 160
#define MSG_SIZE 32
-int mpath_pr_event_handle(struct path *pp);
-void * mpath_pr_event_handler_fn (void * );
+static void mpath_pr_event_handle(struct path *pp);
#define LOG_MSG(lvl, pp) \
do { \
@@ -3945,17 +3944,24 @@ main (int argc, char *argv[])
return (child(NULL));
}
-void * mpath_pr_event_handler_fn (void * pathp )
+static void mpath_pr_event_handle(struct path *pp)
{
struct multipath * mpp;
unsigned int i;
int ret, isFound;
- struct path * pp = (struct path *)pathp;
struct prout_param_descriptor *param;
struct prin_resp *resp;
- rcu_register_thread();
mpp = pp->mpp;
+ if (pp->bus != SYSFS_BUS_SCSI) {
+ mpp->prflag = PRFLAG_UNSET;
+ return;
+ }
+
+ if (!get_be64(mpp->reservation_key)) {
+ mpp->prflag = PRFLAG_UNSET;
+ return;
+ }
resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
if (!resp){
@@ -4022,38 +4028,4 @@ void * mpath_pr_event_handler_fn (void * pathp )
out:
if (resp)
free(resp);
- rcu_unregister_thread();
- return NULL;
-}
-
-int mpath_pr_event_handle(struct path *pp)
-{
- pthread_t thread;
- int rc;
- pthread_attr_t attr;
- struct multipath * mpp;
-
- if (pp->bus != SYSFS_BUS_SCSI)
- goto no_pr;
-
- mpp = pp->mpp;
-
- if (!get_be64(mpp->reservation_key))
- goto no_pr;
-
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
- rc = pthread_create(&thread, NULL , mpath_pr_event_handler_fn, pp);
- if (rc) {
- condlog(0, "%s: ERROR; return code from pthread_create() is %d", pp->dev, rc);
- return -1;
- }
- pthread_attr_destroy(&attr);
- rc = pthread_join(thread, NULL);
- return 0;
-
-no_pr:
- pp->mpp->prflag = PRFLAG_UNSET;
- return 0;
}

View File

@ -0,0 +1,47 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:47 -0400
Subject: [PATCH] libmpathpersist: remove uneeded wrapper function.
mpath_send_prin_activepath() just calls prin_do_scsi_ioctl() with exactly
the same arguments that it is passed, and returns the same return. remove
it.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 14 +-------------
1 file changed, 1 insertion(+), 13 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 128671e5..fb1d0081 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -49,17 +49,6 @@ struct threadinfo {
struct prout_param param;
};
-static int mpath_send_prin_activepath (char * dev, int rq_servact,
- struct prin_resp * resp, int noisy)
-{
-
- int rc;
-
- rc = prin_do_scsi_ioctl(dev, rq_servact, resp, noisy);
-
- return (rc);
-}
-
static int mpath_prin_activepath (struct multipath *mpp, int rq_servact,
struct prin_resp * resp, int noisy)
{
@@ -80,8 +69,7 @@ static int mpath_prin_activepath (struct multipath *mpp, int rq_servact,
condlog(3, "%s: sending pr in command to %s ",
mpp->wwid, pp->dev);
- ret = mpath_send_prin_activepath(pp->dev, rq_servact,
- resp, noisy);
+ ret = prin_do_scsi_ioctl(pp->dev, rq_servact, resp, noisy);
switch(ret)
{
case MPATH_PR_SUCCESS:

View File

@ -0,0 +1,102 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:48 -0400
Subject: [PATCH] libmpathpersist: reduce log level for persistent reservation
checking
Move logging of minor expected behavior to INFO level. Modify the log
level of some messages by whether or not mpp->prflag changed values.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 34 +++++++++++++++++++----------
1 file changed, 22 insertions(+), 12 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index fb1d0081..6233e6b0 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -60,7 +60,7 @@ static int mpath_prin_activepath (struct multipath *mpp, int rq_servact,
vector_foreach_slot (pgp->paths, pp, i){
if (!((pp->state == PATH_UP) ||
(pp->state == PATH_GHOST))){
- condlog(2, "%s: %s not available. Skip.",
+ condlog(3, "%s: %s not available. Skip.",
mpp->wwid, pp->dev);
condlog(3, "%s: status = %d.",
mpp->wwid, pp->state);
@@ -733,12 +733,14 @@ int update_map_pr(struct multipath *mpp)
struct prin_resp *resp;
unsigned int i;
int ret = MPATH_PR_OTHER, isFound;
+ bool was_set = (mpp->prflag == PRFLAG_SET);
if (!get_be64(mpp->reservation_key))
{
/* Nothing to do. Assuming pr mgmt feature is disabled*/
mpp->prflag = PRFLAG_UNSET;
- condlog(4, "%s: reservation_key not set in multipath.conf",
+ condlog(was_set ? 2 : 4,
+ "%s: reservation_key not set in multipath.conf",
mpp->alias);
return MPATH_PR_SUCCESS;
}
@@ -751,8 +753,7 @@ int update_map_pr(struct multipath *mpp)
}
if (count_active_paths(mpp) == 0)
{
- condlog(0,"%s: No available paths to check pr status",
- mpp->alias);
+ condlog(2, "%s: No available paths to check pr status", mpp->alias);
goto out;
}
mpp->prflag = PRFLAG_UNSET;
@@ -768,22 +769,31 @@ int update_map_pr(struct multipath *mpp)
if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
{
- condlog(3,"%s: No key found. Device may not be registered. ", mpp->alias);
+ condlog(was_set ? 1 : 3,
+ "%s: No key found. Device may not be registered. ",
+ mpp->alias);
goto out;
}
- condlog(2, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
+ condlog(3, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
get_be64(mpp->reservation_key));
isFound =0;
for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ )
{
- condlog(2, "%s: PR IN READKEYS[%d] reservation key:", mpp->alias, i);
- dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , 1);
+ if (libmp_verbosity >= 3) {
+ condlog(3, "%s: PR IN READKEYS[%d] reservation key:",
+ mpp->alias, i);
+ dumpHex((char *)&resp->prin_descriptor.prin_readkeys
+ .key_list[i * 8],
+ 8, 1);
+ }
- if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
- {
- condlog(2, "%s: reservation key found in pr in readkeys response", mpp->alias);
+ if (!memcmp(&mpp->reservation_key,
+ &resp->prin_descriptor.prin_readkeys.key_list[i * 8],
+ 8)) {
+ condlog(3, "%s: reservation key found in pr in readkeys response",
+ mpp->alias);
isFound =1;
}
}
@@ -791,7 +801,7 @@ int update_map_pr(struct multipath *mpp)
if (isFound)
{
mpp->prflag = PRFLAG_SET;
- condlog(2, "%s: prflag flag set.", mpp->alias );
+ condlog(was_set ? 3 : 2, "%s: prflag flag set.", mpp->alias);
}
out:

View File

@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:49 -0400
Subject: [PATCH] libmpathpersist: remove pointless update_map_pr ret value
code
Don't set ret to MPATH_PR_SUCCESS, after the code just made sure that
it was already set to MPATH_PR_SUCCESS.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 6233e6b0..39bfc953 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -765,8 +765,6 @@ int update_map_pr(struct multipath *mpp)
goto out;
}
- ret = MPATH_PR_SUCCESS;
-
if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
{
condlog(was_set ? 1 : 3,

View File

@ -0,0 +1,217 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:50 -0400
Subject: [PATCH] multipathd: use update_map_pr in mpath_pr_event_handle
Clean up the duplicate code in mpath_pr_event_handle() and
update_map_pr() by making update_map_pr() take an optional path device
to use for its check, instead of checking all path devices and make
mpath_pr_event_handle() call update_map_pr() to do its checking.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/libmpathpersist.version | 2 +-
libmpathpersist/mpath_persist_int.c | 18 ++++---
libmpathpersist/mpath_persist_int.h | 2 +-
multipathd/main.c | 68 ++++---------------------
4 files changed, 22 insertions(+), 68 deletions(-)
diff --git a/libmpathpersist/libmpathpersist.version b/libmpathpersist/libmpathpersist.version
index a8c6aae7..faa4257b 100644
--- a/libmpathpersist/libmpathpersist.version
+++ b/libmpathpersist/libmpathpersist.version
@@ -27,7 +27,7 @@ global:
local: *;
};
-__LIBMPATHPERSIST_INT_1.0.0 {
+__LIBMPATHPERSIST_INT_2.0.0 {
/* Internal use by multipath-tools */
dumpHex;
mpath_alloc_prin_response;
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 39bfc953..8b01492f 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -727,7 +727,7 @@ out1:
return ret;
}
-int update_map_pr(struct multipath *mpp)
+int update_map_pr(struct multipath *mpp, struct path *pp)
{
int noisy=0;
struct prin_resp *resp;
@@ -742,7 +742,7 @@ int update_map_pr(struct multipath *mpp)
condlog(was_set ? 2 : 4,
"%s: reservation_key not set in multipath.conf",
mpp->alias);
- return MPATH_PR_SUCCESS;
+ return MPATH_PR_SKIP;
}
resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
@@ -751,13 +751,15 @@ int update_map_pr(struct multipath *mpp)
condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
return MPATH_PR_OTHER;
}
- if (count_active_paths(mpp) == 0)
- {
+ if (!pp && count_active_paths(mpp) == 0) {
condlog(2, "%s: No available paths to check pr status", mpp->alias);
goto out;
}
mpp->prflag = PRFLAG_UNSET;
- ret = mpath_prin_activepath(mpp, MPATH_PRIN_RKEY_SA, resp, noisy);
+ if (pp)
+ ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, noisy);
+ else
+ ret = mpath_prin_activepath(mpp, MPATH_PRIN_RKEY_SA, resp, noisy);
if (ret != MPATH_PR_SUCCESS )
{
@@ -799,8 +801,10 @@ int update_map_pr(struct multipath *mpp)
if (isFound)
{
mpp->prflag = PRFLAG_SET;
- condlog(was_set ? 3 : 2, "%s: prflag flag set.", mpp->alias);
- }
+ condlog(was_set ? 3 : 2, "%s: key found. prflag set.", mpp->alias);
+ } else
+ condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
+ mpp->alias);
out:
free(resp);
diff --git a/libmpathpersist/mpath_persist_int.h b/libmpathpersist/mpath_persist_int.h
index 31457535..73c95863 100644
--- a/libmpathpersist/mpath_persist_int.h
+++ b/libmpathpersist/mpath_persist_int.h
@@ -20,6 +20,6 @@ int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int
int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy);
void dumpHex(const char* , int len, int no_ascii);
-int update_map_pr(struct multipath *mpp);
+int update_map_pr(struct multipath *mpp, struct path *pp);
#endif /* _MPATH_PERSIST_INT_H */
diff --git a/multipathd/main.c b/multipathd/main.c
index 04ca47d1..390632a6 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -733,7 +733,7 @@ fail:
sync_map_state(mpp);
if (mpp->prflag != PRFLAG_SET)
- update_map_pr(mpp);
+ update_map_pr(mpp, NULL);
if (mpp->prflag == PRFLAG_SET)
pr_register_active_paths(mpp);
@@ -1383,7 +1383,7 @@ rescan:
if (retries >= 0) {
if (start_waiter)
- update_map_pr(mpp);
+ update_map_pr(mpp, NULL);
if (mpp->prflag == PRFLAG_SET && prflag != PRFLAG_SET)
pr_register_active_paths(mpp);
condlog(2, "%s [%s]: path added to devmap %s",
@@ -3028,7 +3028,7 @@ configure (struct vectors * vecs, enum force_reload_types reload_type)
vector_foreach_slot(mpvec, mpp, i){
if (remember_wwid(mpp->wwid) == 1)
trigger_paths_udev_change(mpp, true);
- update_map_pr(mpp);
+ update_map_pr(mpp, NULL);
if (mpp->prflag == PRFLAG_SET)
pr_register_active_paths(mpp);
}
@@ -3946,70 +3946,24 @@ main (int argc, char *argv[])
static void mpath_pr_event_handle(struct path *pp)
{
- struct multipath * mpp;
- unsigned int i;
- int ret, isFound;
+ struct multipath *mpp = pp->mpp;
+ int ret;
struct prout_param_descriptor *param;
- struct prin_resp *resp;
- mpp = pp->mpp;
if (pp->bus != SYSFS_BUS_SCSI) {
mpp->prflag = PRFLAG_UNSET;
return;
}
- if (!get_be64(mpp->reservation_key)) {
- mpp->prflag = PRFLAG_UNSET;
+ if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS)
return;
- }
-
- resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
- if (!resp){
- condlog(0,"%s Alloc failed for prin response", pp->dev);
- goto out;
- }
-
- mpp->prflag = PRFLAG_UNSET;
- ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, 0);
- if (ret != MPATH_PR_SUCCESS )
- {
- condlog(0,"%s : pr in read keys service action failed. Error=%d", pp->dev, ret);
- goto out;
- }
-
- condlog(3, " event pr=%d addlen=%d",resp->prin_descriptor.prin_readkeys.prgeneration,
- resp->prin_descriptor.prin_readkeys.additional_length );
-
- if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
- {
- condlog(1, "%s: No key found. Device may not be registered.", pp->dev);
- goto out;
- }
- condlog(2, "Multipath reservation_key: 0x%" PRIx64 " ",
- get_be64(mpp->reservation_key));
- isFound =0;
- for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ )
- {
- condlog(2, "PR IN READKEYS[%d] reservation key:",i);
- dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , -1);
- if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
- {
- condlog(2, "%s: pr key found in prin readkeys response", mpp->alias);
- isFound =1;
- break;
- }
- }
- if (!isFound)
- {
- condlog(0, "%s: Either device not registered or ", pp->dev);
- condlog(0, "host is not authorised for registration. Skip path");
- goto out;
- }
+ if (mpp->prflag != PRFLAG_SET)
+ return;
param = (struct prout_param_descriptor *)calloc(1, sizeof(struct prout_param_descriptor));
if (!param)
- goto out;
+ return;
param->sa_flags = mpp->sa_flags;
memcpy(param->sa_key, &mpp->reservation_key, 8);
@@ -4022,10 +3976,6 @@ static void mpath_pr_event_handle(struct path *pp)
{
condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret);
}
- mpp->prflag = PRFLAG_SET;
free(param);
-out:
- if (resp)
- free(resp);
}

View File

@ -0,0 +1,77 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:51 -0400
Subject: [PATCH] libmpathpersist: limit changing prflag in update_map_pr
Do not unset mpp->prflag in update_map_pr() unless the device doesn't
support persistent reservations or it successfully reads the registered
keys and discovers that the device's key is not there. Also, nothing in
the libmpathpersist ever returns MPATH_PR_SENSE_INVALID_OP. It instead
returns MPATH_PR_ILLEGAL_REQ if the device doesn't support persistent
reservations, so check for that instead.
Also, do not even check for the registered keys in update_map_pr() if
mpp->prflag is unset. Otherwise, multipathd will set mpp->prflag if the
key was unregistered (and thus prflag was unset) while a path is down
(and thus could not have its key unregistered) and then that path comes
back up. I should note that the above issue can only occur if multipath
is defining PR keys in /etc/multipath.conf, instead of the prkeys file.
If a device has no registered keys, it must unset prflag, since that
means it's no longer registered. Possibly its registration was removed
by a CLEAR or PREEMPT action. But if a device has a registered key but
it is supposed to be unregistered, it should remain unregistered, since
that key is likely one that libmpathpersist could not unregister at the
time.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 8b01492f..c34bc785 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -73,7 +73,7 @@ static int mpath_prin_activepath (struct multipath *mpp, int rq_servact,
switch(ret)
{
case MPATH_PR_SUCCESS:
- case MPATH_PR_SENSE_INVALID_OP:
+ case MPATH_PR_ILLEGAL_REQ:
return ret;
default:
continue;
@@ -735,6 +735,10 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
int ret = MPATH_PR_OTHER, isFound;
bool was_set = (mpp->prflag == PRFLAG_SET);
+ /* If pr is explicitly unset, it must be manually set */
+ if (mpp->prflag == PRFLAG_UNSET)
+ return MPATH_PR_SKIP;
+
if (!get_be64(mpp->reservation_key))
{
/* Nothing to do. Assuming pr mgmt feature is disabled*/
@@ -755,7 +759,6 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
condlog(2, "%s: No available paths to check pr status", mpp->alias);
goto out;
}
- mpp->prflag = PRFLAG_UNSET;
if (pp)
ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, noisy);
else
@@ -763,9 +766,12 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
if (ret != MPATH_PR_SUCCESS )
{
+ if (ret == MPATH_PR_ILLEGAL_REQ)
+ mpp->prflag = PRFLAG_UNSET;
condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
goto out;
}
+ mpp->prflag = PRFLAG_UNSET;
if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
{

View File

@ -0,0 +1,77 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:52 -0400
Subject: [PATCH] multipathd: Don't call update_map_pr unnecessarily
None of the calls to update_map_pr() outside of mpath_pr_event_handle()
add any benefit. When update_map_pr() is called without a path, it tries
to read the pr keys list on each usable path until it succeeds, and then
checks the keys to see if they include the configured key.
In all cases where update_map_pr() is called outside of
mpath_pr_event_handle(), after it is called, pr_register_active_paths()
is called if a matching key was found. pr_register_active_paths() calls
mpath_pr_event_handle() on each usable path, which calls update_map_pr()
with a path, so it only checks that path. If a matching key is found, it
registers a key on the current path. The result is that after
pr_register_active_paths() is called, update_map_pr() will be called for
each usable path, just like update_map_pr() did. So calling
update_map_pr() first doesn't change the results for multipathd, it just
adds duplicate work, so remove those calls.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
multipathd/main.c | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 390632a6..4a1b38e9 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -644,6 +644,8 @@ pr_register_active_paths(struct multipath *mpp)
vector_foreach_slot (mpp->pg, pgp, i) {
vector_foreach_slot (pgp->paths, pp, j) {
+ if (mpp->prflag == PRFLAG_UNSET)
+ return;
if ((pp->state == PATH_UP) || (pp->state == PATH_GHOST))
mpath_pr_event_handle(pp);
}
@@ -732,10 +734,7 @@ fail:
sync_map_state(mpp);
- if (mpp->prflag != PRFLAG_SET)
- update_map_pr(mpp, NULL);
- if (mpp->prflag == PRFLAG_SET)
- pr_register_active_paths(mpp);
+ pr_register_active_paths(mpp);
if (VECTOR_SIZE(offline_paths) != 0)
handle_orphaned_offline_paths(offline_paths);
@@ -1382,10 +1381,9 @@ rescan:
sync_map_state(mpp);
if (retries >= 0) {
- if (start_waiter)
- update_map_pr(mpp, NULL);
- if (mpp->prflag == PRFLAG_SET && prflag != PRFLAG_SET)
- pr_register_active_paths(mpp);
+ if ((mpp->prflag == PRFLAG_SET && prflag != PRFLAG_SET) ||
+ start_waiter)
+ pr_register_active_paths(mpp);
condlog(2, "%s [%s]: path added to devmap %s",
pp->dev, pp->dev_t, mpp->alias);
return 0;
@@ -3028,9 +3026,7 @@ configure (struct vectors * vecs, enum force_reload_types reload_type)
vector_foreach_slot(mpvec, mpp, i){
if (remember_wwid(mpp->wwid) == 1)
trigger_paths_udev_change(mpp, true);
- update_map_pr(mpp, NULL);
- if (mpp->prflag == PRFLAG_SET)
- pr_register_active_paths(mpp);
+ pr_register_active_paths(mpp);
}
/*

View File

@ -0,0 +1,74 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:53 -0400
Subject: [PATCH] libmpathpersist: remove useless function
send_prout_activepath
send_prout_activepath() creates a single separate thread that just calls
prout_do_scsi_ioctl() and it doesn't take any advantage of the work
being done in another thread. Remove the function and call
prout_do_scsi_ioctl() directly.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 39 ++---------------------------
1 file changed, 2 insertions(+), 37 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index c34bc785..d8f757b7 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -373,40 +373,6 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
}
-static int send_prout_activepath(char *dev, int rq_servact, int rq_scope,
- unsigned int rq_type,
- struct prout_param_descriptor * paramp, int noisy)
-{
- struct prout_param param;
- param.rq_servact = rq_servact;
- param.rq_scope = rq_scope;
- param.rq_type = rq_type;
- param.paramp = paramp;
- param.noisy = noisy;
- param.status = -1;
-
- pthread_t thread;
- pthread_attr_t attr;
- int rc;
-
- memset(&thread, 0, sizeof(thread));
- strlcpy(param.dev, dev, FILE_NAME_SIZE);
- /* Initialize and set thread joinable attribute */
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
- rc = pthread_create(&thread, &attr, mpath_prout_pthread_fn, (void *)(&param));
- if (rc){
- condlog (3, "%s: failed to create thread %d", dev, rc);
- return MPATH_PR_THREAD_ERROR;
- }
- /* Free attribute and wait for the other threads */
- pthread_attr_destroy(&attr);
- rc = pthread_join(thread, NULL);
-
- return (param.status);
-}
-
static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type,
struct prout_param_descriptor* paramp, int noisy)
@@ -426,9 +392,8 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
found = true;
- ret = send_prout_activepath(pp->dev, rq_servact,
- rq_scope, rq_type,
- paramp, noisy);
+ ret = prout_do_scsi_ioctl(pp->dev, rq_servact, rq_scope,
+ rq_type, paramp, noisy);
if (ret != MPATH_PR_RETRYABLE_ERROR)
return ret;
}

View File

@ -0,0 +1,293 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:54 -0400
Subject: [PATCH] libmpathpersist: redesign failed release workaround
The workaround for releasing a reservation held by a failed path
(clearing all persistent reservation data and then reregistering all the
keys) has several problems. It requires devices that support the READ
FULL STATUS command and are capable of specifying TransportIDs with
REGISTRATION commands (SIP_C), neither of which are mandatory to support
SCSI Persistent Reservations. Non SIP_C devices will be left with no
registered keys. Also, not all cleared keys are registered, just the
ones going to the Target Port receiving the REGISTRATION command. To
reregister all the keys, the code would have to also use the "All Target
Ports" flag to register keys on different Target Ports, but this could
end up registering keys on paths they aren't supposed to be on (for
instance if one of the registered keys was only there because the path
was down and it couldn't be released).
The redesign avoids these issues by only using mandatory Persistent
Reservation commands, without extra optional parameters or flags, and
only effects the keys of the multipath device it is being issued on.
The new workaround is:
1. Suspend the multipath device to prevent I/O
2. Preempt the key to move the reservation to an available path. This
also removes the registered keys from every path except the path
issuing the PREEMPT command. Since the device is suspended, not I/O
can go to these unregisted paths and fail.
3. Release the reservation on the path that now holds it.
4. Resume the device (since it no longer matters that most of the paths
no longer have a registered key)
5. Reregister the keys on all the paths.
If steps 3 or 4 fail, the code will attempt to reregister the keys, and
then attempt (or possibly re-attempt) the resume.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 182 +++++++++++++---------------
libmultipath/libmultipath.version | 5 +
2 files changed, 87 insertions(+), 100 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index d8f757b7..36743d41 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -373,9 +373,10 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
}
-static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type,
- struct prout_param_descriptor* paramp, int noisy)
+static int
+mpath_prout_common(struct multipath *mpp, int rq_servact, int rq_scope,
+ unsigned int rq_type, struct prout_param_descriptor *paramp,
+ int noisy, struct path **pptr)
{
int i,j, ret;
struct pathgroup *pgp = NULL;
@@ -394,6 +395,8 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
found = true;
ret = prout_do_scsi_ioctl(pp->dev, rq_servact, rq_scope,
rq_type, paramp, noisy);
+ if (ret == MPATH_PR_SUCCESS && pptr)
+ *pptr = pp;
if (ret != MPATH_PR_RETRYABLE_ERROR)
return ret;
}
@@ -414,14 +417,12 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
struct path *pp = NULL;
int active_pathcount = 0;
pthread_attr_t attr;
- int rc, found = 0;
+ int rc;
int count = 0;
int status = MPATH_PR_SUCCESS;
- struct prin_resp resp;
- struct prout_param_descriptor *pamp = NULL;
- struct prin_resp *pr_buff;
- int length;
- struct transportid *pptr = NULL;
+ struct prin_resp resp = {{{.prgeneration = 0}}};
+ uint16_t udev_flags = (mpp->skip_kpartx) ? MPATH_UDEV_NO_KPARTX_FLAG : 0;
+ bool did_resume = false;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
@@ -511,104 +512,78 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
}
condlog (2, "%s: Path holding reservation is not available.", mpp->wwid);
-
- pr_buff = mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA);
- if (!pr_buff){
- condlog (0, "%s: failed to alloc pr in response buffer.", mpp->wwid);
+ /*
+ * Cannot free the reservation because the path that is holding it
+ * is not usable. Workaround this by:
+ * 1. Suspending the device
+ * 2. Preempting the reservation to move it to a usable path
+ * (this removes the registered keys on all paths except the
+ * preempting one. Since the device is suspended, no IO can
+ * go to these unregistered paths and fail).
+ * 3. Releasing the reservation on the path that now holds it.
+ * 4. Resuming the device (since it no longer matters that most of
+ * that paths no longer have a registered key)
+ * 5. Reregistering keys on all the paths
+ */
+
+ if (!dm_simplecmd_noflush(DM_DEVICE_SUSPEND, mpp->alias, 0)) {
+ condlog(0, "%s: release: failed to suspend dm device.", mpp->wwid);
return MPATH_PR_OTHER;
}
- status = mpath_prin_activepath (mpp, MPATH_PRIN_RFSTAT_SA, pr_buff, noisy);
-
- if (status != MPATH_PR_SUCCESS){
- condlog (0, "%s: pr in read full status command failed.", mpp->wwid);
- goto out;
- }
-
- num = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor;
- if (0 == num){
- goto out;
- }
- length = sizeof (struct prout_param_descriptor) + (sizeof (struct transportid *));
-
- pamp = (struct prout_param_descriptor *)malloc (length);
- if (!pamp){
- condlog (0, "%s: failed to alloc pr out parameter.", mpp->wwid);
- goto out;
- }
-
- memset(pamp, 0, length);
-
- pamp->trnptid_list[0] = (struct transportid *) malloc (sizeof (struct transportid));
- if (!pamp->trnptid_list[0]){
- condlog (0, "%s: failed to alloc pr out transportid.", mpp->wwid);
- goto out1;
- }
- pptr = pamp->trnptid_list[0];
-
- if (get_be64(mpp->reservation_key)){
- memcpy (pamp->key, &mpp->reservation_key, 8);
- condlog (3, "%s: reservation key set.", mpp->wwid);
- }
-
- status = mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA,
- rq_scope, rq_type, pamp, noisy);
-
- if (status) {
- condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid);
- goto out2;
+ memset(paramp, 0, sizeof(*paramp));
+ memcpy(paramp->key, &mpp->reservation_key, 8);
+ memcpy(paramp->sa_key, &mpp->reservation_key, 8);
+ status = mpath_prout_common(mpp, MPATH_PROUT_PREE_SA, rq_scope,
+ rq_type, paramp, noisy, &pp);
+ if (status != MPATH_PR_SUCCESS) {
+ condlog(0, "%s: release: pr preempt command failed.", mpp->wwid);
+ goto fail_resume;
}
- pamp->num_transportid = 1;
-
- for (i = 0; i < num; i++){
- if (get_be64(mpp->reservation_key) &&
- memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key,
- &mpp->reservation_key, 8)){
- /*register with transport id*/
- memset(pamp, 0, length);
- pamp->trnptid_list[0] = pptr;
- memset (pamp->trnptid_list[0], 0, sizeof (struct transportid));
- memcpy (pamp->sa_key,
- pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
- pamp->sa_flags = MPATH_F_SPEC_I_PT_MASK;
- pamp->num_transportid = 1;
-
- memcpy (pamp->trnptid_list[0],
- &pr_buff->prin_descriptor.prin_readfd.descriptors[i]->trnptid,
- sizeof (struct transportid));
- status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
- pamp, noisy);
-
- pamp->sa_flags = 0;
- memcpy (pamp->key, pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
- memset (pamp->sa_key, 0, 8);
- pamp->num_transportid = 0;
- status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
- pamp, noisy);
- }
- else
- {
- if (get_be64(mpp->reservation_key))
- found = 1;
- }
-
-
+ memset(paramp, 0, sizeof(*paramp));
+ memcpy(paramp->key, &mpp->reservation_key, 8);
+ status = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REL_SA, rq_scope,
+ rq_type, paramp, noisy);
+ if (status != MPATH_PR_SUCCESS) {
+ condlog(0, "%s: release on alternate path failed.", mpp->wwid);
+ goto out_reregister;
}
- if (found){
- memset (pamp, 0, length);
- memcpy (pamp->sa_key, &mpp->reservation_key, 8);
- memset (pamp->key, 0, 8);
- status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
+ if (!dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, udev_flags)) {
+ condlog(0, "%s release: failed to resume dm device.", mpp->wwid);
+ /*
+ * leave status set to MPATH_PR_SUCCESS, we will have another
+ * chance to resume the device.
+ */
+ goto out_reregister;
+ }
+ did_resume = true;
+
+out_reregister:
+ memset(paramp, 0, sizeof(*paramp));
+ memcpy(paramp->sa_key, &mpp->reservation_key, 8);
+ rc = mpath_prout_reg(mpp, MPATH_PROUT_REG_IGN_SA, rq_scope, rq_type,
+ paramp, noisy);
+ if (rc != MPATH_PR_SUCCESS)
+ condlog(0, "%s: release: failed to reregister paths.", mpp->wwid);
+
+ /*
+ * If we failed releasing the reservation or resuming earlier
+ * try resuming now. Otherwise, return with the reregistering status
+ * This means we will report failure, even though the resevation
+ * has been released, since the keys were not reregistered.
+ */
+ if (did_resume)
+ return rc;
+ else if (status == MPATH_PR_SUCCESS)
+ status = rc;
+fail_resume:
+ if (!dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, udev_flags)) {
+ condlog(0, "%s: release: failed to resume dm device.", mpp->wwid);
+ if (status == MPATH_PR_SUCCESS)
+ status = MPATH_PR_OTHER;
}
-
-out2:
- free(pptr);
-out1:
- free (pamp);
-out:
- free (pr_buff);
return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
}
@@ -629,6 +604,12 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
conf = get_multipath_config();
select_reservation_key(conf, mpp);
select_all_tg_pt(conf, mpp);
+ /*
+ * If a device preempts itself, it will need to suspend and resume.
+ * Set mpp->skip_kpartx to make sure we set the flags to skip kpartx
+ * if necessary, when doing this.
+ */
+ select_skip_kpartx(conf, mpp);
put_multipath_config(conf);
memcpy(&prkey, paramp->sa_key, 8);
@@ -665,7 +646,8 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
case MPATH_PROUT_PREE_SA :
case MPATH_PROUT_PREE_AB_SA :
case MPATH_PROUT_CLEAR_SA:
- ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
+ paramp, noisy, NULL);
break;
case MPATH_PROUT_REL_SA:
ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index 0b7a1a2b..db125779 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -246,3 +246,8 @@ LIBMULTIPATH_24.0.1 {
global:
can_recheck_wwid;
} LIBMULTIPATH_24.0.0;
+
+LIBMULTIPATH_24.0.2 {
+global:
+ select_skip_kpartx;
+} LIBMULTIPATH_24.0.1;

View File

@ -0,0 +1,51 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:55 -0400
Subject: [PATCH] libmpathpersist: fail the release if all threads fail
If none of the threads succeeds in issuing the release, simply return
failure, instead of trying the workaround.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 36743d41..553fa509 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -423,6 +423,7 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
struct prin_resp resp = {{{.prgeneration = 0}}};
uint16_t udev_flags = (mpp->skip_kpartx) ? MPATH_UDEV_NO_KPARTX_FLAG : 0;
bool did_resume = false;
+ bool all_threads_failed;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
@@ -484,15 +485,22 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
}
}
+ all_threads_failed = true;
for (i = 0; i < count; i++){
/* check thread status here and return the status */
- if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
+ if (thread[i].param.status == MPATH_PR_SUCCESS)
+ all_threads_failed = false;
+ else if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
status = MPATH_PR_RESERV_CONFLICT;
- else if (status == MPATH_PR_SUCCESS
- && thread[i].param.status != MPATH_PR_RESERV_CONFLICT)
+ else if (status == MPATH_PR_SUCCESS)
status = thread[i].param.status;
}
+ if (all_threads_failed) {
+ condlog(0, "%s: all threads failed to release reservation.",
+ mpp->wwid);
+ return status;
+ }
status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
if (status != MPATH_PR_SUCCESS){

View File

@ -0,0 +1,191 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:56 -0400
Subject: [PATCH] libmpathpersist: Handle changing key corner case
When you change the reservation key of a registered multipath device,
some of paths might be down or even deleted since you originally
registered the key. libmpathpersist won't be able to change these
registrations. This can be a problem if the path that is down is holding
the reservation. Other nodes will assume that the reservation is now
under your new key, when it is still under the old one. If they try to
preempt the reservation, they will succeed. But they will just
unregister all the paths with the new key. They won't take over the
reservation, and if the path holding it comes back up, it will still be
able to write to the device.
To avoid this, after libmpathpersist changes the key of a registered
device, it now checks to see if its old key is still holding the
reservation. If it is, limpathpersist preempts the reservation.
Currently this only works on REGISTER commands, not REGISTER AND IGNORE
ones. A future patch will add that capability. This patch also moves
mpath_prout_common() up, without changing it at all.
I should note that this relies on the fact that libmpathpersist has
never worked correctly if two nodes use the same reservation key for the
same device. If another node used the same key, it could be the one
holding the reservation, and preempting the reservation would deregister
it. However, multipathd has always relied on the fact that two nodes
will not use the same registration key to know if it is safe to register
a key on a path that was just added or just came back up. If there is
already a registered key matching the configured key, multipathd assumes
that the device has not been preempted, and registers the key on the
path. If there are no keys matching the configured key, multipathd
assumes that the device has been preempted, and won't register a key.
Again, if another node shared the same key, multipathd could registered
paths on a node that had been preempted.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 127 ++++++++++++++++++++--------
1 file changed, 93 insertions(+), 34 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 553fa509..02b3e59a 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -230,6 +230,97 @@ static void *mpath_prout_pthread_fn(void *p)
pthread_exit(NULL);
}
+static int
+mpath_prout_common(struct multipath *mpp, int rq_servact, int rq_scope,
+ unsigned int rq_type, struct prout_param_descriptor *paramp,
+ int noisy, struct path **pptr)
+{
+ int i, j, ret;
+ struct pathgroup *pgp = NULL;
+ struct path *pp = NULL;
+ bool found = false;
+
+ vector_foreach_slot (mpp->pg, pgp, j) {
+ vector_foreach_slot (pgp->paths, pp, i) {
+ if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))) {
+ condlog(1, "%s: %s path not up. Skip",
+ mpp->wwid, pp->dev);
+ continue;
+ }
+
+ condlog(3, "%s: sending pr out command to %s",
+ mpp->wwid, pp->dev);
+ found = true;
+ ret = prout_do_scsi_ioctl(pp->dev, rq_servact, rq_scope,
+ rq_type, paramp, noisy);
+ if (ret == MPATH_PR_SUCCESS && pptr)
+ *pptr = pp;
+ if (ret != MPATH_PR_RETRYABLE_ERROR)
+ return ret;
+ }
+ }
+ if (found)
+ return MPATH_PR_OTHER;
+ condlog(0, "%s: no path available", mpp->wwid);
+ return MPATH_PR_DMMP_ERROR;
+}
+
+/*
+ * If you are changing the key registered to a device, and that device is
+ * holding the reservation on a path that couldn't get its key updated,
+ * either because it is down or no longer part of the multipath device,
+ * you need to preempt the reservation to a usable path with the new key
+ */
+void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
+ int noisy)
+{
+ uint8_t zero[8] = {0};
+ struct prin_resp resp = {{{.prgeneration = 0}}};
+ int rq_scope;
+ unsigned int rq_type;
+ struct prout_param_descriptor paramp = {.sa_flags = 0};
+ int status;
+
+ /*
+ * If you previously didn't have a key registered or you didn't
+ * switch to a different key, there's no need to preempt. Also, you
+ * can't preempt if you no longer have a registered key
+ */
+ if (memcmp(key, zero, 8) == 0 || memcmp(sa_key, zero, 8) == 0 ||
+ memcmp(key, sa_key, 8) == 0)
+ return;
+
+ status = mpath_prin_activepath(mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
+ if (status != MPATH_PR_SUCCESS) {
+ condlog(0, "%s: register: pr in read reservation command failed.",
+ mpp->wwid);
+ return;
+ }
+ /* If there is no reservation, there's nothing to preempt */
+ if (!resp.prin_descriptor.prin_readresv.additional_length)
+ return;
+ /*
+ * If there reservation is not held by the old key, you don't
+ * want to preempt it
+ */
+ if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) != 0)
+ return;
+ /* Assume this key is being held by an inaccessable path on this
+ * node. libmpathpersist has never worked if multiple nodes share
+ * the same reservation key for a device
+ */
+ rq_type = resp.prin_descriptor.prin_readresv.scope_type & MPATH_PR_TYPE_MASK;
+ rq_scope = (resp.prin_descriptor.prin_readresv.scope_type &
+ MPATH_PR_SCOPE_MASK) >>
+ 4;
+ memcpy(paramp.key, sa_key, 8);
+ memcpy(paramp.sa_key, key, 8);
+ status = mpath_prout_common(mpp, MPATH_PROUT_PREE_SA, rq_scope,
+ rq_type, &paramp, noisy, NULL);
+ if (status != MPATH_PR_SUCCESS)
+ condlog(0, "%s: register: pr preempt command failed.", mpp->wwid);
+}
+
static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type,
struct prout_param_descriptor * paramp, int noisy)
@@ -370,43 +461,11 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
}
pthread_attr_destroy(&attr);
+ if (status == MPATH_PR_SUCCESS)
+ preempt_missing_path(mpp, paramp->key, paramp->sa_key, noisy);
return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
}
-static int
-mpath_prout_common(struct multipath *mpp, int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor *paramp,
- int noisy, struct path **pptr)
-{
- int i,j, ret;
- struct pathgroup *pgp = NULL;
- struct path *pp = NULL;
- bool found = false;
-
- vector_foreach_slot (mpp->pg, pgp, j){
- vector_foreach_slot (pgp->paths, pp, i){
- if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
- condlog (1, "%s: %s path not up. Skip",
- mpp->wwid, pp->dev);
- continue;
- }
-
- condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
- found = true;
- ret = prout_do_scsi_ioctl(pp->dev, rq_servact, rq_scope,
- rq_type, paramp, noisy);
- if (ret == MPATH_PR_SUCCESS && pptr)
- *pptr = pp;
- if (ret != MPATH_PR_RETRYABLE_ERROR)
- return ret;
- }
- }
- if (found)
- return MPATH_PR_OTHER;
- condlog (0, "%s: no path available", mpp->wwid);
- return MPATH_PR_DMMP_ERROR;
-}
-
static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type,
struct prout_param_descriptor * paramp, int noisy)

View File

@ -0,0 +1,182 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:57 -0400
Subject: [PATCH] libmpathpersist: Handle REGISTER AND IGNORE changing key
corner case
When you use REGISTER to change the reservation key of a registered
multipath device, where the path holding the reservation is down,
libmpathpersist will PREEMPT the failed path to move the reservation to
a usable path, so the reservation key will be updated. However, when you
use REGISTER AND IGNORE, you don't pass in the old key, so
libmpathpersist can't use it to check against the key holding the
reservation, which is necessary to know if it needs to PREEMPT.
Since the SCSI spec says that devices must ignore any passed-in key on
REGISTER AND IGNORE, libmpathpersist can still use it to pass in the old
key value. But unlike with REGISTER, the command won't fail if device
isn't actually registered with the old key, so libmpathpersist needs to
check that itself. To do this, it calls update_map_pr() just like
multipathd does to check that the device is registered before
registering new paths. However, libmpathpersist doesn't track the
persistent reservation state like multipathd, so it needs to ask
multipathd if it thinks the device is registered, using the "get
prstatus map <map>" command.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 20 ++++++++++
libmpathpersist/mpath_updatepr.c | 59 +++++++++++++++++++++--------
libmpathpersist/mpathpr.h | 1 +
3 files changed, 65 insertions(+), 15 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 02b3e59a..19220d7a 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -654,6 +654,23 @@ fail_resume:
return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
}
+/*
+ * for MPATH_PROUT_REG_IGN_SA, we use the ignored paramp->key to store the
+ * currently registered key.
+ */
+static void set_ignored_key(struct multipath *mpp, uint8_t *key)
+{
+ memset(key, 0, 8);
+ if (!get_be64(mpp->reservation_key))
+ return;
+ if (get_prflag(mpp->alias) == PRFLAG_UNSET)
+ return;
+ update_map_pr(mpp, NULL);
+ if (mpp->prflag != PRFLAG_SET)
+ return;
+ memcpy(key, &mpp->reservation_key, 8);
+}
+
int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
int rq_servact, int rq_scope, unsigned int rq_type,
struct prout_param_descriptor *paramp, int noisy)
@@ -679,6 +696,9 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
select_skip_kpartx(conf, mpp);
put_multipath_config(conf);
+ if (rq_servact == MPATH_PROUT_REG_IGN_SA)
+ set_ignored_key(mpp, paramp->key);
+
memcpy(&prkey, paramp->sa_key, 8);
if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
index 36bd777e..7cf82f50 100644
--- a/libmpathpersist/mpath_updatepr.c
+++ b/libmpathpersist/mpath_updatepr.c
@@ -19,14 +19,12 @@
#include "config.h"
#include "uxsock.h"
#include "mpathpr.h"
+#include "structs.h"
-
-static int do_update_pr(char *alias, char *cmd, char *key)
+static char *do_pr(char *alias, char *str)
{
int fd;
- char str[256];
char *reply;
- int ret = 0;
int timeout;
struct config *conf;
@@ -37,24 +35,35 @@ static int do_update_pr(char *alias, char *cmd, char *key)
fd = mpath_connect();
if (fd == -1) {
condlog (0, "ux socket connect error");
- return -1;
+ return NULL;
}
- if (key)
- snprintf(str,sizeof(str),"%s map %s key %s", cmd, alias, key);
- else
- snprintf(str,sizeof(str),"%s map %s", cmd, alias);
condlog (2, "%s: pr message=%s", alias, str);
if (send_packet(fd, str) != 0) {
condlog(2, "%s: message=%s send error=%d", alias, str, errno);
mpath_disconnect(fd);
- return -1;
+ return NULL;
}
- ret = recv_packet(fd, &reply, timeout);
- if (ret < 0) {
+ if (recv_packet(fd, &reply, timeout) < 0)
condlog(2, "%s: message=%s recv error=%d", alias, str, errno);
- ret = -1;
- } else {
+
+ mpath_disconnect(fd);
+ return reply;
+}
+
+static int do_update_pr(char *alias, char *cmd, char *key)
+{
+ char str[256];
+ char *reply = NULL;
+ int ret = -1;
+
+ if (key)
+ snprintf(str, sizeof(str), "%s map %s key %s", cmd, alias, key);
+ else
+ snprintf(str, sizeof(str), "%s map %s", cmd, alias);
+
+ reply = do_pr(alias, str);
+ if (reply) {
condlog (2, "%s: message=%s reply=%s", alias, str, reply);
if (reply && strncmp(reply,"ok", 2) == 0)
ret = 0;
@@ -63,10 +72,30 @@ static int do_update_pr(char *alias, char *cmd, char *key)
}
free(reply);
- mpath_disconnect(fd);
return ret;
}
+int get_prflag(char *mapname)
+{
+ char str[256];
+ char *reply;
+ int prflag;
+
+ snprintf(str, sizeof(str), "getprstatus map %s", mapname);
+ reply = do_pr(mapname, str);
+ if (!reply)
+ prflag = PRFLAG_UNKNOWN;
+ else if (strncmp(reply, "unset", 5) == 0)
+ prflag = PRFLAG_UNSET;
+ else if (strncmp(reply, "set", 3) == 0)
+ prflag = PRFLAG_SET;
+ else
+ prflag = PRFLAG_UNKNOWN;
+
+ free(reply);
+ return prflag;
+}
+
int update_prflag(char *mapname, int set) {
return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus",
NULL);
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 39a7d8ed..614be530 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -8,6 +8,7 @@
int update_prflag(char *mapname, int set);
int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags);
+int get_prflag(char *mapname);
#define update_prkey(mapname, prkey) update_prkey_flags(mapname, prkey, 0)
#endif

View File

@ -0,0 +1,249 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:58 -0400
Subject: [PATCH] libmultipath: rename prflag_value enums
These will also be used by another variable, so make their name more
generic.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 16 ++++++++--------
libmpathpersist/mpath_updatepr.c | 8 ++++----
libmultipath/structs.h | 9 ++++-----
multipathd/cli_handlers.c | 22 +++++++++++-----------
multipathd/main.c | 17 ++++++++---------
5 files changed, 35 insertions(+), 37 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 19220d7a..d596b5bd 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -663,10 +663,10 @@ static void set_ignored_key(struct multipath *mpp, uint8_t *key)
memset(key, 0, 8);
if (!get_be64(mpp->reservation_key))
return;
- if (get_prflag(mpp->alias) == PRFLAG_UNSET)
+ if (get_prflag(mpp->alias) == PR_UNSET)
return;
update_map_pr(mpp, NULL);
- if (mpp->prflag != PRFLAG_SET)
+ if (mpp->prflag != PR_SET)
return;
memcpy(key, &mpp->reservation_key, 8);
}
@@ -767,16 +767,16 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
struct prin_resp *resp;
unsigned int i;
int ret = MPATH_PR_OTHER, isFound;
- bool was_set = (mpp->prflag == PRFLAG_SET);
+ bool was_set = (mpp->prflag == PR_SET);
/* If pr is explicitly unset, it must be manually set */
- if (mpp->prflag == PRFLAG_UNSET)
+ if (mpp->prflag == PR_UNSET)
return MPATH_PR_SKIP;
if (!get_be64(mpp->reservation_key))
{
/* Nothing to do. Assuming pr mgmt feature is disabled*/
- mpp->prflag = PRFLAG_UNSET;
+ mpp->prflag = PR_UNSET;
condlog(was_set ? 2 : 4,
"%s: reservation_key not set in multipath.conf",
mpp->alias);
@@ -801,11 +801,11 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
if (ret != MPATH_PR_SUCCESS )
{
if (ret == MPATH_PR_ILLEGAL_REQ)
- mpp->prflag = PRFLAG_UNSET;
+ mpp->prflag = PR_UNSET;
condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
goto out;
}
- mpp->prflag = PRFLAG_UNSET;
+ mpp->prflag = PR_UNSET;
if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
{
@@ -840,7 +840,7 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
if (isFound)
{
- mpp->prflag = PRFLAG_SET;
+ mpp->prflag = PR_SET;
condlog(was_set ? 3 : 2, "%s: key found. prflag set.", mpp->alias);
} else
condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
index 7cf82f50..c8fbe4a2 100644
--- a/libmpathpersist/mpath_updatepr.c
+++ b/libmpathpersist/mpath_updatepr.c
@@ -84,13 +84,13 @@ int get_prflag(char *mapname)
snprintf(str, sizeof(str), "getprstatus map %s", mapname);
reply = do_pr(mapname, str);
if (!reply)
- prflag = PRFLAG_UNKNOWN;
+ prflag = PR_UNKNOWN;
else if (strncmp(reply, "unset", 5) == 0)
- prflag = PRFLAG_UNSET;
+ prflag = PR_UNSET;
else if (strncmp(reply, "set", 3) == 0)
- prflag = PRFLAG_SET;
+ prflag = PR_SET;
else
- prflag = PRFLAG_UNKNOWN;
+ prflag = PR_UNKNOWN;
free(reply);
return prflag;
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 5dc00fbc..24135aa6 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -406,11 +406,10 @@ struct path {
typedef int (pgpolicyfn) (struct multipath *, vector);
-
-enum prflag_value {
- PRFLAG_UNKNOWN,
- PRFLAG_UNSET,
- PRFLAG_SET,
+enum pr_value {
+ PR_UNKNOWN,
+ PR_UNSET,
+ PR_SET,
};
struct multipath {
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 117570e1..8e2e2cea 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -1240,14 +1240,15 @@ cli_shutdown (void * v, struct strbuf *reply, void * data)
return 0;
}
+static const char *const pr_str[] = {
+ [PR_UNKNOWN] = "unknown\n",
+ [PR_UNSET] = "unset\n",
+ [PR_SET] = "set\n",
+};
+
static int
cli_getprstatus (void * v, struct strbuf *reply, void * data)
{
- static const char * const prflag_str[] = {
- [PRFLAG_UNKNOWN] = "unknown\n",
- [PRFLAG_UNSET] = "unset\n",
- [PRFLAG_SET] = "set\n",
- };
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, KEY_MAP);
@@ -1258,7 +1259,7 @@ cli_getprstatus (void * v, struct strbuf *reply, void * data)
if (!mpp)
return -ENODEV;
- append_strbuf_str(reply, prflag_str[mpp->prflag]);
+ append_strbuf_str(reply, pr_str[mpp->prflag]);
condlog(3, "%s: reply = %s", param, get_strbuf_str(reply));
@@ -1278,12 +1279,11 @@ cli_setprstatus(void * v, struct strbuf *reply, void * data)
if (!mpp)
return -ENODEV;
- if (mpp->prflag != PRFLAG_SET) {
- mpp->prflag = PRFLAG_SET;
+ if (mpp->prflag != PR_SET) {
+ mpp->prflag = PR_SET;
condlog(2, "%s: prflag set", param);
}
-
return 0;
}
@@ -1300,8 +1300,8 @@ cli_unsetprstatus(void * v, struct strbuf *reply, void * data)
if (!mpp)
return -ENODEV;
- if (mpp->prflag != PRFLAG_UNSET) {
- mpp->prflag = PRFLAG_UNSET;
+ if (mpp->prflag != PR_UNSET) {
+ mpp->prflag = PR_UNSET;
condlog(2, "%s: prflag unset", param);
}
diff --git a/multipathd/main.c b/multipathd/main.c
index 4a1b38e9..bd4342a3 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -644,7 +644,7 @@ pr_register_active_paths(struct multipath *mpp)
vector_foreach_slot (mpp->pg, pgp, i) {
vector_foreach_slot (pgp->paths, pp, j) {
- if (mpp->prflag == PRFLAG_UNSET)
+ if (mpp->prflag == PR_UNSET)
return;
if ((pp->state == PATH_UP) || (pp->state == PATH_GHOST))
mpath_pr_event_handle(pp);
@@ -1253,7 +1253,7 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map)
int start_waiter = 0;
int ret;
int ro;
- unsigned char prflag = PRFLAG_UNSET;
+ unsigned char prflag = PR_UNSET;
/*
* need path UID to go any further
@@ -1381,8 +1381,7 @@ rescan:
sync_map_state(mpp);
if (retries >= 0) {
- if ((mpp->prflag == PRFLAG_SET && prflag != PRFLAG_SET) ||
- start_waiter)
+ if ((mpp->prflag == PR_SET && prflag != PR_SET) || start_waiter)
pr_register_active_paths(mpp);
condlog(2, "%s [%s]: path added to devmap %s",
pp->dev, pp->dev_t, mpp->alias);
@@ -2645,7 +2644,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
}
if (newstate == PATH_UP || newstate == PATH_GHOST) {
- if (pp->mpp->prflag != PRFLAG_UNSET) {
+ if (pp->mpp->prflag != PR_UNSET) {
int prflag = pp->mpp->prflag;
/*
* Check Persistent Reservation.
@@ -2653,8 +2652,8 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
condlog(2, "%s: checking persistent "
"reservation registration", pp->dev);
mpath_pr_event_handle(pp);
- if (pp->mpp->prflag == PRFLAG_SET &&
- prflag != PRFLAG_SET)
+ if (pp->mpp->prflag == PR_SET &&
+ prflag != PR_SET)
pr_register_active_paths(pp->mpp);
}
}
@@ -3947,14 +3946,14 @@ static void mpath_pr_event_handle(struct path *pp)
struct prout_param_descriptor *param;
if (pp->bus != SYSFS_BUS_SCSI) {
- mpp->prflag = PRFLAG_UNSET;
+ mpp->prflag = PR_UNSET;
return;
}
if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS)
return;
- if (mpp->prflag != PRFLAG_SET)
+ if (mpp->prflag != PR_SET)
return;
param = (struct prout_param_descriptor *)calloc(1, sizeof(struct prout_param_descriptor));

View File

@ -0,0 +1,44 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:10:59 -0400
Subject: [PATCH] libmpathpersist: use a switch statement for prout command
finalizing
Change the code at the end of do_mpath_persistent_reserve_out() to
use a switch statement instead of multiple if statements. A future
patch will add more actions here, and a switch statement looks cleaner.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index d596b5bd..679e82be 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -744,15 +744,19 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
goto out1;
}
- if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) ||
- (rq_servact == MPATH_PROUT_REG_IGN_SA)))
- {
+ if (ret != MPATH_PR_SUCCESS)
+ goto out1;
+
+ switch (rq_servact) {
+ case MPATH_PROUT_REG_SA:
+ case MPATH_PROUT_REG_IGN_SA:
if (prkey == 0) {
update_prflag(alias, 0);
update_prkey(alias, 0);
} else
update_prflag(alias, 1);
- } else if ((ret == MPATH_PR_SUCCESS) && (rq_servact == MPATH_PROUT_CLEAR_SA)) {
+ break;
+ case MPATH_PROUT_CLEAR_SA:
update_prflag(alias, 0);
update_prkey(alias, 0);
}

View File

@ -0,0 +1,444 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 10 Jul 2025 14:11:00 -0400
Subject: [PATCH] libmpathpersist: Add safety check for preempting on key
change
When a reservation key is changed from one non-zero value to another,
libmpathpersist checks if the old key is still holding the reservation,
and preempts it if it is. This was only safe if two nodes never share
the same key. If a node uses the same key as another node that is
holding the reservation, and switches keys so that it no longer matches,
it will end up preempting the reservation. This is clearly unexpected
behavior, and it can come about by a simple accident of registering a
device with the wrong key, and then immediately fixing it.
To handle this, add code to track if a device is the reservation holder
to multipathd. multipathd now has three new commands "getprhold",
"setprhold", and "unsetprhold". These commands work like the equivalent
*prstatus commands. libmpathpersist calls setprhold on RESERVE commands
and PREEMPT commands when the preempted key is holding the reservation
and unsetprhold on RELEASE commands. Also, calling unsetprflag causes
prhold to be unset as well, so CLEAR commands and REGISTER commands with
a 0x0 service action key will also unset prhold. libmpathpersist() will
also unset prhold if it notices that the device cannot be holding a
reservation in preempt_missing_path().
When a new multipath device is created, its initial prhold state is
PR_UNKNOWN until it checks the current reservation, just like with
prflag. If multipathd ever finds that a device's registration has been
preempted or cleared in update_map_pr(), it unsets prhold, just like
with prflag.
Now, before libmpathpersist preempts a reservation when changing keys
it also checks if multipathd thinks that the device is holding
the reservation. If it does not, then libmpathpersist won't preempt
the key. It will assume that another node is holding the reservation
with the same key.
I should note that this safety check only stops a node not holding the
reservation from preempting the node holding the reservation. If the
node holding the reservation changes its key, but it fails to change the
resevation key, because that path is down or gone, it will still issue
the preempt to move the reservation to a usable path, even if another
node is using the same key. This will remove the registrations for that
other node. It also will not work correctly if multipathd stops tracking
a device for some reason. It's only a best-effort safety check.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 85 ++++++++++++++++++++++++-----
libmpathpersist/mpath_updatepr.c | 19 ++++++-
libmpathpersist/mpathpr.h | 2 +
libmultipath/structs.h | 1 +
multipathd/callbacks.c | 3 +
multipathd/cli.c | 4 +-
multipathd/cli.h | 3 +
multipathd/cli_handlers.c | 46 ++++++++++++++++
multipathd/main.c | 34 +++++++++++-
9 files changed, 179 insertions(+), 18 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 679e82be..1948de8d 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -270,6 +270,10 @@ mpath_prout_common(struct multipath *mpp, int rq_servact, int rq_scope,
* holding the reservation on a path that couldn't get its key updated,
* either because it is down or no longer part of the multipath device,
* you need to preempt the reservation to a usable path with the new key
+ *
+ * Also, it's possible that the reservation was preempted, and the device
+ * is being re-registered. If it appears that is the case, clear
+ * mpp->prhold in multipathd.
*/
void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
int noisy)
@@ -282,12 +286,19 @@ void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
int status;
/*
- * If you previously didn't have a key registered or you didn't
- * switch to a different key, there's no need to preempt. Also, you
- * can't preempt if you no longer have a registered key
+ * If you previously didn't have a key registered, you can't
+ * be holding the reservation. Also, you can't preempt if you
+ * no longer have a registered key
*/
- if (memcmp(key, zero, 8) == 0 || memcmp(sa_key, zero, 8) == 0 ||
- memcmp(key, sa_key, 8) == 0)
+ if (memcmp(key, zero, 8) == 0 || memcmp(sa_key, zero, 8) == 0) {
+ update_prhold(mpp->alias, false);
+ return;
+ }
+ /*
+ * If you didn't switch to a different key, there is no need to
+ * preempt.
+ */
+ if (memcmp(key, sa_key, 8) == 0)
return;
status = mpath_prin_activepath(mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
@@ -297,13 +308,29 @@ void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
return;
}
/* If there is no reservation, there's nothing to preempt */
- if (!resp.prin_descriptor.prin_readresv.additional_length)
+ if (!resp.prin_descriptor.prin_readresv.additional_length) {
+ update_prhold(mpp->alias, false);
return;
+ }
/*
* If there reservation is not held by the old key, you don't
* want to preempt it
*/
- if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) != 0)
+ if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) != 0) {
+ /*
+ * If reservation key doesn't match either the old or
+ * the new key, then clear prhold.
+ */
+ if (memcmp(sa_key, resp.prin_descriptor.prin_readresv.key, 8) != 0)
+ update_prhold(mpp->alias, false);
+ return;
+ }
+
+ /*
+ * If multipathd doesn't think it is holding the reservation, don't
+ * preempt it
+ */
+ if (get_prhold(mpp->alias) != PR_SET)
return;
/* Assume this key is being held by an inaccessable path on this
* node. libmpathpersist has never worked if multiple nodes share
@@ -654,19 +681,36 @@ fail_resume:
return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
}
+static int reservation_key_matches(struct multipath *mpp, uint8_t *key, int noisy)
+{
+ struct prin_resp resp = {{{.prgeneration = 0}}};
+ int status;
+
+ status = mpath_prin_activepath(mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
+ if (status != MPATH_PR_SUCCESS) {
+ condlog(0, "%s: pr in read reservation command failed.", mpp->wwid);
+ return YNU_UNDEF;
+ }
+ if (!resp.prin_descriptor.prin_readresv.additional_length)
+ return YNU_NO;
+ if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) == 0)
+ return YNU_YES;
+ return YNU_NO;
+}
+
/*
* for MPATH_PROUT_REG_IGN_SA, we use the ignored paramp->key to store the
- * currently registered key.
+ * currently registered key for use in preempt_missing_path(), but only if
+ * the key is holding the reservation.
*/
static void set_ignored_key(struct multipath *mpp, uint8_t *key)
{
memset(key, 0, 8);
if (!get_be64(mpp->reservation_key))
return;
- if (get_prflag(mpp->alias) == PR_UNSET)
+ if (get_prhold(mpp->alias) == PR_UNSET)
return;
- update_map_pr(mpp, NULL);
- if (mpp->prflag != PR_SET)
+ if (reservation_key_matches(mpp, key, 0) == YNU_NO)
return;
memcpy(key, &mpp->reservation_key, 8);
}
@@ -680,6 +724,7 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
int ret;
uint64_t prkey;
struct config *conf;
+ bool preempting_reservation = false;
ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
if (ret != MPATH_PR_SUCCESS)
@@ -729,9 +774,12 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
case MPATH_PROUT_REG_IGN_SA:
ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
break;
- case MPATH_PROUT_RES_SA :
- case MPATH_PROUT_PREE_SA :
- case MPATH_PROUT_PREE_AB_SA :
+ case MPATH_PROUT_PREE_SA:
+ case MPATH_PROUT_PREE_AB_SA:
+ if (reservation_key_matches(mpp, paramp->sa_key, noisy) == YNU_YES)
+ preempting_reservation = true;
+ /* fallthrough */
+ case MPATH_PROUT_RES_SA:
case MPATH_PROUT_CLEAR_SA:
ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
paramp, noisy, NULL);
@@ -759,6 +807,15 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
case MPATH_PROUT_CLEAR_SA:
update_prflag(alias, 0);
update_prkey(alias, 0);
+ break;
+ case MPATH_PROUT_RES_SA:
+ case MPATH_PROUT_REL_SA:
+ update_prhold(alias, (rq_servact == MPATH_PROUT_RES_SA));
+ break;
+ case MPATH_PROUT_PREE_SA:
+ case MPATH_PROUT_PREE_AB_SA:
+ if (preempting_reservation)
+ update_prhold(alias, true);
}
out1:
free(alias);
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
index c8fbe4a2..dd8dd48e 100644
--- a/libmpathpersist/mpath_updatepr.c
+++ b/libmpathpersist/mpath_updatepr.c
@@ -75,13 +75,13 @@ static int do_update_pr(char *alias, char *cmd, char *key)
return ret;
}
-int get_prflag(char *mapname)
+static int do_get_pr(char *mapname, const char *cmd)
{
char str[256];
char *reply;
int prflag;
- snprintf(str, sizeof(str), "getprstatus map %s", mapname);
+ snprintf(str, sizeof(str), "%s map %s", cmd, mapname);
reply = do_pr(mapname, str);
if (!reply)
prflag = PR_UNKNOWN;
@@ -96,11 +96,26 @@ int get_prflag(char *mapname)
return prflag;
}
+int get_prflag(char *mapname)
+{
+ return do_get_pr(mapname, "getprstatus");
+}
+
+int get_prhold(char *mapname)
+{
+ return do_get_pr(mapname, "getprhold");
+}
+
int update_prflag(char *mapname, int set) {
return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus",
NULL);
}
+int update_prhold(char *mapname, bool set)
+{
+ return do_update_pr(mapname, (set) ? "setprhold" : "unsetprhold", NULL);
+}
+
int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags) {
char str[256];
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 614be530..b668ef58 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -9,6 +9,8 @@
int update_prflag(char *mapname, int set);
int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags);
int get_prflag(char *mapname);
+int get_prhold(char *mapname);
+int update_prhold(char *mapname, bool set);
#define update_prkey(mapname, prkey) update_prkey_flags(mapname, prkey, 0)
#endif
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 24135aa6..6d0cd867 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -489,6 +489,7 @@ struct multipath {
struct be64 reservation_key;
uint8_t sa_flags;
int prflag;
+ int prhold;
int all_tg_pt;
struct gen_multipath generic_mp;
bool fpin_must_reload;
diff --git a/multipathd/callbacks.c b/multipathd/callbacks.c
index fb87b280..b6b57f45 100644
--- a/multipathd/callbacks.c
+++ b/multipathd/callbacks.c
@@ -69,4 +69,7 @@ void init_handler_callbacks(void)
set_handler_callback(VRB_UNSETMARGINAL | Q1_PATH, HANDLER(cli_unset_marginal));
set_handler_callback(VRB_UNSETMARGINAL | Q1_MAP,
HANDLER(cli_unset_all_marginal));
+ set_handler_callback(VRB_GETPRHOLD | Q1_MAP, HANDLER(cli_getprhold));
+ set_handler_callback(VRB_SETPRHOLD | Q1_MAP, HANDLER(cli_setprhold));
+ set_handler_callback(VRB_UNSETPRHOLD | Q1_MAP, HANDLER(cli_unsetprhold));
}
diff --git a/multipathd/cli.c b/multipathd/cli.c
index 0c89b7cd..bccdda48 100644
--- a/multipathd/cli.c
+++ b/multipathd/cli.c
@@ -224,7 +224,9 @@ load_keys (void)
r += add_key(keys, "setmarginal", VRB_SETMARGINAL, 0);
r += add_key(keys, "unsetmarginal", VRB_UNSETMARGINAL, 0);
r += add_key(keys, "all", KEY_ALL, 0);
-
+ r += add_key(keys, "getprhold", VRB_GETPRHOLD, 0);
+ r += add_key(keys, "setprhold", VRB_SETPRHOLD, 0);
+ r += add_key(keys, "unsetprhold", VRB_UNSETPRHOLD, 0);
if (r) {
free_keys(keys);
diff --git a/multipathd/cli.h b/multipathd/cli.h
index c6b79c9d..925575ad 100644
--- a/multipathd/cli.h
+++ b/multipathd/cli.h
@@ -38,6 +38,9 @@ enum {
VRB_UNSETMARGINAL = 23,
VRB_SHUTDOWN = 24,
VRB_QUIT = 25,
+ VRB_GETPRHOLD = 26,
+ VRB_SETPRHOLD = 27,
+ VRB_UNSETPRHOLD = 28,
/* Qualifiers, values must be different from verbs */
KEY_PATH = 65,
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 8e2e2cea..94d0b63f 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -1304,6 +1304,10 @@ cli_unsetprstatus(void * v, struct strbuf *reply, void * data)
mpp->prflag = PR_UNSET;
condlog(2, "%s: prflag unset", param);
}
+ if (mpp->prhold != PR_UNSET) {
+ mpp->prhold = PR_UNSET;
+ condlog(2, "%s: prhold unset (by clearing prflag)", param);
+ }
return 0;
}
@@ -1391,6 +1395,48 @@ cli_setprkey(void * v, struct strbuf *reply, void * data)
return ret;
}
+static int do_prhold(struct vectors *vecs, char *param, int prhold)
+{
+ struct multipath *mpp = find_mp_by_str(vecs->mpvec, param);
+
+ if (!mpp)
+ return -ENODEV;
+
+ if (mpp->prhold != prhold) {
+ mpp->prhold = prhold;
+ condlog(2, "%s: prhold %s", param, pr_str[prhold]);
+ }
+
+ return 0;
+}
+
+static int cli_setprhold(void *v, struct strbuf *reply, void *data)
+{
+ return do_prhold((struct vectors *)data, get_keyparam(v, KEY_MAP), PR_SET);
+}
+
+static int cli_unsetprhold(void *v, struct strbuf *reply, void *data)
+{
+ return do_prhold((struct vectors *)data, get_keyparam(v, KEY_MAP), PR_UNSET);
+}
+
+static int cli_getprhold(void *v, struct strbuf *reply, void *data)
+{
+ struct multipath *mpp;
+ struct vectors *vecs = (struct vectors *)data;
+ char *param = get_keyparam(v, KEY_MAP);
+
+ param = convert_dev(param, 0);
+
+ mpp = find_mp_by_str(vecs->mpvec, param);
+ if (!mpp)
+ return -ENODEV;
+
+ append_strbuf_str(reply, pr_str[mpp->prhold]);
+ condlog(3, "%s: reply = %s", param, get_strbuf_str(reply));
+ return 0;
+}
+
static int cli_set_marginal(void * v, struct strbuf *reply, void * data)
{
struct vectors * vecs = (struct vectors *)data;
diff --git a/multipathd/main.c b/multipathd/main.c
index bd4342a3..d1d209d3 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3939,6 +3939,33 @@ main (int argc, char *argv[])
return (child(NULL));
}
+static void check_prhold(struct multipath *mpp, struct path *pp)
+{
+ struct prin_resp resp = {{{.prgeneration = 0}}};
+ int status;
+
+ if (mpp->prflag == PR_UNSET) {
+ mpp->prhold = PR_UNSET;
+ return;
+ }
+ if (mpp->prflag != PR_SET || mpp->prhold != PR_UNKNOWN)
+ return;
+
+ status = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RRES_SA, &resp, 0);
+ if (status != MPATH_PR_SUCCESS) {
+ condlog(0, "%s: pr in read reservation command failed: %d",
+ mpp->wwid, status);
+ return;
+ }
+ mpp->prhold = PR_UNSET;
+ if (!resp.prin_descriptor.prin_readresv.additional_length)
+ return;
+
+ if (memcmp(&mpp->reservation_key,
+ resp.prin_descriptor.prin_readresv.key, 8) == 0)
+ mpp->prhold = PR_SET;
+}
+
static void mpath_pr_event_handle(struct path *pp)
{
struct multipath *mpp = pp->mpp;
@@ -3950,8 +3977,13 @@ static void mpath_pr_event_handle(struct path *pp)
return;
}
- if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS)
+ if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS) {
+ if (mpp->prflag == PR_UNSET)
+ mpp->prhold = PR_UNSET;
return;
+ }
+
+ check_prhold(mpp, pp);
if (mpp->prflag != PR_SET)
return;

View File

@ -0,0 +1,35 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:42 -0400
Subject: [PATCH] libmpathpersist: remove update_map_pr code for NULL pp
Since update_map_pr is always called with pp set now, remove the code
to handle being called with NULL pp.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 9 +--------
1 file changed, 1 insertion(+), 8 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 1948de8d..04bbc455 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -850,15 +850,8 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
return MPATH_PR_OTHER;
}
- if (!pp && count_active_paths(mpp) == 0) {
- condlog(2, "%s: No available paths to check pr status", mpp->alias);
- goto out;
- }
- if (pp)
- ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, noisy);
- else
- ret = mpath_prin_activepath(mpp, MPATH_PRIN_RKEY_SA, resp, noisy);
+ ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, noisy);
if (ret != MPATH_PR_SUCCESS )
{
if (ret == MPATH_PR_ILLEGAL_REQ)

View File

@ -0,0 +1,222 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:43 -0400
Subject: [PATCH] libmpathpersist: move update_map_pr to multipathd
multipathd is now the only program that calls update_map_pr(), so move
it there, and make it static. There are no other code changes.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/libmpathpersist.version | 1 -
libmpathpersist/mpath_persist_int.c | 83 -------------------------
libmpathpersist/mpath_persist_int.h | 3 +-
multipathd/main.c | 80 ++++++++++++++++++++++++
4 files changed, 81 insertions(+), 86 deletions(-)
diff --git a/libmpathpersist/libmpathpersist.version b/libmpathpersist/libmpathpersist.version
index faa4257b..8068920e 100644
--- a/libmpathpersist/libmpathpersist.version
+++ b/libmpathpersist/libmpathpersist.version
@@ -33,5 +33,4 @@ __LIBMPATHPERSIST_INT_2.0.0 {
mpath_alloc_prin_response;
prin_do_scsi_ioctl;
prout_do_scsi_ioctl;
- update_map_pr;
};
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 04bbc455..76bdbc63 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -821,86 +821,3 @@ out1:
free(alias);
return ret;
}
-
-int update_map_pr(struct multipath *mpp, struct path *pp)
-{
- int noisy=0;
- struct prin_resp *resp;
- unsigned int i;
- int ret = MPATH_PR_OTHER, isFound;
- bool was_set = (mpp->prflag == PR_SET);
-
- /* If pr is explicitly unset, it must be manually set */
- if (mpp->prflag == PR_UNSET)
- return MPATH_PR_SKIP;
-
- if (!get_be64(mpp->reservation_key))
- {
- /* Nothing to do. Assuming pr mgmt feature is disabled*/
- mpp->prflag = PR_UNSET;
- condlog(was_set ? 2 : 4,
- "%s: reservation_key not set in multipath.conf",
- mpp->alias);
- return MPATH_PR_SKIP;
- }
-
- resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
- if (!resp)
- {
- condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
- return MPATH_PR_OTHER;
- }
-
- ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, noisy);
- if (ret != MPATH_PR_SUCCESS )
- {
- if (ret == MPATH_PR_ILLEGAL_REQ)
- mpp->prflag = PR_UNSET;
- condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
- goto out;
- }
- mpp->prflag = PR_UNSET;
-
- if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
- {
- condlog(was_set ? 1 : 3,
- "%s: No key found. Device may not be registered. ",
- mpp->alias);
- goto out;
- }
-
- condlog(3, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
- get_be64(mpp->reservation_key));
-
- isFound =0;
- for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ )
- {
- if (libmp_verbosity >= 3) {
- condlog(3, "%s: PR IN READKEYS[%d] reservation key:",
- mpp->alias, i);
- dumpHex((char *)&resp->prin_descriptor.prin_readkeys
- .key_list[i * 8],
- 8, 1);
- }
-
- if (!memcmp(&mpp->reservation_key,
- &resp->prin_descriptor.prin_readkeys.key_list[i * 8],
- 8)) {
- condlog(3, "%s: reservation key found in pr in readkeys response",
- mpp->alias);
- isFound =1;
- }
- }
-
- if (isFound)
- {
- mpp->prflag = PR_SET;
- condlog(was_set ? 3 : 2, "%s: key found. prflag set.", mpp->alias);
- } else
- condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
- mpp->alias);
-
-out:
- free(resp);
- return ret;
-}
diff --git a/libmpathpersist/mpath_persist_int.h b/libmpathpersist/mpath_persist_int.h
index 73c95863..d9fc7448 100644
--- a/libmpathpersist/mpath_persist_int.h
+++ b/libmpathpersist/mpath_persist_int.h
@@ -19,7 +19,6 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int noisy);
int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy);
-void dumpHex(const char* , int len, int no_ascii);
-int update_map_pr(struct multipath *mpp, struct path *pp);
+void dumpHex(const char *, int len, int no_ascii);
#endif /* _MPATH_PERSIST_INT_H */
diff --git a/multipathd/main.c b/multipathd/main.c
index d1d209d3..0af9cb1c 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3966,6 +3966,86 @@ static void check_prhold(struct multipath *mpp, struct path *pp)
mpp->prhold = PR_SET;
}
+static int update_map_pr(struct multipath *mpp, struct path *pp)
+{
+ int noisy = 0;
+ struct prin_resp *resp;
+ unsigned int i;
+ int ret = MPATH_PR_OTHER, isFound;
+ bool was_set = (mpp->prflag == PR_SET);
+
+ /* If pr is explicitly unset, it must be manually set */
+ if (mpp->prflag == PR_UNSET)
+ return MPATH_PR_SKIP;
+
+ if (!get_be64(mpp->reservation_key)) {
+ /* Nothing to do. Assuming pr mgmt feature is disabled*/
+ mpp->prflag = PR_UNSET;
+ condlog(was_set ? 2 : 4,
+ "%s: reservation_key not set in multipath.conf",
+ mpp->alias);
+ return MPATH_PR_SKIP;
+ }
+
+ resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
+ if (!resp) {
+ condlog(0, "%s : failed to alloc resp in update_map_pr",
+ mpp->alias);
+ return MPATH_PR_OTHER;
+ }
+
+ ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, noisy);
+ if (ret != MPATH_PR_SUCCESS) {
+ if (ret == MPATH_PR_ILLEGAL_REQ)
+ mpp->prflag = PR_UNSET;
+ condlog(0, "%s : pr in read keys service action failed Error=%d",
+ mpp->alias, ret);
+ goto out;
+ }
+ mpp->prflag = PR_UNSET;
+
+ if (resp->prin_descriptor.prin_readkeys.additional_length == 0) {
+ condlog(was_set ? 1 : 3,
+ "%s: No key found. Device may not be registered. ",
+ mpp->alias);
+ goto out;
+ }
+
+ condlog(3, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
+ get_be64(mpp->reservation_key));
+
+ isFound = 0;
+ for (i = 0;
+ i < resp->prin_descriptor.prin_readkeys.additional_length / 8; i++) {
+ if (libmp_verbosity >= 3) {
+ condlog(3, "%s: PR IN READKEYS[%d] reservation key:",
+ mpp->alias, i);
+ dumpHex((char *)&resp->prin_descriptor.prin_readkeys
+ .key_list[i * 8],
+ 8, 1);
+ }
+
+ if (!memcmp(&mpp->reservation_key,
+ &resp->prin_descriptor.prin_readkeys.key_list[i * 8],
+ 8)) {
+ condlog(3, "%s: reservation key found in pr in readkeys response",
+ mpp->alias);
+ isFound = 1;
+ }
+ }
+
+ if (isFound) {
+ mpp->prflag = PR_SET;
+ condlog(was_set ? 3 : 2, "%s: key found. prflag set.", mpp->alias);
+ } else
+ condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
+ mpp->alias);
+
+out:
+ free(resp);
+ return ret;
+}
+
static void mpath_pr_event_handle(struct path *pp)
{
struct multipath *mpp = pp->mpp;

View File

@ -0,0 +1,139 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:44 -0400
Subject: [PATCH] multipathd: clean up update_map_pr and mpath_pr_event_handle
Store the READ KEYS response and the prout_param_descriptor on the stack
to avoid having to fail these functions for allocation reasons. Don't
explicitly check for additional_length == 0, since the for-loop already
handles that. Also cleanup formatting issues,remove redundant messages,
and reduce the log level of others.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/main.c | 62 +++++++++++++++--------------------------------
1 file changed, 19 insertions(+), 43 deletions(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 0af9cb1c..bc42f2fa 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3968,8 +3968,7 @@ static void check_prhold(struct multipath *mpp, struct path *pp)
static int update_map_pr(struct multipath *mpp, struct path *pp)
{
- int noisy = 0;
- struct prin_resp *resp;
+ struct prin_resp resp;
unsigned int i;
int ret = MPATH_PR_OTHER, isFound;
bool was_set = (mpp->prflag == PR_SET);
@@ -3987,51 +3986,34 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
return MPATH_PR_SKIP;
}
- resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
- if (!resp) {
- condlog(0, "%s : failed to alloc resp in update_map_pr",
- mpp->alias);
- return MPATH_PR_OTHER;
- }
+ memset(&resp, 0, sizeof(resp));
- ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, noisy);
+ ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, &resp, 0);
if (ret != MPATH_PR_SUCCESS) {
if (ret == MPATH_PR_ILLEGAL_REQ)
mpp->prflag = PR_UNSET;
condlog(0, "%s : pr in read keys service action failed Error=%d",
mpp->alias, ret);
- goto out;
+ return ret;
}
mpp->prflag = PR_UNSET;
- if (resp->prin_descriptor.prin_readkeys.additional_length == 0) {
- condlog(was_set ? 1 : 3,
- "%s: No key found. Device may not be registered. ",
- mpp->alias);
- goto out;
- }
-
- condlog(3, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
+ condlog(4, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
get_be64(mpp->reservation_key));
isFound = 0;
for (i = 0;
- i < resp->prin_descriptor.prin_readkeys.additional_length / 8; i++) {
- if (libmp_verbosity >= 3) {
- condlog(3, "%s: PR IN READKEYS[%d] reservation key:",
+ i < resp.prin_descriptor.prin_readkeys.additional_length / 8; i++) {
+ uint8_t *keyp = &resp.prin_descriptor.prin_readkeys.key_list[i * 8];
+
+ if (libmp_verbosity >= 4) {
+ condlog(4, "%s: PR IN READKEYS[%d] reservation key:",
mpp->alias, i);
- dumpHex((char *)&resp->prin_descriptor.prin_readkeys
- .key_list[i * 8],
- 8, 1);
+ dumpHex((char *)keyp, 8, 1);
}
- if (!memcmp(&mpp->reservation_key,
- &resp->prin_descriptor.prin_readkeys.key_list[i * 8],
- 8)) {
- condlog(3, "%s: reservation key found in pr in readkeys response",
- mpp->alias);
+ if (!memcmp(&mpp->reservation_key, keyp, 8))
isFound = 1;
- }
}
if (isFound) {
@@ -4041,16 +4023,14 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
mpp->alias);
-out:
- free(resp);
- return ret;
+ return MPATH_PR_SUCCESS;
}
static void mpath_pr_event_handle(struct path *pp)
{
struct multipath *mpp = pp->mpp;
int ret;
- struct prout_param_descriptor *param;
+ struct prout_param_descriptor param;
if (pp->bus != SYSFS_BUS_SCSI) {
mpp->prflag = PR_UNSET;
@@ -4068,21 +4048,17 @@ static void mpath_pr_event_handle(struct path *pp)
if (mpp->prflag != PR_SET)
return;
- param = (struct prout_param_descriptor *)calloc(1, sizeof(struct prout_param_descriptor));
- if (!param)
- return;
+ memset(&param, 0, sizeof(param));
- param->sa_flags = mpp->sa_flags;
- memcpy(param->sa_key, &mpp->reservation_key, 8);
- param->num_transportid = 0;
+ param.sa_flags = mpp->sa_flags;
+ memcpy(param.sa_key, &mpp->reservation_key, 8);
+ param.num_transportid = 0;
condlog(3, "device %s:%s", pp->dev, pp->mpp->wwid);
- ret = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REG_IGN_SA, 0, 0, param, 0);
+ ret = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REG_IGN_SA, 0, 0, &param, 0);
if (ret != MPATH_PR_SUCCESS )
{
condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret);
}
-
- free(param);
}

View File

@ -0,0 +1,65 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:45 -0400
Subject: [PATCH] libmpathpersist: clean up duplicate function declarations
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.h | 1 -
libmpathpersist/mpath_pr_ioctl.c | 10 +++-------
mpathpersist/main.c | 2 --
3 files changed, 3 insertions(+), 10 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.h b/libmpathpersist/mpath_persist_int.h
index d9fc7448..aefc17e4 100644
--- a/libmpathpersist/mpath_persist_int.h
+++ b/libmpathpersist/mpath_persist_int.h
@@ -6,7 +6,6 @@
* but aren't part of the public libmpathpersist API.
*/
-void * mpath_alloc_prin_response(int prin_sa);
int do_mpath_persistent_reserve_in(vector curmp, vector pathvec,
int fd, int rq_servact,
struct prin_resp *resp, int noisy);
diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c
index 7e1d2896..dfdbbb65 100644
--- a/libmpathpersist/mpath_pr_ioctl.c
+++ b/libmpathpersist/mpath_pr_ioctl.c
@@ -14,19 +14,15 @@
#include "mpath_pr_ioctl.h"
#include "mpath_persist.h"
#include "unaligned.h"
-
#include "debug.h"
#include "structs.h" /* FILE_NAME_SIZE */
+#include "mpath_persist_int.h"
#define TIMEOUT 2000
#define MAXRETRY 5
-int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp *resp, int noisy);
-int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr,
- SenseData_t *Sensedata);
-void dumpHex(const char* str, int len, int no_ascii);
-int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy);
+int mpath_translate_response(char *dev, struct sg_io_hdr io_hdr,
+ SenseData_t *Sensedata);
uint32_t format_transportids(struct prout_param_descriptor *paramp);
void convert_be32_to_cpu(uint32_t *num);
void convert_be16_to_cpu(uint16_t *num);
diff --git a/mpathpersist/main.c b/mpathpersist/main.c
index b6617902..2cacafb7 100644
--- a/mpathpersist/main.c
+++ b/mpathpersist/main.c
@@ -38,8 +38,6 @@ void mpath_print_buf_readcap(struct prin_resp *pr_buff);
void mpath_print_buf_readfullstat(struct prin_resp *pr_buff);
void mpath_print_buf_readresv(struct prin_resp *pr_buff);
void mpath_print_buf_readkeys(struct prin_resp *pr_buff);
-void dumpHex(const char* str, int len, int no_ascii);
-void * mpath_alloc_prin_response(int prin_sa);
void mpath_print_transport_id(struct prin_fulldescr *fdesc);
int construct_transportid(const char * inp, struct transportid transid[], int num_transportids);

View File

@ -0,0 +1,148 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:46 -0400
Subject: [PATCH] multipathd: wrap setting and unsetting prflag
When prflag is unset, prhold and sa_flags should also be unset. A future
patch will add another variable to be set when prflag is set. Wrap all
these actions in set_pr() and unset_pr().
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
multipathd/cli_handlers.c | 10 ++++------
multipathd/main.c | 34 ++++++++++++++++++++--------------
multipathd/main.h | 2 ++
3 files changed, 26 insertions(+), 20 deletions(-)
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 94d0b63f..ee2764b9 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -1280,7 +1280,7 @@ cli_setprstatus(void * v, struct strbuf *reply, void * data)
return -ENODEV;
if (mpp->prflag != PR_SET) {
- mpp->prflag = PR_SET;
+ set_pr(mpp);
condlog(2, "%s: prflag set", param);
}
@@ -1301,12 +1301,10 @@ cli_unsetprstatus(void * v, struct strbuf *reply, void * data)
return -ENODEV;
if (mpp->prflag != PR_UNSET) {
- mpp->prflag = PR_UNSET;
condlog(2, "%s: prflag unset", param);
- }
- if (mpp->prhold != PR_UNSET) {
- mpp->prhold = PR_UNSET;
- condlog(2, "%s: prhold unset (by clearing prflag)", param);
+ if (mpp->prhold != PR_UNSET)
+ condlog(2, "%s: prhold unset (by clearing prflag)", param);
+ unset_pr(mpp);
}
return 0;
diff --git a/multipathd/main.c b/multipathd/main.c
index bc42f2fa..e29ab2b8 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3944,10 +3944,6 @@ static void check_prhold(struct multipath *mpp, struct path *pp)
struct prin_resp resp = {{{.prgeneration = 0}}};
int status;
- if (mpp->prflag == PR_UNSET) {
- mpp->prhold = PR_UNSET;
- return;
- }
if (mpp->prflag != PR_SET || mpp->prhold != PR_UNKNOWN)
return;
@@ -3966,6 +3962,18 @@ static void check_prhold(struct multipath *mpp, struct path *pp)
mpp->prhold = PR_SET;
}
+void set_pr(struct multipath *mpp)
+{
+ mpp->prflag = PR_SET;
+}
+
+void unset_pr(struct multipath *mpp)
+{
+ mpp->prflag = PR_UNSET;
+ mpp->prhold = PR_UNSET;
+ mpp->sa_flags = 0;
+}
+
static int update_map_pr(struct multipath *mpp, struct path *pp)
{
struct prin_resp resp;
@@ -3979,7 +3987,7 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
if (!get_be64(mpp->reservation_key)) {
/* Nothing to do. Assuming pr mgmt feature is disabled*/
- mpp->prflag = PR_UNSET;
+ unset_pr(mpp);
condlog(was_set ? 2 : 4,
"%s: reservation_key not set in multipath.conf",
mpp->alias);
@@ -3991,12 +3999,11 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, &resp, 0);
if (ret != MPATH_PR_SUCCESS) {
if (ret == MPATH_PR_ILLEGAL_REQ)
- mpp->prflag = PR_UNSET;
+ unset_pr(mpp);
condlog(0, "%s : pr in read keys service action failed Error=%d",
mpp->alias, ret);
return ret;
}
- mpp->prflag = PR_UNSET;
condlog(4, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
get_be64(mpp->reservation_key));
@@ -4017,11 +4024,13 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
}
if (isFound) {
- mpp->prflag = PR_SET;
+ set_pr(mpp);
condlog(was_set ? 3 : 2, "%s: key found. prflag set.", mpp->alias);
- } else
+ } else {
+ unset_pr(mpp);
condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
mpp->alias);
+ }
return MPATH_PR_SUCCESS;
}
@@ -4033,15 +4042,12 @@ static void mpath_pr_event_handle(struct path *pp)
struct prout_param_descriptor param;
if (pp->bus != SYSFS_BUS_SCSI) {
- mpp->prflag = PR_UNSET;
+ unset_pr(mpp);
return;
}
- if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS) {
- if (mpp->prflag == PR_UNSET)
- mpp->prhold = PR_UNSET;
+ if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS)
return;
- }
check_prhold(mpp, pp);
diff --git a/multipathd/main.h b/multipathd/main.h
index 4fcd6402..c9b3e0fd 100644
--- a/multipathd/main.h
+++ b/multipathd/main.h
@@ -52,4 +52,6 @@ bool check_path_wwid_change(struct path *pp);
int finish_path_init(struct path *pp, struct vectors * vecs);
int resize_map(struct multipath *mpp, unsigned long long size,
struct vectors *vecs);
+void set_pr(struct multipath *mpp);
+void unset_pr(struct multipath *mpp);
#endif /* MAIN_H */

View File

@ -0,0 +1,145 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:47 -0400
Subject: [PATCH] multipathd: unregister PR key when path is restored if
necessary
It is possible that a path was unavailable and either the registered PR
key was removed or the registered PR key was changed and then that new
key was preempted. In both of these situations, this path will still
have a registered key (just not one that matches mpp->reservation_key)
but it should not have one. If the path becomes usable again in this
state, it may allow the multipath device to access storage that it
shouldn't be allowed to access.
To deal with this, track if a multipath device ever had a registered PR
key. If so, and the device no longer has a registered key, explicitly
clear the key when paths get restored.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmultipath/structs.h | 2 ++
multipathd/main.c | 46 ++++++++++++++++++++++++++++++++----------
2 files changed, 37 insertions(+), 11 deletions(-)
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 6d0cd867..90127641 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -491,6 +491,8 @@ struct multipath {
int prflag;
int prhold;
int all_tg_pt;
+ bool ever_registered_pr;
+
struct gen_multipath generic_mp;
bool fpin_must_reload;
};
diff --git a/multipathd/main.c b/multipathd/main.c
index e29ab2b8..484c8ac1 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -2644,7 +2644,8 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
}
if (newstate == PATH_UP || newstate == PATH_GHOST) {
- if (pp->mpp->prflag != PR_UNSET) {
+ if (pp->mpp->prflag != PR_UNSET ||
+ pp->mpp->ever_registered_pr) {
int prflag = pp->mpp->prflag;
/*
* Check Persistent Reservation.
@@ -3964,6 +3965,7 @@ static void check_prhold(struct multipath *mpp, struct path *pp)
void set_pr(struct multipath *mpp)
{
+ mpp->ever_registered_pr = true;
mpp->prflag = PR_SET;
}
@@ -3974,16 +3976,21 @@ void unset_pr(struct multipath *mpp)
mpp->sa_flags = 0;
}
+/*
+ * Returns MPATH_PR_SUCCESS unless if fails to read the PR keys. If
+ * MPATH_PR_SUCCESS is returned, mpp->prflag will be either PR_SET or
+ * PR_UNSET.
+ */
static int update_map_pr(struct multipath *mpp, struct path *pp)
{
struct prin_resp resp;
unsigned int i;
- int ret = MPATH_PR_OTHER, isFound;
+ int ret, isFound;
bool was_set = (mpp->prflag == PR_SET);
/* If pr is explicitly unset, it must be manually set */
if (mpp->prflag == PR_UNSET)
- return MPATH_PR_SKIP;
+ return MPATH_PR_SUCCESS;
if (!get_be64(mpp->reservation_key)) {
/* Nothing to do. Assuming pr mgmt feature is disabled*/
@@ -3991,7 +3998,7 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
condlog(was_set ? 2 : 4,
"%s: reservation_key not set in multipath.conf",
mpp->alias);
- return MPATH_PR_SKIP;
+ return MPATH_PR_SUCCESS;
}
memset(&resp, 0, sizeof(resp));
@@ -4040,6 +4047,7 @@ static void mpath_pr_event_handle(struct path *pp)
struct multipath *mpp = pp->mpp;
int ret;
struct prout_param_descriptor param;
+ bool clear_reg = false;
if (pp->bus != SYSFS_BUS_SCSI) {
unset_pr(mpp);
@@ -4051,20 +4059,36 @@ static void mpath_pr_event_handle(struct path *pp)
check_prhold(mpp, pp);
- if (mpp->prflag != PR_SET)
- return;
+ if (mpp->prflag != PR_SET) {
+ if (!mpp->ever_registered_pr)
+ return;
+ /*
+ * This path may have been unusable and either the
+ * registration was cleared or the registered
+ * key was switched and then that new key was preempted.
+ * In either case, this path should not have a registration
+ * but it might still have one, just with a different
+ * key than mpp->reservation_key is currently set to.
+ * clear it to be sure.
+ */
+ clear_reg = true;
+ }
memset(&param, 0, sizeof(param));
- param.sa_flags = mpp->sa_flags;
- memcpy(param.sa_key, &mpp->reservation_key, 8);
- param.num_transportid = 0;
+ if (!clear_reg) {
+ param.sa_flags = mpp->sa_flags;
+ memcpy(param.sa_key, &mpp->reservation_key, 8);
+ param.num_transportid = 0;
+ }
- condlog(3, "device %s:%s", pp->dev, pp->mpp->wwid);
+ condlog(3, "%s registration for device %s:%s",
+ clear_reg ? "Clearing" : "Setting", pp->dev, pp->mpp->wwid);
ret = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REG_IGN_SA, 0, 0, &param, 0);
if (ret != MPATH_PR_SUCCESS )
{
- condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret);
+ condlog(0, "%s: %s reservation registration failed. Error: %d",
+ clear_reg ? "Clearing" : "Setting", pp->dev, ret);
}
}

View File

@ -0,0 +1,112 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:48 -0400
Subject: [PATCH] libmpathpersist: Fix-up reservation_key checking
The reservation key checking in do_mpath_persistent_reserve_out() was
slightly wrong. It allowed invalid keys for preempting. It now correctly
checks the reservation key for the preempt commands.
It also was a little overly strict in some places. Formerly, it only
allowed registering from any key to the configured key or registering
from the configured key to any key (as long as you use the prkeys file).
Now it also allows unregistering from any key and registering an
unregistered device to any key (as long as you use the prkeys file).
Also, clarify the code by replacing prkey with a bool tracking if you
are registering or unregistering.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 53 +++++++++++++++++++++++------
1 file changed, 42 insertions(+), 11 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 76bdbc63..ded1af38 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -722,9 +722,9 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
struct multipath *mpp;
char *alias;
int ret;
- uint64_t prkey;
+ uint64_t zerokey = 0;
struct config *conf;
- bool preempting_reservation = false;
+ bool unregistering, preempting_reservation = false;
ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
if (ret != MPATH_PR_SUCCESS)
@@ -744,11 +744,12 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
if (rq_servact == MPATH_PROUT_REG_IGN_SA)
set_ignored_key(mpp, paramp->key);
- memcpy(&prkey, paramp->sa_key, 8);
- if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey &&
+ unregistering = (memcmp(&zerokey, paramp->sa_key, 8) == 0);
+ if (mpp->prkey_source == PRKEY_SOURCE_FILE && !unregistering &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
(rq_servact == MPATH_PROUT_REG_SA &&
(!get_be64(mpp->reservation_key) ||
+ memcmp(paramp->key, &zerokey, 8) == 0 ||
memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) {
memcpy(&mpp->reservation_key, paramp->sa_key, 8);
if (update_prkey_flags(alias, get_be64(mpp->reservation_key),
@@ -760,12 +761,42 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
}
}
- if (memcmp(paramp->key, &mpp->reservation_key, 8) &&
- memcmp(paramp->sa_key, &mpp->reservation_key, 8) &&
- (prkey || rq_servact != MPATH_PROUT_REG_IGN_SA)) {
- condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64, alias, get_be64(mpp->reservation_key));
- ret = MPATH_PR_SYNTAX_ERROR;
- goto out1;
+ /*
+ * If you are registering a non-zero key, mpp->reservation_key
+ * must be set and must equal paramp->sa_key.
+ * If you're not registering a key, mpp->reservation_key must be
+ * set, and must equal paramp->key
+ * If you updated the reservation key above, then you cannot fail
+ * these checks, since mpp->reservation_key has already been set
+ * to match paramp->sa_key, and if you are registering a non-zero
+ * key, then it must be set to a non-zero value.
+ */
+ if ((rq_servact == MPATH_PROUT_REG_IGN_SA ||
+ rq_servact == MPATH_PROUT_REG_SA)) {
+ if (!unregistering && !get_be64(mpp->reservation_key)) {
+ condlog(0, "%s: no configured reservation key", alias);
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out1;
+ }
+ if (!unregistering &&
+ memcmp(paramp->sa_key, &mpp->reservation_key, 8)) {
+ condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64,
+ alias, get_be64(mpp->reservation_key));
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out1;
+ }
+ } else {
+ if (!get_be64(mpp->reservation_key)) {
+ condlog(0, "%s: no configured reservation key", alias);
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out1;
+ }
+ if (memcmp(paramp->key, &mpp->reservation_key, 8)) {
+ condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64,
+ alias, get_be64(mpp->reservation_key));
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out1;
+ }
}
switch(rq_servact)
@@ -798,7 +829,7 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
switch (rq_servact) {
case MPATH_PROUT_REG_SA:
case MPATH_PROUT_REG_IGN_SA:
- if (prkey == 0) {
+ if (unregistering) {
update_prflag(alias, 0);
update_prkey(alias, 0);
} else

View File

@ -0,0 +1,139 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:49 -0400
Subject: [PATCH] libmpathpersist: change how reservation conflicts are handled
If registering a key on a path fails with a reservation conflict in
mpath_prout_reg(), libmpathpersist currently tries to roll back the
registration. This code doesn't always make much sense. First, it
updates the configured key, but doesn't fix it if it does a rollback.
Second, it always rolls the key back to 0x0, unregistering paths that
may have been previously registered. These rollback only happen on the
paths where the registration succeeded, meaning that they were in the
expected state when the command was run. The paths where the command
failed, that were in an unexpected state, remain in that state.
The code no longer attempts to rollback registrations that failed
with a reservation conflict. Instead, it checks that at least one
path was in the expected state and was successfully registered. If
so, then it assumes that the registration command was a resonable one
and retries it on the paths that failed with a reservation conflict.
But instead of using MPATH_PROUT_REG_SA, it uses MPATH_PROUT_REG_IGN_SA
so that it will ignore the current key. This will keep it from
failing with a reservation conflict because the path doesn't have the
expected key registered on it. If path reservations failed for reasons
other than a reservation conflict, the command still returns failure.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 71 ++++++++++++++++++-----------
1 file changed, 45 insertions(+), 26 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index ded1af38..e2dc5773 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -356,13 +356,13 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
int i, j, k;
struct pathgroup *pgp = NULL;
struct path *pp = NULL;
- int rollback = 0;
+ bool can_retry = false;
+ bool need_retry = false;
int active_pathcount=0;
int rc;
int count=0;
int status = MPATH_PR_SUCCESS;
int all_tg_pt;
- uint64_t sa_key = 0;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
@@ -451,43 +451,62 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
condlog (0, "%s: Thread[%d] failed to join thread %d", mpp->wwid, i, rc);
}
}
- if (!rollback && (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)){
- rollback = 1;
- sa_key = get_unaligned_be64(&paramp->sa_key[0]);
- status = MPATH_PR_RESERV_CONFLICT ;
- }
- if (!rollback && (status == MPATH_PR_SUCCESS)){
+ /*
+ * We only retry if there is at least one registration that
+ * returned a reservation conflict (which we need to retry)
+ * and at least one registration the return success, so we
+ * know that the command worked on some of the paths. If
+ * the registation fails on all paths, then it wasn't a
+ * valid request, so there's no need to retry.
+ */
+ if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
+ need_retry = true;
+ else if (thread[i].param.status == MPATH_PR_SUCCESS)
+ can_retry = true;
+ else if (status == MPATH_PR_SUCCESS)
status = thread[i].param.status;
- }
}
- if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){
- condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid);
- memcpy(&paramp->key, &paramp->sa_key, 8);
- memset(&paramp->sa_key, 0, 8);
- for( i=0 ; i < count ; i++){
- if(thread[i].param.status == MPATH_PR_SUCCESS) {
- rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn,
- (void *)(&thread[i].param));
- if (rc){
- condlog (0, "%s: failed to create thread for rollback. %d", mpp->wwid, rc);
- thread[i].param.status = MPATH_PR_THREAD_ERROR;
- }
- } else
+ if (need_retry && can_retry && rq_servact == MPATH_PROUT_REG_SA &&
+ status == MPATH_PR_SUCCESS) {
+ condlog(3, "%s: ERROR: initiating pr out retry", mpp->wwid);
+ for (i = 0; i < count; i++) {
+ if (thread[i].param.status != MPATH_PR_RESERV_CONFLICT) {
thread[i].param.status = MPATH_PR_SKIP;
+ continue;
+ }
+ /*
+ * retry using MPATH_PROUT_REG_IGN_SA to avoid
+ * conflicts. We already know that some paths
+ * succeeded using MPATH_PROUT_REG_SA.
+ */
+ thread[i].param.rq_servact = MPATH_PROUT_REG_IGN_SA;
+ rc = pthread_create(&thread[i].id, &attr,
+ mpath_prout_pthread_fn,
+ (void *)(&thread[i].param));
+ if (rc) {
+ condlog(0, "%s: failed to create thread for retry. %d",
+ mpp->wwid, rc);
+ thread[i].param.status = MPATH_PR_THREAD_ERROR;
+ }
}
- for(i=0; i < count ; i++){
+ for (i = 0; i < count; i++) {
if (thread[i].param.status != MPATH_PR_SKIP &&
thread[i].param.status != MPATH_PR_THREAD_ERROR) {
rc = pthread_join(thread[i].id, NULL);
- if (rc){
- condlog (3, "%s: failed to join thread while rolling back %d",
- mpp->wwid, i);
+ if (rc) {
+ condlog(3, "%s: failed to join thread while retrying %d",
+ mpp->wwid, i);
}
+ if (status == MPATH_PR_SUCCESS)
+ status = thread[i].param.status;
}
}
+ need_retry = false;
}
pthread_attr_destroy(&attr);
+ if (need_retry)
+ status = MPATH_PR_RESERV_CONFLICT;
if (status == MPATH_PR_SUCCESS)
preempt_missing_path(mpp, paramp->key, paramp->sa_key, noisy);
return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;

View File

@ -0,0 +1,51 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:50 -0400
Subject: [PATCH] libmpathpersist: Clear prkey in multipathd before
unregistering
When you register or switch keys in libmpathpersist, it updates
mpp->reservation_key in multipathd before doing the registration. This
means that any paths that come online while you are doing the
registration get the new key registered. libmpathpersist didn't do
this when unregistering a key. This could cause the same problem. A
path that got restored while unregistering the device could end up
getting the old key registered on it. Fix this by unsetting the key
before doing the unregister, instead of afterwards.
There is still a race condition associated with updating
mpp->reservation_key before doing the registration (but not on
unregistration). This will be dealt with by a future patch.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index e2dc5773..dd056135 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -764,7 +764,7 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
set_ignored_key(mpp, paramp->key);
unregistering = (memcmp(&zerokey, paramp->sa_key, 8) == 0);
- if (mpp->prkey_source == PRKEY_SOURCE_FILE && !unregistering &&
+ if (mpp->prkey_source == PRKEY_SOURCE_FILE &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
(rq_servact == MPATH_PROUT_REG_SA &&
(!get_be64(mpp->reservation_key) ||
@@ -848,10 +848,9 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
switch (rq_servact) {
case MPATH_PROUT_REG_SA:
case MPATH_PROUT_REG_IGN_SA:
- if (unregistering) {
+ if (unregistering)
update_prflag(alias, 0);
- update_prkey(alias, 0);
- } else
+ else
update_prflag(alias, 1);
break;
case MPATH_PROUT_CLEAR_SA:

View File

@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:51 -0400
Subject: [PATCH] libmpathpersist: only clear the key if we are using the
prkeys file
Otherwise this request will create a useless prkeys file.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index dd056135..a6940d12 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -855,7 +855,8 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
break;
case MPATH_PROUT_CLEAR_SA:
update_prflag(alias, 0);
- update_prkey(alias, 0);
+ if (mpp->prkey_source == PRKEY_SOURCE_FILE)
+ update_prkey(alias, 0);
break;
case MPATH_PROUT_RES_SA:
case MPATH_PROUT_REL_SA:

View File

@ -0,0 +1,51 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:52 -0400
Subject: [PATCH] libmpathpersist: Restore old reservation key on failure
If we updated the key and then failed, restore the old key.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index a6940d12..302bebc2 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -742,8 +742,10 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
char *alias;
int ret;
uint64_t zerokey = 0;
+ struct be64 oldkey = {0};
struct config *conf;
bool unregistering, preempting_reservation = false;
+ bool updated_prkey = false;
ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
if (ret != MPATH_PR_SUCCESS)
@@ -770,6 +772,8 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
(!get_be64(mpp->reservation_key) ||
memcmp(paramp->key, &zerokey, 8) == 0 ||
memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) {
+ updated_prkey = true;
+ memcpy(&oldkey, &mpp->reservation_key, 8);
memcpy(&mpp->reservation_key, paramp->sa_key, 8);
if (update_prkey_flags(alias, get_be64(mpp->reservation_key),
paramp->sa_flags)) {
@@ -842,8 +846,12 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
goto out1;
}
- if (ret != MPATH_PR_SUCCESS)
+ if (ret != MPATH_PR_SUCCESS) {
+ if (updated_prkey)
+ update_prkey_flags(mpp->alias, get_be64(oldkey),
+ mpp->sa_flags);
goto out1;
+ }
switch (rq_servact) {
case MPATH_PROUT_REG_SA:

View File

@ -0,0 +1,151 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:53 -0400
Subject: [PATCH] libmpathpersist: update reservation key before checking paths
There is a race condition when changing reservation keys where a failed
path could come back online after libmpathpersist checks the paths, but
before it updates the reservation key. In this case, the path would come
up and get reregistered with the old key by multipathd, and
libmpathpersist would not update its key, because the path was down
when it checked.
To fix this, check the paths after updating the key, so any path that
comes up after getting checked will use the updated key.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 80 ++++++++++++-----------------
1 file changed, 34 insertions(+), 46 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 302bebc2..d498e69e 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -110,39 +110,18 @@ void *mpath_alloc_prin_response(int prin_sa)
return ptr;
}
-static int get_mpvec(vector curmp, vector pathvec, char *refwwid)
+static int get_path_info(struct multipath *mpp, vector pathvec)
{
- int i;
- struct multipath *mpp;
-
- vector_foreach_slot (curmp, mpp, i){
- /*
- * discard out of scope maps
- */
- if (!mpp->alias) {
- condlog(0, "%s: map with empty alias!", __func__);
- continue;
- }
-
- if (mpp->pg != NULL)
- /* Already seen this one */
- continue;
-
- if (refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE - 1))
- continue;
-
- if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
- update_mpp_paths(mpp, pathvec)) {
- condlog(1, "error parsing map %s", mpp->wwid);
- remove_map(mpp, pathvec, curmp);
- i--;
- } else
- extract_hwe_from_path(mpp);
+ if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
+ update_mpp_paths(mpp, pathvec)) {
+ condlog(0, "error parsing map %s", mpp->wwid);
+ return MPATH_PR_DMMP_ERROR;
}
+ extract_hwe_from_path(mpp);
return MPATH_PR_SUCCESS ;
}
-static int mpath_get_map(vector curmp, vector pathvec, int fd, char **palias,
+static int mpath_get_map(vector curmp, int fd, char **palias,
struct multipath **pmpp)
{
int ret = MPATH_PR_DMMP_ERROR;
@@ -178,12 +157,6 @@ static int mpath_get_map(vector curmp, vector pathvec, int fd, char **palias,
goto out;
}
- /* get info of all paths from the dm device */
- if (get_mpvec(curmp, pathvec, alias)){
- condlog(0, "%s: failed to get device info.", alias);
- goto out;
- }
-
mpp = find_mp_by_alias(curmp, alias);
if (!mpp) {
@@ -210,7 +183,11 @@ int do_mpath_persistent_reserve_in(vector curmp, vector pathvec,
struct multipath *mpp;
int ret;
- ret = mpath_get_map(curmp, pathvec, fd, NULL, &mpp);
+ ret = mpath_get_map(curmp, fd, NULL, &mpp);
+ if (ret != MPATH_PR_SUCCESS)
+ return ret;
+
+ ret = get_path_info(mpp, pathvec);
if (ret != MPATH_PR_SUCCESS)
return ret;
@@ -747,24 +724,14 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
bool unregistering, preempting_reservation = false;
bool updated_prkey = false;
- ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
+ ret = mpath_get_map(curmp, fd, &alias, &mpp);
if (ret != MPATH_PR_SUCCESS)
return ret;
conf = get_multipath_config();
select_reservation_key(conf, mpp);
- select_all_tg_pt(conf, mpp);
- /*
- * If a device preempts itself, it will need to suspend and resume.
- * Set mpp->skip_kpartx to make sure we set the flags to skip kpartx
- * if necessary, when doing this.
- */
- select_skip_kpartx(conf, mpp);
put_multipath_config(conf);
- if (rq_servact == MPATH_PROUT_REG_IGN_SA)
- set_ignored_key(mpp, paramp->key);
-
unregistering = (memcmp(&zerokey, paramp->sa_key, 8) == 0);
if (mpp->prkey_source == PRKEY_SOURCE_FILE &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
@@ -822,6 +789,27 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
}
}
+ ret = get_path_info(mpp, pathvec);
+ if (ret != MPATH_PR_SUCCESS) {
+ if (updated_prkey)
+ update_prkey_flags(alias, get_be64(oldkey),
+ mpp->sa_flags);
+ goto out1;
+ }
+
+ conf = get_multipath_config();
+ select_all_tg_pt(conf, mpp);
+ /*
+ * If a device preempts itself, it will need to suspend and resume.
+ * Set mpp->skip_kpartx to make sure we set the flags to skip kpartx
+ * if necessary, when doing this.
+ */
+ select_skip_kpartx(conf, mpp);
+ put_multipath_config(conf);
+
+ if (rq_servact == MPATH_PROUT_REG_IGN_SA)
+ set_ignored_key(mpp, paramp->key);
+
switch(rq_servact)
{
case MPATH_PROUT_REG_SA:

View File

@ -0,0 +1,79 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:54 -0400
Subject: [PATCH] libmpathpersist: retry on conflicts in mpath_prout_common
mpath_prout_common() just needs to execute a prout command down one
path. If it uses a path that was down when the key was changed and has
since been restored, but multipathd hasn't noticed yet, that path will
still be using the old key. This was causing mpath_prout_common() to
fail with MPATH_PR_RESERV_CONFLICT, even if there were other paths that
would work.
Now, if prout command fails with MPATH_PR_RESERV_CONFLICT,
mpath_prout_common() checks if pp->dmstate is PSTATE_FAILED. If it is,
mpath_prout_common() assumes that multipathd has not yet noticed that
the path is back online and it might still have and old key, so it
doesn't immediately return. If it can't successfully send the command
down another path, it will still return MPATH_PR_RESERV_CONFLICT.
Also, make sure prout_do_scsi_ioctl() always returns a MPATH_PR_*
type error.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 14 +++++++++++++-
libmpathpersist/mpath_pr_ioctl.c | 2 +-
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index d498e69e..762958e8 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -216,6 +216,7 @@ mpath_prout_common(struct multipath *mpp, int rq_servact, int rq_scope,
struct pathgroup *pgp = NULL;
struct path *pp = NULL;
bool found = false;
+ bool conflict = false;
vector_foreach_slot (mpp->pg, pgp, j) {
vector_foreach_slot (pgp->paths, pp, i) {
@@ -232,12 +233,23 @@ mpath_prout_common(struct multipath *mpp, int rq_servact, int rq_scope,
rq_type, paramp, noisy);
if (ret == MPATH_PR_SUCCESS && pptr)
*pptr = pp;
+ /*
+ * If this path is considered down by the kernel,
+ * it may have just come back up, and multipathd
+ * may not have had time to update the key. Allow
+ * reservation conflicts.
+ */
+ if (ret == MPATH_PR_RESERV_CONFLICT &&
+ pp->dmstate == PSTATE_FAILED) {
+ conflict = true;
+ continue;
+ }
if (ret != MPATH_PR_RETRYABLE_ERROR)
return ret;
}
}
if (found)
- return MPATH_PR_OTHER;
+ return conflict ? MPATH_PR_RESERV_CONFLICT : MPATH_PR_OTHER;
condlog(0, "%s: no path available", mpp->wwid);
return MPATH_PR_DMMP_ERROR;
}
diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c
index dfdbbb65..6eaec7cd 100644
--- a/libmpathpersist/mpath_pr_ioctl.c
+++ b/libmpathpersist/mpath_pr_ioctl.c
@@ -103,7 +103,7 @@ retry :
{
condlog(0, "%s: ioctl failed %d", dev, ret);
close(fd);
- return ret;
+ return MPATH_PR_OTHER;
}
condlog(4, "%s: Duration=%u (ms)", dev, io_hdr.duration);

View File

@ -0,0 +1,154 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 23:58:55 -0400
Subject: [PATCH] libmpathpersist: Don't always fail registrations for
retryable errors
When libmpathpersist registers a key, it's possible that a path fails
between when it checks the path's status, and when it tries to do the
registrations on the path. In this case, the registration will fail with
a retryable error. If the registration was allowed to succeed,
multipathd would update the now failed path's key when it came back
online, and everything would work correctly. However it is possible for
a registration to fail with a retryable error on a path that is still
usable.
Libmpathpersist needs to avoid the case where it does not update the
key of a usable path. Otherwise the path might be able to write to
storage it shouldn't be allowed to. Or it could fail writing to storage
that it should be allowed to write to. So if a registration would
succeed except for retryable errors, libmpathpersist now rechecks all
those paths to see if they are still usable. If they are, then it fails
the registration as before. If they are not, then the registration
succeeds.
Also, rename can_retry to had_success, since it is used for checking
more than retries now.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist_int.c | 72 ++++++++++++++++++++++++++---
1 file changed, 65 insertions(+), 7 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 762958e8..de757c09 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -337,6 +337,48 @@ void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
condlog(0, "%s: register: pr preempt command failed.", mpp->wwid);
}
+/*
+ * If libmpathpersist fails at updating the key on a path with a retryable
+ * error, it has probably failed. But there is a chance that the path is
+ * still usable. To make sure a path isn't active without a key, when it
+ * should have one, or with a key, when it shouldn't have one, check if
+ * the path is still usable. If it is, we must fail the registration.
+ */
+static int
+check_failed_paths(struct multipath *mpp, struct threadinfo *thread, int count)
+{
+ int i, j, k;
+ int ret;
+ struct pathgroup *pgp;
+ struct path *pp;
+ struct config *conf;
+
+ for (i = 0; i < count; i++) {
+ if (thread[i].param.status != MPATH_PR_RETRYABLE_ERROR)
+ continue;
+ vector_foreach_slot (mpp->pg, pgp, j) {
+ vector_foreach_slot (pgp->paths, pp, k) {
+ if (strncmp(pp->dev, thread[i].param.dev,
+ FILE_NAME_SIZE) == 0)
+ goto match;
+ }
+ }
+ /* no match. This shouldn't ever happen. */
+ condlog(0, "%s: Error: can't find path %s", mpp->wwid,
+ thread[i].param.dev);
+ continue;
+ match:
+ conf = get_multipath_config();
+ ret = pathinfo(pp, conf, DI_CHECKER);
+ put_multipath_config(conf);
+ /* If pathinfo fails, or if the path is active, return error */
+ if (ret != PATHINFO_OK || pp->state == PATH_UP ||
+ pp->state == PATH_GHOST)
+ return MPATH_PR_OTHER;
+ }
+ return MPATH_PR_SUCCESS;
+}
+
static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type,
struct prout_param_descriptor * paramp, int noisy)
@@ -345,8 +387,9 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
int i, j, k;
struct pathgroup *pgp = NULL;
struct path *pp = NULL;
- bool can_retry = false;
+ bool had_success = false;
bool need_retry = false;
+ bool retryable_error = false;
int active_pathcount=0;
int rc;
int count=0;
@@ -450,16 +493,21 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
*/
if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
need_retry = true;
+ else if (thread[i].param.status == MPATH_PR_RETRYABLE_ERROR)
+ retryable_error = true;
else if (thread[i].param.status == MPATH_PR_SUCCESS)
- can_retry = true;
+ had_success = true;
else if (status == MPATH_PR_SUCCESS)
status = thread[i].param.status;
}
- if (need_retry && can_retry && rq_servact == MPATH_PROUT_REG_SA &&
+ if (need_retry && had_success && rq_servact == MPATH_PROUT_REG_SA &&
status == MPATH_PR_SUCCESS) {
condlog(3, "%s: ERROR: initiating pr out retry", mpp->wwid);
+ retryable_error = false;
for (i = 0; i < count; i++) {
- if (thread[i].param.status != MPATH_PR_RESERV_CONFLICT) {
+ /* retry retryable errors and conflicts */
+ if (thread[i].param.status != MPATH_PR_RESERV_CONFLICT &&
+ thread[i].param.status != MPATH_PR_RETRYABLE_ERROR) {
thread[i].param.status = MPATH_PR_SKIP;
continue;
}
@@ -486,7 +534,10 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
condlog(3, "%s: failed to join thread while retrying %d",
mpp->wwid, i);
}
- if (status == MPATH_PR_SUCCESS)
+ if (thread[i].param.status ==
+ MPATH_PR_RETRYABLE_ERROR)
+ retryable_error = true;
+ else if (status == MPATH_PR_SUCCESS)
status = thread[i].param.status;
}
}
@@ -495,10 +546,17 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
pthread_attr_destroy(&attr);
if (need_retry)
- status = MPATH_PR_RESERV_CONFLICT;
+ return MPATH_PR_RESERV_CONFLICT;
+ if (status != MPATH_PR_SUCCESS)
+ return status;
+ /* If you had retryable errors on all paths, fail the registration */
+ if (!had_success)
+ return MPATH_PR_OTHER;
+ if (retryable_error)
+ status = check_failed_paths(mpp, thread, count);
if (status == MPATH_PR_SUCCESS)
preempt_missing_path(mpp, paramp->key, paramp->sa_key, noisy);
- return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
+ return status;
}
static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,

View File

@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 25 Aug 2025 14:09:29 -0400
Subject: [PATCH] libmpathpersist: Don't try release workaround for invalid
type
When trying to release a reservation, if the user specified the wrong
reservation type, libmpathpersist would try to preempt the reservation,
because the reservation key matched the device key, but it was not
removed. In this case, the preemption would also fail because it also
requires a matching type.
Check if the reservation type matches, to avoid attempting the
workaround in this case.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index de757c09..e5ae0836 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -576,6 +576,7 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
uint16_t udev_flags = (mpp->skip_kpartx) ? MPATH_UDEV_NO_KPARTX_FLAG : 0;
bool did_resume = false;
bool all_threads_failed;
+ unsigned int scope_type;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
@@ -671,6 +672,13 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
return MPATH_PR_SUCCESS;
}
+ scope_type = resp.prin_descriptor.prin_readresv.scope_type;
+ if ((scope_type & MPATH_PR_TYPE_MASK) != rq_type) {
+ condlog(2, "%s: --prout_type %u doesn't match reservation %u",
+ mpp->wwid, rq_type, scope_type & MPATH_PR_TYPE_MASK);
+ return MPATH_PR_RESERV_CONFLICT;
+ }
+
condlog (2, "%s: Path holding reservation is not available.", mpp->wwid);
/*
* Cannot free the reservation because the path that is holding it

View File

@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 4 Sep 2025 16:33:27 -0400
Subject: [PATCH] libmpathpersist: Don't fail RESERVE commands unnecessarily
If you issue a RESERVE to a regular SCSI device that already holds the
reservation, it succeeds (and does nothing). If you issue a RESERVE to a
multipath device that already holds the reservation, it can fail with
a reservation conflict error if you issue the RESERVE to a path that isn't
holding the reservation. Instead, it should try all paths and
succeed if the reservation command succeeds on any of them.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index e5ae0836..45ba68c0 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -238,9 +238,20 @@ mpath_prout_common(struct multipath *mpp, int rq_servact, int rq_scope,
* it may have just come back up, and multipathd
* may not have had time to update the key. Allow
* reservation conflicts.
+ *
+ * If you issue a RESERVE to a regular scsi device
+ * that already holds the reservation, it succeeds
+ * (and does nothing). A multipath device that
+ * holds the reservation should not return a
+ * reservation conflict on a RESERVE command, just
+ * because it issued the RESERVE to a path that
+ * isn't holding the reservation. It should instead
+ * keep trying to see if it succeeds on another
+ * path.
*/
if (ret == MPATH_PR_RESERV_CONFLICT &&
- pp->dmstate == PSTATE_FAILED) {
+ (pp->dmstate == PSTATE_FAILED ||
+ rq_servact == MPATH_PROUT_RES_SA)) {
conflict = true;
continue;
}

View File

@ -0,0 +1,193 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 5 Sep 2025 14:04:58 -0400
Subject: [PATCH] libmpathpersist: reregister keys when self preempting
When a SCSI device preempts its own reservation key, it will remove the
registered keys from all other devices with that reservation key, but
retain its registered key (and possibly acquire the reservation). If a
multipath device preempts its own reservation key, it will also remove
the registered keys from all its paths except the one issuing the
reservation. This means that IO to the device can fail if it goes to one
of these unregistered paths.
To avoid this, whenever a multipath device preempts itself, it must
first suspend, then do the preemption and reregister the removed keys,
and finally resume the device. This is already what libmpathpersist does
if a release fails because the path holding the reservation is currently
unavailable, with the addition of releasing the reservation after
preempting it. This commit refactors that code into a separate function,
makes the release optional, and calls the new function, preempt_self(),
whenever a device preempts itself.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 133 ++++++++++++++--------------
1 file changed, 68 insertions(+), 65 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 45ba68c0..da8a0d04 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -570,6 +570,65 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
return status;
}
+/*
+ * Called to make a multipath device preempt its own reservation (and
+ * optionally release the reservation). Doing this causes the reservation
+ * keys to be removed from all the device paths except that path used to issue
+ * the preempt, so they need to be restored. To avoid the chance that IO
+ * goes to these paths when they don't have a registered key, the device
+ * is suspended before issuing the preemption, and the keys are reregistered
+ * before resuming it.
+ */
+static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
+ unsigned int rq_type, int noisy, bool do_release)
+{
+ int status, rel_status = MPATH_PR_SUCCESS;
+ struct path *pp = NULL;
+ struct prout_param_descriptor paramp = {.sa_flags = 0};
+ uint16_t udev_flags = (mpp->skip_kpartx) ? MPATH_UDEV_NO_KPARTX_FLAG : 0;
+
+ if (!dm_simplecmd_noflush(DM_DEVICE_SUSPEND, mpp->alias, 0)) {
+ condlog(0, "%s: self preempt failed to suspend device.", mpp->wwid);
+ return MPATH_PR_OTHER;
+ }
+
+ memcpy(paramp.key, &mpp->reservation_key, 8);
+ memcpy(paramp.sa_key, &mpp->reservation_key, 8);
+ status = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
+ &paramp, noisy, &pp);
+ if (status != MPATH_PR_SUCCESS) {
+ condlog(0, "%s: self preempt command failed.", mpp->wwid);
+ goto fail_resume;
+ }
+
+ if (do_release) {
+ memset(&paramp, 0, sizeof(paramp));
+ memcpy(paramp.key, &mpp->reservation_key, 8);
+ rel_status = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REL_SA,
+ rq_scope, rq_type, &paramp, noisy);
+ if (rel_status != MPATH_PR_SUCCESS)
+ condlog(0, "%s: release on alternate path failed.",
+ mpp->wwid);
+ }
+
+ memset(&paramp, 0, sizeof(paramp));
+ memcpy(paramp.sa_key, &mpp->reservation_key, 8);
+ status = mpath_prout_reg(mpp, MPATH_PROUT_REG_IGN_SA, rq_scope,
+ rq_type, &paramp, noisy);
+ if (status != MPATH_PR_SUCCESS)
+ condlog(0, "%s: self preempt failed to reregister paths.",
+ mpp->wwid);
+
+fail_resume:
+ if (!dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, udev_flags)) {
+ condlog(0, "%s: self preempt failed to resume device.", mpp->wwid);
+ if (status == MPATH_PR_SUCCESS)
+ status = MPATH_PR_OTHER;
+ }
+ /* return the first error we encountered */
+ return (rel_status != MPATH_PR_SUCCESS) ? rel_status : status;
+}
+
static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type,
struct prout_param_descriptor * paramp, int noisy)
@@ -584,8 +643,6 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
int count = 0;
int status = MPATH_PR_SUCCESS;
struct prin_resp resp = {{{.prgeneration = 0}}};
- uint16_t udev_flags = (mpp->skip_kpartx) ? MPATH_UDEV_NO_KPARTX_FLAG : 0;
- bool did_resume = false;
bool all_threads_failed;
unsigned int scope_type;
@@ -700,70 +757,10 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
* preempting one. Since the device is suspended, no IO can
* go to these unregistered paths and fail).
* 3. Releasing the reservation on the path that now holds it.
- * 4. Resuming the device (since it no longer matters that most of
- * that paths no longer have a registered key)
- * 5. Reregistering keys on all the paths
+ * 4. Reregistering keys on all the paths
+ * 5. Resuming the device
*/
-
- if (!dm_simplecmd_noflush(DM_DEVICE_SUSPEND, mpp->alias, 0)) {
- condlog(0, "%s: release: failed to suspend dm device.", mpp->wwid);
- return MPATH_PR_OTHER;
- }
-
- memset(paramp, 0, sizeof(*paramp));
- memcpy(paramp->key, &mpp->reservation_key, 8);
- memcpy(paramp->sa_key, &mpp->reservation_key, 8);
- status = mpath_prout_common(mpp, MPATH_PROUT_PREE_SA, rq_scope,
- rq_type, paramp, noisy, &pp);
- if (status != MPATH_PR_SUCCESS) {
- condlog(0, "%s: release: pr preempt command failed.", mpp->wwid);
- goto fail_resume;
- }
-
- memset(paramp, 0, sizeof(*paramp));
- memcpy(paramp->key, &mpp->reservation_key, 8);
- status = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REL_SA, rq_scope,
- rq_type, paramp, noisy);
- if (status != MPATH_PR_SUCCESS) {
- condlog(0, "%s: release on alternate path failed.", mpp->wwid);
- goto out_reregister;
- }
-
- if (!dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, udev_flags)) {
- condlog(0, "%s release: failed to resume dm device.", mpp->wwid);
- /*
- * leave status set to MPATH_PR_SUCCESS, we will have another
- * chance to resume the device.
- */
- goto out_reregister;
- }
- did_resume = true;
-
-out_reregister:
- memset(paramp, 0, sizeof(*paramp));
- memcpy(paramp->sa_key, &mpp->reservation_key, 8);
- rc = mpath_prout_reg(mpp, MPATH_PROUT_REG_IGN_SA, rq_scope, rq_type,
- paramp, noisy);
- if (rc != MPATH_PR_SUCCESS)
- condlog(0, "%s: release: failed to reregister paths.", mpp->wwid);
-
- /*
- * If we failed releasing the reservation or resuming earlier
- * try resuming now. Otherwise, return with the reregistering status
- * This means we will report failure, even though the resevation
- * has been released, since the keys were not reregistered.
- */
- if (did_resume)
- return rc;
- else if (status == MPATH_PR_SUCCESS)
- status = rc;
-fail_resume:
- if (!dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, udev_flags)) {
- condlog(0, "%s: release: failed to resume dm device.", mpp->wwid);
- if (status == MPATH_PR_SUCCESS)
- status = MPATH_PR_OTHER;
- }
- return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
+ return preempt_self(mpp, MPATH_PROUT_PREE_SA, rq_scope, rq_type, noisy, true);
}
static int reservation_key_matches(struct multipath *mpp, uint8_t *key, int noisy)
@@ -909,6 +906,12 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
case MPATH_PROUT_PREE_AB_SA:
if (reservation_key_matches(mpp, paramp->sa_key, noisy) == YNU_YES)
preempting_reservation = true;
+ /* if we are preempting ourself */
+ if (memcmp(paramp->sa_key, paramp->key, 8) == 0) {
+ ret = preempt_self(mpp, rq_servact, rq_scope, rq_type,
+ noisy, false);
+ break;
+ }
/* fallthrough */
case MPATH_PROUT_RES_SA:
case MPATH_PROUT_CLEAR_SA:

View File

@ -0,0 +1,108 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 9 Sep 2025 18:55:49 -0400
Subject: [PATCH] libmpathpersist: handle updating key race condition
If a multipath device's registered key is changed, mpathpersist needs
to update the key in multipathd before it registers the new key on the
paths. This means that there is a time when multipathd thinks the paths
should be using the new key, but none of them are. If a path is
restored after mpathpersist checks which paths are usable to set the
key on, but before it sets the key on any path, multipathd will see
that the new key is not registered on any paths, and think that the
key has been preempted or cleared. This will leave the path without
a key, and possibly make multipathd think the device does not hold
the reservation, even if it does.
To avoid this, multipathd will now remember the old key when registering
a new one. Once the registration is finished, and (un)setprstatus is
called, multipathd will forget the old key. Until then, multipathd will
check for either key when it looks to see if there is an existing key.
If the registration fails and the key get reverted, multipathd will
also forget the old key.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/prkey.c | 16 ++++++++++++++--
libmultipath/structs.h | 1 +
multipathd/main.c | 12 +++++++++++-
3 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/libmultipath/prkey.c b/libmultipath/prkey.c
index c66d293b..4fbde5ad 100644
--- a/libmultipath/prkey.c
+++ b/libmultipath/prkey.c
@@ -222,10 +222,22 @@ int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey,
}
else
ret = do_prkey(fd, mpp->wwid, NULL, PRKEY_WRITE);
- if (ret == 0)
+ if (ret == 0) {
+ /*
+ * If you are reverting back to the old key, because you
+ * did not successfully set a new key, don't remember the
+ * key you never successfully set.
+ */
+ if (get_be64(mpp->old_pr_key) == prkey)
+ memset(&mpp->old_pr_key, 0, 8);
+ else
+ memcpy(&mpp->old_pr_key, &mpp->reservation_key, 8);
select_reservation_key(conf, mpp);
- if (get_be64(mpp->reservation_key) != prkey)
+ }
+ if (get_be64(mpp->reservation_key) != prkey) {
+ memset(&mpp->old_pr_key, 0, 8);
ret = 1;
+ }
out_file:
close(fd);
out:
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 90127641..2ff195f3 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -487,6 +487,7 @@ struct multipath {
/* persistent management data*/
int prkey_source;
struct be64 reservation_key;
+ struct be64 old_pr_key;
uint8_t sa_flags;
int prflag;
int prhold;
diff --git a/multipathd/main.c b/multipathd/main.c
index 484c8ac1..3b53d140 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3967,6 +3967,7 @@ void set_pr(struct multipath *mpp)
{
mpp->ever_registered_pr = true;
mpp->prflag = PR_SET;
+ memset(&mpp->old_pr_key, 0, 8);
}
void unset_pr(struct multipath *mpp)
@@ -3974,6 +3975,7 @@ void unset_pr(struct multipath *mpp)
mpp->prflag = PR_UNSET;
mpp->prhold = PR_UNSET;
mpp->sa_flags = 0;
+ memset(&mpp->old_pr_key, 0, 8);
}
/*
@@ -4026,7 +4028,15 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
dumpHex((char *)keyp, 8, 1);
}
- if (!memcmp(&mpp->reservation_key, keyp, 8))
+ /*
+ * If you are in the middle of updating a key (old_pr_key
+ * is set) check for either the new key or the old key,
+ * since you might be checking before any paths have
+ * updated their keys.
+ */
+ if (!memcmp(&mpp->reservation_key, keyp, 8) ||
+ (get_be64(mpp->old_pr_key) &&
+ !memcmp(&mpp->old_pr_key, keyp, 8)))
isFound = 1;
}

View File

@ -0,0 +1,82 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 10 Sep 2025 17:52:12 -0400
Subject: [PATCH] libmpathpersist: handle preempting all registrants
reservations
All Registrants reservations (types 7 and 8) are held by key 0x0. When
preempting one, all registrations are cleared, except for the one on the
path that issued the PREEMPT. libmpathpersist needs to handle this just
like the other cases of self-preemption, so that all the paths of the
preempting multipath device have their registrations restored while the
device is suspended.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 31 +++++++++++++++++++++++++----
1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index da8a0d04..87d7cb6b 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -579,8 +579,9 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
* is suspended before issuing the preemption, and the keys are reregistered
* before resuming it.
*/
-static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
- unsigned int rq_type, int noisy, bool do_release)
+static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
+ int rq_servact, int rq_scope, unsigned int rq_type,
+ int noisy, bool do_release)
{
int status, rel_status = MPATH_PR_SUCCESS;
struct path *pp = NULL;
@@ -593,7 +594,7 @@ static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
}
memcpy(paramp.key, &mpp->reservation_key, 8);
- memcpy(paramp.sa_key, &mpp->reservation_key, 8);
+ memcpy(paramp.sa_key, &sa_key, 8);
status = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
&paramp, noisy, &pp);
if (status != MPATH_PR_SUCCESS) {
@@ -629,6 +630,21 @@ fail_resume:
return (rel_status != MPATH_PR_SUCCESS) ? rel_status : status;
}
+static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
+ unsigned int rq_type, int noisy, bool do_release)
+{
+ return do_preempt_self(mpp, mpp->reservation_key, rq_servact, rq_scope,
+ rq_type, noisy, do_release);
+}
+
+static int preempt_all(struct multipath *mpp, int rq_servact, int rq_scope,
+ unsigned int rq_type, int noisy)
+{
+ struct be64 zerokey = {0};
+ return do_preempt_self(mpp, zerokey, rq_servact, rq_scope, rq_type,
+ noisy, false);
+}
+
static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type,
struct prout_param_descriptor * paramp, int noisy)
@@ -904,8 +920,15 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
break;
case MPATH_PROUT_PREE_SA:
case MPATH_PROUT_PREE_AB_SA:
- if (reservation_key_matches(mpp, paramp->sa_key, noisy) == YNU_YES)
+ if (reservation_key_matches(mpp, paramp->sa_key, noisy) == YNU_YES) {
preempting_reservation = true;
+ if (memcmp(paramp->sa_key, &zerokey, 8) == 0) {
+ /* all registrants case */
+ ret = preempt_all(mpp, rq_servact, rq_scope,
+ rq_type, noisy);
+ break;
+ }
+ }
/* if we are preempting ourself */
if (memcmp(paramp->sa_key, paramp->key, 8) == 0) {
ret = preempt_self(mpp, rq_servact, rq_scope, rq_type,

View File

@ -0,0 +1,73 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 18 Sep 2025 18:12:07 -0400
Subject: [PATCH] libmpathpersist: Fix REGISTER AND IGNORE while holding a
reservation
If a device that is holding a reservation changes its registered key,
but the path holding the reservation is unavailable, libmpathpersist
must preempt the old key to update the reservation. If the key is
changed using REGISTER AND IGNORE, set_ignored_key() determines the old
key to preempt. Unfortunately, commit 165427dda broke it, by comparing
the wrong key against the actual reservation key. Then commit cf0eea85
broke it more, by using mpp->reservation_key after it had been updated,
so it was no longer the old key. Fix this by correctly comparing the old
key against the actual reservation key.
Fixes: 165427dda ("libmpathpersist: Add safety check for preempting on key change")
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 87d7cb6b..e3514137 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -801,16 +801,17 @@ static int reservation_key_matches(struct multipath *mpp, uint8_t *key, int nois
* currently registered key for use in preempt_missing_path(), but only if
* the key is holding the reservation.
*/
-static void set_ignored_key(struct multipath *mpp, uint8_t *key)
+static void set_ignored_key(struct multipath *mpp, uint8_t *curr_key,
+ uint8_t *key)
{
memset(key, 0, 8);
- if (!get_be64(mpp->reservation_key))
+ if (memcmp(curr_key, key, 8) == 0)
return;
if (get_prhold(mpp->alias) == PR_UNSET)
return;
- if (reservation_key_matches(mpp, key, 0) == YNU_NO)
+ if (reservation_key_matches(mpp, curr_key, 0) == YNU_NO)
return;
- memcpy(key, &mpp->reservation_key, 8);
+ memcpy(key, curr_key, 8);
}
int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
@@ -834,6 +835,7 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
select_reservation_key(conf, mpp);
put_multipath_config(conf);
+ memcpy(&oldkey, &mpp->reservation_key, 8);
unregistering = (memcmp(&zerokey, paramp->sa_key, 8) == 0);
if (mpp->prkey_source == PRKEY_SOURCE_FILE &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
@@ -842,7 +844,6 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
memcmp(paramp->key, &zerokey, 8) == 0 ||
memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) {
updated_prkey = true;
- memcpy(&oldkey, &mpp->reservation_key, 8);
memcpy(&mpp->reservation_key, paramp->sa_key, 8);
if (update_prkey_flags(alias, get_be64(mpp->reservation_key),
paramp->sa_flags)) {
@@ -910,7 +911,7 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
put_multipath_config(conf);
if (rq_servact == MPATH_PROUT_REG_IGN_SA)
- set_ignored_key(mpp, paramp->key);
+ set_ignored_key(mpp, (uint8_t *)&oldkey, paramp->key);
switch(rq_servact)
{

View File

@ -0,0 +1,159 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 18 Sep 2025 19:29:08 -0400
Subject: [PATCH] libmpathpersist: Handle RESERVE with reservation held by
failed path
Issuing a RESERVE on a device that already holds the reservation should
succeed, as long as the type is the same. But if the path that holds the
reservation is unavailable, mpathpersist fails, since it gets a
reservation conflict on all available paths. To deal with this, if the
multipath device has failed paths, and the key holding the reservation
matches the multipath device's key, and multipathd says that it is
holding the reservation, assume the reservation is held by a failed path
and claim the RESERVE succeeded, even though none of the actual scsi
commands did
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 47 +++++++++++++++++++++++------
1 file changed, 37 insertions(+), 10 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index e3514137..0cb08f88 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -210,7 +210,7 @@ static void *mpath_prout_pthread_fn(void *p)
static int
mpath_prout_common(struct multipath *mpp, int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor *paramp,
- int noisy, struct path **pptr)
+ int noisy, struct path **pptr, bool *failed_paths)
{
int i, j, ret;
struct pathgroup *pgp = NULL;
@@ -223,6 +223,8 @@ mpath_prout_common(struct multipath *mpp, int rq_servact, int rq_scope,
if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))) {
condlog(1, "%s: %s path not up. Skip",
mpp->wwid, pp->dev);
+ if (failed_paths)
+ *failed_paths = true;
continue;
}
@@ -257,6 +259,8 @@ mpath_prout_common(struct multipath *mpp, int rq_servact, int rq_scope,
}
if (ret != MPATH_PR_RETRYABLE_ERROR)
return ret;
+ if (failed_paths)
+ *failed_paths = true;
}
}
if (found)
@@ -343,7 +347,7 @@ void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
memcpy(paramp.key, sa_key, 8);
memcpy(paramp.sa_key, key, 8);
status = mpath_prout_common(mpp, MPATH_PROUT_PREE_SA, rq_scope,
- rq_type, &paramp, noisy, NULL);
+ rq_type, &paramp, noisy, NULL, NULL);
if (status != MPATH_PR_SUCCESS)
condlog(0, "%s: register: pr preempt command failed.", mpp->wwid);
}
@@ -596,7 +600,7 @@ static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
memcpy(paramp.key, &mpp->reservation_key, 8);
memcpy(paramp.sa_key, &sa_key, 8);
status = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
- &paramp, noisy, &pp);
+ &paramp, noisy, &pp, NULL);
if (status != MPATH_PR_SUCCESS) {
condlog(0, "%s: self preempt command failed.", mpp->wwid);
goto fail_resume;
@@ -779,20 +783,25 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
return preempt_self(mpp, MPATH_PROUT_PREE_SA, rq_scope, rq_type, noisy, true);
}
-static int reservation_key_matches(struct multipath *mpp, uint8_t *key, int noisy)
+static int reservation_key_matches(struct multipath *mpp, uint8_t *key,
+ unsigned int *type)
{
struct prin_resp resp = {{{.prgeneration = 0}}};
int status;
- status = mpath_prin_activepath(mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
+ status = mpath_prin_activepath(mpp, MPATH_PRIN_RRES_SA, &resp, 0);
if (status != MPATH_PR_SUCCESS) {
condlog(0, "%s: pr in read reservation command failed.", mpp->wwid);
return YNU_UNDEF;
}
if (!resp.prin_descriptor.prin_readresv.additional_length)
return YNU_NO;
- if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) == 0)
+ if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) == 0) {
+ if (type)
+ *type = resp.prin_descriptor.prin_readresv.scope_type &
+ MPATH_PR_TYPE_MASK;
return YNU_YES;
+ }
return YNU_NO;
}
@@ -809,11 +818,20 @@ static void set_ignored_key(struct multipath *mpp, uint8_t *curr_key,
return;
if (get_prhold(mpp->alias) == PR_UNSET)
return;
- if (reservation_key_matches(mpp, curr_key, 0) == YNU_NO)
+ if (reservation_key_matches(mpp, curr_key, NULL) == YNU_NO)
return;
memcpy(key, curr_key, 8);
}
+static bool check_holding_reservation(struct multipath *mpp, unsigned int *type)
+{
+ if (get_be64(mpp->reservation_key) &&
+ get_prhold(mpp->alias) == PR_SET &&
+ reservation_key_matches(mpp, (uint8_t *)&mpp->reservation_key, type) == YNU_YES)
+ return true;
+ return false;
+}
+
int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
int rq_servact, int rq_scope, unsigned int rq_type,
struct prout_param_descriptor *paramp, int noisy)
@@ -826,6 +844,7 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
struct config *conf;
bool unregistering, preempting_reservation = false;
bool updated_prkey = false;
+ bool failed_paths = false;
ret = mpath_get_map(curmp, fd, &alias, &mpp);
if (ret != MPATH_PR_SUCCESS)
@@ -921,7 +940,7 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
break;
case MPATH_PROUT_PREE_SA:
case MPATH_PROUT_PREE_AB_SA:
- if (reservation_key_matches(mpp, paramp->sa_key, noisy) == YNU_YES) {
+ if (reservation_key_matches(mpp, paramp->sa_key, NULL) == YNU_YES) {
preempting_reservation = true;
if (memcmp(paramp->sa_key, &zerokey, 8) == 0) {
/* all registrants case */
@@ -938,10 +957,18 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
}
/* fallthrough */
case MPATH_PROUT_RES_SA:
- case MPATH_PROUT_CLEAR_SA:
+ case MPATH_PROUT_CLEAR_SA: {
+ unsigned int res_type;
ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
- paramp, noisy, NULL);
+ paramp, noisy, NULL, &failed_paths);
+ if (rq_servact == MPATH_PROUT_RES_SA &&
+ ret != MPATH_PR_SUCCESS && failed_paths &&
+ check_holding_reservation(mpp, &res_type) &&
+ res_type == rq_type)
+ /* The reserve failed, but multipathd says we hold it */
+ ret = MPATH_PR_SUCCESS;
break;
+ }
case MPATH_PROUT_REL_SA:
ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
break;

View File

@ -0,0 +1,149 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 25 Sep 2025 16:48:49 -0400
Subject: [PATCH] libmpathpersist: use check_holding_reservation in
mpath_prout_rel
Instead of open-coding mostly the same work, just make mpath_prout_rel()
call check_holding_reservation().
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 86 ++++++++++++-----------------
1 file changed, 35 insertions(+), 51 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 0cb08f88..0eb7041e 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -649,12 +649,42 @@ static int preempt_all(struct multipath *mpp, int rq_servact, int rq_scope,
noisy, false);
}
+static int reservation_key_matches(struct multipath *mpp, uint8_t *key,
+ unsigned int *type)
+{
+ struct prin_resp resp = {{{.prgeneration = 0}}};
+ int status;
+
+ status = mpath_prin_activepath(mpp, MPATH_PRIN_RRES_SA, &resp, 0);
+ if (status != MPATH_PR_SUCCESS) {
+ condlog(0, "%s: pr in read reservation command failed.", mpp->wwid);
+ return YNU_UNDEF;
+ }
+ if (!resp.prin_descriptor.prin_readresv.additional_length)
+ return YNU_NO;
+ if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) == 0) {
+ if (type)
+ *type = resp.prin_descriptor.prin_readresv.scope_type &
+ MPATH_PR_TYPE_MASK;
+ return YNU_YES;
+ }
+ return YNU_NO;
+}
+
+static bool check_holding_reservation(struct multipath *mpp, unsigned int *type)
+{
+ if (get_be64(mpp->reservation_key) &&
+ get_prhold(mpp->alias) == PR_SET &&
+ reservation_key_matches(mpp, (uint8_t *)&mpp->reservation_key, type) == YNU_YES)
+ return true;
+ return false;
+}
+
static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type,
struct prout_param_descriptor * paramp, int noisy)
{
int i, j;
- int num = 0;
struct pathgroup *pgp = NULL;
struct path *pp = NULL;
int active_pathcount = 0;
@@ -662,9 +692,8 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
int rc;
int count = 0;
int status = MPATH_PR_SUCCESS;
- struct prin_resp resp = {{{.prgeneration = 0}}};
bool all_threads_failed;
- unsigned int scope_type;
+ unsigned int res_type;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
@@ -743,27 +772,13 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
return status;
}
- status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
- if (status != MPATH_PR_SUCCESS){
- condlog (0, "%s: pr in read reservation command failed.", mpp->wwid);
- return MPATH_PR_OTHER;
- }
-
- num = resp.prin_descriptor.prin_readresv.additional_length / 8;
- if (num == 0){
- condlog (2, "%s: Path holding reservation is released.", mpp->wwid);
- return MPATH_PR_SUCCESS;
- }
- if (!get_be64(mpp->reservation_key) ||
- memcmp(&mpp->reservation_key, resp.prin_descriptor.prin_readresv.key, 8)) {
+ if (!check_holding_reservation(mpp, &res_type)) {
condlog(2, "%s: Releasing key not holding reservation.", mpp->wwid);
return MPATH_PR_SUCCESS;
}
-
- scope_type = resp.prin_descriptor.prin_readresv.scope_type;
- if ((scope_type & MPATH_PR_TYPE_MASK) != rq_type) {
+ if (res_type != rq_type) {
condlog(2, "%s: --prout_type %u doesn't match reservation %u",
- mpp->wwid, rq_type, scope_type & MPATH_PR_TYPE_MASK);
+ mpp->wwid, rq_type, res_type);
return MPATH_PR_RESERV_CONFLICT;
}
@@ -783,28 +798,6 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
return preempt_self(mpp, MPATH_PROUT_PREE_SA, rq_scope, rq_type, noisy, true);
}
-static int reservation_key_matches(struct multipath *mpp, uint8_t *key,
- unsigned int *type)
-{
- struct prin_resp resp = {{{.prgeneration = 0}}};
- int status;
-
- status = mpath_prin_activepath(mpp, MPATH_PRIN_RRES_SA, &resp, 0);
- if (status != MPATH_PR_SUCCESS) {
- condlog(0, "%s: pr in read reservation command failed.", mpp->wwid);
- return YNU_UNDEF;
- }
- if (!resp.prin_descriptor.prin_readresv.additional_length)
- return YNU_NO;
- if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) == 0) {
- if (type)
- *type = resp.prin_descriptor.prin_readresv.scope_type &
- MPATH_PR_TYPE_MASK;
- return YNU_YES;
- }
- return YNU_NO;
-}
-
/*
* for MPATH_PROUT_REG_IGN_SA, we use the ignored paramp->key to store the
* currently registered key for use in preempt_missing_path(), but only if
@@ -823,15 +816,6 @@ static void set_ignored_key(struct multipath *mpp, uint8_t *curr_key,
memcpy(key, curr_key, 8);
}
-static bool check_holding_reservation(struct multipath *mpp, unsigned int *type)
-{
- if (get_be64(mpp->reservation_key) &&
- get_prhold(mpp->alias) == PR_SET &&
- reservation_key_matches(mpp, (uint8_t *)&mpp->reservation_key, type) == YNU_YES)
- return true;
- return false;
-}
-
int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
int rq_servact, int rq_scope, unsigned int rq_type,
struct prout_param_descriptor *paramp, int noisy)

View File

@ -0,0 +1,262 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 19 Sep 2025 18:17:20 -0400
Subject: [PATCH] libmpathpersist: Fix unregistering while holding the
reservation
There were two problems with how libmpathpersist handled unregistering
a key while holding the reseravation (which should also release the
reservation).
1. If the path holding the reservation is not unregistered first, there
will be unregistered paths, while a reservation is still held, which
would cause IO to those paths to fail, when it shouldn't.
2. If the path that holds the reservation is down, libmpathpersist was
not clearing the reservation, since the there were no registered keys
it could use for the PREEMPT command workaround
To fix these, libmpathpersist now releases the reservation first when
trying to unregister a key that is holding the reservation.
mpath_prout_rel() has a new option so that if it needs to self preempt
to clear the reservation, it won't re-register the paths when called
as part of unregistering a key. Also, instead of checking if the device
is currently holding a reservation using mpp->reservation_key in
check_holding_reservation() (which will already be set to 0 when called
as part of unregistering a key), pass in the key to check.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.h | 3 +
libmpathpersist/mpath_persist_int.c | 108 ++++++++++++++++++++--------
2 files changed, 80 insertions(+), 31 deletions(-)
diff --git a/libmpathpersist/mpath_persist.h b/libmpathpersist/mpath_persist.h
index b83fc3bd..22c1d4ac 100644
--- a/libmpathpersist/mpath_persist.h
+++ b/libmpathpersist/mpath_persist.h
@@ -66,6 +66,9 @@ extern "C" {
#define MPATH_PR_RETRYABLE_ERROR 16 /* error that might be succeed
down another path. Internal
only. */
+#define MPATH_PR_SUCCESS_UNREGISTER 17 /* Success, and additionally, all
+ paths were unregistered.
+ Internal only. */
/* PR MASK */
#define MPATH_F_APTPL_MASK 0x01 /* APTPL MASK*/
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 0eb7041e..00334a1a 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -574,18 +574,23 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
return status;
}
+enum preempt_work {
+ PREE_WORK_NONE,
+ PREE_WORK_REL,
+ PREE_WORK_REL_UNREG,
+};
/*
* Called to make a multipath device preempt its own reservation (and
- * optionally release the reservation). Doing this causes the reservation
- * keys to be removed from all the device paths except that path used to issue
- * the preempt, so they need to be restored. To avoid the chance that IO
- * goes to these paths when they don't have a registered key, the device
- * is suspended before issuing the preemption, and the keys are reregistered
- * before resuming it.
+ * optional extra work). Doing this causes the reservation keys to be removed
+ * from all the device paths except that path used to issue the preempt, so
+ * they may need to be restored. To avoid the chance that IO goes to these
+ * paths when they don't have a registered key and a reservation exists, the
+ * device is suspended before issuing the preemption, and the keys are
+ * reregistered (or the reservation is released) before resuming it.
*/
static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
int rq_servact, int rq_scope, unsigned int rq_type,
- int noisy, bool do_release)
+ int noisy, enum preempt_work work)
{
int status, rel_status = MPATH_PR_SUCCESS;
struct path *pp = NULL;
@@ -606,7 +611,7 @@ static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
goto fail_resume;
}
- if (do_release) {
+ if (work != PREE_WORK_NONE) {
memset(&paramp, 0, sizeof(paramp));
memcpy(paramp.key, &mpp->reservation_key, 8);
rel_status = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REL_SA,
@@ -614,15 +619,26 @@ static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
if (rel_status != MPATH_PR_SUCCESS)
condlog(0, "%s: release on alternate path failed.",
mpp->wwid);
+ else if (work == PREE_WORK_REL_UNREG) {
+ /* unregister the last path */
+ rel_status = prout_do_scsi_ioctl(pp->dev,
+ MPATH_PROUT_REG_IGN_SA,
+ rq_scope, rq_type,
+ &paramp, noisy);
+ if (rel_status != MPATH_PR_SUCCESS)
+ condlog(0, "%s: final self preempt unregister failed,",
+ mpp->wwid);
+ }
+ }
+ if (work != PREE_WORK_REL_UNREG) {
+ memset(&paramp, 0, sizeof(paramp));
+ memcpy(paramp.sa_key, &mpp->reservation_key, 8);
+ status = mpath_prout_reg(mpp, MPATH_PROUT_REG_IGN_SA, rq_scope,
+ rq_type, &paramp, noisy);
+ if (status != MPATH_PR_SUCCESS)
+ condlog(0, "%s: self preempt failed to reregister paths.",
+ mpp->wwid);
}
-
- memset(&paramp, 0, sizeof(paramp));
- memcpy(paramp.sa_key, &mpp->reservation_key, 8);
- status = mpath_prout_reg(mpp, MPATH_PROUT_REG_IGN_SA, rq_scope,
- rq_type, &paramp, noisy);
- if (status != MPATH_PR_SUCCESS)
- condlog(0, "%s: self preempt failed to reregister paths.",
- mpp->wwid);
fail_resume:
if (!dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, udev_flags)) {
@@ -635,10 +651,10 @@ fail_resume:
}
static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
- unsigned int rq_type, int noisy, bool do_release)
+ unsigned int rq_type, int noisy, enum preempt_work work)
{
return do_preempt_self(mpp, mpp->reservation_key, rq_servact, rq_scope,
- rq_type, noisy, do_release);
+ rq_type, noisy, work);
}
static int preempt_all(struct multipath *mpp, int rq_servact, int rq_scope,
@@ -646,7 +662,7 @@ static int preempt_all(struct multipath *mpp, int rq_servact, int rq_scope,
{
struct be64 zerokey = {0};
return do_preempt_self(mpp, zerokey, rq_servact, rq_scope, rq_type,
- noisy, false);
+ noisy, PREE_WORK_NONE);
}
static int reservation_key_matches(struct multipath *mpp, uint8_t *key,
@@ -671,18 +687,21 @@ static int reservation_key_matches(struct multipath *mpp, uint8_t *key,
return YNU_NO;
}
-static bool check_holding_reservation(struct multipath *mpp, unsigned int *type)
+static bool check_holding_reservation(struct multipath *mpp, uint8_t *curr_key,
+ unsigned int *type)
{
- if (get_be64(mpp->reservation_key) &&
+ uint64_t zerokey = 0;
+ if (memcmp(curr_key, &zerokey, 8) != 0 &&
get_prhold(mpp->alias) == PR_SET &&
- reservation_key_matches(mpp, (uint8_t *)&mpp->reservation_key, type) == YNU_YES)
+ reservation_key_matches(mpp, curr_key, type) == YNU_YES)
return true;
return false;
}
-static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
+static int mpath_prout_rel(struct multipath *mpp, int rq_servact, int rq_scope,
unsigned int rq_type,
- struct prout_param_descriptor * paramp, int noisy)
+ struct prout_param_descriptor *paramp, int noisy,
+ bool unregister)
{
int i, j;
struct pathgroup *pgp = NULL;
@@ -772,7 +791,8 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
return status;
}
- if (!check_holding_reservation(mpp, &res_type)) {
+ if (!check_holding_reservation(mpp, (uint8_t *)&mpp->reservation_key,
+ &res_type)) {
condlog(2, "%s: Releasing key not holding reservation.", mpp->wwid);
return MPATH_PR_SUCCESS;
}
@@ -795,7 +815,13 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
* 4. Reregistering keys on all the paths
* 5. Resuming the device
*/
- return preempt_self(mpp, MPATH_PROUT_PREE_SA, rq_scope, rq_type, noisy, true);
+ status = preempt_self(mpp, MPATH_PROUT_PREE_SA, rq_scope, rq_type,
+ noisy,
+ unregister ? PREE_WORK_REL_UNREG : PREE_WORK_REL);
+ if (status == MPATH_PR_SUCCESS && unregister)
+ return MPATH_PR_SUCCESS_UNREGISTER;
+ return status;
+
}
/*
@@ -838,7 +864,7 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
select_reservation_key(conf, mpp);
put_multipath_config(conf);
- memcpy(&oldkey, &mpp->reservation_key, 8);
+ oldkey = mpp->reservation_key;
unregistering = (memcmp(&zerokey, paramp->sa_key, 8) == 0);
if (mpp->prkey_source == PRKEY_SOURCE_FILE &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
@@ -920,7 +946,27 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
{
case MPATH_PROUT_REG_SA:
case MPATH_PROUT_REG_IGN_SA:
- ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ if (unregistering && check_holding_reservation(mpp, (uint8_t *)&oldkey, &rq_type)) {
+ struct be64 newkey = mpp->reservation_key;
+ /* temporarily restore reservation key */
+ mpp->reservation_key = oldkey;
+ ret = mpath_prout_rel(mpp, MPATH_PROUT_REL_SA, rq_scope,
+ rq_type, paramp, noisy, true);
+ mpp->reservation_key = newkey;
+ if (ret == MPATH_PR_SUCCESS)
+ /*
+ * Since unregistering it true, paramp->sa_key
+ * must be zero here. So this command will
+ * unregister the key.
+ */
+ ret = mpath_prout_reg(mpp, rq_servact, rq_scope,
+ rq_type, paramp, noisy);
+ else if (ret == MPATH_PR_SUCCESS_UNREGISTER)
+ /* We already unregistered the key */
+ ret = MPATH_PR_SUCCESS;
+ } else
+ ret = mpath_prout_reg(mpp, rq_servact, rq_scope,
+ rq_type, paramp, noisy);
break;
case MPATH_PROUT_PREE_SA:
case MPATH_PROUT_PREE_AB_SA:
@@ -936,7 +982,7 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
/* if we are preempting ourself */
if (memcmp(paramp->sa_key, paramp->key, 8) == 0) {
ret = preempt_self(mpp, rq_servact, rq_scope, rq_type,
- noisy, false);
+ noisy, PREE_WORK_NONE);
break;
}
/* fallthrough */
@@ -947,14 +993,14 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
paramp, noisy, NULL, &failed_paths);
if (rq_servact == MPATH_PROUT_RES_SA &&
ret != MPATH_PR_SUCCESS && failed_paths &&
- check_holding_reservation(mpp, &res_type) &&
+ check_holding_reservation(mpp, paramp->key, &res_type) &&
res_type == rq_type)
/* The reserve failed, but multipathd says we hold it */
ret = MPATH_PR_SUCCESS;
break;
}
case MPATH_PROUT_REL_SA:
- ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy, false);
break;
default:
ret = MPATH_PR_OTHER;

View File

@ -0,0 +1,214 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 30 Sep 2025 19:36:36 -0400
Subject: [PATCH] libmpathpersist: Fix race between restoring a path and
preemption
If a multipath device gets preempted while a path is being restored, the
path could end up getting the old key registered on it anyways. This
happens by:
1. multipathd calling update_map_pr() in mpath_pr_event_handle() and
seeing that there are matching keys.
2. Those matching keys get preempted.
3. multipathd registering the preempted key on the path.
Since there is no way to guarantee that the key won't get preempted
after multipathd checks for it, mpath_pr_event_handle() now calls
update_map_pr() to check the registered keys again after registering the
key. There must be at least as many keys registered after registering
the key on the restored path (which may already have a key registered on
it, so it's possible that the number of registered keys doesn't change).
pr_register_active_paths() calls mpath_pr_event_handle() for all working
paths on a device. In order to avoid calling update_map_pr() twice for
every path, mpath_pr_event_handle() now takes the number of keys to
expect, and skips the initial call to update_map_pr() if it already
knows how many keys are registered before trying to register the new
path.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/main.c | 77 ++++++++++++++++++++++++++++++++---------------
1 file changed, 52 insertions(+), 25 deletions(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 3b53d140..06ff6858 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -89,7 +89,8 @@
#define CMDSIZE 160
#define MSG_SIZE 32
-static void mpath_pr_event_handle(struct path *pp);
+static unsigned int
+mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed);
#define LOG_MSG(lvl, pp) \
do { \
@@ -638,7 +639,7 @@ flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) {
static void
pr_register_active_paths(struct multipath *mpp)
{
- unsigned int i, j;
+ unsigned int i, j, nr_keys = 0;
struct path *pp;
struct pathgroup *pgp;
@@ -647,7 +648,7 @@ pr_register_active_paths(struct multipath *mpp)
if (mpp->prflag == PR_UNSET)
return;
if ((pp->state == PATH_UP) || (pp->state == PATH_GHOST))
- mpath_pr_event_handle(pp);
+ nr_keys = mpath_pr_event_handle(pp, nr_keys);
}
}
}
@@ -1298,7 +1299,7 @@ rescan:
verify_paths(mpp);
mpp->action = ACT_RELOAD;
prflag = mpp->prflag;
- mpath_pr_event_handle(pp);
+ mpath_pr_event_handle(pp, 0);
} else {
if (!should_multipath(pp, vecs->pathvec, vecs->mpvec)) {
orphan_path(pp, "only one path");
@@ -2652,7 +2653,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
*/
condlog(2, "%s: checking persistent "
"reservation registration", pp->dev);
- mpath_pr_event_handle(pp);
+ mpath_pr_event_handle(pp, 0);
if (pp->mpp->prflag == PR_SET &&
prflag != PR_SET)
pr_register_active_paths(pp->mpp);
@@ -3982,12 +3983,16 @@ void unset_pr(struct multipath *mpp)
* Returns MPATH_PR_SUCCESS unless if fails to read the PR keys. If
* MPATH_PR_SUCCESS is returned, mpp->prflag will be either PR_SET or
* PR_UNSET.
+ *
+ * The number of found keys must be at least as large as *nr_keys,
+ * and if MPATH_PR_SUCCESS is returned and mpp->prflag is PR_SET after
+ * the call, *nr_keys will be set to the number of found keys.
*/
-static int update_map_pr(struct multipath *mpp, struct path *pp)
+static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *nr_keys)
{
struct prin_resp resp;
- unsigned int i;
- int ret, isFound;
+ unsigned int i, nr_found = 0;
+ int ret;
bool was_set = (mpp->prflag == PR_SET);
/* If pr is explicitly unset, it must be manually set */
@@ -4017,7 +4022,6 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
condlog(4, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
get_be64(mpp->reservation_key));
- isFound = 0;
for (i = 0;
i < resp.prin_descriptor.prin_readkeys.additional_length / 8; i++) {
uint8_t *keyp = &resp.prin_descriptor.prin_readkeys.key_list[i * 8];
@@ -4037,41 +4041,57 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
if (!memcmp(&mpp->reservation_key, keyp, 8) ||
(get_be64(mpp->old_pr_key) &&
!memcmp(&mpp->old_pr_key, keyp, 8)))
- isFound = 1;
+ nr_found++;
}
- if (isFound) {
+ if (nr_found >= *nr_keys) {
set_pr(mpp);
- condlog(was_set ? 3 : 2, "%s: key found. prflag set.", mpp->alias);
+ condlog(was_set ? 3 : 2, "%s: %u keys found. prflag set.",
+ mpp->alias, nr_found);
+ *nr_keys = nr_found;
} else {
unset_pr(mpp);
- condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
- mpp->alias);
+ condlog(was_set ? 1 : 3,
+ "%s: %u keys found. needed %u. prflag unset.",
+ mpp->alias, nr_found, *nr_keys);
}
return MPATH_PR_SUCCESS;
}
-static void mpath_pr_event_handle(struct path *pp)
+/*
+ * This function is called with the number of registered keys that should be
+ * seen for this device to know that the key has not been preempted while the
+ * path was getting registered. If 0 is passed in, update_mpath_pr is called
+ * before registering the key to figure out the number, assuming that at
+ * least one key must exist.
+ *
+ * The function returns the number of keys that are registered or 0 if
+ * it's unknown.
+ */
+static unsigned int mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed)
{
struct multipath *mpp = pp->mpp;
int ret;
- struct prout_param_descriptor param;
+ struct prout_param_descriptor param = {.sa_flags = 0};
bool clear_reg = false;
if (pp->bus != SYSFS_BUS_SCSI) {
unset_pr(mpp);
- return;
+ return 0;
}
- if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS)
- return;
+ if (nr_keys_needed == 0) {
+ nr_keys_needed = 1;
+ if (update_map_pr(mpp, pp, &nr_keys_needed) != MPATH_PR_SUCCESS)
+ return 0;
+ }
check_prhold(mpp, pp);
if (mpp->prflag != PR_SET) {
if (!mpp->ever_registered_pr)
- return;
+ return 0;
/*
* This path may have been unusable and either the
* registration was cleared or the registered
@@ -4084,21 +4104,28 @@ static void mpath_pr_event_handle(struct path *pp)
clear_reg = true;
}
- memset(&param, 0, sizeof(param));
-
if (!clear_reg) {
param.sa_flags = mpp->sa_flags;
memcpy(param.sa_key, &mpp->reservation_key, 8);
param.num_transportid = 0;
}
-
+retry:
condlog(3, "%s registration for device %s:%s",
clear_reg ? "Clearing" : "Setting", pp->dev, pp->mpp->wwid);
ret = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REG_IGN_SA, 0, 0, &param, 0);
- if (ret != MPATH_PR_SUCCESS )
- {
+ if (ret != MPATH_PR_SUCCESS) {
condlog(0, "%s: %s reservation registration failed. Error: %d",
clear_reg ? "Clearing" : "Setting", pp->dev, ret);
+ } else if (!clear_reg) {
+ if (update_map_pr(mpp, pp, &nr_keys_needed) != MPATH_PR_SUCCESS)
+ return 0;
+ if (mpp->prflag != PR_SET) {
+ memset(&param, 0, sizeof(param));
+ clear_reg = true;
+ goto retry;
+ }
+ return nr_keys_needed;
}
+ return 0;
}

View File

@ -0,0 +1,49 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 30 Sep 2025 23:32:11 -0400
Subject: [PATCH] multipathd: Fix tracking of old PR key
When libmpathpersist is in the process of changing a registered key,
multipathd needs to remember both the old and new values of the key to
deal with a race between limpathpersist updating the key and multipathd
restoring a failed path. It was supposed to stop remembering the old
value when libmpathpersist was done changing the key and issued a
"setprstatus" command. However, clearing the old key was done in
set_pr(), which only gets called by cli_setprstatus() when registering a
new device, not when changing the key on a registered device. Also,
set_pr() is called by update_map_pr(). This means that multipathd was
forgetting the key when it shouldn't and not when it should. Fix this
to only forget the key when cli_setprstatus() is called, and always
then.
Fixes: 1aeffa9e ("libmpathpersist: handle updating key race condition")
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/cli_handlers.c | 1 +
multipathd/main.c | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index ee2764b9..1696dbd4 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -1283,6 +1283,7 @@ cli_setprstatus(void * v, struct strbuf *reply, void * data)
set_pr(mpp);
condlog(2, "%s: prflag set", param);
}
+ memset(&mpp->old_pr_key, 0, 8);
return 0;
}
diff --git a/multipathd/main.c b/multipathd/main.c
index 06ff6858..e17a3355 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3968,7 +3968,6 @@ void set_pr(struct multipath *mpp)
{
mpp->ever_registered_pr = true;
mpp->prflag = PR_SET;
- memset(&mpp->old_pr_key, 0, 8);
}
void unset_pr(struct multipath *mpp)

View File

@ -1,42 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 17 Mar 2020 17:28:24 -0500
Subject: [PATCH] libmultipath: assign variable to make gcc happy
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
There is nothing wrong with is_queueing not being set at the start
of __set_no_path_retry(), it will always get set before it is accessed,
but gcc 8.2.1 is failing with
structs_vec.c: In function __set_no_path_retry:
structs_vec.c:339:7: error: is_queueing may be used uninitialized in
this function [-Werror=maybe-uninitialized]
bool is_queueing;
^~~~~~~~~~~
so, assign a value to make it happy.
Reviewed-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/structs_vec.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 3dbbaa0f..077f2e42 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -336,7 +336,7 @@ static void leave_recovery_mode(struct multipath *mpp)
void __set_no_path_retry(struct multipath *mpp, bool check_features)
{
- bool is_queueing;
+ bool is_queueing = false; /* assign a value to make gcc happy */
check_features = check_features && mpp->features != NULL;
if (check_features)
--
2.17.2

View File

@ -1,66 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Sat, 21 Mar 2020 23:49:59 -0500
Subject: [PATCH] libmutipath: don't close fd on dm_lib_release
If dm_hold_control_open() isn't set, when dm_lib_release() is called, it
will close the control fd. The control fd will get re-opened on the next
dm_task_run() call, but if there is a dm_task_run() call already
in progress in another thread, it can fail. Since many of the
device-mapper callouts happen with the vecs lock held, this wasn't too
noticeable, but there is code that calls dm_task_run() without the
vecs lock held, notably the dmevent waiter code.
Since, as Martin pointed out, dm_hold_control_open() hasn't always
existed in libdevmapper, check if it's supported on compilation,
and update the version requirements if so.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/Makefile | 4 ++++
libmultipath/devmapper.c | 7 ++++++-
2 files changed, 10 insertions(+), 1 deletion(-)
diff --git a/libmultipath/Makefile b/libmultipath/Makefile
index e5651e49..ad690a49 100644
--- a/libmultipath/Makefile
+++ b/libmultipath/Makefile
@@ -36,6 +36,10 @@ ifneq ($(call check_func,dm_task_deferred_remove,/usr/include/libdevmapper.h),0)
CFLAGS += -DLIBDM_API_DEFERRED
endif
+ifneq ($(call check_func,dm_hold_control_dev,/usr/include/libdevmapper.h),0)
+ CFLAGS += -DLIBDM_API_HOLD_CONTROL
+endif
+
OBJS = memory.o parser.o vector.o devmapper.o callout.o \
hwtable.o blacklist.o util.o dmparser.o config.o \
structs.o discovery.o propsel.o dict.o \
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index bed8ddc6..13a1cf53 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -108,7 +108,9 @@ dm_lib_prereq (void)
{
char version[64];
int v[3];
-#if defined(LIBDM_API_DEFERRED)
+#if defined(LIBDM_API_HOLD_CONTROL)
+ int minv[3] = {1, 2, 111};
+#elif defined(LIBDM_API_DEFERRED)
int minv[3] = {1, 2, 89};
#elif defined(DM_SUBSYSTEM_UDEV_FLAG0)
int minv[3] = {1, 2, 82};
@@ -254,6 +256,9 @@ void libmp_dm_init(void)
memcpy(conf->version, version, sizeof(version));
put_multipath_config(conf);
dm_init(verbosity);
+#ifdef LIBDM_API_HOLD_CONTROL
+ dm_hold_control_dev(1);
+#endif
dm_udev_set_sync_support(libmp_dm_udev_sync);
}
--
2.17.2

View File

@ -1,64 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 19 Mar 2020 22:17:51 -0500
Subject: [PATCH] libmultipath: allow force reload with no active paths
If the partition information has changed on multipath devices (say,
because it was updated on another node that has access to the same
storage), users expect that running "multipathd reconfigure" will update
that. However, if the checkers for the multipath device are pending for
too long when the the device is reconfigured, multipathd will give up
waiting for them, and refuse to reload the device, since there are no
active paths. This means that no kpartx update will be triggered.
Multipath is fully capable of reloading a multipath device that has no
active paths. This has been possible for years. If multipath is supposed
to reload the device, it should do so, even if there are no active paths.
Generally, when multipath is force reloaded, kpartx will be updated.
However when a device is reloaded with no paths, the udev rules won't
run kpartx. But they also weren't running kpartx when the first valid
path appeared, even though the dm activation rules get run in this case.
This changes 11-dm-mpath.rules to run kpartx when a device goes from no
usable paths to having usable paths.
Reviewed-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/configure.c | 6 ------
multipath/11-dm-mpath.rules | 2 +-
2 files changed, 1 insertion(+), 7 deletions(-)
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index c95848a0..96c79610 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -710,12 +710,6 @@ select_action (struct multipath * mpp, vector curmp, int force_reload)
return;
}
- if (pathcount(mpp, PATH_UP) == 0) {
- mpp->action = ACT_IMPOSSIBLE;
- condlog(3, "%s: set ACT_IMPOSSIBLE (no usable path)",
- mpp->alias);
- return;
- }
if (force_reload) {
mpp->force_udev_reload = 1;
mpp->action = ACT_RELOAD;
diff --git a/multipath/11-dm-mpath.rules b/multipath/11-dm-mpath.rules
index 07320a14..cd522e8c 100644
--- a/multipath/11-dm-mpath.rules
+++ b/multipath/11-dm-mpath.rules
@@ -75,7 +75,7 @@ ENV{MPATH_DEVICE_READY}=="0", ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="1"
ENV{MPATH_DEVICE_READY}!="0", ENV{.MPATH_DEVICE_READY_OLD}=="0",\
ENV{DM_UDEV_DISABLE_OTHER_RULES_FLAG}="$env{DM_DISABLE_OTHER_RULES_FLAG_OLD}",\
ENV{DM_DISABLE_OTHER_RULES_FLAG_OLD}="",\
- ENV{DM_ACTIVATION}="1"
+ ENV{DM_ACTIVATION}="1", ENV{MPATH_UNCHANGED}="0"
# The code to check multipath state ends here. We need to set
# properties and symlinks regardless whether the map is usable or
--
2.17.2

View File

@ -1,31 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Christian Hesse <mail@eworm.de>
Date: Wed, 6 May 2020 09:35:47 +0200
Subject: [PATCH] libmpathpersist: depend on libmultipath
Without this the build fails with:
/usr/bin/ld: cannot find -lmultipath
Signed-off-by: Christian Hesse <mail@eworm.de>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile b/Makefile
index 1dee3680..ba1d73ba 100644
--- a/Makefile
+++ b/Makefile
@@ -28,7 +28,7 @@ all: $(BUILDDIRS)
$(BUILDDIRS):
$(MAKE) -C $@
-multipath multipathd mpathpersist: libmultipath
+libmpathpersist multipath multipathd mpathpersist: libmultipath
mpathpersist: libmpathpersist
$(BUILDDIRS.clean):
--
2.17.2

View File

@ -1,34 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 12 May 2020 00:39:21 +0200
Subject: [PATCH] multipath-tools: Makefile: more dependency fixes for parallel
build
Extend the late fixes from Christian.
Cc: Christian Hesse <mail@eworm.de>
Signed-off-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/Makefile b/Makefile
index ba1d73ba..fec3b73b 100644
--- a/Makefile
+++ b/Makefile
@@ -28,8 +28,9 @@ all: $(BUILDDIRS)
$(BUILDDIRS):
$(MAKE) -C $@
-libmpathpersist multipath multipathd mpathpersist: libmultipath
-mpathpersist: libmpathpersist
+libmultipath libdmmp: libmpathcmd
+libmpathpersist multipath multipathd: libmultipath
+mpathpersist multipathd: libmpathpersist
$(BUILDDIRS.clean):
$(MAKE) -C ${@:.clean=} clean
--
2.17.2

View File

@ -1,33 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 12 May 2020 00:39:24 +0200
Subject: [PATCH] multipath-tools: Makefile.inc: set -Wno-error=clobbered
We need to ignore -Wclobbered because gcc has trouble dealing with glibc's
implementation of pthread_cleanup_push().
For some variants of gcc, -Wno-clobbered alone isn't enough if -Werror is also
set. Compilation with -Wno-error=clobbered works, though.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile.inc | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Makefile.inc b/Makefile.inc
index d4d1e0dd..9060ac9b 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -91,7 +91,7 @@ TEST_CC_OPTION = $(shell \
STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)
ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,)
-WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered,)
+WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered -Wno-error=clobbered,)
OPTFLAGS = -O2 -g -pipe -Werror -Wall -Wextra -Wformat=2 -Werror=implicit-int \
-Werror=implicit-function-declaration -Werror=format-security \
--
2.17.2

View File

@ -1,91 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 12 May 2020 00:39:25 +0200
Subject: [PATCH] libmultipath: discovery.c: use %z qualifier for size_t
Otherwise compilation for 32bit targets spits out warnings.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/discovery.c | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index ee3290cd..ffec5162 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -986,7 +986,7 @@ parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len)
}
if (len >= out_len) {
- condlog(2, "vpd pg80 overflow, %lu/%lu bytes required",
+ condlog(2, "vpd pg80 overflow, %zu/%zu bytes required",
len + 1, out_len);
len = out_len - 1;
}
@@ -1087,7 +1087,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
len = sprintf(out, "%d", vpd_type);
if (2 * vpd_len >= out_len - len) {
- condlog(1, "%s: WWID overflow, type %d, %lu/%lu bytes required",
+ condlog(1, "%s: WWID overflow, type %d, %zu/%zu bytes required",
__func__, vpd_type,
2 * vpd_len + len + 1, out_len);
vpd_len = (out_len - len - 1) / 2;
@@ -1096,7 +1096,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
len += sprintf(out + len,
"%02x", vpd[i]);
} else if (vpd_type == 0x8 && vpd_len < 4) {
- condlog(1, "%s: VPD length %lu too small for designator type 8",
+ condlog(1, "%s: VPD length %zu too small for designator type 8",
__func__, vpd_len);
return -EINVAL;
} else if (vpd_type == 0x8) {
@@ -1112,7 +1112,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
while (len > 2 && vpd[len - 2] == '\0')
--len;
if (len > out_len - 1) {
- condlog(1, "%s: WWID overflow, type 8/%c, %lu/%lu bytes required",
+ condlog(1, "%s: WWID overflow, type 8/%c, %zu/%zu bytes required",
__func__, out[0], len + 1, out_len);
len = out_len - 1;
}
@@ -1136,7 +1136,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
while ((p = memchr(vpd, ' ', vpd_len))) {
p_len = p - vpd;
if (len + p_len > out_len - 1) {
- condlog(1, "%s: WWID overflow, type 1, %lu/%lu bytes required",
+ condlog(1, "%s: WWID overflow, type 1, %zu/%zu bytes required",
__func__, len + p_len, out_len);
p_len = out_len - len - 1;
}
@@ -1162,7 +1162,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
p_len = vpd_len;
if (p_len > 0 && len < out_len - 1) {
if (len + p_len > out_len - 1) {
- condlog(1, "%s: WWID overflow, type 1, %lu/%lu bytes required",
+ condlog(1, "%s: WWID overflow, type 1, %zu/%zu bytes required",
__func__, len + p_len + 1, out_len);
p_len = out_len - len - 1;
}
@@ -1186,14 +1186,14 @@ parse_vpd_c0_hp3par(const unsigned char *in, size_t in_len,
memset(out, 0x0, out_len);
if (in_len <= 4 || (in[4] > 3 && in_len < 44)) {
- condlog(3, "HP/3PAR vendor specific VPD page length too short: %lu", in_len);
+ condlog(3, "HP/3PAR vendor specific VPD page length too short: %zu", in_len);
return -EINVAL;
}
if (in[4] <= 3) /* revision must be > 3 to have Vomlume Name */
return -ENODATA;
len = get_unaligned_be32(&in[40]);
if (len > out_len || len + 44 > in_len) {
- condlog(3, "HP/3PAR vendor specific Volume name too long: %lu",
+ condlog(3, "HP/3PAR vendor specific Volume name too long: %zu",
len);
return -EINVAL;
}
--
2.17.2

View File

@ -1,185 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 12 May 2020 00:39:26 +0200
Subject: [PATCH] libmultipath: eliminate more signed/unsigned comparisons
Fix some more compiler warnings about signed/unsigned comparison.
I've observed these only on 32bit builds, therefore they went unnoticed
before.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/print.c | 12 ++++++------
libmultipath/prioritizers/alua_spc3.h | 2 +-
multipathd/cli_handlers.c | 20 ++++++++++----------
multipathd/main.c | 2 +-
4 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/libmultipath/print.c b/libmultipath/print.c
index b944ef32..298b3764 100644
--- a/libmultipath/print.c
+++ b/libmultipath/print.c
@@ -1958,25 +1958,25 @@ char *snprint_config(const struct config *conf, int *len,
}
c = reply + snprint_defaults(conf, reply, maxlen);
- if ((c - reply) == maxlen)
+ if (c == reply + maxlen)
continue;
c += snprint_blacklist(conf, c, reply + maxlen - c);
- if ((c - reply) == maxlen)
+ if (c == reply + maxlen)
continue;
c += snprint_blacklist_except(conf, c, reply + maxlen - c);
- if ((c - reply) == maxlen)
+ if (c == reply + maxlen)
continue;
c += snprint_hwtable(conf, c, reply + maxlen - c,
hwtable ? hwtable : conf->hwtable);
- if ((c - reply) == maxlen)
+ if (c == reply + maxlen)
continue;
c += snprint_overrides(conf, c, reply + maxlen - c,
conf->overrides);
- if ((c - reply) == maxlen)
+ if (c == reply + maxlen)
continue;
if (VECTOR_SIZE(conf->mptable) > 0 ||
@@ -1984,7 +1984,7 @@ char *snprint_config(const struct config *conf, int *len,
c += snprint_mptable(conf, c, reply + maxlen - c,
mpvec);
- if ((c - reply) < maxlen) {
+ if (c < reply + maxlen) {
if (len)
*len = c - reply;
return reply;
diff --git a/libmultipath/prioritizers/alua_spc3.h b/libmultipath/prioritizers/alua_spc3.h
index 18b495ef..7ba2cf4c 100644
--- a/libmultipath/prioritizers/alua_spc3.h
+++ b/libmultipath/prioritizers/alua_spc3.h
@@ -284,7 +284,7 @@ struct rtpg_data {
#define RTPG_FOR_EACH_PORT_GROUP(p, g) \
for( \
g = &(p->data[0]); \
- (((char *) g) - ((char *) p)) < get_unaligned_be32(p->length); \
+ ((char *) g) < ((char *) p) + get_unaligned_be32(p->length); \
g = (struct rtpg_tpg_dscr *) ( \
((char *) g) + \
sizeof(struct rtpg_tpg_dscr) + \
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 7d878c88..31c3d9fd 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -66,7 +66,7 @@ show_paths (char ** r, int * len, struct vectors * vecs, char * style,
c += snprint_foreign_paths(c, reply + maxlen - c,
style, pretty);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
@@ -102,7 +102,7 @@ show_path (char ** r, int * len, struct vectors * vecs, struct path *pp,
c += snprint_path(c, reply + maxlen - c, style, pp, 0);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
@@ -131,7 +131,7 @@ show_map_topology (char ** r, int * len, struct multipath * mpp,
c = reply;
c += snprint_multipath_topology(c, reply + maxlen - c, mpp, 2);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
@@ -171,7 +171,7 @@ show_maps_topology (char ** r, int * len, struct vectors * vecs)
}
c += snprint_foreign_topology(c, reply + maxlen - c, 2);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
@@ -209,7 +209,7 @@ show_maps_json (char ** r, int * len, struct vectors * vecs)
c = reply;
c += snprint_multipath_topology_json(c, maxlen, vecs);
- again = ((c - reply) == maxlen);
+ again = (c == reply + maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
@@ -238,7 +238,7 @@ show_map_json (char ** r, int * len, struct multipath * mpp,
c = reply;
c += snprint_multipath_map_json(c, maxlen, mpp);
- again = ((c - reply) == maxlen);
+ again = (c == reply + maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
@@ -487,7 +487,7 @@ show_map (char ** r, int *len, struct multipath * mpp, char * style,
c += snprint_multipath(c, reply + maxlen - c, style,
mpp, pretty);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
@@ -533,7 +533,7 @@ show_maps (char ** r, int *len, struct vectors * vecs, char * style,
}
c += snprint_foreign_multipaths(c, reply + maxlen - c,
style, pretty);
- again = ((c - reply) == (maxlen - 1));
+ again = (c == reply + maxlen - 1);
REALLOC_REPLY(reply, again, maxlen);
}
@@ -1297,7 +1297,7 @@ show_blacklist (char ** r, int * len)
c = reply;
c += snprint_blacklist_report(conf, c, maxlen);
- again = ((c - reply) == maxlen);
+ again = (c == reply + maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
pthread_cleanup_pop(1);
@@ -1339,7 +1339,7 @@ show_devices (char ** r, int * len, struct vectors *vecs)
c = reply;
c += snprint_devices(conf, c, maxlen, vecs);
- again = ((c - reply) == maxlen);
+ again = (c == reply + maxlen);
REALLOC_REPLY(reply, again, maxlen);
}
pthread_cleanup_pop(1);
diff --git a/multipathd/main.c b/multipathd/main.c
index 8baf9abe..6b7db2c0 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -2374,7 +2374,7 @@ checkerloop (void *ap)
conf = get_multipath_config();
max_checkint = conf->max_checkint;
put_multipath_config(conf);
- if (diff_time.tv_sec > max_checkint)
+ if (diff_time.tv_sec > (time_t)max_checkint)
condlog(1, "path checkers took longer "
"than %lu seconds, consider "
"increasing max_polling_interval",
--
2.17.2

View File

@ -1,49 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 12 May 2020 00:39:27 +0200
Subject: [PATCH] libmultipath: set_uint: fix parsing for 32bit
On architectures where sizeof(long) == sizeof(int), the code wouldn't
work as intended. Use strtoul instead. As strtoul happily parses
negative numbers as input, require the number to begin with a digit.
Signed-off-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/dict.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
index 3e25e74f..0e9ea387 100644
--- a/libmultipath/dict.c
+++ b/libmultipath/dict.c
@@ -60,19 +60,22 @@ static int
set_uint(vector strvec, void *ptr)
{
unsigned int *uint_ptr = (unsigned int *)ptr;
- char *buff, *eptr;
- long res;
+ char *buff, *eptr, *p;
+ unsigned long res;
int rc;
buff = set_value(strvec);
if (!buff)
return 1;
- res = strtol(buff, &eptr, 10);
+ p = buff;
+ while (isspace(*p))
+ p++;
+ res = strtoul(p, &eptr, 10);
if (eptr > buff)
while (isspace(*eptr))
eptr++;
- if (*buff == '\0' || *eptr != '\0' || res < 0 || res > UINT_MAX) {
+ if (*buff == '\0' || *eptr != '\0' || !isdigit(*p) || res > UINT_MAX) {
condlog(1, "%s: invalid value for %s: \"%s\"",
__func__, (char*)VECTOR_SLOT(strvec, 0), buff);
rc = 1;
--
2.17.2

View File

@ -1,33 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Tue, 12 May 2020 22:38:22 +0200
Subject: [PATCH] multipath-tools Makefile: add install dependency
$(libdir) must exist before running "make install" on prioritizer, checker,
and foreign libraries.
Cc: Christian Hesse <mail@eworm.de>
Signed-off-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/Makefile b/Makefile
index fec3b73b..8bcaba66 100644
--- a/Makefile
+++ b/Makefile
@@ -32,6 +32,10 @@ libmultipath libdmmp: libmpathcmd
libmpathpersist multipath multipathd: libmultipath
mpathpersist multipathd: libmpathpersist
+libmultipath/checkers.install \
+ libmultipath/prioritizers.install \
+ libmultipath/foreign.install: libmultipath.install
+
$(BUILDDIRS.clean):
$(MAKE) -C ${@:.clean=} clean
--
2.17.2

View File

@ -1,66 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 13 Apr 2017 07:22:23 -0500
Subject: [PATCH] RH: fixup udev rules for redhat
The multipath rules need to run after scsi_id is run. This means moving
them after 60-persistent-storage.rules for redhat. Redhat also uses a
different naming scheme for partitions than SuSE.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile.inc | 2 +-
kpartx/kpartx.rules | 2 +-
multipath/Makefile | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/Makefile.inc b/Makefile.inc
index 9060ac9b..034752d9 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -53,7 +53,7 @@ endif
prefix =
exec_prefix = $(prefix)
usr_prefix = $(prefix)
-bindir = $(exec_prefix)/sbin
+bindir = $(exec_prefix)/usr/sbin
libudevdir = $(prefix)/$(SYSTEMDPATH)/udev
udevrulesdir = $(libudevdir)/rules.d
multipathdir = $(TOPDIR)/libmultipath
diff --git a/kpartx/kpartx.rules b/kpartx/kpartx.rules
index 8f990494..8a3a1718 100644
--- a/kpartx/kpartx.rules
+++ b/kpartx/kpartx.rules
@@ -32,6 +32,6 @@ LABEL="mpath_kpartx_end"
GOTO="kpartx_end"
LABEL="run_kpartx"
-RUN+="/sbin/kpartx -un -p -part /dev/$name"
+RUN+="/sbin/kpartx -un /dev/$name"
LABEL="kpartx_end"
diff --git a/multipath/Makefile b/multipath/Makefile
index 0828a8f7..b9bbb3cf 100644
--- a/multipath/Makefile
+++ b/multipath/Makefile
@@ -24,7 +24,7 @@ install:
$(INSTALL_PROGRAM) -m 755 $(EXEC) $(DESTDIR)$(bindir)/
$(INSTALL_PROGRAM) -d $(DESTDIR)$(udevrulesdir)
$(INSTALL_PROGRAM) -m 644 11-dm-mpath.rules $(DESTDIR)$(udevrulesdir)
- $(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules
+ $(INSTALL_PROGRAM) -m 644 $(EXEC).rules $(DESTDIR)$(libudevdir)/rules.d/62-multipath.rules
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir)
$(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir)
$(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir)
@@ -33,7 +33,7 @@ install:
uninstall:
$(RM) $(DESTDIR)$(bindir)/$(EXEC)
$(RM) $(DESTDIR)$(udevrulesdir)/11-dm-mpath.rules
- $(RM) $(DESTDIR)$(libudevdir)/rules.d/56-multipath.rules
+ $(RM) $(DESTDIR)$(libudevdir)/rules.d/62-multipath.rules
$(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz
$(RM) $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz
--
2.17.2

View File

@ -1,106 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 15 Oct 2014 10:39:30 -0500
Subject: [PATCH] RH: don't start without a config file
If /etc/multipath.conf doesn't exist, don't start multipathd and blacklist
all devices when running multipath. A completely blank configuration file
is almost never what users want. Also, people may have the multipath
packages installed but don't want to use them. This patch provides a
simple way to disable multipath. Simply removing or renaming
/etc/multipath.conf will keep multipath from doing anything.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/config.c | 15 +++++++++++++++
libmultipath/config.h | 1 +
multipath/multipath.rules | 1 +
multipathd/multipathd.8 | 2 ++
multipathd/multipathd.service | 1 +
5 files changed, 20 insertions(+)
diff --git a/libmultipath/config.c b/libmultipath/config.c
index b4d87689..b36778b0 100644
--- a/libmultipath/config.c
+++ b/libmultipath/config.c
@@ -26,6 +26,7 @@
#include "devmapper.h"
#include "mpath_cmd.h"
#include "propsel.h"
+#include "version.h"
static int
hwe_strmatch (const struct hwentry *hwe1, const struct hwentry *hwe2)
@@ -778,6 +779,20 @@ load_config (char * file)
goto out;
}
factorize_hwtable(conf->hwtable, builtin_hwtable_size, file);
+ } else {
+ condlog(0, "/etc/multipath.conf does not exist, blacklisting all devices.");
+ if (conf->blist_devnode == NULL) {
+ conf->blist_devnode = vector_alloc();
+ if (!conf->blist_devnode) {
+ condlog(0, "cannot allocate blacklist\n");
+ goto out;
+ }
+ }
+ if (store_ble(conf->blist_devnode, strdup(".*"),
+ ORIGIN_NO_CONFIG)) {
+ condlog(0, "cannot store default no-config blacklist\n");
+ goto out;
+ }
}
conf->processed_main_config = 1;
diff --git a/libmultipath/config.h b/libmultipath/config.h
index ceecff2d..3368d8c9 100644
--- a/libmultipath/config.h
+++ b/libmultipath/config.h
@@ -9,6 +9,7 @@
#define ORIGIN_DEFAULT 0
#define ORIGIN_CONFIG 1
+#define ORIGIN_NO_CONFIG 2
/*
* In kernel, fast_io_fail == 0 means immediate failure on rport delete.
diff --git a/multipath/multipath.rules b/multipath/multipath.rules
index 9df11a95..0486bf70 100644
--- a/multipath/multipath.rules
+++ b/multipath/multipath.rules
@@ -9,6 +9,7 @@ IMPORT{cmdline}="nompath"
ENV{nompath}=="?*", GOTO="end_mpath"
IMPORT{cmdline}="multipath"
ENV{multipath}=="off", GOTO="end_mpath"
+TEST!="/etc/multipath.conf", GOTO="end_mpath"
ENV{DEVTYPE}!="partition", GOTO="test_dev"
IMPORT{parent}="DM_MULTIPATH_DEVICE_PATH"
diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8
index 048a838d..8bd47a80 100644
--- a/multipathd/multipathd.8
+++ b/multipathd/multipathd.8
@@ -39,6 +39,8 @@ map regains its maximum performance and redundancy.
This daemon executes the external \fBmultipath\fR tool when events occur.
In turn, the multipath tool signals the multipathd daemon when it is done with
devmap reconfiguration, so that it can refresh its failed path list.
+
+In this Linux distribution, multipathd does not run unless a /etc/multipath.conf file exists.
.
.
.\" ----------------------------------------------------------------------------
diff --git a/multipathd/multipathd.service b/multipathd/multipathd.service
index ba24983e..17434cef 100644
--- a/multipathd/multipathd.service
+++ b/multipathd/multipathd.service
@@ -4,6 +4,7 @@ Wants=systemd-udev-trigger.service systemd-udev-settle.service
Before=iscsi.service iscsid.service lvm2-activation-early.service
Before=local-fs-pre.target blk-availability.service
After=multipathd.socket systemd-udev-trigger.service systemd-udev-settle.service
+ConditionPathExists=/etc/multipath.conf
DefaultDependencies=no
Conflicts=shutdown.target
ConditionKernelCommandLine=!nompath
--
2.17.2

View File

@ -1,64 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 19 Apr 2017 06:10:01 -0500
Subject: [PATCH] RH: use rpm optflags if present
Use the passed in optflags when compiling as an RPM, and keep the
default flags as close as possible to the current fedora flags, while
still being generic.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
Makefile.inc | 29 +++++++++++++++++++++--------
1 file changed, 21 insertions(+), 8 deletions(-)
diff --git a/Makefile.inc b/Makefile.inc
index 034752d9..c2abd301 100644
--- a/Makefile.inc
+++ b/Makefile.inc
@@ -89,16 +89,29 @@ TEST_CC_OPTION = $(shell \
echo "$(2)"; \
fi)
-STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)
ERROR_DISCARDED_QUALIFIERS := $(call TEST_CC_OPTION,-Werror=discarded-qualifiers,)
WNOCLOBBERED := $(call TEST_CC_OPTION,-Wno-clobbered -Wno-error=clobbered,)
+ifndef RPM_OPT_FLAGS
+ STACKPROT := $(call TEST_CC_OPTION,-fstack-protector-strong,-fstack-protector)
+ OPTFLAGS = -O2 -g -pipe -Wall -Werror=format-security \
+ -Wp,-D_FORTIFY_SOURCE=2 -fexceptions \
+ $(STACKPROT) -grecord-gcc-switches \
+ -fasynchronous-unwind-tables
+ ifeq ($(shell test -f /usr/lib/rpm/redhat/redhat-hardened-cc1 && echo 1),1)
+ OPTFLAGS += -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1
+ endif
+ ifeq ($(shell test -f /usr/lib/rpm/redhat/redhat-annobin-cc1 && echo 1),1)
+ OPTFLAGS += -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1
+ endif
+else
+ OPTFLAGS = $(RPM_OPT_FLAGS)
+endif
+OPTFLAGS += -Werror -Wextra -Wstrict-prototypes -Wformat=2 \
+ -Werror=implicit-int -Werror=implicit-function-declaration \
+ $(WNOCLOBBERED) \
+ -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) \
+ --param=ssp-buffer-size=4
-OPTFLAGS = -O2 -g -pipe -Werror -Wall -Wextra -Wformat=2 -Werror=implicit-int \
- -Werror=implicit-function-declaration -Werror=format-security \
- $(WNOCLOBBERED) \
- -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) \
- $(STACKPROT) --param=ssp-buffer-size=4
-CPPFLAGS := -Wp,-D_FORTIFY_SOURCE=2
CFLAGS := $(OPTFLAGS) -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \
-MMD -MP $(CFLAGS)
BIN_CFLAGS = -fPIE -DPIE
@@ -135,4 +148,4 @@ check_file = $(shell \
%.o: %.c
@echo building $@ because of $?
- $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<
+ $(CC) $(CFLAGS) -c -o $@ $<
--
2.17.2

View File

@ -1,121 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 6 Nov 2017 21:39:28 -0600
Subject: [PATCH] RH: warn on invalid regex instead of failing
multipath.conf used to allow "*" as a match everything regular expression,
instead of requiring ".*". Instead of erroring when the old style
regular expressions are used, it should print a warning and convert
them.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/dict.c | 27 +++++++++++++++++++++------
libmultipath/parser.c | 13 +++++++++++++
libmultipath/parser.h | 1 +
3 files changed, 35 insertions(+), 6 deletions(-)
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
index 0e9ea387..184d4b22 100644
--- a/libmultipath/dict.c
+++ b/libmultipath/dict.c
@@ -103,6 +103,21 @@ set_str(vector strvec, void *ptr)
return 0;
}
+static int
+set_regex(vector strvec, void *ptr)
+{
+ char **str_ptr = (char **)ptr;
+
+ if (*str_ptr)
+ FREE(*str_ptr);
+ *str_ptr = set_regex_value(strvec);
+
+ if (!*str_ptr)
+ return 1;
+
+ return 0;
+}
+
static int
set_yes_no(vector strvec, void *ptr)
{
@@ -1504,7 +1519,7 @@ ble_ ## option ## _handler (struct config *conf, vector strvec) \
if (!conf->option) \
return 1; \
\
- buff = set_value(strvec); \
+ buff = set_regex_value(strvec); \
if (!buff) \
return 1; \
\
@@ -1520,7 +1535,7 @@ ble_ ## option ## _ ## name ## _handler (struct config *conf, vector strvec) \
if (!conf->option) \
return 1; \
\
- buff = set_value(strvec); \
+ buff = set_regex_value(strvec); \
if (!buff) \
return 1; \
\
@@ -1623,16 +1638,16 @@ device_handler(struct config *conf, vector strvec)
return 0;
}
-declare_hw_handler(vendor, set_str)
+declare_hw_handler(vendor, set_regex)
declare_hw_snprint(vendor, print_str)
-declare_hw_handler(product, set_str)
+declare_hw_handler(product, set_regex)
declare_hw_snprint(product, print_str)
-declare_hw_handler(revision, set_str)
+declare_hw_handler(revision, set_regex)
declare_hw_snprint(revision, print_str)
-declare_hw_handler(bl_product, set_str)
+declare_hw_handler(bl_product, set_regex)
declare_hw_snprint(bl_product, print_str)
declare_hw_handler(hwhandler, set_str)
diff --git a/libmultipath/parser.c b/libmultipath/parser.c
index d478b177..a184511b 100644
--- a/libmultipath/parser.c
+++ b/libmultipath/parser.c
@@ -382,6 +382,19 @@ oom:
return NULL;
}
+void *
+set_regex_value(vector strvec)
+{
+ char *buff = set_value(strvec);
+
+ if (buff && strcmp("*", buff) == 0) {
+ condlog(0, "Invalid regular expression \"*\" in multipath.conf. Using \".*\"");
+ FREE(buff);
+ return strdup(".*");
+ }
+ return buff;
+}
+
/* non-recursive configuration stream handler */
static int kw_level = 0;
diff --git a/libmultipath/parser.h b/libmultipath/parser.h
index 62906e98..b7917052 100644
--- a/libmultipath/parser.h
+++ b/libmultipath/parser.h
@@ -77,6 +77,7 @@ extern void dump_keywords(vector keydump, int level);
extern void free_keywords(vector keywords);
extern vector alloc_strvec(char *string);
extern void *set_value(vector strvec);
+extern void *set_regex_value(vector strvec);
extern int process_file(struct config *conf, char *conf_file);
extern struct keyword * find_keyword(vector keywords, vector v, char * name);
int snprint_keyword(char *buff, int len, char *fmt, struct keyword *kw,
--
2.17.2

View File

@ -1,139 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 8 Jun 2020 14:27:51 -0500
Subject: [PATCH] libmultipath: remove _blacklist_exceptions functions
_blacklist_exceptions() and _blacklist_exceptions_device() are exactly
the same as _blacklist() and _blacklist_device(), so remove them, and
give the remaining functions to a more general name.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/blacklist.c | 62 ++++++++++------------------------------
1 file changed, 15 insertions(+), 47 deletions(-)
diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c
index d9691b17..04d3adb9 100644
--- a/libmultipath/blacklist.c
+++ b/libmultipath/blacklist.c
@@ -101,21 +101,8 @@ int set_ble_device(vector blist, char * vendor, char * product, int origin)
return 0;
}
-int
-_blacklist_exceptions (vector elist, const char * str)
-{
- int i;
- struct blentry * ele;
-
- vector_foreach_slot (elist, ele, i) {
- if (!regexec(&ele->regex, str, 0, NULL, 0))
- return 1;
- }
- return 0;
-}
-
-int
-_blacklist (vector blist, const char * str)
+static int
+match_reglist (vector blist, const char * str)
{
int i;
struct blentry * ble;
@@ -127,28 +114,9 @@ _blacklist (vector blist, const char * str)
return 0;
}
-int
-_blacklist_exceptions_device(const struct _vector *elist, const char * vendor,
- const char * product)
-{
- int i;
- struct blentry_device * ble;
-
- vector_foreach_slot (elist, ble, i) {
- if (!ble->vendor && !ble->product)
- continue;
- if ((!ble->vendor ||
- !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) &&
- (!ble->product ||
- !regexec(&ble->product_reg, product, 0, NULL, 0)))
- return 1;
- }
- return 0;
-}
-
-int
-_blacklist_device (const struct _vector *blist, const char * vendor,
- const char * product)
+static int
+match_reglist_device (const struct _vector *blist, const char * vendor,
+ const char * product)
{
int i;
struct blentry_device * ble;
@@ -294,9 +262,9 @@ filter_device (vector blist, vector elist, char * vendor, char * product,
int r = MATCH_NOTHING;
if (vendor && product) {
- if (_blacklist_exceptions_device(elist, vendor, product))
+ if (match_reglist_device(elist, vendor, product))
r = MATCH_DEVICE_BLIST_EXCEPT;
- else if (_blacklist_device(blist, vendor, product))
+ else if (match_reglist_device(blist, vendor, product))
r = MATCH_DEVICE_BLIST;
}
@@ -310,9 +278,9 @@ filter_devnode (vector blist, vector elist, char * dev)
int r = MATCH_NOTHING;
if (dev) {
- if (_blacklist_exceptions(elist, dev))
+ if (match_reglist(elist, dev))
r = MATCH_DEVNODE_BLIST_EXCEPT;
- else if (_blacklist(blist, dev))
+ else if (match_reglist(blist, dev))
r = MATCH_DEVNODE_BLIST;
}
@@ -326,9 +294,9 @@ filter_wwid (vector blist, vector elist, char * wwid, char * dev)
int r = MATCH_NOTHING;
if (wwid) {
- if (_blacklist_exceptions(elist, wwid))
+ if (match_reglist(elist, wwid))
r = MATCH_WWID_BLIST_EXCEPT;
- else if (_blacklist(blist, wwid))
+ else if (match_reglist(blist, wwid))
r = MATCH_WWID_BLIST;
}
@@ -345,9 +313,9 @@ filter_protocol(vector blist, vector elist, struct path * pp)
if (pp) {
snprint_path_protocol(buf, sizeof(buf), pp);
- if (_blacklist_exceptions(elist, buf))
+ if (match_reglist(elist, buf))
r = MATCH_PROTOCOL_BLIST_EXCEPT;
- else if (_blacklist(blist, buf))
+ else if (match_reglist(blist, buf))
r = MATCH_PROTOCOL_BLIST;
}
@@ -417,11 +385,11 @@ filter_property(struct config *conf, struct udev_device *udev, int lvl,
if (check_missing_prop && !strcmp(env, uid_attribute))
uid_attr_seen = true;
- if (_blacklist_exceptions(conf->elist_property, env)) {
+ if (match_reglist(conf->elist_property, env)) {
r = MATCH_PROPERTY_BLIST_EXCEPT;
break;
}
- if (_blacklist(conf->blist_property, env)) {
+ if (match_reglist(conf->blist_property, env)) {
r = MATCH_PROPERTY_BLIST;
break;
}
--
2.17.2

View File

@ -1,95 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 8 Jun 2020 20:23:56 -0500
Subject: [PATCH] libmultipath: fix parser issue with comments in strings
If a quoted string starts with '#' or '!', the parser will stop
parsing the line, thinking that it's a comment. It should only
be checking for comments outside of quoted strings. Fixed this and
added unit tests to verify it.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/parser.c | 4 +++-
tests/parser.c | 42 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/libmultipath/parser.c b/libmultipath/parser.c
index a184511b..a7285a35 100644
--- a/libmultipath/parser.c
+++ b/libmultipath/parser.c
@@ -300,8 +300,10 @@ alloc_strvec(char *string)
(isspace((int) *cp) || !isascii((int) *cp)))
&& *cp != '\0')
cp++;
- if (*cp == '\0' || *cp == '!' || *cp == '#')
+ if (*cp == '\0' ||
+ (!in_string && (*cp == '!' || *cp == '#'))) {
return strvec;
+ }
}
out:
vector_free(strvec);
diff --git a/tests/parser.c b/tests/parser.c
index 29859dac..5772391e 100644
--- a/tests/parser.c
+++ b/tests/parser.c
@@ -440,6 +440,46 @@ static void test18(void **state)
free_strvec(v);
}
+static void test19(void **state)
+{
+#define QUOTED19 "!value"
+ vector v = alloc_strvec("key \"" QUOTED19 "\"");
+ char *val;
+
+ assert_int_equal(VECTOR_SIZE(v), 4);
+ assert_string_equal(VECTOR_SLOT(v, 0), "key");
+ assert_true(is_quote(VECTOR_SLOT(v, 1)));
+ assert_string_equal(VECTOR_SLOT(v, 2), QUOTED19);
+ assert_true(is_quote(VECTOR_SLOT(v, 3)));
+ assert_int_equal(validate_config_strvec(v, test_file), 0);
+
+ val = set_value(v);
+ assert_string_equal(val, QUOTED19);
+
+ free(val);
+ free_strvec(v);
+}
+
+static void test20(void **state)
+{
+#define QUOTED20 "#value"
+ vector v = alloc_strvec("key \"" QUOTED20 "\"");
+ char *val;
+
+ assert_int_equal(VECTOR_SIZE(v), 4);
+ assert_string_equal(VECTOR_SLOT(v, 0), "key");
+ assert_true(is_quote(VECTOR_SLOT(v, 1)));
+ assert_string_equal(VECTOR_SLOT(v, 2), QUOTED20);
+ assert_true(is_quote(VECTOR_SLOT(v, 3)));
+ assert_int_equal(validate_config_strvec(v, test_file), 0);
+
+ val = set_value(v);
+ assert_string_equal(val, QUOTED20);
+
+ free(val);
+ free_strvec(v);
+}
+
int test_config_parser(void)
{
const struct CMUnitTest tests[] = {
@@ -461,6 +501,8 @@ int test_config_parser(void)
cmocka_unit_test(test16),
cmocka_unit_test(test17),
cmocka_unit_test(test18),
+ cmocka_unit_test(test19),
+ cmocka_unit_test(test20),
};
return cmocka_run_group_tests(tests, setup, teardown);
}
--
2.17.2

View File

@ -1,435 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 8 Jun 2020 13:40:16 -0500
Subject: [PATCH] libmultipath: invert regexes that start with exclamation
point
The number of devices that multipath needs to blacklist keeps growing,
and the udev rules already have
KERNEL!="sd*|dasd*|nvme*", GOTO="end_mpath"
so they only work correctly with these device types. Instead of
individually blacklisting every type of device that can't be
multipathed, multipath's default blacklist should work like the udev
rule, and blacklist all devices that aren't scsi, dasd, or nvme.
Unfortunately, the c regex library doesn't support negative lookahead.
Instead, multipath should treat "!" at the beginning of
blacklist/exceptions regexes as inverse matching the rest of the regex.
If users need to match a literal '!' as the first character of their
regex, they can use "\!" instead. This allows multipath to change the
default devnode blacklist regex to "!^(sd[a-z]|dasd[a-z]|nvme[0-9])".
Extra tests have been added to the blacklist unit tests to verify the
inverse matching code and the new default blacklist.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/blacklist.c | 41 +++++++++-----
libmultipath/blacklist.h | 3 +
multipath/multipath.conf.5 | 17 ++++--
tests/blacklist.c | 110 +++++++++++++++++++++++++++++++++++++
tests/test-lib.c | 2 +-
5 files changed, 155 insertions(+), 18 deletions(-)
diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c
index 04d3adb9..0c58aa32 100644
--- a/libmultipath/blacklist.c
+++ b/libmultipath/blacklist.c
@@ -15,9 +15,24 @@
#include "structs_vec.h"
#include "print.h"
+char *check_invert(char *str, bool *invert)
+{
+ if (str[0] == '!') {
+ *invert = true;
+ return str + 1;
+ }
+ if (str[0] == '\\' && str[1] == '!') {
+ *invert = false;
+ return str + 1;
+ }
+ *invert = false;
+ return str;
+}
+
int store_ble(vector blist, char * str, int origin)
{
struct blentry * ble;
+ char *regex_str;
if (!str)
return 0;
@@ -30,7 +45,8 @@ int store_ble(vector blist, char * str, int origin)
if (!ble)
goto out;
- if (regcomp(&ble->regex, str, REG_EXTENDED|REG_NOSUB))
+ regex_str = check_invert(str, &ble->invert);
+ if (regcomp(&ble->regex, regex_str, REG_EXTENDED|REG_NOSUB))
goto out1;
if (!vector_alloc_slot(blist))
@@ -66,6 +82,7 @@ int alloc_ble_device(vector blist)
int set_ble_device(vector blist, char * vendor, char * product, int origin)
{
struct blentry_device * ble;
+ char *regex_str;
if (!blist)
return 1;
@@ -76,7 +93,8 @@ int set_ble_device(vector blist, char * vendor, char * product, int origin)
return 1;
if (vendor) {
- if (regcomp(&ble->vendor_reg, vendor,
+ regex_str = check_invert(vendor, &ble->vendor_invert);
+ if (regcomp(&ble->vendor_reg, regex_str,
REG_EXTENDED|REG_NOSUB)) {
FREE(vendor);
if (product)
@@ -86,7 +104,8 @@ int set_ble_device(vector blist, char * vendor, char * product, int origin)
ble->vendor = vendor;
}
if (product) {
- if (regcomp(&ble->product_reg, product,
+ regex_str = check_invert(product, &ble->product_invert);
+ if (regcomp(&ble->product_reg, regex_str,
REG_EXTENDED|REG_NOSUB)) {
FREE(product);
if (vendor) {
@@ -108,7 +127,7 @@ match_reglist (vector blist, const char * str)
struct blentry * ble;
vector_foreach_slot (blist, ble, i) {
- if (!regexec(&ble->regex, str, 0, NULL, 0))
+ if (!!regexec(&ble->regex, str, 0, NULL, 0) == ble->invert)
return 1;
}
return 0;
@@ -125,9 +144,11 @@ match_reglist_device (const struct _vector *blist, const char * vendor,
if (!ble->vendor && !ble->product)
continue;
if ((!ble->vendor ||
- !regexec(&ble->vendor_reg, vendor, 0, NULL, 0)) &&
+ !!regexec(&ble->vendor_reg, vendor, 0, NULL, 0) ==
+ ble->vendor_invert) &&
(!ble->product ||
- !regexec(&ble->product_reg, product, 0, NULL, 0)))
+ !!regexec(&ble->product_reg, product, 0, NULL, 0) ==
+ ble->product_invert))
return 1;
}
return 0;
@@ -160,13 +181,7 @@ setup_default_blist (struct config * conf)
char * str;
int i;
- str = STRDUP("^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]");
- if (!str)
- return 1;
- if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
- return 1;
-
- str = STRDUP("^(td|hd|vd)[a-z]");
+ str = STRDUP("!^(sd[a-z]|dasd[a-z]|nvme[0-9])");
if (!str)
return 1;
if (store_ble(conf->blist_devnode, str, ORIGIN_DEFAULT))
diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h
index 2d721f60..4305857d 100644
--- a/libmultipath/blacklist.h
+++ b/libmultipath/blacklist.h
@@ -20,6 +20,7 @@
struct blentry {
char * str;
regex_t regex;
+ bool invert;
int origin;
};
@@ -28,6 +29,8 @@ struct blentry_device {
char * product;
regex_t vendor_reg;
regex_t product_reg;
+ bool vendor_invert;
+ bool product_invert;
int origin;
};
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
index 3455b1cc..6dc26f10 100644
--- a/multipath/multipath.conf.5
+++ b/multipath/multipath.conf.5
@@ -1248,6 +1248,16 @@ being handled by multipath-tools.
.LP
.
.
+In the \fIblacklist\fR and \fIblacklist_exceptions\fR sections, starting a
+quoted value with an exclamation mark \fB"!"\fR will invert the matching
+of the rest of the regular expression. For instance, \fB"!^sd[a-z]"\fR will
+match all values that do not start with \fB"sd[a-z]"\fR. The exclamation mark
+can be escaped \fB"\\!"\fR to match a literal \fB!\fR at the start of a
+regular expression. \fBNote:\fR The exclamation mark must be inside quotes,
+otherwise it will be treated as starting a comment.
+.LP
+.
+.
The \fIblacklist_exceptions\fR section is used to revert the actions of the
\fIblacklist\fR section. This allows one to selectively include ("whitelist") devices which
would normally be excluded via the \fIblacklist\fR section. A common usage is
@@ -1264,10 +1274,9 @@ unless explicitly stated.
Regular expression matching the device nodes to be excluded/included.
.RS
.PP
-The default \fIblacklist\fR consists of the regular expressions
-"^(ram|zram|raw|loop|fd|md|dm-|sr|scd|st|dcssblk)[0-9]" and
-"^(td|hd|vd)[a-z]". This causes virtual devices, non-disk devices, and some other
-device types to be excluded from multipath handling by default.
+The default \fIblacklist\fR consists of the regular expression
+\fB"!^(sd[a-z]|dasd[a-z]|nvme[0-9])"\fR. This causes all device types other
+than scsi, dasd, and nvme to be excluded from multipath handling by default.
.RE
.TP
.B wwid
diff --git a/tests/blacklist.c b/tests/blacklist.c
index cc8a9a4a..d20e97af 100644
--- a/tests/blacklist.c
+++ b/tests/blacklist.c
@@ -60,20 +60,46 @@ __wrap_udev_list_entry_get_name(struct udev_list_entry *list_entry)
return *(const char **)list_entry;
}
+vector elist_property_default;
+vector blist_devnode_default;
vector blist_devnode_sdb;
+vector blist_devnode_sdb_inv;
vector blist_all;
vector blist_device_foo_bar;
+vector blist_device_foo_inv_bar;
+vector blist_device_foo_bar_inv;
vector blist_device_all;
vector blist_wwid_xyzzy;
+vector blist_wwid_xyzzy_inv;
vector blist_protocol_fcp;
+vector blist_protocol_fcp_inv;
vector blist_property_wwn;
+vector blist_property_wwn_inv;
static int setup(void **state)
{
+ struct config conf;
+
+ memset(&conf, 0, sizeof(conf));
+ conf.blist_devnode = vector_alloc();
+ if (!conf.blist_devnode)
+ return -1;
+ conf.elist_property = vector_alloc();
+ if (!conf.elist_property)
+ return -1;
+ if (setup_default_blist(&conf) != 0)
+ return -1;
+ elist_property_default = conf.elist_property;
+ blist_devnode_default = conf.blist_devnode;
+
blist_devnode_sdb = vector_alloc();
if (!blist_devnode_sdb ||
store_ble(blist_devnode_sdb, strdup("sdb"), ORIGIN_CONFIG))
return -1;
+ blist_devnode_sdb_inv = vector_alloc();
+ if (!blist_devnode_sdb_inv ||
+ store_ble(blist_devnode_sdb_inv, strdup("!sdb"), ORIGIN_CONFIG))
+ return -1;
blist_all = vector_alloc();
if (!blist_all || store_ble(blist_all, strdup(".*"), ORIGIN_CONFIG))
@@ -84,6 +110,18 @@ static int setup(void **state)
set_ble_device(blist_device_foo_bar, strdup("foo"), strdup("bar"),
ORIGIN_CONFIG))
return -1;
+ blist_device_foo_inv_bar = vector_alloc();
+ if (!blist_device_foo_inv_bar ||
+ alloc_ble_device(blist_device_foo_inv_bar) ||
+ set_ble_device(blist_device_foo_inv_bar, strdup("!foo"),
+ strdup("bar"), ORIGIN_CONFIG))
+ return -1;
+ blist_device_foo_bar_inv = vector_alloc();
+ if (!blist_device_foo_bar_inv ||
+ alloc_ble_device(blist_device_foo_bar_inv) ||
+ set_ble_device(blist_device_foo_bar_inv, strdup("foo"),
+ strdup("!bar"), ORIGIN_CONFIG))
+ return -1;
blist_device_all = vector_alloc();
if (!blist_device_all || alloc_ble_device(blist_device_all) ||
@@ -95,29 +133,50 @@ static int setup(void **state)
if (!blist_wwid_xyzzy ||
store_ble(blist_wwid_xyzzy, strdup("xyzzy"), ORIGIN_CONFIG))
return -1;
+ blist_wwid_xyzzy_inv = vector_alloc();
+ if (!blist_wwid_xyzzy_inv ||
+ store_ble(blist_wwid_xyzzy_inv, strdup("!xyzzy"), ORIGIN_CONFIG))
+ return -1;
blist_protocol_fcp = vector_alloc();
if (!blist_protocol_fcp ||
store_ble(blist_protocol_fcp, strdup("scsi:fcp"), ORIGIN_CONFIG))
return -1;
+ blist_protocol_fcp_inv = vector_alloc();
+ if (!blist_protocol_fcp_inv ||
+ store_ble(blist_protocol_fcp_inv, strdup("!scsi:fcp"),
+ ORIGIN_CONFIG))
+ return -1;
blist_property_wwn = vector_alloc();
if (!blist_property_wwn ||
store_ble(blist_property_wwn, strdup("ID_WWN"), ORIGIN_CONFIG))
return -1;
+ blist_property_wwn_inv = vector_alloc();
+ if (!blist_property_wwn_inv ||
+ store_ble(blist_property_wwn_inv, strdup("!ID_WWN"), ORIGIN_CONFIG))
+ return -1;
return 0;
}
static int teardown(void **state)
{
+ free_blacklist(elist_property_default);
+ free_blacklist(blist_devnode_default);
free_blacklist(blist_devnode_sdb);
+ free_blacklist(blist_devnode_sdb_inv);
free_blacklist(blist_all);
free_blacklist_device(blist_device_foo_bar);
+ free_blacklist_device(blist_device_foo_inv_bar);
+ free_blacklist_device(blist_device_foo_bar_inv);
free_blacklist_device(blist_device_all);
free_blacklist(blist_wwid_xyzzy);
+ free_blacklist(blist_wwid_xyzzy_inv);
free_blacklist(blist_protocol_fcp);
+ free_blacklist(blist_protocol_fcp_inv);
free_blacklist(blist_property_wwn);
+ free_blacklist(blist_property_wwn_inv);
return 0;
}
@@ -141,6 +200,11 @@ static void test_devnode_blacklist(void **state)
expect_condlog(3, "sdb: device node name blacklisted\n");
assert_int_equal(filter_devnode(blist_devnode_sdb, NULL, "sdb"),
MATCH_DEVNODE_BLIST);
+ assert_int_equal(filter_devnode(blist_devnode_sdb_inv, NULL, "sdb"),
+ MATCH_NOTHING);
+ expect_condlog(3, "sdc: device node name blacklisted\n");
+ assert_int_equal(filter_devnode(blist_devnode_sdb_inv, NULL, "sdc"),
+ MATCH_DEVNODE_BLIST);
}
static void test_devnode_whitelist(void **state)
@@ -159,12 +223,39 @@ static void test_devnode_missing(void **state)
MATCH_NOTHING);
}
+static void test_devnode_default(void **state)
+{
+ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "sdaa"),
+ MATCH_NOTHING);
+ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "nvme0n1"),
+ MATCH_NOTHING);
+ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "dasda"),
+ MATCH_NOTHING);
+ expect_condlog(3, "hda: device node name blacklisted\n");
+ assert_int_equal(filter_devnode(blist_devnode_default, NULL, "hda"),
+ MATCH_DEVNODE_BLIST);
+}
+
static void test_device_blacklist(void **state)
{
expect_condlog(3, "sdb: (foo:bar) vendor/product blacklisted\n");
assert_int_equal(filter_device(blist_device_foo_bar, NULL, "foo",
"bar", "sdb"),
MATCH_DEVICE_BLIST);
+ assert_int_equal(filter_device(blist_device_foo_inv_bar, NULL, "foo",
+ "bar", "sdb"),
+ MATCH_NOTHING);
+ assert_int_equal(filter_device(blist_device_foo_bar_inv, NULL, "foo",
+ "bar", "sdb"),
+ MATCH_NOTHING);
+ expect_condlog(3, "sdb: (baz:bar) vendor/product blacklisted\n");
+ assert_int_equal(filter_device(blist_device_foo_inv_bar, NULL, "baz",
+ "bar", "sdb"),
+ MATCH_DEVICE_BLIST);
+ expect_condlog(3, "sdb: (foo:baz) vendor/product blacklisted\n");
+ assert_int_equal(filter_device(blist_device_foo_bar_inv, NULL, "foo",
+ "baz", "sdb"),
+ MATCH_DEVICE_BLIST);
}
static void test_device_whitelist(void **state)
@@ -191,6 +282,11 @@ static void test_wwid_blacklist(void **state)
expect_condlog(3, "sdb: wwid xyzzy blacklisted\n");
assert_int_equal(filter_wwid(blist_wwid_xyzzy, NULL, "xyzzy", "sdb"),
MATCH_WWID_BLIST);
+ assert_int_equal(filter_wwid(blist_wwid_xyzzy_inv, NULL, "xyzzy",
+ "sdb"), MATCH_NOTHING);
+ expect_condlog(3, "sdb: wwid plugh blacklisted\n");
+ assert_int_equal(filter_wwid(blist_wwid_xyzzy_inv, NULL, "plugh",
+ "sdb"), MATCH_WWID_BLIST);
}
static void test_wwid_whitelist(void **state)
@@ -218,6 +314,12 @@ static void test_protocol_blacklist(void **state)
expect_condlog(3, "sdb: protocol scsi:fcp blacklisted\n");
assert_int_equal(filter_protocol(blist_protocol_fcp, NULL, &pp),
MATCH_PROTOCOL_BLIST);
+ assert_int_equal(filter_protocol(blist_protocol_fcp_inv, NULL, &pp),
+ MATCH_NOTHING);
+ pp.sg_id.proto_id = SCSI_PROTOCOL_ATA;
+ expect_condlog(3, "sdb: protocol scsi:ata blacklisted\n");
+ assert_int_equal(filter_protocol(blist_protocol_fcp_inv, NULL, &pp),
+ MATCH_PROTOCOL_BLIST);
}
static void test_protocol_whitelist(void **state)
@@ -245,10 +347,17 @@ static void test_protocol_missing(void **state)
static void test_property_blacklist(void **state)
{
static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } };
+ static struct udev_device udev_inv = { "sdb", { "ID_WWN", NULL } };
conf.blist_property = blist_property_wwn;
expect_condlog(3, "sdb: udev property ID_WWN blacklisted\n");
assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
MATCH_PROPERTY_BLIST);
+ conf.blist_property = blist_property_wwn_inv;
+ expect_condlog(3, "sdb: udev property ID_FOO blacklisted\n");
+ assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"),
+ MATCH_PROPERTY_BLIST);
+ assert_int_equal(filter_property(&conf, &udev_inv, 3, "ID_SERIAL"),
+ MATCH_NOTHING);
}
/* the property check works different in that you check all the property
@@ -482,6 +591,7 @@ int test_blacklist(void)
cmocka_unit_test(test_devnode_blacklist),
cmocka_unit_test(test_devnode_whitelist),
cmocka_unit_test(test_devnode_missing),
+ cmocka_unit_test(test_devnode_default),
cmocka_unit_test(test_device_blacklist),
cmocka_unit_test(test_device_whitelist),
cmocka_unit_test(test_device_missing),
diff --git a/tests/test-lib.c b/tests/test-lib.c
index 59275163..08ff2d8d 100644
--- a/tests/test-lib.c
+++ b/tests/test-lib.c
@@ -15,7 +15,7 @@
#include "test-lib.h"
const int default_mask = (DI_SYSFS|DI_BLACKLIST|DI_WWID|DI_CHECKER|DI_PRIO);
-const char default_devnode[] = "sdTEST";
+const char default_devnode[] = "sdxTEST";
const char default_wwid[] = "TEST-WWID";
/* default_wwid should be a substring of default_wwid_1! */
const char default_wwid_1[] = "TEST-WWID-1";
--
2.17.2

View File

@ -1,322 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 23 Jun 2020 22:17:31 -0500
Subject: [PATCH] libmultipath: make dm_get_map/status return codes symbolic
dm_get_map() and dm_get_status() now use symbolic return codes. They
also differentiate between failing to get information from device-mapper
and not finding the requested device. These symboilc return codes are
also used by update_multipath_* functions.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/devmapper.c | 51 +++++++++++++++++++++++++-------------
libmultipath/devmapper.h | 6 +++++
libmultipath/structs_vec.c | 45 +++++++++++++++++++--------------
multipathd/main.c | 12 ++++-----
4 files changed, 72 insertions(+), 42 deletions(-)
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index 13a1cf53..f6204e5f 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -525,36 +525,43 @@ int dm_map_present(const char * str)
int dm_get_map(const char *name, unsigned long long *size, char *outparams)
{
- int r = 1;
+ int r = DMP_ERR;
struct dm_task *dmt;
uint64_t start, length;
char *target_type = NULL;
char *params = NULL;
if (!(dmt = libmp_dm_task_create(DM_DEVICE_TABLE)))
- return 1;
+ return r;
if (!dm_task_set_name(dmt, name))
goto out;
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ errno = 0;
+ if (!dm_task_run(dmt)) {
+ if (dm_task_get_errno(dmt) == ENXIO)
+ r = DMP_NOT_FOUND;
goto out;
+ }
+ r = DMP_NOT_FOUND;
/* Fetch 1st target */
- dm_get_next_target(dmt, NULL, &start, &length,
- &target_type, &params);
+ if (dm_get_next_target(dmt, NULL, &start, &length,
+ &target_type, &params) != NULL)
+ /* more than one target */
+ goto out;
if (size)
*size = length;
if (!outparams) {
- r = 0;
+ r = DMP_OK;
goto out;
}
if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE)
- r = 0;
+ r = DMP_OK;
out:
dm_task_destroy(dmt);
return r;
@@ -628,35 +635,45 @@ is_mpath_part(const char *part_name, const char *map_name)
int dm_get_status(const char *name, char *outstatus)
{
- int r = 1;
+ int r = DMP_ERR;
struct dm_task *dmt;
uint64_t start, length;
char *target_type = NULL;
char *status = NULL;
if (!(dmt = libmp_dm_task_create(DM_DEVICE_STATUS)))
- return 1;
+ return r;
if (!dm_task_set_name(dmt, name))
goto out;
dm_task_no_open_count(dmt);
- if (!dm_task_run(dmt))
+ errno = 0;
+ if (!dm_task_run(dmt)) {
+ if (dm_task_get_errno(dmt) == ENXIO)
+ r = DMP_NOT_FOUND;
goto out;
+ }
+ r = DMP_NOT_FOUND;
/* Fetch 1st target */
- dm_get_next_target(dmt, NULL, &start, &length,
- &target_type, &status);
+ if (dm_get_next_target(dmt, NULL, &start, &length,
+ &target_type, &status) != NULL)
+ goto out;
+
+ if (!target_type || strcmp(target_type, TGT_MPATH) != 0)
+ goto out;
+
if (!status) {
condlog(2, "get null status.");
goto out;
}
if (snprintf(outstatus, PARAMS_SIZE, "%s", status) <= PARAMS_SIZE)
- r = 0;
+ r = DMP_OK;
out:
- if (r)
+ if (r != DMP_OK)
condlog(0, "%s: error getting map status string", name);
dm_task_destroy(dmt);
@@ -866,7 +883,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove,
return 1;
if (need_suspend &&
- !dm_get_map(mapname, &mapsize, params) &&
+ dm_get_map(mapname, &mapsize, params) == DMP_OK &&
strstr(params, "queue_if_no_path")) {
if (!dm_queue_if_no_path(mapname, 0))
queue_if_no_path = 1;
@@ -1075,7 +1092,7 @@ struct multipath *dm_get_multipath(const char *name)
if (!mpp->alias)
goto out;
- if (dm_get_map(name, &mpp->size, NULL))
+ if (dm_get_map(name, &mpp->size, NULL) != DMP_OK)
goto out;
dm_get_uuid(name, mpp->wwid, WWID_SIZE);
@@ -1259,7 +1276,7 @@ do_foreach_partmaps (const char * mapname,
/*
* and we can fetch the map table from the kernel
*/
- !dm_get_map(names->name, &size, &params[0]) &&
+ dm_get_map(names->name, &size, &params[0]) == DMP_OK &&
/*
* and the table maps over the multipath map
diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h
index 7557a86b..adb55000 100644
--- a/libmultipath/devmapper.h
+++ b/libmultipath/devmapper.h
@@ -27,6 +27,12 @@
#define UUID_PREFIX "mpath-"
#define UUID_PREFIX_LEN (sizeof(UUID_PREFIX) - 1)
+enum {
+ DMP_ERR,
+ DMP_OK,
+ DMP_NOT_FOUND,
+};
+
void dm_init(int verbosity);
void libmp_dm_init(void);
void libmp_udev_set_sync_support(int on);
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 077f2e42..8137ea21 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -196,43 +196,47 @@ extract_hwe_from_path(struct multipath * mpp)
int
update_multipath_table (struct multipath *mpp, vector pathvec, int is_daemon)
{
+ int r = DMP_ERR;
char params[PARAMS_SIZE] = {0};
if (!mpp)
- return 1;
+ return r;
- if (dm_get_map(mpp->alias, &mpp->size, params)) {
- condlog(3, "%s: cannot get map", mpp->alias);
- return 1;
+ r = dm_get_map(mpp->alias, &mpp->size, params);
+ if (r != DMP_OK) {
+ condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting table" : "map not present");
+ return r;
}
if (disassemble_map(pathvec, params, mpp, is_daemon)) {
condlog(3, "%s: cannot disassemble map", mpp->alias);
- return 1;
+ return DMP_ERR;
}
- return 0;
+ return DMP_OK;
}
int
update_multipath_status (struct multipath *mpp)
{
+ int r = DMP_ERR;
char status[PARAMS_SIZE] = {0};
if (!mpp)
- return 1;
+ return r;
- if (dm_get_status(mpp->alias, status)) {
- condlog(3, "%s: cannot get status", mpp->alias);
- return 1;
+ r = dm_get_status(mpp->alias, status);
+ if (r != DMP_OK) {
+ condlog(3, "%s: %s", mpp->alias, (r == DMP_ERR)? "error getting status" : "map not present");
+ return r;
}
if (disassemble_status(status, mpp)) {
condlog(3, "%s: cannot disassemble status", mpp->alias);
- return 1;
+ return DMP_ERR;
}
- return 0;
+ return DMP_OK;
}
void sync_paths(struct multipath *mpp, vector pathvec)
@@ -264,10 +268,10 @@ int
update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
{
struct pathgroup *pgp;
- int i;
+ int i, r = DMP_ERR;
if (!mpp)
- return 1;
+ return r;
update_mpp_paths(mpp, pathvec);
condlog(4, "%s: %s", mpp->alias, __FUNCTION__);
@@ -276,18 +280,21 @@ update_multipath_strings(struct multipath *mpp, vector pathvec, int is_daemon)
free_pgvec(mpp->pg, KEEP_PATHS);
mpp->pg = NULL;
- if (update_multipath_table(mpp, pathvec, is_daemon))
- return 1;
+ r = update_multipath_table(mpp, pathvec, is_daemon);
+ if (r != DMP_OK)
+ return r;
+
sync_paths(mpp, pathvec);
- if (update_multipath_status(mpp))
- return 1;
+ r = update_multipath_status(mpp);
+ if (r != DMP_OK)
+ return r;
vector_foreach_slot(mpp->pg, pgp, i)
if (pgp->paths)
path_group_prio_update(pgp);
- return 0;
+ return DMP_OK;
}
static void enter_recovery_mode(struct multipath *mpp)
diff --git a/multipathd/main.c b/multipathd/main.c
index 6b7db2c0..e3427d3d 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -414,7 +414,7 @@ int __setup_multipath(struct vectors *vecs, struct multipath *mpp,
goto out;
}
- if (update_multipath_strings(mpp, vecs->pathvec, 1)) {
+ if (update_multipath_strings(mpp, vecs->pathvec, 1) != DMP_OK) {
condlog(0, "%s: failed to setup multipath", mpp->alias);
goto out;
}
@@ -553,9 +553,9 @@ add_map_without_path (struct vectors *vecs, const char *alias)
mpp->mpe = find_mpe(conf->mptable, mpp->wwid);
put_multipath_config(conf);
- if (update_multipath_table(mpp, vecs->pathvec, 1))
+ if (update_multipath_table(mpp, vecs->pathvec, 1) != DMP_OK)
goto out;
- if (update_multipath_status(mpp))
+ if (update_multipath_status(mpp) != DMP_OK)
goto out;
if (!vector_alloc_slot(vecs->mpvec))
@@ -1346,8 +1346,8 @@ map_discovery (struct vectors * vecs)
return 1;
vector_foreach_slot (vecs->mpvec, mpp, i)
- if (update_multipath_table(mpp, vecs->pathvec, 1) ||
- update_multipath_status(mpp)) {
+ if (update_multipath_table(mpp, vecs->pathvec, 1) != DMP_OK ||
+ update_multipath_status(mpp) != DMP_OK) {
remove_map(mpp, vecs, 1);
i--;
}
@@ -2087,7 +2087,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
/*
* Synchronize with kernel state
*/
- if (update_multipath_strings(pp->mpp, vecs->pathvec, 1)) {
+ if (update_multipath_strings(pp->mpp, vecs->pathvec, 1) != DMP_OK) {
condlog(1, "%s: Could not synchronize with kernel state",
pp->dev);
pp->dmstate = PSTATE_UNDEF;
--
2.17.2

View File

@ -1,116 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 11 Jun 2020 15:41:18 -0500
Subject: [PATCH] multipathd: fix check_path errors with removed map
If a multipath device is removed during, or immediately before the call
to check_path(), multipathd can behave incorrectly. A missing multpath
device will cause update_multipath_strings() to fail, setting
pp->dmstate to PSTATE_UNDEF. If the path is up, this state will cause
reinstate_path() to be called, which will also fail. This will trigger
a reload, restoring the recently removed device.
If update_multipath_strings() fails because there is no multipath
device, check_path should just quit, since the remove dmevent and uevent
are likely already queued up. Also, I don't see any reason to reload the
multipath device if reinstate fails. This code was added by
fac68d7a99ef17d496079538a5c6836acd7911ab, which clamined that reinstate
could fail if the path was disabled. Looking through the current kernel
code, I can't see any reason why a reinstate would fail, where a reload
would help. If the path was missing from the multipath device,
update_multipath_strings() would already catch that, and quit
check_path() early, which make more sense to me than reloading does.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/main.c | 44 +++++++++++++++++++-------------------------
1 file changed, 19 insertions(+), 25 deletions(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index e3427d3d..1d9ce7f7 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -1611,22 +1611,18 @@ fail_path (struct path * pp, int del_active)
/*
* caller must have locked the path list before calling that function
*/
-static int
+static void
reinstate_path (struct path * pp)
{
- int ret = 0;
-
if (!pp->mpp)
- return 0;
+ return;
- if (dm_reinstate_path(pp->mpp->alias, pp->dev_t)) {
+ if (dm_reinstate_path(pp->mpp->alias, pp->dev_t))
condlog(0, "%s: reinstate failed", pp->dev_t);
- ret = 1;
- } else {
+ else {
condlog(2, "%s: reinstated", pp->dev_t);
update_queue_mode_add_path(pp->mpp);
}
- return ret;
}
static void
@@ -2087,9 +2083,16 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
/*
* Synchronize with kernel state
*/
- if (update_multipath_strings(pp->mpp, vecs->pathvec, 1) != DMP_OK) {
- condlog(1, "%s: Could not synchronize with kernel state",
- pp->dev);
+ ret = update_multipath_strings(pp->mpp, vecs->pathvec, 1);
+ if (ret != DMP_OK) {
+ if (ret == DMP_NOT_FOUND) {
+ /* multipath device missing. Likely removed */
+ condlog(1, "%s: multipath device '%s' not found",
+ pp->dev, pp->mpp->alias);
+ return 0;
+ } else
+ condlog(1, "%s: Couldn't synchronize with kernel state",
+ pp->dev);
pp->dmstate = PSTATE_UNDEF;
}
/* if update_multipath_strings orphaned the path, quit early */
@@ -2179,12 +2182,8 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
/*
* reinstate this path
*/
- if (!disable_reinstate && reinstate_path(pp)) {
- condlog(3, "%s: reload map", pp->dev);
- ev_add_path(pp, vecs, 1);
- pp->tick = 1;
- return 0;
- }
+ if (!disable_reinstate)
+ reinstate_path(pp);
new_path_up = 1;
if (oldchkrstate != PATH_UP && oldchkrstate != PATH_GHOST)
@@ -2200,15 +2199,10 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
else if (newstate == PATH_UP || newstate == PATH_GHOST) {
if ((pp->dmstate == PSTATE_FAILED ||
pp->dmstate == PSTATE_UNDEF) &&
- !disable_reinstate) {
+ !disable_reinstate)
/* Clear IO errors */
- if (reinstate_path(pp)) {
- condlog(3, "%s: reload map", pp->dev);
- ev_add_path(pp, vecs, 1);
- pp->tick = 1;
- return 0;
- }
- } else {
+ reinstate_path(pp);
+ else {
LOG_MSG(4, verbosity, pp);
if (pp->checkint != max_checkint) {
/*
--
2.17.2

View File

@ -1,45 +0,0 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 17 Jun 2020 13:31:37 -0500
Subject: [PATCH] libmultipath: make dm_flush_maps only return 0 on success
dm_flush_maps() returned both 0 and 1 on error, depending on which part
of the function it was in, but the caller was always treating 0 as a
success. Make dm_flush_maps() always return 1 on error and 0 on success.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/devmapper.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index f6204e5f..cda83ce4 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -953,13 +953,13 @@ dm_flush_map_nopaths(const char * mapname, int deferred_remove)
int dm_flush_maps (int retries)
{
- int r = 0;
+ int r = 1;
struct dm_task *dmt;
struct dm_names *names;
unsigned next = 0;
if (!(dmt = libmp_dm_task_create (DM_DEVICE_LIST)))
- return 0;
+ return r;
dm_task_no_open_count(dmt);
@@ -972,6 +972,7 @@ int dm_flush_maps (int retries)
if (!names->dev)
goto out;
+ r = 0;
do {
r |= dm_suspend_and_flush_map(names->name, retries);
next = names->next;
--
2.17.2

Some files were not shown because too many files have changed in this diff Show More