From: Trond Myklebust <trond.mykleb...@hammerspace.com>

[ Upstream commit aeabb3c96186a0f944fc2b1f25c84d5eb3a93fa9 ]

Fix a deadlock whereby the NFSv4 state manager can get stuck in the
delegation return code, waiting for a layout return to complete in
another thread. If the server reboots before that other thread
completes, then we need to be able to start a second state
manager thread in order to perform recovery.

Signed-off-by: Trond Myklebust <trond.mykleb...@hammerspace.com>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 fs/nfs/nfs4_fs.h   |  2 ++
 fs/nfs/nfs4state.c | 16 +++++++++++-----
 2 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 3a6904173214..63287d911c08 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -41,6 +41,8 @@ enum nfs4_client_state {
        NFS4CLNT_MOVED,
        NFS4CLNT_LEASE_MOVED,
        NFS4CLNT_DELEGATION_EXPIRED,
+       NFS4CLNT_RUN_MANAGER,
+       NFS4CLNT_DELEGRETURN_RUNNING,
 };
 
 #define NFS4_RENEW_TIMEOUT             0x01
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 18920152da14..d2f645d34eb1 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -1210,6 +1210,7 @@ void nfs4_schedule_state_manager(struct nfs_client *clp)
        struct task_struct *task;
        char buf[INET6_ADDRSTRLEN + sizeof("-manager") + 1];
 
+       set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
        if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
                return;
        __module_get(THIS_MODULE);
@@ -2485,6 +2486,7 @@ static void nfs4_state_manager(struct nfs_client *clp)
 
        /* Ensure exclusive access to NFSv4 state */
        do {
+               clear_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
                if (test_bit(NFS4CLNT_PURGE_STATE, &clp->cl_state)) {
                        section = "purge state";
                        status = nfs4_purge_lease(clp);
@@ -2575,14 +2577,18 @@ static void nfs4_state_manager(struct nfs_client *clp)
                }
 
                nfs4_end_drain_session(clp);
-               if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
-                       nfs_client_return_marked_delegations(clp);
-                       continue;
+               nfs4_clear_state_manager_bit(clp);
+
+               if (!test_and_set_bit(NFS4CLNT_DELEGRETURN_RUNNING, 
&clp->cl_state)) {
+                       if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, 
&clp->cl_state)) {
+                               nfs_client_return_marked_delegations(clp);
+                               set_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state);
+                       }
+                       clear_bit(NFS4CLNT_DELEGRETURN_RUNNING, &clp->cl_state);
                }
 
-               nfs4_clear_state_manager_bit(clp);
                /* Did we race with an attempt to give us more work? */
-               if (clp->cl_state == 0)
+               if (!test_bit(NFS4CLNT_RUN_MANAGER, &clp->cl_state))
                        return;
                if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) 
!= 0)
                        return;
-- 
2.17.1

Reply via email to