From 23ec6cc36dbd8104c2bfa1c34ac9bfe760c3e28f Mon Sep 17 00:00:00 2001 From: eabdullin Date: Tue, 12 Nov 2024 10:50:02 +0000 Subject: [PATCH] import UBI device-mapper-multipath-0.8.7-32.el9 --- ...actually-truncate-too-large-vpd-page.patch | 40 ++ ...retical-overflow-in-loop-device-name.patch | 23 ++ ...p-track-of-queueing-state-in-feature.patch | 194 ++++++++++ ...1-libmultipath-export-partmap_in_use.patch | 54 +++ ...nge-flush_on_last_del-to-fix-a-multi.patch | 346 ++++++++++++++++++ ...-dev_loss_tmo-to-avoid-race-with-no_.patch | 40 ++ ...th-remove-pathgroup-wildcard-options.patch | 32 ++ ...print-all-values-in-snprint_failback.patch | 32 ++ ...double-counting-map-failures-for-no_.patch | 34 ++ ...man-pages-add-missing-multipathd-com.patch | 102 ++++++ ...th-change-the-vend-prod-rev-printing.patch | 54 +++ ...man-pages-Add-format-wildcard-descri.patch | 243 ++++++++++++ ...fix-multipath-ll-bug-for-Native-NVME.patch | 103 ++++++ ...eply-length-to-zero-for-NULL-replies.patch | 29 ++ SPECS/device-mapper-multipath.spec | 58 ++- 15 files changed, 1383 insertions(+), 1 deletion(-) create mode 100644 SOURCES/0108-libmultipath-actually-truncate-too-large-vpd-page.patch create mode 100644 SOURCES/0109-kpartx-fix-theoretical-overflow-in-loop-device-name.patch create mode 100644 SOURCES/0110-libmultipath-keep-track-of-queueing-state-in-feature.patch create mode 100644 SOURCES/0111-libmultipath-export-partmap_in_use.patch create mode 100644 SOURCES/0112-libmultipath-change-flush_on_last_del-to-fix-a-multi.patch create mode 100644 SOURCES/0113-libmultipath-pad-dev_loss_tmo-to-avoid-race-with-no_.patch create mode 100644 SOURCES/0114-libmultipath-remove-pathgroup-wildcard-options.patch create mode 100644 SOURCES/0115-libmultipath-print-all-values-in-snprint_failback.patch create mode 100644 SOURCES/0116-multipathd-Stop-double-counting-map-failures-for-no_.patch create mode 100644 SOURCES/0117-multipath-tools-man-pages-add-missing-multipathd-com.patch create mode 100644 SOURCES/0118-libmultipath-change-the-vend-prod-rev-printing.patch create mode 100644 SOURCES/0119-multipath-tools-man-pages-Add-format-wildcard-descri.patch create mode 100644 SOURCES/0120-multipath-tools-fix-multipath-ll-bug-for-Native-NVME.patch create mode 100644 SOURCES/0121-multipathd-set-reply-length-to-zero-for-NULL-replies.patch diff --git a/SOURCES/0108-libmultipath-actually-truncate-too-large-vpd-page.patch b/SOURCES/0108-libmultipath-actually-truncate-too-large-vpd-page.patch new file mode 100644 index 0000000..8240aaa --- /dev/null +++ b/SOURCES/0108-libmultipath-actually-truncate-too-large-vpd-page.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 9 Apr 2024 14:09:49 -0400 +Subject: [PATCH] libmultipath: actually truncate too-large vpd page. + +When multipath notices that the vpd page is too large, it needs to +actually truncate it. Also, whe calling parse_vpd_pg83() with a possibly +truncated page, multipath needs to check that it actually has a whole +vpd entry, before trying to use it. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/discovery.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index adf8bbaa..ae7eb7e6 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -1164,7 +1164,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, + int vpd_type, prio = -1, naa_prio; + + d = in + 4; +- while (d < in + in_len) { ++ while (d + 4 <= in + in_len && d + d[3] + 4 <= in + in_len) { + /* Select 'association: LUN' */ + if ((d[1] & 0x30) != 0) { + d += d[3] + 4; +@@ -1363,8 +1363,10 @@ get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen) + return -ENODATA; + } + buff_len = get_unaligned_be16(&buff[2]) + 4; +- if (buff_len > 4096) ++ if (buff_len > 4096) { + condlog(3, "vpd pg%02x page truncated", pg); ++ buff_len = 4096; ++ } + + if (pg == 0x80) + len = parse_vpd_pg80(buff, str, maxlen); diff --git a/SOURCES/0109-kpartx-fix-theoretical-overflow-in-loop-device-name.patch b/SOURCES/0109-kpartx-fix-theoretical-overflow-in-loop-device-name.patch new file mode 100644 index 0000000..1a632b8 --- /dev/null +++ b/SOURCES/0109-kpartx-fix-theoretical-overflow-in-loop-device-name.patch @@ -0,0 +1,23 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 9 Apr 2024 14:13:34 -0400 +Subject: [PATCH] kpartx: fix theoretical overflow in loop device name + +Signed-off-by: Benjamin Marzinski +--- + kpartx/lopart.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/kpartx/lopart.c b/kpartx/lopart.c +index 9b652554..80ce1312 100644 +--- a/kpartx/lopart.c ++++ b/kpartx/lopart.c +@@ -159,7 +159,7 @@ char *find_loop_by_file(const char *filename) + + char *find_unused_loop_device(void) + { +- char dev[20], *next_loop_dev = NULL; ++ char dev[21], *next_loop_dev = NULL; + int fd, next_loop = 0, somedev = 0, someloop = 0, loop_known = 0; + struct stat statbuf; + struct loop_info loopinfo; diff --git a/SOURCES/0110-libmultipath-keep-track-of-queueing-state-in-feature.patch b/SOURCES/0110-libmultipath-keep-track-of-queueing-state-in-feature.patch new file mode 100644 index 0000000..f047932 --- /dev/null +++ b/SOURCES/0110-libmultipath-keep-track-of-queueing-state-in-feature.patch @@ -0,0 +1,194 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 19 Dec 2023 17:51:50 -0500 +Subject: [PATCH] libmultipath: keep track of queueing state in features + +Make multipathd update mpp->features when in enables or disables +queuing. This patch handles all the cases except failed removes by +dm_suspend_and_flush_map(), which is never called by multipathd. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/configure.c | 4 +--- + libmultipath/devmapper.c | 23 +++++++++++++++++++---- + libmultipath/devmapper.h | 2 +- + libmultipath/structs_vec.c | 10 +++++----- + multipathd/main.c | 8 ++++---- + 5 files changed, 30 insertions(+), 17 deletions(-) + +diff --git a/libmultipath/configure.c b/libmultipath/configure.c +index bbdbb8ca..71acb968 100644 +--- a/libmultipath/configure.c ++++ b/libmultipath/configure.c +@@ -1279,9 +1279,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid, + mpp->no_path_retry != NO_PATH_RETRY_FAIL) + condlog(3, "%s: multipathd not running, unset " + "queue_if_no_path feature", mpp->alias); +- if (!dm_queue_if_no_path(mpp->alias, 0)) +- remove_feature(&mpp->features, +- "queue_if_no_path"); ++ dm_queue_if_no_path(mpp, 0); + } + + if (!is_daemon && mpp->action != ACT_NOTHING) +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index f9de3358..5711f0ee 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -57,6 +57,7 @@ static int dm_cancel_remove_partmaps(const char * mapname); + static int do_foreach_partmaps(const char * mapname, + int (*partmap_func)(const char *, void *), + void *data); ++static int _dm_queue_if_no_path(const char *mapname, int enable); + + #ifndef LIBDM_API_COOKIE + static inline int dm_task_set_cookie(struct dm_task *dmt, uint32_t *c, int a) +@@ -1072,7 +1073,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove, + if (need_suspend && + dm_get_map(mapname, &mapsize, ¶ms) == DMP_OK && + strstr(params, "queue_if_no_path")) { +- if (!dm_queue_if_no_path(mapname, 0)) ++ if (!_dm_queue_if_no_path(mapname, 0)) + queue_if_no_path = 1; + else + /* Leave queue_if_no_path alone if unset failed */ +@@ -1121,7 +1122,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove, + } while (retries-- > 0); + + if (queue_if_no_path == 1) +- dm_queue_if_no_path(mapname, 1); ++ _dm_queue_if_no_path(mapname, 1); + + return 1; + } +@@ -1236,8 +1237,8 @@ dm_reinstate_path(const char * mapname, char * path) + return dm_message(mapname, message); + } + +-int +-dm_queue_if_no_path(const char *mapname, int enable) ++static int ++_dm_queue_if_no_path(const char *mapname, int enable) + { + char *message; + +@@ -1249,6 +1250,20 @@ dm_queue_if_no_path(const char *mapname, int enable) + return dm_message(mapname, message); + } + ++int dm_queue_if_no_path(struct multipath *mpp, int enable) ++{ ++ int r; ++ static const char no_path_retry[] = "queue_if_no_path"; ++ ++ if ((r = _dm_queue_if_no_path(mpp->alias, enable)) == 0) { ++ if (enable) ++ add_feature(&mpp->features, no_path_retry); ++ else ++ remove_feature(&mpp->features, no_path_retry); ++ } ++ return r; ++} ++ + static int + dm_groupmsg (const char * msg, const char * mapname, int index) + { +diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h +index 808da28d..41b8c31d 100644 +--- a/libmultipath/devmapper.h ++++ b/libmultipath/devmapper.h +@@ -58,7 +58,7 @@ int dm_cancel_deferred_remove(struct multipath *mpp); + int dm_flush_maps (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); ++int dm_queue_if_no_path(struct multipath *mpp, int enable); + int dm_switchgroup(const char * mapname, int index); + int dm_enablegroup(const char * mapname, int index); + int dm_disablegroup(const char * mapname, int index); +diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c +index 86ad89ca..56915e96 100644 +--- a/libmultipath/structs_vec.c ++++ b/libmultipath/structs_vec.c +@@ -579,7 +579,7 @@ static void leave_recovery_mode(struct multipath *mpp) + */ + if (recovery && (mpp->no_path_retry == NO_PATH_RETRY_QUEUE || + mpp->no_path_retry > 0)) { +- dm_queue_if_no_path(mpp->alias, 1); ++ dm_queue_if_no_path(mpp, 1); + condlog(2, "%s: queue_if_no_path enabled", mpp->alias); + condlog(1, "%s: Recovered to normal mode", mpp->alias); + } +@@ -598,11 +598,11 @@ void __set_no_path_retry(struct multipath *mpp, bool check_features) + break; + case NO_PATH_RETRY_FAIL: + if (!check_features || is_queueing) +- dm_queue_if_no_path(mpp->alias, 0); ++ dm_queue_if_no_path(mpp, 0); + break; + case NO_PATH_RETRY_QUEUE: + if (!check_features || !is_queueing) +- dm_queue_if_no_path(mpp->alias, 1); ++ dm_queue_if_no_path(mpp, 1); + break; + default: + if (count_active_paths(mpp) > 0) { +@@ -612,7 +612,7 @@ void __set_no_path_retry(struct multipath *mpp, bool check_features) + */ + if ((!check_features || !is_queueing) && + !mpp->in_recovery) +- dm_queue_if_no_path(mpp->alias, 1); ++ dm_queue_if_no_path(mpp, 1); + leave_recovery_mode(mpp); + } else { + /* +@@ -623,7 +623,7 @@ void __set_no_path_retry(struct multipath *mpp, bool check_features) + */ + if ((!check_features || is_queueing) && + mpp->in_recovery && mpp->retry_tick == 0) +- dm_queue_if_no_path(mpp->alias, 0); ++ dm_queue_if_no_path(mpp, 0); + if (pathcount(mpp, PATH_PENDING) == 0) + enter_recovery_mode(mpp); + } +diff --git a/multipathd/main.c b/multipathd/main.c +index 26be6dc3..74f8114c 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -502,7 +502,7 @@ flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) { + mpp->no_path_retry = NO_PATH_RETRY_FAIL; + mpp->disable_queueing = 1; + mpp->stat_map_failures++; +- dm_queue_if_no_path(mpp->alias, 0); ++ dm_queue_if_no_path(mpp, 0); + } + r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove); + if (r) { +@@ -833,7 +833,7 @@ uev_remove_map (struct uevent * uev, struct vectors * vecs) + goto out; + } + +- dm_queue_if_no_path(alias, 0); ++ dm_queue_if_no_path(mpp, 0); + remove_map_and_stop_waiter(mpp, vecs); + out: + lock_cleanup_pop(vecs->lock); +@@ -2015,7 +2015,7 @@ retry_count_tick(vector mpvec) + condlog(4, "%s: Retrying.. No active path", mpp->alias); + if(--mpp->retry_tick == 0) { + mpp->stat_map_failures++; +- dm_queue_if_no_path(mpp->alias, 0); ++ dm_queue_if_no_path(mpp, 0); + condlog(2, "%s: Disable queueing", mpp->alias); + } + } +@@ -3110,7 +3110,7 @@ static void cleanup_maps(struct vectors *vecs) + put_multipath_config(conf); + if (queue_without_daemon == QUE_NO_DAEMON_OFF) + vector_foreach_slot(vecs->mpvec, mpp, i) +- dm_queue_if_no_path(mpp->alias, 0); ++ dm_queue_if_no_path(mpp, 0); + remove_maps_and_stop_waiters(vecs); + vecs->mpvec = NULL; + } diff --git a/SOURCES/0111-libmultipath-export-partmap_in_use.patch b/SOURCES/0111-libmultipath-export-partmap_in_use.patch new file mode 100644 index 0000000..38b1ed7 --- /dev/null +++ b/SOURCES/0111-libmultipath-export-partmap_in_use.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 25 Apr 2024 19:35:13 -0400 +Subject: [PATCH] libmultipath: export partmap_in_use + +A future commit will make use of this function + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +Reviewed-by: Benjamin Marzinski +--- + libmultipath/devmapper.c | 2 +- + libmultipath/devmapper.h | 1 + + libmultipath/libmultipath.version | 5 +++++ + 3 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index 5711f0ee..4a66e2c4 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -1028,7 +1028,7 @@ has_partmap(const char *name __attribute__((unused)), + return 1; + } + +-static int ++int + partmap_in_use(const char *name, void *data) + { + int part_count, *ret_count = (int *)data; +diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h +index 41b8c31d..88e0b114 100644 +--- a/libmultipath/devmapper.h ++++ b/libmultipath/devmapper.h +@@ -48,6 +48,7 @@ int dm_get_map(const char *, unsigned long long *, char **); + int dm_get_status(const char *, char **); + int dm_type(const char *, char *); + int dm_is_mpath(const char *); ++int partmap_in_use(const char *name, void *data); + int _dm_flush_map (const char *, int, int, int, int); + int dm_flush_map_nopaths(const char * mapname, int deferred_remove); + #define dm_flush_map(mapname) _dm_flush_map(mapname, 1, 0, 0, 0) +diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version +index 1d018eab..40d9246d 100644 +--- a/libmultipath/libmultipath.version ++++ b/libmultipath/libmultipath.version +@@ -302,3 +302,8 @@ LIBMULTIPATH_9.1.2 { + global: + cleanup_mutex; + } LIBMULTIPATH_9.1.1; ++ ++LIBMULTIPATH_9.1.3 { ++global: ++ partmap_in_use; ++} LIBMULTIPATH_9.1.2; diff --git a/SOURCES/0112-libmultipath-change-flush_on_last_del-to-fix-a-multi.patch b/SOURCES/0112-libmultipath-change-flush_on_last_del-to-fix-a-multi.patch new file mode 100644 index 0000000..58ef25f --- /dev/null +++ b/SOURCES/0112-libmultipath-change-flush_on_last_del-to-fix-a-multi.patch @@ -0,0 +1,346 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 25 Apr 2024 19:35:14 -0400 +Subject: [PATCH] libmultipath: change flush_on_last_del to fix a multipathd + hang + +Commit 9bd3482e ("multipathd: make flush_map() delete maps like the +multipath command") fixed an issue where deleting a queueing multipath +device through multipathd could hang because the multipath device had +outstanding IO, even though the only openers of it at the time of +deletion were the kpartx partition devices. However it is still possible +to hang multipathd, because autoremoving the device when all paths have +been deleted doesn't disable queueing. To reproduce this hang: + +1. create a multipath device with a kpartx partition on top of it and +no_path_retry set to either "queue" or something long enough to run all +the commands in the reproducer before it disables queueing. +2. disable all the paths to the device with something like: + # echo offline > /sys/block//device/state +3. Write directly to the multipath device with something like: + # dd if=/dev/zero of=/dev/mapper/ bs=4K count=1 +4. delete all the paths to the device with something like: + # echo 1 > /sys/block//device/delete + +Multipathd will hang trying to delete the kpartx device because, as the +last opener, it must flush the multipath device before closing it. +Because it hangs holding the vecs_lock, multipathd will never disable +queueing on the device, so it will hang forever, even if no_path_retry +is set to a number. + +This hang can occur, even if deferred_remove is set. Since nothing has +the kpartx device opened, device-mapper does an immediate remove, which +will still hang. This means that even if deferred_remove is set, +multipathd still cannot delete a map while queueing is enabled. It must +either disable queueing or skip the autoremove. + +Mulitpath can currently be configured to avoid this hang by setting + +flush_on_last_del yes + +However there are good reasons why users wouldn't want to set that. They +may need to be able to survive having all paths getting temporarily +deleted. I should note that this is a pretty rare corner case, since +multipath automatically sets dev_loss_tmo so that it should not trigger +before queueing is disabled. + +This commit avoids the hang by changing the possible values for +flush_on_last_del to "never", "unused", and "always", and sets the +default to "unused". "always" works like "yes" did, "never" will not +disable queueing, and "unused" will only disable queueing if nothing has +the kpartx devices or the multipath device open. In order to be safe, if +the device has queue_if_no_paths set (and in case of "unused", the +device is in-use) the autoremove will be skipped. Also, instead of just +trusting the lack of "queue_if_no_paths" in the current mpp->features, +multipathd will tell the kernel to disable queueing, just to be sure it +actually is. + +I chose "unused" as the default because this should generally only cause +multipathd to work differently from the users perspective when nothing +has the multipath device open but it is queueing and there is +outstanding IO. Without this change, it would have hung instead of +failing the outstanding IO. However, I do understand that an argument +could be made that "never" makes more sense as default, even though it +will cause multipathd to skip autoremoves in cases where it wouldn't +before. The change to the behavior of deffered_remove will be +noticeable, but skipping an autoremove is much better than hanging. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +Reviewed-by: Benjamin Marzinski +--- + libmultipath/defaults.h | 2 +- + libmultipath/dict.c | 72 +++++++++++++++++++++++++++++++++----- + libmultipath/dict.h | 1 + + libmultipath/hwtable.c | 6 ++-- + libmultipath/propsel.c | 4 ++- + libmultipath/structs.h | 7 ++-- + multipath/multipath.conf.5 | 20 ++++++++--- + multipathd/main.c | 39 ++++++++++++++++----- + 8 files changed, 122 insertions(+), 29 deletions(-) + +diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h +index c3788bbc..5e77387e 100644 +--- a/libmultipath/defaults.h ++++ b/libmultipath/defaults.h +@@ -41,7 +41,7 @@ + #define DEFAULT_PRIO PRIO_CONST + #define DEFAULT_PRIO_ARGS "" + #define DEFAULT_CHECKER TUR +-#define DEFAULT_FLUSH FLUSH_DISABLED ++#define DEFAULT_FLUSH FLUSH_UNUSED + #define DEFAULT_USER_FRIENDLY_NAMES USER_FRIENDLY_NAMES_OFF + #define DEFAULT_FORCE_SYNC 0 + #define UNSET_PARTITION_DELIM "/UNSET/" +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index ce1b6c99..3c011ece 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -825,14 +825,70 @@ declare_def_snprint(checker_timeout, print_nonzero) + declare_def_handler(allow_usb_devices, set_yes_no) + declare_def_snprint(allow_usb_devices, print_yes_no) + +-declare_def_handler(flush_on_last_del, set_yes_no_undef) +-declare_def_snprint_defint(flush_on_last_del, print_yes_no_undef, DEFAULT_FLUSH) +-declare_ovr_handler(flush_on_last_del, set_yes_no_undef) +-declare_ovr_snprint(flush_on_last_del, print_yes_no_undef) +-declare_hw_handler(flush_on_last_del, set_yes_no_undef) +-declare_hw_snprint(flush_on_last_del, print_yes_no_undef) +-declare_mp_handler(flush_on_last_del, set_yes_no_undef) +-declare_mp_snprint(flush_on_last_del, print_yes_no_undef) ++ ++static const char * const flush_on_last_del_optvals[] = { ++ [FLUSH_NEVER] = "never", ++ [FLUSH_ALWAYS] = "always", ++ [FLUSH_UNUSED] = "unused", ++}; ++ ++static int ++set_flush_on_last_del(vector strvec, void *ptr, const char *file, int line_nr) ++{ ++ int i; ++ int *flush_val_ptr = (int *)ptr; ++ char *buff; ++ ++ buff = set_value(strvec); ++ if (!buff) ++ return 1; ++ ++ for (i = FLUSH_NEVER; i <= FLUSH_UNUSED; i++) { ++ if (flush_on_last_del_optvals[i] != NULL && ++ !strcmp(buff, flush_on_last_del_optvals[i])) { ++ *flush_val_ptr = i; ++ break; ++ } ++ } ++ ++ if (i > FLUSH_UNUSED) { ++ bool deprecated = true; ++ if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0) ++ *flush_val_ptr = FLUSH_UNUSED; ++ else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0) ++ *flush_val_ptr = FLUSH_ALWAYS; ++ else { ++ deprecated = false; ++ condlog(1, "%s line %d, invalid value for flush_on_last_del: \"%s\"", ++ file, line_nr, buff); ++ } ++ if (deprecated) ++ condlog(3, "%s line %d, \"%s\" is a deprecated value for flush_on_last_del and is treated as \"%s\"", ++ file, line_nr, buff, ++ flush_on_last_del_optvals[*flush_val_ptr]); ++ } ++ ++ free(buff); ++ return 0; ++} ++ ++int ++print_flush_on_last_del(struct strbuf *buff, long v) ++{ ++ if (v == FLUSH_UNDEF) ++ return 0; ++ return append_strbuf_quoted(buff, flush_on_last_del_optvals[(int)v]); ++} ++ ++declare_def_handler(flush_on_last_del, set_flush_on_last_del) ++declare_def_snprint_defint(flush_on_last_del, print_flush_on_last_del, ++ DEFAULT_FLUSH) ++declare_ovr_handler(flush_on_last_del, set_flush_on_last_del) ++declare_ovr_snprint(flush_on_last_del, print_flush_on_last_del) ++declare_hw_handler(flush_on_last_del, set_flush_on_last_del) ++declare_hw_snprint(flush_on_last_del, print_flush_on_last_del) ++declare_mp_handler(flush_on_last_del, set_flush_on_last_del) ++declare_mp_snprint(flush_on_last_del, print_flush_on_last_del) + + declare_def_handler(user_friendly_names, set_yes_no_undef) + declare_def_snprint_defint(user_friendly_names, print_yes_no_undef, +diff --git a/libmultipath/dict.h b/libmultipath/dict.h +index d963b4ad..6b1aae5c 100644 +--- a/libmultipath/dict.h ++++ b/libmultipath/dict.h +@@ -20,4 +20,5 @@ int print_reservation_key(struct strbuf *buff, + struct be64 key, uint8_t flags, int source); + int print_off_int_undef(struct strbuf *buff, long v); + int print_auto_resize(struct strbuf *buff, long v); ++int print_flush_on_last_del(struct strbuf *buff, long v); + #endif /* _DICT_H */ +diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c +index 78ac7988..94012d50 100644 +--- a/libmultipath/hwtable.c ++++ b/libmultipath/hwtable.c +@@ -60,7 +60,7 @@ + .no_path_retry = NO_PATH_RETRY_UNDEF, + .minio = 1000, + .minio_rq = 1, +- .flush_on_last_del = FLUSH_DISABLED, ++ .flush_on_last_del = FLUSH_UNUSED, + .user_friendly_names = USER_FRIENDLY_NAMES_OFF, + .fast_io_fail = 5, + .dev_loss = 600, +@@ -800,7 +800,7 @@ static struct hwentry default_hw[] = { + .no_path_retry = NO_PATH_RETRY_QUEUE, + .pgpolicy = GROUP_BY_PRIO, + .pgfailback = -FAILBACK_IMMEDIATE, +- .flush_on_last_del = FLUSH_ENABLED, ++ .flush_on_last_del = FLUSH_ALWAYS, + .dev_loss = MAX_DEV_LOSS_TMO, + .prio_name = PRIO_ONTAP, + .user_friendly_names = USER_FRIENDLY_NAMES_OFF, +@@ -1122,7 +1122,7 @@ static struct hwentry default_hw[] = { + .no_path_retry = NO_PATH_RETRY_FAIL, + .minio = 1, + .minio_rq = 1, +- .flush_on_last_del = FLUSH_ENABLED, ++ .flush_on_last_del = FLUSH_ALWAYS, + .fast_io_fail = 15, + .dev_loss = 15, + }, +diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c +index 9dea6f92..be781ff7 100644 +--- a/libmultipath/propsel.c ++++ b/libmultipath/propsel.c +@@ -902,6 +902,7 @@ out: + int select_flush_on_last_del(struct config *conf, struct multipath *mp) + { + const char *origin; ++ STRBUF_ON_STACK(buff); + + mp_set_mpe(flush_on_last_del); + mp_set_ovr(flush_on_last_del); +@@ -909,8 +910,9 @@ int select_flush_on_last_del(struct config *conf, struct multipath *mp) + mp_set_conf(flush_on_last_del); + mp_set_default(flush_on_last_del, DEFAULT_FLUSH); + out: ++ print_flush_on_last_del(&buff, mp->flush_on_last_del); + condlog(3, "%s: flush_on_last_del = %s %s", mp->alias, +- (mp->flush_on_last_del == FLUSH_ENABLED)? "yes" : "no", origin); ++ get_strbuf_str(&buff), origin); + return 0; + } + +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index d2ad4867..4bf8c93a 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -109,9 +109,10 @@ enum marginal_pathgroups_mode { + }; + + enum flush_states { +- FLUSH_UNDEF = YNU_UNDEF, +- FLUSH_DISABLED = YNU_NO, +- FLUSH_ENABLED = YNU_YES, ++ FLUSH_UNDEF, ++ FLUSH_NEVER, ++ FLUSH_ALWAYS, ++ FLUSH_UNUSED, + }; + + enum log_checker_err_states { +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 38eb5c90..10eddc0c 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -672,12 +672,22 @@ The default is: \fBno\fR + .TP + .B flush_on_last_del + If set to +-.I yes ++.I always + , multipathd will disable queueing when the last path to a device has been +-deleted. +-.RS +-.TP +-The default is: \fBno\fR ++deleted. If set to ++.I never ++, multipathd will not disable queueing when the last path to a device has ++been deleted. Since multipath cannot safely remove a device while queueing ++is enabled, setting this to \fInever\fR means that multipathd will not ++automatically remove an unused multipath device whose paths are all deleted if ++it is currently set to queue_if_no_path. If set to ++.I unused ++, multipathd will only disable queueing when the last path is removed if ++nothing currently has the multipath device or any of the kpartx partition ++devices on top of it open. ++.RS ++.TP ++The default is: \fBunused\fR + .RE + . + . +diff --git a/multipathd/main.c b/multipathd/main.c +index 74f8114c..8ec58f5d 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -491,19 +491,42 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset) + static bool + flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) { + int r; ++ bool is_queueing = true; + ++ if (mpp->features) ++ is_queueing = strstr(mpp->features, "queue_if_no_path"); ++ ++ /* It's not safe to do a remove of a map that has "queue_if_no_path" ++ * set, since there could be outstanding IO which will cause ++ * multipathd to hang while attempting the remove */ ++ if (mpp->flush_on_last_del == FLUSH_NEVER && is_queueing) { ++ condlog(2, "%s: map is queueing, can't remove", mpp->alias); ++ return false; ++ } ++ if (mpp->flush_on_last_del == FLUSH_UNUSED && ++ partmap_in_use(mpp->alias, NULL) && is_queueing) { ++ condlog(2, "%s: map in use and queueing, can't remove", ++ mpp->alias); ++ return false; ++ } + /* +- * flush_map will fail if the device is open ++ * This will flush FLUSH_NEVER devices and FLUSH_UNUSED devices ++ * that are in use, but only if they are already marked as not ++ * queueing. That is just to make absolutely certain that they ++ * really are not queueing, like they claim. + */ +- if (mpp->flush_on_last_del == FLUSH_ENABLED) { +- condlog(2, "%s Last path deleted, disabling queueing", ++ condlog(is_queueing ? 2 : 3, "%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++; ++ if (dm_queue_if_no_path(mpp, 0) != 0) { ++ condlog(0, "%s: failed to disable queueing. Not removing", + 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, 0); ++ return false; + } ++ + r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove); + if (r) { + if (r == 1) diff --git a/SOURCES/0113-libmultipath-pad-dev_loss_tmo-to-avoid-race-with-no_.patch b/SOURCES/0113-libmultipath-pad-dev_loss_tmo-to-avoid-race-with-no_.patch new file mode 100644 index 0000000..efc59c6 --- /dev/null +++ b/SOURCES/0113-libmultipath-pad-dev_loss_tmo-to-avoid-race-with-no_.patch @@ -0,0 +1,40 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 25 Apr 2024 19:35:16 -0400 +Subject: [PATCH] libmultipath: pad dev_loss_tmo to avoid race with + no_path_retry + +Currently multipath makes sure that dev_loss_tmo is at least as long as +the configured no path queueing time. The goal is to make sure that path +devices aren't removed while the multipath device is still queueing in +hopes that they will become usable again. + +This is racy. Multipathd may take longer to check the paths than +configured. If strict_timing isn't set, it will definitely take longer. +To account for this, pad the minimum dev_loss_tmo value by five seconds +(one default checker interval) plus one second per minute of no path +queueing time. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +Reviewed-by: Benjamin Marzinski +--- + libmultipath/discovery.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index ae7eb7e6..b24594cd 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -884,6 +884,11 @@ sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp) + uint64_t no_path_retry_tmo = + (uint64_t)mpp->no_path_retry * conf->checkint; + ++ /* pad no_path_retry_tmo by one standard check interval ++ * plus one second per minute to account for timing ++ * issues with the rechecks */ ++ no_path_retry_tmo += no_path_retry_tmo / 60 + DEFAULT_CHECKINT; ++ + if (no_path_retry_tmo > MAX_DEV_LOSS_TMO) + min_dev_loss = MAX_DEV_LOSS_TMO; + else diff --git a/SOURCES/0114-libmultipath-remove-pathgroup-wildcard-options.patch b/SOURCES/0114-libmultipath-remove-pathgroup-wildcard-options.patch new file mode 100644 index 0000000..df10a82 --- /dev/null +++ b/SOURCES/0114-libmultipath-remove-pathgroup-wildcard-options.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Nitin Yewale +Date: Thu, 12 Jan 2023 14:28:49 -0600 +Subject: [PATCH] libmultipath: remove pathgroup wildcard options + +The multipathd command "multipathd show wildcards" shows the pathgroup +format wildcards, but there is no way to use them in a multipathd +command. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/print.c | 7 ------- + 1 file changed, 7 deletions(-) + +diff --git a/libmultipath/print.c b/libmultipath/print.c +index 082e4e30..a6e4c774 100644 +--- a/libmultipath/print.c ++++ b/libmultipath/print.c +@@ -803,13 +803,6 @@ int snprint_wildcards(struct strbuf *buff) + pd[i].wildcard, pd[i].header)) < 0) + return rc; + +- if ((rc = append_strbuf_str(buff, "\npathgroup format wildcards:\n")) < 0) +- return rc; +- for (i = 0; pgd[i].header; i++) +- if ((rc = print_strbuf(buff, "%%%c %s\n", +- pgd[i].wildcard, pgd[i].header)) < 0) +- return rc; +- + return get_strbuf_len(buff) - initial_len; + } + diff --git a/SOURCES/0115-libmultipath-print-all-values-in-snprint_failback.patch b/SOURCES/0115-libmultipath-print-all-values-in-snprint_failback.patch new file mode 100644 index 0000000..2fcffd1 --- /dev/null +++ b/SOURCES/0115-libmultipath-print-all-values-in-snprint_failback.patch @@ -0,0 +1,32 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 8 May 2024 19:02:30 -0400 +Subject: [PATCH] libmultipath: print all values in snprint_failback + +Add the missing output for manual failback and print the defferral time +for deferred failbacks, if one isn't currently in progress. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/print.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/print.c b/libmultipath/print.c +index a6e4c774..535e0271 100644 +--- a/libmultipath/print.c ++++ b/libmultipath/print.c +@@ -199,9 +199,13 @@ snprint_failback (struct strbuf *buff, const struct multipath * mpp) + return append_strbuf_str(buff, "immediate"); + if (mpp->pgfailback == -FAILBACK_FOLLOWOVER) + return append_strbuf_str(buff, "followover"); ++ if (mpp->pgfailback == -FAILBACK_MANUAL) ++ return append_strbuf_str(buff, "manual"); ++ if (mpp->pgfailback == FAILBACK_UNDEF) ++ return append_strbuf_str(buff, "undef"); + + if (!mpp->failback_tick) +- return append_strbuf_str(buff, "-"); ++ return print_strbuf(buff, "%i", mpp->pgfailback); + else + return snprint_progress(buff, mpp->failback_tick, + mpp->pgfailback); diff --git a/SOURCES/0116-multipathd-Stop-double-counting-map-failures-for-no_.patch b/SOURCES/0116-multipathd-Stop-double-counting-map-failures-for-no_.patch new file mode 100644 index 0000000..e08dca4 --- /dev/null +++ b/SOURCES/0116-multipathd-Stop-double-counting-map-failures-for-no_.patch @@ -0,0 +1,34 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 9 May 2024 12:58:11 -0400 +Subject: [PATCH] multipathd: Stop double counting map failures for + no_path_retry > 0 + +If no_path_retry was greater than 0, multipathd was counting a map +failure when recovery mode was entered, and again when queueing was +disabled. The first one is incorrect, since the map is still queueing. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/structs_vec.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c +index 56915e96..b8e304e0 100644 +--- a/libmultipath/structs_vec.c ++++ b/libmultipath/structs_vec.c +@@ -781,10 +781,13 @@ int verify_paths(struct multipath *mpp) + void update_queue_mode_del_path(struct multipath *mpp) + { + int active = count_active_paths(mpp); ++ bool is_queueing = mpp->features && ++ strstr(mpp->features, "queue_if_no_path"); + + if (active == 0) { + enter_recovery_mode(mpp); +- if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE) ++ if (mpp->no_path_retry == NO_PATH_RETRY_FAIL || ++ (mpp->no_path_retry == NO_PATH_RETRY_UNDEF && !is_queueing)) + mpp->stat_map_failures++; + } + condlog(2, "%s: remaining active paths: %d", mpp->alias, active); diff --git a/SOURCES/0117-multipath-tools-man-pages-add-missing-multipathd-com.patch b/SOURCES/0117-multipath-tools-man-pages-add-missing-multipathd-com.patch new file mode 100644 index 0000000..2d7729b --- /dev/null +++ b/SOURCES/0117-multipath-tools-man-pages-add-missing-multipathd-com.patch @@ -0,0 +1,102 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 9 May 2024 17:15:34 -0400 +Subject: [PATCH] multipath-tools man pages: add missing multipathd commands + +Also, the description for "del map $map" was incorrect. + +Signed-off-by: Benjamin Marzinski +--- + multipathd/multipathd.8 | 42 ++++++++++++++++++++++++++++++++++++----- + 1 file changed, 37 insertions(+), 5 deletions(-) + +diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8 +index 8bd47a80..40a8dc6d 100644 +--- a/multipathd/multipathd.8 ++++ b/multipathd/multipathd.8 +@@ -100,18 +100,24 @@ The following commands can be used in interactive mode: + Show the paths that multipathd is monitoring, and their state. + . + .TP +-.B list|show paths format $format ++.B list|show paths [raw] format $format + Show the paths that multipathd is monitoring, using a format string with path +-format wildcards. ++format wildcards. Adding \fIraw\fR will remove the headers and alignment ++padding from the ouput. ++. ++.TP ++.B list|show path $path ++Show whether path $path is offline or running. + . + .TP + .B list|show maps|multipaths + Show the multipath devices that the multipathd is monitoring. + . + .TP +-.B list|show maps|multipaths format $format ++.B list|show maps|multipaths [raw] format $format + Show the status of all multipath devices that the multipathd is monitoring, +-using a format string with multipath format wildcards. ++using a format string with multipath format wildcards. Adding \fIraw\fR will ++remove the headers and alignment padding from the output. + . + .TP + .B list|show maps|multipaths status +@@ -124,6 +130,10 @@ Show some statistics of all multipath devices that the multipathd is monitoring. + .TP + .B list|show maps|multipaths topology + Show the current multipath topology. Same as '\fImultipath \-ll\fR'. ++.TP ++. ++.B list|show maps|multipaths json ++Show information about all multipath devices in JSON format. + . + .TP + .B list|show topology +@@ -135,6 +145,16 @@ Show topology of a single multipath device specified by $map, for example + 36005076303ffc56200000000000010aa. This map could be obtained from '\fIlist maps\fR'. + . + .TP ++.B list|show map|multipath $map [raw] format $format. ++Show the status of multipath device $map, using a format string with multipath ++format wildcards. Adding \fIraw\fR will remove the headers and alignment ++padding from the output. ++. ++.TP ++.B list|show map|multipath $map json ++Show information about multipath device $map in JSON format. ++. ++.TP + .B list|show wildcards + Show the format wildcards used in interactive commands taking $format. + . +@@ -168,6 +188,14 @@ paths, and whether multipathd is currently handling a uevent. + Show the current state of the multipathd daemon. + . + .TP ++.B reset maps|multipaths stats ++Reset the statistics of all multipath devices. ++. ++.TP ++.B reset map|multipath $map stats ++Reset the statistics of multipath device $map. ++. ++.TP + .B add path $path + Add a path to the list of monitored paths. $path is as listed in /sys/block (e.g. sda). + . +@@ -183,8 +211,12 @@ for the multipath device (e.g. mpath1) or the uid of the multipath device + (e.g. 36005076303ffc56200000000000010aa). + . + .TP ++.B remove|del maps|multipaths ++Remove all multipath devices. ++. ++.TP + .B remove|del map|multipath $map +-Stop monitoring a multipath device. ++Remove the multipath device $map. + . + .TP + .B resize map|multipath $map diff --git a/SOURCES/0118-libmultipath-change-the-vend-prod-rev-printing.patch b/SOURCES/0118-libmultipath-change-the-vend-prod-rev-printing.patch new file mode 100644 index 0000000..6ad6b37 --- /dev/null +++ b/SOURCES/0118-libmultipath-change-the-vend-prod-rev-printing.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 10 May 2024 15:36:10 -0400 +Subject: [PATCH] libmultipath: change the vend/prod/rev printing + +The %s multipath and path wildcards both say they print the device +vend/prod/rev string, but neither of them do. The multipath wildcards +already provide a way to print the revision string and the %s wildcard +is used in the multipath -l output, so leave the wildcard output alone, +and change the description to only mention the vendor and product. There +is no other way to print the revision by path, and the path %s wildcard +is only used in the verbose multipath output, so make it actually print +the revision. Also check for unset strings, and print "##" instead of +nothing. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/print.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/libmultipath/print.c b/libmultipath/print.c +index 535e0271..4552fd43 100644 +--- a/libmultipath/print.c ++++ b/libmultipath/print.c +@@ -309,7 +309,7 @@ snprint_multipath_uuid (struct strbuf *buff, const struct multipath * mpp) + } + + static int +-snprint_multipath_vpr (struct strbuf *buff, const struct multipath * mpp) ++snprint_multipath_vp (struct strbuf *buff, const struct multipath * mpp) + { + struct pathgroup * pgp; + struct path * pp; +@@ -511,7 +511,10 @@ snprint_dm_path_state (struct strbuf *buff, const struct path * pp) + static int + snprint_vpr (struct strbuf *buff, const struct path * pp) + { +- return print_strbuf(buff, "%s,%s", pp->vendor_id, pp->product_id); ++ return print_strbuf(buff, "%s,%s,%s", ++ strlen(pp->vendor_id) ? pp->vendor_id : "##", ++ strlen(pp->product_id) ? pp->product_id : "##", ++ strlen(pp->rev) ? pp->rev : "##"); + } + + static int +@@ -743,7 +746,7 @@ struct multipath_data mpd[] = { + {'2', "map_loads", 0, snprint_map_loads}, + {'3', "total_q_time", 0, snprint_total_q_time}, + {'4', "q_timeouts", 0, snprint_q_timeouts}, +- {'s', "vend/prod/rev", 0, snprint_multipath_vpr}, ++ {'s', "vend/prod", 0, snprint_multipath_vp}, + {'v', "vend", 0, snprint_multipath_vend}, + {'p', "prod", 0, snprint_multipath_prod}, + {'e', "rev", 0, snprint_multipath_rev}, diff --git a/SOURCES/0119-multipath-tools-man-pages-Add-format-wildcard-descri.patch b/SOURCES/0119-multipath-tools-man-pages-Add-format-wildcard-descri.patch new file mode 100644 index 0000000..61395f4 --- /dev/null +++ b/SOURCES/0119-multipath-tools-man-pages-Add-format-wildcard-descri.patch @@ -0,0 +1,243 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 10 May 2024 20:53:33 -0400 +Subject: [PATCH] multipath-tools man pages: Add format wildcard descriptions + +Suggested-by: Nitin Yewale +Signed-off-by: Benjamin Marzinski +--- + multipathd/multipathd.8 | 193 +++++++++++++++++++++++++++++++++++++++- + 1 file changed, 189 insertions(+), 4 deletions(-) + +diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8 +index 40a8dc6d..d834f89e 100644 +--- a/multipathd/multipathd.8 ++++ b/multipathd/multipathd.8 +@@ -103,7 +103,7 @@ Show the paths that multipathd is monitoring, and their state. + .B list|show paths [raw] format $format + Show the paths that multipathd is monitoring, using a format string with path + format wildcards. Adding \fIraw\fR will remove the headers and alignment +-padding from the ouput. ++padding from the output. See "Path format wildcards" below. + . + .TP + .B list|show path $path +@@ -117,7 +117,8 @@ Show the multipath devices that the multipathd is monitoring. + .B list|show maps|multipaths [raw] format $format + Show the status of all multipath devices that the multipathd is monitoring, + using a format string with multipath format wildcards. Adding \fIraw\fR will +-remove the headers and alignment padding from the output. ++remove the headers and alignment padding from the output. See "Multipath ++format wildcards" below. + . + .TP + .B list|show maps|multipaths status +@@ -148,7 +149,7 @@ Show topology of a single multipath device specified by $map, for example + .B list|show map|multipath $map [raw] format $format. + Show the status of multipath device $map, using a format string with multipath + format wildcards. Adding \fIraw\fR will remove the headers and alignment +-padding from the output. ++padding from the output. See "Multipath format wildcards" below. + . + .TP + .B list|show map|multipath $map json +@@ -156,7 +157,8 @@ Show information about multipath device $map in JSON format. + . + .TP + .B list|show wildcards +-Show the format wildcards used in interactive commands taking $format. ++Show the format wildcards used in interactive commands taking $format. See ++"Format Wildcards" below. + . + .TP + .B list|show config +@@ -339,6 +341,189 @@ Stop multipathd. + . + . + .\" ---------------------------------------------------------------------------- ++.SH "Format Wildcards" ++.\" ---------------------------------------------------------------------------- ++. ++Multipathd commands that take a $format option require a format string. This ++string controls how a device is printed and should include format wildcards. ++When the devices are printed, these wildcards will be replaced by the ++appropriate device information. The following wildcards are supported. ++.TP ++.B Multipath format wildcards ++.RS ++.TP 12 ++.B %n ++The device name. ++.TP ++.B %w ++The device WWID (uuid). ++.TP ++.B %d ++The device sysfs name (dm-). ++.TP ++.B %F ++The device \fBfailback\fR setting. For deferred failbacks, it will either ++print the configured time if a deferred failback is not in progress, or ++it will show the current progress of a deferred failback. ++.TP ++.B %Q ++The device \fBno_path_retry\fR setting. If no_path_retry is set to a ++number of retires, it will either print the configured number of checker ++retries if the device is not in recovery mode, the number of seconds until ++queueing is disabled if the device is queueing in recovery mode, or \fIoff\fR ++if the device has disabled queueing. ++.TP ++.B %N ++The number of active paths for the device. ++.TP ++.B %r ++The device write-protect setting, either \fIro\fR or \fIrw\fR. ++.TP ++.B %t ++The device-mapper state of the device, either \fIsuspend\fR or \fIactive\fR. ++.TP ++.B %S ++The device size. ++.TP ++.B %f ++The device table features string. ++.TP ++.B %x ++The number of times the device has entered a state where it will fail IO. ++This is an alias for the \fB%4\fR wildcard. ++This value can be reset with the '\fIreset map $map stats\fR' command. ++.TP ++.B %h ++The device table hardware handler string. ++.TP ++.B %A ++The last action multipathd took on the device. This wildcard is for debugging ++use, as understanding its meaning requires looking at the code. ++.TP ++.B %0 ++The number of times a path in the device has failed. ++This value can be reset with the '\fIreset map $map stats\fR' command. ++.TP ++.B %1 ++The number of times multipathd has initiated a pathgroup switch for the device. ++This value can be reset with the '\fIreset map $map stats\fR' command. ++.TP ++.B %2 ++The number of times multipathd has loaded a new table for the device. ++This value can be reset with the '\fIreset map $map stats\fR' command. ++.TP ++.B %3 ++The approximate number of seconds that multipathd has spent queueing with ++no usable paths. This value can be reset with the '\fIreset map $map stats\fR' ++command. ++.TP ++.B %4 ++The number of times the device has entered a state where it will fail IO. ++This is an alias for the \fB%x\fR wildcard. ++This value can be reset with the '\fIreset map $map stats\fR' command. ++.TP ++.B %s ++The vendor/product string for the device. ++.TP ++.B %v ++The array vendor string for the device. ++.TP ++.B %p ++The array product string for the device. ++.TP ++.B %e ++The array firmware revision string for the device. ++.TP ++.B %G ++The foreign library used for the device, or \fB--\fR for native device-mapper ++multipath devices. ++.TP ++.B %g ++Data from vendor specific vpd pages for the device, if any. ++.RE ++. ++. ++.TP ++.B Path format wildcards ++.RS ++.TP 12 ++.B %w ++The device WWID (uuid). ++.TP ++.B %i ++The device Host:Channel:Id:Lun ++.TP ++.B %d ++The device sysfs name. ++.TP ++.B %D ++The device major:minor ++.TP ++.B %t ++The device-mapper state of the device, either \fIactive\fR or \fIfailed\fR. ++.TP ++.B %o ++Whether the device is \fIoffline\fR or \fIrunning\fR. ++.TP ++.B %T ++The multipathd path checker state of the device. ++.TP ++.B %s ++The vendor/product/revision string for the device. ++.TP ++.B %c ++The device's path checker name. ++.TP ++.B %C ++The progress towards the next path checker run on the device. ++.TP ++.B %p ++The device priority. ++.TP ++.B %S ++The device size. ++.TP ++.B %z ++The device serial number. ++.TP ++.B %M ++The device marginal state, either \fImarginal\fR or \fInormal\fR. ++.TP ++.B %m ++The multipath device that this device is a path of. ++.TP ++.B %N ++The host World Wide Node Name (WWNN) of the device. ++.TP ++.B %n ++The target World Wide Node Name (WWNN) of the device. ++.TP ++.B %R ++The host World Wide Port Name (WWPN) of the device. ++.TP ++.B %r ++The target World Wide Port Name (WWPN) of the device. ++.TP ++.B %a ++The host adapter name for the device (only SCSI devices). ++.TP ++.B %G ++The foreign library used for the device, or \fB--\fR for paths of native ++device-mapper multipath devices. ++.TP ++.B %g ++Data from vendor specific vpd pages for the device, if any. ++.TP ++.B %0 ++The number of times this device has failed. ++.TP ++.B %P ++The device protocol. This output can be used for \fIprotocol\fR blacklist ++entries. ++.RE ++. ++. ++.\" ---------------------------------------------------------------------------- + .SH "SYSTEMD INTEGRATION" + .\" ---------------------------------------------------------------------------- + . diff --git a/SOURCES/0120-multipath-tools-fix-multipath-ll-bug-for-Native-NVME.patch b/SOURCES/0120-multipath-tools-fix-multipath-ll-bug-for-Native-NVME.patch new file mode 100644 index 0000000..49d409a --- /dev/null +++ b/SOURCES/0120-multipath-tools-fix-multipath-ll-bug-for-Native-NVME.patch @@ -0,0 +1,103 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: chengjike +Date: Fri, 8 Oct 2021 20:24:49 +0800 +Subject: [PATCH] multipath-tools: fix "multipath -ll" bug for Native NVME + Multipath devices + +After "Native NVME Multipath" is configured, +the content displayed is incorrect when you run "multipath -ll" command. +Each NVME devices have the same path name. For example: + +[root@localhost home]# multipath -ll +eui.710032e8fb22a86c24a52c1000000db8 [nvme]:nvme1n1 NVMe,Huawei-XSG1,1000001 +size=10485760 features='n/a' hwhandler='ANA' wp=rw +|-+- policy='n/a' prio=50 status=optimized +| `- 1:4:1 nvme1c4n1 0:0 n/a optimized live +`-+- policy='n/a' prio=50 status=optimized + `- 1:9:1 nvme1c9n1 0:0 n/a optimized live +eui.710032e8fb22a86b24a52c7c00000db7 [nvme]:nvme1n2 NVMe,Huawei-XSG1,1000001 +size=10485760 features='n/a' hwhandler='ANA' wp=rw +|-+- policy='n/a' prio=50 status=optimized +| `- 1:4:1 nvme1c4n1 0:0 n/a optimized live +`-+- policy='n/a' prio=50 status=optimized + `- 1:9:1 nvme1c9n1 0:0 n/a optimized live +[root@localhost home]# + +The logical paths of "nvme1n1" and "nvme1n2" are both "nvme1c4n1" and "nvme1c9n1". +So when multipath-tools aggregates disks, use "nvme_ns_head->instance" for matching. +such as ,Use "b" in "nvmeanb" string to match "z" in "nvmexcynz"(a,b,x,y,z can be any number), +and if "b" and "z" are the same, they are related. + +Signed-off-by: chengjike +Reviewed-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + libmultipath/foreign/nvme.c | 26 ++++++++++++++++++++------ + 1 file changed, 20 insertions(+), 6 deletions(-) + +diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c +index 23355ca5..76b57283 100644 +--- a/libmultipath/foreign/nvme.c ++++ b/libmultipath/foreign/nvme.c +@@ -530,14 +530,18 @@ static int _dirent_controller(const struct dirent *di) + + /* Find the block device for a given nvme controller */ + struct udev_device *get_ctrl_blkdev(const struct context *ctx, +- struct udev_device *ctrl) ++ struct udev_device *ctrl, const char *ctrl_name) + { ++ int ctrl_num, ns_num; + 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) ++ if (enm == NULL || ctrl_name == NULL) ++ return NULL; ++ ++ if (sscanf(ctrl_name, "nvme%dn%d", &ctrl_num, &ns_num) != 2) + return NULL; + + pthread_cleanup_push(_udev_enumerate_unref, enm); +@@ -555,6 +559,8 @@ struct udev_device *get_ctrl_blkdev(const struct context *ctx, + item != NULL; + item = udev_list_entry_get_next(item)) { + struct udev_device *tmp; ++ const char *name = NULL ; ++ int m, n, l; + + tmp = udev_device_new_from_syspath(ctx->udev, + udev_list_entry_get_name(item)); +@@ -562,11 +568,19 @@ struct udev_device *get_ctrl_blkdev(const struct context *ctx, + continue; + + devtype = udev_device_get_devtype(tmp); +- if (devtype && !strcmp(devtype, "disk")) { ++ if (devtype == NULL || strcmp(devtype, "disk")) { ++ udev_device_unref(tmp); ++ continue; ++ } ++ ++ name = udev_device_get_sysname(tmp); ++ if (name != NULL && ++ sscanf(name, "nvme%dc%dn%d", &m, &n, &l) == 3 && ++ l == ns_num) { + blkdev = tmp; + break; +- } else +- udev_device_unref(tmp); ++ } ++ udev_device_unref(tmp); + } + + if (blkdev == NULL) +@@ -679,7 +693,7 @@ static void _find_controllers(struct context *ctx, struct nvme_map *map) + } + + pthread_cleanup_push(_udev_device_unref, ctrl); +- udev = get_ctrl_blkdev(ctx, ctrl); ++ udev = get_ctrl_blkdev(ctx, ctrl, udev_device_get_sysname(map->udev)); + /* + * We give up the reference to the nvme device here and get + * it back from the child below. diff --git a/SOURCES/0121-multipathd-set-reply-length-to-zero-for-NULL-replies.patch b/SOURCES/0121-multipathd-set-reply-length-to-zero-for-NULL-replies.patch new file mode 100644 index 0000000..d304e90 --- /dev/null +++ b/SOURCES/0121-multipathd-set-reply-length-to-zero-for-NULL-replies.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 24 Jul 2024 16:38:52 -0400 +Subject: [PATCH] multipathd: set reply length to zero for NULL replies + +The multipathd cli handlers add one to the reply length to account for +NULL byte, but if the reply is NULL, there is no NULL by to account for, +and len should be 0, so that mutipathd replies to clients with a +default reply. + +Signed-off-by: Benjamin Marzinski +--- + multipathd/cli.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/multipathd/cli.c b/multipathd/cli.c +index eb2e8c1d..03ad0d64 100644 +--- a/multipathd/cli.c ++++ b/multipathd/cli.c +@@ -496,6 +496,9 @@ parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout ) + r = h->fn(cmdvec, reply, len, data); + free_keys(cmdvec); + ++ if (*reply == NULL) ++ *len = 0; ++ + return r; + } + diff --git a/SPECS/device-mapper-multipath.spec b/SPECS/device-mapper-multipath.spec index f7ba6e1..d0d6117 100644 --- a/SPECS/device-mapper-multipath.spec +++ b/SPECS/device-mapper-multipath.spec @@ -1,6 +1,6 @@ Name: device-mapper-multipath Version: 0.8.7 -Release: 27%{?dist} +Release: 32%{?dist} Summary: Tools to manage multipath devices using device-mapper License: GPLv2 URL: http://christophe.varoqui.free.fr/ @@ -117,6 +117,20 @@ Patch0104: 0104-multipathd-disable-queueing-when-removing-unknown-ma.patch Patch0105: 0105-multipathd-fix-null-pointer-dereference-in-uev_updat.patch Patch0106: 0106-multipathd-fix-auto-resize-configuration.patch Patch0107: 0107-libmultipath-fix-displaying-auto_resize-config-setti.patch +Patch0108: 0108-libmultipath-actually-truncate-too-large-vpd-page.patch +Patch0109: 0109-kpartx-fix-theoretical-overflow-in-loop-device-name.patch +Patch0110: 0110-libmultipath-keep-track-of-queueing-state-in-feature.patch +Patch0111: 0111-libmultipath-export-partmap_in_use.patch +Patch0112: 0112-libmultipath-change-flush_on_last_del-to-fix-a-multi.patch +Patch0113: 0113-libmultipath-pad-dev_loss_tmo-to-avoid-race-with-no_.patch +Patch0114: 0114-libmultipath-remove-pathgroup-wildcard-options.patch +Patch0115: 0115-libmultipath-print-all-values-in-snprint_failback.patch +Patch0116: 0116-multipathd-Stop-double-counting-map-failures-for-no_.patch +Patch0117: 0117-multipath-tools-man-pages-add-missing-multipathd-com.patch +Patch0118: 0118-libmultipath-change-the-vend-prod-rev-printing.patch +Patch0119: 0119-multipath-tools-man-pages-Add-format-wildcard-descri.patch +Patch0120: 0120-multipath-tools-fix-multipath-ll-bug-for-Native-NVME.patch +Patch0121: 0121-multipathd-set-reply-length-to-zero-for-NULL-replies.patch # runtime @@ -320,6 +334,48 @@ fi %{_pkgconfdir}/libdmmp.pc %changelog +* Mon Aug 5 2024 Benjamin Marzinski - 0.8.7-32 +- Modify bindings, find_multipaths, and user_friendly_names tests + * Fixes RHEL-28068 & RHEL-4459 +- Resolves: RHEL-28068 +- Resolves: RHEL-44569 + +* Tue Jul 30 2024 Benjamin Marzinski - 0.8.7-31 +- Modify multiple tests especially medium_error_scsi_debug and squelch_scsi_id + * Fixes RHEL-28068 & RHEL-4459 +- Resolves: RHEL-28068 +- Resolves: RHEL-44569 + +* Tue Jul 30 2024 Benjamin Marzinski - 0.8.7-30 +- Add 0120-multipath-tools-fix-multipath-ll-bug-for-Native-NVME.patch + * Fixes RHEL-28068 +- Add 0121-multipathd-set-reply-length-to-zero-for-NULL-replies.patch + * Fixes RHEL-44569 +- Resolves: RHEL-28068 +- Resolves: RHEL-44569 + +* Tue May 21 2024 Benjamin Marzinski - 0.8.7-29 +- Add 0110-libmultipath-keep-track-of-queueing-state-in-feature.patch +- Add 0111-libmultipath-export-partmap_in_use.patch +- Add 0112-libmultipath-change-flush_on_last_del-to-fix-a-multi.patch +- Add 0113-libmultipath-pad-dev_loss_tmo-to-avoid-race-with-no_.patch + * Fixes RHEL-30272 +- Add 0114-libmultipath-remove-pathgroup-wildcard-options.patch +- Add 0115-libmultipath-print-all-values-in-snprint_failback.patch +- Add 0116-multipathd-Stop-double-counting-map-failures-for-no_.patch +- Add 0117-multipath-tools-man-pages-add-missing-multipathd-com.patch +- Add 0118-libmultipath-change-the-vend-prod-rev-printing.patch +- Add 0119-multipath-tools-man-pages-Add-format-wildcard-descri.patch + * Fixes RHEL-8304 +- Resolves: RHEL-8304 +- Resolves: RHEL-30272 + +* Tue Apr 9 2024 Benjamin Marzinski - 0.8.7-28 +- Add 0108-libmultipath-actually-truncate-too-large-vpd-page.patch +- Add 0109-kpartx-fix-theoretical-overflow-in-loop-device-name.patch + * Fixes RHEL-31793 ("RHEL SAST Automation: address (selected) true positives") +- Resolves: RHEL-31793 + * Fri Jan 26 2024 Benjamin Marzinski - 0.8.7-27 - Add 0105-multipathd-fix-null-pointer-dereference-in-uev_updat.patch - Add 0106-multipathd-fix-auto-resize-configuration.patch