diff --git a/SOURCES/0150-libmpathpersist-retry-commands-on-other-paths-in-mpa.patch b/SOURCES/0150-libmpathpersist-retry-commands-on-other-paths-in-mpa.patch new file mode 100644 index 0000000..207a743 --- /dev/null +++ b/SOURCES/0150-libmpathpersist-retry-commands-on-other-paths-in-mpa.patch @@ -0,0 +1,151 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +Reviewed-by: Martin Wilck +--- + 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) + { diff --git a/SOURCES/0151-libmpathpersist-check-released-key-against-the-reser.patch b/SOURCES/0151-libmpathpersist-check-released-key-against-the-reser.patch new file mode 100644 index 0000000..b1ba65d --- /dev/null +++ b/SOURCES/0151-libmpathpersist-check-released-key-against-the-reser.patch @@ -0,0 +1,41 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +Reviewed-by: Martin Wilck +--- + 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){ diff --git a/SOURCES/0152-multipathd-remove-thread-from-mpath_pr_event_handle.patch b/SOURCES/0152-multipathd-remove-thread-from-mpath_pr_event_handle.patch new file mode 100644 index 0000000..2492189 --- /dev/null +++ b/SOURCES/0152-multipathd-remove-thread-from-mpath_pr_event_handle.patch @@ -0,0 +1,112 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); diff --git a/SOURCES/0153-libmpathpersist-remove-uneeded-wrapper-function.patch b/SOURCES/0153-libmpathpersist-remove-uneeded-wrapper-function.patch new file mode 100644 index 0000000..304e582 --- /dev/null +++ b/SOURCES/0153-libmpathpersist-remove-uneeded-wrapper-function.patch @@ -0,0 +1,60 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); diff --git a/SOURCES/0154-libmpathpersist-reduce-log-level-for-persistent-rese.patch b/SOURCES/0154-libmpathpersist-reduce-log-level-for-persistent-rese.patch new file mode 100644 index 0000000..489692e --- /dev/null +++ b/SOURCES/0154-libmpathpersist-reduce-log-level-for-persistent-rese.patch @@ -0,0 +1,93 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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: diff --git a/SOURCES/0155-libmpathpersist-remove-pointless-update_map_pr-ret-v.patch b/SOURCES/0155-libmpathpersist-remove-pointless-update_map_pr-ret-v.patch new file mode 100644 index 0000000..67ba607 --- /dev/null +++ b/SOURCES/0155-libmpathpersist-remove-pointless-update_map_pr-ret-v.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); diff --git a/SOURCES/0156-multipathd-use-update_map_pr-in-mpath_pr_event_handl.patch b/SOURCES/0156-multipathd-use-update_map_pr-in-mpath_pr_event_handl.patch new file mode 100644 index 0000000..5537e05 --- /dev/null +++ b/SOURCES/0156-multipathd-use-update_map_pr-in-mpath_pr_event_handl.patch @@ -0,0 +1,227 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); diff --git a/SOURCES/0157-libmpathpersist-limit-changing-prflag-in-update_map_.patch b/SOURCES/0157-libmpathpersist-limit-changing-prflag-in-update_map_.patch new file mode 100644 index 0000000..9201d4b --- /dev/null +++ b/SOURCES/0157-libmpathpersist-limit-changing-prflag-in-update_map_.patch @@ -0,0 +1,76 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 ) + { diff --git a/SOURCES/0158-multipathd-Don-t-call-update_map_pr-unnecessarily.patch b/SOURCES/0158-multipathd-Don-t-call-update_map_pr-unnecessarily.patch new file mode 100644 index 0000000..8ee029d --- /dev/null +++ b/SOURCES/0158-multipathd-Don-t-call-update_map_pr-unnecessarily.patch @@ -0,0 +1,76 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); + } + + /* diff --git a/SOURCES/0159-libmpathpersist-remove-useless-function-send_prout_a.patch b/SOURCES/0159-libmpathpersist-remove-useless-function-send_prout_a.patch new file mode 100644 index 0000000..a34c81d --- /dev/null +++ b/SOURCES/0159-libmpathpersist-remove-useless-function-send_prout_a.patch @@ -0,0 +1,86 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); diff --git a/SOURCES/0160-libmpathpersist-fix-memory-leak-in-mpath_prout_rel.patch b/SOURCES/0160-libmpathpersist-fix-memory-leak-in-mpath_prout_rel.patch new file mode 100644 index 0000000..ed622f7 --- /dev/null +++ b/SOURCES/0160-libmpathpersist-fix-memory-leak-in-mpath_prout_rel.patch @@ -0,0 +1,72 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +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 +Reviewed-by: Benjamin Marzinski +Signed-off-by: Benjamin Marzinski +--- + 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); diff --git a/SOURCES/0161-limpathpersist-redesign-failed-release-workaround.patch b/SOURCES/0161-limpathpersist-redesign-failed-release-workaround.patch new file mode 100644 index 0000000..1c74724 --- /dev/null +++ b/SOURCES/0161-limpathpersist-redesign-failed-release-workaround.patch @@ -0,0 +1,307 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 { diff --git a/SOURCES/0162-libmpathpersist-fail-the-release-if-all-threads-fail.patch b/SOURCES/0162-libmpathpersist-fail-the-release-if-all-threads-fail.patch new file mode 100644 index 0000000..82c4fa7 --- /dev/null +++ b/SOURCES/0162-libmpathpersist-fail-the-release-if-all-threads-fail.patch @@ -0,0 +1,51 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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){ diff --git a/SOURCES/0163-limpathpersist-Handle-changing-key-corner-case.patch b/SOURCES/0163-limpathpersist-Handle-changing-key-corner-case.patch new file mode 100644 index 0000000..51db209 --- /dev/null +++ b/SOURCES/0163-limpathpersist-Handle-changing-key-corner-case.patch @@ -0,0 +1,124 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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; diff --git a/SOURCES/0164-libmpathpersist-fix-command-keyword-ordering.patch b/SOURCES/0164-libmpathpersist-fix-command-keyword-ordering.patch new file mode 100644 index 0000000..d32555b --- /dev/null +++ b/SOURCES/0164-libmpathpersist-fix-command-keyword-ordering.patch @@ -0,0 +1,70 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +Cc: lixiaokeng +Signed-off-by: Benjamin Marzinski +Reviewed-by: Martin Wilck +--- + 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); + } diff --git a/SOURCES/0165-libmpathpersist-use-conf-timeout-for-updating-persis.patch b/SOURCES/0165-libmpathpersist-use-conf-timeout-for-updating-persis.patch new file mode 100644 index 0000000..dfdc4b0 --- /dev/null +++ b/SOURCES/0165-libmpathpersist-use-conf-timeout-for-updating-persis.patch @@ -0,0 +1,54 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Martin Wilck +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 +Reviewed-by: Benjamin Marzinski +Signed-off-by: Benjamin Marzinski +--- + 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 + #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; diff --git a/SOURCES/0166-libmapthpersist-Handle-REGISTER-AND-IGNORE-changing-.patch b/SOURCES/0166-libmapthpersist-Handle-REGISTER-AND-IGNORE-changing-.patch new file mode 100644 index 0000000..faa9893 --- /dev/null +++ b/SOURCES/0166-libmapthpersist-Handle-REGISTER-AND-IGNORE-changing-.patch @@ -0,0 +1,180 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 " command. + +Signed-off-by: Benjamin Marzinski +--- + 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); diff --git a/SOURCES/0167-libmultipath-rename-prflag_value-enums.patch b/SOURCES/0167-libmultipath-rename-prflag_value-enums.patch new file mode 100644 index 0000000..dc3aa53 --- /dev/null +++ b/SOURCES/0167-libmultipath-rename-prflag_value-enums.patch @@ -0,0 +1,242 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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)); diff --git a/SOURCES/0168-libmpathpersist-use-a-switch-statement-for-prout-com.patch b/SOURCES/0168-libmpathpersist-use-a-switch-statement-for-prout-com.patch new file mode 100644 index 0000000..77670f6 --- /dev/null +++ b/SOURCES/0168-libmpathpersist-use-a-switch-statement-for-prout-com.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); + } diff --git a/SOURCES/0169-libmpathpersist-Add-safety-check-for-preempting-on-k.patch b/SOURCES/0169-libmpathpersist-Add-safety-check-for-preempting-on-k.patch new file mode 100644 index 0000000..e24a1d5 --- /dev/null +++ b/SOURCES/0169-libmpathpersist-Add-safety-check-for-preempting-on-k.patch @@ -0,0 +1,477 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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; diff --git a/SOURCES/0170-limpathpersist-remove-update_map_pr-code-for-NULL-pp.patch b/SOURCES/0170-limpathpersist-remove-update_map_pr-code-for-NULL-pp.patch new file mode 100644 index 0000000..93de0f9 --- /dev/null +++ b/SOURCES/0170-limpathpersist-remove-update_map_pr-code-for-NULL-pp.patch @@ -0,0 +1,37 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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) diff --git a/SOURCES/0171-libmpathpersist-move-update_map_pr-to-multipathd.patch b/SOURCES/0171-libmpathpersist-move-update_map_pr-to-multipathd.patch new file mode 100644 index 0000000..6ae0623 --- /dev/null +++ b/SOURCES/0171-libmpathpersist-move-update_map_pr-to-multipathd.patch @@ -0,0 +1,208 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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; diff --git a/SOURCES/0172-multipathd-clean-up-update_map_pr-and-mpath_pr_event.patch b/SOURCES/0172-multipathd-clean-up-update_map_pr-and-mpath_pr_event.patch new file mode 100644 index 0000000..d3dda74 --- /dev/null +++ b/SOURCES/0172-multipathd-clean-up-update_map_pr-and-mpath_pr_event.patch @@ -0,0 +1,150 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); + } diff --git a/SOURCES/0173-libmpathpersist-clean-up-duplicate-function-declarat.patch b/SOURCES/0173-libmpathpersist-clean-up-duplicate-function-declarat.patch new file mode 100644 index 0000000..9d6b4ff --- /dev/null +++ b/SOURCES/0173-libmpathpersist-clean-up-duplicate-function-declarat.patch @@ -0,0 +1,78 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +Date: Tue, 15 Jul 2025 14:50:55 -0400 +Subject: [PATCH] libmpathpersist: clean up duplicate function declarations + +Signed-off-by: Benjamin Marzinski +--- + 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 + #include + #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); diff --git a/SOURCES/0174-multipathd-wrap-setting-and-unsetting-prflag.patch b/SOURCES/0174-multipathd-wrap-setting-and-unsetting-prflag.patch new file mode 100644 index 0000000..3b68a8f --- /dev/null +++ b/SOURCES/0174-multipathd-wrap-setting-and-unsetting-prflag.patch @@ -0,0 +1,148 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 */ diff --git a/SOURCES/0175-multipathd-unregister-PR-key-when-path-is-restored-i.patch b/SOURCES/0175-multipathd-unregister-PR-key-when-path-is-restored-i.patch new file mode 100644 index 0000000..0f13a0b --- /dev/null +++ b/SOURCES/0175-multipathd-unregister-PR-key-when-path-is-restored-i.patch @@ -0,0 +1,141 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); + } + } diff --git a/SOURCES/0176-libmpathpersist-Fix-up-reservation_key-checking.patch b/SOURCES/0176-libmpathpersist-Fix-up-reservation_key-checking.patch new file mode 100644 index 0000000..e19a4fa --- /dev/null +++ b/SOURCES/0176-libmpathpersist-Fix-up-reservation_key-checking.patch @@ -0,0 +1,108 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 diff --git a/SOURCES/0177-libmpathpersist-change-how-reservation-conflicts-are.patch b/SOURCES/0177-libmpathpersist-change-how-reservation-conflicts-are.patch new file mode 100644 index 0000000..145cd28 --- /dev/null +++ b/SOURCES/0177-libmpathpersist-change-how-reservation-conflicts-are.patch @@ -0,0 +1,137 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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; diff --git a/SOURCES/0178-libmpathpersist-Clear-prkey-in-multipathd-before-unr.patch b/SOURCES/0178-libmpathpersist-Clear-prkey-in-multipathd-before-unr.patch new file mode 100644 index 0000000..d2088fb --- /dev/null +++ b/SOURCES/0178-libmpathpersist-Clear-prkey-in-multipathd-before-unr.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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: diff --git a/SOURCES/0179-libmpathpersist-only-clear-the-key-if-we-are-using-t.patch b/SOURCES/0179-libmpathpersist-only-clear-the-key-if-we-are-using-t.patch new file mode 100644 index 0000000..440a201 --- /dev/null +++ b/SOURCES/0179-libmpathpersist-only-clear-the-key-if-we-are-using-t.patch @@ -0,0 +1,27 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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: diff --git a/SOURCES/0180-libmpathpersist-Restore-old-reservation-key-on-failu.patch b/SOURCES/0180-libmpathpersist-Restore-old-reservation-key-on-failu.patch new file mode 100644 index 0000000..6eea1d0 --- /dev/null +++ b/SOURCES/0180-libmpathpersist-Restore-old-reservation-key-on-failu.patch @@ -0,0 +1,50 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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) + { diff --git a/SOURCES/0181-libmpatpersist-update-reservation-key-before-checkin.patch b/SOURCES/0181-libmpatpersist-update-reservation-key-before-checkin.patch new file mode 100644 index 0000000..cdb5b2d --- /dev/null +++ b/SOURCES/0181-libmpatpersist-update-reservation-key-before-checkin.patch @@ -0,0 +1,169 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); + diff --git a/SOURCES/0182-libmpathpersist-retry-on-conflicts-in-mpath_prout_co.patch b/SOURCES/0182-libmpathpersist-retry-on-conflicts-in-mpath_prout_co.patch new file mode 100644 index 0000000..bd39503 --- /dev/null +++ b/SOURCES/0182-libmpathpersist-retry-on-conflicts-in-mpath_prout_co.patch @@ -0,0 +1,78 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); diff --git a/SOURCES/0183-libmpathpersist-Don-t-always-fail-registrations-for-.patch b/SOURCES/0183-libmpathpersist-Don-t-always-fail-registrations-for-.patch new file mode 100644 index 0000000..dd04657 --- /dev/null +++ b/SOURCES/0183-libmpathpersist-Don-t-always-fail-registrations-for-.patch @@ -0,0 +1,151 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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) diff --git a/SOURCES/0184-libmpathpersist-Don-t-try-release-workaround-for-inv.patch b/SOURCES/0184-libmpathpersist-Don-t-try-release-workaround-for-inv.patch new file mode 100644 index 0000000..aa086d0 --- /dev/null +++ b/SOURCES/0184-libmpathpersist-Don-t-try-release-workaround-for-inv.patch @@ -0,0 +1,46 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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 diff --git a/SOURCES/0185-libmpathpersist-Don-t-fail-RESERVE-commands-unnecess.patch b/SOURCES/0185-libmpathpersist-Don-t-fail-RESERVE-commands-unnecess.patch new file mode 100644 index 0000000..9d0f770 --- /dev/null +++ b/SOURCES/0185-libmpathpersist-Don-t-fail-RESERVE-commands-unnecess.patch @@ -0,0 +1,43 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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; + } diff --git a/SOURCES/0186-libmpathpersist-reregister-keys-when-self-preempting.patch b/SOURCES/0186-libmpathpersist-reregister-keys-when-self-preempting.patch new file mode 100644 index 0000000..e7d5c3f --- /dev/null +++ b/SOURCES/0186-libmpathpersist-reregister-keys-when-self-preempting.patch @@ -0,0 +1,203 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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) diff --git a/SOURCES/0187-libmpathpersist-handle-updating-key-race-condition.patch b/SOURCES/0187-libmpathpersist-handle-updating-key-race-condition.patch new file mode 100644 index 0000000..e93d1ac --- /dev/null +++ b/SOURCES/0187-libmpathpersist-handle-updating-key-race-condition.patch @@ -0,0 +1,108 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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; + } + diff --git a/SOURCES/0188-libmpathpersist-handle-preempting-all-registrants-re.patch b/SOURCES/0188-libmpathpersist-handle-preempting-all-registrants-re.patch new file mode 100644 index 0000000..a320788 --- /dev/null +++ b/SOURCES/0188-libmpathpersist-handle-preempting-all-registrants-re.patch @@ -0,0 +1,94 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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) { diff --git a/SOURCES/0189-libmpathpersist-Fix-REGISTER-AND-IGNORE-while-holdin.patch b/SOURCES/0189-libmpathpersist-Fix-REGISTER-AND-IGNORE-while-holdin.patch new file mode 100644 index 0000000..98221e5 --- /dev/null +++ b/SOURCES/0189-libmpathpersist-Fix-REGISTER-AND-IGNORE-while-holdin.patch @@ -0,0 +1,74 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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) + { diff --git a/SOURCES/0190-libmpathpersist-Handle-RESERVE-with-reservation-held.patch b/SOURCES/0190-libmpathpersist-Handle-RESERVE-with-reservation-held.patch new file mode 100644 index 0000000..e6f4d6c --- /dev/null +++ b/SOURCES/0190-libmpathpersist-Handle-RESERVE-with-reservation-held.patch @@ -0,0 +1,168 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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; diff --git a/SOURCES/0191-libmpathpersist-use-check_holding_reservation-in-mpa.patch b/SOURCES/0191-libmpathpersist-use-check_holding_reservation-in-mpa.patch new file mode 100644 index 0000000..02661cf --- /dev/null +++ b/SOURCES/0191-libmpathpersist-use-check_holding_reservation-in-mpa.patch @@ -0,0 +1,68 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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; + } + diff --git a/SOURCES/0192-libmpathpersist-Fix-unregistering-while-holding-the-.patch b/SOURCES/0192-libmpathpersist-Fix-unregistering-while-holding-the-.patch new file mode 100644 index 0000000..c2ea130 --- /dev/null +++ b/SOURCES/0192-libmpathpersist-Fix-unregistering-while-holding-the-.patch @@ -0,0 +1,287 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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); diff --git a/SOURCES/0193-libmpathpersist-Fix-race-between-restoring-a-path-an.patch b/SOURCES/0193-libmpathpersist-Fix-race-between-restoring-a-path-an.patch new file mode 100644 index 0000000..899308d --- /dev/null +++ b/SOURCES/0193-libmpathpersist-Fix-race-between-restoring-a-path-an.patch @@ -0,0 +1,215 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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; + } diff --git a/SOURCES/0194-multipathd-Fix-tracking-of-old-PR-key.patch b/SOURCES/0194-multipathd-Fix-tracking-of-old-PR-key.patch new file mode 100644 index 0000000..30a0c84 --- /dev/null +++ b/SOURCES/0194-multipathd-Fix-tracking-of-old-PR-key.patch @@ -0,0 +1,49 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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) diff --git a/SOURCES/0195-multipathd-Fix-race-while-registering-PR-key.patch b/SOURCES/0195-multipathd-Fix-race-while-registering-PR-key.patch new file mode 100644 index 0000000..8e2a84f --- /dev/null +++ b/SOURCES/0195-multipathd-Fix-race-while-registering-PR-key.patch @@ -0,0 +1,246 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +Reviewed-by: Martin Wilck +--- + 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 */ diff --git a/SOURCES/0196-mpathpersist-Fix-REPORT-CAPABILITIES-output.patch b/SOURCES/0196-mpathpersist-Fix-REPORT-CAPABILITIES-output.patch new file mode 100644 index 0000000..060b442 --- /dev/null +++ b/SOURCES/0196-mpathpersist-Fix-REPORT-CAPABILITIES-output.patch @@ -0,0 +1,108 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Benjamin Marzinski +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 +--- + 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)); + } + } + diff --git a/SPECS/device-mapper-multipath.spec b/SPECS/device-mapper-multipath.spec index a2d9752..44fac7e 100644 --- a/SPECS/device-mapper-multipath.spec +++ b/SPECS/device-mapper-multipath.spec @@ -1,6 +1,6 @@ Name: device-mapper-multipath Version: 0.8.7 -Release: 39%{?dist} +Release: 39%{?dist}.1 Summary: Tools to manage multipath devices using device-mapper License: GPLv2 URL: http://christophe.varoqui.free.fr/ @@ -159,6 +159,53 @@ Patch0146: 0146-multipath-tools-add-HPE-as-vendor-for-OPEN-XP8-array.patch Patch0147: 0147-multipath-tools-add-HP-HSVX740-to-hwtable.patch Patch0148: 0148-multipath-tools-add-DellEMC-ME5-PowerVault-ME5-to-ha.patch Patch0149: 0149-multipath-tools-add-HPE-MSA-Gen7-2070-2072-to-hwtabl.patch +Patch0150: 0150-libmpathpersist-retry-commands-on-other-paths-in-mpa.patch +Patch0151: 0151-libmpathpersist-check-released-key-against-the-reser.patch +Patch0152: 0152-multipathd-remove-thread-from-mpath_pr_event_handle.patch +Patch0153: 0153-libmpathpersist-remove-uneeded-wrapper-function.patch +Patch0154: 0154-libmpathpersist-reduce-log-level-for-persistent-rese.patch +Patch0155: 0155-libmpathpersist-remove-pointless-update_map_pr-ret-v.patch +Patch0156: 0156-multipathd-use-update_map_pr-in-mpath_pr_event_handl.patch +Patch0157: 0157-libmpathpersist-limit-changing-prflag-in-update_map_.patch +Patch0158: 0158-multipathd-Don-t-call-update_map_pr-unnecessarily.patch +Patch0159: 0159-libmpathpersist-remove-useless-function-send_prout_a.patch +Patch0160: 0160-libmpathpersist-fix-memory-leak-in-mpath_prout_rel.patch +Patch0161: 0161-limpathpersist-redesign-failed-release-workaround.patch +Patch0162: 0162-libmpathpersist-fail-the-release-if-all-threads-fail.patch +Patch0163: 0163-limpathpersist-Handle-changing-key-corner-case.patch +Patch0164: 0164-libmpathpersist-fix-command-keyword-ordering.patch +Patch0165: 0165-libmpathpersist-use-conf-timeout-for-updating-persis.patch +Patch0166: 0166-libmapthpersist-Handle-REGISTER-AND-IGNORE-changing-.patch +Patch0167: 0167-libmultipath-rename-prflag_value-enums.patch +Patch0168: 0168-libmpathpersist-use-a-switch-statement-for-prout-com.patch +Patch0169: 0169-libmpathpersist-Add-safety-check-for-preempting-on-k.patch +Patch0170: 0170-limpathpersist-remove-update_map_pr-code-for-NULL-pp.patch +Patch0171: 0171-libmpathpersist-move-update_map_pr-to-multipathd.patch +Patch0172: 0172-multipathd-clean-up-update_map_pr-and-mpath_pr_event.patch +Patch0173: 0173-libmpathpersist-clean-up-duplicate-function-declarat.patch +Patch0174: 0174-multipathd-wrap-setting-and-unsetting-prflag.patch +Patch0175: 0175-multipathd-unregister-PR-key-when-path-is-restored-i.patch +Patch0176: 0176-libmpathpersist-Fix-up-reservation_key-checking.patch +Patch0177: 0177-libmpathpersist-change-how-reservation-conflicts-are.patch +Patch0178: 0178-libmpathpersist-Clear-prkey-in-multipathd-before-unr.patch +Patch0179: 0179-libmpathpersist-only-clear-the-key-if-we-are-using-t.patch +Patch0180: 0180-libmpathpersist-Restore-old-reservation-key-on-failu.patch +Patch0181: 0181-libmpatpersist-update-reservation-key-before-checkin.patch +Patch0182: 0182-libmpathpersist-retry-on-conflicts-in-mpath_prout_co.patch +Patch0183: 0183-libmpathpersist-Don-t-always-fail-registrations-for-.patch +Patch0184: 0184-libmpathpersist-Don-t-try-release-workaround-for-inv.patch +Patch0185: 0185-libmpathpersist-Don-t-fail-RESERVE-commands-unnecess.patch +Patch0186: 0186-libmpathpersist-reregister-keys-when-self-preempting.patch +Patch0187: 0187-libmpathpersist-handle-updating-key-race-condition.patch +Patch0188: 0188-libmpathpersist-handle-preempting-all-registrants-re.patch +Patch0189: 0189-libmpathpersist-Fix-REGISTER-AND-IGNORE-while-holdin.patch +Patch0190: 0190-libmpathpersist-Handle-RESERVE-with-reservation-held.patch +Patch0191: 0191-libmpathpersist-use-check_holding_reservation-in-mpa.patch +Patch0192: 0192-libmpathpersist-Fix-unregistering-while-holding-the-.patch +Patch0193: 0193-libmpathpersist-Fix-race-between-restoring-a-path-an.patch +Patch0194: 0194-multipathd-Fix-tracking-of-old-PR-key.patch +Patch0195: 0195-multipathd-Fix-race-while-registering-PR-key.patch +Patch0196: 0196-mpathpersist-Fix-REPORT-CAPABILITIES-output.patch # runtime @@ -362,6 +409,58 @@ fi %{_pkgconfdir}/libdmmp.pc %changelog +* Tue Nov 11 2025 Benjamin Marzinski - 0.8.7-39.1 +- Add 0150-libmpathpersist-retry-commands-on-other-paths-in-mpa.patch +- Add 0151-libmpathpersist-check-released-key-against-the-reser.patch +- Add 0152-multipathd-remove-thread-from-mpath_pr_event_handle.patch +- Add 0153-libmpathpersist-remove-uneeded-wrapper-function.patch +- Add 0154-libmpathpersist-reduce-log-level-for-persistent-rese.patch +- Add 0155-libmpathpersist-remove-pointless-update_map_pr-ret-v.patch +- Add 0156-multipathd-use-update_map_pr-in-mpath_pr_event_handl.patch +- Add 0157-libmpathpersist-limit-changing-prflag-in-update_map_.patch +- Add 0158-multipathd-Don-t-call-update_map_pr-unnecessarily.patch +- Add 0159-libmpathpersist-remove-useless-function-send_prout_a.patch +- Add 0160-libmpathpersist-fix-memory-leak-in-mpath_prout_rel.patch +- Add 0161-limpathpersist-redesign-failed-release-workaround.patch +- Add 0162-libmpathpersist-fail-the-release-if-all-threads-fail.patch +- Add 0163-limpathpersist-Handle-changing-key-corner-case.patch +- Add 0164-libmpathpersist-fix-command-keyword-ordering.patch +- Add 0165-libmpathpersist-use-conf-timeout-for-updating-persis.patch +- Add 0166-libmapthpersist-Handle-REGISTER-AND-IGNORE-changing-.patch +- Add 0167-libmultipath-rename-prflag_value-enums.patch +- Add 0168-libmpathpersist-use-a-switch-statement-for-prout-com.patch +- Add 0169-libmpathpersist-Add-safety-check-for-preempting-on-k.patch +- Add 0170-limpathpersist-remove-update_map_pr-code-for-NULL-pp.patch +- Add 0171-libmpathpersist-move-update_map_pr-to-multipathd.patch +- Add 0172-multipathd-clean-up-update_map_pr-and-mpath_pr_event.patch +- Add 0173-libmpathpersist-clean-up-duplicate-function-declarat.patch +- Add 0174-multipathd-wrap-setting-and-unsetting-prflag.patch +- Add 0175-multipathd-unregister-PR-key-when-path-is-restored-i.patch +- Add 0176-libmpathpersist-Fix-up-reservation_key-checking.patch +- Add 0177-libmpathpersist-change-how-reservation-conflicts-are.patch +- Add 0178-libmpathpersist-Clear-prkey-in-multipathd-before-unr.patch +- Add 0179-libmpathpersist-only-clear-the-key-if-we-are-using-t.patch +- Add 0180-libmpathpersist-Restore-old-reservation-key-on-failu.patch +- Add 0181-libmpatpersist-update-reservation-key-before-checkin.patch +- Add 0182-libmpathpersist-retry-on-conflicts-in-mpath_prout_co.patch +- Add 0183-libmpathpersist-Don-t-always-fail-registrations-for-.patch +- Add 0184-libmpathpersist-Don-t-try-release-workaround-for-inv.patch +- Add 0185-libmpathpersist-Don-t-fail-RESERVE-commands-unnecess.patch +- Add 0186-libmpathpersist-reregister-keys-when-self-preempting.patch +- Add 0187-libmpathpersist-handle-updating-key-race-condition.patch +- Add 0188-libmpathpersist-handle-preempting-all-registrants-re.patch +- Add 0189-libmpathpersist-Fix-REGISTER-AND-IGNORE-while-holdin.patch +- Add 0190-libmpathpersist-Handle-RESERVE-with-reservation-held.patch +- Add 0191-libmpathpersist-use-check_holding_reservation-in-mpa.patch +- Add 0192-libmpathpersist-Fix-unregistering-while-holding-the-.patch +- Add 0193-libmpathpersist-Fix-race-between-restoring-a-path-an.patch +- Add 0194-multipathd-Fix-tracking-of-old-PR-key.patch +- Add 0195-multipathd-Fix-race-while-registering-PR-key.patch +- Add 0196-mpathpersist-Fix-REPORT-CAPABILITIES-output.patch + * Fixes RHEL-118723 ("There are many bugs in multipath's persistent + reservation handling [rhel-9.7.z]") +- Resolves: RHEL-118723 + * Mon Jul 14 2025 Benjamin Marzinski - 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