aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2013-04-01 22:23:49 -0400
committerJ. Bruce Fields <bfields@redhat.com>2013-04-03 11:48:39 -0400
commit221a68766973d7a3afe40a05abd8258b5de016a0 (patch)
treece1770ad1f1cc7227d8b8819101875b8f36a449b /fs
parent4f6e6c17733ecf01c05a693ced8349ccf8101fd8 (diff)
nfsd4: don't destroy in-use clients
When a setclientid_confirm or create_session confirms a client after a client reboot, it also destroys any previous state held by that client. The shutdown of that previous state must be careful not to free the client out from under threads processing other requests that refer to the client. This is a particular problem in the NFSv4.1 case when we hold a reference to a session (hence a client) throughout compound processing. The server attempts to handle this by unhashing the client at the time it's destroyed, then delaying the final free to the end. But this still leaves some races in the current code. I believe it's simpler just to fail the attempt to destroy the client by returning NFS4ERR_DELAY. This is a case that should never happen anyway. Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfsd/nfs4state.c209
-rw-r--r--fs/nfsd/nfs4xdr.c3
-rw-r--r--fs/nfsd/state.h16
3 files changed, 131 insertions, 97 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 8e83cef4d0bd..3b4ce41c9db8 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -113,6 +113,90 @@ nfs4_unlock_state(void)
113 mutex_unlock(&client_mutex); 113 mutex_unlock(&client_mutex);
114} 114}
115 115
116static bool is_client_expired(struct nfs4_client *clp)
117{
118 return clp->cl_time == 0;
119}
120
121static __be32 mark_client_expired_locked(struct nfs4_client *clp)
122{
123 if (atomic_read(&clp->cl_refcount))
124 return nfserr_jukebox;
125 clp->cl_time = 0;
126 return nfs_ok;
127}
128
129static __be32 mark_client_expired(struct nfs4_client *clp)
130{
131 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
132 __be32 ret;
133
134 spin_lock(&nn->client_lock);
135 ret = mark_client_expired_locked(clp);
136 spin_unlock(&nn->client_lock);
137 return ret;
138}
139
140static __be32 get_client_locked(struct nfs4_client *clp)
141{
142 if (is_client_expired(clp))
143 return nfserr_expired;
144 atomic_inc(&clp->cl_refcount);
145 return nfs_ok;
146}
147
148/* must be called under the client_lock */
149static inline void
150renew_client_locked(struct nfs4_client *clp)
151{
152 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
153
154 if (is_client_expired(clp)) {
155 WARN_ON(1);
156 printk("%s: client (clientid %08x/%08x) already expired\n",
157 __func__,
158 clp->cl_clientid.cl_boot,
159 clp->cl_clientid.cl_id);
160 return;
161 }
162
163 dprintk("renewing client (clientid %08x/%08x)\n",
164 clp->cl_clientid.cl_boot,
165 clp->cl_clientid.cl_id);
166 list_move_tail(&clp->cl_lru, &nn->client_lru);
167 clp->cl_time = get_seconds();
168}
169
170static inline void
171renew_client(struct nfs4_client *clp)
172{
173 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
174
175 spin_lock(&nn->client_lock);
176 renew_client_locked(clp);
177 spin_unlock(&nn->client_lock);
178}
179
180void put_client_renew_locked(struct nfs4_client *clp)
181{
182 if (!atomic_dec_and_test(&clp->cl_refcount))
183 return;
184 if (!is_client_expired(clp))
185 renew_client_locked(clp);
186}
187
188void put_client_renew(struct nfs4_client *clp)
189{
190 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
191
192 if (!atomic_dec_and_lock(&clp->cl_refcount, &nn->client_lock))
193 return;
194 if (!is_client_expired(clp))
195 renew_client_locked(clp);
196 spin_unlock(&nn->client_lock);
197}
198
199
116static inline u32 200static inline u32
117opaque_hashval(const void *ptr, int nbytes) 201opaque_hashval(const void *ptr, int nbytes)
118{ 202{
@@ -864,7 +948,7 @@ static void free_session(struct kref *kref)
864 __free_session(ses); 948 __free_session(ses);
865} 949}
866 950
867static void nfsd4_put_session(struct nfsd4_session *ses) 951void nfsd4_put_session(struct nfsd4_session *ses)
868{ 952{
869 struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id); 953 struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id);
870 954
@@ -968,38 +1052,6 @@ unhash_session(struct nfsd4_session *ses)
968 spin_unlock(&ses->se_client->cl_lock); 1052 spin_unlock(&ses->se_client->cl_lock);
969} 1053}
970 1054
971/* must be called under the client_lock */
972static inline void
973renew_client_locked(struct nfs4_client *clp)
974{
975 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
976
977 if (is_client_expired(clp)) {
978 WARN_ON(1);
979 printk("%s: client (clientid %08x/%08x) already expired\n",
980 __func__,
981 clp->cl_clientid.cl_boot,
982 clp->cl_clientid.cl_id);
983 return;
984 }
985
986 dprintk("renewing client (clientid %08x/%08x)\n",
987 clp->cl_clientid.cl_boot,
988 clp->cl_clientid.cl_id);
989 list_move_tail(&clp->cl_lru, &nn->client_lru);
990 clp->cl_time = get_seconds();
991}
992
993static inline void
994renew_client(struct nfs4_client *clp)
995{
996 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
997
998 spin_lock(&nn->client_lock);
999 renew_client_locked(clp);
1000 spin_unlock(&nn->client_lock);
1001}
1002
1003/* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ 1055/* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */
1004static int 1056static int
1005STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn) 1057STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn)
@@ -1051,33 +1103,12 @@ free_client(struct nfs4_client *clp)
1051 kfree(clp); 1103 kfree(clp);
1052} 1104}
1053 1105
1054void
1055release_session_client(struct nfsd4_session *session)
1056{
1057 struct nfs4_client *clp = session->se_client;
1058 struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
1059
1060 nfsd4_put_session(session);
1061 if (!atomic_dec_and_lock(&clp->cl_refcount, &nn->client_lock))
1062 return;
1063 /*
1064 * At this point we also know all sessions have refcnt 1,
1065 * so free_client will delete them all if necessary:
1066 */
1067 if (is_client_expired(clp))
1068 free_client(clp);
1069 else
1070 renew_client_locked(clp);
1071 spin_unlock(&nn->client_lock);
1072}
1073
1074/* must be called under the client_lock */ 1106/* must be called under the client_lock */
1075static inline void 1107static inline void
1076unhash_client_locked(struct nfs4_client *clp) 1108unhash_client_locked(struct nfs4_client *clp)
1077{ 1109{
1078 struct nfsd4_session *ses; 1110 struct nfsd4_session *ses;
1079 1111
1080 mark_client_expired(clp);
1081 list_del(&clp->cl_lru); 1112 list_del(&clp->cl_lru);
1082 spin_lock(&clp->cl_lock); 1113 spin_lock(&clp->cl_lock);
1083 list_for_each_entry(ses, &clp->cl_sessions, se_perclnt) 1114 list_for_each_entry(ses, &clp->cl_sessions, se_perclnt)
@@ -1119,8 +1150,8 @@ destroy_client(struct nfs4_client *clp)
1119 rb_erase(&clp->cl_namenode, &nn->unconf_name_tree); 1150 rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
1120 spin_lock(&nn->client_lock); 1151 spin_lock(&nn->client_lock);
1121 unhash_client_locked(clp); 1152 unhash_client_locked(clp);
1122 if (atomic_read(&clp->cl_refcount) == 0) 1153 WARN_ON_ONCE(atomic_read(&clp->cl_refcount));
1123 free_client(clp); 1154 free_client(clp);
1124 spin_unlock(&nn->client_lock); 1155 spin_unlock(&nn->client_lock);
1125} 1156}
1126 1157
@@ -1815,8 +1846,12 @@ nfsd4_create_session(struct svc_rqst *rqstp,
1815 goto out_free_conn; 1846 goto out_free_conn;
1816 } 1847 }
1817 old = find_confirmed_client_by_name(&unconf->cl_name, nn); 1848 old = find_confirmed_client_by_name(&unconf->cl_name, nn);
1818 if (old) 1849 if (old) {
1850 status = mark_client_expired(old);
1851 if (status)
1852 goto out_free_conn;
1819 expire_client(old); 1853 expire_client(old);
1854 }
1820 move_to_confirmed(unconf); 1855 move_to_confirmed(unconf);
1821 conf = unconf; 1856 conf = unconf;
1822 } else { 1857 } else {
@@ -2014,6 +2049,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
2014{ 2049{
2015 struct nfsd4_compoundres *resp = rqstp->rq_resp; 2050 struct nfsd4_compoundres *resp = rqstp->rq_resp;
2016 struct nfsd4_session *session; 2051 struct nfsd4_session *session;
2052 struct nfs4_client *clp;
2017 struct nfsd4_slot *slot; 2053 struct nfsd4_slot *slot;
2018 struct nfsd4_conn *conn; 2054 struct nfsd4_conn *conn;
2019 __be32 status; 2055 __be32 status;
@@ -2034,19 +2070,23 @@ nfsd4_sequence(struct svc_rqst *rqstp,
2034 status = nfserr_badsession; 2070 status = nfserr_badsession;
2035 session = find_in_sessionid_hashtbl(&seq->sessionid, SVC_NET(rqstp)); 2071 session = find_in_sessionid_hashtbl(&seq->sessionid, SVC_NET(rqstp));
2036 if (!session) 2072 if (!session)
2037 goto out; 2073 goto out_no_session;
2074 clp = session->se_client;
2075 status = get_client_locked(clp);
2076 if (status)
2077 goto out_no_session;
2038 2078
2039 status = nfserr_too_many_ops; 2079 status = nfserr_too_many_ops;
2040 if (nfsd4_session_too_many_ops(rqstp, session)) 2080 if (nfsd4_session_too_many_ops(rqstp, session))
2041 goto out; 2081 goto out_put_client;
2042 2082
2043 status = nfserr_req_too_big; 2083 status = nfserr_req_too_big;
2044 if (nfsd4_request_too_big(rqstp, session)) 2084 if (nfsd4_request_too_big(rqstp, session))
2045 goto out; 2085 goto out_put_client;
2046 2086
2047 status = nfserr_badslot; 2087 status = nfserr_badslot;
2048 if (seq->slotid >= session->se_fchannel.maxreqs) 2088 if (seq->slotid >= session->se_fchannel.maxreqs)
2049 goto out; 2089 goto out_put_client;
2050 2090
2051 slot = session->se_slots[seq->slotid]; 2091 slot = session->se_slots[seq->slotid];
2052 dprintk("%s: slotid %d\n", __func__, seq->slotid); 2092 dprintk("%s: slotid %d\n", __func__, seq->slotid);
@@ -2061,7 +2101,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
2061 if (status == nfserr_replay_cache) { 2101 if (status == nfserr_replay_cache) {
2062 status = nfserr_seq_misordered; 2102 status = nfserr_seq_misordered;
2063 if (!(slot->sl_flags & NFSD4_SLOT_INITIALIZED)) 2103 if (!(slot->sl_flags & NFSD4_SLOT_INITIALIZED))
2064 goto out; 2104 goto out_put_client;
2065 cstate->slot = slot; 2105 cstate->slot = slot;
2066 cstate->session = session; 2106 cstate->session = session;
2067 /* Return the cached reply status and set cstate->status 2107 /* Return the cached reply status and set cstate->status
@@ -2071,7 +2111,7 @@ nfsd4_sequence(struct svc_rqst *rqstp,
2071 goto out; 2111 goto out;
2072 } 2112 }
2073 if (status) 2113 if (status)
2074 goto out; 2114 goto out_put_client;
2075 2115
2076 nfsd4_sequence_check_conn(conn, session); 2116 nfsd4_sequence_check_conn(conn, session);
2077 conn = NULL; 2117 conn = NULL;
@@ -2088,26 +2128,24 @@ nfsd4_sequence(struct svc_rqst *rqstp,
2088 cstate->session = session; 2128 cstate->session = session;
2089 2129
2090out: 2130out:
2091 /* Hold a session reference until done processing the compound. */ 2131 nfsd4_get_session(cstate->session);
2092 if (cstate->session) { 2132 switch (clp->cl_cb_state) {
2093 struct nfs4_client *clp = session->se_client; 2133 case NFSD4_CB_DOWN:
2094 2134 seq->status_flags = SEQ4_STATUS_CB_PATH_DOWN;
2095 nfsd4_get_session(cstate->session); 2135 break;
2096 atomic_inc(&clp->cl_refcount); 2136 case NFSD4_CB_FAULT:
2097 switch (clp->cl_cb_state) { 2137 seq->status_flags = SEQ4_STATUS_BACKCHANNEL_FAULT;
2098 case NFSD4_CB_DOWN: 2138 break;
2099 seq->status_flags = SEQ4_STATUS_CB_PATH_DOWN; 2139 default:
2100 break; 2140 seq->status_flags = 0;
2101 case NFSD4_CB_FAULT:
2102 seq->status_flags = SEQ4_STATUS_BACKCHANNEL_FAULT;
2103 break;
2104 default:
2105 seq->status_flags = 0;
2106 }
2107 } 2141 }
2142out_no_session:
2108 kfree(conn); 2143 kfree(conn);
2109 spin_unlock(&nn->client_lock); 2144 spin_unlock(&nn->client_lock);
2110 return status; 2145 return status;
2146out_put_client:
2147 put_client_renew_locked(clp);
2148 goto out_no_session;
2111} 2149}
2112 2150
2113__be32 2151__be32
@@ -2276,8 +2314,12 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
2276 expire_client(unconf); 2314 expire_client(unconf);
2277 } else { /* case 3: normal case; new or rebooted client */ 2315 } else { /* case 3: normal case; new or rebooted client */
2278 conf = find_confirmed_client_by_name(&unconf->cl_name, nn); 2316 conf = find_confirmed_client_by_name(&unconf->cl_name, nn);
2279 if (conf) 2317 if (conf) {
2318 status = mark_client_expired(conf);
2319 if (status)
2320 goto out;
2280 expire_client(conf); 2321 expire_client(conf);
2322 }
2281 move_to_confirmed(unconf); 2323 move_to_confirmed(unconf);
2282 nfsd4_probe_callback(unconf); 2324 nfsd4_probe_callback(unconf);
2283 } 2325 }
@@ -3189,13 +3231,12 @@ nfs4_laundromat(struct nfsd_net *nn)
3189 clientid_val = t; 3231 clientid_val = t;
3190 break; 3232 break;
3191 } 3233 }
3192 if (atomic_read(&clp->cl_refcount)) { 3234 if (mark_client_expired_locked(clp)) {
3193 dprintk("NFSD: client in use (clientid %08x)\n", 3235 dprintk("NFSD: client in use (clientid %08x)\n",
3194 clp->cl_clientid.cl_id); 3236 clp->cl_clientid.cl_id);
3195 continue; 3237 continue;
3196 } 3238 }
3197 unhash_client_locked(clp); 3239 list_move(&clp->cl_lru, &reaplist);
3198 list_add(&clp->cl_lru, &reaplist);
3199 } 3240 }
3200 spin_unlock(&nn->client_lock); 3241 spin_unlock(&nn->client_lock);
3201 list_for_each_safe(pos, next, &reaplist) { 3242 list_for_each_safe(pos, next, &reaplist) {
@@ -4581,6 +4622,8 @@ nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn)
4581 4622
4582u64 nfsd_forget_client(struct nfs4_client *clp, u64 max) 4623u64 nfsd_forget_client(struct nfs4_client *clp, u64 max)
4583{ 4624{
4625 if (mark_client_expired(clp))
4626 return 0;
4584 expire_client(clp); 4627 expire_client(clp);
4585 return 1; 4628 return 1;
4586} 4629}
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 9b02b6652f2b..700de0192834 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -3684,7 +3684,8 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo
3684 cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE; 3684 cs->slot->sl_flags &= ~NFSD4_SLOT_INUSE;
3685 } 3685 }
3686 /* Renew the clientid on success and on replay */ 3686 /* Renew the clientid on success and on replay */
3687 release_session_client(cs->session); 3687 put_client_renew(cs->session->se_client);
3688 nfsd4_put_session(cs->session);
3688 } 3689 }
3689 return 1; 3690 return 1;
3690} 3691}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 327552bb6dba..07f8a822a6ce 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -209,6 +209,8 @@ struct nfsd4_session {
209 struct nfsd4_slot *se_slots[]; /* forward channel slots */ 209 struct nfsd4_slot *se_slots[]; /* forward channel slots */
210}; 210};
211 211
212extern void nfsd4_put_session(struct nfsd4_session *ses);
213
212/* formatted contents of nfs4_sessionid */ 214/* formatted contents of nfs4_sessionid */
213struct nfsd4_sessionid { 215struct nfsd4_sessionid {
214 clientid_t clientid; 216 clientid_t clientid;
@@ -284,18 +286,6 @@ struct nfs4_client {
284 struct net *net; 286 struct net *net;
285}; 287};
286 288
287static inline void
288mark_client_expired(struct nfs4_client *clp)
289{
290 clp->cl_time = 0;
291}
292
293static inline bool
294is_client_expired(struct nfs4_client *clp)
295{
296 return clp->cl_time == 0;
297}
298
299/* struct nfs4_client_reset 289/* struct nfs4_client_reset
300 * one per old client. Populates reset_str_hashtbl. Filled from conf_id_hashtbl 290 * one per old client. Populates reset_str_hashtbl. Filled from conf_id_hashtbl
301 * upon lease reset, or from upcall to state_daemon (to read in state 291 * upon lease reset, or from upcall to state_daemon (to read in state
@@ -484,7 +474,7 @@ extern void nfs4_put_delegation(struct nfs4_delegation *dp);
484extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name, 474extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name,
485 struct nfsd_net *nn); 475 struct nfsd_net *nn);
486extern bool nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn); 476extern bool nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn);
487extern void release_session_client(struct nfsd4_session *); 477extern void put_client_renew(struct nfs4_client *clp);
488extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *); 478extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *);
489 479
490/* nfs4recover operations */ 480/* nfs4recover operations */