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 c89bb3c40a0b..8cc668dc4997 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -1926,41 +1926,35 @@ nfsd4_destroy_session(struct svc_rqst *r, | |||
1926 | struct nfsd4_destroy_session *sessionid) | 1926 | struct nfsd4_destroy_session *sessionid) |
1927 | { | 1927 | { |
1928 | struct nfsd4_session *ses; | 1928 | struct nfsd4_session *ses; |
1929 | __be32 status = nfserr_badsession; | 1929 | __be32 status; |
1930 | struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id); | 1930 | struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id); |
1931 | 1931 | ||
1932 | /* Notes: | 1932 | nfs4_lock_state(); |
1933 | * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid | 1933 | status = nfserr_not_only_op; |
1934 | * - Should we return nfserr_back_chan_busy if waiting for | ||
1935 | * callbacks on to-be-destroyed session? | ||
1936 | * - Do we need to clear any callback info from previous session? | ||
1937 | */ | ||
1938 | |||
1939 | if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) { | 1934 | if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) { |
1940 | if (!nfsd4_last_compound_op(r)) | 1935 | if (!nfsd4_last_compound_op(r)) |
1941 | return nfserr_not_only_op; | 1936 | goto out; |
1942 | } | 1937 | } |
1943 | dump_sessionid(__func__, &sessionid->sessionid); | 1938 | dump_sessionid(__func__, &sessionid->sessionid); |
1944 | spin_lock(&nn->client_lock); | 1939 | spin_lock(&nn->client_lock); |
1945 | ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r)); | 1940 | ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r)); |
1946 | if (!ses) { | 1941 | status = nfserr_badsession; |
1947 | spin_unlock(&nn->client_lock); | 1942 | if (!ses) |
1948 | goto out; | 1943 | goto out_client_lock; |
1949 | } | ||
1950 | 1944 | ||
1951 | unhash_session(ses); | 1945 | unhash_session(ses); |
1952 | spin_unlock(&nn->client_lock); | 1946 | spin_unlock(&nn->client_lock); |
1953 | 1947 | ||
1954 | nfs4_lock_state(); | ||
1955 | nfsd4_probe_callback_sync(ses->se_client); | 1948 | nfsd4_probe_callback_sync(ses->se_client); |
1956 | nfs4_unlock_state(); | ||
1957 | 1949 | ||
1958 | spin_lock(&nn->client_lock); | 1950 | spin_lock(&nn->client_lock); |
1959 | nfsd4_del_conns(ses); | 1951 | nfsd4_del_conns(ses); |
1960 | nfsd4_put_session_locked(ses); | 1952 | nfsd4_put_session_locked(ses); |
1961 | spin_unlock(&nn->client_lock); | ||
1962 | status = nfs_ok; | 1953 | status = nfs_ok; |
1954 | out_client_lock: | ||
1955 | spin_unlock(&nn->client_lock); | ||
1963 | out: | 1956 | out: |
1957 | nfs4_unlock_state(); | ||
1964 | return status; | 1958 | return status; |
1965 | } | 1959 | } |
1966 | 1960 | ||