Compare commits

...

No commits in common. 'c9' and 'c9-beta' have entirely different histories.
c9 ... c9-beta

@ -0,0 +1,300 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Muneendra <muneendra.kumar@broadcom.com>
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 <muneendra.kumar@broadcom.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
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);

@ -0,0 +1,31 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <bmarzins@redhat.com>
---
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",

@ -0,0 +1,70 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 3 Nov 2023 11:13:04 -0400
Subject: [PATCH] RH: multipath: add mpathcleanup man page
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
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 <bmarzins@redhat.com>

@ -0,0 +1,182 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
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<unset>\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<unset>\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

@ -0,0 +1,29 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
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;
}

@ -0,0 +1,68 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
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;
}

@ -0,0 +1,58 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
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

@ -0,0 +1,106 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
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, &params, 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, &params, 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 */

@ -0,0 +1,202 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
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 <name>\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);

@ -0,0 +1,42 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
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 <bmarzins@redhat.com>
---
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))

@ -0,0 +1,76 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <bmarzins@redhat.com>
---
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);

@ -0,0 +1,131 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
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,

@ -0,0 +1,39 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
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;

@ -0,0 +1,83 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <bmarzins@redhat.com>
---
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;
}

@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
multipathd/main.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 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;

@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
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,

@ -0,0 +1,28 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
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 <bmarzins@redhat.com>
---
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" :

@ -0,0 +1,40 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 9 Apr 2024 14:09:49 -0400
Subject: [PATCH] libmultipath: actually truncate too-large vpd page.
When multipath notices that the vpd page is too large, it needs to
actually truncate it. Also, whe calling parse_vpd_pg83() with a possibly
truncated page, multipath needs to check that it actually has a whole
vpd entry, before trying to use it.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/discovery.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index adf8bbaa..ae7eb7e6 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -1164,7 +1164,7 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len,
int vpd_type, prio = -1, naa_prio;
d = in + 4;
- while (d < in + in_len) {
+ while (d + 4 <= in + in_len && d + d[3] + 4 <= in + in_len) {
/* Select 'association: LUN' */
if ((d[1] & 0x30) != 0) {
d += d[3] + 4;
@@ -1363,8 +1363,10 @@ get_vpd_sysfs (struct udev_device *parent, int pg, char * str, int maxlen)
return -ENODATA;
}
buff_len = get_unaligned_be16(&buff[2]) + 4;
- if (buff_len > 4096)
+ if (buff_len > 4096) {
condlog(3, "vpd pg%02x page truncated", pg);
+ buff_len = 4096;
+ }
if (pg == 0x80)
len = parse_vpd_pg80(buff, str, maxlen);

@ -0,0 +1,23 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 9 Apr 2024 14:13:34 -0400
Subject: [PATCH] kpartx: fix theoretical overflow in loop device name
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
kpartx/lopart.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kpartx/lopart.c b/kpartx/lopart.c
index 9b652554..80ce1312 100644
--- a/kpartx/lopart.c
+++ b/kpartx/lopart.c
@@ -159,7 +159,7 @@ char *find_loop_by_file(const char *filename)
char *find_unused_loop_device(void)
{
- char dev[20], *next_loop_dev = NULL;
+ char dev[21], *next_loop_dev = NULL;
int fd, next_loop = 0, somedev = 0, someloop = 0, loop_known = 0;
struct stat statbuf;
struct loop_info loopinfo;

@ -0,0 +1,194 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 19 Dec 2023 17:51:50 -0500
Subject: [PATCH] libmultipath: keep track of queueing state in features
Make multipathd update mpp->features when in enables or disables
queuing. This patch handles all the cases except failed removes by
dm_suspend_and_flush_map(), which is never called by multipathd.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmultipath/configure.c | 4 +---
libmultipath/devmapper.c | 23 +++++++++++++++++++----
libmultipath/devmapper.h | 2 +-
libmultipath/structs_vec.c | 10 +++++-----
multipathd/main.c | 8 ++++----
5 files changed, 30 insertions(+), 17 deletions(-)
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
index bbdbb8ca..71acb968 100644
--- a/libmultipath/configure.c
+++ b/libmultipath/configure.c
@@ -1279,9 +1279,7 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid,
mpp->no_path_retry != NO_PATH_RETRY_FAIL)
condlog(3, "%s: multipathd not running, unset "
"queue_if_no_path feature", mpp->alias);
- if (!dm_queue_if_no_path(mpp->alias, 0))
- remove_feature(&mpp->features,
- "queue_if_no_path");
+ dm_queue_if_no_path(mpp, 0);
}
if (!is_daemon && mpp->action != ACT_NOTHING)
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index f9de3358..5711f0ee 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -57,6 +57,7 @@ static int dm_cancel_remove_partmaps(const char * mapname);
static int do_foreach_partmaps(const char * mapname,
int (*partmap_func)(const char *, void *),
void *data);
+static int _dm_queue_if_no_path(const char *mapname, int enable);
#ifndef LIBDM_API_COOKIE
static inline int dm_task_set_cookie(struct dm_task *dmt, uint32_t *c, int a)
@@ -1072,7 +1073,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove,
if (need_suspend &&
dm_get_map(mapname, &mapsize, &params) == DMP_OK &&
strstr(params, "queue_if_no_path")) {
- if (!dm_queue_if_no_path(mapname, 0))
+ if (!_dm_queue_if_no_path(mapname, 0))
queue_if_no_path = 1;
else
/* Leave queue_if_no_path alone if unset failed */
@@ -1121,7 +1122,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove,
} while (retries-- > 0);
if (queue_if_no_path == 1)
- dm_queue_if_no_path(mapname, 1);
+ _dm_queue_if_no_path(mapname, 1);
return 1;
}
@@ -1236,8 +1237,8 @@ dm_reinstate_path(const char * mapname, char * path)
return dm_message(mapname, message);
}
-int
-dm_queue_if_no_path(const char *mapname, int enable)
+static int
+_dm_queue_if_no_path(const char *mapname, int enable)
{
char *message;
@@ -1249,6 +1250,20 @@ dm_queue_if_no_path(const char *mapname, int enable)
return dm_message(mapname, message);
}
+int dm_queue_if_no_path(struct multipath *mpp, int enable)
+{
+ int r;
+ static const char no_path_retry[] = "queue_if_no_path";
+
+ if ((r = _dm_queue_if_no_path(mpp->alias, enable)) == 0) {
+ if (enable)
+ add_feature(&mpp->features, no_path_retry);
+ else
+ remove_feature(&mpp->features, no_path_retry);
+ }
+ return r;
+}
+
static int
dm_groupmsg (const char * msg, const char * mapname, int index)
{
diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h
index 808da28d..41b8c31d 100644
--- a/libmultipath/devmapper.h
+++ b/libmultipath/devmapper.h
@@ -58,7 +58,7 @@ int dm_cancel_deferred_remove(struct multipath *mpp);
int dm_flush_maps (int retries);
int dm_fail_path(const char * mapname, char * path);
int dm_reinstate_path(const char * mapname, char * path);
-int dm_queue_if_no_path(const char *mapname, int enable);
+int dm_queue_if_no_path(struct multipath *mpp, int enable);
int dm_switchgroup(const char * mapname, int index);
int dm_enablegroup(const char * mapname, int index);
int dm_disablegroup(const char * mapname, int index);
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 86ad89ca..56915e96 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -579,7 +579,7 @@ static void leave_recovery_mode(struct multipath *mpp)
*/
if (recovery && (mpp->no_path_retry == NO_PATH_RETRY_QUEUE ||
mpp->no_path_retry > 0)) {
- dm_queue_if_no_path(mpp->alias, 1);
+ dm_queue_if_no_path(mpp, 1);
condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
condlog(1, "%s: Recovered to normal mode", mpp->alias);
}
@@ -598,11 +598,11 @@ void __set_no_path_retry(struct multipath *mpp, bool check_features)
break;
case NO_PATH_RETRY_FAIL:
if (!check_features || is_queueing)
- dm_queue_if_no_path(mpp->alias, 0);
+ dm_queue_if_no_path(mpp, 0);
break;
case NO_PATH_RETRY_QUEUE:
if (!check_features || !is_queueing)
- dm_queue_if_no_path(mpp->alias, 1);
+ dm_queue_if_no_path(mpp, 1);
break;
default:
if (count_active_paths(mpp) > 0) {
@@ -612,7 +612,7 @@ void __set_no_path_retry(struct multipath *mpp, bool check_features)
*/
if ((!check_features || !is_queueing) &&
!mpp->in_recovery)
- dm_queue_if_no_path(mpp->alias, 1);
+ dm_queue_if_no_path(mpp, 1);
leave_recovery_mode(mpp);
} else {
/*
@@ -623,7 +623,7 @@ void __set_no_path_retry(struct multipath *mpp, bool check_features)
*/
if ((!check_features || is_queueing) &&
mpp->in_recovery && mpp->retry_tick == 0)
- dm_queue_if_no_path(mpp->alias, 0);
+ dm_queue_if_no_path(mpp, 0);
if (pathcount(mpp, PATH_PENDING) == 0)
enter_recovery_mode(mpp);
}
diff --git a/multipathd/main.c b/multipathd/main.c
index 26be6dc3..74f8114c 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -502,7 +502,7 @@ flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) {
mpp->no_path_retry = NO_PATH_RETRY_FAIL;
mpp->disable_queueing = 1;
mpp->stat_map_failures++;
- dm_queue_if_no_path(mpp->alias, 0);
+ dm_queue_if_no_path(mpp, 0);
}
r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove);
if (r) {
@@ -833,7 +833,7 @@ uev_remove_map (struct uevent * uev, struct vectors * vecs)
goto out;
}
- dm_queue_if_no_path(alias, 0);
+ dm_queue_if_no_path(mpp, 0);
remove_map_and_stop_waiter(mpp, vecs);
out:
lock_cleanup_pop(vecs->lock);
@@ -2015,7 +2015,7 @@ retry_count_tick(vector mpvec)
condlog(4, "%s: Retrying.. No active path", mpp->alias);
if(--mpp->retry_tick == 0) {
mpp->stat_map_failures++;
- dm_queue_if_no_path(mpp->alias, 0);
+ dm_queue_if_no_path(mpp, 0);
condlog(2, "%s: Disable queueing", mpp->alias);
}
}
@@ -3110,7 +3110,7 @@ static void cleanup_maps(struct vectors *vecs)
put_multipath_config(conf);
if (queue_without_daemon == QUE_NO_DAEMON_OFF)
vector_foreach_slot(vecs->mpvec, mpp, i)
- dm_queue_if_no_path(mpp->alias, 0);
+ dm_queue_if_no_path(mpp, 0);
remove_maps_and_stop_waiters(vecs);
vecs->mpvec = NULL;
}

@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 25 Apr 2024 19:35:13 -0400
Subject: [PATCH] libmultipath: export partmap_in_use
A future commit will make use of this function
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/devmapper.c | 2 +-
libmultipath/devmapper.h | 1 +
libmultipath/libmultipath.version | 5 +++++
3 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c
index 5711f0ee..4a66e2c4 100644
--- a/libmultipath/devmapper.c
+++ b/libmultipath/devmapper.c
@@ -1028,7 +1028,7 @@ has_partmap(const char *name __attribute__((unused)),
return 1;
}
-static int
+int
partmap_in_use(const char *name, void *data)
{
int part_count, *ret_count = (int *)data;
diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h
index 41b8c31d..88e0b114 100644
--- a/libmultipath/devmapper.h
+++ b/libmultipath/devmapper.h
@@ -48,6 +48,7 @@ int dm_get_map(const char *, unsigned long long *, char **);
int dm_get_status(const char *, char **);
int dm_type(const char *, char *);
int dm_is_mpath(const char *);
+int partmap_in_use(const char *name, void *data);
int _dm_flush_map (const char *, int, int, int, int);
int dm_flush_map_nopaths(const char * mapname, int deferred_remove);
#define dm_flush_map(mapname) _dm_flush_map(mapname, 1, 0, 0, 0)
diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index 1d018eab..40d9246d 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -302,3 +302,8 @@ LIBMULTIPATH_9.1.2 {
global:
cleanup_mutex;
} LIBMULTIPATH_9.1.1;
+
+LIBMULTIPATH_9.1.3 {
+global:
+ partmap_in_use;
+} LIBMULTIPATH_9.1.2;

@ -0,0 +1,346 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 25 Apr 2024 19:35:14 -0400
Subject: [PATCH] libmultipath: change flush_on_last_del to fix a multipathd
hang
Commit 9bd3482e ("multipathd: make flush_map() delete maps like the
multipath command") fixed an issue where deleting a queueing multipath
device through multipathd could hang because the multipath device had
outstanding IO, even though the only openers of it at the time of
deletion were the kpartx partition devices. However it is still possible
to hang multipathd, because autoremoving the device when all paths have
been deleted doesn't disable queueing. To reproduce this hang:
1. create a multipath device with a kpartx partition on top of it and
no_path_retry set to either "queue" or something long enough to run all
the commands in the reproducer before it disables queueing.
2. disable all the paths to the device with something like:
# echo offline > /sys/block/<path_dev>/device/state
3. Write directly to the multipath device with something like:
# dd if=/dev/zero of=/dev/mapper/<mpath_dev> bs=4K count=1
4. delete all the paths to the device with something like:
# echo 1 > /sys/block/<path_dev>/device/delete
Multipathd will hang trying to delete the kpartx device because, as the
last opener, it must flush the multipath device before closing it.
Because it hangs holding the vecs_lock, multipathd will never disable
queueing on the device, so it will hang forever, even if no_path_retry
is set to a number.
This hang can occur, even if deferred_remove is set. Since nothing has
the kpartx device opened, device-mapper does an immediate remove, which
will still hang. This means that even if deferred_remove is set,
multipathd still cannot delete a map while queueing is enabled. It must
either disable queueing or skip the autoremove.
Mulitpath can currently be configured to avoid this hang by setting
flush_on_last_del yes
However there are good reasons why users wouldn't want to set that. They
may need to be able to survive having all paths getting temporarily
deleted. I should note that this is a pretty rare corner case, since
multipath automatically sets dev_loss_tmo so that it should not trigger
before queueing is disabled.
This commit avoids the hang by changing the possible values for
flush_on_last_del to "never", "unused", and "always", and sets the
default to "unused". "always" works like "yes" did, "never" will not
disable queueing, and "unused" will only disable queueing if nothing has
the kpartx devices or the multipath device open. In order to be safe, if
the device has queue_if_no_paths set (and in case of "unused", the
device is in-use) the autoremove will be skipped. Also, instead of just
trusting the lack of "queue_if_no_paths" in the current mpp->features,
multipathd will tell the kernel to disable queueing, just to be sure it
actually is.
I chose "unused" as the default because this should generally only cause
multipathd to work differently from the users perspective when nothing
has the multipath device open but it is queueing and there is
outstanding IO. Without this change, it would have hung instead of
failing the outstanding IO. However, I do understand that an argument
could be made that "never" makes more sense as default, even though it
will cause multipathd to skip autoremoves in cases where it wouldn't
before. The change to the behavior of deffered_remove will be
noticeable, but skipping an autoremove is much better than hanging.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/defaults.h | 2 +-
libmultipath/dict.c | 72 +++++++++++++++++++++++++++++++++-----
libmultipath/dict.h | 1 +
libmultipath/hwtable.c | 6 ++--
libmultipath/propsel.c | 4 ++-
libmultipath/structs.h | 7 ++--
multipath/multipath.conf.5 | 20 ++++++++---
multipathd/main.c | 39 ++++++++++++++++-----
8 files changed, 122 insertions(+), 29 deletions(-)
diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h
index c3788bbc..5e77387e 100644
--- a/libmultipath/defaults.h
+++ b/libmultipath/defaults.h
@@ -41,7 +41,7 @@
#define DEFAULT_PRIO PRIO_CONST
#define DEFAULT_PRIO_ARGS ""
#define DEFAULT_CHECKER TUR
-#define DEFAULT_FLUSH FLUSH_DISABLED
+#define DEFAULT_FLUSH FLUSH_UNUSED
#define DEFAULT_USER_FRIENDLY_NAMES USER_FRIENDLY_NAMES_OFF
#define DEFAULT_FORCE_SYNC 0
#define UNSET_PARTITION_DELIM "/UNSET/"
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
index ce1b6c99..3c011ece 100644
--- a/libmultipath/dict.c
+++ b/libmultipath/dict.c
@@ -825,14 +825,70 @@ declare_def_snprint(checker_timeout, print_nonzero)
declare_def_handler(allow_usb_devices, set_yes_no)
declare_def_snprint(allow_usb_devices, print_yes_no)
-declare_def_handler(flush_on_last_del, set_yes_no_undef)
-declare_def_snprint_defint(flush_on_last_del, print_yes_no_undef, DEFAULT_FLUSH)
-declare_ovr_handler(flush_on_last_del, set_yes_no_undef)
-declare_ovr_snprint(flush_on_last_del, print_yes_no_undef)
-declare_hw_handler(flush_on_last_del, set_yes_no_undef)
-declare_hw_snprint(flush_on_last_del, print_yes_no_undef)
-declare_mp_handler(flush_on_last_del, set_yes_no_undef)
-declare_mp_snprint(flush_on_last_del, print_yes_no_undef)
+
+static const char * const flush_on_last_del_optvals[] = {
+ [FLUSH_NEVER] = "never",
+ [FLUSH_ALWAYS] = "always",
+ [FLUSH_UNUSED] = "unused",
+};
+
+static int
+set_flush_on_last_del(vector strvec, void *ptr, const char *file, int line_nr)
+{
+ int i;
+ int *flush_val_ptr = (int *)ptr;
+ char *buff;
+
+ buff = set_value(strvec);
+ if (!buff)
+ return 1;
+
+ for (i = FLUSH_NEVER; i <= FLUSH_UNUSED; i++) {
+ if (flush_on_last_del_optvals[i] != NULL &&
+ !strcmp(buff, flush_on_last_del_optvals[i])) {
+ *flush_val_ptr = i;
+ break;
+ }
+ }
+
+ if (i > FLUSH_UNUSED) {
+ bool deprecated = true;
+ if (strcmp(buff, "no") == 0 || strcmp(buff, "0") == 0)
+ *flush_val_ptr = FLUSH_UNUSED;
+ else if (strcmp(buff, "yes") == 0 || strcmp(buff, "1") == 0)
+ *flush_val_ptr = FLUSH_ALWAYS;
+ else {
+ deprecated = false;
+ condlog(1, "%s line %d, invalid value for flush_on_last_del: \"%s\"",
+ file, line_nr, buff);
+ }
+ if (deprecated)
+ condlog(3, "%s line %d, \"%s\" is a deprecated value for flush_on_last_del and is treated as \"%s\"",
+ file, line_nr, buff,
+ flush_on_last_del_optvals[*flush_val_ptr]);
+ }
+
+ free(buff);
+ return 0;
+}
+
+int
+print_flush_on_last_del(struct strbuf *buff, long v)
+{
+ if (v == FLUSH_UNDEF)
+ return 0;
+ return append_strbuf_quoted(buff, flush_on_last_del_optvals[(int)v]);
+}
+
+declare_def_handler(flush_on_last_del, set_flush_on_last_del)
+declare_def_snprint_defint(flush_on_last_del, print_flush_on_last_del,
+ DEFAULT_FLUSH)
+declare_ovr_handler(flush_on_last_del, set_flush_on_last_del)
+declare_ovr_snprint(flush_on_last_del, print_flush_on_last_del)
+declare_hw_handler(flush_on_last_del, set_flush_on_last_del)
+declare_hw_snprint(flush_on_last_del, print_flush_on_last_del)
+declare_mp_handler(flush_on_last_del, set_flush_on_last_del)
+declare_mp_snprint(flush_on_last_del, print_flush_on_last_del)
declare_def_handler(user_friendly_names, set_yes_no_undef)
declare_def_snprint_defint(user_friendly_names, print_yes_no_undef,
diff --git a/libmultipath/dict.h b/libmultipath/dict.h
index d963b4ad..6b1aae5c 100644
--- a/libmultipath/dict.h
+++ b/libmultipath/dict.h
@@ -20,4 +20,5 @@ int print_reservation_key(struct strbuf *buff,
struct be64 key, uint8_t flags, int source);
int print_off_int_undef(struct strbuf *buff, long v);
int print_auto_resize(struct strbuf *buff, long v);
+int print_flush_on_last_del(struct strbuf *buff, long v);
#endif /* _DICT_H */
diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c
index 78ac7988..94012d50 100644
--- a/libmultipath/hwtable.c
+++ b/libmultipath/hwtable.c
@@ -60,7 +60,7 @@
.no_path_retry = NO_PATH_RETRY_UNDEF,
.minio = 1000,
.minio_rq = 1,
- .flush_on_last_del = FLUSH_DISABLED,
+ .flush_on_last_del = FLUSH_UNUSED,
.user_friendly_names = USER_FRIENDLY_NAMES_OFF,
.fast_io_fail = 5,
.dev_loss = 600,
@@ -800,7 +800,7 @@ static struct hwentry default_hw[] = {
.no_path_retry = NO_PATH_RETRY_QUEUE,
.pgpolicy = GROUP_BY_PRIO,
.pgfailback = -FAILBACK_IMMEDIATE,
- .flush_on_last_del = FLUSH_ENABLED,
+ .flush_on_last_del = FLUSH_ALWAYS,
.dev_loss = MAX_DEV_LOSS_TMO,
.prio_name = PRIO_ONTAP,
.user_friendly_names = USER_FRIENDLY_NAMES_OFF,
@@ -1122,7 +1122,7 @@ static struct hwentry default_hw[] = {
.no_path_retry = NO_PATH_RETRY_FAIL,
.minio = 1,
.minio_rq = 1,
- .flush_on_last_del = FLUSH_ENABLED,
+ .flush_on_last_del = FLUSH_ALWAYS,
.fast_io_fail = 15,
.dev_loss = 15,
},
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
index 9dea6f92..be781ff7 100644
--- a/libmultipath/propsel.c
+++ b/libmultipath/propsel.c
@@ -902,6 +902,7 @@ out:
int select_flush_on_last_del(struct config *conf, struct multipath *mp)
{
const char *origin;
+ STRBUF_ON_STACK(buff);
mp_set_mpe(flush_on_last_del);
mp_set_ovr(flush_on_last_del);
@@ -909,8 +910,9 @@ int select_flush_on_last_del(struct config *conf, struct multipath *mp)
mp_set_conf(flush_on_last_del);
mp_set_default(flush_on_last_del, DEFAULT_FLUSH);
out:
+ print_flush_on_last_del(&buff, mp->flush_on_last_del);
condlog(3, "%s: flush_on_last_del = %s %s", mp->alias,
- (mp->flush_on_last_del == FLUSH_ENABLED)? "yes" : "no", origin);
+ get_strbuf_str(&buff), origin);
return 0;
}
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index d2ad4867..4bf8c93a 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -109,9 +109,10 @@ enum marginal_pathgroups_mode {
};
enum flush_states {
- FLUSH_UNDEF = YNU_UNDEF,
- FLUSH_DISABLED = YNU_NO,
- FLUSH_ENABLED = YNU_YES,
+ FLUSH_UNDEF,
+ FLUSH_NEVER,
+ FLUSH_ALWAYS,
+ FLUSH_UNUSED,
};
enum log_checker_err_states {
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
index 38eb5c90..10eddc0c 100644
--- a/multipath/multipath.conf.5
+++ b/multipath/multipath.conf.5
@@ -672,12 +672,22 @@ The default is: \fBno\fR
.TP
.B flush_on_last_del
If set to
-.I yes
+.I always
, multipathd will disable queueing when the last path to a device has been
-deleted.
-.RS
-.TP
-The default is: \fBno\fR
+deleted. If set to
+.I never
+, multipathd will not disable queueing when the last path to a device has
+been deleted. Since multipath cannot safely remove a device while queueing
+is enabled, setting this to \fInever\fR means that multipathd will not
+automatically remove an unused multipath device whose paths are all deleted if
+it is currently set to queue_if_no_path. If set to
+.I unused
+, multipathd will only disable queueing when the last path is removed if
+nothing currently has the multipath device or any of the kpartx partition
+devices on top of it open.
+.RS
+.TP
+The default is: \fBunused\fR
.RE
.
.
diff --git a/multipathd/main.c b/multipathd/main.c
index 74f8114c..8ec58f5d 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -491,19 +491,42 @@ int update_multipath (struct vectors *vecs, char *mapname, int reset)
static bool
flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) {
int r;
+ bool is_queueing = true;
+ if (mpp->features)
+ is_queueing = strstr(mpp->features, "queue_if_no_path");
+
+ /* It's not safe to do a remove of a map that has "queue_if_no_path"
+ * set, since there could be outstanding IO which will cause
+ * multipathd to hang while attempting the remove */
+ if (mpp->flush_on_last_del == FLUSH_NEVER && is_queueing) {
+ condlog(2, "%s: map is queueing, can't remove", mpp->alias);
+ return false;
+ }
+ if (mpp->flush_on_last_del == FLUSH_UNUSED &&
+ partmap_in_use(mpp->alias, NULL) && is_queueing) {
+ condlog(2, "%s: map in use and queueing, can't remove",
+ mpp->alias);
+ return false;
+ }
/*
- * flush_map will fail if the device is open
+ * This will flush FLUSH_NEVER devices and FLUSH_UNUSED devices
+ * that are in use, but only if they are already marked as not
+ * queueing. That is just to make absolutely certain that they
+ * really are not queueing, like they claim.
*/
- if (mpp->flush_on_last_del == FLUSH_ENABLED) {
- condlog(2, "%s Last path deleted, disabling queueing",
+ condlog(is_queueing ? 2 : 3, "%s Last path deleted, disabling queueing",
+ mpp->alias);
+ mpp->retry_tick = 0;
+ mpp->no_path_retry = NO_PATH_RETRY_FAIL;
+ mpp->disable_queueing = 1;
+ mpp->stat_map_failures++;
+ if (dm_queue_if_no_path(mpp, 0) != 0) {
+ condlog(0, "%s: failed to disable queueing. Not removing",
mpp->alias);
- mpp->retry_tick = 0;
- mpp->no_path_retry = NO_PATH_RETRY_FAIL;
- mpp->disable_queueing = 1;
- mpp->stat_map_failures++;
- dm_queue_if_no_path(mpp, 0);
+ return false;
}
+
r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove);
if (r) {
if (r == 1)

@ -0,0 +1,40 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 25 Apr 2024 19:35:16 -0400
Subject: [PATCH] libmultipath: pad dev_loss_tmo to avoid race with
no_path_retry
Currently multipath makes sure that dev_loss_tmo is at least as long as
the configured no path queueing time. The goal is to make sure that path
devices aren't removed while the multipath device is still queueing in
hopes that they will become usable again.
This is racy. Multipathd may take longer to check the paths than
configured. If strict_timing isn't set, it will definitely take longer.
To account for this, pad the minimum dev_loss_tmo value by five seconds
(one default checker interval) plus one second per minute of no path
queueing time.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/discovery.c | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
index ae7eb7e6..b24594cd 100644
--- a/libmultipath/discovery.c
+++ b/libmultipath/discovery.c
@@ -884,6 +884,11 @@ sysfs_set_scsi_tmo (struct config *conf, struct multipath *mpp)
uint64_t no_path_retry_tmo =
(uint64_t)mpp->no_path_retry * conf->checkint;
+ /* pad no_path_retry_tmo by one standard check interval
+ * plus one second per minute to account for timing
+ * issues with the rechecks */
+ no_path_retry_tmo += no_path_retry_tmo / 60 + DEFAULT_CHECKINT;
+
if (no_path_retry_tmo > MAX_DEV_LOSS_TMO)
min_dev_loss = MAX_DEV_LOSS_TMO;
else

@ -0,0 +1,32 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Nitin Yewale <nyewale@redhat.com>
Date: Thu, 12 Jan 2023 14:28:49 -0600
Subject: [PATCH] libmultipath: remove pathgroup wildcard options
The multipathd command "multipathd show wildcards" shows the pathgroup
format wildcards, but there is no way to use them in a multipathd
command.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/print.c | 7 -------
1 file changed, 7 deletions(-)
diff --git a/libmultipath/print.c b/libmultipath/print.c
index 082e4e30..a6e4c774 100644
--- a/libmultipath/print.c
+++ b/libmultipath/print.c
@@ -803,13 +803,6 @@ int snprint_wildcards(struct strbuf *buff)
pd[i].wildcard, pd[i].header)) < 0)
return rc;
- if ((rc = append_strbuf_str(buff, "\npathgroup format wildcards:\n")) < 0)
- return rc;
- for (i = 0; pgd[i].header; i++)
- if ((rc = print_strbuf(buff, "%%%c %s\n",
- pgd[i].wildcard, pgd[i].header)) < 0)
- return rc;
-
return get_strbuf_len(buff) - initial_len;
}

@ -0,0 +1,32 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 8 May 2024 19:02:30 -0400
Subject: [PATCH] libmultipath: print all values in snprint_failback
Add the missing output for manual failback and print the defferral time
for deferred failbacks, if one isn't currently in progress.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/print.c | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/libmultipath/print.c b/libmultipath/print.c
index a6e4c774..535e0271 100644
--- a/libmultipath/print.c
+++ b/libmultipath/print.c
@@ -199,9 +199,13 @@ snprint_failback (struct strbuf *buff, const struct multipath * mpp)
return append_strbuf_str(buff, "immediate");
if (mpp->pgfailback == -FAILBACK_FOLLOWOVER)
return append_strbuf_str(buff, "followover");
+ if (mpp->pgfailback == -FAILBACK_MANUAL)
+ return append_strbuf_str(buff, "manual");
+ if (mpp->pgfailback == FAILBACK_UNDEF)
+ return append_strbuf_str(buff, "undef");
if (!mpp->failback_tick)
- return append_strbuf_str(buff, "-");
+ return print_strbuf(buff, "%i", mpp->pgfailback);
else
return snprint_progress(buff, mpp->failback_tick,
mpp->pgfailback);

@ -0,0 +1,34 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 9 May 2024 12:58:11 -0400
Subject: [PATCH] multipathd: Stop double counting map failures for
no_path_retry > 0
If no_path_retry was greater than 0, multipathd was counting a map
failure when recovery mode was entered, and again when queueing was
disabled. The first one is incorrect, since the map is still queueing.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/structs_vec.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
index 56915e96..b8e304e0 100644
--- a/libmultipath/structs_vec.c
+++ b/libmultipath/structs_vec.c
@@ -781,10 +781,13 @@ int verify_paths(struct multipath *mpp)
void update_queue_mode_del_path(struct multipath *mpp)
{
int active = count_active_paths(mpp);
+ bool is_queueing = mpp->features &&
+ strstr(mpp->features, "queue_if_no_path");
if (active == 0) {
enter_recovery_mode(mpp);
- if (mpp->no_path_retry != NO_PATH_RETRY_QUEUE)
+ if (mpp->no_path_retry == NO_PATH_RETRY_FAIL ||
+ (mpp->no_path_retry == NO_PATH_RETRY_UNDEF && !is_queueing))
mpp->stat_map_failures++;
}
condlog(2, "%s: remaining active paths: %d", mpp->alias, active);

@ -0,0 +1,102 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 9 May 2024 17:15:34 -0400
Subject: [PATCH] multipath-tools man pages: add missing multipathd commands
Also, the description for "del map $map" was incorrect.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/multipathd.8 | 42 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 37 insertions(+), 5 deletions(-)
diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8
index 8bd47a80..40a8dc6d 100644
--- a/multipathd/multipathd.8
+++ b/multipathd/multipathd.8
@@ -100,18 +100,24 @@ The following commands can be used in interactive mode:
Show the paths that multipathd is monitoring, and their state.
.
.TP
-.B list|show paths format $format
+.B list|show paths [raw] format $format
Show the paths that multipathd is monitoring, using a format string with path
-format wildcards.
+format wildcards. Adding \fIraw\fR will remove the headers and alignment
+padding from the ouput.
+.
+.TP
+.B list|show path $path
+Show whether path $path is offline or running.
.
.TP
.B list|show maps|multipaths
Show the multipath devices that the multipathd is monitoring.
.
.TP
-.B list|show maps|multipaths format $format
+.B list|show maps|multipaths [raw] format $format
Show the status of all multipath devices that the multipathd is monitoring,
-using a format string with multipath format wildcards.
+using a format string with multipath format wildcards. Adding \fIraw\fR will
+remove the headers and alignment padding from the output.
.
.TP
.B list|show maps|multipaths status
@@ -124,6 +130,10 @@ Show some statistics of all multipath devices that the multipathd is monitoring.
.TP
.B list|show maps|multipaths topology
Show the current multipath topology. Same as '\fImultipath \-ll\fR'.
+.TP
+.
+.B list|show maps|multipaths json
+Show information about all multipath devices in JSON format.
.
.TP
.B list|show topology
@@ -135,6 +145,16 @@ Show topology of a single multipath device specified by $map, for example
36005076303ffc56200000000000010aa. This map could be obtained from '\fIlist maps\fR'.
.
.TP
+.B list|show map|multipath $map [raw] format $format.
+Show the status of multipath device $map, using a format string with multipath
+format wildcards. Adding \fIraw\fR will remove the headers and alignment
+padding from the output.
+.
+.TP
+.B list|show map|multipath $map json
+Show information about multipath device $map in JSON format.
+.
+.TP
.B list|show wildcards
Show the format wildcards used in interactive commands taking $format.
.
@@ -168,6 +188,14 @@ paths, and whether multipathd is currently handling a uevent.
Show the current state of the multipathd daemon.
.
.TP
+.B reset maps|multipaths stats
+Reset the statistics of all multipath devices.
+.
+.TP
+.B reset map|multipath $map stats
+Reset the statistics of multipath device $map.
+.
+.TP
.B add path $path
Add a path to the list of monitored paths. $path is as listed in /sys/block (e.g. sda).
.
@@ -183,8 +211,12 @@ for the multipath device (e.g. mpath1) or the uid of the multipath device
(e.g. 36005076303ffc56200000000000010aa).
.
.TP
+.B remove|del maps|multipaths
+Remove all multipath devices.
+.
+.TP
.B remove|del map|multipath $map
-Stop monitoring a multipath device.
+Remove the multipath device $map.
.
.TP
.B resize map|multipath $map

@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 10 May 2024 15:36:10 -0400
Subject: [PATCH] libmultipath: change the vend/prod/rev printing
The %s multipath and path wildcards both say they print the device
vend/prod/rev string, but neither of them do. The multipath wildcards
already provide a way to print the revision string and the %s wildcard
is used in the multipath -l output, so leave the wildcard output alone,
and change the description to only mention the vendor and product. There
is no other way to print the revision by path, and the path %s wildcard
is only used in the verbose multipath output, so make it actually print
the revision. Also check for unset strings, and print "##" instead of
nothing.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/print.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/libmultipath/print.c b/libmultipath/print.c
index 535e0271..4552fd43 100644
--- a/libmultipath/print.c
+++ b/libmultipath/print.c
@@ -309,7 +309,7 @@ snprint_multipath_uuid (struct strbuf *buff, const struct multipath * mpp)
}
static int
-snprint_multipath_vpr (struct strbuf *buff, const struct multipath * mpp)
+snprint_multipath_vp (struct strbuf *buff, const struct multipath * mpp)
{
struct pathgroup * pgp;
struct path * pp;
@@ -511,7 +511,10 @@ snprint_dm_path_state (struct strbuf *buff, const struct path * pp)
static int
snprint_vpr (struct strbuf *buff, const struct path * pp)
{
- return print_strbuf(buff, "%s,%s", pp->vendor_id, pp->product_id);
+ return print_strbuf(buff, "%s,%s,%s",
+ strlen(pp->vendor_id) ? pp->vendor_id : "##",
+ strlen(pp->product_id) ? pp->product_id : "##",
+ strlen(pp->rev) ? pp->rev : "##");
}
static int
@@ -743,7 +746,7 @@ struct multipath_data mpd[] = {
{'2', "map_loads", 0, snprint_map_loads},
{'3', "total_q_time", 0, snprint_total_q_time},
{'4', "q_timeouts", 0, snprint_q_timeouts},
- {'s', "vend/prod/rev", 0, snprint_multipath_vpr},
+ {'s', "vend/prod", 0, snprint_multipath_vp},
{'v', "vend", 0, snprint_multipath_vend},
{'p', "prod", 0, snprint_multipath_prod},
{'e', "rev", 0, snprint_multipath_rev},

@ -0,0 +1,243 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 10 May 2024 20:53:33 -0400
Subject: [PATCH] multipath-tools man pages: Add format wildcard descriptions
Suggested-by: Nitin Yewale <nyewale@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/multipathd.8 | 193 +++++++++++++++++++++++++++++++++++++++-
1 file changed, 189 insertions(+), 4 deletions(-)
diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8
index 40a8dc6d..d834f89e 100644
--- a/multipathd/multipathd.8
+++ b/multipathd/multipathd.8
@@ -103,7 +103,7 @@ Show the paths that multipathd is monitoring, and their state.
.B list|show paths [raw] format $format
Show the paths that multipathd is monitoring, using a format string with path
format wildcards. Adding \fIraw\fR will remove the headers and alignment
-padding from the ouput.
+padding from the output. See "Path format wildcards" below.
.
.TP
.B list|show path $path
@@ -117,7 +117,8 @@ Show the multipath devices that the multipathd is monitoring.
.B list|show maps|multipaths [raw] format $format
Show the status of all multipath devices that the multipathd is monitoring,
using a format string with multipath format wildcards. Adding \fIraw\fR will
-remove the headers and alignment padding from the output.
+remove the headers and alignment padding from the output. See "Multipath
+format wildcards" below.
.
.TP
.B list|show maps|multipaths status
@@ -148,7 +149,7 @@ Show topology of a single multipath device specified by $map, for example
.B list|show map|multipath $map [raw] format $format.
Show the status of multipath device $map, using a format string with multipath
format wildcards. Adding \fIraw\fR will remove the headers and alignment
-padding from the output.
+padding from the output. See "Multipath format wildcards" below.
.
.TP
.B list|show map|multipath $map json
@@ -156,7 +157,8 @@ Show information about multipath device $map in JSON format.
.
.TP
.B list|show wildcards
-Show the format wildcards used in interactive commands taking $format.
+Show the format wildcards used in interactive commands taking $format. See
+"Format Wildcards" below.
.
.TP
.B list|show config
@@ -339,6 +341,189 @@ Stop multipathd.
.
.
.\" ----------------------------------------------------------------------------
+.SH "Format Wildcards"
+.\" ----------------------------------------------------------------------------
+.
+Multipathd commands that take a $format option require a format string. This
+string controls how a device is printed and should include format wildcards.
+When the devices are printed, these wildcards will be replaced by the
+appropriate device information. The following wildcards are supported.
+.TP
+.B Multipath format wildcards
+.RS
+.TP 12
+.B %n
+The device name.
+.TP
+.B %w
+The device WWID (uuid).
+.TP
+.B %d
+The device sysfs name (dm-<minor_nr>).
+.TP
+.B %F
+The device \fBfailback\fR setting. For deferred failbacks, it will either
+print the configured time if a deferred failback is not in progress, or
+it will show the current progress of a deferred failback.
+.TP
+.B %Q
+The device \fBno_path_retry\fR setting. If no_path_retry is set to a
+number of retires, it will either print the configured number of checker
+retries if the device is not in recovery mode, the number of seconds until
+queueing is disabled if the device is queueing in recovery mode, or \fIoff\fR
+if the device has disabled queueing.
+.TP
+.B %N
+The number of active paths for the device.
+.TP
+.B %r
+The device write-protect setting, either \fIro\fR or \fIrw\fR.
+.TP
+.B %t
+The device-mapper state of the device, either \fIsuspend\fR or \fIactive\fR.
+.TP
+.B %S
+The device size.
+.TP
+.B %f
+The device table features string.
+.TP
+.B %x
+The number of times the device has entered a state where it will fail IO.
+This is an alias for the \fB%4\fR wildcard.
+This value can be reset with the '\fIreset map $map stats\fR' command.
+.TP
+.B %h
+The device table hardware handler string.
+.TP
+.B %A
+The last action multipathd took on the device. This wildcard is for debugging
+use, as understanding its meaning requires looking at the code.
+.TP
+.B %0
+The number of times a path in the device has failed.
+This value can be reset with the '\fIreset map $map stats\fR' command.
+.TP
+.B %1
+The number of times multipathd has initiated a pathgroup switch for the device.
+This value can be reset with the '\fIreset map $map stats\fR' command.
+.TP
+.B %2
+The number of times multipathd has loaded a new table for the device.
+This value can be reset with the '\fIreset map $map stats\fR' command.
+.TP
+.B %3
+The approximate number of seconds that multipathd has spent queueing with
+no usable paths. This value can be reset with the '\fIreset map $map stats\fR'
+command.
+.TP
+.B %4
+The number of times the device has entered a state where it will fail IO.
+This is an alias for the \fB%x\fR wildcard.
+This value can be reset with the '\fIreset map $map stats\fR' command.
+.TP
+.B %s
+The vendor/product string for the device.
+.TP
+.B %v
+The array vendor string for the device.
+.TP
+.B %p
+The array product string for the device.
+.TP
+.B %e
+The array firmware revision string for the device.
+.TP
+.B %G
+The foreign library used for the device, or \fB--\fR for native device-mapper
+multipath devices.
+.TP
+.B %g
+Data from vendor specific vpd pages for the device, if any.
+.RE
+.
+.
+.TP
+.B Path format wildcards
+.RS
+.TP 12
+.B %w
+The device WWID (uuid).
+.TP
+.B %i
+The device Host:Channel:Id:Lun
+.TP
+.B %d
+The device sysfs name.
+.TP
+.B %D
+The device major:minor
+.TP
+.B %t
+The device-mapper state of the device, either \fIactive\fR or \fIfailed\fR.
+.TP
+.B %o
+Whether the device is \fIoffline\fR or \fIrunning\fR.
+.TP
+.B %T
+The multipathd path checker state of the device.
+.TP
+.B %s
+The vendor/product/revision string for the device.
+.TP
+.B %c
+The device's path checker name.
+.TP
+.B %C
+The progress towards the next path checker run on the device.
+.TP
+.B %p
+The device priority.
+.TP
+.B %S
+The device size.
+.TP
+.B %z
+The device serial number.
+.TP
+.B %M
+The device marginal state, either \fImarginal\fR or \fInormal\fR.
+.TP
+.B %m
+The multipath device that this device is a path of.
+.TP
+.B %N
+The host World Wide Node Name (WWNN) of the device.
+.TP
+.B %n
+The target World Wide Node Name (WWNN) of the device.
+.TP
+.B %R
+The host World Wide Port Name (WWPN) of the device.
+.TP
+.B %r
+The target World Wide Port Name (WWPN) of the device.
+.TP
+.B %a
+The host adapter name for the device (only SCSI devices).
+.TP
+.B %G
+The foreign library used for the device, or \fB--\fR for paths of native
+device-mapper multipath devices.
+.TP
+.B %g
+Data from vendor specific vpd pages for the device, if any.
+.TP
+.B %0
+The number of times this device has failed.
+.TP
+.B %P
+The device protocol. This output can be used for \fIprotocol\fR blacklist
+entries.
+.RE
+.
+.
+.\" ----------------------------------------------------------------------------
.SH "SYSTEMD INTEGRATION"
.\" ----------------------------------------------------------------------------
.

@ -0,0 +1,103 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: chengjike <chengjike.cheng@huawei.com>
Date: Fri, 8 Oct 2021 20:24:49 +0800
Subject: [PATCH] multipath-tools: fix "multipath -ll" bug for Native NVME
Multipath devices
After "Native NVME Multipath" is configured,
the content displayed is incorrect when you run "multipath -ll" command.
Each NVME devices have the same path name. For example:
[root@localhost home]# multipath -ll
eui.710032e8fb22a86c24a52c1000000db8 [nvme]:nvme1n1 NVMe,Huawei-XSG1,1000001
size=10485760 features='n/a' hwhandler='ANA' wp=rw
|-+- policy='n/a' prio=50 status=optimized
| `- 1:4:1 nvme1c4n1 0:0 n/a optimized live
`-+- policy='n/a' prio=50 status=optimized
`- 1:9:1 nvme1c9n1 0:0 n/a optimized live
eui.710032e8fb22a86b24a52c7c00000db7 [nvme]:nvme1n2 NVMe,Huawei-XSG1,1000001
size=10485760 features='n/a' hwhandler='ANA' wp=rw
|-+- policy='n/a' prio=50 status=optimized
| `- 1:4:1 nvme1c4n1 0:0 n/a optimized live
`-+- policy='n/a' prio=50 status=optimized
`- 1:9:1 nvme1c9n1 0:0 n/a optimized live
[root@localhost home]#
The logical paths of "nvme1n1" and "nvme1n2" are both "nvme1c4n1" and "nvme1c9n1".
So when multipath-tools aggregates disks, use "nvme_ns_head->instance" for matching.
such as ,Use "b" in "nvmeanb" string to match "z" in "nvmexcynz"(a,b,x,y,z can be any number),
and if "b" and "z" are the same, they are related.
Signed-off-by: chengjike <chengjike.cheng@huawei.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/foreign/nvme.c | 26 ++++++++++++++++++++------
1 file changed, 20 insertions(+), 6 deletions(-)
diff --git a/libmultipath/foreign/nvme.c b/libmultipath/foreign/nvme.c
index 23355ca5..76b57283 100644
--- a/libmultipath/foreign/nvme.c
+++ b/libmultipath/foreign/nvme.c
@@ -530,14 +530,18 @@ static int _dirent_controller(const struct dirent *di)
/* Find the block device for a given nvme controller */
struct udev_device *get_ctrl_blkdev(const struct context *ctx,
- struct udev_device *ctrl)
+ struct udev_device *ctrl, const char *ctrl_name)
{
+ int ctrl_num, ns_num;
struct udev_list_entry *item;
struct udev_device *blkdev = NULL;
struct udev_enumerate *enm = udev_enumerate_new(ctx->udev);
const char *devtype;
- if (enm == NULL)
+ if (enm == NULL || ctrl_name == NULL)
+ return NULL;
+
+ if (sscanf(ctrl_name, "nvme%dn%d", &ctrl_num, &ns_num) != 2)
return NULL;
pthread_cleanup_push(_udev_enumerate_unref, enm);
@@ -555,6 +559,8 @@ struct udev_device *get_ctrl_blkdev(const struct context *ctx,
item != NULL;
item = udev_list_entry_get_next(item)) {
struct udev_device *tmp;
+ const char *name = NULL ;
+ int m, n, l;
tmp = udev_device_new_from_syspath(ctx->udev,
udev_list_entry_get_name(item));
@@ -562,11 +568,19 @@ struct udev_device *get_ctrl_blkdev(const struct context *ctx,
continue;
devtype = udev_device_get_devtype(tmp);
- if (devtype && !strcmp(devtype, "disk")) {
+ if (devtype == NULL || strcmp(devtype, "disk")) {
+ udev_device_unref(tmp);
+ continue;
+ }
+
+ name = udev_device_get_sysname(tmp);
+ if (name != NULL &&
+ sscanf(name, "nvme%dc%dn%d", &m, &n, &l) == 3 &&
+ l == ns_num) {
blkdev = tmp;
break;
- } else
- udev_device_unref(tmp);
+ }
+ udev_device_unref(tmp);
}
if (blkdev == NULL)
@@ -679,7 +693,7 @@ static void _find_controllers(struct context *ctx, struct nvme_map *map)
}
pthread_cleanup_push(_udev_device_unref, ctrl);
- udev = get_ctrl_blkdev(ctx, ctrl);
+ udev = get_ctrl_blkdev(ctx, ctrl, udev_device_get_sysname(map->udev));
/*
* We give up the reference to the nvme device here and get
* it back from the child below.

@ -0,0 +1,29 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 24 Jul 2024 16:38:52 -0400
Subject: [PATCH] multipathd: set reply length to zero for NULL replies
The multipathd cli handlers add one to the reply length to account for
NULL byte, but if the reply is NULL, there is no NULL by to account for,
and len should be 0, so that mutipathd replies to clients with a
default reply.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/cli.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/multipathd/cli.c b/multipathd/cli.c
index eb2e8c1d..03ad0d64 100644
--- a/multipathd/cli.c
+++ b/multipathd/cli.c
@@ -496,6 +496,9 @@ parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout )
r = h->fn(cmdvec, reply, len, data);
free_keys(cmdvec);
+ if (*reply == NULL)
+ *len = 0;
+
return r;
}

@ -1,6 +1,6 @@
Name: device-mapper-multipath
Version: 0.8.7
Release: 22%{?dist}
Release: 32%{?dist}
Summary: Tools to manage multipath devices using device-mapper
License: GPLv2
URL: http://christophe.varoqui.free.fr/
@ -100,7 +100,37 @@ 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
Patch0108: 0108-libmultipath-actually-truncate-too-large-vpd-page.patch
Patch0109: 0109-kpartx-fix-theoretical-overflow-in-loop-device-name.patch
Patch0110: 0110-libmultipath-keep-track-of-queueing-state-in-feature.patch
Patch0111: 0111-libmultipath-export-partmap_in_use.patch
Patch0112: 0112-libmultipath-change-flush_on_last_del-to-fix-a-multi.patch
Patch0113: 0113-libmultipath-pad-dev_loss_tmo-to-avoid-race-with-no_.patch
Patch0114: 0114-libmultipath-remove-pathgroup-wildcard-options.patch
Patch0115: 0115-libmultipath-print-all-values-in-snprint_failback.patch
Patch0116: 0116-multipathd-Stop-double-counting-map-failures-for-no_.patch
Patch0117: 0117-multipath-tools-man-pages-add-missing-multipathd-com.patch
Patch0118: 0118-libmultipath-change-the-vend-prod-rev-printing.patch
Patch0119: 0119-multipath-tools-man-pages-Add-format-wildcard-descri.patch
Patch0120: 0120-multipath-tools-fix-multipath-ll-bug-for-Native-NVME.patch
Patch0121: 0121-multipathd-set-reply-length-to-zero-for-NULL-replies.patch
# runtime
@ -243,6 +273,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 +334,98 @@ fi
%{_pkgconfdir}/libdmmp.pc
%changelog
* Mon Aug 5 2024 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-32
- Modify bindings, find_multipaths, and user_friendly_names tests
* Fixes RHEL-28068 & RHEL-4459
- Resolves: RHEL-28068
- Resolves: RHEL-44569
* Tue Jul 30 2024 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-31
- Modify multiple tests especially medium_error_scsi_debug and squelch_scsi_id
* Fixes RHEL-28068 & RHEL-4459
- Resolves: RHEL-28068
- Resolves: RHEL-44569
* Tue Jul 30 2024 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-30
- Add 0120-multipath-tools-fix-multipath-ll-bug-for-Native-NVME.patch
* Fixes RHEL-28068
- Add 0121-multipathd-set-reply-length-to-zero-for-NULL-replies.patch
* Fixes RHEL-44569
- Resolves: RHEL-28068
- Resolves: RHEL-44569
* Tue May 21 2024 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-29
- Add 0110-libmultipath-keep-track-of-queueing-state-in-feature.patch
- Add 0111-libmultipath-export-partmap_in_use.patch
- Add 0112-libmultipath-change-flush_on_last_del-to-fix-a-multi.patch
- Add 0113-libmultipath-pad-dev_loss_tmo-to-avoid-race-with-no_.patch
* Fixes RHEL-30272
- Add 0114-libmultipath-remove-pathgroup-wildcard-options.patch
- Add 0115-libmultipath-print-all-values-in-snprint_failback.patch
- Add 0116-multipathd-Stop-double-counting-map-failures-for-no_.patch
- Add 0117-multipath-tools-man-pages-add-missing-multipathd-com.patch
- Add 0118-libmultipath-change-the-vend-prod-rev-printing.patch
- Add 0119-multipath-tools-man-pages-Add-format-wildcard-descri.patch
* Fixes RHEL-8304
- Resolves: RHEL-8304
- Resolves: RHEL-30272
* Tue Apr 9 2024 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-28
- Add 0108-libmultipath-actually-truncate-too-large-vpd-page.patch
- Add 0109-kpartx-fix-theoretical-overflow-in-loop-device-name.patch
* Fixes RHEL-31793 ("RHEL SAST Automation: address (selected) true positives")
- Resolves: RHEL-31793
* Fri Jan 26 2024 Benjamin Marzinski <bmarzins@redhat.com> - 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 <bmarzins@redhat.com> - 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 <bmarzins@redhat.com> - 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 <bmarzins@redhat.com> - 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 <bmarzins@redhat.com> - 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 <bmarzins@redhat.com> - 0.8.7-22
- Add 0089-RH-Add-mpathcleanup.patch
* Fixes RHEL-782

Loading…
Cancel
Save