device-mapper-multipath/0069-libmpathpersist-reregister-keys-when-self-preempting.patch
Benjamin Marzinski 695f9436f4 device-mapper-multipath-0.9.9-13
Add 0035-libmpathpersist-fix-memory-leak-in-mpath_prout_rel.patch
Add 0036-libmpathpersist-retry-commands-on-other-paths-in-mpa.patch
Add 0037-libmpathpersist-check-released-key-against-the-reser.patch
Add 0038-multipathd-remove-thread-from-mpath_pr_event_handle.patch
Add 0039-libmpathpersist-remove-uneeded-wrapper-function.patch
Add 0040-libmpathpersist-reduce-log-level-for-persistent-rese.patch
Add 0041-libmpathpersist-remove-pointless-update_map_pr-ret-v.patch
Add 0042-multipathd-use-update_map_pr-in-mpath_pr_event_handl.patch
Add 0043-libmpathpersist-limit-changing-prflag-in-update_map_.patch
Add 0044-multipathd-Don-t-call-update_map_pr-unnecessarily.patch
Add 0045-libmpathpersist-remove-useless-function-send_prout_a.patch
Add 0046-libmpathpersist-redesign-failed-release-workaround.patch
Add 0047-libmpathpersist-fail-the-release-if-all-threads-fail.patch
Add 0048-libmpathpersist-Handle-changing-key-corner-case.patch
Add 0049-libmpathpersist-Handle-REGISTER-AND-IGNORE-changing-.patch
Add 0050-libmultipath-rename-prflag_value-enums.patch
Add 0051-libmpathpersist-use-a-switch-statement-for-prout-com.patch
Add 0052-libmpathpersist-Add-safety-check-for-preempting-on-k.patch
Add 0053-libmpathpersist-remove-update_map_pr-code-for-NULL-p.patch
Add 0054-libmpathpersist-move-update_map_pr-to-multipathd.patch
Add 0055-multipathd-clean-up-update_map_pr-and-mpath_pr_event.patch
Add 0056-libmpathpersist-clean-up-duplicate-function-declarat.patch
Add 0057-multipathd-wrap-setting-and-unsetting-prflag.patch
Add 0058-multipathd-unregister-PR-key-when-path-is-restored-i.patch
Add 0059-libmpathpersist-Fix-up-reservation_key-checking.patch
Add 0060-libmpathpersist-change-how-reservation-conflicts-are.patch
Add 0061-libmpathpersist-Clear-prkey-in-multipathd-before-unr.patch
Add 0062-libmpathpersist-only-clear-the-key-if-we-are-using-t.patch
Add 0063-libmpathpersist-Restore-old-reservation-key-on-failu.patch
Add 0064-libmpathpersist-update-reservation-key-before-checki.patch
Add 0065-libmpathpersist-retry-on-conflicts-in-mpath_prout_co.patch
Add 0066-libmpathpersist-Don-t-always-fail-registrations-for-.patch
Add 0067-libmpathpersist-Don-t-try-release-workaround-for-inv.patch
Add 0068-libmpathpersist-Don-t-fail-RESERVE-commands-unnecess.patch
Add 0069-libmpathpersist-reregister-keys-when-self-preempting.patch
Add 0070-libmpathpersist-handle-updating-key-race-condition.patch
Add 0071-libmpathpersist-handle-preempting-all-registrants-re.patch
Add 0072-libmpathpersist-Fix-REGISTER-AND-IGNORE-while-holdin.patch
Add 0073-libmpathpersist-Handle-RESERVE-with-reservation-held.patch
Add 0074-libmpathpersist-use-check_holding_reservation-in-mpa.patch
Add 0075-libmpathpersist-Fix-unregistering-while-holding-the-.patch
Add 0076-libmpathpersist-Fix-race-between-restoring-a-path-an.patch
Add 0077-multipathd-Fix-tracking-of-old-PR-key.patch
  * Fixes RHEL-118720 ("There are many bugs in multipath's persistent
    reservation handling [rhel-10]")
Resolves: RHEL-118720
2025-10-01 16:53:32 -04:00

194 lines
7.7 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 5 Sep 2025 14:04:58 -0400
Subject: [PATCH] libmpathpersist: reregister keys when self preempting
When a SCSI device preempts its own reservation key, it will remove the
registered keys from all other devices with that reservation key, but
retain its registered key (and possibly acquire the reservation). If a
multipath device preempts its own reservation key, it will also remove
the registered keys from all its paths except the one issuing the
reservation. This means that IO to the device can fail if it goes to one
of these unregistered paths.
To avoid this, whenever a multipath device preempts itself, it must
first suspend, then do the preemption and reregister the removed keys,
and finally resume the device. This is already what libmpathpersist does
if a release fails because the path holding the reservation is currently
unavailable, with the addition of releasing the reservation after
preempting it. This commit refactors that code into a separate function,
makes the release optional, and calls the new function, preempt_self(),
whenever a device preempts itself.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist_int.c | 133 ++++++++++++++--------------
1 file changed, 68 insertions(+), 65 deletions(-)
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
index 45ba68c0..da8a0d04 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -570,6 +570,65 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
return status;
}
+/*
+ * Called to make a multipath device preempt its own reservation (and
+ * optionally release the reservation). Doing this causes the reservation
+ * keys to be removed from all the device paths except that path used to issue
+ * the preempt, so they need to be restored. To avoid the chance that IO
+ * goes to these paths when they don't have a registered key, the device
+ * is suspended before issuing the preemption, and the keys are reregistered
+ * before resuming it.
+ */
+static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
+ unsigned int rq_type, int noisy, bool do_release)
+{
+ int status, rel_status = MPATH_PR_SUCCESS;
+ struct path *pp = NULL;
+ struct prout_param_descriptor paramp = {.sa_flags = 0};
+ uint16_t udev_flags = (mpp->skip_kpartx) ? MPATH_UDEV_NO_KPARTX_FLAG : 0;
+
+ if (!dm_simplecmd_noflush(DM_DEVICE_SUSPEND, mpp->alias, 0)) {
+ condlog(0, "%s: self preempt failed to suspend device.", mpp->wwid);
+ return MPATH_PR_OTHER;
+ }
+
+ memcpy(paramp.key, &mpp->reservation_key, 8);
+ memcpy(paramp.sa_key, &mpp->reservation_key, 8);
+ status = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
+ &paramp, noisy, &pp);
+ if (status != MPATH_PR_SUCCESS) {
+ condlog(0, "%s: self preempt command failed.", mpp->wwid);
+ goto fail_resume;
+ }
+
+ if (do_release) {
+ memset(&paramp, 0, sizeof(paramp));
+ memcpy(paramp.key, &mpp->reservation_key, 8);
+ rel_status = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REL_SA,
+ rq_scope, rq_type, &paramp, noisy);
+ if (rel_status != MPATH_PR_SUCCESS)
+ condlog(0, "%s: release on alternate path failed.",
+ mpp->wwid);
+ }
+
+ memset(&paramp, 0, sizeof(paramp));
+ memcpy(paramp.sa_key, &mpp->reservation_key, 8);
+ status = mpath_prout_reg(mpp, MPATH_PROUT_REG_IGN_SA, rq_scope,
+ rq_type, &paramp, noisy);
+ if (status != MPATH_PR_SUCCESS)
+ condlog(0, "%s: self preempt failed to reregister paths.",
+ mpp->wwid);
+
+fail_resume:
+ if (!dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, udev_flags)) {
+ condlog(0, "%s: self preempt failed to resume device.", mpp->wwid);
+ if (status == MPATH_PR_SUCCESS)
+ status = MPATH_PR_OTHER;
+ }
+ /* return the first error we encountered */
+ return (rel_status != MPATH_PR_SUCCESS) ? rel_status : status;
+}
+
static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type,
struct prout_param_descriptor * paramp, int noisy)
@@ -584,8 +643,6 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
int count = 0;
int status = MPATH_PR_SUCCESS;
struct prin_resp resp = {{{.prgeneration = 0}}};
- uint16_t udev_flags = (mpp->skip_kpartx) ? MPATH_UDEV_NO_KPARTX_FLAG : 0;
- bool did_resume = false;
bool all_threads_failed;
unsigned int scope_type;
@@ -700,70 +757,10 @@ static int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
* preempting one. Since the device is suspended, no IO can
* go to these unregistered paths and fail).
* 3. Releasing the reservation on the path that now holds it.
- * 4. Resuming the device (since it no longer matters that most of
- * that paths no longer have a registered key)
- * 5. Reregistering keys on all the paths
+ * 4. Reregistering keys on all the paths
+ * 5. Resuming the device
*/
-
- if (!dm_simplecmd_noflush(DM_DEVICE_SUSPEND, mpp->alias, 0)) {
- condlog(0, "%s: release: failed to suspend dm device.", mpp->wwid);
- return MPATH_PR_OTHER;
- }
-
- memset(paramp, 0, sizeof(*paramp));
- memcpy(paramp->key, &mpp->reservation_key, 8);
- memcpy(paramp->sa_key, &mpp->reservation_key, 8);
- status = mpath_prout_common(mpp, MPATH_PROUT_PREE_SA, rq_scope,
- rq_type, paramp, noisy, &pp);
- if (status != MPATH_PR_SUCCESS) {
- condlog(0, "%s: release: pr preempt command failed.", mpp->wwid);
- goto fail_resume;
- }
-
- memset(paramp, 0, sizeof(*paramp));
- memcpy(paramp->key, &mpp->reservation_key, 8);
- status = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REL_SA, rq_scope,
- rq_type, paramp, noisy);
- if (status != MPATH_PR_SUCCESS) {
- condlog(0, "%s: release on alternate path failed.", mpp->wwid);
- goto out_reregister;
- }
-
- if (!dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, udev_flags)) {
- condlog(0, "%s release: failed to resume dm device.", mpp->wwid);
- /*
- * leave status set to MPATH_PR_SUCCESS, we will have another
- * chance to resume the device.
- */
- goto out_reregister;
- }
- did_resume = true;
-
-out_reregister:
- memset(paramp, 0, sizeof(*paramp));
- memcpy(paramp->sa_key, &mpp->reservation_key, 8);
- rc = mpath_prout_reg(mpp, MPATH_PROUT_REG_IGN_SA, rq_scope, rq_type,
- paramp, noisy);
- if (rc != MPATH_PR_SUCCESS)
- condlog(0, "%s: release: failed to reregister paths.", mpp->wwid);
-
- /*
- * If we failed releasing the reservation or resuming earlier
- * try resuming now. Otherwise, return with the reregistering status
- * This means we will report failure, even though the resevation
- * has been released, since the keys were not reregistered.
- */
- if (did_resume)
- return rc;
- else if (status == MPATH_PR_SUCCESS)
- status = rc;
-fail_resume:
- if (!dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, udev_flags)) {
- condlog(0, "%s: release: failed to resume dm device.", mpp->wwid);
- if (status == MPATH_PR_SUCCESS)
- status = MPATH_PR_OTHER;
- }
- return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
+ return preempt_self(mpp, MPATH_PROUT_PREE_SA, rq_scope, rq_type, noisy, true);
}
static int reservation_key_matches(struct multipath *mpp, uint8_t *key, int noisy)
@@ -909,6 +906,12 @@ int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
case MPATH_PROUT_PREE_AB_SA:
if (reservation_key_matches(mpp, paramp->sa_key, noisy) == YNU_YES)
preempting_reservation = true;
+ /* if we are preempting ourself */
+ if (memcmp(paramp->sa_key, paramp->key, 8) == 0) {
+ ret = preempt_self(mpp, rq_servact, rq_scope, rq_type,
+ noisy, false);
+ break;
+ }
/* fallthrough */
case MPATH_PROUT_RES_SA:
case MPATH_PROUT_CLEAR_SA: