Commit ea028ac9 authored by Andy Adamson's avatar Andy Adamson Committed by Trond Myklebust

nfs41: nfs41: fix state manager deadlock in session reset

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: default avatarTrond Myklebust <trond@netapp.com>
Signed-off-by: default avatarAndy Adamson <andros@netapp.com>
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
parent 05f0d236
...@@ -45,6 +45,7 @@ enum nfs4_client_state { ...@@ -45,6 +45,7 @@ enum nfs4_client_state {
NFS4CLNT_RECLAIM_NOGRACE, NFS4CLNT_RECLAIM_NOGRACE,
NFS4CLNT_DELEGRETURN, NFS4CLNT_DELEGRETURN,
NFS4CLNT_SESSION_RESET, NFS4CLNT_SESSION_RESET,
NFS4CLNT_SESSION_DRAINING,
}; };
/* /*
......
...@@ -361,6 +361,16 @@ void nfs41_sequence_free_slot(const struct nfs_client *clp, ...@@ -361,6 +361,16 @@ void nfs41_sequence_free_slot(const struct nfs_client *clp,
} }
nfs4_free_slot(tbl, res->sr_slotid); nfs4_free_slot(tbl, res->sr_slotid);
res->sr_slotid = NFS4_MAX_SLOT_TABLE; 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, static void nfs41_sequence_done(struct nfs_client *clp,
...@@ -457,15 +467,11 @@ static int nfs41_setup_sequence(struct nfs4_session *session, ...@@ -457,15 +467,11 @@ static int nfs41_setup_sequence(struct nfs4_session *session,
spin_lock(&tbl->slot_tbl_lock); spin_lock(&tbl->slot_tbl_lock);
if (test_bit(NFS4CLNT_SESSION_RESET, &session->clp->cl_state)) { 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); * The state manager will wait until the slot table is empty.
spin_unlock(&tbl->slot_tbl_lock); * Schedule the reset thread
dprintk("<-- %s: Session reset: draining\n", __func__); */
return -EAGAIN; dprintk("%s Schedule Session Reset\n", __func__);
}
/* The slot table is empty; start the reset thread */
dprintk("%s Session Reset\n", __func__);
rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL); rpc_sleep_on(&tbl->slot_tbl_waitq, task, NULL);
nfs4_schedule_state_manager(session->clp); nfs4_schedule_state_manager(session->clp);
spin_unlock(&tbl->slot_tbl_lock); spin_unlock(&tbl->slot_tbl_lock);
...@@ -4506,6 +4512,7 @@ static int nfs4_reset_slot_tables(struct nfs4_session *session) ...@@ -4506,6 +4512,7 @@ static int nfs4_reset_slot_tables(struct nfs4_session *session)
1); 1);
if (status) if (status)
return status; return status;
init_completion(&session->complete);
status = nfs4_reset_slot_table(&session->bc_slot_table, status = nfs4_reset_slot_table(&session->bc_slot_table,
session->bc_attrs.max_reqs, session->bc_attrs.max_reqs,
...@@ -4608,6 +4615,7 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp) ...@@ -4608,6 +4615,7 @@ struct nfs4_session *nfs4_alloc_session(struct nfs_client *clp)
* nfs_client struct * nfs_client struct
*/ */
clp->cl_cons_state = NFS_CS_SESSION_INITING; clp->cl_cons_state = NFS_CS_SESSION_INITING;
init_completion(&session->complete);
tbl = &session->fc_slot_table; tbl = &session->fc_slot_table;
spin_lock_init(&tbl->slot_tbl_lock); spin_lock_init(&tbl->slot_tbl_lock);
......
...@@ -1181,8 +1181,23 @@ static void nfs4_session_recovery_handle_error(struct nfs_client *clp, int err) ...@@ -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) 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; 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); status = nfs4_proc_destroy_session(clp->cl_session);
if (status && status != -NFS4ERR_BADSESSION && if (status && status != -NFS4ERR_BADSESSION &&
status != -NFS4ERR_DEADSESSION) { status != -NFS4ERR_DEADSESSION) {
......
...@@ -209,6 +209,7 @@ struct nfs4_session { ...@@ -209,6 +209,7 @@ struct nfs4_session {
unsigned long session_state; unsigned long session_state;
u32 hash_alg; u32 hash_alg;
u32 ssv_len; u32 ssv_len;
struct completion complete;
/* The fore and back channel */ /* The fore and back channel */
struct nfs4_channel_attrs fc_attrs; struct nfs4_channel_attrs fc_attrs;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment