From c90e5b87de8e71d2146a8a06bb8dc5fb585194de Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Nov 08 2022 06:55:46 +0000 Subject: import device-mapper-multipath-0.8.4-28.el8 --- 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/0091-multipath-tools-update-mpp-force_readonly-in-ev_add_.patch b/SOURCES/0091-multipath-tools-update-mpp-force_readonly-in-ev_add_.patch deleted file mode 100644 index c432bbc..0000000 --- a/SOURCES/0091-multipath-tools-update-mpp-force_readonly-in-ev_add_.patch +++ /dev/null @@ -1,78 +0,0 @@ -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/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/0092-multipathd-ignore-duplicated-multipathd-command-keys.patch b/SOURCES/0092-multipathd-ignore-duplicated-multipathd-command-keys.patch deleted file mode 100644 index 8053b04..0000000 --- a/SOURCES/0092-multipathd-ignore-duplicated-multipathd-command-keys.patch +++ /dev/null @@ -1,153 +0,0 @@ -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 bdc9fb10..a411680e 100644 ---- a/multipathd/cli.c -+++ b/multipathd/cli.c -@@ -336,9 +336,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 eeded52b..6106204d 100644 ---- a/multipathd/main.c -+++ b/multipathd/main.c -@@ -1625,62 +1625,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/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/SPECS/device-mapper-multipath.spec b/SPECS/device-mapper-multipath.spec index 9493d5e..b76d258 100644 --- a/SPECS/device-mapper-multipath.spec +++ b/SPECS/device-mapper-multipath.spec @@ -1,7 +1,7 @@ Summary: Tools to manage multipath devices using device-mapper Name: device-mapper-multipath Version: 0.8.4 -Release: 22%{?dist}.2 +Release: 28%{?dist} License: GPLv2 Group: System Environment/Base URL: http://christophe.varoqui.free.fr/ @@ -101,14 +101,33 @@ 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-update-mpp-force_readonly-in-ev_add_.patch -Patch00092: 0092-multipathd-ignore-duplicated-multipathd-command-keys.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 # 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 @@ -116,7 +135,7 @@ Requires(postun): systemd-units # build/setup BuildRequires: libaio-devel, device-mapper-devel >= 1.02.89 BuildRequires: libselinux-devel, libsepol-devel -BuildRequires: readline-devel, ncurses-devel +BuildRequires: libedit-devel, ncurses-devel BuildRequires: systemd-units, systemd-devel BuildRequires: json-c-devel, perl-interpreter, pkgconfig, gcc BuildRequires: userspace-rcu-devel @@ -304,13 +323,49 @@ fi %{_pkgconfdir}/libdmmp.pc %changelog -* Thu Oct 13 2022 Benjamin Marzinski 0.8.4-22.2 -- Add 0092-multipathd-ignore-duplicated-multipathd-command-keys.patch -- Resolves: bz #2133994 - -* Tue Jun 7 2022 Benjamin Marzinski 0.8.4-22.1 -- Add 0091-multipath-tools-update-mpp-force_readonly-in-ev_add_.patch -- Resolves: bz #2094472 +* 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