nfs41: nfs41: fix state manager deadlock in session reset
authorAndy Adamson <andros@netapp.com>
Fri, 4 Dec 2009 20:55:38 +0000 (15:55 -0500)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Fri, 4 Dec 2009 20:55:38 +0000 (15:55 -0500)
If the session is reset during state recovery, the state manager thread can
sleep on the slot_tbl_waitq causing a deadlock.

Add a completion framework to the session.  Have the state manager thread set
a new session state (NFS4CLNT_SESSION_DRAINING) and wait for the session slot
table to drain.

Signal the state manager thread in nfs41_sequence_free_slot when the
NFS4CLNT_SESSION_DRAINING bit is set and the session is drained.

Reported-by: Trond Myklebust <trond@netapp.com>
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4state.c
include/linux/nfs_fs_sb.h

index e9ecd6bf9257895c644fa115f4e35cb62f7a9c5d..5c7740178a08635b8d7f8527c3c8f73156716d3a 100644 (file)
@@ -45,6 +45,7 @@ enum nfs4_client_state {
        NFS4CLNT_RECLAIM_NOGRACE,
        NFS4CLNT_DELEGRETURN,
        NFS4CLNT_SESSION_RESET,
+       NFS4CLNT_SESSION_DRAINING,
 };
 
 /*
index fb62919f7f2ca5e4c4bc152a3cf5b6ba8aae494a..c1bc9cad5e859d2ba4b3c2a12958d4f13b232ffe 100644 (file)
@@ -361,6 +361,16 @@ void nfs41_sequence_free_slot(const struct nfs_client *clp,
        }
        nfs4_free_slot(tbl, res->sr_slotid);
        res->sr_slotid = NFS4_MAX_SLOT_TABLE;
+
+       /* Signal state manager thread if session is drained */
+       if (test_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state)) {
+               spin_lock(&tbl->slot_tbl_lock);
+               if (tbl->highest_used_slotid == -1) {
+                       dprintk("%s COMPLETE: Session Drained\n", __func__);
+                       complete(&clp->cl_session->complete);
+               }
+               spin_unlock(&tbl->slot_tbl_lock);
+       }
 }
 
 static void nfs41_sequence_done(struct nfs_client *clp,
@@ -457,15 +467,11 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
 
        spin_lock(&tbl->slot_tbl_lock);
        if (test_bit(NFS4CLNT_SESSION_RESET, &session->clp->cl_state)) {
-               if (tbl->highest_used_slotid != -1) {
-                       rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
-                       spin_unlock(&tbl->slot_tbl_lock);
-                       dprintk("<-- %s: Session reset: draining\n", __func__);
-                       return -EAGAIN;
-               }
-
-               /* The slot table is empty; start the reset thread */
-               dprintk("%s Session Reset\n", __func__);
+               /*
+                * The state manager will wait until the slot table is empty.
+                * Schedule the reset thread
+                */
+               dprintk("%s Schedule Session Reset\n", __func__);
                rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
                nfs4_schedule_state_manager(session->clp);
                spin_unlock(&tbl->slot_tbl_lock);
@@ -4506,6 +4512,7 @@ static int nfs4_reset_slot_tables(struct nfs4_session *session)
                        1);
        if (status)
                return status;
+       init_completion(&session->complete);
 
        status = nfs4_reset_slot_table(&session->bc_slot_table,
                        session->bc_attrs.max_reqs,
@@ -4608,6 +4615,7 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
         * nfs_client struct
         */
        clp->cl_cons_state = NFS_CS_SESSION_INITING;
+       init_completion(&session->complete);
 
        tbl = &session->fc_slot_table;
        spin_lock_init(&tbl->slot_tbl_lock);
index f56f6be5e3145c8ae64c03d9b22af944b001c7ce..3c1433598b60296e847f61549c82a4213068e1ea 100644 (file)
@@ -1181,8 +1181,23 @@ static void nfs4_session_recovery_handle_error(struct nfs_client *clp, int err)
 
 static int nfs4_reset_session(struct nfs_client *clp)
 {
+       struct nfs4_session *ses = clp->cl_session;
+       struct nfs4_slot_table *tbl = &ses->fc_slot_table;
        int status;
 
+       INIT_COMPLETION(ses->complete);
+       spin_lock(&tbl->slot_tbl_lock);
+       if (tbl->highest_used_slotid != -1) {
+               set_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state);
+               spin_unlock(&tbl->slot_tbl_lock);
+               status = wait_for_completion_interruptible(&ses->complete);
+               clear_bit(NFS4CLNT_SESSION_DRAINING, &clp->cl_state);
+               if (status) /* -ERESTARTSYS */
+                       goto out;
+       } else {
+               spin_unlock(&tbl->slot_tbl_lock);
+       }
+
        status = nfs4_proc_destroy_session(clp->cl_session);
        if (status && status != -NFS4ERR_BADSESSION &&
            status != -NFS4ERR_DEADSESSION) {
index 320569eabe3bcc88dea530ac8634d59223a80409..34fc6be5bfcfd95d03534b59687564fe82071042 100644 (file)
@@ -209,6 +209,7 @@ struct nfs4_session {
        unsigned long                   session_state;
        u32                             hash_alg;
        u32                             ssv_len;
+       struct completion               complete;
 
        /* The fore and back channel */
        struct nfs4_channel_attrs       fc_attrs;