Skip to content

Commit

Permalink
nfs41: nfs41: fix state manager deadlock in session reset
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Andy Adamson authored and Trond Myklebust committed Dec 4, 2009
1 parent 05f0d23 commit ea028ac
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 9 deletions.
1 change: 1 addition & 0 deletions fs/nfs/nfs4_fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ enum nfs4_client_state {
NFS4CLNT_RECLAIM_NOGRACE,
NFS4CLNT_DELEGRETURN,
NFS4CLNT_SESSION_RESET,
NFS4CLNT_SESSION_DRAINING,
};

/*
Expand Down
26 changes: 17 additions & 9 deletions fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand Down
15 changes: 15 additions & 0 deletions fs/nfs/nfs4state.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
1 change: 1 addition & 0 deletions include/linux/nfs_fs_sb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit ea028ac

Please sign in to comment.