import CS device-mapper-multipath-0.8.7-45.el9
This commit is contained in:
parent
4607fe0059
commit
c4627c7fb1
@ -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)
|
||||
{
|
||||
@ -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){
|
||||
@ -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);
|
||||
@ -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);
|
||||
@ -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:
|
||||
@ -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);
|
||||
@ -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);
|
||||
@ -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 )
|
||||
{
|
||||
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -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 *)(¶m));
|
||||
- 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);
|
||||
@ -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);
|
||||
@ -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 {
|
||||
@ -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){
|
||||
@ -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,
|
||||
+ ¶mp, 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;
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
@ -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);
|
||||
242
SOURCES/0167-libmultipath-rename-prflag_value-enums.patch
Normal file
242
SOURCES/0167-libmultipath-rename-prflag_value-enums.patch
Normal 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));
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
@ -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)
|
||||
@ -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;
|
||||
@ -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(¶m, 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, ¶m, 0);
|
||||
if (ret != MPATH_PR_SUCCESS )
|
||||
{
|
||||
condlog(0,"%s: Reservation registration failed. Error: %d", pp->dev, ret);
|
||||
}
|
||||
-
|
||||
- free(param);
|
||||
}
|
||||
@ -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);
|
||||
148
SOURCES/0174-multipathd-wrap-setting-and-unsetting-prflag.patch
Normal file
148
SOURCES/0174-multipathd-wrap-setting-and-unsetting-prflag.patch
Normal 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 */
|
||||
@ -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(¶m, 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, ¶m, 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);
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
@ -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(¶mp->sa_key[0]);
|
||||
- status = MPATH_PR_RESERV_CONFLICT ;
|
||||
- }
|
||||
- if (!rollback && (status == MPATH_PR_SUCCESS)){
|
||||
+ /*
|
||||
+ * We only retry if there is at least one registration that
|
||||
+ * returned a reservation conflict (which we need to retry)
|
||||
+ * and at least one registration the return success, so we
|
||||
+ * know that the command worked on some of the paths. If
|
||||
+ * the registation fails on all paths, then it wasn't a
|
||||
+ * valid request, so there's no need to retry.
|
||||
+ */
|
||||
+ if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
|
||||
+ need_retry = true;
|
||||
+ else if (thread[i].param.status == MPATH_PR_SUCCESS)
|
||||
+ can_retry = true;
|
||||
+ else if (status == MPATH_PR_SUCCESS)
|
||||
status = thread[i].param.status;
|
||||
- }
|
||||
}
|
||||
- if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){
|
||||
- condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid);
|
||||
- memcpy(¶mp->key, ¶mp->sa_key, 8);
|
||||
- memset(¶mp->sa_key, 0, 8);
|
||||
- for( i=0 ; i < count ; i++){
|
||||
- if(thread[i].param.status == MPATH_PR_SUCCESS) {
|
||||
- rc = pthread_create(&thread[i].id, &attr, mpath_prout_pthread_fn,
|
||||
- (void *)(&thread[i].param));
|
||||
- if (rc){
|
||||
- condlog (0, "%s: failed to create thread for rollback. %d", mpp->wwid, rc);
|
||||
- thread[i].param.status = MPATH_PR_THREAD_ERROR;
|
||||
- }
|
||||
- } else
|
||||
+ if (need_retry && can_retry && rq_servact == MPATH_PROUT_REG_SA &&
|
||||
+ status == MPATH_PR_SUCCESS) {
|
||||
+ condlog (3, "%s: ERROR: initiating pr out retry", mpp->wwid);
|
||||
+ for (i = 0; i < count; i++) {
|
||||
+ if (thread[i].param.status != MPATH_PR_RESERV_CONFLICT) {
|
||||
thread[i].param.status = MPATH_PR_SKIP;
|
||||
+ continue;
|
||||
+ }
|
||||
+ /*
|
||||
+ * retry using MPATH_PROUT_REG_IGN_SA to avoid
|
||||
+ * conflicts. We already know that some paths
|
||||
+ * succeeded using MPATH_PROUT_REG_SA.
|
||||
+ */
|
||||
+ thread[i].param.rq_servact = MPATH_PROUT_REG_IGN_SA;
|
||||
+ rc = pthread_create(&thread[i].id, &attr,
|
||||
+ mpath_prout_pthread_fn,
|
||||
+ (void *)(&thread[i].param));
|
||||
+ if (rc) {
|
||||
+ condlog(0, "%s: failed to create thread for retry. %d",
|
||||
+ mpp->wwid, rc);
|
||||
+ thread[i].param.status = MPATH_PR_THREAD_ERROR;
|
||||
+ }
|
||||
}
|
||||
- for(i=0; i < count ; i++){
|
||||
+ for (i = 0; i < count; i++) {
|
||||
if (thread[i].param.status != MPATH_PR_SKIP &&
|
||||
thread[i].param.status != MPATH_PR_THREAD_ERROR) {
|
||||
rc = pthread_join(thread[i].id, NULL);
|
||||
- if (rc){
|
||||
- condlog (3, "%s: failed to join thread while rolling back %d",
|
||||
+ 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;
|
||||
@ -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:
|
||||
@ -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:
|
||||
@ -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)
|
||||
{
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
@ -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)
|
||||
@ -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
|
||||
@ -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;
|
||||
}
|
||||
@ -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,
|
||||
+ ¶mp, noisy, &pp);
|
||||
+ if (status != MPATH_PR_SUCCESS) {
|
||||
+ condlog(0, "%s: self preempt command failed.", mpp->wwid);
|
||||
+ goto fail_resume;
|
||||
+ }
|
||||
+
|
||||
+ if (do_release) {
|
||||
+ memset(¶mp, 0, sizeof(paramp));
|
||||
+ memcpy(paramp.key, &mpp->reservation_key, 8);
|
||||
+ rel_status = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REL_SA,
|
||||
+ rq_scope, rq_type, ¶mp, noisy);
|
||||
+ if (rel_status != MPATH_PR_SUCCESS)
|
||||
+ condlog(0, "%s: release on alternate path failed.",
|
||||
+ mpp->wwid);
|
||||
+ }
|
||||
+
|
||||
+ memset(¶mp, 0, sizeof(paramp));
|
||||
+ memcpy(paramp.sa_key, &mpp->reservation_key, 8);
|
||||
+ status = mpath_prout_reg(mpp, MPATH_PROUT_REG_IGN_SA, rq_scope,
|
||||
+ rq_type, ¶mp, noisy);
|
||||
+ if (status != MPATH_PR_SUCCESS)
|
||||
+ condlog(0, "%s: self preempt failed to reregister paths.",
|
||||
+ mpp->wwid);
|
||||
+
|
||||
+fail_resume:
|
||||
+ if (!dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, udev_flags)) {
|
||||
+ condlog(0, "%s: self preempt failed to resume device.", mpp->wwid);
|
||||
+ if (status == MPATH_PR_SUCCESS)
|
||||
+ status = MPATH_PR_OTHER;
|
||||
+ }
|
||||
+ /* return the first error we encountered */
|
||||
+ return (rel_status != MPATH_PR_SUCCESS) ? rel_status : status;
|
||||
+}
|
||||
+
|
||||
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)
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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,
|
||||
¶mp, noisy, &pp);
|
||||
if (status != MPATH_PR_SUCCESS) {
|
||||
@ -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)
|
||||
{
|
||||
@ -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,
|
||||
- ¶mp, noisy, NULL);
|
||||
+ ¶mp, 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,
|
||||
- ¶mp, noisy, &pp);
|
||||
+ ¶mp, noisy, &pp, NULL);
|
||||
if (status != MPATH_PR_SUCCESS) {
|
||||
condlog(0, "%s: self preempt command failed.", mpp->wwid);
|
||||
goto fail_resume;
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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(¶mp, 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,
|
||||
+ ¶mp, noisy);
|
||||
+ if (rel_status != MPATH_PR_SUCCESS)
|
||||
+ condlog(0, "%s: final self preempt unregister failed,",
|
||||
+ mpp->wwid);
|
||||
+ }
|
||||
+ }
|
||||
+ if (work != PREE_WORK_REL_UNREG) {
|
||||
+ memset(¶mp, 0, sizeof(paramp));
|
||||
+ memcpy(paramp.sa_key, &mpp->reservation_key, 8);
|
||||
+ status = mpath_prout_reg(mpp, MPATH_PROUT_REG_IGN_SA, rq_scope,
|
||||
+ rq_type, ¶mp, noisy);
|
||||
+ if (status != MPATH_PR_SUCCESS)
|
||||
+ condlog(0, "%s: self preempt failed to reregister paths.",
|
||||
+ mpp->wwid);
|
||||
}
|
||||
-
|
||||
- memset(¶mp, 0, sizeof(paramp));
|
||||
- memcpy(paramp.sa_key, &mpp->reservation_key, 8);
|
||||
- status = mpath_prout_reg(mpp, MPATH_PROUT_REG_IGN_SA, rq_scope,
|
||||
- rq_type, ¶mp, noisy);
|
||||
- if (status != MPATH_PR_SUCCESS)
|
||||
- condlog(0, "%s: self preempt failed to reregister paths.",
|
||||
- mpp->wwid);
|
||||
|
||||
fail_resume:
|
||||
if (!dm_simplecmd_noflush(DM_DEVICE_RESUME, mpp->alias, udev_flags)) {
|
||||
@@ -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);
|
||||
@ -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(¶m, 0, sizeof(param));
|
||||
-
|
||||
if (!clear_reg) {
|
||||
param.sa_flags = mpp->sa_flags;
|
||||
memcpy(param.sa_key, &mpp->reservation_key, 8);
|
||||
param.num_transportid = 0;
|
||||
}
|
||||
-
|
||||
+retry:
|
||||
condlog(3, "%s registration for device %s:%s",
|
||||
clear_reg ? "Clearing" : "Setting", pp->dev, pp->mpp->wwid);
|
||||
|
||||
ret = prout_do_scsi_ioctl(pp->dev, MPATH_PROUT_REG_IGN_SA, 0, 0, ¶m, 0);
|
||||
- if (ret != MPATH_PR_SUCCESS )
|
||||
- {
|
||||
+ if (ret != MPATH_PR_SUCCESS) {
|
||||
condlog(0, "%s: %s reservation registration failed. Error: %d",
|
||||
clear_reg ? "Clearing" : "Setting", pp->dev, ret);
|
||||
+ } else if (!clear_reg) {
|
||||
+ if (update_map_pr(mpp, pp, &nr_keys_needed) != MPATH_PR_SUCCESS)
|
||||
+ return 0;
|
||||
+ if (mpp->prflag != PR_SET) {
|
||||
+ memset(¶m, 0, sizeof(param));
|
||||
+ clear_reg = true;
|
||||
+ goto retry;
|
||||
+ }
|
||||
+ return nr_keys_needed;
|
||||
}
|
||||
+ return 0;
|
||||
}
|
||||
49
SOURCES/0194-multipathd-Fix-tracking-of-old-PR-key.patch
Normal file
49
SOURCES/0194-multipathd-Fix-tracking-of-old-PR-key.patch
Normal 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)
|
||||
246
SOURCES/0195-multipathd-Fix-race-while-registering-PR-key.patch
Normal file
246
SOURCES/0195-multipathd-Fix-race-while-registering-PR-key.patch
Normal 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 */
|
||||
108
SOURCES/0196-mpathpersist-Fix-REPORT-CAPABILITIES-output.patch
Normal file
108
SOURCES/0196-mpathpersist-Fix-REPORT-CAPABILITIES-output.patch
Normal 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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,65 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Xose Vazquez Perez <xose.vazquez@gmail.com>
|
||||
Date: Sat, 16 Aug 2025 20:16:23 +0200
|
||||
Subject: [PATCH] multipath-tools: update NFINIDAT/InfiniBox config in hwtable
|
||||
|
||||
New recommended values for SLES-15, RHEL-8, and Ubuntu-22, or above:
|
||||
https://lh3.googleusercontent.com/pw/AP1GczMchJ6bcMIohp_g3Ik5DS6BZv_IW0iwaEXf968sJsR8fr_p3mR4ThRpmHpZE_VMnlcV8j0uuuI2kz-eoqekRCUBxyTBKS7n-4WFMsUiecq8i7nIjEuhfZFrV54DBQLDzGt6ofakAAF1L-ZcAuCWW18=w0-h0
|
||||
|
||||
device {
|
||||
vendor "NFINIDAT"
|
||||
product "InfiniBox"
|
||||
path_grouping_policy "group_by_prio"
|
||||
path_checker "tur"
|
||||
features 0
|
||||
hardware_handler "1 alua"
|
||||
prio "alua"
|
||||
rr_weight "priorities"
|
||||
no_path_retry "queue"
|
||||
rr_min_io 1
|
||||
rr_min_io_rq 1
|
||||
flush_on_last_del "yes"
|
||||
fast_io_fail_tmo 15
|
||||
dev_loss_tmo "infinity"
|
||||
path_selector "service-time 0"
|
||||
failback "immediate"
|
||||
detect_prio "no"
|
||||
user_friendly_names "no"
|
||||
}
|
||||
|
||||
Cc: Martin Wilck <mwilck@suse.com>
|
||||
Cc: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Cc: Christophe Varoqui <christophe.varoqui@opensvc.com>
|
||||
Cc: DM_DEVEL-ML <dm-devel@lists.linux.dev>
|
||||
Signed-off-by: Xose Vazquez Perez <xose.vazquez@gmail.com>
|
||||
Reviewed-by: Martin Wilck <mwilck@suse.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/hwtable.c | 11 ++++-------
|
||||
1 file changed, 4 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c
|
||||
index 2d359829..188ad5e0 100644
|
||||
--- a/libmultipath/hwtable.c
|
||||
+++ b/libmultipath/hwtable.c
|
||||
@@ -1124,16 +1124,13 @@ static struct hwentry default_hw[] = {
|
||||
.vendor = "NFINIDAT",
|
||||
.product = "InfiniBox",
|
||||
.pgpolicy = GROUP_BY_PRIO,
|
||||
- .pgfailback = 30,
|
||||
+ .pgfailback = -FAILBACK_IMMEDIATE,
|
||||
.prio_name = PRIO_ALUA,
|
||||
- .selector = "round-robin 0",
|
||||
- .rr_weight = RR_WEIGHT_PRIO,
|
||||
- .no_path_retry = NO_PATH_RETRY_FAIL,
|
||||
- .minio = 1,
|
||||
- .minio_rq = 1,
|
||||
+ .no_path_retry = NO_PATH_RETRY_QUEUE,
|
||||
.flush_on_last_del = FLUSH_ALWAYS,
|
||||
.fast_io_fail = 15,
|
||||
- .dev_loss = 15,
|
||||
+ .dev_loss = MAX_DEV_LOSS_TMO,
|
||||
+ .detect_prio = DETECT_PRIO_OFF,
|
||||
},
|
||||
/*
|
||||
* Kaminario
|
||||
@ -0,0 +1,36 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Date: Wed, 21 Jan 2026 16:03:13 -0500
|
||||
Subject: [PATCH] multipathd: print path offline message even without a checker
|
||||
|
||||
If a path has a checker selected and is offline, multipathd will print a
|
||||
"path offline" message. However if the checker isn't selected, for
|
||||
instance because multipathd was started or reconfigured while the path
|
||||
was offline, multipathd was not printing the "path offline" message.
|
||||
Fix that.
|
||||
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Reviewed-by: Martin Wilck <mwilck@suse.com>
|
||||
---
|
||||
multipathd/main.c | 5 ++---
|
||||
1 file changed, 2 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/multipathd/main.c b/multipathd/main.c
|
||||
index a85c0db4..9beb0e06 100644
|
||||
--- a/multipathd/main.c
|
||||
+++ b/multipathd/main.c
|
||||
@@ -97,12 +97,11 @@ mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed,
|
||||
|
||||
#define LOG_MSG(lvl, pp) \
|
||||
do { \
|
||||
- if (pp->mpp && checker_selected(&pp->checker) && \
|
||||
- lvl <= libmp_verbosity) { \
|
||||
+ if (pp->mpp && lvl <= libmp_verbosity) { \
|
||||
if (pp->offline) \
|
||||
condlog(lvl, "%s: %s - path offline", \
|
||||
pp->mpp->alias, pp->dev); \
|
||||
- else { \
|
||||
+ else if (checker_selected(&pp->checker)) { \
|
||||
const char *__m = \
|
||||
checker_message(&pp->checker); \
|
||||
\
|
||||
@ -0,0 +1,133 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Thu, 9 Sep 2021 23:59:42 +0200
|
||||
Subject: [PATCH] libmultipath: improve cleanup of uevent queues on exit
|
||||
|
||||
uevents listed on merge_node must be cleaned up, too. uevents
|
||||
cancelled while being serviced and temporary queues, likewise.
|
||||
The global uevq must be cleaned out in the uevent listener thread,
|
||||
because it might have added events after the dispatcher thread
|
||||
had already finished.
|
||||
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/uevent.c | 49 ++++++++++++++++++++++++++++++++-----------
|
||||
1 file changed, 37 insertions(+), 12 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index 4265904b..80941f87 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -91,16 +91,25 @@ struct uevent * alloc_uevent (void)
|
||||
return uev;
|
||||
}
|
||||
|
||||
+static void uevq_cleanup(struct list_head *tmpq);
|
||||
+
|
||||
+static void cleanup_uev(void *arg)
|
||||
+{
|
||||
+ struct uevent *uev = arg;
|
||||
+
|
||||
+ uevq_cleanup(&uev->merge_node);
|
||||
+ if (uev->udev)
|
||||
+ udev_device_unref(uev->udev);
|
||||
+ free(uev);
|
||||
+}
|
||||
+
|
||||
static void uevq_cleanup(struct list_head *tmpq)
|
||||
{
|
||||
struct uevent *uev, *tmp;
|
||||
|
||||
list_for_each_entry_safe(uev, tmp, tmpq, node) {
|
||||
list_del_init(&uev->node);
|
||||
-
|
||||
- if (uev->udev)
|
||||
- udev_device_unref(uev->udev);
|
||||
- FREE(uev);
|
||||
+ cleanup_uev(uev);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -384,14 +393,10 @@ service_uevq(struct list_head *tmpq)
|
||||
list_for_each_entry_safe(uev, tmp, tmpq, node) {
|
||||
list_del_init(&uev->node);
|
||||
|
||||
+ pthread_cleanup_push(cleanup_uev, uev);
|
||||
if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))
|
||||
condlog(0, "uevent trigger error");
|
||||
-
|
||||
- uevq_cleanup(&uev->merge_node);
|
||||
-
|
||||
- if (uev->udev)
|
||||
- udev_device_unref(uev->udev);
|
||||
- FREE(uev);
|
||||
+ pthread_cleanup_pop(1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,6 +416,18 @@ static void monitor_cleanup(void *arg)
|
||||
udev_monitor_unref(monitor);
|
||||
}
|
||||
|
||||
+static void cleanup_uevq(void *arg)
|
||||
+{
|
||||
+ uevq_cleanup(arg);
|
||||
+}
|
||||
+
|
||||
+static void cleanup_global_uevq(void *arg __attribute__((unused)))
|
||||
+{
|
||||
+ pthread_mutex_lock(uevq_lockp);
|
||||
+ uevq_cleanup(&uevq);
|
||||
+ pthread_mutex_unlock(uevq_lockp);
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* Service the uevent queue.
|
||||
*/
|
||||
@@ -425,6 +442,7 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
|
||||
while (1) {
|
||||
LIST_HEAD(uevq_tmp);
|
||||
|
||||
+ pthread_cleanup_push(cleanup_mutex, uevq_lockp);
|
||||
pthread_mutex_lock(uevq_lockp);
|
||||
servicing_uev = 0;
|
||||
/*
|
||||
@@ -436,14 +454,17 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
|
||||
}
|
||||
servicing_uev = 1;
|
||||
list_splice_init(&uevq, &uevq_tmp);
|
||||
- pthread_mutex_unlock(uevq_lockp);
|
||||
+ pthread_cleanup_pop(1);
|
||||
+
|
||||
if (!my_uev_trigger)
|
||||
break;
|
||||
+
|
||||
+ pthread_cleanup_push(cleanup_uevq, &uevq_tmp);
|
||||
merge_uevq(&uevq_tmp);
|
||||
service_uevq(&uevq_tmp);
|
||||
+ pthread_cleanup_pop(1);
|
||||
}
|
||||
condlog(3, "Terminating uev service queue");
|
||||
- uevq_cleanup(&uevq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -600,6 +621,8 @@ int uevent_listen(struct udev *udev)
|
||||
|
||||
events = 0;
|
||||
gettimeofday(&start_time, NULL);
|
||||
+ pthread_cleanup_push(cleanup_global_uevq, NULL);
|
||||
+ pthread_cleanup_push(cleanup_uevq, &uevlisten_tmp);
|
||||
while (1) {
|
||||
struct uevent *uev;
|
||||
struct udev_device *dev;
|
||||
@@ -650,6 +673,8 @@ int uevent_listen(struct udev *udev)
|
||||
gettimeofday(&start_time, NULL);
|
||||
timeout = 30;
|
||||
}
|
||||
+ pthread_cleanup_pop(1);
|
||||
+ pthread_cleanup_pop(1);
|
||||
out:
|
||||
pthread_cleanup_pop(1);
|
||||
out_udev:
|
||||
38
SOURCES/0200-uevent_dispatch-use-while-in-wait-loop.patch
Normal file
38
SOURCES/0200-uevent_dispatch-use-while-in-wait-loop.patch
Normal file
@ -0,0 +1,38 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Tue, 29 Mar 2022 16:06:25 +0200
|
||||
Subject: [PATCH] uevent_dispatch(): use while in wait loop
|
||||
|
||||
Callers of pthread_cond_wait() should generally use a while loop
|
||||
to test the condition. Also, remove the misleading comment.
|
||||
Condition variables aren't unreliable, they're just not strictly
|
||||
tied to the condition tested.
|
||||
|
||||
Signed-off-by: Martin Wilck <mwilck@suse.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/uevent.c | 9 +++------
|
||||
1 file changed, 3 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index 80941f87..e3ec1ac1 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -445,13 +445,10 @@ int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
|
||||
pthread_cleanup_push(cleanup_mutex, uevq_lockp);
|
||||
pthread_mutex_lock(uevq_lockp);
|
||||
servicing_uev = 0;
|
||||
- /*
|
||||
- * Condition signals are unreliable,
|
||||
- * so make sure we only wait if we have to.
|
||||
- */
|
||||
- if (list_empty(&uevq)) {
|
||||
+
|
||||
+ while (list_empty(&uevq))
|
||||
pthread_cond_wait(uev_condp, uevq_lockp);
|
||||
- }
|
||||
+
|
||||
servicing_uev = 1;
|
||||
list_splice_init(&uevq, &uevq_tmp);
|
||||
pthread_cleanup_pop(1);
|
||||
@ -0,0 +1,319 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Tue, 29 Mar 2022 18:04:42 +0200
|
||||
Subject: [PATCH] libmultipath: uevent_dispatch(): process uevents one by one
|
||||
|
||||
The main rationale for delaying uevents is that the
|
||||
uevent dispatcher may have to wait for other threads to release the
|
||||
vecs lock, may the vecs lock for an extended amount of time, and
|
||||
even sleep occasionally. By delaying them, we have the chance
|
||||
to accumulate events for the same path device ("filtering") or
|
||||
WWID ("merging"), thus avoiding duplicate work if we merge these
|
||||
into one.
|
||||
|
||||
A similar effect can be obtained in the uevent dispatcher itself
|
||||
by looking for new uevents after each dispatched event, and trying
|
||||
to merge the newly arrived events with those that remained
|
||||
in the queue.
|
||||
|
||||
When uevq_work is non-empty and we append a list of new events,
|
||||
we don't need to check the entire list for filterable and mergeable
|
||||
uevents. uevq_work had been filtered and merged already. So we just
|
||||
need to check the newly appended events. These must of course be
|
||||
checked for merges with earlier events, too.
|
||||
|
||||
We must deal with some special cases here, like previously merged
|
||||
uevents being filtered later.
|
||||
|
||||
Signed-off-by: Martin Wilck <mwilck@suse.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/list.h | 53 +++++++++++++++++
|
||||
libmultipath/uevent.c | 129 ++++++++++++++++++++++++++++++------------
|
||||
2 files changed, 147 insertions(+), 35 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/list.h b/libmultipath/list.h
|
||||
index ced021f5..248f72bc 100644
|
||||
--- a/libmultipath/list.h
|
||||
+++ b/libmultipath/list.h
|
||||
@@ -246,6 +246,35 @@ static inline void list_splice_tail_init(struct list_head *list,
|
||||
#define list_entry(ptr, type, member) \
|
||||
container_of(ptr, type, member)
|
||||
|
||||
+
|
||||
+/**
|
||||
+ * list_pop - unlink and return the first list element
|
||||
+ * @head: the &struct list_head pointer.
|
||||
+ */
|
||||
+static inline struct list_head *list_pop(struct list_head *head)
|
||||
+{
|
||||
+ struct list_head *tmp;
|
||||
+
|
||||
+ if (list_empty(head))
|
||||
+ return NULL;
|
||||
+ tmp = head->next;
|
||||
+ list_del_init(tmp);
|
||||
+ return tmp;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
+ * list_pop_entry - unlink and return the entry of the first list element
|
||||
+ * @head: the &struct list_head pointer.
|
||||
+ * @type: the type of the struct this is embedded in.
|
||||
+ * @member: the name of the list_struct within the struct.
|
||||
+ */
|
||||
+#define list_pop_entry(head, type, member) \
|
||||
+({ \
|
||||
+ struct list_head *__h = list_pop(head); \
|
||||
+ \
|
||||
+ (__h ? container_of(__h, type, member) : NULL); \
|
||||
+})
|
||||
+
|
||||
/**
|
||||
* list_for_each - iterate over a list
|
||||
* @pos: the &struct list_head to use as a loop counter.
|
||||
@@ -334,6 +363,30 @@ static inline void list_splice_tail_init(struct list_head *list,
|
||||
&pos->member != (head); \
|
||||
pos = n, n = list_entry(n->member.prev, typeof(*n), member))
|
||||
|
||||
+/**
|
||||
+ * list_for_some_entry - iterate list from the given begin node to the given end node
|
||||
+ * @pos: the type * to use as a loop counter.
|
||||
+ * @from: the begin node of the iteration.
|
||||
+ * @to: the end node of the iteration.
|
||||
+ * @member: the name of the list_struct within the struct.
|
||||
+ */
|
||||
+#define list_for_some_entry(pos, from, to, member) \
|
||||
+ for (pos = list_entry((from)->next, typeof(*pos), member); \
|
||||
+ &pos->member != (to); \
|
||||
+ pos = list_entry(pos->member.next, typeof(*pos), member))
|
||||
+
|
||||
+/**
|
||||
+ * list_for_some_entry_reverse - iterate backwards list from the given begin node to the given end node
|
||||
+ * @pos: the type * to use as a loop counter.
|
||||
+ * @from: the begin node of the iteration.
|
||||
+ * @to: the end node of the iteration.
|
||||
+ * @member: the name of the list_struct within the struct.
|
||||
+ */
|
||||
+#define list_for_some_entry_reverse(pos, from, to, member) \
|
||||
+ for (pos = list_entry((from)->prev, typeof(*pos), member); \
|
||||
+ &pos->member != (to); \
|
||||
+ pos = list_entry(pos->member.prev, typeof(*pos), member))
|
||||
+
|
||||
/**
|
||||
* list_for_some_entry_safe - iterate list from the given begin node to the given end node safe against removal of list entry
|
||||
* @pos: the type * to use as a loop counter.
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index e3ec1ac1..2198e254 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -308,17 +308,64 @@ uevent_can_merge(struct uevent *earlier, struct uevent *later)
|
||||
return false;
|
||||
}
|
||||
|
||||
+static void uevent_delete_from_list(struct uevent *to_delete,
|
||||
+ struct uevent **previous,
|
||||
+ struct list_head **old_tail)
|
||||
+{
|
||||
+ /*
|
||||
+ * "old_tail" is the list_head before the last list element to which
|
||||
+ * the caller iterates (the list anchor if the caller iterates over
|
||||
+ * the entire list). If this element is removed (which can't happen
|
||||
+ * for the anchor), "old_tail" must be moved. It can happen that
|
||||
+ * "old_tail" ends up pointing at the anchor.
|
||||
+ */
|
||||
+ if (*old_tail == &to_delete->node)
|
||||
+ *old_tail = to_delete->node.prev;
|
||||
+
|
||||
+ list_del_init(&to_delete->node);
|
||||
+
|
||||
+ /*
|
||||
+ * The "to_delete" uevent has been merged with other uevents
|
||||
+ * previously. Re-insert them into the list, at the point we're
|
||||
+ * currently at. This must be done after the list_del_init() above,
|
||||
+ * otherwise previous->next would still point to to_delete.
|
||||
+ */
|
||||
+ if (!list_empty(&to_delete->merge_node)) {
|
||||
+ struct uevent *last = list_entry(to_delete->merge_node.prev,
|
||||
+ typeof(*last), node);
|
||||
+
|
||||
+ list_splice(&to_delete->merge_node, &(*previous)->node);
|
||||
+ *previous = last;
|
||||
+ }
|
||||
+ if (to_delete->udev)
|
||||
+ udev_device_unref(to_delete->udev);
|
||||
+
|
||||
+ free(to_delete);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Use this function to delete events that are known not to
|
||||
+ * be equal to old_tail, and have an empty merge_node list.
|
||||
+ * For others, use uevent_delete_from_list().
|
||||
+ */
|
||||
+static void uevent_delete_simple(struct uevent *to_delete)
|
||||
+{
|
||||
+ list_del_init(&to_delete->node);
|
||||
+
|
||||
+ if (to_delete->udev)
|
||||
+ udev_device_unref(to_delete->udev);
|
||||
+
|
||||
+ free(to_delete);
|
||||
+}
|
||||
+
|
||||
static void
|
||||
-uevent_prepare(struct list_head *tmpq)
|
||||
+uevent_prepare(struct list_head *tmpq, const struct list_head *stop)
|
||||
{
|
||||
struct uevent *uev, *tmp;
|
||||
|
||||
- list_for_each_entry_reverse_safe(uev, tmp, tmpq, node) {
|
||||
+ list_for_some_entry_reverse_safe(uev, tmp, tmpq, stop, node) {
|
||||
if (uevent_can_discard(uev)) {
|
||||
- list_del_init(&uev->node);
|
||||
- if (uev->udev)
|
||||
- udev_device_unref(uev->udev);
|
||||
- FREE(uev);
|
||||
+ uevent_delete_simple(uev);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -329,7 +376,7 @@ uevent_prepare(struct list_head *tmpq)
|
||||
}
|
||||
|
||||
static void
|
||||
-uevent_filter(struct uevent *later, struct list_head *tmpq)
|
||||
+uevent_filter(struct uevent *later, struct list_head *tmpq, struct list_head **stop)
|
||||
{
|
||||
struct uevent *earlier, *tmp;
|
||||
|
||||
@@ -343,16 +390,13 @@ uevent_filter(struct uevent *later, struct list_head *tmpq)
|
||||
earlier->kernel, earlier->action,
|
||||
later->kernel, later->action);
|
||||
|
||||
- list_del_init(&earlier->node);
|
||||
- if (earlier->udev)
|
||||
- udev_device_unref(earlier->udev);
|
||||
- FREE(earlier);
|
||||
+ uevent_delete_from_list(earlier, &tmp, stop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
-uevent_merge(struct uevent *later, struct list_head *tmpq)
|
||||
+uevent_merge(struct uevent *later, struct list_head *tmpq, struct list_head **stop)
|
||||
{
|
||||
struct uevent *earlier, *tmp;
|
||||
|
||||
@@ -367,37 +411,42 @@ uevent_merge(struct uevent *later, struct list_head *tmpq)
|
||||
earlier->action, earlier->kernel, earlier->wwid,
|
||||
later->action, later->kernel, later->wwid);
|
||||
|
||||
+ /* See comment in uevent_delete_from_list() */
|
||||
+ if (&earlier->node == *stop)
|
||||
+ *stop = earlier->node.prev;
|
||||
+
|
||||
list_move(&earlier->node, &later->merge_node);
|
||||
+ list_splice_init(&earlier->merge_node,
|
||||
+ &later->merge_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
-merge_uevq(struct list_head *tmpq)
|
||||
+merge_uevq(struct list_head *tmpq, struct list_head *stop)
|
||||
{
|
||||
struct uevent *later;
|
||||
|
||||
- uevent_prepare(tmpq);
|
||||
- list_for_each_entry_reverse(later, tmpq, node) {
|
||||
- uevent_filter(later, tmpq);
|
||||
+ uevent_prepare(tmpq, stop);
|
||||
+ list_for_some_entry_reverse(later, tmpq, stop, node) {
|
||||
+ uevent_filter(later, tmpq, &stop);
|
||||
if(uevent_need_merge())
|
||||
- uevent_merge(later, tmpq);
|
||||
+ uevent_merge(later, tmpq, &stop);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
service_uevq(struct list_head *tmpq)
|
||||
{
|
||||
- struct uevent *uev, *tmp;
|
||||
-
|
||||
- list_for_each_entry_safe(uev, tmp, tmpq, node) {
|
||||
- list_del_init(&uev->node);
|
||||
-
|
||||
- pthread_cleanup_push(cleanup_uev, uev);
|
||||
- if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))
|
||||
- condlog(0, "uevent trigger error");
|
||||
- pthread_cleanup_pop(1);
|
||||
- }
|
||||
+ struct uevent *uev = list_pop_entry(tmpq, typeof(*uev), node);
|
||||
+
|
||||
+ if (uev == NULL)
|
||||
+ return;
|
||||
+ condlog(4, "servicing uevent '%s %s'", uev->action, uev->kernel);
|
||||
+ pthread_cleanup_push(cleanup_uev, uev);
|
||||
+ if (my_uev_trigger && my_uev_trigger(uev, my_trigger_data))
|
||||
+ condlog(0, "uevent trigger error");
|
||||
+ pthread_cleanup_pop(1);
|
||||
}
|
||||
|
||||
static void uevent_cleanup(void *arg)
|
||||
@@ -434,33 +483,43 @@ static void cleanup_global_uevq(void *arg __attribute__((unused)))
|
||||
int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
|
||||
void * trigger_data)
|
||||
{
|
||||
+ LIST_HEAD(uevq_work);
|
||||
+
|
||||
my_uev_trigger = uev_trigger;
|
||||
my_trigger_data = trigger_data;
|
||||
|
||||
mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||
|
||||
+ pthread_cleanup_push(cleanup_uevq, &uevq_work);
|
||||
while (1) {
|
||||
- LIST_HEAD(uevq_tmp);
|
||||
+ struct list_head *stop;
|
||||
|
||||
pthread_cleanup_push(cleanup_mutex, uevq_lockp);
|
||||
pthread_mutex_lock(uevq_lockp);
|
||||
- servicing_uev = 0;
|
||||
|
||||
- while (list_empty(&uevq))
|
||||
+ servicing_uev = !list_empty(&uevq_work);
|
||||
+
|
||||
+ while (list_empty(&uevq_work) && list_empty(&uevq))
|
||||
pthread_cond_wait(uev_condp, uevq_lockp);
|
||||
|
||||
servicing_uev = 1;
|
||||
- list_splice_init(&uevq, &uevq_tmp);
|
||||
+ /*
|
||||
+ * "stop" is the list element towards which merge_uevq()
|
||||
+ * will iterate: the last element of uevq_work before
|
||||
+ * appending new uevents. If uveq_is empty, uevq_work.prev
|
||||
+ * equals &uevq_work, which is what we need.
|
||||
+ */
|
||||
+ stop = uevq_work.prev;
|
||||
+ list_splice_tail_init(&uevq, &uevq_work);
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
if (!my_uev_trigger)
|
||||
break;
|
||||
|
||||
- pthread_cleanup_push(cleanup_uevq, &uevq_tmp);
|
||||
- merge_uevq(&uevq_tmp);
|
||||
- service_uevq(&uevq_tmp);
|
||||
- pthread_cleanup_pop(1);
|
||||
+ merge_uevq(&uevq_work, stop);
|
||||
+ service_uevq(&uevq_work);
|
||||
}
|
||||
+ pthread_cleanup_pop(1);
|
||||
condlog(3, "Terminating uev service queue");
|
||||
return 0;
|
||||
}
|
||||
@ -0,0 +1,204 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Tue, 29 Mar 2022 23:25:48 +0200
|
||||
Subject: [PATCH] libmultipath: uevent_listen(): don't delay uevents
|
||||
|
||||
When multipathd starts up early, basically all devices are added
|
||||
through uevent processing. This takes much more time than necessary
|
||||
because of the artificial delays introduced for passing uevents
|
||||
between the listener and the receiver thread in ee8888f
|
||||
("multipath-tools: improve processing efficiency for addition and deletion of
|
||||
multipath devices"). This delay could be up to 30s.
|
||||
|
||||
It's generally not a good idea to delay uevent processing in multipathd.
|
||||
ADD events must normally be handled ASAP in order to avoid maps entering
|
||||
queueing mode or eventually failing. Handling REMOVE events quickly is
|
||||
also important to make multipathd aware of deleted devices and keep
|
||||
kernel and multipathd state in sync.
|
||||
|
||||
If uevents arrive quickly, the assumption is that the dispatcher will process
|
||||
them more slowly than the listener. This was the idea of commit ee8888f,
|
||||
AFAIU: if a queue of unprocessed events piles up because the dispatcher is
|
||||
too slow, use filtering and merging to reduce the length of the queue, and
|
||||
thus the work to be done for the uevent dispatcher, especially the work
|
||||
that needs to be done while holding the vecs lock. In ee8888f, the
|
||||
queue was created by allowing uevents to accumulate in the listener.
|
||||
|
||||
This patch changes the logic of ee8888f, while keeping the uevent
|
||||
filtering and discarding features. The idea is that the uevent dispatcher
|
||||
shouldn't be idle if there are uevents to process. Therefore uevents
|
||||
are passed to it immediately. But it now checks for new uevents after
|
||||
processing every individual event, before processing the entire queue,
|
||||
and it applies filtering and merging to the queue as it grows.
|
||||
|
||||
This patch set avoids any delay when the uevent dispatcher is idle or
|
||||
able to keep up with the rate of incoming uevents, while applying an
|
||||
increasing amount of filtering and merging as pressure on the uevent
|
||||
dispatcher increases. It's reasonable to assume that filtering and
|
||||
merging get more efficient with increasing queue length, because the
|
||||
probability of finding matching events will increase.
|
||||
|
||||
Signed-off-by: Martin Wilck <mwilck@suse.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/uevent.c | 108 +++++++++++++++---------------------------
|
||||
1 file changed, 37 insertions(+), 71 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index 2198e254..c3984fef 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -54,10 +54,6 @@
|
||||
#include "blacklist.h"
|
||||
#include "devmapper.h"
|
||||
|
||||
-#define MAX_ACCUMULATION_COUNT 2048
|
||||
-#define MAX_ACCUMULATION_TIME 30*1000
|
||||
-#define MIN_BURST_SPEED 10
|
||||
-
|
||||
typedef int (uev_trigger)(struct uevent *, void * trigger_data);
|
||||
|
||||
static LIST_HEAD(uevq);
|
||||
@@ -586,44 +582,43 @@ static struct uevent *uevent_from_udev_device(struct udev_device *dev)
|
||||
return uev;
|
||||
}
|
||||
|
||||
-static bool uevent_burst(struct timeval *start_time, int events)
|
||||
+#define MAX_UEVENTS 1000
|
||||
+static int uevent_receive_events(int fd, struct list_head *tmpq,
|
||||
+ struct udev_monitor *monitor)
|
||||
{
|
||||
- struct timeval diff_time, end_time;
|
||||
- unsigned long speed;
|
||||
- unsigned long eclipse_ms;
|
||||
-
|
||||
- if(events > MAX_ACCUMULATION_COUNT) {
|
||||
- condlog(2, "burst got %u uevents, too much uevents, stopped", events);
|
||||
- return false;
|
||||
- }
|
||||
+ struct pollfd ev_poll;
|
||||
+ int n = 0;
|
||||
|
||||
- gettimeofday(&end_time, NULL);
|
||||
- timersub(&end_time, start_time, &diff_time);
|
||||
+ do {
|
||||
+ struct uevent *uev;
|
||||
+ struct udev_device *dev;
|
||||
|
||||
- eclipse_ms = diff_time.tv_sec * 1000 + diff_time.tv_usec / 1000;
|
||||
+ dev = udev_monitor_receive_device(monitor);
|
||||
+ if (!dev) {
|
||||
+ condlog(0, "failed getting udev device");
|
||||
+ break;
|
||||
+ }
|
||||
+ uev = uevent_from_udev_device(dev);
|
||||
+ if (!uev)
|
||||
+ break;
|
||||
|
||||
- if (eclipse_ms == 0)
|
||||
- return true;
|
||||
+ list_add_tail(&uev->node, tmpq);
|
||||
+ n++;
|
||||
+ condlog(4, "received uevent \"%s %s\"", uev->action, uev->kernel);
|
||||
|
||||
- if (eclipse_ms > MAX_ACCUMULATION_TIME) {
|
||||
- condlog(2, "burst continued %lu ms, too long time, stopped", eclipse_ms);
|
||||
- return false;
|
||||
- }
|
||||
+ ev_poll.fd = fd;
|
||||
+ ev_poll.events = POLLIN;
|
||||
|
||||
- speed = (events * 1000) / eclipse_ms;
|
||||
- if (speed > MIN_BURST_SPEED)
|
||||
- return true;
|
||||
+ } while (n < MAX_UEVENTS && poll(&ev_poll, 1, 0) > 0);
|
||||
|
||||
- return false;
|
||||
+ return n;
|
||||
}
|
||||
|
||||
int uevent_listen(struct udev *udev)
|
||||
{
|
||||
int err = 2;
|
||||
struct udev_monitor *monitor = NULL;
|
||||
- int fd, socket_flags, events;
|
||||
- struct timeval start_time;
|
||||
- int timeout = 30;
|
||||
+ int fd, socket_flags;
|
||||
LIST_HEAD(uevlisten_tmp);
|
||||
|
||||
/*
|
||||
@@ -675,59 +670,30 @@ int uevent_listen(struct udev *udev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
- events = 0;
|
||||
- gettimeofday(&start_time, NULL);
|
||||
pthread_cleanup_push(cleanup_global_uevq, NULL);
|
||||
pthread_cleanup_push(cleanup_uevq, &uevlisten_tmp);
|
||||
while (1) {
|
||||
- struct uevent *uev;
|
||||
- struct udev_device *dev;
|
||||
- struct pollfd ev_poll;
|
||||
- int poll_timeout;
|
||||
- int fdcount;
|
||||
+ int fdcount, events;
|
||||
+ struct pollfd ev_poll = { .fd = fd, .events = POLLIN, };
|
||||
|
||||
- memset(&ev_poll, 0, sizeof(struct pollfd));
|
||||
- ev_poll.fd = fd;
|
||||
- ev_poll.events = POLLIN;
|
||||
- poll_timeout = timeout * 1000;
|
||||
- errno = 0;
|
||||
- fdcount = poll(&ev_poll, 1, poll_timeout);
|
||||
- if (fdcount > 0 && ev_poll.revents & POLLIN) {
|
||||
- timeout = uevent_burst(&start_time, events + 1) ? 1 : 0;
|
||||
- dev = udev_monitor_receive_device(monitor);
|
||||
- if (!dev) {
|
||||
- condlog(0, "failed getting udev device");
|
||||
- continue;
|
||||
- }
|
||||
- uev = uevent_from_udev_device(dev);
|
||||
- if (!uev)
|
||||
- continue;
|
||||
- list_add_tail(&uev->node, &uevlisten_tmp);
|
||||
- events++;
|
||||
- continue;
|
||||
- }
|
||||
+ fdcount = poll(&ev_poll, 1, -1);
|
||||
if (fdcount < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
- condlog(0, "error receiving "
|
||||
- "uevent message: %m");
|
||||
+ condlog(0, "error receiving uevent message: %m");
|
||||
err = -errno;
|
||||
break;
|
||||
}
|
||||
- if (!list_empty(&uevlisten_tmp)) {
|
||||
- /*
|
||||
- * Queue uevents and poke service pthread.
|
||||
- */
|
||||
- condlog(3, "Forwarding %d uevents", events);
|
||||
- pthread_mutex_lock(uevq_lockp);
|
||||
- list_splice_tail_init(&uevlisten_tmp, &uevq);
|
||||
- pthread_cond_signal(uev_condp);
|
||||
- pthread_mutex_unlock(uevq_lockp);
|
||||
- events = 0;
|
||||
- }
|
||||
- gettimeofday(&start_time, NULL);
|
||||
- timeout = 30;
|
||||
+ events = uevent_receive_events(fd, &uevlisten_tmp, monitor);
|
||||
+ if (events <= 0)
|
||||
+ continue;
|
||||
+
|
||||
+ condlog(4, "Forwarding %d uevents", events);
|
||||
+ pthread_mutex_lock(uevq_lockp);
|
||||
+ list_splice_tail_init(&uevlisten_tmp, &uevq);
|
||||
+ pthread_cond_signal(uev_condp);
|
||||
+ pthread_mutex_unlock(uevq_lockp);
|
||||
}
|
||||
pthread_cleanup_pop(1);
|
||||
pthread_cleanup_pop(1);
|
||||
@ -0,0 +1,356 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Wed, 30 Mar 2022 00:06:15 +0200
|
||||
Subject: [PATCH] libmultipath: uevent: use struct to pass parameters around
|
||||
|
||||
libmultipath: uevent_dispatch(): just grab config once
|
||||
|
||||
Introduce struct uevent_filter_state to pass parameters around.
|
||||
This simplifies the function signatures and allows for easy extension
|
||||
later.
|
||||
|
||||
Instead of grabbing multipath config repeatedly, do it just
|
||||
once per dispatcher iteration, and pass the pointer around in
|
||||
struct uevent_filter_state. We shouldn't use different configs
|
||||
for different paths in a single iteration, anyway.
|
||||
|
||||
Also, properly constify get_uid_attribute_by_attrs() and
|
||||
pp->uid_attribute.
|
||||
|
||||
Signed-off-by: Martin Wilck <mwilck@suse.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/config.c | 6 +--
|
||||
libmultipath/config.h | 4 +-
|
||||
libmultipath/discovery.c | 2 +-
|
||||
libmultipath/structs.h | 2 +-
|
||||
libmultipath/uevent.c | 110 +++++++++++++++++----------------------
|
||||
libmultipath/uevent.h | 3 +-
|
||||
tests/uevent.c | 2 +-
|
||||
7 files changed, 58 insertions(+), 71 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/config.c b/libmultipath/config.c
|
||||
index f31200a3..bd8296bf 100644
|
||||
--- a/libmultipath/config.c
|
||||
+++ b/libmultipath/config.c
|
||||
@@ -1112,10 +1112,10 @@ out:
|
||||
return 1;
|
||||
}
|
||||
|
||||
-char *get_uid_attribute_by_attrs(struct config *conf,
|
||||
- const char *path_dev)
|
||||
+const char *get_uid_attribute_by_attrs(const struct config *conf,
|
||||
+ const char *path_dev)
|
||||
{
|
||||
- vector uid_attrs = &conf->uid_attrs;
|
||||
+ const struct _vector *uid_attrs = &conf->uid_attrs;
|
||||
int j;
|
||||
char *att, *col;
|
||||
|
||||
diff --git a/libmultipath/config.h b/libmultipath/config.h
|
||||
index 5807ac68..d3abbaea 100644
|
||||
--- a/libmultipath/config.h
|
||||
+++ b/libmultipath/config.h
|
||||
@@ -329,7 +329,7 @@ void libmp_put_multipath_config(void *);
|
||||
void put_multipath_config(void *);
|
||||
|
||||
int parse_uid_attrs(char *uid_attrs, struct config *conf);
|
||||
-char *get_uid_attribute_by_attrs(struct config *conf,
|
||||
- const char *path_dev);
|
||||
+const char *get_uid_attribute_by_attrs(const struct config *conf,
|
||||
+ const char *path_dev);
|
||||
|
||||
#endif
|
||||
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
|
||||
index 22d114b3..186423e0 100644
|
||||
--- a/libmultipath/discovery.c
|
||||
+++ b/libmultipath/discovery.c
|
||||
@@ -2071,7 +2071,7 @@ fix_broken_nvme_wwid(struct path *pp, const char *value, size_t size)
|
||||
}
|
||||
|
||||
static int
|
||||
-get_udev_uid(struct path * pp, char *uid_attribute, struct udev_device *udev)
|
||||
+get_udev_uid(struct path * pp, const char *uid_attribute, struct udev_device *udev)
|
||||
{
|
||||
ssize_t len;
|
||||
const char *value;
|
||||
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
|
||||
index 2f69e831..423c8b78 100644
|
||||
--- a/libmultipath/structs.h
|
||||
+++ b/libmultipath/structs.h
|
||||
@@ -350,7 +350,7 @@ struct path {
|
||||
int detect_prio;
|
||||
int detect_checker;
|
||||
int tpgs;
|
||||
- char * uid_attribute;
|
||||
+ const char *uid_attribute;
|
||||
char * getuid;
|
||||
struct prio prio;
|
||||
struct checker checker;
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index c3984fef..4ef7181c 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -65,6 +65,12 @@ static uev_trigger *my_uev_trigger;
|
||||
static void *my_trigger_data;
|
||||
static int servicing_uev;
|
||||
|
||||
+struct uevent_filter_state {
|
||||
+ struct list_head uevq;
|
||||
+ struct list_head *old_tail;
|
||||
+ struct config *conf;
|
||||
+};
|
||||
+
|
||||
int is_uevent_busy(void)
|
||||
{
|
||||
int empty;
|
||||
@@ -160,40 +166,24 @@ int uevent_get_env_positive_int(const struct uevent *uev,
|
||||
}
|
||||
|
||||
void
|
||||
-uevent_get_wwid(struct uevent *uev)
|
||||
+uevent_get_wwid(struct uevent *uev, const struct config *conf)
|
||||
{
|
||||
- char *uid_attribute;
|
||||
+ const char *uid_attribute;
|
||||
const char *val;
|
||||
- struct config * conf;
|
||||
|
||||
- conf = get_multipath_config();
|
||||
- pthread_cleanup_push(put_multipath_config, conf);
|
||||
uid_attribute = get_uid_attribute_by_attrs(conf, uev->kernel);
|
||||
- pthread_cleanup_pop(1);
|
||||
-
|
||||
val = uevent_get_env_var(uev, uid_attribute);
|
||||
if (val)
|
||||
uev->wwid = val;
|
||||
}
|
||||
|
||||
-static bool uevent_need_merge(void)
|
||||
+static bool uevent_need_merge(const struct config *conf)
|
||||
{
|
||||
- struct config * conf;
|
||||
- bool need_merge = false;
|
||||
-
|
||||
- conf = get_multipath_config();
|
||||
- if (VECTOR_SIZE(&conf->uid_attrs) > 0)
|
||||
- need_merge = true;
|
||||
- put_multipath_config(conf);
|
||||
-
|
||||
- return need_merge;
|
||||
+ return VECTOR_SIZE(&conf->uid_attrs) > 0;
|
||||
}
|
||||
|
||||
-static bool uevent_can_discard(struct uevent *uev)
|
||||
+static bool uevent_can_discard(struct uevent *uev, const struct config *conf)
|
||||
{
|
||||
- int invalid = 0;
|
||||
- struct config * conf;
|
||||
-
|
||||
/*
|
||||
* do not filter dm devices by devnode
|
||||
*/
|
||||
@@ -202,15 +192,10 @@ static bool uevent_can_discard(struct uevent *uev)
|
||||
/*
|
||||
* filter paths devices by devnode
|
||||
*/
|
||||
- conf = get_multipath_config();
|
||||
- pthread_cleanup_push(put_multipath_config, conf);
|
||||
if (filter_devnode(conf->blist_devnode, conf->elist_devnode,
|
||||
uev->kernel) > 0)
|
||||
- invalid = 1;
|
||||
- pthread_cleanup_pop(1);
|
||||
-
|
||||
- if (invalid)
|
||||
return true;
|
||||
+
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -354,29 +339,28 @@ static void uevent_delete_simple(struct uevent *to_delete)
|
||||
free(to_delete);
|
||||
}
|
||||
|
||||
-static void
|
||||
-uevent_prepare(struct list_head *tmpq, const struct list_head *stop)
|
||||
+static void uevent_prepare(struct uevent_filter_state *st)
|
||||
{
|
||||
struct uevent *uev, *tmp;
|
||||
|
||||
- list_for_some_entry_reverse_safe(uev, tmp, tmpq, stop, node) {
|
||||
- if (uevent_can_discard(uev)) {
|
||||
+ list_for_some_entry_reverse_safe(uev, tmp, &st->uevq, st->old_tail, node) {
|
||||
+ if (uevent_can_discard(uev, st->conf)) {
|
||||
uevent_delete_simple(uev);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strncmp(uev->kernel, "dm-", 3) &&
|
||||
- uevent_need_merge())
|
||||
- uevent_get_wwid(uev);
|
||||
+ uevent_need_merge(st->conf))
|
||||
+ uevent_get_wwid(uev, st->conf);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
-uevent_filter(struct uevent *later, struct list_head *tmpq, struct list_head **stop)
|
||||
+uevent_filter(struct uevent *later, struct uevent_filter_state *st)
|
||||
{
|
||||
struct uevent *earlier, *tmp;
|
||||
|
||||
- list_for_some_entry_reverse_safe(earlier, tmp, &later->node, tmpq, node) {
|
||||
+ list_for_some_entry_reverse_safe(earlier, tmp, &later->node, &st->uevq, node) {
|
||||
/*
|
||||
* filter unnessary earlier uevents
|
||||
* by the later uevent
|
||||
@@ -386,17 +370,16 @@ uevent_filter(struct uevent *later, struct list_head *tmpq, struct list_head **s
|
||||
earlier->kernel, earlier->action,
|
||||
later->kernel, later->action);
|
||||
|
||||
- uevent_delete_from_list(earlier, &tmp, stop);
|
||||
+ uevent_delete_from_list(earlier, &tmp, &st->old_tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-static void
|
||||
-uevent_merge(struct uevent *later, struct list_head *tmpq, struct list_head **stop)
|
||||
+static void uevent_merge(struct uevent *later, struct uevent_filter_state *st)
|
||||
{
|
||||
struct uevent *earlier, *tmp;
|
||||
|
||||
- list_for_some_entry_reverse_safe(earlier, tmp, &later->node, tmpq, node) {
|
||||
+ list_for_some_entry_reverse_safe(earlier, tmp, &later->node, &st->uevq, node) {
|
||||
if (merge_need_stop(earlier, later))
|
||||
break;
|
||||
/*
|
||||
@@ -408,8 +391,8 @@ uevent_merge(struct uevent *later, struct list_head *tmpq, struct list_head **st
|
||||
later->action, later->kernel, later->wwid);
|
||||
|
||||
/* See comment in uevent_delete_from_list() */
|
||||
- if (&earlier->node == *stop)
|
||||
- *stop = earlier->node.prev;
|
||||
+ if (&earlier->node == st->old_tail)
|
||||
+ st->old_tail = earlier->node.prev;
|
||||
|
||||
list_move(&earlier->node, &later->merge_node);
|
||||
list_splice_init(&earlier->merge_node,
|
||||
@@ -418,16 +401,15 @@ uevent_merge(struct uevent *later, struct list_head *tmpq, struct list_head **st
|
||||
}
|
||||
}
|
||||
|
||||
-static void
|
||||
-merge_uevq(struct list_head *tmpq, struct list_head *stop)
|
||||
+static void merge_uevq(struct uevent_filter_state *st)
|
||||
{
|
||||
struct uevent *later;
|
||||
|
||||
- uevent_prepare(tmpq, stop);
|
||||
- list_for_some_entry_reverse(later, tmpq, stop, node) {
|
||||
- uevent_filter(later, tmpq, &stop);
|
||||
- if(uevent_need_merge())
|
||||
- uevent_merge(later, tmpq, &stop);
|
||||
+ uevent_prepare(st);
|
||||
+ list_for_some_entry_reverse(later, &st->uevq, st->old_tail, node) {
|
||||
+ uevent_filter(later, st);
|
||||
+ if(uevent_need_merge(st->conf))
|
||||
+ uevent_merge(later, st);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -479,41 +461,45 @@ static void cleanup_global_uevq(void *arg __attribute__((unused)))
|
||||
int uevent_dispatch(int (*uev_trigger)(struct uevent *, void * trigger_data),
|
||||
void * trigger_data)
|
||||
{
|
||||
- LIST_HEAD(uevq_work);
|
||||
+ struct uevent_filter_state filter_state;
|
||||
|
||||
+ INIT_LIST_HEAD(&filter_state.uevq);
|
||||
my_uev_trigger = uev_trigger;
|
||||
my_trigger_data = trigger_data;
|
||||
|
||||
mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||
|
||||
- pthread_cleanup_push(cleanup_uevq, &uevq_work);
|
||||
+ pthread_cleanup_push(cleanup_uevq, &filter_state.uevq);
|
||||
while (1) {
|
||||
- struct list_head *stop;
|
||||
-
|
||||
pthread_cleanup_push(cleanup_mutex, uevq_lockp);
|
||||
pthread_mutex_lock(uevq_lockp);
|
||||
|
||||
- servicing_uev = !list_empty(&uevq_work);
|
||||
+ servicing_uev = !list_empty(&filter_state.uevq);
|
||||
|
||||
- while (list_empty(&uevq_work) && list_empty(&uevq))
|
||||
+ while (list_empty(&filter_state.uevq) && list_empty(&uevq))
|
||||
pthread_cond_wait(uev_condp, uevq_lockp);
|
||||
|
||||
servicing_uev = 1;
|
||||
/*
|
||||
- * "stop" is the list element towards which merge_uevq()
|
||||
- * will iterate: the last element of uevq_work before
|
||||
- * appending new uevents. If uveq_is empty, uevq_work.prev
|
||||
- * equals &uevq_work, which is what we need.
|
||||
+ * "old_tail" is the list element towards which merge_uevq()
|
||||
+ * will iterate: the last element of uevq before
|
||||
+ * appending new uevents. If uveq empty, uevq.prev
|
||||
+ * equals &uevq, which is what we need.
|
||||
*/
|
||||
- stop = uevq_work.prev;
|
||||
- list_splice_tail_init(&uevq, &uevq_work);
|
||||
+ filter_state.old_tail = filter_state.uevq.prev;
|
||||
+ list_splice_tail_init(&uevq, &filter_state.uevq);
|
||||
pthread_cleanup_pop(1);
|
||||
|
||||
if (!my_uev_trigger)
|
||||
break;
|
||||
|
||||
- merge_uevq(&uevq_work, stop);
|
||||
- service_uevq(&uevq_work);
|
||||
+
|
||||
+ pthread_cleanup_push(put_multipath_config, filter_state.conf);
|
||||
+ filter_state.conf = get_multipath_config();
|
||||
+ merge_uevq(&filter_state);
|
||||
+ pthread_cleanup_pop(1);
|
||||
+
|
||||
+ service_uevq(&filter_state.uevq);
|
||||
}
|
||||
pthread_cleanup_pop(1);
|
||||
condlog(3, "Terminating uev service queue");
|
||||
diff --git a/libmultipath/uevent.h b/libmultipath/uevent.h
|
||||
index 61ca1b56..53a7ca29 100644
|
||||
--- a/libmultipath/uevent.h
|
||||
+++ b/libmultipath/uevent.h
|
||||
@@ -10,6 +10,7 @@
|
||||
#define OBJECT_SIZE 512
|
||||
|
||||
struct udev;
|
||||
+struct config;
|
||||
|
||||
struct uevent {
|
||||
struct list_head node;
|
||||
@@ -31,7 +32,7 @@ int uevent_listen(struct udev *udev);
|
||||
int uevent_dispatch(int (*store_uev)(struct uevent *, void * trigger_data),
|
||||
void * trigger_data);
|
||||
bool uevent_is_mpath(const struct uevent *uev);
|
||||
-void uevent_get_wwid(struct uevent *uev);
|
||||
+void uevent_get_wwid(struct uevent *uev, const struct config *conf);
|
||||
|
||||
int uevent_get_env_positive_int(const struct uevent *uev,
|
||||
const char *attr);
|
||||
diff --git a/tests/uevent.c b/tests/uevent.c
|
||||
index 648ff268..e237a208 100644
|
||||
--- a/tests/uevent.c
|
||||
+++ b/tests/uevent.c
|
||||
@@ -111,7 +111,7 @@ static void test_uid_attrs(void **state)
|
||||
static void test_wwid(void **state)
|
||||
{
|
||||
struct uevent *uev = *state;
|
||||
- uevent_get_wwid(uev);
|
||||
+ uevent_get_wwid(uev, &conf);
|
||||
|
||||
assert_string_equal(uev->wwid, WWID);
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Martin Wilck <mwilck@suse.com>
|
||||
Date: Fri, 5 Jan 2024 18:51:02 +0100
|
||||
Subject: [PATCH] libmultipath: is_uevent_busy(): check servicing_uev under
|
||||
lock
|
||||
|
||||
This fixes a coverity-reported defect (413384 Data race condition).
|
||||
Indeed, we always set servicing_uev with the lock held, so it makes
|
||||
sense to read it with the lock held, too.
|
||||
|
||||
Signed-off-by: Martin Wilck <mwilck@suse.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/uevent.c | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index 4ef7181c..8cd928a9 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -73,12 +73,13 @@ struct uevent_filter_state {
|
||||
|
||||
int is_uevent_busy(void)
|
||||
{
|
||||
- int empty;
|
||||
+ int empty, servicing;
|
||||
|
||||
pthread_mutex_lock(uevq_lockp);
|
||||
empty = list_empty(&uevq);
|
||||
+ servicing = servicing_uev;
|
||||
pthread_mutex_unlock(uevq_lockp);
|
||||
- return (!empty || servicing_uev);
|
||||
+ return (!empty || servicing);
|
||||
}
|
||||
|
||||
struct uevent * alloc_uevent (void)
|
||||
@ -0,0 +1,63 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Date: Wed, 21 Jan 2026 16:03:12 -0500
|
||||
Subject: [PATCH] multipathd: make "multipathd show status" busy checker better
|
||||
|
||||
while uevent_listen() was grabbing new uevents, "multipathd show status"
|
||||
would still show show busy as "False". Add a check there, to make catch
|
||||
multipathd's uevent processing earlier. Also, access servicing_uev (as
|
||||
well as the new variable, adding_uev) atomically, just to make sure that
|
||||
the compiler doesn't do stupid things trying to optimize them.
|
||||
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Reviewed-by: Martin Wilck <mwilck@suse.com>
|
||||
---
|
||||
libmultipath/uevent.c | 9 +++++++--
|
||||
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c
|
||||
index 8cd928a9..c230e963 100644
|
||||
--- a/libmultipath/uevent.c
|
||||
+++ b/libmultipath/uevent.c
|
||||
@@ -64,6 +64,7 @@ static pthread_cond_t *uev_condp = &uev_cond;
|
||||
static uev_trigger *my_uev_trigger;
|
||||
static void *my_trigger_data;
|
||||
static int servicing_uev;
|
||||
+static int adding_uev; /* uatomic access only */
|
||||
|
||||
struct uevent_filter_state {
|
||||
struct list_head uevq;
|
||||
@@ -73,13 +74,14 @@ struct uevent_filter_state {
|
||||
|
||||
int is_uevent_busy(void)
|
||||
{
|
||||
- int empty, servicing;
|
||||
+ int empty, servicing, adding;
|
||||
|
||||
pthread_mutex_lock(uevq_lockp);
|
||||
empty = list_empty(&uevq);
|
||||
servicing = servicing_uev;
|
||||
+ adding = uatomic_read(&adding_uev);
|
||||
pthread_mutex_unlock(uevq_lockp);
|
||||
- return (!empty || servicing);
|
||||
+ return (!empty || servicing || adding);
|
||||
}
|
||||
|
||||
struct uevent * alloc_uevent (void)
|
||||
@@ -663,6 +665,7 @@ int uevent_listen(struct udev *udev)
|
||||
int fdcount, events;
|
||||
struct pollfd ev_poll = { .fd = fd, .events = POLLIN, };
|
||||
|
||||
+ uatomic_set(&adding_uev, 0);
|
||||
fdcount = poll(&ev_poll, 1, -1);
|
||||
if (fdcount < 0) {
|
||||
if (errno == EINTR)
|
||||
@@ -672,6 +675,8 @@ int uevent_listen(struct udev *udev)
|
||||
err = -errno;
|
||||
break;
|
||||
}
|
||||
+ uatomic_set(&adding_uev, 1);
|
||||
+
|
||||
events = uevent_receive_events(fd, &uevlisten_tmp, monitor);
|
||||
if (events <= 0)
|
||||
continue;
|
||||
@ -0,0 +1,279 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Brian Bunker <brian@purestorage.com>
|
||||
Date: Fri, 9 Jan 2026 16:50:43 -0800
|
||||
Subject: [PATCH] libmultipath: add purge_disconnected configuration option
|
||||
|
||||
Add a new configuration option 'purge_disconnected' that can be set
|
||||
per multipath device, hardware entry, or globally. This option will
|
||||
be used to control whether multipathd should automatically remove
|
||||
paths that are in a disconnected state.
|
||||
|
||||
The option is disabled by default (PURGE_DISCONNECTED_OFF).
|
||||
|
||||
This patch only adds the configuration infrastructure. The actual
|
||||
purge functionality will be implemented in a subsequent patch.
|
||||
|
||||
Signed-off-by: Brian Bunker <brian@purestorage.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Reviewed-by: Martin Wilck <mwilck@suse.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/config.c | 2 ++
|
||||
libmultipath/config.h | 3 +++
|
||||
libmultipath/configure.c | 1 +
|
||||
libmultipath/defaults.h | 1 +
|
||||
libmultipath/dict.c | 14 ++++++++++++++
|
||||
libmultipath/propsel.c | 16 ++++++++++++++++
|
||||
libmultipath/propsel.h | 1 +
|
||||
libmultipath/structs.h | 12 ++++++++++++
|
||||
multipath/multipath.conf.5 | 22 ++++++++++++++++++++++
|
||||
9 files changed, 72 insertions(+)
|
||||
|
||||
diff --git a/libmultipath/config.c b/libmultipath/config.c
|
||||
index bd8296bf..2aa69cb4 100644
|
||||
--- a/libmultipath/config.c
|
||||
+++ b/libmultipath/config.c
|
||||
@@ -476,6 +476,7 @@ merge_hwe (struct hwentry * dst, struct hwentry * src)
|
||||
merge_num(marginal_path_err_rate_threshold);
|
||||
merge_num(marginal_path_err_recheck_gap_time);
|
||||
merge_num(marginal_path_double_failed_time);
|
||||
+ merge_num(purge_disconnected);
|
||||
|
||||
snprintf(id, sizeof(id), "%s/%s", dst->vendor, dst->product);
|
||||
reconcile_features_with_options(id, &dst->features,
|
||||
@@ -524,6 +525,7 @@ merge_mpe(struct mpentry *dst, struct mpentry *src)
|
||||
merge_num(skip_kpartx);
|
||||
merge_num(max_sectors_kb);
|
||||
merge_num(ghost_delay);
|
||||
+ merge_num(purge_disconnected);
|
||||
merge_num(uid);
|
||||
merge_num(gid);
|
||||
merge_num(mode);
|
||||
diff --git a/libmultipath/config.h b/libmultipath/config.h
|
||||
index d3abbaea..e0b1e4c8 100644
|
||||
--- a/libmultipath/config.h
|
||||
+++ b/libmultipath/config.h
|
||||
@@ -87,6 +87,7 @@ struct hwentry {
|
||||
int marginal_path_err_rate_threshold;
|
||||
int marginal_path_err_recheck_gap_time;
|
||||
int marginal_path_double_failed_time;
|
||||
+ int purge_disconnected;
|
||||
int skip_kpartx;
|
||||
int max_sectors_kb;
|
||||
int ghost_delay;
|
||||
@@ -130,6 +131,7 @@ struct mpentry {
|
||||
int marginal_path_err_rate_threshold;
|
||||
int marginal_path_err_recheck_gap_time;
|
||||
int marginal_path_double_failed_time;
|
||||
+ int purge_disconnected;
|
||||
int skip_kpartx;
|
||||
int max_sectors_kb;
|
||||
int ghost_delay;
|
||||
@@ -186,6 +188,7 @@ struct config {
|
||||
int marginal_path_err_rate_threshold;
|
||||
int marginal_path_err_recheck_gap_time;
|
||||
int marginal_path_double_failed_time;
|
||||
+ int purge_disconnected;
|
||||
int uxsock_timeout;
|
||||
int strict_timing;
|
||||
int retrigger_tries;
|
||||
diff --git a/libmultipath/configure.c b/libmultipath/configure.c
|
||||
index 71acb968..c1b6a077 100644
|
||||
--- a/libmultipath/configure.c
|
||||
+++ b/libmultipath/configure.c
|
||||
@@ -389,6 +389,7 @@ int setup_map(struct multipath *mpp, char **params, struct vectors *vecs)
|
||||
select_max_sectors_kb(conf, mpp);
|
||||
select_ghost_delay(conf, mpp);
|
||||
select_flush_on_last_del(conf, mpp);
|
||||
+ select_purge_disconnected(conf, mpp);
|
||||
|
||||
sysfs_set_scsi_tmo(conf, mpp);
|
||||
marginal_pathgroups = conf->marginal_pathgroups;
|
||||
diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h
|
||||
index 1ab5b99c..caf4ccce 100644
|
||||
--- a/libmultipath/defaults.h
|
||||
+++ b/libmultipath/defaults.h
|
||||
@@ -55,6 +55,7 @@
|
||||
#define DEFAULT_ALL_TG_PT ALL_TG_PT_OFF
|
||||
#define DEFAULT_RECHECK_WWID RECHECK_WWID_OFF
|
||||
#define DEFAULT_AUTO_RESIZE AUTO_RESIZE_NEVER
|
||||
+#define DEFAULT_PURGE_DISCONNECTED PURGE_DISCONNECTED_OFF
|
||||
/* Enable no foreign libraries by default */
|
||||
#define DEFAULT_ENABLE_FOREIGN "NONE"
|
||||
|
||||
diff --git a/libmultipath/dict.c b/libmultipath/dict.c
|
||||
index 3c011ece..421070df 100644
|
||||
--- a/libmultipath/dict.c
|
||||
+++ b/libmultipath/dict.c
|
||||
@@ -979,6 +979,16 @@ static int snprint_def_disable_changed_wwids(struct config *conf,
|
||||
return print_ignored(buff);
|
||||
}
|
||||
|
||||
+declare_def_handler(purge_disconnected, set_yes_no_undef)
|
||||
+declare_def_snprint_defint(purge_disconnected, print_yes_no_undef,
|
||||
+ DEFAULT_PURGE_DISCONNECTED)
|
||||
+declare_ovr_handler(purge_disconnected, set_yes_no_undef)
|
||||
+declare_ovr_snprint(purge_disconnected, print_yes_no_undef)
|
||||
+declare_hw_handler(purge_disconnected, set_yes_no_undef)
|
||||
+declare_hw_snprint(purge_disconnected, print_yes_no_undef)
|
||||
+declare_mp_handler(purge_disconnected, set_yes_no_undef)
|
||||
+declare_mp_snprint(purge_disconnected, print_yes_no_undef)
|
||||
+
|
||||
declare_def_range_handler(remove_retries, 0, INT_MAX)
|
||||
declare_def_snprint(remove_retries, print_int)
|
||||
|
||||
@@ -2289,6 +2299,7 @@ init_keywords(vector keywords)
|
||||
install_keyword("retrigger_delay", &def_retrigger_delay_handler, &snprint_def_retrigger_delay);
|
||||
install_keyword("missing_uev_wait_timeout", &def_uev_wait_timeout_handler, &snprint_def_uev_wait_timeout);
|
||||
install_keyword("skip_kpartx", &def_skip_kpartx_handler, &snprint_def_skip_kpartx);
|
||||
+ install_keyword("purge_disconnected", &def_purge_disconnected_handler, &snprint_def_purge_disconnected);
|
||||
install_keyword("disable_changed_wwids", &def_disable_changed_wwids_handler, &snprint_def_disable_changed_wwids);
|
||||
install_keyword("remove_retries", &def_remove_retries_handler, &snprint_def_remove_retries);
|
||||
install_keyword("max_sectors_kb", &def_max_sectors_kb_handler, &snprint_def_max_sectors_kb);
|
||||
@@ -2386,6 +2397,7 @@ init_keywords(vector keywords)
|
||||
install_keyword("marginal_path_err_recheck_gap_time", &hw_marginal_path_err_recheck_gap_time_handler, &snprint_hw_marginal_path_err_recheck_gap_time);
|
||||
install_keyword("marginal_path_double_failed_time", &hw_marginal_path_double_failed_time_handler, &snprint_hw_marginal_path_double_failed_time);
|
||||
install_keyword("skip_kpartx", &hw_skip_kpartx_handler, &snprint_hw_skip_kpartx);
|
||||
+ install_keyword("purge_disconnected", &hw_purge_disconnected_handler, &snprint_hw_purge_disconnected);
|
||||
install_keyword("max_sectors_kb", &hw_max_sectors_kb_handler, &snprint_hw_max_sectors_kb);
|
||||
install_keyword("ghost_delay", &hw_ghost_delay_handler, &snprint_hw_ghost_delay);
|
||||
install_keyword("all_tg_pt", &hw_all_tg_pt_handler, &snprint_hw_all_tg_pt);
|
||||
@@ -2429,6 +2441,7 @@ init_keywords(vector keywords)
|
||||
install_keyword("marginal_path_double_failed_time", &ovr_marginal_path_double_failed_time_handler, &snprint_ovr_marginal_path_double_failed_time);
|
||||
|
||||
install_keyword("skip_kpartx", &ovr_skip_kpartx_handler, &snprint_ovr_skip_kpartx);
|
||||
+ install_keyword("purge_disconnected", &ovr_purge_disconnected_handler, &snprint_ovr_purge_disconnected);
|
||||
install_keyword("max_sectors_kb", &ovr_max_sectors_kb_handler, &snprint_ovr_max_sectors_kb);
|
||||
install_keyword("ghost_delay", &ovr_ghost_delay_handler, &snprint_ovr_ghost_delay);
|
||||
install_keyword("all_tg_pt", &ovr_all_tg_pt_handler, &snprint_ovr_all_tg_pt);
|
||||
@@ -2474,6 +2487,7 @@ init_keywords(vector keywords)
|
||||
install_keyword("marginal_path_err_recheck_gap_time", &mp_marginal_path_err_recheck_gap_time_handler, &snprint_mp_marginal_path_err_recheck_gap_time);
|
||||
install_keyword("marginal_path_double_failed_time", &mp_marginal_path_double_failed_time_handler, &snprint_mp_marginal_path_double_failed_time);
|
||||
install_keyword("skip_kpartx", &mp_skip_kpartx_handler, &snprint_mp_skip_kpartx);
|
||||
+ install_keyword("purge_disconnected", &mp_purge_disconnected_handler, &snprint_mp_purge_disconnected);
|
||||
install_keyword("max_sectors_kb", &mp_max_sectors_kb_handler, &snprint_mp_max_sectors_kb);
|
||||
install_keyword("ghost_delay", &mp_ghost_delay_handler, &snprint_mp_ghost_delay);
|
||||
install_sublevel_end();
|
||||
diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c
|
||||
index 0b6e22c1..c45488f7 100644
|
||||
--- a/libmultipath/propsel.c
|
||||
+++ b/libmultipath/propsel.c
|
||||
@@ -1306,6 +1306,22 @@ out:
|
||||
return 0;
|
||||
}
|
||||
|
||||
+int select_purge_disconnected(struct config *conf, struct multipath *mp)
|
||||
+{
|
||||
+ const char *origin;
|
||||
+
|
||||
+ mp_set_mpe(purge_disconnected);
|
||||
+ mp_set_ovr(purge_disconnected);
|
||||
+ mp_set_hwe(purge_disconnected);
|
||||
+ mp_set_conf(purge_disconnected);
|
||||
+ mp_set_default(purge_disconnected, DEFAULT_PURGE_DISCONNECTED);
|
||||
+out:
|
||||
+ condlog(3, "%s: purge_disconnected = %s %s", mp->alias,
|
||||
+ (mp->purge_disconnected == PURGE_DISCONNECTED_ON) ? "yes" : "no",
|
||||
+ origin);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
int select_max_sectors_kb(struct config *conf, struct multipath * mp)
|
||||
{
|
||||
const char *origin;
|
||||
diff --git a/libmultipath/propsel.h b/libmultipath/propsel.h
|
||||
index 152ca44c..325c1ee3 100644
|
||||
--- a/libmultipath/propsel.h
|
||||
+++ b/libmultipath/propsel.h
|
||||
@@ -35,6 +35,7 @@ int select_marginal_path_err_rate_threshold(struct config *conf, struct multipat
|
||||
int select_marginal_path_err_recheck_gap_time(struct config *conf, struct multipath *mp);
|
||||
int select_marginal_path_double_failed_time(struct config *conf, struct multipath *mp);
|
||||
int select_ghost_delay(struct config *conf, struct multipath * mp);
|
||||
+int select_purge_disconnected(struct config *conf, struct multipath *mp);
|
||||
void reconcile_features_with_options(const char *id, char **features,
|
||||
int* no_path_retry,
|
||||
int *retain_hwhandler);
|
||||
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
|
||||
index 423c8b78..e1969b95 100644
|
||||
--- a/libmultipath/structs.h
|
||||
+++ b/libmultipath/structs.h
|
||||
@@ -174,6 +174,17 @@ enum auto_resize_state {
|
||||
AUTO_RESIZE_GROW_SHRINK,
|
||||
};
|
||||
|
||||
+/*
|
||||
+ * purge_disconnected configuration option (per multipath device)
|
||||
+ * Controls whether paths that become disconnected at the storage target
|
||||
+ * should be automatically removed from the system via sysfs.
|
||||
+ */
|
||||
+enum purge_disconnected_states {
|
||||
+ PURGE_DISCONNECTED_UNDEF = YNU_UNDEF,
|
||||
+ PURGE_DISCONNECTED_OFF = YNU_NO, /* Don't purge */
|
||||
+ PURGE_DISCONNECTED_ON = YNU_YES, /* Purge disconnected paths */
|
||||
+};
|
||||
+
|
||||
#define PROTOCOL_UNSET -1
|
||||
|
||||
enum scsi_protocol {
|
||||
@@ -427,6 +438,7 @@ struct multipath {
|
||||
int ghost_delay;
|
||||
int ghost_delay_tick;
|
||||
int queue_mode;
|
||||
+ int purge_disconnected;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
mode_t mode;
|
||||
diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5
|
||||
index 8684bd63..529bd98e 100644
|
||||
--- a/multipath/multipath.conf.5
|
||||
+++ b/multipath/multipath.conf.5
|
||||
@@ -1265,6 +1265,22 @@ The default is: \fBno\fR
|
||||
.
|
||||
.
|
||||
.TP
|
||||
+.B purge_disconnected
|
||||
+If set to
|
||||
+.I yes
|
||||
+, multipathd will automatically remove devices that are in a disconnected state.
|
||||
+A path is considered disconnected when the TUR (Test Unit Ready) path checker
|
||||
+receives the SCSI sense code "LOGICAL UNIT NOT SUPPORTED" (sense key 0x5,
|
||||
+ASC/ASCQ 0x25/0x00). This typically indicates that the LUN has been unmapped
|
||||
+or is no longer presented by the storage array. This option helps clean up
|
||||
+stale device entries that would otherwise remain in the system.
|
||||
+.RS
|
||||
+.TP
|
||||
+The default is: \fBno\fR
|
||||
+.RE
|
||||
+.
|
||||
+.
|
||||
+.TP
|
||||
.B disable_changed_wwids
|
||||
This option is deprecated and ignored. If the WWID of a path suddenly changes,
|
||||
multipathd handles it as if it was removed and then added again.
|
||||
@@ -1540,6 +1556,8 @@ section:
|
||||
.TP
|
||||
.B skip_kpartx
|
||||
.TP
|
||||
+.B purge_disconnected
|
||||
+.TP
|
||||
.B max_sectors_kb
|
||||
.TP
|
||||
.B ghost_delay
|
||||
@@ -1715,6 +1733,8 @@ section:
|
||||
.TP
|
||||
.B skip_kpartx
|
||||
.TP
|
||||
+.B purge_disconnected
|
||||
+.TP
|
||||
.B max_sectors_kb
|
||||
.TP
|
||||
.B ghost_delay
|
||||
@@ -1801,6 +1821,8 @@ the values are taken from the \fIdevices\fR or \fIdefaults\fR sections:
|
||||
.TP
|
||||
.B skip_kpartx
|
||||
.TP
|
||||
+.B purge_disconnected
|
||||
+.TP
|
||||
.B max_sectors_kb
|
||||
.TP
|
||||
.B ghost_delay
|
||||
@ -0,0 +1,752 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Brian Bunker <brian@purestorage.com>
|
||||
Date: Fri, 9 Jan 2026 16:50:43 -0800
|
||||
Subject: [PATCH] multipathd: implement purge functionality for disconnected
|
||||
paths
|
||||
|
||||
Implement automatic purging of paths that have been disconnected at the
|
||||
storage target (e.g., LUN unmapped). This builds on the purge_disconnected
|
||||
configuration option added in the previous patch.
|
||||
|
||||
This adds:
|
||||
- New PATH_DISCONNECTED checker state to signal disconnection
|
||||
- TUR checker support for detecting LUN NOT SUPPORTED (ASC/ASCQ 0x25/0x00)
|
||||
- Purge thread (purgeloop) that removes paths via sysfs delete attribute
|
||||
- State machine to track disconnection and delay purging
|
||||
- Conversion of PATH_DISCONNECTED to PATH_DOWN for normal processing
|
||||
|
||||
The purge thread runs independently and processes paths that have been
|
||||
marked for purging by the checker thread. Paths are only purged after
|
||||
remaining disconnected for delay_wait_checks intervals to avoid removing
|
||||
paths that are temporarily flapping.
|
||||
|
||||
Signed-off-by: Brian Bunker <brian@purestorage.com>
|
||||
Signed-off-by: Krishna Kant <krishna.kant@purestorage.com>
|
||||
Reviewed-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Reviewed-by: Martin Wilck <mwilck@suse.com>
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
---
|
||||
libmultipath/checkers.c | 2 +
|
||||
libmultipath/checkers.h | 15 +-
|
||||
libmultipath/checkers/tur.c | 10 ++
|
||||
libmultipath/discovery.c | 17 ++
|
||||
libmultipath/io_err_stat.c | 1 +
|
||||
libmultipath/print.c | 2 +
|
||||
libmultipath/structs.h | 14 ++
|
||||
multipathd/Makefile | 2 +-
|
||||
multipathd/main.c | 74 +++++++-
|
||||
multipathd/purge.c | 326 ++++++++++++++++++++++++++++++++++++
|
||||
multipathd/purge.h | 41 +++++
|
||||
11 files changed, 496 insertions(+), 8 deletions(-)
|
||||
create mode 100644 multipathd/purge.c
|
||||
create mode 100644 multipathd/purge.h
|
||||
|
||||
diff --git a/libmultipath/checkers.c b/libmultipath/checkers.c
|
||||
index 9eb3e261..e1b84df0 100644
|
||||
--- a/libmultipath/checkers.c
|
||||
+++ b/libmultipath/checkers.c
|
||||
@@ -39,6 +39,7 @@ static const char *checker_state_names[PATH_MAX_STATE] = {
|
||||
[PATH_TIMEOUT] = "timeout",
|
||||
[PATH_REMOVED] = "removed",
|
||||
[PATH_DELAYED] = "delayed",
|
||||
+ [PATH_DISCONNECTED] = "disconnected",
|
||||
};
|
||||
|
||||
static LIST_HEAD(checkers);
|
||||
@@ -343,6 +344,7 @@ static const char *generic_msg[CHECKER_GENERIC_MSGTABLE_SIZE] = {
|
||||
[CHECKER_MSGID_DOWN] = " reports path is down",
|
||||
[CHECKER_MSGID_GHOST] = " reports path is ghost",
|
||||
[CHECKER_MSGID_UNSUPPORTED] = " doesn't support this device",
|
||||
+ [CHECKER_MSGID_DISCONNECTED] = " no access to this device",
|
||||
};
|
||||
|
||||
const char *checker_message(const struct checker *c)
|
||||
diff --git a/libmultipath/checkers.h b/libmultipath/checkers.h
|
||||
index 2fd1d1c6..2f32f612 100644
|
||||
--- a/libmultipath/checkers.h
|
||||
+++ b/libmultipath/checkers.h
|
||||
@@ -66,6 +66,15 @@
|
||||
* delay_watch_checks checks, when it comes back up again, it will not
|
||||
* be marked as up until it has been up for delay_wait_checks checks.
|
||||
* During this time, it is marked as "delayed"
|
||||
+ *
|
||||
+ * PATH_DISCONNECTED is a special ephemeral state used to signal that a path
|
||||
+ * has been disconnected at the storage target (e.g., LUN unmapped). When a
|
||||
+ * checker returns PATH_DISCONNECTED:
|
||||
+ * 1. The path's pp->disconnected field is set to track purge state
|
||||
+ * 2. The state is immediately converted to PATH_DOWN for normal processing
|
||||
+ * 3. If purge_disconnected is enabled, the path will be removed via sysfs
|
||||
+ * This state should never be stored in pp->state or pp->chkrstate; it exists
|
||||
+ * only as a transient return value from checkers to trigger special handling.
|
||||
*/
|
||||
enum path_check_state {
|
||||
PATH_WILD = 0,
|
||||
@@ -78,6 +87,7 @@ enum path_check_state {
|
||||
PATH_TIMEOUT,
|
||||
PATH_REMOVED,
|
||||
PATH_DELAYED,
|
||||
+ PATH_DISCONNECTED, /* Ephemeral: mapped to PATH_DOWN */
|
||||
PATH_MAX_STATE
|
||||
};
|
||||
|
||||
@@ -113,9 +123,10 @@ enum {
|
||||
CHECKER_MSGID_DOWN,
|
||||
CHECKER_MSGID_GHOST,
|
||||
CHECKER_MSGID_UNSUPPORTED,
|
||||
+ CHECKER_MSGID_DISCONNECTED,
|
||||
CHECKER_GENERIC_MSGTABLE_SIZE,
|
||||
- CHECKER_FIRST_MSGID = 100, /* lowest msgid for checkers */
|
||||
- CHECKER_MSGTABLE_SIZE = 100, /* max msg table size for checkers */
|
||||
+ CHECKER_FIRST_MSGID = 100, /* lowest msgid for checkers */
|
||||
+ CHECKER_MSGTABLE_SIZE = 100, /* max msg table size for checkers */
|
||||
};
|
||||
|
||||
struct checker_class;
|
||||
diff --git a/libmultipath/checkers/tur.c b/libmultipath/checkers/tur.c
|
||||
index d82f7dbc..370a02a6 100644
|
||||
--- a/libmultipath/checkers/tur.c
|
||||
+++ b/libmultipath/checkers/tur.c
|
||||
@@ -188,6 +188,16 @@ retry:
|
||||
*msgid = CHECKER_MSGID_GHOST;
|
||||
return PATH_GHOST;
|
||||
}
|
||||
+ } else if (key == 0x5) {
|
||||
+ /* Illegal request */
|
||||
+ if (asc == 0x25 && ascq == 0x00) {
|
||||
+ /*
|
||||
+ * LUN NOT SUPPORTED: unmapped at target.
|
||||
+ * Signals pp->disconnected, becomes PATH_DOWN.
|
||||
+ */
|
||||
+ *msgid = CHECKER_MSGID_DISCONNECTED;
|
||||
+ return PATH_DISCONNECTED;
|
||||
+ }
|
||||
}
|
||||
*msgid = CHECKER_MSGID_DOWN;
|
||||
return PATH_DOWN;
|
||||
diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c
|
||||
index 186423e0..c529f336 100644
|
||||
--- a/libmultipath/discovery.c
|
||||
+++ b/libmultipath/discovery.c
|
||||
@@ -2416,8 +2416,25 @@ int pathinfo(struct path *pp, struct config *conf, int mask)
|
||||
pp->state == PATH_UNCHECKED ||
|
||||
pp->state == PATH_WILD)
|
||||
pp->chkrstate = pp->state = newstate;
|
||||
+ /*
|
||||
+ * PATH_TIMEOUT and PATH_DISCONNECTED are ephemeral
|
||||
+ * states that should never be stored in pp->state.
|
||||
+ * Convert them to PATH_DOWN immediately.
|
||||
+ */
|
||||
if (pp->state == PATH_TIMEOUT)
|
||||
pp->state = PATH_DOWN;
|
||||
+ if (pp->state == PATH_DISCONNECTED) {
|
||||
+ int purge_enabled = pp->mpp &&
|
||||
+ pp->mpp->purge_disconnected ==
|
||||
+ PURGE_DISCONNECTED_ON;
|
||||
+ if (purge_enabled &&
|
||||
+ pp->disconnected == NOT_DISCONNECTED) {
|
||||
+ condlog(2, "%s: mark path for purge",
|
||||
+ pp->dev);
|
||||
+ pp->disconnected = DISCONNECTED_READY_FOR_PURGE;
|
||||
+ }
|
||||
+ pp->state = PATH_DOWN;
|
||||
+ }
|
||||
if (pp->state == PATH_UP && !pp->size) {
|
||||
condlog(3, "%s: device size is 0, "
|
||||
"path unusable", pp->dev);
|
||||
diff --git a/libmultipath/io_err_stat.c b/libmultipath/io_err_stat.c
|
||||
index d8d91f64..d744d50e 100644
|
||||
--- a/libmultipath/io_err_stat.c
|
||||
+++ b/libmultipath/io_err_stat.c
|
||||
@@ -380,6 +380,7 @@ static void account_async_io_state(struct io_err_stat_path *pp, int rc)
|
||||
switch (rc) {
|
||||
case PATH_DOWN:
|
||||
case PATH_TIMEOUT:
|
||||
+ case PATH_DISCONNECTED:
|
||||
pp->io_err_nr++;
|
||||
break;
|
||||
case PATH_UNCHECKED:
|
||||
diff --git a/libmultipath/print.c b/libmultipath/print.c
|
||||
index ff224bc4..42d1d44c 100644
|
||||
--- a/libmultipath/print.c
|
||||
+++ b/libmultipath/print.c
|
||||
@@ -487,6 +487,8 @@ snprint_chk_state (struct strbuf *buff, const struct path * pp)
|
||||
return append_strbuf_str(buff, "i/o timeout");
|
||||
case PATH_DELAYED:
|
||||
return append_strbuf_str(buff, "delayed");
|
||||
+ case PATH_DISCONNECTED:
|
||||
+ return append_strbuf_str(buff, "disconnected");
|
||||
default:
|
||||
return append_strbuf_str(buff, "undef");
|
||||
}
|
||||
diff --git a/libmultipath/structs.h b/libmultipath/structs.h
|
||||
index e1969b95..32643684 100644
|
||||
--- a/libmultipath/structs.h
|
||||
+++ b/libmultipath/structs.h
|
||||
@@ -185,6 +185,18 @@ enum purge_disconnected_states {
|
||||
PURGE_DISCONNECTED_ON = YNU_YES, /* Purge disconnected paths */
|
||||
};
|
||||
|
||||
+/*
|
||||
+ * Path disconnection state (per path)
|
||||
+ * Tracks whether a path has been marked for purge and whether it's already queued.
|
||||
+ */
|
||||
+enum path_disconnected_state {
|
||||
+ NOT_DISCONNECTED, /* Path is not disconnected */
|
||||
+ DISCONNECTED_READY_FOR_PURGE, /* Path is disconnected and ready to be
|
||||
+ queued for purge */
|
||||
+ DISCONNECTED_QUEUED_FOR_PURGE, /* Path is disconnected and already
|
||||
+ queued for purge */
|
||||
+};
|
||||
+
|
||||
#define PROTOCOL_UNSET -1
|
||||
|
||||
enum scsi_protocol {
|
||||
@@ -355,6 +367,8 @@ struct path {
|
||||
int state;
|
||||
int dmstate;
|
||||
int chkrstate;
|
||||
+ enum path_disconnected_state disconnected; /* Marked for purge due to
|
||||
+ disconnection */
|
||||
int failcount;
|
||||
int priority;
|
||||
int pgindex;
|
||||
diff --git a/multipathd/Makefile b/multipathd/Makefile
|
||||
index 00342464..a49c4973 100644
|
||||
--- a/multipathd/Makefile
|
||||
+++ b/multipathd/Makefile
|
||||
@@ -46,7 +46,7 @@ ifeq ($(ENABLE_DMEVENTS_POLL),0)
|
||||
endif
|
||||
|
||||
OBJS = main.o pidfile.o uxlsnr.o uxclnt.o cli.o cli_handlers.o waiter.o \
|
||||
- dmevents.o init_unwinder.o
|
||||
+ dmevents.o init_unwinder.o purge.o
|
||||
|
||||
ifeq ($(FPIN_SUPPORT),1)
|
||||
OBJS += fpin_handlers.o
|
||||
diff --git a/multipathd/main.c b/multipathd/main.c
|
||||
index 9beb0e06..d91a4d49 100644
|
||||
--- a/multipathd/main.c
|
||||
+++ b/multipathd/main.c
|
||||
@@ -84,6 +84,7 @@
|
||||
#include "io_err_stat.h"
|
||||
#include "wwids.h"
|
||||
#include "foreign.h"
|
||||
+#include "purge.h"
|
||||
#include "../third-party/valgrind/drd.h"
|
||||
#include "init_unwinder.h"
|
||||
|
||||
@@ -135,11 +136,11 @@ static volatile enum daemon_status running_state = DAEMON_INIT;
|
||||
pid_t daemon_pid;
|
||||
static pthread_mutex_t config_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static pthread_cond_t config_cond;
|
||||
-static pthread_t check_thr, uevent_thr, uxlsnr_thr, uevq_thr, dmevent_thr,
|
||||
- fpin_thr, fpin_consumer_thr;
|
||||
-static bool check_thr_started, uevent_thr_started, uxlsnr_thr_started,
|
||||
- uevq_thr_started, dmevent_thr_started, fpin_thr_started,
|
||||
- fpin_consumer_thr_started;
|
||||
+static pthread_t check_thr, purge_thr, uevent_thr, uxlsnr_thr, uevq_thr,
|
||||
+ dmevent_thr, fpin_thr, fpin_consumer_thr;
|
||||
+static bool check_thr_started, purge_thr_started, uevent_thr_started,
|
||||
+ uxlsnr_thr_started, uevq_thr_started, dmevent_thr_started,
|
||||
+ fpin_thr_started, fpin_consumer_thr_started;
|
||||
static int pid_fd = -1;
|
||||
|
||||
static inline enum daemon_status get_running_state(void)
|
||||
@@ -2377,6 +2378,28 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
|
||||
if (newstate == PATH_REMOVED)
|
||||
newstate = PATH_DOWN;
|
||||
|
||||
+ /*
|
||||
+ * PATH_DISCONNECTED is an ephemeral state used to signal that a path
|
||||
+ * has been disconnected at the storage target (LUN unmapped). We use
|
||||
+ * it to set pp->disconnected for purge tracking, then immediately
|
||||
+ * convert it to PATH_DOWN for normal path failure handling.
|
||||
+ *
|
||||
+ * This ensures PATH_DISCONNECTED never gets stored in pp->state or
|
||||
+ * pp->chkrstate - it exists only as a transient signal from the
|
||||
+ * checker to trigger special handling before becoming PATH_DOWN.
|
||||
+ */
|
||||
+ if (newstate == PATH_DISCONNECTED) {
|
||||
+ if (pp->mpp &&
|
||||
+ pp->mpp->purge_disconnected == PURGE_DISCONNECTED_ON &&
|
||||
+ pp->disconnected == NOT_DISCONNECTED) {
|
||||
+ condlog(2, "%s: mark (%s) path for purge", pp->dev,
|
||||
+ checker_state_name(newstate));
|
||||
+ pp->disconnected = DISCONNECTED_READY_FOR_PURGE;
|
||||
+ }
|
||||
+ /* Always convert to PATH_DOWN for normal processing */
|
||||
+ newstate = PATH_DOWN;
|
||||
+ }
|
||||
+
|
||||
if (newstate == PATH_WILD || newstate == PATH_UNCHECKED) {
|
||||
condlog(2, "%s: unusable path (%s) - checker failed",
|
||||
pp->dev, checker_state_name(newstate));
|
||||
@@ -2684,6 +2707,7 @@ checkerloop (void *ap)
|
||||
struct timespec diff_time, start_time, end_time;
|
||||
int num_paths = 0, strict_timing, rc = 0;
|
||||
unsigned int ticks = 0;
|
||||
+ LIST_HEAD(purge_list);
|
||||
|
||||
get_monotonic_time(&start_time);
|
||||
if (start_time.tv_sec && last_time.tv_sec) {
|
||||
@@ -2724,6 +2748,12 @@ checkerloop (void *ap)
|
||||
}
|
||||
lock_cleanup_pop(vecs->lock);
|
||||
|
||||
+ /*
|
||||
+ * Cleanup handler to free purge_list if thread is cancelled.
|
||||
+ * This prevents memory leaks during shutdown.
|
||||
+ */
|
||||
+ pthread_cleanup_push(cleanup_purge_list, &purge_list);
|
||||
+
|
||||
pthread_cleanup_push(cleanup_lock, &vecs->lock);
|
||||
lock(&vecs->lock);
|
||||
pthread_testcancel();
|
||||
@@ -2731,6 +2761,11 @@ checkerloop (void *ap)
|
||||
retry_count_tick(vecs->mpvec);
|
||||
missing_uev_wait_tick(vecs);
|
||||
ghost_delay_tick(vecs);
|
||||
+ /*
|
||||
+ * Build purge list for disconnected paths.
|
||||
+ * The caller will queue it after releasing vecs->lock.
|
||||
+ */
|
||||
+ build_purge_list(vecs, &purge_list);
|
||||
lock_cleanup_pop(vecs->lock);
|
||||
|
||||
if (count)
|
||||
@@ -2745,6 +2780,26 @@ checkerloop (void *ap)
|
||||
lock_cleanup_pop(vecs->lock);
|
||||
}
|
||||
|
||||
+ /*
|
||||
+ * Queue purge work for disconnected paths.
|
||||
+ * This is done after releasing vecs->lock to avoid holding
|
||||
+ * the lock while signaling the purge thread.
|
||||
+ */
|
||||
+ if (!list_empty(&purge_list)) {
|
||||
+ pthread_cleanup_push(cleanup_mutex, &purge_mutex);
|
||||
+ pthread_mutex_lock(&purge_mutex);
|
||||
+ pthread_testcancel();
|
||||
+ list_splice_tail_init(&purge_list, &purge_queue);
|
||||
+ pthread_cond_signal(&purge_cond);
|
||||
+ pthread_cleanup_pop(1);
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Pop cleanup handler. Execute it (arg=1) to free purge_list
|
||||
+ * at the end of each iteration.
|
||||
+ */
|
||||
+ pthread_cleanup_pop(1);
|
||||
+
|
||||
diff_time.tv_nsec = 0;
|
||||
if (start_time.tv_sec) {
|
||||
get_monotonic_time(&end_time);
|
||||
@@ -3225,6 +3280,8 @@ static void cleanup_threads(void)
|
||||
|
||||
if (check_thr_started)
|
||||
pthread_cancel(check_thr);
|
||||
+ if (purge_thr_started)
|
||||
+ pthread_cancel(purge_thr);
|
||||
if (uevent_thr_started)
|
||||
pthread_cancel(uevent_thr);
|
||||
if (uxlsnr_thr_started)
|
||||
@@ -3241,6 +3298,8 @@ static void cleanup_threads(void)
|
||||
|
||||
if (check_thr_started)
|
||||
pthread_join(check_thr, NULL);
|
||||
+ if (purge_thr_started)
|
||||
+ pthread_join(purge_thr, NULL);
|
||||
if (uevent_thr_started)
|
||||
pthread_join(uevent_thr, NULL);
|
||||
if (uxlsnr_thr_started)
|
||||
@@ -3496,6 +3555,11 @@ child (__attribute__((unused)) void *param)
|
||||
goto failed;
|
||||
} else
|
||||
check_thr_started = true;
|
||||
+ if ((rc = pthread_create(&purge_thr, &misc_attr, purgeloop, vecs))) {
|
||||
+ condlog(0, "failed to create purge loop thread: %d", rc);
|
||||
+ goto failed;
|
||||
+ } else
|
||||
+ purge_thr_started = true;
|
||||
if ((rc = pthread_create(&uevq_thr, &misc_attr, uevqloop, vecs))) {
|
||||
condlog(0, "failed to create uevent dispatcher: %d", rc);
|
||||
goto failed;
|
||||
diff --git a/multipathd/purge.c b/multipathd/purge.c
|
||||
new file mode 100644
|
||||
index 00000000..44f0c905
|
||||
--- /dev/null
|
||||
+++ b/multipathd/purge.c
|
||||
@@ -0,0 +1,326 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/*
|
||||
+ * Copyright (C) 2025 Brian Bunker <brian@purestorage.com>
|
||||
+ * Copyright (C) 2025 Krishna Kant <krishna.kant@purestorage.com>
|
||||
+ */
|
||||
+
|
||||
+#include <pthread.h>
|
||||
+#include <sys/mman.h>
|
||||
+#include <sys/stat.h>
|
||||
+#include <errno.h>
|
||||
+#include <string.h>
|
||||
+#include <unistd.h>
|
||||
+#include <libudev.h>
|
||||
+#include <urcu.h>
|
||||
+
|
||||
+#include "vector.h"
|
||||
+#include "structs.h"
|
||||
+#include "structs_vec.h"
|
||||
+#include "debug.h"
|
||||
+#include "util.h"
|
||||
+#include "lock.h"
|
||||
+#include "sysfs.h"
|
||||
+#include "list.h"
|
||||
+#include "purge.h"
|
||||
+
|
||||
+pthread_mutex_t purge_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
+pthread_cond_t purge_cond = PTHREAD_COND_INITIALIZER;
|
||||
+LIST_HEAD(purge_queue);
|
||||
+
|
||||
+/*
|
||||
+ * Information needed to purge a path. We copy this data while holding
|
||||
+ * vecs->lock, then release the lock before doing the actual sysfs write.
|
||||
+ * This prevents blocking other operations while waiting for sysfs I/O.
|
||||
+ *
|
||||
+ * The udev device reference captures the sysfs path (including H:C:T:L).
|
||||
+ * The duplicated fd prevents device name/number reuse: the kernel will not
|
||||
+ * reuse the device's minor number (which maps to the device name) for a new
|
||||
+ * device while we hold an open file descriptor, even if the original device
|
||||
+ * has been removed. This protects against deleting a new device that reused
|
||||
+ * the same name after the original was removed externally.
|
||||
+ */
|
||||
+struct purge_path_info {
|
||||
+ struct list_head node; /* List linkage */
|
||||
+ struct udev_device *udev; /* Udev device (refcounted) */
|
||||
+ int fd; /* Dup'd fd prevents device reuse */
|
||||
+};
|
||||
+
|
||||
+/*
|
||||
+ * Attempt to delete a path by writing to the SCSI device's sysfs delete
|
||||
+ * attribute. This triggers kernel-level device removal. The actual cleanup
|
||||
+ * of the path structure from pathvec happens later when a uevent arrives
|
||||
+ * (handled by uev_remove_path).
|
||||
+ *
|
||||
+ * This function does NOT require vecs->lock to be held, as it operates on
|
||||
+ * copied data. This function may block while writing to sysfs, which is
|
||||
+ * why it's called without holding any locks.
|
||||
+ *
|
||||
+ * Protection against device reuse:
|
||||
+ * The duplicated fd in purge_path_info prevents the kernel from reusing
|
||||
+ * the device's minor number (and thus the device name like /dev/sdd) for
|
||||
+ * a new device, even if the original device has been removed externally.
|
||||
+ * This ensures we cannot accidentally delete a new device that reused the
|
||||
+ * same name. The kernel maintains this guarantee as long as we hold the
|
||||
+ * open file descriptor.
|
||||
+ */
|
||||
+static void delete_path_sysfs(struct purge_path_info *info)
|
||||
+{
|
||||
+ struct udev_device *ud;
|
||||
+ const char *devname;
|
||||
+
|
||||
+ if (!info->udev)
|
||||
+ goto out;
|
||||
+
|
||||
+ devname = udev_device_get_devnode(info->udev);
|
||||
+
|
||||
+ /*
|
||||
+ * Get the SCSI device parent. This is where we'll write to the
|
||||
+ * "delete" attribute to trigger device removal.
|
||||
+ */
|
||||
+ ud = udev_device_get_parent_with_subsystem_devtype(info->udev, "scsi",
|
||||
+ "scsi_device");
|
||||
+ if (!ud) {
|
||||
+ condlog(3, "%s: failed to purge, no SCSI parent found", devname);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Write "1" to the SCSI device's delete attribute to trigger
|
||||
+ * kernel-level device removal.
|
||||
+ */
|
||||
+ if (sysfs_attr_set_value(ud, "delete", "1", 1) < 0)
|
||||
+ condlog(3, "%s: failed to purge", devname);
|
||||
+ else
|
||||
+ condlog(2, "%s: purged", devname);
|
||||
+
|
||||
+out:
|
||||
+ return;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Prepare purge info for a path while holding vecs->lock.
|
||||
+ * Takes a reference on the udev device and duplicates the fd.
|
||||
+ * Returns allocated purge_path_info on success, NULL on failure.
|
||||
+ *
|
||||
+ * We require a valid fd because it prevents the kernel from reusing
|
||||
+ * the device's minor number (and device name) for a new device while
|
||||
+ * we hold it open. This protects against accidentally deleting a new
|
||||
+ * device that reused the same name after the original was removed.
|
||||
+ */
|
||||
+static struct purge_path_info *prepare_purge_path_info(struct path *pp)
|
||||
+{
|
||||
+ struct purge_path_info *info = NULL;
|
||||
+
|
||||
+ if (!pp->udev || !pp->mpp)
|
||||
+ goto out;
|
||||
+
|
||||
+ /*
|
||||
+ * We require a valid fd to prevent device name reuse.
|
||||
+ * Without it, we cannot safely purge the device.
|
||||
+ */
|
||||
+ if (pp->fd < 0) {
|
||||
+ condlog(3, "%s: no fd available, cannot safely purge", pp->dev);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ info = calloc(1, sizeof(*info));
|
||||
+ if (!info)
|
||||
+ goto out;
|
||||
+
|
||||
+ INIT_LIST_HEAD(&info->node);
|
||||
+ info->udev = udev_device_ref(pp->udev);
|
||||
+ if (!info->udev)
|
||||
+ goto out_free;
|
||||
+
|
||||
+ info->fd = dup(pp->fd);
|
||||
+ if (info->fd < 0) {
|
||||
+ condlog(3, "%s: failed to dup fd: %s, cannot safely purge",
|
||||
+ pp->dev, strerror(errno));
|
||||
+ goto out_unref;
|
||||
+ }
|
||||
+
|
||||
+ return info;
|
||||
+
|
||||
+out_unref:
|
||||
+ udev_device_unref(info->udev);
|
||||
+out_free:
|
||||
+ free(info);
|
||||
+ info = NULL;
|
||||
+out:
|
||||
+ return info;
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Clean up and free purge info.
|
||||
+ */
|
||||
+static void free_purge_path_info(struct purge_path_info *info)
|
||||
+{
|
||||
+ if (!info)
|
||||
+ return;
|
||||
+
|
||||
+ if (info->fd >= 0)
|
||||
+ close(info->fd);
|
||||
+ if (info->udev)
|
||||
+ udev_device_unref(info->udev);
|
||||
+ free(info);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Build a list of purge_path_info for all paths marked for purge.
|
||||
+ * This should be called while holding vecs->lock. It clears the
|
||||
+ * disconnected flag and prepares purge info for each path, adding
|
||||
+ * them to tmpq.
|
||||
+ */
|
||||
+void build_purge_list(struct vectors *vecs, struct list_head *tmpq)
|
||||
+{
|
||||
+ struct path *pp;
|
||||
+ unsigned int i;
|
||||
+
|
||||
+ vector_foreach_slot (vecs->pathvec, pp, i) {
|
||||
+ struct purge_path_info *info;
|
||||
+
|
||||
+ if (pp->disconnected != DISCONNECTED_READY_FOR_PURGE)
|
||||
+ continue;
|
||||
+
|
||||
+ /*
|
||||
+ * Mark as queued whether we succeed or fail.
|
||||
+ * On success, we're purging it now.
|
||||
+ * On failure, retrying is unlikely to help until
|
||||
+ * the checker re-evaluates the path.
|
||||
+ */
|
||||
+ pp->disconnected = DISCONNECTED_QUEUED_FOR_PURGE;
|
||||
+
|
||||
+ info = prepare_purge_path_info(pp);
|
||||
+ if (info) {
|
||||
+ condlog(2, "%s: queuing path for purge", pp->dev);
|
||||
+ list_add_tail(&info->node, tmpq);
|
||||
+ } else
|
||||
+ condlog(3, "%s: failed to prepare purge info", pp->dev);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void rcu_unregister(__attribute__((unused)) void *param)
|
||||
+{
|
||||
+ rcu_unregister_thread();
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Cleanup handler for a single purge_path_info.
|
||||
+ * Used to prevent memory leaks if thread is cancelled while processing.
|
||||
+ */
|
||||
+static void cleanup_purge_path_info(void *arg)
|
||||
+{
|
||||
+ struct purge_path_info *info = arg;
|
||||
+
|
||||
+ free_purge_path_info(info);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Cleanup handler for purge list. Frees all purge_path_info entries.
|
||||
+ * Can be called as a pthread cleanup handler or directly.
|
||||
+ */
|
||||
+void cleanup_purge_list(void *arg)
|
||||
+{
|
||||
+ struct list_head *purge_list = arg;
|
||||
+ struct purge_path_info *info, *tmp;
|
||||
+
|
||||
+ list_for_each_entry_safe(info, tmp, purge_list, node)
|
||||
+ {
|
||||
+ list_del_init(&info->node);
|
||||
+ free_purge_path_info(info);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Cleanup handler for the global purge queue.
|
||||
+ * Used during shutdown to free any remaining queued items.
|
||||
+ */
|
||||
+static void cleanup_global_purge_queue(void *arg __attribute__((unused)))
|
||||
+{
|
||||
+ pthread_mutex_lock(&purge_mutex);
|
||||
+ cleanup_purge_list(&purge_queue);
|
||||
+ pthread_mutex_unlock(&purge_mutex);
|
||||
+}
|
||||
+
|
||||
+/*
|
||||
+ * Main purge thread loop.
|
||||
+ *
|
||||
+ * This thread waits for purge_path_info structs to be queued by the checker
|
||||
+ * thread, then processes them by writing to their sysfs delete attributes.
|
||||
+ * The checker thread builds the list while holding vecs->lock, so this
|
||||
+ * thread doesn't need to grab that lock at all.
|
||||
+ *
|
||||
+ * Uses list_splice_tail_init() like uevent_dispatch() to safely transfer
|
||||
+ * items from the global queue to a local list for processing.
|
||||
+ *
|
||||
+ * Cleanup handlers are registered for both the local purge_list and the
|
||||
+ * global purge_queue (similar to uevent_listen), and for each individual
|
||||
+ * purge_path_info after it's popped off the list (similar to service_uevq).
|
||||
+ * This ensures no memory leaks if the thread is cancelled at any point.
|
||||
+ */
|
||||
+void *purgeloop(void *ap __attribute__((unused)))
|
||||
+{
|
||||
+ pthread_cleanup_push(rcu_unregister, NULL);
|
||||
+ rcu_register_thread();
|
||||
+ mlockall(MCL_CURRENT | MCL_FUTURE);
|
||||
+
|
||||
+ /*
|
||||
+ * Cleanup handler for global purge_queue.
|
||||
+ * This handles items that were queued but not yet moved to purge_list.
|
||||
+ */
|
||||
+ pthread_cleanup_push(cleanup_global_purge_queue, NULL);
|
||||
+
|
||||
+ while (1) {
|
||||
+ LIST_HEAD(purge_list);
|
||||
+ struct purge_path_info *info;
|
||||
+
|
||||
+ /*
|
||||
+ * Cleanup handler for local purge_list.
|
||||
+ * This handles items that were moved from purge_queue but
|
||||
+ * not yet processed.
|
||||
+ */
|
||||
+ pthread_cleanup_push(cleanup_purge_list, &purge_list);
|
||||
+
|
||||
+ /*
|
||||
+ * Cleanup handler for purge_mutex.
|
||||
+ * Note: pthread_cond_wait() reacquires the mutex before
|
||||
+ * returning, even on cancellation, so this cleanup handler
|
||||
+ * will properly unlock it if we're cancelled.
|
||||
+ */
|
||||
+ pthread_cleanup_push(cleanup_mutex, &purge_mutex);
|
||||
+ pthread_mutex_lock(&purge_mutex);
|
||||
+ pthread_testcancel();
|
||||
+ while (list_empty(&purge_queue)) {
|
||||
+ condlog(4, "purgeloop waiting for work");
|
||||
+ pthread_cond_wait(&purge_cond, &purge_mutex);
|
||||
+ }
|
||||
+ list_splice_tail_init(&purge_queue, &purge_list);
|
||||
+ pthread_cleanup_pop(1);
|
||||
+
|
||||
+ /*
|
||||
+ * Process all paths in the list without holding any locks.
|
||||
+ * The sysfs operations may block, but that's fine since we're
|
||||
+ * not holding vecs->lock.
|
||||
+ *
|
||||
+ * After popping each info off the list, we immediately push
|
||||
+ * a cleanup handler for it. This ensures it gets freed even
|
||||
+ * if we're cancelled inside delete_path_sysfs().
|
||||
+ */
|
||||
+ while ((info = list_pop_entry(&purge_list, typeof(*info), node))) {
|
||||
+ pthread_cleanup_push(cleanup_purge_path_info, info);
|
||||
+ delete_path_sysfs(info);
|
||||
+ pthread_cleanup_pop(1);
|
||||
+ }
|
||||
+
|
||||
+ /*
|
||||
+ * Pop cleanup handler without executing it (0) since we've
|
||||
+ * already freed everything above. The handler only runs if
|
||||
+ * the thread is cancelled during processing.
|
||||
+ */
|
||||
+ pthread_cleanup_pop(0);
|
||||
+ }
|
||||
+
|
||||
+ pthread_cleanup_pop(1);
|
||||
+ pthread_cleanup_pop(1);
|
||||
+ return NULL;
|
||||
+}
|
||||
diff --git a/multipathd/purge.h b/multipathd/purge.h
|
||||
new file mode 100644
|
||||
index 00000000..1fe755f3
|
||||
--- /dev/null
|
||||
+++ b/multipathd/purge.h
|
||||
@@ -0,0 +1,41 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
+/*
|
||||
+ * Copyright (C) 2025 Brian Bunker <brian@purestorage.com>
|
||||
+ * Copyright (C) 2025 Krishna Kant <krishna.kant@purestorage.com>
|
||||
+ */
|
||||
+
|
||||
+#ifndef PURGE_H_INCLUDED
|
||||
+#define PURGE_H_INCLUDED
|
||||
+
|
||||
+#include <pthread.h>
|
||||
+#include "list.h"
|
||||
+
|
||||
+struct vectors;
|
||||
+
|
||||
+/*
|
||||
+ * Purge thread synchronization.
|
||||
+ * The checker thread builds a list of paths to purge and queues them here.
|
||||
+ * The purge thread picks up the queue and processes it.
|
||||
+ */
|
||||
+extern pthread_mutex_t purge_mutex;
|
||||
+extern pthread_cond_t purge_cond;
|
||||
+extern struct list_head purge_queue;
|
||||
+
|
||||
+/*
|
||||
+ * Build a list of paths to purge and add them to tmpq. Called by checker
|
||||
+ * thread while holding vecs->lock.
|
||||
+ */
|
||||
+void build_purge_list(struct vectors *vecs, struct list_head *tmpq);
|
||||
+
|
||||
+/*
|
||||
+ * Cleanup handler for purge list. Frees all purge_path_info entries.
|
||||
+ * Can be called as a pthread cleanup handler or directly for shutdown cleanup.
|
||||
+ */
|
||||
+void cleanup_purge_list(void *arg);
|
||||
+
|
||||
+/*
|
||||
+ * Main purge thread loop
|
||||
+ */
|
||||
+void *purgeloop(void *ap);
|
||||
+
|
||||
+#endif /* PURGE_H_INCLUDED */
|
||||
@ -0,0 +1,47 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Date: Mon, 1 Dec 2025 22:02:10 -0500
|
||||
Subject: [PATCH] libmpathpersist: fix register retry status checking
|
||||
|
||||
If there libmpathpersist failed to create a thread to retry the register
|
||||
and ignore command, mpath_prout_reg should fail. Instead, the code was
|
||||
simply ignoring the failed threads. Fix that.
|
||||
|
||||
Fixes: 2a4ca250 ("libmpathpersist: change how reservation conflicts are handled")
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Reviewed-by: Martin Wilck <mwilck@suse.com>
|
||||
---
|
||||
libmpathpersist/mpath_persist.c | 15 ++++++++-------
|
||||
1 file changed, 8 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
|
||||
index 72be48c1..6cb56dab 100644
|
||||
--- a/libmpathpersist/mpath_persist.c
|
||||
+++ b/libmpathpersist/mpath_persist.c
|
||||
@@ -858,18 +858,19 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope,
|
||||
}
|
||||
}
|
||||
for (i = 0; i < count; i++) {
|
||||
- if (thread[i].param.status != MPATH_PR_SKIP &&
|
||||
- thread[i].param.status != MPATH_PR_THREAD_ERROR) {
|
||||
+ if (thread[i].param.status == MPATH_PR_SKIP)
|
||||
+ continue;
|
||||
+ if (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 retrying %d",
|
||||
- mpp->wwid, i);
|
||||
+ mpp->wwid, i);
|
||||
}
|
||||
- if (thread[i].param.status == MPATH_PR_RETRYABLE_ERROR)
|
||||
- retryable_error = true;
|
||||
- else if (status == MPATH_PR_SUCCESS)
|
||||
- status = thread[i].param.status;
|
||||
}
|
||||
+ if (thread[i].param.status == MPATH_PR_RETRYABLE_ERROR)
|
||||
+ retryable_error = true;
|
||||
+ else if (status == MPATH_PR_SUCCESS)
|
||||
+ status = thread[i].param.status;
|
||||
}
|
||||
need_retry = false;
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Date: Mon, 1 Dec 2025 22:02:12 -0500
|
||||
Subject: [PATCH] multipathd: remember number of registered keys when ioctl
|
||||
fails
|
||||
|
||||
If prin_do_scsi_ioctl() fails in update_map_pr() for some reason other
|
||||
than Persistent Reservations not being supported, It shouldn't clear the
|
||||
number of registered keys, since there's no reason to think that it has
|
||||
changed. Similarly, if update_map_pr() fails in mpath_pr_event_handle(),
|
||||
don't assume that the nr_keys_needed was cleared. Just return whatever
|
||||
the value is now. This saves multipathd from doing pointless calls to
|
||||
update_map_pr(), if one of the paths is failing.
|
||||
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Reviewed-by: Martin Wilck <mwilck@suse.com>
|
||||
---
|
||||
multipathd/main.c | 11 +++++++----
|
||||
1 file changed, 7 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/multipathd/main.c b/multipathd/main.c
|
||||
index d91a4d49..2526d8d1 100644
|
||||
--- a/multipathd/main.c
|
||||
+++ b/multipathd/main.c
|
||||
@@ -3842,7 +3842,9 @@ 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. Otherwise
|
||||
- * it will be set to 0.
|
||||
+ * if mpp->prflag is PR_UNSET it will be set to 0. If MPATH_PR_SUCCESS
|
||||
+ * is not returned and mpp->prflag is not PR_UNSET, nr_keys will not be
|
||||
+ * changed.
|
||||
*/
|
||||
static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *nr_keys)
|
||||
{
|
||||
@@ -3871,11 +3873,12 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n
|
||||
|
||||
ret = prin_do_scsi_ioctl(pp->dev, MPATH_PRIN_RKEY_SA, &resp, 0);
|
||||
if (ret != MPATH_PR_SUCCESS) {
|
||||
- if (ret == MPATH_PR_ILLEGAL_REQ)
|
||||
+ if (ret == MPATH_PR_ILLEGAL_REQ) {
|
||||
unset_pr(mpp);
|
||||
+ *nr_keys = 0;
|
||||
+ }
|
||||
condlog(0, "%s : pr in read keys service action failed Error=%d",
|
||||
mpp->alias, ret);
|
||||
- *nr_keys = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -3990,7 +3993,7 @@ retry:
|
||||
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;
|
||||
+ return nr_keys_needed;
|
||||
if (mpp->prflag != PR_SET) {
|
||||
memset(¶m, 0, sizeof(param));
|
||||
clear_reg = true;
|
||||
@ -0,0 +1,459 @@
|
||||
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
||||
From: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Date: Mon, 15 Dec 2025 15:29:58 -0500
|
||||
Subject: [PATCH] libmpathpersist: fix code for skipping multipathd path
|
||||
registration
|
||||
|
||||
When libmpathpersist notifies multipathd that a key has been registered,
|
||||
cli_setprstatus() calls pr_register_active_paths() with a flag to let it
|
||||
know that the paths are likely already registered, and it can skip
|
||||
re-registering them, as long as the number of active paths matches the
|
||||
number of registered keys. This shortcut can fail, causing multipathd to
|
||||
not register needed paths, if either a path becomes usable and another
|
||||
becomes unusable while libmpathpersist is running or if there already
|
||||
were registered keys for I_T Nexus's that don't correspond to path
|
||||
devices.
|
||||
|
||||
To make this shortcut work in cases like that, this commit adds a new
|
||||
multipathd command "setprstatus map <map> pathlist <pathlist>", where
|
||||
<pathlist> is a quoted, whitespace separated list of scsi path devices.
|
||||
libmpathpersist will send out the list of paths it registered the key
|
||||
on. pr_register_active_paths() will skip calling mpath_pr_event_handle()
|
||||
for paths on that list.
|
||||
|
||||
In order to deal with the possiblity of a preempt occuring while
|
||||
libmpathpersist was running, the code still needs to check that it has
|
||||
the expected number of keys.
|
||||
|
||||
Fixes: f7d6cd17 ("multipathd: Fix race while registering PR key")
|
||||
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
|
||||
Reviewed-by: Martin Wilck <mwilck@suse.com>
|
||||
---
|
||||
libmpathpersist/mpath_persist.c | 6 +--
|
||||
libmpathpersist/mpath_updatepr.c | 50 ++++++++++++++++++-------
|
||||
libmpathpersist/mpathpr.h | 2 +-
|
||||
multipathd/cli.c | 2 +
|
||||
multipathd/cli.h | 2 +
|
||||
multipathd/cli_handlers.c | 37 +++++++++++++++++--
|
||||
multipathd/cli_handlers.h | 1 +
|
||||
multipathd/main.c | 63 +++++++++++++++++++++-----------
|
||||
multipathd/main.h | 4 +-
|
||||
multipathd/multipathd.8 | 10 ++++-
|
||||
10 files changed, 132 insertions(+), 45 deletions(-)
|
||||
|
||||
diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c
|
||||
index 6cb56dab..ab8fa630 100644
|
||||
--- a/libmpathpersist/mpath_persist.c
|
||||
+++ b/libmpathpersist/mpath_persist.c
|
||||
@@ -527,12 +527,12 @@ 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)
|
||||
- update_prflag(alias, 0);
|
||||
+ update_prflag(mpp, 0);
|
||||
else
|
||||
- update_prflag(alias, 1);
|
||||
+ update_prflag(mpp, 1);
|
||||
break;
|
||||
case MPATH_PROUT_CLEAR_SA:
|
||||
- update_prflag(alias, 0);
|
||||
+ update_prflag(mpp, 0);
|
||||
if (mpp->prkey_source == PRKEY_SOURCE_FILE)
|
||||
update_prkey(alias, 0);
|
||||
break;
|
||||
diff --git a/libmpathpersist/mpath_updatepr.c b/libmpathpersist/mpath_updatepr.c
|
||||
index bfa6e089..e24432bb 100644
|
||||
--- a/libmpathpersist/mpath_updatepr.c
|
||||
+++ b/libmpathpersist/mpath_updatepr.c
|
||||
@@ -19,9 +19,9 @@
|
||||
#include "memory.h"
|
||||
#include "mpathpr.h"
|
||||
#include "structs.h"
|
||||
+#include "strbuf.h"
|
||||
|
||||
-
|
||||
-static char *do_pr(char *alias, char *str)
|
||||
+static char *do_pr(char *alias, const char *str)
|
||||
{
|
||||
int fd;
|
||||
char *reply;
|
||||
@@ -51,24 +51,26 @@ static char *do_pr(char *alias, char *str)
|
||||
return reply;
|
||||
}
|
||||
|
||||
-static int do_update_pr(char *alias, char *cmd, char *key)
|
||||
+static int do_update_pr(char *alias, char *cmd, const char *data)
|
||||
{
|
||||
- char str[256];
|
||||
+ STRBUF_ON_STACK(buf);
|
||||
char *reply = NULL;
|
||||
int ret = -1;
|
||||
|
||||
- if (key)
|
||||
- snprintf(str,sizeof(str),"%s map %s key %s", cmd, alias, key);
|
||||
+ if (data)
|
||||
+ print_strbuf(&buf, "%s map %s %s %s", cmd, alias,
|
||||
+ strcmp(cmd, "setprkey") ? "pathlist" : "key", data);
|
||||
else
|
||||
- snprintf(str,sizeof(str),"%s map %s", cmd, alias);
|
||||
+ print_strbuf(&buf, "%s map %s", cmd, alias);
|
||||
|
||||
- reply = do_pr(alias, str);
|
||||
+ reply = do_pr(alias, get_strbuf_str(&buf));
|
||||
if (reply) {
|
||||
- condlog (2, "%s: message=%s reply=%s", alias, str, reply);
|
||||
- if (reply && strncmp(reply,"ok", 2) == 0)
|
||||
+ if (strncmp(reply, "ok", 2) == 0)
|
||||
ret = 0;
|
||||
else
|
||||
ret = -1;
|
||||
+ condlog(ret ? 0 : 4, "%s: message=%s reply=%s", alias,
|
||||
+ get_strbuf_str(&buf), reply);
|
||||
}
|
||||
|
||||
free(reply);
|
||||
@@ -104,9 +106,31 @@ 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_prflag(struct multipath *mpp, int set)
|
||||
+{
|
||||
+ STRBUF_ON_STACK(buf);
|
||||
+ int i, j;
|
||||
+ bool first = true;
|
||||
+ struct pathgroup *pgp = NULL;
|
||||
+ struct path *pp = NULL;
|
||||
+
|
||||
+ if (!set)
|
||||
+ return do_update_pr(mpp->alias, "unsetprstatus", NULL);
|
||||
+
|
||||
+ append_strbuf_str(&buf, "\"");
|
||||
+ vector_foreach_slot (mpp->pg, pgp, j) {
|
||||
+ vector_foreach_slot (pgp->paths, pp, i) {
|
||||
+ if (pp->state == PATH_UP || pp->state == PATH_GHOST) {
|
||||
+ if (first) {
|
||||
+ append_strbuf_str(&buf, pp->dev);
|
||||
+ first = false;
|
||||
+ } else
|
||||
+ print_strbuf(&buf, " %s", pp->dev_t);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ append_strbuf_str(&buf, "\"");
|
||||
+ return do_update_pr(mpp->alias, "setprstatus", get_strbuf_str(&buf));
|
||||
}
|
||||
|
||||
int update_prhold(char *mapname, bool set) {
|
||||
diff --git a/libmpathpersist/mpathpr.h b/libmpathpersist/mpathpr.h
|
||||
index 69f402f2..67d04247 100644
|
||||
--- a/libmpathpersist/mpathpr.h
|
||||
+++ b/libmpathpersist/mpathpr.h
|
||||
@@ -40,7 +40,7 @@ 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 update_prflag(char *mapname, int set);
|
||||
+int update_prflag(struct multipath *mpp, 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);
|
||||
diff --git a/multipathd/cli.c b/multipathd/cli.c
|
||||
index d33b571d..9e833b02 100644
|
||||
--- a/multipathd/cli.c
|
||||
+++ b/multipathd/cli.c
|
||||
@@ -226,6 +226,7 @@ load_keys (void)
|
||||
r += add_key(keys, "getprhold", GETPRHOLD, 0);
|
||||
r += add_key(keys, "setprhold", SETPRHOLD, 0);
|
||||
r += add_key(keys, "unsetprhold", UNSETPRHOLD, 0);
|
||||
+ r += add_key(keys, "pathlist", PATHLIST, 1);
|
||||
|
||||
if (r) {
|
||||
free_keys(keys);
|
||||
@@ -572,6 +573,7 @@ cli_init (void) {
|
||||
add_handler(SHUTDOWN, NULL);
|
||||
add_handler(GETPRSTATUS+MAP, NULL);
|
||||
add_handler(SETPRSTATUS+MAP, NULL);
|
||||
+ add_handler(SETPRSTATUS+MAP+PATHLIST, NULL);
|
||||
add_handler(UNSETPRSTATUS+MAP, NULL);
|
||||
add_handler(GETPRKEY+MAP, NULL);
|
||||
add_handler(SETPRKEY+MAP+KEY, NULL);
|
||||
diff --git a/multipathd/cli.h b/multipathd/cli.h
|
||||
index 4d12f8fd..17344cb4 100644
|
||||
--- a/multipathd/cli.h
|
||||
+++ b/multipathd/cli.h
|
||||
@@ -50,6 +50,7 @@ enum {
|
||||
__GETPRHOLD,
|
||||
__SETPRHOLD,
|
||||
__UNSETPRHOLD,
|
||||
+ __PATHLIST,
|
||||
};
|
||||
|
||||
#define LIST (1 << __LIST)
|
||||
@@ -99,6 +100,7 @@ enum {
|
||||
#define GETPRHOLD (1ULL << __GETPRHOLD)
|
||||
#define SETPRHOLD (1ULL << __SETPRHOLD)
|
||||
#define UNSETPRHOLD (1ULL << __UNSETPRHOLD)
|
||||
+#define PATHLIST (1ULL << __PATHLIST)
|
||||
|
||||
#define INITIAL_REPLY_LEN 1200
|
||||
|
||||
diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c
|
||||
index 0c63ca9a..f183fceb 100644
|
||||
--- a/multipathd/cli_handlers.c
|
||||
+++ b/multipathd/cli_handlers.c
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "foreign.h"
|
||||
#include "strbuf.h"
|
||||
#include "cli_handlers.h"
|
||||
+#include <ctype.h>
|
||||
|
||||
#define SET_REPLY_AND_LEN(__rep, __len, string_literal) \
|
||||
do { \
|
||||
@@ -1308,8 +1309,8 @@ cli_getprstatus (void * v, char ** reply, int * len, void * data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
-int
|
||||
-cli_setprstatus(void * v, char ** reply, int * len, void * data)
|
||||
+static int do_setprstatus(void * v, char ** reply, int * len, void * data,
|
||||
+ const struct _vector *registered_paths)
|
||||
{
|
||||
struct multipath * mpp;
|
||||
struct vectors * vecs = (struct vectors *)data;
|
||||
@@ -1324,7 +1325,7 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data)
|
||||
|
||||
if (mpp->prflag != PR_SET) {
|
||||
set_pr(mpp);
|
||||
- pr_register_active_paths(mpp, true);
|
||||
+ pr_register_active_paths(mpp, registered_paths);
|
||||
if (mpp->prflag == PR_SET)
|
||||
condlog(2, "%s: prflag set", param);
|
||||
else
|
||||
@@ -1336,6 +1337,36 @@ cli_setprstatus(void * v, char ** reply, int * len, void * data)
|
||||
return 0;
|
||||
}
|
||||
|
||||
+int
|
||||
+cli_setprstatus(void * v, char ** reply, int * len, void * data)
|
||||
+{
|
||||
+ return do_setprstatus(v, reply, len, data, NULL);
|
||||
+}
|
||||
+
|
||||
+int
|
||||
+cli_setprstatus_list(void * v, char ** reply, int * len, void *data)
|
||||
+{
|
||||
+ int r;
|
||||
+ struct _vector registered_paths_vec = {.allocated = 0};
|
||||
+ vector registered_paths
|
||||
+ __attribute__((cleanup(cleanup_reset_vec))) = ®istered_paths_vec;
|
||||
+ char *ptr = get_keyparam(v, PATHLIST);
|
||||
+
|
||||
+ while (isspace(*ptr))
|
||||
+ ptr++;
|
||||
+ while (*ptr) {
|
||||
+ if (!vector_alloc_slot(registered_paths))
|
||||
+ return -ENOMEM;
|
||||
+ vector_set_slot(registered_paths, ptr);
|
||||
+ while (*ptr && !isspace(*ptr))
|
||||
+ ptr++;
|
||||
+ while (isspace(*ptr))
|
||||
+ *ptr++ = '\0';
|
||||
+ }
|
||||
+ r = do_setprstatus(v, reply, len, data, registered_paths);
|
||||
+ return r;
|
||||
+}
|
||||
+
|
||||
int
|
||||
cli_unsetprstatus(void * v, char ** reply, int * len, void * data)
|
||||
{
|
||||
diff --git a/multipathd/cli_handlers.h b/multipathd/cli_handlers.h
|
||||
index 348c8485..28b6f2bb 100644
|
||||
--- a/multipathd/cli_handlers.h
|
||||
+++ b/multipathd/cli_handlers.h
|
||||
@@ -46,6 +46,7 @@ int cli_shutdown(void * v, char ** reply, int * len, void * data);
|
||||
int cli_reassign (void * v, char ** reply, int * len, void * data);
|
||||
int cli_getprstatus(void * v, char ** reply, int * len, void * data);
|
||||
int cli_setprstatus(void * v, char ** reply, int * len, void * data);
|
||||
+int cli_setprstatus_list(void * v, char ** reply, int * len, void * data);
|
||||
int cli_unsetprstatus(void * v, char ** reply, int * len, void * data);
|
||||
int cli_getprkey(void * v, char ** reply, int * len, void * data);
|
||||
int cli_setprkey(void * v, char ** reply, int * len, void * data);
|
||||
diff --git a/multipathd/main.c b/multipathd/main.c
|
||||
index 2526d8d1..09d26fe2 100644
|
||||
--- a/multipathd/main.c
|
||||
+++ b/multipathd/main.c
|
||||
@@ -548,28 +548,47 @@ flush_map_nopaths(struct multipath *mpp, struct vectors *vecs) {
|
||||
return true;
|
||||
}
|
||||
|
||||
-void pr_register_active_paths(struct multipath *mpp, bool check_nr_active)
|
||||
+/*
|
||||
+ * If reg_paths in non-NULL, it is a vector of paths that libmpathpersist
|
||||
+ * registered. If the number of registered keys is smaller than the number
|
||||
+ * of registered paths, then likely a preempt that occurred while
|
||||
+ * libmpathpersist was registering the key. As long as there are still some
|
||||
+ * registered keys, treat the preempt as happening first, and make sure to
|
||||
+ * register keys on all the paths. If the number of registered keys is at
|
||||
+ * least as large as the number of registered paths, then no preempt happened,
|
||||
+ * and multipathd does not need to re-register the paths that libmpathpersist
|
||||
+ * handled
|
||||
+ */
|
||||
+void pr_register_active_paths(struct multipath *mpp, const struct _vector *reg_paths)
|
||||
{
|
||||
- unsigned int i, j, nr_keys = 0;
|
||||
- unsigned int nr_active = 0;
|
||||
+ unsigned int i, j, k, nr_keys = 0;
|
||||
+ unsigned int wanted_nr = VECTOR_SIZE(reg_paths);
|
||||
struct path *pp;
|
||||
struct pathgroup *pgp;
|
||||
-
|
||||
- if (check_nr_active) {
|
||||
- nr_active = count_active_paths(mpp);
|
||||
- if (!nr_active)
|
||||
- return;
|
||||
- }
|
||||
+ char *pathname;
|
||||
|
||||
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, nr_active);
|
||||
- if (check_nr_active && nr_keys == nr_active)
|
||||
- return;
|
||||
+ if (pp->state != PATH_UP && pp->state != PATH_GHOST)
|
||||
+ continue;
|
||||
+ if (wanted_nr && nr_keys) {
|
||||
+ vector_foreach_slot (reg_paths, pathname, k) {
|
||||
+ if (strcmp(pp->dev_t, pathname) == 0) {
|
||||
+ goto skip;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
+ nr_keys = mpath_pr_event_handle(pp, nr_keys, wanted_nr);
|
||||
+ if (nr_keys && nr_keys < wanted_nr) {
|
||||
+ /*
|
||||
+ * Incorrect number of registered keys. Need
|
||||
+ * to register all devices
|
||||
+ */
|
||||
+ wanted_nr = 0;
|
||||
+ }
|
||||
+ skip:; /* a statement must follow a label on pre C23 clang */
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -599,8 +618,7 @@ handle_orphaned_offline_paths(vector offline_paths)
|
||||
pp->add_when_online = true;
|
||||
}
|
||||
|
||||
-static void
|
||||
-cleanup_reset_vec(struct _vector **v)
|
||||
+void cleanup_reset_vec(struct _vector **v)
|
||||
{
|
||||
vector_reset(*v);
|
||||
}
|
||||
@@ -656,7 +674,7 @@ fail:
|
||||
|
||||
sync_map_state(mpp);
|
||||
|
||||
- pr_register_active_paths(mpp, false);
|
||||
+ pr_register_active_paths(mpp, NULL);
|
||||
|
||||
if (VECTOR_SIZE(offline_paths) != 0)
|
||||
handle_orphaned_offline_paths(offline_paths);
|
||||
@@ -1279,7 +1297,7 @@ rescan:
|
||||
|
||||
if (retries >= 0) {
|
||||
if ((mpp->prflag == PR_SET && prflag != PR_SET) || start_waiter)
|
||||
- pr_register_active_paths(mpp, false);
|
||||
+ pr_register_active_paths(mpp, NULL);
|
||||
condlog(2, "%s [%s]: path added to devmap %s",
|
||||
pp->dev, pp->dev_t, mpp->alias);
|
||||
return 0;
|
||||
@@ -1875,6 +1893,7 @@ uxlsnrloop (void * ap)
|
||||
set_unlocked_handler_callback(SHUTDOWN, cli_shutdown);
|
||||
set_handler_callback(GETPRSTATUS|MAP, cli_getprstatus);
|
||||
set_handler_callback(SETPRSTATUS|MAP, cli_setprstatus);
|
||||
+ set_handler_callback(SETPRSTATUS|MAP|PATHLIST, cli_setprstatus_list);
|
||||
set_handler_callback(UNSETPRSTATUS|MAP, cli_unsetprstatus);
|
||||
set_handler_callback(FORCEQ|DAEMON, cli_force_no_daemon_q);
|
||||
set_handler_callback(RESTOREQ|DAEMON, cli_restore_no_daemon_q);
|
||||
@@ -2581,7 +2600,7 @@ check_path (struct vectors * vecs, struct path * pp, unsigned int ticks)
|
||||
mpath_pr_event_handle(pp, 0, 0);
|
||||
if (pp->mpp->prflag == PR_SET &&
|
||||
prflag != PR_SET)
|
||||
- pr_register_active_paths(pp->mpp, false);
|
||||
+ pr_register_active_paths(pp->mpp, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2950,7 +2969,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, false);
|
||||
+ pr_register_active_paths(mpp, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3933,8 +3952,8 @@ static int update_map_pr(struct multipath *mpp, struct path *pp, unsigned int *n
|
||||
*
|
||||
* 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.
|
||||
+ * number of keys found by the initial call to update_map_pr() is at least
|
||||
+ * as large as 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.
|
||||
@@ -3957,7 +3976,7 @@ mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed,
|
||||
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)
|
||||
+ if (nr_keys_wanted && nr_keys_wanted <= nr_keys_needed)
|
||||
return nr_keys_needed;
|
||||
}
|
||||
|
||||
diff --git a/multipathd/main.h b/multipathd/main.h
|
||||
index 8a4c5f88..ea12455f 100644
|
||||
--- a/multipathd/main.h
|
||||
+++ b/multipathd/main.h
|
||||
@@ -60,5 +60,7 @@ 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);
|
||||
+void pr_register_active_paths(struct multipath *mpp,
|
||||
+ const struct _vector *registered_paths);
|
||||
+void cleanup_reset_vec(struct _vector **v);
|
||||
#endif /* MAIN_H */
|
||||
diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8
|
||||
index 2ed036d4..0a76f3e3 100644
|
||||
--- a/multipathd/multipathd.8
|
||||
+++ b/multipathd/multipathd.8
|
||||
@@ -324,11 +324,17 @@ will not be disabled when the daemon stops.
|
||||
Restores configured queue_without_daemon mode.
|
||||
.
|
||||
.TP
|
||||
-.B map|multipath $map setprstatus
|
||||
+.B setprstatus map|multipath $map
|
||||
Enable persistent reservation management on $map.
|
||||
.
|
||||
.TP
|
||||
-.B map|multipath $map unsetprstatus
|
||||
+.B setprstatus map|multipath $map pathlist $pathlist
|
||||
+Enable persistent reservation management on $map, and notify multipathd of
|
||||
+the paths that have been registered, so it doesn't attempt to re-register
|
||||
+them.
|
||||
+.
|
||||
+.TP
|
||||
+.B unsetprstatus map|multipath $map
|
||||
Disable persistent reservation management on $map.
|
||||
.
|
||||
.TP
|
||||
@ -1,6 +1,6 @@
|
||||
Name: device-mapper-multipath
|
||||
Version: 0.8.7
|
||||
Release: 39%{?dist}
|
||||
Release: 45%{?dist}
|
||||
Summary: Tools to manage multipath devices using device-mapper
|
||||
License: GPLv2
|
||||
URL: http://christophe.varoqui.free.fr/
|
||||
@ -159,7 +159,67 @@ 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
|
||||
Patch0197: 0197-multipath-tools-update-NFINIDAT-InfiniBox-config-in-.patch
|
||||
Patch0198: 0198-multipathd-print-path-offline-message-even-without-a.patch
|
||||
Patch0199: 0199-libmultipath-improve-cleanup-of-uevent-queues-on-exi.patch
|
||||
Patch0200: 0200-uevent_dispatch-use-while-in-wait-loop.patch
|
||||
Patch0201: 0201-libmultipath-uevent_dispatch-process-uevents-one-by-.patch
|
||||
Patch0202: 0202-libmultipath-uevent_listen-don-t-delay-uevents.patch
|
||||
Patch0203: 0203-libmultipath-uevent-use-struct-to-pass-parameters-ar.patch
|
||||
Patch0204: 0204-libmultipath-is_uevent_busy-check-servicing_uev-unde.patch
|
||||
Patch0205: 0205-multipathd-make-multipathd-show-status-busy-checker-.patch
|
||||
Patch0206: 0206-libmultipath-add-purge_disconnected-configuration-op.patch
|
||||
Patch0207: 0207-multipathd-implement-purge-functionality-for-disconn.patch
|
||||
Patch0208: 0208-libmpathpersist-fix-register-retry-status-checking.patch
|
||||
Patch0209: 0209-multipathd-remember-number-of-registered-keys-when-i.patch
|
||||
Patch0210: 0210-libmpathpersist-fix-code-for-skipping-multipathd-pat.patch
|
||||
|
||||
# runtime
|
||||
Requires: %{name}-libs = %{version}-%{release}
|
||||
@ -362,6 +422,100 @@ fi
|
||||
%{_pkgconfdir}/libdmmp.pc
|
||||
|
||||
%changelog
|
||||
* Thu Feb 19 2026 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-45
|
||||
- Add 0208-libmpathpersist-fix-register-retry-status-checking.patch
|
||||
- Add 0209-multipathd-remember-number-of-registered-keys-when-i.patch
|
||||
- Add 0210-libmpathpersist-fix-code-for-skipping-multipathd-pat.patch
|
||||
* Fixes RHEL-148462 ("Improve multipathd's handling of updating
|
||||
persistent reservations on restored paths. [rhel-9]")
|
||||
- Resolves: RHEL-148462
|
||||
|
||||
* Thu Jan 29 2026 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-44
|
||||
- Add 0206-libmultipath-add-purge_disconnected-configuration-op.patch
|
||||
- Add 0207-multipathd-implement-purge-functionality-for-disconn.patch
|
||||
* Fixes RHEL-141291 ("Add purge_disconnected support to multipathd
|
||||
[rhel-9]")
|
||||
- Resolves: RHEL-141291
|
||||
|
||||
* Thu Jan 22 2026 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-43
|
||||
- Add 0198-multipathd-print-path-offline-message-even-without-a.patch
|
||||
* Fixes RHEL-133814 ("log_checker_err is not printing messages
|
||||
repeatedly for failed path [rhel-9]")
|
||||
- Add 0199-libmultipath-improve-cleanup-of-uevent-queues-on-exi.patch
|
||||
- Add 0200-uevent_dispatch-use-while-in-wait-loop.patch
|
||||
- Add 0201-libmultipath-uevent_dispatch-process-uevents-one-by-.patch
|
||||
- Add 0202-libmultipath-uevent_listen-don-t-delay-uevents.patch
|
||||
- Add 0203-libmultipath-uevent-use-struct-to-pass-parameters-ar.patch
|
||||
- Add 0204-libmultipath-is_uevent_busy-check-servicing_uev-unde.patch
|
||||
- Add 0205-multipathd-make-multipathd-show-status-busy-checker-.patch
|
||||
* Fixes RHEL-135904 (VM reboot in RHOSP environment fails with error
|
||||
"Could not open '/dev/dm-95': No such file or directory")
|
||||
- Resolves: RHEL-133814
|
||||
- Resolves: RHEL-135904
|
||||
|
||||
* Wed Nov 19 2025 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-42
|
||||
- Add 0197-multipath-tools-update-NFINIDAT-InfiniBox-config-in-.patch
|
||||
* Fixes RHEL-128396 ("Update the multipath.conf stanza for Infinidat
|
||||
storage [rhel-9]")
|
||||
- Resolves: RHEL-128396
|
||||
|
||||
* Tue Nov 11 2025 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-41
|
||||
- Add 0195-multipathd-Fix-race-while-registering-PR-key.patch
|
||||
- Add 0196-mpathpersist-Fix-REPORT-CAPABILITIES-output.patch
|
||||
* Fixes RHEL-118515 ("There are many bugs in multipath's persistent
|
||||
reservation handling")
|
||||
- Resolves: RHEL-118515
|
||||
|
||||
* Wed Oct 1 2025 Benjamin Marzinski <bmarzins@redhat.com> - 0.8.7-40
|
||||
- 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
|
||||
* Fixes RHEL-118515 ("There are many bugs in multipath's persistent
|
||||
reservation handling")
|
||||
- Resolves: RHEL-118515
|
||||
|
||||
* 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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user