commit f7a85d25bf0624e3979ca5180a03de3cee8f420d Author: CentOS Sources Date: Tue May 16 06:22:15 2023 +0000 import device-mapper-multipath-0.8.4-37.el8 diff --git a/.device-mapper-multipath.metadata b/.device-mapper-multipath.metadata new file mode 100644 index 0000000..6f07c28 --- /dev/null +++ b/.device-mapper-multipath.metadata @@ -0,0 +1 @@ +b52c2be340449664f0a122070838f6d8edd42e4a SOURCES/multipath-tools-0.8.4.tgz diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..044d015 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +SOURCES/multipath-tools-0.8.4.tgz diff --git a/SOURCES/0001-libmultipath-assign-variable-to-make-gcc-happy.patch b/SOURCES/0001-libmultipath-assign-variable-to-make-gcc-happy.patch new file mode 100644 index 0000000..06de678 --- /dev/null +++ b/SOURCES/0001-libmultipath-assign-variable-to-make-gcc-happy.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +Signed-off-by: Benjamin Marzinski +--- + 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 + diff --git a/SOURCES/0002-libmutipath-don-t-close-fd-on-dm_lib_release.patch b/SOURCES/0002-libmutipath-don-t-close-fd-on-dm_lib_release.patch new file mode 100644 index 0000000..9a1ce41 --- /dev/null +++ b/SOURCES/0002-libmutipath-don-t-close-fd-on-dm_lib_release.patch @@ -0,0 +1,66 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 + diff --git a/SOURCES/0003-libmultipath-allow-force-reload-with-no-active-paths.patch b/SOURCES/0003-libmultipath-allow-force-reload-with-no-active-paths.patch new file mode 100644 index 0000000..a6efaaa --- /dev/null +++ b/SOURCES/0003-libmultipath-allow-force-reload-with-no-active-paths.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +Signed-off-by: Benjamin Marzinski +--- + 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 + diff --git a/SOURCES/0004-libmpathpersist-depend-on-libmultipath.patch b/SOURCES/0004-libmpathpersist-depend-on-libmultipath.patch new file mode 100644 index 0000000..47aad9b --- /dev/null +++ b/SOURCES/0004-libmpathpersist-depend-on-libmultipath.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Christian Hesse +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 +Signed-off-by: Benjamin Marzinski +--- + 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 + diff --git a/SOURCES/0005-multipath-tools-Makefile-more-dependency-fixes-for-p.patch b/SOURCES/0005-multipath-tools-Makefile-more-dependency-fixes-for-p.patch new file mode 100644 index 0000000..c75ba30 --- /dev/null +++ b/SOURCES/0005-multipath-tools-Makefile-more-dependency-fixes-for-p.patch @@ -0,0 +1,34 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +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 +Signed-off-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + 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 + diff --git a/SOURCES/0006-multipath-tools-Makefile.inc-set-Wno-error-clobbered.patch b/SOURCES/0006-multipath-tools-Makefile.inc-set-Wno-error-clobbered.patch new file mode 100644 index 0000000..1ad917f --- /dev/null +++ b/SOURCES/0006-multipath-tools-Makefile.inc-set-Wno-error-clobbered.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +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 +Signed-off-by: Benjamin Marzinski +--- + 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 + diff --git a/SOURCES/0007-libmultipath-discovery.c-use-z-qualifier-for-size_t.patch b/SOURCES/0007-libmultipath-discovery.c-use-z-qualifier-for-size_t.patch new file mode 100644 index 0000000..b24cca6 --- /dev/null +++ b/SOURCES/0007-libmultipath-discovery.c-use-z-qualifier-for-size_t.patch @@ -0,0 +1,91 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +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 +Signed-off-by: Benjamin Marzinski +--- + 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 + diff --git a/SOURCES/0008-libmultipath-eliminate-more-signed-unsigned-comparis.patch b/SOURCES/0008-libmultipath-eliminate-more-signed-unsigned-comparis.patch new file mode 100644 index 0000000..ac5b284 --- /dev/null +++ b/SOURCES/0008-libmultipath-eliminate-more-signed-unsigned-comparis.patch @@ -0,0 +1,185 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +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 +Signed-off-by: Benjamin Marzinski +--- + 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 + diff --git a/SOURCES/0009-libmultipath-set_uint-fix-parsing-for-32bit.patch b/SOURCES/0009-libmultipath-set_uint-fix-parsing-for-32bit.patch new file mode 100644 index 0000000..25f83c3 --- /dev/null +++ b/SOURCES/0009-libmultipath-set_uint-fix-parsing-for-32bit.patch @@ -0,0 +1,49 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +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 +Signed-off-by: Benjamin Marzinski +--- + 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 + diff --git a/SOURCES/0010-multipath-tools-Makefile-add-install-dependency.patch b/SOURCES/0010-multipath-tools-Makefile-add-install-dependency.patch new file mode 100644 index 0000000..ee1da02 --- /dev/null +++ b/SOURCES/0010-multipath-tools-Makefile-add-install-dependency.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +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 +Signed-off-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + 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 + diff --git a/SOURCES/0011-RH-fixup-udev-rules-for-redhat.patch b/SOURCES/0011-RH-fixup-udev-rules-for-redhat.patch new file mode 100644 index 0000000..1a8e108 --- /dev/null +++ b/SOURCES/0011-RH-fixup-udev-rules-for-redhat.patch @@ -0,0 +1,66 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 + diff --git a/SOURCES/0012-RH-Remove-the-property-blacklist-exception-builtin.patch b/SOURCES/0012-RH-Remove-the-property-blacklist-exception-builtin.patch new file mode 100644 index 0000000..10cc1b3 --- /dev/null +++ b/SOURCES/0012-RH-Remove-the-property-blacklist-exception-builtin.patch @@ -0,0 +1,105 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 2 Jul 2014 12:49:53 -0500 +Subject: [PATCH] RH: Remove the property blacklist exception builtin + +Multipath set the default property blacklist exceptions to +(ID_SCSI_VPD|ID_WWN). This has the effect of blacklisting some internal +devices. These devices may never have multiple paths, but it is nice +to be able to set multipath up on them all the same. This patch simply +removes the default, and makes it so that if no property +blacklist_exception is given, then devices aren't failed for not matching +it. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/blacklist.c | 9 ++------- + multipath/multipath.conf.5 | 11 ++++++----- + tests/blacklist.c | 6 ++---- + 3 files changed, 10 insertions(+), 16 deletions(-) + +diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c +index 00e8dbdb..d9691b17 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; + +- 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, + *uid_attribute != '\0'; + bool uid_attr_seen = false; + +- r = MATCH_PROPERTY_BLIST_MISSING; ++ if (VECTOR_SIZE(conf->elist_property)) ++ r = MATCH_PROPERTY_BLIST_MISSING; + 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 + Regular expression for an udev property. All + devices that have matching udev properties will be excluded/included. + The handling of the \fIproperty\fR keyword is special, +-because devices \fBmust\fR have at least one whitelisted udev property; ++because if a property blacklist_exception is set, devices \fBmust\fR have at ++least one whitelisted udev property; + otherwise they're treated as blacklisted, and the message + "\fIblacklisted, udev property missing\fR" is displayed in the logs. ++For example, setting the property blacklist_exception to ++\fB(SCSI_IDENT_|ID_WWN)\fR, will cause well-behaved SCSI devices and devices ++that provide a WWN (World Wide Number) to be included, and all others to be ++excluded. This works to exclude most non-multipathable devices. + . + .RS + .PP +@@ -1299,10 +1304,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. +-.PP +-The default \fIblacklist exception\fR is: \fB(SCSI_IDENT_|ID_WWN)\fR, causing +-well-behaved SCSI devices and devices that provide a WWN (World Wide Number) +-to be included, and all others to be excluded. + .RE + .TP + .B protocol +diff --git a/tests/blacklist.c b/tests/blacklist.c +index 6e7c1864..cc8a9a4a 100644 +--- a/tests/blacklist.c ++++ b/tests/blacklist.c +@@ -271,7 +271,7 @@ static void test_property_missing(void **state) + conf.blist_property = blist_property_wwn; + 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) + conf.blist_device = blist_device_foo_bar; + conf.blist_protocol = blist_protocol_fcp; + conf.blist_wwid = blist_wwid_xyzzy; +- expect_condlog(3, "sdb: blacklisted, udev property missing\n"); +- assert_int_equal(filter_path(&conf, &miss1_pp), +- MATCH_PROPERTY_BLIST_MISSING); ++ assert_int_equal(filter_path(&conf, &miss1_pp), MATCH_NOTHING); + } + + /* This one matches the property whitelist, to test the other missing +-- +2.17.2 + diff --git a/SOURCES/0013-RH-don-t-start-without-a-config-file.patch b/SOURCES/0013-RH-don-t-start-without-a-config-file.patch new file mode 100644 index 0000000..86cdf68 --- /dev/null +++ b/SOURCES/0013-RH-don-t-start-without-a-config-file.patch @@ -0,0 +1,106 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 + diff --git a/SOURCES/0014-RH-use-rpm-optflags-if-present.patch b/SOURCES/0014-RH-use-rpm-optflags-if-present.patch new file mode 100644 index 0000000..be1af2e --- /dev/null +++ b/SOURCES/0014-RH-use-rpm-optflags-if-present.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 + diff --git a/SOURCES/0015-RH-add-mpathconf.patch b/SOURCES/0015-RH-add-mpathconf.patch new file mode 100644 index 0000000..009a033 --- /dev/null +++ b/SOURCES/0015-RH-add-mpathconf.patch @@ -0,0 +1,774 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 16 Oct 2014 15:49:01 -0500 +Subject: [PATCH] RH: add mpathconf + +mpathconf is a program (largely based on lvmcomf) to help users +configure /etc/multipath.conf and enable or disable multipathing. It +has a couple of built-in options that can be set directly from the +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. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/config.c | 2 + + multipath/Makefile | 5 + + multipath/mpathconf | 555 ++++++++++++++++++++++++++++++++++++++++++ + multipath/mpathconf.8 | 135 ++++++++++ + 4 files changed, 697 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 +--- a/libmultipath/config.c ++++ b/libmultipath/config.c +@@ -781,6 +781,8 @@ load_config (char * file) + factorize_hwtable(conf->hwtable, builtin_hwtable_size, file); + } else { + condlog(0, "/etc/multipath.conf does not exist, blacklisting all devices."); ++ condlog(0, "You can run \"/sbin/mpathconf --enable\" to create"); ++ condlog(0, "/etc/multipath.conf. See man mpathconf(8) for more details"); + if (conf->blist_devnode == NULL) { + conf->blist_devnode = vector_alloc(); + if (!conf->blist_devnode) { +diff --git a/multipath/Makefile b/multipath/Makefile +index b9bbb3cf..e720c7f6 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 + + 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) + + 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 + + clean: dep_clean + $(RM) core *.o $(EXEC) *.gz +diff --git a/multipath/mpathconf b/multipath/mpathconf +new file mode 100644 +index 00000000..f34003c9 +--- /dev/null ++++ b/multipath/mpathconf +@@ -0,0 +1,555 @@ ++#!/bin/bash ++# ++# Copyright (C) 2010 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 ++ ++# ++# Simple editting of /etc/multipath.conf ++# 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 ++ ++DEFAULT_CONFIG="# device-mapper-multipath configuration file ++ ++# For a complete list of the default configuration values, run either: ++# # multipath -t ++# or ++# # multipathd show config ++ ++# For a list of configuration options with descriptions, see the ++# multipath.conf man page. ++ ++defaults { ++ user_friendly_names yes ++ find_multipaths yes ++ enable_foreign \"^$\" ++} ++ ++blacklist_exceptions { ++ property \"(SCSI_IDENT_|ID_WWN)\" ++}" ++ ++CONFIGFILE="/etc/multipath.conf" ++OUTPUTFILE="/etc/multipath.conf" ++MULTIPATHDIR="/etc/multipath" ++TMPFILE="/etc/multipath/.multipath.conf.tmp" ++WWIDS=0 ++ ++function usage ++{ ++ echo "usage: $0 " ++ echo "" ++ echo "Commands:" ++ echo "Enable: --enable " ++ echo "Disable: --disable" ++ echo "Only allow certain wwids (instead of enable): --allow " ++ echo "Set user_friendly_names (Default y): --user_friendly_names " ++ echo "Set find_multipaths (Default y): --find_multipaths " ++ echo "Set default property blacklist (Default y): --property_blacklist " ++ echo "Set enable_foreign to show foreign devices (Default n): --enable_foreign " ++ echo "Load the dm-multipath modules on enable (Default y): --with_module " ++ echo "start/stop/reload multipathd (Default n): --with_multipathd " ++ echo "select output file (Default /etc/multipath.conf): --outfile " ++ echo "" ++} ++ ++function add_wwid ++{ ++ INDEX=0 ++ while [ "$INDEX" -lt "$WWIDS" ] ; do ++ if [ "$1" = "${WWID_LIST[$INDEX]}" ] ; then ++ return ++ fi ++ ((INDEX++)) ++ done ++ WWID_LIST[$WWIDS]="$1" ++ ((WWIDS++)) ++} ++ ++function get_dm_deps ++{ ++ shift 3 ++ while [ -n "$1" -a -n "$2" ]; do ++ MAJOR=$(echo $1 | tr -d '(,') ++ MINOR=$(echo $2 | tr -d ')') ++ UUID=`dmsetup info -c --noheadings -o uuid -j $MAJOR -m $MINOR 2> /dev/null` ++ if [ -n "$UUID" ] ; then ++ set_dm_wwid $UUID ++ fi ++ shift 2 ++ done ++} ++ ++function set_dm_wwid ++{ ++ if [[ "$1" =~ ^part[[:digit:]]+-mpath- ]] ; then ++ add_wwid "${1##part*-mpath-}" ++ elif [[ "$1" =~ ^mpath- ]] ; then ++ add_wwid "${1##mpath-}" ++ else ++ get_dm_deps `dmsetup deps -u $1` ++ fi ++} ++ ++function set_wwid ++{ ++ UUID="" ++ if [[ "$1" =~ ^[[:digit:]]+:[[:digit:]]+$ ]] ; then ++ MAJOR=${1%%:*} ++ MINOR=${1##*:} ++ UUID=`dmsetup info -c --noheadings -o uuid -j $MAJOR -m $MINOR 2> /dev/null` ++ else ++ UUID=`dmsetup info -c --noheadings -o uuid $1 2> /dev/null` ++ fi ++ if [ -n "$UUID" ] ; then ++ set_dm_wwid $UUID ++ else ++ add_wwid "$1" ++ fi ++} ++ ++function parse_args ++{ ++ while [ -n "$1" ]; do ++ case $1 in ++ --enable) ++ ENABLE=1 ++ shift ++ ;; ++ --disable) ++ ENABLE=0 ++ shift ++ ;; ++ --allow) ++ ENABLE=2 ++ if [ -n "$2" ]; then ++ set_wwid $2 ++ shift 2 ++ else ++ usage ++ exit 1 ++ fi ++ ;; ++ --user_friendly_names) ++ if [ -n "$2" ]; then ++ FRIENDLY=$2 ++ shift 2 ++ else ++ usage ++ exit 1 ++ fi ++ ;; ++ --find_multipaths) ++ if [ -n "$2" ]; then ++ FIND=$2 ++ shift 2 ++ else ++ usage ++ exit 1 ++ fi ++ ;; ++ --property_blacklist) ++ if [ -n "$2" ]; then ++ PROPERTY=$2 ++ shift 2 ++ else ++ usage ++ exit 1 ++ fi ++ ;; ++ --enable_foreign) ++ if [ -n "$2" ]; then ++ FOREIGN=$2 ++ shift 2 ++ else ++ usage ++ exit 1 ++ fi ++ ;; ++ --with_module) ++ if [ -n "$2" ]; then ++ MODULE=$2 ++ shift 2 ++ else ++ usage ++ exit 1 ++ fi ++ ;; ++ --with_multipathd) ++ if [ -n "$2" ]; then ++ MULTIPATHD=$2 ++ shift 2 ++ else ++ usage ++ exit 1 ++ fi ++ ;; ++ --outfile) ++ if [ -n "$2" ]; then ++ OUTPUTFILE=$2 ++ HAVE_OUTFILE=1 ++ shift 2 ++ else ++ usage ++ exit 1 ++ fi ++ ;; ++ *) ++ usage ++ exit ++ esac ++ done ++} ++ ++function validate_args ++{ ++ if [ "$ENABLE" = "0" ] && [ -n "$FRIENDLY" -o -n "$FIND" -o -n "$PROPERTY" -o -n "$MODULE" ]; then ++ echo "ignoring extra parameters on disable" ++ FRIENDLY="" ++ FIND="" ++ PROPERTY="" ++ MODULE="" ++ 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'" ++ exit 1 ++ fi ++ if [ -n "$PROPERTY" ] && [ "$PROPERTY" != "y" -a "$PROPERTY" != "n" ]; then ++ echo "--property_blacklist must be either 'y' or 'n'" ++ exit 1 ++ fi ++ if [ -n "$FOREIGN" ] && [ "$FOREIGN" != "y" -a "$FOREIGN" != "n" ]; then ++ 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 ++ SHOW_STATUS=1 ++ fi ++ if [ -n "$MODULE" ] && [ "$MODULE" != "y" -a "$MODULE" != "n" ]; then ++ echo "--with_module must be either 'y' or 'n'" ++ exit 1 ++ fi ++ if [ -n "$MULTIPATHD" ] && [ "$MULTIPATHD" != "y" -a "$MULTIPATHD" != "n" ]; then ++ echo "--with_multipathd must be either 'y' or 'n'" ++ exit 1 ++ fi ++ if [ "$ENABLE" = 2 -a -z "$HAVE_OUTFILE" ]; then ++ echo "Because --allow makes changes that cannot be automatically reversed," ++ echo "you must set --outfile when you set --allow" ++ exit 1 ++ fi ++} ++ ++function add_blacklist_exceptions ++{ ++ INDEX=0 ++ while [ "$INDEX" -lt "$WWIDS" ] ; do ++ sed -i '/^blacklist_exceptions[[:space:]]*{/ a\ ++ wwid '"\"${WWID_LIST[$INDEX]}\""' ++' $TMPFILE ++ ((INDEX++)) ++ done ++} ++ ++umask 0077 ++ ++parse_args "$@" ++ ++validate_args ++ ++if [ ! -d "$MULTIPATHDIR" ]; then ++ echo "/etc/multipath/ does not exist. failing" ++ exit 1 ++fi ++ ++rm $TMPFILE 2> /dev/null ++echo "$DEFAULT_CONFIG" > $TMPFILE ++if [ -f "$CONFIGFILE" ]; then ++ cp $CONFIGFILE $TMPFILE ++fi ++ ++if grep -q "^blacklist[[:space:]]*{" $TMPFILE ; then ++ HAVE_BLACKLIST=1 ++fi ++ ++if grep -q "^blacklist_exceptions[[:space:]]*{" $TMPFILE ; then ++ HAVE_EXCEPTIONS=1 ++fi ++ ++if grep -q "^defaults[[:space:]]*{" $TMPFILE ; then ++ HAVE_DEFAULTS=1 ++fi ++ ++if [ -z "$MODULE" -o "$MODULE" = "y" ]; then ++ if lsmod | grep -q "dm_multipath" ; then ++ HAVE_MODULE=1 ++ else ++ HAVE_MODULE=0 ++ fi ++fi ++ ++if [ "$MULTIPATHD" = "y" ]; then ++ if /bin/systemctl status multipathd.service > /dev/null 2>&1 ; then ++ HAVE_MULTIPATHD=1 ++ else ++ HAVE_MULTIPATHD=0 ++ fi ++fi ++ ++if [ "$HAVE_BLACKLIST" = "1" ]; then ++ if sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*devnode \"\.\?\*\"" ; then ++ HAVE_DISABLE=1 ++ elif sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*#[[:space:]]*devnode \"\.\?\*\"" ; then ++ HAVE_DISABLE=0 ++ fi ++fi ++ ++if [ "$HAVE_BLACKLIST" = "1" ]; then ++ if sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*wwid \"\.\?\*\"" ; then ++ HAVE_WWID_DISABLE=1 ++ elif sed -n '/^blacklist[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*#[[:space:]]*wwid \"\.\?\*\"" ; 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 ++ fi ++ if sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*user_friendly_names[[:space:]]*\(yes\|1\)" ; then ++ HAVE_FRIENDLY=1 ++ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*user_friendly_names[[:space:]]*\(no\|0\)" ; then ++ HAVE_FRIENDLY=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 ++ HAVE_FOREIGN=1 ++ elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*enable_foreign" ; then ++ HAVE_FOREIGN=2 ++ 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 ++ HAVE_PROPERTY=1 ++ elif sed -n '/^blacklist_exceptions[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*#[[:space:]]*property[[:space:]]*\"(SCSI_IDENT_|ID_WWN)\"" ; then ++ HAVE_PROPERTY=0 ++ fi ++fi ++ ++if [ -n "$SHOW_STATUS" ]; then ++ if [ -z "$HAVE_DISABLE" -o "$HAVE_DISABLE" = 0 ]; then ++ echo "multipath is enabled" ++ else ++ echo "multipath is disabled" ++ fi ++ if [ -z "$HAVE_FIND" -o "$HAVE_FIND" = 0 ]; then ++ echo "find_multipaths is disabled" ++ else ++ echo "find_multipaths is enabled" ++ 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_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)" ++ elif [ "$HAVE_FOREIGN" = 1 ]; 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)" ++ fi ++ if [ -n "$HAVE_MODULE" ]; then ++ if [ "$HAVE_MODULE" = 1 ]; then ++ echo "dm_multipath module is loaded" ++ else ++ echo "dm_multipath module is not loaded" ++ fi ++ fi ++ if [ -z "$HAVE_MULTIPATHD" ]; then ++ if /bin/systemctl status multipathd.service > /dev/null 2>&1 ; then ++ HAVE_MULTIPATHD=1 ++ else ++ HAVE_MULTIPATHD=0 ++ fi ++ fi ++ if [ "$HAVE_MULTIPATHD" = 1 ]; then ++ echo "multipathd is running" ++ else ++ echo "multipathd is not running" ++ fi ++ exit 0 ++fi ++ ++if [ -z "$HAVE_BLACKLIST" ]; then ++ cat >> $TMPFILE <<- _EOF_ ++ ++blacklist { ++} ++_EOF_ ++fi ++ ++if [ -z "$HAVE_DEFAULTS" ]; then ++ cat >> $TMPFILE <<- _EOF_ ++ ++defaults { ++} ++_EOF_ ++fi ++ ++if [ "$ENABLE" = 2 ]; then ++ if [ "$HAVE_DISABLE" = 1 ]; then ++ sed -i '/^blacklist[[:space:]]*{/,/^}/ s/^[[:space:]]*devnode \"\.\?\*\"/# 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 ++ fi ++ if [ "$HAVE_EXCEPTIONS" = 1 ]; then ++ sed -i '/^blacklist_exceptions[[:space:]]*{/,/^}/ {/^[[:space:]]*wwid/ d}' $TMPFILE ++ else ++ cat >> $TMPFILE <<- _EOF_ ++ ++blacklist_exceptions { ++} ++_EOF_ ++ fi ++ add_blacklist_exceptions ++elif [ "$ENABLE" = 1 ]; then ++ if [ "$HAVE_DISABLE" = 1 ]; then ++ sed -i '/^blacklist[[:space:]]*{/,/^}/ s/^[[:space:]]*devnode \"\.\?\*\"/# devnode ".*"/' $TMPFILE ++ fi ++elif [ "$ENABLE" = 0 ]; then ++ if [ -z "$HAVE_DISABLE" ]; then ++ sed -i '/^blacklist[[:space:]]*{/ a\ ++ devnode ".*" ++' $TMPFILE ++ elif [ "$HAVE_DISABLE" = 0 ]; then ++ sed -i '/^blacklist[[:space:]]*{/,/^}/ s/^[[:space:]]*#[[:space:]]*devnode \"\.\?\*\"/ 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 [ -z "$HAVE_FIND" ]; then ++ sed -i '/^defaults[[:space:]]*{/ a\ ++ find_multipaths yes ++' $TMPFILE ++ CHANGED_CONFIG=1 ++ elif [ "$HAVE_FIND" = 0 ]; then ++ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*find_multipaths[[:space:]]*\(no\|0\)/ find_multipaths yes/' $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 ++ CHANGED_CONFIG=1 ++ fi ++elif [ "$FRIENDLY" = "y" ]; then ++ if [ -z "$HAVE_FRIENDLY" ]; then ++ sed -i '/^defaults[[:space:]]*{/ a\ ++ user_friendly_names yes ++' $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 ++ 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 ++ CHANGED_CONFIG=1 ++ fi ++elif [ "$PROPERTY" = "y" ]; then ++ if [ -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 ++ CHANGED_CONFIG=1 ++ fi ++fi ++ ++if [ "$FOREIGN" = "y" ]; then ++ if [ "$HAVE_FOREIGN" = 1 -o "$HAVE_FOREIGN" = 2 ]; then ++ sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:space:]]*enable_foreign/# enable_foreign/' $TMPFILE ++ CHANGED_CONFIG=1 ++ fi ++elif [ "$FOREIGN" = "n" ]; then ++ if [ -z "$HAVE_FOREIGN" ]; then ++ sed -i '/^defaults[[:space:]]*{/ a\ ++ 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 ++ CHANGED_CONFIG=1 ++ fi ++fi ++ ++if [ -f "$OUTPUTFILE" ]; then ++ cp $OUTPUTFILE $OUTPUTFILE.old ++ if [ $? != 0 ]; then ++ echo "failed to backup old config file, $OUTPUTFILE not updated" ++ exit 1 ++ fi ++fi ++ ++cp $TMPFILE $OUTPUTFILE ++if [ $? != 0 ]; then ++ echo "failed to copy new config file into place, check $OUTPUTFILE is still OK" ++ exit 1 ++fi ++ ++rm -f $TMPFILE ++ ++if [ "$ENABLE" = 1 ]; then ++ if [ "$HAVE_MODULE" = 0 ]; then ++ modprobe dm_multipath ++ fi ++ if [ "$HAVE_MULTIPATHD" = 0 ]; then ++ systemctl start multipathd.service ++ fi ++elif [ "$ENABLE" = 0 ]; then ++ if [ "$HAVE_MULTIPATHD" = 1 ]; then ++ systemctl stop multipathd.service ++ fi ++elif [ -n "$CHANGED_CONFIG" -a "$HAVE_MULTIPATHD" = 1 ]; then ++ systemctl reload multipathd.service ++fi +diff --git a/multipath/mpathconf.8 b/multipath/mpathconf.8 +new file mode 100644 +index 00000000..b82961d6 +--- /dev/null ++++ b/multipath/mpathconf.8 +@@ -0,0 +1,135 @@ ++.TH MPATHCONF 8 "June 2010" "" "Linux Administrator's Manual" ++.SH NAME ++mpathconf - A tool for configuring device-mapper-multipath ++.SH SYNOPSIS ++.B mpathconf ++.RB [\| commands \|] ++.RB [\| options \|] ++.SH DESCRIPTION ++.B mpathconf ++is a utility that creates or modifies ++.B /etc/multipath.conf. ++It can enable or disable multipathing and configure some common options. ++.B mpathconf ++can also load the ++.B dm_multipath ++module, start and stop the ++.B multipathd ++daemon, and configure the ++.B multipathd ++service to start automatically or not. If ++.B mpathconf ++is called with no commands, it will display the current configuration, but ++will not create of modify ++.B /etc/multipath.conf ++ ++The default options for mpathconf are ++.B --with_module ++The ++.B --with_multipathd ++option is not set by default. Enabling multipathing will load the ++.B dm_multipath ++module but it will not immediately start it. This is so ++that users can manually edit their config file if necessary, before starting ++.B multipathd. ++ ++If ++.B /etc/multipath.conf ++already exists, mpathconf will edit it. If it does not exist, mpathconf will ++create a default file with ++.B user_friendly_names ++and ++.B find_multipaths ++set. To disable these, use the ++.B --user_friendly_names n ++and ++.B --find_multipaths n ++options ++.SH COMMANDS ++.TP ++.B --enable ++Removes any line that blacklists all device nodes from the ++.B /etc/multipath.conf ++blacklist section. Also, creates ++.B /etc/multipath.conf ++if it doesn't exist. ++.TP ++.B --disable ++Adds a line that blacklists all device nodes to the ++.B /etc/multipath.conf ++blacklist section. If no blacklist section exists, it will create one. ++.TP ++.B --allow \fB\fP ++Modifies the \fB/etc/multipath/conf\fP blacklist to blacklist all ++wwids and the blacklist_exceptions to whitelist \fB\fP. \fB\fP ++can be in the form of MAJOR:MINOR, a wwid, or the name of a device-mapper ++device, either a multipath device, or any device on stacked on top of one or ++more multipath devices. This command can be used multiple times to allow ++multiple devices. \fBNOTE:\fP This action will create a configuration file that ++mpathconf will not be able to revert back to its previous state. Because ++of this, \fB--outfile\fP is required when using \fB--allow\fP. ++.TP ++.B --user_friendly_names \fP { \fBy\fP | \fBn\fP } ++If set to \fBy\fP, this adds the line ++.B user_friendly_names 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. ++.TP ++.B --find_multipaths\fP { \fBy\fP | \fBn\fP } ++If set to \fBy\fP, this adds the line ++.B find_multipaths 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. ++.TP ++.B --property_blacklist \fP { \fBy\fP | \fBn\fP } ++If set to \fBy\fP, this adds the line ++.B property "(SCSI_IDENT_|ID_WWN)" ++to the ++.B /etc/multipath.conf ++blacklist_exceptions section. If set to \fBn\fP, this removes the line, if ++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 "^$" ++to the ++.B /etc/multipath.conf ++defaults section. if set to \fBy\fP, this removes the line, if present. This ++command can be used along with any other command. ++.TP ++.B --outfile \fB\fP ++Write the resulting multipath configuration to \fB\fP instead of ++\fB/etc/multipath.conf\fP. ++.SH OPTIONS ++.TP ++.B --with_module\fP { \fBy\fP | \fBn\fP } ++If set to \fBy\fP, this runs ++.B modprobe dm_multipath ++to install the multipath modules. This option only works with the ++.B --enable ++command. This option is set to \fBy\fP by default. ++.TP ++.B --with_multipathd { \fBy\fP | \fBn\fP } ++If set to \fBy\fP, this runs ++.B service multipathd start ++to start the multipathd daemon on \fB--enable\fP, ++.B service multipathd stop ++to stop the multipathd daemon on \fB--disable\fP, and ++.B service multipathd reload ++to reconfigure multipathd on \fB--user_frindly_names\fP and ++\fB--find_multipaths\fP. ++This option is set to \fBn\fP by default. ++.SH FILES ++.BR /etc/multipath.conf ++.SH "SEE ALSO" ++.BR multipath.conf (5), ++.BR modprobe (8), ++.BR multipath (8), ++.BR multipathd (8), ++.BR service (8), ++.SH AUTHOR ++Benjamin Marzinski +-- +2.17.2 + diff --git a/SOURCES/0016-RH-add-wwids-from-kernel-cmdline-mpath.wwids-with-A.patch b/SOURCES/0016-RH-add-wwids-from-kernel-cmdline-mpath.wwids-with-A.patch new file mode 100644 index 0000000..fb0d281 --- /dev/null +++ b/SOURCES/0016-RH-add-wwids-from-kernel-cmdline-mpath.wwids-with-A.patch @@ -0,0 +1,169 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 17 Oct 2014 11:20:34 -0500 +Subject: [PATCH] RH: add wwids from kernel cmdline mpath.wwids with -A + +This patch adds another option to multipath, "-A", which reads +/proc/cmdline for mpath.wwid= options, and adds any wwids it finds +to /etc/multipath/wwids. While this isn't usually important during +normal operation, since these wwids should already be added, it can be +helpful during installation, to make sure that multipath can claim +devices as its own, before LVM or something else makes use of them. The +patch also execs "/sbin/multipath -A" before running multipathd in +multipathd.service + +Signed-off-by: Benjamin Marzinski +--- + 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(-) + +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) ++{ ++ FILE *f = NULL; ++ char buf[LINE_MAX], *next, *ptr; ++ int ret = 0; ++ ++ f = fopen("/proc/cmdline", "re"); ++ if (!f) { ++ condlog(0, "can't open /proc/cmdline : %s", strerror(errno)); ++ return -1; ++ } ++ ++ if (!fgets(buf, sizeof(buf), f)) { ++ if (ferror(f)) ++ condlog(0, "read of /proc/cmdline failed : %s", ++ strerror(errno)); ++ else ++ condlog(0, "couldn't read /proc/cmdline"); ++ fclose(f); ++ return -1; ++ } ++ fclose(f); ++ next = buf; ++ while((ptr = strstr(next, "mpath.wwid="))) { ++ ptr += 11; ++ next = strpbrk(ptr, " \t\n"); ++ if (next) { ++ *next = '\0'; ++ next++; ++ } ++ if (strlen(ptr)) { ++ if (remember_wwid(ptr) != 0) ++ ret = -1; ++ } ++ else { ++ condlog(0, "empty mpath.wwid kernel command line option"); ++ ret = -1; ++ } ++ if (!next) ++ break; ++ } ++ 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 ) { + switch(arg) { + case 1: printf("optarg : %s\n",optarg); + break; +@@ -977,6 +979,10 @@ main (int argc, char *argv[]) + case 'T': + cmd = CMD_DUMP_CONFIG; + break; ++ case 'A': ++ if (remember_cmdline_wwid() != 0) ++ exit(RTVL_FAIL); ++ exit(RTVL_OK); + 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. + .B multipath + .RB [\| \-v\ \c + .IR level \|] +-.B -W ++.RB [\| \-A | \-W \|] + . + .LP + .B multipath +@@ -145,6 +145,11 @@ device mapper, path checkers ...). + Add the WWID for the specified device to the WWIDs file. + . + .TP ++.B \-A ++Add the WWIDs from any kernel command line \fImpath.wwid\fR parameters to the ++WWIDs file. ++. ++.TP + .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 + 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 + diff --git a/SOURCES/0017-RH-warn-on-invalid-regex-instead-of-failing.patch b/SOURCES/0017-RH-warn-on-invalid-regex-instead-of-failing.patch new file mode 100644 index 0000000..18dd530 --- /dev/null +++ b/SOURCES/0017-RH-warn-on-invalid-regex-instead-of-failing.patch @@ -0,0 +1,121 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 + diff --git a/SOURCES/0018-RH-reset-default-find_mutipaths-value-to-off.patch b/SOURCES/0018-RH-reset-default-find_mutipaths-value-to-off.patch new file mode 100644 index 0000000..2e53cd9 --- /dev/null +++ b/SOURCES/0018-RH-reset-default-find_mutipaths-value-to-off.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 7 Jun 2018 17:43:52 -0500 +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". + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/defaults.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h +index e5ee6afe..52fe05b9 100644 +--- a/libmultipath/defaults.h ++++ b/libmultipath/defaults.h +@@ -22,7 +22,7 @@ + #define DEFAULT_NO_PATH_RETRY NO_PATH_RETRY_UNDEF + #define DEFAULT_VERBOSITY 2 + #define DEFAULT_REASSIGN_MAPS 0 +-#define DEFAULT_FIND_MULTIPATHS FIND_MULTIPATHS_STRICT ++#define DEFAULT_FIND_MULTIPATHS FIND_MULTIPATHS_OFF + #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/SOURCES/0019-RH-Fix-nvme-compilation-warning.patch b/SOURCES/0019-RH-Fix-nvme-compilation-warning.patch new file mode 100644 index 0000000..fc6ccd7 --- /dev/null +++ b/SOURCES/0019-RH-Fix-nvme-compilation-warning.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 25 Jan 2019 14:54:56 -0600 +Subject: [PATCH] RH: Fix nvme compilation warning + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/nvme/argconfig.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libmultipath/nvme/argconfig.h b/libmultipath/nvme/argconfig.h +index adb192b6..bfd10ef8 100644 +--- a/libmultipath/nvme/argconfig.h ++++ b/libmultipath/nvme/argconfig.h +@@ -76,7 +76,7 @@ struct argconfig_commandline_options { + extern "C" { + #endif + +-typedef void argconfig_help_func(); ++typedef void argconfig_help_func(void); + void argconfig_append_usage(const char *str); + void argconfig_print_help(const char *program_desc, + const struct argconfig_commandline_options *options); +-- +2.17.2 + diff --git a/SOURCES/0020-RH-attempt-to-get-ANA-info-via-sysfs-first.patch b/SOURCES/0020-RH-attempt-to-get-ANA-info-via-sysfs-first.patch new file mode 100644 index 0000000..14b3367 --- /dev/null +++ b/SOURCES/0020-RH-attempt-to-get-ANA-info-via-sysfs-first.patch @@ -0,0 +1,87 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 11 Apr 2019 13:25:42 -0500 +Subject: [PATCH] RH: attempt to get ANA info via sysfs first + +When the ANA prioritizer is run, first see if the "ana_state" sysfs file +exists, and if it does, try to read the state from there. If that fails, +fallback to using an ioctl. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/prioritizers/ana.c | 31 +++++++++++++++++++++++++++++-- + 1 file changed, 29 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/prioritizers/ana.c b/libmultipath/prioritizers/ana.c +index b5c7873d..e139360c 100644 +--- a/libmultipath/prioritizers/ana.c ++++ b/libmultipath/prioritizers/ana.c +@@ -24,6 +24,7 @@ + #include "prio.h" + #include "util.h" + #include "structs.h" ++#include "sysfs.h" + + enum { + ANA_ERR_GETCTRL_FAILED = 1, +@@ -36,6 +37,7 @@ enum { + ANA_ERR_GETNS_FAILED, + ANA_ERR_NO_MEMORY, + ANA_ERR_NO_INFORMATION, ++ ANA_ERR_INVALID_STATE, + }; + + static const char *ana_errmsg[] = { +@@ -49,6 +51,7 @@ static const char *ana_errmsg[] = { + [ANA_ERR_GETNS_FAILED] = "couldn't get namespace info", + [ANA_ERR_NO_MEMORY] = "out of memory", + [ANA_ERR_NO_INFORMATION] = "invalid fd", ++ [ANA_ERR_INVALID_STATE] = "invalid state", + }; + + static const char *anas_string[] = { +@@ -107,6 +110,27 @@ static int get_ana_state(__u32 nsid, __u32 anagrpid, void *ana_log, + return -ANA_ERR_GETANAS_NOTFOUND; + } + ++static int get_ana_info_sysfs(struct path *pp) ++{ ++ char state[32]; ++ ++ if (!pp->udev || sysfs_attr_get_value(pp->udev, "ana_state", state, ++ sizeof(state)) <= 0) ++ return -ANA_ERR_NO_INFORMATION; ++ ++ if (strcmp(state, "optimized") == 0) ++ return NVME_ANA_OPTIMIZED; ++ if (strcmp(state, "non-optimized") == 0) ++ return NVME_ANA_NONOPTIMIZED; ++ if (strcmp(state, "inaccessible") == 0) ++ return NVME_ANA_INACCESSIBLE; ++ if (strcmp(state, "persistent-loss") == 0) ++ return NVME_ANA_PERSISTENT_LOSS; ++ if (strcmp(state, "change") == 0) ++ return NVME_ANA_CHANGE; ++ return -ANA_ERR_INVALID_STATE; ++} ++ + static int get_ana_info(struct path * pp) + { + int rc; +@@ -210,8 +234,11 @@ int getprio(struct path *pp, __attribute__((unused)) char *args, + + if (pp->fd < 0) + rc = -ANA_ERR_NO_INFORMATION; +- else +- rc = get_ana_info(pp); ++ else { ++ rc = get_ana_info_sysfs(pp); ++ if (rc < 0) ++ rc = get_ana_info(pp); ++ } + + switch (rc) { + case NVME_ANA_OPTIMIZED: +-- +2.17.2 + diff --git a/SOURCES/0021-libmultipath-remove-_blacklist_exceptions-functions.patch b/SOURCES/0021-libmultipath-remove-_blacklist_exceptions-functions.patch new file mode 100644 index 0000000..1177dd5 --- /dev/null +++ b/SOURCES/0021-libmultipath-remove-_blacklist_exceptions-functions.patch @@ -0,0 +1,139 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 + diff --git a/SOURCES/0022-libmultipath-fix-parser-issue-with-comments-in-strin.patch b/SOURCES/0022-libmultipath-fix-parser-issue-with-comments-in-strin.patch new file mode 100644 index 0000000..898826c --- /dev/null +++ b/SOURCES/0022-libmultipath-fix-parser-issue-with-comments-in-strin.patch @@ -0,0 +1,95 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 + diff --git a/SOURCES/0023-libmultipath-invert-regexes-that-start-with-exclamat.patch b/SOURCES/0023-libmultipath-invert-regexes-that-start-with-exclamat.patch new file mode 100644 index 0000000..575e771 --- /dev/null +++ b/SOURCES/0023-libmultipath-invert-regexes-that-start-with-exclamat.patch @@ -0,0 +1,435 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 + diff --git a/SOURCES/0024-libmultipath-make-dm_get_map-status-return-codes-sym.patch b/SOURCES/0024-libmultipath-make-dm_get_map-status-return-codes-sym.patch new file mode 100644 index 0000000..e5159bb --- /dev/null +++ b/SOURCES/0024-libmultipath-make-dm_get_map-status-return-codes-sym.patch @@ -0,0 +1,322 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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, ¶ms); ++ if (dm_get_next_target(dmt, NULL, &start, &length, ++ &target_type, ¶ms) != 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, ¶ms[0]) && ++ dm_get_map(names->name, &size, ¶ms[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 + diff --git a/SOURCES/0025-multipathd-fix-check_path-errors-with-removed-map.patch b/SOURCES/0025-multipathd-fix-check_path-errors-with-removed-map.patch new file mode 100644 index 0000000..1ea59c7 --- /dev/null +++ b/SOURCES/0025-multipathd-fix-check_path-errors-with-removed-map.patch @@ -0,0 +1,116 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 + diff --git a/SOURCES/0026-libmultipath-make-dm_flush_maps-only-return-0-on-suc.patch b/SOURCES/0026-libmultipath-make-dm_flush_maps-only-return-0-on-suc.patch new file mode 100644 index 0000000..55381a3 --- /dev/null +++ b/SOURCES/0026-libmultipath-make-dm_flush_maps-only-return-0-on-suc.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 + diff --git a/SOURCES/0027-multipathd-add-del-maps-multipathd-command.patch b/SOURCES/0027-multipathd-add-del-maps-multipathd-command.patch new file mode 100644 index 0000000..2f535e2 --- /dev/null +++ b/SOURCES/0027-multipathd-add-del-maps-multipathd-command.patch @@ -0,0 +1,160 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 15 Jun 2020 17:00:54 -0500 +Subject: [PATCH] multipathd: add "del maps" multipathd command + +This will flush all multipath devices. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/devmapper.c | 7 +++++-- + libmultipath/devmapper.h | 2 +- + multipath/main.c | 2 +- + multipathd/cli.c | 1 + + multipathd/cli_handlers.c | 19 +++++++++++++++++++ + multipathd/cli_handlers.h | 1 + + multipathd/main.c | 3 ++- + multipathd/main.h | 1 + + 8 files changed, 31 insertions(+), 5 deletions(-) + +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index cda83ce4..7f98bf9d 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -951,7 +951,7 @@ dm_flush_map_nopaths(const char * mapname, int deferred_remove) + + #endif + +-int dm_flush_maps (int retries) ++int dm_flush_maps (int need_suspend, int retries) + { + int r = 1; + struct dm_task *dmt; +@@ -974,7 +974,10 @@ int dm_flush_maps (int retries) + + r = 0; + do { +- r |= dm_suspend_and_flush_map(names->name, retries); ++ if (need_suspend) ++ r |= dm_suspend_and_flush_map(names->name, retries); ++ else ++ r |= dm_flush_map(names->name); + next = names->next; + names = (void *) names + next; + } while (next); +diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h +index adb55000..7e8812ad 100644 +--- a/libmultipath/devmapper.h ++++ b/libmultipath/devmapper.h +@@ -55,7 +55,7 @@ int dm_flush_map_nopaths(const char * mapname, int deferred_remove); + #define dm_suspend_and_flush_map(mapname, retries) \ + _dm_flush_map(mapname, 1, 0, 1, retries) + int dm_cancel_deferred_remove(struct multipath *mpp); +-int dm_flush_maps (int retries); ++int dm_flush_maps (int need_suspend, int retries); + int dm_fail_path(const char * mapname, char * path); + int dm_reinstate_path(const char * mapname, char * path); + int dm_queue_if_no_path(const char *mapname, int enable); +diff --git a/multipath/main.c b/multipath/main.c +index 78822ee1..7ab3102f 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -1127,7 +1127,7 @@ main (int argc, char *argv[]) + goto out; + } + else if (conf->remove == FLUSH_ALL) { +- r = dm_flush_maps(retries) ? RTVL_FAIL : RTVL_OK; ++ r = dm_flush_maps(1, retries) ? RTVL_FAIL : RTVL_OK; + goto out; + } + while ((r = configure(conf, cmd, dev_type, dev)) == RTVL_RETRY) +diff --git a/multipathd/cli.c b/multipathd/cli.c +index 800c0fbe..bdc9fb10 100644 +--- a/multipathd/cli.c ++++ b/multipathd/cli.c +@@ -568,6 +568,7 @@ cli_init (void) { + add_handler(DEL+PATH, NULL); + add_handler(ADD+MAP, NULL); + add_handler(DEL+MAP, NULL); ++ add_handler(DEL+MAPS, NULL); + add_handler(SWITCH+MAP+GROUP, NULL); + add_handler(RECONFIGURE, NULL); + add_handler(SUSPEND+MAP, NULL); +diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c +index 31c3d9fd..782bb003 100644 +--- a/multipathd/cli_handlers.c ++++ b/multipathd/cli_handlers.c +@@ -852,6 +852,25 @@ cli_del_map (void * v, char ** reply, int * len, void * data) + return rc; + } + ++int ++cli_del_maps (void *v, char **reply, int *len, void *data) ++{ ++ struct vectors * vecs = (struct vectors *)data; ++ struct multipath *mpp; ++ int i, ret = 0; ++ ++ condlog(2, "remove maps (operator)"); ++ vector_foreach_slot(vecs->mpvec, mpp, i) { ++ if (flush_map(mpp, vecs, 0)) ++ ret++; ++ else ++ i--; ++ } ++ /* flush any multipath maps that aren't currently known by multipathd */ ++ ret |= dm_flush_maps(0, 0); ++ return ret; ++} ++ + int + cli_reload(void *v, char **reply, int *len, void *data) + { +diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h +index 0f451064..6f57b429 100644 +--- a/multipathd/cli_handlers.h ++++ b/multipathd/cli_handlers.h +@@ -26,6 +26,7 @@ int cli_add_path (void * v, char ** reply, int * len, void * data); + int cli_del_path (void * v, char ** reply, int * len, void * data); + int cli_add_map (void * v, char ** reply, int * len, void * data); + int cli_del_map (void * v, char ** reply, int * len, void * data); ++int cli_del_maps (void * v, char ** reply, int * len, void * data); + int cli_switch_group(void * v, char ** reply, int * len, void * data); + int cli_reconfigure(void * v, char ** reply, int * len, void * data); + int cli_resize(void * v, char ** reply, int * len, void * data); +diff --git a/multipathd/main.c b/multipathd/main.c +index 1d9ce7f7..1d0579e9 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -631,7 +631,7 @@ sync_maps_state(vector mpvec) + sync_map_state(mpp); + } + +-static int ++int + flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths) + { + int r; +@@ -1551,6 +1551,7 @@ uxlsnrloop (void * ap) + set_handler_callback(DEL+PATH, cli_del_path); + set_handler_callback(ADD+MAP, cli_add_map); + set_handler_callback(DEL+MAP, cli_del_map); ++ set_handler_callback(DEL+MAPS, cli_del_maps); + set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group); + set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure); + set_handler_callback(SUSPEND+MAP, cli_suspend); +diff --git a/multipathd/main.h b/multipathd/main.h +index 7bb8463f..5dff17e5 100644 +--- a/multipathd/main.h ++++ b/multipathd/main.h +@@ -28,6 +28,7 @@ int ev_add_path (struct path *, struct vectors *, int); + int ev_remove_path (struct path *, struct vectors *, int); + int ev_add_map (char *, const char *, struct vectors *); + int ev_remove_map (char *, char *, int, struct vectors *); ++int flush_map(struct multipath *, struct vectors *, int); + int set_config_state(enum daemon_status); + void * mpath_alloc_prin_response(int prin_sa); + int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp, +-- +2.17.2 + diff --git a/SOURCES/0028-multipath-make-flushing-maps-work-like-other-command.patch b/SOURCES/0028-multipath-make-flushing-maps-work-like-other-command.patch new file mode 100644 index 0000000..1674230 --- /dev/null +++ b/SOURCES/0028-multipath-make-flushing-maps-work-like-other-command.patch @@ -0,0 +1,103 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 15 Jun 2020 23:54:29 -0500 +Subject: [PATCH] multipath: make flushing maps work like other commands + +The config structure doesn't need a special variable just for removes. +Multipath can just use the cmd variable, like it does for the other +commands. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/config.h | 3 ++- + libmultipath/configure.h | 3 --- + multipath/main.c | 20 ++++++++++---------- + 3 files changed, 12 insertions(+), 14 deletions(-) + +diff --git a/libmultipath/config.h b/libmultipath/config.h +index 3368d8c9..4042eba6 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -39,6 +39,8 @@ enum mpath_cmds { + CMD_ADD_WWID, + CMD_USABLE_PATHS, + CMD_DUMP_CONFIG, ++ CMD_FLUSH_ONE, ++ CMD_FLUSH_ALL, + }; + + enum force_reload_types { +@@ -143,7 +145,6 @@ struct config { + unsigned int max_checkint; + bool use_watchdog; + int pgfailback; +- int remove; + int rr_weight; + int no_path_retry; + int user_friendly_names; +diff --git a/libmultipath/configure.h b/libmultipath/configure.h +index d7509000..0e33bf40 100644 +--- a/libmultipath/configure.h ++++ b/libmultipath/configure.h +@@ -45,9 +45,6 @@ enum { + CP_RETRY, + }; + +-#define FLUSH_ONE 1 +-#define FLUSH_ALL 2 +- + struct vectors; + + int setup_map (struct multipath * mpp, char * params, int params_size, +diff --git a/multipath/main.c b/multipath/main.c +index 7ab3102f..a2080029 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -942,10 +942,10 @@ main (int argc, char *argv[]) + cmd = CMD_DRY_RUN; + break; + case 'f': +- conf->remove = FLUSH_ONE; ++ cmd = CMD_FLUSH_ONE; + break; + case 'F': +- conf->remove = FLUSH_ALL; ++ cmd = CMD_FLUSH_ALL; + break; + case 'l': + if (optarg && !strncmp(optarg, "l", 1)) +@@ -1084,6 +1084,10 @@ main (int argc, char *argv[]) + condlog(0, "the -w option requires a device"); + goto out; + } ++ if (cmd == CMD_FLUSH_ONE && dev_type != DEV_DEVMAP) { ++ condlog(0, "the -f option requires a map name to remove"); ++ goto out; ++ } + + switch(delegate_to_multipathd(cmd, dev, dev_type, conf)) { + case DELEGATE_OK: +@@ -1117,16 +1121,12 @@ main (int argc, char *argv[]) + } + if (retries < 0) + retries = conf->remove_retries; +- if (conf->remove == FLUSH_ONE) { +- if (dev_type == DEV_DEVMAP) { +- r = dm_suspend_and_flush_map(dev, retries) ? +- RTVL_FAIL : RTVL_OK; +- } else +- condlog(0, "must provide a map name to remove"); +- ++ if (cmd == CMD_FLUSH_ONE) { ++ r = dm_suspend_and_flush_map(dev, retries) ? ++ RTVL_FAIL : RTVL_OK; + goto out; + } +- else if (conf->remove == FLUSH_ALL) { ++ else if (cmd == CMD_FLUSH_ALL) { + r = dm_flush_maps(1, retries) ? RTVL_FAIL : RTVL_OK; + goto out; + } +-- +2.17.2 + diff --git a/SOURCES/0029-multipath-delegate-flushing-maps-to-multipathd.patch b/SOURCES/0029-multipath-delegate-flushing-maps-to-multipathd.patch new file mode 100644 index 0000000..f610b93 --- /dev/null +++ b/SOURCES/0029-multipath-delegate-flushing-maps-to-multipathd.patch @@ -0,0 +1,63 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 16 Jun 2020 16:25:34 -0500 +Subject: [PATCH] multipath: delegate flushing maps to multipathd + +Since there can be problems with removing maps outside of multipathd, +multipath should attempt to delegate this command to multipathd. +However, multipathd doesn't attempt to suspend the device, in order +to avoid potential hangs. If delegating to multipathd fails, multipath +should try the remove itself. + +Signed-off-by: Benjamin Marzinski +--- + multipath/main.c | 14 ++++++++++++++ + multipath/multipath.8 | 4 ++-- + 2 files changed, 16 insertions(+), 2 deletions(-) + +diff --git a/multipath/main.c b/multipath/main.c +index a2080029..612c6815 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -828,6 +828,20 @@ int delegate_to_multipathd(enum mpath_cmds cmd, + if (cmd == CMD_CREATE && conf->force_reload == FORCE_RELOAD_YES) { + p += snprintf(p, n, "reconfigure"); + } ++ else if (cmd == CMD_FLUSH_ONE && dev && dev_type == DEV_DEVMAP) { ++ p += snprintf(p, n, "del map %s", dev); ++ /* multipathd doesn't try as hard, to avoid potentially ++ * hanging. If it fails, retry with the regular multipath ++ * command */ ++ r = NOT_DELEGATED; ++ } ++ else if (cmd == CMD_FLUSH_ALL) { ++ p += snprintf(p, n, "del maps"); ++ /* multipathd doesn't try as hard, to avoid potentially ++ * hanging. If it fails, retry with the regular multipath ++ * command */ ++ r = NOT_DELEGATED; ++ } + /* Add other translations here */ + + if (strlen(command) == 0) +diff --git a/multipath/multipath.8 b/multipath/multipath.8 +index 8befc45a..47a33f9b 100644 +--- a/multipath/multipath.8 ++++ b/multipath/multipath.8 +@@ -125,11 +125,11 @@ the system. + Other operation modes are chosen by using one of the following command line switches: + .TP + .B \-f +-Flush (remove) a multipath device map specified as parameter, if unused. ++Flush (remove) a multipath device map specified as parameter, if unused. This operation is delegated to the multipathd daemon if it's running. + . + .TP + .B \-F +-Flush (remove) all unused multipath device maps. ++Flush (remove) all unused multipath device maps. This operation is delegated to the multipathd daemon if it's running. + . + .TP + .B \-l +-- +2.17.2 + diff --git a/SOURCES/0030-multipath-add-option-to-skip-multipathd-delegation.patch b/SOURCES/0030-multipath-add-option-to-skip-multipathd-delegation.patch new file mode 100644 index 0000000..141a565 --- /dev/null +++ b/SOURCES/0030-multipath-add-option-to-skip-multipathd-delegation.patch @@ -0,0 +1,62 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 16 Jun 2020 17:36:33 -0500 +Subject: [PATCH] multipath: add option to skip multipathd delegation + +Add the -D option to allow users to skip delegating commands to +multipathd. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/config.h | 1 + + multipath/main.c | 8 +++++++- + 2 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/config.h b/libmultipath/config.h +index 4042eba6..160867cd 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -191,6 +191,7 @@ struct config { + int ghost_delay; + int find_multipaths_timeout; + int marginal_pathgroups; ++ int skip_delegate; + unsigned int version[3]; + unsigned int sequence_nr; + +diff --git a/multipath/main.c b/multipath/main.c +index 612c6815..3c3d2398 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -825,6 +825,9 @@ int delegate_to_multipathd(enum mpath_cmds cmd, + *p = '\0'; + n = sizeof(command); + ++ if (conf->skip_delegate) ++ return NOT_DELEGATED; ++ + if (cmd == CMD_CREATE && conf->force_reload == FORCE_RELOAD_YES) { + p += snprintf(p, n, "reconfigure"); + } +@@ -923,7 +926,7 @@ main (int argc, char *argv[]) + multipath_conf = conf; + conf->retrigger_tries = 0; + conf->force_sync = 1; +- while ((arg = getopt(argc, argv, ":aAdcChl::FfM:v:p:b:BrR:itTquUwW")) != EOF ) { ++ while ((arg = getopt(argc, argv, ":aAdDcChl::FfM:v:p:b:BrR:itTquUwW")) != EOF ) { + switch(arg) { + case 1: printf("optarg : %s\n",optarg); + break; +@@ -955,6 +958,9 @@ main (int argc, char *argv[]) + if (cmd == CMD_CREATE) + cmd = CMD_DRY_RUN; + break; ++ case 'D': ++ conf->skip_delegate = 1; ++ break; + case 'f': + cmd = CMD_FLUSH_ONE; + break; +-- +2.17.2 + diff --git a/SOURCES/0031-libmultipath-fix-sysfs-dev_loss_tmo-parsing.patch b/SOURCES/0031-libmultipath-fix-sysfs-dev_loss_tmo-parsing.patch new file mode 100644 index 0000000..6954d3c --- /dev/null +++ b/SOURCES/0031-libmultipath-fix-sysfs-dev_loss_tmo-parsing.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 25 Jun 2020 20:46:08 -0500 +Subject: [PATCH] libmultipath: fix sysfs dev_loss_tmo parsing + +dev_loss_tmo is a u32 value. However the kernel sysfs code prints it as +a signed integer. This means that if dev_loss_tmo is above INT_MAX, the +sysfs value will be a negative number. Parsing this was causing +sysfs_set_rport_tmo() to fail. + +Signed-off-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + libmultipath/discovery.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index ffec5162..83a41a4a 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -583,7 +583,7 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) + struct udev_device *rport_dev = NULL; + char value[16], *eptr; + char rport_id[32]; +- unsigned long long tmo = 0; ++ unsigned int tmo; + int ret; + + sprintf(rport_id, "rport-%d:%d-%d", +@@ -607,8 +607,8 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) + "error %d", rport_id, -ret); + goto out; + } +- tmo = strtoull(value, &eptr, 0); +- if (value == eptr || tmo == ULLONG_MAX) { ++ tmo = strtoul(value, &eptr, 0); ++ if (value == eptr) { + condlog(0, "%s: Cannot parse dev_loss_tmo " + "attribute '%s'", rport_id, value); + goto out; +-- +2.17.2 + diff --git a/SOURCES/0032-kpartx-read-devices-with-direct-IO.patch b/SOURCES/0032-kpartx-read-devices-with-direct-IO.patch new file mode 100644 index 0000000..16d8937 --- /dev/null +++ b/SOURCES/0032-kpartx-read-devices-with-direct-IO.patch @@ -0,0 +1,267 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 26 Jun 2020 20:06:24 -0500 +Subject: [PATCH] kpartx: read devices with direct IO + +If kpartx is used on top of shared storage, and a device has its +partition table changed on one machine, and then kpartx is run on +another, it may not see the new data, because the cache still contains +the old data, and there is nothing to tell the machine running kpartx to +invalidate it. To solve this, kpartx should read the devices using +direct io. + +One issue with how this code has been updated is that the original code +for getblock() always read 1024 bytes. The new code reads a logical +sector size chunk of the device, and returns a pointer to the 512 byte +sector that the caller asked for, within that (possibly larger) chunk. +This means that if the logical sector size is 512, then the code is now +only reading 512 bytes. Looking through the code for the various +partition types, I can't see a case where more than 512 bytes is needed +and getblock() is used. If anyone has a reason why this code should be +reading 1024 bytes at minmum, I can certainly change this. But when I +looked, I couldn't find a case where reading 512 bytes would cause a +problem. + +Signed-off-by: Benjamin Marzinski +--- + kpartx/dasd.c | 7 ++++--- + kpartx/gpt.c | 22 +++++++++---------- + kpartx/kpartx.c | 56 +++++++++++++++++++++++++++++++++++++++---------- + kpartx/kpartx.h | 2 ++ + 4 files changed, 61 insertions(+), 26 deletions(-) + +diff --git a/kpartx/dasd.c b/kpartx/dasd.c +index 14b9d3aa..f0398645 100644 +--- a/kpartx/dasd.c ++++ b/kpartx/dasd.c +@@ -22,6 +22,7 @@ + * along with this program. If not, see . + */ + ++#define _GNU_SOURCE + #include + #include + #include +@@ -117,13 +118,13 @@ read_dasd_pt(int fd, __attribute__((unused)) struct slice all, + + sprintf(pathname, "/dev/.kpartx-node-%u-%u", + (unsigned int)major(dev), (unsigned int)minor(dev)); +- if ((fd_dasd = open(pathname, O_RDONLY)) == -1) { ++ if ((fd_dasd = open(pathname, O_RDONLY | O_DIRECT)) == -1) { + /* Devicenode does not exist. Try to create one */ + if (mknod(pathname, 0600 | S_IFBLK, dev) == -1) { + /* Couldn't create a device node */ + return -1; + } +- fd_dasd = open(pathname, O_RDONLY); ++ fd_dasd = open(pathname, O_RDONLY | O_DIRECT); + /* + * The file will vanish when the last process (we) + * has ceased to access it. +@@ -175,7 +176,7 @@ read_dasd_pt(int fd, __attribute__((unused)) struct slice all, + * Get volume label, extract name and type. + */ + +- if (!(data = (unsigned char *)malloc(blocksize))) ++ if (aligned_malloc((void **)&data, blocksize, NULL)) + goto out; + + +diff --git a/kpartx/gpt.c b/kpartx/gpt.c +index 785b34ea..f7fefb70 100644 +--- a/kpartx/gpt.c ++++ b/kpartx/gpt.c +@@ -243,8 +243,7 @@ alloc_read_gpt_entries(int fd, gpt_header * gpt) + + if (!count) return NULL; + +- pte = (gpt_entry *)malloc(count); +- if (!pte) ++ if (aligned_malloc((void **)&pte, get_sector_size(fd), &count)) + return NULL; + memset(pte, 0, count); + +@@ -269,12 +268,11 @@ static gpt_header * + alloc_read_gpt_header(int fd, uint64_t lba) + { + gpt_header *gpt; +- gpt = (gpt_header *) +- malloc(sizeof (gpt_header)); +- if (!gpt) ++ size_t size = sizeof (gpt_header); ++ if (aligned_malloc((void **)&gpt, get_sector_size(fd), &size)) + return NULL; +- memset(gpt, 0, sizeof (*gpt)); +- if (!read_lba(fd, lba, gpt, sizeof (gpt_header))) { ++ memset(gpt, 0, size); ++ if (!read_lba(fd, lba, gpt, size)) { + free(gpt); + return NULL; + } +@@ -498,6 +496,7 @@ find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) + gpt_header *pgpt = NULL, *agpt = NULL; + gpt_entry *pptes = NULL, *aptes = NULL; + legacy_mbr *legacymbr = NULL; ++ size_t size = sizeof(legacy_mbr); + uint64_t lastlba; + if (!gpt || !ptes) + return 0; +@@ -526,11 +525,10 @@ find_valid_gpt(int fd, gpt_header ** gpt, gpt_entry ** ptes) + } + + /* This will be added to the EFI Spec. per Intel after v1.02. */ +- legacymbr = malloc(sizeof (*legacymbr)); +- if (legacymbr) { +- memset(legacymbr, 0, sizeof (*legacymbr)); +- read_lba(fd, 0, (uint8_t *) legacymbr, +- sizeof (*legacymbr)); ++ if (aligned_malloc((void **)&legacymbr, get_sector_size(fd), ++ &size) == 0) { ++ memset(legacymbr, 0, size); ++ read_lba(fd, 0, (uint8_t *) legacymbr, size); + good_pmbr = is_pmbr_valid(legacymbr); + free(legacymbr); + legacymbr=NULL; +diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c +index d3620c5c..c24ad6d9 100644 +--- a/kpartx/kpartx.c ++++ b/kpartx/kpartx.c +@@ -19,6 +19,7 @@ + * cva, 2002-10-26 + */ + ++#define _GNU_SOURCE + #include + #include + #include +@@ -41,7 +42,6 @@ + + #define SIZE(a) (sizeof(a)/sizeof((a)[0])) + +-#define READ_SIZE 1024 + #define MAXTYPES 64 + #define MAXSLICES 256 + #define DM_TARGET "linear" +@@ -388,7 +388,7 @@ main(int argc, char **argv){ + set_delimiter(mapname, delim); + } + +- fd = open(device, O_RDONLY); ++ fd = open(device, O_RDONLY | O_DIRECT); + + if (fd == -1) { + perror(device); +@@ -690,9 +690,9 @@ xmalloc (size_t size) { + */ + + static int +-sseek(int fd, unsigned int secnr) { ++sseek(int fd, unsigned int secnr, int secsz) { + off64_t in, out; +- in = ((off64_t) secnr << 9); ++ in = ((off64_t) secnr * secsz); + out = 1; + + if ((out = lseek64(fd, in, SEEK_SET)) != in) +@@ -703,6 +703,31 @@ sseek(int fd, unsigned int secnr) { + return 0; + } + ++int ++aligned_malloc(void **mem_p, size_t align, size_t *size_p) ++{ ++ static size_t pgsize = 0; ++ size_t size; ++ int err; ++ ++ if (!mem_p || !align || (size_p && !*size_p)) ++ return EINVAL; ++ ++ if (!pgsize) ++ pgsize = getpagesize(); ++ ++ if (size_p) ++ size = ((*size_p + align - 1) / align) * align; ++ else ++ size = pgsize; ++ ++ err = posix_memalign(mem_p, pgsize, size); ++ if (!err && size_p) ++ *size_p = size; ++ return err; ++} ++ ++/* always in sector size blocks */ + static + struct block { + unsigned int secnr; +@@ -710,30 +735,39 @@ struct block { + struct block *next; + } *blockhead; + ++/* blknr is always in 512 byte blocks */ + char * +-getblock (int fd, unsigned int secnr) { ++getblock (int fd, unsigned int blknr) { ++ unsigned int secsz = get_sector_size(fd); ++ unsigned int blks_per_sec = secsz / 512; ++ unsigned int secnr = blknr / blks_per_sec; ++ unsigned int blk_off = (blknr % blks_per_sec) * 512; + struct block *bp; + + for (bp = blockhead; bp; bp = bp->next) + + if (bp->secnr == secnr) +- return bp->block; ++ return bp->block + blk_off; + +- if (sseek(fd, secnr)) ++ if (sseek(fd, secnr, secsz)) + return NULL; + + bp = xmalloc(sizeof(struct block)); + bp->secnr = secnr; + bp->next = blockhead; + blockhead = bp; +- bp->block = (char *) xmalloc(READ_SIZE); ++ if (aligned_malloc((void **)&bp->block, secsz, NULL)) { ++ fprintf(stderr, "aligned_malloc failed\n"); ++ exit(1); ++ } + +- if (read(fd, bp->block, READ_SIZE) != READ_SIZE) { ++ if (read(fd, bp->block, secsz) != secsz) { + fprintf(stderr, "read error, sector %d\n", secnr); +- bp->block = NULL; ++ blockhead = bp->next; ++ return NULL; + } + +- return bp->block; ++ return bp->block + blk_off; + } + + int +diff --git a/kpartx/kpartx.h b/kpartx/kpartx.h +index 67edeb82..727632c1 100644 +--- a/kpartx/kpartx.h ++++ b/kpartx/kpartx.h +@@ -1,6 +1,7 @@ + #ifndef _KPARTX_H + #define _KPARTX_H + ++#include + #include + #include + +@@ -61,6 +62,7 @@ extern ptreader read_mac_pt; + extern ptreader read_sun_pt; + extern ptreader read_ps3_pt; + ++int aligned_malloc(void **mem_p, size_t align, size_t *size_p); + char *getblock(int fd, unsigned int secnr); + + static inline unsigned int +-- +2.17.2 + diff --git a/SOURCES/0033-kpartx-handle-alternate-bsd-disklabel-location.patch b/SOURCES/0033-kpartx-handle-alternate-bsd-disklabel-location.patch new file mode 100644 index 0000000..556ea24 --- /dev/null +++ b/SOURCES/0033-kpartx-handle-alternate-bsd-disklabel-location.patch @@ -0,0 +1,52 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 30 Jun 2020 10:49:59 -0500 +Subject: [PATCH] kpartx: handle alternate bsd disklabel location + +bsd disk labels can either be at the start of the second sector, or 64 +bytes into the first sector, but kpartx only handled the first case. +However the second case is what parted creates, and what the linux +kernel partition code expects. kpartx should handle both cases. + +Signed-off-by: Benjamin Marzinski +--- + kpartx/bsd.c | 16 ++++++++++++++-- + 1 file changed, 14 insertions(+), 2 deletions(-) + +diff --git a/kpartx/bsd.c b/kpartx/bsd.c +index 0e661fbc..950b0f92 100644 +--- a/kpartx/bsd.c ++++ b/kpartx/bsd.c +@@ -1,6 +1,7 @@ + #include "kpartx.h" + #include + ++#define BSD_LABEL_OFFSET 64 + #define BSD_DISKMAGIC (0x82564557UL) /* The disk magic number */ + #define XBSD_MAXPARTITIONS 16 + #define BSD_FS_UNUSED 0 +@@ -60,8 +61,19 @@ read_bsd_pt(int fd, struct slice all, struct slice *sp, unsigned int ns) { + return -1; + + l = (struct bsd_disklabel *) bp; +- if (l->d_magic != BSD_DISKMAGIC) +- return -1; ++ if (l->d_magic != BSD_DISKMAGIC) { ++ /* ++ * BSD disklabels can also start 64 bytes offset from the ++ * start of the first sector ++ */ ++ bp = getblock(fd, offset); ++ if (bp == NULL) ++ return -1; ++ ++ l = (struct bsd_disklabel *)(bp + 64); ++ if (l->d_magic != BSD_DISKMAGIC) ++ return -1; ++ } + + max_partitions = 16; + if (l->d_npartitions < max_partitions) +-- +2.17.2 + diff --git a/SOURCES/0034-libmultipath-fix-checker-detection-for-nvme-devices.patch b/SOURCES/0034-libmultipath-fix-checker-detection-for-nvme-devices.patch new file mode 100644 index 0000000..ac5a32c --- /dev/null +++ b/SOURCES/0034-libmultipath-fix-checker-detection-for-nvme-devices.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 30 Jun 2020 13:59:13 -0500 +Subject: [PATCH] libmultipath: fix checker detection for nvme devices + +In order to fix hwhandler autodetection, commit 8794a776 made +detect_alua() differentiate between failures to detect whether alua was +supported, and successfully detecting that it was not supported. +However, this causes nvme devices to get the TUR checker assigned to +them. This is because there is nothing in detect_alua() to make it only +work on scsi devices, and select_checker wasn't updated to handle +detect_alua() failing without setting pp->tpgs to TPGS_NONE. + +detect_alua() should automatically set pp->tpgs to TPGS_NONE and exit on +non-scsi devices. Also, select_checker() should not assume that a +devices is ALUA, simply because if failed to detect if alua was +supported. + +Fixes: 8794a776 "libmultipath: fix ALUA autodetection when paths are + down" +Signed-off-by: Benjamin Marzinski +--- + libmultipath/discovery.c | 6 ++++++ + libmultipath/propsel.c | 4 +++- + 2 files changed, 9 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index 83a41a4a..aa5942c3 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -887,6 +887,12 @@ detect_alua(struct path * pp) + int tpgs; + unsigned int timeout; + ++ ++ if (pp->bus != SYSFS_BUS_SCSI) { ++ pp->tpgs = TPGS_NONE; ++ return; ++ } ++ + if (sysfs_get_timeout(pp, &timeout) <= 0) + timeout = DEF_TIMEOUT; + +diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c +index 897e48ca..d362beb4 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -521,7 +521,9 @@ int select_checker(struct config *conf, struct path *pp) + if (check_rdac(pp)) { + ckr_name = RDAC; + goto out; +- } else if (path_get_tpgs(pp) != TPGS_NONE) { ++ } ++ path_get_tpgs(pp); ++ if (pp->tpgs != TPGS_NONE && pp->tpgs != TPGS_UNDEF) { + ckr_name = TUR; + goto out; + } +-- +2.17.2 + diff --git a/SOURCES/0035-Makefile.inc-trim-extra-information-from-systemd-ver.patch b/SOURCES/0035-Makefile.inc-trim-extra-information-from-systemd-ver.patch new file mode 100644 index 0000000..27ce5fe --- /dev/null +++ b/SOURCES/0035-Makefile.inc-trim-extra-information-from-systemd-ver.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 6 Jul 2020 13:21:12 -0500 +Subject: [PATCH] Makefile.inc: trim extra information from systemd version + +Some systemd versions print extra information in the +"pkg-config --modversion" output, which confuses make. Trim this +off. + +Signed-off-by: Benjamin Marzinski +--- + Makefile.inc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Makefile.inc b/Makefile.inc +index c2abd301..220009e0 100644 +--- a/Makefile.inc ++++ b/Makefile.inc +@@ -37,7 +37,7 @@ endif + + ifndef SYSTEMD + ifeq ($(shell pkg-config --modversion libsystemd >/dev/null 2>&1 && echo 1), 1) +- SYSTEMD = $(shell pkg-config --modversion libsystemd) ++ SYSTEMD = $(shell pkg-config --modversion libsystemd | awk '{print $$1}') + else + ifeq ($(shell systemctl --version >/dev/null 2>&1 && echo 1), 1) + SYSTEMD = $(shell systemctl --version 2> /dev/null | \ +-- +2.17.2 + diff --git a/SOURCES/0036-kpartx-fix-Wsign-compare-error.patch b/SOURCES/0036-kpartx-fix-Wsign-compare-error.patch new file mode 100644 index 0000000..ffaa477 --- /dev/null +++ b/SOURCES/0036-kpartx-fix-Wsign-compare-error.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 6 Jul 2020 17:28:46 -0500 +Subject: [PATCH] kpartx: fix -Wsign-compare error + +Signed-off-by: Benjamin Marzinski +--- + kpartx/kpartx.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c +index c24ad6d9..653ce0c8 100644 +--- a/kpartx/kpartx.c ++++ b/kpartx/kpartx.c +@@ -738,7 +738,7 @@ struct block { + /* blknr is always in 512 byte blocks */ + char * + getblock (int fd, unsigned int blknr) { +- unsigned int secsz = get_sector_size(fd); ++ int secsz = get_sector_size(fd); + unsigned int blks_per_sec = secsz / 512; + unsigned int secnr = blknr / blks_per_sec; + unsigned int blk_off = (blknr % blks_per_sec) * 512; +-- +2.17.2 + diff --git a/SOURCES/0037-libmultipath-count-pending-paths-as-active-on-loads.patch b/SOURCES/0037-libmultipath-count-pending-paths-as-active-on-loads.patch new file mode 100644 index 0000000..d4ff3c2 --- /dev/null +++ b/SOURCES/0037-libmultipath-count-pending-paths-as-active-on-loads.patch @@ -0,0 +1,86 @@ +From 7159242be31dbb3f25aa67920462107bc2bc5fe0 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 9 Jul 2020 18:20:18 -0500 +Subject: [PATCH] libmultipath: count pending paths as active on loads + +When multipath loads a table, it signals to udev if there are no active +paths. Multipath wasn't counting pending paths as active. This meant +that if all the paths were pending, udev would treat the device as not +ready, and not run kpartx on it. Even if the pending paths later +because active and were reinstated, the kernel would not send a new +uevent, because from its point of view, they were always up. + +The alternative would be to continue to treat them as failed in the udev +rules, but then also tell the kernel that they were down, so that it +would trigger a uevent when they were reinstated. However, this could +lead to newly created multipath devices failing IO, simply because the +path checkers hadn't returned yet. Having udev assume that the the +device is up, like the kernel does, seems like the safer option. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/devmapper.c | 3 ++- + libmultipath/structs.c | 20 ++++++++++++++++++++ + libmultipath/structs.h | 1 + + 3 files changed, 23 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index 7f98bf9d..91ff0b3d 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -408,7 +408,8 @@ static uint16_t build_udev_flags(const struct multipath *mpp, int reload) + /* DM_UDEV_DISABLE_LIBRARY_FALLBACK is added in dm_addmap */ + return (mpp->skip_kpartx == SKIP_KPARTX_ON ? + MPATH_UDEV_NO_KPARTX_FLAG : 0) | +- ((count_active_paths(mpp) == 0 || mpp->ghost_delay_tick > 0) ? ++ ((count_active_pending_paths(mpp) == 0 || ++ mpp->ghost_delay_tick > 0) ? + MPATH_UDEV_NO_PATHS_FLAG : 0) | + (reload && !mpp->force_udev_reload ? + MPATH_UDEV_RELOAD_FLAG : 0); +diff --git a/libmultipath/structs.c b/libmultipath/structs.c +index 2dd378c4..dda9884c 100644 +--- a/libmultipath/structs.c ++++ b/libmultipath/structs.c +@@ -500,6 +500,26 @@ int count_active_paths(const struct multipath *mpp) + return count; + } + ++int count_active_pending_paths(const struct multipath *mpp) ++{ ++ struct pathgroup *pgp; ++ struct path *pp; ++ int count = 0; ++ int i, j; ++ ++ if (!mpp->pg) ++ return 0; ++ ++ vector_foreach_slot (mpp->pg, pgp, i) { ++ vector_foreach_slot (pgp->paths, pp, j) { ++ if (pp->state == PATH_UP || pp->state == PATH_GHOST || ++ pp->state == PATH_PENDING) ++ count++; ++ } ++ } ++ return count; ++} ++ + int pathcmp(const struct pathgroup *pgp, const struct pathgroup *cpgp) + { + int i, j; +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 9bd39eb1..8e78b8c0 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -465,6 +465,7 @@ struct path * first_path (const struct multipath *mpp); + int pathcountgr (const struct pathgroup *, int); + int pathcount (const struct multipath *, int); + int count_active_paths(const struct multipath *); ++int count_active_pending_paths(const struct multipath *); + int pathcmp (const struct pathgroup *, const struct pathgroup *); + int add_feature (char **, const char *); + int remove_feature (char **, const char *); +-- +2.17.2 + diff --git a/SOURCES/0038-multipath-deal-with-failures-flushing-maps.patch b/SOURCES/0038-multipath-deal-with-failures-flushing-maps.patch new file mode 100644 index 0000000..a73e25f --- /dev/null +++ b/SOURCES/0038-multipath-deal-with-failures-flushing-maps.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 13 Jul 2020 15:41:15 -0500 +Subject: [PATCH] multipath: deal with failures flushing maps + +dm_flush_maps() was failing if there were no device-mapper devices at +all, instead of returning success, since there is nothing to do. + +delegate_to_multipathd() was returning success, even if the multipathd +command failed. Also, if the command was set to fail with NOT_DELEGATED, +it shouldn't print any errors, since multipath will try to issue to +command itself. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/devmapper.c | 2 +- + multipath/main.c | 9 ++++++--- + 2 files changed, 7 insertions(+), 4 deletions(-) + +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index 91ff0b3d..3f70e576 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -970,10 +970,10 @@ int dm_flush_maps (int need_suspend, int retries) + if (!(names = dm_task_get_names (dmt))) + goto out; + ++ r = 0; + if (!names->dev) + goto out; + +- r = 0; + do { + if (need_suspend) + r |= dm_suspend_and_flush_map(names->name, retries); +diff --git a/multipath/main.c b/multipath/main.c +index 3c3d2398..607cada2 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -869,9 +869,12 @@ int delegate_to_multipathd(enum mpath_cmds cmd, + goto out; + } + +- if (reply != NULL && *reply != '\0' && strcmp(reply, "ok\n")) +- printf("%s", reply); +- r = DELEGATE_OK; ++ if (reply != NULL && *reply != '\0') { ++ if (strcmp(reply, "fail\n")) ++ r = DELEGATE_OK; ++ if (r != NOT_DELEGATED && strcmp(reply, "ok\n")) ++ printf("%s", reply); ++ } + + out: + FREE(reply); +-- +2.17.2 + diff --git a/SOURCES/0039-libmultipath-factor-out-code-to-get-vpd-page-data.patch b/SOURCES/0039-libmultipath-factor-out-code-to-get-vpd-page-data.patch new file mode 100644 index 0000000..9862e22 --- /dev/null +++ b/SOURCES/0039-libmultipath-factor-out-code-to-get-vpd-page-data.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 3 Nov 2020 14:27:58 -0600 +Subject: [PATCH] libmultipath: factor out code to get vpd page data + +A future patch will reuse the code to get the vpd page data, so factor +it out from get_vpd_sgio(). + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/discovery.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index aa5942c3..eb1e735d 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -1239,11 +1239,10 @@ get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen) + return len; + } + +-int +-get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen) ++static int ++fetch_vpd_page(int fd, int pg, unsigned char *buff) + { +- int len, buff_len; +- unsigned char buff[4096]; ++ int buff_len; + + memset(buff, 0x0, 4096); + if (sgio_get_vpd(buff, 4096, fd, pg) < 0) { +@@ -1264,6 +1263,18 @@ get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen) + condlog(3, "vpd pg%02x page truncated", pg); + buff_len = 4096; + } ++ return buff_len; ++} ++ ++int ++get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen) ++{ ++ int len, buff_len; ++ unsigned char buff[4096]; ++ ++ buff_len = fetch_vpd_page(fd, pg, buff); ++ if (buff_len < 0) ++ return buff_len; + if (pg == 0x80) + len = parse_vpd_pg80(buff, str, maxlen); + else if (pg == 0x83) +-- +2.17.2 + diff --git a/SOURCES/0040-libmultipath-limit-reading-0xc9-vpd-page.patch b/SOURCES/0040-libmultipath-limit-reading-0xc9-vpd-page.patch new file mode 100644 index 0000000..d23bdaf --- /dev/null +++ b/SOURCES/0040-libmultipath-limit-reading-0xc9-vpd-page.patch @@ -0,0 +1,98 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 7 Oct 2020 21:43:02 -0500 +Subject: [PATCH] libmultipath: limit reading 0xc9 vpd page + +Only rdac arrays support 0xC9 vpd page inquiries. All other arrays will +return a failure. Only do the rdac inquiry when detecting array +capabilities if the array's path checker is explicitly set to rdac, or +the path checker is not set, and the array reports that it supports vpd +page 0xC9 in the Supported VPD Pages (0x00) vpd page. + +Multipath was doing the check if either the path checker was set to +rdac, or no path checker was set. This means that for almost all +non-rdac arrays, multipath was issuing a bad inquiry. This was annoying +users. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/discovery.c | 25 +++++++++++++++++++++++++ + libmultipath/discovery.h | 1 + + libmultipath/propsel.c | 10 ++++++---- + 3 files changed, 32 insertions(+), 4 deletions(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index eb1e735d..01aadba9 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -1266,6 +1266,31 @@ fetch_vpd_page(int fd, int pg, unsigned char *buff) + return buff_len; + } + ++/* heavily based on sg_inq.c from sg3_utils */ ++bool ++is_vpd_page_supported(int fd, int pg) ++{ ++ int i, len, buff_len; ++ unsigned char buff[4096]; ++ ++ buff_len = fetch_vpd_page(fd, 0x00, buff); ++ if (buff_len < 0) ++ return false; ++ if (buff_len < 4) { ++ condlog(3, "VPD page 00h too short"); ++ return false; ++ } ++ ++ len = buff[3] + 4; ++ if (len > buff_len) ++ condlog(3, "vpd page 00h trucated, expected %d, have %d", ++ len, buff_len); ++ for (i = 4; i < len; ++i) ++ if (buff[i] == pg) ++ return true; ++ return false; ++} ++ + int + get_vpd_sgio (int fd, int pg, int vend_id, char * str, int maxlen) + { +diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h +index 6444887d..d3193daf 100644 +--- a/libmultipath/discovery.h ++++ b/libmultipath/discovery.h +@@ -56,6 +56,7 @@ int sysfs_get_asymmetric_access_state(struct path *pp, + char *buff, int buflen); + 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); + + /* + * discovery bitmask +diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c +index d362beb4..d7febec6 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -496,13 +496,15 @@ check_rdac(struct path * pp) + { + int len; + char buff[44]; +- const char *checker_name; ++ const char *checker_name = NULL; + + if (pp->bus != SYSFS_BUS_SCSI) + return 0; +- /* Avoid ioctl if this is likely not an RDAC array */ +- if (__do_set_from_hwe(checker_name, pp, checker_name) && +- strcmp(checker_name, RDAC)) ++ /* Avoid checking 0xc9 if this is likely not an RDAC array */ ++ if (!__do_set_from_hwe(checker_name, pp, checker_name) && ++ !is_vpd_page_supported(pp->fd, 0xC9)) ++ return 0; ++ if (checker_name && strcmp(checker_name, RDAC)) + return 0; + len = get_vpd_sgio(pp->fd, 0xC9, 0, buff, 44); + if (len <= 0) +-- +2.17.2 + diff --git a/SOURCES/0041-libmultipath-add-device-to-hwtable.c.patch b/SOURCES/0041-libmultipath-add-device-to-hwtable.c.patch new file mode 100644 index 0000000..9d5e780 --- /dev/null +++ b/SOURCES/0041-libmultipath-add-device-to-hwtable.c.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Steve Schremmer +Date: Mon, 6 Jul 2020 20:22:35 +0000 +Subject: [PATCH] libmultipath: add device to hwtable.c + +Add FUJITSU ETERNUS_AHB defaults. + +Signed-off-by: Steve Schremmer +Reviewed-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + libmultipath/hwtable.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c +index d1fcfdb3..d680bdfc 100644 +--- a/libmultipath/hwtable.c ++++ b/libmultipath/hwtable.c +@@ -428,6 +428,22 @@ static struct hwentry default_hw[] = { + .pgpolicy = MULTIBUS, + .no_path_retry = 10, + }, ++ { ++ /* ++ * ETERNUS AB/HB ++ * Maintainer: NetApp RDAC team ++ */ ++ .vendor = "FUJITSU", ++ .product = "ETERNUS_AHB", ++ .bl_product = "Universal Xport", ++ .pgpolicy = GROUP_BY_PRIO, ++ .checker_name = RDAC, ++ .features = "2 pg_init_retries 50", ++ .hwhandler = "1 rdac", ++ .prio_name = PRIO_RDAC, ++ .pgfailback = -FAILBACK_IMMEDIATE, ++ .no_path_retry = 30, ++ }, + /* + * Hitachi Vantara + * +-- +2.17.2 + diff --git a/SOURCES/0042-libmultipath-move-fast_io_fail-defines-to-structs.h.patch b/SOURCES/0042-libmultipath-move-fast_io_fail-defines-to-structs.h.patch new file mode 100644 index 0000000..0cd6997 --- /dev/null +++ b/SOURCES/0042-libmultipath-move-fast_io_fail-defines-to-structs.h.patch @@ -0,0 +1,159 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 12 Oct 2020 16:06:11 -0500 +Subject: [PATCH] libmultipath: move fast_io_fail defines to structs.h + +Since fast_io_fail is part of the multipath struct, its symbolic values +belong in structs.h. Also, make it an instance of a general enum, which +will be used again in future patches, and change the set/print functions +which use it to use the general enum instead. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/config.h | 8 -------- + libmultipath/dict.c | 30 +++++++++++++++--------------- + libmultipath/dict.h | 2 +- + libmultipath/propsel.c | 2 +- + libmultipath/structs.h | 17 +++++++++++++++++ + 5 files changed, 34 insertions(+), 25 deletions(-) + +diff --git a/libmultipath/config.h b/libmultipath/config.h +index 160867cd..f38c7639 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -11,14 +11,6 @@ + #define ORIGIN_CONFIG 1 + #define ORIGIN_NO_CONFIG 2 + +-/* +- * In kernel, fast_io_fail == 0 means immediate failure on rport delete. +- * OTOH '0' means not-configured in various places in multipath-tools. +- */ +-#define MP_FAST_IO_FAIL_UNSET (0) +-#define MP_FAST_IO_FAIL_OFF (-1) +-#define MP_FAST_IO_FAIL_ZERO (-2) +- + enum devtypes { + DEV_NONE, + DEV_DEVT, +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index 184d4b22..ce8e1cda 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -834,7 +834,7 @@ declare_mp_attr_handler(gid, set_gid) + declare_mp_attr_snprint(gid, print_gid) + + static int +-set_fast_io_fail(vector strvec, void *ptr) ++set_undef_off_zero(vector strvec, void *ptr) + { + char * buff; + int *int_ptr = (int *)ptr; +@@ -844,36 +844,36 @@ set_fast_io_fail(vector strvec, void *ptr) + return 1; + + if (strcmp(buff, "off") == 0) +- *int_ptr = MP_FAST_IO_FAIL_OFF; ++ *int_ptr = UOZ_OFF; + else if (sscanf(buff, "%d", int_ptr) != 1 || +- *int_ptr < MP_FAST_IO_FAIL_ZERO) +- *int_ptr = MP_FAST_IO_FAIL_UNSET; ++ *int_ptr < UOZ_ZERO) ++ *int_ptr = UOZ_UNDEF; + else if (*int_ptr == 0) +- *int_ptr = MP_FAST_IO_FAIL_ZERO; ++ *int_ptr = UOZ_ZERO; + + FREE(buff); + return 0; + } + + int +-print_fast_io_fail(char * buff, int len, long v) ++print_undef_off_zero(char * buff, int len, long v) + { +- if (v == MP_FAST_IO_FAIL_UNSET) ++ if (v == UOZ_UNDEF) + return 0; +- if (v == MP_FAST_IO_FAIL_OFF) ++ if (v == UOZ_OFF) + return snprintf(buff, len, "\"off\""); +- if (v == MP_FAST_IO_FAIL_ZERO) ++ if (v == UOZ_ZERO) + return snprintf(buff, len, "0"); + return snprintf(buff, len, "%ld", v); + } + +-declare_def_handler(fast_io_fail, set_fast_io_fail) +-declare_def_snprint_defint(fast_io_fail, print_fast_io_fail, ++declare_def_handler(fast_io_fail, set_undef_off_zero) ++declare_def_snprint_defint(fast_io_fail, print_undef_off_zero, + DEFAULT_FAST_IO_FAIL) +-declare_ovr_handler(fast_io_fail, set_fast_io_fail) +-declare_ovr_snprint(fast_io_fail, print_fast_io_fail) +-declare_hw_handler(fast_io_fail, set_fast_io_fail) +-declare_hw_snprint(fast_io_fail, print_fast_io_fail) ++declare_ovr_handler(fast_io_fail, set_undef_off_zero) ++declare_ovr_snprint(fast_io_fail, print_undef_off_zero) ++declare_hw_handler(fast_io_fail, set_undef_off_zero) ++declare_hw_snprint(fast_io_fail, print_undef_off_zero) + + static int + set_dev_loss(vector strvec, void *ptr) +diff --git a/libmultipath/dict.h b/libmultipath/dict.h +index a40ac66f..a917e1ca 100644 +--- a/libmultipath/dict.h ++++ b/libmultipath/dict.h +@@ -13,7 +13,7 @@ int print_rr_weight(char *buff, int len, long v); + int print_pgfailback(char *buff, int len, long v); + int print_pgpolicy(char *buff, int len, long v); + int print_no_path_retry(char *buff, int len, long v); +-int print_fast_io_fail(char *buff, int len, long v); ++int print_undef_off_zero(char *buff, int len, long v); + int print_dev_loss(char *buff, int len, unsigned long v); + int print_reservation_key(char * buff, int len, struct be64 key, uint8_t + flags, int source); +diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c +index d7febec6..725db2b1 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -755,7 +755,7 @@ int select_fast_io_fail(struct config *conf, struct multipath *mp) + mp_set_conf(fast_io_fail); + mp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL); + out: +- print_fast_io_fail(buff, 12, mp->fast_io_fail); ++ print_undef_off_zero(buff, 12, mp->fast_io_fail); + condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias, buff, origin); + return 0; + } +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 8e78b8c0..29209984 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -229,6 +229,23 @@ enum vpd_vendor_ids { + VPD_VP_ARRAY_SIZE, /* This must remain the last entry */ + }; + ++/* ++ * Multipath treats 0 as undefined for optional config parameters. ++ * Use this for cases where 0 is a valid option for systems multipath ++ * is communicating with ++ */ ++enum undefined_off_zero { ++ UOZ_UNDEF = 0, ++ UOZ_OFF = -1, ++ UOZ_ZERO = -2, ++}; ++ ++enum fast_io_fail_states { ++ MP_FAST_IO_FAIL_UNSET = UOZ_UNDEF, ++ MP_FAST_IO_FAIL_OFF = UOZ_OFF, ++ MP_FAST_IO_FAIL_ZERO = UOZ_ZERO, ++}; ++ + struct vpd_vendor_page { + int pg; + const char *name; +-- +2.17.2 + diff --git a/SOURCES/0043-libmultipath-add-eh_deadline-multipath.conf-paramete.patch b/SOURCES/0043-libmultipath-add-eh_deadline-multipath.conf-paramete.patch new file mode 100644 index 0000000..a8ebb2c --- /dev/null +++ b/SOURCES/0043-libmultipath-add-eh_deadline-multipath.conf-paramete.patch @@ -0,0 +1,295 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 14 Oct 2020 18:38:20 -0500 +Subject: [PATCH] libmultipath: add eh_deadline multipath.conf parameter + +There are times a fc rport is never lost, meaning that fast_io_fail_tmo +and dev_loss_tmo never trigger, but scsi commands still hang. This can +cause problems in cases where users have string timing requirements, and +the easiest way to solve these issues is to set eh_deadline. Since it's +already possible to set fast_io_fail_tmo and dev_loss_tmo from +multipath.conf, and have multipath take care of setting it correctly for +the scsi devices in sysfs, it makes sense to allow users to set +eh_deadline here as well. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/config.c | 2 ++ + libmultipath/config.h | 2 ++ + libmultipath/configure.c | 1 + + libmultipath/dict.c | 10 +++++++ + libmultipath/discovery.c | 58 +++++++++++++++++++++++++++++++++----- + libmultipath/propsel.c | 17 +++++++++++ + libmultipath/propsel.h | 1 + + libmultipath/structs.h | 7 +++++ + multipath/multipath.conf.5 | 16 +++++++++++ + 9 files changed, 107 insertions(+), 7 deletions(-) + +diff --git a/libmultipath/config.c b/libmultipath/config.c +index 26f8e050..a71db2d0 100644 +--- a/libmultipath/config.c ++++ b/libmultipath/config.c +@@ -359,6 +359,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src) + merge_num(flush_on_last_del); + merge_num(fast_io_fail); + merge_num(dev_loss); ++ merge_num(eh_deadline); + merge_num(user_friendly_names); + merge_num(retain_hwhandler); + merge_num(detect_prio); +@@ -514,6 +515,7 @@ store_hwe (vector hwtable, struct hwentry * dhwe) + hwe->flush_on_last_del = dhwe->flush_on_last_del; + hwe->fast_io_fail = dhwe->fast_io_fail; + hwe->dev_loss = dhwe->dev_loss; ++ hwe->eh_deadline = dhwe->eh_deadline; + hwe->user_friendly_names = dhwe->user_friendly_names; + hwe->retain_hwhandler = dhwe->retain_hwhandler; + hwe->detect_prio = dhwe->detect_prio; +diff --git a/libmultipath/config.h b/libmultipath/config.h +index f38c7639..a22c1b4e 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -64,6 +64,7 @@ struct hwentry { + int flush_on_last_del; + int fast_io_fail; + unsigned int dev_loss; ++ int eh_deadline; + int user_friendly_names; + int retain_hwhandler; + int detect_prio; +@@ -149,6 +150,7 @@ struct config { + int attribute_flags; + int fast_io_fail; + unsigned int dev_loss; ++ int eh_deadline; + int log_checker_err; + int allow_queueing; + int find_multipaths; +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index 96c79610..b7113291 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -340,6 +340,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size, + select_gid(conf, mpp); + select_fast_io_fail(conf, mpp); + select_dev_loss(conf, mpp); ++ select_eh_deadline(conf, mpp); + select_reservation_key(conf, mpp); + select_deferred_remove(conf, mpp); + select_marginal_path_err_sample_time(conf, mpp); +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index ce8e1cda..8fd91d8c 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -911,6 +911,13 @@ declare_ovr_snprint(dev_loss, print_dev_loss) + declare_hw_handler(dev_loss, set_dev_loss) + declare_hw_snprint(dev_loss, print_dev_loss) + ++declare_def_handler(eh_deadline, set_undef_off_zero) ++declare_def_snprint(eh_deadline, print_undef_off_zero) ++declare_ovr_handler(eh_deadline, set_undef_off_zero) ++declare_ovr_snprint(eh_deadline, print_undef_off_zero) ++declare_hw_handler(eh_deadline, set_undef_off_zero) ++declare_hw_snprint(eh_deadline, print_undef_off_zero) ++ + static int + set_pgpolicy(vector strvec, void *ptr) + { +@@ -1776,6 +1783,7 @@ init_keywords(vector keywords) + install_keyword("gid", &def_gid_handler, &snprint_def_gid); + install_keyword("fast_io_fail_tmo", &def_fast_io_fail_handler, &snprint_def_fast_io_fail); + install_keyword("dev_loss_tmo", &def_dev_loss_handler, &snprint_def_dev_loss); ++ install_keyword("eh_deadline", &def_eh_deadline_handler, &snprint_def_eh_deadline); + install_keyword("bindings_file", &def_bindings_file_handler, &snprint_def_bindings_file); + install_keyword("wwids_file", &def_wwids_file_handler, &snprint_def_wwids_file); + install_keyword("prkeys_file", &def_prkeys_file_handler, &snprint_def_prkeys_file); +@@ -1885,6 +1893,7 @@ init_keywords(vector keywords) + install_keyword("flush_on_last_del", &hw_flush_on_last_del_handler, &snprint_hw_flush_on_last_del); + install_keyword("fast_io_fail_tmo", &hw_fast_io_fail_handler, &snprint_hw_fast_io_fail); + install_keyword("dev_loss_tmo", &hw_dev_loss_handler, &snprint_hw_dev_loss); ++ install_keyword("eh_deadline", &hw_eh_deadline_handler, &snprint_hw_eh_deadline); + install_keyword("user_friendly_names", &hw_user_friendly_names_handler, &snprint_hw_user_friendly_names); + install_keyword("retain_attached_hw_handler", &hw_retain_hwhandler_handler, &snprint_hw_retain_hwhandler); + install_keyword("detect_prio", &hw_detect_prio_handler, &snprint_hw_detect_prio); +@@ -1925,6 +1934,7 @@ init_keywords(vector keywords) + install_keyword("flush_on_last_del", &ovr_flush_on_last_del_handler, &snprint_ovr_flush_on_last_del); + install_keyword("fast_io_fail_tmo", &ovr_fast_io_fail_handler, &snprint_ovr_fast_io_fail); + install_keyword("dev_loss_tmo", &ovr_dev_loss_handler, &snprint_ovr_dev_loss); ++ install_keyword("eh_deadline", &ovr_eh_deadline_handler, &snprint_ovr_eh_deadline); + install_keyword("user_friendly_names", &ovr_user_friendly_names_handler, &snprint_ovr_user_friendly_names); + install_keyword("retain_attached_hw_handler", &ovr_retain_hwhandler_handler, &snprint_ovr_retain_hwhandler); + install_keyword("detect_prio", &ovr_detect_prio_handler, &snprint_ovr_detect_prio); +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index 01aadba9..a328aafa 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -577,6 +577,42 @@ sysfs_get_asymmetric_access_state(struct path *pp, char *buff, int buflen) + return !!preferred; + } + ++static int ++sysfs_set_eh_deadline(struct multipath *mpp, struct path *pp) ++{ ++ struct udev_device *hostdev; ++ char host_name[HOST_NAME_LEN], value[16]; ++ int ret; ++ ++ if (mpp->eh_deadline == EH_DEADLINE_UNSET) ++ return 0; ++ ++ sprintf(host_name, "host%d", pp->sg_id.host_no); ++ hostdev = udev_device_new_from_subsystem_sysname(udev, ++ "scsi_host", host_name); ++ if (!hostdev) ++ return 1; ++ ++ if (mpp->eh_deadline == EH_DEADLINE_OFF) ++ sprintf(value, "off"); ++ else if (mpp->eh_deadline == EH_DEADLINE_ZERO) ++ sprintf(value, "0"); ++ else ++ snprintf(value, 16, "%u", mpp->eh_deadline); ++ ++ ret = sysfs_attr_set_value(hostdev, "eh_deadline", ++ value, strlen(value)); ++ /* ++ * not all scsi drivers support setting eh_deadline, so failing ++ * is totally reasonable ++ */ ++ if (ret <= 0) ++ condlog(4, "%s: failed to set eh_deadline to %s, error %d", udev_device_get_sysname(hostdev), value, -ret); ++ ++ udev_device_unref(hostdev); ++ return (ret <= 0); ++} ++ + static void + sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) + { +@@ -787,16 +823,24 @@ sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint) + mpp->alias, mpp->fast_io_fail); + mpp->fast_io_fail = MP_FAST_IO_FAIL_OFF; + } +- if (!mpp->dev_loss && mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET) ++ if (!mpp->dev_loss && mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET && ++ mpp->eh_deadline == EH_DEADLINE_UNSET) + return 0; + + vector_foreach_slot(mpp->paths, pp, i) { +- if (pp->sg_id.proto_id == SCSI_PROTOCOL_FCP) +- sysfs_set_rport_tmo(mpp, pp); +- if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI) +- sysfs_set_session_tmo(mpp, pp); +- if (pp->sg_id.proto_id == SCSI_PROTOCOL_SAS) +- sysfs_set_nexus_loss_tmo(mpp, pp); ++ if (pp->bus != SYSFS_BUS_SCSI) ++ continue; ++ ++ if (mpp->dev_loss || ++ mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { ++ if (pp->sg_id.proto_id == SCSI_PROTOCOL_FCP) ++ sysfs_set_rport_tmo(mpp, pp); ++ else if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI) ++ sysfs_set_session_tmo(mpp, pp); ++ else if (pp->sg_id.proto_id == SCSI_PROTOCOL_SAS) ++ sysfs_set_nexus_loss_tmo(mpp, pp); ++ } ++ sysfs_set_eh_deadline(mpp, pp); + } + return 0; + } +diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c +index 725db2b1..1150cfe8 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -776,6 +776,23 @@ out: + return 0; + } + ++int select_eh_deadline(struct config *conf, struct multipath *mp) ++{ ++ const char *origin; ++ char buff[12]; ++ ++ mp_set_ovr(eh_deadline); ++ mp_set_hwe(eh_deadline); ++ mp_set_conf(eh_deadline); ++ mp->eh_deadline = EH_DEADLINE_UNSET; ++ /* not changing sysfs in default cause, so don't print anything */ ++ return 0; ++out: ++ print_undef_off_zero(buff, 12, mp->eh_deadline); ++ condlog(3, "%s: eh_deadline = %s %s", mp->alias, buff, origin); ++ return 0; ++} ++ + int select_flush_on_last_del(struct config *conf, struct multipath *mp) + { + const char *origin; +diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h +index 3d6edd8a..a68bacf0 100644 +--- a/libmultipath/propsel.h ++++ b/libmultipath/propsel.h +@@ -17,6 +17,7 @@ int select_uid(struct config *conf, struct multipath *mp); + int select_gid(struct config *conf, struct multipath *mp); + int select_fast_io_fail(struct config *conf, struct multipath *mp); + int select_dev_loss(struct config *conf, struct multipath *mp); ++int select_eh_deadline(struct config *conf, struct multipath *mp); + int select_reservation_key(struct config *conf, struct multipath *mp); + int select_retain_hwhandler (struct config *conf, struct multipath * mp); + int select_detect_prio(struct config *conf, struct path * pp); +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 29209984..65542dea 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -246,6 +246,12 @@ enum fast_io_fail_states { + MP_FAST_IO_FAIL_ZERO = UOZ_ZERO, + }; + ++enum eh_deadline_states { ++ EH_DEADLINE_UNSET = UOZ_UNDEF, ++ EH_DEADLINE_OFF = UOZ_OFF, ++ EH_DEADLINE_ZERO = UOZ_ZERO, ++}; ++ + struct vpd_vendor_page { + int pg; + const char *name; +@@ -366,6 +372,7 @@ struct multipath { + int ghost_delay; + int ghost_delay_tick; + unsigned int dev_loss; ++ int eh_deadline; + uid_t uid; + gid_t gid; + mode_t mode; +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 6dc26f10..60954574 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -700,6 +700,22 @@ The default is: \fB600\fR + . + . + .TP ++.B eh_deadline ++Specify the maximum number of seconds the SCSI layer will spend doing error ++handling when scsi devices fail. After this timeout the scsi layer will perform ++a full HBA reset. Setting this may be necessary in cases where the rport is ++never lost, so \fIfast_io_fail_tmo\fR and \fIdev_loss_tmo\fR will never ++trigger, but (frequently do to load) scsi commands still hang. \fBNote:\fR when ++the scsi error handler performs the HBA reset, all target paths on that HBA ++will be affected. eh_deadline should only be set in cases where all targets on ++the affected HBAs are multipathed. ++.RS ++.TP ++The default is: \fB\fR ++.RE ++. ++. ++.TP + .B bindings_file + The full pathname of the binding file to be used when the user_friendly_names + option is set. +-- +2.17.2 + diff --git a/SOURCES/0044-libmultipath-don-t-dlclose-tur-checker-DSO.patch b/SOURCES/0044-libmultipath-don-t-dlclose-tur-checker-DSO.patch new file mode 100644 index 0000000..18ed578 --- /dev/null +++ b/SOURCES/0044-libmultipath-don-t-dlclose-tur-checker-DSO.patch @@ -0,0 +1,89 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 23 Oct 2020 11:38:24 -0500 +Subject: [PATCH] libmultipath: don't dlclose tur checker DSO + +The multipathd tur checker thread is designed to be able to finish at +any time, even after the tur checker itself has been freed. The +multipathd shutdown code makes sure all the checkers have been freed +before freeing the checker_class and calling dlclose() to unload the +DSO, but this doesn't guarantee that the checker threads have finished. +If one hasn't, the DSO will get unloaded while the thread still running +code from it, causing a segfault. Unfortunately, it's not possible to be +sure that all tur checker threads have ended during shutdown, without +making them joinable. + +However, since libmultipath will never be reinitialized after it has +been uninitialzed, not dlclosing the tur checker DSO once a thread is +started has minimal cost (keeping the DSO code around until the program +exits, which usually happens right after freeing the checkers). + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/checkers.c | 10 +++++++++- + libmultipath/checkers.h | 1 + + libmultipath/checkers/tur.c | 1 + + 3 files changed, 11 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c +index 8d2be8a9..6359e5d8 100644 +--- a/libmultipath/checkers.c ++++ b/libmultipath/checkers.c +@@ -21,6 +21,7 @@ struct checker_class { + void (*reset)(void); /* to reset the global variables */ + const char **msgtable; + short msgtable_size; ++ int keep_dso; + }; + + char *checker_state_names[] = { +@@ -69,7 +70,7 @@ void free_checker_class(struct checker_class *c) + list_del(&c->node); + if (c->reset) + c->reset(); +- if (c->handle) { ++ if (c->handle && !c->keep_dso) { + if (dlclose(c->handle) != 0) { + condlog(0, "Cannot unload checker %s: %s", + c->name, dlerror()); +@@ -192,6 +193,13 @@ out: + return NULL; + } + ++void checker_keep_dso(struct checker * c) ++{ ++ if (!c || !c->cls) ++ return; ++ c->cls->keep_dso = 1; ++} ++ + void checker_set_fd (struct checker * c, int fd) + { + if (!c) +diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h +index b458118d..f183cff9 100644 +--- a/libmultipath/checkers.h ++++ b/libmultipath/checkers.h +@@ -145,6 +145,7 @@ void checker_reset (struct checker *); + void checker_set_sync (struct checker *); + void checker_set_async (struct checker *); + void checker_set_fd (struct checker *, int); ++void checker_keep_dso(struct checker *c); + void checker_enable (struct checker *); + void checker_disable (struct checker *); + int checker_check (struct checker *, int); +diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c +index e886fcf8..fd58d62a 100644 +--- a/libmultipath/checkers/tur.c ++++ b/libmultipath/checkers/tur.c +@@ -394,6 +394,7 @@ int libcheck_check(struct checker * c) + uatomic_set(&ct->running, 1); + tur_set_async_timeout(c); + setup_thread_attr(&attr, 32 * 1024, 1); ++ checker_keep_dso(c); + r = pthread_create(&ct->thread, &attr, tur_thread, ct); + pthread_attr_destroy(&attr); + if (r) { +-- +2.17.2 + diff --git a/SOURCES/0045-libmultipath-warn-about-missing-braces-at-end-of-mul.patch b/SOURCES/0045-libmultipath-warn-about-missing-braces-at-end-of-mul.patch new file mode 100644 index 0000000..f8f45f8 --- /dev/null +++ b/SOURCES/0045-libmultipath-warn-about-missing-braces-at-end-of-mul.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 16 Nov 2020 13:52:09 -0600 +Subject: [PATCH] libmultipath: warn about missing braces at end of + multipath.conf + +Multipath doesn't warn when multipath.conf is missing closing braces at +the end of the file. This has confused people about the correct config +file syntax, so add a warning. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/parser.c | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/parser.c b/libmultipath/parser.c +index a7285a35..48b54e87 100644 +--- a/libmultipath/parser.c ++++ b/libmultipath/parser.c +@@ -544,7 +544,7 @@ process_stream(struct config *conf, FILE *stream, vector keywords, char *file) + if (!strcmp(str, EOB)) { + if (kw_level > 0) { + free_strvec(strvec); +- break; ++ goto out; + } + condlog(0, "unmatched '%s' at line %d of %s", + EOB, line_nr, file); +@@ -583,7 +583,8 @@ process_stream(struct config *conf, FILE *stream, vector keywords, char *file) + + free_strvec(strvec); + } +- ++ if (kw_level == 1) ++ condlog(1, "missing '%s' at end of %s", EOB, file); + out: + FREE(buf); + free_uniques(uniques); +-- +2.17.2 + diff --git a/SOURCES/0046-libmultipath-ignore-multipaths-sections-without-wwid.patch b/SOURCES/0046-libmultipath-ignore-multipaths-sections-without-wwid.patch new file mode 100644 index 0000000..3ccf175 --- /dev/null +++ b/SOURCES/0046-libmultipath-ignore-multipaths-sections-without-wwid.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 16 Nov 2020 16:38:21 -0600 +Subject: [PATCH] libmultipath: ignore multipaths sections without wwid option + +"multipathd show config local" was crashing in find_mp_by_wwid() if +the multipath configuration included a multipaths section that did +not set a wwid option. There is no reason to keep a mpentry that +didn't set its wwid. Remove it in merge_mptable(). + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/config.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/libmultipath/config.c b/libmultipath/config.c +index a71db2d0..dc81c994 100644 +--- a/libmultipath/config.c ++++ b/libmultipath/config.c +@@ -444,6 +444,13 @@ void merge_mptable(vector mptable) + int i, j; + + vector_foreach_slot(mptable, mp1, i) { ++ /* drop invalid multipath configs */ ++ if (!mp1->wwid) { ++ condlog(0, "multipaths config section missing wwid"); ++ vector_del_slot(mptable, i--); ++ free_mpe(mp1); ++ continue; ++ } + j = i + 1; + vector_foreach_slot_after(mptable, mp2, j) { + if (strcmp(mp1->wwid, mp2->wwid)) +-- +2.17.2 + diff --git a/SOURCES/0047-tests-fix-missing-priority-blacklist-test.patch b/SOURCES/0047-tests-fix-missing-priority-blacklist-test.patch new file mode 100644 index 0000000..a73ffe6 --- /dev/null +++ b/SOURCES/0047-tests-fix-missing-priority-blacklist-test.patch @@ -0,0 +1,25 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 16 Nov 2020 23:21:41 -0600 +Subject: [PATCH] tests: fix missing priority blacklist test + +Signed-off-by: Benjamin Marzinski +--- + tests/blacklist.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/tests/blacklist.c b/tests/blacklist.c +index d20e97af..401600d9 100644 +--- a/tests/blacklist.c ++++ b/tests/blacklist.c +@@ -378,7 +378,6 @@ 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"); + assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"), + MATCH_NOTHING); + assert_int_equal(filter_property(&conf, &udev, 3, "ID_BLAH"), +-- +2.17.2 + diff --git a/SOURCES/0048-mpathpersist-Fix-Register-and-Ignore-with-0x00-SARK.patch b/SOURCES/0048-mpathpersist-Fix-Register-and-Ignore-with-0x00-SARK.patch new file mode 100644 index 0000000..fa9d703 --- /dev/null +++ b/SOURCES/0048-mpathpersist-Fix-Register-and-Ignore-with-0x00-SARK.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 5 Nov 2020 09:15:43 -0600 +Subject: [PATCH] mpathpersist: Fix Register and Ignore with 0x00 SARK + +When the Register and Ignore command is run with sg_persist, if a 0x00 +Service Action Reservation Key is given or the --param-sark option is +not used at all, sg_persist will clear the registration. mpathpersist +will fail with an error. This patch fixes mpathpersist to work like +sg_persist in this case. + +Signed-off-by: Benjamin Marzinski +--- + libmpathpersist/mpath_persist.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c +index 3da7a6cf..aa196008 100644 +--- a/libmpathpersist/mpath_persist.c ++++ b/libmpathpersist/mpath_persist.c +@@ -321,7 +321,8 @@ int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, + } + + if (memcmp(paramp->key, &mpp->reservation_key, 8) && +- memcmp(paramp->sa_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; +-- +2.17.2 + diff --git a/SOURCES/0049-mpathpersist-update-prkeys-file-on-changing-registra.patch b/SOURCES/0049-mpathpersist-update-prkeys-file-on-changing-registra.patch new file mode 100644 index 0000000..0cd0ab2 --- /dev/null +++ b/SOURCES/0049-mpathpersist-update-prkeys-file-on-changing-registra.patch @@ -0,0 +1,38 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 23 Nov 2020 20:45:50 -0600 +Subject: [PATCH] mpathpersist: update prkeys file on changing registrations + +When the "reservation_key" option is set to "file" and Register command +is run with both the current Reservation Key and a new Service Action +Reservation Key, mpathpersist will change the registration, but will not +update the prkeys file. This means that future paths that come online +will not be able to register, since multipathd is still using the old +reservation key. Fix this. + +Signed-off-by: Benjamin Marzinski +--- + libmpathpersist/mpath_persist.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c +index aa196008..a01dfb0b 100644 +--- a/libmpathpersist/mpath_persist.c ++++ b/libmpathpersist/mpath_persist.c +@@ -307,9 +307,10 @@ int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, + + memcpy(&prkey, paramp->sa_key, 8); + if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey && +- ((!get_be64(mpp->reservation_key) && +- rq_servact == MPATH_PROUT_REG_SA) || +- rq_servact == MPATH_PROUT_REG_IGN_SA)) { ++ (rq_servact == MPATH_PROUT_REG_IGN_SA || ++ (rq_servact == MPATH_PROUT_REG_SA && ++ (!get_be64(mpp->reservation_key) || ++ memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) { + memcpy(&mpp->reservation_key, paramp->sa_key, 8); + if (update_prkey_flags(alias, get_be64(mpp->reservation_key), + paramp->sa_flags)) { +-- +2.17.2 + diff --git a/SOURCES/0050-multipathd-Fix-multipathd-stopping-on-shutdown.patch b/SOURCES/0050-multipathd-Fix-multipathd-stopping-on-shutdown.patch new file mode 100644 index 0000000..a9f8969 --- /dev/null +++ b/SOURCES/0050-multipathd-Fix-multipathd-stopping-on-shutdown.patch @@ -0,0 +1,37 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 14 Dec 2020 14:16:29 -0600 +Subject: [PATCH] multipathd: Fix multipathd stopping on shutdown + +According to man "systemd.special" + +"shutdown.target: ... Services that shall be terminated on system +shutdown shall add Conflicts= and Before= dependencies to this unit for +their service unit, which is implicitly done when +DefaultDependencies=yes is set (the default)." + +multipathd.service sets DefaultDependencies=no and includes the +Conflits= dependency, but not the Before= one. This can cause multipathd +to continue running past when it is supposed to during shutdown. + +Signed-off-by: Benjamin Marzinski +--- + multipathd/multipathd.service | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/multipathd/multipathd.service b/multipathd/multipathd.service +index 0fbcc46b..bc8fa07a 100644 +--- a/multipathd/multipathd.service ++++ b/multipathd/multipathd.service +@@ -2,7 +2,7 @@ + Description=Device-Mapper Multipath Device Controller + 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 ++Before=local-fs-pre.target blk-availability.service shutdown.target + After=multipathd.socket systemd-udev-trigger.service systemd-udev-settle.service + ConditionPathExists=/etc/multipath.conf + DefaultDependencies=no +-- +2.17.2 + diff --git a/SOURCES/0051-multipath.conf.5-Improve-checker_timeout-description.patch b/SOURCES/0051-multipath.conf.5-Improve-checker_timeout-description.patch new file mode 100644 index 0000000..1e1f249 --- /dev/null +++ b/SOURCES/0051-multipath.conf.5-Improve-checker_timeout-description.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 15 Dec 2020 12:47:14 -0600 +Subject: [PATCH] multipath.conf.5: Improve checker_timeout description + +I was asked to explain how checker_timeout works for checkers like +directio, that don't issue scsi commands with an explicit timeout + +Signed-off-by: Benjamin Marzinski +--- + multipath/multipath.conf.5 | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 60954574..a5686090 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -634,8 +634,13 @@ The default is: \fBno\fR + . + .TP + .B checker_timeout +-Specify the timeout to use for path checkers and prioritizers that issue SCSI +-commands with an explicit timeout, in seconds. ++Specify the timeout to use for path checkers and prioritizers, in seconds. ++Only prioritizers that issue scsi commands use checker_timeout. Checkers ++that support an asynchronous mode (\fItur\fR and \fIdirectio\fR), will ++return shortly after being called by multipathd, regardless of whether the ++storage array responds. If the storage array hasn't responded, mulitpathd will ++check for a response every second, until \fIchecker_timeout\fR seconds have ++elapsed. + .RS + .TP + The default is: in \fB/sys/block/sd/device/timeout\fR +-- +2.17.2 + diff --git a/SOURCES/0052-libmultipath-check-for-null-wwid-before-strcmp.patch b/SOURCES/0052-libmultipath-check-for-null-wwid-before-strcmp.patch new file mode 100644 index 0000000..fe33783 --- /dev/null +++ b/SOURCES/0052-libmultipath-check-for-null-wwid-before-strcmp.patch @@ -0,0 +1,34 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 4 Jan 2021 21:59:54 -0600 +Subject: [PATCH] libmultipath: check for null wwid before strcmp + +Commit 749aabd0 (libmultipath: ignore multipaths sections without wwid +option) removed all mpentries with a NULL wwid, but didn't stop strcmp() +from being run on them in merge_mptable(). The result of strcmp() with +a NULL parameter is undefined, so fix that. + +Signed-off-by: Benjamin Marzinski + +Reviewed-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + libmultipath/config.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libmultipath/config.c b/libmultipath/config.c +index dc81c994..dd645f17 100644 +--- a/libmultipath/config.c ++++ b/libmultipath/config.c +@@ -453,7 +453,7 @@ void merge_mptable(vector mptable) + } + j = i + 1; + vector_foreach_slot_after(mptable, mp2, j) { +- if (strcmp(mp1->wwid, mp2->wwid)) ++ if (!mp2->wwid || strcmp(mp1->wwid, mp2->wwid)) + continue; + condlog(1, "%s: duplicate multipath config section for %s", + __func__, mp1->wwid); +-- +2.17.2 + diff --git a/SOURCES/0053-libmultipath-make-find_err_path_by_dev-static.patch b/SOURCES/0053-libmultipath-make-find_err_path_by_dev-static.patch new file mode 100644 index 0000000..8375b10 --- /dev/null +++ b/SOURCES/0053-libmultipath-make-find_err_path_by_dev-static.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 14 Jan 2021 20:20:22 -0600 +Subject: [PATCH] libmultipath: make find_err_path_by_dev() static + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/io_err_stat.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c +index 1b9cd6c0..449760a0 100644 +--- a/libmultipath/io_err_stat.c ++++ b/libmultipath/io_err_stat.c +@@ -89,7 +89,7 @@ static void rcu_unregister(__attribute__((unused)) void *param) + rcu_unregister_thread(); + } + +-struct io_err_stat_path *find_err_path_by_dev(vector pathvec, char *dev) ++static struct io_err_stat_path *find_err_path_by_dev(vector pathvec, char *dev) + { + int i; + struct io_err_stat_path *pp; +-- +2.17.2 + diff --git a/SOURCES/0054-multipathd-avoid-io_err_stat-crash-during-shutdown.patch b/SOURCES/0054-multipathd-avoid-io_err_stat-crash-during-shutdown.patch new file mode 100644 index 0000000..67aa4e2 --- /dev/null +++ b/SOURCES/0054-multipathd-avoid-io_err_stat-crash-during-shutdown.patch @@ -0,0 +1,291 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 14 Jan 2021 20:20:23 -0600 +Subject: [PATCH] multipathd: avoid io_err_stat crash during shutdown + +The checker thread is reponsible for enqueueing paths for the +io_err_stat thread to check. During shutdown, the io_err_stat thread is +shut down and cleaned up before the checker thread. There is no code +to make sure that the checker thread isn't accessing the io_err_stat +pathvec or its mutex while they are being freed, which can lead to +memory corruption crashes. + +To solve this, get rid of the io_err_stat_pathvec structure, and +statically define the mutex. This means that the mutex is always valid +to access, and the io_err_stat pathvec can only be accessed while +holding it. If the io_err_stat thread has already been cleaned up +when the checker tries to access the pathvec, it will be NULL, and the +checker will simply fail to enqueue the path. + +This change also fixes a bug in free_io_err_pathvec(), which previously +only attempted to free the pathvec if it was not set, instead of when it +was set. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/io_err_stat.c | 112 +++++++++++++++---------------------- + libmultipath/util.c | 5 ++ + libmultipath/util.h | 1 + + 3 files changed, 50 insertions(+), 68 deletions(-) + +diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c +index 449760a0..f6c564f0 100644 +--- a/libmultipath/io_err_stat.c ++++ b/libmultipath/io_err_stat.c +@@ -34,6 +34,7 @@ + #include "lock.h" + #include "time-util.h" + #include "io_err_stat.h" ++#include "util.h" + + #define IOTIMEOUT_SEC 60 + #define TIMEOUT_NO_IO_NSEC 10000000 /*10ms = 10000000ns*/ +@@ -46,12 +47,6 @@ + #define io_err_stat_log(prio, fmt, args...) \ + condlog(prio, "io error statistic: " fmt, ##args) + +- +-struct io_err_stat_pathvec { +- pthread_mutex_t mutex; +- vector pathvec; +-}; +- + struct dio_ctx { + struct timespec io_starttime; + unsigned int blksize; +@@ -76,9 +71,10 @@ pthread_attr_t io_err_stat_attr; + + static pthread_mutex_t io_err_thread_lock = PTHREAD_MUTEX_INITIALIZER; + static pthread_cond_t io_err_thread_cond = PTHREAD_COND_INITIALIZER; ++static pthread_mutex_t io_err_pathvec_lock = PTHREAD_MUTEX_INITIALIZER; + static int io_err_thread_running = 0; + +-static struct io_err_stat_pathvec *paths; ++static vector io_err_pathvec; + struct vectors *vecs; + io_context_t ioctx; + +@@ -208,46 +204,23 @@ static void free_io_err_stat_path(struct io_err_stat_path *p) + FREE(p); + } + +-static struct io_err_stat_pathvec *alloc_pathvec(void) +-{ +- struct io_err_stat_pathvec *p; +- int r; +- +- p = (struct io_err_stat_pathvec *)MALLOC(sizeof(*p)); +- if (!p) +- return NULL; +- p->pathvec = vector_alloc(); +- if (!p->pathvec) +- goto out_free_struct_pathvec; +- r = pthread_mutex_init(&p->mutex, NULL); +- if (r) +- goto out_free_member_pathvec; +- +- return p; +- +-out_free_member_pathvec: +- vector_free(p->pathvec); +-out_free_struct_pathvec: +- FREE(p); +- return NULL; +-} +- +-static void free_io_err_pathvec(struct io_err_stat_pathvec *p) ++static void free_io_err_pathvec(void) + { + struct io_err_stat_path *path; + int i; + +- if (!p) +- return; +- pthread_mutex_destroy(&p->mutex); +- if (!p->pathvec) { +- vector_foreach_slot(p->pathvec, path, i) { +- destroy_directio_ctx(path); +- free_io_err_stat_path(path); +- } +- vector_free(p->pathvec); ++ pthread_mutex_lock(&io_err_pathvec_lock); ++ pthread_cleanup_push(cleanup_mutex, &io_err_pathvec_lock); ++ if (!io_err_pathvec) ++ goto out; ++ vector_foreach_slot(io_err_pathvec, path, i) { ++ destroy_directio_ctx(path); ++ free_io_err_stat_path(path); + } +- FREE(p); ++ vector_free(io_err_pathvec); ++ io_err_pathvec = NULL; ++out: ++ pthread_cleanup_pop(1); + } + + /* +@@ -259,13 +232,13 @@ static int enqueue_io_err_stat_by_path(struct path *path) + { + struct io_err_stat_path *p; + +- pthread_mutex_lock(&paths->mutex); +- p = find_err_path_by_dev(paths->pathvec, path->dev); ++ pthread_mutex_lock(&io_err_pathvec_lock); ++ p = find_err_path_by_dev(io_err_pathvec, path->dev); + if (p) { +- pthread_mutex_unlock(&paths->mutex); ++ pthread_mutex_unlock(&io_err_pathvec_lock); + return 0; + } +- pthread_mutex_unlock(&paths->mutex); ++ pthread_mutex_unlock(&io_err_pathvec_lock); + + p = alloc_io_err_stat_path(); + if (!p) +@@ -277,18 +250,18 @@ static int enqueue_io_err_stat_by_path(struct path *path) + + if (setup_directio_ctx(p)) + goto free_ioerr_path; +- pthread_mutex_lock(&paths->mutex); +- if (!vector_alloc_slot(paths->pathvec)) ++ pthread_mutex_lock(&io_err_pathvec_lock); ++ if (!vector_alloc_slot(io_err_pathvec)) + goto unlock_destroy; +- vector_set_slot(paths->pathvec, p); +- pthread_mutex_unlock(&paths->mutex); ++ vector_set_slot(io_err_pathvec, p); ++ pthread_mutex_unlock(&io_err_pathvec_lock); + + io_err_stat_log(2, "%s: enqueue path %s to check", + path->mpp->alias, path->dev); + return 0; + + unlock_destroy: +- pthread_mutex_unlock(&paths->mutex); ++ pthread_mutex_unlock(&io_err_pathvec_lock); + destroy_directio_ctx(p); + free_ioerr_path: + free_io_err_stat_path(p); +@@ -421,9 +394,9 @@ static int delete_io_err_stat_by_addr(struct io_err_stat_path *p) + { + int i; + +- i = find_slot(paths->pathvec, p); ++ i = find_slot(io_err_pathvec, p); + if (i != -1) +- vector_del_slot(paths->pathvec, i); ++ vector_del_slot(io_err_pathvec, i); + + destroy_directio_ctx(p); + free_io_err_stat_path(p); +@@ -594,7 +567,7 @@ static void poll_async_io_timeout(void) + + if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0) + return; +- vector_foreach_slot(paths->pathvec, pp, i) { ++ vector_foreach_slot(io_err_pathvec, pp, i) { + for (j = 0; j < CONCUR_NR_EVENT; j++) { + rc = try_to_cancel_timeout_io(pp->dio_ctx_array + j, + &curr_time, pp->devname); +@@ -640,7 +613,7 @@ static void handle_async_io_done_event(struct io_event *io_evt) + int rc = PATH_UNCHECKED; + int i, j; + +- vector_foreach_slot(paths->pathvec, pp, i) { ++ vector_foreach_slot(io_err_pathvec, pp, i) { + for (j = 0; j < CONCUR_NR_EVENT; j++) { + ct = pp->dio_ctx_array + j; + if (&ct->io == io_evt->obj) { +@@ -674,19 +647,14 @@ static void service_paths(void) + struct io_err_stat_path *pp; + int i; + +- pthread_mutex_lock(&paths->mutex); +- vector_foreach_slot(paths->pathvec, pp, i) { ++ pthread_mutex_lock(&io_err_pathvec_lock); ++ vector_foreach_slot(io_err_pathvec, pp, i) { + send_batch_async_ios(pp); + process_async_ios_event(TIMEOUT_NO_IO_NSEC, pp->devname); + poll_async_io_timeout(); + poll_io_err_stat(vecs, pp); + } +- pthread_mutex_unlock(&paths->mutex); +-} +- +-static void cleanup_unlock(void *arg) +-{ +- pthread_mutex_unlock((pthread_mutex_t*) arg); ++ pthread_mutex_unlock(&io_err_pathvec_lock); + } + + static void cleanup_exited(__attribute__((unused)) void *arg) +@@ -744,12 +712,17 @@ int start_io_err_stat_thread(void *data) + io_err_stat_log(4, "io_setup failed"); + return 1; + } +- paths = alloc_pathvec(); +- if (!paths) ++ ++ pthread_mutex_lock(&io_err_pathvec_lock); ++ io_err_pathvec = vector_alloc(); ++ if (!io_err_pathvec) { ++ pthread_mutex_unlock(&io_err_pathvec_lock); + goto destroy_ctx; ++ } ++ pthread_mutex_unlock(&io_err_pathvec_lock); + + pthread_mutex_lock(&io_err_thread_lock); +- pthread_cleanup_push(cleanup_unlock, &io_err_thread_lock); ++ pthread_cleanup_push(cleanup_mutex, &io_err_thread_lock); + + ret = pthread_create(&io_err_stat_thr, &io_err_stat_attr, + io_err_stat_loop, data); +@@ -769,7 +742,10 @@ int start_io_err_stat_thread(void *data) + return 0; + + out_free: +- free_io_err_pathvec(paths); ++ pthread_mutex_lock(&io_err_pathvec_lock); ++ vector_free(io_err_pathvec); ++ io_err_pathvec = NULL; ++ pthread_mutex_unlock(&io_err_pathvec_lock); + destroy_ctx: + io_destroy(ioctx); + io_err_stat_log(0, "failed to start io_error statistic thread"); +@@ -785,6 +761,6 @@ void stop_io_err_stat_thread(void) + pthread_cancel(io_err_stat_thr); + + pthread_join(io_err_stat_thr, NULL); +- free_io_err_pathvec(paths); ++ free_io_err_pathvec(); + io_destroy(ioctx); + } +diff --git a/libmultipath/util.c b/libmultipath/util.c +index 51c38c87..dd30a46e 100644 +--- a/libmultipath/util.c ++++ b/libmultipath/util.c +@@ -469,3 +469,8 @@ void close_fd(void *arg) + { + close((long)arg); + } ++ ++void cleanup_mutex(void *arg) ++{ ++ pthread_mutex_unlock(arg); ++} +diff --git a/libmultipath/util.h b/libmultipath/util.h +index 56bd78c7..ce277680 100644 +--- a/libmultipath/util.h ++++ b/libmultipath/util.h +@@ -44,6 +44,7 @@ void set_max_fds(rlim_t max_fds); + pthread_cleanup_push(((void (*)(void *))&f), (arg)) + + void close_fd(void *arg); ++void cleanup_mutex(void *arg); + + struct scandir_result { + struct dirent **di; +-- +2.17.2 + diff --git a/SOURCES/0055-multipathd-avoid-io_err_stat-ABBA-deadlock.patch b/SOURCES/0055-multipathd-avoid-io_err_stat-ABBA-deadlock.patch new file mode 100644 index 0000000..8fd1605 --- /dev/null +++ b/SOURCES/0055-multipathd-avoid-io_err_stat-ABBA-deadlock.patch @@ -0,0 +1,146 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 14 Jan 2021 20:20:24 -0600 +Subject: [PATCH] multipathd: avoid io_err_stat ABBA deadlock + +When the checker thread enqueues paths for the io_err_stat thread to +check, it calls enqueue_io_err_stat_by_path() with the vecs lock held. +start_io_err_stat_thread() is also called with the vecs lock held. +These two functions both lock io_err_pathvec_lock. When the io_err_stat +thread updates the paths in vecs->pathvec in poll_io_err_stat(), it has +the io_err_pathvec_lock held, and then locks the vecs lock. This can +cause an ABBA deadlock. + +To solve this, service_paths() no longer updates the paths in +vecs->pathvec with the io_err_pathvec_lock held. It does this by moving +the io_err_stat_path from io_err_pathvec to a local vector when it needs +to update the path. After releasing the io_err_pathvec_lock, it goes +through this temporary vector, updates the paths with the vecs lock +held, and then frees everything. + +This change fixes a bug in service_paths() where elements were being +deleted from io_err_pathvec, without the index being decremented, +causing the loop to skip elements. Also, service_paths() could be +cancelled while holding the io_err_pathvec_lock, so it should have a +cleanup handler. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/io_err_stat.c | 56 ++++++++++++++++++++++---------------- + 1 file changed, 32 insertions(+), 24 deletions(-) + +diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c +index f6c564f0..63ee2e07 100644 +--- a/libmultipath/io_err_stat.c ++++ b/libmultipath/io_err_stat.c +@@ -390,20 +390,6 @@ recover: + return 0; + } + +-static int delete_io_err_stat_by_addr(struct io_err_stat_path *p) +-{ +- int i; +- +- i = find_slot(io_err_pathvec, p); +- if (i != -1) +- vector_del_slot(io_err_pathvec, i); +- +- destroy_directio_ctx(p); +- free_io_err_stat_path(p); +- +- return 0; +-} +- + static void account_async_io_state(struct io_err_stat_path *pp, int rc) + { + switch (rc) { +@@ -420,17 +406,26 @@ static void account_async_io_state(struct io_err_stat_path *pp, int rc) + } + } + +-static int poll_io_err_stat(struct vectors *vecs, struct io_err_stat_path *pp) ++static int io_err_stat_time_up(struct io_err_stat_path *pp) + { + struct timespec currtime, difftime; +- struct path *path; +- double err_rate; + + if (clock_gettime(CLOCK_MONOTONIC, &currtime) != 0) +- return 1; ++ return 0; + timespecsub(&currtime, &pp->start_time, &difftime); + if (difftime.tv_sec < pp->total_time) + return 0; ++ return 1; ++} ++ ++static void end_io_err_stat(struct io_err_stat_path *pp) ++{ ++ struct timespec currtime; ++ struct path *path; ++ double err_rate; ++ ++ if (clock_gettime(CLOCK_MONOTONIC, &currtime) != 0) ++ currtime = pp->start_time; + + io_err_stat_log(4, "%s: check end", pp->devname); + +@@ -469,10 +464,6 @@ static int poll_io_err_stat(struct vectors *vecs, struct io_err_stat_path *pp) + pp->devname); + } + lock_cleanup_pop(vecs->lock); +- +- delete_io_err_stat_by_addr(pp); +- +- return 0; + } + + static int send_each_async_io(struct dio_ctx *ct, int fd, char *dev) +@@ -632,6 +623,7 @@ static void process_async_ios_event(int timeout_nsecs, char *dev) + struct timespec timeout = { .tv_nsec = timeout_nsecs }; + + errno = 0; ++ pthread_testcancel(); + n = io_getevents(ioctx, 1L, CONCUR_NR_EVENT, events, &timeout); + if (n < 0) { + io_err_stat_log(3, "%s: async io events returned %d (errno=%s)", +@@ -644,17 +636,33 @@ static void process_async_ios_event(int timeout_nsecs, char *dev) + + static void service_paths(void) + { ++ struct _vector _pathvec = {0}; ++ /* avoid gcc warnings that &_pathvec will never be NULL in vector ops */ ++ struct _vector * const tmp_pathvec = &_pathvec; + struct io_err_stat_path *pp; + int i; + + pthread_mutex_lock(&io_err_pathvec_lock); ++ pthread_cleanup_push(cleanup_mutex, &io_err_pathvec_lock); + vector_foreach_slot(io_err_pathvec, pp, i) { + send_batch_async_ios(pp); + process_async_ios_event(TIMEOUT_NO_IO_NSEC, pp->devname); + poll_async_io_timeout(); +- poll_io_err_stat(vecs, pp); ++ if (io_err_stat_time_up(pp)) { ++ if (!vector_alloc_slot(tmp_pathvec)) ++ continue; ++ vector_del_slot(io_err_pathvec, i--); ++ vector_set_slot(tmp_pathvec, pp); ++ } + } +- pthread_mutex_unlock(&io_err_pathvec_lock); ++ pthread_cleanup_pop(1); ++ vector_foreach_slot_backwards(tmp_pathvec, pp, i) { ++ end_io_err_stat(pp); ++ vector_del_slot(tmp_pathvec, i); ++ destroy_directio_ctx(pp); ++ free_io_err_stat_path(pp); ++ } ++ vector_reset(tmp_pathvec); + } + + static void cleanup_exited(__attribute__((unused)) void *arg) +-- +2.17.2 + diff --git a/SOURCES/0056-multipathd-use-get_monotonic_time-in-io_err_stat-cod.patch b/SOURCES/0056-multipathd-use-get_monotonic_time-in-io_err_stat-cod.patch new file mode 100644 index 0000000..c6ebd39 --- /dev/null +++ b/SOURCES/0056-multipathd-use-get_monotonic_time-in-io_err_stat-cod.patch @@ -0,0 +1,111 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 14 Jan 2021 20:20:25 -0600 +Subject: [PATCH] multipathd: use get_monotonic_time() in io_err_stat code + +Instead of calling clock_gettime(), and dealing with failure +conditions, just call get_monotonic_time(). + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/io_err_stat.c | 34 +++++++++++----------------------- + 1 file changed, 11 insertions(+), 23 deletions(-) + +diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c +index 63ee2e07..3389d693 100644 +--- a/libmultipath/io_err_stat.c ++++ b/libmultipath/io_err_stat.c +@@ -305,8 +305,7 @@ int io_err_stat_handle_pathfail(struct path *path) + * the repeated count threshold and time frame, we assume a path + * which fails at least twice within 60 seconds is flaky. + */ +- if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0) +- return 1; ++ get_monotonic_time(&curr_time); + if (path->io_err_pathfail_cnt == 0) { + path->io_err_pathfail_cnt++; + path->io_err_pathfail_starttime = curr_time.tv_sec; +@@ -362,9 +361,9 @@ int need_io_err_check(struct path *pp) + } + if (pp->io_err_pathfail_cnt != PATH_IO_ERR_WAITING_TO_CHECK) + return 1; +- if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0 || +- (curr_time.tv_sec - pp->io_err_dis_reinstate_time) > +- pp->mpp->marginal_path_err_recheck_gap_time) { ++ get_monotonic_time(&curr_time); ++ if ((curr_time.tv_sec - pp->io_err_dis_reinstate_time) > ++ pp->mpp->marginal_path_err_recheck_gap_time) { + io_err_stat_log(4, "%s: reschedule checking after %d seconds", + pp->dev, + pp->mpp->marginal_path_err_recheck_gap_time); +@@ -410,8 +409,7 @@ static int io_err_stat_time_up(struct io_err_stat_path *pp) + { + struct timespec currtime, difftime; + +- if (clock_gettime(CLOCK_MONOTONIC, &currtime) != 0) +- return 0; ++ get_monotonic_time(&currtime); + timespecsub(&currtime, &pp->start_time, &difftime); + if (difftime.tv_sec < pp->total_time) + return 0; +@@ -424,8 +422,7 @@ static void end_io_err_stat(struct io_err_stat_path *pp) + struct path *path; + double err_rate; + +- if (clock_gettime(CLOCK_MONOTONIC, &currtime) != 0) +- currtime = pp->start_time; ++ get_monotonic_time(&currtime); + + io_err_stat_log(4, "%s: check end", pp->devname); + +@@ -474,11 +471,7 @@ static int send_each_async_io(struct dio_ctx *ct, int fd, char *dev) + ct->io_starttime.tv_sec == 0) { + struct iocb *ios[1] = { &ct->io }; + +- if (clock_gettime(CLOCK_MONOTONIC, &ct->io_starttime) != 0) { +- ct->io_starttime.tv_sec = 0; +- ct->io_starttime.tv_nsec = 0; +- return rc; +- } ++ get_monotonic_time(&ct->io_starttime); + io_prep_pread(&ct->io, fd, ct->buf, ct->blksize, 0); + if (io_submit(ioctx, 1, ios) != 1) { + io_err_stat_log(5, "%s: io_submit error %i", +@@ -497,8 +490,7 @@ static void send_batch_async_ios(struct io_err_stat_path *pp) + struct dio_ctx *ct; + struct timespec currtime, difftime; + +- if (clock_gettime(CLOCK_MONOTONIC, &currtime) != 0) +- return; ++ get_monotonic_time(&currtime); + /* + * Give a free time for all IO to complete or timeout + */ +@@ -513,11 +505,8 @@ static void send_batch_async_ios(struct io_err_stat_path *pp) + if (!send_each_async_io(ct, pp->fd, pp->devname)) + pp->io_nr++; + } +- if (pp->start_time.tv_sec == 0 && pp->start_time.tv_nsec == 0 && +- clock_gettime(CLOCK_MONOTONIC, &pp->start_time)) { +- pp->start_time.tv_sec = 0; +- pp->start_time.tv_nsec = 0; +- } ++ if (pp->start_time.tv_sec == 0 && pp->start_time.tv_nsec == 0) ++ get_monotonic_time(&pp->start_time); + } + + static int try_to_cancel_timeout_io(struct dio_ctx *ct, struct timespec *t, +@@ -556,8 +545,7 @@ static void poll_async_io_timeout(void) + int rc = PATH_UNCHECKED; + int i, j; + +- if (clock_gettime(CLOCK_MONOTONIC, &curr_time) != 0) +- return; ++ get_monotonic_time(&curr_time); + vector_foreach_slot(io_err_pathvec, pp, i) { + for (j = 0; j < CONCUR_NR_EVENT; j++) { + rc = try_to_cancel_timeout_io(pp->dio_ctx_array + j, +-- +2.17.2 + diff --git a/SOURCES/0057-multipathd-combine-free_io_err_stat_path-and-destroy.patch b/SOURCES/0057-multipathd-combine-free_io_err_stat_path-and-destroy.patch new file mode 100644 index 0000000..c3573c7 --- /dev/null +++ b/SOURCES/0057-multipathd-combine-free_io_err_stat_path-and-destroy.patch @@ -0,0 +1,101 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 14 Jan 2021 20:20:26 -0600 +Subject: [PATCH] multipathd: combine free_io_err_stat_path and + destroy_directio_ctx + +destroy_directio_ctx() is only called from free_io_err_stat_path(), and +free_io_err_stat_path() is very short, so combine them. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/io_err_stat.c | 24 ++++++++++-------------- + 1 file changed, 10 insertions(+), 14 deletions(-) + +diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c +index 3389d693..bf1d3910 100644 +--- a/libmultipath/io_err_stat.c ++++ b/libmultipath/io_err_stat.c +@@ -163,12 +163,15 @@ fail_close: + return 1; + } + +-static void destroy_directio_ctx(struct io_err_stat_path *p) ++static void free_io_err_stat_path(struct io_err_stat_path *p) + { + int i; + +- if (!p || !p->dio_ctx_array) ++ if (!p) + return; ++ if (!p->dio_ctx_array) ++ goto free_path; ++ + cancel_inflight_io(p); + + for (i = 0; i < CONCUR_NR_EVENT; i++) +@@ -177,6 +180,8 @@ static void destroy_directio_ctx(struct io_err_stat_path *p) + + if (p->fd > 0) + close(p->fd); ++free_path: ++ FREE(p); + } + + static struct io_err_stat_path *alloc_io_err_stat_path(void) +@@ -199,11 +204,6 @@ static struct io_err_stat_path *alloc_io_err_stat_path(void) + return p; + } + +-static void free_io_err_stat_path(struct io_err_stat_path *p) +-{ +- FREE(p); +-} +- + static void free_io_err_pathvec(void) + { + struct io_err_stat_path *path; +@@ -213,10 +213,8 @@ static void free_io_err_pathvec(void) + pthread_cleanup_push(cleanup_mutex, &io_err_pathvec_lock); + if (!io_err_pathvec) + goto out; +- vector_foreach_slot(io_err_pathvec, path, i) { +- destroy_directio_ctx(path); ++ vector_foreach_slot(io_err_pathvec, path, i) + free_io_err_stat_path(path); +- } + vector_free(io_err_pathvec); + io_err_pathvec = NULL; + out: +@@ -252,7 +250,7 @@ static int enqueue_io_err_stat_by_path(struct path *path) + goto free_ioerr_path; + pthread_mutex_lock(&io_err_pathvec_lock); + if (!vector_alloc_slot(io_err_pathvec)) +- goto unlock_destroy; ++ goto unlock_pathvec; + vector_set_slot(io_err_pathvec, p); + pthread_mutex_unlock(&io_err_pathvec_lock); + +@@ -260,9 +258,8 @@ static int enqueue_io_err_stat_by_path(struct path *path) + path->mpp->alias, path->dev); + return 0; + +-unlock_destroy: ++unlock_pathvec: + pthread_mutex_unlock(&io_err_pathvec_lock); +- destroy_directio_ctx(p); + free_ioerr_path: + free_io_err_stat_path(p); + +@@ -647,7 +644,6 @@ static void service_paths(void) + vector_foreach_slot_backwards(tmp_pathvec, pp, i) { + end_io_err_stat(pp); + vector_del_slot(tmp_pathvec, i); +- destroy_directio_ctx(pp); + free_io_err_stat_path(pp); + } + vector_reset(tmp_pathvec); +-- +2.17.2 + diff --git a/SOURCES/0058-multipathd-cleanup-logging-for-marginal-paths.patch b/SOURCES/0058-multipathd-cleanup-logging-for-marginal-paths.patch new file mode 100644 index 0000000..2983712 --- /dev/null +++ b/SOURCES/0058-multipathd-cleanup-logging-for-marginal-paths.patch @@ -0,0 +1,110 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 18 Jan 2021 22:46:04 -0600 +Subject: [PATCH] multipathd: cleanup logging for marginal paths + +io_err_stat logged at level 2 whenever it enqueued a path to check, +which could happen multiple times while a path was marginal. On the +other hand if marginal_pathgroups wasn't set, multipathd didn't log when +paths were set to marginal. Now io_err_stat only logs at level 2 when +something unexpected happens, but multipathd will always log when a +path switches its marginal state. + +This patch also fixes an issue where paths in the delayed state could +get set to the pending state if they could not be checked in time. +Aside from going against the idea the paths should not be set to pending +if they already have a valid state, this caused multipathd to log a +message whenever the path state switched to from delayed to pending and +then back. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/io_err_stat.c | 7 +++---- + multipathd/main.c | 20 +++++++++++++------- + 2 files changed, 16 insertions(+), 11 deletions(-) + +diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c +index bf1d3910..ee711f7f 100644 +--- a/libmultipath/io_err_stat.c ++++ b/libmultipath/io_err_stat.c +@@ -254,7 +254,7 @@ static int enqueue_io_err_stat_by_path(struct path *path) + vector_set_slot(io_err_pathvec, p); + pthread_mutex_unlock(&io_err_pathvec_lock); + +- io_err_stat_log(2, "%s: enqueue path %s to check", ++ io_err_stat_log(3, "%s: enqueue path %s to check", + path->mpp->alias, path->dev); + return 0; + +@@ -353,7 +353,7 @@ int need_io_err_check(struct path *pp) + if (uatomic_read(&io_err_thread_running) == 0) + return 0; + if (count_active_paths(pp->mpp) <= 0) { +- io_err_stat_log(2, "%s: recover path early", pp->dev); ++ io_err_stat_log(2, "%s: no paths. recovering early", pp->dev); + goto recover; + } + if (pp->io_err_pathfail_cnt != PATH_IO_ERR_WAITING_TO_CHECK) +@@ -371,8 +371,7 @@ int need_io_err_check(struct path *pp) + * Or else, return 1 to set path state to PATH_SHAKY + */ + if (r == 1) { +- io_err_stat_log(3, "%s: enqueue fails, to recover", +- pp->dev); ++ io_err_stat_log(2, "%s: enqueue failed. recovering early", pp->dev); + goto recover; + } else + pp->io_err_pathfail_cnt = PATH_IO_ERR_IN_CHECKING; +diff --git a/multipathd/main.c b/multipathd/main.c +index 1d0579e9..cc1aeea2 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -2041,8 +2041,8 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks) + pathinfo(pp, conf, 0); + pthread_cleanup_pop(1); + return 1; +- } else if ((newstate != PATH_UP && newstate != PATH_GHOST) && +- (pp->state == PATH_DELAYED)) { ++ } else if ((newstate != PATH_UP && newstate != PATH_GHOST && ++ newstate != PATH_PENDING) && (pp->state == PATH_DELAYED)) { + /* If path state become failed again cancel path delay state */ + pp->state = newstate; + return 1; +@@ -2104,8 +2104,9 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks) + if ((newstate == PATH_UP || newstate == PATH_GHOST) && + (san_path_check_enabled(pp->mpp) || + marginal_path_check_enabled(pp->mpp))) { +- int was_marginal = pp->marginal; + if (should_skip_path(pp)) { ++ if (!pp->marginal && pp->state != PATH_DELAYED) ++ condlog(2, "%s: path is now marginal", pp->dev); + if (!marginal_pathgroups) { + if (marginal_path_check_enabled(pp->mpp)) + /* to reschedule as soon as possible, +@@ -2115,13 +2116,18 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks) + pp->state = PATH_DELAYED; + return 1; + } +- if (!was_marginal) { ++ if (!pp->marginal) { + pp->marginal = 1; + marginal_changed = 1; + } +- } else if (marginal_pathgroups && was_marginal) { +- pp->marginal = 0; +- marginal_changed = 1; ++ } else { ++ if (pp->marginal || pp->state == PATH_DELAYED) ++ condlog(2, "%s: path is no longer marginal", ++ pp->dev); ++ if (marginal_pathgroups && pp->marginal) { ++ pp->marginal = 0; ++ marginal_changed = 1; ++ } + } + } + +-- +2.17.2 + diff --git a/SOURCES/0059-libmpathpersist-fix-thread-safety-of-default-functio.patch b/SOURCES/0059-libmpathpersist-fix-thread-safety-of-default-functio.patch new file mode 100644 index 0000000..5bbd255 --- /dev/null +++ b/SOURCES/0059-libmpathpersist-fix-thread-safety-of-default-functio.patch @@ -0,0 +1,277 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 25 Jan 2021 23:31:04 -0600 +Subject: [PATCH] libmpathpersist: fix thread safety of default functions + +commit a839e39e ("libmpathpersist: factor out initialization and +teardown") made mpath_presistent_reserve_{in,out} use share variables +for curmp and pathvec. There are users of this library that call these +functions in a multi-threaded process, and this change causes their +application to crash. config and udev are also shared variables, but +libmpathpersist doesn't write to the config in +mpath_presistent_reserve_{in,out}, and looking into the libudev code, I +don't see any place where libmpathpersist uses the udev object in a way +that isn't thread-safe. + +This patch makes mpath_presistent_reserve_{in,out} go back to using +local variables for curmp and pathvec, so that multiple threads won't +be operating on these variables at the same time. + +Fixes: a839e39e ("libmpathpersist: factor out initialization and teardown") +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmpathpersist/mpath_persist.c | 116 +++++++++++++++++++++----------- + libmpathpersist/mpath_persist.h | 24 +++++-- + 2 files changed, 94 insertions(+), 46 deletions(-) + +diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c +index a01dfb0b..07a5f17f 100644 +--- a/libmpathpersist/mpath_persist.c ++++ b/libmpathpersist/mpath_persist.c +@@ -147,72 +147,60 @@ mpath_prin_activepath (struct multipath *mpp, int rq_servact, + return ret; + } + +-int mpath_persistent_reserve_in (int fd, int rq_servact, +- struct prin_resp *resp, int noisy, int verbose) +-{ +- int ret = mpath_persistent_reserve_init_vecs(verbose); +- +- if (ret != MPATH_PR_SUCCESS) +- return ret; +- ret = __mpath_persistent_reserve_in(fd, rq_servact, resp, noisy); +- mpath_persistent_reserve_free_vecs(); +- return ret; +-} +- +-int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, +- unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose) +-{ +- int ret = mpath_persistent_reserve_init_vecs(verbose); +- +- if (ret != MPATH_PR_SUCCESS) +- return ret; +- ret = __mpath_persistent_reserve_out(fd, rq_servact, rq_scope, rq_type, +- paramp, noisy); +- mpath_persistent_reserve_free_vecs(); +- return ret; +-} +- + static vector curmp; + static vector pathvec; + +-void mpath_persistent_reserve_free_vecs(void) ++static void __mpath_persistent_reserve_free_vecs(vector curmp, vector pathvec) + { + free_multipathvec(curmp, KEEP_PATHS); + free_pathvec(pathvec, FREE_PATHS); ++} ++ ++void mpath_persistent_reserve_free_vecs(void) ++{ ++ __mpath_persistent_reserve_free_vecs(curmp, pathvec); + curmp = pathvec = NULL; + } + +-int mpath_persistent_reserve_init_vecs(int verbose) ++static int __mpath_persistent_reserve_init_vecs(vector *curmp_p, ++ vector *pathvec_p, int verbose) + { + struct config *conf = get_multipath_config(); + + conf->verbosity = verbose; + put_multipath_config(conf); + +- if (curmp) ++ if (*curmp_p) + return MPATH_PR_SUCCESS; + /* + * allocate core vectors to store paths and multipaths + */ +- curmp = vector_alloc (); +- pathvec = vector_alloc (); ++ *curmp_p = vector_alloc (); ++ *pathvec_p = vector_alloc (); + +- if (!curmp || !pathvec){ ++ if (!*curmp_p || !*pathvec_p){ + condlog (0, "vector allocation failed."); + goto err; + } + +- if (dm_get_maps(curmp)) ++ if (dm_get_maps(*curmp_p)) + goto err; + + return MPATH_PR_SUCCESS; + + err: +- mpath_persistent_reserve_free_vecs(); ++ __mpath_persistent_reserve_free_vecs(*curmp_p, *pathvec_p); ++ *curmp_p = *pathvec_p = NULL; + return MPATH_PR_DMMP_ERROR; + } + +-static int mpath_get_map(int fd, char **palias, struct multipath **pmpp) ++int mpath_persistent_reserve_init_vecs(int verbose) ++{ ++ return __mpath_persistent_reserve_init_vecs(&curmp, &pathvec, verbose); ++} ++ ++static int mpath_get_map(vector curmp, vector pathvec, int fd, char **palias, ++ struct multipath **pmpp) + { + int ret = MPATH_PR_DMMP_ERROR; + struct stat info; +@@ -272,13 +260,13 @@ out: + return ret; + } + +-int __mpath_persistent_reserve_in (int fd, int rq_servact, +- struct prin_resp *resp, int noisy) ++static int do_mpath_persistent_reserve_in (vector curmp, vector pathvec, ++ int fd, int rq_servact, struct prin_resp *resp, int noisy) + { + struct multipath *mpp; + int ret; + +- ret = mpath_get_map(fd, NULL, &mpp); ++ ret = mpath_get_map(curmp, pathvec, fd, NULL, &mpp); + if (ret != MPATH_PR_SUCCESS) + return ret; + +@@ -287,8 +275,17 @@ int __mpath_persistent_reserve_in (int fd, int rq_servact, + return ret; + } + +-int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, +- unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy) ++ ++int __mpath_persistent_reserve_in (int fd, int rq_servact, ++ struct prin_resp *resp, int noisy) ++{ ++ return do_mpath_persistent_reserve_in(curmp, pathvec, fd, rq_servact, ++ resp, noisy); ++} ++ ++static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd, ++ int rq_servact, int rq_scope, unsigned int rq_type, ++ struct prout_param_descriptor *paramp, int noisy) + { + struct multipath *mpp; + char *alias; +@@ -296,7 +293,7 @@ int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, + uint64_t prkey; + struct config *conf; + +- ret = mpath_get_map(fd, &alias, &mpp); ++ ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp); + if (ret != MPATH_PR_SUCCESS) + return ret; + +@@ -366,6 +363,45 @@ out1: + return ret; + } + ++ ++int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, ++ unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy) ++{ ++ return do_mpath_persistent_reserve_out(curmp, pathvec, fd, rq_servact, ++ rq_scope, rq_type, paramp, ++ noisy); ++} ++ ++int mpath_persistent_reserve_in (int fd, int rq_servact, ++ struct prin_resp *resp, int noisy, int verbose) ++{ ++ vector curmp = NULL, pathvec; ++ int ret = __mpath_persistent_reserve_init_vecs(&curmp, &pathvec, ++ verbose); ++ ++ if (ret != MPATH_PR_SUCCESS) ++ return ret; ++ ret = do_mpath_persistent_reserve_in(curmp, pathvec, fd, rq_servact, ++ resp, noisy); ++ __mpath_persistent_reserve_free_vecs(curmp, pathvec); ++ return ret; ++} ++ ++int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, ++ unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose) ++{ ++ vector curmp = NULL, pathvec; ++ int ret = __mpath_persistent_reserve_init_vecs(&curmp, &pathvec, ++ verbose); ++ ++ if (ret != MPATH_PR_SUCCESS) ++ return ret; ++ ret = do_mpath_persistent_reserve_out(curmp, pathvec, fd, rq_servact, ++ rq_scope, rq_type, paramp, noisy); ++ __mpath_persistent_reserve_free_vecs(curmp, pathvec); ++ return ret; ++} ++ + int + get_mpvec (vector curmp, vector pathvec, char * refwwid) + { +diff --git a/libmpathpersist/mpath_persist.h b/libmpathpersist/mpath_persist.h +index 7cf4faf9..0e4e0e53 100644 +--- a/libmpathpersist/mpath_persist.h ++++ b/libmpathpersist/mpath_persist.h +@@ -215,9 +215,13 @@ extern int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp + + /* + * DESCRIPTION : +- * This function is like mpath_persistent_reserve_in(), except that it doesn't call +- * mpath_persistent_reserve_init_vecs() and mpath_persistent_reserve_free_vecs() +- * before and after the actual PR call. ++ * This function is like mpath_persistent_reserve_in(), except that it ++ * requires mpath_persistent_reserve_init_vecs() to be called before the ++ * PR call to set up internal variables. These must later be cleanup up ++ * by calling mpath_persistent_reserve_free_vecs(). ++ * ++ * RESTRICTIONS: ++ * This function uses static internal variables, and is not thread-safe. + */ + extern int __mpath_persistent_reserve_in(int fd, int rq_servact, + struct prin_resp *resp, int noisy); +@@ -249,9 +253,13 @@ extern int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, + int verbose); + /* + * DESCRIPTION : +- * This function is like mpath_persistent_reserve_out(), except that it doesn't call +- * mpath_persistent_reserve_init_vecs() and mpath_persistent_reserve_free_vecs() +- * before and after the actual PR call. ++ * This function is like mpath_persistent_reserve_out(), except that it ++ * requires mpath_persistent_reserve_init_vecs() to be called before the ++ * PR call to set up internal variables. These must later be cleanup up ++ * by calling mpath_persistent_reserve_free_vecs(). ++ * ++ * RESTRICTIONS: ++ * This function uses static internal variables, and is not thread-safe. + */ + extern int __mpath_persistent_reserve_out( int fd, int rq_servact, int rq_scope, + unsigned int rq_type, struct prout_param_descriptor *paramp, +@@ -265,6 +273,7 @@ extern int __mpath_persistent_reserve_out( int fd, int rq_servact, int rq_scope, + * @verbose: Set verbosity level. Input argument. value:0 to 3. 0->disabled, 3->Max verbose + * + * RESTRICTIONS: ++ * This function uses static internal variables, and is not thread-safe. + * + * RETURNS: MPATH_PR_SUCCESS if successful else returns any of the status specified + * above in RETURN_STATUS. +@@ -275,6 +284,9 @@ int mpath_persistent_reserve_init_vecs(int verbose); + * DESCRIPTION : + * This function frees data structures allocated by + * mpath_persistent_reserve_init_vecs(). ++ * ++ * RESTRICTIONS: ++ * This function uses static internal variables, and is not thread-safe. + */ + void mpath_persistent_reserve_free_vecs(void); + +-- +2.17.2 + diff --git a/SOURCES/0060-kpartx-free-loop-device-after-listing-partitions.patch b/SOURCES/0060-kpartx-free-loop-device-after-listing-partitions.patch new file mode 100644 index 0000000..c50bbc0 --- /dev/null +++ b/SOURCES/0060-kpartx-free-loop-device-after-listing-partitions.patch @@ -0,0 +1,61 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 9 Feb 2021 17:16:04 -0600 +Subject: [PATCH] kpartx: free loop device after listing partitions + +If "kpartx -l" is run on a file that doesn't already have a loop device +associated with it, it will create a loop device to run the command. +Starting with da59d15c6 ("Fix loopback file with kpartx -av"), it will +not free the loop device when exitting. This is because it checks if the +the file it stat()ed is a regular file, before freeing the loop device. +However, after da59d15c6, stat() is rerun on the loop device itself, so +the check fails. There is no need to check this, if loopcreated is +true, then the file will be a kpartx created loop device, and should be +freed. + +Also, keep kpartx from printing that the loop device has been removed +at normal verbosity. + +Fixes: da59d15c6 ("Fix loopback file with kpartx -av") +Signed-off-by: Benjamin Marzinski +--- + kpartx/kpartx.c | 9 +++++---- + 1 file changed, 5 insertions(+), 4 deletions(-) + +diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c +index 653ce0c8..a337a07b 100644 +--- a/kpartx/kpartx.c ++++ b/kpartx/kpartx.c +@@ -407,7 +407,7 @@ main(int argc, char **argv){ + fprintf(stderr, "can't del loop : %s\n", + loopdev); + r = 1; +- } else ++ } else if (verbose) + fprintf(stderr, "loop deleted : %s\n", loopdev); + } + goto end; +@@ -649,16 +649,17 @@ main(int argc, char **argv){ + if (n > 0) + break; + } +- if (what == LIST && loopcreated && S_ISREG (buf.st_mode)) { ++ if (what == LIST && loopcreated) { + if (fd != -1) + close(fd); + if (del_loop(device)) { + if (verbose) +- printf("can't del loop : %s\n", ++ fprintf(stderr, "can't del loop : %s\n", + device); + exit(1); + } +- printf("loop deleted : %s\n", device); ++ if (verbose) ++ fprintf(stderr, "loop deleted : %s\n", device); + } + + end: +-- +2.17.2 + diff --git a/SOURCES/0061-RH-fix-find_multipaths-in-mpathconf.patch b/SOURCES/0061-RH-fix-find_multipaths-in-mpathconf.patch new file mode 100644 index 0000000..ea9d530 --- /dev/null +++ b/SOURCES/0061-RH-fix-find_multipaths-in-mpathconf.patch @@ -0,0 +1,134 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 10 Feb 2021 15:42:42 -0600 +Subject: [PATCH] RH: fix find_multipaths in mpathconf + +mpathconf wasn't correctly dealing with the new rhel-8 values for +find_multipaths + +Signed-off-by: Benjamin Marzinski +--- + multipath/mpathconf | 38 +++++++++++++++++++------------------- + multipath/mpathconf.8 | 14 +++++++------- + 2 files changed, 26 insertions(+), 26 deletions(-) + +diff --git a/multipath/mpathconf b/multipath/mpathconf +index f34003c9..2f4f3eaf 100644 +--- a/multipath/mpathconf ++++ b/multipath/mpathconf +@@ -54,7 +54,7 @@ function usage + echo "Disable: --disable" + echo "Only allow certain wwids (instead of enable): --allow " + echo "Set user_friendly_names (Default y): --user_friendly_names " +- echo "Set find_multipaths (Default y): --find_multipaths " ++ echo "Set find_multipaths (Default y): --find_multipaths " + echo "Set default property blacklist (Default y): --property_blacklist " + echo "Set enable_foreign to show foreign devices (Default n): --enable_foreign " + echo "Load the dm-multipath modules on enable (Default y): --with_module " +@@ -224,8 +224,12 @@ function validate_args + 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 [ "$FIND" = "y" ]; then ++ FIND="yes" ++ elif [ "$FIND" = "n" ]; then ++ FIND="no" ++ elif [ -n "$FIND" ] && [ "$FIND" != "yes" -a "$FIND" != "no" -a "$FIND" != "strict" -a "$FIND" != "greedy" -a "$FIND" != "smart" ]; then ++ echo "--find_multipaths must be one of 'yes' 'no' 'strict' 'greedy' or 'smart'" + exit 1 + fi + if [ -n "$PROPERTY" ] && [ "$PROPERTY" != "y" -a "$PROPERTY" != "n" ]; then +@@ -327,10 +331,11 @@ if [ "$HAVE_BLACKLIST" = "1" ]; then + 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:]]*\).*$/\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 + HAVE_FRIENDLY=1 +@@ -360,10 +365,10 @@ if [ -n "$SHOW_STATUS" ]; then + 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 no" + 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" +@@ -455,19 +460,14 @@ elif [ "$ENABLE" = 0 ]; then + 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:]]*/ find_multipaths '"$FIND"'/' $TMPFILE + CHANGED_CONFIG=1 + fi + fi +diff --git a/multipath/mpathconf.8 b/multipath/mpathconf.8 +index b82961d6..83515eb4 100644 +--- a/multipath/mpathconf.8 ++++ b/multipath/mpathconf.8 +@@ -38,9 +38,9 @@ If + 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 \fByes\fP. To disable these, use the + .B --user_friendly_names n + and + .B --find_multipaths n +@@ -77,13 +77,13 @@ to the + 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 } +-If set to \fBy\fP, this adds the line +-.B find_multipaths yes ++.B --find_multipaths\fP { \fByes\fP | \fBno\fP | \fBstrict\fP | \fBgreedy\fP | \fBsmart\fP } ++If set to \fB\fP, this adds the line ++.B find_multipaths + 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. 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 +-- +2.17.2 + diff --git a/SOURCES/0062-libmultipath-select_action-don-t-drop-map-if-alias-c.patch b/SOURCES/0062-libmultipath-select_action-don-t-drop-map-if-alias-c.patch new file mode 100644 index 0000000..d34e9e3 --- /dev/null +++ b/SOURCES/0062-libmultipath-select_action-don-t-drop-map-if-alias-c.patch @@ -0,0 +1,45 @@ +From 7a7b96246b84ccf533a6f4dc0424830792fdb96a Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Fri, 3 Jul 2020 15:17:09 +0200 +Subject: [PATCH] libmultipath: select_action(): don't drop map if alias + clashes + +If for a given map, if we find that the requested alias is already +used by a map with different WWID, while the map's own WWID is +not used yet, give up the alias and use the WWID instead. This +is safer than trying to destroy the existing map, which is likely +to fail. + +This allows us to make use const for the "curmp" parameter. + +Reviewed-by: Benjamin Marzinski +--- + libmultipath/configure.c | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index b7113291..2e8f34f9 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -690,12 +690,13 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + } + + if (!cmpp) { +- condlog(2, "%s: remove (wwid changed)", mpp->alias); +- dm_flush_map(mpp->alias); +- strlcpy(cmpp_by_name->wwid, mpp->wwid, WWID_SIZE); +- drop_multipath(curmp, cmpp_by_name->wwid, KEEP_PATHS); ++ condlog(1, "%s: can't use alias \"%s\" used by %s, falling back to WWID", ++ mpp->wwid, mpp->alias, cmpp_by_name->wwid); ++ /* We can do this because wwid wasn't found */ ++ free(mpp->alias); ++ mpp->alias = strdup(mpp->wwid); + mpp->action = ACT_CREATE; +- condlog(3, "%s: set ACT_CREATE (map wwid change)", ++ condlog(3, "%s: set ACT_CREATE (map does not exist, name changed)", + mpp->alias); + return; + } +-- +2.17.2 + diff --git a/SOURCES/0063-libmultipath-check-if-user_friendly_name-is-in-use.patch b/SOURCES/0063-libmultipath-check-if-user_friendly_name-is-in-use.patch new file mode 100644 index 0000000..81947be --- /dev/null +++ b/SOURCES/0063-libmultipath-check-if-user_friendly_name-is-in-use.patch @@ -0,0 +1,231 @@ +From e714eb26fddc8768a8de279d1de3ffedab35929e Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 5 Mar 2021 21:40:57 -0600 +Subject: [PATCH] libmultipath: check if user_friendly_name is in use + +If there are multipath devices that have user_friendly_names but do not +have their bindings in the bindings_file, get_user_friendly_alias() can +currently give out those names again. This can result in an incorrect +entry in the bindings file, and a device that gets created with a WWID +alias instead of a user_friendly_name. This situation can happen after +the pivot root, if a multipath device is created in the initramfs. If +this device doesn't have a binding in the regular filesystem +bindings_file and a new multipath device is created before it can add +its binding, the new device can steal that user_friendly_name during +multipathd's initial configure. + +To solve this, get_user_friendly_alias() now calls lookup_binding() with +a new paramter, telling it to check if the id it found is already in use +by a diffent device. If so, lookup_binding() will continue to check open +ids, until it finds one that it not currently in use by a dm device. +--- + libmultipath/alias.c | 48 +++++++++++++++++++++++++++++++++++++++++--- + tests/alias.c | 22 ++++++++++---------- + 2 files changed, 56 insertions(+), 14 deletions(-) + +diff --git a/libmultipath/alias.c b/libmultipath/alias.c +index 14401cae..01f737f4 100644 +--- a/libmultipath/alias.c ++++ b/libmultipath/alias.c +@@ -17,6 +17,7 @@ + #include "vector.h" + #include "checkers.h" + #include "structs.h" ++#include "devmapper.h" + + + /* +@@ -104,6 +105,28 @@ scan_devname(const char *alias, const char *prefix) + return n; + } + ++static int ++id_already_taken(int id, const char *prefix, const char *map_wwid) ++{ ++ char alias[LINE_MAX]; ++ ++ if (format_devname(alias, id, LINE_MAX, prefix) < 0) ++ return 0; ++ ++ if (dm_map_present(alias)) { ++ char wwid[WWID_SIZE]; ++ ++ /* If both the name and the wwid match, then it's fine.*/ ++ if (dm_get_uuid(alias, wwid, sizeof(wwid)) == 0 && ++ strncmp(map_wwid, wwid, sizeof(wwid)) == 0) ++ return 0; ++ condlog(3, "%s: alias '%s' already taken, but not in bindings file. reselecting alias", map_wwid, alias); ++ return 1; ++ } ++ return 0; ++} ++ ++ + /* + * Returns: 0 if matching entry in WWIDs file found + * -1 if an error occurs +@@ -113,7 +136,7 @@ scan_devname(const char *alias, const char *prefix) + */ + static int + lookup_binding(FILE *f, const char *map_wwid, char **map_alias, +- const char *prefix) ++ const char *prefix, int check_if_taken) + { + char buf[LINE_MAX]; + unsigned int line_nr = 0; +@@ -168,12 +191,31 @@ lookup_binding(FILE *f, const char *map_wwid, char **map_alias, + return 0; + } + } ++ if (!prefix && check_if_taken) ++ id = -1; + if (id >= smallest_bigger_id) { + if (biggest_id < INT_MAX) + id = biggest_id + 1; + else + id = -1; + } ++ if (id > 0 && check_if_taken) { ++ while(id_already_taken(id, prefix, map_wwid)) { ++ if (id == INT_MAX) { ++ id = -1; ++ break; ++ } ++ id++; ++ if (id == smallest_bigger_id) { ++ if (biggest_id == INT_MAX) { ++ id = -1; ++ break; ++ } ++ if (biggest_id >= smallest_bigger_id) ++ id = biggest_id + 1; ++ } ++ } ++ } + if (id < 0) { + condlog(0, "no more available user_friendly_names"); + return -1; +@@ -316,7 +358,7 @@ use_existing_alias (const char *wwid, const char *file, const char *alias_old, + goto out; + } + +- id = lookup_binding(f, wwid, &alias, NULL); ++ id = lookup_binding(f, wwid, &alias, NULL, 0); + if (alias) { + condlog(3, "Use existing binding [%s] for WWID [%s]", + alias, wwid); +@@ -373,7 +415,7 @@ get_user_friendly_alias(const char *wwid, const char *file, const char *prefix, + return NULL; + } + +- id = lookup_binding(f, wwid, &alias, prefix); ++ id = lookup_binding(f, wwid, &alias, prefix, 1); + if (id < 0) { + fclose(f); + return NULL; +diff --git a/tests/alias.c b/tests/alias.c +index 30414db0..ab1a9325 100644 +--- a/tests/alias.c ++++ b/tests/alias.c +@@ -356,7 +356,7 @@ static void lb_empty(void **state) + + will_return(__wrap_fgets, NULL); + expect_condlog(3, "No matching wwid [WWID0] in bindings file.\n"); +- rc = lookup_binding(NULL, "WWID0", &alias, NULL); ++ rc = lookup_binding(NULL, "WWID0", &alias, NULL, 0); + assert_int_equal(rc, 1); + assert_ptr_equal(alias, NULL); + } +@@ -369,7 +369,7 @@ static void lb_match_a(void **state) + will_return(__wrap_fgets, "MPATHa WWID0\n"); + expect_condlog(3, "Found matching wwid [WWID0] in bindings file." + " Setting alias to MPATHa\n"); +- rc = lookup_binding(NULL, "WWID0", &alias, "MPATH"); ++ rc = lookup_binding(NULL, "WWID0", &alias, "MPATH", 0); + assert_int_equal(rc, 0); + assert_ptr_not_equal(alias, NULL); + assert_string_equal(alias, "MPATHa"); +@@ -384,7 +384,7 @@ static void lb_nomatch_a(void **state) + will_return(__wrap_fgets, "MPATHa WWID0\n"); + will_return(__wrap_fgets, NULL); + expect_condlog(3, "No matching wwid [WWID1] in bindings file.\n"); +- rc = lookup_binding(NULL, "WWID1", &alias, "MPATH"); ++ rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 0); + assert_int_equal(rc, 2); + assert_ptr_equal(alias, NULL); + } +@@ -398,7 +398,7 @@ static void lb_match_c(void **state) + will_return(__wrap_fgets, "MPATHc WWID1\n"); + expect_condlog(3, "Found matching wwid [WWID1] in bindings file." + " Setting alias to MPATHc\n"); +- rc = lookup_binding(NULL, "WWID1", &alias, "MPATH"); ++ rc = lookup_binding(NULL, "WWID1", &alias, "MPATH", 0); + assert_int_equal(rc, 0); + assert_ptr_not_equal(alias, NULL); + assert_string_equal(alias, "MPATHc"); +@@ -414,7 +414,7 @@ static void lb_nomatch_a_c(void **state) + will_return(__wrap_fgets, "MPATHc WWID1\n"); + will_return(__wrap_fgets, NULL); + expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n"); +- rc = lookup_binding(NULL, "WWID2", &alias, "MPATH"); ++ rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0); + assert_int_equal(rc, 2); + assert_ptr_equal(alias, NULL); + } +@@ -428,7 +428,7 @@ static void lb_nomatch_c_a(void **state) + will_return(__wrap_fgets, "MPATHa WWID0\n"); + will_return(__wrap_fgets, NULL); + expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n"); +- rc = lookup_binding(NULL, "WWID2", &alias, "MPATH"); ++ rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0); + assert_int_equal(rc, 2); + assert_ptr_equal(alias, NULL); + } +@@ -443,7 +443,7 @@ static void lb_nomatch_a_b(void **state) + will_return(__wrap_fgets, "MPATHb WWID1\n"); + will_return(__wrap_fgets, NULL); + expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n"); +- rc = lookup_binding(NULL, "WWID2", &alias, "MPATH"); ++ rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0); + assert_int_equal(rc, 3); + assert_ptr_equal(alias, NULL); + } +@@ -459,7 +459,7 @@ static void lb_nomatch_a_b_bad(void **state) + will_return(__wrap_fgets, NULL); + expect_condlog(3, "Ignoring malformed line 3 in bindings file\n"); + expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n"); +- rc = lookup_binding(NULL, "WWID2", &alias, "MPATH"); ++ rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0); + assert_int_equal(rc, 3); + assert_ptr_equal(alias, NULL); + } +@@ -474,7 +474,7 @@ static void lb_nomatch_b_a(void **state) + will_return(__wrap_fgets, "MPATHa WWID0\n"); + will_return(__wrap_fgets, NULL); + expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n"); +- rc = lookup_binding(NULL, "WWID2", &alias, "MPATH"); ++ rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0); + assert_int_equal(rc, 27); + assert_ptr_equal(alias, NULL); + } +@@ -490,7 +490,7 @@ static void lb_nomatch_int_max(void **state) + will_return(__wrap_fgets, "MPATHa WWID0\n"); + will_return(__wrap_fgets, NULL); + expect_condlog(0, "no more available user_friendly_names\n"); +- rc = lookup_binding(NULL, "WWID2", &alias, "MPATH"); ++ rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0); + assert_int_equal(rc, -1); + assert_ptr_equal(alias, NULL); + } +@@ -505,7 +505,7 @@ static void lb_nomatch_int_max_m1(void **state) + will_return(__wrap_fgets, "MPATHa WWID0\n"); + will_return(__wrap_fgets, NULL); + expect_condlog(3, "No matching wwid [WWID2] in bindings file.\n"); +- rc = lookup_binding(NULL, "WWID2", &alias, "MPATH"); ++ rc = lookup_binding(NULL, "WWID2", &alias, "MPATH", 0); + assert_int_equal(rc, INT_MAX); + assert_ptr_equal(alias, NULL); + } +-- +2.17.2 + diff --git a/SOURCES/0064-libmultipath-check-udev_device_get_-return-value-to-.patch b/SOURCES/0064-libmultipath-check-udev_device_get_-return-value-to-.patch new file mode 100644 index 0000000..20afb10 --- /dev/null +++ b/SOURCES/0064-libmultipath-check-udev_device_get_-return-value-to-.patch @@ -0,0 +1,129 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: lixiaokeng +Date: Mon, 21 Sep 2020 12:00:39 +0800 +Subject: [PATCH] libmultipath: check udev_device_get_* return value to avoid + segfault + +The udev_device_get_* function may return NULL, and it will be +deregerenced in str* and sscanf func. We check the return value +to avoid segfault. Fix all. + +Reviewed-by: Martin Wilck +Signed-off-by:Lixiaokeng +Signed-off-by: Zhiqiang Liu +Signed-off-by: Linfeilong +Signed-off-by: Benjamin Marzinski +--- + libmultipath/configure.c | 4 +++- + libmultipath/discovery.c | 9 +++++++-- + libmultipath/foreign/nvme.c | 10 +++++++--- + 3 files changed, 17 insertions(+), 6 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index 2e8f34f9..a6893d8d 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -511,6 +511,7 @@ static void trigger_partitions_udev_change(struct udev_device *dev, + { + struct udev_enumerate *part_enum; + struct udev_list_entry *item; ++ const char *devtype; + + part_enum = udev_enumerate_new(udev); + if (!part_enum) +@@ -531,7 +532,8 @@ static void trigger_partitions_udev_change(struct udev_device *dev, + if (!part) + continue; + +- if (!strcmp("partition", udev_device_get_devtype(part))) { ++ devtype = udev_device_get_devtype(part); ++ if (devtype && !strcmp("partition", devtype)) { + condlog(4, "%s: triggering %s event for %s", __func__, + action, syspath); + sysfs_attr_set_value(part, "uevent", action, len); +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index a328aafa..74abf34d 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -353,7 +353,7 @@ sysfs_get_tgt_nodename(struct path *pp, char *node) + tgtdev = udev_device_get_parent(parent); + while (tgtdev) { + tgtname = udev_device_get_sysname(tgtdev); +- if (sscanf(tgtname, "end_device-%d:%d", ++ if (tgtname && sscanf(tgtname, "end_device-%d:%d", + &host, &tgtid) == 2) + break; + tgtdev = udev_device_get_parent(tgtdev); +@@ -386,7 +386,7 @@ sysfs_get_tgt_nodename(struct path *pp, char *node) + /* Check for FibreChannel */ + tgtdev = udev_device_get_parent(parent); + value = udev_device_get_sysname(tgtdev); +- if (sscanf(value, "rport-%d:%d-%d", ++ if (value && sscanf(value, "rport-%d:%d-%d", + &host, &channel, &tgtid) == 3) { + tgtdev = udev_device_new_from_subsystem_sysname(udev, + "fc_remote_ports", value); +@@ -516,6 +516,9 @@ int sysfs_get_host_pci_name(const struct path *pp, char *pci_name) + */ + value = udev_device_get_sysname(parent); + ++ if (!value) ++ return 1; ++ + strncpy(pci_name, value, SLOT_NAME_SIZE); + udev_device_unref(hostdev); + return 0; +@@ -1518,6 +1521,8 @@ ccw_sysfs_pathinfo (struct path * pp, vector hwtable) + * host / bus / target / lun + */ + attr_path = udev_device_get_sysname(parent); ++ if (!attr_path) ++ return PATHINFO_FAILED; + pp->sg_id.lun = 0; + if (sscanf(attr_path, "%i.%i.%x", + &pp->sg_id.host_no, +diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c +index 09cdddf0..5feb1e95 100644 +--- a/libmultipath/foreign/nvme.c ++++ b/libmultipath/foreign/nvme.c +@@ -482,6 +482,7 @@ _find_path_by_syspath(struct nvme_map *map, const char *syspath) + struct nvme_pathgroup *pg; + char real[PATH_MAX]; + const char *ppath; ++ const char *psyspath; + int i; + + ppath = realpath(syspath, real); +@@ -493,8 +494,8 @@ _find_path_by_syspath(struct nvme_map *map, const char *syspath) + vector_foreach_slot(&map->pgvec, pg, i) { + struct nvme_path *path = nvme_pg_to_path(pg); + +- if (!strcmp(ppath, +- udev_device_get_syspath(path->udev))) ++ psyspath = udev_device_get_syspath(path->udev); ++ if (psyspath && !strcmp(ppath, psyspath)) + return path; + } + condlog(4, "%s: %s: %s not found", __func__, THIS, ppath); +@@ -538,6 +539,7 @@ struct udev_device *get_ctrl_blkdev(const struct context *ctx, + struct udev_list_entry *item; + struct udev_device *blkdev = NULL; + struct udev_enumerate *enm = udev_enumerate_new(ctx->udev); ++ const char *devtype; + + if (enm == NULL) + return NULL; +@@ -562,7 +564,9 @@ struct udev_device *get_ctrl_blkdev(const struct context *ctx, + udev_list_entry_get_name(item)); + if (tmp == NULL) + continue; +- if (!strcmp(udev_device_get_devtype(tmp), "disk")) { ++ ++ devtype = udev_device_get_devtype(tmp); ++ if (devtype && !strcmp(devtype, "disk")) { + blkdev = tmp; + break; + } else +-- +2.17.2 + diff --git a/SOURCES/0065-libmultipath-cleanup-code-to-strip-wwid-trailing-spa.patch b/SOURCES/0065-libmultipath-cleanup-code-to-strip-wwid-trailing-spa.patch new file mode 100644 index 0000000..41aed73 --- /dev/null +++ b/SOURCES/0065-libmultipath-cleanup-code-to-strip-wwid-trailing-spa.patch @@ -0,0 +1,47 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 24 Feb 2021 00:33:20 -0600 +Subject: [PATCH] libmultipath: cleanup code to strip wwid trailing spaces + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/discovery.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index 74abf34d..126a70b3 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -2062,11 +2062,11 @@ int + get_uid (struct path * pp, int path_state, struct udev_device *udev, + int allow_fallback) + { +- char *c; + const char *origin = "unknown"; + ssize_t len = 0; + struct config *conf; + int used_fallback = 0; ++ size_t i; + + if (!pp->uid_attribute && !pp->getuid) { + conf = get_multipath_config(); +@@ -2119,12 +2119,9 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev, + return 1; + } else { + /* Strip any trailing blanks */ +- c = strchr(pp->wwid, '\0'); +- c--; +- while (c && c >= pp->wwid && *c == ' ') { +- *c = '\0'; +- c--; +- } ++ for (i = strlen(pp->wwid); i > 0 && pp->wwid[i-1] == ' '; i--); ++ /* no-op */ ++ pp->wwid[i] = '\0'; + } + condlog((used_fallback)? 1 : 3, "%s: uid = %s (%s)", pp->dev, + *pp->wwid == '\0' ? "" : pp->wwid, origin); +-- +2.17.2 + diff --git a/SOURCES/0066-multipathd-add-recheck_wwid-option-to-verify-the-pat.patch b/SOURCES/0066-multipathd-add-recheck_wwid-option-to-verify-the-pat.patch new file mode 100644 index 0000000..500c1ce --- /dev/null +++ b/SOURCES/0066-multipathd-add-recheck_wwid-option-to-verify-the-pat.patch @@ -0,0 +1,429 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 24 Feb 2021 00:33:22 -0600 +Subject: [PATCH] multipathd: add recheck_wwid option to verify the path wwid + +There are cases where the wwid of a path changes due to LUN remapping +without triggering uevent for the changed path. Multipathd has no method +for trying to catch these cases, and corruption has resulted because of +it. + +In order to have a better chance at catching these cases, multipath now +has a recheck_wwid option. If this is set to "yes", when a failed path +has become active again, multipathd will recheck its wwid. If multipathd +notices that a path's wwid has changed, it will remove and re-add the +path, just like the existing wwid checking code for change events does. +In cases where the no uevent occurs, both the udev database entry and +sysfs will have the old wwid, so the only way to get a current wwid is +to ask the device directly. Currently multipath only has code to +directly get the wwid for scsi devices, so this option only effects scsi +devices, and they must be configured to be able to use the uid_fallback +methods. To make sure both the sysfs and udev database values are +updated, multipathd triggers a both a rescan of the device and a udev +add event. + +Co-developed-by: Chongyun Wu +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck Reviewed-by: Martin Wilck +--- + libmultipath/config.c | 2 + + libmultipath/config.h | 2 + + libmultipath/configure.c | 4 +- + libmultipath/configure.h | 2 + + libmultipath/defaults.h | 1 + + libmultipath/dict.c | 11 ++++++ + libmultipath/discovery.c | 3 +- + libmultipath/discovery.h | 1 + + libmultipath/propsel.c | 21 ++++++++++ + libmultipath/propsel.h | 1 + + libmultipath/structs.h | 7 ++++ + multipath/multipath.conf.5 | 14 +++++++ + multipathd/cli_handlers.c | 9 +++++ + multipathd/main.c | 78 ++++++++++++++++++++++++++++++++++++++ + multipathd/main.h | 2 + + 15 files changed, 155 insertions(+), 3 deletions(-) + +diff --git a/libmultipath/config.c b/libmultipath/config.c +index dd645f17..abbddaf1 100644 +--- a/libmultipath/config.c ++++ b/libmultipath/config.c +@@ -371,6 +371,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src) + merge_num(max_sectors_kb); + merge_num(ghost_delay); + merge_num(all_tg_pt); ++ merge_num(recheck_wwid); + merge_num(vpd_vendor_id); + merge_num(san_path_err_threshold); + merge_num(san_path_err_forget_rate); +@@ -762,6 +763,7 @@ load_config (char * file) + conf->remove_retries = 0; + conf->ghost_delay = DEFAULT_GHOST_DELAY; + conf->all_tg_pt = DEFAULT_ALL_TG_PT; ++ conf->recheck_wwid = DEFAULT_RECHECK_WWID; + /* + * preload default hwtable + */ +diff --git a/libmultipath/config.h b/libmultipath/config.h +index a22c1b4e..e2e3f143 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -84,6 +84,7 @@ struct hwentry { + int ghost_delay; + int all_tg_pt; + int vpd_vendor_id; ++ int recheck_wwid; + char * bl_product; + }; + +@@ -188,6 +189,7 @@ struct config { + int skip_delegate; + unsigned int version[3]; + unsigned int sequence_nr; ++ int recheck_wwid; + + char * multipath_dir; + char * selector; +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index a6893d8d..f24d9283 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -506,8 +506,8 @@ trigger_udev_change(const struct multipath *mpp) + udev_device_unref(udd); + } + +-static void trigger_partitions_udev_change(struct udev_device *dev, +- const char *action, int len) ++void trigger_partitions_udev_change(struct udev_device *dev, ++ const char *action, int len) + { + struct udev_enumerate *part_enum; + struct udev_list_entry *item; +diff --git a/libmultipath/configure.h b/libmultipath/configure.h +index 0e33bf40..81090dd4 100644 +--- a/libmultipath/configure.h ++++ b/libmultipath/configure.h +@@ -57,3 +57,5 @@ int get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type, + int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, int is_daemon); + struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type); + void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath); ++void trigger_partitions_udev_change(struct udev_device *dev, const char *action, ++ int len); +diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h +index 52fe05b9..f1cb000d 100644 +--- a/libmultipath/defaults.h ++++ b/libmultipath/defaults.h +@@ -50,6 +50,7 @@ + #define DEFAULT_FIND_MULTIPATHS_TIMEOUT -10 + #define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1 + #define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF ++#define DEFAULT_RECHECK_WWID RECHECK_WWID_OFF + /* Enable all foreign libraries by default */ + #define DEFAULT_ENABLE_FOREIGN "" + +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index 8fd91d8c..13698b76 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -1413,6 +1413,14 @@ declare_hw_snprint(all_tg_pt, print_yes_no_undef) + declare_def_handler(marginal_pathgroups, set_yes_no) + declare_def_snprint(marginal_pathgroups, print_yes_no) + ++declare_def_handler(recheck_wwid, set_yes_no_undef) ++declare_def_snprint_defint(recheck_wwid, print_yes_no_undef, DEFAULT_RECHECK_WWID) ++declare_ovr_handler(recheck_wwid, set_yes_no_undef) ++declare_ovr_snprint(recheck_wwid, print_yes_no_undef) ++declare_hw_handler(recheck_wwid, set_yes_no_undef) ++declare_hw_snprint(recheck_wwid, print_yes_no_undef) ++ ++ + static int + def_uxsock_timeout_handler(struct config *conf, vector strvec) + { +@@ -1824,6 +1832,7 @@ init_keywords(vector keywords) + install_keyword("enable_foreign", &def_enable_foreign_handler, + &snprint_def_enable_foreign); + install_keyword("marginal_pathgroups", &def_marginal_pathgroups_handler, &snprint_def_marginal_pathgroups); ++ install_keyword("recheck_wwid", &def_recheck_wwid_handler, &snprint_def_recheck_wwid); + __deprecated install_keyword("default_selector", &def_selector_handler, NULL); + __deprecated install_keyword("default_path_grouping_policy", &def_pgpolicy_handler, NULL); + __deprecated install_keyword("default_uid_attribute", &def_uid_attribute_handler, NULL); +@@ -1913,6 +1922,7 @@ init_keywords(vector keywords) + install_keyword("ghost_delay", &hw_ghost_delay_handler, &snprint_hw_ghost_delay); + install_keyword("all_tg_pt", &hw_all_tg_pt_handler, &snprint_hw_all_tg_pt); + install_keyword("vpd_vendor", &hw_vpd_vendor_handler, &snprint_hw_vpd_vendor); ++ install_keyword("recheck_wwid", &hw_recheck_wwid_handler, &snprint_hw_recheck_wwid); + install_sublevel_end(); + + install_keyword_root("overrides", &overrides_handler); +@@ -1954,6 +1964,7 @@ init_keywords(vector keywords) + install_keyword("max_sectors_kb", &ovr_max_sectors_kb_handler, &snprint_ovr_max_sectors_kb); + install_keyword("ghost_delay", &ovr_ghost_delay_handler, &snprint_ovr_ghost_delay); + install_keyword("all_tg_pt", &ovr_all_tg_pt_handler, &snprint_ovr_all_tg_pt); ++ install_keyword("recheck_wwid", &ovr_recheck_wwid_handler, &snprint_ovr_recheck_wwid); + + install_keyword_root("multipaths", &multipaths_handler); + install_keyword_multi("multipath", &multipath_handler, NULL); +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index 126a70b3..bc267609 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -2040,7 +2040,7 @@ static ssize_t uid_fallback(struct path *pp, int path_state, + return len; + } + +-static bool has_uid_fallback(struct path *pp) ++bool has_uid_fallback(struct path *pp) + { + /* + * Falling back to direct WWID determination is dangerous +@@ -2072,6 +2072,7 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev, + conf = get_multipath_config(); + pthread_cleanup_push(put_multipath_config, conf); + select_getuid(conf, pp); ++ select_recheck_wwid(conf, pp); + pthread_cleanup_pop(1); + } + +diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h +index d3193daf..a5446b4d 100644 +--- a/libmultipath/discovery.h ++++ b/libmultipath/discovery.h +@@ -54,6 +54,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); + 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 1150cfe8..127b3370 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -581,6 +581,27 @@ out: + return 0; + } + ++/* must be called after select_getuid */ ++int select_recheck_wwid(struct config *conf, struct path * pp) ++{ ++ const char *origin; ++ ++ pp_set_ovr(recheck_wwid); ++ pp_set_hwe(recheck_wwid); ++ 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 || pp->getuid != NULL || ++ !has_uid_fallback(pp))) { ++ pp->recheck_wwid = RECHECK_WWID_OFF; ++ origin = "(setting: unsupported by device type/config)"; ++ } ++ condlog(3, "%s: recheck_wwid = %i %s", pp->dev, pp->recheck_wwid, ++ origin); ++ return 0; ++} ++ + void + detect_prio(struct config *conf, struct path * pp) + { +diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h +index a68bacf0..72a7e33c 100644 +--- a/libmultipath/propsel.h ++++ b/libmultipath/propsel.h +@@ -7,6 +7,7 @@ int select_features (struct config *conf, struct multipath * mp); + int select_hwhandler (struct config *conf, struct multipath * mp); + int select_checker(struct config *conf, struct path *pp); + int select_getuid (struct config *conf, struct path * pp); ++int select_recheck_wwid(struct config *conf, struct path * pp); + int select_prio (struct config *conf, struct path * pp); + int select_find_multipaths_timeout(struct config *conf, struct path *pp); + int select_no_path_retry(struct config *conf, struct multipath *mp); +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 65542dea..a5dbad5b 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -252,6 +252,12 @@ enum eh_deadline_states { + EH_DEADLINE_ZERO = UOZ_ZERO, + }; + ++enum recheck_wwid_states { ++ RECHECK_WWID_UNDEF = YNU_UNDEF, ++ RECHECK_WWID_OFF = YNU_NO, ++ RECHECK_WWID_ON = YNU_YES, ++}; ++ + struct vpd_vendor_page { + int pg; + const char *name; +@@ -326,6 +332,7 @@ struct path { + int find_multipaths_timeout; + int marginal; + int vpd_vendor_id; ++ int recheck_wwid; + /* configlet pointers */ + vector hwe; + struct gen_path generic_path; +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index a5686090..6da15aad 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -1256,6 +1256,20 @@ The default is: \fB\(dq\(dq\fR (the empty regular expression) + .RE + . + . ++.TP ++.B recheck_wwid ++If set to \fIyes\fR, when a failed path is restored, its wwid is rechecked. If ++the wwid has changed, the path is removed from the current multipath device, ++and re-added as a new path. Multipathd will also recheck a path's wwid if it is ++manually re-added. This option only works for SCSI devices that are configured ++to use the default uid_attribute, \fIID_SERIAL\fR, or sysfs for getting their ++wwid. ++.RS ++.TP ++The default is \fBno\fR ++.RE ++. ++. + + . + .\" ---------------------------------------------------------------------------- +diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c +index 782bb003..8b4bd187 100644 +--- a/multipathd/cli_handlers.c ++++ b/multipathd/cli_handlers.c +@@ -715,6 +715,15 @@ cli_add_path (void * v, char ** reply, int * len, void * data) + pp = find_path_by_dev(vecs->pathvec, param); + if (pp) { + condlog(2, "%s: path already in pathvec", param); ++ ++ if (pp->recheck_wwid == RECHECK_WWID_ON && ++ check_path_wwid_change(pp)) { ++ condlog(0, "%s: wwid changed. Removing device", ++ pp->dev); ++ handle_path_wwid_change(pp, vecs); ++ return 1; ++ } ++ + if (pp->mpp) + return 0; + } else { +diff --git a/multipathd/main.c b/multipathd/main.c +index cc1aeea2..1fbc31eb 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -822,6 +822,73 @@ ev_remove_map (char * devname, char * alias, int minor, struct vectors * vecs) + return flush_map(mpp, vecs, 0); + } + ++static void ++rescan_path(struct udev_device *parent) ++{ ++ while(parent) { ++ const char *subsys = udev_device_get_subsystem(parent); ++ if (subsys && !strncmp(subsys, "scsi", 4)) ++ break; ++ parent = udev_device_get_parent(parent); ++ } ++ if (parent) ++ sysfs_attr_set_value(parent, "rescan", "1", strlen("1")); ++} ++ ++void ++handle_path_wwid_change(struct path *pp, struct vectors *vecs) ++{ ++ struct udev_device *udd; ++ ++ if (!pp || !pp->udev) ++ return; ++ ++ udd = udev_device_ref(pp->udev); ++ if (ev_remove_path(pp, vecs, 1) != 0 && pp->mpp) { ++ pp->dmstate = PSTATE_FAILED; ++ dm_fail_path(pp->mpp->alias, pp->dev_t); ++ } ++ rescan_path(udd); ++ sysfs_attr_set_value(udd, "uevent", "add", strlen("add")); ++ trigger_partitions_udev_change(udd, "add", strlen("add")); ++ udev_device_unref(udd); ++} ++ ++bool ++check_path_wwid_change(struct path *pp) ++{ ++ char wwid[WWID_SIZE]; ++ int len = 0; ++ size_t i; ++ ++ if (!strlen(pp->wwid)) ++ return false; ++ ++ /* Get the real fresh device wwid by sgio. sysfs still has old ++ * data, so only get_vpd_sgio will work to get the new wwid */ ++ len = get_vpd_sgio(pp->fd, 0x83, 0, wwid, WWID_SIZE); ++ ++ if (len <= 0) { ++ condlog(2, "%s: failed to check wwid by sgio: len = %d", ++ pp->dev, len); ++ return false; ++ } ++ ++ /*Strip any trailing blanks */ ++ for (i = strlen(pp->wwid); i > 0 && pp->wwid[i-1] == ' '; i--); ++ /* no-op */ ++ pp->wwid[i] = '\0'; ++ condlog(4, "%s: Got wwid %s by sgio", pp->dev, wwid); ++ ++ if (strncmp(wwid, pp->wwid, WWID_SIZE)) { ++ condlog(0, "%s: wwid '%s' doesn't match wwid '%s' from device", ++ pp->dev, pp->wwid, wwid); ++ return true; ++ } ++ ++ return false; ++} ++ + static int + uev_add_path (struct uevent *uev, struct vectors * vecs, int need_do_map) + { +@@ -1241,6 +1308,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) + condlog(0, "%s: path wwid changed from '%s' to '%s'", + uev->kernel, wwid, pp->wwid); + ev_remove_path(pp, vecs, 1); ++ rescan_path(uev->udev); + needs_reinit = 1; + goto out; + } else { +@@ -2101,6 +2169,16 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks) + return 0; + set_no_path_retry(pp->mpp); + ++ if (pp->recheck_wwid == RECHECK_WWID_ON && ++ (newstate == PATH_UP || newstate == PATH_GHOST) && ++ ((pp->state != PATH_UP && pp->state != PATH_GHOST) || ++ pp->dmstate == PSTATE_FAILED) && ++ check_path_wwid_change(pp)) { ++ condlog(0, "%s: path wwid change detected. Removing", pp->dev); ++ handle_path_wwid_change(pp, vecs); ++ return 0; ++ } ++ + if ((newstate == PATH_UP || newstate == PATH_GHOST) && + (san_path_check_enabled(pp->mpp) || + marginal_path_check_enabled(pp->mpp))) { +diff --git a/multipathd/main.h b/multipathd/main.h +index 5dff17e5..8f0028a9 100644 +--- a/multipathd/main.h ++++ b/multipathd/main.h +@@ -49,4 +49,6 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset); + int update_path_groups(struct multipath *mpp, struct vectors *vecs, + int refresh); + ++void handle_path_wwid_change(struct path *pp, struct vectors *vecs); ++bool check_path_wwid_change(struct path *pp); + #endif /* MAIN_H */ +-- +2.17.2 + diff --git a/SOURCES/0067-libmultipath-avoid-infinite-loop-with-bad-vpd-page-8.patch b/SOURCES/0067-libmultipath-avoid-infinite-loop-with-bad-vpd-page-8.patch new file mode 100644 index 0000000..c261ee2 --- /dev/null +++ b/SOURCES/0067-libmultipath-avoid-infinite-loop-with-bad-vpd-page-8.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 25 Mar 2021 19:52:43 -0500 +Subject: [PATCH] libmultipath: avoid infinite loop with bad vpd page 83 + identifier + +If a device with a scsi name identifier has an unknown prefix, +parse_vpd_pg83() needs to advance to the next identifier, instead of +simply trying the same one again in an infinite loop. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/discovery.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index bc267609..8c2ab073 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -1101,7 +1101,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, + if (memcmp(d + 4, "eui.", 4) && + memcmp(d + 4, "naa.", 4) && + memcmp(d + 4, "iqn.", 4)) +- continue; ++ break; + if (prio < 4) { + prio = 4; + vpd = d; +-- +2.17.2 + diff --git a/SOURCES/0068-libmultipath-fix-priorities-in-parse_vpd_pg83.patch b/SOURCES/0068-libmultipath-fix-priorities-in-parse_vpd_pg83.patch new file mode 100644 index 0000000..d4ab400 --- /dev/null +++ b/SOURCES/0068-libmultipath-fix-priorities-in-parse_vpd_pg83.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 25 Mar 2021 19:52:44 -0500 +Subject: [PATCH] libmultipath: fix priorities in parse_vpd_pg83 + +The priorities for the EUI-64 (0x02) and NAME (0x08) scsi identifiers in +parse_vpd_pg83() don't match their priorities in 55-scsi-sg3_id.rules. +Switch them so that they match. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/discovery.c | 16 ++++++++-------- + 1 file changed, 8 insertions(+), 8 deletions(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index 8c2ab073..5e988631 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -1096,19 +1096,19 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, + vpd = d; + } + break; +- case 0x8: +- /* SCSI Name: Prio 4 */ +- if (memcmp(d + 4, "eui.", 4) && +- memcmp(d + 4, "naa.", 4) && +- memcmp(d + 4, "iqn.", 4)) +- break; ++ case 0x2: ++ /* EUI-64: Prio 4 */ + if (prio < 4) { + prio = 4; + vpd = d; + } + break; +- case 0x2: +- /* EUI-64: Prio 3 */ ++ case 0x8: ++ /* SCSI Name: Prio 3 */ ++ if (memcmp(d + 4, "eui.", 4) && ++ memcmp(d + 4, "naa.", 4) && ++ memcmp(d + 4, "iqn.", 4)) ++ break; + if (prio < 3) { + prio = 3; + vpd = d; +-- +2.17.2 + diff --git a/SOURCES/0069-RH-make-parse_vpd_pg83-match-scsi_id-output.patch b/SOURCES/0069-RH-make-parse_vpd_pg83-match-scsi_id-output.patch new file mode 100644 index 0000000..d8f7ac1 --- /dev/null +++ b/SOURCES/0069-RH-make-parse_vpd_pg83-match-scsi_id-output.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + libmultipath/discovery.c | 18 ++---------------- + 1 file changed, 2 insertions(+), 16 deletions(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index 5e988631..2404cb87 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -1079,12 +1079,9 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, + naa_prio = 7; + break; + case 2: +- /* IEEE Extended: Prio 6 */ +- naa_prio = 6; +- break; + case 3: +- /* IEEE Locally assigned: Prio 1 */ +- naa_prio = 1; ++ /* IEEE Extended or Locally assigned: Prio 6 */ ++ naa_prio = 6; + break; + default: + /* Default: no priority */ +@@ -1103,17 +1100,6 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, + vpd = d; + } + break; +- case 0x8: +- /* SCSI Name: Prio 3 */ +- if (memcmp(d + 4, "eui.", 4) && +- memcmp(d + 4, "naa.", 4) && +- memcmp(d + 4, "iqn.", 4)) +- break; +- if (prio < 3) { +- prio = 3; +- vpd = d; +- } +- break; + case 0x1: + /* T-10 Vendor ID: Prio 2 */ + if (prio < 2) { +-- +2.17.2 + diff --git a/SOURCES/0070-multipathd-improve-getting-parent-udevice-in-rescan_.patch b/SOURCES/0070-multipathd-improve-getting-parent-udevice-in-rescan_.patch new file mode 100644 index 0000000..6bb94fd --- /dev/null +++ b/SOURCES/0070-multipathd-improve-getting-parent-udevice-in-rescan_.patch @@ -0,0 +1,44 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 25 Mar 2021 19:52:45 -0500 +Subject: [PATCH] multipathd: improve getting parent udevice in rescan_path + +Instead of looping through parents and checking, just call +udev_device_get_parent_with_subsystem_devtype() to get the +right one. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + multipathd/main.c | 14 +++++--------- + 1 file changed, 5 insertions(+), 9 deletions(-) + +diff --git a/multipathd/main.c b/multipathd/main.c +index 1fbc31eb..4598d354 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -823,16 +823,12 @@ ev_remove_map (char * devname, char * alias, int minor, struct vectors * vecs) + } + + static void +-rescan_path(struct udev_device *parent) ++rescan_path(struct udev_device *ud) + { +- while(parent) { +- const char *subsys = udev_device_get_subsystem(parent); +- if (subsys && !strncmp(subsys, "scsi", 4)) +- break; +- parent = udev_device_get_parent(parent); +- } +- if (parent) +- sysfs_attr_set_value(parent, "rescan", "1", strlen("1")); ++ ud = udev_device_get_parent_with_subsystem_devtype(ud, "scsi", ++ "scsi_device"); ++ if (ud) ++ sysfs_attr_set_value(ud, "rescan", "1", strlen("1")); + } + + void +-- +2.17.2 + diff --git a/SOURCES/0071-multipathd-don-t-trigger-uevent-for-partitions-on-ww.patch b/SOURCES/0071-multipathd-don-t-trigger-uevent-for-partitions-on-ww.patch new file mode 100644 index 0000000..b96d496 --- /dev/null +++ b/SOURCES/0071-multipathd-don-t-trigger-uevent-for-partitions-on-ww.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 25 Mar 2021 19:52:46 -0500 +Subject: [PATCH] multipathd: don't trigger uevent for partitions on wwid + change + +If the wwid changed, the device is no longer the same, so sending add +events to the devices partitions doesn't make any sense. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + multipathd/main.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/multipathd/main.c b/multipathd/main.c +index 4598d354..e6c19ab2 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -846,7 +846,6 @@ handle_path_wwid_change(struct path *pp, struct vectors *vecs) + } + rescan_path(udd); + sysfs_attr_set_value(udd, "uevent", "add", strlen("add")); +- trigger_partitions_udev_change(udd, "add", strlen("add")); + udev_device_unref(udd); + } + +-- +2.17.2 + diff --git a/SOURCES/0072-RH-mpathconf-correctly-handle-spaces-after-option-na.patch b/SOURCES/0072-RH-mpathconf-correctly-handle-spaces-after-option-na.patch new file mode 100644 index 0000000..0d39a77 --- /dev/null +++ b/SOURCES/0072-RH-mpathconf-correctly-handle-spaces-after-option-na.patch @@ -0,0 +1,165 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 9 Jul 2021 14:30:10 -0500 +Subject: [PATCH] RH: mpathconf: correctly handle spaces after option names + +mpathconf was either accepting any number of spaces, including zero, +after option names, or it was only accepting one space. It should +accept one or more spaces. + +Signed-off-by: Benjamin Marzinski +--- + multipath/mpathconf | 40 ++++++++++++++++++++-------------------- + 1 file changed, 20 insertions(+), 20 deletions(-) + +diff --git a/multipath/mpathconf b/multipath/mpathconf +index 2f4f3eaf..5f2285ab 100644 +--- a/multipath/mpathconf ++++ b/multipath/mpathconf +@@ -240,7 +240,7 @@ function validate_args + 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 [ -z "$ENABLE" -a -z "$FIND" -a -z "$FRIENDLY" -a -z "$PROPERTY" -a -z "$FOREIGN" ]; then + SHOW_STATUS=1 + fi + if [ -n "$MODULE" ] && [ "$MODULE" != "y" -a "$MODULE" != "n" ]; then +@@ -315,36 +315,36 @@ if [ "$MULTIPATHD" = "y" ]; then + 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 +- HAVE_FIND=`sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | sed -n 's/^[[:blank:]]*find_multipaths[[:blank:]]*\([^[:blank:]]*\).*$/\1/p' | sed -n 1p` ++ 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:]]*#[[: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 + HAVE_FOREIGN=2 +@@ -352,9 +352,9 @@ if [ "$HAVE_DEFAULTS" = "1" ]; then + 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 +@@ -427,14 +427,14 @@ fi + + 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 +@@ -448,7 +448,7 @@ _EOF_ + 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 +@@ -456,7 +456,7 @@ elif [ "$ENABLE" = 0 ]; then + 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 + +@@ -467,14 +467,14 @@ if [ -n "$FIND" ]; then + ' $TMPFILE + CHANGED_CONFIG=1 + elif [ "$FIND" != "$HAVE_FIND" ]; then +- sed -i '/^defaults[[:space:]]*{/,/^}/ s/^[[:blank:]]*find_multipaths[[:blank:]]*[^[:blank:]]*/ find_multipaths '"$FIND"'/' $TMPFILE ++ 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 +@@ -484,14 +484,14 @@ elif [ "$FRIENDLY" = "y" ]; then + ' $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 [ "$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 +@@ -501,7 +501,7 @@ elif [ "$PROPERTY" = "y" ]; then + ' $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 +-- +2.30.2 + diff --git a/SOURCES/0073-multipath.conf-fix-typo-in-checker_timeout-descripti.patch b/SOURCES/0073-multipath.conf-fix-typo-in-checker_timeout-descripti.patch new file mode 100644 index 0000000..849186f --- /dev/null +++ b/SOURCES/0073-multipath.conf-fix-typo-in-checker_timeout-descripti.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 15 Jul 2021 14:48:15 -0500 +Subject: [PATCH] multipath.conf: fix typo in checker_timeout description + +Signed-off-by: Benjamin Marzinski +--- + multipath/multipath.conf.5 | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 6da15aad..0c04c7e4 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -638,7 +638,7 @@ Specify the timeout to use for path checkers and prioritizers, in seconds. + Only prioritizers that issue scsi commands use checker_timeout. Checkers + that support an asynchronous mode (\fItur\fR and \fIdirectio\fR), will + return shortly after being called by multipathd, regardless of whether the +-storage array responds. If the storage array hasn't responded, mulitpathd will ++storage array responds. If the storage array hasn't responded, multipathd will + check for a response every second, until \fIchecker_timeout\fR seconds have + elapsed. + .RS diff --git a/SOURCES/0074-mpathpersist-fail-commands-when-no-usable-paths-exis.patch b/SOURCES/0074-mpathpersist-fail-commands-when-no-usable-paths-exis.patch new file mode 100644 index 0000000..0834837 --- /dev/null +++ b/SOURCES/0074-mpathpersist-fail-commands-when-no-usable-paths-exis.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 22 Jul 2021 17:48:06 -0500 +Subject: [PATCH] mpathpersist: fail commands when no usable paths exist + +"mpathpersist -oCK " will return success if it +is run on devices with no usable paths, but nothing is actually done. +The -L command will fail, but it should give up sooner, and with a more +helpful error message. + +Signed-off-by: Benjamin Marzinski +--- + libmpathpersist/mpath_persist.c | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c +index 07a5f17f..d0744773 100644 +--- a/libmpathpersist/mpath_persist.c ++++ b/libmpathpersist/mpath_persist.c +@@ -629,7 +629,8 @@ int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope, + return ret ; + } + } +- return MPATH_PR_SUCCESS; ++ condlog (0, "%s: no path available", mpp->wwid); ++ return MPATH_PR_DMMP_ERROR; + } + + int send_prout_activepath(char * dev, int rq_servact, int rq_scope, +@@ -688,6 +689,11 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, + + active_pathcount = pathcount (mpp, PATH_UP) + pathcount (mpp, PATH_GHOST); + ++ if (active_pathcount == 0) { ++ condlog (0, "%s: no path available", mpp->wwid); ++ return MPATH_PR_DMMP_ERROR; ++ } ++ + struct threadinfo thread[active_pathcount]; + memset(thread, 0, sizeof(thread)); + for (i = 0; i < active_pathcount; i++){ diff --git a/SOURCES/0075-multipath-print-warning-if-multipathd-is-not-running.patch b/SOURCES/0075-multipath-print-warning-if-multipathd-is-not-running.patch new file mode 100644 index 0000000..3c42a01 --- /dev/null +++ b/SOURCES/0075-multipath-print-warning-if-multipathd-is-not-running.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 16 Jul 2021 12:39:17 -0500 +Subject: [PATCH] multipath: print warning if multipathd is not running. + +If multipath notices that multipath devices exist or were created, and +multipathd is not running, it now prints a warning message, so users are +notified of the issue. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/configure.c | 13 +++++++++++-- + libmultipath/configure.h | 1 + + multipath/main.c | 5 +++++ + 3 files changed, 17 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index f24d9283..9c8d3e34 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -1043,7 +1043,8 @@ deadmap (struct multipath * mpp) + return 1; /* dead */ + } + +-int check_daemon(void) ++extern int ++check_daemon(void) + { + int fd; + char *reply; +@@ -1097,6 +1098,8 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, + struct config *conf; + int allow_queueing; + uint64_t *size_mismatch_seen; ++ bool map_processed = false; ++ bool no_daemon = false; + + /* ignore refwwid if it's empty */ + if (refwwid && !strlen(refwwid)) +@@ -1239,7 +1242,9 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, + conf = get_multipath_config(); + allow_queueing = conf->allow_queueing; + put_multipath_config(conf); +- if (!is_daemon && !allow_queueing && !check_daemon()) { ++ if (!is_daemon && !allow_queueing && ++ (no_daemon || !check_daemon())) { ++ no_daemon = true; + if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && + mpp->no_path_retry != NO_PATH_RETRY_FAIL) + condlog(3, "%s: multipathd not running, unset " +@@ -1267,6 +1272,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, + else + remove_map(mpp, vecs, 0); + } ++ map_processed = true; + } + /* + * Flush maps with only dead paths (ie not in sysfs) +@@ -1292,6 +1298,9 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, + condlog(2, "%s: remove (dead)", alias); + } + } ++ if (map_processed && !is_daemon && (no_daemon || !check_daemon())) ++ condlog(2, "multipath devices exist, but multipathd service is not running"); ++ + ret = CP_OK; + out: + free(size_mismatch_seen); +diff --git a/libmultipath/configure.h b/libmultipath/configure.h +index 81090dd4..8a266d31 100644 +--- a/libmultipath/configure.h ++++ b/libmultipath/configure.h +@@ -59,3 +59,4 @@ struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type); + void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath); + void trigger_partitions_udev_change(struct udev_device *dev, const char *action, + int len); ++int check_daemon(void); +diff --git a/multipath/main.c b/multipath/main.c +index 607cada2..14d045c9 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -254,6 +254,7 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid) + int i; + struct multipath * mpp; + char params[PARAMS_SIZE], status[PARAMS_SIZE]; ++ bool maps_present = false; + + if (dm_get_maps(curmp)) + return 1; +@@ -302,6 +303,8 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid) + + if (cmd == CMD_CREATE) + reinstate_paths(mpp); ++ ++ maps_present = true; + } + + if (cmd == CMD_LIST_SHORT || cmd == CMD_LIST_LONG) { +@@ -311,6 +314,8 @@ get_dm_mpvec (enum mpath_cmds cmd, vector curmp, vector pathvec, char * refwwid) + put_multipath_config(conf); + } + ++ if (maps_present && !check_daemon()) ++ condlog(2, "multipath devices exist, but multipathd service is not running"); + return 0; + } + diff --git a/SOURCES/0076-multipathd-don-t-access-path-if-it-was-deleted.patch b/SOURCES/0076-multipathd-don-t-access-path-if-it-was-deleted.patch new file mode 100644 index 0000000..88aa305 --- /dev/null +++ b/SOURCES/0076-multipathd-don-t-access-path-if-it-was-deleted.patch @@ -0,0 +1,64 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 23 Jul 2021 14:10:06 -0500 +Subject: [PATCH] multipathd: don't access path if it was deleted + +ev_remove_path() could fail and still delete the path. This could cause +problems for handle_path_wwid_change(), which expected that a failure +meant that the path still existed. ev_remove_path now returns a +different error code for failure to reload the multipath device, so that +it can be differentiated from cases where the path was no removed. + +Signed-off-by: Benjamin Marzinski +--- + multipathd/main.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/multipathd/main.c b/multipathd/main.c +index e6c19ab2..823b53a2 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -86,6 +86,9 @@ + #define FILE_NAME_SIZE 256 + #define CMDSIZE 160 + ++#define PATH_REMOVE_FAILED 1 ++#define MAP_RELOAD_FAILED 2 ++ + #define LOG_MSG(lvl, verb, pp) \ + do { \ + if (pp->mpp && checker_selected(&pp->checker) && \ +@@ -840,7 +843,7 @@ handle_path_wwid_change(struct path *pp, struct vectors *vecs) + return; + + udd = udev_device_ref(pp->udev); +- if (ev_remove_path(pp, vecs, 1) != 0 && pp->mpp) { ++ if (ev_remove_path(pp, vecs, 1) == PATH_REMOVE_FAILED && pp->mpp) { + pp->dmstate = PSTATE_FAILED; + dm_fail_path(pp->mpp->alias, pp->dev_t); + } +@@ -1226,13 +1229,13 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map) + condlog(0, "%s: failed in domap for " + "removal of path %s", + mpp->alias, pp->dev); +- retval = 1; ++ retval = MAP_RELOAD_FAILED; + } else { + /* + * update our state from kernel + */ + if (setup_multipath(vecs, mpp)) +- return 1; ++ return PATH_REMOVE_FAILED; + sync_map_state(mpp); + + condlog(2, "%s [%s]: path removed from map %s", +@@ -1250,7 +1253,7 @@ out: + + fail: + remove_map_and_stop_waiter(mpp, vecs); +- return 1; ++ return PATH_REMOVE_FAILED; + } + + static int diff --git a/SOURCES/0077-multipathd.socket-add-missing-conditions-from-servic.patch b/SOURCES/0077-multipathd.socket-add-missing-conditions-from-servic.patch new file mode 100644 index 0000000..3b1fb1c --- /dev/null +++ b/SOURCES/0077-multipathd.socket-add-missing-conditions-from-servic.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Luca BRUNO +Date: Fri, 24 Sep 2021 09:34:01 +0000 +Subject: [PATCH] multipathd.socket: add missing conditions from service unit + +Upstream Status: https://github.com/openSUSE/multipath-tools.git +Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2008101 +Conflicts: Match the conditions with RHEL-8 multipathd.service unit + +commit 345ccf564ce7d904641bd32baf4fc53c2283d95c +Author: Luca BRUNO +Date: Fri Sep 24 09:34:01 2021 +0000 + + multipathd.socket: add missing conditions from service unit + + This aligns 'multipathd' socket and service units, by adding the + start conditions that are set on the service but not on the socket. + It should help avoiding situations where the socket unit ends up + marked as failed after hitting its retry-limit. + + Fixes: https://github.com/opensvc/multipath-tools/issues/15 + Signed-off-by: Luca BRUNO + Reviewed-by: Martin Wilck + +Signed-off-by: Benjamin Marzinski +--- + multipathd/multipathd.socket | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/multipathd/multipathd.socket b/multipathd/multipathd.socket +index 0ed4a1f7..c62c0fc8 100644 +--- a/multipathd/multipathd.socket ++++ b/multipathd/multipathd.socket +@@ -1,6 +1,9 @@ + [Unit] + Description=multipathd control socket + DefaultDependencies=no ++ConditionPathExists=/etc/multipath.conf ++ConditionKernelCommandLine=!nompath ++ConditionKernelCommandLine=!multipath=off + Before=sockets.target + + [Socket] diff --git a/SOURCES/0078-libmulitpath-add-section-name-to-invalid-keyword-out.patch b/SOURCES/0078-libmulitpath-add-section-name-to-invalid-keyword-out.patch new file mode 100644 index 0000000..8006ecd --- /dev/null +++ b/SOURCES/0078-libmulitpath-add-section-name-to-invalid-keyword-out.patch @@ -0,0 +1,70 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 23 Sep 2021 14:16:51 -0500 +Subject: [PATCH] libmulitpath: add section name to invalid keyword output + +If users forget the closing brace for a section in multipath.conf, +multipath has no way to detect that. When it sees the keyword at the +start of the next section, it will complain that there is an invalid +keyword, because that keyword doesn't belong in previous section (which +was never ended with a closing brace). This can confuse users. To make +this easier to understand, when multipath prints and invalid keyword +message, it now also prints the current section name, which can give +users a hint that they didn't end the previous section. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/parser.c | 21 ++++++++++++++------- + 1 file changed, 14 insertions(+), 7 deletions(-) + +diff --git a/libmultipath/parser.c b/libmultipath/parser.c +index 48b54e87..96b95936 100644 +--- a/libmultipath/parser.c ++++ b/libmultipath/parser.c +@@ -507,7 +507,8 @@ validate_config_strvec(vector strvec, char *file) + } + + static int +-process_stream(struct config *conf, FILE *stream, vector keywords, char *file) ++process_stream(struct config *conf, FILE *stream, vector keywords, ++ const char *section, char *file) + { + int i; + int r = 0, t; +@@ -571,16 +572,22 @@ process_stream(struct config *conf, FILE *stream, vector keywords, char *file) + if (keyword->sub) { + kw_level++; + r += process_stream(conf, stream, +- keyword->sub, file); ++ keyword->sub, ++ keyword->string, ++ file); + kw_level--; + } + break; + } + } +- if (i >= VECTOR_SIZE(keywords)) +- condlog(1, "%s line %d, invalid keyword: %s", +- file, line_nr, str); +- ++ if (i >= VECTOR_SIZE(keywords)) { ++ if (section) ++ condlog(1, "%s line %d, invalid keyword in the %s section: %s", ++ file, line_nr, section, str); ++ else ++ condlog(1, "%s line %d, invalid keyword: %s", ++ file, line_nr, str); ++ } + free_strvec(strvec); + } + if (kw_level == 1) +@@ -611,7 +618,7 @@ process_file(struct config *conf, char *file) + + /* Stream handling */ + line_nr = 0; +- r = process_stream(conf, stream, conf->keywords, file); ++ r = process_stream(conf, stream, conf->keywords, NULL, file); + fclose(stream); + //free_keywords(keywords); + diff --git a/SOURCES/0079-libmultipath-use-typedef-for-keyword-handler-and-pri.patch b/SOURCES/0079-libmultipath-use-typedef-for-keyword-handler-and-pri.patch new file mode 100644 index 0000000..5b786f0 --- /dev/null +++ b/SOURCES/0079-libmultipath-use-typedef-for-keyword-handler-and-pri.patch @@ -0,0 +1,97 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 23 Sep 2021 21:39:36 -0500 +Subject: [PATCH] libmultipath: use typedef for keyword handler and print + functions + +Don't keep writing out the function type. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/parser.c | 10 +++++----- + libmultipath/parser.h | 25 ++++++++++++------------- + 2 files changed, 17 insertions(+), 18 deletions(-) + +diff --git a/libmultipath/parser.c b/libmultipath/parser.c +index 96b95936..e511acf9 100644 +--- a/libmultipath/parser.c ++++ b/libmultipath/parser.c +@@ -32,8 +32,8 @@ static int line_nr; + + int + keyword_alloc(vector keywords, char *string, +- int (*handler) (struct config *, vector), +- int (*print) (struct config *, char *, int, const void*), ++ handler_fn *handler, ++ print_fn *print, + int unique) + { + struct keyword *keyword; +@@ -71,8 +71,8 @@ install_sublevel_end(void) + + int + _install_keyword(vector keywords, char *string, +- int (*handler) (struct config *, vector), +- int (*print) (struct config *, char *, int, const void*), ++ handler_fn *handler, ++ print_fn *print, + int unique) + { + int i = 0; +@@ -562,7 +562,7 @@ process_stream(struct config *conf, FILE *stream, vector keywords, + goto out; + } + if (keyword->handler) { +- t = (*keyword->handler) (conf, strvec); ++ t = keyword->handler(conf, strvec); + r += t; + if (t) + condlog(1, "multipath.conf +%d, parsing failed: %s", +diff --git a/libmultipath/parser.h b/libmultipath/parser.h +index b7917052..e8d89607 100644 +--- a/libmultipath/parser.h ++++ b/libmultipath/parser.h +@@ -39,11 +39,15 @@ + #define EOB "}" + #define MAXBUF 1024 + +-/* ketword definition */ ++ ++/* keyword definition */ ++typedef int print_fn(struct config *, char *, int, const void *); ++typedef int handler_fn(struct config *, vector); ++ + struct keyword { + char *string; +- int (*handler) (struct config *, vector); +- int (*print) (struct config *, char *, int, const void *); ++ handler_fn *handler; ++ print_fn *print; + vector sub; + int unique; + }; +@@ -58,19 +62,14 @@ struct keyword { + for (i = 0; i < (k)->sub->allocated && ((p) = (k)->sub->slot[i]); i++) + + /* Prototypes */ +-extern int keyword_alloc(vector keywords, char *string, +- int (*handler) (struct config *, vector), +- int (*print) (struct config *, char *, int, +- const void *), +- int unique); ++extern int keyword_alloc(vector keywords, char *string, handler_fn *handler, ++ print_fn *print, int unique); + #define install_keyword_root(str, h) keyword_alloc(keywords, str, h, NULL, 1) + extern void install_sublevel(void); + extern void install_sublevel_end(void); +-extern int _install_keyword(vector keywords, char *string, +- int (*handler) (struct config *, vector), +- int (*print) (struct config *, char *, int, +- const void *), +- int unique); ++ ++extern int _install_keyword(vector keywords, char *string, handler_fn *handler, ++ print_fn *print, int unique); + #define install_keyword(str, vec, pri) _install_keyword(keywords, str, vec, pri, 1) + #define install_keyword_multi(str, vec, pri) _install_keyword(keywords, str, vec, pri, 0) + extern void dump_keywords(vector keydump, int level); diff --git a/SOURCES/0080-libmultipath-print-the-correct-file-when-parsing-fai.patch b/SOURCES/0080-libmultipath-print-the-correct-file-when-parsing-fai.patch new file mode 100644 index 0000000..50173e6 --- /dev/null +++ b/SOURCES/0080-libmultipath-print-the-correct-file-when-parsing-fai.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 24 Sep 2021 13:13:31 -0500 +Subject: [PATCH] libmultipath: print the correct file when parsing fails + +Don't assume that parsing failed on multipath.conf + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/parser.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/parser.c b/libmultipath/parser.c +index e511acf9..341f2b80 100644 +--- a/libmultipath/parser.c ++++ b/libmultipath/parser.c +@@ -565,8 +565,8 @@ process_stream(struct config *conf, FILE *stream, vector keywords, + t = keyword->handler(conf, strvec); + r += t; + if (t) +- condlog(1, "multipath.conf +%d, parsing failed: %s", +- line_nr, buf); ++ condlog(1, "%s line %d, parsing failed: %s", ++ file, line_nr, buf); + } + + if (keyword->sub) { diff --git a/SOURCES/0081-libmultipath-pass-file-and-line-number-to-keyword-ha.patch b/SOURCES/0081-libmultipath-pass-file-and-line-number-to-keyword-ha.patch new file mode 100644 index 0000000..e099ea7 --- /dev/null +++ b/SOURCES/0081-libmultipath-pass-file-and-line-number-to-keyword-ha.patch @@ -0,0 +1,536 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 24 Sep 2021 17:59:12 -0500 +Subject: [PATCH] libmultipath: pass file and line number to keyword handlers + +This will make it possible for the keyword handlers to print more useful +warning messages. It will be used by future patches. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/dict.c | 145 +++++++++++++++++++++++++----------------- + libmultipath/parser.c | 3 +- + libmultipath/parser.h | 2 +- + 3 files changed, 91 insertions(+), 59 deletions(-) + +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index 13698b76..a8872da7 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -28,7 +28,7 @@ + #include "dict.h" + + static int +-set_int(vector strvec, void *ptr) ++set_int(vector strvec, void *ptr, const char *file, int line_nr) + { + int *int_ptr = (int *)ptr; + char *buff, *eptr; +@@ -57,7 +57,7 @@ set_int(vector strvec, void *ptr) + } + + static int +-set_uint(vector strvec, void *ptr) ++set_uint(vector strvec, void *ptr, const char *file, int line_nr) + { + unsigned int *uint_ptr = (unsigned int *)ptr; + char *buff, *eptr, *p; +@@ -89,7 +89,7 @@ set_uint(vector strvec, void *ptr) + } + + static int +-set_str(vector strvec, void *ptr) ++set_str(vector strvec, void *ptr, const char *file, int line_nr) + { + char **str_ptr = (char **)ptr; + +@@ -104,7 +104,7 @@ set_str(vector strvec, void *ptr) + } + + static int +-set_regex(vector strvec, void *ptr) ++set_regex(vector strvec, void *ptr, const char *file, int line_nr) + { + char **str_ptr = (char **)ptr; + +@@ -119,7 +119,7 @@ set_regex(vector strvec, void *ptr) + } + + static int +-set_yes_no(vector strvec, void *ptr) ++set_yes_no(vector strvec, void *ptr, const char *file, int line_nr) + { + char * buff; + int *int_ptr = (int *)ptr; +@@ -138,7 +138,7 @@ set_yes_no(vector strvec, void *ptr) + } + + static int +-set_yes_no_undef(vector strvec, void *ptr) ++set_yes_no_undef(vector strvec, void *ptr, const char *file, int line_nr) + { + char * buff; + int *int_ptr = (int *)ptr; +@@ -240,9 +240,10 @@ print_yes_no_undef (char *buff, int len, long v) + + #define declare_def_handler(option, function) \ + static int \ +-def_ ## option ## _handler (struct config *conf, vector strvec) \ ++def_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ + { \ +- return function (strvec, &conf->option); \ ++ return function (strvec, &conf->option, file, line_nr); \ + } + + #define declare_def_snprint(option, function) \ +@@ -277,12 +278,13 @@ snprint_def_ ## option (struct config *conf, char * buff, int len, \ + + #define declare_hw_handler(option, function) \ + static int \ +-hw_ ## option ## _handler (struct config *conf, vector strvec) \ ++hw_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ + { \ + struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); \ + if (!hwe) \ + return 1; \ +- return function (strvec, &hwe->option); \ ++ return function (strvec, &hwe->option, file, line_nr); \ + } + + #define declare_hw_snprint(option, function) \ +@@ -296,11 +298,12 @@ snprint_hw_ ## option (struct config *conf, char * buff, int len, \ + + #define declare_ovr_handler(option, function) \ + static int \ +-ovr_ ## option ## _handler (struct config *conf, vector strvec) \ ++ovr_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ + { \ + if (!conf->overrides) \ + return 1; \ +- return function (strvec, &conf->overrides->option); \ ++ return function (strvec, &conf->overrides->option, file, line_nr); \ + } + + #define declare_ovr_snprint(option, function) \ +@@ -313,12 +316,13 @@ snprint_ovr_ ## option (struct config *conf, char * buff, int len, \ + + #define declare_mp_handler(option, function) \ + static int \ +-mp_ ## option ## _handler (struct config *conf, vector strvec) \ ++mp_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ + { \ + struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ + if (!mpe) \ + return 1; \ +- return function (strvec, &mpe->option); \ ++ return function (strvec, &mpe->option, file, line_nr); \ + } + + #define declare_mp_snprint(option, function) \ +@@ -330,9 +334,10 @@ snprint_mp_ ## option (struct config *conf, char * buff, int len, \ + return function (buff, len, mpe->option); \ + } + +-static int checkint_handler(struct config *conf, vector strvec) ++static int checkint_handler(struct config *conf, vector strvec, ++ const char *file, int line_nr) + { +- int rc = set_uint(strvec, &conf->checkint); ++ int rc = set_uint(strvec, &conf->checkint, file, line_nr); + + if (rc) + return rc; +@@ -355,9 +360,10 @@ declare_def_snprint(reassign_maps, print_yes_no) + declare_def_handler(multipath_dir, set_str) + declare_def_snprint(multipath_dir, print_str) + +-static int def_partition_delim_handler(struct config *conf, vector strvec) ++static int def_partition_delim_handler(struct config *conf, vector strvec, ++ const char *file, int line_nr) + { +- int rc = set_str(strvec, &conf->partition_delim); ++ int rc = set_str(strvec, &conf->partition_delim, file, line_nr); + + if (rc != 0) + return rc; +@@ -387,13 +393,13 @@ static const char * const find_multipaths_optvals[] = { + }; + + static int +-def_find_multipaths_handler(struct config *conf, vector strvec) ++def_find_multipaths_handler(struct config *conf, vector strvec, ++ const char *file, int line_nr) + { + char *buff; + int i; + +- if (set_yes_no_undef(strvec, &conf->find_multipaths) == 0 && +- conf->find_multipaths != FIND_MULTIPATHS_UNDEF) ++ if (set_yes_no_undef(strvec, &conf->find_multipaths, file, line_nr) == 0 && conf->find_multipaths != FIND_MULTIPATHS_UNDEF) + return 0; + + buff = set_value(strvec); +@@ -451,7 +457,8 @@ static int snprint_uid_attrs(struct config *conf, char *buff, int len, + return p - buff; + } + +-static int uid_attrs_handler(struct config *conf, vector strvec) ++static int uid_attrs_handler(struct config *conf, vector strvec, ++ const char *file, int line_nr) + { + char *val; + +@@ -644,7 +651,8 @@ declare_hw_handler(skip_kpartx, set_yes_no_undef) + declare_hw_snprint(skip_kpartx, print_yes_no_undef) + declare_mp_handler(skip_kpartx, set_yes_no_undef) + declare_mp_snprint(skip_kpartx, print_yes_no_undef) +-static int def_disable_changed_wwids_handler(struct config *conf, vector strvec) ++static int def_disable_changed_wwids_handler(struct config *conf, vector strvec, ++ const char *file, int line_nr) + { + return 0; + } +@@ -675,20 +683,23 @@ declare_def_snprint_defstr(enable_foreign, print_str, + DEFAULT_ENABLE_FOREIGN) + + static int +-def_config_dir_handler(struct config *conf, vector strvec) ++def_config_dir_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + /* this is only valid in the main config file */ + if (conf->processed_main_config) + return 0; +- return set_str(strvec, &conf->config_dir); ++ return set_str(strvec, &conf->config_dir, file, line_nr); + } + declare_def_snprint(config_dir, print_str) + + #define declare_def_attr_handler(option, function) \ + static int \ +-def_ ## option ## _handler (struct config *conf, vector strvec) \ ++def_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ + { \ +- return function (strvec, &conf->option, &conf->attribute_flags);\ ++ return function (strvec, &conf->option, &conf->attribute_flags, \ ++ file, line_nr); \ + } + + #define declare_def_attr_snprint(option, function) \ +@@ -702,12 +713,14 @@ snprint_def_ ## option (struct config *conf, char * buff, int len, \ + + #define declare_mp_attr_handler(option, function) \ + static int \ +-mp_ ## option ## _handler (struct config *conf, vector strvec) \ ++mp_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ + { \ + struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ + if (!mpe) \ + return 1; \ +- return function (strvec, &mpe->option, &mpe->attribute_flags); \ ++ return function (strvec, &mpe->option, &mpe->attribute_flags, \ ++ file, line_nr); \ + } + + #define declare_mp_attr_snprint(option, function) \ +@@ -721,7 +734,7 @@ snprint_mp_ ## option (struct config *conf, char * buff, int len, \ + } + + static int +-set_mode(vector strvec, void *ptr, int *flags) ++set_mode(vector strvec, void *ptr, int *flags, const char *file, int line_nr) + { + mode_t mode; + mode_t *mode_ptr = (mode_t *)ptr; +@@ -742,7 +755,7 @@ set_mode(vector strvec, void *ptr, int *flags) + } + + static int +-set_uid(vector strvec, void *ptr, int *flags) ++set_uid(vector strvec, void *ptr, int *flags, const char *file, int line_nr) + { + uid_t uid; + uid_t *uid_ptr = (uid_t *)ptr; +@@ -767,7 +780,7 @@ set_uid(vector strvec, void *ptr, int *flags) + } + + static int +-set_gid(vector strvec, void *ptr, int *flags) ++set_gid(vector strvec, void *ptr, int *flags, const char *file, int line_nr) + { + gid_t gid; + gid_t *gid_ptr = (gid_t *)ptr; +@@ -834,7 +847,7 @@ declare_mp_attr_handler(gid, set_gid) + declare_mp_attr_snprint(gid, print_gid) + + static int +-set_undef_off_zero(vector strvec, void *ptr) ++set_undef_off_zero(vector strvec, void *ptr, const char *file, int line_nr) + { + char * buff; + int *int_ptr = (int *)ptr; +@@ -876,7 +889,7 @@ declare_hw_handler(fast_io_fail, set_undef_off_zero) + declare_hw_snprint(fast_io_fail, print_undef_off_zero) + + static int +-set_dev_loss(vector strvec, void *ptr) ++set_dev_loss(vector strvec, void *ptr, const char *file, int line_nr) + { + char * buff; + unsigned int *uint_ptr = (unsigned int *)ptr; +@@ -919,7 +932,7 @@ declare_hw_handler(eh_deadline, set_undef_off_zero) + declare_hw_snprint(eh_deadline, print_undef_off_zero) + + static int +-set_pgpolicy(vector strvec, void *ptr) ++set_pgpolicy(vector strvec, void *ptr, const char *file, int line_nr) + { + char * buff; + int *int_ptr = (int *)ptr; +@@ -985,7 +998,8 @@ get_sys_max_fds(int *max_fds) + + + static int +-max_fds_handler(struct config *conf, vector strvec) ++max_fds_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + char * buff; + int r = 0, max_fds; +@@ -1030,7 +1044,7 @@ snprint_max_fds (struct config *conf, char * buff, int len, const void * data) + } + + static int +-set_rr_weight(vector strvec, void *ptr) ++set_rr_weight(vector strvec, void *ptr, const char *file, int line_nr) + { + int *int_ptr = (int *)ptr; + char * buff; +@@ -1074,7 +1088,7 @@ declare_mp_handler(rr_weight, set_rr_weight) + declare_mp_snprint(rr_weight, print_rr_weight) + + static int +-set_pgfailback(vector strvec, void *ptr) ++set_pgfailback(vector strvec, void *ptr, const char *file, int line_nr) + { + int *int_ptr = (int *)ptr; + char * buff; +@@ -1124,7 +1138,7 @@ declare_mp_handler(pgfailback, set_pgfailback) + declare_mp_snprint(pgfailback, print_pgfailback) + + static int +-no_path_retry_helper(vector strvec, void *ptr) ++no_path_retry_helper(vector strvec, void *ptr, const char *file, int line_nr) + { + int *int_ptr = (int *)ptr; + char * buff; +@@ -1169,7 +1183,8 @@ declare_mp_handler(no_path_retry, no_path_retry_helper) + declare_mp_snprint(no_path_retry, print_no_path_retry) + + static int +-def_log_checker_err_handler(struct config *conf, vector strvec) ++def_log_checker_err_handler(struct config *conf, vector strvec, ++ const char *file, int line_nr) + { + char * buff; + +@@ -1243,7 +1258,8 @@ print_reservation_key(char * buff, int len, struct be64 key, uint8_t flags, + } + + static int +-def_reservation_key_handler(struct config *conf, vector strvec) ++def_reservation_key_handler(struct config *conf, vector strvec, ++ const char *file, int line_nr) + { + return set_reservation_key(strvec, &conf->reservation_key, + &conf->sa_flags, +@@ -1260,7 +1276,8 @@ snprint_def_reservation_key (struct config *conf, char * buff, int len, + } + + static int +-mp_reservation_key_handler(struct config *conf, vector strvec) ++mp_reservation_key_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); + if (!mpe) +@@ -1281,7 +1298,7 @@ snprint_mp_reservation_key (struct config *conf, char * buff, int len, + } + + static int +-set_off_int_undef(vector strvec, void *ptr) ++set_off_int_undef(vector strvec, void *ptr, const char *file, int line_nr) + { + int *int_ptr = (int *)ptr; + char * buff; +@@ -1422,7 +1439,8 @@ declare_hw_snprint(recheck_wwid, print_yes_no_undef) + + + static int +-def_uxsock_timeout_handler(struct config *conf, vector strvec) ++def_uxsock_timeout_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + unsigned int uxsock_timeout; + char *buff; +@@ -1442,7 +1460,8 @@ def_uxsock_timeout_handler(struct config *conf, vector strvec) + } + + static int +-hw_vpd_vendor_handler(struct config *conf, vector strvec) ++hw_vpd_vendor_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + int i; + char *buff; +@@ -1482,7 +1501,8 @@ snprint_hw_vpd_vendor(struct config *conf, char * buff, int len, + * blacklist block handlers + */ + static int +-blacklist_handler(struct config *conf, vector strvec) ++blacklist_handler(struct config *conf, vector strvec, const char*file, ++ int line_nr) + { + if (!conf->blist_devnode) + conf->blist_devnode = vector_alloc(); +@@ -1504,7 +1524,8 @@ blacklist_handler(struct config *conf, vector strvec) + } + + static int +-blacklist_exceptions_handler(struct config *conf, vector strvec) ++blacklist_exceptions_handler(struct config *conf, vector strvec, ++ const char *file, int line_nr) + { + if (!conf->elist_devnode) + conf->elist_devnode = vector_alloc(); +@@ -1527,7 +1548,8 @@ blacklist_exceptions_handler(struct config *conf, vector strvec) + + #define declare_ble_handler(option) \ + static int \ +-ble_ ## option ## _handler (struct config *conf, vector strvec) \ ++ble_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ + { \ + char * buff; \ + \ +@@ -1543,7 +1565,8 @@ ble_ ## option ## _handler (struct config *conf, vector strvec) \ + + #define declare_ble_device_handler(name, option, vend, prod) \ + static int \ +-ble_ ## option ## _ ## name ## _handler (struct config *conf, vector strvec) \ ++ble_ ## option ## _ ## name ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ + { \ + char * buff; \ + \ +@@ -1583,13 +1606,15 @@ snprint_ble_simple (struct config *conf, char * buff, int len, + } + + static int +-ble_device_handler(struct config *conf, vector strvec) ++ble_device_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + return alloc_ble_device(conf->blist_device); + } + + static int +-ble_except_device_handler(struct config *conf, vector strvec) ++ble_except_device_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + return alloc_ble_device(conf->elist_device); + } +@@ -1623,7 +1648,8 @@ snprint_bled_product (struct config *conf, char * buff, int len, + * devices block handlers + */ + static int +-devices_handler(struct config *conf, vector strvec) ++devices_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + if (!conf->hwtable) + conf->hwtable = vector_alloc(); +@@ -1635,7 +1661,8 @@ devices_handler(struct config *conf, vector strvec) + } + + static int +-device_handler(struct config *conf, vector strvec) ++device_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + struct hwentry * hwe; + +@@ -1672,7 +1699,8 @@ declare_hw_snprint(hwhandler, print_str) + * overrides handlers + */ + static int +-overrides_handler(struct config *conf, vector strvec) ++overrides_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + if (!conf->overrides) + conf->overrides = alloc_hwe(); +@@ -1689,7 +1717,8 @@ overrides_handler(struct config *conf, vector strvec) + * multipaths block handlers + */ + static int +-multipaths_handler(struct config *conf, vector strvec) ++multipaths_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + if (!conf->mptable) + conf->mptable = vector_alloc(); +@@ -1701,7 +1730,8 @@ multipaths_handler(struct config *conf, vector strvec) + } + + static int +-multipath_handler(struct config *conf, vector strvec) ++multipath_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + struct mpentry * mpe; + +@@ -1730,7 +1760,8 @@ declare_mp_snprint(alias, print_str) + */ + + static int +-deprecated_handler(struct config *conf, vector strvec) ++deprecated_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) + { + char * buff; + +diff --git a/libmultipath/parser.c b/libmultipath/parser.c +index 341f2b80..29e8cee0 100644 +--- a/libmultipath/parser.c ++++ b/libmultipath/parser.c +@@ -562,7 +562,8 @@ process_stream(struct config *conf, FILE *stream, vector keywords, + goto out; + } + if (keyword->handler) { +- t = keyword->handler(conf, strvec); ++ t = keyword->handler(conf, strvec, file, ++ line_nr); + r += t; + if (t) + condlog(1, "%s line %d, parsing failed: %s", +diff --git a/libmultipath/parser.h b/libmultipath/parser.h +index e8d89607..8b424b7a 100644 +--- a/libmultipath/parser.h ++++ b/libmultipath/parser.h +@@ -42,7 +42,7 @@ + + /* keyword definition */ + typedef int print_fn(struct config *, char *, int, const void *); +-typedef int handler_fn(struct config *, vector); ++typedef int handler_fn(struct config *, vector, const char *file, int line_nr); + + struct keyword { + char *string; diff --git a/SOURCES/0082-libmultipath-make-set_int-take-a-range-for-valid-val.patch b/SOURCES/0082-libmultipath-make-set_int-take-a-range-for-valid-val.patch new file mode 100644 index 0000000..712bceb --- /dev/null +++ b/SOURCES/0082-libmultipath-make-set_int-take-a-range-for-valid-val.patch @@ -0,0 +1,250 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 28 Sep 2021 15:59:19 -0500 +Subject: [PATCH] libmultipath: make set_int take a range for valid values + +If a value outside of the valid range is passed to set_int, it caps the +value at appropriate limit, and issues a warning. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/dict.c | 121 +++++++++++++++++++++++++++----------------- + 1 file changed, 75 insertions(+), 46 deletions(-) + +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index a8872da7..686f4d5c 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -28,7 +28,8 @@ + #include "dict.h" + + static int +-set_int(vector strvec, void *ptr, const char *file, int line_nr) ++set_int(vector strvec, void *ptr, int min, int max, const char *file, ++ int line_nr) + { + int *int_ptr = (int *)ptr; + char *buff, *eptr; +@@ -43,11 +44,17 @@ set_int(vector strvec, void *ptr, const char *file, int line_nr) + if (eptr > buff) + while (isspace(*eptr)) + eptr++; +- if (*buff == '\0' || *eptr != '\0' || res > INT_MAX || res < INT_MIN) { +- condlog(1, "%s: invalid value for %s: \"%s\"", +- __func__, (char*)VECTOR_SLOT(strvec, 0), buff); ++ if (*buff == '\0' || *eptr != '\0') { ++ condlog(1, "%s line %d, invalid value for %s: \"%s\"", ++ file, line_nr, (char*)VECTOR_SLOT(strvec, 0), buff); + rc = 1; + } else { ++ if (res > max || res < min) { ++ res = (res > max) ? max : min; ++ condlog(1, "%s line %d, value for %s too %s, capping at %ld", ++ file, line_nr, (char*)VECTOR_SLOT(strvec, 0), ++ (res == max)? "large" : "small", res); ++ } + rc = 0; + *int_ptr = res; + } +@@ -76,8 +83,8 @@ set_uint(vector strvec, void *ptr, const char *file, int line_nr) + while (isspace(*eptr)) + eptr++; + 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); ++ condlog(1, "%s line %d, invalid value for %s: \"%s\"", ++ file, line_nr, (char*)VECTOR_SLOT(strvec, 0), buff); + rc = 1; + } else { + rc = 0; +@@ -246,6 +253,14 @@ def_ ## option ## _handler (struct config *conf, vector strvec, \ + return function (strvec, &conf->option, file, line_nr); \ + } + ++#define declare_def_range_handler(option, minval, maxval) \ ++static int \ ++def_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ ++{ \ ++ return set_int(strvec, &conf->option, minval, maxval, file, line_nr); \ ++} ++ + #define declare_def_snprint(option, function) \ + static int \ + snprint_def_ ## option (struct config *conf, char * buff, int len, \ +@@ -287,6 +302,18 @@ hw_ ## option ## _handler (struct config *conf, vector strvec, \ + return function (strvec, &hwe->option, file, line_nr); \ + } + ++#define declare_hw_range_handler(option, minval, maxval) \ ++static int \ ++hw_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ ++{ \ ++ struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); \ ++ if (!hwe) \ ++ return 1; \ ++ return set_int(strvec, &hwe->option, minval, maxval, file, line_nr); \ ++} ++ ++ + #define declare_hw_snprint(option, function) \ + static int \ + snprint_hw_ ## option (struct config *conf, char * buff, int len, \ +@@ -306,6 +333,17 @@ ovr_ ## option ## _handler (struct config *conf, vector strvec, \ + return function (strvec, &conf->overrides->option, file, line_nr); \ + } + ++#define declare_ovr_range_handler(option, minval, maxval) \ ++static int \ ++ovr_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ ++{ \ ++ if (!conf->overrides) \ ++ return 1; \ ++ return set_int(strvec, &conf->overrides->option, minval, maxval, \ ++ file, line_nr); \ ++} ++ + #define declare_ovr_snprint(option, function) \ + static int \ + snprint_ovr_ ## option (struct config *conf, char * buff, int len, \ +@@ -325,6 +363,17 @@ mp_ ## option ## _handler (struct config *conf, vector strvec, \ + return function (strvec, &mpe->option, file, line_nr); \ + } + ++#define declare_mp_range_handler(option, minval, maxval) \ ++static int \ ++mp_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ ++{ \ ++ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ ++ if (!mpe) \ ++ return 1; \ ++ return set_int(strvec, &mpe->option, minval, maxval, file, line_nr); \ ++} ++ + #define declare_mp_snprint(option, function) \ + static int \ + snprint_mp_ ## option (struct config *conf, char * buff, int len, \ +@@ -351,7 +400,7 @@ declare_def_snprint(checkint, print_int) + declare_def_handler(max_checkint, set_uint) + declare_def_snprint(max_checkint, print_int) + +-declare_def_handler(verbosity, set_int) ++declare_def_range_handler(verbosity, 0, MAX_VERBOSITY) + declare_def_snprint(verbosity, print_int) + + declare_def_handler(reassign_maps, set_yes_no) +@@ -528,22 +577,22 @@ declare_ovr_snprint(checker_name, print_str) + declare_hw_handler(checker_name, set_str) + declare_hw_snprint(checker_name, print_str) + +-declare_def_handler(minio, set_int) ++declare_def_range_handler(minio, 0, INT_MAX) + declare_def_snprint_defint(minio, print_int, DEFAULT_MINIO) +-declare_ovr_handler(minio, set_int) ++declare_ovr_range_handler(minio, 0, INT_MAX) + declare_ovr_snprint(minio, print_nonzero) +-declare_hw_handler(minio, set_int) ++declare_hw_range_handler(minio, 0, INT_MAX) + declare_hw_snprint(minio, print_nonzero) +-declare_mp_handler(minio, set_int) ++declare_mp_range_handler(minio, 0, INT_MAX) + declare_mp_snprint(minio, print_nonzero) + +-declare_def_handler(minio_rq, set_int) ++declare_def_range_handler(minio_rq, 0, INT_MAX) + declare_def_snprint_defint(minio_rq, print_int, DEFAULT_MINIO_RQ) +-declare_ovr_handler(minio_rq, set_int) ++declare_ovr_range_handler(minio_rq, 0, INT_MAX) + declare_ovr_snprint(minio_rq, print_nonzero) +-declare_hw_handler(minio_rq, set_int) ++declare_hw_range_handler(minio_rq, 0, INT_MAX) + declare_hw_snprint(minio_rq, print_nonzero) +-declare_mp_handler(minio_rq, set_int) ++declare_mp_range_handler(minio_rq, 0, INT_MAX) + declare_mp_snprint(minio_rq, print_nonzero) + + declare_def_handler(queue_without_daemon, set_yes_no) +@@ -562,7 +611,7 @@ snprint_def_queue_without_daemon (struct config *conf, + return 0; + } + +-declare_def_handler(checker_timeout, set_int) ++declare_def_range_handler(checker_timeout, 0, INT_MAX) + declare_def_snprint(checker_timeout, print_nonzero) + + declare_def_handler(flush_on_last_del, set_yes_no_undef) +@@ -630,13 +679,13 @@ declare_hw_snprint(deferred_remove, print_yes_no_undef) + declare_mp_handler(deferred_remove, set_yes_no_undef) + declare_mp_snprint(deferred_remove, print_yes_no_undef) + +-declare_def_handler(retrigger_tries, set_int) ++declare_def_range_handler(retrigger_tries, 0, INT_MAX) + declare_def_snprint(retrigger_tries, print_int) + +-declare_def_handler(retrigger_delay, set_int) ++declare_def_range_handler(retrigger_delay, 0, INT_MAX) + declare_def_snprint(retrigger_delay, print_int) + +-declare_def_handler(uev_wait_timeout, set_int) ++declare_def_range_handler(uev_wait_timeout, 0, INT_MAX) + declare_def_snprint(uev_wait_timeout, print_int) + + declare_def_handler(strict_timing, set_yes_no) +@@ -662,19 +711,19 @@ static int snprint_def_disable_changed_wwids(struct config *conf, char *buff, + return print_ignored(buff, len); + } + +-declare_def_handler(remove_retries, set_int) ++declare_def_range_handler(remove_retries, 0, INT_MAX) + declare_def_snprint(remove_retries, print_int) + +-declare_def_handler(max_sectors_kb, set_int) ++declare_def_range_handler(max_sectors_kb, 0, INT_MAX) + declare_def_snprint(max_sectors_kb, print_nonzero) +-declare_ovr_handler(max_sectors_kb, set_int) ++declare_ovr_range_handler(max_sectors_kb, 0, INT_MAX) + declare_ovr_snprint(max_sectors_kb, print_nonzero) +-declare_hw_handler(max_sectors_kb, set_int) ++declare_hw_range_handler(max_sectors_kb, 0, INT_MAX) + declare_hw_snprint(max_sectors_kb, print_nonzero) +-declare_mp_handler(max_sectors_kb, set_int) ++declare_mp_range_handler(max_sectors_kb, 0, INT_MAX) + declare_mp_snprint(max_sectors_kb, print_nonzero) + +-declare_def_handler(find_multipaths_timeout, set_int) ++declare_def_range_handler(find_multipaths_timeout, INT_MIN, INT_MAX) + declare_def_snprint_defint(find_multipaths_timeout, print_int, + DEFAULT_FIND_MULTIPATHS_TIMEOUT) + +@@ -1437,27 +1486,7 @@ declare_ovr_snprint(recheck_wwid, print_yes_no_undef) + declare_hw_handler(recheck_wwid, set_yes_no_undef) + declare_hw_snprint(recheck_wwid, print_yes_no_undef) + +- +-static int +-def_uxsock_timeout_handler(struct config *conf, vector strvec, const char *file, +- int line_nr) +-{ +- unsigned int uxsock_timeout; +- char *buff; +- +- buff = set_value(strvec); +- if (!buff) +- return 1; +- +- if (sscanf(buff, "%u", &uxsock_timeout) == 1 && +- uxsock_timeout > DEFAULT_REPLY_TIMEOUT) +- conf->uxsock_timeout = uxsock_timeout; +- else +- conf->uxsock_timeout = DEFAULT_REPLY_TIMEOUT; +- +- free(buff); +- return 0; +-} ++declare_def_range_handler(uxsock_timeout, DEFAULT_REPLY_TIMEOUT, INT_MAX) + + static int + hw_vpd_vendor_handler(struct config *conf, vector strvec, const char *file, diff --git a/SOURCES/0083-libmultipath-improve-checks-for-set_str.patch b/SOURCES/0083-libmultipath-improve-checks-for-set_str.patch new file mode 100644 index 0000000..f48bda6 --- /dev/null +++ b/SOURCES/0083-libmultipath-improve-checks-for-set_str.patch @@ -0,0 +1,177 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 29 Sep 2021 12:56:04 -0500 +Subject: [PATCH] libmultipath: improve checks for set_str + +multipath always requires absolute pathnames, so make sure all file and +directory names start with a slash. Also check that the directories +exist. Finally, some strings, like the alias, will be used in paths. +These must not contain the slash character '/', since it is a forbidden +character in file/directory names. This patch adds seperate handlers for +these three cases. If a config line is invalid, these handlers retain +the existing config string, if any. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/dict.c | 89 +++++++++++++++++++++++++++++++++++++++------ + 1 file changed, 78 insertions(+), 11 deletions(-) + +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index 686f4d5c..d547d898 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -5,6 +5,8 @@ + * Copyright (c) 2005 Kiyoshi Ueda, NEC + */ + #include ++#include ++#include + #include + #include + #include "checkers.h" +@@ -121,7 +123,72 @@ set_regex(vector strvec, void *ptr, const char *file, int line_nr) + + if (!*str_ptr) + return 1; ++ return 0; ++} + ++static int ++set_dir(vector strvec, void *ptr, const char *file, int line_nr) ++{ ++ char **str_ptr = (char **)ptr; ++ char *old_str = *str_ptr; ++ struct stat sb; ++ ++ *str_ptr = set_value(strvec); ++ if (!*str_ptr) { ++ free(old_str); ++ return 1; ++ } ++ if ((*str_ptr)[0] != '/'){ ++ condlog(1, "%s line %d, %s is not an absolute directory path. Ignoring", file, line_nr, *str_ptr); ++ *str_ptr = old_str; ++ } else { ++ if (stat(*str_ptr, &sb) == 0 && S_ISDIR(sb.st_mode)) ++ free(old_str); ++ else { ++ condlog(1, "%s line %d, %s is not an existing directory. Ignoring", file, line_nr, *str_ptr); ++ *str_ptr = old_str; ++ } ++ } ++ return 0; ++} ++ ++static int ++set_path(vector strvec, void *ptr, const char *file, int line_nr) ++{ ++ char **str_ptr = (char **)ptr; ++ char *old_str = *str_ptr; ++ ++ *str_ptr = set_value(strvec); ++ if (!*str_ptr) { ++ free(old_str); ++ return 1; ++ } ++ if ((*str_ptr)[0] != '/'){ ++ condlog(1, "%s line %d, %s is not an absolute path. Ignoring", ++ file, line_nr, *str_ptr); ++ *str_ptr = old_str; ++ } else ++ free(old_str); ++ return 0; ++} ++ ++static int ++set_str_noslash(vector strvec, void *ptr, const char *file, int line_nr) ++{ ++ char **str_ptr = (char **)ptr; ++ char *old_str = *str_ptr; ++ ++ *str_ptr = set_value(strvec); ++ if (!*str_ptr) { ++ free(old_str); ++ return 1; ++ } ++ if (strchr(*str_ptr, '/')) { ++ condlog(1, "%s line %d, %s cannot contain a slash. Ignoring", ++ file, line_nr, *str_ptr); ++ *str_ptr = old_str; ++ } else ++ free(old_str); + return 0; + } + +@@ -400,19 +467,19 @@ declare_def_snprint(checkint, print_int) + declare_def_handler(max_checkint, set_uint) + declare_def_snprint(max_checkint, print_int) + +-declare_def_range_handler(verbosity, 0, MAX_VERBOSITY) ++declare_def_range_handler(verbosity, 0, 4) + declare_def_snprint(verbosity, print_int) + + declare_def_handler(reassign_maps, set_yes_no) + declare_def_snprint(reassign_maps, print_yes_no) + +-declare_def_handler(multipath_dir, set_str) ++declare_def_handler(multipath_dir, set_dir) + declare_def_snprint(multipath_dir, print_str) + + static int def_partition_delim_handler(struct config *conf, vector strvec, + const char *file, int line_nr) + { +- int rc = set_str(strvec, &conf->partition_delim, file, line_nr); ++ int rc = set_str_noslash(strvec, &conf->partition_delim, file, line_nr); + + if (rc != 0) + return rc; +@@ -545,11 +612,11 @@ declare_hw_snprint(prio_name, print_str) + declare_mp_handler(prio_name, set_str) + declare_mp_snprint(prio_name, print_str) + +-declare_def_handler(alias_prefix, set_str) ++declare_def_handler(alias_prefix, set_str_noslash) + declare_def_snprint_defstr(alias_prefix, print_str, DEFAULT_ALIAS_PREFIX) +-declare_ovr_handler(alias_prefix, set_str) ++declare_ovr_handler(alias_prefix, set_str_noslash) + declare_ovr_snprint(alias_prefix, print_str) +-declare_hw_handler(alias_prefix, set_str) ++declare_hw_handler(alias_prefix, set_str_noslash) + declare_hw_snprint(alias_prefix, print_str) + + declare_def_handler(prio_args, set_str) +@@ -633,13 +700,13 @@ declare_hw_snprint(user_friendly_names, print_yes_no_undef) + declare_mp_handler(user_friendly_names, set_yes_no_undef) + declare_mp_snprint(user_friendly_names, print_yes_no_undef) + +-declare_def_handler(bindings_file, set_str) ++declare_def_handler(bindings_file, set_path) + declare_def_snprint(bindings_file, print_str) + +-declare_def_handler(wwids_file, set_str) ++declare_def_handler(wwids_file, set_path) + declare_def_snprint(wwids_file, print_str) + +-declare_def_handler(prkeys_file, set_str) ++declare_def_handler(prkeys_file, set_path) + declare_def_snprint(prkeys_file, print_str) + + declare_def_handler(retain_hwhandler, set_yes_no_undef) +@@ -738,7 +805,7 @@ def_config_dir_handler(struct config *conf, vector strvec, const char *file, + /* this is only valid in the main config file */ + if (conf->processed_main_config) + return 0; +- return set_str(strvec, &conf->config_dir, file, line_nr); ++ return set_path(strvec, &conf->config_dir, file, line_nr); + } + declare_def_snprint(config_dir, print_str) + +@@ -1781,7 +1848,7 @@ multipath_handler(struct config *conf, vector strvec, const char *file, + declare_mp_handler(wwid, set_str) + declare_mp_snprint(wwid, print_str) + +-declare_mp_handler(alias, set_str) ++declare_mp_handler(alias, set_str_noslash) + declare_mp_snprint(alias, print_str) + + /* diff --git a/SOURCES/0084-libmultipath-split-set_int-to-enable-reuse.patch b/SOURCES/0084-libmultipath-split-set_int-to-enable-reuse.patch new file mode 100644 index 0000000..92fdfec --- /dev/null +++ b/SOURCES/0084-libmultipath-split-set_int-to-enable-reuse.patch @@ -0,0 +1,191 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 4 Oct 2021 15:27:36 -0500 +Subject: [PATCH] libmultipath: split set_int to enable reuse + +Split the code that does the actual value parsing out of set_int(), into +a helper function, do_set_int(), so that it can be used by other +handlers. These functions no longer set the config value at all, when +they have invalid input. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/dict.c | 82 +++++++++++++++++++++++++-------------------- + 1 file changed, 46 insertions(+), 36 deletions(-) + +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index d547d898..6330836a 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -30,17 +30,12 @@ + #include "dict.h" + + static int +-set_int(vector strvec, void *ptr, int min, int max, const char *file, +- int line_nr) ++do_set_int(vector strvec, void *ptr, int min, int max, const char *file, ++ int line_nr, char *buff) + { + int *int_ptr = (int *)ptr; +- char *buff, *eptr; ++ char *eptr; + long res; +- int rc; +- +- buff = set_value(strvec); +- if (!buff) +- return 1; + + res = strtol(buff, &eptr, 10); + if (eptr > buff) +@@ -49,17 +44,30 @@ set_int(vector strvec, void *ptr, int min, int max, const char *file, + if (*buff == '\0' || *eptr != '\0') { + condlog(1, "%s line %d, invalid value for %s: \"%s\"", + file, line_nr, (char*)VECTOR_SLOT(strvec, 0), buff); +- rc = 1; +- } else { +- if (res > max || res < min) { +- res = (res > max) ? max : min; +- condlog(1, "%s line %d, value for %s too %s, capping at %ld", ++ return 1; ++ } ++ if (res > max || res < min) { ++ res = (res > max) ? max : min; ++ condlog(1, "%s line %d, value for %s too %s, capping at %ld", + file, line_nr, (char*)VECTOR_SLOT(strvec, 0), +- (res == max)? "large" : "small", res); +- } +- rc = 0; +- *int_ptr = res; ++ (res == max)? "large" : "small", res); + } ++ *int_ptr = res; ++ return 0; ++} ++ ++static int ++set_int(vector strvec, void *ptr, int min, int max, const char *file, ++ int line_nr) ++{ ++ char *buff; ++ int rc; ++ ++ buff = set_value(strvec); ++ if (!buff) ++ return 1; ++ ++ rc = do_set_int(strvec, ptr, min, max, file, line_nr, buff); + + FREE(buff); + return rc; +@@ -965,6 +973,7 @@ declare_mp_attr_snprint(gid, print_gid) + static int + set_undef_off_zero(vector strvec, void *ptr, const char *file, int line_nr) + { ++ int rc = 0; + char * buff; + int *int_ptr = (int *)ptr; + +@@ -974,10 +983,10 @@ set_undef_off_zero(vector strvec, void *ptr, const char *file, int line_nr) + + if (strcmp(buff, "off") == 0) + *int_ptr = UOZ_OFF; +- else if (sscanf(buff, "%d", int_ptr) != 1 || +- *int_ptr < UOZ_ZERO) +- *int_ptr = UOZ_UNDEF; +- else if (*int_ptr == 0) ++ else ++ rc = do_set_int(strvec, int_ptr, 0, INT_MAX, file, line_nr, ++ buff); ++ if (rc == 0 && *int_ptr == 0) + *int_ptr = UOZ_ZERO; + + FREE(buff); +@@ -1130,14 +1139,12 @@ max_fds_handler(struct config *conf, vector strvec, const char *file, + /* Assume safe limit */ + max_fds = 4096; + } +- if (strlen(buff) == 3 && +- !strcmp(buff, "max")) +- conf->max_fds = max_fds; +- else +- conf->max_fds = atoi(buff); +- +- if (conf->max_fds > max_fds) ++ if (!strcmp(buff, "max")) { + conf->max_fds = max_fds; ++ r = 0; ++ } else ++ r = do_set_int(strvec, &conf->max_fds, 0, max_fds, file, ++ line_nr, buff); + + FREE(buff); + +@@ -1206,6 +1213,7 @@ declare_mp_snprint(rr_weight, print_rr_weight) + static int + set_pgfailback(vector strvec, void *ptr, const char *file, int line_nr) + { ++ int rc = 0; + int *int_ptr = (int *)ptr; + char * buff; + +@@ -1220,11 +1228,11 @@ set_pgfailback(vector strvec, void *ptr, const char *file, int line_nr) + else if (strlen(buff) == 10 && !strcmp(buff, "followover")) + *int_ptr = -FAILBACK_FOLLOWOVER; + else +- *int_ptr = atoi(buff); ++ rc = do_set_int(strvec, ptr, 0, INT_MAX, file, line_nr, buff); + + FREE(buff); + +- return 0; ++ return rc; + } + + int +@@ -1256,6 +1264,7 @@ declare_mp_snprint(pgfailback, print_pgfailback) + static int + no_path_retry_helper(vector strvec, void *ptr, const char *file, int line_nr) + { ++ int rc = 0; + int *int_ptr = (int *)ptr; + char * buff; + +@@ -1267,11 +1276,11 @@ no_path_retry_helper(vector strvec, void *ptr, const char *file, int line_nr) + *int_ptr = NO_PATH_RETRY_FAIL; + else if (!strcmp(buff, "queue")) + *int_ptr = NO_PATH_RETRY_QUEUE; +- else if ((*int_ptr = atoi(buff)) < 1) +- *int_ptr = NO_PATH_RETRY_UNDEF; ++ else ++ rc = do_set_int(strvec, ptr, 1, INT_MAX, file, line_nr, buff); + + FREE(buff); +- return 0; ++ return rc; + } + + int +@@ -1416,6 +1425,7 @@ snprint_mp_reservation_key (struct config *conf, char * buff, int len, + static int + set_off_int_undef(vector strvec, void *ptr, const char *file, int line_nr) + { ++ int rc =0; + int *int_ptr = (int *)ptr; + char * buff; + +@@ -1425,11 +1435,11 @@ set_off_int_undef(vector strvec, void *ptr, const char *file, int line_nr) + + if (!strcmp(buff, "no") || !strcmp(buff, "0")) + *int_ptr = NU_NO; +- else if ((*int_ptr = atoi(buff)) < 1) +- *int_ptr = NU_UNDEF; ++ else ++ rc = do_set_int(strvec, ptr, 1, INT_MAX, file, line_nr, buff); + + FREE(buff); +- return 0; ++ return rc; + } + + int diff --git a/SOURCES/0085-libmultipath-cleanup-invalid-config-handling.patch b/SOURCES/0085-libmultipath-cleanup-invalid-config-handling.patch new file mode 100644 index 0000000..7b0d2ff --- /dev/null +++ b/SOURCES/0085-libmultipath-cleanup-invalid-config-handling.patch @@ -0,0 +1,201 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 4 Oct 2021 16:52:55 -0500 +Subject: [PATCH] libmultipath: cleanup invalid config handling + +Add error reporting to the remaining config handlers. If the value is +invalid, do not change the existing config option's value. Also print +an error whenever 0 is returned for an invalid value. When the handler +returns 1, config processing already fails with an error message. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/dict.c | 73 +++++++++++++++++++++++++++++++-------------- + 1 file changed, 51 insertions(+), 22 deletions(-) + +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index 6330836a..b255322e 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -212,8 +212,11 @@ set_yes_no(vector strvec, void *ptr, const char *file, int line_nr) + + if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) + *int_ptr = YN_YES; +- else ++ else if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0) + *int_ptr = YN_NO; ++ else ++ condlog(1, "%s line %d, invalid value for %s: \"%s\"", ++ file, line_nr, (char*)VECTOR_SLOT(strvec, 0), buff); + + FREE(buff); + return 0; +@@ -234,7 +237,8 @@ set_yes_no_undef(vector strvec, void *ptr, const char *file, int line_nr) + else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) + *int_ptr = YNU_YES; + else +- *int_ptr = YNU_UNDEF; ++ condlog(1, "%s line %d, invalid value for %s: \"%s\"", ++ file, line_nr, (char*)VECTOR_SLOT(strvec, 0), buff); + + FREE(buff); + return 0; +@@ -523,9 +527,6 @@ def_find_multipaths_handler(struct config *conf, vector strvec, + char *buff; + int i; + +- if (set_yes_no_undef(strvec, &conf->find_multipaths, file, line_nr) == 0 && conf->find_multipaths != FIND_MULTIPATHS_UNDEF) +- return 0; +- + buff = set_value(strvec); + if (!buff) + return 1; +@@ -538,9 +539,14 @@ def_find_multipaths_handler(struct config *conf, vector strvec, + } + } + +- if (conf->find_multipaths == YNU_UNDEF) { +- condlog(0, "illegal value for find_multipaths: %s", buff); +- conf->find_multipaths = DEFAULT_FIND_MULTIPATHS; ++ if (i >= __FIND_MULTIPATHS_LAST) { ++ if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0) ++ conf->find_multipaths = FIND_MULTIPATHS_OFF; ++ else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) ++ conf->find_multipaths = FIND_MULTIPATHS_ON; ++ else ++ condlog(1, "%s line %d, invalid value for find_multipaths: \"%s\"", ++ file, line_nr, buff); + } + + FREE(buff); +@@ -591,8 +597,10 @@ static int uid_attrs_handler(struct config *conf, vector strvec, + if (!val) + return 1; + if (parse_uid_attrs(val, conf)) +- condlog(1, "error parsing uid_attrs: \"%s\"", val); +- condlog(3, "parsed %d uid_attrs", VECTOR_SIZE(&conf->uid_attrs)); ++ condlog(1, "%s line %d,error parsing uid_attrs: \"%s\"", file, ++ line_nr, val); ++ else ++ condlog(4, "parsed %d uid_attrs", VECTOR_SIZE(&conf->uid_attrs)); + FREE(val); + return 0; + } +@@ -811,8 +819,11 @@ def_config_dir_handler(struct config *conf, vector strvec, const char *file, + int line_nr) + { + /* this is only valid in the main config file */ +- if (conf->processed_main_config) ++ if (conf->processed_main_config) { ++ condlog(1, "%s line %d, config_dir option only valid in /etc/multipath.conf", ++ file, line_nr); + return 0; ++ } + return set_path(strvec, &conf->config_dir, file, line_nr); + } + declare_def_snprint(config_dir, print_str) +@@ -872,7 +883,9 @@ set_mode(vector strvec, void *ptr, int *flags, const char *file, int line_nr) + if (sscanf(buff, "%o", &mode) == 1 && mode <= 0777) { + *flags |= (1 << ATTR_MODE); + *mode_ptr = mode; +- } ++ } else ++ condlog(1, "%s line %d, invalid value for mode: \"%s\"", ++ file, line_nr, buff); + + FREE(buff); + return 0; +@@ -897,7 +910,9 @@ set_uid(vector strvec, void *ptr, int *flags, const char *file, int line_nr) + else if (sscanf(buff, "%u", &uid) == 1){ + *flags |= (1 << ATTR_UID); + *uid_ptr = uid; +- } ++ } else ++ condlog(1, "%s line %d, invalid value for uid: \"%s\"", ++ file, line_nr, buff); + + FREE(buff); + return 0; +@@ -923,7 +938,9 @@ set_gid(vector strvec, void *ptr, int *flags, const char *file, int line_nr) + else if (sscanf(buff, "%u", &gid) == 1){ + *flags |= (1 << ATTR_GID); + *gid_ptr = gid; +- } ++ } else ++ condlog(1, "%s line %d, invalid value for gid: \"%s\"", ++ file, line_nr, buff); + FREE(buff); + return 0; + } +@@ -1026,7 +1043,8 @@ set_dev_loss(vector strvec, void *ptr, const char *file, int line_nr) + if (!strcmp(buff, "infinity")) + *uint_ptr = MAX_DEV_LOSS_TMO; + else if (sscanf(buff, "%u", uint_ptr) != 1) +- *uint_ptr = 0; ++ condlog(1, "%s line %d, invalid value for dev_loss_tmo: \"%s\"", ++ file, line_nr, buff); + + FREE(buff); + return 0; +@@ -1060,13 +1078,19 @@ static int + set_pgpolicy(vector strvec, void *ptr, const char *file, int line_nr) + { + char * buff; ++ int policy; + int *int_ptr = (int *)ptr; + + buff = set_value(strvec); + if (!buff) + return 1; + +- *int_ptr = get_pgpolicy_id(buff); ++ policy = get_pgpolicy_id(buff); ++ if (policy != IOPOLICY_UNDEF) ++ *int_ptr = policy; ++ else ++ condlog(1, "%s line %d, invalid value for path_grouping_policy: \"%s\"", ++ file, line_nr, buff); + FREE(buff); + + return 0; +@@ -1179,10 +1203,11 @@ set_rr_weight(vector strvec, void *ptr, const char *file, int line_nr) + + if (!strcmp(buff, "priorities")) + *int_ptr = RR_WEIGHT_PRIO; +- +- if (!strcmp(buff, "uniform")) ++ else if (!strcmp(buff, "uniform")) + *int_ptr = RR_WEIGHT_NONE; +- ++ else ++ condlog(1, "%s line %d, invalid value for rr_weight: \"%s\"", ++ file, line_nr, buff); + FREE(buff); + + return 0; +@@ -1318,10 +1343,13 @@ def_log_checker_err_handler(struct config *conf, vector strvec, + if (!buff) + return 1; + +- if (strlen(buff) == 4 && !strcmp(buff, "once")) ++ if (!strcmp(buff, "once")) + conf->log_checker_err = LOG_CHKR_ERR_ONCE; +- else if (strlen(buff) == 6 && !strcmp(buff, "always")) ++ else if (!strcmp(buff, "always")) + conf->log_checker_err = LOG_CHKR_ERR_ALWAYS; ++ else ++ condlog(1, "%s line %d, invalid value for log_checker_err: \"%s\"", ++ file, line_nr, buff); + + free(buff); + return 0; +@@ -1585,7 +1613,8 @@ hw_vpd_vendor_handler(struct config *conf, vector strvec, const char *file, + goto out; + } + } +- hwe->vpd_vendor_id = 0; ++ condlog(1, "%s line %d, invalid value for vpd_vendor: \"%s\"", ++ file, line_nr, buff); + out: + FREE(buff); + return 0; diff --git a/SOURCES/0086-libmultipath-don-t-return-error-on-invalid-values.patch b/SOURCES/0086-libmultipath-don-t-return-error-on-invalid-values.patch new file mode 100644 index 0000000..952adb9 --- /dev/null +++ b/SOURCES/0086-libmultipath-don-t-return-error-on-invalid-values.patch @@ -0,0 +1,218 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 11 Nov 2021 17:37:05 -0600 +Subject: [PATCH] libmultipath: don't return error on invalid values + +do_set_int and set_uint return 1 for invalid values. This can cause +multipath to fail completely, while reading the config. The config +handlers should only return a non-zero value if there is an internal +error, not if there is just an invalid value. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/dict.c | 64 ++++++++++++++++++--------------------------- + 1 file changed, 25 insertions(+), 39 deletions(-) + +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index b255322e..5a0255b0 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -29,7 +29,7 @@ + #include "mpath_cmd.h" + #include "dict.h" + +-static int ++static void + do_set_int(vector strvec, void *ptr, int min, int max, const char *file, + int line_nr, char *buff) + { +@@ -44,7 +44,7 @@ do_set_int(vector strvec, void *ptr, int min, int max, const char *file, + if (*buff == '\0' || *eptr != '\0') { + condlog(1, "%s line %d, invalid value for %s: \"%s\"", + file, line_nr, (char*)VECTOR_SLOT(strvec, 0), buff); +- return 1; ++ return; + } + if (res > max || res < min) { + res = (res > max) ? max : min; +@@ -53,7 +53,7 @@ do_set_int(vector strvec, void *ptr, int min, int max, const char *file, + (res == max)? "large" : "small", res); + } + *int_ptr = res; +- return 0; ++ return; + } + + static int +@@ -61,16 +61,15 @@ set_int(vector strvec, void *ptr, int min, int max, const char *file, + int line_nr) + { + char *buff; +- int rc; + + buff = set_value(strvec); + if (!buff) + return 1; + +- rc = do_set_int(strvec, ptr, min, max, file, line_nr, buff); ++ do_set_int(strvec, ptr, min, max, file, line_nr, buff); + + FREE(buff); +- return rc; ++ return 0; + } + + static int +@@ -79,7 +78,6 @@ set_uint(vector strvec, void *ptr, const char *file, int line_nr) + unsigned int *uint_ptr = (unsigned int *)ptr; + char *buff, *eptr, *p; + unsigned long res; +- int rc; + + buff = set_value(strvec); + if (!buff) +@@ -92,17 +90,14 @@ set_uint(vector strvec, void *ptr, const char *file, int line_nr) + if (eptr > buff) + while (isspace(*eptr)) + eptr++; +- if (*buff == '\0' || *eptr != '\0' || !isdigit(*p) || res > UINT_MAX) { ++ if (*buff == '\0' || *eptr != '\0' || !isdigit(*p) || res > UINT_MAX) + condlog(1, "%s line %d, invalid value for %s: \"%s\"", + file, line_nr, (char*)VECTOR_SLOT(strvec, 0), buff); +- rc = 1; +- } else { +- rc = 0; ++ else + *uint_ptr = res; +- } + + FREE(buff); +- return rc; ++ return 0; + } + + static int +@@ -990,7 +985,6 @@ declare_mp_attr_snprint(gid, print_gid) + static int + set_undef_off_zero(vector strvec, void *ptr, const char *file, int line_nr) + { +- int rc = 0; + char * buff; + int *int_ptr = (int *)ptr; + +@@ -1000,11 +994,10 @@ set_undef_off_zero(vector strvec, void *ptr, const char *file, int line_nr) + + if (strcmp(buff, "off") == 0) + *int_ptr = UOZ_OFF; +- else +- rc = do_set_int(strvec, int_ptr, 0, INT_MAX, file, line_nr, +- buff); +- if (rc == 0 && *int_ptr == 0) ++ else if (strcmp(buff, "0") == 0) + *int_ptr = UOZ_ZERO; ++ else ++ do_set_int(strvec, int_ptr, 1, INT_MAX, file, line_nr, buff); + + FREE(buff); + return 0; +@@ -1151,28 +1144,24 @@ max_fds_handler(struct config *conf, vector strvec, const char *file, + int line_nr) + { + char * buff; +- int r = 0, max_fds; ++ int max_fds; + + buff = set_value(strvec); + + if (!buff) + return 1; + +- r = get_sys_max_fds(&max_fds); +- if (r) { +- /* Assume safe limit */ +- max_fds = 4096; +- } +- if (!strcmp(buff, "max")) { ++ if (get_sys_max_fds(&max_fds) != 0) ++ max_fds = 4096; /* Assume safe limit */ ++ if (!strcmp(buff, "max")) + conf->max_fds = max_fds; +- r = 0; +- } else +- r = do_set_int(strvec, &conf->max_fds, 0, max_fds, file, +- line_nr, buff); ++ else ++ do_set_int(strvec, &conf->max_fds, 0, max_fds, file, line_nr, ++ buff); + + FREE(buff); + +- return r; ++ return 0; + } + + static int +@@ -1238,7 +1227,6 @@ declare_mp_snprint(rr_weight, print_rr_weight) + static int + set_pgfailback(vector strvec, void *ptr, const char *file, int line_nr) + { +- int rc = 0; + int *int_ptr = (int *)ptr; + char * buff; + +@@ -1253,11 +1241,11 @@ set_pgfailback(vector strvec, void *ptr, const char *file, int line_nr) + else if (strlen(buff) == 10 && !strcmp(buff, "followover")) + *int_ptr = -FAILBACK_FOLLOWOVER; + else +- rc = do_set_int(strvec, ptr, 0, INT_MAX, file, line_nr, buff); ++ do_set_int(strvec, ptr, 0, INT_MAX, file, line_nr, buff); + + FREE(buff); + +- return rc; ++ return 0; + } + + int +@@ -1289,7 +1277,6 @@ declare_mp_snprint(pgfailback, print_pgfailback) + static int + no_path_retry_helper(vector strvec, void *ptr, const char *file, int line_nr) + { +- int rc = 0; + int *int_ptr = (int *)ptr; + char * buff; + +@@ -1302,10 +1289,10 @@ no_path_retry_helper(vector strvec, void *ptr, const char *file, int line_nr) + else if (!strcmp(buff, "queue")) + *int_ptr = NO_PATH_RETRY_QUEUE; + else +- rc = do_set_int(strvec, ptr, 1, INT_MAX, file, line_nr, buff); ++ do_set_int(strvec, ptr, 1, INT_MAX, file, line_nr, buff); + + FREE(buff); +- return rc; ++ return 0; + } + + int +@@ -1453,7 +1440,6 @@ snprint_mp_reservation_key (struct config *conf, char * buff, int len, + static int + set_off_int_undef(vector strvec, void *ptr, const char *file, int line_nr) + { +- int rc =0; + int *int_ptr = (int *)ptr; + char * buff; + +@@ -1464,10 +1450,10 @@ set_off_int_undef(vector strvec, void *ptr, const char *file, int line_nr) + if (!strcmp(buff, "no") || !strcmp(buff, "0")) + *int_ptr = NU_NO; + else +- rc = do_set_int(strvec, ptr, 1, INT_MAX, file, line_nr, buff); ++ do_set_int(strvec, ptr, 1, INT_MAX, file, line_nr, buff); + + FREE(buff); +- return rc; ++ return 0; + } + + int diff --git a/SOURCES/0087-multipathd-avoid-unnecessary-path-read-only-reloads.patch b/SOURCES/0087-multipathd-avoid-unnecessary-path-read-only-reloads.patch new file mode 100644 index 0000000..18cb434 --- /dev/null +++ b/SOURCES/0087-multipathd-avoid-unnecessary-path-read-only-reloads.patch @@ -0,0 +1,115 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 15 Nov 2021 10:54:35 -0600 +Subject: [PATCH] multipathd: avoid unnecessary path read-only reloads + +A mulitpath device can only be reloaded read/write when all paths are +read/write. Also, whenever a read-only device is rescanned, the scsi +subsystem will first unconditionally issue a uevent with DISK_RO=0 +before checking the read-only status, and if it the device is still +read-only, issuing another uevent with DISK_RO=1. These uevents cause +pointless reloads when read-only paths are rescanned. To avoid this, +check to see if all paths are read/write before changing a multipath +device from read-only to read/write. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/sysfs.c | 22 ++++++++++++++++++++++ + libmultipath/sysfs.h | 1 + + multipathd/main.c | 31 ++++++++++++++++++++++++++++++- + 3 files changed, 53 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c +index 62ec2ed7..a57bd60e 100644 +--- a/libmultipath/sysfs.c ++++ b/libmultipath/sysfs.c +@@ -236,6 +236,28 @@ sysfs_get_size (struct path *pp, unsigned long long * size) + return 0; + } + ++int ++sysfs_get_ro (struct path *pp) ++{ ++ int ro; ++ char buff[3]; /* Either "0\n\0" or "1\n\0" */ ++ ++ if (!pp->udev) ++ return -1; ++ ++ if (sysfs_attr_get_value(pp->udev, "ro", buff, sizeof(buff)) <= 0) { ++ condlog(3, "%s: Cannot read ro attribute in sysfs", pp->dev); ++ return -1; ++ } ++ ++ if (sscanf(buff, "%d\n", &ro) != 1 || ro < 0 || ro > 1) { ++ condlog(3, "%s: Cannot parse ro attribute", pp->dev); ++ return -1; ++ } ++ ++ return ro; ++} ++ + int sysfs_check_holders(char * check_devt, char * new_devt) + { + unsigned int major, new_minor, table_minor; +diff --git a/libmultipath/sysfs.h b/libmultipath/sysfs.h +index 9ae30b39..91092e44 100644 +--- a/libmultipath/sysfs.h ++++ b/libmultipath/sysfs.h +@@ -13,6 +13,7 @@ ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name, + ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name, + unsigned char * value, size_t value_len); + int sysfs_get_size (struct path *pp, unsigned long long * size); ++int sysfs_get_ro(struct path *pp); + int sysfs_check_holders(char * check_devt, char * new_devt); + bool sysfs_is_multipathed(const struct path *pp); + #endif +diff --git a/multipathd/main.c b/multipathd/main.c +index 823b53a2..e2b9d546 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -1256,6 +1256,35 @@ fail: + return PATH_REMOVE_FAILED; + } + ++static bool ++needs_ro_update(struct multipath *mpp, int ro) ++{ ++ struct pathgroup * pgp; ++ struct path * pp; ++ unsigned int i, j; ++ struct dm_info *dmi = NULL; ++ ++ if (!mpp || ro < 0) ++ return false; ++ dm_get_info(mpp->alias, &dmi); ++ if (!dmi) /* assume we do need to reload the device */ ++ return true; ++ if (dmi->read_only == ro) { ++ free(dmi); ++ return false; ++ } ++ free(dmi); ++ if (ro == 1) ++ return true; ++ vector_foreach_slot (mpp->pg, pgp, i) { ++ vector_foreach_slot (pgp->paths, pp, j) { ++ if (sysfs_get_ro(pp) == 1) ++ return false; ++ } ++ } ++ return true; ++} ++ + static int + uev_update_path (struct uevent *uev, struct vectors * vecs) + { +@@ -1321,7 +1350,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) + } + + ro = uevent_get_disk_ro(uev); +- if (mpp && ro >= 0) { ++ if (needs_ro_update(mpp, ro)) { + condlog(2, "%s: update path write_protect to '%d' (uevent)", uev->kernel, ro); + + if (mpp->wait_for_udev) diff --git a/SOURCES/0088-libmultipath-make-helper-function-to-trigger-path-ue.patch b/SOURCES/0088-libmultipath-make-helper-function-to-trigger-path-ue.patch new file mode 100644 index 0000000..40b4f0e --- /dev/null +++ b/SOURCES/0088-libmultipath-make-helper-function-to-trigger-path-ue.patch @@ -0,0 +1,133 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 17 Jan 2022 14:45:38 -0600 +Subject: [PATCH] libmultipath: make helper function to trigger path uevents + +Pull the code that checks if a path needs to trigger a uevent, and +triggers, out of trigger_paths_udev_change() and into a new function, +trigger_path_udev_change(). This function will be used separately by +a future patch. No functional changes. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/configure.c | 79 +++++++++++++++++++++------------------- + libmultipath/configure.h | 1 + + 2 files changed, 43 insertions(+), 37 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index 9c8d3e34..9a9890f5 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -545,11 +545,8 @@ unref: + } + + void +-trigger_paths_udev_change(struct multipath *mpp, bool is_mpath) ++trigger_path_udev_change(struct path *pp, bool is_mpath) + { +- struct pathgroup *pgp; +- struct path *pp; +- int i, j; + /* + * If a path changes from multipath to non-multipath, we must + * synthesize an artificial "add" event, otherwise the LVM2 rules +@@ -557,6 +554,45 @@ trigger_paths_udev_change(struct multipath *mpp, bool is_mpath) + * irritate ourselves with an "add", so use "change". + */ + const char *action = is_mpath ? "change" : "add"; ++ const char *env; ++ ++ if (!pp->udev) ++ return; ++ /* ++ * Paths that are already classified as multipath ++ * members don't need another uevent. ++ */ ++ env = udev_device_get_property_value( ++ pp->udev, "DM_MULTIPATH_DEVICE_PATH"); ++ ++ if (is_mpath && env != NULL && !strcmp(env, "1")) { ++ /* ++ * If FIND_MULTIPATHS_WAIT_UNTIL is not "0", ++ * path is in "maybe" state and timer is running ++ * Send uevent now (see multipath.rules). ++ */ ++ env = udev_device_get_property_value( ++ pp->udev, "FIND_MULTIPATHS_WAIT_UNTIL"); ++ if (env == NULL || !strcmp(env, "0")) ++ return; ++ } else if (!is_mpath && ++ (env == NULL || !strcmp(env, "0"))) ++ return; ++ ++ condlog(3, "triggering %s uevent for %s (is %smultipath member)", ++ action, pp->dev, is_mpath ? "" : "no "); ++ sysfs_attr_set_value(pp->udev, "uevent", ++ action, strlen(action)); ++ trigger_partitions_udev_change(pp->udev, action, ++ strlen(action)); ++} ++ ++void ++trigger_paths_udev_change(struct multipath *mpp, bool is_mpath) ++{ ++ struct pathgroup *pgp; ++ struct path *pp; ++ int i, j; + + if (!mpp || !mpp->pg) + return; +@@ -564,39 +600,8 @@ trigger_paths_udev_change(struct multipath *mpp, bool is_mpath) + vector_foreach_slot (mpp->pg, pgp, i) { + if (!pgp->paths) + continue; +- vector_foreach_slot(pgp->paths, pp, j) { +- const char *env; +- +- if (!pp->udev) +- continue; +- /* +- * Paths that are already classified as multipath +- * members don't need another uevent. +- */ +- env = udev_device_get_property_value( +- pp->udev, "DM_MULTIPATH_DEVICE_PATH"); +- +- if (is_mpath && env != NULL && !strcmp(env, "1")) { +- /* +- * If FIND_MULTIPATHS_WAIT_UNTIL is not "0", +- * path is in "maybe" state and timer is running +- * Send uevent now (see multipath.rules). +- */ +- env = udev_device_get_property_value( +- pp->udev, "FIND_MULTIPATHS_WAIT_UNTIL"); +- if (env == NULL || !strcmp(env, "0")) +- continue; +- } else if (!is_mpath && +- (env == NULL || !strcmp(env, "0"))) +- continue; +- +- condlog(3, "triggering %s uevent for %s (is %smultipath member)", +- action, pp->dev, is_mpath ? "" : "no "); +- sysfs_attr_set_value(pp->udev, "uevent", +- action, strlen(action)); +- trigger_partitions_udev_change(pp->udev, action, +- strlen(action)); +- } ++ vector_foreach_slot(pgp->paths, pp, j) ++ trigger_path_udev_change(pp, is_mpath); + } + + mpp->needs_paths_uevent = 0; +diff --git a/libmultipath/configure.h b/libmultipath/configure.h +index 8a266d31..5cf08d45 100644 +--- a/libmultipath/configure.h ++++ b/libmultipath/configure.h +@@ -56,6 +56,7 @@ int get_refwwid (enum mpath_cmds cmd, char * dev, enum devtypes dev_type, + vector pathvec, char **wwid); + int reload_map(struct vectors *vecs, struct multipath *mpp, int refresh, int is_daemon); + struct udev_device *get_udev_device(const char *dev, enum devtypes dev_type); ++void trigger_path_udev_change(struct path *pp, bool is_mpath); + void trigger_paths_udev_change(struct multipath *mpp, bool is_mpath); + void trigger_partitions_udev_change(struct udev_device *dev, const char *action, + int len); diff --git a/SOURCES/0089-multipathd-trigger-udev-change-on-path-addition.patch b/SOURCES/0089-multipathd-trigger-udev-change-on-path-addition.patch new file mode 100644 index 0000000..3a8b2a3 --- /dev/null +++ b/SOURCES/0089-multipathd-trigger-udev-change-on-path-addition.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Mon, 17 Jan 2022 16:46:18 -0600 +Subject: [PATCH] multipathd: trigger udev change on path addition + +When a multipath device is created for the first time, there is a window +where some path devices way be added to the multipath device, but never +claimed in udev. This can allow other device owners, like lvm, to think +they can use the device. + +When a multipath device is first created, all the existing paths that +are not claimed by multipath have a uevent triggered so that they can +get claimed. After that, multipath assumes all future paths added to the +multipath device will have been claimed by multipath, since the device's +WWID is now in the wwids file. This doesn't work for any paths that +have already been processed by the multipath.rules udev rules before +the multipath device was created. + +To close this window, when path device is added, and a matching +multipath device already exists, multipathd now checks if the device is +claimed by multipath, and if not, triggers a uevent to claim it. + +Signed-off-by: Benjamin Marzinski +--- + multipathd/main.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/multipathd/main.c b/multipathd/main.c +index e2b9d546..f4b79882 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -1005,6 +1005,8 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map) + free_path(pp); + return 1; + } ++ if (mpp) ++ trigger_path_udev_change(pp, true); + if (mpp && mpp->wait_for_udev && + (pathcount(mpp, PATH_UP) > 0 || + (pathcount(mpp, PATH_GHOST) > 0 && diff --git a/SOURCES/0090-RH-add-support-to-mpathconf-for-setting-arbitrary-de.patch b/SOURCES/0090-RH-add-support-to-mpathconf-for-setting-arbitrary-de.patch new file mode 100644 index 0000000..1e490cb --- /dev/null +++ b/SOURCES/0090-RH-add-support-to-mpathconf-for-setting-arbitrary-de.patch @@ -0,0 +1,149 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 2 Feb 2022 17:00:21 -0600 +Subject: [PATCH] RH: add support to mpathconf for setting arbitrary default + options + +mpathconf now supports --option :[] for setting, changing, +or removing options from the defaults section of multipath.conf. + +Signed-off-by: Benjamin Marzinski +--- + multipath/mpathconf | 58 ++++++++++++++++++++++++++++++++++++++++--- + multipath/mpathconf.8 | 7 ++++++ + 2 files changed, 62 insertions(+), 3 deletions(-) + +diff --git a/multipath/mpathconf b/multipath/mpathconf +index 5f2285ab..870512c0 100644 +--- a/multipath/mpathconf ++++ b/multipath/mpathconf +@@ -17,7 +17,7 @@ + # 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 + + DEFAULT_CONFIG="# device-mapper-multipath configuration file + +@@ -57,6 +57,7 @@ function usage + echo "Set find_multipaths (Default y): --find_multipaths " + echo "Set default property blacklist (Default y): --property_blacklist " + echo "Set enable_foreign to show foreign devices (Default n): --enable_foreign " ++ echo "Add/Change/Remove option in defaults section: --option :" + echo "Load the dm-multipath modules on enable (Default y): --with_module " + echo "start/stop/reload multipathd (Default n): --with_multipathd " + echo "select output file (Default /etc/multipath.conf): --outfile " +@@ -167,6 +168,20 @@ function parse_args + 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 +@@ -213,12 +228,15 @@ function parse_args + + 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" ]; then + echo "ignoring extra parameters on disable" + FRIENDLY="" + FIND="" + PROPERTY="" + MODULE="" ++ FOREIGN="" ++ OPTION_NAME="" ++ OPTION_VALUE="" + fi + if [ -n "$FRIENDLY" ] && [ "$FRIENDLY" != "y" -a "$FRIENDLY" != "n" ]; then + echo "--user_friendly_names must be either 'y' or 'n'" +@@ -240,7 +258,19 @@ function validate_args + echo "--enable_foreign must be either 'y' or 'n'" + exit 1 + fi +- if [ -z "$ENABLE" -a -z "$FIND" -a -z "$FRIENDLY" -a -z "$PROPERTY" -a -z "$FOREIGN" ]; 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" ]; then + SHOW_STATUS=1 + fi + if [ -n "$MODULE" ] && [ "$MODULE" != "y" -a "$MODULE" != "n" ]; then +@@ -349,6 +379,13 @@ if [ "$HAVE_DEFAULTS" = "1" ]; then + elif sed -n '/^defaults[[:space:]]*{/,/^}/ p' $TMPFILE | grep -q "^[[:space:]]*enable_foreign" ; then + HAVE_FOREIGN=2 + 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 +@@ -523,6 +560,21 @@ elif [ "$FOREIGN" = "n" ]; then + 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 +diff --git a/multipath/mpathconf.8 b/multipath/mpathconf.8 +index 83515eb4..63fe633e 100644 +--- a/multipath/mpathconf.8 ++++ b/multipath/mpathconf.8 +@@ -101,6 +101,13 @@ to the + defaults section. if set to \fBy\fP, this removes the line, if present. This + command can be used along with any other command. + .TP ++.B --option \fB:[]\fP ++Sets the defaults section option \fB\fP to \fB\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\fP. If \fB\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\fP + Write the resulting multipath configuration to \fB\fP instead of + \fB/etc/multipath.conf\fP. diff --git a/SOURCES/0091-multipath-tools-add-HPE-MSA-1060-2060-to-hwtable.patch b/SOURCES/0091-multipath-tools-add-HPE-MSA-1060-2060-to-hwtable.patch new file mode 100644 index 0000000..d49cec8 --- /dev/null +++ b/SOURCES/0091-multipath-tools-add-HPE-MSA-1060-2060-to-hwtable.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Xose Vazquez Perez +Date: Wed, 9 Sep 2020 17:48:05 +0200 +Subject: [PATCH] multipath-tools: add HPE MSA 1060/2060 to hwtable + +Cc: Martin Wilck +Cc: Christophe Varoqui +Cc: DM-DEVEL ML +Reviewed-by: Martin Wilck +Signed-off-by: Xose Vazquez Perez +Signed-off-by: Benjamin Marzinski +--- + libmultipath/hwtable.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c +index d680bdfc..59bc0d6e 100644 +--- a/libmultipath/hwtable.c ++++ b/libmultipath/hwtable.c +@@ -181,9 +181,9 @@ static struct hwentry default_hw[] = { + .prio_name = PRIO_ALUA, + }, + { +- /* MSA 1040, 1050, 2040 and 2050 families */ ++ /* MSA 1040, 1050, 1060, 2040, 2050 and 2060 families */ + .vendor = "HP", +- .product = "MSA [12]0[45]0 SA[NS]", ++ .product = "MSA [12]0[456]0 SA[NS]", + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .no_path_retry = 18, diff --git a/SOURCES/0092-multipath-tools-update-mpp-force_readonly-in-ev_add_.patch b/SOURCES/0092-multipath-tools-update-mpp-force_readonly-in-ev_add_.patch new file mode 100644 index 0000000..c432bbc --- /dev/null +++ b/SOURCES/0092-multipath-tools-update-mpp-force_readonly-in-ev_add_.patch @@ -0,0 +1,78 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Uday Shankar +Date: Wed, 9 Mar 2022 13:03:26 -0700 +Subject: [PATCH] multipath-tools: update mpp->force_readonly in ev_add_path + +When NVMe disks are added to the system, no uevent containing the +DISK_RO property is generated. As a result, dm-* nodes backed by +readonly NVMe disks will not have their RO state set properly. The +result looks like this: + +$ multipath -l +eui.00c92c091fd6564424a9376600011bd1 dm-3 NVME,Pure Storage FlashArray +size=1.0T features='0' hwhandler='0' wp=rw +|-+- policy='service-time 0' prio=0 status=active +| `- 0:2:2:72657 nvme0n2 259:4 active undef running +`-+- policy='service-time 0' prio=0 status=enabled + `- 1:0:2:72657 nvme1n2 259:1 active undef running +$ cat /sys/block/dm-3/ro +0 +$ cat /sys/block/nvme*n2/ro +1 +1 + +This is not a problem for SCSI disks, since the kernel will emit change +uevents containing the DISK_RO property when the disk is added to the +system. See the following thread for my initial attempt to fix this +issue at the kernel level: +https://lore.kernel.org/linux-block/Yib8GqCA5e3SQYty@infradead.org/T/#t + +Fix the issue by picking up the path ro state from sysfs in ev_add_path, +setting the mpp->force_readonly accordingly, and changing +dm_addmap_create to be aware of mpp->force_readonly. + +Signed-off-by: Uday Shankar +Reviewed-by: Benjamin Marzinski +Signed-off-by: Benjamin Marzinski +--- + libmultipath/devmapper.c | 2 +- + multipathd/main.c | 6 ++++++ + 2 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index 3f70e576..e3c3c119 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -420,7 +420,7 @@ int dm_addmap_create (struct multipath *mpp, char * params) + int ro; + uint16_t udev_flags = build_udev_flags(mpp, 0); + +- for (ro = 0; ro <= 1; ro++) { ++ for (ro = mpp->force_readonly ? 1 : 0; ro <= 1; ro++) { + int err; + + if (dm_addmap(DM_DEVICE_CREATE, TGT_MPATH, mpp, params, ro, +diff --git a/multipathd/main.c b/multipathd/main.c +index f4b79882..eeded52b 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -988,6 +988,7 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map) + int retries = 3; + int start_waiter = 0; + int ret; ++ int ro; + + /* + * need path UID to go any further +@@ -1051,6 +1052,11 @@ rescan: + /* persistent reservation check*/ + mpath_pr_event_handle(pp); + ++ /* ro check - if new path is ro, force map to be ro as well */ ++ ro = sysfs_get_ro(pp); ++ if (ro == 1) ++ mpp->force_readonly = 1; ++ + if (!need_do_map) + return 0; + diff --git a/SOURCES/0093-updated-HPE-MSA-builtin-config.patch b/SOURCES/0093-updated-HPE-MSA-builtin-config.patch new file mode 100644 index 0000000..0c61ec3 --- /dev/null +++ b/SOURCES/0093-updated-HPE-MSA-builtin-config.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 29 Apr 2022 15:57:12 -0500 +Subject: [PATCH] updated HPE MSA builtin config + +Make the config better align to MSA 4th, 5th and 6th Generation systems. + +Cc: Jon.Paul@hpe.com +Signed-off-by: Benjamin Marzinski +--- + libmultipath/hwtable.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c +index 59bc0d6e..d6325864 100644 +--- a/libmultipath/hwtable.c ++++ b/libmultipath/hwtable.c +@@ -182,8 +182,8 @@ static struct hwentry default_hw[] = { + }, + { + /* MSA 1040, 1050, 1060, 2040, 2050 and 2060 families */ +- .vendor = "HP", +- .product = "MSA [12]0[456]0 SA[NS]", ++ .vendor = "(HP|HPE)", ++ .product = "MSA [12]0[456]0 (SAN|SAS|FC|iSCSI)", + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, + .no_path_retry = 18, diff --git a/SOURCES/0094-multipath-return-failure-on-an-invalid-remove-comman.patch b/SOURCES/0094-multipath-return-failure-on-an-invalid-remove-comman.patch new file mode 100644 index 0000000..add7207 --- /dev/null +++ b/SOURCES/0094-multipath-return-failure-on-an-invalid-remove-comman.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 30 Mar 2022 15:14:56 -0500 +Subject: [PATCH] multipath: return failure on an invalid remove command + +When "multipath -f" is run on a device that doesn't exist or isn't a +multipath device, the command will not remove any device, but it will +still return success. Multiple functions rely on _dm_flush_map() +returning success when called with name that doesn't match any +multipath device. So before calling _dm_flush_map(), call dm_is_mpath(), +to check if the device exists and is a multipath device, and return +failure if it's not. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck remove_retries; + if (cmd == CMD_FLUSH_ONE) { ++ if (dm_is_mpath(dev) != 1) { ++ condlog(0, "%s is not a multipath device", dev); ++ r = RTVL_FAIL; ++ goto out; ++ } + r = dm_suspend_and_flush_map(dev, retries) ? + RTVL_FAIL : RTVL_OK; + goto out; diff --git a/SOURCES/0095-libmultipath-steal-the-src-string-pointer-in-merge_s.patch b/SOURCES/0095-libmultipath-steal-the-src-string-pointer-in-merge_s.patch new file mode 100644 index 0000000..5de4eaf --- /dev/null +++ b/SOURCES/0095-libmultipath-steal-the-src-string-pointer-in-merge_s.patch @@ -0,0 +1,67 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 13 Apr 2022 23:27:35 -0500 +Subject: [PATCH] libmultipath: steal the src string pointer in merge_str() + +Instead of allocating a copy when the original string is going to be +freed right after the merge, just steal the pointer. Also, merge_mpe() +can't get called with NULL arguments. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/config.c | 16 +++++----------- + 1 file changed, 5 insertions(+), 11 deletions(-) + +diff --git a/libmultipath/config.c b/libmultipath/config.c +index abbddaf1..aa79561e 100644 +--- a/libmultipath/config.c ++++ b/libmultipath/config.c +@@ -323,9 +323,9 @@ set_param_str(const char * str) + } + + #define merge_str(s) \ +- if (!dst->s && src->s) { \ +- if (!(dst->s = set_param_str(src->s))) \ +- return 1; \ ++ if (!dst->s && src->s && strlen(src->s)) { \ ++ dst->s = src->s; \ ++ src->s = NULL; \ + } + + #define merge_num(s) \ +@@ -333,7 +333,7 @@ set_param_str(const char * str) + dst->s = src->s + + +-static int ++static void + merge_hwe (struct hwentry * dst, struct hwentry * src) + { + char id[SCSI_VENDOR_SIZE+PATH_PRODUCT_SIZE]; +@@ -385,15 +385,11 @@ merge_hwe (struct hwentry * dst, struct hwentry * src) + reconcile_features_with_options(id, &dst->features, + &dst->no_path_retry, + &dst->retain_hwhandler); +- return 0; + } + +-static int ++static void + merge_mpe(struct mpentry *dst, struct mpentry *src) + { +- if (!dst || !src) +- return 1; +- + merge_str(alias); + merge_str(uid_attribute); + merge_str(getuid); +@@ -435,8 +431,6 @@ merge_mpe(struct mpentry *dst, struct mpentry *src) + merge_num(uid); + merge_num(gid); + merge_num(mode); +- +- return 0; + } + + void merge_mptable(vector mptable) diff --git a/SOURCES/0096-libmultipath-enable-linear-ordering-of-bus-proto-tup.patch b/SOURCES/0096-libmultipath-enable-linear-ordering-of-bus-proto-tup.patch new file mode 100644 index 0000000..76a9407 --- /dev/null +++ b/SOURCES/0096-libmultipath-enable-linear-ordering-of-bus-proto-tup.patch @@ -0,0 +1,75 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Thu, 17 Feb 2022 17:22:32 +0100 +Subject: [PATCH] libmultipath: enable linear ordering of bus/proto tuple + +We categorized protocols by bus/proto_id, while we only differentiate +protocol IDs for SCSI. Allow transforming this into a linear sequence +of bus/protocol IDs by having non-SCSI first, and follwing up with +the different SCSI protocols. + +Signed-off-by: Martin Wilck +Reviewed-by: Benjamin Marzinski +Signed-off-by: Benjamin Marzinski +--- + libmultipath/structs.c | 10 ++++++++++ + libmultipath/structs.h | 14 ++++++++++++-- + 2 files changed, 22 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/structs.c b/libmultipath/structs.c +index dda9884c..aaf85297 100644 +--- a/libmultipath/structs.c ++++ b/libmultipath/structs.c +@@ -722,3 +722,13 @@ out: + + return 0; + } ++ ++unsigned int bus_protocol_id(const struct path *pp) { ++ if (!pp || pp->bus < 0 || pp->bus > SYSFS_BUS_SCSI) ++ return SYSFS_BUS_UNDEF; ++ if (pp->bus != SYSFS_BUS_SCSI) ++ return pp->bus; ++ if ((int)pp->sg_id.proto_id < 0 || pp->sg_id.proto_id > SCSI_PROTOCOL_UNSPEC) ++ return SYSFS_BUS_UNDEF; ++ return SYSFS_BUS_SCSI + pp->sg_id.proto_id; ++} +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index a5dbad5b..5e29ae38 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -57,12 +57,13 @@ enum failback_mode { + FAILBACK_FOLLOWOVER + }; + ++/* SYSFS_BUS_SCSI should be last, see bus_protocol_id() */ + enum sysfs_buses { + SYSFS_BUS_UNDEF, +- SYSFS_BUS_SCSI, + SYSFS_BUS_CCW, + SYSFS_BUS_CCISS, + SYSFS_BUS_NVME, ++ SYSFS_BUS_SCSI, + }; + + enum pathstates { +@@ -190,9 +191,18 @@ enum scsi_protocol { + SCSI_PROTOCOL_SAS = 6, + SCSI_PROTOCOL_ADT = 7, /* Media Changers */ + SCSI_PROTOCOL_ATA = 8, +- SCSI_PROTOCOL_UNSPEC = 0xf, /* No specific protocol */ ++ SCSI_PROTOCOL_USB = 9, /* USB Attached SCSI (UAS), and others */ ++ SCSI_PROTOCOL_UNSPEC = 0xa, /* No specific protocol */ + }; + ++/* ++ * Linear ordering of bus/protocol ++ * This assumes that SYSFS_BUS_SCSI is last in enum sysfs_buses ++ * SCSI is the only bus type for which we distinguish protocols. ++ */ ++#define LAST_BUS_PROTOCOL_ID (SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC) ++unsigned int bus_protocol_id(const struct path *pp); ++ + enum no_undef_states { + NU_NO = -1, + NU_UNDEF = 0, diff --git a/SOURCES/0097-libmultipath-use-bus_protocol_id-in-snprint_path_pro.patch b/SOURCES/0097-libmultipath-use-bus_protocol_id-in-snprint_path_pro.patch new file mode 100644 index 0000000..31976e7 --- /dev/null +++ b/SOURCES/0097-libmultipath-use-bus_protocol_id-in-snprint_path_pro.patch @@ -0,0 +1,82 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Thu, 17 Feb 2022 17:24:25 +0100 +Subject: [PATCH] libmultipath: use bus_protocol_id() in + snprint_path_protocol() + +Simplify bus_protocol_id() by using the linear ordering. + +Signed-off-by: Martin Wilck +Reviewed-by: Benjamin Marzinski +Signed-off-by: Benjamin Marzinski +--- + libmultipath/print.c | 56 +++++++++++++++++--------------------------- + 1 file changed, 21 insertions(+), 35 deletions(-) + +diff --git a/libmultipath/print.c b/libmultipath/print.c +index 298b3764..ff4d1854 100644 +--- a/libmultipath/print.c ++++ b/libmultipath/print.c +@@ -662,41 +662,27 @@ snprint_path_failures(char * buff, size_t len, const struct path * pp) + int + snprint_path_protocol(char * buff, size_t len, const struct path * pp) + { +- switch (pp->bus) { +- case SYSFS_BUS_SCSI: +- switch (pp->sg_id.proto_id) { +- case SCSI_PROTOCOL_FCP: +- return snprintf(buff, len, "scsi:fcp"); +- case SCSI_PROTOCOL_SPI: +- return snprintf(buff, len, "scsi:spi"); +- case SCSI_PROTOCOL_SSA: +- return snprintf(buff, len, "scsi:ssa"); +- case SCSI_PROTOCOL_SBP: +- return snprintf(buff, len, "scsi:sbp"); +- case SCSI_PROTOCOL_SRP: +- return snprintf(buff, len, "scsi:srp"); +- case SCSI_PROTOCOL_ISCSI: +- return snprintf(buff, len, "scsi:iscsi"); +- case SCSI_PROTOCOL_SAS: +- return snprintf(buff, len, "scsi:sas"); +- case SCSI_PROTOCOL_ADT: +- return snprintf(buff, len, "scsi:adt"); +- case SCSI_PROTOCOL_ATA: +- return snprintf(buff, len, "scsi:ata"); +- case SCSI_PROTOCOL_UNSPEC: +- default: +- return snprintf(buff, len, "scsi:unspec"); +- } +- case SYSFS_BUS_CCW: +- return snprintf(buff, len, "ccw"); +- case SYSFS_BUS_CCISS: +- return snprintf(buff, len, "cciss"); +- case SYSFS_BUS_NVME: +- return snprintf(buff, len, "nvme"); +- case SYSFS_BUS_UNDEF: +- default: +- return snprintf(buff, len, "undef"); +- } ++ static const char * const protocol_name[LAST_BUS_PROTOCOL_ID + 1] = { ++ [SYSFS_BUS_UNDEF] = "undef", ++ [SYSFS_BUS_CCW] = "ccw", ++ [SYSFS_BUS_CCISS] = "cciss", ++ [SYSFS_BUS_NVME] = "nvme", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_FCP] = "scsi:fcp", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SPI] = "scsi:spi", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SSA] = "scsi:ssa", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SBP] = "scsi:sbp", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SRP] = "scsi:srp", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ISCSI] = "scsi:iscsi", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SAS] = "scsi:sas", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ADT] = "scsi:adt", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ATA] = "scsi:ata", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_USB] = "scsi:usb", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC] = "scsi:unspec", ++ }; ++ const char *pn = protocol_name[bus_protocol_id(pp)]; ++ ++ assert(pn != NULL); ++ return snprintf(buff, len, "%s", pn); + } + + int diff --git a/SOURCES/0098-libmultipath-make-protocol_name-global.patch b/SOURCES/0098-libmultipath-make-protocol_name-global.patch new file mode 100644 index 0000000..9d6a329 --- /dev/null +++ b/SOURCES/0098-libmultipath-make-protocol_name-global.patch @@ -0,0 +1,85 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 13 Apr 2022 23:27:36 -0500 +Subject: [PATCH] libmultipath: make protocol_name global + +Future patches will make use of this, so move it out of +snprint_path_protocol() + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/print.c | 17 ----------------- + libmultipath/structs.c | 18 ++++++++++++++++++ + libmultipath/structs.h | 1 + + 3 files changed, 19 insertions(+), 17 deletions(-) + +diff --git a/libmultipath/print.c b/libmultipath/print.c +index ff4d1854..1f6d27bd 100644 +--- a/libmultipath/print.c ++++ b/libmultipath/print.c +@@ -662,23 +662,6 @@ snprint_path_failures(char * buff, size_t len, const struct path * pp) + int + snprint_path_protocol(char * buff, size_t len, const struct path * pp) + { +- static const char * const protocol_name[LAST_BUS_PROTOCOL_ID + 1] = { +- [SYSFS_BUS_UNDEF] = "undef", +- [SYSFS_BUS_CCW] = "ccw", +- [SYSFS_BUS_CCISS] = "cciss", +- [SYSFS_BUS_NVME] = "nvme", +- [SYSFS_BUS_SCSI + SCSI_PROTOCOL_FCP] = "scsi:fcp", +- [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SPI] = "scsi:spi", +- [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SSA] = "scsi:ssa", +- [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SBP] = "scsi:sbp", +- [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SRP] = "scsi:srp", +- [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ISCSI] = "scsi:iscsi", +- [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SAS] = "scsi:sas", +- [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ADT] = "scsi:adt", +- [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ATA] = "scsi:ata", +- [SYSFS_BUS_SCSI + SCSI_PROTOCOL_USB] = "scsi:usb", +- [SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC] = "scsi:unspec", +- }; + const char *pn = protocol_name[bus_protocol_id(pp)]; + + assert(pn != NULL); +diff --git a/libmultipath/structs.c b/libmultipath/structs.c +index aaf85297..19099bed 100644 +--- a/libmultipath/structs.c ++++ b/libmultipath/structs.c +@@ -20,6 +20,24 @@ + #include "prioritizers/alua_spc3.h" + #include "dm-generic.h" + ++const char * const protocol_name[LAST_BUS_PROTOCOL_ID + 1] = { ++ [SYSFS_BUS_UNDEF] = "undef", ++ [SYSFS_BUS_CCW] = "ccw", ++ [SYSFS_BUS_CCISS] = "cciss", ++ [SYSFS_BUS_NVME] = "nvme", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_FCP] = "scsi:fcp", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SPI] = "scsi:spi", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SSA] = "scsi:ssa", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SBP] = "scsi:sbp", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SRP] = "scsi:srp", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ISCSI] = "scsi:iscsi", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SAS] = "scsi:sas", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ADT] = "scsi:adt", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ATA] = "scsi:ata", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_USB] = "scsi:usb", ++ [SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC] = "scsi:unspec", ++}; ++ + struct adapter_group * + alloc_adaptergroup(void) + { +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 5e29ae38..ab99852f 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -202,6 +202,7 @@ enum scsi_protocol { + */ + #define LAST_BUS_PROTOCOL_ID (SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC) + unsigned int bus_protocol_id(const struct path *pp); ++extern const char * const protocol_name[]; + + enum no_undef_states { + NU_NO = -1, diff --git a/SOURCES/0099-libmultipath-add-a-protocol-subsection-to-multipath..patch b/SOURCES/0099-libmultipath-add-a-protocol-subsection-to-multipath..patch new file mode 100644 index 0000000..ae16fa9 --- /dev/null +++ b/SOURCES/0099-libmultipath-add-a-protocol-subsection-to-multipath..patch @@ -0,0 +1,419 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 13 Apr 2022 23:27:37 -0500 +Subject: [PATCH] libmultipath: add a protocol subsection to multipath.conf + +Some storage arrays can be accessed using multiple protocols at the same +time. In these cases, users may want to set path attributes +differently, depending on the protocol that the path is using. To allow +this, add a protocol subsection to the overrides section in +multipath.conf, which allows select path-specific options to be set. +This commit simply adds the subsection, and handles merging matching +entries. Future patches will make use of the section. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/config.c | 83 ++++++++++++++++++++++++++++++++++++ + libmultipath/config.h | 10 +++++ + libmultipath/dict.c | 99 +++++++++++++++++++++++++++++++++++++++++++ + libmultipath/print.c | 44 +++++++++++++++++++ + 4 files changed, 236 insertions(+) + +diff --git a/libmultipath/config.c b/libmultipath/config.c +index aa79561e..88975323 100644 +--- a/libmultipath/config.c ++++ b/libmultipath/config.c +@@ -173,6 +173,18 @@ char *get_mpe_wwid(vector mptable, char *alias) + return NULL; + } + ++static void ++free_pctable (vector pctable) ++{ ++ int i; ++ struct pcentry *pce; ++ ++ vector_foreach_slot(pctable, pce, i) ++ free(pce); ++ ++ vector_free(pctable); ++} ++ + void + free_hwe (struct hwentry * hwe) + { +@@ -218,6 +230,9 @@ free_hwe (struct hwentry * hwe) + if (hwe->bl_product) + FREE(hwe->bl_product); + ++ if (hwe->pctable) ++ free_pctable(hwe->pctable); ++ + FREE(hwe); + } + +@@ -299,6 +314,15 @@ alloc_hwe (void) + return hwe; + } + ++struct pcentry * ++alloc_pce (void) ++{ ++ struct pcentry *pce = (struct pcentry *) ++ calloc(1, sizeof(struct pcentry)); ++ pce->type = -1; ++ return pce; ++} ++ + static char * + set_param_str(const char * str) + { +@@ -332,6 +356,13 @@ set_param_str(const char * str) + if (!dst->s && src->s) \ + dst->s = src->s + ++static void ++merge_pce(struct pcentry *dst, struct pcentry *src) ++{ ++ merge_num(fast_io_fail); ++ merge_num(dev_loss); ++ merge_num(eh_deadline); ++} + + static void + merge_hwe (struct hwentry * dst, struct hwentry * src) +@@ -538,6 +569,51 @@ out: + return 1; + } + ++static void ++validate_pctable(struct hwentry *ovr, int idx, const char *table_desc) ++{ ++ struct pcentry *pce; ++ ++ if (!ovr || !ovr->pctable) ++ return; ++ ++ vector_foreach_slot_after(ovr->pctable, pce, idx) { ++ if (pce->type < 0) { ++ condlog(0, "protocol section in %s missing type", ++ table_desc); ++ vector_del_slot(ovr->pctable, idx--); ++ free(pce); ++ } ++ } ++ ++ if (VECTOR_SIZE(ovr->pctable) == 0) { ++ vector_free(ovr->pctable); ++ ovr->pctable = NULL; ++ } ++} ++ ++static void ++merge_pctable(struct hwentry *ovr) ++{ ++ struct pcentry *pce1, *pce2; ++ int i, j; ++ ++ if (!ovr || !ovr->pctable) ++ return; ++ ++ vector_foreach_slot(ovr->pctable, pce1, i) { ++ j = i + 1; ++ vector_foreach_slot_after(ovr->pctable, pce2, j) { ++ if (pce1->type != pce2->type) ++ continue; ++ merge_pce(pce2,pce1); ++ vector_del_slot(ovr->pctable, i--); ++ free(pce1); ++ break; ++ } ++ } ++} ++ + static void + factorize_hwtable (vector hw, int n, const char *table_desc) + { +@@ -666,6 +742,7 @@ process_config_dir(struct config *conf, char *dir) + int i, n; + char path[LINE_MAX]; + int old_hwtable_size; ++ int old_pctable_size = 0; + + if (dir[0] != '/') { + condlog(1, "config_dir '%s' must be a fully qualified path", +@@ -692,11 +769,15 @@ process_config_dir(struct config *conf, char *dir) + continue; + + old_hwtable_size = VECTOR_SIZE(conf->hwtable); ++ old_pctable_size = conf->overrides ? ++ VECTOR_SIZE(conf->overrides->pctable) : 0; + snprintf(path, LINE_MAX, "%s/%s", dir, namelist[i]->d_name); + path[LINE_MAX-1] = '\0'; + process_file(conf, path); + factorize_hwtable(conf->hwtable, old_hwtable_size, + namelist[i]->d_name); ++ validate_pctable(conf->overrides, old_pctable_size, ++ namelist[i]->d_name); + } + pthread_cleanup_pop(1); + } +@@ -784,6 +865,7 @@ load_config (char * file) + goto out; + } + 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."); + condlog(0, "You can run \"/sbin/mpathconf --enable\" to create"); +@@ -898,6 +980,7 @@ load_config (char * file) + goto out; + } + ++ merge_pctable(conf->overrides); + merge_mptable(conf->mptable); + merge_blacklist(conf->blist_devnode); + merge_blacklist(conf->blist_property); +diff --git a/libmultipath/config.h b/libmultipath/config.h +index e2e3f143..143116b3 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -41,6 +41,13 @@ enum force_reload_types { + FORCE_RELOAD_WEAK, + }; + ++struct pcentry { ++ int type; ++ int fast_io_fail; ++ unsigned int dev_loss; ++ int eh_deadline; ++}; ++ + struct hwentry { + char * vendor; + char * product; +@@ -86,6 +93,8 @@ struct hwentry { + int vpd_vendor_id; + int recheck_wwid; + char * bl_product; ++ ++ vector pctable; + }; + + struct mpentry { +@@ -240,6 +249,7 @@ char * get_mpe_wwid (vector mptable, char * alias); + + struct hwentry * alloc_hwe (void); + struct mpentry * alloc_mpe (void); ++struct pcentry * alloc_pce (void); + + void free_hwe (struct hwentry * hwe); + void free_hwtable (vector hwtable); +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index 5a0255b0..8321ec1e 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -457,6 +457,29 @@ snprint_mp_ ## option (struct config *conf, char * buff, int len, \ + return function (buff, len, mpe->option); \ + } + ++#define declare_pc_handler(option, function) \ ++static int \ ++pc_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ ++{ \ ++ struct pcentry *pce; \ ++ if (!conf->overrides || !conf->overrides->pctable) \ ++ return 1; \ ++ pce = VECTOR_LAST_SLOT(conf->overrides->pctable); \ ++ if (!pce) \ ++ return 1; \ ++ return function (strvec, &pce->option, file, line_nr); \ ++} ++ ++#define declare_pc_snprint(option, function) \ ++static int \ ++snprint_pc_ ## option (struct config *conf, char * buff, int len, \ ++ const void *data) \ ++{ \ ++ const struct pcentry *pce = (const struct pcentry *)data; \ ++ return function(buff, len, pce->option); \ ++} ++ + static int checkint_handler(struct config *conf, vector strvec, + const char *file, int line_nr) + { +@@ -1022,6 +1045,8 @@ declare_ovr_handler(fast_io_fail, set_undef_off_zero) + declare_ovr_snprint(fast_io_fail, print_undef_off_zero) + declare_hw_handler(fast_io_fail, set_undef_off_zero) + declare_hw_snprint(fast_io_fail, print_undef_off_zero) ++declare_pc_handler(fast_io_fail, set_undef_off_zero) ++declare_pc_snprint(fast_io_fail, print_undef_off_zero) + + static int + set_dev_loss(vector strvec, void *ptr, const char *file, int line_nr) +@@ -1059,6 +1084,8 @@ declare_ovr_handler(dev_loss, set_dev_loss) + declare_ovr_snprint(dev_loss, print_dev_loss) + declare_hw_handler(dev_loss, set_dev_loss) + declare_hw_snprint(dev_loss, print_dev_loss) ++declare_pc_handler(dev_loss, set_dev_loss) ++declare_pc_snprint(dev_loss, print_dev_loss) + + declare_def_handler(eh_deadline, set_undef_off_zero) + declare_def_snprint(eh_deadline, print_undef_off_zero) +@@ -1066,6 +1093,8 @@ declare_ovr_handler(eh_deadline, set_undef_off_zero) + declare_ovr_snprint(eh_deadline, print_undef_off_zero) + declare_hw_handler(eh_deadline, set_undef_off_zero) + declare_hw_snprint(eh_deadline, print_undef_off_zero) ++declare_pc_handler(eh_deadline, set_undef_off_zero) ++declare_pc_snprint(eh_deadline, print_undef_off_zero) + + static int + set_pgpolicy(vector strvec, void *ptr, const char *file, int line_nr) +@@ -1876,6 +1905,69 @@ declare_mp_snprint(wwid, print_str) + declare_mp_handler(alias, set_str_noslash) + declare_mp_snprint(alias, print_str) + ++ ++static int ++protocol_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) ++{ ++ struct pcentry *pce; ++ ++ if (!conf->overrides) ++ return 1; ++ ++ if (!conf->overrides->pctable && ++ !(conf->overrides->pctable = vector_alloc())) ++ return 1; ++ ++ if (!(pce = alloc_pce())) ++ return 1; ++ ++ if (!vector_alloc_slot(conf->overrides->pctable)) { ++ free(pce); ++ return 1; ++ } ++ vector_set_slot(conf->overrides->pctable, pce); ++ ++ return 0; ++} ++ ++static int ++set_protocol_type(vector strvec, void *ptr, const char *file, int line_nr) ++{ ++ int *int_ptr = (int *)ptr; ++ char *buff; ++ int i; ++ ++ buff = set_value(strvec); ++ ++ if (!buff) ++ return 1; ++ ++ for (i = 0; i <= LAST_BUS_PROTOCOL_ID; i++) { ++ if (protocol_name[i] && !strcmp(buff, protocol_name[i])) { ++ *int_ptr = i; ++ break; ++ } ++ } ++ if (i > LAST_BUS_PROTOCOL_ID) ++ condlog(1, "%s line %d, invalid value for type: \"%s\"", ++ file, line_nr, buff); ++ ++ free(buff); ++ return 0; ++} ++ ++static int ++print_protocol_type(char *buff, int len, int type) ++{ ++ if (type < 0) ++ return 0; ++ return snprintf(buff, len, "\"%s\"", protocol_name[type]); ++} ++ ++declare_pc_handler(type, set_protocol_type) ++declare_pc_snprint(type, print_protocol_type) ++ + /* + * deprecated handlers + */ +@@ -2117,6 +2209,13 @@ init_keywords(vector keywords) + install_keyword("ghost_delay", &ovr_ghost_delay_handler, &snprint_ovr_ghost_delay); + install_keyword("all_tg_pt", &ovr_all_tg_pt_handler, &snprint_ovr_all_tg_pt); + install_keyword("recheck_wwid", &ovr_recheck_wwid_handler, &snprint_ovr_recheck_wwid); ++ install_keyword_multi("protocol", &protocol_handler, NULL); ++ install_sublevel(); ++ install_keyword("type", &pc_type_handler, &snprint_pc_type); ++ install_keyword("fast_io_fail_tmo", &pc_fast_io_fail_handler, &snprint_pc_fast_io_fail); ++ install_keyword("dev_loss_tmo", &pc_dev_loss_handler, &snprint_pc_dev_loss); ++ install_keyword("eh_deadline", &pc_eh_deadline_handler, &snprint_pc_eh_deadline); ++ install_sublevel_end(); + + install_keyword_root("multipaths", &multipaths_handler); + install_keyword_multi("multipath", &multipath_handler, NULL); +diff --git a/libmultipath/print.c b/libmultipath/print.c +index 1f6d27bd..8a6fbe83 100644 +--- a/libmultipath/print.c ++++ b/libmultipath/print.c +@@ -1392,6 +1392,39 @@ snprint_multipath_topology_json (char * buff, int len, const struct vectors * ve + return fwd; + } + ++static int ++snprint_pcentry (const struct config *conf, char *buff, int len, ++ const struct pcentry *pce) ++{ ++ int i; ++ int fwd = 0; ++ struct keyword *kw; ++ struct keyword *rootkw; ++ ++ rootkw = find_keyword(conf->keywords, NULL, "overrides"); ++ if (!rootkw || !rootkw->sub) ++ return 0; ++ ++ rootkw = find_keyword(conf->keywords, rootkw->sub, "protocol"); ++ if (!rootkw) ++ return 0; ++ ++ fwd += snprintf(buff + fwd, len - fwd, "\tprotocol {\n"); ++ if (fwd >= len) ++ return len; ++ ++ iterate_sub_keywords(rootkw, kw, i) { ++ fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", ++ kw, pce); ++ if (fwd >= len) ++ return len; ++ } ++ fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); ++ if (fwd >= len) ++ return len; ++ return fwd; ++} ++ + static int + snprint_hwentry (const struct config *conf, + char * buff, int len, const struct hwentry * hwe) +@@ -1575,6 +1608,17 @@ static int snprint_overrides(const struct config *conf, char * buff, int len, + if (fwd >= len) + return len; + } ++ ++ if (overrides->pctable) { ++ struct pcentry *pce; ++ ++ vector_foreach_slot(overrides->pctable, pce, i) { ++ fwd += snprint_pcentry(conf, buff + fwd, len - fwd, ++ pce); ++ if (fwd >= len) ++ return len; ++ } ++ } + out: + fwd += snprintf(buff + fwd, len - fwd, "}\n"); + if (fwd >= len) diff --git a/SOURCES/0100-libmultipath-Set-the-scsi-timeout-parameters-by-path.patch b/SOURCES/0100-libmultipath-Set-the-scsi-timeout-parameters-by-path.patch new file mode 100644 index 0000000..368e52e --- /dev/null +++ b/SOURCES/0100-libmultipath-Set-the-scsi-timeout-parameters-by-path.patch @@ -0,0 +1,472 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 13 Apr 2022 23:27:38 -0500 +Subject: [PATCH] libmultipath: Set the scsi timeout parameters by path + +Instead of dev_loss, fast_io_fail, and eh_deadline belonging to the +multipath structure, have them belong to the path structure. This means +that they are selected per path, and that sysfs_set_scsi_tmo() doesn't +assume that all paths of a multipath device will have the same value. +Currently they will all be the same, but a future patch will make it +possible for paths to have different values based on their protocol. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/configure.c | 5 +- + libmultipath/discovery.c | 149 +++++++++++++++++++++++---------------- + libmultipath/discovery.h | 2 +- + libmultipath/propsel.c | 42 +++++------ + libmultipath/propsel.h | 6 +- + libmultipath/structs.c | 1 - + libmultipath/structs.h | 6 +- + 7 files changed, 118 insertions(+), 93 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index 9a9890f5..6cad0468 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -338,9 +338,6 @@ int setup_map(struct multipath *mpp, char *params, int params_size, + select_mode(conf, mpp); + select_uid(conf, mpp); + select_gid(conf, mpp); +- select_fast_io_fail(conf, mpp); +- select_dev_loss(conf, mpp); +- select_eh_deadline(conf, mpp); + select_reservation_key(conf, mpp); + select_deferred_remove(conf, mpp); + select_marginal_path_err_sample_time(conf, mpp); +@@ -356,7 +353,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size, + select_ghost_delay(conf, mpp); + select_flush_on_last_del(conf, mpp); + +- sysfs_set_scsi_tmo(mpp, conf->checkint); ++ sysfs_set_scsi_tmo(conf, mpp); + marginal_pathgroups = conf->marginal_pathgroups; + pthread_cleanup_pop(1); + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index 2404cb87..36cc389e 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -581,13 +581,13 @@ sysfs_get_asymmetric_access_state(struct path *pp, char *buff, int buflen) + } + + static int +-sysfs_set_eh_deadline(struct multipath *mpp, struct path *pp) ++sysfs_set_eh_deadline(struct path *pp) + { + struct udev_device *hostdev; + char host_name[HOST_NAME_LEN], value[16]; + int ret; + +- if (mpp->eh_deadline == EH_DEADLINE_UNSET) ++ if (pp->eh_deadline == EH_DEADLINE_UNSET) + return 0; + + sprintf(host_name, "host%d", pp->sg_id.host_no); +@@ -596,12 +596,12 @@ sysfs_set_eh_deadline(struct multipath *mpp, struct path *pp) + if (!hostdev) + return 1; + +- if (mpp->eh_deadline == EH_DEADLINE_OFF) ++ if (pp->eh_deadline == EH_DEADLINE_OFF) + sprintf(value, "off"); +- else if (mpp->eh_deadline == EH_DEADLINE_ZERO) ++ else if (pp->eh_deadline == EH_DEADLINE_ZERO) + sprintf(value, "0"); + else +- snprintf(value, 16, "%u", mpp->eh_deadline); ++ snprintf(value, 16, "%u", pp->eh_deadline); + + ret = sysfs_attr_set_value(hostdev, "eh_deadline", + value, strlen(value)); +@@ -625,6 +625,9 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) + unsigned int tmo; + int ret; + ++ if (!pp->dev_loss && pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET) ++ return; ++ + sprintf(rport_id, "rport-%d:%d-%d", + pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id); + rport_dev = udev_device_new_from_subsystem_sysname(udev, +@@ -664,14 +667,14 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) + * then set fast_io_fail, and _then_ set dev_loss_tmo + * to the correct value. + */ +- if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET && +- mpp->fast_io_fail != MP_FAST_IO_FAIL_ZERO && +- mpp->fast_io_fail != MP_FAST_IO_FAIL_OFF) { ++ if (pp->fast_io_fail != MP_FAST_IO_FAIL_UNSET && ++ pp->fast_io_fail != MP_FAST_IO_FAIL_ZERO && ++ pp->fast_io_fail != MP_FAST_IO_FAIL_OFF) { + /* Check if we need to temporarily increase dev_loss_tmo */ +- if ((unsigned int)mpp->fast_io_fail >= tmo) { ++ if ((unsigned int)pp->fast_io_fail >= tmo) { + /* Increase dev_loss_tmo temporarily */ + snprintf(value, sizeof(value), "%u", +- (unsigned int)mpp->fast_io_fail + 1); ++ (unsigned int)pp->fast_io_fail + 1); + ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo", + value, strlen(value)); + if (ret <= 0) { +@@ -685,20 +688,20 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) + goto out; + } + } +- } else if (mpp->dev_loss > DEFAULT_DEV_LOSS_TMO && +- mpp->no_path_retry != NO_PATH_RETRY_QUEUE) { ++ } else if (pp->dev_loss > DEFAULT_DEV_LOSS_TMO && ++ mpp->no_path_retry != NO_PATH_RETRY_QUEUE) { + condlog(3, "%s: limiting dev_loss_tmo to %d, since " + "fast_io_fail is not set", + rport_id, DEFAULT_DEV_LOSS_TMO); +- mpp->dev_loss = DEFAULT_DEV_LOSS_TMO; ++ pp->dev_loss = DEFAULT_DEV_LOSS_TMO; + } +- if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { +- if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF) ++ if (pp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { ++ if (pp->fast_io_fail == MP_FAST_IO_FAIL_OFF) + sprintf(value, "off"); +- else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) ++ else if (pp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) + sprintf(value, "0"); + else +- snprintf(value, 16, "%u", mpp->fast_io_fail); ++ snprintf(value, 16, "%u", pp->fast_io_fail); + ret = sysfs_attr_set_value(rport_dev, "fast_io_fail_tmo", + value, strlen(value)); + if (ret <= 0) { +@@ -709,8 +712,8 @@ sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) + rport_id, value, -ret); + } + } +- if (mpp->dev_loss > 0) { +- snprintf(value, 16, "%u", mpp->dev_loss); ++ if (pp->dev_loss > 0) { ++ snprintf(value, 16, "%u", pp->dev_loss); + ret = sysfs_attr_set_value(rport_dev, "dev_loss_tmo", + value, strlen(value)); + if (ret <= 0) { +@@ -726,7 +729,7 @@ out: + } + + static void +-sysfs_set_session_tmo(struct multipath *mpp, struct path *pp) ++sysfs_set_session_tmo(struct path *pp) + { + struct udev_device *session_dev = NULL; + char session_id[64]; +@@ -743,18 +746,18 @@ sysfs_set_session_tmo(struct multipath *mpp, struct path *pp) + condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no, + pp->sg_id.channel, pp->sg_id.scsi_id, session_id); + +- if (mpp->dev_loss) { ++ if (pp->dev_loss) { + condlog(3, "%s: ignoring dev_loss_tmo on iSCSI", pp->dev); + } +- if (mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { +- if (mpp->fast_io_fail == MP_FAST_IO_FAIL_OFF) { ++ if (pp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { ++ if (pp->fast_io_fail == MP_FAST_IO_FAIL_OFF) { + condlog(3, "%s: can't switch off fast_io_fail_tmo " + "on iSCSI", pp->dev); +- } else if (mpp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) { ++ } else if (pp->fast_io_fail == MP_FAST_IO_FAIL_ZERO) { + condlog(3, "%s: can't set fast_io_fail_tmo to '0'" + "on iSCSI", pp->dev); + } else { +- snprintf(value, 11, "%u", mpp->fast_io_fail); ++ snprintf(value, 11, "%u", pp->fast_io_fail); + if (sysfs_attr_set_value(session_dev, "recovery_tmo", + value, strlen(value)) <= 0) { + condlog(3, "%s: Failed to set recovery_tmo, " +@@ -767,12 +770,15 @@ sysfs_set_session_tmo(struct multipath *mpp, struct path *pp) + } + + static void +-sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp) ++sysfs_set_nexus_loss_tmo(struct path *pp) + { + struct udev_device *sas_dev = NULL; + char end_dev_id[64]; + char value[11]; + ++ if (!pp->udev || !pp->dev_loss) ++ return; ++ + sprintf(end_dev_id, "end_device-%d:%d", + pp->sg_id.host_no, pp->sg_id.transport_id); + sas_dev = udev_device_new_from_subsystem_sysname(udev, +@@ -785,8 +791,8 @@ sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp) + condlog(4, "target%d:%d:%d -> %s", pp->sg_id.host_no, + pp->sg_id.channel, pp->sg_id.scsi_id, end_dev_id); + +- if (mpp->dev_loss) { +- snprintf(value, 11, "%u", mpp->dev_loss); ++ if (pp->dev_loss) { ++ snprintf(value, 11, "%u", pp->dev_loss); + if (sysfs_attr_set_value(sas_dev, "I_T_nexus_loss_timeout", + value, strlen(value)) <= 0) + condlog(3, "%s: failed to update " +@@ -798,53 +804,76 @@ sysfs_set_nexus_loss_tmo(struct multipath *mpp, struct path *pp) + } + + int +-sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint) ++sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp) + { + struct path *pp; + int i; +- unsigned int dev_loss_tmo = mpp->dev_loss; ++ unsigned int min_dev_loss = 0; ++ bool warn_dev_loss = false; ++ bool warn_fast_io_fail = false; + + if (mpp->no_path_retry > 0) { + uint64_t no_path_retry_tmo = +- (uint64_t)mpp->no_path_retry * checkint; ++ (uint64_t)mpp->no_path_retry * conf->checkint; + + if (no_path_retry_tmo > MAX_DEV_LOSS_TMO) +- no_path_retry_tmo = MAX_DEV_LOSS_TMO; +- if (no_path_retry_tmo > dev_loss_tmo) +- dev_loss_tmo = no_path_retry_tmo; +- condlog(3, "%s: update dev_loss_tmo to %u", +- mpp->alias, dev_loss_tmo); +- } else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE) { +- dev_loss_tmo = MAX_DEV_LOSS_TMO; +- condlog(3, "%s: update dev_loss_tmo to %u", +- mpp->alias, dev_loss_tmo); +- } +- mpp->dev_loss = dev_loss_tmo; +- if (mpp->dev_loss && mpp->fast_io_fail > 0 && +- (unsigned int)mpp->fast_io_fail >= mpp->dev_loss) { +- condlog(3, "%s: turning off fast_io_fail (%d is not smaller than dev_loss_tmo)", +- mpp->alias, mpp->fast_io_fail); +- mpp->fast_io_fail = MP_FAST_IO_FAIL_OFF; +- } +- if (!mpp->dev_loss && mpp->fast_io_fail == MP_FAST_IO_FAIL_UNSET && +- mpp->eh_deadline == EH_DEADLINE_UNSET) +- return 0; ++ min_dev_loss = MAX_DEV_LOSS_TMO; ++ else ++ min_dev_loss = no_path_retry_tmo; ++ } else if (mpp->no_path_retry == NO_PATH_RETRY_QUEUE) ++ min_dev_loss = MAX_DEV_LOSS_TMO; + + vector_foreach_slot(mpp->paths, pp, i) { ++ select_fast_io_fail(conf, pp); ++ select_dev_loss(conf, pp); ++ select_eh_deadline(conf, pp); ++ ++ if (!pp->dev_loss && pp->eh_deadline == EH_DEADLINE_UNSET && ++ pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET) ++ continue; ++ + if (pp->bus != SYSFS_BUS_SCSI) + continue; ++ sysfs_set_eh_deadline(pp); ++ ++ if (!pp->dev_loss && pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET) ++ continue; ++ ++ if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP && ++ pp->sg_id.proto_id != SCSI_PROTOCOL_ISCSI && ++ pp->sg_id.proto_id != SCSI_PROTOCOL_SAS) ++ continue; ++ ++ if (pp->dev_loss > 0 && pp->dev_loss < min_dev_loss) { ++ warn_dev_loss = true; ++ pp->dev_loss = min_dev_loss; ++ } ++ if (pp->dev_loss > 0 && pp->fast_io_fail > 0 && ++ (unsigned int)pp->fast_io_fail >= pp->dev_loss) { ++ warn_fast_io_fail = true; ++ pp->fast_io_fail = MP_FAST_IO_FAIL_OFF; ++ } + +- if (mpp->dev_loss || +- mpp->fast_io_fail != MP_FAST_IO_FAIL_UNSET) { +- if (pp->sg_id.proto_id == SCSI_PROTOCOL_FCP) +- sysfs_set_rport_tmo(mpp, pp); +- else if (pp->sg_id.proto_id == SCSI_PROTOCOL_ISCSI) +- sysfs_set_session_tmo(mpp, pp); +- else if (pp->sg_id.proto_id == SCSI_PROTOCOL_SAS) +- sysfs_set_nexus_loss_tmo(mpp, pp); ++ switch (pp->sg_id.proto_id) { ++ case SCSI_PROTOCOL_FCP: ++ sysfs_set_rport_tmo(mpp, pp); ++ break; ++ case SCSI_PROTOCOL_ISCSI: ++ sysfs_set_session_tmo(pp); ++ break; ++ case SCSI_PROTOCOL_SAS: ++ sysfs_set_nexus_loss_tmo(pp); ++ break; ++ default: ++ break; + } +- sysfs_set_eh_deadline(mpp, pp); + } ++ if (warn_dev_loss) ++ condlog(2, "%s: Raising dev_loss_tmo to %u because of no_path_retry setting", ++ mpp->alias, min_dev_loss); ++ if (warn_fast_io_fail) ++ condlog(3, "%s: turning off fast_io_fail (not smaller than dev_loss_tmo)", ++ mpp->alias); + return 0; + } + +diff --git a/libmultipath/discovery.h b/libmultipath/discovery.h +index a5446b4d..b6eea258 100644 +--- a/libmultipath/discovery.h ++++ b/libmultipath/discovery.h +@@ -42,7 +42,7 @@ int alloc_path_with_pathinfo (struct config *conf, struct udev_device *udevice, + int store_pathinfo (vector pathvec, struct config *conf, + struct udev_device *udevice, int flag, + struct path **pp_ptr); +-int sysfs_set_scsi_tmo (struct multipath *mpp, unsigned int checkint); ++int sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp); + int sysfs_get_timeout(const struct path *pp, unsigned int *timeout); + int sysfs_get_host_pci_name(const struct path *pp, char *pci_name); + int sysfs_get_iscsi_ip_address(const struct path *pp, char *ip_address); +diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c +index 127b3370..25326eb6 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -766,51 +766,51 @@ int select_minio(struct config *conf, struct multipath *mp) + return select_minio_bio(conf, mp); + } + +-int select_fast_io_fail(struct config *conf, struct multipath *mp) ++int select_fast_io_fail(struct config *conf, struct path *pp) + { + const char *origin; + char buff[12]; + +- mp_set_ovr(fast_io_fail); +- mp_set_hwe(fast_io_fail); +- mp_set_conf(fast_io_fail); +- mp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL); ++ pp_set_ovr(fast_io_fail); ++ pp_set_hwe(fast_io_fail); ++ pp_set_conf(fast_io_fail); ++ pp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL); + out: +- print_undef_off_zero(buff, 12, mp->fast_io_fail); +- condlog(3, "%s: fast_io_fail_tmo = %s %s", mp->alias, buff, origin); ++ print_undef_off_zero(buff, 12, pp->fast_io_fail); ++ condlog(3, "%s: fast_io_fail_tmo = %s %s", pp->dev, buff, origin); + return 0; + } + +-int select_dev_loss(struct config *conf, struct multipath *mp) ++int select_dev_loss(struct config *conf, struct path *pp) + { + const char *origin; + char buff[12]; + +- mp_set_ovr(dev_loss); +- mp_set_hwe(dev_loss); +- mp_set_conf(dev_loss); +- mp->dev_loss = 0; ++ pp_set_ovr(dev_loss); ++ pp_set_hwe(dev_loss); ++ pp_set_conf(dev_loss); ++ pp->dev_loss = 0; + return 0; + out: +- print_dev_loss(buff, 12, mp->dev_loss); +- condlog(3, "%s: dev_loss_tmo = %s %s", mp->alias, buff, origin); ++ print_dev_loss(buff, 12, pp->dev_loss); ++ condlog(3, "%s: dev_loss_tmo = %s %s", pp->dev, buff, origin); + return 0; + } + +-int select_eh_deadline(struct config *conf, struct multipath *mp) ++int select_eh_deadline(struct config *conf, struct path *pp) + { + const char *origin; + char buff[12]; + +- mp_set_ovr(eh_deadline); +- mp_set_hwe(eh_deadline); +- mp_set_conf(eh_deadline); +- mp->eh_deadline = EH_DEADLINE_UNSET; ++ pp_set_ovr(eh_deadline); ++ pp_set_hwe(eh_deadline); ++ pp_set_conf(eh_deadline); ++ pp->eh_deadline = EH_DEADLINE_UNSET; + /* not changing sysfs in default cause, so don't print anything */ + return 0; + out: +- print_undef_off_zero(buff, 12, mp->eh_deadline); +- condlog(3, "%s: eh_deadline = %s %s", mp->alias, buff, origin); ++ print_undef_off_zero(buff, 12, pp->eh_deadline); ++ condlog(3, "%s: eh_deadline = %s %s", pp->dev, buff, origin); + return 0; + } + +diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h +index 72a7e33c..152ca44c 100644 +--- a/libmultipath/propsel.h ++++ b/libmultipath/propsel.h +@@ -16,9 +16,9 @@ int select_minio(struct config *conf, struct multipath *mp); + int select_mode(struct config *conf, struct multipath *mp); + int select_uid(struct config *conf, struct multipath *mp); + int select_gid(struct config *conf, struct multipath *mp); +-int select_fast_io_fail(struct config *conf, struct multipath *mp); +-int select_dev_loss(struct config *conf, struct multipath *mp); +-int select_eh_deadline(struct config *conf, struct multipath *mp); ++int select_fast_io_fail(struct config *conf, struct path *pp); ++int select_dev_loss(struct config *conf, struct path *pp); ++int select_eh_deadline(struct config *conf, struct path *pp); + int select_reservation_key(struct config *conf, struct multipath *mp); + int select_retain_hwhandler (struct config *conf, struct multipath * mp); + int select_detect_prio(struct config *conf, struct path * pp); +diff --git a/libmultipath/structs.c b/libmultipath/structs.c +index 19099bed..9f86eb69 100644 +--- a/libmultipath/structs.c ++++ b/libmultipath/structs.c +@@ -230,7 +230,6 @@ alloc_multipath (void) + mpp->bestpg = 1; + mpp->mpcontext = NULL; + mpp->no_path_retry = NO_PATH_RETRY_UNDEF; +- mpp->fast_io_fail = MP_FAST_IO_FAIL_UNSET; + dm_multipath_to_gen(mpp)->ops = &dm_gen_multipath_ops; + } + return mpp; +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index ab99852f..875e726e 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -344,6 +344,9 @@ struct path { + int marginal; + int vpd_vendor_id; + int recheck_wwid; ++ int fast_io_fail; ++ unsigned int dev_loss; ++ int eh_deadline; + /* configlet pointers */ + vector hwe; + struct gen_path generic_path; +@@ -371,7 +374,6 @@ struct multipath { + int minio; + int flush_on_last_del; + int attribute_flags; +- int fast_io_fail; + int retain_hwhandler; + int deferred_remove; + bool in_recovery; +@@ -389,8 +391,6 @@ struct multipath { + int needs_paths_uevent; + int ghost_delay; + int ghost_delay_tick; +- unsigned int dev_loss; +- int eh_deadline; + uid_t uid; + gid_t gid; + mode_t mode; diff --git a/SOURCES/0101-libmultipath-check-the-overrides-pctable-for-path-va.patch b/SOURCES/0101-libmultipath-check-the-overrides-pctable-for-path-va.patch new file mode 100644 index 0000000..369ab5a --- /dev/null +++ b/SOURCES/0101-libmultipath-check-the-overrides-pctable-for-path-va.patch @@ -0,0 +1,84 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 13 Apr 2022 23:27:39 -0500 +Subject: [PATCH] libmultipath: check the overrides pctable for path variables + +Paths will now also check the pctable when checking for attribtes that +exists in both the overrides section and the protocol subsection. Values +in a matching pcentry will be used in preference to values in the +overrides hwentry. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/propsel.c | 29 ++++++++++++++++++++++++++--- + 1 file changed, 26 insertions(+), 3 deletions(-) + +diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c +index 25326eb6..209c1d67 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -79,6 +79,8 @@ static const char conf_origin[] = + "(setting: multipath.conf defaults/devices section)"; + static const char overrides_origin[] = + "(setting: multipath.conf overrides section)"; ++static const char overrides_pce_origin[] = ++ "(setting: multipath.conf overrides protocol section)"; + static const char cmdline_origin[] = + "(setting: multipath command line [-p] flag)"; + static const char autodetect_origin[] = +@@ -144,6 +146,27 @@ do { \ + } \ + } while (0) + ++#define pp_set_ovr_pce(var) \ ++do { \ ++ struct pcentry *_pce; \ ++ int _i; \ ++ \ ++ if (conf->overrides) { \ ++ vector_foreach_slot(conf->overrides->pctable, _pce, _i) { \ ++ if (_pce->type == (int)bus_protocol_id(pp) && _pce->var) { \ ++ pp->var = _pce->var; \ ++ origin = overrides_pce_origin; \ ++ goto out; \ ++ } \ ++ } \ ++ if (conf->overrides->var) { \ ++ pp->var = conf->overrides->var; \ ++ origin = overrides_origin; \ ++ goto out; \ ++ } \ ++ } \ ++} while (0) ++ + int select_mode(struct config *conf, struct multipath *mp) + { + const char *origin; +@@ -771,7 +794,7 @@ int select_fast_io_fail(struct config *conf, struct path *pp) + const char *origin; + char buff[12]; + +- pp_set_ovr(fast_io_fail); ++ pp_set_ovr_pce(fast_io_fail); + pp_set_hwe(fast_io_fail); + pp_set_conf(fast_io_fail); + pp_set_default(fast_io_fail, DEFAULT_FAST_IO_FAIL); +@@ -786,7 +809,7 @@ int select_dev_loss(struct config *conf, struct path *pp) + const char *origin; + char buff[12]; + +- pp_set_ovr(dev_loss); ++ pp_set_ovr_pce(dev_loss); + pp_set_hwe(dev_loss); + pp_set_conf(dev_loss); + pp->dev_loss = 0; +@@ -802,7 +825,7 @@ int select_eh_deadline(struct config *conf, struct path *pp) + const char *origin; + char buff[12]; + +- pp_set_ovr(eh_deadline); ++ pp_set_ovr_pce(eh_deadline); + pp_set_hwe(eh_deadline); + pp_set_conf(eh_deadline); + pp->eh_deadline = EH_DEADLINE_UNSET; diff --git a/SOURCES/0102-libmultipath-fix-eh_deadline-documentation.patch b/SOURCES/0102-libmultipath-fix-eh_deadline-documentation.patch new file mode 100644 index 0000000..167590b --- /dev/null +++ b/SOURCES/0102-libmultipath-fix-eh_deadline-documentation.patch @@ -0,0 +1,33 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 13 Apr 2022 23:27:40 -0500 +Subject: [PATCH] libmultipath: fix eh_deadline documentation + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + multipath/multipath.conf.5 | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 0c04c7e4..b14fd215 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -1602,6 +1602,8 @@ section: + .TP + .B dev_loss_tmo + .TP ++.B eh_deadline ++.TP + .B flush_on_last_del + .TP + .B user_friendly_names +@@ -1688,6 +1690,8 @@ the values are taken from the \fIdevices\fR or \fIdefaults\fR sections: + .TP + .B dev_loss_tmo + .TP ++.B eh_deadline ++.TP + .B user_friendly_names + .TP + .B retain_attached_hw_handler diff --git a/SOURCES/0103-libmultipath-Add-documentation-for-the-protocol-subs.patch b/SOURCES/0103-libmultipath-Add-documentation-for-the-protocol-subs.patch new file mode 100644 index 0000000..114f02e --- /dev/null +++ b/SOURCES/0103-libmultipath-Add-documentation-for-the-protocol-subs.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 13 Apr 2022 23:27:41 -0500 +Subject: [PATCH] libmultipath: Add documentation for the protocol subsection + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + multipath/multipath.conf.5 | 32 ++++++++++++++++++++++++++++++++ + 1 file changed, 32 insertions(+) + +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index b14fd215..abbc89af 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -1730,6 +1730,38 @@ the values are taken from the \fIdevices\fR or \fIdefaults\fR sections: + .RE + .PD + .LP ++The overrides section also recognizes the optional \fIprotocol\fR subsection, ++and can contain multiple protocol subsections. Path devices are matched against ++the protocol subsection using the mandatory \fItype\fR attribute. Attributes ++in a matching protocol subsection take precedence over attributes in the rest ++of the overrides section. If there are multiple matching protocol subsections, ++later entries take precedence. ++.TP ++.B protocol subsection ++The protocol subsection recognizes the following mandatory attribute: ++.RS ++.TP ++.B type ++The protocol string of the path device. The possible values are \fIscsi:fcp\fR, ++\fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR, ++\fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR, ++\fIscsi:unspec\fR, \fIccw\fR, \fIcciss\fR, \fInvme\fR, and \fIundef\fR. This is ++\fBnot\fR a regular expression. the path device protcol string must match ++exactly. The protocol that a path is using can be viewed by running ++\fBmultipathd show paths format "%d %P"\fR ++.LP ++The following attributes are optional; if not set, the default values are taken ++from the \fIoverrides\fR, \fIdevices\fR, or \fIdefaults\fR section: ++.sp 1 ++.PD .1v ++.RS ++.TP ++.B fast_io_fail_tmo ++.TP ++.B dev_loss_tmo ++.TP ++.B eh_deadline ++.PD + . + . + .\" ---------------------------------------------------------------------------- diff --git a/SOURCES/0104-libmultipath-use-symbolic-value-for-invalid-pcentry.patch b/SOURCES/0104-libmultipath-use-symbolic-value-for-invalid-pcentry.patch new file mode 100644 index 0000000..da6eb9d --- /dev/null +++ b/SOURCES/0104-libmultipath-use-symbolic-value-for-invalid-pcentry.patch @@ -0,0 +1,47 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 14 Apr 2022 16:11:10 -0500 +Subject: [PATCH] libmultipath: use symbolic value for invalid pcentry + +Suggested-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/config.c | 4 ++-- + libmultipath/config.h | 1 + + 2 files changed, 3 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/config.c b/libmultipath/config.c +index 88975323..bcd8b541 100644 +--- a/libmultipath/config.c ++++ b/libmultipath/config.c +@@ -319,7 +319,7 @@ alloc_pce (void) + { + struct pcentry *pce = (struct pcentry *) + calloc(1, sizeof(struct pcentry)); +- pce->type = -1; ++ pce->type = PCE_INVALID; + return pce; + } + +@@ -578,7 +578,7 @@ validate_pctable(struct hwentry *ovr, int idx, const char *table_desc) + return; + + vector_foreach_slot_after(ovr->pctable, pce, idx) { +- if (pce->type < 0) { ++ if (pce->type == PCE_INVALID) { + condlog(0, "protocol section in %s missing type", + table_desc); + vector_del_slot(ovr->pctable, idx--); +diff --git a/libmultipath/config.h b/libmultipath/config.h +index 143116b3..477f8bfa 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -41,6 +41,7 @@ enum force_reload_types { + FORCE_RELOAD_WEAK, + }; + ++#define PCE_INVALID -1 + struct pcentry { + int type; + int fast_io_fail; diff --git a/SOURCES/0105-multipathd-handle-fpin-events.patch b/SOURCES/0105-multipathd-handle-fpin-events.patch new file mode 100644 index 0000000..e8fa26a --- /dev/null +++ b/SOURCES/0105-multipathd-handle-fpin-events.patch @@ -0,0 +1,1037 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Muneendra Kumar +Date: Wed, 9 Feb 2022 19:28:10 -0800 +Subject: [PATCH] multipathd: handle fpin events + +This patch incorporates the functionality to handle +FPIN ELS events present as part of FCTransport daemon +(available in EPEL8) into the multipathd. This helps us to +reduce the response time to react and take the necessary actions +on receiving the FPIN events. + +This patch currently support FPIN-Li Events. + +It adds a new thread to listen for ELS frames from driver and on +receiving the frame payload, push the payload to a list and notify the +fpin_els_li_consumer thread to process it.Once consumer thread is +notified, it returns to listen for more ELS frames from driver. + +The consumer thread process the ELS frames and moves the devices paths +which are affected due to link integrity to marginal path groups. +This also sets the associated portstate to marginal. +The paths which are set to marginal path group will be unset +on receiving the RSCN events + +[ MW: minor fixup for 32bit compilation ] + +Signed-off-by: Muneendra Kumar +Signed-off-by: Benjamin Marzinski +Signed-off-by: Martin Wilck +Reviewed-by: Martin Wilck +--- + Makefile.inc | 13 + + libmultipath/Makefile | 5 + + libmultipath/dict.c | 56 +++- + libmultipath/propsel.c | 47 +++- + libmultipath/structs.h | 7 + + multipath/multipath.conf.5 | 19 +- + multipathd/Makefile | 10 + + multipathd/fpin.h | 20 ++ + multipathd/fpin_handlers.c | 541 +++++++++++++++++++++++++++++++++++++ + multipathd/main.c | 35 ++- + 10 files changed, 738 insertions(+), 15 deletions(-) + create mode 100644 multipathd/fpin.h + create mode 100644 multipathd/fpin_handlers.c + +diff --git a/Makefile.inc b/Makefile.inc +index 220009e0..25c16f4e 100644 +--- a/Makefile.inc ++++ b/Makefile.inc +@@ -146,6 +146,19 @@ check_file = $(shell \ + echo "$$found" \ + ) + ++# Check whether a file contains a variable with name $1 in header file $2 ++check_var = $(shell \ ++ if grep -Eq "(^|[[:blank:]])$1([[:blank:]]|=|$$)" "$2"; then \ ++ found=1; \ ++ status="yes"; \ ++ else \ ++ found=0; \ ++ status="no"; \ ++ fi; \ ++ echo 1>&2 "Checking for .. $1 in $2 ... $$status"; \ ++ echo "$$found" \ ++ ) ++ + %.o: %.c + @echo building $@ because of $? + $(CC) $(CFLAGS) -c -o $@ $< +diff --git a/libmultipath/Makefile b/libmultipath/Makefile +index ad690a49..49f71dfa 100644 +--- a/libmultipath/Makefile ++++ b/libmultipath/Makefile +@@ -40,6 +40,11 @@ ifneq ($(call check_func,dm_hold_control_dev,/usr/include/libdevmapper.h),0) + CFLAGS += -DLIBDM_API_HOLD_CONTROL + endif + ++ifneq ($(call check_var,ELS_DTAG_LNK_INTEGRITY,/usr/include/scsi/fc/fc_els.h),0) ++ CFLAGS += -DFPIN_EVENT_HANDLER ++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/dict.c b/libmultipath/dict.c +index 8321ec1e..d7cd94a5 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -579,6 +579,59 @@ snprint_def_find_multipaths(struct config *conf, char *buff, int len, + find_multipaths_optvals[conf->find_multipaths]); + } + ++static const char * const marginal_pathgroups_optvals[] = { ++ [MARGINAL_PATHGROUP_OFF] = "off", ++ [MARGINAL_PATHGROUP_ON] = "on", ++#ifdef FPIN_EVENT_HANDLER ++ [MARGINAL_PATHGROUP_FPIN] = "fpin", ++#endif ++}; ++ ++static int ++def_marginal_pathgroups_handler(struct config *conf, vector strvec, ++ const char *file, int line_nr) ++{ ++ char *buff; ++ unsigned int i; ++ ++ buff = set_value(strvec); ++ if (!buff) ++ return 1; ++ for (i = MARGINAL_PATHGROUP_OFF; ++ i < ARRAY_SIZE(marginal_pathgroups_optvals); i++) { ++ if (marginal_pathgroups_optvals[i] != NULL && ++ !strcmp(buff, marginal_pathgroups_optvals[i])) { ++ conf->marginal_pathgroups = i; ++ break; ++ } ++ } ++ ++ if (i >= ARRAY_SIZE(marginal_pathgroups_optvals)) { ++ if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0) ++ conf->marginal_pathgroups = MARGINAL_PATHGROUP_OFF; ++ else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) ++ conf->marginal_pathgroups = MARGINAL_PATHGROUP_ON; ++ /* This can only be true if FPIN_EVENT_HANDLER isn't defined, ++ * otherwise this check will have already happened above */ ++ else if (strcmp(buff, "fpin") == 0) ++ condlog(1, "%s line %d, support for \"fpin\" is not compiled in for marginal_pathgroups", file, line_nr); ++ else ++ condlog(1, "%s line %d, invalid value for marginal_pathgroups: \"%s\"", ++ file, line_nr, buff); ++ } ++ free(buff); ++ return 0; ++} ++ ++static int ++snprint_def_marginal_pathgroups(struct config *conf, char *buff, int len, ++ const void *data) ++{ ++ return snprintf(buff, len, "\"%s\"", ++ marginal_pathgroups_optvals[conf->marginal_pathgroups]); ++} ++ ++ + declare_def_handler(selector, set_str) + declare_def_snprint_defstr(selector, print_str, DEFAULT_SELECTOR) + declare_hw_handler(selector, set_str) +@@ -1596,9 +1649,6 @@ declare_ovr_snprint(all_tg_pt, print_yes_no_undef) + declare_hw_handler(all_tg_pt, set_yes_no_undef) + declare_hw_snprint(all_tg_pt, print_yes_no_undef) + +-declare_def_handler(marginal_pathgroups, set_yes_no) +-declare_def_snprint(marginal_pathgroups, print_yes_no) +- + declare_def_handler(recheck_wwid, set_yes_no_undef) + declare_def_snprint_defint(recheck_wwid, print_yes_no_undef, DEFAULT_RECHECK_WWID) + declare_ovr_handler(recheck_wwid, set_yes_no_undef) +diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c +index 209c1d67..be79902f 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -85,6 +85,8 @@ static const char cmdline_origin[] = + "(setting: multipath command line [-p] flag)"; + static const char autodetect_origin[] = + "(setting: storage device autodetected)"; ++static const char fpin_marginal_path_origin[] = ++ "(setting: overridden by marginal_path_fpin)"; + static const char marginal_path_origin[] = + "(setting: implied by marginal_path check)"; + static const char delay_watch_origin[] = +@@ -1052,9 +1054,12 @@ int select_san_path_err_threshold(struct config *conf, struct multipath *mp) + const char *origin; + char buff[12]; + +- if (marginal_path_check_enabled(mp)) { ++ if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) { + mp->san_path_err_threshold = NU_NO; +- origin = marginal_path_origin; ++ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) ++ origin = fpin_marginal_path_origin; ++ else ++ origin = marginal_path_origin; + goto out; + } + mp_set_mpe(san_path_err_threshold); +@@ -1075,9 +1080,12 @@ int select_san_path_err_forget_rate(struct config *conf, struct multipath *mp) + const char *origin; + char buff[12]; + +- if (marginal_path_check_enabled(mp)) { ++ if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) { + mp->san_path_err_forget_rate = NU_NO; +- origin = marginal_path_origin; ++ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) ++ origin = fpin_marginal_path_origin; ++ else ++ origin = marginal_path_origin; + goto out; + } + mp_set_mpe(san_path_err_forget_rate); +@@ -1099,9 +1107,12 @@ int select_san_path_err_recovery_time(struct config *conf, struct multipath *mp) + const char *origin; + char buff[12]; + +- if (marginal_path_check_enabled(mp)) { ++ if (marginal_path_check_enabled(mp) || (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) { + mp->san_path_err_recovery_time = NU_NO; +- origin = marginal_path_origin; ++ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) ++ origin = fpin_marginal_path_origin; ++ else ++ origin = marginal_path_origin; + goto out; + } + mp_set_mpe(san_path_err_recovery_time); +@@ -1123,6 +1134,12 @@ int select_marginal_path_err_sample_time(struct config *conf, struct multipath * + const char *origin; + char buff[12]; + ++ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) { ++ mp->marginal_path_err_sample_time = NU_NO; ++ origin = fpin_marginal_path_origin; ++ goto out; ++ } ++ + mp_set_mpe(marginal_path_err_sample_time); + mp_set_ovr(marginal_path_err_sample_time); + mp_set_hwe(marginal_path_err_sample_time); +@@ -1141,6 +1158,12 @@ int select_marginal_path_err_rate_threshold(struct config *conf, struct multipat + const char *origin; + char buff[12]; + ++ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) { ++ mp->marginal_path_err_rate_threshold = NU_NO; ++ origin = fpin_marginal_path_origin; ++ goto out; ++ } ++ + mp_set_mpe(marginal_path_err_rate_threshold); + mp_set_ovr(marginal_path_err_rate_threshold); + mp_set_hwe(marginal_path_err_rate_threshold); +@@ -1159,6 +1182,12 @@ int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multip + const char *origin; + char buff[12]; + ++ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) { ++ mp->marginal_path_err_recheck_gap_time = NU_NO; ++ origin = fpin_marginal_path_origin; ++ goto out; ++ } ++ + mp_set_mpe(marginal_path_err_recheck_gap_time); + mp_set_ovr(marginal_path_err_recheck_gap_time); + mp_set_hwe(marginal_path_err_recheck_gap_time); +@@ -1177,6 +1206,12 @@ int select_marginal_path_double_failed_time(struct config *conf, struct multipat + const char *origin; + char buff[12]; + ++ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) { ++ mp->marginal_path_double_failed_time = NU_NO; ++ origin = fpin_marginal_path_origin; ++ goto out; ++ } ++ + mp_set_mpe(marginal_path_double_failed_time); + mp_set_ovr(marginal_path_double_failed_time); + mp_set_hwe(marginal_path_double_failed_time); +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 875e726e..3ed5cfc1 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -128,6 +128,12 @@ enum find_multipaths_states { + __FIND_MULTIPATHS_LAST, + }; + ++enum marginal_pathgroups_mode { ++ MARGINAL_PATHGROUP_OFF = YN_NO, ++ MARGINAL_PATHGROUP_ON = YN_YES, ++ MARGINAL_PATHGROUP_FPIN, ++}; ++ + enum flush_states { + FLUSH_UNDEF = YNU_UNDEF, + FLUSH_DISABLED = YNU_NO, +@@ -429,6 +435,7 @@ struct multipath { + unsigned char prflag; + int all_tg_pt; + struct gen_multipath generic_mp; ++ bool fpin_must_reload; + }; + + static inline int marginal_path_check_enabled(const struct multipath *mpp) +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index abbc89af..805b7a5e 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -1063,20 +1063,26 @@ The default is: \fBno\fR + . + .TP + .B marginal_pathgroups +-If set to \fIno\fR, the \fIdelay_*_checks\fR, \fImarginal_path_*\fR, and ++If set to \fIoff\fR, the \fIdelay_*_checks\fR, \fImarginal_path_*\fR, and + \fIsan_path_err_*\fR options will keep marginal, or \(dqshaky\(dq, paths from + being reinstated until they have been monitored for some time. This can cause + situations where all non-marginal paths are down, and no paths are usable + until multipathd detects this and reinstates a marginal path. If the multipath + device is not configured to queue IO in this case, it can cause IO errors to + occur, even though there are marginal paths available. However, if this +-option is set to \fIyes\fR, when one of the marginal path detecting methods ++option is set to \fIon\fR, when one of the marginal path detecting methods + determines that a path is marginal, it will be reinstated and placed in a + seperate pathgroup that will only be used after all the non-marginal pathgroups + have been tried first. This prevents the possibility of IO errors occuring + while marginal paths are still usable. After the path has been monitored + for the configured time, and is declared healthy, it will be returned to its +-normal pathgroup. See "Shaky paths detection" below for more information. ++normal pathgroup. ++However if this option is set to \fIfpin\fR multipathd will receive fpin ++notifications, set path states to "marginal" accordingly, and regroup paths ++as described for "marginal_pathgroups yes". This option can't be used in combination ++with other options for "Shaky path detection" (see below).If it is set to fpin, ++marginal_path_xyz and san_path_err_xyz parameters are implicitly set to 0. ++See "Shaky paths detection" below for more information. + .RS + .TP + The default is: \fBno\fR +@@ -1852,6 +1858,13 @@ increase and the threshold is never reached. Ticks are the time between + path checks by multipathd, which is variable and controlled by the + \fIpolling_interval\fR and \fImax_polling_interval\fR parameters. + . ++.TP ++.B \(dqFPIN \(dq failure tracking ++Fibre channel fabrics can notify hosts about fabric-level issues such ++as integrity failures or congestion with so-called Fabric Performance ++Impact Notifications (FPINs).On receiving the fpin notifications through ELS ++multipathd will move the affected path and port states to marginal. ++. + .RS 8 + .LP + This method is \fBdeprecated\fR in favor of the \(dqmarginal_path\(dq failure +diff --git a/multipathd/Makefile b/multipathd/Makefile +index 8d901178..835edd93 100644 +--- a/multipathd/Makefile ++++ b/multipathd/Makefile +@@ -1,5 +1,9 @@ + include ../Makefile.inc + ++ifneq ($(call check_var,ELS_DTAG_LNK_INTEGRITY,/usr/include/scsi/fc/fc_els.h),0) ++ CFLAGS += -DFPIN_EVENT_HANDLER ++ FPIN_SUPPORT = 1 ++endif + # + # debugging stuff + # +@@ -28,6 +32,12 @@ endif + OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \ + dmevents.o + ++ifeq ($(FPIN_SUPPORT),1) ++OBJS += fpin_handlers.o ++endif ++ ++ ++ + EXEC = multipathd + + all : $(EXEC) +diff --git a/multipathd/fpin.h b/multipathd/fpin.h +new file mode 100644 +index 00000000..bfcc1ce2 +--- /dev/null ++++ b/multipathd/fpin.h +@@ -0,0 +1,20 @@ ++#ifndef __FPIN_H__ ++#define __FPIN_H__ ++ ++#ifdef FPIN_EVENT_HANDLER ++void *fpin_fabric_notification_receiver(void *unused); ++void *fpin_els_li_consumer(void *data); ++void fpin_clean_marginal_dev_list(__attribute__((unused)) void *arg); ++#else ++static void *fpin_fabric_notification_receiver(__attribute__((unused))void *unused) ++{ ++ return NULL; ++} ++static void *fpin_els_li_consumer(__attribute__((unused))void *data) ++{ ++ return NULL; ++} ++/* fpin_clean_marginal_dev_list() is never called */ ++#endif ++ ++#endif +diff --git a/multipathd/fpin_handlers.c b/multipathd/fpin_handlers.c +new file mode 100644 +index 00000000..b14366d7 +--- /dev/null ++++ b/multipathd/fpin_handlers.c +@@ -0,0 +1,541 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "parser.h" ++#include "vector.h" ++#include "structs.h" ++#include "structs_vec.h" ++#include "main.h" ++#include "debug.h" ++#include "util.h" ++#include "sysfs.h" ++ ++#include "fpin.h" ++#include "devmapper.h" ++ ++static pthread_cond_t fpin_li_cond = PTHREAD_COND_INITIALIZER; ++static pthread_mutex_t fpin_li_mutex = PTHREAD_MUTEX_INITIALIZER; ++static pthread_mutex_t fpin_li_marginal_dev_mutex = PTHREAD_MUTEX_INITIALIZER; ++ ++static LIST_HEAD(els_marginal_list_head); ++static LIST_HEAD(fpin_li_marginal_dev_list_head); ++ ++ ++#define DEF_RX_BUF_SIZE 4096 ++#define DEV_NAME_LEN 128 ++#define FCH_EVT_LINKUP 0x2 ++#define FCH_EVT_LINK_FPIN 0x501 ++#define FCH_EVT_RSCN 0x5 ++ ++#define list_first_entry(ptr, type, member) \ ++ list_entry((ptr)->next, type, member) ++ ++/* max ELS frame Size */ ++#define FC_PAYLOAD_MAXLEN 2048 ++ ++struct els_marginal_list { ++ uint32_t event_code; ++ uint16_t host_num; ++ uint16_t length; ++ char payload[FC_PAYLOAD_MAXLEN]; ++ struct list_head node; ++}; ++/* Structure to store the marginal devices info */ ++struct marginal_dev_list { ++ char dev_t[BLK_DEV_SIZE]; ++ uint32_t host_num; ++ struct list_head node; ++}; ++ ++static void _udev_device_unref(void *p) ++{ ++ udev_device_unref(p); ++} ++ ++ ++/*set/unset the path state to marginal*/ ++static int fpin_set_pathstate(struct path *pp, bool set) ++{ ++ const char *action = set ? "set" : "unset"; ++ ++ if (!pp || !pp->mpp || !pp->mpp->alias) ++ return -1; ++ ++ condlog(3, "\n%s: %s marginal path %s (fpin)", ++ action, pp->mpp->alias, pp->dev_t); ++ pp->marginal = set; ++ pp->mpp->fpin_must_reload = true; ++ return 0; ++} ++ ++/* This will unset marginal state of a device*/ ++static void fpin_path_unsetmarginal(char *devname, struct vectors *vecs) ++{ ++ struct path *pp; ++ ++ pp = find_path_by_dev(vecs->pathvec, devname); ++ if (!pp) ++ pp = find_path_by_devt(vecs->pathvec, devname); ++ ++ fpin_set_pathstate(pp, false); ++} ++ ++/*This will set the marginal state of a device*/ ++static int fpin_path_setmarginal(struct path *pp) ++{ ++ return fpin_set_pathstate(pp, true); ++} ++ ++/* Unsets all the devices in the list from marginal state */ ++static void ++fpin_unset_marginal_dev(uint32_t host_num, struct vectors *vecs) ++{ ++ struct marginal_dev_list *tmp_marg = NULL; ++ struct marginal_dev_list *marg = NULL; ++ struct multipath *mpp; ++ int ret = 0; ++ int i; ++ ++ pthread_cleanup_push(cleanup_lock, &vecs->lock); ++ lock(&vecs->lock); ++ pthread_testcancel(); ++ ++ pthread_mutex_lock(&fpin_li_marginal_dev_mutex); ++ pthread_cleanup_push(cleanup_mutex, &fpin_li_marginal_dev_mutex); ++ pthread_testcancel(); ++ if (list_empty(&fpin_li_marginal_dev_list_head)) { ++ condlog(4, "Marginal List is empty\n"); ++ goto empty; ++ } ++ list_for_each_entry_safe(marg, tmp_marg, &fpin_li_marginal_dev_list_head, node) { ++ if (marg->host_num != host_num) ++ continue; ++ condlog(4, " unsetting marginal dev: is %s %d\n", ++ tmp_marg->dev_t, tmp_marg->host_num); ++ fpin_path_unsetmarginal(marg->dev_t, vecs); ++ list_del(&marg->node); ++ free(marg); ++ } ++empty: ++ pthread_cleanup_pop(1); ++ /* walk backwards because update_path_groups() can remove mpp */ ++ vector_foreach_slot_backwards(vecs->mpvec, mpp, i) { ++ if (mpp->fpin_must_reload) { ++ ret = update_path_groups(mpp, vecs, 0); ++ if (ret == 2) ++ condlog(2, "map removed during reload"); ++ else ++ mpp->fpin_must_reload = false; ++ } ++ } ++ pthread_cleanup_pop(1); ++} ++ ++/* ++ * On Receiving the frame from HBA driver, insert the frame into link ++ * integrity frame list which will be picked up later by consumer thread for ++ * processing. ++ */ ++static int ++fpin_els_add_li_frame(struct fc_nl_event *fc_event) ++{ ++ struct els_marginal_list *els_mrg = NULL; ++ int ret = 0; ++ ++ if (fc_event->event_datalen > FC_PAYLOAD_MAXLEN) ++ return -EINVAL; ++ ++ pthread_mutex_lock(&fpin_li_mutex); ++ pthread_cleanup_push(cleanup_mutex, &fpin_li_mutex); ++ pthread_testcancel(); ++ els_mrg = calloc(1, sizeof(struct els_marginal_list)); ++ if (els_mrg != NULL) { ++ els_mrg->host_num = fc_event->host_no; ++ els_mrg->event_code = fc_event->event_code; ++ els_mrg->length = fc_event->event_datalen; ++ memcpy(els_mrg->payload, &(fc_event->event_data), fc_event->event_datalen); ++ list_add_tail(&els_mrg->node, &els_marginal_list_head); ++ pthread_cond_signal(&fpin_li_cond); ++ } else ++ ret = -ENOMEM; ++ pthread_cleanup_pop(1); ++ return ret; ++ ++} ++ ++/*Sets the rport port_state to marginal*/ ++static void fpin_set_rport_marginal(struct udev_device *rport_dev) ++{ ++ sysfs_attr_set_value(rport_dev, "port_state", ++ "Marginal", strlen("Marginal")); ++} ++ ++/*Add the marginal devices info into the list*/ ++static void ++fpin_add_marginal_dev_info(uint32_t host_num, char *devname) ++{ ++ struct marginal_dev_list *newdev = NULL; ++ ++ newdev = calloc(1, sizeof(struct marginal_dev_list)); ++ if (newdev != NULL) { ++ newdev->host_num = host_num; ++ strlcpy(newdev->dev_t, devname, BLK_DEV_SIZE); ++ condlog(4, "\n%s hostno %d devname %s\n", __func__, ++ host_num, newdev->dev_t); ++ pthread_mutex_lock(&fpin_li_marginal_dev_mutex); ++ list_add_tail(&(newdev->node), ++ &fpin_li_marginal_dev_list_head); ++ pthread_mutex_unlock(&fpin_li_marginal_dev_mutex); ++ } ++} ++ ++/* ++ * This function goes through the vecs->pathvec, and for ++ * each path, check that the host number, ++ * the target WWPN associated with the path matches ++ * with the els wwpn and sets the path and port state to ++ * Marginal ++ */ ++static int fpin_chk_wwn_setpath_marginal(uint16_t host_num, struct vectors *vecs, ++ uint64_t els_wwpn) ++{ ++ struct path *pp; ++ struct multipath *mpp; ++ int i, k; ++ char rport_id[42]; ++ const char *value = NULL; ++ struct udev_device *rport_dev = NULL; ++ uint64_t wwpn; ++ int ret = 0; ++ ++ pthread_cleanup_push(cleanup_lock, &vecs->lock); ++ lock(&vecs->lock); ++ pthread_testcancel(); ++ ++ vector_foreach_slot(vecs->pathvec, pp, k) { ++ /* Checks the host number and also for the SCSI FCP */ ++ if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP || host_num != pp->sg_id.host_no) ++ continue; ++ sprintf(rport_id, "rport-%d:%d-%d", ++ pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id); ++ rport_dev = udev_device_new_from_subsystem_sysname(udev, ++ "fc_remote_ports", rport_id); ++ if (!rport_dev) { ++ condlog(2, "%s: No fc_remote_port device for '%s'", pp->dev, ++ rport_id); ++ continue; ++ } ++ pthread_cleanup_push(_udev_device_unref, rport_dev); ++ value = udev_device_get_sysattr_value(rport_dev, "port_name"); ++ if (!value) ++ goto unref; ++ ++ if (value) ++ wwpn = strtol(value, NULL, 16); ++ /* ++ * If the port wwpn matches sets the path and port state ++ * to marginal ++ */ ++ if (wwpn == els_wwpn) { ++ ret = fpin_path_setmarginal(pp); ++ if (ret < 0) ++ goto unref; ++ fpin_set_rport_marginal(rport_dev); ++ fpin_add_marginal_dev_info(host_num, pp->dev); ++ } ++unref: ++ pthread_cleanup_pop(1); ++ } ++ /* walk backwards because update_path_groups() can remove mpp */ ++ vector_foreach_slot_backwards(vecs->mpvec, mpp, i) { ++ if (mpp->fpin_must_reload) { ++ ret = update_path_groups(mpp, vecs, 0); ++ if (ret == 2) ++ condlog(2, "map removed during reload"); ++ else ++ mpp->fpin_must_reload = false; ++ } ++ } ++ pthread_cleanup_pop(1); ++ return ret; ++} ++ ++/* ++ * This function loops around all the impacted wwns received as part of els ++ * frame and sets the associated path and port states to marginal. ++ */ ++static int ++fpin_parse_li_els_setpath_marginal(uint16_t host_num, struct fc_tlv_desc *tlv, ++ struct vectors *vecs) ++{ ++ uint32_t wwn_count = 0, iter = 0; ++ uint64_t wwpn; ++ struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv; ++ int count = 0; ++ int ret = 0; ++ ++ /* Update the wwn to list */ ++ wwn_count = be32_to_cpu(li_desc->pname_count); ++ condlog(4, "Got wwn count as %d\n", wwn_count); ++ ++ for (iter = 0; iter < wwn_count; iter++) { ++ wwpn = be64_to_cpu(li_desc->pname_list[iter]); ++ ret = fpin_chk_wwn_setpath_marginal(host_num, vecs, wwpn); ++ if (ret < 0) ++ condlog(2, "failed to set the path marginal associated with wwpn: 0x%" PRIx64 "\n", wwpn); ++ ++ count++; ++ } ++ return count; ++} ++ ++/* ++ * This function process the ELS frame received from HBA driver, ++ * and sets the path associated with the port wwn to marginal ++ * and also set the port state to marginal. ++ */ ++static int ++fpin_process_els_frame(uint16_t host_num, char *fc_payload, struct vectors *vecs) ++{ ++ ++ int count = -1; ++ struct fc_els_fpin *fpin = (struct fc_els_fpin *)fc_payload; ++ struct fc_tlv_desc *tlv; ++ ++ tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0]; ++ ++ /* ++ * Parse the els frame and set the affected paths and port ++ * state to marginal ++ */ ++ count = fpin_parse_li_els_setpath_marginal(host_num, tlv, vecs); ++ if (count <= 0) ++ condlog(4, "Could not find any WWNs, ret = %d\n", ++ count); ++ return count; ++} ++ ++/* ++ * This function process the FPIN ELS frame received from HBA driver, ++ * and push the frame to appropriate frame list. Currently we have only FPIN ++ * LI frame list. ++ */ ++static int ++fpin_handle_els_frame(struct fc_nl_event *fc_event) ++{ ++ int ret = -1; ++ uint32_t els_cmd; ++ struct fc_els_fpin *fpin = (struct fc_els_fpin *)&fc_event->event_data; ++ struct fc_tlv_desc *tlv; ++ uint32_t dtag; ++ ++ els_cmd = (uint32_t)fc_event->event_data; ++ tlv = (struct fc_tlv_desc *)&fpin->fpin_desc[0]; ++ dtag = be32_to_cpu(tlv->desc_tag); ++ condlog(4, "Got CMD in add as 0x%x fpin_cmd 0x%x dtag 0x%x\n", ++ els_cmd, fpin->fpin_cmd, dtag); ++ ++ if ((fc_event->event_code == FCH_EVT_LINK_FPIN) || ++ (fc_event->event_code == FCH_EVT_LINKUP) || ++ (fc_event->event_code == FCH_EVT_RSCN)) { ++ ++ if (els_cmd == ELS_FPIN) { ++ /* ++ * Check the type of fpin by checking the tag info ++ * At present we are supporting only LI events ++ */ ++ if (dtag == ELS_DTAG_LNK_INTEGRITY) { ++ /*Push the Payload to FPIN frame queue. */ ++ ret = fpin_els_add_li_frame(fc_event); ++ if (ret != 0) ++ condlog(0, "Failed to process LI frame with error %d\n", ++ ret); ++ } else { ++ condlog(4, "Unsupported FPIN received 0x%x\n", dtag); ++ return ret; ++ } ++ } else { ++ /*Push the Payload to FPIN frame queue. */ ++ ret = fpin_els_add_li_frame(fc_event); ++ if (ret != 0) ++ condlog(0, "Failed to process Linkup/RSCN event with error %d evnt %d\n", ++ ret, fc_event->event_code); ++ } ++ } else ++ condlog(4, "Invalid command received: 0x%x\n", els_cmd); ++ return ret; ++} ++ ++/*cleans the global marginal dev list*/ ++void fpin_clean_marginal_dev_list(__attribute__((unused)) void *arg) ++{ ++ struct marginal_dev_list *tmp_marg = NULL; ++ ++ pthread_mutex_lock(&fpin_li_marginal_dev_mutex); ++ while (!list_empty(&fpin_li_marginal_dev_list_head)) { ++ tmp_marg = list_first_entry(&fpin_li_marginal_dev_list_head, ++ struct marginal_dev_list, node); ++ list_del(&tmp_marg->node); ++ free(tmp_marg); ++ } ++ pthread_mutex_unlock(&fpin_li_marginal_dev_mutex); ++} ++ ++/* Cleans the global els marginal list */ ++static void fpin_clean_els_marginal_list(void *arg) ++{ ++ struct list_head *head = (struct list_head *)arg; ++ struct els_marginal_list *els_marg; ++ ++ while (!list_empty(head)) { ++ els_marg = list_first_entry(head, struct els_marginal_list, ++ node); ++ list_del(&els_marg->node); ++ free(els_marg); ++ } ++} ++ ++static void rcu_unregister(__attribute__((unused)) void *param) ++{ ++ rcu_unregister_thread(); ++} ++/* ++ * This is the FPIN ELS consumer thread. The thread sleeps on pthread cond ++ * variable unless notified by fpin_fabric_notification_receiver thread. ++ * This thread is only to process FPIN-LI ELS frames. A new thread and frame ++ * list will be added if any more ELS frames types are to be supported. ++ */ ++void *fpin_els_li_consumer(void *data) ++{ ++ struct list_head marginal_list_head; ++ int ret = 0; ++ uint16_t host_num; ++ struct els_marginal_list *els_marg; ++ uint32_t event_code; ++ struct vectors *vecs = (struct vectors *)data; ++ ++ pthread_cleanup_push(rcu_unregister, NULL); ++ rcu_register_thread(); ++ pthread_cleanup_push(fpin_clean_marginal_dev_list, NULL); ++ INIT_LIST_HEAD(&marginal_list_head); ++ pthread_cleanup_push(fpin_clean_els_marginal_list, ++ (void *)&marginal_list_head); ++ for ( ; ; ) { ++ pthread_mutex_lock(&fpin_li_mutex); ++ pthread_cleanup_push(cleanup_mutex, &fpin_li_mutex); ++ pthread_testcancel(); ++ while (list_empty(&els_marginal_list_head)) ++ pthread_cond_wait(&fpin_li_cond, &fpin_li_mutex); ++ ++ if (!list_empty(&els_marginal_list_head)) { ++ condlog(4, "Invoke List splice tail\n"); ++ list_splice_tail_init(&els_marginal_list_head, &marginal_list_head); ++ } ++ pthread_cleanup_pop(1); ++ ++ while (!list_empty(&marginal_list_head)) { ++ els_marg = list_first_entry(&marginal_list_head, ++ struct els_marginal_list, node); ++ host_num = els_marg->host_num; ++ event_code = els_marg->event_code; ++ /* Now finally process FPIN LI ELS Frame */ ++ condlog(4, "Got a new Payload buffer, processing it\n"); ++ if ((event_code == FCH_EVT_LINKUP) || (event_code == FCH_EVT_RSCN)) ++ fpin_unset_marginal_dev(host_num, vecs); ++ else { ++ ret = fpin_process_els_frame(host_num, els_marg->payload, vecs); ++ if (ret <= 0) ++ condlog(0, "ELS frame processing failed with ret %d\n", ret); ++ } ++ list_del(&els_marg->node); ++ free(els_marg); ++ ++ } ++ } ++ ++ pthread_cleanup_pop(1); ++ pthread_cleanup_pop(1); ++ pthread_cleanup_pop(1); ++ return NULL; ++} ++ ++static void receiver_cleanup_list(__attribute__((unused)) void *arg) ++{ ++ pthread_mutex_lock(&fpin_li_mutex); ++ fpin_clean_els_marginal_list(&els_marginal_list_head); ++ pthread_mutex_unlock(&fpin_li_mutex); ++} ++ ++/* ++ * Listen for ELS frames from driver. on receiving the frame payload, ++ * push the payload to a list, and notify the fpin_els_li_consumer thread to ++ * process it. Once consumer thread is notified, return to listen for more ELS ++ * frames from driver. ++ */ ++void *fpin_fabric_notification_receiver(__attribute__((unused))void *unused) ++{ ++ int ret; ++ long fd; ++ uint32_t els_cmd; ++ struct fc_nl_event *fc_event = NULL; ++ struct sockaddr_nl fc_local; ++ unsigned char buf[DEF_RX_BUF_SIZE] __attribute__((aligned(sizeof(uint64_t)))); ++ size_t plen = 0; ++ ++ pthread_cleanup_push(rcu_unregister, NULL); ++ rcu_register_thread(); ++ ++ pthread_cleanup_push(receiver_cleanup_list, NULL); ++ fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_SCSITRANSPORT); ++ if (fd < 0) { ++ condlog(0, "fc socket error %ld", fd); ++ return NULL; ++ } ++ ++ pthread_cleanup_push(close_fd, (void *)fd); ++ memset(&fc_local, 0, sizeof(fc_local)); ++ fc_local.nl_family = AF_NETLINK; ++ fc_local.nl_groups = ~0; ++ fc_local.nl_pid = getpid(); ++ ret = bind(fd, (struct sockaddr *)&fc_local, sizeof(fc_local)); ++ if (ret == -1) { ++ condlog(0, "fc socket bind error %d\n", ret); ++ goto out; ++ } ++ for ( ; ; ) { ++ condlog(4, "Waiting for ELS...\n"); ++ ret = read(fd, buf, DEF_RX_BUF_SIZE); ++ if (ret < 0) { ++ condlog(0, "failed to read the els frame (%d)", ret); ++ continue; ++ } ++ condlog(4, "Got a new request %d\n", ret); ++ if (!NLMSG_OK((struct nlmsghdr *)buf, (unsigned int)ret)) { ++ condlog(0, "bad els frame read (%d)", ret); ++ continue; ++ } ++ /* Push the frame to appropriate frame list */ ++ plen = NLMSG_PAYLOAD((struct nlmsghdr *)buf, 0); ++ fc_event = (struct fc_nl_event *)NLMSG_DATA(buf); ++ if (plen < sizeof(*fc_event)) { ++ condlog(0, "too short (%d) to be an FC event", ret); ++ continue; ++ } ++ els_cmd = (uint32_t)fc_event->event_data; ++ condlog(4, "Got host no as %d, event 0x%x, len %d evntnum %d evntcode %d\n", ++ fc_event->host_no, els_cmd, fc_event->event_datalen, ++ fc_event->event_num, fc_event->event_code); ++ fpin_handle_els_frame(fc_event); ++ } ++out: ++ pthread_cleanup_pop(1); ++ pthread_cleanup_pop(1); ++ pthread_cleanup_pop(1); ++ return NULL; ++} +diff --git a/multipathd/main.c b/multipathd/main.c +index eeded52b..4cf5bc41 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -16,6 +16,7 @@ + #include + #include + #include ++#include "fpin.h" + #ifdef USE_SYSTEMD + #include + #endif +@@ -2704,7 +2705,9 @@ reconfigure (struct vectors * vecs) + conf->sequence_nr = old->sequence_nr + 1; + rcu_assign_pointer(multipath_conf, conf); + call_rcu(&old->rcu, rcu_free_config); +- ++#ifdef FPIN_EVENT_HANDLER ++ fpin_clean_marginal_dev_list(NULL); ++#endif + configure(vecs); + + +@@ -2878,7 +2881,8 @@ set_oom_adj (void) + static int + child (__attribute__((unused)) void *param) + { +- pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr; ++ pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr, ++ fpin_thr, fpin_consumer_thr; + pthread_attr_t log_attr, misc_attr, uevent_attr; + struct vectors * vecs; + struct multipath * mpp; +@@ -2892,6 +2896,7 @@ child (__attribute__((unused)) void *param) + char *envp; + int queue_without_daemon; + enum daemon_status state; ++ int fpin_marginal_paths = 0; + + mlockall(MCL_CURRENT | MCL_FUTURE); + signal_init(); +@@ -2959,7 +2964,10 @@ child (__attribute__((unused)) void *param) + + setscheduler(); + set_oom_adj(); +- ++#ifdef FPIN_EVENT_HANDLER ++ if (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) ++ fpin_marginal_paths = 1; ++#endif + /* + * Startup done, invalidate configuration + */ +@@ -3020,6 +3028,19 @@ child (__attribute__((unused)) void *param) + condlog(0, "failed to create uevent dispatcher: %d", rc); + goto failed; + } ++ if (fpin_marginal_paths) { ++ if ((rc = pthread_create(&fpin_thr, &misc_attr, ++ fpin_fabric_notification_receiver, NULL))) { ++ condlog(0, "failed to create the fpin receiver thread: %d", rc); ++ goto failed; ++ } ++ ++ if ((rc = pthread_create(&fpin_consumer_thr, ++ &misc_attr, fpin_els_li_consumer, vecs))) { ++ condlog(0, "failed to create the fpin consumer thread thread: %d", rc); ++ goto failed; ++ } ++ } + pthread_attr_destroy(&misc_attr); + + while (1) { +@@ -3070,6 +3091,10 @@ child (__attribute__((unused)) void *param) + pthread_cancel(uevq_thr); + if (poll_dmevents) + pthread_cancel(dmevent_thr); ++ if (fpin_marginal_paths) { ++ pthread_cancel(fpin_thr); ++ pthread_cancel(fpin_consumer_thr); ++ } + + pthread_join(check_thr, NULL); + pthread_join(uevent_thr, NULL); +@@ -3077,6 +3102,10 @@ child (__attribute__((unused)) void *param) + pthread_join(uevq_thr, NULL); + if (poll_dmevents) + pthread_join(dmevent_thr, NULL); ++ if (fpin_marginal_paths) { ++ pthread_join(fpin_thr, NULL); ++ pthread_join(fpin_consumer_thr, NULL); ++ } + + stop_io_err_stat_thread(); + diff --git a/SOURCES/0106-multipathd-disallow-changing-to-from-fpin-marginal-p.patch b/SOURCES/0106-multipathd-disallow-changing-to-from-fpin-marginal-p.patch new file mode 100644 index 0000000..7245625 --- /dev/null +++ b/SOURCES/0106-multipathd-disallow-changing-to-from-fpin-marginal-p.patch @@ -0,0 +1,73 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 11 Feb 2022 17:23:39 -0600 +Subject: [PATCH] multipathd: disallow changing to/from fpin marginal paths on + reconfig + +Setting marginal_pathgroups to fpin causes two new threads to be created +when multipathd starts. Turning it on after multipathd starts up won't +cause the theads to start, and turing it off won't keep the threads from +working. So disallow changing marginal_pathgroups to/from "fpin" on +reconfigure. + +Signed-off-by: Benjamin Marzinski +--- + multipath/multipath.conf.5 | 13 ++++++++----- + multipathd/main.c | 9 +++++++++ + 2 files changed, 17 insertions(+), 5 deletions(-) + +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 805b7a5e..8e418372 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -1077,15 +1077,18 @@ have been tried first. This prevents the possibility of IO errors occuring + while marginal paths are still usable. After the path has been monitored + for the configured time, and is declared healthy, it will be returned to its + normal pathgroup. +-However if this option is set to \fIfpin\fR multipathd will receive fpin ++If this option is set to \fIfpin\fR, multipathd will receive fpin + notifications, set path states to "marginal" accordingly, and regroup paths +-as described for "marginal_pathgroups yes". This option can't be used in combination +-with other options for "Shaky path detection" (see below).If it is set to fpin, +-marginal_path_xyz and san_path_err_xyz parameters are implicitly set to 0. ++as described for \fIon\fR. This option can't be used in combination ++with other options for "Shaky path detection" (see below). \fBNote:\fR If this ++is set to \fIfpin\fR, the \fImarginal_path_*\fR and \fIsan_path_err_*\fR ++options are implicitly set to \fIno\fP. Also, this option cannot be switched ++either to or from \fIfpin\fR on a multipathd reconfigure. multipathd must be ++restarted for the change to take effect. + See "Shaky paths detection" below for more information. + .RS + .TP +-The default is: \fBno\fR ++The default is: \fBoff\fR + .RE + . + . +diff --git a/multipathd/main.c b/multipathd/main.c +index 4cf5bc41..a6ffbe32 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -2675,6 +2675,7 @@ int + reconfigure (struct vectors * vecs) + { + struct config * old, *conf; ++ int old_marginal_pathgroups; + + conf = load_config(DEFAULT_CONFIGFILE); + if (!conf) +@@ -2702,6 +2703,14 @@ reconfigure (struct vectors * vecs) + uxsock_timeout = conf->uxsock_timeout; + + old = rcu_dereference(multipath_conf); ++ old_marginal_pathgroups = old->marginal_pathgroups; ++ if ((old_marginal_pathgroups == MARGINAL_PATHGROUP_FPIN) != ++ (conf->marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)) { ++ condlog(1, "multipathd must be restarted to turn %s fpin marginal paths", ++ (old_marginal_pathgroups == MARGINAL_PATHGROUP_FPIN)? ++ "off" : "on"); ++ conf->marginal_pathgroups = old_marginal_pathgroups; ++ } + conf->sequence_nr = old->sequence_nr + 1; + rcu_assign_pointer(multipath_conf, conf); + call_rcu(&old->rcu, rcu_free_config); diff --git a/SOURCES/0107-libmultipath-unset-detect_checker-for-clariion-Unity.patch b/SOURCES/0107-libmultipath-unset-detect_checker-for-clariion-Unity.patch new file mode 100644 index 0000000..5c82cc8 --- /dev/null +++ b/SOURCES/0107-libmultipath-unset-detect_checker-for-clariion-Unity.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 10 May 2022 14:17:22 -0500 +Subject: [PATCH] libmultipath: unset detect_checker for clariion / Unity + arrays + +Dell EMC would like to always use the emc_clariion checker. Currently +detect_checker will switch the checker to TUR for Unity arrays. +This can cause problems on some setups with replicated Unity LUNs, +which are handled correctly the the emc_checker, but not the TUR +checker. + +Cc: vincent.chen1@dell.com +Signed-off-by: Benjamin Marzinski +--- + libmultipath/hwtable.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c +index d6325864..2bb274c5 100644 +--- a/libmultipath/hwtable.c ++++ b/libmultipath/hwtable.c +@@ -343,6 +343,7 @@ static struct hwentry default_hw[] = { + .no_path_retry = (300 / DEFAULT_CHECKINT), + .checker_name = EMC_CLARIION, + .prio_name = PRIO_EMC, ++ .detect_checker = DETECT_CHECKER_OFF, + }, + { + /* Invista / VPLEX */ diff --git a/SOURCES/0108-multipathd-Add-missing-ctype-include.patch b/SOURCES/0108-multipathd-Add-missing-ctype-include.patch new file mode 100644 index 0000000..7e2d464 --- /dev/null +++ b/SOURCES/0108-multipathd-Add-missing-ctype-include.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Bastian Germann +Date: Thu, 14 Oct 2021 00:34:33 +0200 +Subject: [PATCH] multipathd: Add missing ctype include + +In uxclnt.c, there are isspace calls. Add an explicit include. + +Signed-off-by: Bastian Germann +Reviewed-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + multipathd/uxclnt.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c +index a76f8e29..f16a7309 100644 +--- a/multipathd/uxclnt.c ++++ b/multipathd/uxclnt.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include diff --git a/SOURCES/0109-multipathd-replace-libreadline-with-libedit.patch b/SOURCES/0109-multipathd-replace-libreadline-with-libedit.patch new file mode 100644 index 0000000..c256ef4 --- /dev/null +++ b/SOURCES/0109-multipathd-replace-libreadline-with-libedit.patch @@ -0,0 +1,102 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Fri, 12 Aug 2022 18:58:15 +0200 +Subject: [PATCH] multipathd: replace libreadline with libedit + +Linking multipathd with libreadline may cause a license conflict, +because libreadline is licensed under GPL-3.0-or-later, and +libmultipath contains several files under GPL-2.0. + +See: + https://github.com/opensvc/multipath-tools/issues/36 + https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=979095 + https://www.gnu.org/licenses/gpl-faq.html#AllCompatibility + +Replace the readline functionality with libedit, which comes under +a BSD license. The readline library can still be enabled (e.g. for +binaries not intended to be distributed) by running +"make READLINE=libreadline". + +Signed-off-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + Makefile.inc | 5 +++++ + multipathd/Makefile | 11 ++++++++++- + multipathd/cli.c | 5 +++++ + multipathd/uxclnt.c | 6 ++++++ + 4 files changed, 26 insertions(+), 1 deletion(-) + +diff --git a/Makefile.inc b/Makefile.inc +index 25c16f4e..d471f045 100644 +--- a/Makefile.inc ++++ b/Makefile.inc +@@ -14,6 +14,11 @@ + # + # Uncomment to disable dmevents polling support + # ENABLE_DMEVENTS_POLL = 0 ++# ++# Readline library to use, libedit or libreadline ++# Caution: Using libreadline may make the multipathd binary undistributable, ++# see https://github.com/opensvc/multipath-tools/issues/36 ++READLINE = libedit + + ifeq ($(TOPDIR),) + TOPDIR = .. +diff --git a/multipathd/Makefile b/multipathd/Makefile +index 835edd93..4874ec3a 100644 +--- a/multipathd/Makefile ++++ b/multipathd/Makefile +@@ -15,7 +15,16 @@ CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathpersistdir) \ + LDFLAGS += $(BIN_LDFLAGS) + LIBDEPS += -L$(multipathdir) -lmultipath -L$(mpathpersistdir) -lmpathpersist \ + -L$(mpathcmddir) -lmpathcmd -ludev -ldl -lurcu -lpthread \ +- -ldevmapper -lreadline ++ -ldevmapper ++ ++ifeq ($(READLINE),libedit) ++CFLAGS += -DUSE_LIBEDIT ++LIBDEPS += -ledit ++endif ++ifeq ($(READLINE),libreadline) ++CFLAGS += -DUSE_LIBREADLINE ++LIBDEPS += -lreadline ++endif + + ifdef SYSTEMD + CFLAGS += -DUSE_SYSTEMD=$(SYSTEMD) +diff --git a/multipathd/cli.c b/multipathd/cli.c +index bdc9fb10..85d73dfb 100644 +--- a/multipathd/cli.c ++++ b/multipathd/cli.c +@@ -11,7 +11,12 @@ + #include "parser.h" + #include "util.h" + #include "version.h" ++#ifdef USE_LIBEDIT ++#include ++#endif ++#ifdef USE_LIBREADLINE + #include ++#endif + + #include "mpath_cmd.h" + #include "cli.h" +diff --git a/multipathd/uxclnt.c b/multipathd/uxclnt.c +index f16a7309..2c17d8fc 100644 +--- a/multipathd/uxclnt.c ++++ b/multipathd/uxclnt.c +@@ -16,8 +16,14 @@ + #include + #include + #include ++ ++#ifdef USE_LIBEDIT ++#include ++#endif ++#ifdef USE_LIBREADLINE + #include + #include ++#endif + + #include "mpath_cmd.h" + #include "uxsock.h" diff --git a/SOURCES/0110-multipath-fix-systemd-timers-in-the-initramfs.patch b/SOURCES/0110-multipath-fix-systemd-timers-in-the-initramfs.patch new file mode 100644 index 0000000..2d7f6d7 --- /dev/null +++ b/SOURCES/0110-multipath-fix-systemd-timers-in-the-initramfs.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 5 Aug 2022 18:16:03 -0500 +Subject: [PATCH] multipath: fix systemd timers in the initramfs + +The systemd timers created for "find_multipaths smart" conflict with +shutdown.target, but not with initrd-cleanup.service. This can make +these timers trigger after the inirtd has started shutting down, +restarting multipathd (which then stops initrd-cleanup.service, since it +conflicts). To avoid this, make sure the timers and the unit they +trigger conflict with inird-cleanup.service. Also don't make them start +multipathd. "multipath -u" will not return "maybe" if multipathd isn't +running or set to run, and since we no longer wait for udev-settle, +multipathd starts up pretty quickly, so it shouldn't be a problem to +not trigger it here. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + multipath/multipath.rules | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/multipath/multipath.rules b/multipath/multipath.rules +index 0486bf70..68c30644 100644 +--- a/multipath/multipath.rules ++++ b/multipath/multipath.rules +@@ -72,7 +72,7 @@ ENV{.SAVED_FM_WAIT_UNTIL}=="?*", GOTO="pretend_mpath" + # + # We must trigger an "add" event because LVM2 will only act on those. + +-RUN+="/usr/bin/systemd-run --unit=cancel-multipath-wait-$kernel --description 'cancel waiting for multipath siblings of $kernel' --no-block --timer-property DefaultDependencies=no --timer-property Conflicts=shutdown.target --timer-property Before=shutdown.target --timer-property AccuracySec=500ms --property DefaultDependencies=no --property Conflicts=shutdown.target --property Before=shutdown.target --property Wants=multipathd.service --property After=multipathd.service --on-active=$env{FIND_MULTIPATHS_WAIT_UNTIL} /usr/bin/udevadm trigger --action=add $sys$devpath" ++RUN+="/usr/bin/systemd-run --unit=cancel-multipath-wait-$kernel --description 'cancel waiting for multipath siblings of $kernel' --no-block --timer-property DefaultDependencies=no --timer-property Conflicts=shutdown.target --timer-property Before=shutdown.target --timer-property Conflicts=initrd-cleanup.service --timer-property Before=initrd-cleanup.service --timer-property AccuracySec=500ms --property DefaultDependencies=no --property Conflicts=shutdown.target --property Before=shutdown.target --property Conflicts=initrd-cleanup.service --property Before=initrd-cleanup.service --on-active=$env{FIND_MULTIPATHS_WAIT_UNTIL} /usr/bin/udevadm trigger --action=add $sys$devpath" + + LABEL="pretend_mpath" + ENV{DM_MULTIPATH_DEVICE_PATH}="1" diff --git a/SOURCES/0111-multipathd-factor-out-the-code-to-flush-a-map-with-n.patch b/SOURCES/0111-multipathd-factor-out-the-code-to-flush-a-map-with-n.patch new file mode 100644 index 0000000..27f4b98 --- /dev/null +++ b/SOURCES/0111-multipathd-factor-out-the-code-to-flush-a-map-with-n.patch @@ -0,0 +1,90 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 9 Aug 2022 16:46:26 -0500 +Subject: [PATCH] multipathd: factor out the code to flush a map with no paths + +The code to flush a multipath device because all of its paths have +been removed will be used by another caller, so factor it out of +ev_remove_path(). + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + multipathd/main.c | 56 ++++++++++++++++++++++++----------------------- + 1 file changed, 29 insertions(+), 27 deletions(-) + +diff --git a/multipathd/main.c b/multipathd/main.c +index a6ffbe32..9b1098f6 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -487,6 +487,30 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset) + return 0; + } + ++static bool ++flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) { ++ char alias[WWID_SIZE]; ++ ++ /* ++ * flush_map will fail if the device is open ++ */ ++ strlcpy(alias, mpp->alias, WWID_SIZE); ++ if (mpp->flush_on_last_del == FLUSH_ENABLED) { ++ condlog(2, "%s Last path deleted, disabling queueing", ++ mpp->alias); ++ mpp->retry_tick = 0; ++ mpp->no_path_retry = NO_PATH_RETRY_FAIL; ++ mpp->disable_queueing = 1; ++ mpp->stat_map_failures++; ++ dm_queue_if_no_path(mpp->alias, 0); ++ } ++ if (!flush_map(mpp, vecs, 1)) { ++ condlog(2, "%s: removed map after removing all paths", alias); ++ return true; ++ } ++ return false; ++} ++ + static int + update_map (struct multipath *mpp, struct vectors *vecs, int new_map) + { +@@ -1185,34 +1209,12 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map) + vector_del_slot(mpp->paths, i); + + /* +- * remove the map IF removing the last path ++ * remove the map IF removing the last path. If ++ * flush_map_nopaths succeeds, the path has been removed. + */ +- if (VECTOR_SIZE(mpp->paths) == 0) { +- char alias[WWID_SIZE]; +- +- /* +- * flush_map will fail if the device is open +- */ +- strlcpy(alias, mpp->alias, WWID_SIZE); +- if (mpp->flush_on_last_del == FLUSH_ENABLED) { +- condlog(2, "%s Last path deleted, disabling queueing", mpp->alias); +- mpp->retry_tick = 0; +- mpp->no_path_retry = NO_PATH_RETRY_FAIL; +- mpp->disable_queueing = 1; +- mpp->stat_map_failures++; +- dm_queue_if_no_path(mpp->alias, 0); +- } +- if (!flush_map(mpp, vecs, 1)) { +- condlog(2, "%s: removed map after" +- " removing all paths", +- alias); +- retval = 0; +- goto out; +- } +- /* +- * Not an error, continue +- */ +- } ++ if (VECTOR_SIZE(mpp->paths) == 0 && ++ flush_map_nopaths(mpp, vecs)) ++ goto out; + + if (mpp->hwe == NULL) + extract_hwe_from_path(mpp); diff --git a/SOURCES/0112-libmultipath-return-success-if-we-raced-to-remove-a-.patch b/SOURCES/0112-libmultipath-return-success-if-we-raced-to-remove-a-.patch new file mode 100644 index 0000000..ce2a539 --- /dev/null +++ b/SOURCES/0112-libmultipath-return-success-if-we-raced-to-remove-a-.patch @@ -0,0 +1,47 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 9 Aug 2022 16:46:27 -0500 +Subject: [PATCH] libmultipath: return success if we raced to remove a map and + lost + +_dm_flush_map() was returning failure if it failed to remove a map, +even if that was because the map had already been removed. Return +success in this case. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/devmapper.c | 4 ++++ + multipathd/main.c | 4 ---- + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index e3c3c119..ee81acc3 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -916,6 +916,10 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove, + } + condlog(4, "multipath map %s removed", mapname); + return 0; ++ } else if (dm_is_mpath(mapname) != 1) { ++ condlog(4, "multipath map %s removed externally", ++ mapname); ++ return 0; /*we raced with someone else removing it */ + } else { + condlog(2, "failed to remove multipath map %s", + mapname); +diff --git a/multipathd/main.c b/multipathd/main.c +index 9b1098f6..9eafd5b7 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -673,10 +673,6 @@ flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths) + * the spurious uevent we may generate with the dm_flush_map call below + */ + if (r) { +- /* +- * May not really be an error -- if the map was already flushed +- * from the device mapper by dmsetup(8) for instance. +- */ + if (r == 1) + condlog(0, "%s: can't flush", mpp->alias); + else { diff --git a/SOURCES/0113-multipathd-Handle-losing-all-path-in-update_map.patch b/SOURCES/0113-multipathd-Handle-losing-all-path-in-update_map.patch new file mode 100644 index 0000000..28878dd --- /dev/null +++ b/SOURCES/0113-multipathd-Handle-losing-all-path-in-update_map.patch @@ -0,0 +1,36 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 9 Aug 2022 16:46:28 -0500 +Subject: [PATCH] multipathd: Handle losing all path in update_map + +Its possible that when a multipath device is being updated, it will end +up that all the paths for it are gone. This can happen if paths are +added and then removed again before multipathd processes the uevent for +the newly created multipath device. In this case multipathd wasn't +taking the proper action for the case where all the paths had been +removed. If flush_on_last_del was set, multipathd wasn't disabling +flushing and if deferred_remove was set, it wasn't doing a deferred +remove. Multipathd should call flush_map_nopaths(), just like +ev_remove_path() does when the last path is removed. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + multipathd/main.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/multipathd/main.c b/multipathd/main.c +index 9eafd5b7..870ae7d8 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -526,6 +526,10 @@ retry: + goto fail; + } + verify_paths(mpp, vecs); ++ if (VECTOR_SIZE(mpp->paths) == 0 && ++ flush_map_nopaths(mpp, vecs)) ++ return 1; ++ + mpp->action = ACT_RELOAD; + + if (setup_map(mpp, params, PARAMS_SIZE, vecs)) { diff --git a/SOURCES/0114-multipathd-ignore-duplicated-multipathd-command-keys.patch b/SOURCES/0114-multipathd-ignore-duplicated-multipathd-command-keys.patch new file mode 100644 index 0000000..4192095 --- /dev/null +++ b/SOURCES/0114-multipathd-ignore-duplicated-multipathd-command-keys.patch @@ -0,0 +1,153 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 8 Sep 2022 11:54:49 -0500 +Subject: [PATCH] multipathd: ignore duplicated multipathd command keys + +multipath adds rather than or-s the values of command keys. Fix this. +Also, return an invalid fingerprint if a key is used more than once. + +Signed-off-by: Benjamin Marzinski +--- + multipathd/cli.c | 8 ++-- + multipathd/main.c | 106 +++++++++++++++++++++++----------------------- + 2 files changed, 58 insertions(+), 56 deletions(-) + +diff --git a/multipathd/cli.c b/multipathd/cli.c +index 85d73dfb..45e80197 100644 +--- a/multipathd/cli.c ++++ b/multipathd/cli.c +@@ -341,9 +341,11 @@ fingerprint(vector vec) + if (!vec) + return 0; + +- vector_foreach_slot(vec, kw, i) +- fp += kw->code; +- ++ vector_foreach_slot(vec, kw, i) { ++ if (fp & kw->code) ++ return (uint64_t)-1; ++ fp |= kw->code; ++ } + return fp; + } + +diff --git a/multipathd/main.c b/multipathd/main.c +index 870ae7d8..cd68a9d2 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -1628,62 +1628,62 @@ uxlsnrloop (void * ap) + /* Tell main thread that thread has started */ + post_config_state(DAEMON_CONFIGURE); + +- set_handler_callback(LIST+PATHS, cli_list_paths); +- set_handler_callback(LIST+PATHS+FMT, cli_list_paths_fmt); +- set_handler_callback(LIST+PATHS+RAW+FMT, cli_list_paths_raw); +- set_handler_callback(LIST+PATH, cli_list_path); +- set_handler_callback(LIST+MAPS, cli_list_maps); +- set_handler_callback(LIST+STATUS, cli_list_status); +- set_unlocked_handler_callback(LIST+DAEMON, cli_list_daemon); +- set_handler_callback(LIST+MAPS+STATUS, cli_list_maps_status); +- set_handler_callback(LIST+MAPS+STATS, cli_list_maps_stats); +- set_handler_callback(LIST+MAPS+FMT, cli_list_maps_fmt); +- set_handler_callback(LIST+MAPS+RAW+FMT, cli_list_maps_raw); +- set_handler_callback(LIST+MAPS+TOPOLOGY, cli_list_maps_topology); +- set_handler_callback(LIST+TOPOLOGY, cli_list_maps_topology); +- set_handler_callback(LIST+MAPS+JSON, cli_list_maps_json); +- set_handler_callback(LIST+MAP+TOPOLOGY, cli_list_map_topology); +- set_handler_callback(LIST+MAP+FMT, cli_list_map_fmt); +- set_handler_callback(LIST+MAP+RAW+FMT, cli_list_map_fmt); +- set_handler_callback(LIST+MAP+JSON, cli_list_map_json); +- set_handler_callback(LIST+CONFIG+LOCAL, cli_list_config_local); +- set_handler_callback(LIST+CONFIG, cli_list_config); +- set_handler_callback(LIST+BLACKLIST, cli_list_blacklist); +- set_handler_callback(LIST+DEVICES, cli_list_devices); +- set_handler_callback(LIST+WILDCARDS, cli_list_wildcards); +- set_handler_callback(RESET+MAPS+STATS, cli_reset_maps_stats); +- set_handler_callback(RESET+MAP+STATS, cli_reset_map_stats); +- set_handler_callback(ADD+PATH, cli_add_path); +- set_handler_callback(DEL+PATH, cli_del_path); +- set_handler_callback(ADD+MAP, cli_add_map); +- set_handler_callback(DEL+MAP, cli_del_map); +- set_handler_callback(DEL+MAPS, cli_del_maps); +- set_handler_callback(SWITCH+MAP+GROUP, cli_switch_group); ++ set_handler_callback(LIST|PATHS, cli_list_paths); ++ set_handler_callback(LIST|PATHS|FMT, cli_list_paths_fmt); ++ set_handler_callback(LIST|PATHS|RAW|FMT, cli_list_paths_raw); ++ set_handler_callback(LIST|PATH, cli_list_path); ++ set_handler_callback(LIST|MAPS, cli_list_maps); ++ set_handler_callback(LIST|STATUS, cli_list_status); ++ set_unlocked_handler_callback(LIST|DAEMON, cli_list_daemon); ++ set_handler_callback(LIST|MAPS|STATUS, cli_list_maps_status); ++ set_handler_callback(LIST|MAPS|STATS, cli_list_maps_stats); ++ set_handler_callback(LIST|MAPS|FMT, cli_list_maps_fmt); ++ set_handler_callback(LIST|MAPS|RAW|FMT, cli_list_maps_raw); ++ set_handler_callback(LIST|MAPS|TOPOLOGY, cli_list_maps_topology); ++ set_handler_callback(LIST|TOPOLOGY, cli_list_maps_topology); ++ set_handler_callback(LIST|MAPS|JSON, cli_list_maps_json); ++ set_handler_callback(LIST|MAP|TOPOLOGY, cli_list_map_topology); ++ set_handler_callback(LIST|MAP|FMT, cli_list_map_fmt); ++ set_handler_callback(LIST|MAP|RAW|FMT, cli_list_map_fmt); ++ set_handler_callback(LIST|MAP|JSON, cli_list_map_json); ++ set_handler_callback(LIST|CONFIG|LOCAL, cli_list_config_local); ++ set_handler_callback(LIST|CONFIG, cli_list_config); ++ set_handler_callback(LIST|BLACKLIST, cli_list_blacklist); ++ set_handler_callback(LIST|DEVICES, cli_list_devices); ++ set_handler_callback(LIST|WILDCARDS, cli_list_wildcards); ++ set_handler_callback(RESET|MAPS|STATS, cli_reset_maps_stats); ++ set_handler_callback(RESET|MAP|STATS, cli_reset_map_stats); ++ set_handler_callback(ADD|PATH, cli_add_path); ++ set_handler_callback(DEL|PATH, cli_del_path); ++ set_handler_callback(ADD|MAP, cli_add_map); ++ set_handler_callback(DEL|MAP, cli_del_map); ++ set_handler_callback(DEL|MAPS, cli_del_maps); ++ set_handler_callback(SWITCH|MAP|GROUP, cli_switch_group); + set_unlocked_handler_callback(RECONFIGURE, cli_reconfigure); +- set_handler_callback(SUSPEND+MAP, cli_suspend); +- set_handler_callback(RESUME+MAP, cli_resume); +- set_handler_callback(RESIZE+MAP, cli_resize); +- set_handler_callback(RELOAD+MAP, cli_reload); +- set_handler_callback(RESET+MAP, cli_reassign); +- set_handler_callback(REINSTATE+PATH, cli_reinstate); +- set_handler_callback(FAIL+PATH, cli_fail); +- set_handler_callback(DISABLEQ+MAP, cli_disable_queueing); +- set_handler_callback(RESTOREQ+MAP, cli_restore_queueing); +- set_handler_callback(DISABLEQ+MAPS, cli_disable_all_queueing); +- set_handler_callback(RESTOREQ+MAPS, cli_restore_all_queueing); ++ set_handler_callback(SUSPEND|MAP, cli_suspend); ++ set_handler_callback(RESUME|MAP, cli_resume); ++ set_handler_callback(RESIZE|MAP, cli_resize); ++ set_handler_callback(RELOAD|MAP, cli_reload); ++ set_handler_callback(RESET|MAP, cli_reassign); ++ set_handler_callback(REINSTATE|PATH, cli_reinstate); ++ set_handler_callback(FAIL|PATH, cli_fail); ++ set_handler_callback(DISABLEQ|MAP, cli_disable_queueing); ++ set_handler_callback(RESTOREQ|MAP, cli_restore_queueing); ++ set_handler_callback(DISABLEQ|MAPS, cli_disable_all_queueing); ++ set_handler_callback(RESTOREQ|MAPS, cli_restore_all_queueing); + set_unlocked_handler_callback(QUIT, cli_quit); + set_unlocked_handler_callback(SHUTDOWN, cli_shutdown); +- set_handler_callback(GETPRSTATUS+MAP, cli_getprstatus); +- set_handler_callback(SETPRSTATUS+MAP, cli_setprstatus); +- set_handler_callback(UNSETPRSTATUS+MAP, cli_unsetprstatus); +- set_handler_callback(FORCEQ+DAEMON, cli_force_no_daemon_q); +- set_handler_callback(RESTOREQ+DAEMON, cli_restore_no_daemon_q); +- set_handler_callback(GETPRKEY+MAP, cli_getprkey); +- set_handler_callback(SETPRKEY+MAP+KEY, cli_setprkey); +- set_handler_callback(UNSETPRKEY+MAP, cli_unsetprkey); +- set_handler_callback(SETMARGINAL+PATH, cli_set_marginal); +- set_handler_callback(UNSETMARGINAL+PATH, cli_unset_marginal); +- set_handler_callback(UNSETMARGINAL+MAP, cli_unset_all_marginal); ++ set_handler_callback(GETPRSTATUS|MAP, cli_getprstatus); ++ set_handler_callback(SETPRSTATUS|MAP, cli_setprstatus); ++ set_handler_callback(UNSETPRSTATUS|MAP, cli_unsetprstatus); ++ set_handler_callback(FORCEQ|DAEMON, cli_force_no_daemon_q); ++ set_handler_callback(RESTOREQ|DAEMON, cli_restore_no_daemon_q); ++ set_handler_callback(GETPRKEY|MAP, cli_getprkey); ++ set_handler_callback(SETPRKEY|MAP|KEY, cli_setprkey); ++ set_handler_callback(UNSETPRKEY|MAP, cli_unsetprkey); ++ set_handler_callback(SETMARGINAL|PATH, cli_set_marginal); ++ set_handler_callback(UNSETMARGINAL|PATH, cli_unset_marginal); ++ set_handler_callback(UNSETMARGINAL|MAP, cli_unset_all_marginal); + + umask(077); + uxsock_listen(&uxsock_trigger, ux_sock, ap); diff --git a/SOURCES/0115-multipath-tools-use-run-instead-of-dev-shm.patch b/SOURCES/0115-multipath-tools-use-run-instead-of-dev-shm.patch new file mode 100644 index 0000000..f7a05fc --- /dev/null +++ b/SOURCES/0115-multipath-tools-use-run-instead-of-dev-shm.patch @@ -0,0 +1,141 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Thu, 1 Sep 2022 19:21:30 +0200 +Subject: [PATCH] multipath-tools: use /run instead of /dev/shm + +/dev/shm may have unsafe permissions. Use /run instead. +Use systemd's tmpfiles.d mechanism to create /run/multipath +early during boot. + +For backward compatibilty, make the runtime directory configurable +via the "runtimedir" make variable. + +Signed-off-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + .gitignore | 2 ++ + Makefile.inc | 4 +++- + libmultipath/defaults.h | 2 +- + multipath/Makefile | 9 +++++++-- + multipath/{multipath.rules => multipath.rules.in} | 4 ++-- + multipath/tmpfiles.conf.in | 1 + + 6 files changed, 16 insertions(+), 6 deletions(-) + rename multipath/{multipath.rules => multipath.rules.in} (96%) + create mode 100644 multipath/tmpfiles.conf.in + +diff --git a/.gitignore b/.gitignore +index 9926756b..f90b0350 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -8,6 +8,8 @@ + *.d + kpartx/kpartx + multipath/multipath ++multipath/multipath.rules ++multipath/tmpfiles.conf + multipathd/multipathd + mpathpersist/mpathpersist + .nfs* +diff --git a/Makefile.inc b/Makefile.inc +index d471f045..287f0e7b 100644 +--- a/Makefile.inc ++++ b/Makefile.inc +@@ -60,6 +60,7 @@ exec_prefix = $(prefix) + usr_prefix = $(prefix) + bindir = $(exec_prefix)/usr/sbin + libudevdir = $(prefix)/$(SYSTEMDPATH)/udev ++tmpfilesdir = $(prefix)/$(SYSTEMDPATH)/tmpfiles.d + udevrulesdir = $(libudevdir)/rules.d + multipathdir = $(TOPDIR)/libmultipath + man8dir = $(prefix)/usr/share/man/man8 +@@ -76,6 +77,7 @@ libdmmpdir = $(TOPDIR)/libdmmp + nvmedir = $(TOPDIR)/libmultipath/nvme + includedir = $(prefix)/usr/include + pkgconfdir = $(usrlibdir)/pkgconfig ++runtimedir = /$(RUN) + + GZIP = gzip -9 -c + RM = rm -f +@@ -117,7 +119,7 @@ OPTFLAGS += -Werror -Wextra -Wstrict-prototypes -Wformat=2 \ + -Werror=cast-qual $(ERROR_DISCARDED_QUALIFIERS) \ + --param=ssp-buffer-size=4 + +-CFLAGS := $(OPTFLAGS) -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" \ ++CFLAGS := $(OPTFLAGS) -DBIN_DIR=\"$(bindir)\" -DLIB_STRING=\"${LIB}\" -DRUN_DIR=\"${RUN}\" -DRUNTIME_DIR=\"$(runtimedir)\" \ + -MMD -MP $(CFLAGS) + BIN_CFLAGS = -fPIE -DPIE + LIB_CFLAGS = -fPIC +diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h +index f1cb000d..dcd9f5a7 100644 +--- a/libmultipath/defaults.h ++++ b/libmultipath/defaults.h +@@ -65,7 +65,7 @@ + #define DEFAULT_WWIDS_FILE "/etc/multipath/wwids" + #define DEFAULT_PRKEYS_FILE "/etc/multipath/prkeys" + #define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d" +-#define MULTIPATH_SHM_BASE "/dev/shm/multipath/" ++#define MULTIPATH_SHM_BASE RUNTIME_DIR "/multipath/" + + + static inline char *set_default(char *str) +diff --git a/multipath/Makefile b/multipath/Makefile +index e720c7f6..f3d98012 100644 +--- a/multipath/Makefile ++++ b/multipath/Makefile +@@ -12,7 +12,7 @@ EXEC = multipath + + OBJS = main.o + +-all: $(EXEC) ++all: $(EXEC) multipath.rules tmpfiles.conf + + $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so + $(CC) $(CFLAGS) $(OBJS) -o $(EXEC) $(LDFLAGS) $(LIBDEPS) +@@ -27,6 +27,8 @@ install: + $(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 ++ $(INSTALL_PROGRAM) -d $(DESTDIR)$(tmpfilesdir) ++ $(INSTALL_PROGRAM) -m 644 tmpfiles.conf $(DESTDIR)$(tmpfilesdir)/multipath.conf + $(INSTALL_PROGRAM) -d $(DESTDIR)$(man8dir) + $(INSTALL_PROGRAM) -m 644 $(EXEC).8.gz $(DESTDIR)$(man8dir) + $(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir) +@@ -43,9 +45,12 @@ uninstall: + $(RM) $(DESTDIR)$(man8dir)/mpathconf.8.gz + + clean: dep_clean +- $(RM) core *.o $(EXEC) *.gz ++ $(RM) core *.o $(EXEC) *.gz multipath.rules tmpfiles.conf + + include $(wildcard $(OBJS:.o=.d)) + + dep_clean: + $(RM) $(OBJS:.o=.d) ++ ++%: %.in ++ sed 's,@RUNTIME_DIR@,$(runtimedir),' $< >$@ +diff --git a/multipath/multipath.rules b/multipath/multipath.rules.in +similarity index 96% +rename from multipath/multipath.rules +rename to multipath/multipath.rules.in +index 68c30644..5c4447a2 100644 +--- a/multipath/multipath.rules ++++ b/multipath/multipath.rules.in +@@ -1,8 +1,8 @@ + # Set DM_MULTIPATH_DEVICE_PATH if the device should be handled by multipath + SUBSYSTEM!="block", GOTO="end_mpath" + KERNEL!="sd*|dasd*|nvme*", GOTO="end_mpath" +-ACTION=="remove", TEST=="/dev/shm/multipath/find_multipaths/$major:$minor", \ +- RUN+="/usr/bin/rm -f /dev/shm/multipath/find_multipaths/$major:$minor" ++ACTION=="remove", TEST=="@RUNTIME_DIR@/multipath/find_multipaths/$major:$minor", \ ++ RUN+="/usr/bin/rm -f @RUNTIME_DIR@/multipath/find_multipaths/$major:$minor" + ACTION!="add|change", GOTO="end_mpath" + + IMPORT{cmdline}="nompath" +diff --git a/multipath/tmpfiles.conf.in b/multipath/tmpfiles.conf.in +new file mode 100644 +index 00000000..21be438a +--- /dev/null ++++ b/multipath/tmpfiles.conf.in +@@ -0,0 +1 @@ ++d @RUNTIME_DIR@/multipath 0700 root root - diff --git a/SOURCES/0116-kpartx-hold-device-open-until-partitions-have-been-c.patch b/SOURCES/0116-kpartx-hold-device-open-until-partitions-have-been-c.patch new file mode 100644 index 0000000..fa9033a --- /dev/null +++ b/SOURCES/0116-kpartx-hold-device-open-until-partitions-have-been-c.patch @@ -0,0 +1,47 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 19 Oct 2022 12:57:10 -0500 +Subject: [PATCH] kpartx: hold device open until partitions have been created + +kpartx was closing the whole device after it read the partition +information off it. This allowed a race, where the device could be +removed and another one created with the same major:minor, after kpartx +read the partition information but before it created the partition +devices. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + kpartx/kpartx.c | 11 +++-------- + 1 file changed, 3 insertions(+), 8 deletions(-) + +diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c +index a337a07b..e62b764f 100644 +--- a/kpartx/kpartx.c ++++ b/kpartx/kpartx.c +@@ -426,12 +426,7 @@ main(int argc, char **argv){ + if (n >= 0) + printf("%s: %d slices\n", ptp->type, n); + #endif +- +- if (n > 0) { +- close(fd); +- fd = -1; +- } +- else ++ if (n <= 0) + continue; + + switch(what) { +@@ -649,9 +644,9 @@ main(int argc, char **argv){ + if (n > 0) + break; + } ++ if (fd != -1) ++ close(fd); + if (what == LIST && loopcreated) { +- if (fd != -1) +- close(fd); + if (del_loop(device)) { + if (verbose) + fprintf(stderr, "can't del loop : %s\n", diff --git a/SOURCES/0117-libmultipath-cleanup-remove_feature.patch b/SOURCES/0117-libmultipath-cleanup-remove_feature.patch new file mode 100644 index 0000000..4ed7925 --- /dev/null +++ b/SOURCES/0117-libmultipath-cleanup-remove_feature.patch @@ -0,0 +1,153 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 7 Oct 2022 12:35:37 -0500 +Subject: [PATCH] libmultipath: cleanup remove_feature + +remove_feature() didn't correctly handle feature strings that used +whitespace other than spaces, which the kernel allows. It also didn't +check if the feature string to be removed was part of a larger feature +token. Finally, it did a lot of unnecessary work. By failing if the +feature string to be removed contains leading or trailing whitespace, +the function can be significanly simplified. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/structs.c | 82 +++++++++++++++--------------------------- + 1 file changed, 29 insertions(+), 53 deletions(-) + +diff --git a/libmultipath/structs.c b/libmultipath/structs.c +index 9f86eb69..471087e2 100644 +--- a/libmultipath/structs.c ++++ b/libmultipath/structs.c +@@ -6,6 +6,7 @@ + #include + #include + #include ++#include + + #include "checkers.h" + #include "memory.h" +@@ -633,7 +634,7 @@ int add_feature(char **f, const char *n) + + int remove_feature(char **f, const char *o) + { +- int c = 0, d, l; ++ int c = 0, d; + char *e, *p, *n; + const char *q; + +@@ -644,33 +645,35 @@ int remove_feature(char **f, const char *o) + if (!o || *o == '\0') + return 0; + +- /* Check if not present */ +- if (!strstr(*f, o)) ++ d = strlen(o); ++ if (isspace(*o) || isspace(*(o + d - 1))) { ++ condlog(0, "internal error: feature \"%s\" has leading or trailing spaces", o); ++ return 1; ++ } ++ ++ /* Check if present and not part of a larger feature token*/ ++ p = *f + 1; /* the size must be at the start of the features string */ ++ while ((p = strstr(p, o)) != NULL) { ++ if (isspace(*(p - 1)) && ++ (isspace(*(p + d)) || *(p + d) == '\0')) ++ break; ++ p += d; ++ } ++ if (!p) + return 0; + + /* Get feature count */ + c = strtoul(*f, &e, 10); +- if (*f == e) +- /* parse error */ ++ if (*f == e || !isspace(*e)) { ++ condlog(0, "parse error in feature string \"%s\"", *f); + return 1; +- +- /* Normalize features */ +- while (*o == ' ') { +- o++; + } +- /* Just spaces, return */ +- if (*o == '\0') +- return 0; +- q = o + strlen(o); +- while (*q == ' ') +- q--; +- d = (int)(q - o); + + /* Update feature count */ + c--; + q = o; +- while (q[0] != '\0') { +- if (q[0] == ' ' && q[1] != ' ' && q[1] != '\0') ++ while (*q != '\0') { ++ if (isspace(*q) && !isspace(*(q + 1)) && *(q + 1) != '\0') + c--; + q++; + } +@@ -684,15 +687,8 @@ int remove_feature(char **f, const char *o) + goto out; + } + +- /* Search feature to be removed */ +- e = strstr(*f, o); +- if (!e) +- /* Not found, return */ +- return 0; +- + /* Update feature count space */ +- l = strlen(*f) - d; +- n = MALLOC(l + 1); ++ n = MALLOC(strlen(*f) - d + 1); + if (!n) + return 1; + +@@ -702,36 +698,16 @@ int remove_feature(char **f, const char *o) + * Copy existing features up to the feature + * about to be removed + */ +- p = strchr(*f, ' '); +- if (!p) { +- /* Internal error, feature string inconsistent */ +- FREE(n); +- return 1; +- } +- while (*p == ' ') +- p++; +- p--; +- if (e != p) { +- do { +- e--; +- d++; +- } while (*e == ' '); +- e++; d--; +- strncat(n, p, (size_t)(e - p)); +- p += (size_t)(e - p); +- } ++ strncat(n, e, (size_t)(p - e)); + /* Skip feature to be removed */ + p += d; +- + /* Copy remaining features */ +- if (strlen(p)) { +- while (*p == ' ') +- p++; +- if (strlen(p)) { +- p--; +- strcat(n, p); +- } +- } ++ while (isspace(*p)) ++ p++; ++ if (*p != '\0') ++ strcat(n, p); ++ else ++ strchop(n); + + out: + FREE(*f); diff --git a/SOURCES/0118-libmultipath-cleanup-add_feature.patch b/SOURCES/0118-libmultipath-cleanup-add_feature.patch new file mode 100644 index 0000000..85e1656 --- /dev/null +++ b/SOURCES/0118-libmultipath-cleanup-add_feature.patch @@ -0,0 +1,107 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 7 Oct 2022 12:35:38 -0500 +Subject: [PATCH] libmultipath: cleanup add_feature + +add_feature() didn't correctly handle feature strings that used +whitespace other than spaces, which the kernel allows. It also didn't +allow adding features with multiple tokens. When it looked to see if the +feature string to be added already existed, it didn't check if the match +was part of a larger token. Finally, it did unnecessary work. By using +asprintf() to create the string, the function can be signifcantly +simplified. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/structs.c | 49 +++++++++++++++++++++--------------------- + 1 file changed, 24 insertions(+), 25 deletions(-) + +diff --git a/libmultipath/structs.c b/libmultipath/structs.c +index 471087e2..84f9c959 100644 +--- a/libmultipath/structs.c ++++ b/libmultipath/structs.c +@@ -572,23 +572,33 @@ int add_feature(char **f, const char *n) + { + int c = 0, d, l; + char *e, *t; ++ const char *p; + + if (!f) + return 1; + + /* Nothing to do */ +- if (!n || *n == '0') ++ if (!n || *n == '\0') + return 0; + +- if (strchr(n, ' ') != NULL) { +- condlog(0, "internal error: feature \"%s\" contains spaces", n); ++ l = strlen(n); ++ if (isspace(*n) || isspace(*(n + l - 1))) { ++ condlog(0, "internal error: feature \"%s\" has leading or trailing spaces", n); + return 1; + } + ++ p = n; ++ d = 1; ++ while (*p != '\0') { ++ if (isspace(*p) && !isspace(*(p + 1)) && *(p + 1) != '\0') ++ d++; ++ p++; ++ } ++ + /* default feature is null */ + if(!*f) + { +- l = asprintf(&t, "1 %s", n); ++ l = asprintf(&t, "%0d %s", d, n); + if(l == -1) + return 1; + +@@ -597,35 +607,24 @@ int add_feature(char **f, const char *n) + } + + /* Check if feature is already present */ +- if (strstr(*f, n)) +- return 0; ++ e = *f; ++ while ((e = strstr(e, n)) != NULL) { ++ if (isspace(*(e - 1)) && ++ (isspace(*(e + l)) || *(e + l) == '\0')) ++ return 0; ++ e += l; ++ } + + /* Get feature count */ + c = strtoul(*f, &e, 10); +- if (*f == e || (*e != ' ' && *e != '\0')) { ++ if (*f == e || (!isspace(*e) && *e != '\0')) { + condlog(0, "parse error in feature string \"%s\"", *f); + return 1; + } +- +- /* Add 1 digit and 1 space */ +- l = strlen(e) + strlen(n) + 2; +- +- c++; +- /* Check if we need more digits for feature count */ +- for (d = c; d >= 10; d /= 10) +- l++; +- +- t = MALLOC(l + 1); +- if (!t) ++ c += d; ++ if (asprintf(&t, "%0d%s %s", c, e, n) < 0) + return 1; + +- /* e: old feature string with leading space, or "" */ +- if (*e == ' ') +- while (*(e + 1) == ' ') +- e++; +- +- snprintf(t, l + 1, "%0d%s %s", c, e, n); +- + FREE(*f); + *f = t; + diff --git a/SOURCES/0119-multipath-tests-tests-for-adding-and-removing-featur.patch b/SOURCES/0119-multipath-tests-tests-for-adding-and-removing-featur.patch new file mode 100644 index 0000000..d450d48 --- /dev/null +++ b/SOURCES/0119-multipath-tests-tests-for-adding-and-removing-featur.patch @@ -0,0 +1,350 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 7 Oct 2022 12:35:39 -0500 +Subject: [PATCH] multipath tests: tests for adding and removing features + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + tests/Makefile | 2 +- + tests/features.c | 318 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 319 insertions(+), 1 deletion(-) + create mode 100644 tests/features.c + +diff --git a/tests/Makefile b/tests/Makefile +index 77ff3249..914413b8 100644 +--- a/tests/Makefile ++++ b/tests/Makefile +@@ -13,7 +13,7 @@ CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) \ + LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka + + TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd pgpolicy \ +- alias directio ++ alias directio features + + .SILENT: $(TESTS:%=%.o) + .PRECIOUS: $(TESTS:%=%-test) +diff --git a/tests/features.c b/tests/features.c +new file mode 100644 +index 00000000..1e2e6bff +--- /dev/null ++++ b/tests/features.c +@@ -0,0 +1,318 @@ ++#include ++#include ++#include ++#include ++ ++#include "structs.h" ++#include "globals.c" ++ ++static void test_af_null_features_ptr(void **state) ++{ ++ assert_int_equal(add_feature(NULL, "test"), 1); ++} ++ ++static void af_helper(const char *features_start, const char *addition, ++ const char *features_end, int result) ++{ ++ char *f = NULL, *orig = NULL; ++ ++ if (features_start) { ++ f = strdup(features_start); ++ assert_non_null(f); ++ orig = f; ++ } ++ assert_int_equal(add_feature(&f, addition), result); ++ if (result != 0 || features_end == NULL) ++ assert_ptr_equal(orig, f); ++ else ++ assert_string_equal(f, features_end); ++ free(f); ++} ++ ++static void test_af_null_addition1(void **state) ++{ ++ af_helper("0", NULL, NULL, 0); ++} ++ ++static void test_af_null_addition2(void **state) ++{ ++ af_helper("1 queue_if_no_path", NULL, NULL, 0); ++} ++ ++static void test_af_empty_addition(void **state) ++{ ++ af_helper("2 pg_init_retries 5", "", NULL, 0); ++} ++ ++static void test_af_invalid_addition1(void **state) ++{ ++ af_helper("2 pg_init_retries 5", " ", NULL, 1); ++} ++ ++static void test_af_invalid_addition2(void **state) ++{ ++ af_helper("2 pg_init_retries 5", "\tbad", NULL, 1); ++} ++ ++static void test_af_invalid_addition3(void **state) ++{ ++ af_helper("2 pg_init_retries 5", "bad ", NULL, 1); ++} ++ ++static void test_af_invalid_addition4(void **state) ++{ ++ af_helper("2 pg_init_retries 5", " bad ", NULL, 1); ++} ++ ++static void test_af_null_features1(void **state) ++{ ++ af_helper(NULL, "test", "1 test", 0); ++} ++ ++static void test_af_null_features2(void **state) ++{ ++ af_helper(NULL, "test\t more", "2 test\t more", 0); ++} ++ ++static void test_af_null_features3(void **state) ++{ ++ af_helper(NULL, "test\neven\tmore", "3 test\neven\tmore", 0); ++} ++ ++static void test_af_already_exists1(void **state) ++{ ++ af_helper("4 this is a test", "test", NULL, 0); ++} ++ ++static void test_af_already_exists2(void **state) ++{ ++ af_helper("5 contest testy intestine test retest", "test", NULL, 0); ++} ++ ++static void test_af_almost_exists(void **state) ++{ ++ af_helper("3 contest testy intestine", "test", ++ "4 contest testy intestine test", 0); ++} ++ ++static void test_af_bad_features1(void **state) ++{ ++ af_helper("bad", "test", NULL, 1); ++} ++ ++static void test_af_bad_features2(void **state) ++{ ++ af_helper("1bad", "test", NULL, 1); ++} ++ ++static void test_af_add1(void **state) ++{ ++ af_helper("0", "test", "1 test", 0); ++} ++ ++static void test_af_add2(void **state) ++{ ++ af_helper("0", "this is a test", "4 this is a test", 0); ++} ++ ++static void test_af_add3(void **state) ++{ ++ af_helper("1 features", "more values", "3 features more values", 0); ++} ++ ++static void test_af_add4(void **state) ++{ ++ af_helper("2 one\ttwo", "three\t four", "4 one\ttwo three\t four", 0); ++} ++ ++static int test_add_features(void) ++{ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test(test_af_null_features_ptr), ++ cmocka_unit_test(test_af_null_addition1), ++ cmocka_unit_test(test_af_null_addition2), ++ cmocka_unit_test(test_af_empty_addition), ++ cmocka_unit_test(test_af_invalid_addition1), ++ cmocka_unit_test(test_af_invalid_addition2), ++ cmocka_unit_test(test_af_invalid_addition3), ++ cmocka_unit_test(test_af_invalid_addition4), ++ cmocka_unit_test(test_af_null_features1), ++ cmocka_unit_test(test_af_null_features2), ++ cmocka_unit_test(test_af_null_features3), ++ cmocka_unit_test(test_af_already_exists1), ++ cmocka_unit_test(test_af_already_exists2), ++ cmocka_unit_test(test_af_almost_exists), ++ cmocka_unit_test(test_af_bad_features1), ++ cmocka_unit_test(test_af_bad_features2), ++ cmocka_unit_test(test_af_add1), ++ cmocka_unit_test(test_af_add2), ++ cmocka_unit_test(test_af_add3), ++ cmocka_unit_test(test_af_add4), ++ }; ++ return cmocka_run_group_tests(tests, NULL, NULL); ++} ++ ++static void test_rf_null_features_ptr(void **state) ++{ ++ assert_int_equal(remove_feature(NULL, "test"), 1); ++} ++ ++static void test_rf_null_features(void **state) ++{ ++ char *f = NULL; ++ assert_int_equal(remove_feature(&f, "test"), 1); ++} ++ ++static void rf_helper(const char *features_start, const char *removal, ++ const char *features_end, int result) ++{ ++ char *f = strdup(features_start); ++ char *orig = f; ++ ++ assert_non_null(f); ++ assert_int_equal(remove_feature(&f, removal), result); ++ if (result != 0 || features_end == NULL) ++ assert_ptr_equal(orig, f); ++ else ++ assert_string_equal(f, features_end); ++ free(f); ++} ++ ++static void test_rf_null_removal(void **state) ++{ ++ rf_helper("1 feature", NULL, NULL, 0); ++} ++ ++static void test_rf_empty_removal(void **state) ++{ ++ rf_helper("1 feature", "", NULL, 0); ++} ++ ++static void test_rf_invalid_removal1(void **state) ++{ ++ rf_helper("1 feature", " ", NULL, 1); ++} ++ ++static void test_rf_invalid_removal2(void **state) ++{ ++ rf_helper("1 feature", " bad", NULL, 1); ++} ++ ++static void test_rf_invalid_removal3(void **state) ++{ ++ rf_helper("1 feature", "bad\n", NULL, 1); ++} ++ ++static void test_rf_invalid_removal4(void **state) ++{ ++ rf_helper("1 feature", "\tbad \n", NULL, 1); ++} ++ ++static void test_rf_bad_features1(void **state) ++{ ++ rf_helper("invalid feature test string", "test", NULL, 1); ++} ++ ++static void test_rf_bad_features2(void **state) ++{ ++ rf_helper("2no space test", "test", NULL, 1); ++} ++ ++static void test_rf_missing_removal1(void **state) ++{ ++ rf_helper("0", "test", NULL, 0); ++} ++ ++static void test_rf_missing_removal2(void **state) ++{ ++ rf_helper("1 detest", "test", NULL, 0); ++} ++ ++static void test_rf_missing_removal3(void **state) ++{ ++ rf_helper("4 testing one two three", "test", NULL, 0); ++} ++ ++static void test_rf_missing_removal4(void **state) ++{ ++ rf_helper("1 contestant", "test", NULL, 0); ++} ++ ++static void test_rf_missing_removal5(void **state) ++{ ++ rf_helper("3 testament protest detestable", "test", NULL, 0); ++} ++ ++static void test_rf_remove_all_features1(void **state) ++{ ++ rf_helper("1 test", "test", "0", 0); ++} ++ ++static void test_rf_remove_all_features2(void **state) ++{ ++ rf_helper("2 another\t test", "another\t test", "0", 0); ++} ++ ++static void test_rf_remove1(void **state) ++{ ++ rf_helper("2 feature1 feature2", "feature2", "1 feature1", 0); ++} ++ ++static void test_rf_remove2(void **state) ++{ ++ rf_helper("2 feature1 feature2", "feature1", "1 feature2", 0); ++} ++ ++static void test_rf_remove3(void **state) ++{ ++ rf_helper("3 test1 test\ttest2", "test", "2 test1 test2", 0); ++} ++ ++static void test_rf_remove4(void **state) ++{ ++ rf_helper("4 this\t is a test", "is a", "2 this\t test", 0); ++} ++ ++static void test_rf_remove5(void **state) ++{ ++ rf_helper("3 one more test", "more test", "1 one", 0); ++} ++ ++static int test_remove_features(void) ++{ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test(test_rf_null_features_ptr), ++ cmocka_unit_test(test_rf_null_features), ++ cmocka_unit_test(test_rf_null_removal), ++ cmocka_unit_test(test_rf_empty_removal), ++ cmocka_unit_test(test_rf_invalid_removal1), ++ cmocka_unit_test(test_rf_invalid_removal2), ++ cmocka_unit_test(test_rf_invalid_removal3), ++ cmocka_unit_test(test_rf_invalid_removal4), ++ cmocka_unit_test(test_rf_bad_features1), ++ cmocka_unit_test(test_rf_bad_features2), ++ cmocka_unit_test(test_rf_missing_removal1), ++ cmocka_unit_test(test_rf_missing_removal2), ++ cmocka_unit_test(test_rf_missing_removal3), ++ cmocka_unit_test(test_rf_missing_removal4), ++ cmocka_unit_test(test_rf_missing_removal5), ++ cmocka_unit_test(test_rf_remove_all_features1), ++ cmocka_unit_test(test_rf_remove_all_features2), ++ cmocka_unit_test(test_rf_remove1), ++ cmocka_unit_test(test_rf_remove2), ++ cmocka_unit_test(test_rf_remove3), ++ cmocka_unit_test(test_rf_remove4), ++ cmocka_unit_test(test_rf_remove5), ++ }; ++ return cmocka_run_group_tests(tests, NULL, NULL); ++} ++ ++int main(void) ++{ ++ int ret = 0; ++ ++ ret += test_add_features(); ++ ret += test_remove_features(); ++ ++ return ret; ++} diff --git a/SOURCES/0120-libmultipath-fix-queue_mode-feature-handling.patch b/SOURCES/0120-libmultipath-fix-queue_mode-feature-handling.patch new file mode 100644 index 0000000..b5b8b02 --- /dev/null +++ b/SOURCES/0120-libmultipath-fix-queue_mode-feature-handling.patch @@ -0,0 +1,212 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 7 Oct 2022 12:35:40 -0500 +Subject: [PATCH] libmultipath: fix queue_mode feature handling + +device-mapper is not able to change the queue_mode on a table reload. +Make sure that when multipath sets up the map, both on regular reloads +and reconfigures, it keeps the queue_mode the same. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/configure.c | 4 +++ + libmultipath/dmparser.c | 2 ++ + libmultipath/propsel.c | 55 ++++++++++++++++++++++++++++++++++++++ + libmultipath/structs.h | 7 +++++ + libmultipath/util.c | 10 +++++++ + libmultipath/util.h | 1 + + multipath/multipath.conf.5 | 7 +++-- + 7 files changed, 84 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index 6cad0468..287289f7 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -1102,6 +1102,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, + uint64_t *size_mismatch_seen; + bool map_processed = false; + bool no_daemon = false; ++ struct multipath * cmpp; + + /* ignore refwwid if it's empty */ + if (refwwid && !strlen(refwwid)) +@@ -1197,6 +1198,9 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, + } + verify_paths(mpp, vecs); + ++ cmpp = find_mp_by_wwid(curmp, mpp->wwid); ++ if (cmpp) ++ mpp->queue_mode = cmpp->queue_mode; + params[0] = '\0'; + if (setup_map(mpp, params, PARAMS_SIZE, vecs)) { + remove_map(mpp, vecs, 0); +diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c +index b856a07f..b9c4dabc 100644 +--- a/libmultipath/dmparser.c ++++ b/libmultipath/dmparser.c +@@ -164,6 +164,8 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp, + + FREE(word); + } ++ mpp->queue_mode = strstr(mpp->features, "queue_mode bio") ? ++ QUEUE_MODE_BIO : QUEUE_MODE_RQ; + + /* + * hwhandler +diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c +index be79902f..3f119dd9 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -26,6 +26,7 @@ + #include "propsel.h" + #include + #include ++#include + + pgpolicyfn *pgpolicies[] = { + NULL, +@@ -413,6 +414,59 @@ void reconcile_features_with_options(const char *id, char **features, int* no_pa + } + } + ++static void reconcile_features_with_queue_mode(struct multipath *mp) ++{ ++ char *space = NULL, *val = NULL, *mode_str = NULL, *feat; ++ int features_mode = QUEUE_MODE_UNDEF; ++ ++ if (!mp->features) ++ return; ++ ++ pthread_cleanup_push(cleanup_free_ptr, &space); ++ pthread_cleanup_push(cleanup_free_ptr, &val); ++ pthread_cleanup_push(cleanup_free_ptr, &mode_str); ++ ++ if (!(feat = strstr(mp->features, "queue_mode")) || ++ feat == mp->features || !isspace(*(feat - 1)) || ++ sscanf(feat, "queue_mode%m[ \f\n\r\t\v]%ms", &space, &val) != 2) ++ goto sync_mode; ++ if (asprintf(&mode_str, "queue_mode%s%s", space, val) < 0) { ++ condlog(1, "failed to allocate space for queue_mode feature string"); ++ mode_str = NULL; /* value undefined on failure */ ++ goto exit; ++ } ++ ++ if (!strcmp(val, "rq") || !strcmp(val, "mq")) ++ features_mode = QUEUE_MODE_RQ; ++ else if (!strcmp(val, "bio")) ++ features_mode = QUEUE_MODE_BIO; ++ if (features_mode == QUEUE_MODE_UNDEF) { ++ condlog(2, "%s: ignoring invalid feature '%s'", ++ mp->alias, mode_str); ++ goto sync_mode; ++ } ++ ++ if (mp->queue_mode == QUEUE_MODE_UNDEF) ++ mp->queue_mode = features_mode; ++ if (mp->queue_mode == features_mode) ++ goto exit; ++ ++ condlog(2, ++ "%s: ignoring feature '%s' because queue_mode is set to '%s'", ++ mp->alias, mode_str, ++ (mp->queue_mode == QUEUE_MODE_RQ)? "rq" : "bio"); ++ ++sync_mode: ++ if (mode_str) ++ remove_feature(&mp->features, mode_str); ++ if (mp->queue_mode == QUEUE_MODE_BIO) ++ add_feature(&mp->features, "queue_mode bio"); ++exit: ++ pthread_cleanup_pop(1); ++ pthread_cleanup_pop(1); ++ pthread_cleanup_pop(1); ++} ++ + int select_features(struct config *conf, struct multipath *mp) + { + const char *origin; +@@ -428,6 +482,7 @@ out: + reconcile_features_with_options(mp->alias, &mp->features, + &mp->no_path_retry, + &mp->retain_hwhandler); ++ reconcile_features_with_queue_mode(mp); + condlog(3, "%s: features = \"%s\" %s", mp->alias, mp->features, origin); + return 0; + } +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 3ed5cfc1..9a404da7 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -187,6 +187,12 @@ enum max_sectors_kb_states { + MAX_SECTORS_KB_MIN = 4, /* can't be smaller than page size */ + }; + ++enum queue_mode_states { ++ QUEUE_MODE_UNDEF = 0, ++ QUEUE_MODE_BIO, ++ QUEUE_MODE_RQ, ++}; ++ + enum scsi_protocol { + SCSI_PROTOCOL_FCP = 0, /* Fibre Channel */ + SCSI_PROTOCOL_SPI = 1, /* parallel SCSI */ +@@ -397,6 +403,7 @@ struct multipath { + int needs_paths_uevent; + int ghost_delay; + int ghost_delay_tick; ++ int queue_mode; + uid_t uid; + gid_t gid; + mode_t mode; +diff --git a/libmultipath/util.c b/libmultipath/util.c +index dd30a46e..e04d20ab 100644 +--- a/libmultipath/util.c ++++ b/libmultipath/util.c +@@ -465,6 +465,16 @@ void free_scandir_result(struct scandir_result *res) + FREE(res->di); + } + ++void cleanup_free_ptr(void *arg) ++{ ++ void **p = arg; ++ ++ if (p && *p) { ++ free(*p); ++ *p = NULL; ++ } ++} ++ + void close_fd(void *arg) + { + close((long)arg); +diff --git a/libmultipath/util.h b/libmultipath/util.h +index ce277680..f898c829 100644 +--- a/libmultipath/util.h ++++ b/libmultipath/util.h +@@ -44,6 +44,7 @@ void set_max_fds(rlim_t max_fds); + pthread_cleanup_push(((void (*)(void *))&f), (arg)) + + void close_fd(void *arg); ++void cleanup_free_ptr(void *arg); + void cleanup_mutex(void *arg); + + struct scandir_result { +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 8e418372..61d2712b 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -458,8 +458,11 @@ precedence. See KNOWN ISSUES. + can be \fIbio\fR, \fIrq\fR or \fImq\fR, which corresponds to + bio-based, request-based, and block-multiqueue (blk-mq) request-based, + respectively. +-The default depends on the kernel parameter \fBdm_mod.use_blk_mq\fR. It is +-\fImq\fR if the latter is set, and \fIrq\fR otherwise. ++Before kernel 4.20 The default depends on the kernel parameter ++\fBdm_mod.use_blk_mq\fR. It is \fImq\fR if the latter is set, and \fIrq\fR ++otherwise. Since kernel 4.20, \fIrq\fR and \fImq\fR both correspond to ++block-multiqueue. Once a multipath device has been created, its queue_mode ++cannot be changed. + .TP + The default is: \fB\fR + .RE diff --git a/SOURCES/0121-multipath-tests-tests-for-reconcile_features_with_qu.patch b/SOURCES/0121-multipath-tests-tests-for-reconcile_features_with_qu.patch new file mode 100644 index 0000000..d8da724 --- /dev/null +++ b/SOURCES/0121-multipath-tests-tests-for-reconcile_features_with_qu.patch @@ -0,0 +1,290 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 7 Oct 2022 12:35:41 -0500 +Subject: [PATCH] multipath tests: tests for reconcile_features_with_queue_mode + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + tests/Makefile | 2 + + tests/features.c | 232 ++++++++++++++++++++++++++++++++++++++++++++++- + 2 files changed, 233 insertions(+), 1 deletion(-) + +diff --git a/tests/Makefile b/tests/Makefile +index 914413b8..f3e49487 100644 +--- a/tests/Makefile ++++ b/tests/Makefile +@@ -29,6 +29,7 @@ endif + ifneq ($(DIO_TEST_DEV),) + directio-test_FLAGS := -DDIO_TEST_DEV=\"$(DIO_TEST_DEV)\" + endif ++features-test_FLAGS := -I$(multipathdir)/nvme + + # test-specific linker flags + # XYZ-test_TESTDEPS: test libraries containing __wrap_xyz functions +@@ -53,6 +54,7 @@ alias-test_LIBDEPS := -lpthread -ldl + ifneq ($(DIO_TEST_DEV),) + directio-test_LIBDEPS := -laio + endif ++features-test_LIBDEPS := -ludev -lpthread + + %.o: %.c + $(CC) $(CFLAGS) $($*-test_FLAGS) -c -o $@ $< +diff --git a/tests/features.c b/tests/features.c +index 1e2e6bff..01fbccb7 100644 +--- a/tests/features.c ++++ b/tests/features.c +@@ -1,9 +1,10 @@ ++#define _GNU_SOURCE + #include + #include + #include + #include + +-#include "structs.h" ++#include "../libmultipath/propsel.c" + #include "globals.c" + + static void test_af_null_features_ptr(void **state) +@@ -307,12 +308,241 @@ static int test_remove_features(void) + return cmocka_run_group_tests(tests, NULL, NULL); + } + ++static void test_cf_null_features(void **state) ++{ ++ struct multipath mp = { ++ .alias = "test", ++ }; ++ reconcile_features_with_queue_mode(&mp); ++ assert_null(mp.features); ++} ++ ++static void cf_helper(const char *features_start, const char *features_end, ++ int queue_mode_start, int queue_mode_end) ++{ ++ struct multipath mp = { ++ .alias = "test", ++ .features = strdup(features_start), ++ .queue_mode = queue_mode_start, ++ }; ++ char *orig = mp.features; ++ ++ assert_non_null(orig); ++ reconcile_features_with_queue_mode(&mp); ++ if (!features_end) ++ assert_ptr_equal(orig, mp.features); ++ else ++ assert_string_equal(mp.features, features_end); ++ free(mp.features); ++ assert_int_equal(mp.queue_mode, queue_mode_end); ++} ++ ++static void test_cf_unset_unset1(void **state) ++{ ++ cf_helper("0", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_UNDEF); ++} ++ ++static void test_cf_unset_unset2(void **state) ++{ ++ cf_helper("1 queue_mode", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_UNDEF); ++} ++ ++static void test_cf_unset_unset3(void **state) ++{ ++ cf_helper("queue_mode", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_UNDEF); ++} ++ ++static void test_cf_unset_unset4(void **state) ++{ ++ cf_helper("2 queue_model bio", NULL, QUEUE_MODE_UNDEF, ++ QUEUE_MODE_UNDEF); ++} ++ ++static void test_cf_unset_unset5(void **state) ++{ ++ cf_helper("1 queue_if_no_path", NULL, QUEUE_MODE_UNDEF, ++ QUEUE_MODE_UNDEF); ++} ++ ++static void test_cf_invalid_unset1(void **state) ++{ ++ cf_helper("2 queue_mode biop", "0", QUEUE_MODE_UNDEF, QUEUE_MODE_UNDEF); ++} ++ ++static void test_cf_invalid_unset2(void **state) ++{ ++ cf_helper("3 queue_mode rqs queue_if_no_path", "1 queue_if_no_path", ++ QUEUE_MODE_UNDEF, QUEUE_MODE_UNDEF); ++} ++ ++static void test_cf_rq_unset1(void **state) ++{ ++ cf_helper("2 queue_mode rq", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_RQ); ++} ++ ++static void test_cf_rq_unset2(void **state) ++{ ++ cf_helper("2 queue_mode mq", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_RQ); ++} ++ ++static void test_cf_bio_unset(void **state) ++{ ++ cf_helper("2 queue_mode bio", NULL, QUEUE_MODE_UNDEF, QUEUE_MODE_BIO); ++} ++ ++static void test_cf_unset_bio1(void **state) ++{ ++ cf_helper("1 queue_if_no_path", "3 queue_if_no_path queue_mode bio", ++ QUEUE_MODE_BIO, QUEUE_MODE_BIO); ++} ++ ++static void test_cf_unset_bio2(void **state) ++{ ++ cf_helper("0", "2 queue_mode bio", QUEUE_MODE_BIO, QUEUE_MODE_BIO); ++} ++ ++static void test_cf_unset_bio3(void **state) ++{ ++ cf_helper("2 pg_init_retries 50", "4 pg_init_retries 50 queue_mode bio", ++ QUEUE_MODE_BIO, QUEUE_MODE_BIO); ++} ++ ++static void test_cf_invalid_bio1(void **state) ++{ ++ cf_helper("2 queue_mode bad", "2 queue_mode bio", ++ QUEUE_MODE_BIO, QUEUE_MODE_BIO); ++} ++ ++static void test_cf_invalid_bio2(void **state) ++{ ++ cf_helper("3 queue_if_no_path queue_mode\tbad", "3 queue_if_no_path queue_mode bio", ++ QUEUE_MODE_BIO, QUEUE_MODE_BIO); ++} ++ ++static void test_cf_bio_bio1(void **state) ++{ ++ cf_helper("2 queue_mode bio", NULL, QUEUE_MODE_BIO, QUEUE_MODE_BIO); ++} ++ ++static void test_cf_bio_bio2(void **state) ++{ ++ cf_helper("3 queue_if_no_path queue_mode bio", NULL, QUEUE_MODE_BIO, QUEUE_MODE_BIO); ++} ++ ++static void test_cf_bio_bio3(void **state) ++{ ++ cf_helper("3 queue_mode\nbio queue_if_no_path", NULL, QUEUE_MODE_BIO, QUEUE_MODE_BIO); ++} ++ ++static void test_cf_bio_rq1(void **state) ++{ ++ cf_helper("2\nqueue_mode\tbio", "0", QUEUE_MODE_RQ, QUEUE_MODE_RQ); ++} ++ ++static void test_cf_bio_rq2(void **state) ++{ ++ cf_helper("3 queue_if_no_path\nqueue_mode bio", "1 queue_if_no_path", ++ QUEUE_MODE_RQ, QUEUE_MODE_RQ); ++} ++ ++static void test_cf_bio_rq3(void **state) ++{ ++ cf_helper("4 queue_mode bio pg_init_retries 20", "2 pg_init_retries 20", ++ QUEUE_MODE_RQ, QUEUE_MODE_RQ); ++} ++ ++static void test_cf_unset_rq1(void **state) ++{ ++ cf_helper("0", NULL, QUEUE_MODE_RQ, QUEUE_MODE_RQ); ++} ++ ++static void test_cf_unset_rq2(void **state) ++{ ++ cf_helper("2 pg_init_retries 15", NULL, QUEUE_MODE_RQ, QUEUE_MODE_RQ); ++} ++ ++static void test_cf_invalid_rq1(void **state) ++{ ++ cf_helper("2 queue_mode bionic", "0", QUEUE_MODE_RQ, QUEUE_MODE_RQ); ++} ++ ++static void test_cf_invalid_rq2(void **state) ++{ ++ cf_helper("3 queue_mode b\nqueue_if_no_path", "1 queue_if_no_path", ++ QUEUE_MODE_RQ, QUEUE_MODE_RQ); ++} ++ ++static void test_cf_rq_rq1(void **state) ++{ ++ cf_helper("2 queue_mode rq", NULL, QUEUE_MODE_RQ, QUEUE_MODE_RQ); ++} ++ ++static void test_cf_rq_rq2(void **state) ++{ ++ cf_helper("3 queue_mode\t \trq\nqueue_if_no_path", NULL, QUEUE_MODE_RQ, QUEUE_MODE_RQ); ++} ++ ++static void test_cf_rq_bio1(void **state) ++{ ++ cf_helper("2 queue_mode rq", "2 queue_mode bio", QUEUE_MODE_BIO, ++ QUEUE_MODE_BIO); ++} ++ ++static void test_cf_rq_bio2(void **state) ++{ ++ cf_helper("3 queue_if_no_path\nqueue_mode rq", "3 queue_if_no_path queue_mode bio", QUEUE_MODE_BIO, QUEUE_MODE_BIO); ++} ++ ++static void test_cf_rq_bio3(void **state) ++{ ++ cf_helper("3 queue_mode rq\nqueue_if_no_path", "3 queue_if_no_path queue_mode bio", QUEUE_MODE_BIO, QUEUE_MODE_BIO); ++} ++ ++static int test_reconcile_features(void) ++{ ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test(test_cf_null_features), ++ cmocka_unit_test(test_cf_unset_unset1), ++ cmocka_unit_test(test_cf_unset_unset2), ++ cmocka_unit_test(test_cf_unset_unset3), ++ cmocka_unit_test(test_cf_unset_unset4), ++ cmocka_unit_test(test_cf_unset_unset5), ++ cmocka_unit_test(test_cf_invalid_unset1), ++ cmocka_unit_test(test_cf_invalid_unset2), ++ cmocka_unit_test(test_cf_rq_unset1), ++ cmocka_unit_test(test_cf_rq_unset2), ++ cmocka_unit_test(test_cf_bio_unset), ++ cmocka_unit_test(test_cf_unset_bio1), ++ cmocka_unit_test(test_cf_unset_bio2), ++ cmocka_unit_test(test_cf_unset_bio3), ++ cmocka_unit_test(test_cf_invalid_bio1), ++ cmocka_unit_test(test_cf_invalid_bio2), ++ cmocka_unit_test(test_cf_bio_bio1), ++ cmocka_unit_test(test_cf_bio_bio2), ++ cmocka_unit_test(test_cf_bio_bio3), ++ cmocka_unit_test(test_cf_bio_rq1), ++ cmocka_unit_test(test_cf_bio_rq2), ++ cmocka_unit_test(test_cf_bio_rq3), ++ cmocka_unit_test(test_cf_unset_rq1), ++ cmocka_unit_test(test_cf_unset_rq2), ++ cmocka_unit_test(test_cf_invalid_rq1), ++ cmocka_unit_test(test_cf_invalid_rq2), ++ cmocka_unit_test(test_cf_rq_rq1), ++ cmocka_unit_test(test_cf_rq_rq2), ++ cmocka_unit_test(test_cf_rq_bio1), ++ cmocka_unit_test(test_cf_rq_bio2), ++ cmocka_unit_test(test_cf_rq_bio3), ++ }; ++ return cmocka_run_group_tests(tests, NULL, NULL); ++} ++ + int main(void) + { + int ret = 0; + + ret += test_add_features(); + ret += test_remove_features(); ++ ret += test_reconcile_features(); + + return ret; + } diff --git a/SOURCES/0122-libmultipath-prepare-proto_id-for-use-by-non-scsi-de.patch b/SOURCES/0122-libmultipath-prepare-proto_id-for-use-by-non-scsi-de.patch new file mode 100644 index 0000000..b725c15 --- /dev/null +++ b/SOURCES/0122-libmultipath-prepare-proto_id-for-use-by-non-scsi-de.patch @@ -0,0 +1,143 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 7 Oct 2022 12:35:42 -0500 +Subject: [PATCH] libmultipath: prepare proto_id for use by non-scsi devivces + +Make sure that when we are checking for a scsi protocol, we are first +checking that we are working with a scsi path. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/configure.c | 9 +++++---- + libmultipath/discovery.c | 13 ++++++++----- + libmultipath/print.c | 6 ++++-- + libmultipath/structs.c | 2 +- + libmultipath/structs.h | 4 +++- + multipathd/fpin_handlers.c | 2 +- + 6 files changed, 22 insertions(+), 14 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index 287289f7..8e1bc488 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -223,10 +223,11 @@ int rr_optimize_path_order(struct pathgroup *pgp) + + total_paths = VECTOR_SIZE(pgp->paths); + vector_foreach_slot(pgp->paths, pp, i) { +- if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP && +- pp->sg_id.proto_id != SCSI_PROTOCOL_SAS && +- pp->sg_id.proto_id != SCSI_PROTOCOL_ISCSI && +- pp->sg_id.proto_id != SCSI_PROTOCOL_SRP) { ++ if (pp->bus != SYSFS_BUS_SCSI || ++ (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP && ++ pp->sg_id.proto_id != SCSI_PROTOCOL_SAS && ++ pp->sg_id.proto_id != SCSI_PROTOCOL_ISCSI && ++ pp->sg_id.proto_id != SCSI_PROTOCOL_SRP)) { + /* return success as default path order + * is maintained in path group + */ +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index 36cc389e..5f4e0794 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -468,10 +468,11 @@ int sysfs_get_host_adapter_name(const struct path *pp, char *adapter_name) + + proto_id = pp->sg_id.proto_id; + +- if (proto_id != SCSI_PROTOCOL_FCP && +- proto_id != SCSI_PROTOCOL_SAS && +- proto_id != SCSI_PROTOCOL_ISCSI && +- proto_id != SCSI_PROTOCOL_SRP) { ++ if (pp->bus != SYSFS_BUS_SCSI || ++ (proto_id != SCSI_PROTOCOL_FCP && ++ proto_id != SCSI_PROTOCOL_SAS && ++ proto_id != SCSI_PROTOCOL_ISCSI && ++ proto_id != SCSI_PROTOCOL_SRP)) { + return 1; + } + /* iscsi doesn't have adapter info in sysfs +@@ -1722,8 +1723,10 @@ sysfs_pathinfo(struct path * pp, vector hwtable) + pp->bus = SYSFS_BUS_CCISS; + if (!strncmp(pp->dev,"dasd", 4)) + pp->bus = SYSFS_BUS_CCW; +- if (!strncmp(pp->dev,"sd", 2)) ++ if (!strncmp(pp->dev,"sd", 2)) { + pp->bus = SYSFS_BUS_SCSI; ++ pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC; ++ } + if (!strncmp(pp->dev,"nvme", 4)) + pp->bus = SYSFS_BUS_NVME; + +diff --git a/libmultipath/print.c b/libmultipath/print.c +index 8a6fbe83..8a85df66 100644 +--- a/libmultipath/print.c ++++ b/libmultipath/print.c +@@ -558,7 +558,8 @@ snprint_host_attr (char * buff, size_t len, const struct path * pp, char *attr) + const char *value = NULL; + int ret; + +- if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP) ++ if (pp->bus != SYSFS_BUS_SCSI || ++ pp->sg_id.proto_id != SCSI_PROTOCOL_FCP) + return snprintf(buff, len, "[undef]"); + sprintf(host_id, "host%d", pp->sg_id.host_no); + host_dev = udev_device_new_from_subsystem_sysname(udev, "fc_host", +@@ -597,7 +598,8 @@ snprint_tgt_wwpn (char * buff, size_t len, const struct path * pp) + const char *value = NULL; + int ret; + +- if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP) ++ if (pp->bus != SYSFS_BUS_SCSI || ++ pp->sg_id.proto_id != SCSI_PROTOCOL_FCP) + return snprintf(buff, len, "[undef]"); + sprintf(rport_id, "rport-%d:%d-%d", + pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id); +diff --git a/libmultipath/structs.c b/libmultipath/structs.c +index 84f9c959..1122cfae 100644 +--- a/libmultipath/structs.c ++++ b/libmultipath/structs.c +@@ -115,7 +115,7 @@ alloc_path (void) + pp->sg_id.channel = -1; + pp->sg_id.scsi_id = -1; + pp->sg_id.lun = -1; +- pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC; ++ pp->sg_id.proto_id = PROTOCOL_UNSET; + pp->fd = -1; + pp->tpgs = TPGS_UNDEF; + pp->priority = PRIO_UNDEF; +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 9a404da7..960ea024 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -193,6 +193,8 @@ enum queue_mode_states { + QUEUE_MODE_RQ, + }; + ++#define PROTOCOL_UNSET -1 ++ + enum scsi_protocol { + SCSI_PROTOCOL_FCP = 0, /* Fibre Channel */ + SCSI_PROTOCOL_SPI = 1, /* parallel SCSI */ +@@ -294,7 +296,7 @@ struct sg_id { + int lun; + short h_cmd_per_lun; + short d_queue_depth; +- enum scsi_protocol proto_id; ++ int proto_id; + int transport_id; + }; + +diff --git a/multipathd/fpin_handlers.c b/multipathd/fpin_handlers.c +index b14366d7..599f2893 100644 +--- a/multipathd/fpin_handlers.c ++++ b/multipathd/fpin_handlers.c +@@ -220,7 +220,7 @@ static int fpin_chk_wwn_setpath_marginal(uint16_t host_num, struct vectors *ve + + vector_foreach_slot(vecs->pathvec, pp, k) { + /* Checks the host number and also for the SCSI FCP */ +- if (pp->sg_id.proto_id != SCSI_PROTOCOL_FCP || host_num != pp->sg_id.host_no) ++ if (pp->bus != SYSFS_BUS_SCSI || pp->sg_id.proto_id != SCSI_PROTOCOL_FCP || host_num != pp->sg_id.host_no) + continue; + sprintf(rport_id, "rport-%d:%d-%d", + pp->sg_id.host_no, pp->sg_id.channel, pp->sg_id.transport_id); diff --git a/SOURCES/0123-libmultipath-get-nvme-path-transport-protocol.patch b/SOURCES/0123-libmultipath-get-nvme-path-transport-protocol.patch new file mode 100644 index 0000000..b64c392 --- /dev/null +++ b/SOURCES/0123-libmultipath-get-nvme-path-transport-protocol.patch @@ -0,0 +1,194 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 7 Oct 2022 12:35:43 -0500 +Subject: [PATCH] libmultipath: get nvme path transport protocol + +Read the transport protocol from /sys/block/nvmeXnY/device/transport. +Update protocol_name[] and bus_protocol_id() to store the nvme protocol +names after the scsi protocol names. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/discovery.c | 18 ++++++++++++++++-- + libmultipath/structs.c | 22 +++++++++++++++++----- + libmultipath/structs.h | 33 +++++++++++++++++++++------------ + multipath/multipath.conf.5 | 10 +++++++--- + 4 files changed, 61 insertions(+), 22 deletions(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index 5f4e0794..eb7a634b 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -1455,6 +1455,7 @@ nvme_sysfs_pathinfo (struct path * pp, vector hwtable) + struct udev_device *parent; + const char *attr_path = NULL; + const char *attr; ++ int i; + + attr_path = udev_device_get_sysname(pp->udev); + if (!attr_path) +@@ -1476,6 +1477,18 @@ nvme_sysfs_pathinfo (struct path * pp, vector hwtable) + attr = udev_device_get_sysattr_value(parent, "cntlid"); + pp->sg_id.channel = attr ? atoi(attr) : 0; + ++ attr = udev_device_get_sysattr_value(parent, "transport"); ++ if (attr) { ++ for (i = 0; i < NVME_PROTOCOL_UNSPEC; i++){ ++ if (protocol_name[SYSFS_BUS_NVME + i] && ++ !strcmp(attr, ++ protocol_name[SYSFS_BUS_NVME + i] + 5)) { ++ pp->sg_id.proto_id = i; ++ break; ++ } ++ } ++ } ++ + snprintf(pp->vendor_id, SCSI_VENDOR_SIZE, "NVME"); + snprintf(pp->product_id, PATH_PRODUCT_SIZE, "%s", + udev_device_get_sysattr_value(parent, "model")); +@@ -1727,9 +1740,10 @@ sysfs_pathinfo(struct path * pp, vector hwtable) + pp->bus = SYSFS_BUS_SCSI; + pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC; + } +- if (!strncmp(pp->dev,"nvme", 4)) ++ if (!strncmp(pp->dev,"nvme", 4)) { + pp->bus = SYSFS_BUS_NVME; +- ++ pp->sg_id.proto_id = NVME_PROTOCOL_UNSPEC; ++ } + switch (pp->bus) { + case SYSFS_BUS_SCSI: + return scsi_sysfs_pathinfo(pp, hwtable); +diff --git a/libmultipath/structs.c b/libmultipath/structs.c +index 1122cfae..7bdf9152 100644 +--- a/libmultipath/structs.c ++++ b/libmultipath/structs.c +@@ -25,7 +25,6 @@ const char * const protocol_name[LAST_BUS_PROTOCOL_ID + 1] = { + [SYSFS_BUS_UNDEF] = "undef", + [SYSFS_BUS_CCW] = "ccw", + [SYSFS_BUS_CCISS] = "cciss", +- [SYSFS_BUS_NVME] = "nvme", + [SYSFS_BUS_SCSI + SCSI_PROTOCOL_FCP] = "scsi:fcp", + [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SPI] = "scsi:spi", + [SYSFS_BUS_SCSI + SCSI_PROTOCOL_SSA] = "scsi:ssa", +@@ -37,6 +36,13 @@ const char * const protocol_name[LAST_BUS_PROTOCOL_ID + 1] = { + [SYSFS_BUS_SCSI + SCSI_PROTOCOL_ATA] = "scsi:ata", + [SYSFS_BUS_SCSI + SCSI_PROTOCOL_USB] = "scsi:usb", + [SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC] = "scsi:unspec", ++ [SYSFS_BUS_NVME + NVME_PROTOCOL_PCIE] = "nvme:pcie", ++ [SYSFS_BUS_NVME + NVME_PROTOCOL_RDMA] = "nvme:rdma", ++ [SYSFS_BUS_NVME + NVME_PROTOCOL_FC] = "nvme:fc", ++ [SYSFS_BUS_NVME + NVME_PROTOCOL_TCP] = "nvme:tcp", ++ [SYSFS_BUS_NVME + NVME_PROTOCOL_LOOP] = "nvme:loop", ++ [SYSFS_BUS_NVME + NVME_PROTOCOL_APPLE_NVME] = "nvme:apple-nvme", ++ [SYSFS_BUS_NVME + NVME_PROTOCOL_UNSPEC] = "nvme:unspec", + }; + + struct adapter_group * +@@ -716,11 +722,17 @@ out: + } + + unsigned int bus_protocol_id(const struct path *pp) { +- if (!pp || pp->bus < 0 || pp->bus > SYSFS_BUS_SCSI) ++ if (!pp || pp->bus < 0 || pp->bus > SYSFS_BUS_NVME) + return SYSFS_BUS_UNDEF; +- if (pp->bus != SYSFS_BUS_SCSI) ++ if (pp->bus != SYSFS_BUS_SCSI && pp->bus != SYSFS_BUS_NVME) + return pp->bus; +- if ((int)pp->sg_id.proto_id < 0 || pp->sg_id.proto_id > SCSI_PROTOCOL_UNSPEC) ++ if (pp->sg_id.proto_id < 0) + return SYSFS_BUS_UNDEF; +- return SYSFS_BUS_SCSI + pp->sg_id.proto_id; ++ if (pp->bus == SYSFS_BUS_SCSI && ++ pp->sg_id.proto_id > SCSI_PROTOCOL_UNSPEC) ++ return SYSFS_BUS_UNDEF; ++ if (pp->bus == SYSFS_BUS_NVME && ++ pp->sg_id.proto_id > NVME_PROTOCOL_UNSPEC) ++ return SYSFS_BUS_UNDEF; ++ return pp->bus + pp->sg_id.proto_id; + } +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 960ea024..9130efb5 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -57,15 +57,6 @@ enum failback_mode { + FAILBACK_FOLLOWOVER + }; + +-/* SYSFS_BUS_SCSI should be last, see bus_protocol_id() */ +-enum sysfs_buses { +- SYSFS_BUS_UNDEF, +- SYSFS_BUS_CCW, +- SYSFS_BUS_CCISS, +- SYSFS_BUS_NVME, +- SYSFS_BUS_SCSI, +-}; +- + enum pathstates { + PSTATE_UNDEF, + PSTATE_FAILED, +@@ -207,14 +198,32 @@ enum scsi_protocol { + SCSI_PROTOCOL_ATA = 8, + SCSI_PROTOCOL_USB = 9, /* USB Attached SCSI (UAS), and others */ + SCSI_PROTOCOL_UNSPEC = 0xa, /* No specific protocol */ ++ SCSI_PROTOCOL_END = 0xb, /* offset of the next sysfs_buses entry */ ++}; ++ ++/* values from /sys/class/nvme/nvmeX */ ++enum nvme_protocol { ++ NVME_PROTOCOL_PCIE = 0, ++ NVME_PROTOCOL_RDMA = 1, ++ NVME_PROTOCOL_FC = 2, ++ NVME_PROTOCOL_TCP = 3, ++ NVME_PROTOCOL_LOOP = 4, ++ NVME_PROTOCOL_APPLE_NVME = 5, ++ NVME_PROTOCOL_UNSPEC = 6, /* unknown protocol */ ++}; ++ ++enum sysfs_buses { ++ SYSFS_BUS_UNDEF, ++ SYSFS_BUS_CCW, ++ SYSFS_BUS_CCISS, ++ SYSFS_BUS_SCSI, ++ SYSFS_BUS_NVME = SYSFS_BUS_SCSI + SCSI_PROTOCOL_END, + }; + + /* + * Linear ordering of bus/protocol +- * This assumes that SYSFS_BUS_SCSI is last in enum sysfs_buses +- * SCSI is the only bus type for which we distinguish protocols. + */ +-#define LAST_BUS_PROTOCOL_ID (SYSFS_BUS_SCSI + SCSI_PROTOCOL_UNSPEC) ++#define LAST_BUS_PROTOCOL_ID (SYSFS_BUS_NVME + NVME_PROTOCOL_UNSPEC) + unsigned int bus_protocol_id(const struct path *pp); + extern const char * const protocol_name[]; + +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 61d2712b..1f5a40b6 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -1369,7 +1369,9 @@ Regular expression for the protocol of a device to be excluded/included. + The protocol strings that multipath recognizes are \fIscsi:fcp\fR, + \fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR, + \fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR, +-\fIscsi:unspec\fR, \fIccw\fR, \fIcciss\fR, \fInvme\fR, and \fIundef\fR. ++\fIscsi:unspec\fR, \fInvme:pcie\fR, \fInvme:rdma\fR, \fInvme:fc\fR, ++\fInvme:tcp\fR, \fInvme:loop\fR, \fInvme:apple-nvme\fR, \fInvme:unspec\fR, ++\fIccw\fR, \fIcciss\fR, and \fIundef\fR. + The protocol that a path is using can be viewed by running + \fBmultipathd show paths format "%d %P"\fR + .RE +@@ -1757,8 +1759,10 @@ The protocol subsection recognizes the following mandatory attribute: + The protocol string of the path device. The possible values are \fIscsi:fcp\fR, + \fIscsi:spi\fR, \fIscsi:ssa\fR, \fIscsi:sbp\fR, \fIscsi:srp\fR, + \fIscsi:iscsi\fR, \fIscsi:sas\fR, \fIscsi:adt\fR, \fIscsi:ata\fR, +-\fIscsi:unspec\fR, \fIccw\fR, \fIcciss\fR, \fInvme\fR, and \fIundef\fR. This is +-\fBnot\fR a regular expression. the path device protcol string must match ++\fIscsi:unspec\fR, \fInvme:pcie\fR, \fInvme:rdma\fR, \fInvme:fc\fR, ++\fInvme:tcp\fR, \fInvme:loop\fR, \fInvme:apple-nvme\fR, \fInvme:unspec\fR, ++\fIccw\fR, \fIcciss\fR, and \fIundef\fR. This is ++\fBnot\fR a regular expression. the path device protocol string must match + exactly. The protocol that a path is using can be viewed by running + \fBmultipathd show paths format "%d %P"\fR + .LP diff --git a/SOURCES/0124-libmultipath-enforce-queue_mode-bio-for-nmve-tcp-pat.patch b/SOURCES/0124-libmultipath-enforce-queue_mode-bio-for-nmve-tcp-pat.patch new file mode 100644 index 0000000..61169b6 --- /dev/null +++ b/SOURCES/0124-libmultipath-enforce-queue_mode-bio-for-nmve-tcp-pat.patch @@ -0,0 +1,111 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 7 Oct 2022 12:35:44 -0500 +Subject: [PATCH] libmultipath: enforce queue_mode bio for nmve:tcp paths + +nvme:tcp devices set BLK_MQ_F_BLOCKING (they are the only block devices +which multipath supports that do so), meaning that block_mq expects that +they can block at certain points while servicing a request. However, +due to the way device-mapper sets up its queue, it is not able to set +BLK_MQ_F_BLOCKING when it includes paths that set this flag. Patches +were written to address this issue but they were rejected upstream + +https://lore.kernel.org/linux-block/YcH%2FE4JNag0QYYAa@infradead.org/T/#t + +The proposed solution was to have multipath use the bio queue_mode for +multipath devices that include nvme:tcp paths. + +Multipath devices now automatically add the "queue_mode bio" feature if +they include nvme:tcp paths. If a multipath devices was created with +"queue_mode rq", it will disallow the addition of nvme:tcp paths. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/configure.c | 17 ++++++++++++++++- + libmultipath/structs_vec.c | 7 +++++++ + multipath/multipath.conf.5 | 4 +++- + 3 files changed, 26 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index 8e1bc488..c341793c 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -297,6 +297,7 @@ int setup_map(struct multipath *mpp, char *params, int params_size, + struct vectors *vecs) + { + struct pathgroup * pgp; ++ struct path *pp; + struct config *conf; + int i, n_paths, marginal_pathgroups; + +@@ -308,6 +309,14 @@ int setup_map(struct multipath *mpp, char *params, int params_size, + return 1; + } + ++ /* Force QUEUE_MODE_BIO for maps with nvme:tcp paths */ ++ vector_foreach_slot(mpp->paths, pp, i) { ++ if (pp->bus == SYSFS_BUS_NVME && ++ pp->sg_id.proto_id == NVME_PROTOCOL_TCP) { ++ mpp->queue_mode = QUEUE_MODE_BIO; ++ break; ++ } ++ } + /* + * free features, selector, and hwhandler properties if they are being reused + */ +@@ -1161,6 +1170,13 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, + continue; + } + ++ cmpp = find_mp_by_wwid(curmp, pp1->wwid); ++ if (cmpp && cmpp->queue_mode == QUEUE_MODE_RQ && ++ pp1->bus == SYSFS_BUS_NVME && pp1->sg_id.proto_id == ++ NVME_PROTOCOL_TCP) { ++ orphan_path(pp1, "nvme:tcp path not allowed with request queue_mode multipath device"); ++ continue; ++ } + /* + * at this point, we know we really got a new mp + */ +@@ -1199,7 +1215,6 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, + } + verify_paths(mpp, vecs); + +- cmpp = find_mp_by_wwid(curmp, mpp->wwid); + if (cmpp) + mpp->queue_mode = cmpp->queue_mode; + params[0] = '\0'; +diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c +index 8137ea21..24ac022e 100644 +--- a/libmultipath/structs_vec.c ++++ b/libmultipath/structs_vec.c +@@ -68,6 +68,13 @@ int adopt_paths(vector pathvec, struct multipath *mpp) + pp->dev, mpp->alias); + continue; + } ++ if (mpp->queue_mode == QUEUE_MODE_RQ && ++ pp->bus == SYSFS_BUS_NVME && ++ pp->sg_id.proto_id == NVME_PROTOCOL_TCP) { ++ condlog(2, "%s: mulitpath device %s created with request queue_mode. Unable to add nvme:tcp paths", ++ pp->dev, mpp->alias); ++ continue; ++ } + condlog(3, "%s: ownership set to %s", + pp->dev, mpp->alias); + pp->mpp = mpp; +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 1f5a40b6..cb07a62c 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -462,7 +462,9 @@ Before kernel 4.20 The default depends on the kernel parameter + \fBdm_mod.use_blk_mq\fR. It is \fImq\fR if the latter is set, and \fIrq\fR + otherwise. Since kernel 4.20, \fIrq\fR and \fImq\fR both correspond to + block-multiqueue. Once a multipath device has been created, its queue_mode +-cannot be changed. ++cannot be changed. \fInvme:tcp\fR paths are only supported in multipath ++devices with queue_mode set to \fIbio\fR. multipath will automatically ++set this when creating a device with \fInvme:tcp\fR paths. + .TP + The default is: \fB\fR + .RE diff --git a/SOURCES/0125-multipath-add-historical-service-time-to-the-man-pag.patch b/SOURCES/0125-multipath-add-historical-service-time-to-the-man-pag.patch new file mode 100644 index 0000000..78fc931 --- /dev/null +++ b/SOURCES/0125-multipath-add-historical-service-time-to-the-man-pag.patch @@ -0,0 +1,26 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 15 Nov 2022 09:01:36 -0600 +Subject: [PATCH] multipath: add historical-service-time to the man page + +Signed-off-by: Benjamin Marzinski +--- + multipath/multipath.conf.5 | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index cb07a62c..d8a98435 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -205,6 +205,11 @@ of outstanding I/O to the path. + (Since 2.6.31 kernel) Choose the path for the next bunch of I/O based on the amount + of outstanding I/O to the path and its relative throughput. + .TP ++.I "historical-service-time 0" ++(Since 4.18.0-305.3.el8 kernel) Choose the path for the next bunch of I/O based ++on the estimation of future service time based on the history of previous I/O ++submitted to each path. ++.TP + The default is: \fBservice-time 0\fR + .RE + . diff --git a/SOURCES/0126-libmultipath-copy-mpp-hwe-from-pp-hwe.patch b/SOURCES/0126-libmultipath-copy-mpp-hwe-from-pp-hwe.patch new file mode 100644 index 0000000..c176bdd --- /dev/null +++ b/SOURCES/0126-libmultipath-copy-mpp-hwe-from-pp-hwe.patch @@ -0,0 +1,215 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Wed, 16 Sep 2020 22:22:36 +0200 +Subject: [PATCH] libmultipath: copy mpp->hwe from pp->hwe + +Since f0462f0 ("libmultipath: use vector for for pp->hwe and mp->hwe"), +we've been trying to fix issues caused by paths getting freed and mpp->hwe +dangling. This approach couldn't work because we need mpp->hwe to persist, +even if all paths are removed from the map. Before f0462f0, a simple +assignment worked, because the lifetime of the hwe wasn't bound to the +path. But now, we need to copy the vector. It turns out that we need to set +mpp->hwe only in two places, add_map_with_path() and setup_map(), and +that the code is simplified overall. + +Even now, it can happen that a map is added with add_map_without_paths(), +and has no paths. In that case, calling do_set_from_hwe() with a NULL +pointer is not a bug, so remove the message. + +Fixes: f0462f0 ("libmultipath: use vector for for pp->hwe and mp->hwe") +Reviewed-by: Benjamin Marzinski +Signed-off-by: Benjamin Marzinski +--- + libmultipath/configure.c | 8 ++++++++ + libmultipath/propsel.c | 2 +- + libmultipath/structs.c | 15 ++++++++++++++ + libmultipath/structs.h | 1 + + libmultipath/structs_vec.c | 41 +++++++++++++++++++------------------- + multipathd/main.c | 10 ---------- + 6 files changed, 45 insertions(+), 32 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index c341793c..6e06fea2 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -324,6 +324,14 @@ int setup_map(struct multipath *mpp, char *params, int params_size, + if (mpp->disable_queueing && VECTOR_SIZE(mpp->paths) != 0) + mpp->disable_queueing = 0; + ++ /* ++ * If this map was created with add_map_without_path(), ++ * mpp->hwe might not be set yet. ++ */ ++ if (!mpp->hwe) ++ extract_hwe_from_path(mpp); ++ ++ + /* + * properties selectors + * +diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c +index 3f119dd9..bc5c27ab 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -66,7 +66,7 @@ do { \ + __do_set_from_vec(struct hwentry, var, (src)->hwe, dest) + + #define do_set_from_hwe(var, src, dest, msg) \ +- if (__do_set_from_hwe(var, src, dest)) { \ ++ if (src->hwe && __do_set_from_hwe(var, src, dest)) { \ + origin = msg; \ + goto out; \ + } +diff --git a/libmultipath/structs.c b/libmultipath/structs.c +index 7bdf9152..5bb6caf8 100644 +--- a/libmultipath/structs.c ++++ b/libmultipath/structs.c +@@ -242,6 +242,17 @@ alloc_multipath (void) + return mpp; + } + ++void *set_mpp_hwe(struct multipath *mpp, const struct path *pp) ++{ ++ if (!mpp || !pp || !pp->hwe) ++ return NULL; ++ if (mpp->hwe) ++ return mpp->hwe; ++ mpp->hwe = vector_convert(NULL, pp->hwe, ++ struct hwentry, identity); ++ return mpp->hwe; ++} ++ + void free_multipath_attributes(struct multipath *mpp) + { + if (!mpp) +@@ -283,6 +294,10 @@ free_multipath (struct multipath * mpp, enum free_path_mode free_paths) + + free_pathvec(mpp->paths, free_paths); + free_pgvec(mpp->pg, free_paths); ++ if (mpp->hwe) { ++ vector_free(mpp->hwe); ++ mpp->hwe = NULL; ++ } + FREE_PTR(mpp->mpcontext); + FREE(mpp); + } +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 9130efb5..44980b4e 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -499,6 +499,7 @@ struct host_group { + struct path * alloc_path (void); + struct pathgroup * alloc_pathgroup (void); + struct multipath * alloc_multipath (void); ++void *set_mpp_hwe(struct multipath *mpp, const struct path *pp); + void free_path (struct path *); + void free_pathvec (vector vec, enum free_path_mode free_paths); + void free_pathgroup (struct pathgroup * pgp, enum free_path_mode free_paths); +diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c +index 24ac022e..9613252f 100644 +--- a/libmultipath/structs_vec.c ++++ b/libmultipath/structs_vec.c +@@ -180,24 +180,24 @@ extract_hwe_from_path(struct multipath * mpp) + if (mpp->hwe || !mpp->paths) + return; + +- condlog(3, "%s: searching paths for valid hwe", mpp->alias); ++ condlog(4, "%s: searching paths for valid hwe", mpp->alias); + /* doing this in two passes seems like paranoia to me */ + vector_foreach_slot(mpp->paths, pp, i) { +- if (pp->state != PATH_UP) +- continue; +- if (pp->hwe) { +- mpp->hwe = pp->hwe; +- return; +- } ++ if (pp->state == PATH_UP && pp->hwe) ++ goto done; + } + vector_foreach_slot(mpp->paths, pp, i) { +- if (pp->state == PATH_UP) +- continue; +- if (pp->hwe) { +- mpp->hwe = pp->hwe; +- return; +- } ++ if (pp->state != PATH_UP && pp->hwe) ++ goto done; + } ++done: ++ if (i < VECTOR_SIZE(mpp->paths)) ++ (void)set_mpp_hwe(mpp, pp); ++ ++ if (mpp->hwe) ++ condlog(3, "%s: got hwe from path %s", mpp->alias, pp->dev); ++ else ++ condlog(2, "%s: no hwe found", mpp->alias); + } + + int +@@ -445,9 +445,15 @@ struct multipath *add_map_with_path(struct vectors *vecs, struct path *pp, + + conf = get_multipath_config(); + mpp->mpe = find_mpe(conf->mptable, pp->wwid); +- mpp->hwe = pp->hwe; + put_multipath_config(conf); + ++ /* ++ * We need to call this before select_alias(), ++ * because that accesses hwe properties. ++ */ ++ if (pp->hwe && !set_mpp_hwe(mpp, pp)) ++ goto out; ++ + strcpy(mpp->wwid, pp->wwid); + find_existing_alias(mpp, vecs); + if (select_alias(conf, mpp)) +@@ -497,12 +503,6 @@ int verify_paths(struct multipath *mpp, struct vectors *vecs) + vector_del_slot(mpp->paths, i); + i--; + +- /* Make sure mpp->hwe doesn't point to freed memory. +- * We call extract_hwe_from_path() below to restore +- * mpp->hwe +- */ +- if (mpp->hwe == pp->hwe) +- mpp->hwe = NULL; + if ((j = find_slot(vecs->pathvec, + (void *)pp)) != -1) + vector_del_slot(vecs->pathvec, j); +@@ -512,7 +512,6 @@ int verify_paths(struct multipath *mpp, struct vectors *vecs) + mpp->alias, pp->dev, pp->dev_t); + } + } +- extract_hwe_from_path(mpp); + return count; + } + +diff --git a/multipathd/main.c b/multipathd/main.c +index cd68a9d2..769dcaee 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -1198,13 +1198,6 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map) + goto fail; + } + +- /* +- * Make sure mpp->hwe doesn't point to freed memory +- * We call extract_hwe_from_path() below to restore mpp->hwe +- */ +- if (mpp->hwe == pp->hwe) +- mpp->hwe = NULL; +- + if ((i = find_slot(mpp->paths, (void *)pp)) != -1) + vector_del_slot(mpp->paths, i); + +@@ -1216,9 +1209,6 @@ ev_remove_path (struct path *pp, struct vectors * vecs, int need_do_map) + flush_map_nopaths(mpp, vecs)) + goto out; + +- if (mpp->hwe == NULL) +- extract_hwe_from_path(mpp); +- + if (setup_map(mpp, params, PARAMS_SIZE, vecs)) { + condlog(0, "%s: failed to setup map for" + " removal of path %s", mpp->alias, pp->dev); diff --git a/SOURCES/0127-libmultipath-don-t-leak-memory-on-invalid-strings.patch b/SOURCES/0127-libmultipath-don-t-leak-memory-on-invalid-strings.patch new file mode 100644 index 0000000..ded6361 --- /dev/null +++ b/SOURCES/0127-libmultipath-don-t-leak-memory-on-invalid-strings.patch @@ -0,0 +1,35 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 14 Dec 2022 15:38:19 -0600 +Subject: [PATCH] libmultipath: don't leak memory on invalid strings + +If set_path() or set_str_noslash() are called with a bad value, they +ignore it and continue to use the old value. But they weren't freeing +the bad value, causing a memory leak. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/dict.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index d7cd94a5..a8c9e989 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -169,6 +169,7 @@ set_path(vector strvec, void *ptr, const char *file, int line_nr) + if ((*str_ptr)[0] != '/'){ + condlog(1, "%s line %d, %s is not an absolute path. Ignoring", + file, line_nr, *str_ptr); ++ free(*str_ptr); + *str_ptr = old_str; + } else + free(old_str); +@@ -189,6 +190,7 @@ set_str_noslash(vector strvec, void *ptr, const char *file, int line_nr) + if (strchr(*str_ptr, '/')) { + condlog(1, "%s line %d, %s cannot contain a slash. Ignoring", + file, line_nr, *str_ptr); ++ free(*str_ptr); + *str_ptr = old_str; + } else + free(old_str); diff --git a/SOURCES/0128-libmutipath-validate-the-argument-count-of-config-st.patch b/SOURCES/0128-libmutipath-validate-the-argument-count-of-config-st.patch new file mode 100644 index 0000000..ced384f --- /dev/null +++ b/SOURCES/0128-libmutipath-validate-the-argument-count-of-config-st.patch @@ -0,0 +1,195 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 14 Dec 2022 15:38:20 -0600 +Subject: [PATCH] libmutipath: validate the argument count of config strings + +The features, path_selector, and hardware_handler config options pass +their strings directly into the kernel. If users omit the argument +counts from these strings, or use the wrong value, the kernel's table +parsing gets completely messed up, and the error messages it prints +don't reflect what actully went wrong. To avoid messing up the +kernel table parsing, verify that these strings correctly set the +argument count to the number of arguments they have. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/dict.c | 110 ++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 101 insertions(+), 9 deletions(-) + +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index a8c9e989..952b33d0 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -155,6 +155,58 @@ set_dir(vector strvec, void *ptr, const char *file, int line_nr) + return 0; + } + ++static int ++set_arg_str(vector strvec, void *ptr, int count_idx, const char *file, ++ int line_nr) ++{ ++ char **str_ptr = (char **)ptr; ++ char *old_str = *str_ptr; ++ const char * const spaces = " \f\r\t\v"; ++ char *p, *end; ++ int idx = -1; ++ long int count = -1; ++ ++ *str_ptr = set_value(strvec); ++ if (!*str_ptr) { ++ free(old_str); ++ return 1; ++ } ++ p = *str_ptr; ++ while (*p != '\0') { ++ p += strspn(p, spaces); ++ if (*p == '\0') ++ break; ++ idx += 1; ++ if (idx == count_idx) { ++ errno = 0; ++ count = strtol(p, &end, 10); ++ if (errno == ERANGE || end == p || ++ !(isspace(*end) || *end == '\0')) { ++ count = -1; ++ break; ++ } ++ } ++ p += strcspn(p, spaces); ++ } ++ if (count < 0) { ++ condlog(1, "%s line %d, missing argument count for %s", ++ file, line_nr, (char*)VECTOR_SLOT(strvec, 0)); ++ goto fail; ++ } ++ if (count != idx - count_idx) { ++ condlog(1, "%s line %d, invalid argument count for %s:, got '%ld' expected '%d'", ++ file, line_nr, (char*)VECTOR_SLOT(strvec, 0), count, ++ idx - count_idx); ++ goto fail; ++ } ++ free(old_str); ++ return 0; ++fail: ++ free(*str_ptr); ++ *str_ptr = old_str; ++ return 0; ++} ++ + static int + set_path(vector strvec, void *ptr, const char *file, int line_nr) + { +@@ -337,6 +389,14 @@ def_ ## option ## _handler (struct config *conf, vector strvec, \ + return set_int(strvec, &conf->option, minval, maxval, file, line_nr); \ + } + ++#define declare_def_arg_str_handler(option, count_idx) \ ++static int \ ++def_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ ++{ \ ++ return set_arg_str(strvec, &conf->option, count_idx, file, line_nr); \ ++} ++ + #define declare_def_snprint(option, function) \ + static int \ + snprint_def_ ## option (struct config *conf, char * buff, int len, \ +@@ -389,6 +449,17 @@ hw_ ## option ## _handler (struct config *conf, vector strvec, \ + return set_int(strvec, &hwe->option, minval, maxval, file, line_nr); \ + } + ++#define declare_hw_arg_str_handler(option, count_idx) \ ++static int \ ++hw_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ ++{ \ ++ struct hwentry * hwe = VECTOR_LAST_SLOT(conf->hwtable); \ ++ if (!hwe) \ ++ return 1; \ ++ return set_arg_str(strvec, &hwe->option, count_idx, file, line_nr); \ ++} ++ + + #define declare_hw_snprint(option, function) \ + static int \ +@@ -420,6 +491,16 @@ ovr_ ## option ## _handler (struct config *conf, vector strvec, \ + file, line_nr); \ + } + ++#define declare_ovr_arg_str_handler(option, count_idx) \ ++static int \ ++ovr_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ ++{ \ ++ if (!conf->overrides) \ ++ return 1; \ ++ return set_arg_str(strvec, &conf->overrides->option, count_idx, file, line_nr); \ ++} ++ + #define declare_ovr_snprint(option, function) \ + static int \ + snprint_ovr_ ## option (struct config *conf, char * buff, int len, \ +@@ -450,6 +531,17 @@ mp_ ## option ## _handler (struct config *conf, vector strvec, \ + return set_int(strvec, &mpe->option, minval, maxval, file, line_nr); \ + } + ++#define declare_mp_arg_str_handler(option, count_idx) \ ++static int \ ++mp_ ## option ## _handler (struct config *conf, vector strvec, \ ++ const char *file, int line_nr) \ ++{ \ ++ struct mpentry * mpe = VECTOR_LAST_SLOT(conf->mptable); \ ++ if (!mpe) \ ++ return 1; \ ++ return set_arg_str(strvec, &mpe->option, count_idx, file, line_nr); \ ++} ++ + #define declare_mp_snprint(option, function) \ + static int \ + snprint_mp_ ## option (struct config *conf, char * buff, int len, \ +@@ -634,13 +726,13 @@ snprint_def_marginal_pathgroups(struct config *conf, char *buff, int len, + } + + +-declare_def_handler(selector, set_str) ++declare_def_arg_str_handler(selector, 1) + declare_def_snprint_defstr(selector, print_str, DEFAULT_SELECTOR) +-declare_hw_handler(selector, set_str) ++declare_hw_arg_str_handler(selector, 1) + declare_hw_snprint(selector, print_str) +-declare_ovr_handler(selector, set_str) ++declare_ovr_arg_str_handler(selector, 1) + declare_ovr_snprint(selector, print_str) +-declare_mp_handler(selector, set_str) ++declare_mp_arg_str_handler(selector, 1) + declare_mp_snprint(selector, print_str) + + static int snprint_uid_attrs(struct config *conf, char *buff, int len, +@@ -717,13 +809,13 @@ declare_hw_snprint(prio_args, print_str) + declare_mp_handler(prio_args, set_str) + declare_mp_snprint(prio_args, print_str) + +-declare_def_handler(features, set_str) ++declare_def_arg_str_handler(features, 0) + declare_def_snprint_defstr(features, print_str, DEFAULT_FEATURES) +-declare_ovr_handler(features, set_str) ++declare_ovr_arg_str_handler(features, 0) + declare_ovr_snprint(features, print_str) +-declare_hw_handler(features, set_str) ++declare_hw_arg_str_handler(features, 0) + declare_hw_snprint(features, print_str) +-declare_mp_handler(features, set_str) ++declare_mp_arg_str_handler(features, 0) + declare_mp_snprint(features, print_str) + + declare_def_handler(checker_name, set_str) +@@ -1894,7 +1986,7 @@ declare_hw_snprint(revision, print_str) + declare_hw_handler(bl_product, set_regex) + declare_hw_snprint(bl_product, print_str) + +-declare_hw_handler(hwhandler, set_str) ++declare_hw_arg_str_handler(hwhandler, 0) + declare_hw_snprint(hwhandler, print_str) + + /* diff --git a/SOURCES/0129-libmultipath-select-resize-action-even-if-reload-is-.patch b/SOURCES/0129-libmultipath-select-resize-action-even-if-reload-is-.patch new file mode 100644 index 0000000..97fd724 --- /dev/null +++ b/SOURCES/0129-libmultipath-select-resize-action-even-if-reload-is-.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 31 Jan 2023 09:58:55 -0600 +Subject: [PATCH] libmultipath: select resize action even if reload is forced + +The ACT_RESIZE action is the same as the ACT_RELOAD action, except that +it flushes outstanding IO because the device size is changing and +the new size might be too small for some of the outstanding IO. If we've +detected a size change, and a forced reload is requested, we still need +to flush the IO because the reload will change the device size. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/configure.c | 13 +++++++------ + 1 file changed, 7 insertions(+), 6 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index 6e06fea2..ecf24f95 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -734,17 +734,18 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + return; + } + +- if (force_reload) { ++ if (cmpp->size != mpp->size) { + mpp->force_udev_reload = 1; +- mpp->action = ACT_RELOAD; +- condlog(3, "%s: set ACT_RELOAD (forced by user)", ++ mpp->action = ACT_RESIZE; ++ condlog(3, "%s: set ACT_RESIZE (size change)", + mpp->alias); + return; + } +- if (cmpp->size != mpp->size) { ++ ++ if (force_reload) { + mpp->force_udev_reload = 1; +- mpp->action = ACT_RESIZE; +- condlog(3, "%s: set ACT_RESIZE (size change)", ++ mpp->action = ACT_RELOAD; ++ condlog(3, "%s: set ACT_RELOAD (forced by user)", + mpp->alias); + return; + } diff --git a/SOURCES/0130-libmultipath-cleanup-ACT_CREATE-code-in-select_actio.patch b/SOURCES/0130-libmultipath-cleanup-ACT_CREATE-code-in-select_actio.patch new file mode 100644 index 0000000..0b8473b --- /dev/null +++ b/SOURCES/0130-libmultipath-cleanup-ACT_CREATE-code-in-select_actio.patch @@ -0,0 +1,67 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 31 Jan 2023 10:35:10 -0600 +Subject: [PATCH] libmultipath: cleanup ACT_CREATE code in select_action + +Combine the two separate blocks that set ACT_CREATE into one. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/configure.c | 38 +++++++++++++++++--------------------- + 1 file changed, 17 insertions(+), 21 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index ecf24f95..303d2380 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -693,33 +693,29 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + cmpp = find_mp_by_wwid(curmp, mpp->wwid); + cmpp_by_name = find_mp_by_alias(curmp, mpp->alias); + +- if (!cmpp_by_name) { +- if (cmpp) { +- condlog(2, "%s: rename %s to %s", mpp->wwid, +- cmpp->alias, mpp->alias); +- strlcpy(mpp->alias_old, cmpp->alias, WWID_SIZE); +- mpp->action = ACT_RENAME; +- if (force_reload) { +- mpp->force_udev_reload = 1; +- mpp->action = ACT_FORCERENAME; +- } +- return; ++ if (!cmpp) { ++ if (cmpp_by_name) { ++ condlog(1, "%s: can't use alias \"%s\" used by %s, falling back to WWID", ++ mpp->wwid, mpp->alias, cmpp_by_name->wwid); ++ /* We can do this because wwid wasn't found */ ++ free(mpp->alias); ++ mpp->alias = strdup(mpp->wwid); + } + mpp->action = ACT_CREATE; +- condlog(3, "%s: set ACT_CREATE (map does not exist)", +- mpp->alias); ++ condlog(3, "%s: set ACT_CREATE (map does not exist%s)", ++ mpp->alias, cmpp_by_name ? ", name changed" : ""); + return; + } + +- if (!cmpp) { +- condlog(1, "%s: can't use alias \"%s\" used by %s, falling back to WWID", +- mpp->wwid, mpp->alias, cmpp_by_name->wwid); +- /* We can do this because wwid wasn't found */ +- free(mpp->alias); +- mpp->alias = strdup(mpp->wwid); +- mpp->action = ACT_CREATE; +- condlog(3, "%s: set ACT_CREATE (map does not exist, name changed)", ++ if (!cmpp_by_name) { ++ condlog(2, "%s: rename %s to %s", mpp->wwid, cmpp->alias, + mpp->alias); ++ strlcpy(mpp->alias_old, cmpp->alias, WWID_SIZE); ++ mpp->action = ACT_RENAME; ++ if (force_reload) { ++ mpp->force_udev_reload = 1; ++ mpp->action = ACT_FORCERENAME; ++ } + return; + } + diff --git a/SOURCES/0131-libmultipath-keep-renames-from-stopping-other-multip.patch b/SOURCES/0131-libmultipath-keep-renames-from-stopping-other-multip.patch new file mode 100644 index 0000000..c57ab7e --- /dev/null +++ b/SOURCES/0131-libmultipath-keep-renames-from-stopping-other-multip.patch @@ -0,0 +1,270 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 31 Jan 2023 12:00:31 -0600 +Subject: [PATCH] libmultipath: keep renames from stopping other multipath + actions + +If select_action() is called and a multipath device needs to be renamed, +the code currently checks if force_reload is set, and if so, does the +reload after the rename. But if force_reload isn't set, only the rename +happens, regardless of what other actions are needed. This can happen if +multipathd starts up and a device needs both a reload and a rename. + +Make multipath check for resize, reload, and switch pathgroup along with +rename, and do both if necessary. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/configure.c | 92 +++++++++++++++++++++------------------- + libmultipath/configure.h | 4 +- + 2 files changed, 51 insertions(+), 45 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index 303d2380..65a0b208 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -690,6 +690,7 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + struct multipath * cmpp_by_name; + char * mpp_feat, * cmpp_feat; + ++ mpp->action = ACT_NOTHING; + cmpp = find_mp_by_wwid(curmp, mpp->wwid); + cmpp_by_name = find_mp_by_alias(curmp, mpp->alias); + +@@ -712,14 +713,8 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + mpp->alias); + strlcpy(mpp->alias_old, cmpp->alias, WWID_SIZE); + mpp->action = ACT_RENAME; +- if (force_reload) { +- mpp->force_udev_reload = 1; +- mpp->action = ACT_FORCERENAME; +- } +- return; +- } +- +- if (cmpp != cmpp_by_name) { ++ /* don't return here. Check for other needed actions */ ++ } else if (cmpp != cmpp_by_name) { + condlog(2, "%s: unable to rename %s to %s (%s is used by %s)", + mpp->wwid, cmpp->alias, mpp->alias, + mpp->alias, cmpp_by_name->wwid); +@@ -727,12 +722,13 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + FREE(mpp->alias); + mpp->alias = STRDUP(cmpp->alias); + mpp->action = ACT_IMPOSSIBLE; +- return; ++ /* don't return here. Check for other needed actions */ + } + + if (cmpp->size != mpp->size) { + mpp->force_udev_reload = 1; +- mpp->action = ACT_RESIZE; ++ mpp->action = mpp->action == ACT_RENAME ? ACT_RESIZE_RENAME : ++ ACT_RESIZE; + condlog(3, "%s: set ACT_RESIZE (size change)", + mpp->alias); + return; +@@ -740,7 +736,8 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + + if (force_reload) { + mpp->force_udev_reload = 1; +- mpp->action = ACT_RELOAD; ++ mpp->action = mpp->action == ACT_RENAME ? ACT_RELOAD_RENAME : ++ ACT_RELOAD; + condlog(3, "%s: set ACT_RELOAD (forced by user)", + mpp->alias); + return; +@@ -749,7 +746,8 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + if (mpp->no_path_retry != NO_PATH_RETRY_UNDEF && + !!strstr(mpp->features, "queue_if_no_path") != + !!strstr(cmpp->features, "queue_if_no_path")) { +- mpp->action = ACT_RELOAD; ++ mpp->action = mpp->action == ACT_RENAME ? ACT_RELOAD_RENAME : ++ ACT_RELOAD; + condlog(3, "%s: set ACT_RELOAD (no_path_retry change)", + mpp->alias); + return; +@@ -759,7 +757,8 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + (strlen(cmpp->hwhandler) != strlen(mpp->hwhandler) || + strncmp(cmpp->hwhandler, mpp->hwhandler, + strlen(mpp->hwhandler)))) { +- mpp->action = ACT_RELOAD; ++ mpp->action = mpp->action == ACT_RENAME ? ACT_RELOAD_RENAME : ++ ACT_RELOAD; + condlog(3, "%s: set ACT_RELOAD (hwhandler change)", + mpp->alias); + return; +@@ -769,7 +768,8 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + !!strstr(mpp->features, "retain_attached_hw_handler") != + !!strstr(cmpp->features, "retain_attached_hw_handler") && + get_linux_version_code() < KERNEL_VERSION(4, 3, 0)) { +- mpp->action = ACT_RELOAD; ++ mpp->action = mpp->action == ACT_RENAME ? ACT_RELOAD_RENAME : ++ ACT_RELOAD; + condlog(3, "%s: set ACT_RELOAD (retain_hwhandler change)", + mpp->alias); + return; +@@ -783,9 +783,13 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + remove_feature(&cmpp_feat, "queue_if_no_path"); + remove_feature(&cmpp_feat, "retain_attached_hw_handler"); + if (strncmp(mpp_feat, cmpp_feat, PARAMS_SIZE)) { +- mpp->action = ACT_RELOAD; ++ mpp->action = mpp->action == ACT_RENAME ? ++ ACT_RELOAD_RENAME : ACT_RELOAD; + condlog(3, "%s: set ACT_RELOAD (features change)", + mpp->alias); ++ FREE(cmpp_feat); ++ FREE(mpp_feat); ++ return; + } + } + FREE(cmpp_feat); +@@ -793,44 +797,49 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) + + if (!cmpp->selector || strncmp(cmpp->selector, mpp->selector, + strlen(mpp->selector))) { +- mpp->action = ACT_RELOAD; ++ mpp->action = mpp->action == ACT_RENAME ? ACT_RELOAD_RENAME : ++ ACT_RELOAD; + condlog(3, "%s: set ACT_RELOAD (selector change)", + mpp->alias); + return; + } + if (cmpp->minio != mpp->minio) { +- mpp->action = ACT_RELOAD; ++ mpp->action = mpp->action == ACT_RENAME ? ACT_RELOAD_RENAME : ++ ACT_RELOAD; + condlog(3, "%s: set ACT_RELOAD (minio change, %u->%u)", + mpp->alias, cmpp->minio, mpp->minio); + return; + } + if (!cmpp->pg || VECTOR_SIZE(cmpp->pg) != VECTOR_SIZE(mpp->pg)) { +- mpp->action = ACT_RELOAD; ++ mpp->action = mpp->action == ACT_RENAME ? ACT_RELOAD_RENAME : ++ ACT_RELOAD; + condlog(3, "%s: set ACT_RELOAD (path group number change)", + mpp->alias); + return; + } + if (pgcmp(mpp, cmpp)) { +- mpp->action = ACT_RELOAD; ++ mpp->action = mpp->action == ACT_RENAME ? ACT_RELOAD_RENAME : ++ ACT_RELOAD; + condlog(3, "%s: set ACT_RELOAD (path group topology change)", + mpp->alias); + return; + } + if (cmpp->nextpg != mpp->bestpg) { +- mpp->action = ACT_SWITCHPG; ++ mpp->action = mpp->action == ACT_RENAME ? ACT_SWITCHPG_RENAME : ++ ACT_SWITCHPG; + condlog(3, "%s: set ACT_SWITCHPG (next path group change)", + mpp->alias); + return; + } + if (!is_mpp_known_to_udev(cmpp)) { +- mpp->action = ACT_RELOAD; ++ mpp->action = mpp->action == ACT_RENAME ? ACT_SWITCHPG_RENAME : ++ ACT_SWITCHPG; + condlog(3, "%s: set ACT_RELOAD (udev device not initialized)", + mpp->alias); + return; + } +- mpp->action = ACT_NOTHING; +- condlog(3, "%s: set ACT_NOTHING (map unchanged)", +- mpp->alias); ++ if (mpp->action == ACT_NOTHING) ++ condlog(3, "%s: set ACT_NOTHING (map unchanged)", mpp->alias); + return; + } + +@@ -924,6 +933,17 @@ int domap(struct multipath *mpp, char *params, int is_daemon) + mpp->action = ACT_RELOAD; + } + ++ if (mpp->action == ACT_RENAME || mpp->action == ACT_SWITCHPG_RENAME || ++ mpp->action == ACT_RELOAD_RENAME || ++ mpp->action == ACT_RESIZE_RENAME) { ++ conf = get_multipath_config(); ++ pthread_cleanup_push(put_multipath_config, conf); ++ r = dm_rename(mpp->alias_old, mpp->alias, ++ conf->partition_delim, mpp->skip_kpartx); ++ pthread_cleanup_pop(1); ++ if (r == DOMAP_FAIL) ++ return r; ++ } + switch (mpp->action) { + case ACT_REJECT: + case ACT_NOTHING: +@@ -931,6 +951,7 @@ int domap(struct multipath *mpp, char *params, int is_daemon) + return DOMAP_EXIST; + + case ACT_SWITCHPG: ++ case ACT_SWITCHPG_RENAME: + dm_switchgroup(mpp->alias, mpp->bestpg); + /* + * we may have avoided reinstating paths because there where in +@@ -957,6 +978,7 @@ int domap(struct multipath *mpp, char *params, int is_daemon) + break; + + case ACT_RELOAD: ++ case ACT_RELOAD_RENAME: + sysfs_set_max_sectors_kb(mpp, 1); + if (mpp->ghost_delay_tick > 0 && pathcount(mpp, PATH_UP)) + mpp->ghost_delay_tick = 0; +@@ -964,6 +986,7 @@ int domap(struct multipath *mpp, char *params, int is_daemon) + break; + + case ACT_RESIZE: ++ case ACT_RESIZE_RENAME: + sysfs_set_max_sectors_kb(mpp, 1); + if (mpp->ghost_delay_tick > 0 && pathcount(mpp, PATH_UP)) + mpp->ghost_delay_tick = 0; +@@ -971,29 +994,10 @@ int domap(struct multipath *mpp, char *params, int is_daemon) + break; + + case ACT_RENAME: +- conf = get_multipath_config(); +- pthread_cleanup_push(put_multipath_config, conf); +- r = dm_rename(mpp->alias_old, mpp->alias, +- conf->partition_delim, mpp->skip_kpartx); +- pthread_cleanup_pop(1); +- break; +- +- case ACT_FORCERENAME: +- conf = get_multipath_config(); +- pthread_cleanup_push(put_multipath_config, conf); +- r = dm_rename(mpp->alias_old, mpp->alias, +- conf->partition_delim, mpp->skip_kpartx); +- pthread_cleanup_pop(1); +- if (r) { +- sysfs_set_max_sectors_kb(mpp, 1); +- if (mpp->ghost_delay_tick > 0 && +- pathcount(mpp, PATH_UP)) +- mpp->ghost_delay_tick = 0; +- r = dm_addmap_reload(mpp, params, 0); +- } + break; + + default: ++ r = DOMAP_FAIL; + break; + } + +diff --git a/libmultipath/configure.h b/libmultipath/configure.h +index 5cf08d45..1a93f49d 100644 +--- a/libmultipath/configure.h ++++ b/libmultipath/configure.h +@@ -18,9 +18,11 @@ enum actions { + ACT_RENAME, + ACT_CREATE, + ACT_RESIZE, +- ACT_FORCERENAME, ++ ACT_RELOAD_RENAME, + ACT_DRY_RUN, + ACT_IMPOSSIBLE, ++ ACT_RESIZE_RENAME, ++ ACT_SWITCHPG_RENAME, + }; + + /* diff --git a/SOURCES/multipath.conf b/SOURCES/multipath.conf new file mode 100644 index 0000000..c7684fe --- /dev/null +++ b/SOURCES/multipath.conf @@ -0,0 +1,93 @@ +# This is a basic configuration file with some examples, for device mapper +# multipath. +# +# For a complete list of the default configuration values, run either +# multipath -t +# or +# multipathd show config +# +# For a list of configuration options with descriptions, see the multipath.conf +# man page + +## By default, devices with vendor = "IBM" and product = "S/390.*" are +## blacklisted. To enable mulitpathing on these devies, uncomment the +## following lines. +#blacklist_exceptions { +# device { +# vendor "IBM" +# product "S/390.*" +# } +#} + +## Use user friendly names, instead of using WWIDs as names. +defaults { + user_friendly_names yes + find_multipaths yes +} +## +## Here is an example of how to configure some standard options. +## +# +#defaults { +# udev_dir /dev +# polling_interval 10 +# selector "round-robin 0" +# path_grouping_policy multibus +# prio alua +# path_checker readsector0 +# rr_min_io 100 +# max_fds 8192 +# rr_weight priorities +# failback immediate +# no_path_retry fail +# user_friendly_names yes +#} +## +## The wwid line in the following blacklist section is shown as an example +## of how to blacklist devices by wwid. The 2 devnode lines are the +## compiled in default blacklist. If you want to blacklist entire types +## of devices, such as all scsi devices, you should use a devnode line. +## However, if you want to blacklist specific devices, you should use +## a wwid line. Since there is no guarantee that a specific device will +## not change names on reboot (from /dev/sda to /dev/sdb for example) +## devnode lines are not recommended for blacklisting specific devices. +## +#blacklist { +# wwid 26353900f02796769 +# devnode "^(ram|raw|loop|fd|md|dm-|sr|scd|st)[0-9]*" +# devnode "^hd[a-z]" +#} +#multipaths { +# multipath { +# wwid 3600508b4000156d700012000000b0000 +# alias yellow +# path_grouping_policy multibus +# path_checker readsector0 +# path_selector "round-robin 0" +# failback manual +# rr_weight priorities +# no_path_retry 5 +# } +# multipath { +# wwid 1DEC_____321816758474 +# alias red +# } +#} +#devices { +# device { +# vendor "COMPAQ " +# product "HSV110 (C)COMPAQ" +# path_grouping_policy multibus +# path_checker readsector0 +# path_selector "round-robin 0" +# hardware_handler "0" +# failback 15 +# rr_weight priorities +# no_path_retry queue +# } +# device { +# vendor "COMPAQ " +# product "MSA1000 " +# path_grouping_policy multibus +# } +#} diff --git a/SPECS/device-mapper-multipath.spec b/SPECS/device-mapper-multipath.spec new file mode 100644 index 0000000..8847caa --- /dev/null +++ b/SPECS/device-mapper-multipath.spec @@ -0,0 +1,2131 @@ +Summary: Tools to manage multipath devices using device-mapper +Name: device-mapper-multipath +Version: 0.8.4 +Release: 37%{?dist} +License: GPLv2 +Group: System Environment/Base +URL: http://christophe.varoqui.free.fr/ + +# The source for this package was pulled from upstream's git repo. Use the +# following command to generate the tarball +#curl "https://git.opensvc.com/?p=multipath-tools/.git;a=snapshot;h=refs/tags/0.8.4;sf=tgz" -o multipath-tools-0.8.4.tgz +Source0: multipath-tools-0.8.4.tgz +Source1: multipath.conf +Patch00001: 0001-libmultipath-assign-variable-to-make-gcc-happy.patch +Patch00002: 0002-libmutipath-don-t-close-fd-on-dm_lib_release.patch +Patch00003: 0003-libmultipath-allow-force-reload-with-no-active-paths.patch +Patch00004: 0004-libmpathpersist-depend-on-libmultipath.patch +Patch00005: 0005-multipath-tools-Makefile-more-dependency-fixes-for-p.patch +Patch00006: 0006-multipath-tools-Makefile.inc-set-Wno-error-clobbered.patch +Patch00007: 0007-libmultipath-discovery.c-use-z-qualifier-for-size_t.patch +Patch00008: 0008-libmultipath-eliminate-more-signed-unsigned-comparis.patch +Patch00009: 0009-libmultipath-set_uint-fix-parsing-for-32bit.patch +Patch00010: 0010-multipath-tools-Makefile-add-install-dependency.patch +Patch00011: 0011-RH-fixup-udev-rules-for-redhat.patch +Patch00012: 0012-RH-Remove-the-property-blacklist-exception-builtin.patch +Patch00013: 0013-RH-don-t-start-without-a-config-file.patch +Patch00014: 0014-RH-use-rpm-optflags-if-present.patch +Patch00015: 0015-RH-add-mpathconf.patch +Patch00016: 0016-RH-add-wwids-from-kernel-cmdline-mpath.wwids-with-A.patch +Patch00017: 0017-RH-warn-on-invalid-regex-instead-of-failing.patch +Patch00018: 0018-RH-reset-default-find_mutipaths-value-to-off.patch +Patch00019: 0019-RH-Fix-nvme-compilation-warning.patch +Patch00020: 0020-RH-attempt-to-get-ANA-info-via-sysfs-first.patch +Patch00021: 0021-libmultipath-remove-_blacklist_exceptions-functions.patch +Patch00022: 0022-libmultipath-fix-parser-issue-with-comments-in-strin.patch +Patch00023: 0023-libmultipath-invert-regexes-that-start-with-exclamat.patch +Patch00024: 0024-libmultipath-make-dm_get_map-status-return-codes-sym.patch +Patch00025: 0025-multipathd-fix-check_path-errors-with-removed-map.patch +Patch00026: 0026-libmultipath-make-dm_flush_maps-only-return-0-on-suc.patch +Patch00027: 0027-multipathd-add-del-maps-multipathd-command.patch +Patch00028: 0028-multipath-make-flushing-maps-work-like-other-command.patch +Patch00029: 0029-multipath-delegate-flushing-maps-to-multipathd.patch +Patch00030: 0030-multipath-add-option-to-skip-multipathd-delegation.patch +Patch00031: 0031-libmultipath-fix-sysfs-dev_loss_tmo-parsing.patch +Patch00032: 0032-kpartx-read-devices-with-direct-IO.patch +Patch00033: 0033-kpartx-handle-alternate-bsd-disklabel-location.patch +Patch00034: 0034-libmultipath-fix-checker-detection-for-nvme-devices.patch +Patch00035: 0035-Makefile.inc-trim-extra-information-from-systemd-ver.patch +Patch00036: 0036-kpartx-fix-Wsign-compare-error.patch +Patch00037: 0037-libmultipath-count-pending-paths-as-active-on-loads.patch +Patch00038: 0038-multipath-deal-with-failures-flushing-maps.patch +Patch00039: 0039-libmultipath-factor-out-code-to-get-vpd-page-data.patch +Patch00040: 0040-libmultipath-limit-reading-0xc9-vpd-page.patch +Patch00041: 0041-libmultipath-add-device-to-hwtable.c.patch +Patch00042: 0042-libmultipath-move-fast_io_fail-defines-to-structs.h.patch +Patch00043: 0043-libmultipath-add-eh_deadline-multipath.conf-paramete.patch +Patch00044: 0044-libmultipath-don-t-dlclose-tur-checker-DSO.patch +Patch00045: 0045-libmultipath-warn-about-missing-braces-at-end-of-mul.patch +Patch00046: 0046-libmultipath-ignore-multipaths-sections-without-wwid.patch +Patch00047: 0047-tests-fix-missing-priority-blacklist-test.patch +Patch00048: 0048-mpathpersist-Fix-Register-and-Ignore-with-0x00-SARK.patch +Patch00049: 0049-mpathpersist-update-prkeys-file-on-changing-registra.patch +Patch00050: 0050-multipathd-Fix-multipathd-stopping-on-shutdown.patch +Patch00051: 0051-multipath.conf.5-Improve-checker_timeout-description.patch +Patch00052: 0052-libmultipath-check-for-null-wwid-before-strcmp.patch +Patch00053: 0053-libmultipath-make-find_err_path_by_dev-static.patch +Patch00054: 0054-multipathd-avoid-io_err_stat-crash-during-shutdown.patch +Patch00055: 0055-multipathd-avoid-io_err_stat-ABBA-deadlock.patch +Patch00056: 0056-multipathd-use-get_monotonic_time-in-io_err_stat-cod.patch +Patch00057: 0057-multipathd-combine-free_io_err_stat_path-and-destroy.patch +Patch00058: 0058-multipathd-cleanup-logging-for-marginal-paths.patch +Patch00059: 0059-libmpathpersist-fix-thread-safety-of-default-functio.patch +Patch00060: 0060-kpartx-free-loop-device-after-listing-partitions.patch +Patch00061: 0061-RH-fix-find_multipaths-in-mpathconf.patch +Patch00062: 0062-libmultipath-select_action-don-t-drop-map-if-alias-c.patch +Patch00063: 0063-libmultipath-check-if-user_friendly_name-is-in-use.patch +Patch00064: 0064-libmultipath-check-udev_device_get_-return-value-to-.patch +Patch00065: 0065-libmultipath-cleanup-code-to-strip-wwid-trailing-spa.patch +Patch00066: 0066-multipathd-add-recheck_wwid-option-to-verify-the-pat.patch +Patch00067: 0067-libmultipath-avoid-infinite-loop-with-bad-vpd-page-8.patch +Patch00068: 0068-libmultipath-fix-priorities-in-parse_vpd_pg83.patch +Patch00069: 0069-RH-make-parse_vpd_pg83-match-scsi_id-output.patch +Patch00070: 0070-multipathd-improve-getting-parent-udevice-in-rescan_.patch +Patch00071: 0071-multipathd-don-t-trigger-uevent-for-partitions-on-ww.patch +Patch00072: 0072-RH-mpathconf-correctly-handle-spaces-after-option-na.patch +Patch00073: 0073-multipath.conf-fix-typo-in-checker_timeout-descripti.patch +Patch00074: 0074-mpathpersist-fail-commands-when-no-usable-paths-exis.patch +Patch00075: 0075-multipath-print-warning-if-multipathd-is-not-running.patch +Patch00076: 0076-multipathd-don-t-access-path-if-it-was-deleted.patch +Patch00077: 0077-multipathd.socket-add-missing-conditions-from-servic.patch +Patch00078: 0078-libmulitpath-add-section-name-to-invalid-keyword-out.patch +Patch00079: 0079-libmultipath-use-typedef-for-keyword-handler-and-pri.patch +Patch00080: 0080-libmultipath-print-the-correct-file-when-parsing-fai.patch +Patch00081: 0081-libmultipath-pass-file-and-line-number-to-keyword-ha.patch +Patch00082: 0082-libmultipath-make-set_int-take-a-range-for-valid-val.patch +Patch00083: 0083-libmultipath-improve-checks-for-set_str.patch +Patch00084: 0084-libmultipath-split-set_int-to-enable-reuse.patch +Patch00085: 0085-libmultipath-cleanup-invalid-config-handling.patch +Patch00086: 0086-libmultipath-don-t-return-error-on-invalid-values.patch +Patch00087: 0087-multipathd-avoid-unnecessary-path-read-only-reloads.patch +Patch00088: 0088-libmultipath-make-helper-function-to-trigger-path-ue.patch +Patch00089: 0089-multipathd-trigger-udev-change-on-path-addition.patch +Patch00090: 0090-RH-add-support-to-mpathconf-for-setting-arbitrary-de.patch +Patch00091: 0091-multipath-tools-add-HPE-MSA-1060-2060-to-hwtable.patch +Patch00092: 0092-multipath-tools-update-mpp-force_readonly-in-ev_add_.patch +Patch00093: 0093-updated-HPE-MSA-builtin-config.patch +Patch00094: 0094-multipath-return-failure-on-an-invalid-remove-comman.patch +Patch00095: 0095-libmultipath-steal-the-src-string-pointer-in-merge_s.patch +Patch00096: 0096-libmultipath-enable-linear-ordering-of-bus-proto-tup.patch +Patch00097: 0097-libmultipath-use-bus_protocol_id-in-snprint_path_pro.patch +Patch00098: 0098-libmultipath-make-protocol_name-global.patch +Patch00099: 0099-libmultipath-add-a-protocol-subsection-to-multipath..patch +Patch00100: 0100-libmultipath-Set-the-scsi-timeout-parameters-by-path.patch +Patch00101: 0101-libmultipath-check-the-overrides-pctable-for-path-va.patch +Patch00102: 0102-libmultipath-fix-eh_deadline-documentation.patch +Patch00103: 0103-libmultipath-Add-documentation-for-the-protocol-subs.patch +Patch00104: 0104-libmultipath-use-symbolic-value-for-invalid-pcentry.patch +Patch00105: 0105-multipathd-handle-fpin-events.patch +Patch00106: 0106-multipathd-disallow-changing-to-from-fpin-marginal-p.patch +Patch00107: 0107-libmultipath-unset-detect_checker-for-clariion-Unity.patch +Patch00108: 0108-multipathd-Add-missing-ctype-include.patch +Patch00109: 0109-multipathd-replace-libreadline-with-libedit.patch +Patch00110: 0110-multipath-fix-systemd-timers-in-the-initramfs.patch +Patch00111: 0111-multipathd-factor-out-the-code-to-flush-a-map-with-n.patch +Patch00112: 0112-libmultipath-return-success-if-we-raced-to-remove-a-.patch +Patch00113: 0113-multipathd-Handle-losing-all-path-in-update_map.patch +Patch00114: 0114-multipathd-ignore-duplicated-multipathd-command-keys.patch +Patch00115: 0115-multipath-tools-use-run-instead-of-dev-shm.patch +Patch00116: 0116-kpartx-hold-device-open-until-partitions-have-been-c.patch +Patch00117: 0117-libmultipath-cleanup-remove_feature.patch +Patch00118: 0118-libmultipath-cleanup-add_feature.patch +Patch00119: 0119-multipath-tests-tests-for-adding-and-removing-featur.patch +Patch00120: 0120-libmultipath-fix-queue_mode-feature-handling.patch +Patch00121: 0121-multipath-tests-tests-for-reconcile_features_with_qu.patch +Patch00122: 0122-libmultipath-prepare-proto_id-for-use-by-non-scsi-de.patch +Patch00123: 0123-libmultipath-get-nvme-path-transport-protocol.patch +Patch00124: 0124-libmultipath-enforce-queue_mode-bio-for-nmve-tcp-pat.patch +Patch00125: 0125-multipath-add-historical-service-time-to-the-man-pag.patch +Patch00126: 0126-libmultipath-copy-mpp-hwe-from-pp-hwe.patch +Patch00127: 0127-libmultipath-don-t-leak-memory-on-invalid-strings.patch +Patch00128: 0128-libmutipath-validate-the-argument-count-of-config-st.patch +Patch00129: 0129-libmultipath-select-resize-action-even-if-reload-is-.patch +Patch00130: 0130-libmultipath-cleanup-ACT_CREATE-code-in-select_actio.patch +Patch00131: 0131-libmultipath-keep-renames-from-stopping-other-multip.patch + +# runtime +Requires: %{name}-libs = %{version}-%{release} +Requires: kpartx = %{version}-%{release} +Requires: device-mapper >= 1.02.96 +Requires: userspace-rcu +Requires: libedit +Requires(post): systemd-units +Requires(preun): systemd-units +Requires(postun): systemd-units + +# build/setup +BuildRequires: libaio-devel, device-mapper-devel >= 1.02.89 +BuildRequires: libselinux-devel, libsepol-devel +BuildRequires: libedit-devel, ncurses-devel +BuildRequires: systemd-units, systemd-devel +BuildRequires: json-c-devel, perl-interpreter, pkgconfig, gcc +BuildRequires: userspace-rcu-devel + +%description +%{name} provides tools to manage multipath devices by +instructing the device-mapper multipath kernel module what to do. +The tools are : +* multipath - Scan the system for multipath devices and assemble them. +* multipathd - Detects when paths fail and execs multipath to update things. + +%package libs +Summary: The %{name} modules and shared library +Group: System Environment/Libraries +# only libmpathcmd is LGPLv2+ +License: GPLv2 and LGPLv2+ + +%description libs +The %{name}-libs provides the path checker +and prioritizer modules. It also contains the libmpathpersist and +libmpathcmd shared libraries, as well as multipath's internal library, +libmultipath. + +%package devel +Summary: Development libraries and headers for %{name} +Group: Development/Libraries +Requires: %{name} = %{version}-%{release} +Requires: %{name}-libs = %{version}-%{release} + +%description devel +This package contains the files need to develop applications that use +device-mapper-multipath's lbmpathpersist and libmpathcmd libraries. + +%package -n kpartx +Summary: Partition device manager for device-mapper devices +Group: System Environment/Base + +%description -n kpartx +kpartx manages partition creation and removal for device-mapper devices. + +%package -n libdmmp +Summary: device-mapper-multipath C API library +Group: System Environment/Libraries +License: GPLv3+ +Requires: json-c +Requires: %{name} = %{version}-%{release} +Requires: %{name}-libs = %{version}-%{release} + +%description -n libdmmp +This package contains the shared library for the device-mapper-multipath +C API library. + +%package -n libdmmp-devel +Summary: device-mapper-multipath C API library headers +Group: Development/Libraries +Requires: pkgconfig +Requires: libdmmp = %{version}-%{release} + +%description -n libdmmp-devel +This package contains the files needed to develop applications that use +device-mapper-multipath's libdmmp C API library + +%prep +%autosetup -n multipath-tools-0.8.4 -p1 +cp %{SOURCE1} . + +%build +%define _sbindir /usr/sbin +%define _libdir /usr/%{_lib} +%define _libmpathdir %{_libdir}/multipath +%define _pkgconfdir %{_libdir}/pkgconfig +make %{?_smp_mflags} LIB=%{_lib} + +%install +make install \ + DESTDIR=%{buildroot} \ + bindir=%{_sbindir} \ + syslibdir=%{_libdir} \ + usrlibdir=%{_libdir} \ + libdir=%{_libmpathdir} \ + rcdir=%{_initrddir} \ + unitdir=%{_unitdir} \ + includedir=%{_includedir} \ + pkgconfdir=%{_pkgconfdir} \ + tmpfilesdir=%{_tmpfilesdir} + +# tree fix up +install -d %{buildroot}/etc/multipath +rm -rf %{buildroot}/%{_initrddir} + + +%post +%systemd_post multipathd.service + +%preun +%systemd_preun multipathd.service + +%postun +if [ $1 -ge 1 ] ; then + /sbin/multipathd forcequeueing daemon > /dev/null 2>&1 || : +fi +%systemd_postun_with_restart multipathd.service + +%triggerun -- %{name} < 0.4.9-37 +# make sure old systemd symlinks are removed after changing the [Install] +# section in multipathd.service from multi-user.target to sysinit.target +/bin/systemctl --quiet is-enabled multipathd.service >/dev/null 2>&1 && /bin/systemctl reenable multipathd.service ||: + +%files +%defattr(-,root,root,-) +%{_sbindir}/multipath +%{_sbindir}/multipathd +%{_sbindir}/mpathconf +%{_sbindir}/mpathpersist +%{_unitdir}/multipathd.service +%{_unitdir}/multipathd.socket +%{_mandir}/man5/multipath.conf.5.gz +%{_mandir}/man8/multipath.8.gz +%{_mandir}/man8/multipathd.8.gz +%{_mandir}/man8/mpathconf.8.gz +%{_mandir}/man8/mpathpersist.8.gz +%config %{_udevrulesdir}/62-multipath.rules +%config %{_udevrulesdir}/11-dm-mpath.rules +%{_tmpfilesdir}/multipath.conf +%{!?_licensedir:%global license %%doc} +%license LICENSES/GPL-2.0 LICENSES/LGPL-2.0 +%doc README +%doc README.alua +%doc multipath.conf +%dir /etc/multipath + +%files libs +%defattr(-,root,root,-) +%doc README +%{!?_licensedir:%global license %%doc} +%license LICENSES/GPL-2.0 LICENSES/LGPL-2.0 +%{_libdir}/libmultipath.so +%{_libdir}/libmultipath.so.* +%{_libdir}/libmpathpersist.so.* +%{_libdir}/libmpathcmd.so.* +%dir %{_libmpathdir} +%{_libmpathdir}/* + +%post libs -p /sbin/ldconfig + +%postun libs -p /sbin/ldconfig + +%files devel +%defattr(-,root,root,-) +%doc README LICENSES/GPL-2.0 LICENSES/LGPL-2.0 +%{_libdir}/libmpathpersist.so +%{_libdir}/libmpathcmd.so +%{_includedir}/mpath_cmd.h +%{_includedir}/mpath_persist.h +%{_mandir}/man3/mpath_persistent_reserve_in.3.gz +%{_mandir}/man3/mpath_persistent_reserve_out.3.gz + +%files -n kpartx +%defattr(-,root,root,-) +%doc README +%{!?_licensedir:%global license %%doc} +%license LICENSES/GPL-2.0 +%{_sbindir}/kpartx +/usr/lib/udev/kpartx_id +%{_mandir}/man8/kpartx.8.gz +%config %{_udevrulesdir}/11-dm-parts.rules +%config %{_udevrulesdir}/66-kpartx.rules +%config %{_udevrulesdir}/68-del-part-nodes.rules + +%files -n libdmmp +%defattr(-,root,root,-) +%doc README LICENSES/GPL-3.0 +%{_libdir}/libdmmp.so.* + +%post -n libdmmp -p /sbin/ldconfig + +%postun -n libdmmp -p /sbin/ldconfig + +%files -n libdmmp-devel +%defattr(-,root,root,-) +%doc README LICENSES/GPL-3.0 +%{_libdir}/libdmmp.so +%dir %{_includedir}/libdmmp +%{_includedir}/libdmmp/* +%{_mandir}/man3/dmmp_* +%{_mandir}/man3/libdmmp.h.3.gz +%{_pkgconfdir}/libdmmp.pc + +%changelog +* Fri Feb 3 2023 Benjamin Marzinski 0.8.4-37 +- Fix bugzilla linked to the changes (was previously linked to + the wrong bug, 2162537) +- Resolves: bz #2166468 + +* Wed Feb 1 2023 Benjamin Marzinski 0.8.4-36 +- Add 0129-libmultipath-select-resize-action-even-if-reload-is-.patch +- Add 0130-libmultipath-cleanup-ACT_CREATE-code-in-select_actio.patch +- Add 0131-libmultipath-keep-renames-from-stopping-other-multip.patch +- Resolves: bz #2166468 + +* Mon Jan 16 2023 Benjamin Marzinski 0.8.4-35 +- Add 0127-libmultipath-don-t-leak-memory-on-invalid-strings.patch +- Add 0128-libmutipath-validate-the-argument-count-of-config-st.patch +- Resolves: bz #2155560 + +* Tue Nov 29 2022 Benjamin Marzinski 0.8.4-34 +- Add 0126-libmultipath-copy-mpp-hwe-from-pp-hwe.patch + * Fixes bz #2126714 +- Cleanup multiple CI tests +- Resolves: bz #2126714 + +* Wed Nov 23 2022 Benjamin Marzinski 0.8.4-33 +- Add 0125-multipath-add-historical-service-time-to-the-man-pag.patch + * Fixes bz #2141996 +- Modify tests/multipath_conf_syntax/main.sh + * fix unrelated test error +- Resolves: bz #2141996 + +* Thu Nov 10 2022 Benjamin Marzinski 0.8.4-32 +- Add 0116-kpartx-hold-device-open-until-partitions-have-been-c.patch + * Fixes bz #2128885 +- Add 0117-libmultipath-cleanup-remove_feature.patch +- Add 0118-libmultipath-cleanup-add_feature.patch +- Add 0119-multipath-tests-tests-for-adding-and-removing-featur.patch +- Add 0120-libmultipath-fix-queue_mode-feature-handling.patch +- Add 0121-multipath-tests-tests-for-reconcile_features_with_qu.patch +- Add 0122-libmultipath-prepare-proto_id-for-use-by-non-scsi-de.patch +- Add 0123-libmultipath-get-nvme-path-transport-protocol.patch +- Add 0124-libmultipath-enforce-queue_mode-bio-for-nmve-tcp-pat.patch + * Fixes bz #2022359 +- Resolves: bz #2022359, #2128885 + +* Thu Oct 13 2022 Benjamin Marzinski 0.8.4-31 +- Add 0114-multipathd-ignore-duplicated-multipathd-command-keys.patch + * Fixes bz #2133996 +- Add 0115-multipath-tools-use-run-instead-of-dev-shm.patch + * Fixes bz #2133990 +- Resolves: bz #2133990, #2133996 + +* Fri Sep 9 2022 Benjamin Marzinski 0.8.4-30 +- Add 0111-multipathd-factor-out-the-code-to-flush-a-map-with-n.patch +- Add 0112-libmultipath-return-success-if-we-raced-to-remove-a-.patch +- Add 0113-multipathd-Handle-losing-all-path-in-update_map.patch +- Resolves: bz #2110485 + +* Fri Sep 2 2022 Benjamin Marzinski 0.8.4-29 +- Rebuild for rhel-8.8.0 +- Resolves: bz #2123446 + +* Wed Aug 24 2022 Benjamin Marzinski 0.8.4-28 +- Add 0110-multipath-fix-systemd-timers-in-the-initramfs.patch +- Resolves: bz #1916168 + +* Fri Aug 19 2022 Benjamin Marzinski 0.8.4-27 +- Add 0108-multipathd-Add-missing-ctype-include.patch +- Add 0109-multipathd-replace-libreadline-with-libedit.patch + * replace readline with libedit, to avoid license conflicts +- Resolves: bz #2119887 + +* Wed Jun 8 2022 Benjamin Marzinski 0.8.4-26 +- Add 0107-libmultipath-unset-detect_checker-for-clariion-Unity.patch +- Resolves: bz #2082205 + +* Tue May 17 2022 Benjamin Marzinski 0.8.4-25 +- Add 0105-multipathd-handle-fpin-events.patch +- Add 0106-multipathd-disallow-changing-to-from-fpin-marginal-p.patch +- Resolves: bz #2083077 + +* Mon May 16 2022 Benjamin Marzinski 0.8.4-24 +- Add 0094-multipath-return-failure-on-an-invalid-remove-comman.patch + * Fixes bz #2052054 +- Add 0095-libmultipath-steal-the-src-string-pointer-in-merge_s.patch +- Add 0096-libmultipath-enable-linear-ordering-of-bus-proto-tup.patch +- Add 0097-libmultipath-use-bus_protocol_id-in-snprint_path_pro.patch +- Add 0098-libmultipath-make-protocol_name-global.patch +- Add 0099-libmultipath-add-a-protocol-subsection-to-multipath..patch +- Add 0100-libmultipath-Set-the-scsi-timeout-parameters-by-path.patch +- Add 0101-libmultipath-check-the-overrides-pctable-for-path-va.patch +- Add 0102-libmultipath-fix-eh_deadline-documentation.patch +- Add 0103-libmultipath-Add-documentation-for-the-protocol-subs.patch +- Add 0104-libmultipath-use-symbolic-value-for-invalid-pcentry.patch + * The above 10 patches implement feature from bz #2065477 +- Resolves: bz #2052054, #2065477 + +* Tue May 10 2022 Benjamin Marzinski 0.8.4-23 +- Add 0091-multipath-tools-add-HPE-MSA-1060-2060-to-hwtable.patch + * Partial fix for bz #2058222 +- Add 0092-multipath-tools-update-mpp-force_readonly-in-ev_add_.patch + * Fixes bz #2065468 +- Add 0093-updated-HPE-MSA-builtin-config.patch + * Fixes bz #2058222 +- Resolves bz #2065468, #2058222 + +* Mon Feb 7 2022 Benjamin Marzinski 0.8.4-22 +- Add 0090-RH-add-support-to-mpathconf-for-setting-arbitrary-de.patch +- Resolves: bz #1981240 + +* Tue Jan 18 2022 Benjamin Marzinski 0.8.4-21 +- Add 0088-libmultipath-make-helper-function-to-trigger-path-ue.patch +- Add 0089-multipathd-trigger-udev-change-on-path-addition.patch +- Resolves: bz #1862032 + +* Thu Nov 18 2021 Benjamin Marzinski 0.8.4-20 +- Add 0079-libmultipath-use-typedef-for-keyword-handler-and-pri.patch +- Add 0080-libmultipath-print-the-correct-file-when-parsing-fai.patch +- Add 0081-libmultipath-pass-file-and-line-number-to-keyword-ha.patch +- Add 0082-libmultipath-make-set_int-take-a-range-for-valid-val.patch +- Add 0083-libmultipath-improve-checks-for-set_str.patch +- Add 0084-libmultipath-split-set_int-to-enable-reuse.patch +- Add 0085-libmultipath-cleanup-invalid-config-handling.patch +- Add 0086-libmultipath-don-t-return-error-on-invalid-values.patch + * Above 8 patches fix bz #1900595 +- Add 0087-multipathd-avoid-unnecessary-path-read-only-reloads.patch + * Fixes bz #2009624 +- Resolves: bz #1900595, #2009624, #2021823 + +* Thu Oct 28 2021 Benjamin Marzinski 0.8.4-19 +- Fix multipath_conf_syntax test to work with bz #1918150 +- Resolves: bz #1918150 + +* Thu Oct 7 2021 Benjamin Marzinski 0.8.4-18 +- Add 0077-multipathd.socket-add-missing-conditions-from-servic.patch + * Fixes bz #2008101 +- Add 0078-libmulitpath-add-section-name-to-invalid-keyword-out.patch + * Fixes bz #1918150 +- Resolves: bz #1918150, #2008101 + +* Fri Jul 23 2021 Benjamin Marzinski 0.8.4-17 +- Add 0074-mpathpersist-fail-commands-when-no-usable-paths-exis.patch + * Fixes bz #1984723 +- Add 0075-multipath-print-warning-if-multipathd-is-not-running.patch + * Fixes bz #1973592 +- Add 0076-multipathd-don-t-access-path-if-it-was-deleted.patch + * Found by coverity +- Resolves: bz #1973592, #1984723 + +* Thu Jul 22 2021 Benjamin Marzinski 0.8.4-16 +- Add missing patch +- Related: bz #1982598 + +* Thu Jul 22 2021 Benjamin Marzinski 0.8.4-15 +- Add 0073-multipath.conf-fix-typo-in-checker_timeout-descripti.patch +- Resolves: bz #1982598 + +* Mon Jul 12 2021 Benjamin Marzinski 0.8.4-14 +- Add 0072-RH-mpathconf-correctly-handle-spaces-after-option-na.patch +- Resolves: bz #1979470 + +* Wed Apr 28 2021 Benjamin Marzinski 0.8.4-13 +- Add 0065-libmultipath-cleanup-code-to-strip-wwid-trailing-spa.patch +- Add 0066-multipathd-add-recheck_wwid-option-to-verify-the-pat.patch +- Add 0067-libmultipath-avoid-infinite-loop-with-bad-vpd-page-8.patch +- Add 0068-libmultipath-fix-priorities-in-parse_vpd_pg83.patch +- Add 0069-RH-make-parse_vpd_pg83-match-scsi_id-output.patch +- Add 0070-multipathd-improve-getting-parent-udevice-in-rescan_.patch +- Add 0071-multipathd-don-t-trigger-uevent-for-partitions-on-ww.patch + * add recheck_wwid option + * Fixes bz #1907904 +- Resolves: bz #1907904 + +* Wed Apr 21 2021 Benjamin Marzinski 0.8.4-12 +- Add 0064-libmultipath-check-udev_device_get_-return-value-to-.patch + * Fixes bz #1946940 +- Resolves: bz #1946940 + +* Fri Mar 12 2021 Benjamin Marzinski 0.8.4-11 +- Add 0062-libmultipath-select_action-don-t-drop-map-if-alias-c.patch + * Fall back to WWID names instead of removing existing device +- Add 0063-libmultipath-check-if-user_friendly_name-is-in-use.patch + * make sure to choose a user_friendly_name that isn't in use + * Fixes bz #1937832 +- Add alias_clash CI test +- Remove unused multipath.conf +- Resolves: bz #1937832 + +* Wed Feb 10 2021 Benjamin Marzinski 0.8.4-9 +- Add 0060-kpartx-free-loop-device-after-listing-partitions.patch + * Fixes bz #1925490 +- Add 0061-RH-fix-find_multipaths-in-mpathconf.patch + * Fixes bz #1921651 +- Resolves: bz #1921651, #1925490 + +* Wed Jan 27 2021 Benjamin Marzinski 0.8.4-7 +- Add 0052-libmultipath-check-for-null-wwid-before-strcmp.patch + * Missing part of fix for bz #1897815 +- Add 0053-libmultipath-make-find_err_path_by_dev-static.patch +- Add 0054-multipathd-avoid-io_err_stat-crash-during-shutdown.patch +- Add 0055-multipathd-avoid-io_err_stat-ABBA-deadlock.patch +- Add 0056-multipathd-use-get_monotonic_time-in-io_err_stat-cod.patch +- Add 0057-multipathd-combine-free_io_err_stat_path-and-destroy.patch +- Add 0058-multipathd-cleanup-logging-for-marginal-paths.patch + * The above 6 patches fix bz #1920795 +- Add 0059-libmpathpersist-fix-thread-safety-of-default-functio.patch + * Fixes bz #1913549 +- Resolves: bz #1897815, #1913549, #1920795 + +* Tue Dec 15 2020 Benjamin Marzinski 0.8.4-6 +- Add 0050-multipathd-Fix-multipathd-stopping-on-shutdown.patch + * Fixes bz #1892496 +- Add 0051-multipath.conf.5-Improve-checker_timeout-description.patch + * Fixes bz #1906073 +- Resolves: bz #1892496, #1906073 + +* Tue Nov 10 2020 Benjamin Marzinski 0.8.4-6 +- Add 0039-libmultipath-factor-out-code-to-get-vpd-page-data.patch +- Add 0040-libmultipath-limit-reading-0xc9-vpd-page.patch + * Only check for rdac support on configured devices, or devices where + vpd page 0x00 indicates support for vpd page 0xc9 + * Fixes bz #1877415 +- Add 0041-libmultipath-add-device-to-hwtable.c.patch + * Add support for Fujitsu Eternus AB/HB + * Fixes bz #1857429 +- Add 0042-libmultipath-move-fast_io_fail-defines-to-structs.h.patch +- Add 0043-libmultipath-add-eh_deadline-multipath.conf-paramete.patch + * Fixes bz #1819897 +- Add 0044-libmultipath-don-t-dlclose-tur-checker-DSO.patch + * Fixes bz #1875384 +- Add 0045-libmultipath-warn-about-missing-braces-at-end-of-mul.patch + * Fixes bz #1897530 +- Add 0046-libmultipath-ignore-multipaths-sections-without-wwid.patch + * Fixes bz #1897815 +- Add 0047-tests-fix-missing-priority-blacklist-test.patch +- Add 0048-mpathpersist-Fix-Register-and-Ignore-with-0x00-SARK.patch + * Make mpathpersist Register and Ignore command clear the registration + with a 0x0 SARK, like sg_persist does. + * Fixes bz #1894103 +- Add 0049-mpathpersist-update-prkeys-file-on-changing-registra.patch + * Fixes bz #1900522 + * The above 11 patches have all been submitted upstream +- Resolves: bz #1819897, #1857429, #1875384, #1877415, #1894103, #1897530 +- Resolves: bz #1897815, #1900522 + +* Mon Jul 13 2020 Benjamin Marzinski 0.8.4-5 +- Add 0038-multipath-deal-with-failures-flushing-maps.patch + * Don't print "fail" if multipath needs to failback to removing + a device itself. + * Fixes bz #1845875 +- Modify multipath_conf_syntax CI test + * It started failing because "multpath -F" can now start multipathd + via the socket, and the test expected multipathd to not be running +- Resolves: bz #1845875 + +* Fri Jul 10 2020 Benjamin Marzinski 0.8.4-4 +- Add 0037-libmultipath-count-pending-paths-as-active-on-loads.patch + * make udev treat the device as having paths, so it will run kpartx + * Fixes bz #1846866 +- Resolves: bz #1846866 + +* Mon Jul 6 2020 Benjamin Marzinski 0.8.4-3 +- Add 0024-libmultipath-make-dm_get_map-status-return-codes-sym.patch +- Add 0025-multipathd-fix-check_path-errors-with-removed-map.patch +- Add 0026-libmultipath-make-dm_flush_maps-only-return-0-on-suc.patch +- Add 0027-multipathd-add-del-maps-multipathd-command.patch +- Add 0028-multipath-make-flushing-maps-work-like-other-command.patch +- Add 0029-multipath-delegate-flushing-maps-to-multipathd.patch +- Add 0030-multipath-add-option-to-skip-multipathd-delegation.patch + * The above 7 patches fix bz #1845875. Multipath now attempts to + delegate device removal to multipathd, and multipathd handles + external device removal better. +- Add 0031-libmultipath-fix-sysfs-dev_loss_tmo-parsing.patch + * Fixes bz #1852843 +- Add 0032-kpartx-read-devices-with-direct-IO.patch + * Fixes bz #1814858 +- Add 0033-kpartx-handle-alternate-bsd-disklabel-location.patch +- Add 0034-libmultipath-fix-checker-detection-for-nvme-devices.patch + * fixes bz #1845915 +- Add 0035-Makefile.inc-trim-extra-information-from-systemd-ver.patch +- Add 0036-kpartx-fix-Wsign-compare-error.patch +- Resolves: bz #1814858, #1845875, #1845915, #1852843 + +* Tue Jun 9 2020 Benjamin Marzinski 0.8.4-2 +- Add 0021-libmultipath-remove-_blacklist_exceptions-functions.patch +- Add 0022-libmultipath-fix-parser-issue-with-comments-in-strin.patch +- Add 0023-libmultipath-invert-regexes-that-start-with-exclamat.patch + * This commit also changes the default devnode blacklist to + blacklist all devices except scsi, dasd, and nvme. +- Resolves: bz #1828180 + +* Wed May 27 2020 Benjamin Marzinski 0.8.4-1 +- Update Source to upstream version 0.8.4 + * This version includes the fixes for bz #1768894 & #1821214 + * Previous patches 0001-0009 are included in this version +- Rename files + * Previous patches 0010-0019 are now 0011-0020 +- Add 0001-libmultipath-assign-variable-to-make-gcc-happy.patch +- Add 0002-libmutipath-don-t-close-fd-on-dm_lib_release.patch +- Add 0003-libmultipath-allow-force-reload-with-no-active-paths.patch + * make multipath and multipathd reload maps with no active paths + (bz #1814858) +- Add 0004-libmpathpersist-depend-on-libmultipath.patch +- Add 0005-multipath-tools-Makefile-more-dependency-fixes-for-p.patch +- Add 0006-multipath-tools-Makefile.inc-set-Wno-error-clobbered.patch +- Add 0007-libmultipath-discovery.c-use-z-qualifier-for-size_t.patch +- Add 0008-libmultipath-eliminate-more-signed-unsigned-comparis.patch +- Add 0009-libmultipath-set_uint-fix-parsing-for-32bit.patch +- Add 0010-multipath-tools-Makefile-add-install-dependency.patch + * The above 10 patches have been submitted upstream +- Resolves: bz #1768894, #1814858, #1821214 + +* Fri Nov 8 2019 Benjamin Marzinski 0.8.3-3 +- Rename files + * Previous patches 0004-0013 are now 0010-0019 + * 0014-RH-add-mpathconf.patch now makes mpathconf default to not + printing foreign devices (bz #1760709) +- Add 0004-libmultipath-remove-unused-path-prio_args.patch +- Add 0005-libmultipath-constify-get_unaligned_be.patch +- Add 0006-libmultipath-add-missing-hwe-mpe-variable-merges.patch +- Add 0007-libmultipath-fix-sgio_get_vpd-looping.patch +- Add 0008-libmultipath-add-vend_id-to-get_vpd_sgio.patch +- Add 0009-libmultipath-add-code-to-get-vendor-specific-vpd-dat.patch + * Add the '%g' maps and paths format wildcard, and the vpd_vendor + multipath.conf devices section parameter. (bz #1527212) +- Resolves: bz #1527212, #1760709 + +* Mon Oct 14 2019 Benjamin Marzinski 0.8.3-2 +- Update CI tests +- Related: bz #1690515 + +* Tue Oct 8 2019 Benjamin Marzinski 0.8.3-1 +- Update Source to upstream version 0.8.3 + * This version includes the fixes for bz #1690515, #1703439, + #1719562 & #1747534 + * Previous patches 0001-0017 & 0031-0036 are included in this version +- Rename files + * Previous patches 0018-0026 & 0028 are now 0004-0013 + * 0008-RH-add-mpathconf.patch has been modified to add a + --property_blacklist option to fix bz #1753729 +- Add 0001-multipathd-warn-when-configuration-has-been-changed.patch + * Multipath now logs a warning message when the configuration file + has been changed to fix bz #1750594 +- Add 0002-libmultipath-fix-leak-in-foreign-code.patch +- Add 0003-Fix-leak-in-mpathpersist.patch + * The above 3 patches have been submitted upstream +- Resolves: bz #1690515, #1703439, #1719562, #1747534, #1750594, #1753729 + +* Mon Jun 3 2019 Benjamin Marzinski 0.8.0-5 +- Bump release number for test fix commit 0b68e623 +- Related: bz #1666322 + +* Tue May 28 2019 Benjamin Marzinski 0.8.0-4 +- Add 0035-BZ-1700911-hwtable-add-Lenovo-DE-series.patch +- Add 0036-libmultipath-make-vector_foreach_slot_backwards-work.patch + * Fix for bz #1713459 +- Fixes for bz #1666322 and #1669097 were included in 0.8.0 upstream + code +- Resolves: bz #1666322, #1669097, #1700911, #1713459 + +* Thu May 16 2019 Benjamin Marzinski 0.8.0-3 +- Add 0031-libmultipath-handle-clock_gettime-failures-in-tur-ch.patch +- Add 0032-kpartx-fail-if-dup-of-dasd-file-descriptor-fails.patch +- Add 0033-multipathd-fix-REALLOC_REPLY-with-max-length-reply.patch +- Add 0034-multipathd-handle-NULL-return-from-genhelp_handler.patch + * The above 4 patches have been submitted upstream +- Related: bz #1498546 + +* Tue Apr 30 2019 Benjamin Marzinski 0.8.0-2 +- Add 0029-BZ-1700451-check-on-multipathd-without-starting-it.patch +- Add 0030-BZ-1700451-test-socket-connection-in-non-blocking-mo.patch + * the "multipath -u" call in 62-multipath.rules should no longer hang + on multipathd startup. +- Resolves: bz #1700451 + +* Mon Apr 8 2019 Benjamin Marzinski 0.8.0-1 +- Update Source to upstream version 0.8.0 + * Previous patches 0001-0006 & 0015-0019 are included in this commit +- Rename files + * Previous patches 0007-0014 are now 0018-0025 + * Previous patches 0020-0023 are now 0001, 0006 & 0007 +- Add 0002-libmultipath-handle-existing-paths-in-marginal_path-.patch +- Add 0003-multipathd-cleanup-marginal-paths-checking-timers.patch +- Add 0004-libmultipath-fix-marginal-paths-queueing-errors.patch +- Add 0005-libmultipath-fix-marginal_paths-nr_active-check.patch +- Add 0008-libmutipath-continue-to-use-old-state-on-PATH_PENDIN.patch +- Add 0009-multipathd-use-update_path_groups-instead-of-reload_.patch +- Add 0010-multipath.conf-add-missing-options-to-man-page.patch +- Add 0011-libmultipath-add-get_uid-fallback-code-for-NVMe-devi.patch +- Add 0012-libmulitpath-cleanup-uid_fallback-code.patch +- Add 0013-multipathd-handle-changed-wwids-by-removal-and-addit.patch + * Multipath will now automatically remove and re-add paths if + their wwid changes +- Add 0014-multipathd-remove-wwid_changed-path-attribute.patch +- Add 0015-multipathd-ignore-disable_changed_wwids.patch + * Since paths are now getting removed and re-added there is + no need to disable them. +- Add 0016-multipathd-Don-t-use-fallback-code-after-getting-wwi.patch +- Add 0017-libmultipath-silence-dm_is_mpath-error-messages.patch +- Add 0026-RH-Fix-nvme-compilation-warning.patch +- Add 0027-Fix-systemd-version-detection.patch + * The above 16 patches have been submitted upstream +- Add 0028-RH-attempt-to-get-ANA-info-via-sysfs-first.patch + * Red Hat has a sysfs method to access the ANA state, which isn't + upstream, and will not be. This will probably go away in the + future. +- Resolves: bz #1498546 + +* Mon Feb 25 2019 Benjamin Marzinski 0.7.8-7 +- Add 0023-BZ-1673167-fixup-wwid-recheck.patch +- Resolves: bz #1673167 + +* Sun Feb 24 2019 Benjamin Marzinski 0.7.8-6 +- Fix multipath_conf_syntax CI test + * need to wait for udev to settle before removing multipath device +- Resolves: bz #1673167 + +* Mon Feb 18 2019 Benjamin Marzinski 0.7.8-5 +- Add 0021-BZ-1673167-Fix-miscounting-active-paths.patch +- Add 0022-BZ-1673167-ignore-failed-wwid-recheck.patch +- Resolves: bz #1673167 + +* Thu Jan 24 2019 Benjamin Marzinski 0.7.8-4 +- Add 0020-BZ-1668693-disable-user_friendly_names-for-NetApp.patch +- Resolves: bz #1668693 + +* Tue Dec 11 2018 Benjamin Marzinski 0.7.8-3 +- Add 0017-BZ-1648397-fix-mpp-hwe-handling-when-paths-are-freed.patch + * Fix crash by deleting in use hardware entry list pointer +- Add 0018-libmultipath-cleanup-pthread_cleanup_pop-call.patch + * Fix compiler warning +- Add 0019-libmultipath-fix-false-removes-in-dmevents-polling-c.patch + * The above 3 patches have been accepted upstream +- Resolves: bz #1648397 + +* Wed Oct 10 2018 Benjamin Marzinski 0.7.8-2 +- Add 0015-libmultipath-free-allocated-value-in-set_int.patch +- Add 0016-kpartx-fix-new-memory-leak-in-dm_find_part.patch +- change hardcoded /usr/lib/udev/rules.d to _udevrulesdir +- Resolves: bz #1606947 + +* Wed Oct 10 2018 Benjamin Marzinski 0.7.8-1 +- Update Source to upstream version 0.7.8 + * Previous patches 0001-0011 are included in this version +- Rename files + * Previous patches 0012-0019 are now patches 0007-0014 +- Add 0001-multipath-tweak-logging-style.patch +- Add 0002-multipathd-check-for-NULL-udevice-in-cli_add_path.patch +- Add 0003-libmultipath-remove-max_fds-code-duplication.patch +- Add 0004-multipathd-set-return-code-for-multipathd-commands.patch +- Add 0005-mpathpersist-fix-registration-rollback-issue.patch +- Add 0006-libmultipath-timeout-on-unresponsive-tur-thread.patch + * The above 6 patches have been submitted upstream +- Resolves: bz #1606947, #1631883 + +* Tue Jul 17 2018 Benjamin Marzinski 0.7.7-2.gitef6d98b +- Update Source to latest upstream commit + * Previous patches 0001-0018 are included in this commit +- Rename files + * Previous patches 0019-0028 are now patches 0002-0003 & 0012-0019 +- Add 0001-libmultipath-remove-last-of-rbd-code.patch +- Add 0004-mpathpersist-add-param-alltgpt-option.patch + * mpathpersist now accepts --param-alltgpt +- Add 0005-libmutipath-remove-unused-IDE-bus-type.patch +- Add 0006-multipathd-add-new-protocol-path-wildcard.patch + * multipathd show paths format now accepts %%P for the path protocol/transport +- Add 0007-libmultipath-add-protocol-blacklist-option.patch + * You can now use the "protocol" blacklist section parameter to blacklist + by protocol/transport +- Add 0008-libmultipath-remove-_filter_-blacklist-functions.patch +- Add 0009-multipath-tests-change-to-work-with-old-make-version.patch +- Add 0010-multipath-tests-add-blacklist-tests.patch +- Add 0011-mpathpersist-add-missing-param-rk-usage-info.patch +- Refresh 0013-RH-Remove-the-property-blacklist-exception-builtin.patch +- Modify 0016-RH-add-mpathconf.patch + * improve usage message and man page +- Modify device-mapper-multipath.spec + * updated dependencies + +* Tue Jun 12 2018 Benjamin Marzinski 0.7.7-1 +- Update Source to 0.7.7 + * Previous patches 0001-0009 & 0016 are included in this commit +- Add upstream patches since 0.7.7 + * patches 0001-0012 are from upstream commits since 0.7.7 +- Add 0013-libmultipath-print-correct-default-for-delay_-_check.patch + * fix minor configuration printing issue. posted upstream +- Add 0014-multipath.conf.5-clarify-property-whitelist-handling.patch + * clarify property blacklist_excecptions handling in man page + posted upstream +- Add 0015-mpathpersist-add-all_tg_pt-option.patch + * add new all_tg_pt multpiath.conf option. posted upstream +- Add 0016-libmultipath-remove-rbd-code.patch + * remove unused rbd code. posted upstream +- Add 0017-mpathpersist-fix-aptpl-support.patch + * add ":aptpl" suffix for reservation_key to fix aptpl support. + posted upstream +- Add 0018-multipath-don-t-check-timestamps-without-a-path.patch + * fix multipath null dereference crash. posted upstream +- Add 0019-libmultipath-fix-detect-alua-corner-case.patch + * fix alua detection with retain_hardware_handler set to off. posted + upstream +- Add 0020-multipath-fix-setting-conf-version.patch + * multipath wasn't setting the kernel version correctly. posted + upstream +- Add 0028-RH-reset-default-find_mutipaths-value-to-off.patch + * default to RHEL7 and older device detection style. Redhat specific, + to keep customer experience the same. +- Rename files + * Previous patches 0010-0015 & 0017 are now patches 0021-0027 +- Modify 0021-RH-fixup-udev-rules-for-redhat.patch + * Fix spurious compile warning with redhat compile options +- Modify 0022-RH-Remove-the-property-blacklist-exception-builtin.patch + * clarify changes in man page + +* Tue May 15 2018 Benjamin Marzinski 0.7.6-4.git1cb704b +- Rename files + * Previous patches 0010-0017 are now patches 0012-0019 + +* Tue Apr 24 2018 Benjamin Marzinski 0.7.6-3.git1cb704b +- Add 0008-multipathd-add-failures-path-format-wildcard.patch +- Add 0009-multipathd-fix-reservation_key-check.patch +- Rename files + * Previous patches 0008-0015 are now patches 0010-0017 + +* Fri Apr 13 2018 Benjamin Marzinski 0.7.6-2.git1cb704b +- Add 0007-libmultipath-Fix-logic-in-should_multipath.patch + * fix bug in identifying multipathable devices. posted upstream +- Rename files + * Previous patches 0007-0014 are now patches 0008-0015 + +* Mon Apr 02 2018 Benjamin Marzinski - 0.7.6-1.git1cb704b +- Update Source to the latest upstream commit + * Previous patches 0001-0014 are included in this commit + * Previous patches 0015-0022 are now patches 0007-0014 +- 0001-multipathd-remove-incorrect-pthread_testcancel.patch + * Fixed pthread cancellation issue. posted upstream +- 0002-multipath-add-comments.patch + * Posted upstream +- 0003-multipathd-minor-dmevents-polling-code-cleanups.patch + * Fixed minor polling issues. posted upstream +- 0004-multipathd-remove-unneeded-function-parameter.patch + * Posted upstream +- 0005-mpathcmd-fix-libmpathcmd-license.patch + * License clarification. posted upstream +- 0006-libmultipath-don-t-print-undefined-values.patch + * Fixed bug in 'multipath show config'. posted upstream + +* Tue Mar 06 2018 Björn Esser - 0.7.4-2.git07e7bd5 +- Rebuilt for libjson-c.so.4 (json-c v0.13.1) + +* Thu Feb 15 2018 Benjamin Marzinski 0.7.4-1.git07e7bd5 +- Update Source to the latest upstream commit + * Previous patches 0001-0006 are included in this commit + * Previous patches 0007-0014 are now patches 0015-0022 +- Add 0001-libmultipath-fix-tur-checker-locking.patch + * Fixed spinlock bug. posted upstream +- Add 0002-multipath-fix-DEF_TIMEOUT-use.patch + * Add missing sec to ms conversion. posted upstream +- Add 0003-multipathd-remove-coalesce_paths-from-ev_add_map.patch + * Remove unused code. posted upstream +- Add 0004-multipathd-remove-unused-configure-parameter.patch + * Remove unused code. posted upstream +- Add 0005-Fix-set_no_path_retry-regression.patch + * Fix issue with queueing and path addition. posted upstream +- Add 0006-multipathd-change-spurious-uevent-msg-priority.patch + * Change message priority to Notice. posted upstream +- Add 0007-multipath-print-sysfs-state-in-fast-list-mode.patch + * Show sysfs state correctly in fast list mode (-l). posted upstream +- Add 0008-libmultipath-move-remove_map-waiter-code-to-multipat.patch + * Move code around. posted upstream +- Add 0009-move-waiter-code-from-libmultipath-to-multipathd.patch + * Move code around. posted upstream +- Add 0010-call-start_waiter_thread-before-setup_multipath.patch + * Fix race on multipath device creations. posted upstream +- Add 0011-libmultipath-add-helper-functions.patch + * posted upstream +- Add 0012-multipathd-RFC-add-new-polling-dmevents-waiter-threa.patch + * Add alternate method of getting dmevents, that doesn't + require a thread per device. posted upstream +- Add 0013-libmultipath-condlog-log-to-stderr.patch + * change condlog to log to stderr instead of stdout. posted upstream +- Add 0014-multipathd-fix-compiler-warning-for-uev_pathfail_che.patch + * fix indentation issue. posted upstream + +* Wed Feb 07 2018 Fedora Release Engineering - 0.7.3-3 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild + +* Sun Dec 10 2017 Björn Esser - 0.7.3-2 +- Rebuilt for libjson-c.so.3 + +* Tue Nov 7 2017 Benjamin Marzinski 0.7.3-1 +- Update Source to upstream 0.7.3 release + * Previous patch 0001 is included in this commit, and 0002 was solved in a + different manner causing some change to previous patch 0003 + * Previous patches 0003-0010 are now patches 0007-0014 +- Add 0001-mpathpersist-Fix-invalid-condition-check.patch + * Fix incorrect check. posted upstream +- Add 0002-multipath-add-man-page-info-for-my-prkey-changes.patch + * Add missing man page info. posted upstream +- Add 0003-multipath-there-is-no-none-path-state.patch + * remove incorrect path state. posted upstream +- Add 0004-mutipath-updated-Huawei-storage-config.patch + * update builtin device configuration. posted upstream +- Add 0005-multipath-fix-doc-typo.patch + * fix man page typo. posted upstream +- Add 0006-multipath-add-ghost_delay-parameter.patch + * add new multipath.conf parameter "ghost_delay". posted upstream + + +* Tue Nov 7 2017 Benjamin Marzinski 0.7.1-8.git847cc43 +- Refresh 0001-libmultipath-update-3PARdata-builtin-config.patch +- Add 0010-RH-warn-on-invalid-regex-instead-of-failing.patch + * Change old-style multipath.conf regex "*" to a proper ".*" instead of + failing + +* Wed Aug 2 2017 Benjamin Marzinski 0.7.1-7.git847cc43 +- Modify 0005-RH-don-t-start-without-a-config-file.patch + * Fix man page typos + +* Mon Jul 31 2017 Troy Dawson - 0.7.1-6.git847cc43 +- Clean spec file - remove pre-fedora 23 cruft + +* Wed Jul 26 2017 Fedora Release Engineering - 0.7.1-5.git847cc43 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild + +* Fri Jul 21 2017 Benjamin Marzinski 0.7.1-4.git847cc43 +- Update Source to the latest upstream commit + * Previous patches 0001 and 0010-0013 are included in this commit. +- Add 0001-libmultipath-update-3PARdata-builtin-config.patch + * Change for building configuration. Posted upstream +- Modify 0006-RH-use-rpm-optflags-if-present.patch + * Add missing lines to actually use RPM_OPT_FLAGS. + +* Fri Jun 23 2017 Tom Callaway - 0.7.1-3.gitf21166a +- rebuild to resolve broken deps + +* Fri Jun 2 2017 Benjamin Marzinski 0.7.1-2.gitf21166a +- Modify 0004-RH-Remove-the-property-blacklist-exception-builtin.patch + * update multipath.conf.5 man page to remove builtin listing +- Modify 0005-RH-don-t-start-without-a-config-file.patch + * update multipathd.8 man page to note that a config file is necessary +- Modify 0007-RH-add-mpathconf.patch + * add property blacklist-exception to default config file +- Add 0010-libmultipath-change-how-RADOS-checker-is-enabled.patch + * Makefile now autodetects librados. Posted upstream +- Remove related RADOS option from spec file +- Add 0011-multipath-set-verbosity-to-default-during-config.patch + * Allow multipath to print warning messages during configuration. + Posted upstream +- Add 0012-mpath-skip-device-configs-without-vendor-product.patch + * device entries without vendor/product were breaking configurations. + Posted upsteam +- Add 0013-multipathd-fix-show-maps-json-crash.patch + * multipathd crashed showing json output with no devices. Posted + upstream + +* Tue May 23 2017 Benjamin Marzinski 0.7.1-1.gitf21166a +- Update Source to the latest upstream commit +- Add 0001-libmultipath-add-comment-about-resuming.patch + * posted upstream +- Add 0002-multipath-attempt-at-common-multipath.rules.patch + * under discussion upstream +- Add 0003-RH-fixup-udev-rules-for-redhat.patch + * Redhat uses different udev rules that some other distros, so multipath + has run at a different time. Not all upstream distros link /sbin and + /usr/sbin either. +- Add 0004-RH-Remove-the-property-blacklist-exception-builtin.patch + * Allow multipath to be used on devices without multiple paths. NAK'ed + upstream, but requested by Red Hat +- Add 0005-RH-don-t-start-without-a-config-file.patch + * Don't start multipath unless a config file exists. NAK'ed upstream, + but requested by Red Hat +- Add 0006-RH-use-rpm-optflags-if-present.patch + * Make the build system fedora friendly +- Add 0007-RH-add-mpathconf.patch + * Add tool to help configure multipath with Red Hat defaults. +- Add 0008-RH-add-wwids-from-kernel-cmdline-mpath.wwids-with-A.patch + * Make multipath able to claim devices based on the kernel command line + NAK'ed upstream but requested by Red Hat +- Add 0009-RH-trigger-change-uevent-on-new-device-creation.patch + * under discussion upstream + +* Wed Apr 12 2017 Benjamin Marzinski 0.4.9-87 +- Remove Epoch from device-mapper requires + * The RHEL releases of device-mapper set the Epoch, and this was + accidentally retained in the fedora spec file. + +* Fri Apr 7 2017 Benjamin Marzinski 0.4.9-86 +- Modify 0136-RHBZ-1304687-wait-for-map-add.patch + * switch to missing_uev_wait_timeout to stop waiting for uev +- Refresh 0137-RHBZ-1280524-clear-chkr-msg.patch +- Refresh 0150-RHBZ-1253913-fix-startup-msg.patch +- Refresh 0154-UPBZ-1291406-disable-reinstate.patch +- Refresh 0156-UPBZ-1313324-dont-fail-discovery.patch +- Refresh 0161-RHBZ-1311659-no-kpartx.patch +- Refresh 0167-RHBZ-1335176-fix-show-cmds.patch +- Add 0173-RH-update-man-page.patch +- Add 0174-RHBZ-1362396-modprobe.patch + * make starting the multipathd service modprobe dm-multipath in the + sysvinit scripts +- Add 0175-RHBZ-1357382-ordering.patch + * force multipathd.service to start after systemd-udev-trigger.service +- Add 0176-RHBZ-1363830-fix-rename.patch + * initialized a variable to make dm_rename not fail randomly +- Add 0177-libmultipath-correctly-initialize-pp-sg_id.patch + * This and all the following patches add the rbd patch checker +- Add 0178-libmultipath-add-rbd-discovery.patch +- Add 0179-multipath-tools-add-checker-callout-to-repair-path.patch +- Add 0180-multipath-tools-Add-rbd-checker.patch +- Add 0181-multipath-tools-Add-rbd-to-the-hwtable.patch +- Add 0182-multipath-tools-check-for-initialized-checker-before.patch +- Add 0183-multipathd-Don-t-call-repair-on-blacklisted-path.patch +- Add 0184-rbd-fix-sync-repair-support.patch +- Add 0185-rbd-check-for-nonshared-clients.patch +- Add 0186-rbd-check-for-exclusive-lock-enabled.patch +- Add 0187-rbd-fixup-log-messages.patch +- Add 0188-RHBZ-1368501-dont-exit.patch + * make multipathd not exit if it encounters recoverable errors on startup +- Add 0189-RHBZ-1368211-remove-retries.patch + * add "remove_retries" multipath.conf parameter to make multiple attempts + to remove a multipath device if it is busy. +- Add 0190-RHBZ-1380602-rbd-lock-on-read.patch + * pass lock_on_read when remapping image +- Add 0191-RHBZ-1169168-disable-changed-paths.patch + * add "disabled_changed_wwids" multipath.conf parameter to disable + paths whose wwid changes +- Add 0192-RHBZ-1362409-infinibox-config.patch +- Add 0194-RHBZ-1351964-kpartx-recurse.patch + * fix recursion on corrupt dos partitions +- Add 0195-RHBZ-1359510-no-daemon-msg.patch + * print a messages when multipathd isn't running +- Add 0196-RHBZ-1239173-dont-set-flag.patch + * don't set reload flag on reloads when you gain your first + valid path +- Add 0197-RHBZ-1394059-max-sectors-kb.patch + * add "max_sectors_kb" multipath.conf parameter to set max_sectors_kb + on a multipath device and all its path devices +- Add 0198-RHBZ-1372032-detect-path-checker.patch + * add "detect_checker" multipath.conf parameter to detect ALUA arrays + and set the path checker to TUR +- Add 0199-RHBZ-1279355-3pardata-config.patch +- Add 0200-RHBZ-1402092-orphan-status.patch + * clear status on orphan paths +- Add 0201-RHBZ-1403552-silence-warning.patch +- Add 0202-RHBZ-1362120-skip-prio.patch + * don't run prio on failed paths +- Add 0203-RHBZ-1363718-add-msgs.patch +- Add 0204-RHBZ-1406226-nimble-config.patch +- Add 0205-RHBZ-1416569-reset-stats.patch + * add "reset maps stats" and "reset map stats" multipathd + interactive commands to reset the stats tracked by multipathd +- Add 0206-RHBZ-1239173-pt2-no-paths.patch + * make multipath correctly disable scanning and rules running when + it gets a uevent and there are not valid paths. +- Add 0207-UP-add-libmpathcmd.patch + * New shared library, libmpathcmd, that sends and receives messages from + multipathd. device-mapper-multipath now uses this library internally. +- Add 0208-UPBZ-1430097-multipathd-IPC-changes.patch + * validation that modifying commands are coming from root. +- Add 0209-UPBZ-1430097-multipath-C-API.patch + * New shared library. libdmmp, that presents the information from multipathd + in a structured manner to make it easier for callers to use +- Add 0210-RH-fix-uninstall.patch + * Minor compilation fixes +- Add 0211-RH-strlen-fix.patch + * checks that variables are not NULL before passing them to strlen +- Add 0212-RHBZ-1431562-for-read-only.patch +- Make 3 new subpackages + * device-mapper-multipath-devel, libdmmp, and libdmmp-devel. libmpathcmd + and libmpathprio are in device-mapper-multipath-libs and + device-mapper-multipath-devel. libdmmp is in its own subpackages +- Move libmpathprio devel files to device-mapper-multipath-devel +- Added BuildRequires on librados2-devel + + +* Fri Feb 10 2017 Fedora Release Engineering - 0.4.9-85 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild + +* Thu Jan 12 2017 Igor Gnatenko - 0.4.9-84 +- Rebuild for readline 7.x + +* Fri Jul 22 2016 Benjamin Marzinski 0.4.9-83 +- Modify 0135-RHBZ-1299600-path-dev-uevents.patch + * trigger uevents when adding wwids for existing devices during startup +- Refresh 0136-RHBZ-1304687-wait-for-map-add.patch +- Refresh 0150-RHBZ-1253913-fix-startup-msg.patch +- Modify 0159-UPBZ-1255885-udev-waits.patch + * fix bug in failure path +- Add 0160-RH-udev-flags.patch +- Add 0161-RHBZ-1311659-no-kpartx.patch + * skip_kpartx option disables kpartx running on multipath devices +- Add 0162-RHBZ-1333331-huawei-config.patch + * Add default config for Huawei XSG1 array +- Add 0163-UPBZ-1333492-resize-map.patch + * restore old size if resize fails +- Add 0164-RHBZ-1311463-dos-part-rollover.patch + * fix incorrect partition size due to 4k device size rollover +- Add 0165-UPBZ-1341748-MSA-2040-conf.patch + * Add default config for MSA 2040 array +- Add 0166-RHBZ-1323429-dont-allow-new-wwid.patch + * don't allow path wwid to change while it is in use +- Add 0167-RHBZ-1335176-fix-show-cmds.patch + * and new show multipath format wildcard, 'f' to sho number of failures. + This will hopefully be useful for tracking what happens to multipath + devices for bz #1335176 +- Add 0168-RHBZ-1347769-shared-lock.patch + * make multipath lock the path devices with a shared lock +- Add 0169-UPBZ-1353357-json-output.patch + * add mulitpathd json output command +- Add 0170-UPBZ-1352925-fix-typo.patch +- Add 0171-UPBZ-1356651-allow-zero-size.patch + * Allow zero-sized paths to be added to a multipath device +- Add 0172-RHBZ-1350931-no-active-add.patch + * Allow paths to be added to a new map if no active paths exist. Also + fixes 1351430 + + +* Thu Apr 21 2016 Benjamin Marzinski 0.4.9-82 +- Modify 0005-RH-add-mpathconf.patch + * changed warning message +- Modify 0102-RHBZ-1160478-mpathconf-template.patch + * updated man page +- Modify 0104-RHBZ-631009-deferred-remove.patch + * refactor code and minor fix +- Refresh 0107-RHBZ-1169935-no-new-devs.patch +- Refresh 0112-RHBZ-1194917-add-config_dir-option.patch +- Refresh 0126-RHBZ-1211383-alias-collision.patch +- Add 0133-RHBZ-1296979-fix-define.patch + * look for the correct libudev function to set define +- Add 0134-RHBZ-1241528-check-mpath-prefix.patch + * only touch devices with a "mpath-" dm uuid prefix +- Add 0135-RHBZ-1299600-path-dev-uevents.patch + * trigger path uevent the first time a path is claimed by multipath +- Add 0136-RHBZ-1304687-wait-for-map-add.patch + * wait for the device to finish being added before reloading it. +- Add 0137-RHBZ-1280524-clear-chkr-msg.patch +- Add 0138-RHBZ-1288660-fix-mpathconf-allow.patch + * don't remove existing lines from blacklist_exceptions section +- Add 0139-RHBZ-1273173-queue-no-daemon-doc.patch +- Add 0140-RHBZ-1299647-fix-help.patch +- Add 0141-RHBZ-1303953-mpathpersist-typo.patch +- Add 0142-RHBZ-1283750-kpartx-fix.patch + * only remove devices if their uuid says that they are the correct + partition device +- Add 0143-RHBZ-1299648-kpartx-sync.patch + * default to using udev sync mode +- Add 0144-RHBZ-1299652-alua-pref-arg.patch + * allow "exclusive_pref_bit" argument to alua prioritizer +- Add 0145-UP-resize-help-msg.patch +- Add 0146-UPBZ-1299651-raw-output.patch + * allow raw format mutipathd show commands, that remove headers and padding +- Add 0147-RHBZ-1272620-fail-rm-msg.patch +- Add 0148-RHBZ-1292599-verify-before-remove.patch + * verify that all partitions are unused before attempting to remove a device +- Add 0149-RHBZ-1292599-restore-removed-parts.patch + * don't disable kpartx when restoring the first path of a device. +- Add 0150-RHBZ-1253913-fix-startup-msg.patch + * wait for multipathd daemon to write pidfile before returning +- Add 0151-RHBZ-1297456-weighted-fix.patch + * add wwn keyword to weighted prioritizer for persistent naming +- Add 0152-RHBZ-1269293-fix-blk-unit-file.patch + * use "Wants" instead of "Requires" +- Add 0153-RH-fix-i686-size-bug.patch + * use 64-bit keycodes for multipathd client commands +- Add 0154-UPBZ-1291406-disable-reinstate.patch + * don't automatically reinstate ghost paths for implicit alua devices +- Add 0155-UPBZ-1300415-PURE-config.patch + * Add default config for PURE FlashArray +- Add 0156-UPBZ-1313324-dont-fail-discovery.patch + * don't fail discovery because individual paths failed. +- Add 0157-RHBZ-1319853-multipath-c-error-msg.patch + * better error reporting for multipath -c +- Add 0158-RHBZ-1318581-timestamp-doc-fix.patch + * add documentation for -T +- Add 0159-UPBZ-1255885-udev-waits.patch + * make multipath and kpartx wait after for udev after each command + +* Wed Feb 03 2016 Fedora Release Engineering - 0.4.9-81 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_24_Mass_Rebuild + +* Fri Sep 25 2015 Benjamin Marzinski 0.4.9-80 +- Add 0131-RHBZ-1259523-host_name_len.patch + * increase size of host string +- Add 0132-UPBZ-1259831-lock-retry.patch + * retry locking when creating multipath devices + +* Mon Aug 17 2015 Benjamin Marzinski 0.4.9-79 +- Add 0130-UPBZ-1254292-iscsi-targetname.patch + * check for targetname iscsi sysfs value + +* Thu Aug 13 2015 Benjamin Marzinski 0.4.9-78 +- fix triggerun issue and updated requires in spec file. + +* Fri Aug 7 2015 Benjamin Marzinski 0.4.9-77 +- Modify 0104-RHBZ-631009-deferred-remove.patch + * add man page info +- Refresh 0112-RHBZ-1194917-add-config_dir-option.patch +- Refresh 0114-RHBZ-1196394-delayed-reintegration.patch +- Add 0118-UPBZ-1200738-update-eternus-config.patch + * update default config +- Add 0119-RHBZ-1081397-save-alua-info.patch + * make prioritizers save information between calls to speed them up. +- Add 0120-RHBZ-1043093-realloc-fix.patch + * free old memory if realloc fails. +- Add 0121-RHBZ-1197234-rules-fix.patch + * make sure kpartx runs after an DM_ACTIVATION event occurs. +- Add 0122-RHBZ-1212590-dont-use-var.patch + * use /run instead of /var/run +- Add 0123-UPBZ-1166072-fix-path-offline.patch + * Don't mark quiesce and transport-offline paths as offline +- Add 0124-RHBZ-1209275-retrigger-uevents.patch + * Make multipathd retrigger uevents when paths haven't successfully had + their udev_attribute environment variable set by udev and add + "retrigger_ties" and "retrigger_delay" to control this +- Add 0125-RHBZ-1153832-kpartx-delete.patch + * Delete all partition devices with -d (not just the ones in the partition + table) +- Add 0126-RHBZ-1211383-alias-collision.patch + * make multipathd use the old alias, if rename failed and add + "new_bindings_in_boot" to determine if new bindings can be added to + the bindings file in the initramfs +- Add 0127-RHBZ-1201030-use-blk-availability.patch + * Make multipath use blk-availability.service +- Add 0128-RHBZ-1222123-mpathconf-allow.patch + * Add mpathconf --allow for creating specialized config files. +- Add 0129-RHBZ-1241774-sun-partition-numbering.patch + * Make kpartx correctly number sun partitions. + + +* Wed Jun 17 2015 Fedora Release Engineering - 0.4.9-76 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild + +* Wed Mar 11 2015 Benjamin Marzinski 0.4.9-75 +- Add 0111-RH-dont-show-pg-timeout.patch + * The kernel doesn't support pg_timeout, so multipath shouldn't + bother to display it +- Add 0112-RHBZ-1194917-add-config_dir-option.patch + * multipath will now also read its configuration from files with + the .conf suffix in the directory specified by config_dir + which defaults to /etc/multipath/conf.d +- Add 0113-RHBZ-1194917-cleanup.patch + * cleanup some unnecessary code +- Add 0114-RHBZ-1196394-delayed-reintegration.patch + * Add "delay_watch_checks" and "delay_wait_checks" options to delay + reintegration of flakey paths. +- Add 0115-RHBZ-1198418-fix-double-free.patch + * multipath was freeing the multipath alias twice if it failed to create the + multipath device. +- Add 0116-UPBZ-1188179-dell-36xxi.patch + * new builtin configurations. +- Add 0117-RHBZ-1198424-autodetect-clariion-alua.patch + * configure multipath to automatically detect alua settings on clariion + devices. + +* Thu Mar 05 2015 Adam Jackson 0.4.9-74 +- Drop sysvinit subpackage from F23+ + +* Wed Feb 18 2015 Benjamin Marzinski 0.4.9-73 +- Add 0110-RHBZ-blacklist-vd-devs.patch + * blacklist vd[a-z] devices, since they don't have a WWID for + multipath to use. + +* Thu Dec 18 2014 Benjamin Marzinski 0.4.9-72 +- Modify 0107-RHBZ-1169935-no-new-devs.patch + * instead of using "-n" there is now a new configuration option, + 'ignore_new_boot_devs'. If set to 'yes', multipath will ignore + devices that aren't in /etc/multipath/wwids when running in the + initramfs. This option does nothing while multipathd is running + in the real root filesystem. +- Update 0109-RH-read-only-bindings.patch + +* Mon Dec 15 2014 Benjamin Marzinski 0.4.9-71 +- Add 0103-RH-cleanup-partmaps-code.patch + * code refactoring to prepare for next patch +- Add 0104-RHBZ-631009-deferred-remove.patch + * add deferred_remove option to /etc/multipath.conf +- Add 0105-RHBZ-1148979-fix-partition-mapping-creation-race-with-kpartx.patch + * Only run kpartx on device activation +- Add 0106-RHBZ-1159337-fix-double-free.patch + * made ev_remove_path exit immediately after failing setup_multipath, since + it handles cleaning up the device +- Add 0107-RHBZ-1169935-no-new-devs.patch + * Add new multipathd option '-n' which keeps multipathd from creating any + multipath devices that aren't in the /etc/multipath/wwids file. +- Add 0108-RHBZ-1153832-kpartx-remove-devs.patch + * switch from 'kpartx -a' to 'kpartx -u' to remove missing devices as well. +- Add 0109-RH-read-only-bindings.patch + * re-enabled -B option for multipathd + +* Tue Dec 9 2014 Benjamin Marzinski 0.4.9-70 +- Add 0102-RHBZ-1160478-mpathconf-template.patch + * mpathconf no longer copies the default config template for the + docs directory. It simply writes the template itself. +- Resolves: bz# 1160478 + +* Thu Nov 13 2014 Benjmain Marzinski 0.4.9-69 +- Rebuild + +* Tue Sep 16 2014 Benjamin Marzinski 0.4.9-68 +- Modify multipath.conf + * remove getuid_callout example +- Re-add 0050-RH-listing-speedup.patch +- Add 0081-RHBZ-1066264-check-prefix-on-rename.patch + * make multipath check the prefix on kpartx partitions during rename, and + copy the existing behaviour +- Add 0082-UPBZ-1109995-no-sync-turs-on-pthread_cancel.patch + * If async tur checker fails on threads, don't retry with the sync version +- Add 0083-RHBZ-1080055-orphan-paths-on-reload.patch + * Fix case where pathlist wasn't getting updated properly +- Add 0084-RHBZ-1110000-multipath-man.patch + * fix errors in multipath man page +- Add 0085-UPBZ-1110006-datacore-config.patch + * Add support for DataCore Virtual Disk +- Add 0086-RHBZ-1110007-orphan-path-on-failed-add.patch + * If multipathd fails to add path correctly, it now fully orphans the path +- Add 0087-RHBZ-1110013-config-error-checking.patch + * Improve multipath.conf error checking. +- Add 0088-RHBZ-1069811-configurable-prio-timeout.patch + * checker_timeout now adjusts the timeouts of the prioritizers as well. +- Add 0089-RHBZ-1110016-add-noasync-option.patch + * Add a new defaults option, "force_sync", that disables the async mode + of the path checkers. This is for cases where to many parallel checkers + hog the cpu +- Add 0090-UPBZ-1080038-reorder-paths-for-round-robin.patch + * make multipathd order paths for better throughput in round-robin mode +- Add 0091-RHBZ-1069584-fix-empty-values-fast-io-fail-and-dev-loss.patch + * check for null pointers in configuration reading code. +- Add 0092-UPBZ-1104605-reload-on-rename.patch + * Reload table on rename if necessary +- Add 0093-UPBZ-1086825-user-friendly-name-remap.patch + * Keep existing user_friend_name if possible +- Add 0094-RHBZ-1086825-cleanup-remap.patch + * Cleanup issues with upstream patch +- Add 0095-RHBZ-1127944-xtremIO-config.patch + * Add support for EMC ExtremIO devices +- Add 0096-RHBZ-979474-new-wildcards.patch + * Add N, n, R, and r path wildcards to print World Wide ids +- Add 0097-RH-fix-coverity-errors.patch + * Fix a number of unterminated strings and memory leaks on failure + paths. +- Add 0098-UPBZ-1067171-mutipath-i.patch + * Add -i option to ignore wwids file when checking for valid paths +- Add 0099-RH-add-all-devs.patch + * Add new devices config option all_devs. This makes the configuration + overwrite the specified values in all builtin configs +- Add 0100-RHBZ-1067171-multipath-i-update.patch + * make -i work correctly with find_multipaths +- Add 0101-RH-adapter-name-wildcard.patch + * Add 'a' path wildcard to print adapter name + +* Sat Aug 16 2014 Fedora Release Engineering - 0.4.9-67 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild + +* Fri Jul 11 2014 Tom Callaway - 0.4.9-66 +- fix license handling + +* Sat Jun 07 2014 Fedora Release Engineering - 0.4.9-65 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild + +* Mon Mar 31 2014 Benjamin Marzinski 0.4.9-64 +- Modify 0076-RHBZ-1056686-add-hw_str_match.patch + * free temporary memory used during configuration +- Add 0078-RHBZ-1054044-fix-mpathconf-manpage.patch + * fix typo +- Add 0079-RHBZ-1070581-add-wwid-option.patch + * add multipath option "-a". To add a device's wwid to the wwids file +- Add 0080-RHBZ-1075796-cmdline-wwid.patch + * add multipath option "-A" to add wwids specified by the kernel + command line mapth.wwid options. + +* Fri Jan 24 2014 Benjamin Marzinski 0.4.9-63 +- Add 0074-RHBZ-1056976-dm-mpath-rules.patch + * Add rules to keep from doing work in udev if there are no + active paths, or if the event was for a multipath device + reloading its table due to a path change. +- Add 0075-RHBZ-1056976-reload-flag.patch + * multipath code to identify reloads that the new rules can + ignore +- Add 0076-RHBZ-1056686-add-hw_str_match.patch + * add a new default config paramter, "hw_str_match", to make user + device configs only overwrite builtin device configs if the + identifier strings match exactly, like the default in RHEL6. + +* Fri Jan 10 2014 Benjamin Marzinski 0.4.9-62 +- Modify 0072-RHBZ-1039199-check-loop-control.patch + * only call close on the /dev/loop-control fd the open succeeds +- Add 0073-RH-update-build-flags.patch + * fix print call to work with -Werror=format-security compile flag + +* Tue Dec 10 2013 Benjamin Marzinski 0.4.9-61 +- Add 0072-RHBZ-1039199-check-loop-control.patch + * Make kpartx use LOOP_CTL_GET_FREE and loop-control to find a free + loop device. This will autoload the loop module. + +* Mon Dec 9 2013 Benjamin Marzinski 0.4.9-60 +- Add 0067-RHBZ-1022899-fix-udev-partition-handling.patch + * Make sure to wipe partition devices on change event if they weren't + wiped on the device add event +- Add 0068-RHBZ-1034578-label-partition-devices.patch + * Make sure that partition devices are labeled like the whole device +- Add 0069-UPBZ-1033791-improve-rdac-checker.patch + * Use RTPG data in RDAC checker +- Add 0070-RHBZ-1036503-blacklist-td-devs.patch +- Add 0071-RHBZ-1031546-strip-dev.patch + * make multipathd interactive commands able to handle /dev/ + instead of just + +* Sat Oct 12 2013 Benjamin Marzinski 0.4.9-59 +- Add 0066-UP-dos-4k-partition-fix.patch + * Make kpartx correctly handle 4K sector size devices with dos partitions. + +* Fri Sep 27 2013 Benjamin Marzinski 0.4.9-58 +- Add 0065-UPBZ-995538-fail-rdac-on-unavailable.patch + * make rdac checker always mark paths with asymmetric access state of + unavailable as down + +* Fri Sep 20 2013 Benjamin Marzinski 0.4.9-57 +- Add 0063-RH-fix-warning.patch + * Fix complier warning +- 0064-fix-ID_FS-attrs.patch + * make multipath create a timestamp file /run/multipathd/timestamp, and + add -T: option to shortcut processing if the + timestamp hasn't changed + +* Thu Sep 5 2013 Benjamin Marzinski 0.4.9-56 +- Add 0061-RH-display-find-mpaths.patch + * display the find_multipaths value in show config +- Add 0062-RH-dont-free-vecs.patch + * don't free the vecs structure on shutdown. It's more pain than + it's worth. + +* Thu Jul 25 2013 Benjamin Marzinski 0.4.9-55 +- Modify 0015-RH-fix-output-buffer.patch + * Fix memory leak +- Add 0047-RHBZ-kpartx-read-only-loop-devs.patch + * Fix read only loop device handling +- Add 0048-RH-print-defaults.patch +- Add 0049-RH-remove-ID_FS_TYPE.patch + * remove ID_FS_TYPE udev enviroment variable for multipath devices +- Add 0051-UP-fix-cli-resize.patch + * check before dereferencing variables +- Add 0052-RH-fix-bad-derefs.patch + * setup multipath free the multipath device when it fails, so don't keep + using it. +- Add 0053-UP-fix-failback.patch + * setting failback in the devices section was broken +- Add 0054-UP-keep-udev-ref.patch + * multipathd needs to keep the same udev object across reconfigures +- Add 0055-UP-handle-quiesced-paths.patch + * quiesced paths should be treated as down +- Add 0056-UP-alua-prio-fix.patch + * Don't count the preferred bit for paths that are active/optimized +- Add 0057-UP-fix-tmo.patch + * Cleanup how multipath sets dev_loss_tmo and fast_io_fail_tmo. Also + make multipath get changing values directly from sysfs, instead of + from udev, which caches them. +- Add 0058-UP-fix-failback.patch + * make failback print the default value when you show configs. +- Add 0059-UP-flush-failure-queueing.patch + * If you can't flush a multipath device, restore the queue_if_no_paths + value +- Add 0060-UP-uevent-loop-udev.patch + * make ueventloop grab it's own udev reference, since it is cancelled + asychnrously. + +* Fri Jul 5 2013 Benjamin Marzinski 0.4.9-54 +- Add 0047-RHBZ-980777-kpartx-read-only-loop-devs.patch + * make kpartx support read-only files better +- Resolves: bz #980777 + +* Wed Jul 3 2013 Benjamin Marzinski 0.4.9-53 +- Add 0044-RHBZ-976688-fix-wipe-wwids.patch + * Seek back to the start of the file after truncating it +- Add 0045-RHBZ-977297-man-page-fix.patch + * update man page to match actual defaults +- Add 0046-RHBZ-883981-move-udev-rules.patch + * move udev rules file from /lib to /usr/lib +- Resolves: bz #883981, #976688, #977297 + +* Fri Jun 21 2013 Benjamin Marzinski 0.4.9-52 +- Add 0038-RHBZ-799860-netapp-config.patch +- Add 0039-RH-detect-prio-fix.patch + * Don't autodetect ALUA prioritizer unless it actually can get a priority +- Add 0040-RH-bindings-fix.patch + * Do a better job of trying to get the first free user_friendly_name +- Add 0041-RH-check-for-erofs.patch + * Don't create/reload a device read-only unless doing it read/write fails + with EROFS +- Remove 0017-RH-fix-sigusr1.patch + * fix signal handling upstream way instead +- Add 0042-UP-fix-signal-handling.patch + * uxlsnr now handles all the signals sent to multipathd. This makes its + signal handling posix compliant, and harder to mess up. +- Add 0043-RH-signal-waiter.patch + * ioctl isn't a pthread cancellation point. Send a signal to the waiter + thread to break out of waiting in ioctl for a dm event. + +* Fri May 17 2013 Benjamin Marzinski 0.4.9-51 +- Add 0032-RHBZ-956464-mpathconf-defaults.patch + * fix defaults listed in usage +- Add 0033-RHBZ-829963-e-series-conf.patch +- Add 0034-RHBZ-851416-mpathconf-display.patch + * display whether or not multipathd is running in the status +- Add 0035-RHBZ-891921-list-mpp.patch + * add a new path format wilcard to list the multipath device associated + with a path +- Add 0036-RHBZ-949239-load-multipath-module.patch + * load the dm-multipath kernel module when multipathd starts +- Add 0037-RHBZ-768873-fix-rename.patch + * When deciding on a multipth devices name on reload, don't default to + the existing name if there is no config file alias and user_friendly_names + isn't set. Use the wwid. +- Modify multipath.conf +- Resolves: bz #768873, #950252 + +* Tue Apr 30 2013 Benjamin Marzinski 0.4.9-50 +- Add 0031-RHBZ-957188-kpartx-use-dm-name.patch + * use the basename of the devices that will be created to choose the + delimiter instead of using the device name from the command line +- Resolves: bz #957188 + +* Fri Apr 26 2013 Benjamin Marzinski 0.4.9-49 +- Modify 0020-RHBZ-907360-static-pthread-init.patch + * Don't initialize uevent list twice +- Add 0029-RH-no-prio-put-msg.patch +- Add 0030-RHBZ-916528-override-queue-no-daemon.patch + * Default to "queue_without_daemon no" + * Add "forcequeueing daemon" and "restorequeueing daemon" cli commands +- Modify spec file to force queue_without_daemon when restarting + multipathd on upgrades. + +* Thu Apr 4 2013 Benjamin Marzinski 0.4.9-48 +- Add 0026-fix-checker-time.patch + * Once multipathd hit it max checker interval, it was reverting to + to shortest checker interval +- Add 0027-RH-get-wwid.patch + * Multipath wasn't correctly setting the multipath wwid when it read devices + in from the kernel +- Add 0028-RHBZ-929078-refresh-udev-dev.patch + * Make multipath try to get the UID of down devices. Also, on ev_add_path, + make multipathd reinitialize existing devices that weren't fully + initialized before. + +* Mon Apr 1 2013 Benjamin Marzinski 0.4.9-47 +- Add 0021-RHBZ-919119-respect-kernel-cmdline.patch + * keep the multipath.rules udev file from running and multipathd from + starting if nompath is on the kernel command line +- Add 0022-RH-multipathd-check-wwids.patch + * Whenever multipath runs configure, it will check the wwids, and + add any missing ones to the wwids file +- Add 0023-RH-multipath-wipe-wwid.patch + * multipath's -w command will remove a wwid from the wwids file +- Add 0024-RH-multipath-wipe-wwids.patch + * multipath's -W command will set reset the wwids file to just the current + devices +- Add 0025-UPBZ-916668_add_maj_min.patch +- Resolves: bz #919119 + +* Thu Mar 28 2013 Benjamin Marzinski 0.4.9-46 +- Add 0020-RHBZ-907360-static-pthread-init.patch + * statically initialize the uevent pthread structures + +* Sat Mar 2 2013 Benjamin Marzinski 0.4.9-45 +- Updated to latest upstrem 0.4.9 code: multipath-tools-130222 + (git commit id: 67b82ad6fe280caa1770025a6bb8110b633fa136) +- Refresh 0001-RH-dont_start_with_no_config.patch +- Modify 0002-RH-multipath.rules.patch +- Modify 0003-RH-Make-build-system-RH-Fedora-friendly.patch +- Refresh 0004-RH-multipathd-blacklist-all-by-default.patch +- Refresh 0005-RH-add-mpathconf.patch +- Refresh 0006-RH-add-find-multipaths.patch +- Add 0008-RH-revert-partition-changes.patch +- Rename 0008-RH-RHEL5-style-partitions.patch to + 0009-RH-RHEL5-style-partitions.patch +- Rename 0009-RH-dont-remove-map-on-enomem.patch to + 0010-RH-dont-remove-map-on-enomem.patch +- Rename 0010-RH-deprecate-uid-gid-mode.patch to + 0011-RH-deprecate-uid-gid-mode.patch +- Rename 0013-RH-kpartx-msg.patch to 0012-RH-kpartx-msg.patch +- Rename 0035-RHBZ-883981-cleanup-rpmdiff-issues.patch to + 0013-RHBZ-883981-cleanup-rpmdiff-issues.patch +- Rename 0039-RH-handle-other-sector-sizes.patch to + 0014-RH-handle-other-sector-sizes.patch +- Rename 0040-RH-fix-output-buffer.patch to 0015-RH-fix-output-buffer.patch +- Add 0016-RH-dont-print-ghost-messages.patch +- Add 0017-RH-fix-sigusr1.patch + * Actually this fixes a number of issues related to signals +- Rename 0018-RH-remove-config-dups.patch to 0018-RH-fix-factorize.patch + * just the part that isn't upstream +- Add 0019-RH-fix-sockets.patch + * makes abstract multipathd a cli sockets use the correct name. +- Set find_multipaths in the default config + +* Wed Feb 20 2013 Benjamin Marzinski 0.4.9-44 +- Add 0036-UP-fix-state-handling.patch + * handle transport-offline and quiesce sysfs state +- Add 0037-UP-fix-params-size.patch +- Add 0038-RH-fix-multipath.rules.patch + * make sure multipath's link priority gets increased +- Add 0039-RH-handle-other-sector-sizes.patch + * allow gpt partitions on 4k sector size block devices. +- Add 0040-RH-fix-output-buffer.patch + * fix multipath -ll for large configuration. + +* Wed Feb 13 2013 Fedora Release Engineering - 0.4.9-43 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_19_Mass_Rebuild + +* Fri Dec 21 2012 Benjamin Marzinski 0.4.9-42 +- Add 0034-RHBZ-887737-check-for-null-key.patch +- Add 0035-RHBZ-883981-cleanup-rpmdiff-issues.patch + * Compile multipathd with full RELRO and PIE and install to /usr + +* Mon Dec 17 2012 Benjamin Marzinski 0.4.9-41 +- Add 0033-RH-dont-disable-libdm-failback-for-sync-case.patch + * make kpartx -s and multipath use libdm failback device creation, so + that they work in environments without udev + +* Fri Nov 30 2012 Benjamin Marzinski 0.4.9-40 +- Add 0032-RH-make-path-fd-readonly.patch + * revert change made when adding persistent reservations, so that path fds + are again opened O_RDONLY + +* Fri Nov 30 2012 Benjamin Marzinski 0.4.9-39 +- Add 0031-RHBZ-882060-fix-null-strncmp.patch + +* Fri Nov 30 2012 Benjamin Marzinski 0.4.9-38 +- Add 0026-RH-fix-mpathpersist-fns.patch +- Add 0027-RH-default-partition-delimiters.patch + * Only use the -p delimiter when the device name ends in a number +- Add 0028-RH-storagetek-config.patch +- Add 0029-RH-kpartx-retry.patch + * retry delete on busy loop devices +- Add 0030-RH-early-blacklist.patch + * multipath will now blacklist devices by device type and wwid in + store_pathinfo, so that it doesn't do a bunch of unnecessary work + on paths that it would only be removing later on. + +* Sat Nov 03 2012 Peter Rajnoha 0.4.9-37 +- Install multipathd.service for sysinit.target instead of multi-user.target. + +* Thu Nov 01 2012 Peter Rajnoha 0.4.9-36 +- Start multipathd.service systemd unit before LVM units. + +* Wed Oct 24 2012 Benjamin Marzinski 0.4.9-35 +- Add 0022-RHBZ-864368-disable-libdm-failback.patch + * make kpartx and multiapthd disable libdm failback device creation +- Add 0023-RHBZ-866291-update-documentation.patch +- Resolves: bz #864368, #866291 + +* Tue Oct 23 2012 Benjamin Marzinski 0.4.9-34 +- Add 0021-RH-fix-oom-adj.patch + * don't use OOM_ADJUST_MIN unless you're sure it's defined + +* Tue Oct 23 2012 Benjamin Marzinski 0.4.9-33 +- Modify 0016-RH-retain_hwhandler.patch + * Check the dm-multipath module version, and don't enable + retain_attached_hw_handler if the kernel doesn't support it +- Add 0019-RH-detect-prio.patch + * add detect_prio option, to make multipath check if the device + supports the ALUA prio, before defaulting to the configured prio +- Remove 0017-RH-netapp_config.patch +- Add 0020-RH-netapp-config.patch + * new netapp config that uses retain_attached_hw_handler and + detect_prio to autoconfigure ALUA and non-ALUA devices. + +* Tue Oct 2 2012 Benjamin Marzinski 0.4.9-32 +- Modified 0018-RH-remove-config-dups.patch + * Made modified config remove original only if the vendor/product + exactly match + +* Thu Sep 27 2012 Benjamin Marzinski 0.4.9-31 +- Add 0014-RH-dm_reassign.patch + * Fix reassign_maps option +- Add 0015-RH-selector_change.patch + * devices default to using service-time selector +- Add 0016-RH-retain_hwhandler.patch + * add retain_attached_hw_handler option, to let multipath keep an + already attached scsi device handler +- Add 0017-RH-netapp_config.patch +- Add 0018-RH-remove-config-dups.patch + * Clean up duplicates in the devices and blacklist sections + +* Wed Sep 05 2012 Václav Pavlín - 0.4.9-30 +- Scriptlets replaced with new systemd macros (#850088) + +* Tue Aug 21 2012 Benjamin Marzinski 0.4.9-29 +- Updated to latest upstrem 0.4.9 code: multipath-tools-120821.tgz + (git commit id: 050b24b33d3c60e29f7820d2fb75e84a9edde528) + * includes 0001-RH-remove_callout.patch, 0002-RH-add-wwids-file.patch, + 0003-RH-add-followover.patch, 0004-RH-fix-cciss-names.patch +- Add 0013-RH-kpartx-msg.patch +- Modify 0002-RH-multipath.rules.patch + * removed socket call from rules file + +* Wed Jul 18 2012 Fedora Release Engineering - 0.4.9-28 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild + +* Thu Jun 28 2012 Benjamin Marzinski 0.4.9-27 +- Updated to latest upstream 0.4.9 code : multipath-tools-120613.tgz + (git commit id: cb0f7127ba90ab5e8e71fc534a0a16cdbe96a88f) +- Add 0001-RH-remove_callout.patch + * multipath no longer uses the getuid callout. It now gets the + wwid from the udev database or the environment variables +- Add 0004-RH-fix-cciss-names.patch + * convert cciss device names from cciss/cXdY to sysfs style cciss!cXdY +- Split 0009-RH-add-find-multipaths.patch into 0002-RH-add-wwids-file.patch + and 0010-RH-add-find-multipaths.patch +- Add 0016-RH-change-configs.patch + * default fast_io_fail to 5 and don't set the path selector in the + builtin configs. +Resolves: bz #831978 + + +* Thu May 17 2012 Benjamin Marzinski 0.4.9-26 +- Add 0025-RHBZ-822714-update-nodes.patch +- Resolves: bz #822714 + +* Mon Apr 30 2012 Benjamin Marzinski 0.4.9-25 +- Modify 0024-RH-libudev-monitor.patch +- Resolves: bz #805493 + +* Mon Apr 30 2012 Benjamin Marzinski 0.4.9-24 +- Add requirements on libudev to spec file +- Resolves: bz #805493 + +* Mon Apr 30 2012 Benjamin Marzinski 0.4.9-23 +- Add 0024-RH-libudev-monitor.patch + +* Fri Feb 10 2012 Benjamin Marzinski 0.4.9-22 +- Add 0012-RH-update-on-show-topology.patch +- Add 0013-RH-manpage-update.patch +- Add 0014-RH-RHEL5-style-partitions.patch +- Add 0015-RH-add-followover.patch +- Add 0016-RH-dont-remove-map-on-enomem.patch +- Add 0017-RH-fix-shutdown-crash.patch +- Add 0018-RH-warn-on-bad-dev-loss-tmo.patch +- Add 0019-RH-deprecate-uid-gid-mode.patch +- Add 0020-RH-dont-remove-map-twice.patch +- Add 0021-RH-validate-guid-partitions.patch +- Add 0022-RH-adjust-messages.patch +- Add 0023-RH-manpage-update.patch + +* Tue Jan 24 2012 Benjamin Marzinski 0.4.9-21 +- Updated to latest upstream 0.4.9 code : multipath-tools-120123.tgz + (git commit id: 63704387009443bdb37d9deaaafa9ab121d45bfb) +- Add 0001-RH-fix-async-tur.patch +- Add 0002-RH-dont_start_with_no_config.patch +- Add 0003-RH-multipath.rules.patch +- Add 0004-RH-update-init-script.patch +- Add 0005-RH-cciss_id.patch +- Add 0006-RH-Make-build-system-RH-Fedora-friendly.patch +- Add 0007-RH-multipathd-blacklist-all-by-default.patch +- Add 0008-RH-add-mpathconf.patch +- Add 0009-RH-add-find-multipaths.patch +- Add 0010-RH-check-if-multipath-owns-path.patch +- Add 0011-RH-add-hp_tur-checker.patch + +* Fri Jan 13 2012 Fedora Release Engineering - 0.4.9-20 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild + +* Tue Sep 20 2011 Benjamin Marzinski -0.4.9-19 +- Modify 0103-add-disable-sync-option.patch +- Add 0104-RHBZ-737989-systemd-unit-fix.patch + * systemd will only start multipathd if /etc/multipath.conf exists +- Add 0105-fix-oom-adj.patch + * first try setting oom_score_adj + +* Mon Aug 15 2011 Kalev Lember - 0.4.9-18 +- Rebuilt for rpm bug #728707 + +* Tue Jul 19 2011 Benjamin Marzinski -0.4.9-17 +- Add 0103-add-disable-sync-option.patch + * add a -n (nosync) option to multipath. This disables synchronous + file creation with udev. + +* Fri Jul 15 2011 Benjamin Marzinski -0.4.9-16 +- Modify 0012-RH-udev-sync-support.patch +- Modify 0021-RHBZ-548874-add-find-multipaths.patch +- Modify 0022-RHBZ-557845-RHEL5-style-partitions.patch +- Add 0025-RHBZ-508827-update-multipathd-manpage.patch through + 0101-RHBZ-631009-disable-udev-disk-rules-on-reload.patch + * sync with current state of RHEL6. Next release should include a updated + source tarball with most of these fixes rolled in. +- Add 0102-RHBZ-690828-systemd-unit-file.patch + * Add Jóhann B. Guðmundsson's unit file for systemd. + * Add sub-package sysvinit for SysV init script. +- Resolves: bz #690828 + +* Tue Feb 08 2011 Fedora Release Engineering - 0.4.9-15 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild + +* Tue Feb 16 2010 Benjamin Marzinski -0.4.9-14 +- Modify 0021-RHBZ-548874-add-find-multipaths.patch + * fix bug where mpathconf wouldn't create a multpath.conf file unless one + already existed. + +* Tue Feb 16 2010 Benjamin Marzinski -0.4.9-13 +- Replace 0012-RH-explicitly-disable-dm-udev-sync-support-in-kpartx.patch + with 0012-RH-udev-sync-support.patch + * Add udev sync support to kpartx and multipath. In kpartx it is disabled + unless you use the -s option. +- Refresh 0013-RH-add-weighted_prio-prioritizer.patch +- Refresh 0021-RHBZ-548874-add-find-multipaths.patch +- Modify 0022-RHBZ-557845-RHEL5-style-partitions.patch + * kpartx now creates a 2 sector large device for dos extended + partitions, just like the kernel does on the regular block devices. +- Add 0023-RHBZ-557810-emc-invista-config.patch +- Add 0024-RHBZ-565933-checker-timeout.patch + * Multipath has a new option checker_timeout. If this is not set, + all path checker functions with explicit timeouts use + /sys/block/sd/device/timeout. If this is set, they use it instead. + +* Fri Jan 22 2010 Benjamin Marzinski -0.4.9-12 +- Refresh 0001-RH-queue-without-daemon.patch +- Refresh 0002-RH-path-checker.patch +- Modify 0010-RH-multipath-rules-udev-changes.patch + * Fix udev rules to use DM_SBIN_PATH when calling kpartx + * install udev rules to /lib/udev/rules.d instead of /etc/udev/rules.d +- Modify 0014-RH-add-hp_tur-checker.patch +- Add 0003-for-upstream-default-configs.patch +- Add 0016-RHBZ-554561-fix-init-error-msg.patch +- Add 0017-RHBZ-554592-man-page-note.patch +- Add 0018-RHBZ-554596-SUN-6540-config.patch +- Add 0019-RHBZ-554598-fix-multipath-locking.patch +- Add 0020-RHBZ-554605-fix-manual-failover.patch +- Add 0021-RHBZ-548874-add-find-multipaths.patch + * Added find_multipaths multipath.conf option + * Added /sbin/mpathconf for simple editting of multipath.conf +- Add 0022-RHBZ-557845-RHEL5-style-partitions.patch + * Make kpartx deal with logical partitions like it did in RHEL5. + Don't create a dm-device for the extended partition itself. + Create the logical partitions on top of the dm-device for the whole disk. + +* Mon Nov 16 2009 Benjamin Marzinski -0.4.9-11 +- Add 0002-for-upstream-add-tmo-config-options.patch + * Add fail_io_fail_tmo and dev_loss_tmo multipath.conf options +- Add 0013-RH-add-weighted_prio-prioritizer.patch +- Add 0014-RH-add-hp_tur-checker.patch +- Add 0015-RH-add-multipathd-count-paths-cmd.patch +- rename multipath.conf.redhat to multipath.conf, and remove the default + blacklist. + +* Tue Oct 27 2009 Fabio M. Di Nitto - 0.4.9-10 +- Updated to latest upstream 0.4.9 code : multipath-tools-091027.tar.gz + (git commit id: a946bd4e2a529e5fba9c9547d03d3f91806618a3) +- Drop unrequired for-upstream patches. +- BuildRequires and Requires new device-mapper version for udev sync support. + +* Tue Oct 20 2009 Fabio M. Di Nitto - 0.4.9-9 +- 0012-RH-explicitly-disable-dm-udev-sync-support-in-kpartx.patch + +* Mon Oct 19 2009 Fabio M. Di Nitto - 0.4.9-8 +- Split patches in "for-upstream" and "RH" series. +- Replace 0011-RH-multipathd-blacklist-all-by-default.patch with + version from Benjamin Marzinski. +- Update udev rules 0010-RH-multipath-rules-udev-changes.patch. +- rpmlint cleanup: + * Drop useless-provides kpartx. + * Cleanup tab vs spaces usage. + * Summary not capitalized. + * Missing docs in libs package. + * Fix init script LSB headers. +- Drop README* files from doc sections (they are empty). + +* Thu Oct 15 2009 Fabio M. Di Nitto - 0.4.9-7 +- Add patch 0010-RH-Set-friendly-defaults.patch: + * set rcdir to fedora default. + * do not install kpartx udev bits. + * install redhat init script. + * Cleanup spec file install target. +- Add patch 0011-RH-multipathd-blacklist-all-by-default.patch: + * Fix BZ#528059 + * Stop installing default config in /etc and move it to the doc dir. + +* Tue Oct 13 2009 Fabio M. Di Nitto - 0.4.9-6 +- Updated to latest upstream 0.4.9 code : multipath-tools-091013.tar.gz + (git commit id: aa0a885e1f19359c41b63151bfcface38ccca176) +- Drop, now upstream, patches: + * fix_missed_uevs.patch. + * log_all_messages.patch. + * uninstall.patch. + * select_lib.patch. + * directio_message_cleanup.patch. + * stop_warnings.patch. +- Drop redhatification.patch in favour of spec file hacks. +- Drop mpath_wait.patch: no longer required. +- Merge multipath_rules.patch and udev_change.patch. +- Rename all patches based on source. +- Add patch 0009-RH-fix-hp-sw-hardware-table-entries.patch to fix + default entry for hp_sw and match current kernel. +- Add multipath.conf.redhat as source instead of patch. +- spec file: + * divide runtime and build/setup bits. + * update BuildRoot. + * update install section to apply all the little hacks here and there, + in favour of patches against upstream. + * move ldconfig invokation to libs package where it belong. + * fix libs package directory ownership and files. + +* Thu Aug 20 2009 Benjamin Marzinski - 0.4.9-5 +- Fixed problem where maps were being added and then removed. +- Changed the udev rules to fix some issues. + +* Thu Jul 30 2009 Benjamin Marzinski - 0.4.9-4 +- Fixed build issue on i686 machines. + +* Wed Jul 29 2009 Benjamin Marzinski - 0.4.9-3 +- Updated to latest upstream 0.4.9 code : multipath-tools-090729.tgz + (git commit id: d678c139719d5631194b50e49f16ca97162ecd0f) +- moved multipath bindings file from /var/lib/multipath to /etc/multipath +- Fixed 354961, 432520 + +* Fri Jul 24 2009 Fedora Release Engineering - 0.4.9-2 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild + +* Wed May 6 2009 Mike Snitzer - 0.4.9-1 +- Updated to latest upstream 0.4.9 code: multipath-tools-090429.tgz + (git commit id: 7395bcda3a218df2eab1617df54628af0dc3456e) +- split the multipath libs out to a device-mapper-multipath-libs package +- if appropriate, install multipath libs in /lib64 and /lib64/multipath + +* Tue Apr 7 2009 Milan Broz - 0.4.8-10 +- Fix insecure permissions on multipathd.sock (CVE-2009-0115) + +* Fri Mar 6 2009 Milan Broz - 0.4.8-9 +- Fix kpartx extended partition handling (475283) + +* Tue Feb 24 2009 Fedora Release Engineering - 0.4.8-8 +- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild + +* Fri Sep 26 2008 Benjamin Marzinski 0.4.8-7 +- Since libaio is now in /lib, not /usr/lib, multipath no longer needs to + statically link against it. Fixed an error with binding file and WWIDs + that include spaces. Cleaned up the messages from the directio checker + function. Fixed the udev rules. Fixed a regression in multipath.conf + parsing +- Fixed 457530, 457589 + +* Wed Aug 20 2008 Benjamin Marzinski 0.4.8-6 +- Updated to latest upstream 0.4.8 code: multipath-tools-080804.tgz + (git commit id: eb87cbd0df8adf61d1c74c025f7326d833350f78) +- fixed 451817, 456397 (scsi_id_change.patch), 457530 (config_space_fix.patch) + 457589 (static_libaio.patch) + +* Fri Jun 13 2008 Alasdair Kergon - 0.4.8-5 +- Rebuild (rogue vendor tag). (451292) + +* Mon May 19 2008 Benjamin Marzinksi 0.4.8-4 +- Fixed Makefile issues. + +* Mon May 19 2008 Benjamin Marzinksi 0.4.8-3 +- Fixed ownership build error. + +* Mon May 19 2008 Benjamin Marzinksi 0.4.8-2 +- Forgot to commit some patches. + +* Mon May 19 2008 Benjamin Marzinski 0.4.8-1 +- Updated to latest Upstream 0.4.8 code: multipath-tools-080519.tgz + (git commit id: 42704728855376d2f7da2de1967d7bc71bc54a2f) + +* Tue May 06 2008 Alasdair Kergon - 0.4.7-15 +- Remove unnecessary multipath & kpartx static binaries. (bz 234928) + +* Fri Feb 29 2008 Tom "spot" Callaway - 0.4.7-14 +- fix sparc64 +- fix license tag + +* Tue Feb 19 2008 Fedora Release Engineering - 0.4.7-13 +- Autorebuild for GCC 4.3 + +* Wed Nov 14 2007 Benjamin Marzinski - 0.4.7-12 +- Fixed the dist tag so building will work properly. + +* Mon Feb 05 2007 Alasdair Kergon - 0.4.7-11.fc7 +- Add build dependency on new device-mapper-devel package. +- Add dependency on device-mapper. + +* Wed Jan 31 2007 Benjamin Marzinksi - 0.4.7-10.fc7 +- Update BuildRoot and PreReq lines. + +* Mon Jan 15 2007 Benjamin Marzinksi - 0.4.7-9.fc7 +- Fixed spec file. + +* Mon Jan 15 2007 Benjamin Marzinski - 0.4.7-8.fc7 +- Update to latest code (t0_4_7_head2) + +* Wed Dec 13 2006 Benjamin Marzinski - 0.4.7-7.fc7 +- Update to latest code (t0_4_7_head1) + +* Thu Sep 7 2006 Peter Jones - 0.4.7-5 +- Fix kpartx to handle with drives >2TB correctly. + +* Thu Aug 31 2006 Peter Jones - 0.4.7-4.1 +- Split kpartx out into its own package so dmraid can use it without + installing multipathd +- Fix a segfault in kpartx + +* Mon Jul 17 2006 Benjamin Marzinski 0.4.7-4.0 +- Updated to latest source. Fixes bug in default multipath.conf + +* Wed Jul 12 2006 Benjamin Marzinski 0.4.7-3.1 +- Added ncurses-devel to BuildRequires + +* Wed Jul 12 2006 Benjamin Marzinski 0.4.7-3.0 +- Updated to latest source. deals with change in libsysfs API + +* Wed Jul 12 2006 Jesse Keating - 0.4.7-2.2.1 +- rebuild + +* Mon Jul 10 2006 Benjamin Marzinski 0.4.7-2.2 +- fix tagging issue. + +* Mon Jul 10 2006 Benjamin Marzinski 0.4.7-2.1 +- changed BuildRequires from sysfsutils-devel to libsysfs-devel + +* Wed Jun 28 2006 Benjamin Marzinski 0.4.7-2.0 +- Updated to latest upstream source, fixes kpartx udev rule issue + +* Tue Jun 06 2006 Benjamin Marzinski 0.4.7-1.0 +- Updated to Christophe's latest source + +* Mon May 22 2006 Alasdair Kergon - 0.4.5-16.0 +- Newer upstream source (t0_4_5_post59). + +* Mon May 22 2006 Alasdair Kergon - 0.4.5-12.3 +- BuildRequires: libsepol-devel, readline-devel + +* Mon Feb 27 2006 Benjamin Marzinski 0.4.5-12.2 +- Prereq: chkconfig + +* Mon Feb 20 2006 Karsten Hopp 0.4.5-12.1 +- BuildRequires: libselinux-devel + +* Fri Feb 10 2006 Jesse Keating - 0.4.5-12.0.1 +- bump again for double-long bug on ppc(64) + +* Tue Feb 07 2006 Benjamin Marzinski -0.4.5-12.0 +- Updated to latest upstream source (t0_4_5_post56) + +* Tue Feb 07 2006 Jesse Keating - 0.4.5-9.1.1 +- rebuilt for new gcc4.1 snapshot and glibc changes + +* Mon Dec 19 2005 Benjamin Marzinski - 0.4.5-9.1 +- added patch for fedora changes + +* Fri Dec 16 2005 Benjamin Marzinski - 0.4.5-9.0 +- Updated to latest upstream source (t)_4_5_post52) + +* Fri Dec 09 2005 Jesse Keating +- rebuilt + +* Sun Dec 4 2005 Peter Jones - 0.4.4-2.6 +- rebuild for newer libs + +* Tue Nov 15 2005 Peter Jones - 0.4.4-2.5 +- unsplit kpartx. parted knows how to do this now, so we don't + need this in a separate package. + +* Tue Nov 15 2005 Peter Jones - 0.4.4-2.4 +- split kpartx out into its own package + +* Fri May 06 2005 Bill Nottingham - 0.4.4-2.3 +- Fix last fix. + +* Thu May 05 2005 Alasdair Kergon - 0.4.4-2.2 +- Fix last fix. + +* Wed May 04 2005 Alasdair Kergon - 0.4.4-2.1 +- By default, disable the multipathd service. + +* Tue Apr 19 2005 Alasdair Kergon - 0.4.4-2.0 +- Fix core dump from last build. + +* Tue Apr 19 2005 Alasdair Kergon - 0.4.4-1.0 +- Move cache file into /var/cache/multipath. + +* Fri Apr 08 2005 Alasdair Kergon - 0.4.4-0.pre8.1 +- Remove pp_balance_units. + +* Mon Apr 04 2005 Alasdair Kergon - 0.4.4-0.pre8.0 +- Incorporate numerous upstream fixes. +- Update init script to distribution standards. + +* Tue Mar 01 2005 Alasdair Kergon - 0.4.2-1.0 +- Initial import based on Christophe Varoqui's spec file.