import UBI device-mapper-multipath-0.8.7-39.el9_7.1

This commit is contained in:
eabdullin 2026-01-28 07:19:51 +00:00
parent 633e98645b
commit daa7dc6b75
48 changed files with 5910 additions and 1 deletions

View File

@ -0,0 +1,151 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 3 Jun 2025 23:35:03 -0400
Subject: [PATCH] libmpathpersist: retry commands on other paths in
mpath_prout_common
mpath_prout_common() will only try sending the prout command to one
path. If that fails, it will give up. There are a number of cases where
it is reasonable to assume that sending the command down another path
could succeed. Keep trying other available paths in these cases.
Do do this, this patch adds a new error code, MPATH_PR_RETRYABLE_ERROR.
libmpathpersist will not return this error to users. It will change it
to MPATH_PR_OTHER if it fails trying on all the paths.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist.c | 13 +++++++++----
libmpathpersist/mpath_persist.h | 3 +++
libmpathpersist/mpath_pr_ioctl.c | 25 ++++++++++++++-----------
3 files changed, 26 insertions(+), 15 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 29b64937..af5d3070 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -130,7 +130,7 @@ mpath_prin_activepath (struct multipath *mpp, int rq_servact,
}
}
}
- return ret;
+ return (ret == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : ret;
}
static vector curmp;
@@ -568,7 +568,7 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
}
pthread_attr_destroy(&attr);
- return (status);
+ return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
}
void * mpath_prout_pthread_fn(void *p)
@@ -588,6 +588,7 @@ int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
int i,j, ret;
struct pathgroup *pgp = NULL;
struct path *pp = NULL;
+ bool found = false;
vector_foreach_slot (mpp->pg, pgp, j){
vector_foreach_slot (pgp->paths, pp, i){
@@ -598,12 +599,16 @@ int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
}
condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
+ found = true;
ret = send_prout_activepath(pp->dev, rq_servact,
rq_scope, rq_type,
paramp, noisy);
- return ret ;
+ if (ret != MPATH_PR_RETRYABLE_ERROR)
+ return ret;
}
}
+ if (found)
+ return MPATH_PR_OTHER;
condlog (0, "%s: no path available", mpp->wwid);
return MPATH_PR_DMMP_ERROR;
}
@@ -839,7 +844,7 @@ out1:
free (pamp);
out:
free (pr_buff);
- return (status);
+ return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
}
void * mpath_alloc_prin_response(int prin_sa)
diff --git a/libmpathpersist/mpath_persist.h b/libmpathpersist/mpath_persist.h
index 9e9c0a82..8d441d04 100644
--- a/libmpathpersist/mpath_persist.h
+++ b/libmpathpersist/mpath_persist.h
@@ -63,6 +63,9 @@ extern "C" {
#define MPATH_PR_THREAD_ERROR 14 /* pthreads error (e.g. unable to create new thread) */
#define MPATH_PR_OTHER 15 /*other error/warning has occurred(transport
or driver error) */
+#define MPATH_PR_RETRYABLE_ERROR 16 /* error that might be succeed
+ down another path. Internal
+ only. */
/* PR MASK */
#define MPATH_F_APTPL_MASK 0x01 /* APTPL MASK*/
diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c
index 126601c3..9cd83729 100644
--- a/libmpathpersist/mpath_pr_ioctl.c
+++ b/libmpathpersist/mpath_pr_ioctl.c
@@ -54,7 +54,7 @@ int prout_do_scsi_ioctl(char * dev, int rq_servact, int rq_scope,
fd = open(devname, O_RDONLY);
if(fd < 0){
condlog (1, "%s: unable to open device.", dev);
- return MPATH_PR_FILE_ERROR;
+ return MPATH_PR_RETRYABLE_ERROR;
}
unsigned char cdb[MPATH_PROUT_CMDLEN] =
@@ -125,14 +125,17 @@ retry :
goto retry;
}
- if (((status == MPATH_PR_SENSE_NOT_READY )&& (Sensedata.ASC == 0x04)&&
- (Sensedata.ASCQ == 0x07))&& (retry > 0))
- {
- usleep(1000);
- --retry;
- condlog(3, "%s: retrying for sense 02/04/07."
- " Remaining retries = %d", dev, retry);
- goto retry;
+ if (status == MPATH_PR_SENSE_NOT_READY) {
+ if (Sensedata.ASC == 0x04 && Sensedata.ASCQ == 0x07 && retry > 0) {
+ usleep(1000);
+ --retry;
+ condlog(3,
+ "%s: retrying for sense 02/04/07."
+ " Remaining retries = %d",
+ dev, retry);
+ goto retry;
+ } else
+ status = MPATH_PR_RETRYABLE_ERROR;
}
close(fd);
@@ -344,7 +347,7 @@ int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int
fd = open(devname, O_RDONLY);
if(fd < 0){
condlog(0, "%s: Unable to open device ", dev);
- return MPATH_PR_FILE_ERROR;
+ return MPATH_PR_RETRYABLE_ERROR;
}
if (mpath_mx_alloc_len)
@@ -490,7 +493,7 @@ int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr,
case DID_OK :
break;
default :
- return MPATH_PR_OTHER;
+ return MPATH_PR_RETRYABLE_ERROR;
}
switch(io_hdr.driver_status)
{

View File

@ -0,0 +1,41 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 3 Jun 2025 23:35:04 -0400
Subject: [PATCH] libmpathpersist: check released key against the reservation
key
According to the SCSI Spec, if a persistent reservation RELEASE is
issued using the correct key for the I_T_L Nexus that it is issued on
but the reservation is held by a different key, the command should
return Success and do nothing. When libmpathpersist tried to release a
reservation that was held by a different key, it ended up using the
failback code designed to release a reservation held by a failed path to
release it anyways. This means that any node could release a scsi
persistent reservation held by another node. Fix this to follow the SCSI
Spec.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_persist.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index af5d3070..9d42704d 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -745,7 +745,13 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
condlog (2, "%s: Path holding reservation is released.", mpp->wwid);
return MPATH_PR_SUCCESS;
}
- condlog (2, "%s: Path holding reservation is not avialable.", mpp->wwid);
+ if (!get_be64(mpp->reservation_key) ||
+ memcmp(&mpp->reservation_key, resp.prin_descriptor.prin_readresv.key, 8)) {
+ condlog(2, "%s: Releasing key not holding reservation.", mpp->wwid);
+ return MPATH_PR_SUCCESS;
+ }
+
+ condlog (2, "%s: Path holding reservation is not available.", mpp->wwid);
pr_buff = mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA);
if (!pr_buff){

View File

@ -0,0 +1,112 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 16 Jun 2025 19:25:50 -0400
Subject: [PATCH] multipathd: remove thread from mpath_pr_event_handle
mpath_pr_event_handle() creates a separate thread to do the persistent
reservation work, but it doesn't take any advantage of the work being
done in another thread. Merge mpath_pr_event_handle() and
mpath_pr_event_handler_fn() into a single function with no separate
thread.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/main.c | 49 ++++++++++++-----------------------------------
multipathd/main.h | 3 ---
2 files changed, 12 insertions(+), 40 deletions(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 4119ad79..3f4b8ff6 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -90,6 +90,8 @@
#define CMDSIZE 160
#define MSG_SIZE 32
+static void mpath_pr_event_handle(struct path *pp);
+
#define LOG_MSG(lvl, pp) \
do { \
if (pp->mpp && checker_selected(&pp->checker) && \
@@ -3722,17 +3724,24 @@ main (int argc, char *argv[])
return (child(NULL));
}
-void * mpath_pr_event_handler_fn (void * pathp )
+static void mpath_pr_event_handle(struct path *pp)
{
struct multipath * mpp;
unsigned int i;
int ret, isFound;
- struct path * pp = (struct path *)pathp;
struct prout_param_descriptor *param;
struct prin_resp *resp;
- rcu_register_thread();
mpp = pp->mpp;
+ if (pp->bus != SYSFS_BUS_SCSI) {
+ mpp->prflag = PRFLAG_UNSET;
+ return;
+ }
+
+ if (!get_be64(mpp->reservation_key)) {
+ mpp->prflag = PRFLAG_UNSET;
+ return;
+ }
resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
if (!resp){
@@ -3799,38 +3808,4 @@ void * mpath_pr_event_handler_fn (void * pathp )
out:
if (resp)
free(resp);
- rcu_unregister_thread();
- return NULL;
-}
-
-int mpath_pr_event_handle(struct path *pp)
-{
- pthread_t thread;
- int rc;
- pthread_attr_t attr;
- struct multipath * mpp;
-
- if (pp->bus != SYSFS_BUS_SCSI)
- goto no_pr;
-
- mpp = pp->mpp;
-
- if (!get_be64(mpp->reservation_key))
- goto no_pr;
-
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
- rc = pthread_create(&thread, NULL , mpath_pr_event_handler_fn, pp);
- if (rc) {
- condlog(0, "%s: ERROR; return code from pthread_create() is %d", pp->dev, rc);
- return -1;
- }
- pthread_attr_destroy(&attr);
- rc = pthread_join(thread, NULL);
- return 0;
-
-no_pr:
- pp->mpp->prflag = PRFLAG_UNSET;
- return 0;
}
diff --git a/multipathd/main.h b/multipathd/main.h
index 4138faa4..897051e0 100644
--- a/multipathd/main.h
+++ b/multipathd/main.h
@@ -52,10 +52,7 @@ void dumpHex(const char * , int len, int no_ascii);
int prout_do_scsi_ioctl(char * , int rq_servact, int rq_scope,
unsigned int rq_type,
struct prout_param_descriptor *param, int noisy);
-int mpath_pr_event_handle(struct path *pp);
-void * mpath_pr_event_handler_fn (void * );
int update_map_pr(struct multipath *mpp);
-void * mpath_pr_event_handler_fn (void * pathp );
void handle_signals(bool);
int __setup_multipath (struct vectors * vecs, struct multipath * mpp,
int reset);

View File

@ -0,0 +1,60 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 16 Jun 2025 19:39:04 -0400
Subject: [PATCH] libmpathpersist: remove uneeded wrapper function.
mpath_send_prin_activepath() just calls prin_do_scsi_ioctl() with exactly
the same arguments that it is passed, and returns the same return. remove
it.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 15 ++-------------
libmpathpersist/mpathpr.h | 1 -
2 files changed, 2 insertions(+), 14 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 9d42704d..a7d07963 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -118,8 +118,8 @@ mpath_prin_activepath (struct multipath *mpp, int rq_servact,
condlog(3, "%s: sending pr in command to %s ",
mpp->wwid, pp->dev);
- ret = mpath_send_prin_activepath(pp->dev, rq_servact,
- resp, noisy);
+ ret = prin_do_scsi_ioctl(pp->dev, rq_servact, resp,
+ noisy);
switch(ret)
{
case MPATH_PR_SUCCESS:
@@ -418,17 +418,6 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid)
return MPATH_PR_SUCCESS ;
}
-int mpath_send_prin_activepath (char * dev, int rq_servact,
- struct prin_resp * resp, int noisy)
-{
-
- int rc;
-
- rc = prin_do_scsi_ioctl(dev, rq_servact, resp, noisy);
-
- return (rc);
-}
-
int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
{
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 5ea8cd6f..0292bc50 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -31,7 +31,6 @@ int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int
int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy);
void * _mpath_pr_update (void *arg);
-int mpath_send_prin_activepath (char * dev, int rq_servact, struct prin_resp * resp, int noisy);
int get_mpvec (vector curmp, vector pathvec, char * refwwid);
void * mpath_prout_pthread_fn(void *p);
void dumpHex(const char* , int len, int no_ascii);

View File

@ -0,0 +1,93 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 16 Jun 2025 20:08:08 -0400
Subject: [PATCH] libmpathpersist: reduce log level for persistent reservation
checking
Move logging of minor expected behavior to INFO level. Modify the log
level of some messages by whether or not mpp->prflag changed values.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 26 ++++++++++++++------------
1 file changed, 14 insertions(+), 12 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index a7d07963..1ce43de1 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -109,7 +109,7 @@ mpath_prin_activepath (struct multipath *mpp, int rq_servact,
vector_foreach_slot (pgp->paths, pp, i){
if (!((pp->state == PATH_UP) ||
(pp->state == PATH_GHOST))){
- condlog(2, "%s: %s not available. Skip.",
+ condlog(3, "%s: %s not available. Skip.",
mpp->wwid, pp->dev);
condlog(3, "%s: status = %d.",
mpp->wwid, pp->state);
@@ -875,13 +875,13 @@ int update_map_pr(struct multipath *mpp)
struct prin_resp *resp;
unsigned int i;
int ret = MPATH_PR_OTHER, isFound;
+ bool was_set = (mpp->prflag == PRFLAG_SET);
if (!get_be64(mpp->reservation_key))
{
/* Nothing to do. Assuming pr mgmt feature is disabled*/
mpp->prflag = PRFLAG_UNSET;
- condlog(4, "%s: reservation_key not set in multipath.conf",
- mpp->alias);
+ condlog(was_set ? 2 : 4, "%s: reservation_key not set in multipath.conf", mpp->alias);
return MPATH_PR_SUCCESS;
}
@@ -893,7 +893,7 @@ int update_map_pr(struct multipath *mpp)
}
if (count_active_paths(mpp) == 0)
{
- condlog(0,"%s: No available paths to check pr status",
+ condlog(2, "%s: No available paths to check pr status",
mpp->alias);
goto out;
}
@@ -910,22 +910,24 @@ int update_map_pr(struct multipath *mpp)
if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
{
- condlog(3,"%s: No key found. Device may not be registered. ", mpp->alias);
+ condlog(was_set ? 1 : 3, "%s: No key found. Device may not be registered. ", mpp->alias);
goto out;
}
- condlog(2, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
+ condlog(3, "%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++ )
{
- condlog(2, "%s: PR IN READKEYS[%d] reservation key:", mpp->alias, i);
- dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , 1);
+ if (libmp_verbosity >= 3) {
+ condlog(3, "%s: PR IN READKEYS[%d] reservation key:",
+ mpp->alias, i);
+ dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i * 8], 8, 1);
+ }
- if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
- {
- condlog(2, "%s: reservation key found in pr in readkeys response", mpp->alias);
+ if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i * 8], 8)) {
+ condlog(3, "%s: reservation key found in pr in readkeys response", mpp->alias);
isFound =1;
}
}
@@ -933,7 +935,7 @@ int update_map_pr(struct multipath *mpp)
if (isFound)
{
mpp->prflag = PRFLAG_SET;
- condlog(2, "%s: prflag flag set.", mpp->alias );
+ condlog(was_set ? 3 : 2, "%s: prflag flag set.", mpp->alias );
}
out:

View File

@ -0,0 +1,27 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 23 Jun 2025 13:43:45 -0400
Subject: [PATCH] libmpathpersist: remove pointless update_map_pr ret value
code
Don't set ret to MPATH_PR_SUCCESS, after the code just made sure that
it was already set to MPATH_PR_SUCCESS.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 1ce43de1..d9439292 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -906,8 +906,6 @@ int update_map_pr(struct multipath *mpp)
goto out;
}
- ret = MPATH_PR_SUCCESS;
-
if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
{
condlog(was_set ? 1 : 3, "%s: No key found. Device may not be registered. ", mpp->alias);

View File

@ -0,0 +1,227 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 18 Jun 2025 19:22:07 -0400
Subject: [PATCH] multipathd: use update_map_pr in mpath_pr_event_handle
Clean up the duplicate code in mpath_pr_event_handle() and
update_map_pr() by making update_map_pr() take an optional path device
to use for its check, instead of checking all path devices and make
mpath_pr_event_handle() call update_map_pr() to do its checking.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 21 ++++++----
libmpathpersist/mpathpr.h | 2 +-
multipathd/main.c | 69 +++++----------------------------
multipathd/main.h | 1 -
4 files changed, 25 insertions(+), 68 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index d9439292..2c35cb3e 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -869,7 +869,7 @@ void * mpath_alloc_prin_response(int prin_sa)
return ptr;
}
-int update_map_pr(struct multipath *mpp)
+int update_map_pr(struct multipath *mpp, struct path *pp)
{
int noisy=0;
struct prin_resp *resp;
@@ -882,7 +882,7 @@ int update_map_pr(struct multipath *mpp)
/* Nothing to do. Assuming pr mgmt feature is disabled*/
mpp->prflag = PRFLAG_UNSET;
condlog(was_set ? 2 : 4, "%s: reservation_key not set in multipath.conf", mpp->alias);
- return MPATH_PR_SUCCESS;
+ return MPATH_PR_SKIP;
}
resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
@@ -891,14 +891,18 @@ int update_map_pr(struct multipath *mpp)
condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
return MPATH_PR_OTHER;
}
- if (count_active_paths(mpp) == 0)
- {
+ if (!pp && count_active_paths(mpp) == 0) {
condlog(2, "%s: No available paths to check pr status",
mpp->alias);
goto out;
}
mpp->prflag = PRFLAG_UNSET;
- ret = mpath_prin_activepath(mpp, MPATH_PRIN_RKEY_SA, resp, noisy);
+ if (pp)
+ ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp,
+ noisy);
+ else
+ ret = mpath_prin_activepath(mpp, MPATH_PRIN_RKEY_SA, resp,
+ noisy);
if (ret != MPATH_PR_SUCCESS )
{
@@ -933,8 +937,11 @@ int update_map_pr(struct multipath *mpp)
if (isFound)
{
mpp->prflag = PRFLAG_SET;
- condlog(was_set ? 3 : 2, "%s: prflag flag set.", mpp->alias );
- }
+ condlog(was_set ? 3 : 2, "%s: key found. prflag set.",
+ mpp->alias);
+ } else
+ condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
+ mpp->alias);
out:
free(resp);
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 0292bc50..dd84e6d9 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -48,6 +48,6 @@ int update_prflag(char *mapname, int set);
int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags);
#define update_prkey(mapname, prkey) update_prkey_flags(mapname, prkey, 0)
void * mpath_alloc_prin_response(int prin_sa);
-int update_map_pr(struct multipath *mpp);
+int update_map_pr(struct multipath *mpp, struct path *pp);
#endif
diff --git a/multipathd/main.c b/multipathd/main.c
index 3f4b8ff6..3b99bbe2 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -68,6 +68,7 @@
#include "mpath_cmd.h"
#include "mpath_persist.h"
+#include "mpathpr.h"
#include "prioritizers/alua_rtpg.h"
@@ -643,7 +644,7 @@ fail:
sync_map_state(mpp);
if (mpp->prflag != PRFLAG_SET)
- update_map_pr(mpp);
+ update_map_pr(mpp, NULL);
if (mpp->prflag == PRFLAG_SET)
pr_register_active_paths(mpp);
@@ -1268,7 +1269,7 @@ rescan:
if (retries >= 0) {
if (start_waiter)
- update_map_pr(mpp);
+ update_map_pr(mpp, NULL);
if (mpp->prflag == PRFLAG_SET && prflag != PRFLAG_SET)
pr_register_active_paths(mpp);
condlog(2, "%s [%s]: path added to devmap %s",
@@ -2883,7 +2884,7 @@ configure (struct vectors * vecs)
vector_foreach_slot(mpvec, mpp, i){
if (remember_wwid(mpp->wwid) == 1)
trigger_paths_udev_change(mpp, true);
- update_map_pr(mpp);
+ update_map_pr(mpp, NULL);
if (mpp->prflag == PRFLAG_SET)
pr_register_active_paths(mpp);
}
@@ -3726,70 +3727,24 @@ main (int argc, char *argv[])
static void mpath_pr_event_handle(struct path *pp)
{
- struct multipath * mpp;
- unsigned int i;
- int ret, isFound;
+ struct multipath *mpp = pp->mpp;
+ int ret;
struct prout_param_descriptor *param;
- struct prin_resp *resp;
- mpp = pp->mpp;
if (pp->bus != SYSFS_BUS_SCSI) {
mpp->prflag = PRFLAG_UNSET;
return;
}
- if (!get_be64(mpp->reservation_key)) {
- mpp->prflag = PRFLAG_UNSET;
+ if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS)
return;
- }
-
- resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
- if (!resp){
- condlog(0,"%s Alloc failed for prin response", pp->dev);
- goto out;
- }
-
- mpp->prflag = PRFLAG_UNSET;
- ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, 0);
- if (ret != MPATH_PR_SUCCESS )
- {
- condlog(0,"%s : pr in read keys service action failed. Error=%d", pp->dev, ret);
- goto out;
- }
-
- condlog(3, " event pr=%d addlen=%d",resp->prin_descriptor.prin_readkeys.prgeneration,
- resp->prin_descriptor.prin_readkeys.additional_length );
-
- if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
- {
- condlog(1, "%s: No key found. Device may not be registered.", pp->dev);
- goto out;
- }
- condlog(2, "Multipath reservation_key: 0x%" PRIx64 " ",
- get_be64(mpp->reservation_key));
- isFound =0;
- for (i = 0; i < resp->prin_descriptor.prin_readkeys.additional_length/8; i++ )
- {
- condlog(2, "PR IN READKEYS[%d] reservation key:",i);
- dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i*8], 8 , -1);
- if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i*8], 8))
- {
- condlog(2, "%s: pr key found in prin readkeys response", mpp->alias);
- isFound =1;
- break;
- }
- }
- if (!isFound)
- {
- condlog(0, "%s: Either device not registered or ", pp->dev);
- condlog(0, "host is not authorised for registration. Skip path");
- goto out;
- }
+ if (mpp->prflag != PRFLAG_SET)
+ return;
param = (struct prout_param_descriptor *)MALLOC(sizeof(struct prout_param_descriptor));
if (!param)
- goto out;
+ return;
param->sa_flags = mpp->sa_flags;
memcpy(param->sa_key, &mpp->reservation_key, 8);
@@ -3802,10 +3757,6 @@ static void mpath_pr_event_handle(struct path *pp)
{
condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret);
}
- mpp->prflag = PRFLAG_SET;
free(param);
-out:
- if (resp)
- free(resp);
}
diff --git a/multipathd/main.h b/multipathd/main.h
index 897051e0..6d2ae72a 100644
--- a/multipathd/main.h
+++ b/multipathd/main.h
@@ -52,7 +52,6 @@ void dumpHex(const char * , int len, int no_ascii);
int prout_do_scsi_ioctl(char * , int rq_servact, int rq_scope,
unsigned int rq_type,
struct prout_param_descriptor *param, int noisy);
-int update_map_pr(struct multipath *mpp);
void handle_signals(bool);
int __setup_multipath (struct vectors * vecs, struct multipath * mpp,
int reset);

View File

@ -0,0 +1,76 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 20 Jun 2025 14:31:13 -0400
Subject: [PATCH] libmpathpersist: limit changing prflag in update_map_pr
Do not unset mpp->prflag in update_map_pr() unless the device doesn't
support persistent reservations or it successfully reads the registered
keys and discovers that the device's key is not there. Also, nothing in
the libmpathpersist ever returns MPATH_PR_SENSE_INVALID_OP. It instead
returns MPATH_PR_ILLEGAL_REQ if the device doesn't support persistent
reservations, so check for that instead.
Also, do not even check for the registered keys in update_map_pr() if
mpp->prflag is unset. Otherwise, multipathd will set mpp->prflag if the
key was unregistered (and thus prflag was unset) while a path is down
(and thus could not have its key unregistered) and then that path comes
back up. I should note that the above issue can only occur if multipath
is defining PR keys in /etc/multipath.conf, instead of the prkeys file.
If a device has no registered keys, it must unset prflag, since that
means it's no longer registered. Possibly its registration was removed
by a CLEAR or PREEMPT action. But if a device has a registered key but
it is supposed to be unregistered, it should remain unregistered, since
that key is likely one that libmpathpersist could not unregister at the
time.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 2c35cb3e..65a81faf 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -123,7 +123,7 @@ mpath_prin_activepath (struct multipath *mpp, int rq_servact,
switch(ret)
{
case MPATH_PR_SUCCESS:
- case MPATH_PR_SENSE_INVALID_OP:
+ case MPATH_PR_ILLEGAL_REQ:
return ret;
default:
continue;
@@ -877,6 +877,10 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
int ret = MPATH_PR_OTHER, isFound;
bool was_set = (mpp->prflag == PRFLAG_SET);
+ /* If pr is explicitly unset, it must be manually set */
+ if (mpp->prflag == PRFLAG_UNSET)
+ return MPATH_PR_SKIP;
+
if (!get_be64(mpp->reservation_key))
{
/* Nothing to do. Assuming pr mgmt feature is disabled*/
@@ -896,7 +900,6 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
mpp->alias);
goto out;
}
- mpp->prflag = PRFLAG_UNSET;
if (pp)
ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp,
noisy);
@@ -906,9 +909,12 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
if (ret != MPATH_PR_SUCCESS )
{
+ if (ret == MPATH_PR_ILLEGAL_REQ)
+ mpp->prflag = PRFLAG_UNSET;
condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
goto out;
}
+ mpp->prflag = PRFLAG_UNSET;
if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
{

View File

@ -0,0 +1,76 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 20 Jun 2025 16:26:01 -0400
Subject: [PATCH] multipathd: Don't call update_map_pr unnecessarily
None of the calls to update_map_pr() outside of mpath_pr_event_handle()
add any benefit. When update_map_pr() is called without a path, it tries
to read the pr keys list on each usable path until it succeeds, and then
checks the keys to see if they include the configured key.
In all cases where update_map_pr() is called outside of
mpath_pr_event_handle(), after it is called, pr_register_active_paths()
is called if a matching key was found. pr_register_active_paths() calls
mpath_pr_event_handle() on each usable path, which calls update_map_pr()
with a path, so it only checks that path. If a matching key is found, it
registers a key on the current path. The result is that after
pr_register_active_paths() is called, update_map_pr() will be called for
each usable path, just like update_map_pr() did. So calling
update_map_pr() first doesn't change the results for multipathd, it just
adds duplicate work, so remove those calls.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/main.c | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 3b99bbe2..bd87851d 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -555,6 +555,8 @@ pr_register_active_paths(struct multipath *mpp)
vector_foreach_slot (mpp->pg, pgp, i) {
vector_foreach_slot (pgp->paths, pp, j) {
+ if (mpp->prflag == PRFLAG_UNSET)
+ return;
if ((pp->state == PATH_UP) || (pp->state == PATH_GHOST))
mpath_pr_event_handle(pp);
}
@@ -643,10 +645,7 @@ fail:
sync_map_state(mpp);
- if (mpp->prflag != PRFLAG_SET)
- update_map_pr(mpp, NULL);
- if (mpp->prflag == PRFLAG_SET)
- pr_register_active_paths(mpp);
+ pr_register_active_paths(mpp);
if (VECTOR_SIZE(offline_paths) != 0)
handle_orphaned_offline_paths(offline_paths);
@@ -1268,10 +1267,9 @@ rescan:
sync_map_state(mpp);
if (retries >= 0) {
- if (start_waiter)
- update_map_pr(mpp, NULL);
- if (mpp->prflag == PRFLAG_SET && prflag != PRFLAG_SET)
- pr_register_active_paths(mpp);
+ if ((mpp->prflag == PRFLAG_SET && prflag != PRFLAG_SET) ||
+ start_waiter)
+ pr_register_active_paths(mpp);
condlog(2, "%s [%s]: path added to devmap %s",
pp->dev, pp->dev_t, mpp->alias);
return 0;
@@ -2884,9 +2882,7 @@ configure (struct vectors * vecs)
vector_foreach_slot(mpvec, mpp, i){
if (remember_wwid(mpp->wwid) == 1)
trigger_paths_udev_change(mpp, true);
- update_map_pr(mpp, NULL);
- if (mpp->prflag == PRFLAG_SET)
- pr_register_active_paths(mpp);
+ pr_register_active_paths(mpp);
}
/*

View File

@ -0,0 +1,86 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 23 Jun 2025 19:06:12 -0400
Subject: [PATCH] libmpathpersist: remove useless function
send_prout_activepath
send_prout_activepath() creates a single separate thread that just calls
prout_do_scsi_ioctl() and it doesn't take any advantage of the work
being done in another thread. Remove the function and call
prout_do_scsi_ioctl() directly.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 38 ++-------------------------------
libmpathpersist/mpathpr.h | 2 --
2 files changed, 2 insertions(+), 38 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 65a81faf..346abe71 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -589,9 +589,8 @@ int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev);
found = true;
- ret = send_prout_activepath(pp->dev, rq_servact,
- rq_scope, rq_type,
- paramp, noisy);
+ ret = prout_do_scsi_ioctl(pp->dev, rq_servact, rq_scope,
+ rq_type, paramp, noisy);
if (ret != MPATH_PR_RETRYABLE_ERROR)
return ret;
}
@@ -602,39 +601,6 @@ int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
return MPATH_PR_DMMP_ERROR;
}
-int send_prout_activepath(char * dev, int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
-{
- struct prout_param param;
- param.rq_servact = rq_servact;
- param.rq_scope = rq_scope;
- param.rq_type = rq_type;
- param.paramp = paramp;
- param.noisy = noisy;
- param.status = -1;
-
- pthread_t thread;
- pthread_attr_t attr;
- int rc;
-
- memset(&thread, 0, sizeof(thread));
- strlcpy(param.dev, dev, FILE_NAME_SIZE);
- /* Initialize and set thread joinable attribute */
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
-
- rc = pthread_create(&thread, &attr, mpath_prout_pthread_fn, (void *)(&param));
- if (rc){
- condlog (3, "%s: failed to create thread %d", dev, rc);
- return MPATH_PR_THREAD_ERROR;
- }
- /* Free attribute and wait for the other threads */
- pthread_attr_destroy(&attr);
- rc = pthread_join(thread, NULL);
-
- return (param.status);
-}
-
int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
{
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index dd84e6d9..0d1075e5 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -41,8 +41,6 @@ int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
-int send_prout_activepath(char * dev, int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
int update_prflag(char *mapname, int set);
int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags);

View File

@ -0,0 +1,72 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Mon, 5 May 2025 12:47:47 +0200
Subject: [PATCH] libmpathpersist: fix memory leak in mpath_prout_rel()
Found by Fedora's static analysis [1].
[1] https://openscanhub.fedoraproject.org/task/51915/log/device-mapper-multipath-0.11.1-1.fc43/scan-results.html#def44
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 346abe71..59159737 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -614,10 +614,10 @@ 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;
- struct prout_param_descriptor *pamp;
+ struct prout_param_descriptor *pamp = NULL;
struct prin_resp *pr_buff;
int length;
- struct transportid *pptr;
+ struct transportid *pptr = NULL;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
@@ -730,7 +730,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
pamp = (struct prout_param_descriptor *)malloc (length);
if (!pamp){
condlog (0, "%s: failed to alloc pr out parameter.", mpp->wwid);
- goto out1;
+ goto out;
}
memset(pamp, 0, length);
@@ -740,6 +740,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
condlog (0, "%s: failed to alloc pr out transportid.", mpp->wwid);
goto out1;
}
+ pptr = pamp->trnptid_list[0];
if (get_be64(mpp->reservation_key)){
memcpy (pamp->key, &mpp->reservation_key, 8);
@@ -751,11 +752,10 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
if (status) {
condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid);
- goto out1;
+ goto out2;
}
pamp->num_transportid = 1;
- pptr=pamp->trnptid_list[0];
for (i = 0; i < num; i++){
if (get_be64(mpp->reservation_key) &&
@@ -799,7 +799,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
}
-
+out2:
free(pptr);
out1:
free (pamp);

View File

@ -0,0 +1,307 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 23 Jun 2025 20:40:48 -0400
Subject: [PATCH] limpathpersist: redesign failed release workaround
The workaround for releasing a reservation held by a failed path
(clearing all persistent reservation data and then reregistering all the
keys) has several problems. It requires devices that support the READ
FULL STATUS command and are capable of specifying TransportIDs with
REGISTRATION commands (SIP_C), neither of which are mandatory to support
SCSI Persistent Reservations. Non SIP_C devices will be left with no
registered keys. Also, not all cleared keys are registered, just the
ones going to the Target Port receiving the REGISTRATION command. To
reregister all the keys, the code would have to also use the "All Target
Ports" flag to register keys on different Target Ports, but this could
end up registering keys on paths they aren't supposed to be on (for
instance if one of the registered keys was only there because the path
was down and it couldn't be released).
The redesign avoids these issues by only using mandatory Persistent
Reservation commands, without extra optional parameters or flags, and
only effects the keys of the multipath device it is being issued on.
The new workaround is:
1. Suspend the multipath device to prevent I/O
2. Preempt the key to move the reservation to an available path. This
also removes the registered keys from every path except the path
issuing the PREEMPT command. Since the device is suspended, not I/O
can go to these unregisted paths and fail.
3. Release the reservation on the path that now holds it.
4. Resume the device (since it no longer matters that most of the paths
no longer have a registered key)
5. Reregister the keys on all the paths.
If steps 3 or 4 fail, the code will attempt to reregister the keys, and
then attempt (or possibly re-attempt) the resume.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 171 ++++++++++++++----------------
libmpathpersist/mpathpr.h | 2 -
libmultipath/libmultipath.version | 1 +
3 files changed, 78 insertions(+), 96 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 59159737..7e60aa47 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -97,6 +97,11 @@ int libmpathpersist_exit(void)
return 0;
}
+static int mpath_prout_common(struct multipath *mpp,int rq_servact,
+ int rq_scope, unsigned int rq_type,
+ struct prout_param_descriptor* paramp, int noisy,
+ struct path **pptr);
+
int
mpath_prin_activepath (struct multipath *mpp, int rq_servact,
struct prin_resp * resp, int noisy)
@@ -283,6 +288,7 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
conf = get_multipath_config();
select_reservation_key(conf, mpp);
select_all_tg_pt(conf, mpp);
+ select_skip_kpartx(conf, mpp);
put_multipath_config(conf);
memcpy(&prkey, paramp->sa_key, 8);
@@ -319,7 +325,8 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
case MPATH_PROUT_PREE_SA :
case MPATH_PROUT_PREE_AB_SA :
case MPATH_PROUT_CLEAR_SA:
- ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
+ paramp, noisy, NULL);
break;
case MPATH_PROUT_REL_SA:
ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
@@ -571,8 +578,10 @@ void * mpath_prout_pthread_fn(void *p)
pthread_exit(NULL);
}
-int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor* paramp, int noisy)
+static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
+ unsigned int rq_type,
+ struct prout_param_descriptor* paramp, int noisy,
+ struct path **pptr)
{
int i,j, ret;
struct pathgroup *pgp = NULL;
@@ -591,6 +600,8 @@ int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
found = true;
ret = prout_do_scsi_ioctl(pp->dev, rq_servact, rq_scope,
rq_type, paramp, noisy);
+ if (ret == MPATH_PR_SUCCESS && pptr)
+ *pptr = pp;
if (ret != MPATH_PR_RETRYABLE_ERROR)
return ret;
}
@@ -610,14 +621,12 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
struct path *pp = NULL;
int active_pathcount = 0;
pthread_attr_t attr;
- int rc, found = 0;
+ int rc;
int count = 0;
int status = MPATH_PR_SUCCESS;
struct prin_resp resp;
- struct prout_param_descriptor *pamp = NULL;
- struct prin_resp *pr_buff;
- int length;
- struct transportid *pptr = NULL;
+ uint16_t udev_flags = (mpp->skip_kpartx)? MPATH_UDEV_NO_KPARTX_FLAG : 0;
+ bool did_resume = false;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
@@ -707,104 +716,78 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
}
condlog (2, "%s: Path holding reservation is not available.", mpp->wwid);
+ /*
+ * Cannot free the reservation because the path that is holding it
+ * is not usable. Workaround this by:
+ * 1. Suspending the device
+ * 2. Preempting the reservation to move it to a usable path
+ * (this removes the registered keys on all paths except the
+ * 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
+ */
- pr_buff = mpath_alloc_prin_response(MPATH_PRIN_RFSTAT_SA);
- if (!pr_buff){
- condlog (0, "%s: failed to alloc pr in response buffer.", mpp->wwid);
+ 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;
}
- status = mpath_prin_activepath (mpp, MPATH_PRIN_RFSTAT_SA, pr_buff, noisy);
-
- if (status != MPATH_PR_SUCCESS){
- condlog (0, "%s: pr in read full status command failed.", mpp->wwid);
- goto out;
- }
-
- num = pr_buff->prin_descriptor.prin_readfd.number_of_descriptor;
- if (0 == num){
- goto out;
- }
- length = sizeof (struct prout_param_descriptor) + (sizeof (struct transportid *));
-
- pamp = (struct prout_param_descriptor *)malloc (length);
- if (!pamp){
- condlog (0, "%s: failed to alloc pr out parameter.", mpp->wwid);
- goto out;
- }
-
- memset(pamp, 0, length);
-
- pamp->trnptid_list[0] = (struct transportid *) malloc (sizeof (struct transportid));
- if (!pamp->trnptid_list[0]){
- condlog (0, "%s: failed to alloc pr out transportid.", mpp->wwid);
- goto out1;
+ 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;
}
- pptr = pamp->trnptid_list[0];
- if (get_be64(mpp->reservation_key)){
- memcpy (pamp->key, &mpp->reservation_key, 8);
- condlog (3, "%s: reservation key set.", mpp->wwid);
+ 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;
}
- status = mpath_prout_common (mpp, MPATH_PROUT_CLEAR_SA,
- rq_scope, rq_type, pamp, noisy);
-
- if (status) {
- condlog(0, "%s: failed to send CLEAR_SA", mpp->wwid);
- goto out2;
+ 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;
- pamp->num_transportid = 1;
-
- for (i = 0; i < num; i++){
- if (get_be64(mpp->reservation_key) &&
- memcmp(pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key,
- &mpp->reservation_key, 8)){
- /*register with tarnsport id*/
- memset(pamp, 0, length);
- pamp->trnptid_list[0] = pptr;
- memset (pamp->trnptid_list[0], 0, sizeof (struct transportid));
- memcpy (pamp->sa_key,
- pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
- pamp->sa_flags = MPATH_F_SPEC_I_PT_MASK;
- pamp->num_transportid = 1;
-
- memcpy (pamp->trnptid_list[0],
- &pr_buff->prin_descriptor.prin_readfd.descriptors[i]->trnptid,
- sizeof (struct transportid));
- status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
- pamp, noisy);
-
- pamp->sa_flags = 0;
- memcpy (pamp->key, pr_buff->prin_descriptor.prin_readfd.descriptors[i]->key, 8);
- memset (pamp->sa_key, 0, 8);
- pamp->num_transportid = 0;
- status = mpath_prout_common (mpp, MPATH_PROUT_REG_SA, 0, rq_type,
- pamp, noisy);
- }
- else
- {
- if (get_be64(mpp->reservation_key))
- found = 1;
- }
-
-
- }
+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 (found){
- memset (pamp, 0, length);
- memcpy (pamp->sa_key, &mpp->reservation_key, 8);
- memset (pamp->key, 0, 8);
- status = mpath_prout_reg(mpp, MPATH_PROUT_REG_SA, rq_scope, rq_type, pamp, noisy);
+ /*
+ * 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;
}
-
-out2:
- free(pptr);
-out1:
- free (pamp);
-out:
- free (pr_buff);
return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
}
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 0d1075e5..85eac1c7 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -37,8 +37,6 @@ void dumpHex(const char* , int len, int no_ascii);
int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
-int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
diff --git a/libmultipath/libmultipath.version b/libmultipath/libmultipath.version
index 46907278..5340957c 100644
--- a/libmultipath/libmultipath.version
+++ b/libmultipath/libmultipath.version
@@ -301,6 +301,7 @@ global:
LIBMULTIPATH_9.1.2 {
global:
cleanup_mutex;
+ select_skip_kpartx;
} LIBMULTIPATH_9.1.1;
LIBMULTIPATH_9.1.3 {

View File

@ -0,0 +1,51 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 8 Jul 2025 16:23:13 -0400
Subject: [PATCH] libmpathpersist: fail the release if all threads fail
If none of the threads succeeds in issuing the release, simply return
failure, instead of trying the workaround.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 7e60aa47..04a08b76 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -627,6 +627,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
struct prin_resp resp;
uint16_t udev_flags = (mpp->skip_kpartx)? MPATH_UDEV_NO_KPARTX_FLAG : 0;
bool did_resume = false;
+ bool all_threads_failed;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
@@ -688,15 +689,22 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
}
}
+ all_threads_failed = true;
for (i = 0; i < count; i++){
/* check thread status here and return the status */
- if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
+ if (thread[i].param.status == MPATH_PR_SUCCESS)
+ all_threads_failed = false;
+ else if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
status = MPATH_PR_RESERV_CONFLICT;
- else if (status == MPATH_PR_SUCCESS
- && thread[i].param.status != MPATH_PR_RESERV_CONFLICT)
+ else if (status == MPATH_PR_SUCCESS)
status = thread[i].param.status;
}
+ if (all_threads_failed) {
+ condlog(0, "%s: all threads failed to release reservation.",
+ mpp->wwid);
+ return status;
+ }
status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
if (status != MPATH_PR_SUCCESS){

View File

@ -0,0 +1,124 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 27 Jun 2025 18:17:40 -0400
Subject: [PATCH] limpathpersist: Handle changing key corner case
When you change the reservation key of a registered multipath device,
some of paths might be down or even deleted since you originally
registered the key. libmpathpersist won't be able to change these
registrations. This can be a problem if the path that is down is holding
the reservation. Other nodes will assume that the reservation is now
under your new key, when it is still under the old one. If they try to
preempt the reservation, they will succeed. But they will just
unregister all the paths with the new key. They won't take over the
reservation, and if the path holding it comes back up, it will still be
able to write to the device.
To avoid this, after libmpathpersist changes the key of a registered
device, it now checks to see if its old key is still holding the
reservation. If it is, limpathpersist preempts the reservation.
Currently this only works on REGISTER commands, not REGISTER AND IGNORE
ones. A future patch will add that capability.
I should note that this relies on the fact that libmpathpersist has
never worked correctly if two nodes use the same reservation key for the
same device. If another node used the same key, it could be the one
holding the reservation, and preempting the reservation would deregister
it. However, multipathd has always relied on the fact that two nodes
will not use the same registration key to know if it is safe to register
a key on a path that was just added or just came back up. If there is
already a registered key matching the configured key, multipathd assumes
that the device has not been preempted, and registers the key on the
path. If there are no keys matching the configured key, multipathd
assumes that the device has been preempted, and won't register a key.
Again, if another node shared the same key, multipathd could registered
paths on a node that had been preempted.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 58 ++++++++++++++++++++++++++++++++-
1 file changed, 57 insertions(+), 1 deletion(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 04a08b76..c09fbbe7 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -425,6 +425,60 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid)
return MPATH_PR_SUCCESS ;
}
+/*
+ * If you are changing the key registered to a device, and that device is
+ * holding the reservation on a path that couldn't get its key updated,
+ * either because it is down or no longer part of the multipath device,
+ * you need to preempt the reservation to a usable path with the new key
+ */
+void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
+ int noisy)
+{
+ uint8_t zero[8] = {0};
+ struct prin_resp resp = {0};
+ int rq_scope;
+ unsigned int rq_type;
+ struct prout_param_descriptor paramp = {0};
+ int status;
+
+ /*
+ * If you previously didn't have a key registered or you didn't
+ * switch to a different key, there's no need to preempt. Also, you
+ * can't preempt if you no longer have a registered key
+ */
+ if (memcmp(key, zero, 8) == 0 || memcmp(sa_key, zero, 8) == 0 ||
+ memcmp(key, sa_key, 8) == 0)
+ return;
+
+ status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
+ if (status != MPATH_PR_SUCCESS) {
+ condlog(0, "%s: register: pr in read reservation command failed.", mpp->wwid);
+ return;
+ }
+ /* If there is no reservation, there's nothing to preempt */
+ if (!resp.prin_descriptor.prin_readresv.additional_length)
+ return;
+ /*
+ * If there reservation is not held by the old key, you don't
+ * want to preempt it
+ */
+ if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) != 0)
+ return;
+ /* Assume this key is being held by an inaccessable path on this
+ * node. libmpathpersist has never worked if multiple nodes share
+ * the same reservation key for a device
+ */
+ rq_type = resp.prin_descriptor.prin_readresv.scope_type & MPATH_PR_TYPE_MASK;
+ rq_scope = (resp.prin_descriptor.prin_readresv.scope_type & MPATH_PR_SCOPE_MASK) >> 4;
+ memcpy(paramp.key, sa_key, 8);
+ memcpy(paramp.sa_key, key, 8);
+ status = mpath_prout_common(mpp, MPATH_PROUT_PREE_SA, rq_scope, rq_type,
+ &paramp, noisy, NULL);
+ if (status != MPATH_PR_SUCCESS)
+ condlog(0, "%s: register: pr preempt command failed.",
+ mpp->wwid);
+}
+
int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
{
@@ -564,6 +618,8 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
}
pthread_attr_destroy(&attr);
+ 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;
}
@@ -624,7 +680,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
int rc;
int count = 0;
int status = MPATH_PR_SUCCESS;
- struct prin_resp resp;
+ struct prin_resp resp = {0};
uint16_t udev_flags = (mpp->skip_kpartx)? MPATH_UDEV_NO_KPARTX_FLAG : 0;
bool did_resume = false;
bool all_threads_failed;

View File

@ -0,0 +1,70 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 29 Nov 2022 22:56:48 -0600
Subject: [PATCH] libmpathpersist: fix command keyword ordering
When libmpathpersist was communicating with multipathd, it wasn't using
the correct keyword order for the commands, as specified in the CLI
commands reference. Since commit f812466f, multipathd requires commands
to be ordered correctly. Fix the ordering.
Fixes: f812466f ("multipathd: more robust command parsing")
Reported-by: miaoguanqin <miaoguanqin@huawei.com>
Cc: lixiaokeng <lixiaokeng@huawei.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
libmpathpersist/mpath_updatepr.c | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
index 0aca28eb..af2c5549 100644
--- a/libmpathpersist/mpath_updatepr.c
+++ b/libmpathpersist/mpath_updatepr.c
@@ -18,7 +18,7 @@
#include "mpathpr.h"
-static int do_update_pr(char *alias, char *arg)
+static int do_update_pr(char *alias, char *cmd, char *key)
{
int fd;
char str[256];
@@ -31,7 +31,10 @@ static int do_update_pr(char *alias, char *arg)
return -1;
}
- snprintf(str,sizeof(str),"map %s %s", alias, arg);
+ if (key)
+ snprintf(str,sizeof(str),"%s map %s key %s", cmd, alias, key);
+ else
+ snprintf(str,sizeof(str),"%s map %s", cmd, alias);
condlog (2, "%s: pr message=%s", alias, str);
if (send_packet(fd, str) != 0) {
condlog(2, "%s: message=%s send error=%d", alias, str, errno);
@@ -56,18 +59,16 @@ static int do_update_pr(char *alias, char *arg)
}
int update_prflag(char *mapname, int set) {
- return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus");
+ return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus",
+ NULL);
}
int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags) {
char str[256];
- char *flagstr = "";
- if (sa_flags & MPATH_F_APTPL_MASK)
- flagstr = ":aptpl";
- if (prkey)
- sprintf(str, "setprkey key %" PRIx64 "%s", prkey, flagstr);
- else
- sprintf(str, "unsetprkey");
- return do_update_pr(mapname, str);
+ if (!prkey)
+ return do_update_pr(mapname, "unsetprkey", NULL);
+ sprintf(str, "%" PRIx64 "%s", prkey,
+ (sa_flags & MPATH_F_APTPL_MASK) ? ":aptpl" : "");
+ return do_update_pr(mapname, "setprkey", str);
}

View File

@ -0,0 +1,54 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Martin Wilck <mwilck@suse.com>
Date: Mon, 2 Jan 2023 12:39:36 +0100
Subject: [PATCH] libmpathpersist: use conf->timeout for updating persistent
reservations
On systems with many LUNs, multipathd may fail to respond within the
default timeout to a "setprkey" command because the vecs lock is held
by the path checker. Honor the globally configured uxsock timeout in
libmpathpersist.
Reported-by: boposki (github.com/opensvc/multipath-tools/pull/58)
Signed-off-by: Martin Wilck <mwilck@suse.com>
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_updatepr.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
index af2c5549..39dfa218 100644
--- a/libmpathpersist/mpath_updatepr.c
+++ b/libmpathpersist/mpath_updatepr.c
@@ -13,6 +13,8 @@
#include <mpath_persist.h>
#include "debug.h"
#include "mpath_cmd.h"
+#include "vector.h"
+#include "config.h"
#include "uxsock.h"
#include "memory.h"
#include "mpathpr.h"
@@ -24,6 +26,12 @@ static int do_update_pr(char *alias, char *cmd, char *key)
char str[256];
char *reply;
int ret = 0;
+ int timeout;
+ struct config *conf;
+
+ conf = get_multipath_config();
+ timeout = conf->uxsock_timeout;
+ put_multipath_config(conf);
fd = mpath_connect();
if (fd == -1) {
@@ -41,7 +49,7 @@ static int do_update_pr(char *alias, char *cmd, char *key)
mpath_disconnect(fd);
return -1;
}
- ret = recv_packet(fd, &reply, DEFAULT_REPLY_TIMEOUT);
+ ret = recv_packet(fd, &reply, timeout);
if (ret < 0) {
condlog(2, "%s: message=%s recv error=%d", alias, str, errno);
ret = -1;

View File

@ -0,0 +1,180 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 30 Jun 2025 20:10:59 -0400
Subject: [PATCH] libmapthpersist: Handle REGISTER AND IGNORE changing key
corner case
When you use REGISTER to change the reservation key of a registered
multipath device, where the path holding the reservation is down,
libmpathpersist will PREEMPT the failed path to move the reservation to
a usable path, so the reservation key will be updated. However, when you
use REGISTER AND IGNORE, you don't pass in the old key, so
libmpathpersist can't use it to check against the key holding the
reservation, which is necessary to know if it needs to PREEMPT.
Since the SCSI spec says that devices must ignore any passed-in key on
REGISTER AND IGNORE, libmpathpersist can still use it to pass in the old
key value. But unlike with REGISTER, the command won't fail if device
isn't actually registered with the old key, so libmpathpersist needs to
check that itself. To do this, it calls update_map_pr() just like
multipathd does to check that the device is registered before
registering new paths. However, libmpathpersist doesn't track the
persistent reservation state like multipathd, so it needs to ask
multipathd if it thinks the device is registered, using the "get
prstatus map <map>" command.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 20 +++++++++++
libmpathpersist/mpath_updatepr.c | 57 ++++++++++++++++++++++++--------
libmpathpersist/mpathpr.h | 1 +
3 files changed, 64 insertions(+), 14 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index c09fbbe7..324d88a2 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -271,6 +271,23 @@ int __mpath_persistent_reserve_in (int fd, int rq_servact,
resp, noisy);
}
+/*
+ * for MPATH_PROUT_REG_IGN_SA, we use the ignored paramp->key to store the
+ * currently registered key.
+ */
+static void set_ignored_key(struct multipath *mpp, uint8_t *key)
+{
+ memset(key, 0, 8);
+ if (!get_be64(mpp->reservation_key))
+ return;
+ if (get_prflag(mpp->alias) == PRFLAG_UNSET)
+ return;
+ update_map_pr(mpp, NULL);
+ if (mpp->prflag != PRFLAG_SET)
+ return;
+ memcpy(key, &mpp->reservation_key, 8);
+}
+
static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
int rq_servact, int rq_scope, unsigned int rq_type,
struct prout_param_descriptor *paramp, int noisy)
@@ -291,6 +308,9 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
select_skip_kpartx(conf, mpp);
put_multipath_config(conf);
+ if (rq_servact == MPATH_PROUT_REG_IGN_SA)
+ set_ignored_key(mpp, paramp->key);
+
memcpy(&prkey, paramp->sa_key, 8);
if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
index 39dfa218..1172ec8a 100644
--- a/libmpathpersist/mpath_updatepr.c
+++ b/libmpathpersist/mpath_updatepr.c
@@ -18,14 +18,13 @@
#include "uxsock.h"
#include "memory.h"
#include "mpathpr.h"
+#include "structs.h"
-static int do_update_pr(char *alias, char *cmd, char *key)
+static char *do_pr(char *alias, char *str)
{
int fd;
- char str[256];
char *reply;
- int ret = 0;
int timeout;
struct config *conf;
@@ -36,24 +35,35 @@ static int do_update_pr(char *alias, char *cmd, char *key)
fd = mpath_connect();
if (fd == -1) {
condlog (0, "ux socket connect error");
- return -1;
+ return NULL;
}
- if (key)
- snprintf(str,sizeof(str),"%s map %s key %s", cmd, alias, key);
- else
- snprintf(str,sizeof(str),"%s map %s", cmd, alias);
condlog (2, "%s: pr message=%s", alias, str);
if (send_packet(fd, str) != 0) {
condlog(2, "%s: message=%s send error=%d", alias, str, errno);
mpath_disconnect(fd);
- return -1;
+ return NULL;
}
- ret = recv_packet(fd, &reply, timeout);
- if (ret < 0) {
+ if (recv_packet(fd, &reply, timeout) < 0)
condlog(2, "%s: message=%s recv error=%d", alias, str, errno);
- ret = -1;
- } else {
+
+ mpath_disconnect(fd);
+ return reply;
+}
+
+static int do_update_pr(char *alias, char *cmd, char *key)
+{
+ char str[256];
+ char *reply = NULL;
+ int ret = -1;
+
+ if (key)
+ snprintf(str,sizeof(str),"%s map %s key %s", cmd, alias, key);
+ else
+ snprintf(str,sizeof(str),"%s map %s", cmd, alias);
+
+ reply = do_pr(alias, str);
+ if (reply) {
condlog (2, "%s: message=%s reply=%s", alias, str, reply);
if (reply && strncmp(reply,"ok", 2) == 0)
ret = 0;
@@ -62,10 +72,29 @@ static int do_update_pr(char *alias, char *cmd, char *key)
}
free(reply);
- mpath_disconnect(fd);
return ret;
}
+int get_prflag(char *mapname) {
+ char str[256];
+ char *reply;
+ int prflag;
+
+ snprintf(str, sizeof(str), "getprstatus map %s", mapname);
+ reply = do_pr(mapname, str);
+ if (!reply)
+ prflag = PRFLAG_UNKNOWN;
+ else if (strncmp(reply,"unset", 5) == 0)
+ prflag = PRFLAG_UNSET;
+ else if (strncmp(reply,"set", 3) == 0)
+ prflag = PRFLAG_SET;
+ else
+ prflag = PRFLAG_UNKNOWN;
+
+ free(reply);
+ return prflag;
+}
+
int update_prflag(char *mapname, int set) {
return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus",
NULL);
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 85eac1c7..35ef82c8 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -42,6 +42,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
int update_prflag(char *mapname, int set);
int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags);
+int get_prflag(char *mapname);
#define update_prkey(mapname, prkey) update_prkey_flags(mapname, prkey, 0)
void * mpath_alloc_prin_response(int prin_sa);
int update_map_pr(struct multipath *mpp, struct path *pp);

View File

@ -0,0 +1,242 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 1 Jul 2025 16:45:00 -0400
Subject: [PATCH] libmultipath: rename prflag_value enums
These will also be used by another variable, so make their name more
generic.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 16 ++++++++--------
libmpathpersist/mpath_updatepr.c | 8 ++++----
libmultipath/structs.h | 8 ++++----
multipathd/cli_handlers.c | 21 +++++++++++----------
multipathd/main.c | 16 ++++++++--------
5 files changed, 35 insertions(+), 34 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 324d88a2..2935be76 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -280,10 +280,10 @@ static void set_ignored_key(struct multipath *mpp, uint8_t *key)
memset(key, 0, 8);
if (!get_be64(mpp->reservation_key))
return;
- if (get_prflag(mpp->alias) == PRFLAG_UNSET)
+ if (get_prflag(mpp->alias) == PR_UNSET)
return;
update_map_pr(mpp, NULL);
- if (mpp->prflag != PRFLAG_SET)
+ if (mpp->prflag != PR_SET)
return;
memcpy(key, &mpp->reservation_key, 8);
}
@@ -908,16 +908,16 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
struct prin_resp *resp;
unsigned int i;
int ret = MPATH_PR_OTHER, isFound;
- bool was_set = (mpp->prflag == PRFLAG_SET);
+ bool was_set = (mpp->prflag == PR_SET);
/* If pr is explicitly unset, it must be manually set */
- if (mpp->prflag == PRFLAG_UNSET)
+ if (mpp->prflag == PR_UNSET)
return MPATH_PR_SKIP;
if (!get_be64(mpp->reservation_key))
{
/* Nothing to do. Assuming pr mgmt feature is disabled*/
- mpp->prflag = PRFLAG_UNSET;
+ mpp->prflag = PR_UNSET;
condlog(was_set ? 2 : 4, "%s: reservation_key not set in multipath.conf", mpp->alias);
return MPATH_PR_SKIP;
}
@@ -943,11 +943,11 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
if (ret != MPATH_PR_SUCCESS )
{
if (ret == MPATH_PR_ILLEGAL_REQ)
- mpp->prflag = PRFLAG_UNSET;
+ mpp->prflag = PR_UNSET;
condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
goto out;
}
- mpp->prflag = PRFLAG_UNSET;
+ mpp->prflag = PR_UNSET;
if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
{
@@ -975,7 +975,7 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
if (isFound)
{
- mpp->prflag = PRFLAG_SET;
+ mpp->prflag = PR_SET;
condlog(was_set ? 3 : 2, "%s: key found. prflag set.",
mpp->alias);
} else
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
index 1172ec8a..00748264 100644
--- a/libmpathpersist/mpath_updatepr.c
+++ b/libmpathpersist/mpath_updatepr.c
@@ -83,13 +83,13 @@ int get_prflag(char *mapname) {
snprintf(str, sizeof(str), "getprstatus map %s", mapname);
reply = do_pr(mapname, str);
if (!reply)
- prflag = PRFLAG_UNKNOWN;
+ prflag = PR_UNKNOWN;
else if (strncmp(reply,"unset", 5) == 0)
- prflag = PRFLAG_UNSET;
+ prflag = PR_UNSET;
else if (strncmp(reply,"set", 3) == 0)
- prflag = PRFLAG_SET;
+ prflag = PR_SET;
else
- prflag = PRFLAG_UNKNOWN;
+ prflag = PR_UNKNOWN;
free(reply);
return prflag;
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 0846e833..f47683c1 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -383,10 +383,10 @@ struct path {
typedef int (pgpolicyfn) (struct multipath *, vector);
-enum prflag_value {
- PRFLAG_UNKNOWN,
- PRFLAG_UNSET,
- PRFLAG_SET,
+enum pr_value {
+ PR_UNKNOWN,
+ PR_UNSET,
+ PR_SET,
};
struct multipath {
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index aca8e2df..1e735aab 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -1279,14 +1279,15 @@ cli_shutdown (void * v, char ** reply, int * len, void * data)
return 0;
}
+static const char * const pr_str[] = {
+ [PR_UNKNOWN] = "unknown\n",
+ [PR_UNSET] = "unset\n",
+ [PR_SET] = "set\n",
+};
+
int
cli_getprstatus (void * v, char ** reply, int * len, void * data)
{
- static const char * const prflag_str[] = {
- [PRFLAG_UNKNOWN] = "unknown\n",
- [PRFLAG_UNSET] = "unset\n",
- [PRFLAG_SET] = "set\n",
- };
struct multipath * mpp;
struct vectors * vecs = (struct vectors *)data;
char * param = get_keyparam(v, MAP);
@@ -1298,7 +1299,7 @@ cli_getprstatus (void * v, char ** reply, int * len, void * data)
if (!mpp)
return 1;
- *len = asprintf(reply, "%s", prflag_str[mpp->prflag]);
+ *len = asprintf(reply, "%s", pr_str[mpp->prflag]);
if (*len < 0)
return 1;
@@ -1321,8 +1322,8 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data)
if (!mpp)
return 1;
- if (mpp->prflag != PRFLAG_SET) {
- mpp->prflag = PRFLAG_SET;
+ if (mpp->prflag != PR_SET) {
+ mpp->prflag = PR_SET;
condlog(2, "%s: prflag set", param);
}
@@ -1344,8 +1345,8 @@ cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
if (!mpp)
return 1;
- if (mpp->prflag != PRFLAG_UNSET) {
- mpp->prflag = PRFLAG_UNSET;
+ if (mpp->prflag != PR_UNSET) {
+ mpp->prflag = PR_UNSET;
condlog(2, "%s: prflag unset", param);
}
diff --git a/multipathd/main.c b/multipathd/main.c
index bd87851d..56050a06 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -555,7 +555,7 @@ pr_register_active_paths(struct multipath *mpp)
vector_foreach_slot (mpp->pg, pgp, i) {
vector_foreach_slot (pgp->paths, pp, j) {
- if (mpp->prflag == PRFLAG_UNSET)
+ if (mpp->prflag == PR_UNSET)
return;
if ((pp->state == PATH_UP) || (pp->state == PATH_GHOST))
mpath_pr_event_handle(pp);
@@ -1139,7 +1139,7 @@ ev_add_path (struct path * pp, struct vectors * vecs, int need_do_map)
int start_waiter = 0;
int ret;
int ro;
- unsigned char prflag = PRFLAG_UNSET;
+ unsigned char prflag = PR_UNSET;
/*
* need path UID to go any further
@@ -1267,7 +1267,7 @@ rescan:
sync_map_state(mpp);
if (retries >= 0) {
- if ((mpp->prflag == PRFLAG_SET && prflag != PRFLAG_SET) ||
+ if ((mpp->prflag == PR_SET && prflag != PR_SET) ||
start_waiter)
pr_register_active_paths(mpp);
condlog(2, "%s [%s]: path added to devmap %s",
@@ -2535,7 +2535,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
}
if (newstate == PATH_UP || newstate == PATH_GHOST) {
- if (pp->mpp->prflag != PRFLAG_UNSET) {
+ if (pp->mpp->prflag != PR_UNSET) {
int prflag = pp->mpp->prflag;
/*
* Check Persistent Reservation.
@@ -2543,8 +2543,8 @@ 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);
- if (pp->mpp->prflag == PRFLAG_SET &&
- prflag != PRFLAG_SET)
+ if (pp->mpp->prflag == PR_SET &&
+ prflag != PR_SET)
pr_register_active_paths(pp->mpp);
}
}
@@ -3728,14 +3728,14 @@ static void mpath_pr_event_handle(struct path *pp)
struct prout_param_descriptor *param;
if (pp->bus != SYSFS_BUS_SCSI) {
- mpp->prflag = PRFLAG_UNSET;
+ mpp->prflag = PR_UNSET;
return;
}
if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS)
return;
- if (mpp->prflag != PRFLAG_SET)
+ if (mpp->prflag != PR_SET)
return;
param = (struct prout_param_descriptor *)MALLOC(sizeof(struct prout_param_descriptor));

View File

@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 3 Jul 2025 14:09:08 -0400
Subject: [PATCH] libmpathpersist: use a switch statement for prout command
finalizing
Change the code at the end of do_mpath_persistent_reserve_out() to
use a switch statement instead of multiple if statements. A future
patch will add more actions here, and a switch statement looks cleaner.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 11 ++++++++---
1 file changed, 8 insertions(+), 3 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 2935be76..12888f90 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -356,15 +356,20 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
goto out1;
}
- if ((ret == MPATH_PR_SUCCESS) && ((rq_servact == MPATH_PROUT_REG_SA) ||
- (rq_servact == MPATH_PROUT_REG_IGN_SA)))
+ if (ret != MPATH_PR_SUCCESS)
+ goto out1;
+
+ switch(rq_servact)
{
+ case MPATH_PROUT_REG_SA:
+ case MPATH_PROUT_REG_IGN_SA:
if (prkey == 0) {
update_prflag(alias, 0);
update_prkey(alias, 0);
} else
update_prflag(alias, 1);
- } else if ((ret == MPATH_PR_SUCCESS) && (rq_servact == MPATH_PROUT_CLEAR_SA)) {
+ break;
+ case MPATH_PROUT_CLEAR_SA:
update_prflag(alias, 0);
update_prkey(alias, 0);
}

View File

@ -0,0 +1,477 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 2 Jul 2025 12:39:08 -0400
Subject: [PATCH] libmpathpersist: Add safety check for preempting on key
change
When a reservation key is changed from one non-zero value to another,
libmpathpersist checks if the old key is still holding the reservation,
and preempts it if it is. This was only safe if two nodes never share
the same key. If a node uses the same key as another node that is
holding the reservation, and switches keys so that it no longer matches,
it will end up preempting the reservation. This is clearly unexpected
behavior, and it can come about by a simple accident of registering a
device with the wrong key, and then immediately fixing it.
To handle this, add code to track if a device is the reservation holder
to multipathd. multipathd now has three new commands "getprhold",
"setprhold", and "unsetprhold". These commands work like the equivalent
*prstatus commands. libmpathpersist calls setprhold on RESERVE commands
and PREEMPT commands when the preempted key is holding the reservation
and unsetprhold on RELEASE commands. Also, calling unsetprflag causes
prhold to be unset as well, so CLEAR commands and REGISTER commands with
a 0x0 service action key will also unset prhold. libmpathpersist() will
also unset prhold if it notices that the device cannot be holding a
reservation in preempt_missing_path().
When a new multipath device is created, its initial prhold state is
PR_UNKNOWN until it checks the current reservation, just like with
prflag. If multipathd ever finds that a device's registration has been
preempted or cleared in update_map_pr(), it unsets prhold, just like
with prflag.
Now, before libmpathpersist preempts a reservation when changing keys
it also checks if multipathd thinks that the device is holding
the reservation. If it does not, then libmpathpersist won't preempt
the key. It will assume that another node is holding the reservation
with the same key.
I should note that this safety check only stops a node not holding the
reservation from preempting the node holding the reservation. If the
node holding the reservation changes its key, but it fails to change the
resevation key, because that path is down or gone, it will still issue
the preempt to move the reservation to a usable path, even if another
node is using the same key. This will remove the registrations for that
other node. It also will not work correctly if multipathd stops tracking
a device for some reason. It's only a best-effort safety check.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 84 +++++++++++++++++++++++++++-----
libmpathpersist/mpath_updatepr.c | 17 ++++++-
libmpathpersist/mpathpr.h | 2 +
libmultipath/structs.h | 1 +
multipathd/cli.c | 7 ++-
multipathd/cli.h | 6 +++
multipathd/cli_handlers.c | 54 ++++++++++++++++++++
multipathd/cli_handlers.h | 3 ++
multipathd/main.c | 36 +++++++++++++-
9 files changed, 194 insertions(+), 16 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 12888f90..e47e9190 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -271,19 +271,39 @@ int __mpath_persistent_reserve_in (int fd, int rq_servact,
resp, noisy);
}
+static int reservation_key_matches(struct multipath *mpp, uint8_t *key,
+ int noisy)
+{
+ struct prin_resp resp = {0};
+ int status;
+
+ status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
+ if (status != MPATH_PR_SUCCESS) {
+ condlog(0, "%s: pr in read reservation command failed.",
+ mpp->wwid);
+ return YNU_UNDEF;
+ }
+ if (!resp.prin_descriptor.prin_readresv.additional_length)
+ return YNU_NO;
+ if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) == 0)
+ return YNU_YES;
+ return YNU_NO;
+}
+
/*
* for MPATH_PROUT_REG_IGN_SA, we use the ignored paramp->key to store the
- * currently registered key.
+ * currently registered key for use in preempt_missing_path(), but only if
+ * the key is holding the reservation.
*/
static void set_ignored_key(struct multipath *mpp, uint8_t *key)
{
memset(key, 0, 8);
if (!get_be64(mpp->reservation_key))
return;
- if (get_prflag(mpp->alias) == PR_UNSET)
+ if (get_prhold(mpp->alias) == PR_UNSET)
return;
- update_map_pr(mpp, NULL);
- if (mpp->prflag != PR_SET)
+ if (reservation_key_matches(mpp, (uint8_t *)&mpp->reservation_key,
+ 0) == YNU_NO)
return;
memcpy(key, &mpp->reservation_key, 8);
}
@@ -297,6 +317,7 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
int ret;
uint64_t prkey;
struct config *conf;
+ bool preempting_reservation = false;
ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
if (ret != MPATH_PR_SUCCESS)
@@ -341,9 +362,12 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
case MPATH_PROUT_REG_IGN_SA:
ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
break;
- case MPATH_PROUT_RES_SA :
case MPATH_PROUT_PREE_SA :
case MPATH_PROUT_PREE_AB_SA :
+ if (reservation_key_matches(mpp, paramp->sa_key, noisy) == YNU_YES)
+ preempting_reservation = true;
+ /* fallthrough */
+ case MPATH_PROUT_RES_SA :
case MPATH_PROUT_CLEAR_SA:
ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
paramp, noisy, NULL);
@@ -372,6 +396,15 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
case MPATH_PROUT_CLEAR_SA:
update_prflag(alias, 0);
update_prkey(alias, 0);
+ break;
+ case MPATH_PROUT_RES_SA:
+ case MPATH_PROUT_REL_SA:
+ update_prhold(alias, (rq_servact == MPATH_PROUT_RES_SA));
+ break;
+ case MPATH_PROUT_PREE_SA:
+ case MPATH_PROUT_PREE_AB_SA:
+ if (preempting_reservation)
+ update_prhold(alias, 1);
}
out1:
FREE(alias);
@@ -455,6 +488,10 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid)
* holding the reservation on a path that couldn't get its key updated,
* either because it is down or no longer part of the multipath device,
* you need to preempt the reservation to a usable path with the new key
+ *
+ * Also, it's possible that the reservation was preempted, and the device
+ * is being re-registered. If it appears that is the case, clear
+ * mpp->prhold in multipathd.
*/
void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
int noisy)
@@ -467,12 +504,19 @@ void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
int status;
/*
- * If you previously didn't have a key registered or you didn't
- * switch to a different key, there's no need to preempt. Also, you
- * can't preempt if you no longer have a registered key
+ * If you previously didn't have a key registered, you can't
+ * be holding the reservation. Also, you can't preempt if you
+ * no longer have a registered key
*/
- if (memcmp(key, zero, 8) == 0 || memcmp(sa_key, zero, 8) == 0 ||
- memcmp(key, sa_key, 8) == 0)
+ if (memcmp(key, zero, 8) == 0 || memcmp(sa_key, zero, 8) == 0) {
+ update_prhold(mpp->alias, false);
+ return;
+ }
+ /*
+ * If you didn't switch to a different key, there is no need to
+ * preempt.
+ */
+ if (memcmp(key, sa_key, 8) == 0)
return;
status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
@@ -481,13 +525,29 @@ void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
return;
}
/* If there is no reservation, there's nothing to preempt */
- if (!resp.prin_descriptor.prin_readresv.additional_length)
+ if (!resp.prin_descriptor.prin_readresv.additional_length) {
+ update_prhold(mpp->alias, false);
return;
+ }
/*
* If there reservation is not held by the old key, you don't
* want to preempt it
*/
- if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) != 0)
+ if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) != 0) {
+ /*
+ * If reseravation key doesn't match either the old or
+ * the new key, then clear prhold.
+ */
+ if (memcmp(sa_key, resp.prin_descriptor.prin_readresv.key, 8) != 0)
+ update_prhold(mpp->alias, false);
+ return;
+ }
+
+ /*
+ * If multipathd doesn't think it is holding the reservation, don't
+ * preempt it
+ */
+ if (get_prhold(mpp->alias) != PR_SET)
return;
/* Assume this key is being held by an inaccessable path on this
* node. libmpathpersist has never worked if multiple nodes share
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
index 00748264..bfa6e089 100644
--- a/libmpathpersist/mpath_updatepr.c
+++ b/libmpathpersist/mpath_updatepr.c
@@ -75,12 +75,13 @@ static int do_update_pr(char *alias, char *cmd, char *key)
return ret;
}
-int get_prflag(char *mapname) {
+
+static int do_get_pr(char *mapname, const char *cmd) {
char str[256];
char *reply;
int prflag;
- snprintf(str, sizeof(str), "getprstatus map %s", mapname);
+ snprintf(str, sizeof(str), "%s map %s", cmd, mapname);
reply = do_pr(mapname, str);
if (!reply)
prflag = PR_UNKNOWN;
@@ -95,11 +96,23 @@ int get_prflag(char *mapname) {
return prflag;
}
+int get_prflag(char *mapname) {
+ return do_get_pr(mapname, "getprstatus");
+}
+
+int get_prhold(char *mapname) {
+ return do_get_pr(mapname, "getprhold");
+}
+
int update_prflag(char *mapname, int set) {
return do_update_pr(mapname, (set)? "setprstatus" : "unsetprstatus",
NULL);
}
+int update_prhold(char *mapname, bool set) {
+ return do_update_pr(mapname, (set)? "setprhold" : "unsetprhold", NULL);
+}
+
int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags) {
char str[256];
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 35ef82c8..60fa0b09 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -43,6 +43,8 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
int update_prflag(char *mapname, int set);
int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags);
int get_prflag(char *mapname);
+int get_prhold(char *mapname);
+int update_prhold(char *mapname, bool set);
#define update_prkey(mapname, prkey) update_prkey_flags(mapname, prkey, 0)
void * mpath_alloc_prin_response(int prin_sa);
int update_map_pr(struct multipath *mpp, struct path *pp);
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index f47683c1..761fc642 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -466,6 +466,7 @@ struct multipath {
int all_tg_pt;
struct gen_multipath generic_mp;
bool fpin_must_reload;
+ int prhold;
};
static inline int marginal_path_check_enabled(const struct multipath *mpp)
diff --git a/multipathd/cli.c b/multipathd/cli.c
index 03ad0d64..d33b571d 100644
--- a/multipathd/cli.c
+++ b/multipathd/cli.c
@@ -223,7 +223,9 @@ load_keys (void)
r += add_key(keys, "local", LOCAL, 0);
r += add_key(keys, "setmarginal", SETMARGINAL, 0);
r += add_key(keys, "unsetmarginal", UNSETMARGINAL, 0);
-
+ r += add_key(keys, "getprhold", GETPRHOLD, 0);
+ r += add_key(keys, "setprhold", SETPRHOLD, 0);
+ r += add_key(keys, "unsetprhold", UNSETPRHOLD, 0);
if (r) {
free_keys(keys);
@@ -574,6 +576,9 @@ cli_init (void) {
add_handler(GETPRKEY+MAP, NULL);
add_handler(SETPRKEY+MAP+KEY, NULL);
add_handler(UNSETPRKEY+MAP, NULL);
+ add_handler(GETPRHOLD+MAP, NULL);
+ add_handler(SETPRHOLD+MAP, NULL);
+ add_handler(UNSETPRHOLD+MAP, NULL);
add_handler(FORCEQ+DAEMON, NULL);
add_handler(RESTOREQ+DAEMON, NULL);
add_handler(SETMARGINAL+PATH, NULL);
diff --git a/multipathd/cli.h b/multipathd/cli.h
index fdfb9aed..4d12f8fd 100644
--- a/multipathd/cli.h
+++ b/multipathd/cli.h
@@ -47,6 +47,9 @@ enum {
__LOCAL,
__SETMARGINAL,
__UNSETMARGINAL,
+ __GETPRHOLD,
+ __SETPRHOLD,
+ __UNSETPRHOLD,
};
#define LIST (1 << __LIST)
@@ -93,6 +96,9 @@ enum {
#define LOCAL (1ULL << __LOCAL)
#define SETMARGINAL (1ULL << __SETMARGINAL)
#define UNSETMARGINAL (1ULL << __UNSETMARGINAL)
+#define GETPRHOLD (1ULL << __GETPRHOLD)
+#define SETPRHOLD (1ULL << __SETPRHOLD)
+#define UNSETPRHOLD (1ULL << __UNSETPRHOLD)
#define INITIAL_REPLY_LEN 1200
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 1e735aab..f6353b61 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -1349,6 +1349,10 @@ cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
mpp->prflag = PR_UNSET;
condlog(2, "%s: prflag unset", param);
}
+ if (mpp->prhold != PR_UNSET) {
+ mpp->prhold = PR_UNSET;
+ condlog(2, "%s: prhold unset (by clearing prflag)", param);
+ }
return 0;
}
@@ -1441,6 +1445,56 @@ cli_setprkey(void * v, char ** reply, int * len, void * data)
return ret;
}
+static int
+do_prhold(struct vectors *vecs, char *param, int prhold) {
+ struct multipath *mpp = find_mp_by_str(vecs->mpvec, param);
+
+ if (!mpp)
+ return 1;
+
+ if (mpp->prhold != prhold) {
+ mpp->prhold = prhold;
+ condlog(2, "%s: prhold %s", param, pr_str[prhold]);
+ }
+
+ return 0;
+}
+
+int
+cli_setprhold(void * v, char ** reply, int * len, void * data)
+{
+ return do_prhold((struct vectors *)data, get_keyparam(v, MAP),
+ PR_SET);
+}
+
+int
+cli_unsetprhold(void * v, char ** reply, int * len, void * data)
+{
+ return do_prhold((struct vectors *)data, get_keyparam(v, MAP),
+ PR_UNSET);
+}
+
+int
+cli_getprhold(void * v, char ** reply, int * len, void * data)
+{
+ struct multipath *mpp;
+ struct vectors *vecs = (struct vectors *)data;
+ char *param = get_keyparam(v, MAP);
+
+ param = convert_dev(param, 0);
+
+ mpp = find_mp_by_str(vecs->mpvec, param);
+ if (!mpp)
+ return 1;
+
+ *len = asprintf(reply, "%s", pr_str[mpp->prhold]);
+ if (*len < 0)
+ return 1;
+
+ condlog(3, "%s: reply = %s", param, *reply);
+ return 0;
+}
+
int cli_set_marginal(void * v, char ** reply, int * len, void * data)
{
struct vectors * vecs = (struct vectors *)data;
diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h
index 6f57b429..348c8485 100644
--- a/multipathd/cli_handlers.h
+++ b/multipathd/cli_handlers.h
@@ -53,3 +53,6 @@ int cli_unsetprkey(void * v, char ** reply, int * len, void * data);
int cli_set_marginal(void * v, char ** reply, int * len, void * data);
int cli_unset_marginal(void * v, char ** reply, int * len, void * data);
int cli_unset_all_marginal(void * v, char ** reply, int * len, void * data);
+int cli_getprhold(void * v, char ** reply, int * len, void * data);
+int cli_setprhold(void * v, char ** reply, int * len, void * data);
+int cli_unsetprhold(void * v, char ** reply, int * len, void * data);
diff --git a/multipathd/main.c b/multipathd/main.c
index 56050a06..060d3468 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -1874,6 +1874,9 @@ uxlsnrloop (void * ap)
set_handler_callback(SETMARGINAL|PATH, cli_set_marginal);
set_handler_callback(UNSETMARGINAL|PATH, cli_unset_marginal);
set_handler_callback(UNSETMARGINAL|MAP, cli_unset_all_marginal);
+ set_handler_callback(GETPRHOLD|MAP, cli_getprhold);
+ set_handler_callback(SETPRHOLD|MAP, cli_setprhold);
+ set_handler_callback(UNSETPRHOLD|MAP, cli_unsetprhold);
umask(077);
uxsock_listen(num, ux_sock, ap);
@@ -3721,6 +3724,32 @@ main (int argc, char *argv[])
return (child(NULL));
}
+static void check_prhold(struct multipath *mpp, struct path *pp)
+{
+ struct prin_resp resp = {0};
+ int status;
+
+ if (mpp->prflag == PR_UNSET) {
+ mpp->prhold = PR_UNSET;
+ return;
+ }
+ if (mpp->prflag != PR_SET || mpp->prhold != PR_UNKNOWN)
+ return;
+
+ status = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RRES_SA, &resp, 0);
+ if (status != MPATH_PR_SUCCESS) {
+ condlog (0, "%s: pr in read reservation command failed: %d",
+ mpp->wwid, status);
+ return;
+ }
+ mpp->prhold = PR_UNSET;
+ if (!resp.prin_descriptor.prin_readresv.additional_length)
+ return;
+
+ if (memcmp(&mpp->reservation_key, resp.prin_descriptor.prin_readresv.key, 8) == 0)
+ mpp->prhold = PR_SET;
+}
+
static void mpath_pr_event_handle(struct path *pp)
{
struct multipath *mpp = pp->mpp;
@@ -3732,8 +3761,13 @@ static void mpath_pr_event_handle(struct path *pp)
return;
}
- if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS)
+ if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS) {
+ if (mpp->prflag == PR_UNSET)
+ mpp->prhold = PR_UNSET;
return;
+ }
+
+ check_prhold(mpp, pp);
if (mpp->prflag != PR_SET)
return;

View File

@ -0,0 +1,37 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 14 Jul 2025 20:14:06 -0400
Subject: [PATCH] limpathpersist: remove update_map_pr code for NULL pp
Since update_map_pr is always called with pp set now, remove the code
to handle being called with NULL pp.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 12 +-----------
1 file changed, 1 insertion(+), 11 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index e47e9190..fbf6161f 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -993,18 +993,8 @@ int update_map_pr(struct multipath *mpp, struct path *pp)
condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
return MPATH_PR_OTHER;
}
- if (!pp && count_active_paths(mpp) == 0) {
- condlog(2, "%s: No available paths to check pr status",
- mpp->alias);
- goto out;
- }
- if (pp)
- ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp,
- noisy);
- else
- ret = mpath_prin_activepath(mpp, MPATH_PRIN_RKEY_SA, resp,
- noisy);
+ ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, noisy);
if (ret != MPATH_PR_SUCCESS )
{
if (ret == MPATH_PR_ILLEGAL_REQ)

View File

@ -0,0 +1,208 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 14 Jul 2025 20:20:08 -0400
Subject: [PATCH] libmpathpersist: move update_map_pr to multipathd
multipathd is now the only program that calls update_map_pr(), so move
it there, and make it static. There are no other code changes.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/libmpathpersist.version | 1 -
libmpathpersist/mpath_persist.c | 75 -------------------------
libmpathpersist/mpathpr.h | 1 -
multipathd/main.c | 75 +++++++++++++++++++++++++
4 files changed, 75 insertions(+), 77 deletions(-)
diff --git a/libmpathpersist/libmpathpersist.version b/libmpathpersist/libmpathpersist.version
index e0748138..2758e233 100644
--- a/libmpathpersist/libmpathpersist.version
+++ b/libmpathpersist/libmpathpersist.version
@@ -26,7 +26,6 @@ global:
mpath_persistent_reserve_free_vecs;
prin_do_scsi_ioctl;
prout_do_scsi_ioctl;
- update_map_pr;
local: *;
};
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index fbf6161f..22d4e117 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -966,78 +966,3 @@ void * mpath_alloc_prin_response(int prin_sa)
}
return ptr;
}
-
-int update_map_pr(struct multipath *mpp, struct path *pp)
-{
- int noisy=0;
- struct prin_resp *resp;
- unsigned int i;
- int ret = MPATH_PR_OTHER, isFound;
- bool was_set = (mpp->prflag == PR_SET);
-
- /* If pr is explicitly unset, it must be manually set */
- if (mpp->prflag == PR_UNSET)
- return MPATH_PR_SKIP;
-
- if (!get_be64(mpp->reservation_key))
- {
- /* Nothing to do. Assuming pr mgmt feature is disabled*/
- mpp->prflag = PR_UNSET;
- condlog(was_set ? 2 : 4, "%s: reservation_key not set in multipath.conf", mpp->alias);
- return MPATH_PR_SKIP;
- }
-
- resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
- if (!resp)
- {
- condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
- return MPATH_PR_OTHER;
- }
-
- ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, noisy);
- if (ret != MPATH_PR_SUCCESS )
- {
- if (ret == MPATH_PR_ILLEGAL_REQ)
- mpp->prflag = PR_UNSET;
- condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
- goto out;
- }
- mpp->prflag = PR_UNSET;
-
- if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
- {
- condlog(was_set ? 1 : 3, "%s: No key found. Device may not be registered. ", mpp->alias);
- goto out;
- }
-
- condlog(3, "%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++ )
- {
- if (libmp_verbosity >= 3) {
- condlog(3, "%s: PR IN READKEYS[%d] reservation key:",
- mpp->alias, i);
- dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i * 8], 8, 1);
- }
-
- if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i * 8], 8)) {
- condlog(3, "%s: reservation key found in pr in readkeys response", mpp->alias);
- isFound =1;
- }
- }
-
- if (isFound)
- {
- mpp->prflag = PR_SET;
- condlog(was_set ? 3 : 2, "%s: key found. prflag set.",
- mpp->alias);
- } else
- condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
- mpp->alias);
-
-out:
- free(resp);
- return ret;
-}
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 60fa0b09..052af85d 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -47,6 +47,5 @@ int get_prhold(char *mapname);
int update_prhold(char *mapname, bool set);
#define update_prkey(mapname, prkey) update_prkey_flags(mapname, prkey, 0)
void * mpath_alloc_prin_response(int prin_sa);
-int update_map_pr(struct multipath *mpp, struct path *pp);
#endif
diff --git a/multipathd/main.c b/multipathd/main.c
index 060d3468..29c788e3 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3750,6 +3750,81 @@ static void check_prhold(struct multipath *mpp, struct path *pp)
mpp->prhold = PR_SET;
}
+static int update_map_pr(struct multipath *mpp, struct path *pp)
+{
+ int noisy=0;
+ struct prin_resp *resp;
+ unsigned int i;
+ int ret = MPATH_PR_OTHER, isFound;
+ bool was_set = (mpp->prflag == PR_SET);
+
+ /* If pr is explicitly unset, it must be manually set */
+ if (mpp->prflag == PR_UNSET)
+ return MPATH_PR_SKIP;
+
+ if (!get_be64(mpp->reservation_key))
+ {
+ /* Nothing to do. Assuming pr mgmt feature is disabled*/
+ mpp->prflag = PR_UNSET;
+ condlog(was_set ? 2 : 4, "%s: reservation_key not set in multipath.conf", mpp->alias);
+ return MPATH_PR_SKIP;
+ }
+
+ resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
+ if (!resp)
+ {
+ condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
+ return MPATH_PR_OTHER;
+ }
+
+ ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, noisy);
+ if (ret != MPATH_PR_SUCCESS )
+ {
+ if (ret == MPATH_PR_ILLEGAL_REQ)
+ mpp->prflag = PR_UNSET;
+ condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
+ goto out;
+ }
+ mpp->prflag = PR_UNSET;
+
+ if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
+ {
+ condlog(was_set ? 1 : 3, "%s: No key found. Device may not be registered. ", mpp->alias);
+ goto out;
+ }
+
+ condlog(3, "%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++ )
+ {
+ if (libmp_verbosity >= 3) {
+ condlog(3, "%s: PR IN READKEYS[%d] reservation key:",
+ mpp->alias, i);
+ dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i * 8], 8, 1);
+ }
+
+ if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i * 8], 8)) {
+ condlog(3, "%s: reservation key found in pr in readkeys response", mpp->alias);
+ isFound =1;
+ }
+ }
+
+ if (isFound)
+ {
+ mpp->prflag = PR_SET;
+ condlog(was_set ? 3 : 2, "%s: key found. prflag set.",
+ mpp->alias);
+ } else
+ condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
+ mpp->alias);
+
+out:
+ free(resp);
+ return ret;
+}
+
static void mpath_pr_event_handle(struct path *pp)
{
struct multipath *mpp = pp->mpp;

View File

@ -0,0 +1,150 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 15 Jul 2025 14:14:42 -0400
Subject: [PATCH] multipathd: clean up update_map_pr and mpath_pr_event_handle
Store the READ KEYS response and the prout_param_descriptor on the stack
to avoid having to fail these functions for allocation reasons. Don't
explicitly check for additional_length == 0, since the for-loop already
handles that. Also cleanup formatting issues,remove redundant messages,
and reduce the log level of others.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/main.c | 70 ++++++++++++++++-------------------------------
1 file changed, 24 insertions(+), 46 deletions(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 29c788e3..8466d4d4 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3752,8 +3752,7 @@ static void check_prhold(struct multipath *mpp, struct path *pp)
static int update_map_pr(struct multipath *mpp, struct path *pp)
{
- int noisy=0;
- struct prin_resp *resp;
+ struct prin_resp resp;
unsigned int i;
int ret = MPATH_PR_OTHER, isFound;
bool was_set = (mpp->prflag == PR_SET);
@@ -3762,57 +3761,42 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
if (mpp->prflag == PR_UNSET)
return MPATH_PR_SKIP;
- if (!get_be64(mpp->reservation_key))
- {
+ if (!get_be64(mpp->reservation_key)) {
/* Nothing to do. Assuming pr mgmt feature is disabled*/
mpp->prflag = PR_UNSET;
condlog(was_set ? 2 : 4, "%s: reservation_key not set in multipath.conf", mpp->alias);
return MPATH_PR_SKIP;
}
- resp = mpath_alloc_prin_response(MPATH_PRIN_RKEY_SA);
- if (!resp)
- {
- condlog(0,"%s : failed to alloc resp in update_map_pr", mpp->alias);
- return MPATH_PR_OTHER;
- }
+ memset(&resp, 0, sizeof(resp));
- ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, resp, noisy);
- if (ret != MPATH_PR_SUCCESS )
- {
+ ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, &resp, 0);
+ if (ret != MPATH_PR_SUCCESS) {
if (ret == MPATH_PR_ILLEGAL_REQ)
mpp->prflag = PR_UNSET;
condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
- goto out;
+ return ret;
}
mpp->prflag = PR_UNSET;
- if (resp->prin_descriptor.prin_readkeys.additional_length == 0 )
- {
- condlog(was_set ? 1 : 3, "%s: No key found. Device may not be registered. ", mpp->alias);
- goto out;
- }
-
- condlog(3, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
+ 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++ )
- {
- if (libmp_verbosity >= 3) {
- condlog(3, "%s: PR IN READKEYS[%d] 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];
+
+ if (libmp_verbosity >= 4) {
+ condlog(4, "%s: PR IN READKEYS[%d] reservation key:",
mpp->alias, i);
- dumpHex((char *)&resp->prin_descriptor.prin_readkeys.key_list[i * 8], 8, 1);
+ dumpHex((char *)keyp, 8, 1);
}
- if (!memcmp(&mpp->reservation_key, &resp->prin_descriptor.prin_readkeys.key_list[i * 8], 8)) {
- condlog(3, "%s: reservation key found in pr in readkeys response", mpp->alias);
- isFound =1;
- }
+ if (!memcmp(&mpp->reservation_key, keyp, 8))
+ isFound = 1;
}
- if (isFound)
- {
+ if (isFound) {
mpp->prflag = PR_SET;
condlog(was_set ? 3 : 2, "%s: key found. prflag set.",
mpp->alias);
@@ -3820,16 +3804,14 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
mpp->alias);
-out:
- free(resp);
- return ret;
+ return MPATH_PR_SUCCESS;
}
static void mpath_pr_event_handle(struct path *pp)
{
struct multipath *mpp = pp->mpp;
int ret;
- struct prout_param_descriptor *param;
+ struct prout_param_descriptor param;
if (pp->bus != SYSFS_BUS_SCSI) {
mpp->prflag = PR_UNSET;
@@ -3847,21 +3829,17 @@ static void mpath_pr_event_handle(struct path *pp)
if (mpp->prflag != PR_SET)
return;
- param = (struct prout_param_descriptor *)MALLOC(sizeof(struct prout_param_descriptor));
- if (!param)
- return;
+ memset(&param, 0, sizeof(param));
- param->sa_flags = mpp->sa_flags;
- memcpy(param->sa_key, &mpp->reservation_key, 8);
- param->num_transportid = 0;
+ param.sa_flags = mpp->sa_flags;
+ memcpy(param.sa_key, &mpp->reservation_key, 8);
+ param.num_transportid = 0;
condlog(3, "device %s:%s", pp->dev, pp->mpp->wwid);
- ret = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REG_IGN_SA, 0, 0, param, 0);
+ ret = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REG_IGN_SA, 0, 0, &param, 0);
if (ret != MPATH_PR_SUCCESS )
{
condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret);
}
-
- free(param);
}

View File

@ -0,0 +1,78 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 15 Jul 2025 14:50:55 -0400
Subject: [PATCH] libmpathpersist: clean up duplicate function declarations
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_pr_ioctl.c | 10 +++-------
mpathpersist/main.c | 3 +--
multipathd/main.h | 6 ------
3 files changed, 4 insertions(+), 15 deletions(-)
diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c
index 9cd83729..e5b25692 100644
--- a/libmpathpersist/mpath_pr_ioctl.c
+++ b/libmpathpersist/mpath_pr_ioctl.c
@@ -15,20 +15,16 @@
#include "mpath_pr_ioctl.h"
#include "mpath_persist.h"
#include "unaligned.h"
-
#include "debug.h"
#define FILE_NAME_SIZE 256
+#include "mpathpr.h"
#define TIMEOUT 2000
#define MAXRETRY 5
-int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp *resp, int noisy);
-int mpath_translate_response (char * dev, struct sg_io_hdr io_hdr,
- SenseData_t *Sensedata);
-void dumpHex(const char* str, int len, int no_ascii);
-int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy);
+int mpath_translate_response(char *dev, struct sg_io_hdr io_hdr,
+ SenseData_t *Sensedata);
uint32_t format_transportids(struct prout_param_descriptor *paramp);
void convert_be32_to_cpu(uint32_t *num);
void convert_be16_to_cpu(uint16_t *num);
diff --git a/mpathpersist/main.c b/mpathpersist/main.c
index 14245cc3..26c69b01 100644
--- a/mpathpersist/main.c
+++ b/mpathpersist/main.c
@@ -17,6 +17,7 @@
#include <string.h>
#include <errno.h>
#include "version.h"
+#include "mpathpr.h"
static const char * pr_type_strs[] = {
"obsolete [0]",
@@ -37,8 +38,6 @@ void mpath_print_buf_readcap(struct prin_resp *pr_buff);
void mpath_print_buf_readfullstat(struct prin_resp *pr_buff);
void mpath_print_buf_readresv(struct prin_resp *pr_buff);
void mpath_print_buf_readkeys(struct prin_resp *pr_buff);
-void dumpHex(const char* str, int len, int no_ascii);
-void * mpath_alloc_prin_response(int prin_sa);
void mpath_print_transport_id(struct prin_fulldescr *fdesc);
int construct_transportid(const char * inp, struct transportid transid[], int num_transportids);
diff --git a/multipathd/main.h b/multipathd/main.h
index 6d2ae72a..5b5840c0 100644
--- a/multipathd/main.h
+++ b/multipathd/main.h
@@ -46,12 +46,6 @@ int ev_remove_map (char *, char *, int, struct vectors *);
int flush_map(struct multipath *, struct vectors *);
int set_config_state(enum daemon_status);
void * mpath_alloc_prin_response(int prin_sa);
-int prin_do_scsi_ioctl(char *, int rq_servact, struct prin_resp * resp,
- int noisy);
-void dumpHex(const char * , int len, int no_ascii);
-int prout_do_scsi_ioctl(char * , int rq_servact, int rq_scope,
- unsigned int rq_type,
- struct prout_param_descriptor *param, int noisy);
void handle_signals(bool);
int __setup_multipath (struct vectors * vecs, struct multipath * mpp,
int reset);

View File

@ -0,0 +1,148 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 15 Jul 2025 17:54:16 -0400
Subject: [PATCH] multipathd: wrap setting and unsetting prflag
When prflag is unset, prhold and sa_flags should also be unset. A future
patch will add another variable to be set when prflag is set. Wrap all
these actions in set_pr() and unset_pr().
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/cli_handlers.c | 11 +++++------
multipathd/main.c | 34 ++++++++++++++++++++--------------
multipathd/main.h | 2 ++
3 files changed, 27 insertions(+), 20 deletions(-)
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index f6353b61..93768ef3 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -1323,7 +1323,7 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data)
return 1;
if (mpp->prflag != PR_SET) {
- mpp->prflag = PR_SET;
+ set_pr(mpp);
condlog(2, "%s: prflag set", param);
}
@@ -1346,12 +1346,11 @@ cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
return 1;
if (mpp->prflag != PR_UNSET) {
- mpp->prflag = PR_UNSET;
condlog(2, "%s: prflag unset", param);
- }
- if (mpp->prhold != PR_UNSET) {
- mpp->prhold = PR_UNSET;
- condlog(2, "%s: prhold unset (by clearing prflag)", param);
+ if (mpp->prhold != PR_UNSET)
+ condlog(2, "%s: prhold unset (by clearing prflag)",
+ param);
+ unset_pr(mpp);
}
return 0;
diff --git a/multipathd/main.c b/multipathd/main.c
index 8466d4d4..bc4cf66e 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3729,10 +3729,6 @@ static void check_prhold(struct multipath *mpp, struct path *pp)
struct prin_resp resp = {0};
int status;
- if (mpp->prflag == PR_UNSET) {
- mpp->prhold = PR_UNSET;
- return;
- }
if (mpp->prflag != PR_SET || mpp->prhold != PR_UNKNOWN)
return;
@@ -3750,6 +3746,18 @@ static void check_prhold(struct multipath *mpp, struct path *pp)
mpp->prhold = PR_SET;
}
+void set_pr(struct multipath *mpp)
+{
+ mpp->prflag = PR_SET;
+}
+
+void unset_pr(struct multipath *mpp)
+{
+ mpp->prflag = PR_UNSET;
+ mpp->prhold = PR_UNSET;
+ mpp->sa_flags = 0;
+}
+
static int update_map_pr(struct multipath *mpp, struct path *pp)
{
struct prin_resp resp;
@@ -3763,7 +3771,7 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
if (!get_be64(mpp->reservation_key)) {
/* Nothing to do. Assuming pr mgmt feature is disabled*/
- mpp->prflag = PR_UNSET;
+ unset_pr(mpp);
condlog(was_set ? 2 : 4, "%s: reservation_key not set in multipath.conf", mpp->alias);
return MPATH_PR_SKIP;
}
@@ -3773,11 +3781,10 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, &resp, 0);
if (ret != MPATH_PR_SUCCESS) {
if (ret == MPATH_PR_ILLEGAL_REQ)
- mpp->prflag = PR_UNSET;
+ unset_pr(mpp);
condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
return ret;
}
- mpp->prflag = PR_UNSET;
condlog(4, "%s: Multipath reservation_key: 0x%" PRIx64 " ", mpp->alias,
get_be64(mpp->reservation_key));
@@ -3797,12 +3804,14 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
}
if (isFound) {
- mpp->prflag = PR_SET;
+ set_pr(mpp);
condlog(was_set ? 3 : 2, "%s: key found. prflag set.",
mpp->alias);
- } else
+ } else {
+ unset_pr(mpp);
condlog(was_set ? 1 : 3, "%s: key not found. prflag unset.",
mpp->alias);
+ }
return MPATH_PR_SUCCESS;
}
@@ -3814,15 +3823,12 @@ static void mpath_pr_event_handle(struct path *pp)
struct prout_param_descriptor param;
if (pp->bus != SYSFS_BUS_SCSI) {
- mpp->prflag = PR_UNSET;
+ unset_pr(mpp);
return;
}
- if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS) {
- if (mpp->prflag == PR_UNSET)
- mpp->prhold = PR_UNSET;
+ if (update_map_pr(mpp, pp) != MPATH_PR_SUCCESS)
return;
- }
check_prhold(mpp, pp);
diff --git a/multipathd/main.h b/multipathd/main.h
index 5b5840c0..77603751 100644
--- a/multipathd/main.h
+++ b/multipathd/main.h
@@ -58,4 +58,6 @@ void handle_path_wwid_change(struct path *pp, struct vectors *vecs);
bool check_path_wwid_change(struct path *pp);
int resize_map(struct multipath *mpp, unsigned long long size,
struct vectors *vecs);
+void set_pr(struct multipath *mpp);
+void unset_pr(struct multipath *mpp);
#endif /* MAIN_H */

View File

@ -0,0 +1,141 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 16 Jul 2025 13:15:05 -0400
Subject: [PATCH] multipathd: unregister PR key when path is restored if
necessary
It is possible that a path was unavailable and either the registered PR
key was removed or the registered PR key was changed and then that new
key was preempted. In both of these situations, this path will still
have a registered key (just not one that matches mpp->reservation_key)
but it should not have one. If the path becomes usable again in this
state, it may allow the multipath device to access storage that it
shouldn't be allowed to access.
To deal with this, track if a multipath device ever had a registered PR
key. If so, and the device no longer has a registered key, explicitly
clear the key when paths get restored.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/structs.h | 1 +
multipathd/main.c | 46 ++++++++++++++++++++++++++++++++----------
2 files changed, 36 insertions(+), 11 deletions(-)
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index 761fc642..a67e767d 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -467,6 +467,7 @@ struct multipath {
struct gen_multipath generic_mp;
bool fpin_must_reload;
int prhold;
+ bool ever_registered_pr;
};
static inline int marginal_path_check_enabled(const struct multipath *mpp)
diff --git a/multipathd/main.c b/multipathd/main.c
index bc4cf66e..56d51c48 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -2538,7 +2538,8 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
}
if (newstate == PATH_UP || newstate == PATH_GHOST) {
- if (pp->mpp->prflag != PR_UNSET) {
+ if (pp->mpp->prflag != PR_UNSET ||
+ pp->mpp->ever_registered_pr) {
int prflag = pp->mpp->prflag;
/*
* Check Persistent Reservation.
@@ -3748,6 +3749,7 @@ static void check_prhold(struct multipath *mpp, struct path *pp)
void set_pr(struct multipath *mpp)
{
+ mpp->ever_registered_pr = true;
mpp->prflag = PR_SET;
}
@@ -3758,22 +3760,27 @@ void unset_pr(struct multipath *mpp)
mpp->sa_flags = 0;
}
+/*
+ * 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.
+ */
static int update_map_pr(struct multipath *mpp, struct path *pp)
{
struct prin_resp resp;
unsigned int i;
- int ret = MPATH_PR_OTHER, isFound;
+ int ret, isFound;
bool was_set = (mpp->prflag == PR_SET);
/* If pr is explicitly unset, it must be manually set */
if (mpp->prflag == PR_UNSET)
- return MPATH_PR_SKIP;
+ return MPATH_PR_SUCCESS;
if (!get_be64(mpp->reservation_key)) {
/* Nothing to do. Assuming pr mgmt feature is disabled*/
unset_pr(mpp);
condlog(was_set ? 2 : 4, "%s: reservation_key not set in multipath.conf", mpp->alias);
- return MPATH_PR_SKIP;
+ return MPATH_PR_SUCCESS;
}
memset(&resp, 0, sizeof(resp));
@@ -3821,6 +3828,7 @@ static void mpath_pr_event_handle(struct path *pp)
struct multipath *mpp = pp->mpp;
int ret;
struct prout_param_descriptor param;
+ bool clear_reg = false;
if (pp->bus != SYSFS_BUS_SCSI) {
unset_pr(mpp);
@@ -3832,20 +3840,36 @@ static void mpath_pr_event_handle(struct path *pp)
check_prhold(mpp, pp);
- if (mpp->prflag != PR_SET)
- return;
+ if (mpp->prflag != PR_SET) {
+ if (!mpp->ever_registered_pr)
+ return;
+ /*
+ * This path may have been unusable and either the
+ * registration was cleared or the registered
+ * key was switched and then that new key was preempted.
+ * In either case, this path should not have a registration
+ * but it might still have one, just with a different
+ * key than mpp->reservation_key is currently set to.
+ * clear it to be sure.
+ */
+ clear_reg = true;
+ }
memset(&param, 0, sizeof(param));
- param.sa_flags = mpp->sa_flags;
- memcpy(param.sa_key, &mpp->reservation_key, 8);
- param.num_transportid = 0;
+ if (!clear_reg) {
+ param.sa_flags = mpp->sa_flags;
+ memcpy(param.sa_key, &mpp->reservation_key, 8);
+ param.num_transportid = 0;
+ }
- condlog(3, "device %s:%s", pp->dev, pp->mpp->wwid);
+ 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, &param, 0);
if (ret != MPATH_PR_SUCCESS )
{
- condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret);
+ condlog(0, "%s: %s reservation registration failed. Error: %d",
+ clear_reg ? "Clearing" : "Setting", pp->dev, ret);
}
}

View File

@ -0,0 +1,108 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 17 Jul 2025 15:32:37 -0400
Subject: [PATCH] libmpathpersist: Fix-up reservation_key checking
The reservation key checking in do_mpath_persistent_reserve_out() was
slightly wrong. It allowed invalid keys for preempting. It now correctly
checks the reservation key for the preempt commands.
It also was a little overly strict in some places. Formerly, it only
allowed registering from any key to the configured key or registering
from the configured key to any key (as long as you use the prkeys file).
Now it also allows unregistering from any key and registering an
unregistered device to any key (as long as you use the prkeys file).
Also, clarify the code by replacing prkey with a bool tracking if you
are registering or unregistering.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 49 +++++++++++++++++++++++++--------
1 file changed, 38 insertions(+), 11 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 22d4e117..98b6997c 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -315,9 +315,9 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
struct multipath *mpp;
char *alias;
int ret;
- uint64_t prkey;
+ uint64_t zerokey = 0;
struct config *conf;
- bool preempting_reservation = false;
+ bool unregistering, preempting_reservation = false;
ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
if (ret != MPATH_PR_SUCCESS)
@@ -332,11 +332,12 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
if (rq_servact == MPATH_PROUT_REG_IGN_SA)
set_ignored_key(mpp, paramp->key);
- memcpy(&prkey, paramp->sa_key, 8);
- if (mpp->prkey_source == PRKEY_SOURCE_FILE && prkey &&
+ unregistering = (memcmp(&zerokey, paramp->sa_key, 8) == 0);
+ if (mpp->prkey_source == PRKEY_SOURCE_FILE && !unregistering &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
(rq_servact == MPATH_PROUT_REG_SA &&
(!get_be64(mpp->reservation_key) ||
+ memcmp(paramp->key, &zerokey, 8) == 0 ||
memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) {
memcpy(&mpp->reservation_key, paramp->sa_key, 8);
if (update_prkey_flags(alias, get_be64(mpp->reservation_key),
@@ -348,12 +349,38 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
}
}
- if (memcmp(paramp->key, &mpp->reservation_key, 8) &&
- memcmp(paramp->sa_key, &mpp->reservation_key, 8) &&
- (prkey || rq_servact != MPATH_PROUT_REG_IGN_SA)) {
- condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64, alias, get_be64(mpp->reservation_key));
- ret = MPATH_PR_SYNTAX_ERROR;
- goto out1;
+ /*
+ * If you are registering a non-zero key, mpp->reservation_key
+ * must be set and must equal paramp->sa_key.
+ * If you're not registering a key, mpp->reservation_key must be
+ * set, and must equal paramp->key
+ */
+ if ((rq_servact == MPATH_PROUT_REG_IGN_SA ||
+ rq_servact == MPATH_PROUT_REG_SA)) {
+ if (!unregistering && !get_be64(mpp->reservation_key)) {
+ condlog(0, "%s: no configured reservation key", alias);
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out1;
+ }
+ if (!unregistering &&
+ memcmp(paramp->sa_key, &mpp->reservation_key, 8)) {
+ condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64,
+ mpp->alias, get_be64(mpp->reservation_key));
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out1;
+ }
+ } else {
+ if (!get_be64(mpp->reservation_key)) {
+ condlog(0, "%s: no configured reservation key", alias);
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out1;
+ }
+ if (memcmp(paramp->key, &mpp->reservation_key, 8)) {
+ condlog(0, "%s: configured reservation key doesn't match: 0x%" PRIx64,
+ mpp->alias, get_be64(mpp->reservation_key));
+ ret = MPATH_PR_SYNTAX_ERROR;
+ goto out1;
+ }
}
switch(rq_servact)
@@ -387,7 +414,7 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
{
case MPATH_PROUT_REG_SA:
case MPATH_PROUT_REG_IGN_SA:
- if (prkey == 0) {
+ if (unregistering) {
update_prflag(alias, 0);
update_prkey(alias, 0);
} else

View File

@ -0,0 +1,137 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 17 Jul 2025 19:12:23 -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>
---
libmpathpersist/mpath_persist.c | 69 +++++++++++++++++++++------------
1 file changed, 44 insertions(+), 25 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 98b6997c..af1fdd61 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -598,13 +598,13 @@ 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;
@@ -693,43 +693,62 @@ 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(&paramp->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(&paramp->key, &paramp->sa_key, 8);
- memset(&paramp->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",
+ 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;

View File

@ -0,0 +1,50 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 21 Jul 2025 19:27:17 -0400
Subject: [PATCH] libmpathpersist: Clear prkey in multipathd before
unregistering
When you register or switch keys in libmpathpersist, it updates
mpp->reservation_key in multipathd before doing the registration. This
means that any paths that come online while you are doing the
registration get the new key registered. libmpathpersist didn't do
this when unregistering a key. This could cause the same problem. A
path that got restored while unregistering the device could end up
getting the old key registered on it. Fix this by unsetting the key
before doing the unregister, instead of afterwards.
There is still a race condition associated with updating
mpp->reservation_key before doing the registration (but not on
unregistration). This will be dealt with by a future patch.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index af1fdd61..ee49c27b 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -333,7 +333,7 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
set_ignored_key(mpp, paramp->key);
unregistering = (memcmp(&zerokey, paramp->sa_key, 8) == 0);
- if (mpp->prkey_source == PRKEY_SOURCE_FILE && !unregistering &&
+ if (mpp->prkey_source == PRKEY_SOURCE_FILE &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
(rq_servact == MPATH_PROUT_REG_SA &&
(!get_be64(mpp->reservation_key) ||
@@ -414,10 +414,9 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
{
case MPATH_PROUT_REG_SA:
case MPATH_PROUT_REG_IGN_SA:
- if (unregistering) {
+ if (unregistering)
update_prflag(alias, 0);
- update_prkey(alias, 0);
- } else
+ else
update_prflag(alias, 1);
break;
case MPATH_PROUT_CLEAR_SA:

View File

@ -0,0 +1,27 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 23 Jul 2025 16:52:53 -0400
Subject: [PATCH] libmpathpersist: only clear the key if we are using the
prkeys file
Otherwise this request will create a useless prkeys file.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index ee49c27b..f585d8e9 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -421,7 +421,8 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
break;
case MPATH_PROUT_CLEAR_SA:
update_prflag(alias, 0);
- update_prkey(alias, 0);
+ if (mpp->prkey_source == PRKEY_SOURCE_FILE)
+ update_prkey(alias, 0);
break;
case MPATH_PROUT_RES_SA:
case MPATH_PROUT_REL_SA:

View File

@ -0,0 +1,50 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 23 Jul 2025 17:54:45 -0400
Subject: [PATCH] libmpathpersist: Restore old reservation key on failure
If we updated the key and then failed, restore the old key.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index f585d8e9..5b81c67f 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -316,8 +316,10 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
char *alias;
int ret;
uint64_t zerokey = 0;
+ struct be64 oldkey = {0};
struct config *conf;
bool unregistering, preempting_reservation = false;
+ bool updated_prkey = false;
ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
if (ret != MPATH_PR_SUCCESS)
@@ -339,6 +341,8 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
(!get_be64(mpp->reservation_key) ||
memcmp(paramp->key, &zerokey, 8) == 0 ||
memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) {
+ updated_prkey = true;
+ memcpy(&oldkey, &mpp->reservation_key, 8);
memcpy(&mpp->reservation_key, paramp->sa_key, 8);
if (update_prkey_flags(alias, get_be64(mpp->reservation_key),
paramp->sa_flags)) {
@@ -407,8 +411,12 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
goto out1;
}
- if (ret != MPATH_PR_SUCCESS)
+ if (ret != MPATH_PR_SUCCESS) {
+ if (updated_prkey)
+ update_prkey_flags(alias, get_be64(oldkey),
+ mpp->sa_flags);
goto out1;
+ }
switch(rq_servact)
{

View File

@ -0,0 +1,169 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 23 Jul 2025 18:28:52 -0400
Subject: [PATCH] libmpatpersist: update reservation key before checking paths
There is a race condition when changing reservation keys where a failed
path could come back online after libmpathpersist checks the paths, but
before it updates the reservation key. In this case, the path would come
up and get reregistered with the old key by multipathd, and
libmpathpersist would not update its key, because the path was down
when it checked.
To fix this, check the paths after updating the key, so any path that
comes up after getting checked will use the updated key.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 83 ++++++++++++++-------------------
libmpathpersist/mpathpr.h | 1 -
2 files changed, 36 insertions(+), 48 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 5b81c67f..3d4c0c2c 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -187,7 +187,19 @@ int mpath_persistent_reserve_init_vecs(int verbose)
return __mpath_persistent_reserve_init_vecs(&curmp, &pathvec, verbose);
}
-static int mpath_get_map(vector curmp, vector pathvec, int fd, char **palias,
+static int
+get_path_info(struct multipath *mpp, vector pathvec)
+{
+ if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
+ update_mpp_paths(mpp, pathvec)) {
+ condlog(0, "error parsing map %s", mpp->wwid);
+ return MPATH_PR_DMMP_ERROR;
+ }
+ extract_hwe_from_path(mpp);
+ return MPATH_PR_SUCCESS ;
+}
+
+static int mpath_get_map(vector curmp, int fd, char **palias,
struct multipath **pmpp)
{
int ret = MPATH_PR_DMMP_ERROR;
@@ -223,12 +235,6 @@ static int mpath_get_map(vector curmp, vector pathvec, int fd, char **palias,
goto out;
}
- /* get info of all paths from the dm device */
- if (get_mpvec(curmp, pathvec, alias)){
- condlog(0, "%s: failed to get device info.", alias);
- goto out;
- }
-
mpp = find_mp_by_alias(curmp, alias);
if (!mpp) {
@@ -254,7 +260,11 @@ static int do_mpath_persistent_reserve_in (vector curmp, vector pathvec,
struct multipath *mpp;
int ret;
- ret = mpath_get_map(curmp, pathvec, fd, NULL, &mpp);
+ ret = mpath_get_map(curmp, fd, NULL, &mpp);
+ if (ret != MPATH_PR_SUCCESS)
+ return ret;
+
+ ret = get_path_info(mpp, pathvec);
if (ret != MPATH_PR_SUCCESS)
return ret;
@@ -321,19 +331,15 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
bool unregistering, preempting_reservation = false;
bool updated_prkey = false;
- ret = mpath_get_map(curmp, pathvec, fd, &alias, &mpp);
+ ret = mpath_get_map(curmp, fd, &alias, &mpp);
if (ret != MPATH_PR_SUCCESS)
return ret;
conf = get_multipath_config();
+ mpp->mpe = find_mpe(conf->mptable, mpp->wwid);
select_reservation_key(conf, mpp);
- select_all_tg_pt(conf, mpp);
- select_skip_kpartx(conf, mpp);
put_multipath_config(conf);
- if (rq_servact == MPATH_PROUT_REG_IGN_SA)
- set_ignored_key(mpp, paramp->key);
-
unregistering = (memcmp(&zerokey, paramp->sa_key, 8) == 0);
if (mpp->prkey_source == PRKEY_SOURCE_FILE &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
@@ -387,6 +393,22 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
}
}
+ ret = get_path_info(mpp, pathvec);
+ if (ret != MPATH_PR_SUCCESS) {
+ if (updated_prkey)
+ update_prkey_flags(alias, get_be64(oldkey),
+ mpp->sa_flags);
+ goto out1;
+ }
+
+ conf = get_multipath_config();
+ select_all_tg_pt(conf, mpp);
+ select_skip_kpartx(conf, mpp);
+ put_multipath_config(conf);
+
+ if (rq_servact == MPATH_PROUT_REG_IGN_SA)
+ set_ignored_key(mpp, paramp->key);
+
switch(rq_servact)
{
case MPATH_PROUT_REG_SA:
@@ -485,39 +507,6 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope,
return ret;
}
-int
-get_mpvec (vector curmp, vector pathvec, char * refwwid)
-{
- int i;
- struct multipath *mpp;
-
- vector_foreach_slot (curmp, mpp, i){
- /*
- * discard out of scope maps
- */
- if (!mpp->alias) {
- condlog(0, "%s: map with empty alias!", __func__);
- continue;
- }
-
- if (mpp->pg != NULL)
- /* Already seen this one */
- continue;
-
- if (refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE - 1))
- continue;
-
- if (update_multipath_table(mpp, pathvec, DI_CHECKER) != DMP_OK ||
- update_mpp_paths(mpp, pathvec)) {
- condlog(1, "error parsing map %s", mpp->wwid);
- remove_map(mpp, pathvec, curmp);
- i--;
- } else
- extract_hwe_from_path(mpp);
- }
- return MPATH_PR_SUCCESS ;
-}
-
/*
* If you are changing the key registered to a device, and that device is
* holding the reservation on a path that couldn't get its key updated,
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 052af85d..27df5fdd 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -31,7 +31,6 @@ int prin_do_scsi_ioctl(char * dev, int rq_servact, struct prin_resp * resp, int
int prout_do_scsi_ioctl( char * dev, int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy);
void * _mpath_pr_update (void *arg);
-int get_mpvec (vector curmp, vector pathvec, char * refwwid);
void * mpath_prout_pthread_fn(void *p);
void dumpHex(const char* , int len, int no_ascii);

View File

@ -0,0 +1,78 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 24 Jul 2025 14:05:38 -0400
Subject: [PATCH] libmpathpersist: retry on conflicts in mpath_prout_common
mpath_prout_common() just needs to execute a prout command down one
path. If it uses a path that was down when the key was changed and has
since been restored, but multipathd hasn't noticed yet, that path will
still be using the old key. This was causing mpath_prout_common() to
fail with MPATH_PR_RESERV_CONFLICT, even if there were other paths that
would work.
Now, if prout command fails with MPATH_PR_RESERV_CONFLICT,
mpath_prout_common() checks if pp->dmstate is PSTATE_FAILED. If it is,
mpath_prout_common() assumes that multipathd has not yet noticed that
the path is back online and it might still have and old key, so it
doesn't immediately return. If it can't successfully send the command
down another path, it will still return MPATH_PR_RESERV_CONFLICT.
Also, make sure prout_do_scsi_ioctl() always returns a MPATH_PR_*
type error.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 14 +++++++++++++-
libmpathpersist/mpath_pr_ioctl.c | 2 +-
2 files changed, 14 insertions(+), 2 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 3d4c0c2c..560c9ae7 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -771,6 +771,7 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
struct pathgroup *pgp = NULL;
struct path *pp = NULL;
bool found = false;
+ bool conflict = false;
vector_foreach_slot (mpp->pg, pgp, j){
vector_foreach_slot (pgp->paths, pp, i){
@@ -786,12 +787,23 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
rq_type, paramp, noisy);
if (ret == MPATH_PR_SUCCESS && pptr)
*pptr = pp;
+ /*
+ * If this path is considered down by the kernel,
+ * it may have just come back up, and multipathd
+ * may not have had time to update the key. Allow
+ * reservation conflicts.
+ */
+ if (ret == MPATH_PR_RESERV_CONFLICT &&
+ pp->dmstate == PSTATE_FAILED) {
+ conflict = true;
+ continue;
+ }
if (ret != MPATH_PR_RETRYABLE_ERROR)
return ret;
}
}
if (found)
- return MPATH_PR_OTHER;
+ return conflict ? MPATH_PR_RESERV_CONFLICT : MPATH_PR_OTHER;
condlog (0, "%s: no path available", mpp->wwid);
return MPATH_PR_DMMP_ERROR;
}
diff --git a/libmpathpersist/mpath_pr_ioctl.c b/libmpathpersist/mpath_pr_ioctl.c
index e5b25692..689578ac 100644
--- a/libmpathpersist/mpath_pr_ioctl.c
+++ b/libmpathpersist/mpath_pr_ioctl.c
@@ -105,7 +105,7 @@ retry :
{
condlog(0, "%s: ioctl failed %d", dev, ret);
close(fd);
- return ret;
+ return MPATH_PR_OTHER;
}
condlog(4, "%s: Duration=%u (ms)", dev, io_hdr.duration);

View File

@ -0,0 +1,151 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 25 Jul 2025 15:53:16 -0400
Subject: [PATCH] libmpathpersist: Don't always fail registrations for
retryable errors
When libmpathpersist registers a key, it's possible that a path fails
between when it checks the path's status, and when it tries to do the
registrations on the path. In this case, the registration will fail with
a retryable error. If the registration was allowed to succeed,
multipathd would update the now failed path's key when it came back
online, and everything would work correctly. However it is possible for
a registration to fail with a retryable error on a path that is still
usable.
Libmpathpersist needs to avoid the case where it does not update the
key of a usable path. Otherwise the path might be able to write to
storage it shouldn't be allowed to. Or it could fail writing to storage
that it should be allowed to write to. So if a registration would
succeed except for retryable errors, libmpathpersist now rechecks all
those paths to see if they are still usable. If they are, then it fails
the registration as before. If they are not, then the registration
succeeds.
Also, rename can_retry to had_success, since it is used for checking
more than retries now.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 70 +++++++++++++++++++++++++++++----
1 file changed, 63 insertions(+), 7 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 560c9ae7..54408e91 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -588,6 +588,48 @@ void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
mpp->wwid);
}
+/*
+ * If libmpathpersist fails at updating the key on a path with a retryable
+ * error, it has probably failed. But there is a chance that the path is
+ * still usable. To make sure a path isn't active without a key, when it
+ * should have one, or with a key, when it shouldn't have one, check if
+ * the path is still usable. If it is, we must fail the registration.
+ */
+static int check_failed_paths(struct multipath *mpp,
+ struct threadinfo *thread, int count)
+{
+ int i, j, k;
+ int ret;
+ struct pathgroup *pgp;
+ struct path *pp;
+ struct config *conf;
+
+ for (i = 0; i < count; i++) {
+ if (thread[i].param.status != MPATH_PR_RETRYABLE_ERROR)
+ continue;
+ vector_foreach_slot (mpp->pg, pgp, j) {
+ vector_foreach_slot (pgp->paths, pp, k) {
+ if (strncmp(pp->dev, thread[i].param.dev,
+ FILE_NAME_SIZE) == 0)
+ goto match;
+ }
+ }
+ /* no match. This shouldn't ever happen. */
+ condlog(0, "%s: Error: can't find path %s", mpp->wwid,
+ thread[i].param.dev);
+ continue;
+match:
+ conf = get_multipath_config();
+ ret = pathinfo(pp, conf, DI_CHECKER);
+ put_multipath_config(conf);
+ /* If pathinfo fails, or if the path is active, return error */
+ if (ret != PATHINFO_OK || pp->state == PATH_UP ||
+ pp->state == PATH_GHOST)
+ return MPATH_PR_OTHER;
+ }
+ return MPATH_PR_SUCCESS;
+}
+
int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
{
@@ -595,8 +637,9 @@ 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;
- bool can_retry = false;
+ bool had_success = false;
bool need_retry = false;
+ bool retryable_error = false;
int active_pathcount=0;
int rc;
int count=0;
@@ -700,16 +743,20 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
*/
if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
need_retry = true;
+ else if (thread[i].param.status == MPATH_PR_RETRYABLE_ERROR)
+ retryable_error = true;
else if (thread[i].param.status == MPATH_PR_SUCCESS)
- can_retry = true;
+ had_success = true;
else if (status == MPATH_PR_SUCCESS)
status = thread[i].param.status;
}
- if (need_retry && can_retry && rq_servact == MPATH_PROUT_REG_SA &&
+ if (need_retry && had_success && rq_servact == MPATH_PROUT_REG_SA &&
status == MPATH_PR_SUCCESS) {
condlog (3, "%s: ERROR: initiating pr out retry", mpp->wwid);
+ retryable_error = false;
for (i = 0; i < count; i++) {
- if (thread[i].param.status != MPATH_PR_RESERV_CONFLICT) {
+ /* retry retryable errors and conflicts */
+ if (thread[i].param.status != MPATH_PR_RESERV_CONFLICT && thread[i].param.status != MPATH_PR_RETRYABLE_ERROR) {
thread[i].param.status = MPATH_PR_SKIP;
continue;
}
@@ -736,7 +783,9 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
condlog(3, "%s: failed to join thread while retrying %d",
mpp->wwid, i);
}
- if (status == MPATH_PR_SUCCESS)
+ if (thread[i].param.status == MPATH_PR_RETRYABLE_ERROR)
+ retryable_error = true;
+ else if (status == MPATH_PR_SUCCESS)
status = thread[i].param.status;
}
}
@@ -745,10 +794,17 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
pthread_attr_destroy(&attr);
if (need_retry)
- status = MPATH_PR_RESERV_CONFLICT;
+ return MPATH_PR_RESERV_CONFLICT;
+ if (status != MPATH_PR_SUCCESS)
+ return status;
+ /* If you had retryable errors on all paths, fail the registration */
+ if (!had_success)
+ return MPATH_PR_OTHER;
+ if (retryable_error)
+ status = check_failed_paths(mpp, thread, count);
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;
+ return status;
}
void * mpath_prout_pthread_fn(void *p)

View File

@ -0,0 +1,46 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 25 Aug 2025 14:09:29 -0400
Subject: [PATCH] libmpathpersist: Don't try release workaround for invalid
type
When trying to release a reservation, if the user specified the wrong
reservation type, libmpathpersist would try to preempt the reservation,
because the reservation key matched the device key, but it was not
removed. In this case, the preemption would also fail because it also
requires a matching type.
Check if the reservation type matches, to avoid attempting the
workaround in this case.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 54408e91..ad2e0e80 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -880,6 +880,7 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
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;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
@@ -975,6 +976,13 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
return MPATH_PR_SUCCESS;
}
+ scope_type = resp.prin_descriptor.prin_readresv.scope_type;
+ if ((scope_type & MPATH_PR_TYPE_MASK) != rq_type) {
+ condlog(2, "%s: --prout_type %u doesn't match reservation %u",
+ mpp->wwid, rq_type, scope_type & MPATH_PR_TYPE_MASK);
+ return MPATH_PR_RESERV_CONFLICT;
+ }
+
condlog (2, "%s: Path holding reservation is not available.", mpp->wwid);
/*
* Cannot free the reservation because the path that is holding it

View File

@ -0,0 +1,43 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 4 Sep 2025 16:33:27 -0400
Subject: [PATCH] libmpathpersist: Don't fail RESERVE commands unnecessarily
If you issue a RESERVE to a regular SCSI device that already holds the
reservation, it succeeds (and does nothing). If you issue a RESERVE to a
multipath device that already holds the reservation, it can fail with
a reservation conflict error if you issue the RESERVE to a path that isn't
holding the reservation. Instead, it should try all paths and
succeed if the reservation command succeeds on any of them.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index ad2e0e80..52fbc0aa 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -848,9 +848,20 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
* it may have just come back up, and multipathd
* may not have had time to update the key. Allow
* reservation conflicts.
+ *
+ * If you issue a RESERVE to a regular scsi device
+ * that already holds the reservation, it succeeds
+ * (and does nothing). A multipath device that
+ * holds the reservation should not return a
+ * reservation conflict on a RESERVE command, just
+ * because it issued the RESERVE to a path that
+ * isn't holding the reservation. It should instead
+ * keep trying to see if it succeeds on another
+ * path.
*/
if (ret == MPATH_PR_RESERV_CONFLICT &&
- pp->dmstate == PSTATE_FAILED) {
+ (pp->dmstate == PSTATE_FAILED ||
+ rq_servact == MPATH_PROUT_RES_SA)) {
conflict = true;
continue;
}

View File

@ -0,0 +1,203 @@
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.c | 136 +++++++++++++++++---------------
1 file changed, 71 insertions(+), 65 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 52fbc0aa..3d3b62dd 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -37,6 +37,9 @@
extern struct udev *udev;
+static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
+ unsigned int rq_type, int noisy, bool do_release);
+
static void adapt_config(struct config *conf)
{
conf->force_sync = 1;
@@ -419,6 +422,12 @@ static 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:
@@ -875,6 +884,65 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
return MPATH_PR_DMMP_ERROR;
}
+/*
+ * 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;
+}
+
int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
{
@@ -888,8 +956,6 @@ 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 = {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;
@@ -1004,70 +1070,10 @@ 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);
}
void * mpath_alloc_prin_response(int prin_sa)

View File

@ -0,0 +1,108 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 9 Sep 2025 18:55:49 -0400
Subject: [PATCH] libmpathpersist: handle updating key race condition
If a multipath device's registered key is changed, mpathpersist needs
to update the key in multipathd before it registers the new key on the
paths. This means that there is a time when multipathd thinks the paths
should be using the new key, but none of them are. If a path is
restored after mpathpersist checks which paths are usable to set the
key on, but before it sets the key on any path, multipathd will see
that the new key is not registered on any paths, and think that the
key has been preempted or cleared. This will leave the path without
a key, and possibly make multipathd think the device does not hold
the reservation, even if it does.
To avoid this, multipathd will now remember the old key when registering
a new one. Once the registration is finished, and (un)setprstatus is
called, multipathd will forget the old key. Until then, multipathd will
check for either key when it looks to see if there is an existing key.
If the registration fails and the key get reverted, multipathd will
also forget the old key.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmultipath/prkey.c | 16 ++++++++++++++--
libmultipath/structs.h | 1 +
multipathd/main.c | 12 +++++++++++-
3 files changed, 26 insertions(+), 3 deletions(-)
diff --git a/libmultipath/prkey.c b/libmultipath/prkey.c
index d645f813..ab45f176 100644
--- a/libmultipath/prkey.c
+++ b/libmultipath/prkey.c
@@ -176,10 +176,22 @@ int set_prkey(struct config *conf, struct multipath *mpp, uint64_t prkey,
}
else
ret = do_prkey(fd, mpp->wwid, NULL, PRKEY_WRITE);
- if (ret == 0)
+ if (ret == 0) {
+ /*
+ * If you are reverting back to the old key, because you
+ * did not successfully set a new key, don't remember the
+ * key you never successfully set.
+ */
+ if (get_be64(mpp->old_pr_key) == prkey)
+ memset(&mpp->old_pr_key, 0, 8);
+ else
+ memcpy(&mpp->old_pr_key, &mpp->reservation_key, 8);
select_reservation_key(conf, mpp);
- if (get_be64(mpp->reservation_key) != prkey)
+ }
+ if (get_be64(mpp->reservation_key) != prkey) {
+ memset(&mpp->old_pr_key, 0, 8);
ret = 1;
+ }
out_file:
close(fd);
out:
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
index a67e767d..2f69e831 100644
--- a/libmultipath/structs.h
+++ b/libmultipath/structs.h
@@ -468,6 +468,7 @@ struct multipath {
bool fpin_must_reload;
int prhold;
bool ever_registered_pr;
+ struct be64 old_pr_key;
};
static inline int marginal_path_check_enabled(const struct multipath *mpp)
diff --git a/multipathd/main.c b/multipathd/main.c
index 56d51c48..8a0d9edc 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3751,6 +3751,7 @@ void set_pr(struct multipath *mpp)
{
mpp->ever_registered_pr = true;
mpp->prflag = PR_SET;
+ memset(&mpp->old_pr_key, 0, 8);
}
void unset_pr(struct multipath *mpp)
@@ -3758,6 +3759,7 @@ void unset_pr(struct multipath *mpp)
mpp->prflag = PR_UNSET;
mpp->prhold = PR_UNSET;
mpp->sa_flags = 0;
+ memset(&mpp->old_pr_key, 0, 8);
}
/*
@@ -3806,7 +3808,15 @@ static int update_map_pr(struct multipath *mpp, struct path *pp)
dumpHex((char *)keyp, 8, 1);
}
- if (!memcmp(&mpp->reservation_key, keyp, 8))
+ /*
+ * If you are in the middle of updating a key (old_pr_key
+ * is set) check for either the new key or the old key,
+ * since you might be checking before any paths have
+ * updated their keys.
+ */
+ if (!memcmp(&mpp->reservation_key, keyp, 8) ||
+ (get_be64(mpp->old_pr_key) &&
+ !memcmp(&mpp->old_pr_key, keyp, 8)))
isFound = 1;
}

View File

@ -0,0 +1,94 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Wed, 10 Sep 2025 17:52:12 -0400
Subject: [PATCH] libmpathpersist: handle preempting all registrants
reservations
All Registrants reservations (types 7 and 8) are held by key 0x0. When
preempting one, all registrations are cleared, except for the one on the
path that issued the PREEMPT. libmpathpersist needs to handle this just
like the other cases of self-preemption, so that all the paths of the
preempting multipath device have their registrations restored while the
device is suspended.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 36 +++++++++++++++++++++++++++------
1 file changed, 30 insertions(+), 6 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 3d3b62dd..92a0021d 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -37,8 +37,9 @@
extern struct udev *udev;
-static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
- unsigned int rq_type, int noisy, bool do_release);
+static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
+ int rq_servact, int rq_scope, unsigned int rq_type,
+ int noisy, bool do_release);
static void adapt_config(struct config *conf)
{
@@ -321,6 +322,21 @@ static void set_ignored_key(struct multipath *mpp, uint8_t *key)
memcpy(key, &mpp->reservation_key, 8);
}
+static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
+ unsigned int rq_type, int noisy, bool do_release)
+{
+ return do_preempt_self(mpp, mpp->reservation_key, rq_servact, rq_scope,
+ rq_type, noisy, do_release);
+}
+
+static int preempt_all(struct multipath *mpp, int rq_servact, int rq_scope,
+ unsigned int rq_type, int noisy)
+{
+ struct be64 zerokey = {0};
+ return do_preempt_self(mpp, zerokey, rq_servact, rq_scope, rq_type,
+ noisy, false);
+}
+
static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
int rq_servact, int rq_scope, unsigned int rq_type,
struct prout_param_descriptor *paramp, int noisy)
@@ -420,8 +436,15 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
break;
case MPATH_PROUT_PREE_SA :
case MPATH_PROUT_PREE_AB_SA :
- if (reservation_key_matches(mpp, paramp->sa_key, noisy) == YNU_YES)
+ if (reservation_key_matches(mpp, paramp->sa_key, noisy) == YNU_YES) {
preempting_reservation = true;
+ if (memcmp(paramp->sa_key, &zerokey, 8) == 0) {
+ /* all registrants case */
+ ret = preempt_all(mpp, rq_servact, rq_scope,
+ rq_type, noisy);
+ break;
+ }
+ }
/* if we are preempting ourself */
if (memcmp(paramp->sa_key, paramp->key, 8) == 0) {
ret = preempt_self(mpp, rq_servact, rq_scope, rq_type,
@@ -893,8 +916,9 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
* 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)
+static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
+ 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;
@@ -907,7 +931,7 @@ static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
}
memcpy(paramp.key, &mpp->reservation_key, 8);
- memcpy(paramp.sa_key, &mpp->reservation_key, 8);
+ memcpy(paramp.sa_key, &sa_key, 8);
status = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
&paramp, noisy, &pp);
if (status != MPATH_PR_SUCCESS) {

View File

@ -0,0 +1,74 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 18 Sep 2025 18:12:07 -0400
Subject: [PATCH] libmpathpersist: Fix REGISTER AND IGNORE while holding a
reservation
If a device that is holding a reservation changes its registered key,
but the path holding the reservation is unavailable, libmpathpersist
must preempt the old key to update the reservation. If the key is
changed using REGISTER AND IGNORE, set_ignored_key() determines the old
key to preempt. Unfortunately, commit 165427dda broke it, by comparing
the wrong key against the actual reservation key. Then commit cf0eea85
broke it more, by using mpp->reservation_key after it had been updated,
so it was no longer the old key. Fix this by correctly comparing the old
key against the actual reservation key.
Fixes: 165427dda ("libmpathpersist: Add safety check for preempting on key change")
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 92a0021d..6a254d2e 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -309,17 +309,17 @@ static int reservation_key_matches(struct multipath *mpp, uint8_t *key,
* currently registered key for use in preempt_missing_path(), but only if
* the key is holding the reservation.
*/
-static void set_ignored_key(struct multipath *mpp, uint8_t *key)
+static void set_ignored_key(struct multipath *mpp, uint8_t *curr_key,
+ uint8_t *key)
{
memset(key, 0, 8);
- if (!get_be64(mpp->reservation_key))
+ if (memcmp(curr_key, key, 8) == 0)
return;
if (get_prhold(mpp->alias) == PR_UNSET)
return;
- if (reservation_key_matches(mpp, (uint8_t *)&mpp->reservation_key,
- 0) == YNU_NO)
+ if (reservation_key_matches(mpp, curr_key, 0) == YNU_NO)
return;
- memcpy(key, &mpp->reservation_key, 8);
+ memcpy(key, curr_key, 8);
}
static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
@@ -359,6 +359,7 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
select_reservation_key(conf, mpp);
put_multipath_config(conf);
+ memcpy(&oldkey, &mpp->reservation_key, 8);
unregistering = (memcmp(&zerokey, paramp->sa_key, 8) == 0);
if (mpp->prkey_source == PRKEY_SOURCE_FILE &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
@@ -367,7 +368,6 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
memcmp(paramp->key, &zerokey, 8) == 0 ||
memcmp(paramp->key, &mpp->reservation_key, 8) == 0)))) {
updated_prkey = true;
- memcpy(&oldkey, &mpp->reservation_key, 8);
memcpy(&mpp->reservation_key, paramp->sa_key, 8);
if (update_prkey_flags(alias, get_be64(mpp->reservation_key),
paramp->sa_flags)) {
@@ -426,7 +426,7 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
put_multipath_config(conf);
if (rq_servact == MPATH_PROUT_REG_IGN_SA)
- set_ignored_key(mpp, paramp->key);
+ set_ignored_key(mpp, (uint8_t *)&oldkey, paramp->key);
switch(rq_servact)
{

View File

@ -0,0 +1,168 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 18 Sep 2025 19:29:08 -0400
Subject: [PATCH] libmpathpersist: Handle RESERVE with reservation held by
failed path
Issuing a RESERVE on a device that already holds the reservation should
succeed, as long as the type is the same. But if the path that holds the
reservation is unavailable, mpathpersist fails, since it gets a
reservation conflict on all available paths. To deal with this, if the
multipath device has failed paths, and the key holding the reservation
matches the multipath device's key, and multipathd says that it is
holding the reservation, assume the reservation is held by a failed path
and claim the RESERVE succeeded, even though none of the actual scsi
commands did
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 48 +++++++++++++++++++++++++--------
1 file changed, 37 insertions(+), 11 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 6a254d2e..fce28723 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -104,7 +104,7 @@ int libmpathpersist_exit(void)
static int mpath_prout_common(struct multipath *mpp,int rq_servact,
int rq_scope, unsigned int rq_type,
struct prout_param_descriptor* paramp, int noisy,
- struct path **pptr);
+ struct path **pptr, bool *failed_paths);
int
mpath_prin_activepath (struct multipath *mpp, int rq_servact,
@@ -286,12 +286,12 @@ int __mpath_persistent_reserve_in (int fd, int rq_servact,
}
static int reservation_key_matches(struct multipath *mpp, uint8_t *key,
- int noisy)
+ unsigned int *type)
{
struct prin_resp resp = {0};
int status;
- status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
+ status = mpath_prin_activepath(mpp, MPATH_PRIN_RRES_SA, &resp, 0);
if (status != MPATH_PR_SUCCESS) {
condlog(0, "%s: pr in read reservation command failed.",
mpp->wwid);
@@ -299,8 +299,12 @@ static int reservation_key_matches(struct multipath *mpp, uint8_t *key,
}
if (!resp.prin_descriptor.prin_readresv.additional_length)
return YNU_NO;
- if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) == 0)
+ if (memcmp(key, resp.prin_descriptor.prin_readresv.key, 8) == 0) {
+ if (type)
+ *type = resp.prin_descriptor.prin_readresv.scope_type &
+ MPATH_PR_TYPE_MASK;
return YNU_YES;
+ }
return YNU_NO;
}
@@ -317,11 +321,20 @@ static void set_ignored_key(struct multipath *mpp, uint8_t *curr_key,
return;
if (get_prhold(mpp->alias) == PR_UNSET)
return;
- if (reservation_key_matches(mpp, curr_key, 0) == YNU_NO)
+ if (reservation_key_matches(mpp, curr_key, NULL) == YNU_NO)
return;
memcpy(key, curr_key, 8);
}
+static bool check_holding_reservation(struct multipath *mpp, unsigned int *type)
+{
+ if (get_be64(mpp->reservation_key) &&
+ get_prhold(mpp->alias) == PR_SET &&
+ reservation_key_matches(mpp, (uint8_t *)&mpp->reservation_key, type) == YNU_YES)
+ return true;
+ return false;
+}
+
static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
unsigned int rq_type, int noisy, bool do_release)
{
@@ -349,6 +362,7 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
struct config *conf;
bool unregistering, preempting_reservation = false;
bool updated_prkey = false;
+ bool failed_paths = false;
ret = mpath_get_map(curmp, fd, &alias, &mpp);
if (ret != MPATH_PR_SUCCESS)
@@ -436,7 +450,7 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
break;
case MPATH_PROUT_PREE_SA :
case MPATH_PROUT_PREE_AB_SA :
- if (reservation_key_matches(mpp, paramp->sa_key, noisy) == YNU_YES) {
+ if (reservation_key_matches(mpp, paramp->sa_key, NULL) == YNU_YES) {
preempting_reservation = true;
if (memcmp(paramp->sa_key, &zerokey, 8) == 0) {
/* all registrants case */
@@ -453,10 +467,18 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
}
/* fallthrough */
case MPATH_PROUT_RES_SA :
- case MPATH_PROUT_CLEAR_SA:
+ case MPATH_PROUT_CLEAR_SA: {
+ unsigned int res_type;
ret = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
- paramp, noisy, NULL);
+ paramp, noisy, NULL, &failed_paths);
+ if (rq_servact == MPATH_PROUT_RES_SA &&
+ ret != MPATH_PR_SUCCESS && failed_paths &&
+ check_holding_reservation(mpp, &res_type) &&
+ res_type == rq_type)
+ /* The reserve failed, but multipathd says we hold it */
+ ret = MPATH_PR_SUCCESS;
break;
+ }
case MPATH_PROUT_REL_SA:
ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
break;
@@ -614,7 +636,7 @@ void preempt_missing_path(struct multipath *mpp, uint8_t *key, uint8_t *sa_key,
memcpy(paramp.key, sa_key, 8);
memcpy(paramp.sa_key, key, 8);
status = mpath_prout_common(mpp, MPATH_PROUT_PREE_SA, rq_scope, rq_type,
- &paramp, noisy, NULL);
+ &paramp, noisy, NULL, NULL);
if (status != MPATH_PR_SUCCESS)
condlog(0, "%s: register: pr preempt command failed.",
mpp->wwid);
@@ -853,7 +875,7 @@ void * mpath_prout_pthread_fn(void *p)
static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type,
struct prout_param_descriptor* paramp, int noisy,
- struct path **pptr)
+ struct path **pptr, bool *failed_paths)
{
int i,j, ret;
struct pathgroup *pgp = NULL;
@@ -866,6 +888,8 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
if (!((pp->state == PATH_UP) || (pp->state == PATH_GHOST))){
condlog (1, "%s: %s path not up. Skip",
mpp->wwid, pp->dev);
+ if (failed_paths)
+ *failed_paths = true;
continue;
}
@@ -899,6 +923,8 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
}
if (ret != MPATH_PR_RETRYABLE_ERROR)
return ret;
+ if (failed_paths)
+ *failed_paths = true;
}
}
if (found)
@@ -933,7 +959,7 @@ static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
memcpy(paramp.key, &mpp->reservation_key, 8);
memcpy(paramp.sa_key, &sa_key, 8);
status = mpath_prout_common(mpp, rq_servact, rq_scope, rq_type,
- &paramp, noisy, &pp);
+ &paramp, noisy, &pp, NULL);
if (status != MPATH_PR_SUCCESS) {
condlog(0, "%s: self preempt command failed.", mpp->wwid);
goto fail_resume;

View File

@ -0,0 +1,68 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 22 Sep 2025 17:40:00 -0400
Subject: [PATCH] libmpathpersist: use check_holding_reservation in
mpath_prout_rel
Instead of open-coding mostly the same work, just make mpath_prout_rel()
call check_holding_reservation().
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 24 ++++--------------------
1 file changed, 4 insertions(+), 20 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index fce28723..15141be4 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -997,7 +997,6 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
{
int i, j;
- int num = 0;
struct pathgroup *pgp = NULL;
struct path *pp = NULL;
int active_pathcount = 0;
@@ -1005,9 +1004,8 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
int rc;
int count = 0;
int status = MPATH_PR_SUCCESS;
- struct prin_resp resp = {0};
bool all_threads_failed;
- unsigned int scope_type;
+ unsigned int res_type;
if (!mpp)
return MPATH_PR_DMMP_ERROR;
@@ -1086,27 +1084,13 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
return status;
}
- status = mpath_prin_activepath (mpp, MPATH_PRIN_RRES_SA, &resp, noisy);
- if (status != MPATH_PR_SUCCESS){
- condlog (0, "%s: pr in read reservation command failed.", mpp->wwid);
- return MPATH_PR_OTHER;
- }
-
- num = resp.prin_descriptor.prin_readresv.additional_length / 8;
- if (num == 0){
- condlog (2, "%s: Path holding reservation is released.", mpp->wwid);
- return MPATH_PR_SUCCESS;
- }
- if (!get_be64(mpp->reservation_key) ||
- memcmp(&mpp->reservation_key, resp.prin_descriptor.prin_readresv.key, 8)) {
+ if (!check_holding_reservation(mpp, &res_type)) {
condlog(2, "%s: Releasing key not holding reservation.", mpp->wwid);
return MPATH_PR_SUCCESS;
}
-
- scope_type = resp.prin_descriptor.prin_readresv.scope_type;
- if ((scope_type & MPATH_PR_TYPE_MASK) != rq_type) {
+ if (res_type != rq_type) {
condlog(2, "%s: --prout_type %u doesn't match reservation %u",
- mpp->wwid, rq_type, scope_type & MPATH_PR_TYPE_MASK);
+ mpp->wwid, rq_type, res_type);
return MPATH_PR_RESERV_CONFLICT;
}

View File

@ -0,0 +1,287 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Fri, 19 Sep 2025 18:17:20 -0400
Subject: [PATCH] libmpathpersist: Fix unregistering while holding the
reservation
There were two problems with how libmpathpersist handled unregistering
a key while holding the reseravation (which should also release the
reservation).
1. If the path holding the reservation is not unregistered first, there
will be unregistered paths, while a reservation is still held, which
would cause IO to those paths to fail, when it shouldn't.
2. If the path that holds the reservation is down, libmpathpersist was
not clearing the reservation, since the there were no registered keys
it could use for the PREEMPT command workaround
To fix these, libmpathpersist now releases the reservation first when
trying to unregister a key that is holding the reservation.
mpath_prout_rel() has a new option so that if it needs to self preempt
to clear the reservation, it won't re-register the paths when called
as part of unregistering a key. Also, instead of checking if the device
is currently holding a reservation using mpp->reservation_key in
check_holding_reservation() (which will already be set to 0 when called
as part of unregistering a key), pass in the key to check.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.c | 111 +++++++++++++++++++++++---------
libmpathpersist/mpath_persist.h | 3 +
libmpathpersist/mpathpr.h | 3 +-
3 files changed, 84 insertions(+), 33 deletions(-)
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
index 15141be4..72be48c1 100644
--- a/libmpathpersist/mpath_persist.c
+++ b/libmpathpersist/mpath_persist.c
@@ -37,9 +37,15 @@
extern struct udev *udev;
+enum preempt_work {
+ PREE_WORK_NONE,
+ PREE_WORK_REL,
+ PREE_WORK_REL_UNREG,
+};
+
static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
int rq_servact, int rq_scope, unsigned int rq_type,
- int noisy, bool do_release);
+ int noisy, enum preempt_work work);
static void adapt_config(struct config *conf)
{
@@ -326,20 +332,22 @@ static void set_ignored_key(struct multipath *mpp, uint8_t *curr_key,
memcpy(key, curr_key, 8);
}
-static bool check_holding_reservation(struct multipath *mpp, unsigned int *type)
+static bool check_holding_reservation(struct multipath *mpp, uint8_t *curr_key,
+ unsigned int *type)
{
- if (get_be64(mpp->reservation_key) &&
+ uint64_t zerokey = 0;
+ if (memcmp(curr_key, &zerokey, 8) != 0 &&
get_prhold(mpp->alias) == PR_SET &&
- reservation_key_matches(mpp, (uint8_t *)&mpp->reservation_key, type) == YNU_YES)
+ reservation_key_matches(mpp, curr_key, type) == YNU_YES)
return true;
return false;
}
static int preempt_self(struct multipath *mpp, int rq_servact, int rq_scope,
- unsigned int rq_type, int noisy, bool do_release)
+ unsigned int rq_type, int noisy, enum preempt_work work)
{
return do_preempt_self(mpp, mpp->reservation_key, rq_servact, rq_scope,
- rq_type, noisy, do_release);
+ rq_type, noisy, work);
}
static int preempt_all(struct multipath *mpp, int rq_servact, int rq_scope,
@@ -347,7 +355,7 @@ static int preempt_all(struct multipath *mpp, int rq_servact, int rq_scope,
{
struct be64 zerokey = {0};
return do_preempt_self(mpp, zerokey, rq_servact, rq_scope, rq_type,
- noisy, false);
+ noisy, PREE_WORK_NONE);
}
static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
@@ -373,7 +381,7 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
select_reservation_key(conf, mpp);
put_multipath_config(conf);
- memcpy(&oldkey, &mpp->reservation_key, 8);
+ oldkey = mpp->reservation_key;
unregistering = (memcmp(&zerokey, paramp->sa_key, 8) == 0);
if (mpp->prkey_source == PRKEY_SOURCE_FILE &&
(rq_servact == MPATH_PROUT_REG_IGN_SA ||
@@ -446,7 +454,27 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
{
case MPATH_PROUT_REG_SA:
case MPATH_PROUT_REG_IGN_SA:
- ret= mpath_prout_reg(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ if (unregistering && check_holding_reservation(mpp, (uint8_t *)&oldkey, &rq_type)) {
+ struct be64 newkey = mpp->reservation_key;
+ /* temporarily restore reservation key */
+ mpp->reservation_key = oldkey;
+ ret = mpath_prout_rel(mpp, MPATH_PROUT_REL_SA, rq_scope,
+ rq_type, paramp, noisy, true);
+ mpp->reservation_key = newkey;
+ if (ret == MPATH_PR_SUCCESS)
+ /*
+ * Since unregistering it true, paramp->sa_key
+ * must be zero here. So this command will
+ * unregister the key.
+ */
+ ret = mpath_prout_reg(mpp, rq_servact, rq_scope,
+ rq_type, paramp, noisy);
+ else if (ret == MPATH_PR_SUCCESS_UNREGISTER)
+ /* We already unregistered the key */
+ ret = MPATH_PR_SUCCESS;
+ } else
+ ret = mpath_prout_reg(mpp, rq_servact, rq_scope,
+ rq_type, paramp, noisy);
break;
case MPATH_PROUT_PREE_SA :
case MPATH_PROUT_PREE_AB_SA :
@@ -462,7 +490,7 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
/* 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);
+ noisy, PREE_WORK_NONE);
break;
}
/* fallthrough */
@@ -473,14 +501,14 @@ static int do_mpath_persistent_reserve_out(vector curmp, vector pathvec, int fd,
paramp, noisy, NULL, &failed_paths);
if (rq_servact == MPATH_PROUT_RES_SA &&
ret != MPATH_PR_SUCCESS && failed_paths &&
- check_holding_reservation(mpp, &res_type) &&
+ check_holding_reservation(mpp, paramp->key, &res_type) &&
res_type == rq_type)
/* The reserve failed, but multipathd says we hold it */
ret = MPATH_PR_SUCCESS;
break;
}
case MPATH_PROUT_REL_SA:
- ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy);
+ ret = mpath_prout_rel(mpp, rq_servact, rq_scope, rq_type, paramp, noisy, false);
break;
default:
ret = MPATH_PR_OTHER;
@@ -935,16 +963,16 @@ static int mpath_prout_common(struct multipath *mpp,int rq_servact, int rq_scope
/*
* 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.
+ * optional extra work). Doing this causes the reservation keys to be removed
+ * from all the device paths except that path used to issue the preempt, so
+ * they may need to be restored. To avoid the chance that IO goes to these
+ * paths when they don't have a registered key and a reservation exists, the
+ * device is suspended before issuing the preemption, and the keys are
+ * reregistered (or the reservation is released) before resuming it.
*/
static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
int rq_servact, int rq_scope, unsigned int rq_type,
- int noisy, bool do_release)
+ int noisy, enum preempt_work work)
{
int status, rel_status = MPATH_PR_SUCCESS;
struct path *pp = NULL;
@@ -965,7 +993,7 @@ static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
goto fail_resume;
}
- if (do_release) {
+ if (work != PREE_WORK_NONE) {
memset(&paramp, 0, sizeof(paramp));
memcpy(paramp.key, &mpp->reservation_key, 8);
rel_status = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REL_SA,
@@ -973,15 +1001,26 @@ static int do_preempt_self(struct multipath *mpp, struct be64 sa_key,
if (rel_status != MPATH_PR_SUCCESS)
condlog(0, "%s: release on alternate path failed.",
mpp->wwid);
+ else if (work == PREE_WORK_REL_UNREG) {
+ /* unregister the last path */
+ rel_status = prout_do_scsi_ioctl(pp->dev,
+ MPATH_PROUT_REG_IGN_SA,
+ rq_scope, rq_type,
+ &paramp, noisy);
+ if (rel_status != MPATH_PR_SUCCESS)
+ condlog(0, "%s: final self preempt unregister failed,",
+ mpp->wwid);
+ }
+ }
+ if (work != PREE_WORK_REL_UNREG) {
+ 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);
}
-
- 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)) {
@@ -993,8 +1032,9 @@ fail_resume:
return (rel_status != MPATH_PR_SUCCESS) ? rel_status : status;
}
-int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy)
+int mpath_prout_rel(struct multipath *mpp, int rq_servact, int rq_scope,
+ unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy,
+ bool unregister)
{
int i, j;
struct pathgroup *pgp = NULL;
@@ -1084,7 +1124,8 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
return status;
}
- if (!check_holding_reservation(mpp, &res_type)) {
+ if (!check_holding_reservation(mpp, (uint8_t *)&mpp->reservation_key,
+ &res_type)) {
condlog(2, "%s: Releasing key not holding reservation.", mpp->wwid);
return MPATH_PR_SUCCESS;
}
@@ -1107,7 +1148,13 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
* 4. Reregistering keys on all the paths
* 5. Resuming the device
*/
- return preempt_self(mpp, MPATH_PROUT_PREE_SA, rq_scope, rq_type, noisy, true);
+ status = preempt_self(mpp, MPATH_PROUT_PREE_SA, rq_scope, rq_type,
+ noisy,
+ unregister ? PREE_WORK_REL_UNREG : PREE_WORK_REL);
+ if (status == MPATH_PR_SUCCESS && unregister)
+ return MPATH_PR_SUCCESS_UNREGISTER;
+ return status;
+
}
void * mpath_alloc_prin_response(int prin_sa)
diff --git a/libmpathpersist/mpath_persist.h b/libmpathpersist/mpath_persist.h
index 8d441d04..807787b5 100644
--- a/libmpathpersist/mpath_persist.h
+++ b/libmpathpersist/mpath_persist.h
@@ -66,6 +66,9 @@ extern "C" {
#define MPATH_PR_RETRYABLE_ERROR 16 /* error that might be succeed
down another path. Internal
only. */
+#define MPATH_PR_SUCCESS_UNREGISTER 17 /* Success, and additionally, all
+ paths were unregistered.
+ Internal only. */
/* PR MASK */
#define MPATH_F_APTPL_MASK 0x01 /* APTPL MASK*/
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
index 27df5fdd..69f402f2 100644
--- a/libmpathpersist/mpathpr.h
+++ b/libmpathpersist/mpathpr.h
@@ -37,7 +37,8 @@ void dumpHex(const char* , int len, int no_ascii);
int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope,
- unsigned int rq_type, struct prout_param_descriptor * paramp, int noisy);
+ unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy,
+ bool unregister);
int update_prflag(char *mapname, int set);
int update_prkey_flags(char *mapname, uint64_t prkey, uint8_t sa_flags);

View File

@ -0,0 +1,215 @@
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 | 78 +++++++++++++++++++++++++++++++----------------
1 file changed, 52 insertions(+), 26 deletions(-)
diff --git a/multipathd/main.c b/multipathd/main.c
index 8a0d9edc..4b0935b5 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -91,7 +91,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 { \
@@ -549,7 +550,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;
@@ -558,7 +559,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);
}
}
}
@@ -1184,7 +1185,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");
@@ -2546,7 +2547,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);
@@ -3766,12 +3767,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 */
@@ -3798,7 +3803,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];
@@ -3817,42 +3821,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
@@ -3865,21 +3884,28 @@ static void mpath_pr_event_handle(struct path *pp)
clear_reg = true;
}
- memset(&param, 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, &param, 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(&param, 0, sizeof(param));
+ clear_reg = true;
+ goto retry;
+ }
+ return nr_keys_needed;
}
+ return 0;
}

View File

@ -0,0 +1,49 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Tue, 30 Sep 2025 23:32:11 -0400
Subject: [PATCH] multipathd: Fix tracking of old PR key
When libmpathpersist is in the process of changing a registered key,
multipathd needs to remember both the old and new values of the key to
deal with a race between limpathpersist updating the key and multipathd
restoring a failed path. It was supposed to stop remembering the old
value when libmpathpersist was done changing the key and issued a
"setprstatus" command. However, clearing the old key was done in
set_pr(), which only gets called by cli_setprstatus() when registering a
new device, not when changing the key on a registered device. Also,
set_pr() is called by update_map_pr(). This means that multipathd was
forgetting the key when it shouldn't and not when it should. Fix this
to only forget the key when cli_setprstatus() is called, and always
then.
Fixes: 1aeffa9e ("libmpathpersist: handle updating key race condition")
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
multipathd/cli_handlers.c | 1 +
multipathd/main.c | 1 -
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 93768ef3..541dd700 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -1326,6 +1326,7 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data)
set_pr(mpp);
condlog(2, "%s: prflag set", param);
}
+ memset(&mpp->old_pr_key, 0, 8);
return 0;
diff --git a/multipathd/main.c b/multipathd/main.c
index 4b0935b5..1811747e 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -3752,7 +3752,6 @@ void set_pr(struct multipath *mpp)
{
mpp->ever_registered_pr = true;
mpp->prflag = PR_SET;
- memset(&mpp->old_pr_key, 0, 8);
}
void unset_pr(struct multipath *mpp)

View File

@ -0,0 +1,246 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Thu, 6 Nov 2025 20:08:14 -0500
Subject: [PATCH] multipathd: Fix race while registering PR key
When libmpathpersist registers a key, It first checks which paths are
active, then registers the key on those paths, and then tells multipathd
that the key has been registered, and it should start tracking it. If
a path comes up after libmpathpersist checks for active paths, but before
it tells multipathd that the key has been registered, multipathd will not
register a key no that path (since it hasn't been told to that the device
has a registered key yet). This can leave the device with a path that is
missing a key.
To solve this, when multipathd is told that a key has been registered,
it checks if there are the same number of registered keys as active
paths. If there aren't, it registers keys on all the paths (at least
until the number of registered keys and active paths are equal). To
avoid doing a bunch of unnecessary PR work, pr_register_active_paths()
has a new option to track the number of active paths, and
mpath_pr_event_handle() now takes the number of keys to expect. The
first time it's called, when there is an unknown number of keys, if the
number of keys it finds matches the number of active_paths, it and
pr_register_active_paths() exit early, since there is no registering
work to do.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Reviewed-by: Martin Wilck <mwilck@suse.com>
---
multipathd/cli_handlers.c | 6 +++-
multipathd/main.c | 67 ++++++++++++++++++++++++++++-----------
multipathd/main.h | 1 +
3 files changed, 54 insertions(+), 20 deletions(-)
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
index 541dd700..0c63ca9a 100644
--- a/multipathd/cli_handlers.c
+++ b/multipathd/cli_handlers.c
@@ -1324,7 +1324,11 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data)
if (mpp->prflag != PR_SET) {
set_pr(mpp);
- condlog(2, "%s: prflag set", param);
+ pr_register_active_paths(mpp, true);
+ if (mpp->prflag == PR_SET)
+ condlog(2, "%s: prflag set", param);
+ else
+ condlog(0, "%s: Failed to set prflag", param);
}
memset(&mpp->old_pr_key, 0, 8);
diff --git a/multipathd/main.c b/multipathd/main.c
index 1811747e..a85c0db4 100644
--- a/multipathd/main.c
+++ b/multipathd/main.c
@@ -92,7 +92,8 @@
#define MSG_SIZE 32
static unsigned int
-mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed);
+mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed,
+ unsigned int nr_keys_wanted);
#define LOG_MSG(lvl, pp) \
do { \
@@ -547,19 +548,28 @@ flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) {
return true;
}
-static void
-pr_register_active_paths(struct multipath *mpp)
+void pr_register_active_paths(struct multipath *mpp, bool check_nr_active)
{
unsigned int i, j, nr_keys = 0;
+ unsigned int nr_active = 0;
struct path *pp;
struct pathgroup *pgp;
+ if (check_nr_active) {
+ nr_active = count_active_paths(mpp);
+ if (!nr_active)
+ return;
+ }
+
vector_foreach_slot (mpp->pg, pgp, i) {
vector_foreach_slot (pgp->paths, pp, j) {
if (mpp->prflag == PR_UNSET)
return;
- if ((pp->state == PATH_UP) || (pp->state == PATH_GHOST))
- nr_keys = mpath_pr_event_handle(pp, nr_keys);
+ if (pp->state == PATH_UP || pp->state == PATH_GHOST) {
+ nr_keys = mpath_pr_event_handle(pp, nr_keys, nr_active);
+ if (check_nr_active && nr_keys == nr_active)
+ return;
+ }
}
}
}
@@ -646,7 +656,7 @@ fail:
sync_map_state(mpp);
- pr_register_active_paths(mpp);
+ pr_register_active_paths(mpp, false);
if (VECTOR_SIZE(offline_paths) != 0)
handle_orphaned_offline_paths(offline_paths);
@@ -1185,7 +1195,7 @@ rescan:
verify_paths(mpp);
mpp->action = ACT_RELOAD;
prflag = mpp->prflag;
- mpath_pr_event_handle(pp, 0);
+ mpath_pr_event_handle(pp, 0, 0);
} else {
if (!should_multipath(pp, vecs->pathvec, vecs->mpvec)) {
orphan_path(pp, "only one path");
@@ -1268,9 +1278,8 @@ rescan:
sync_map_state(mpp);
if (retries >= 0) {
- if ((mpp->prflag == PR_SET && prflag != PR_SET) ||
- start_waiter)
- pr_register_active_paths(mpp);
+ if ((mpp->prflag == PR_SET && prflag != PR_SET) || start_waiter)
+ pr_register_active_paths(mpp, false);
condlog(2, "%s [%s]: path added to devmap %s",
pp->dev, pp->dev_t, mpp->alias);
return 0;
@@ -2547,10 +2556,10 @@ 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, 0);
+ mpath_pr_event_handle(pp, 0, 0);
if (pp->mpp->prflag == PR_SET &&
prflag != PR_SET)
- pr_register_active_paths(pp->mpp);
+ pr_register_active_paths(pp->mpp, false);
}
}
@@ -2887,7 +2896,7 @@ configure (struct vectors * vecs)
vector_foreach_slot(mpvec, mpp, i){
if (remember_wwid(mpp->wwid) == 1)
trigger_paths_udev_change(mpp, true);
- pr_register_active_paths(mpp);
+ pr_register_active_paths(mpp, false);
}
/*
@@ -3769,7 +3778,8 @@ void unset_pr(struct multipath *mpp)
*
* 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.
+ * the call, *nr_keys will be set to the number of found keys. Otherwise
+ * it will be set to 0.
*/
static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *nr_keys)
{
@@ -3779,13 +3789,18 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n
bool was_set = (mpp->prflag == PR_SET);
/* If pr is explicitly unset, it must be manually set */
- if (mpp->prflag == PR_UNSET)
+ if (mpp->prflag == PR_UNSET) {
+ *nr_keys = 0;
return MPATH_PR_SUCCESS;
+ }
if (!get_be64(mpp->reservation_key)) {
/* Nothing to do. Assuming pr mgmt feature is disabled*/
unset_pr(mpp);
- condlog(was_set ? 2 : 4, "%s: reservation_key not set in multipath.conf", mpp->alias);
+ condlog(was_set ? 2 : 4,
+ "%s: reservation_key not set in multipath.conf",
+ mpp->alias);
+ *nr_keys = 0;
return MPATH_PR_SUCCESS;
}
@@ -3795,7 +3810,9 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n
if (ret != MPATH_PR_SUCCESS) {
if (ret == MPATH_PR_ILLEGAL_REQ)
unset_pr(mpp);
- condlog(0,"%s : pr in read keys service action failed Error=%d", mpp->alias, ret);
+ condlog(0, "%s : pr in read keys service action failed Error=%d",
+ mpp->alias, ret);
+ *nr_keys = 0;
return ret;
}
@@ -3833,22 +3850,32 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n
condlog(was_set ? 1 : 3,
"%s: %u keys found. needed %u. prflag unset.",
mpp->alias, nr_found, *nr_keys);
+ *nr_keys = 0;
}
return MPATH_PR_SUCCESS;
}
/*
- * This function is called with the number of registered keys that should be
+ * This function is called with two numbers
+ *
+ * nr_keys_needed: 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.
*
+ * nr_keys_wanted: Only used if nr_keys_needed is 0, so we don't know how
+ * many keys we currently have. If nr_keys_wanted in non-zero and the
+ * number of keys found by the initial call to update_map_pr() matches it,
+ * exit early, since we have all the keys we are expecting.
+ *
* 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)
+static unsigned int
+mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed,
+ unsigned int nr_keys_wanted)
{
struct multipath *mpp = pp->mpp;
int ret;
@@ -3864,6 +3891,8 @@ static unsigned int mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_
nr_keys_needed = 1;
if (update_map_pr(mpp, pp, &nr_keys_needed) != MPATH_PR_SUCCESS)
return 0;
+ if (nr_keys_wanted && nr_keys_wanted == nr_keys_needed)
+ return nr_keys_needed;
}
check_prhold(mpp, pp);
diff --git a/multipathd/main.h b/multipathd/main.h
index 77603751..8a4c5f88 100644
--- a/multipathd/main.h
+++ b/multipathd/main.h
@@ -60,4 +60,5 @@ int resize_map(struct multipath *mpp, unsigned long long size,
struct vectors *vecs);
void set_pr(struct multipath *mpp);
void unset_pr(struct multipath *mpp);
+void pr_register_active_paths(struct multipath *mpp, bool check_active_nr);
#endif /* MAIN_H */

View File

@ -0,0 +1,108 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Benjamin Marzinski <bmarzins@redhat.com>
Date: Mon, 10 Nov 2025 15:25:51 -0500
Subject: [PATCH] mpathpersist: Fix REPORT CAPABILITIES output
mpathpersist was incorrectly parsing the REPORT CAPABILITES service
action output. In reality, the type mask is two bytes where the type
information is stored in bits 7, 6, 5, 3, & 1 (0xea) of the first byte
and bit 0 (0x01) of the second byte. libmpathpersist was treating these
two bytes as a big endian 16 bit number, but mpathpersist was looking
for bits in that number as if it was little endian number.
Ideally, libmpathpersist would treat prin_capdescr.pr_type_mask as
two bytes, like it does for the flags. But we already expose this
as a 16 bit number, where we treated the input bytes as a big endian
number. There's no great reason to mess with the libmpathpersist API,
when we can just make mpathpersist treat this data like libmpathpersist
provides it. So, fix mpathpersist to print the data out correctly.
Additionally, instead of printing a 1 or a 0 to indicate if a type was
supported or not, it was printing the value of the type flag. Also,
Persist Through Power Loss Capable (PTPL_C) was being reported if any
bit in flags[0] was set. Fix these as well. Reformat all of the
capability printing lines, since it is less confusing than only
reformatting some of them.
Fixes: ae4e8a6 ("mpathpersist: Add new utility for managing persistent reservation on dm multipath device")
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
libmpathpersist/mpath_persist.h | 4 ++-
mpathpersist/main.c | 43 +++++++++++++++++++++++----------
2 files changed, 33 insertions(+), 14 deletions(-)
diff --git a/libmpathpersist/mpath_persist.h b/libmpathpersist/mpath_persist.h
index 807787b5..3d6fa51d 100644
--- a/libmpathpersist/mpath_persist.h
+++ b/libmpathpersist/mpath_persist.h
@@ -118,7 +118,9 @@ struct prin_capdescr
{
uint16_t length;
uint8_t flags[2];
- uint16_t pr_type_mask;
+ uint16_t pr_type_mask; /* The two bytes of the type mask are treated
+ as a single big-endian number. So the valid
+ type bits are 0xea01 */
uint16_t _reserved;
};
diff --git a/mpathpersist/main.c b/mpathpersist/main.c
index 26c69b01..a0646cde 100644
--- a/mpathpersist/main.c
+++ b/mpathpersist/main.c
@@ -754,25 +754,42 @@ void mpath_print_buf_readcap( struct prin_resp *pr_buff)
printf("Report capabilities response:\n");
- printf(" Compatible Reservation Handling(CRH): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x10));
- printf(" Specify Initiator Ports Capable(SIP_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x8));
- printf(" All Target Ports Capable(ATP_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x4 ));
- printf(" Persist Through Power Loss Capable(PTPL_C): %d\n",!!(pr_buff->prin_descriptor.prin_readcap.flags[0]));
- printf(" Type Mask Valid(TMV): %d\n", !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80));
- printf(" Allow Commands: %d\n", !!(( pr_buff->prin_descriptor.prin_readcap.flags[1] >> 4) & 0x7));
+ printf(" Compatible Reservation Handling(CRH): %d\n",
+ !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x10));
+ printf(" Specify Initiator Ports Capable(SIP_C): %d\n",
+ !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x8));
+ printf(" All Target Ports Capable(ATP_C): %d\n",
+ !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x4));
+ printf(" Persist Through Power Loss Capable(PTPL_C): %d\n",
+ !!(pr_buff->prin_descriptor.prin_readcap.flags[0] & 0x1));
+ printf(" Type Mask Valid(TMV): %d\n",
+ !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80));
+ printf(" Allow Commands: %d\n",
+ !!((pr_buff->prin_descriptor.prin_readcap.flags[1] >> 4) & 0x7));
printf(" Persist Through Power Loss Active(PTPL_A): %d\n",
- !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x1));
+ !!(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x1));
if(pr_buff->prin_descriptor.prin_readcap.flags[1] & 0x80)
{
printf(" Support indicated in Type mask:\n");
- printf(" %s: %d\n", pr_type_strs[7], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x80);
- printf(" %s: %d\n", pr_type_strs[6], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x40);
- printf(" %s: %d\n", pr_type_strs[5], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x20);
- printf(" %s: %d\n", pr_type_strs[3], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x8);
- printf(" %s: %d\n", pr_type_strs[1], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x2);
- printf(" %s: %d\n", pr_type_strs[8], pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x100);
+ printf(" %s: %d\n", pr_type_strs[7],
+ !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask &
+ 0x8000));
+ printf(" %s: %d\n", pr_type_strs[6],
+ !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask &
+ 0x4000));
+ printf(" %s: %d\n", pr_type_strs[5],
+ !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask &
+ 0x2000));
+ printf(" %s: %d\n", pr_type_strs[3],
+ !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask &
+ 0x800));
+ printf(" %s: %d\n", pr_type_strs[1],
+ !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask &
+ 0x200));
+ printf(" %s: %d\n", pr_type_strs[8],
+ !!(pr_buff->prin_descriptor.prin_readcap.pr_type_mask & 0x1));
}
}

View File

@ -1,6 +1,6 @@
Name: device-mapper-multipath
Version: 0.8.7
Release: 39%{?dist}
Release: 39%{?dist}.1
Summary: Tools to manage multipath devices using device-mapper
License: GPLv2
URL: http://christophe.varoqui.free.fr/
@ -159,6 +159,53 @@ Patch0146: 0146-multipath-tools-add-HPE-as-vendor-for-OPEN-XP8-array.patch
Patch0147: 0147-multipath-tools-add-HP-HSVX740-to-hwtable.patch
Patch0148: 0148-multipath-tools-add-DellEMC-ME5-PowerVault-ME5-to-ha.patch
Patch0149: 0149-multipath-tools-add-HPE-MSA-Gen7-2070-2072-to-hwtabl.patch
Patch0150: 0150-libmpathpersist-retry-commands-on-other-paths-in-mpa.patch
Patch0151: 0151-libmpathpersist-check-released-key-against-the-reser.patch
Patch0152: 0152-multipathd-remove-thread-from-mpath_pr_event_handle.patch
Patch0153: 0153-libmpathpersist-remove-uneeded-wrapper-function.patch
Patch0154: 0154-libmpathpersist-reduce-log-level-for-persistent-rese.patch
Patch0155: 0155-libmpathpersist-remove-pointless-update_map_pr-ret-v.patch
Patch0156: 0156-multipathd-use-update_map_pr-in-mpath_pr_event_handl.patch
Patch0157: 0157-libmpathpersist-limit-changing-prflag-in-update_map_.patch
Patch0158: 0158-multipathd-Don-t-call-update_map_pr-unnecessarily.patch
Patch0159: 0159-libmpathpersist-remove-useless-function-send_prout_a.patch
Patch0160: 0160-libmpathpersist-fix-memory-leak-in-mpath_prout_rel.patch
Patch0161: 0161-limpathpersist-redesign-failed-release-workaround.patch
Patch0162: 0162-libmpathpersist-fail-the-release-if-all-threads-fail.patch
Patch0163: 0163-limpathpersist-Handle-changing-key-corner-case.patch
Patch0164: 0164-libmpathpersist-fix-command-keyword-ordering.patch
Patch0165: 0165-libmpathpersist-use-conf-timeout-for-updating-persis.patch
Patch0166: 0166-libmapthpersist-Handle-REGISTER-AND-IGNORE-changing-.patch
Patch0167: 0167-libmultipath-rename-prflag_value-enums.patch
Patch0168: 0168-libmpathpersist-use-a-switch-statement-for-prout-com.patch
Patch0169: 0169-libmpathpersist-Add-safety-check-for-preempting-on-k.patch
Patch0170: 0170-limpathpersist-remove-update_map_pr-code-for-NULL-pp.patch
Patch0171: 0171-libmpathpersist-move-update_map_pr-to-multipathd.patch
Patch0172: 0172-multipathd-clean-up-update_map_pr-and-mpath_pr_event.patch
Patch0173: 0173-libmpathpersist-clean-up-duplicate-function-declarat.patch
Patch0174: 0174-multipathd-wrap-setting-and-unsetting-prflag.patch
Patch0175: 0175-multipathd-unregister-PR-key-when-path-is-restored-i.patch
Patch0176: 0176-libmpathpersist-Fix-up-reservation_key-checking.patch
Patch0177: 0177-libmpathpersist-change-how-reservation-conflicts-are.patch
Patch0178: 0178-libmpathpersist-Clear-prkey-in-multipathd-before-unr.patch
Patch0179: 0179-libmpathpersist-only-clear-the-key-if-we-are-using-t.patch
Patch0180: 0180-libmpathpersist-Restore-old-reservation-key-on-failu.patch
Patch0181: 0181-libmpatpersist-update-reservation-key-before-checkin.patch
Patch0182: 0182-libmpathpersist-retry-on-conflicts-in-mpath_prout_co.patch
Patch0183: 0183-libmpathpersist-Don-t-always-fail-registrations-for-.patch
Patch0184: 0184-libmpathpersist-Don-t-try-release-workaround-for-inv.patch
Patch0185: 0185-libmpathpersist-Don-t-fail-RESERVE-commands-unnecess.patch
Patch0186: 0186-libmpathpersist-reregister-keys-when-self-preempting.patch
Patch0187: 0187-libmpathpersist-handle-updating-key-race-condition.patch
Patch0188: 0188-libmpathpersist-handle-preempting-all-registrants-re.patch
Patch0189: 0189-libmpathpersist-Fix-REGISTER-AND-IGNORE-while-holdin.patch
Patch0190: 0190-libmpathpersist-Handle-RESERVE-with-reservation-held.patch
Patch0191: 0191-libmpathpersist-use-check_holding_reservation-in-mpa.patch
Patch0192: 0192-libmpathpersist-Fix-unregistering-while-holding-the-.patch
Patch0193: 0193-libmpathpersist-Fix-race-between-restoring-a-path-an.patch
Patch0194: 0194-multipathd-Fix-tracking-of-old-PR-key.patch
Patch0195: 0195-multipathd-Fix-race-while-registering-PR-key.patch
Patch0196: 0196-mpathpersist-Fix-REPORT-CAPABILITIES-output.patch
# runtime
@ -362,6 +409,58 @@ fi
%{_pkgconfdir}/libdmmp.pc
%changelog
* Tue Nov 11 2025 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-39.1
- Add 0150-libmpathpersist-retry-commands-on-other-paths-in-mpa.patch
- Add 0151-libmpathpersist-check-released-key-against-the-reser.patch
- Add 0152-multipathd-remove-thread-from-mpath_pr_event_handle.patch
- Add 0153-libmpathpersist-remove-uneeded-wrapper-function.patch
- Add 0154-libmpathpersist-reduce-log-level-for-persistent-rese.patch
- Add 0155-libmpathpersist-remove-pointless-update_map_pr-ret-v.patch
- Add 0156-multipathd-use-update_map_pr-in-mpath_pr_event_handl.patch
- Add 0157-libmpathpersist-limit-changing-prflag-in-update_map_.patch
- Add 0158-multipathd-Don-t-call-update_map_pr-unnecessarily.patch
- Add 0159-libmpathpersist-remove-useless-function-send_prout_a.patch
- Add 0160-libmpathpersist-fix-memory-leak-in-mpath_prout_rel.patch
- Add 0161-limpathpersist-redesign-failed-release-workaround.patch
- Add 0162-libmpathpersist-fail-the-release-if-all-threads-fail.patch
- Add 0163-limpathpersist-Handle-changing-key-corner-case.patch
- Add 0164-libmpathpersist-fix-command-keyword-ordering.patch
- Add 0165-libmpathpersist-use-conf-timeout-for-updating-persis.patch
- Add 0166-libmapthpersist-Handle-REGISTER-AND-IGNORE-changing-.patch
- Add 0167-libmultipath-rename-prflag_value-enums.patch
- Add 0168-libmpathpersist-use-a-switch-statement-for-prout-com.patch
- Add 0169-libmpathpersist-Add-safety-check-for-preempting-on-k.patch
- Add 0170-limpathpersist-remove-update_map_pr-code-for-NULL-pp.patch
- Add 0171-libmpathpersist-move-update_map_pr-to-multipathd.patch
- Add 0172-multipathd-clean-up-update_map_pr-and-mpath_pr_event.patch
- Add 0173-libmpathpersist-clean-up-duplicate-function-declarat.patch
- Add 0174-multipathd-wrap-setting-and-unsetting-prflag.patch
- Add 0175-multipathd-unregister-PR-key-when-path-is-restored-i.patch
- Add 0176-libmpathpersist-Fix-up-reservation_key-checking.patch
- Add 0177-libmpathpersist-change-how-reservation-conflicts-are.patch
- Add 0178-libmpathpersist-Clear-prkey-in-multipathd-before-unr.patch
- Add 0179-libmpathpersist-only-clear-the-key-if-we-are-using-t.patch
- Add 0180-libmpathpersist-Restore-old-reservation-key-on-failu.patch
- Add 0181-libmpatpersist-update-reservation-key-before-checkin.patch
- Add 0182-libmpathpersist-retry-on-conflicts-in-mpath_prout_co.patch
- Add 0183-libmpathpersist-Don-t-always-fail-registrations-for-.patch
- Add 0184-libmpathpersist-Don-t-try-release-workaround-for-inv.patch
- Add 0185-libmpathpersist-Don-t-fail-RESERVE-commands-unnecess.patch
- Add 0186-libmpathpersist-reregister-keys-when-self-preempting.patch
- Add 0187-libmpathpersist-handle-updating-key-race-condition.patch
- Add 0188-libmpathpersist-handle-preempting-all-registrants-re.patch
- Add 0189-libmpathpersist-Fix-REGISTER-AND-IGNORE-while-holdin.patch
- Add 0190-libmpathpersist-Handle-RESERVE-with-reservation-held.patch
- Add 0191-libmpathpersist-use-check_holding_reservation-in-mpa.patch
- Add 0192-libmpathpersist-Fix-unregistering-while-holding-the-.patch
- Add 0193-libmpathpersist-Fix-race-between-restoring-a-path-an.patch
- Add 0194-multipathd-Fix-tracking-of-old-PR-key.patch
- Add 0195-multipathd-Fix-race-while-registering-PR-key.patch
- Add 0196-mpathpersist-Fix-REPORT-CAPABILITIES-output.patch
* Fixes RHEL-118723 ("There are many bugs in multipath's persistent
reservation handling [rhel-9.7.z]")
- Resolves: RHEL-118723
* Mon Jul 14 2025 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-39
- Add 0145-multipath-tools-add-DellEMC-ME4-PowerVault-ME4-to-ha.patch
- Add 0146-multipath-tools-add-HPE-as-vendor-for-OPEN-XP8-array.patch