Compare commits
No commits in common. 'c9-beta' and 'c9' have entirely different histories.
@ -1,300 +0,0 @@
|
|||||||
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);
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
|||||||
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",
|
|
@ -1,70 +0,0 @@
|
|||||||
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>
|
|
@ -1,182 +0,0 @@
|
|||||||
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
|
|
@ -1,29 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,68 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
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
|
|
@ -1,106 +0,0 @@
|
|||||||
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, ¶ms, vecs) != 0) {
|
|
||||||
- condlog(0, "%s: failed to setup map for resize : %s",
|
|
||||||
- mpp->alias, strerror(errno));
|
|
||||||
- mpp->size = orig_size;
|
|
||||||
- return 1;
|
|
||||||
- }
|
|
||||||
- mpp->action = ACT_RESIZE;
|
|
||||||
- mpp->force_udev_reload = 1;
|
|
||||||
- if (domap(mpp, params, 1) == DOMAP_FAIL) {
|
|
||||||
- condlog(0, "%s: failed to resize map : %s", mpp->alias,
|
|
||||||
- strerror(errno));
|
|
||||||
- mpp->size = orig_size;
|
|
||||||
- return 1;
|
|
||||||
- }
|
|
||||||
- if (setup_multipath(vecs, mpp) != 0)
|
|
||||||
- return 2;
|
|
||||||
- sync_map_state(mpp);
|
|
||||||
-
|
|
||||||
- return 0;
|
|
||||||
-}
|
|
||||||
-
|
|
||||||
int
|
|
||||||
cli_resize(void *v, char **reply, int *len, void *data)
|
|
||||||
{
|
|
||||||
diff --git a/multipathd/main.c b/multipathd/main.c
|
|
||||||
index 075e7b13..d99cad72 100644
|
|
||||||
--- a/multipathd/main.c
|
|
||||||
+++ b/multipathd/main.c
|
|
||||||
@@ -1379,6 +1379,35 @@ needs_ro_update(struct multipath *mpp, int ro)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
+int resize_map(struct multipath *mpp, unsigned long long size,
|
|
||||||
+ struct vectors * vecs)
|
|
||||||
+{
|
|
||||||
+ char *params __attribute__((cleanup(cleanup_charp))) = NULL;
|
|
||||||
+ unsigned long long orig_size = mpp->size;
|
|
||||||
+
|
|
||||||
+ mpp->size = size;
|
|
||||||
+ update_mpp_paths(mpp, vecs->pathvec);
|
|
||||||
+ if (setup_map(mpp, ¶ms, vecs) != 0) {
|
|
||||||
+ condlog(0, "%s: failed to setup map for resize : %s",
|
|
||||||
+ mpp->alias, strerror(errno));
|
|
||||||
+ mpp->size = orig_size;
|
|
||||||
+ return 1;
|
|
||||||
+ }
|
|
||||||
+ mpp->action = ACT_RESIZE;
|
|
||||||
+ mpp->force_udev_reload = 1;
|
|
||||||
+ if (domap(mpp, params, 1) == DOMAP_FAIL) {
|
|
||||||
+ condlog(0, "%s: failed to resize map : %s", mpp->alias,
|
|
||||||
+ strerror(errno));
|
|
||||||
+ mpp->size = orig_size;
|
|
||||||
+ return 1;
|
|
||||||
+ }
|
|
||||||
+ if (setup_multipath(vecs, mpp) != 0)
|
|
||||||
+ return 2;
|
|
||||||
+ sync_map_state(mpp);
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int
|
|
||||||
uev_update_path (struct uevent *uev, struct vectors * vecs)
|
|
||||||
{
|
|
||||||
diff --git a/multipathd/main.h b/multipathd/main.h
|
|
||||||
index bc1f938f..dbae4935 100644
|
|
||||||
--- a/multipathd/main.h
|
|
||||||
+++ b/multipathd/main.h
|
|
||||||
@@ -66,4 +66,6 @@ int reload_and_sync_map(struct multipath *mpp, struct vectors *vecs,
|
|
||||||
|
|
||||||
void handle_path_wwid_change(struct path *pp, struct vectors *vecs);
|
|
||||||
bool check_path_wwid_change(struct path *pp);
|
|
||||||
+int resize_map(struct multipath *mpp, unsigned long long size,
|
|
||||||
+ struct vectors *vecs);
|
|
||||||
#endif /* MAIN_H */
|
|
@ -1,202 +0,0 @@
|
|||||||
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);
|
|
@ -1,42 +0,0 @@
|
|||||||
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))
|
|
@ -1,76 +0,0 @@
|
|||||||
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);
|
|
@ -1,131 +0,0 @@
|
|||||||
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,
|
|
@ -1,39 +0,0 @@
|
|||||||
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;
|
|
@ -1,83 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
|||||||
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;
|
|
@ -1,43 +0,0 @@
|
|||||||
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,
|
|
@ -1,28 +0,0 @@
|
|||||||
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" :
|
|
@ -1,40 +0,0 @@
|
|||||||
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);
|
|
@ -1,23 +0,0 @@
|
|||||||
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;
|
|
@ -1,194 +0,0 @@
|
|||||||
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, ¶ms) == DMP_OK &&
|
|
||||||
strstr(params, "queue_if_no_path")) {
|
|
||||||
- if (!dm_queue_if_no_path(mapname, 0))
|
|
||||||
+ if (!_dm_queue_if_no_path(mapname, 0))
|
|
||||||
queue_if_no_path = 1;
|
|
||||||
else
|
|
||||||
/* Leave queue_if_no_path alone if unset failed */
|
|
||||||
@@ -1121,7 +1122,7 @@ int _dm_flush_map (const char * mapname, int need_sync, int deferred_remove,
|
|
||||||
} while (retries-- > 0);
|
|
||||||
|
|
||||||
if (queue_if_no_path == 1)
|
|
||||||
- dm_queue_if_no_path(mapname, 1);
|
|
||||||
+ _dm_queue_if_no_path(mapname, 1);
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
@@ -1236,8 +1237,8 @@ dm_reinstate_path(const char * mapname, char * path)
|
|
||||||
return dm_message(mapname, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
-int
|
|
||||||
-dm_queue_if_no_path(const char *mapname, int enable)
|
|
||||||
+static int
|
|
||||||
+_dm_queue_if_no_path(const char *mapname, int enable)
|
|
||||||
{
|
|
||||||
char *message;
|
|
||||||
|
|
||||||
@@ -1249,6 +1250,20 @@ dm_queue_if_no_path(const char *mapname, int enable)
|
|
||||||
return dm_message(mapname, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
+int dm_queue_if_no_path(struct multipath *mpp, int enable)
|
|
||||||
+{
|
|
||||||
+ int r;
|
|
||||||
+ static const char no_path_retry[] = "queue_if_no_path";
|
|
||||||
+
|
|
||||||
+ if ((r = _dm_queue_if_no_path(mpp->alias, enable)) == 0) {
|
|
||||||
+ if (enable)
|
|
||||||
+ add_feature(&mpp->features, no_path_retry);
|
|
||||||
+ else
|
|
||||||
+ remove_feature(&mpp->features, no_path_retry);
|
|
||||||
+ }
|
|
||||||
+ return r;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static int
|
|
||||||
dm_groupmsg (const char * msg, const char * mapname, int index)
|
|
||||||
{
|
|
||||||
diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h
|
|
||||||
index 808da28d..41b8c31d 100644
|
|
||||||
--- a/libmultipath/devmapper.h
|
|
||||||
+++ b/libmultipath/devmapper.h
|
|
||||||
@@ -58,7 +58,7 @@ int dm_cancel_deferred_remove(struct multipath *mpp);
|
|
||||||
int dm_flush_maps (int retries);
|
|
||||||
int dm_fail_path(const char * mapname, char * path);
|
|
||||||
int dm_reinstate_path(const char * mapname, char * path);
|
|
||||||
-int dm_queue_if_no_path(const char *mapname, int enable);
|
|
||||||
+int dm_queue_if_no_path(struct multipath *mpp, int enable);
|
|
||||||
int dm_switchgroup(const char * mapname, int index);
|
|
||||||
int dm_enablegroup(const char * mapname, int index);
|
|
||||||
int dm_disablegroup(const char * mapname, int index);
|
|
||||||
diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c
|
|
||||||
index 86ad89ca..56915e96 100644
|
|
||||||
--- a/libmultipath/structs_vec.c
|
|
||||||
+++ b/libmultipath/structs_vec.c
|
|
||||||
@@ -579,7 +579,7 @@ static void leave_recovery_mode(struct multipath *mpp)
|
|
||||||
*/
|
|
||||||
if (recovery && (mpp->no_path_retry == NO_PATH_RETRY_QUEUE ||
|
|
||||||
mpp->no_path_retry > 0)) {
|
|
||||||
- dm_queue_if_no_path(mpp->alias, 1);
|
|
||||||
+ dm_queue_if_no_path(mpp, 1);
|
|
||||||
condlog(2, "%s: queue_if_no_path enabled", mpp->alias);
|
|
||||||
condlog(1, "%s: Recovered to normal mode", mpp->alias);
|
|
||||||
}
|
|
||||||
@@ -598,11 +598,11 @@ void __set_no_path_retry(struct multipath *mpp, bool check_features)
|
|
||||||
break;
|
|
||||||
case NO_PATH_RETRY_FAIL:
|
|
||||||
if (!check_features || is_queueing)
|
|
||||||
- dm_queue_if_no_path(mpp->alias, 0);
|
|
||||||
+ dm_queue_if_no_path(mpp, 0);
|
|
||||||
break;
|
|
||||||
case NO_PATH_RETRY_QUEUE:
|
|
||||||
if (!check_features || !is_queueing)
|
|
||||||
- dm_queue_if_no_path(mpp->alias, 1);
|
|
||||||
+ dm_queue_if_no_path(mpp, 1);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (count_active_paths(mpp) > 0) {
|
|
||||||
@@ -612,7 +612,7 @@ void __set_no_path_retry(struct multipath *mpp, bool check_features)
|
|
||||||
*/
|
|
||||||
if ((!check_features || !is_queueing) &&
|
|
||||||
!mpp->in_recovery)
|
|
||||||
- dm_queue_if_no_path(mpp->alias, 1);
|
|
||||||
+ dm_queue_if_no_path(mpp, 1);
|
|
||||||
leave_recovery_mode(mpp);
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
@@ -623,7 +623,7 @@ void __set_no_path_retry(struct multipath *mpp, bool check_features)
|
|
||||||
*/
|
|
||||||
if ((!check_features || is_queueing) &&
|
|
||||||
mpp->in_recovery && mpp->retry_tick == 0)
|
|
||||||
- dm_queue_if_no_path(mpp->alias, 0);
|
|
||||||
+ dm_queue_if_no_path(mpp, 0);
|
|
||||||
if (pathcount(mpp, PATH_PENDING) == 0)
|
|
||||||
enter_recovery_mode(mpp);
|
|
||||||
}
|
|
||||||
diff --git a/multipathd/main.c b/multipathd/main.c
|
|
||||||
index 26be6dc3..74f8114c 100644
|
|
||||||
--- a/multipathd/main.c
|
|
||||||
+++ b/multipathd/main.c
|
|
||||||
@@ -502,7 +502,7 @@ flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) {
|
|
||||||
mpp->no_path_retry = NO_PATH_RETRY_FAIL;
|
|
||||||
mpp->disable_queueing = 1;
|
|
||||||
mpp->stat_map_failures++;
|
|
||||||
- dm_queue_if_no_path(mpp->alias, 0);
|
|
||||||
+ dm_queue_if_no_path(mpp, 0);
|
|
||||||
}
|
|
||||||
r = dm_flush_map_nopaths(mpp->alias, mpp->deferred_remove);
|
|
||||||
if (r) {
|
|
||||||
@@ -833,7 +833,7 @@ uev_remove_map (struct uevent * uev, struct vectors * vecs)
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
- dm_queue_if_no_path(alias, 0);
|
|
||||||
+ dm_queue_if_no_path(mpp, 0);
|
|
||||||
remove_map_and_stop_waiter(mpp, vecs);
|
|
||||||
out:
|
|
||||||
lock_cleanup_pop(vecs->lock);
|
|
||||||
@@ -2015,7 +2015,7 @@ retry_count_tick(vector mpvec)
|
|
||||||
condlog(4, "%s: Retrying.. No active path", mpp->alias);
|
|
||||||
if(--mpp->retry_tick == 0) {
|
|
||||||
mpp->stat_map_failures++;
|
|
||||||
- dm_queue_if_no_path(mpp->alias, 0);
|
|
||||||
+ dm_queue_if_no_path(mpp, 0);
|
|
||||||
condlog(2, "%s: Disable queueing", mpp->alias);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3110,7 +3110,7 @@ static void cleanup_maps(struct vectors *vecs)
|
|
||||||
put_multipath_config(conf);
|
|
||||||
if (queue_without_daemon == QUE_NO_DAEMON_OFF)
|
|
||||||
vector_foreach_slot(vecs->mpvec, mpp, i)
|
|
||||||
- dm_queue_if_no_path(mpp->alias, 0);
|
|
||||||
+ dm_queue_if_no_path(mpp, 0);
|
|
||||||
remove_maps_and_stop_waiters(vecs);
|
|
||||||
vecs->mpvec = NULL;
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
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;
|
|
@ -1,346 +0,0 @@
|
|||||||
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)
|
|
@ -1,40 +0,0 @@
|
|||||||
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
|
|
@ -1,32 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
|||||||
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);
|
|
@ -1,34 +0,0 @@
|
|||||||
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);
|
|
@ -1,102 +0,0 @@
|
|||||||
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
|
|
@ -1,54 +0,0 @@
|
|||||||
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},
|
|
@ -1,243 +0,0 @@
|
|||||||
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"
|
|
||||||
.\" ----------------------------------------------------------------------------
|
|
||||||
.
|
|
@ -1,103 +0,0 @@
|
|||||||
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.
|
|
@ -1,29 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in new issue