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
194 lines
7.7 KiB
Diff
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,
|
|
+ ¶mp, noisy, &pp);
|
|
+ if (status != MPATH_PR_SUCCESS) {
|
|
+ condlog(0, "%s: self preempt command failed.", mpp->wwid);
|
|
+ goto fail_resume;
|
|
+ }
|
|
+
|
|
+ if (do_release) {
|
|
+ memset(¶mp, 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, ¶mp, noisy);
|
|
+ if (rel_status != MPATH_PR_SUCCESS)
|
|
+ condlog(0, "%s: release on alternate path failed.",
|
|
+ mpp->wwid);
|
|
+ }
|
|
+
|
|
+ memset(¶mp, 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, ¶mp, 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:
|