diff options
author | J. Bruce Fields <bfields@redhat.com> | 2013-03-14 19:55:33 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2013-04-03 11:48:38 -0400 |
commit | abcdff09a05117112aa22cd84939039655bca710 (patch) | |
tree | 307abc0998ddfdfcf2cc1d251190ea606476b060 /fs/nfsd | |
parent | bfa85e83a87aec71cbb231256eaad7341aa8b4fa (diff) |
nfsd4: fix destroy_session race
destroy_session uses the session and client without continuously holding
any reference or locks.
Put the whole thing under the state lock for now.
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r-- | fs/nfsd/nfs4state.c | 26 |
1 files changed, 10 insertions, 16 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index c89bb3c40a0..8cc668dc499 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -1926,41 +1926,35 @@ nfsd4_destroy_session(struct svc_rqst *r, struct nfsd4_destroy_session *sessionid) { struct nfsd4_session *ses; - __be32 status = nfserr_badsession; + __be32 status; struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id); - /* Notes: - * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid - * - Should we return nfserr_back_chan_busy if waiting for - * callbacks on to-be-destroyed session? - * - Do we need to clear any callback info from previous session? - */ - + nfs4_lock_state(); + status = nfserr_not_only_op; if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) { if (!nfsd4_last_compound_op(r)) - return nfserr_not_only_op; + goto out; } dump_sessionid(__func__, &sessionid->sessionid); spin_lock(&nn->client_lock); ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r)); - if (!ses) { - spin_unlock(&nn->client_lock); - goto out; - } + status = nfserr_badsession; + if (!ses) + goto out_client_lock; unhash_session(ses); spin_unlock(&nn->client_lock); - nfs4_lock_state(); nfsd4_probe_callback_sync(ses->se_client); - nfs4_unlock_state(); spin_lock(&nn->client_lock); nfsd4_del_conns(ses); nfsd4_put_session_locked(ses); - spin_unlock(&nn->client_lock); status = nfs_ok; +out_client_lock: + spin_unlock(&nn->client_lock); out: + nfs4_unlock_state(); return status; } |