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 <bmarz...@redhat.com>
---
 libmpathpersist/mpath_persist_int.c | 71 ++++++++++++++++++-----------
 1 file changed, 45 insertions(+), 26 deletions(-)

diff --git a/libmpathpersist/mpath_persist_int.c 
b/libmpathpersist/mpath_persist_int.c
index 71933fe5..ad98001d 100644
--- a/libmpathpersist/mpath_persist_int.c
+++ b/libmpathpersist/mpath_persist_int.c
@@ -344,13 +344,13 @@ static int mpath_prout_reg(struct multipath *mpp,int 
rq_servact, int rq_scope,
        int i, j, k;
        struct pathgroup *pgp = NULL;
        struct path *pp = NULL;
-       int rollback = 0;
+       bool can_retry = false;
+       bool need_retry = false;
        int active_pathcount=0;
        int rc;
        int count=0;
        int status = MPATH_PR_SUCCESS;
        int all_tg_pt;
-       uint64_t sa_key = 0;
 
        if (!mpp)
                return MPATH_PR_DMMP_ERROR;
@@ -439,43 +439,62 @@ static int mpath_prout_reg(struct multipath *mpp,int 
rq_servact, int rq_scope,
                                condlog (0, "%s: Thread[%d] failed to join 
thread %d", mpp->wwid, i, rc);
                        }
                }
-               if (!rollback && (thread[i].param.status == 
MPATH_PR_RESERV_CONFLICT)){
-                       rollback = 1;
-                       sa_key = get_unaligned_be64(&paramp->sa_key[0]);
-                       status = MPATH_PR_RESERV_CONFLICT ;
-               }
-               if (!rollback && (status == MPATH_PR_SUCCESS)){
+               /*
+                * We only retry if there is at least one registration that
+                * returned a reservation conflict (which we need to retry)
+                * and at least one registration the return success, so we
+                * know that the command worked on some of the paths. If
+                * the registation fails on all paths, then it wasn't a
+                * valid request, so there's no need to retry.
+                */
+               if (thread[i].param.status == MPATH_PR_RESERV_CONFLICT)
+                       need_retry = true;
+               else if (thread[i].param.status == MPATH_PR_SUCCESS)
+                       can_retry = true;
+               else if (status == MPATH_PR_SUCCESS)
                        status = thread[i].param.status;
-               }
        }
-       if (rollback && ((rq_servact == MPATH_PROUT_REG_SA) && sa_key != 0 )){
-               condlog (3, "%s: ERROR: initiating pr out rollback", mpp->wwid);
-               memcpy(&paramp->key, &paramp->sa_key, 8);
-               memset(&paramp->sa_key, 0, 8);
-               for( i=0 ; i < count ; i++){
-                       if(thread[i].param.status == MPATH_PR_SUCCESS) {
-                               rc = pthread_create(&thread[i].id, &attr, 
mpath_prout_pthread_fn,
-                                               (void *)(&thread[i].param));
-                               if (rc){
-                                       condlog (0, "%s: failed to create 
thread for rollback. %d",  mpp->wwid, rc);
-                                       thread[i].param.status = 
MPATH_PR_THREAD_ERROR;
-                               }
-                       } else
+       if (need_retry && can_retry && rq_servact == MPATH_PROUT_REG_SA &&
+           status == MPATH_PR_SUCCESS) {
+               condlog(3, "%s: ERROR: initiating pr out retry", mpp->wwid);
+               for (i = 0; i < count; i++) {
+                       if (thread[i].param.status != MPATH_PR_RESERV_CONFLICT) 
{
                                thread[i].param.status = MPATH_PR_SKIP;
+                               continue;
+                       }
+                       /*
+                        * retry using MPATH_PROUT_REG_IGN_SA to avoid
+                        * conflicts. We already know that some paths
+                        * succeeded using MPATH_PROUT_REG_SA.
+                        */
+                       thread[i].param.rq_servact = MPATH_PROUT_REG_IGN_SA;
+                       rc = pthread_create(&thread[i].id, &attr,
+                                           mpath_prout_pthread_fn,
+                                           (void *)(&thread[i].param));
+                       if (rc) {
+                               condlog(0, "%s: failed to create thread for 
retry. %d",
+                                       mpp->wwid, rc);
+                               thread[i].param.status = MPATH_PR_THREAD_ERROR;
+                       }
                }
-               for(i=0; i < count ; i++){
+               for (i = 0; i < count; i++) {
                        if (thread[i].param.status != MPATH_PR_SKIP &&
                            thread[i].param.status != MPATH_PR_THREAD_ERROR) {
                                rc = pthread_join(thread[i].id, NULL);
-                               if (rc){
-                                       condlog (3, "%s: failed to join thread 
while rolling back %d",
-                                                mpp->wwid, i);
+                               if (rc) {
+                                       condlog(3, "%s: failed to join thread 
while retrying %d",
+                                               mpp->wwid, i);
                                }
+                               if (status == MPATH_PR_SUCCESS)
+                                       status = thread[i].param.status;
                        }
                }
+               need_retry = false;
        }
 
        pthread_attr_destroy(&attr);
+       if (need_retry)
+               status = MPATH_PR_RESERV_CONFLICT;
        if (status == MPATH_PR_SUCCESS)
                preempt_missing_path(mpp, paramp->key, paramp->sa_key, noisy);
        return (status == MPATH_PR_RETRYABLE_ERROR) ? MPATH_PR_OTHER : status;
-- 
2.48.1


Reply via email to