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
215 lines
7.1 KiB
Diff
215 lines
7.1 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Benjamin Marzinski <bmarzins@redhat.com>
|
|
Date: Tue, 30 Sep 2025 19:36:36 -0400
|
|
Subject: [PATCH] libmpathpersist: Fix race between restoring a path and
|
|
preemption
|
|
|
|
If a multipath device gets preempted while a path is being restored, the
|
|
path could end up getting the old key registered on it anyways. This
|
|
happens by:
|
|
1. multipathd calling update_map_pr() in mpath_pr_event_handle() and
|
|
seeing that there are matching keys.
|
|
2. Those matching keys get preempted.
|
|
3. multipathd registering the preempted key on the path.
|
|
|
|
Since there is no way to guarantee that the key won't get preempted
|
|
after multipathd checks for it, mpath_pr_event_handle() now calls
|
|
update_map_pr() to check the registered keys again after registering the
|
|
key. There must be at least as many keys registered after registering
|
|
the key on the restored path (which may already have a key registered on
|
|
it, so it's possible that the number of registered keys doesn't change).
|
|
|
|
pr_register_active_paths() calls mpath_pr_event_handle() for all working
|
|
paths on a device. In order to avoid calling update_map_pr() twice for
|
|
every path, mpath_pr_event_handle() now takes the number of keys to
|
|
expect, and skips the initial call to update_map_pr() if it already
|
|
knows how many keys are registered before trying to register the new
|
|
path.
|
|
|
|
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
|
---
|
|
multipathd/main.c | 77 ++++++++++++++++++++++++++++++++---------------
|
|
1 file changed, 52 insertions(+), 25 deletions(-)
|
|
|
|
diff --git a/multipathd/main.c b/multipathd/main.c
|
|
index 3b53d140..06ff6858 100644
|
|
--- a/multipathd/main.c
|
|
+++ b/multipathd/main.c
|
|
@@ -89,7 +89,8 @@
|
|
#define CMDSIZE 160
|
|
#define MSG_SIZE 32
|
|
|
|
-static void mpath_pr_event_handle(struct path *pp);
|
|
+static unsigned int
|
|
+mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed);
|
|
|
|
#define LOG_MSG(lvl, pp) \
|
|
do { \
|
|
@@ -638,7 +639,7 @@ flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) {
|
|
static void
|
|
pr_register_active_paths(struct multipath *mpp)
|
|
{
|
|
- unsigned int i, j;
|
|
+ unsigned int i, j, nr_keys = 0;
|
|
struct path *pp;
|
|
struct pathgroup *pgp;
|
|
|
|
@@ -647,7 +648,7 @@ pr_register_active_paths(struct multipath *mpp)
|
|
if (mpp->prflag == PR_UNSET)
|
|
return;
|
|
if ((pp->state == PATH_UP) || (pp->state == PATH_GHOST))
|
|
- mpath_pr_event_handle(pp);
|
|
+ nr_keys = mpath_pr_event_handle(pp, nr_keys);
|
|
}
|
|
}
|
|
}
|
|
@@ -1298,7 +1299,7 @@ rescan:
|
|
verify_paths(mpp);
|
|
mpp->action = ACT_RELOAD;
|
|
prflag = mpp->prflag;
|
|
- mpath_pr_event_handle(pp);
|
|
+ mpath_pr_event_handle(pp, 0);
|
|
} else {
|
|
if (!should_multipath(pp, vecs->pathvec, vecs->mpvec)) {
|
|
orphan_path(pp, "only one path");
|
|
@@ -2652,7 +2653,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
|
|
*/
|
|
condlog(2, "%s: checking persistent "
|
|
"reservation registration", pp->dev);
|
|
- mpath_pr_event_handle(pp);
|
|
+ mpath_pr_event_handle(pp, 0);
|
|
if (pp->mpp->prflag == PR_SET &&
|
|
prflag != PR_SET)
|
|
pr_register_active_paths(pp->mpp);
|
|
@@ -3982,12 +3983,16 @@ void unset_pr(struct multipath *mpp)
|
|
* Returns MPATH_PR_SUCCESS unless if fails to read the PR keys. If
|
|
* MPATH_PR_SUCCESS is returned, mpp->prflag will be either PR_SET or
|
|
* PR_UNSET.
|
|
+ *
|
|
+ * The number of found keys must be at least as large as *nr_keys,
|
|
+ * and if MPATH_PR_SUCCESS is returned and mpp->prflag is PR_SET after
|
|
+ * the call, *nr_keys will be set to the number of found keys.
|
|
*/
|
|
-static int update_map_pr(struct multipath *mpp, struct path *pp)
|
|
+static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *nr_keys)
|
|
{
|
|
struct prin_resp resp;
|
|
- unsigned int i;
|
|
- int ret, isFound;
|
|
+ unsigned int i, nr_found = 0;
|
|
+ int ret;
|
|
bool was_set = (mpp->prflag == PR_SET);
|
|
|
|
/* If pr is explicitly unset, it must be manually set */
|
|
@@ -4017,7 +4022,6 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
|
|
condlog(4, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
|
|
get_be64(mpp->reservation_key));
|
|
|
|
- isFound = 0;
|
|
for (i = 0;
|
|
i < resp.prin_descriptor.prin_readkeys.additional_length / 8; i++) {
|
|
uint8_t *keyp = &resp.prin_descriptor.prin_readkeys.key_list[i * 8];
|
|
@@ -4037,41 +4041,57 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
|
|
if (!memcmp(&mpp->reservation_key, keyp, 8) ||
|
|
(get_be64(mpp->old_pr_key) &&
|
|
!memcmp(&mpp->old_pr_key, keyp, 8)))
|
|
- isFound = 1;
|
|
+ nr_found++;
|
|
}
|
|
|
|
- if (isFound) {
|
|
+ if (nr_found >= *nr_keys) {
|
|
set_pr(mpp);
|
|
- condlog(was_set ? 3 : 2, "%s: key found. prflag set.", mpp->alias);
|
|
+ condlog(was_set ? 3 : 2, "%s: %u keys found. prflag set.",
|
|
+ mpp->alias, nr_found);
|
|
+ *nr_keys = nr_found;
|
|
} else {
|
|
unset_pr(mpp);
|
|
- condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
|
|
- mpp->alias);
|
|
+ condlog(was_set ? 1 : 3,
|
|
+ "%s: %u keys found. needed %u. prflag unset.",
|
|
+ mpp->alias, nr_found, *nr_keys);
|
|
}
|
|
|
|
return MPATH_PR_SUCCESS;
|
|
}
|
|
|
|
-static void mpath_pr_event_handle(struct path *pp)
|
|
+/*
|
|
+ * This function is called with the number of registered keys that should be
|
|
+ * seen for this device to know that the key has not been preempted while the
|
|
+ * path was getting registered. If 0 is passed in, update_mpath_pr is called
|
|
+ * before registering the key to figure out the number, assuming that at
|
|
+ * least one key must exist.
|
|
+ *
|
|
+ * The function returns the number of keys that are registered or 0 if
|
|
+ * it's unknown.
|
|
+ */
|
|
+static unsigned int mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed)
|
|
{
|
|
struct multipath *mpp = pp->mpp;
|
|
int ret;
|
|
- struct prout_param_descriptor param;
|
|
+ struct prout_param_descriptor param = {.sa_flags = 0};
|
|
bool clear_reg = false;
|
|
|
|
if (pp->bus != SYSFS_BUS_SCSI) {
|
|
unset_pr(mpp);
|
|
- return;
|
|
+ return 0;
|
|
}
|
|
|
|
- if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS)
|
|
- return;
|
|
+ if (nr_keys_needed == 0) {
|
|
+ nr_keys_needed = 1;
|
|
+ if (update_map_pr(mpp, pp, &nr_keys_needed) != MPATH_PR_SUCCESS)
|
|
+ return 0;
|
|
+ }
|
|
|
|
check_prhold(mpp, pp);
|
|
|
|
if (mpp->prflag != PR_SET) {
|
|
if (!mpp->ever_registered_pr)
|
|
- return;
|
|
+ return 0;
|
|
/*
|
|
* This path may have been unusable and either the
|
|
* registration was cleared or the registered
|
|
@@ -4084,21 +4104,28 @@ static void mpath_pr_event_handle(struct path *pp)
|
|
clear_reg = true;
|
|
}
|
|
|
|
- memset(¶m, 0, sizeof(param));
|
|
-
|
|
if (!clear_reg) {
|
|
param.sa_flags = mpp->sa_flags;
|
|
memcpy(param.sa_key, &mpp->reservation_key, 8);
|
|
param.num_transportid = 0;
|
|
}
|
|
-
|
|
+retry:
|
|
condlog(3, "%s registration for device %s:%s",
|
|
clear_reg ? "Clearing" : "Setting", pp->dev, pp->mpp->wwid);
|
|
|
|
ret = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REG_IGN_SA, 0, 0, ¶m, 0);
|
|
- if (ret != MPATH_PR_SUCCESS )
|
|
- {
|
|
+ if (ret != MPATH_PR_SUCCESS) {
|
|
condlog(0, "%s: %s reservation registration failed. Error: %d",
|
|
clear_reg ? "Clearing" : "Setting", pp->dev, ret);
|
|
+ } else if (!clear_reg) {
|
|
+ if (update_map_pr(mpp, pp, &nr_keys_needed) != MPATH_PR_SUCCESS)
|
|
+ return 0;
|
|
+ if (mpp->prflag != PR_SET) {
|
|
+ memset(¶m, 0, sizeof(param));
|
|
+ clear_reg = true;
|
|
+ goto retry;
|
|
+ }
|
|
+ return nr_keys_needed;
|
|
}
|
|
+ return 0;
|
|
}
|