diff --git a/SOURCES/0091-multipathd-Added-support-to-handle-FPIN-Li-events-fo.patch b/SOURCES/0091-multipathd-Added-support-to-handle-FPIN-Li-events-fo.patch new file mode 100644 index 0000000..4d2aab3 --- /dev/null +++ b/SOURCES/0091-multipathd-Added-support-to-handle-FPIN-Li-events-fo.patch @@ -0,0 +1,300 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Muneendra +Date: Wed, 20 Sep 2023 20:41:15 -0700 +Subject: [PATCH] multipathd: Added support to handle FPIN-Li events for + FC-NVMe + + This patch adds the support to handle FPIN-Li for FC-NVMe. + On receiving the FPIN-Li events this patch moves the devices paths + which are affected due to link integrity to marginal path groups. + The paths which are set to marginal path group will be unset + on receiving the RSCN events + +(mwilck: minor compile fix for 32-bit architectures) + +Signed-off-by: Muneendra +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + multipathd/fpin_handlers.c | 206 +++++++++++++++++++++++++++---------- + 1 file changed, 151 insertions(+), 55 deletions(-) + +diff --git a/multipathd/fpin_handlers.c b/multipathd/fpin_handlers.c +index 571796e7..d5f7594d 100644 +--- a/multipathd/fpin_handlers.c ++++ b/multipathd/fpin_handlers.c +@@ -59,18 +59,15 @@ static void _udev_device_unref(void *p) + + + /*set/unset the path state to marginal*/ +-static int fpin_set_pathstate(struct path *pp, bool set) ++static void 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); ++ condlog(3, "%s: %s marginal path %s (fpin)", ++ pp->mpp ? pp->mpp->alias : "orphan", action, pp->dev_t); + pp->marginal = set; +- pp->mpp->fpin_must_reload = true; +- return 0; ++ if (pp->mpp) ++ pp->mpp->fpin_must_reload = true; + } + + /* This will unset marginal state of a device*/ +@@ -81,14 +78,14 @@ static void fpin_path_unsetmarginal(char *devname, struct vectors *vecs) + pp = find_path_by_dev(vecs->pathvec, devname); + if (!pp) + pp = find_path_by_devt(vecs->pathvec, devname); +- +- fpin_set_pathstate(pp, false); ++ if (pp) ++ fpin_set_pathstate(pp, false); + } + + /*This will set the marginal state of a device*/ +-static int fpin_path_setmarginal(struct path *pp) ++static void fpin_path_setmarginal(struct path *pp) + { +- return fpin_set_pathstate(pp, true); ++ fpin_set_pathstate(pp, true); + } + + /* Unsets all the devices in the list from marginal state */ +@@ -175,8 +172,8 @@ static void fpin_set_rport_marginal(struct udev_device *rport_dev) + "Marginal", strlen("Marginal")); + } + +-/*Add the marginal devices info into the list*/ +-static void ++/*Add the marginal devices info into the list and return 0 on success*/ ++static int + fpin_add_marginal_dev_info(uint32_t host_num, char *devname) + { + struct marginal_dev_list *newdev = NULL; +@@ -191,65 +188,160 @@ fpin_add_marginal_dev_info(uint32_t host_num, char *devname) + list_add_tail(&(newdev->node), + &fpin_li_marginal_dev_list_head); + pthread_mutex_unlock(&fpin_li_marginal_dev_mutex); +- } ++ } else ++ return -ENOMEM; ++ return 0; + } + + /* +- * 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 ++ * This function compares Transport Address Controller Port pn, ++ * Host Transport Address Controller Port pn with the els wwpn ,attached_wwpn ++ * and return 1 (match) or 0 (no match) or a negative error code ++ */ ++static int extract_nvme_addresses_chk_path_pwwn(const char *address, ++ uint64_t els_wwpn, uint64_t els_attached_wwpn) ++ ++{ ++ uint64_t traddr; ++ uint64_t host_traddr; ++ ++ /* ++ * Find the position of "traddr=" and "host_traddr=" ++ * and the address will be in the below format ++ * "traddr=nn-0x200400110dff9400:pn-0x200400110dff9400, ++ * host_traddr=nn-0x200400110dff9400:pn-0x200400110dff9400" ++ */ ++ const char *traddr_start = strstr(address, "traddr="); ++ const char *host_traddr_start = strstr(address, "host_traddr="); ++ ++ if (!traddr_start || !host_traddr_start) ++ return -EINVAL; ++ ++ /* Extract traddr pn */ ++ if (sscanf(traddr_start, "traddr=nn-%*[^:]:pn-%" SCNx64, &traddr) != 1) ++ return -EINVAL; ++ ++ /* Extract host_traddr pn*/ ++ if (sscanf(host_traddr_start, "host_traddr=nn-%*[^:]:pn-%" SCNx64, ++ &host_traddr) != 1) ++ return -EINVAL; ++ condlog(4, "traddr 0x%" PRIx64 " hosttraddr 0x%" PRIx64 " els_wwpn 0x%" ++ PRIx64" els_host_traddr 0x%" PRIx64, ++ traddr, host_traddr, ++ els_wwpn, els_attached_wwpn); ++ if ((host_traddr == els_attached_wwpn) && (traddr == els_wwpn)) ++ return 1; ++ return 0; ++} ++ ++/* ++ * This function check that the Transport Address Controller Port pn, ++ * Host Transport Address Controller Port pn associated with the path matches ++ * with the els wwpn ,attached_wwpn and sets the path state to + * Marginal + */ +-static int fpin_chk_wwn_setpath_marginal(uint16_t host_num, struct vectors *vecs, ++static void fpin_check_set_nvme_path_marginal(uint16_t host_num, struct path *pp, ++ uint64_t els_wwpn, uint64_t attached_wwpn) ++{ ++ struct udev_device *ctl = NULL; ++ const char *address = NULL; ++ int ret = 0; ++ ++ ctl = udev_device_get_parent_with_subsystem_devtype(pp->udev, "nvme", NULL); ++ if (ctl == NULL) { ++ condlog(2, "%s: No parent device for ", pp->dev); ++ return; ++ } ++ address = udev_device_get_sysattr_value(ctl, "address"); ++ if (!address) { ++ condlog(2, "%s: unable to get the address ", pp->dev); ++ return; ++ } ++ condlog(4, "\n address %s: dev :%s\n", address, pp->dev); ++ ret = extract_nvme_addresses_chk_path_pwwn(address, els_wwpn, attached_wwpn); ++ if (ret <= 0) ++ return; ++ ret = fpin_add_marginal_dev_info(host_num, pp->dev); ++ if (ret < 0) ++ return; ++ fpin_path_setmarginal(pp); ++} ++ ++/* ++ * This function check 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 void fpin_check_set_scsi_path_marginal(uint16_t host_num, struct path *pp, + 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; ++ 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); ++ return; ++ } ++ pthread_cleanup_push(_udev_device_unref, rport_dev); ++ value = udev_device_get_sysattr_value(rport_dev, "port_name"); ++ if (!value) ++ goto unref; ++ ++ wwpn = strtol(value, NULL, 16); ++ /* ++ * If the port wwpn matches sets the path and port state ++ * to marginal ++ */ ++ if (wwpn == els_wwpn) { ++ ret = fpin_add_marginal_dev_info(host_num, pp->dev); ++ if (ret < 0) ++ goto unref; ++ fpin_path_setmarginal(pp); ++ fpin_set_rport_marginal(rport_dev); ++ } ++unref: ++ pthread_cleanup_pop(1); ++ return; ++ ++} ++ ++/* ++ * This function goes through the vecs->pathvec, and for ++ * each path, it checks and sets the path state to marginal ++ * if the path's associated port wwpn ,hostnum matches with ++ * els wwnpn ,attached_wwpn ++ */ ++static int fpin_chk_wwn_setpath_marginal(uint16_t host_num, struct vectors *vecs, ++ uint64_t els_wwpn, uint64_t attached_wwpn) ++{ ++ struct path *pp; ++ struct multipath *mpp; ++ int i, k; ++ 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->bus != SYSFS_BUS_SCSI || pp->sg_id.proto_id != SCSI_PROTOCOL_FCP || host_num != pp->sg_id.host_no) ++ if (!pp->mpp) + 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); ++ /*checks if the bus type is nvme and the protocol is FC-NVMe*/ ++ if ((pp->bus == SYSFS_BUS_NVME) && (pp->sg_id.proto_id == NVME_PROTOCOL_FC)) { ++ fpin_check_set_nvme_path_marginal(host_num, pp, els_wwpn, attached_wwpn); ++ } else if ((pp->bus == SYSFS_BUS_SCSI) && ++ (pp->sg_id.proto_id == SCSI_PROTOCOL_FCP) && ++ (host_num == pp->sg_id.host_no)) { ++ /* Checks the host number and also for the SCSI FCP */ ++ fpin_check_set_scsi_path_marginal(host_num, pp, els_wwpn); + } +-unref: +- pthread_cleanup_pop(1); + } + /* walk backwards because reload_and_sync_map() can remove mpp */ + vector_foreach_slot_backwards(vecs->mpvec, mpp, i) { +@@ -278,14 +370,18 @@ fpin_parse_li_els_setpath_marginal(uint16_t host_num, struct fc_tlv_desc *tlv, + struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv; + int count = 0; + int ret = 0; ++ uint64_t attached_wwpn; + + /* Update the wwn to list */ + wwn_count = be32_to_cpu(li_desc->pname_count); +- condlog(4, "Got wwn count as %d\n", wwn_count); ++ attached_wwpn = be64_to_cpu(li_desc->attached_wwpn); ++ condlog(4, "Got wwn count as %d detecting wwn 0x%" PRIx64 ++ " attached_wwpn 0x%" PRIx64 "\n", ++ wwn_count, be64_to_cpu(li_desc->detecting_wwpn), attached_wwpn); + + 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); ++ ret = fpin_chk_wwn_setpath_marginal(host_num, vecs, wwpn, attached_wwpn); + if (ret < 0) + condlog(2, "failed to set the path marginal associated with wwpn: 0x%" PRIx64 "\n", wwpn); + diff --git a/SOURCES/0092-multipath-tools-add-HPE-Alletra-9000-NVMe-to-hardwar.patch b/SOURCES/0092-multipath-tools-add-HPE-Alletra-9000-NVMe-to-hardwar.patch new file mode 100644 index 0000000..8203796 --- /dev/null +++ b/SOURCES/0092-multipath-tools-add-HPE-Alletra-9000-NVMe-to-hardwar.patch @@ -0,0 +1,31 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 26 Oct 2023 13:24:34 -0400 +Subject: [PATCH] multipath-tools: add HPE Alletra 9000 NVMe to hardware table + +Add config to match configuration in 0.9.6 release + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/hwtable.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c +index 22ff1881..78ac7988 100644 +--- a/libmultipath/hwtable.c ++++ b/libmultipath/hwtable.c +@@ -119,6 +119,14 @@ static struct hwentry default_hw[] = { + .dev_loss = MAX_DEV_LOSS_TMO, + .vpd_vendor_id = VPD_VP_HP3PAR, + }, ++ { ++ /* Alletra 9000 NVMe */ ++ .vendor = "NVME", ++ .product = "HPE Alletra", ++ .pgpolicy = GROUP_BY_PRIO, ++ .pgfailback = -FAILBACK_IMMEDIATE, ++ .no_path_retry = NO_PATH_RETRY_QUEUE, ++ }, + { + /* RA8000 / ESA12000 */ + .vendor = "DEC", diff --git a/SOURCES/0093-RH-multipath-add-mpathcleanup-man-page.patch b/SOURCES/0093-RH-multipath-add-mpathcleanup-man-page.patch new file mode 100644 index 0000000..d4fccaa --- /dev/null +++ b/SOURCES/0093-RH-multipath-add-mpathcleanup-man-page.patch @@ -0,0 +1,70 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 3 Nov 2023 11:13:04 -0400 +Subject: [PATCH] RH: multipath: add mpathcleanup man page + +Signed-off-by: Benjamin Marzinski +--- + multipath/Makefile | 3 +++ + multipath/mpathcleanup.8 | 24 ++++++++++++++++++++++++ + 2 files changed, 27 insertions(+) + create mode 100644 multipath/mpathcleanup.8 + +diff --git a/multipath/Makefile b/multipath/Makefile +index 1fc04c8d..cdfa160b 100644 +--- a/multipath/Makefile ++++ b/multipath/Makefile +@@ -19,6 +19,7 @@ $(EXEC): $(OBJS) $(multipathdir)/libmultipath.so $(mpathcmddir)/libmpathcmd.so + $(GZIP) $(EXEC).8 > $(EXEC).8.gz + $(GZIP) $(EXEC).conf.5 > $(EXEC).conf.5.gz + $(GZIP) mpathconf.8 > mpathconf.8.gz ++ $(GZIP) mpathcleanup.8 > mpathcleanup.8.gz + + install: + $(INSTALL_PROGRAM) -d $(DESTDIR)$(bindir) +@@ -35,6 +36,7 @@ install: + $(INSTALL_PROGRAM) -d $(DESTDIR)$(man5dir) + $(INSTALL_PROGRAM) -m 644 $(EXEC).conf.5.gz $(DESTDIR)$(man5dir) + $(INSTALL_PROGRAM) -m 644 mpathconf.8.gz $(DESTDIR)$(man8dir) ++ $(INSTALL_PROGRAM) -m 644 mpathcleanup.8.gz $(DESTDIR)$(man8dir) + + uninstall: + $(RM) $(DESTDIR)$(bindir)/$(EXEC) +@@ -45,6 +47,7 @@ uninstall: + $(RM) $(DESTDIR)$(man8dir)/$(EXEC).8.gz + $(RM) $(DESTDIR)$(man5dir)/$(EXEC).conf.5.gz + $(RM) $(DESTDIR)$(man8dir)/mpathconf.8.gz ++ $(RM) $(DESTDIR)$(man8dir)/mpathcleanup.8.gz + + clean: dep_clean + $(RM) core *.o $(EXEC) *.gz multipath.rules tmpfiles.conf +diff --git a/multipath/mpathcleanup.8 b/multipath/mpathcleanup.8 +new file mode 100644 +index 00000000..184c35c9 +--- /dev/null ++++ b/multipath/mpathcleanup.8 +@@ -0,0 +1,24 @@ ++.TH MPATHCLEANUP 8 "November 2023" "" "Linux Administrator's Manual" ++.SH NAME ++mpathcleanup - A tool to remove a multipath device and its scsi path devices ++.SH SYNOPSIS ++.B mpathcleanup ++[\fB\-h\fR] [\fB\-\-flush\fR] \fBdevice\fR ++.SH DESCRIPTION ++\fBmpathcleanup\fR is a utility that attempts to remove a multipath device and ++its underlying paths. It only works for multipath devices built on top of scsi ++devices. ++.SH OPTIONS ++.TP ++.B \-\-flush ++Disable queueing on the multipath device and flush the path devices before ++removing. ++.TP ++\fB\-h\fR|\fB\-\-help\fR ++Display help text. ++.SH "SEE ALSO" ++.BR multipath.conf (5), ++.BR multipath (8), ++.BR multipathd (8) ++.SH AUTHOR ++Benjamin Marzinski diff --git a/SOURCES/0094-libmultipath-Add-max_retries-config-option.patch b/SOURCES/0094-libmultipath-Add-max_retries-config-option.patch new file mode 100644 index 0000000..9d52675 --- /dev/null +++ b/SOURCES/0094-libmultipath-Add-max_retries-config-option.patch @@ -0,0 +1,182 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 9 Nov 2023 18:46:11 -0500 +Subject: [PATCH] libmultipath: Add max_retries config option + +This option lets multipath set a scsi disk's max_retries sysfs value. +Setting this can be helpful for cases where the path checker succeeds, +but IO commands hang and timeout. By default, the SCSI layer will retry +IOs 5 times. Reducing this value will allow multipath to retry the IO +down another path sooner. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/config.h | 1 + + libmultipath/dict.c | 25 ++++++++++++++++++++++++ + libmultipath/discovery.c | 40 +++++++++++++++++++++++++++++++++++++- + libmultipath/structs.h | 6 ++++++ + multipath/multipath.conf.5 | 14 +++++++++++++ + 5 files changed, 85 insertions(+), 1 deletion(-) + +diff --git a/libmultipath/config.h b/libmultipath/config.h +index c1e18363..b0ee8241 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -162,6 +162,7 @@ struct config { + int fast_io_fail; + unsigned int dev_loss; + int eh_deadline; ++ int max_retries; + int log_checker_err; + int allow_queueing; + int allow_usb_devices; +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index eb2f33a2..0c66c1e1 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -1206,6 +1206,30 @@ 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 ++def_max_retries_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) ++{ ++ char * buff; ++ ++ buff = set_value(strvec); ++ if (!buff) ++ return 1; ++ ++ if (strcmp(buff, "off") == 0) ++ conf->max_retries = MAX_RETRIES_OFF; ++ else if (strcmp(buff, "0") == 0) ++ conf->max_retries = MAX_RETRIES_ZERO; ++ else ++ do_set_int(strvec, &conf->max_retries, 1, 5, file, line_nr, ++ buff); ++ ++ free(buff); ++ return 0; ++} ++ ++declare_def_snprint(max_retries, print_undef_off_zero) ++ + static int + set_pgpolicy(vector strvec, void *ptr, const char *file, int line_nr) + { +@@ -2143,6 +2167,7 @@ init_keywords(vector keywords) + install_keyword("fast_io_fail_tmo", &def_fast_io_fail_handler, &snprint_def_fast_io_fail); + install_keyword("dev_loss_tmo", &def_dev_loss_handler, &snprint_def_dev_loss); + install_keyword("eh_deadline", &def_eh_deadline_handler, &snprint_def_eh_deadline); ++ install_keyword("max_retries", &def_max_retries_handler, &snprint_def_max_retries); + install_keyword("bindings_file", &def_bindings_file_handler, &snprint_def_bindings_file); + install_keyword("wwids_file", &def_wwids_file_handler, &snprint_def_wwids_file); + install_keyword("prkeys_file", &def_prkeys_file_handler, &snprint_def_prkeys_file); +diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c +index a592a54e..adf8bbaa 100644 +--- a/libmultipath/discovery.c ++++ b/libmultipath/discovery.c +@@ -632,6 +632,42 @@ sysfs_set_eh_deadline(struct path *pp) + return (ret <= 0); + } + ++static int ++sysfs_set_max_retries(struct config *conf, struct path *pp) ++{ ++ struct udev_device *parent; ++ char value[16]; ++ STRBUF_ON_STACK(buf); ++ int ret, len; ++ ++ if (conf->max_retries == MAX_RETRIES_UNSET) ++ return 0; ++ ++ if (!pp->udev || pp->sg_id.host_no < 0) ++ return 1; ++ ++ len = sprintf(value, "%d", (conf->max_retries == MAX_RETRIES_OFF)? -1 : ++ (conf->max_retries == MAX_RETRIES_ZERO)? 0 : ++ conf->max_retries); ++ ++ parent = udev_device_get_parent_with_subsystem_devtype(pp->udev, ++ "scsi", "scsi_device"); ++ if (!parent) ++ return 1; ++ ++ if (print_strbuf(&buf, "scsi_disk/%i:%i:%i:%" PRIu64 "/max_retries", ++ pp->sg_id.host_no, pp->sg_id.channel, ++ pp->sg_id.scsi_id, pp->sg_id.lun) < 0) ++ return 1; ++ ++ ret = sysfs_attr_set_value(parent, get_strbuf_str(&buf), value, len); ++ if (len != ret) ++ condlog(3, "%s/%s: failed to set value to %s: %s", ++ udev_device_get_sysname(parent), get_strbuf_str(&buf), ++ value, (ret < 0)? strerror(-ret) : "write underflow"); ++ return (len != ret); ++} ++ + static void + sysfs_set_rport_tmo(struct multipath *mpp, struct path *pp) + { +@@ -862,13 +898,15 @@ sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp) + + if (pp->dev_loss == DEV_LOSS_TMO_UNSET && + pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET && +- pp->eh_deadline == EH_DEADLINE_UNSET) ++ pp->eh_deadline == EH_DEADLINE_UNSET && ++ conf->max_retries == MAX_RETRIES_UNSET) + continue; + + if (pp->bus != SYSFS_BUS_SCSI) + continue; + + sysfs_set_eh_deadline(pp); ++ sysfs_set_max_retries(conf, pp); + + if (pp->dev_loss == DEV_LOSS_TMO_UNSET && + pp->fast_io_fail == MP_FAST_IO_FAIL_UNSET) +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index c1e93e6e..b4252ab5 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -276,6 +276,12 @@ enum eh_deadline_states { + EH_DEADLINE_ZERO = UOZ_ZERO, + }; + ++enum max_retries_states { ++ MAX_RETRIES_UNSET = UOZ_UNDEF, ++ MAX_RETRIES_OFF = UOZ_OFF, ++ MAX_RETRIES_ZERO = UOZ_ZERO, ++}; ++ + enum recheck_wwid_states { + RECHECK_WWID_UNDEF = YNU_UNDEF, + RECHECK_WWID_OFF = YNU_NO, +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 5e447e67..789f0bfc 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -743,6 +743,20 @@ The default is: \fB\fR + . + . + .TP ++.B max_retries ++Specify the maximum number of times the SCSI layer will retry IO commands for ++some types of SCSI errors before returning failure. Setting this can be helpful ++for cases where IO commands hang and timeout. By default, the SCSI layer will ++retry IOs 5 times. Reducing this value will allow multipath to retry the IO ++down another path sooner. Valid values are ++\fB0\fR through \fB5\fR. ++.RS ++.TP ++The default is: \fB\fR ++.RE ++. ++. ++.TP + .B bindings_file + This option is deprecated, and will be removed in a future release. + The full pathname of the binding file to be used when the user_friendly_names diff --git a/SOURCES/0095-libmutipath-Retain-device-size-if-sysfs_get_size-fai.patch b/SOURCES/0095-libmutipath-Retain-device-size-if-sysfs_get_size-fai.patch new file mode 100644 index 0000000..32eab9a --- /dev/null +++ b/SOURCES/0095-libmutipath-Retain-device-size-if-sysfs_get_size-fai.patch @@ -0,0 +1,29 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 9 Nov 2023 18:46:12 -0500 +Subject: [PATCH] libmutipath: Retain device size if sysfs_get_size fails. + +When paths are allocated their size is initialized to 0. If they've +already set a size, and a future call to sysfs_get_size() fails during +the parsing, assume that the size hasn't changed, instead of setting it +to 0. All other failures in sysfs_get_size() already retain the existing +size. + +Reviewed-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + libmultipath/sysfs.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/libmultipath/sysfs.c b/libmultipath/sysfs.c +index 24c12b6a..41354f91 100644 +--- a/libmultipath/sysfs.c ++++ b/libmultipath/sysfs.c +@@ -229,7 +229,6 @@ sysfs_get_size (struct path *pp, unsigned long long * size) + + if (r != 1) { + condlog(3, "%s: Cannot parse size attribute", pp->dev); +- *size = 0; + return 1; + } + diff --git a/SOURCES/0096-multipathd-check-and-update-all-paths-when-in-cli_re.patch b/SOURCES/0096-multipathd-check-and-update-all-paths-when-in-cli_re.patch new file mode 100644 index 0000000..114c1a5 --- /dev/null +++ b/SOURCES/0096-multipathd-check-and-update-all-paths-when-in-cli_re.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 9 Nov 2023 18:46:13 -0500 +Subject: [PATCH] multipathd: check and update all paths when in cli_resize + +When resizing a multipath device, make sure that all the paths have +been updated to the new size first. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + multipathd/cli_handlers.c | 31 ++++++++++++++++++------------- + 1 file changed, 18 insertions(+), 13 deletions(-) + +diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c +index f322f10f..93c32c5b 100644 +--- a/multipathd/cli_handlers.c ++++ b/multipathd/cli_handlers.c +@@ -866,9 +866,11 @@ cli_resize(void *v, char **reply, int *len, void *data) + char * mapname = get_keyparam(v, MAP); + struct multipath *mpp; + int minor; +- unsigned long long size; ++ unsigned long long size = 0; + struct pathgroup *pgp; + struct path *pp; ++ unsigned int i, j; ++ bool mismatch = false; + + mapname = convert_dev(mapname, 0); + condlog(2, "%s: resize map (operator)", mapname); +@@ -888,21 +890,24 @@ cli_resize(void *v, char **reply, int *len, void *data) + return 1; + } + +- pgp = VECTOR_SLOT(mpp->pg, 0); +- +- if (!pgp){ +- condlog(0, "%s: couldn't get path group. cannot resize", +- mapname); +- return 1; ++ vector_foreach_slot(mpp->pg, pgp, i) { ++ vector_foreach_slot (pgp->paths, pp, j) { ++ sysfs_get_size(pp, &pp->size); ++ if (!pp->size) ++ continue; ++ if (!size) ++ size = pp->size; ++ else if (pp->size != size) ++ mismatch = true; ++ } + } +- pp = VECTOR_SLOT(pgp->paths, 0); +- +- if (!pp){ +- condlog(0, "%s: couldn't get path. cannot resize", mapname); ++ if (!size) { ++ condlog(0, "%s: couldn't get size from sysfs. cannot resize", ++ mapname); + return 1; + } +- if (!pp->udev || sysfs_get_size(pp, &size)) { +- condlog(0, "%s: couldn't get size for sysfs. cannot resize", ++ if (mismatch) { ++ condlog(0, "%s: path size not consistent. cannot resize", + mapname); + return 1; + } diff --git a/SOURCES/0097-multipathd-move-post-reloading-commands-into-resize_.patch b/SOURCES/0097-multipathd-move-post-reloading-commands-into-resize_.patch new file mode 100644 index 0000000..1feb11d --- /dev/null +++ b/SOURCES/0097-multipathd-move-post-reloading-commands-into-resize_.patch @@ -0,0 +1,58 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 9 Nov 2023 18:46:14 -0500 +Subject: [PATCH] multipathd: move post-reloading commands into resize_map() + +In preparation for reusing resize_map() in other code, move all code +necessary to resize the map to the resize_map() function. Also track if +map was removed in the function. + +Reviewed-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + multipathd/cli_handlers.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c +index 93c32c5b..b08b248f 100644 +--- a/multipathd/cli_handlers.c ++++ b/multipathd/cli_handlers.c +@@ -856,6 +856,10 @@ int resize_map(struct multipath *mpp, unsigned long long size, + mpp->size = orig_size; + return 1; + } ++ if (setup_multipath(vecs, mpp) != 0) ++ return 2; ++ sync_map_state(mpp); ++ + return 0; + } + +@@ -869,7 +873,7 @@ cli_resize(void *v, char **reply, int *len, void *data) + unsigned long long size = 0; + struct pathgroup *pgp; + struct path *pp; +- unsigned int i, j; ++ unsigned int i, j, ret; + bool mismatch = false; + + mapname = convert_dev(mapname, 0); +@@ -919,14 +923,12 @@ cli_resize(void *v, char **reply, int *len, void *data) + condlog(3, "%s old size is %llu, new size is %llu", mapname, mpp->size, + size); + +- if (resize_map(mpp, size, vecs) != 0) +- return 1; ++ ret = resize_map(mpp, size, vecs); + +- if (setup_multipath(vecs, mpp) != 0) +- return 1; +- sync_map_state(mpp); ++ if (ret == 2) ++ condlog(0, "%s: map removed while trying to resize", mapname); + +- return 0; ++ return (ret != 0); + } + + int diff --git a/SOURCES/0098-multipathd-move-resize_map-to-multipathd-main.c.patch b/SOURCES/0098-multipathd-move-resize_map-to-multipathd-main.c.patch new file mode 100644 index 0000000..2be9228 --- /dev/null +++ b/SOURCES/0098-multipathd-move-resize_map-to-multipathd-main.c.patch @@ -0,0 +1,106 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 10 Nov 2023 17:59:42 -0500 +Subject: [PATCH] multipathd: move resize_map() to multipathd/main.c + +No functional changes. + +Reviewed-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + multipathd/cli_handlers.c | 29 ----------------------------- + multipathd/main.c | 29 +++++++++++++++++++++++++++++ + multipathd/main.h | 2 ++ + 3 files changed, 31 insertions(+), 29 deletions(-) + +diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c +index b08b248f..53bebc8d 100644 +--- a/multipathd/cli_handlers.c ++++ b/multipathd/cli_handlers.c +@@ -834,35 +834,6 @@ cli_reload(void *v, char **reply, int *len, void *data) + return reload_and_sync_map(mpp, vecs, 0); + } + +-int resize_map(struct multipath *mpp, unsigned long long size, +- struct vectors * vecs) +-{ +- char *params __attribute__((cleanup(cleanup_charp))) = NULL; +- unsigned long long orig_size = mpp->size; +- +- mpp->size = size; +- update_mpp_paths(mpp, vecs->pathvec); +- if (setup_map(mpp, ¶ms, vecs) != 0) { +- condlog(0, "%s: failed to setup map for resize : %s", +- mpp->alias, strerror(errno)); +- mpp->size = orig_size; +- return 1; +- } +- mpp->action = ACT_RESIZE; +- mpp->force_udev_reload = 1; +- if (domap(mpp, params, 1) == DOMAP_FAIL) { +- condlog(0, "%s: failed to resize map : %s", mpp->alias, +- strerror(errno)); +- mpp->size = orig_size; +- return 1; +- } +- if (setup_multipath(vecs, mpp) != 0) +- return 2; +- sync_map_state(mpp); +- +- return 0; +-} +- + int + cli_resize(void *v, char **reply, int *len, void *data) + { +diff --git a/multipathd/main.c b/multipathd/main.c +index 075e7b13..d99cad72 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -1379,6 +1379,35 @@ needs_ro_update(struct multipath *mpp, int ro) + return true; + } + ++int resize_map(struct multipath *mpp, unsigned long long size, ++ struct vectors * vecs) ++{ ++ char *params __attribute__((cleanup(cleanup_charp))) = NULL; ++ unsigned long long orig_size = mpp->size; ++ ++ mpp->size = size; ++ update_mpp_paths(mpp, vecs->pathvec); ++ if (setup_map(mpp, ¶ms, vecs) != 0) { ++ condlog(0, "%s: failed to setup map for resize : %s", ++ mpp->alias, strerror(errno)); ++ mpp->size = orig_size; ++ return 1; ++ } ++ mpp->action = ACT_RESIZE; ++ mpp->force_udev_reload = 1; ++ if (domap(mpp, params, 1) == DOMAP_FAIL) { ++ condlog(0, "%s: failed to resize map : %s", mpp->alias, ++ strerror(errno)); ++ mpp->size = orig_size; ++ return 1; ++ } ++ if (setup_multipath(vecs, mpp) != 0) ++ return 2; ++ sync_map_state(mpp); ++ ++ return 0; ++} ++ + static int + uev_update_path (struct uevent *uev, struct vectors * vecs) + { +diff --git a/multipathd/main.h b/multipathd/main.h +index bc1f938f..dbae4935 100644 +--- a/multipathd/main.h ++++ b/multipathd/main.h +@@ -66,4 +66,6 @@ int reload_and_sync_map(struct multipath *mpp, struct vectors *vecs, + + void handle_path_wwid_change(struct path *pp, struct vectors *vecs); + bool check_path_wwid_change(struct path *pp); ++int resize_map(struct multipath *mpp, unsigned long long size, ++ struct vectors *vecs); + #endif /* MAIN_H */ diff --git a/SOURCES/0099-multipathd-Add-auto_resize-config-option.patch b/SOURCES/0099-multipathd-Add-auto_resize-config-option.patch new file mode 100644 index 0000000..9da16f5 --- /dev/null +++ b/SOURCES/0099-multipathd-Add-auto_resize-config-option.patch @@ -0,0 +1,202 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 9 Nov 2023 18:46:16 -0500 +Subject: [PATCH] multipathd: Add auto_resize config option + +This option gives multipathd the ability to automatically resize a +device when it detects that all of the path devices have changed. By +default it is set to never, and multipathd will continue to work like it +always has, where a users must manually resize a multipath device. + +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/config.h | 1 + + libmultipath/defaults.h | 1 + + libmultipath/dict.c | 38 ++++++++++++++++++++++++++++++++++++++ + libmultipath/dict.h | 1 + + libmultipath/structs.h | 7 +++++++ + multipath/multipath.conf.5 | 15 +++++++++++++++ + multipathd/main.c | 28 ++++++++++++++++++++++++++++ + 7 files changed, 91 insertions(+) + +diff --git a/libmultipath/config.h b/libmultipath/config.h +index b0ee8241..5807ac68 100644 +--- a/libmultipath/config.h ++++ b/libmultipath/config.h +@@ -201,6 +201,7 @@ struct config { + int skip_delegate; + unsigned int sequence_nr; + int recheck_wwid; ++ int auto_resize; + + char * multipath_dir; + char * selector; +diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h +index cec82f07..c3788bbc 100644 +--- a/libmultipath/defaults.h ++++ b/libmultipath/defaults.h +@@ -54,6 +54,7 @@ + #define DEFAULT_UNKNOWN_FIND_MULTIPATHS_TIMEOUT 1 + #define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF + #define DEFAULT_RECHECK_WWID RECHECK_WWID_OFF ++#define DEFAULT_AUTO_RESIZE AUTO_RESIZE_NEVER + /* Enable no foreign libraries by default */ + #define DEFAULT_ENABLE_FOREIGN "NONE" + +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index 0c66c1e1..c4db60df 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -1736,6 +1736,43 @@ declare_hw_snprint(recheck_wwid, print_yes_no_undef) + + declare_def_range_handler(uxsock_timeout, DEFAULT_REPLY_TIMEOUT, INT_MAX) + ++static int ++def_auto_resize_handler(struct config *conf, vector strvec, const char *file, ++ int line_nr) ++{ ++ char * buff; ++ ++ buff = set_value(strvec); ++ if (!buff) ++ return 1; ++ ++ if (strcmp(buff, "never") == 0) ++ conf->auto_resize = AUTO_RESIZE_NEVER; ++ else if (strcmp(buff, "grow_only") == 0) ++ conf->auto_resize = AUTO_RESIZE_GROW_ONLY; ++ else if (strcmp(buff, "grow_shrink") == 0) ++ conf->auto_resize = AUTO_RESIZE_GROW_SHRINK; ++ else ++ condlog(1, "%s line %d, invalid value for auto_resize: \"%s\"", ++ file, line_nr, buff); ++ ++ free(buff); ++ return 0; ++} ++ ++int ++print_auto_resize(struct strbuf *buff, long v) ++{ ++ if (!v) ++ return 0; ++ return append_strbuf_quoted(buff, ++ v == AUTO_RESIZE_GROW_ONLY ? "grow_only" : ++ v == AUTO_RESIZE_GROW_SHRINK ? "grow_shrink" : ++ "never"); ++} ++ ++declare_def_snprint(auto_resize, print_auto_resize) ++ + static int + hw_vpd_vendor_handler(struct config *conf, vector strvec, const char *file, + int line_nr) +@@ -2202,6 +2239,7 @@ init_keywords(vector keywords) + install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries); + install_keyword("max_sectors_kb", &def_max_sectors_kb_handler, &snprint_def_max_sectors_kb); + install_keyword("ghost_delay", &def_ghost_delay_handler, &snprint_def_ghost_delay); ++ install_keyword("auto_resize", &def_auto_resize_handler, &snprint_def_auto_resize); + install_keyword("find_multipaths_timeout", + &def_find_multipaths_timeout_handler, + &snprint_def_find_multipaths_timeout); +diff --git a/libmultipath/dict.h b/libmultipath/dict.h +index d80f990a..d963b4ad 100644 +--- a/libmultipath/dict.h ++++ b/libmultipath/dict.h +@@ -19,4 +19,5 @@ int print_dev_loss(struct strbuf *buff, unsigned long v); + int print_reservation_key(struct strbuf *buff, + struct be64 key, uint8_t flags, int source); + int print_off_int_undef(struct strbuf *buff, long v); ++int print_auto_resize(struct strbuf *buff, long v); + #endif /* _DICT_H */ +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index b4252ab5..8c2d7131 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -167,6 +167,13 @@ enum queue_mode_states { + QUEUE_MODE_RQ, + }; + ++enum auto_resize_state { ++ AUTO_RESIZE_UNDEF = 0, ++ AUTO_RESIZE_NEVER, ++ AUTO_RESIZE_GROW_ONLY, ++ AUTO_RESIZE_GROW_SHRINK, ++}; ++ + #define PROTOCOL_UNSET -1 + + enum scsi_protocol { +diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 +index 789f0bfc..38eb5c90 100644 +--- a/multipath/multipath.conf.5 ++++ b/multipath/multipath.conf.5 +@@ -1293,6 +1293,21 @@ The default is: \fBno\fR + . + . + .TP ++.B auto_resize ++Controls when multipathd will automatically resize a multipath device. If set ++to \fInever\fR, multipath devices must always be manually resized by either ++running \fBmultipathd resize map \fR. If set to \fIgrow_only\fR, when ++multipathd detects that all of a multipath device's paths have increased in ++size, it will automatically grow the multipath device to the new size. If set ++to \fIgrow_shrink\fR, multipathd will also automatically shrink the device ++once it detects all of its paths have decreased in size. ++.RS ++.TP ++The default is: \fBnever\fR ++.RE ++. ++. ++.TP + .B enable_foreign + Enables or disables foreign libraries (see section + .I FOREIGN MULTIPATH SUPPORT +diff --git a/multipathd/main.c b/multipathd/main.c +index d99cad72..6d1a5e4e 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -1439,6 +1439,11 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) + if (pp) { + struct multipath *mpp = pp->mpp; + char wwid[WWID_SIZE]; ++ int auto_resize; ++ ++ conf = get_multipath_config(); ++ auto_resize = conf->auto_resize; ++ put_multipath_config(conf); + + if (pp->initialized == INIT_REQUESTED_UDEV) { + needs_reinit = 1; +@@ -1489,6 +1494,29 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) + } + } + } ++ if (auto_resize != AUTO_RESIZE_NEVER && ++ !mpp->wait_for_udev) { ++ struct pathgroup *pgp; ++ struct path *pp2; ++ unsigned int i, j; ++ unsigned long long orig_size = mpp->size; ++ ++ if (!pp->size || pp->size == mpp->size || ++ (pp->size < mpp->size && ++ auto_resize == AUTO_RESIZE_GROW_ONLY)) ++ goto out; ++ ++ vector_foreach_slot(mpp->pg, pgp, i) ++ vector_foreach_slot (pgp->paths, pp2, j) ++ if (pp2->size && pp2->size != pp->size) ++ goto out; ++ retval = resize_map(mpp, pp->size, vecs); ++ if (retval == 2) ++ condlog(2, "%s: map removed during resize", pp->dev); ++ else if (retval == 0) ++ condlog(2, "%s: resized map from %llu to %llu", ++ mpp->alias, orig_size, pp->size); ++ } + } + out: + lock_cleanup_pop(vecs->lock); diff --git a/SOURCES/0100-libmultipath-avoid-temporarily-enabling-queueing-on-.patch b/SOURCES/0100-libmultipath-avoid-temporarily-enabling-queueing-on-.patch new file mode 100644 index 0000000..2784399 --- /dev/null +++ b/SOURCES/0100-libmultipath-avoid-temporarily-enabling-queueing-on-.patch @@ -0,0 +1,42 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +Date: Mon, 18 Dec 2023 16:30:42 -0500 +Subject: [PATCH] libmultipath: avoid temporarily enabling queueing on reload + +Instead of always enabling queueing when a map is reloaded with +no_path_retry set to a positive number, check if the map has timed out +in recovery mode, and only enable queueing if it has not. This saves +multipathd from having to disable queueing on the map immediately after +the reload. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/dmparser.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c +index 16377c54..1ea2d619 100644 +--- a/libmultipath/dmparser.c ++++ b/libmultipath/dmparser.c +@@ -61,9 +61,19 @@ int assemble_map(struct multipath *mp, char **params) + nr_priority_groups = VECTOR_SIZE(mp->pg); + initial_pg_nr = (nr_priority_groups ? mp->bestpg : 0); + +- if (mp->no_path_retry != NO_PATH_RETRY_UNDEF && +- mp->no_path_retry != NO_PATH_RETRY_FAIL) { ++ switch (mp->no_path_retry) { ++ case NO_PATH_RETRY_UNDEF: ++ case NO_PATH_RETRY_FAIL: ++ break; ++ default: ++ /* don't enable queueing if no_path_retry has timed out */ ++ if (mp->in_recovery && mp->retry_tick == 0 && ++ count_active_paths(mp) == 0) ++ break; ++ /* fallthrough */ ++ case NO_PATH_RETRY_QUEUE: + add_feature(&mp->features, no_path_retry); ++ break; + } + if (mp->retain_hwhandler == RETAIN_HWHANDLER_ON && + get_linux_version_code() < KERNEL_VERSION(4, 3, 0)) diff --git a/SOURCES/0101-multipathd-Make-sure-to-disable-queueing-if-recovery.patch b/SOURCES/0101-multipathd-Make-sure-to-disable-queueing-if-recovery.patch new file mode 100644 index 0000000..62c3ed0 --- /dev/null +++ b/SOURCES/0101-multipathd-Make-sure-to-disable-queueing-if-recovery.patch @@ -0,0 +1,76 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 22 Nov 2023 16:41:22 -0500 +Subject: [PATCH] multipathd: Make sure to disable queueing if recovery has + failed. + +If a multipath device has no_path_retry set to a number and has lost all +paths, gone into recovery mode, and timed out, it will disable +queue_if_no_paths. After that, if the device is reloaded by multipath +outside of multipathd, it will re-enable queuieng on the device. When +multipathd later calls set_no_path_retry() to update the queueing state, +it will not disable queue_if_no_paths, since the device is still in the +recovery state, so it believes no work needs to be done. The device will +remain in the recovery state, with retry_ticks at 0, and queueing +enabled, even though there are no usable paths. + +To fix this, in set_no_path_retry(), if no_path_retry is set to a number +and the device is queueing but it is in recovery mode and out of +retries with no usable paths, manually disable queue_if_no_path. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/structs_vec.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c +index 4a32b405..86ad89ca 100644 +--- a/libmultipath/structs_vec.c ++++ b/libmultipath/structs_vec.c +@@ -614,8 +614,19 @@ void __set_no_path_retry(struct multipath *mpp, bool check_features) + !mpp->in_recovery) + dm_queue_if_no_path(mpp->alias, 1); + leave_recovery_mode(mpp); +- } else +- enter_recovery_mode(mpp); ++ } else { ++ /* ++ * If in_recovery is set, enter_recovery_mode does ++ * nothing. If the device is already in recovery ++ * mode and has already timed out, manually call ++ * dm_queue_if_no_path to stop it from queueing. ++ */ ++ if ((!check_features || is_queueing) && ++ mpp->in_recovery && mpp->retry_tick == 0) ++ dm_queue_if_no_path(mpp->alias, 0); ++ if (pathcount(mpp, PATH_PENDING) == 0) ++ enter_recovery_mode(mpp); ++ } + break; + } + } +@@ -761,6 +772,11 @@ int verify_paths(struct multipath *mpp) + * -1 (FAIL) : fail_if_no_path + * 0 (UNDEF) : nothing + * >0 : queue_if_no_path enabled, turned off after polling n times ++ * ++ * Since this will only be called when fail_path(), update_multipath(), or ++ * io_err_stat_handle_pathfail() are failing a previously active path, the ++ * device cannot already be in recovery mode, so there will never be a need ++ * to disable queueing here. + */ + void update_queue_mode_del_path(struct multipath *mpp) + { +@@ -774,6 +790,12 @@ void update_queue_mode_del_path(struct multipath *mpp) + condlog(2, "%s: remaining active paths: %d", mpp->alias, active); + } + ++/* ++ * Since this will only be called from check_path() -> reinstate_path() after ++ * the queueing state has been updated in set_no_path_retry, this does not ++ * need to worry about modifying the queueing state except when actually ++ * leaving recovery mode. ++ */ + void update_queue_mode_add_path(struct multipath *mpp) + { + int active = count_active_paths(mpp); diff --git a/SOURCES/0102-multipathd-remove-nopath-flushing-code-from-flush_ma.patch b/SOURCES/0102-multipathd-remove-nopath-flushing-code-from-flush_ma.patch new file mode 100644 index 0000000..ef04153 --- /dev/null +++ b/SOURCES/0102-multipathd-remove-nopath-flushing-code-from-flush_ma.patch @@ -0,0 +1,131 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 6 Dec 2023 17:22:02 -0500 +Subject: [PATCH] multipathd: remove nopath flushing code from flush_map() + +Instead of flush_map() handling both user requested flushes and +automatic flushes when the last path has been deleted, make +flush_map_nopaths() handle the automatic flushes itself, since a later +patch will change the behavior of flush_map(). + +Reviewed-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + multipathd/cli_handlers.c | 2 +- + multipathd/main.c | 45 +++++++++++++++++---------------------- + multipathd/main.h | 2 +- + 3 files changed, 21 insertions(+), 28 deletions(-) + +diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c +index 53bebc8d..f04fb558 100644 +--- a/multipathd/cli_handlers.c ++++ b/multipathd/cli_handlers.c +@@ -796,7 +796,7 @@ cli_del_maps (void *v, char **reply, int *len, void *data) + + condlog(2, "remove maps (operator)"); + vector_foreach_slot(vecs->mpvec, mpp, i) { +- if (flush_map(mpp, vecs, 0)) ++ if (flush_map(mpp, vecs)) + ret++; + else + i--; +diff --git a/multipathd/main.c b/multipathd/main.c +index 6d1a5e4e..1b5f82e7 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -490,12 +490,11 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset) + + static bool + flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) { +- char alias[WWID_SIZE]; ++ int r; + + /* + * flush_map will fail if the device is open + */ +- strlcpy(alias, mpp->alias, WWID_SIZE); + if (mpp->flush_on_last_del == FLUSH_ENABLED) { + condlog(2, "%s Last path deleted, disabling queueing", + mpp->alias); +@@ -505,11 +504,20 @@ flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) { + mpp->stat_map_failures++; + dm_queue_if_no_path(mpp->alias, 0); + } +- if (!flush_map(mpp, vecs, 1)) { +- condlog(2, "%s: removed map after removing all paths", alias); +- return true; ++ r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove); ++ if (r) { ++ if (r == 1) ++ condlog(0, "%s: can't flush", mpp->alias); ++ else { ++ condlog(2, "%s: devmap deferred remove", mpp->alias); ++ mpp->deferred_remove = DEFERRED_REMOVE_IN_PROGRESS; ++ } ++ return false; + } +- return false; ++ ++ condlog(2, "%s: map flushed after removing all paths", mpp->alias); ++ remove_map_and_stop_waiter(mpp, vecs); ++ return true; + } + + static void +@@ -685,30 +693,15 @@ sync_maps_state(vector mpvec) + } + + int +-flush_map(struct multipath * mpp, struct vectors * vecs, int nopaths) ++flush_map(struct multipath * mpp, struct vectors * vecs) + { +- int r; +- +- if (nopaths) +- r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove); +- else +- r = dm_flush_map(mpp->alias); +- /* +- * clear references to this map before flushing so we can ignore +- * the spurious uevent we may generate with the dm_flush_map call below +- */ ++ int r = dm_flush_map(mpp->alias); + if (r) { +- if (r == 1) +- condlog(0, "%s: can't flush", mpp->alias); +- else { +- condlog(2, "%s: devmap deferred remove", mpp->alias); +- mpp->deferred_remove = DEFERRED_REMOVE_IN_PROGRESS; +- } ++ condlog(0, "%s: can't flush", mpp->alias); + return r; + } +- else +- condlog(2, "%s: map flushed", mpp->alias); + ++ condlog(2, "%s: map flushed", mpp->alias); + remove_map_and_stop_waiter(mpp, vecs); + + return 0; +@@ -866,7 +859,7 @@ ev_remove_map (char * devname, char * alias, int minor, struct vectors * vecs) + mpp->alias, mpp->dmi->minor, minor); + return 1; + } +- return flush_map(mpp, vecs, 0); ++ return flush_map(mpp, vecs); + } + + static void +diff --git a/multipathd/main.h b/multipathd/main.h +index dbae4935..4138faa4 100644 +--- a/multipathd/main.h ++++ b/multipathd/main.h +@@ -43,7 +43,7 @@ int ev_add_path (struct path *, struct vectors *, int); + int ev_remove_path (struct path *, struct vectors *, int); + int ev_add_map (char *, const char *, struct vectors *); + int ev_remove_map (char *, char *, int, struct vectors *); +-int flush_map(struct multipath *, struct vectors *, int); ++int flush_map(struct multipath *, struct vectors *); + int set_config_state(enum daemon_status); + void * mpath_alloc_prin_response(int prin_sa); + int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp, diff --git a/SOURCES/0103-multipathd-make-flush_map-delete-maps-like-the-multi.patch b/SOURCES/0103-multipathd-make-flush_map-delete-maps-like-the-multi.patch new file mode 100644 index 0000000..d3fa6e3 --- /dev/null +++ b/SOURCES/0103-multipathd-make-flush_map-delete-maps-like-the-multi.patch @@ -0,0 +1,39 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Thu, 7 Dec 2023 11:23:18 -0500 +Subject: [PATCH] multipathd: make flush_map() delete maps like the multipath + command + +When the multipath command tries to delete a multipath device, it first +disables queueing and then suspends the device to force the IOs to get +flushed. Then it attempts to delete the device and any kpartx +partitions. multipathd, on the other hand, simply tries to delete the +device and kpartx partitions, without disabling queueing or suspending. +If there are no paths but there is outstanding IO, multipathd will hang +trying to delete the last kpartx device. This is because it must be the +last opener of the multipath device (multipath won't try to delete the +device if it has any openers besides the kpartx devices) and the kernel +will not allow the last opener of a block device to close until all the +outstanding IO is flushed. This hang can be avoided if multipathd calls +dm_suspend_and_flush_map() like the multipath command does, instead of +dm_flush_map(). + +Reviewed-by: Martin Wilck +Signed-off-by: Benjamin Marzinski +--- + multipathd/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/multipathd/main.c b/multipathd/main.c +index 1b5f82e7..3eeca82f 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -695,7 +695,7 @@ sync_maps_state(vector mpvec) + int + flush_map(struct multipath * mpp, struct vectors * vecs) + { +- int r = dm_flush_map(mpp->alias); ++ int r = dm_suspend_and_flush_map(mpp->alias, 0); + if (r) { + condlog(0, "%s: can't flush", mpp->alias); + return r; diff --git a/SOURCES/0104-multipathd-disable-queueing-when-removing-unknown-ma.patch b/SOURCES/0104-multipathd-disable-queueing-when-removing-unknown-ma.patch new file mode 100644 index 0000000..51d68ae --- /dev/null +++ b/SOURCES/0104-multipathd-disable-queueing-when-removing-unknown-ma.patch @@ -0,0 +1,83 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 8 Dec 2023 14:50:31 -0500 +Subject: [PATCH] multipathd: disable queueing when removing unknown maps + +Make cli_del_maps() call dm_suspend_and_flush_map() for the unknown +multipath devices as well. + +After this change, all callers of cli_del_maps() set need_suspend, so +simplify dm_flush_maps(). + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/devmapper.c | 7 ++----- + libmultipath/devmapper.h | 2 +- + multipath/main.c | 2 +- + multipathd/cli_handlers.c | 2 +- + 4 files changed, 5 insertions(+), 8 deletions(-) + +diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c +index 4b2e8a15..f9de3358 100644 +--- a/libmultipath/devmapper.c ++++ b/libmultipath/devmapper.c +@@ -1145,7 +1145,7 @@ dm_flush_map_nopaths(const char * mapname, + + #endif + +-int dm_flush_maps (int need_suspend, int retries) ++int dm_flush_maps (int retries) + { + int r = 1; + struct dm_task *dmt; +@@ -1170,10 +1170,7 @@ int dm_flush_maps (int need_suspend, int retries) + goto out; + + do { +- if (need_suspend) +- r |= dm_suspend_and_flush_map(names->name, retries); +- else +- r |= dm_flush_map(names->name); ++ r |= dm_suspend_and_flush_map(names->name, retries); + next = names->next; + names = (void *) names + next; + } while (next); +diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h +index 45a676de..808da28d 100644 +--- a/libmultipath/devmapper.h ++++ b/libmultipath/devmapper.h +@@ -55,7 +55,7 @@ int dm_flush_map_nopaths(const char * mapname, int deferred_remove); + #define dm_suspend_and_flush_map(mapname, retries) \ + _dm_flush_map(mapname, 1, 0, 1, retries) + int dm_cancel_deferred_remove(struct multipath *mpp); +-int dm_flush_maps (int need_suspend, int retries); ++int dm_flush_maps (int retries); + int dm_fail_path(const char * mapname, char * path); + int dm_reinstate_path(const char * mapname, char * path); + int dm_queue_if_no_path(const char *mapname, int enable); +diff --git a/multipath/main.c b/multipath/main.c +index f1077421..e296be6e 100644 +--- a/multipath/main.c ++++ b/multipath/main.c +@@ -1126,7 +1126,7 @@ main (int argc, char *argv[]) + goto out; + } + else if (cmd == CMD_FLUSH_ALL) { +- r = dm_flush_maps(1, retries) ? RTVL_FAIL : RTVL_OK; ++ r = dm_flush_maps(retries) ? RTVL_FAIL : RTVL_OK; + goto out; + } + while ((r = configure(conf, cmd, dev_type, dev)) == RTVL_RETRY) +diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c +index f04fb558..aca8e2df 100644 +--- a/multipathd/cli_handlers.c ++++ b/multipathd/cli_handlers.c +@@ -802,7 +802,7 @@ cli_del_maps (void *v, char **reply, int *len, void *data) + i--; + } + /* flush any multipath maps that aren't currently known by multipathd */ +- ret |= dm_flush_maps(0, 0); ++ ret |= dm_flush_maps(0); + return ret; + } + diff --git a/SOURCES/0105-multipathd-fix-null-pointer-dereference-in-uev_updat.patch b/SOURCES/0105-multipathd-fix-null-pointer-dereference-in-uev_updat.patch new file mode 100644 index 0000000..908d202 --- /dev/null +++ b/SOURCES/0105-multipathd-fix-null-pointer-dereference-in-uev_updat.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 16 Jan 2024 20:19:11 -0500 +Subject: [PATCH] multipathd: fix null pointer dereference in uev_update_path + +The Auto-resize code added a check that deferences pp->mpp without +checking that it's non-NULL. Fix it. + +Fixes: 981b83ad1 ("multipathd: Add auto_resize config option") +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + multipathd/main.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/multipathd/main.c b/multipathd/main.c +index 3eeca82f..26be6dc3 100644 +--- a/multipathd/main.c ++++ b/multipathd/main.c +@@ -1487,7 +1487,7 @@ uev_update_path (struct uevent *uev, struct vectors * vecs) + } + } + } +- if (auto_resize != AUTO_RESIZE_NEVER && ++ if (auto_resize != AUTO_RESIZE_NEVER && mpp && + !mpp->wait_for_udev) { + struct pathgroup *pgp; + struct path *pp2; diff --git a/SOURCES/0106-multipathd-fix-auto-resize-configuration.patch b/SOURCES/0106-multipathd-fix-auto-resize-configuration.patch new file mode 100644 index 0000000..b154ac4 --- /dev/null +++ b/SOURCES/0106-multipathd-fix-auto-resize-configuration.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 16 Jan 2024 20:19:12 -0500 +Subject: [PATCH] multipathd: fix auto-resize configuration + +The code acted like AUTO_RESIZE_UNDEFINED didn't exist, but since +conf->auto_resize was never set to AUTO_RESIZE_NEVER, the default was in +fact AUTO_RESIZE_UNDEFINED, which ended up getting treated like +AUTO_RESIZE_GROW_SHRINK. Remove AUTO_RESIZE_UNDEFINED and explicitly +default auto_resize tp AUTO_RESIZE_NEVER. + +Fixes: 981b83ad1 ("multipathd: Add auto_resize config option") +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + libmultipath/config.c | 1 + + libmultipath/structs.h | 1 - + 2 files changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libmultipath/config.c b/libmultipath/config.c +index 61b0dd51..f31200a3 100644 +--- a/libmultipath/config.c ++++ b/libmultipath/config.c +@@ -940,6 +940,7 @@ int _init_config (const char *file, struct config *conf) + conf->retrigger_tries = DEFAULT_RETRIGGER_TRIES; + conf->retrigger_delay = DEFAULT_RETRIGGER_DELAY; + conf->uev_wait_timeout = DEFAULT_UEV_WAIT_TIMEOUT; ++ conf->auto_resize = DEFAULT_AUTO_RESIZE; + conf->remove_retries = 0; + conf->ghost_delay = DEFAULT_GHOST_DELAY; + conf->all_tg_pt = DEFAULT_ALL_TG_PT; +diff --git a/libmultipath/structs.h b/libmultipath/structs.h +index 8c2d7131..d2ad4867 100644 +--- a/libmultipath/structs.h ++++ b/libmultipath/structs.h +@@ -168,7 +168,6 @@ enum queue_mode_states { + }; + + enum auto_resize_state { +- AUTO_RESIZE_UNDEF = 0, + AUTO_RESIZE_NEVER, + AUTO_RESIZE_GROW_ONLY, + AUTO_RESIZE_GROW_SHRINK, diff --git a/SOURCES/0107-libmultipath-fix-displaying-auto_resize-config-setti.patch b/SOURCES/0107-libmultipath-fix-displaying-auto_resize-config-setti.patch new file mode 100644 index 0000000..9c41615 --- /dev/null +++ b/SOURCES/0107-libmultipath-fix-displaying-auto_resize-config-setti.patch @@ -0,0 +1,28 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Fri, 26 Jan 2024 15:40:42 -0500 +Subject: [PATCH] libmultipath: fix displaying auto_resize config setting + +When 56476ebd3 ("multipathd: fix auto-resize configuration") removed +AUTO_RESIZE_UNDEFINED, it didn't update print_auto_resize() to print +a value for when it was set to 0 (which is now AUTO_RESIZE_NEVER). + +Fixes: 56476ebd3 ("multipathd: fix auto-resize configuration") +Signed-off-by: Benjamin Marzinski +--- + libmultipath/dict.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/libmultipath/dict.c b/libmultipath/dict.c +index c4db60df..ce1b6c99 100644 +--- a/libmultipath/dict.c ++++ b/libmultipath/dict.c +@@ -1763,8 +1763,6 @@ def_auto_resize_handler(struct config *conf, vector strvec, const char *file, + int + print_auto_resize(struct strbuf *buff, long v) + { +- if (!v) +- return 0; + return append_strbuf_quoted(buff, + v == AUTO_RESIZE_GROW_ONLY ? "grow_only" : + v == AUTO_RESIZE_GROW_SHRINK ? "grow_shrink" : diff --git a/SPECS/device-mapper-multipath.spec b/SPECS/device-mapper-multipath.spec index 8863f91..f7ba6e1 100644 --- a/SPECS/device-mapper-multipath.spec +++ b/SPECS/device-mapper-multipath.spec @@ -1,6 +1,6 @@ Name: device-mapper-multipath Version: 0.8.7 -Release: 22%{?dist} +Release: 27%{?dist} Summary: Tools to manage multipath devices using device-mapper License: GPLv2 URL: http://christophe.varoqui.free.fr/ @@ -100,7 +100,23 @@ Patch0087: 0087-multipathd-handle-no-active-paths-in-update_map_pr.patch Patch0088: 0088-libmpathpersist-fix-resource-leak-in-update_map_pr.patch Patch0089: 0089-RH-Add-mpathcleanup.patch Patch0090: 0090-RH-make-listing-return-an-error-if-the-config-file-i.patch - +Patch0091: 0091-multipathd-Added-support-to-handle-FPIN-Li-events-fo.patch +Patch0092: 0092-multipath-tools-add-HPE-Alletra-9000-NVMe-to-hardwar.patch +Patch0093: 0093-RH-multipath-add-mpathcleanup-man-page.patch +Patch0094: 0094-libmultipath-Add-max_retries-config-option.patch +Patch0095: 0095-libmutipath-Retain-device-size-if-sysfs_get_size-fai.patch +Patch0096: 0096-multipathd-check-and-update-all-paths-when-in-cli_re.patch +Patch0097: 0097-multipathd-move-post-reloading-commands-into-resize_.patch +Patch0098: 0098-multipathd-move-resize_map-to-multipathd-main.c.patch +Patch0099: 0099-multipathd-Add-auto_resize-config-option.patch +Patch0100: 0100-libmultipath-avoid-temporarily-enabling-queueing-on-.patch +Patch0101: 0101-multipathd-Make-sure-to-disable-queueing-if-recovery.patch +Patch0102: 0102-multipathd-remove-nopath-flushing-code-from-flush_ma.patch +Patch0103: 0103-multipathd-make-flush_map-delete-maps-like-the-multi.patch +Patch0104: 0104-multipathd-disable-queueing-when-removing-unknown-ma.patch +Patch0105: 0105-multipathd-fix-null-pointer-dereference-in-uev_updat.patch +Patch0106: 0106-multipathd-fix-auto-resize-configuration.patch +Patch0107: 0107-libmultipath-fix-displaying-auto_resize-config-setti.patch # runtime @@ -243,6 +259,7 @@ fi %{_mandir}/man8/multipath.8.gz %{_mandir}/man8/multipathd.8.gz %{_mandir}/man8/mpathconf.8.gz +%{_mandir}/man8/mpathcleanup.8.gz %{_mandir}/man8/mpathpersist.8.gz %config %{_udevrulesdir}/62-multipath.rules %config %{_udevrulesdir}/11-dm-mpath.rules @@ -303,6 +320,56 @@ fi %{_pkgconfdir}/libdmmp.pc %changelog +* Fri Jan 26 2024 Benjamin Marzinski - 0.8.7-27 +- Add 0105-multipathd-fix-null-pointer-dereference-in-uev_updat.patch +- Add 0106-multipathd-fix-auto-resize-configuration.patch +- Add 0107-libmultipath-fix-displaying-auto_resize-config-setti.patch + * Fixes RHEL-986 ("Add option to allow multipathd to detect device + resizes and autoresize.") +- Resolves: RHEL-986 + +* Wed Jan 3 2024 Benjamin Marzinski - 0.8.7-26 +- Add 0100-libmultipath-avoid-temporarily-enabling-queueing-on-.patch +- Add 0101-multipathd-Make-sure-to-disable-queueing-if-recovery.patch + * Fixes RHEL-17234 ("RHEL9 dm-multipath no_path_retry [retry number] is + undone if paths are later lost for an open map.") +- Add 0102-multipathd-remove-nopath-flushing-code-from-flush_ma.patch +- Add 0103-multipathd-make-flush_map-delete-maps-like-the-multi.patch +- Add 0104-multipathd-disable-queueing-when-removing-unknown-ma.patch + * Fixes RHEL-4998 ("When remove external lun from host, rescan lun status + will cause the OS hang and no response") +- Resolves: RHEL-4998 +- Resolves: RHEL-17234 + +* Mon Nov 20 2023 Benjamin Marzinski - 0.8.7-25 +- Add 0094-libmultipath-Add-max_retries-config-option.patch + * Fixes RHEL-1729 ("Allow multipathd to set the max_retries of the + scsi_device for paths") +- Add 0095-libmutipath-Retain-device-size-if-sysfs_get_size-fai.patch +- Add 0096-multipathd-check-and-update-all-paths-when-in-cli_re.patch +- Add 0097-multipathd-move-post-reloading-commands-into-resize_.patch +- Add 0098-multipathd-move-resize_map-to-multipathd-main.c.patch +- Add 0099-multipathd-Add-auto_resize-config-option.patch + * Fixes RHEL-986 ("Add option to allow multipathd to detect device + resizes and autoresize.") +- Resolves: RHEL-986 +- Resolves: RHEL-1729 + +* Fri Nov 3 2023 Benjamin Marzinski - 0.8.7-24 +- Add 0093-RH-multipath-add-mpathcleanup-man-page.patch + * Fixes RHEL-1266 ("There is no man page for mpathcleanup") +- Resolves: RHEL-1266 + +* Wed Nov 1 2023 Benjamin Marzinski - 0.8.7-23 +- Add 0091-multipathd-Added-support-to-handle-FPIN-Li-events-fo.patch + * Fixes RHEL-6678 (Add support in multipathd for NVMe to listen for FPIN-Li + events and mark effected paths as marginal) +- Add 0092-multipath-tools-add-HPE-Alletra-9000-NVMe-to-hardwar.patch + * Fixes RHEL-1830 (Changes to Arcus NVMeoFC multipath.conf settings for RHEL + 9.x to be included in kernel by default) +- Resolves: RHEL-6678 +- Resolves: RHEL-1830 + * Fri Jul 28 2023 Benjamin Marzinski - 0.8.7-22 - Add 0089-RH-Add-mpathcleanup.patch * Fixes RHEL-782