diff --git a/SOURCES/0136-multipathd-Added-support-to-handle-FPIN-Li-events-fo.patch b/SOURCES/0136-multipathd-Added-support-to-handle-FPIN-Li-events-fo.patch new file mode 100644 index 0000000..1e593f7 --- /dev/null +++ b/SOURCES/0136-multipathd-Added-support-to-handle-FPIN-Li-events-fo.patch @@ -0,0 +1,300 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Muneendra +Date: Wed, 20 Sep 2023 20:41:15 -0700 +Subject: [PATCH] multipathd: Added support to handle FPIN-Li events for + FC-NVMe + + This patch adds the support to handle FPIN-Li for FC-NVMe. + On receiving the FPIN-Li events this patch moves the devices paths + which are affected due to link integrity to marginal path groups. + The paths which are set to marginal path group will be unset + on receiving the RSCN events + +(mwilck: minor compile fix for 32-bit architectures) + +Signed-off-by: Muneendra +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + multipathd/fpin_handlers.c | 206 +++++++++++++++++++++++++++---------- + 1 file changed, 151 insertions(+), 55 deletions(-) + +diff --git a/multipathd/fpin_handlers.c b/multipathd/fpin_handlers.c +index 599f2893..6b90cded 100644 +--- a/multipathd/fpin_handlers.c ++++ b/multipathd/fpin_handlers.c +@@ -60,18 +60,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*/ +@@ -82,14 +79,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 */ +@@ -176,8 +173,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; +@@ -192,65 +189,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 update_path_groups() can remove mpp */ + vector_foreach_slot_backwards(vecs->mpvec, mpp, i) { +@@ -279,14 +371,18 @@ fpin_parse_li_els_setpath_marginal(uint16_t host_num, struct fc_tlv_desc *tlv, + struct fc_fn_li_desc *li_desc = (struct fc_fn_li_desc *)tlv; + int count = 0; + int ret = 0; ++ uint64_t attached_wwpn; + + /* Update the wwn to list */ + wwn_count = be32_to_cpu(li_desc->pname_count); +- condlog(4, "Got wwn count as %d\n", wwn_count); ++ attached_wwpn = be64_to_cpu(li_desc->attached_wwpn); ++ condlog(4, "Got wwn count as %d detecting wwn 0x%" PRIx64 ++ " attached_wwpn 0x%" PRIx64 "\n", ++ wwn_count, be64_to_cpu(li_desc->detecting_wwpn), attached_wwpn); + + for (iter = 0; iter < wwn_count; iter++) { + wwpn = be64_to_cpu(li_desc->pname_list[iter]); +- ret = fpin_chk_wwn_setpath_marginal(host_num, vecs, wwpn); ++ ret = fpin_chk_wwn_setpath_marginal(host_num, vecs, wwpn, attached_wwpn); + if (ret < 0) + condlog(2, "failed to set the path marginal associated with wwpn: 0x%" PRIx64 "\n", wwpn); + diff --git a/SOURCES/0137-multipathd-Make-sure-to-disable-queueing-if-recovery.patch b/SOURCES/0137-multipathd-Make-sure-to-disable-queueing-if-recovery.patch new file mode 100644 index 0000000..1f87568 --- /dev/null +++ b/SOURCES/0137-multipathd-Make-sure-to-disable-queueing-if-recovery.patch @@ -0,0 +1,76 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Wed, 22 Nov 2023 16:41:22 -0500 +Subject: [PATCH] multipathd: Make sure to disable queueing if recovery has + failed. + +If a multipath device has no_path_retry set to a number and has lost all +paths, gone into recovery mode, and timed out, it will disable +queue_if_no_paths. After that, if the device is reloaded by multipath +outside of multipathd, it will re-enable queuieng on the device. When +multipathd later calls set_no_path_retry() to update the queueing state, +it will not disable queue_if_no_paths, since the device is still in the +recovery state, so it believes no work needs to be done. The device will +remain in the recovery state, with retry_ticks at 0, and queueing +enabled, even though there are no usable paths. + +To fix this, in set_no_path_retry(), if no_path_retry is set to a number +and the device is queueing but it is in recovery mode and out of +retries with no usable paths, manually disable queue_if_no_path. + +Signed-off-by: Benjamin Marzinski +--- + libmultipath/structs_vec.c | 26 ++++++++++++++++++++++++-- + 1 file changed, 24 insertions(+), 2 deletions(-) + +diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c +index 9613252f..0304777b 100644 +--- a/libmultipath/structs_vec.c ++++ b/libmultipath/structs_vec.c +@@ -377,8 +377,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 ((!mpp->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; + } + } +@@ -521,6 +532,11 @@ int verify_paths(struct multipath *mpp, struct vectors *vecs) + * -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) + { +@@ -534,6 +550,12 @@ void update_queue_mode_del_path(struct multipath *mpp) + condlog(2, "%s: remaining active paths: %d", mpp->alias, active); + } + ++/* ++ * Since this will only be called from check_path() -> reinstate_path() after ++ * the queueing state has been updated in set_no_path_retry, this does not ++ * need to worry about modifying the queueing state except when actually ++ * leaving recovery mode. ++ */ + void update_queue_mode_add_path(struct multipath *mpp) + { + int active = count_active_paths(mpp); diff --git a/SPECS/device-mapper-multipath.spec b/SPECS/device-mapper-multipath.spec index 0c7b301..70e4bff 100644 --- a/SPECS/device-mapper-multipath.spec +++ b/SPECS/device-mapper-multipath.spec @@ -1,7 +1,7 @@ Summary: Tools to manage multipath devices using device-mapper Name: device-mapper-multipath Version: 0.8.4 -Release: 39%{?dist} +Release: 41%{?dist} License: GPLv2 Group: System Environment/Base URL: http://christophe.varoqui.free.fr/ @@ -146,6 +146,8 @@ Patch00132: 0132-multipathd-make-pr-registration-consistent.patch Patch00133: 0133-libmultipath-make-prflag-an-enum.patch Patch00134: 0134-multipathd-handle-no-active-paths-in-update_map_pr.patch Patch00135: 0135-libmpathpersist-fix-resource-leak-in-update_map_pr.patch +Patch00136: 0136-multipathd-Added-support-to-handle-FPIN-Li-events-fo.patch +Patch00137: 0137-multipathd-Make-sure-to-disable-queueing-if-recovery.patch # runtime Requires: %{name}-libs = %{version}-%{release} @@ -350,6 +352,16 @@ fi %{_pkgconfdir}/libdmmp.pc %changelog +* Fri Jan 5 2024 Benjamin Marzinski 0.8.4-41 +- Add 0137-multipathd-Make-sure-to-disable-queueing-if-recovery.patch +- Resolves: RHEL-16563 + +* Thu Nov 2 2023 Benjamin Marzinski 0.8.4-40 +- Add 0136-multipathd-Added-support-to-handle-FPIN-Li-events-fo.patch + * Add support in multipathd for NVMe to listen for FPIN-Li events and + mark effected paths as marginal +- Resolves: RHEL-6677 + * Thu Mar 16 2023 Benjamin Marzinski 0.8.4-39 - Add OSCI tests directory - Make kpartx_id installation location relative to %{_udevrulesdir}