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
140 lines
5.5 KiB
Diff
140 lines
5.5 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Benjamin Marzinski <bmarzins@redhat.com>
|
|
Date: Fri, 25 Jul 2025 23:58:49 -0400
|
|
Subject: [PATCH] libmpathpersist: change how reservation conflicts are handled
|
|
|
|
If registering a key on a path fails with a reservation conflict in
|
|
mpath_prout_reg(), libmpathpersist currently tries to roll back the
|
|
registration. This code doesn't always make much sense. First, it
|
|
updates the configured key, but doesn't fix it if it does a rollback.
|
|
Second, it always rolls the key back to 0x0, unregistering paths that
|
|
may have been previously registered. These rollback only happen on the
|
|
paths where the registration succeeded, meaning that they were in the
|
|
expected state when the command was run. The paths where the command
|
|
failed, that were in an unexpected state, remain in that state.
|
|
|
|
The code no longer attempts to rollback registrations that failed
|
|
with a reservation conflict. Instead, it checks that at least one
|
|
path was in the expected state and was successfully registered. If
|
|
so, then it assumes that the registration command was a resonable one
|
|
and retries it on the paths that failed with a reservation conflict.
|
|
But instead of using MPATH_PROUT_REG_SA, it uses MPATH_PROUT_REG_IGN_SA
|
|
so that it will ignore the current key. This will keep it from
|
|
failing with a reservation conflict because the path doesn't have the
|
|
expected key registered on it. If path reservations failed for reasons
|
|
other than a reservation conflict, the command still returns failure.
|
|
|
|
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
|
Reviewed-by: Martin Wilck <mwilck@suse.com>
|
|
---
|
|
libmpathpersist/mpath_persist_int.c | 71 ++++++++++++++++++-----------
|
|
1 file changed, 45 insertions(+), 26 deletions(-)
|
|
|
|
diff --git a/libmpathpersist/mpath_persist_int.c b/libmpathpersist/mpath_persist_int.c
|
|
index ded1af38..e2dc5773 100644
|
|
--- a/libmpathpersist/mpath_persist_int.c
|
|
+++ b/libmpathpersist/mpath_persist_int.c
|
|
@@ -356,13 +356,13 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
|
|
int i, j, k;
|
|
struct pathgroup *pgp = NULL;
|
|
struct path *pp = NULL;
|
|
- int rollback = 0;
|
|
+ bool can_retry = false;
|
|
+ bool need_retry = false;
|
|
int active_pathcount=0;
|
|
int rc;
|
|
int count=0;
|
|
int status = MPATH_PR_SUCCESS;
|
|
int all_tg_pt;
|
|
- uint64_t sa_key = 0;
|
|
|
|
if (!mpp)
|
|
return MPATH_PR_DMMP_ERROR;
|
|
@@ -451,43 +451,62 @@ static int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
|
|
condlog (0, "%s: Thread[%d] failed to join thread %d", mpp->wwid, i, rc);
|
|
}
|
|
}
|
|
- if (!rollback && (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)){
|
|
- rollback = 1;
|
|
- sa_key = get_unaligned_be64(¶mp->sa_key[0]);
|
|
- status = MPATH_PR_RESERV_CONFLICT ;
|
|
- }
|
|
- if (!rollback && (status == MPATH_PR_SUCCESS)){
|
|
+ /*
|
|
+ * We only retry if there is at least one registration that
|
|
+ * returned a reservation conflict (which we need to retry)
|
|
+ * and at least one registration the return success, so we
|
|
+ * know that the command worked on some of the paths. If
|
|
+ * the registation fails on all paths, then it wasn't a
|
|
+ * valid request, so there's no need to retry.
|
|
+ */
|
|
+ if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
|
|
+ need_retry = true;
|
|
+ else if (thread[i].param.status == MPATH_PR_SUCCESS)
|
|
+ can_retry = true;
|
|
+ else if (status == MPATH_PR_SUCCESS)
|
|
status = thread[i].param.status;
|
|
- }
|
|
}
|
|
- if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){
|
|
- condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid);
|
|
- memcpy(¶mp->key, ¶mp->sa_key, 8);
|
|
- memset(¶mp->sa_key, 0, 8);
|
|
- for( i=0 ; i < count ; i++){
|
|
- if(thread[i].param.status == MPATH_PR_SUCCESS) {
|
|
- rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn,
|
|
- (void *)(&thread[i].param));
|
|
- if (rc){
|
|
- condlog (0, "%s: failed to create thread for rollback. %d", mpp->wwid, rc);
|
|
- thread[i].param.status = MPATH_PR_THREAD_ERROR;
|
|
- }
|
|
- } else
|
|
+ if (need_retry && can_retry && rq_servact == MPATH_PROUT_REG_SA &&
|
|
+ status == MPATH_PR_SUCCESS) {
|
|
+ condlog(3, "%s: ERROR: initiating pr out retry", mpp->wwid);
|
|
+ for (i = 0; i < count; i++) {
|
|
+ if (thread[i].param.status != MPATH_PR_RESERV_CONFLICT) {
|
|
thread[i].param.status = MPATH_PR_SKIP;
|
|
+ continue;
|
|
+ }
|
|
+ /*
|
|
+ * retry using MPATH_PROUT_REG_IGN_SA to avoid
|
|
+ * conflicts. We already know that some paths
|
|
+ * succeeded using MPATH_PROUT_REG_SA.
|
|
+ */
|
|
+ thread[i].param.rq_servact = MPATH_PROUT_REG_IGN_SA;
|
|
+ rc = pthread_create(&thread[i].id, &attr,
|
|
+ mpath_prout_pthread_fn,
|
|
+ (void *)(&thread[i].param));
|
|
+ if (rc) {
|
|
+ condlog(0, "%s: failed to create thread for retry. %d",
|
|
+ mpp->wwid, rc);
|
|
+ thread[i].param.status = MPATH_PR_THREAD_ERROR;
|
|
+ }
|
|
}
|
|
- for(i=0; i < count ; i++){
|
|
+ for (i = 0; i < count; i++) {
|
|
if (thread[i].param.status != MPATH_PR_SKIP &&
|
|
thread[i].param.status != MPATH_PR_THREAD_ERROR) {
|
|
rc = pthread_join(thread[i].id, NULL);
|
|
- if (rc){
|
|
- condlog (3, "%s: failed to join thread while rolling back %d",
|
|
- mpp->wwid, i);
|
|
+ if (rc) {
|
|
+ condlog(3, "%s: failed to join thread while retrying %d",
|
|
+ mpp->wwid, i);
|
|
}
|
|
+ if (status == MPATH_PR_SUCCESS)
|
|
+ status = thread[i].param.status;
|
|
}
|
|
}
|
|
+ need_retry = false;
|
|
}
|
|
|
|
pthread_attr_destroy(&attr);
|
|
+ if (need_retry)
|
|
+ status = MPATH_PR_RESERV_CONFLICT;
|
|
if (status == MPATH_PR_SUCCESS)
|
|
preempt_missing_path(mpp, paramp->key, paramp->sa_key, noisy);
|
|
return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
|