diff options
author | J. Bruce Fields <bfields@redhat.com> | 2013-03-19 12:05:39 -0400 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2013-04-03 11:48:40 -0400 |
commit | 66b2b9b2b0e8a9034806293a436628400a44a71d (patch) | |
tree | a1a142425240c28f7b942dc665afe77752c82ae1 /fs | |
parent | 221a68766973d7a3afe40a05abd8258b5de016a0 (diff) |
nfsd4: don't destroy in-use session
This changes session destruction to be similar to client destruction in
that attempts to destroy a session while in use (which should be rare
corner cases) result in DELAY. This simplifies things somewhat and
helps meet a coming 4.2 requirement.
Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfsd/nfs4state.c | 72 | ||||
-rw-r--r-- | fs/nfsd/state.h | 4 |
2 files changed, 43 insertions, 33 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 3b4ce41c9db8..2fd015587167 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -94,17 +94,32 @@ nfs4_lock_state(void) | |||
94 | mutex_lock(&client_mutex); | 94 | mutex_lock(&client_mutex); |
95 | } | 95 | } |
96 | 96 | ||
97 | static void free_session(struct kref *); | 97 | static void free_session(struct nfsd4_session *); |
98 | 98 | ||
99 | /* Must be called under the client_lock */ | 99 | void nfsd4_put_session(struct nfsd4_session *ses) |
100 | static void nfsd4_put_session_locked(struct nfsd4_session *ses) | 100 | { |
101 | atomic_dec(&ses->se_ref); | ||
102 | } | ||
103 | |||
104 | static bool is_session_dead(struct nfsd4_session *ses) | ||
101 | { | 105 | { |
102 | kref_put(&ses->se_ref, free_session); | 106 | return ses->se_flags & NFS4_SESSION_DEAD; |
107 | } | ||
108 | |||
109 | static __be32 mark_session_dead_locked(struct nfsd4_session *ses) | ||
110 | { | ||
111 | if (atomic_read(&ses->se_ref)) | ||
112 | return nfserr_jukebox; | ||
113 | ses->se_flags |= NFS4_SESSION_DEAD; | ||
114 | return nfs_ok; | ||
103 | } | 115 | } |
104 | 116 | ||
105 | static void nfsd4_get_session(struct nfsd4_session *ses) | 117 | static __be32 nfsd4_get_session_locked(struct nfsd4_session *ses) |
106 | { | 118 | { |
107 | kref_get(&ses->se_ref); | 119 | if (is_session_dead(ses)) |
120 | return nfserr_badsession; | ||
121 | atomic_inc(&ses->se_ref); | ||
122 | return nfs_ok; | ||
108 | } | 123 | } |
109 | 124 | ||
110 | void | 125 | void |
@@ -935,28 +950,15 @@ static void __free_session(struct nfsd4_session *ses) | |||
935 | kfree(ses); | 950 | kfree(ses); |
936 | } | 951 | } |
937 | 952 | ||
938 | static void free_session(struct kref *kref) | 953 | static void free_session(struct nfsd4_session *ses) |
939 | { | 954 | { |
940 | struct nfsd4_session *ses; | 955 | struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id); |
941 | struct nfsd_net *nn; | ||
942 | |||
943 | ses = container_of(kref, struct nfsd4_session, se_ref); | ||
944 | nn = net_generic(ses->se_client->net, nfsd_net_id); | ||
945 | 956 | ||
946 | lockdep_assert_held(&nn->client_lock); | 957 | lockdep_assert_held(&nn->client_lock); |
947 | nfsd4_del_conns(ses); | 958 | nfsd4_del_conns(ses); |
948 | __free_session(ses); | 959 | __free_session(ses); |
949 | } | 960 | } |
950 | 961 | ||
951 | void nfsd4_put_session(struct nfsd4_session *ses) | ||
952 | { | ||
953 | struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id); | ||
954 | |||
955 | spin_lock(&nn->client_lock); | ||
956 | nfsd4_put_session_locked(ses); | ||
957 | spin_unlock(&nn->client_lock); | ||
958 | } | ||
959 | |||
960 | static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan, | 962 | static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan, |
961 | struct nfsd_net *nn) | 963 | struct nfsd_net *nn) |
962 | { | 964 | { |
@@ -997,7 +999,7 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru | |||
997 | new->se_flags = cses->flags; | 999 | new->se_flags = cses->flags; |
998 | new->se_cb_prog = cses->callback_prog; | 1000 | new->se_cb_prog = cses->callback_prog; |
999 | new->se_cb_sec = cses->cb_sec; | 1001 | new->se_cb_sec = cses->cb_sec; |
1000 | kref_init(&new->se_ref); | 1002 | atomic_set(&new->se_ref, 0); |
1001 | idx = hash_sessionid(&new->se_sessionid); | 1003 | idx = hash_sessionid(&new->se_sessionid); |
1002 | spin_lock(&nn->client_lock); | 1004 | spin_lock(&nn->client_lock); |
1003 | list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]); | 1005 | list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]); |
@@ -1095,7 +1097,8 @@ free_client(struct nfs4_client *clp) | |||
1095 | ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, | 1097 | ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, |
1096 | se_perclnt); | 1098 | se_perclnt); |
1097 | list_del(&ses->se_perclnt); | 1099 | list_del(&ses->se_perclnt); |
1098 | nfsd4_put_session_locked(ses); | 1100 | WARN_ON_ONCE(atomic_read(&ses->se_ref)); |
1101 | free_session(ses); | ||
1099 | } | 1102 | } |
1100 | free_svc_cred(&clp->cl_cred); | 1103 | free_svc_cred(&clp->cl_cred); |
1101 | kfree(clp->cl_name.data); | 1104 | kfree(clp->cl_name.data); |
@@ -1976,15 +1979,16 @@ nfsd4_destroy_session(struct svc_rqst *r, | |||
1976 | status = nfserr_badsession; | 1979 | status = nfserr_badsession; |
1977 | if (!ses) | 1980 | if (!ses) |
1978 | goto out_client_lock; | 1981 | goto out_client_lock; |
1979 | 1982 | status = mark_session_dead_locked(ses); | |
1983 | if (status) | ||
1984 | goto out_client_lock; | ||
1980 | unhash_session(ses); | 1985 | unhash_session(ses); |
1981 | spin_unlock(&nn->client_lock); | 1986 | spin_unlock(&nn->client_lock); |
1982 | 1987 | ||
1983 | nfsd4_probe_callback_sync(ses->se_client); | 1988 | nfsd4_probe_callback_sync(ses->se_client); |
1984 | 1989 | ||
1985 | spin_lock(&nn->client_lock); | 1990 | spin_lock(&nn->client_lock); |
1986 | nfsd4_del_conns(ses); | 1991 | free_session(ses); |
1987 | nfsd4_put_session_locked(ses); | ||
1988 | status = nfs_ok; | 1992 | status = nfs_ok; |
1989 | out_client_lock: | 1993 | out_client_lock: |
1990 | spin_unlock(&nn->client_lock); | 1994 | spin_unlock(&nn->client_lock); |
@@ -2075,18 +2079,21 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
2075 | status = get_client_locked(clp); | 2079 | status = get_client_locked(clp); |
2076 | if (status) | 2080 | if (status) |
2077 | goto out_no_session; | 2081 | goto out_no_session; |
2082 | status = nfsd4_get_session_locked(session); | ||
2083 | if (status) | ||
2084 | goto out_put_client; | ||
2078 | 2085 | ||
2079 | status = nfserr_too_many_ops; | 2086 | status = nfserr_too_many_ops; |
2080 | if (nfsd4_session_too_many_ops(rqstp, session)) | 2087 | if (nfsd4_session_too_many_ops(rqstp, session)) |
2081 | goto out_put_client; | 2088 | goto out_put_session; |
2082 | 2089 | ||
2083 | status = nfserr_req_too_big; | 2090 | status = nfserr_req_too_big; |
2084 | if (nfsd4_request_too_big(rqstp, session)) | 2091 | if (nfsd4_request_too_big(rqstp, session)) |
2085 | goto out_put_client; | 2092 | goto out_put_session; |
2086 | 2093 | ||
2087 | status = nfserr_badslot; | 2094 | status = nfserr_badslot; |
2088 | if (seq->slotid >= session->se_fchannel.maxreqs) | 2095 | if (seq->slotid >= session->se_fchannel.maxreqs) |
2089 | goto out_put_client; | 2096 | goto out_put_session; |
2090 | 2097 | ||
2091 | slot = session->se_slots[seq->slotid]; | 2098 | slot = session->se_slots[seq->slotid]; |
2092 | dprintk("%s: slotid %d\n", __func__, seq->slotid); | 2099 | dprintk("%s: slotid %d\n", __func__, seq->slotid); |
@@ -2101,7 +2108,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
2101 | if (status == nfserr_replay_cache) { | 2108 | if (status == nfserr_replay_cache) { |
2102 | status = nfserr_seq_misordered; | 2109 | status = nfserr_seq_misordered; |
2103 | if (!(slot->sl_flags & NFSD4_SLOT_INITIALIZED)) | 2110 | if (!(slot->sl_flags & NFSD4_SLOT_INITIALIZED)) |
2104 | goto out_put_client; | 2111 | goto out_put_session; |
2105 | cstate->slot = slot; | 2112 | cstate->slot = slot; |
2106 | cstate->session = session; | 2113 | cstate->session = session; |
2107 | /* Return the cached reply status and set cstate->status | 2114 | /* Return the cached reply status and set cstate->status |
@@ -2111,7 +2118,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
2111 | goto out; | 2118 | goto out; |
2112 | } | 2119 | } |
2113 | if (status) | 2120 | if (status) |
2114 | goto out_put_client; | 2121 | goto out_put_session; |
2115 | 2122 | ||
2116 | nfsd4_sequence_check_conn(conn, session); | 2123 | nfsd4_sequence_check_conn(conn, session); |
2117 | conn = NULL; | 2124 | conn = NULL; |
@@ -2128,7 +2135,6 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
2128 | cstate->session = session; | 2135 | cstate->session = session; |
2129 | 2136 | ||
2130 | out: | 2137 | out: |
2131 | nfsd4_get_session(cstate->session); | ||
2132 | switch (clp->cl_cb_state) { | 2138 | switch (clp->cl_cb_state) { |
2133 | case NFSD4_CB_DOWN: | 2139 | case NFSD4_CB_DOWN: |
2134 | seq->status_flags = SEQ4_STATUS_CB_PATH_DOWN; | 2140 | seq->status_flags = SEQ4_STATUS_CB_PATH_DOWN; |
@@ -2143,6 +2149,8 @@ out_no_session: | |||
2143 | kfree(conn); | 2149 | kfree(conn); |
2144 | spin_unlock(&nn->client_lock); | 2150 | spin_unlock(&nn->client_lock); |
2145 | return status; | 2151 | return status; |
2152 | out_put_session: | ||
2153 | nfsd4_put_session(session); | ||
2146 | out_put_client: | 2154 | out_put_client: |
2147 | put_client_renew_locked(clp); | 2155 | put_client_renew_locked(clp); |
2148 | goto out_no_session; | 2156 | goto out_no_session; |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 07f8a822a6ce..f6ae4db3efdb 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -194,9 +194,11 @@ struct nfsd4_conn { | |||
194 | }; | 194 | }; |
195 | 195 | ||
196 | struct nfsd4_session { | 196 | struct nfsd4_session { |
197 | struct kref se_ref; | 197 | atomic_t se_ref; |
198 | struct list_head se_hash; /* hash by sessionid */ | 198 | struct list_head se_hash; /* hash by sessionid */ |
199 | struct list_head se_perclnt; | 199 | struct list_head se_perclnt; |
200 | /* See SESSION4_PERSIST, etc. for standard flags; this is internal-only: */ | ||
201 | #define NFS4_SESSION_DEAD 0x010 | ||
200 | u32 se_flags; | 202 | u32 se_flags; |
201 | struct nfs4_client *se_client; | 203 | struct nfs4_client *se_client; |
202 | struct nfs4_sessionid se_sessionid; | 204 | struct nfs4_sessionid se_sessionid; |