aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/callback_proc.c
diff options
context:
space:
mode:
authorAndy Adamson <andros@netapp.com>2011-01-05 21:04:32 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2011-01-06 14:46:24 -0500
commitc36fca52f5e4594ffd0ff175b328966b0d393184 (patch)
tree6d771744cc49f0edc0d2b6b2f9fe919163002346 /fs/nfs/callback_proc.c
parent2c2618c6f29c41a0a966f14f05c8bf45fcabb750 (diff)
NFS refactor nfs_find_client and reference client across callback processing
Fixes a bug where the nfs_client could be freed during callback processing. Refactor nfs_find_client to use minorversion specific means to locate the correct nfs_client structure. In the NFS layer, V4.0 clients are found using the callback_ident field in the CB_COMPOUND header. V4.1 clients are found using the sessionID in the CB_SEQUENCE operation which is also compared against the sessionID associated with the back channel thread after a successful CREATE_SESSION. Each of these methods finds the one an only nfs_client associated with the incoming callback request - so nfs_find_client_next is not needed. In the RPC layer, the pg_authenticate call needs to find the nfs_client. For the v4.0 callback service, the callback identifier has not been decoded so a search by address, version, and minorversion is used. The sessionid for the sessions based callback service has (usually) not been set for the pg_authenticate on a CB_NULL call which can be sent prior to the return of a CREATE_SESSION call, so the sessionid associated with the back channel thread is not used to find the client in pg_authenticate for CB_NULL calls. Pass the referenced nfs_client to each CB_COMPOUND operation being proceesed via the new cb_process_state structure. The reference is held across cb_compound processing. Use the new cb_process_state struct to move the NFS4ERR_RETRY_UNCACHED_REP processing from process_op into nfs4_callback_sequence where it belongs. Signed-off-by: Andy Adamson <andros@netapp.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/callback_proc.c')
-rw-r--r--fs/nfs/callback_proc.c167
1 files changed, 65 insertions, 102 deletions
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 2950fca0c61b..b70e46da16fc 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -16,26 +16,28 @@
16#ifdef NFS_DEBUG 16#ifdef NFS_DEBUG
17#define NFSDBG_FACILITY NFSDBG_CALLBACK 17#define NFSDBG_FACILITY NFSDBG_CALLBACK
18#endif 18#endif
19 19
20__be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res) 20__be32 nfs4_callback_getattr(struct cb_getattrargs *args,
21 struct cb_getattrres *res,
22 struct cb_process_state *cps)
21{ 23{
22 struct nfs_client *clp;
23 struct nfs_delegation *delegation; 24 struct nfs_delegation *delegation;
24 struct nfs_inode *nfsi; 25 struct nfs_inode *nfsi;
25 struct inode *inode; 26 struct inode *inode;
26 27
28 res->status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
29 if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
30 goto out;
31
27 res->bitmap[0] = res->bitmap[1] = 0; 32 res->bitmap[0] = res->bitmap[1] = 0;
28 res->status = htonl(NFS4ERR_BADHANDLE); 33 res->status = htonl(NFS4ERR_BADHANDLE);
29 clp = nfs_find_client(args->addr, 4);
30 if (clp == NULL)
31 goto out;
32 34
33 dprintk("NFS: GETATTR callback request from %s\n", 35 dprintk("NFS: GETATTR callback request from %s\n",
34 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); 36 rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
35 37
36 inode = nfs_delegation_find_inode(clp, &args->fh); 38 inode = nfs_delegation_find_inode(cps->clp, &args->fh);
37 if (inode == NULL) 39 if (inode == NULL)
38 goto out_putclient; 40 goto out;
39 nfsi = NFS_I(inode); 41 nfsi = NFS_I(inode);
40 rcu_read_lock(); 42 rcu_read_lock();
41 delegation = rcu_dereference(nfsi->delegation); 43 delegation = rcu_dereference(nfsi->delegation);
@@ -55,49 +57,41 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *
55out_iput: 57out_iput:
56 rcu_read_unlock(); 58 rcu_read_unlock();
57 iput(inode); 59 iput(inode);
58out_putclient:
59 nfs_put_client(clp);
60out: 60out:
61 dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status)); 61 dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status));
62 return res->status; 62 return res->status;
63} 63}
64 64
65__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) 65__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
66 struct cb_process_state *cps)
66{ 67{
67 struct nfs_client *clp;
68 struct inode *inode; 68 struct inode *inode;
69 __be32 res; 69 __be32 res;
70 70
71 res = htonl(NFS4ERR_BADHANDLE); 71 res = htonl(NFS4ERR_OP_NOT_IN_SESSION);
72 clp = nfs_find_client(args->addr, 4); 72 if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
73 if (clp == NULL)
74 goto out; 73 goto out;
75 74
76 dprintk("NFS: RECALL callback request from %s\n", 75 dprintk("NFS: RECALL callback request from %s\n",
77 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); 76 rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
78 77
79 do { 78 res = htonl(NFS4ERR_BADHANDLE);
80 struct nfs_client *prev = clp; 79 inode = nfs_delegation_find_inode(cps->clp, &args->fh);
81 80 if (inode == NULL)
82 inode = nfs_delegation_find_inode(clp, &args->fh); 81 goto out;
83 if (inode != NULL) { 82 /* Set up a helper thread to actually return the delegation */
84 /* Set up a helper thread to actually return the delegation */ 83 switch (nfs_async_inode_return_delegation(inode, &args->stateid)) {
85 switch (nfs_async_inode_return_delegation(inode, &args->stateid)) { 84 case 0:
86 case 0: 85 res = 0;
87 res = 0; 86 break;
88 break; 87 case -ENOENT:
89 case -ENOENT: 88 if (res != 0)
90 if (res != 0) 89 res = htonl(NFS4ERR_BAD_STATEID);
91 res = htonl(NFS4ERR_BAD_STATEID); 90 break;
92 break; 91 default:
93 default: 92 res = htonl(NFS4ERR_RESOURCE);
94 res = htonl(NFS4ERR_RESOURCE); 93 }
95 } 94 iput(inode);
96 iput(inode);
97 }
98 clp = nfs_find_client_next(prev);
99 nfs_put_client(prev);
100 } while (clp != NULL);
101out: 95out:
102 dprintk("%s: exit with status = %d\n", __func__, ntohl(res)); 96 dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
103 return res; 97 return res;
@@ -185,42 +179,6 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args)
185} 179}
186 180
187/* 181/*
188 * Returns a pointer to a held 'struct nfs_client' that matches the server's
189 * address, major version number, and session ID. It is the caller's
190 * responsibility to release the returned reference.
191 *
192 * Returns NULL if there are no connections with sessions, or if no session
193 * matches the one of interest.
194 */
195 static struct nfs_client *find_client_with_session(
196 const struct sockaddr *addr, u32 nfsversion,
197 struct nfs4_sessionid *sessionid)
198{
199 struct nfs_client *clp;
200
201 clp = nfs_find_client(addr, 4);
202 if (clp == NULL)
203 return NULL;
204
205 do {
206 struct nfs_client *prev = clp;
207
208 if (clp->cl_session != NULL) {
209 if (memcmp(clp->cl_session->sess_id.data,
210 sessionid->data,
211 NFS4_MAX_SESSIONID_LEN) == 0) {
212 /* Returns a held reference to clp */
213 return clp;
214 }
215 }
216 clp = nfs_find_client_next(prev);
217 nfs_put_client(prev);
218 } while (clp != NULL);
219
220 return NULL;
221}
222
223/*
224 * For each referring call triple, check the session's slot table for 182 * For each referring call triple, check the session's slot table for
225 * a match. If the slot is in use and the sequence numbers match, the 183 * a match. If the slot is in use and the sequence numbers match, the
226 * client is still waiting for a response to the original request. 184 * client is still waiting for a response to the original request.
@@ -276,20 +234,28 @@ out:
276} 234}
277 235
278__be32 nfs4_callback_sequence(struct cb_sequenceargs *args, 236__be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
279 struct cb_sequenceres *res) 237 struct cb_sequenceres *res,
238 struct cb_process_state *cps)
280{ 239{
281 struct nfs_client *clp; 240 struct nfs_client *clp;
282 int i; 241 int i;
283 __be32 status; 242 __be32 status;
284 243
244 cps->clp = NULL;
245
285 status = htonl(NFS4ERR_BADSESSION); 246 status = htonl(NFS4ERR_BADSESSION);
286 clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid); 247 /* Incoming session must match the callback session */
248 if (memcmp(&args->csa_sessionid, cps->svc_sid, NFS4_MAX_SESSIONID_LEN))
249 goto out;
250
251 clp = nfs4_find_client_sessionid(args->csa_addr,
252 &args->csa_sessionid, 1);
287 if (clp == NULL) 253 if (clp == NULL)
288 goto out; 254 goto out;
289 255
290 status = validate_seqid(&clp->cl_session->bc_slot_table, args); 256 status = validate_seqid(&clp->cl_session->bc_slot_table, args);
291 if (status) 257 if (status)
292 goto out_putclient; 258 goto out;
293 259
294 /* 260 /*
295 * Check for pending referring calls. If a match is found, a 261 * Check for pending referring calls. If a match is found, a
@@ -298,7 +264,7 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
298 */ 264 */
299 if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) { 265 if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) {
300 status = htonl(NFS4ERR_DELAY); 266 status = htonl(NFS4ERR_DELAY);
301 goto out_putclient; 267 goto out;
302 } 268 }
303 269
304 memcpy(&res->csr_sessionid, &args->csa_sessionid, 270 memcpy(&res->csr_sessionid, &args->csa_sessionid,
@@ -307,36 +273,36 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
307 res->csr_slotid = args->csa_slotid; 273 res->csr_slotid = args->csa_slotid;
308 res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; 274 res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
309 res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; 275 res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
276 cps->clp = clp; /* put in nfs4_callback_compound */
310 277
311out_putclient:
312 nfs_put_client(clp);
313out: 278out:
314 for (i = 0; i < args->csa_nrclists; i++) 279 for (i = 0; i < args->csa_nrclists; i++)
315 kfree(args->csa_rclists[i].rcl_refcalls); 280 kfree(args->csa_rclists[i].rcl_refcalls);
316 kfree(args->csa_rclists); 281 kfree(args->csa_rclists);
317 282
318 if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) 283 if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) {
319 res->csr_status = 0; 284 cps->drc_status = status;
320 else 285 status = 0;
286 } else
321 res->csr_status = status; 287 res->csr_status = status;
288
322 dprintk("%s: exit with status = %d res->csr_status %d\n", __func__, 289 dprintk("%s: exit with status = %d res->csr_status %d\n", __func__,
323 ntohl(status), ntohl(res->csr_status)); 290 ntohl(status), ntohl(res->csr_status));
324 return status; 291 return status;
325} 292}
326 293
327__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy) 294__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy,
295 struct cb_process_state *cps)
328{ 296{
329 struct nfs_client *clp;
330 __be32 status; 297 __be32 status;
331 fmode_t flags = 0; 298 fmode_t flags = 0;
332 299
333 status = htonl(NFS4ERR_OP_NOT_IN_SESSION); 300 status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
334 clp = nfs_find_client(args->craa_addr, 4); 301 if (!cps->clp) /* set in cb_sequence */
335 if (clp == NULL)
336 goto out; 302 goto out;
337 303
338 dprintk("NFS: RECALL_ANY callback request from %s\n", 304 dprintk("NFS: RECALL_ANY callback request from %s\n",
339 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); 305 rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
340 306
341 if (test_bit(RCA4_TYPE_MASK_RDATA_DLG, (const unsigned long *) 307 if (test_bit(RCA4_TYPE_MASK_RDATA_DLG, (const unsigned long *)
342 &args->craa_type_mask)) 308 &args->craa_type_mask))
@@ -346,7 +312,7 @@ __be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy)
346 flags |= FMODE_WRITE; 312 flags |= FMODE_WRITE;
347 313
348 if (flags) 314 if (flags)
349 nfs_expire_all_delegation_types(clp, flags); 315 nfs_expire_all_delegation_types(cps->clp, flags);
350 status = htonl(NFS4_OK); 316 status = htonl(NFS4_OK);
351out: 317out:
352 dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); 318 dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
@@ -354,36 +320,33 @@ out:
354} 320}
355 321
356/* Reduce the fore channel's max_slots to the target value */ 322/* Reduce the fore channel's max_slots to the target value */
357__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy) 323__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy,
324 struct cb_process_state *cps)
358{ 325{
359 struct nfs_client *clp;
360 struct nfs4_slot_table *fc_tbl; 326 struct nfs4_slot_table *fc_tbl;
361 __be32 status; 327 __be32 status;
362 328
363 status = htonl(NFS4ERR_OP_NOT_IN_SESSION); 329 status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
364 clp = nfs_find_client(args->crsa_addr, 4); 330 if (!cps->clp) /* set in cb_sequence */
365 if (clp == NULL)
366 goto out; 331 goto out;
367 332
368 dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n", 333 dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n",
369 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR), 334 rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR),
370 args->crsa_target_max_slots); 335 args->crsa_target_max_slots);
371 336
372 fc_tbl = &clp->cl_session->fc_slot_table; 337 fc_tbl = &cps->clp->cl_session->fc_slot_table;
373 338
374 status = htonl(NFS4ERR_BAD_HIGH_SLOT); 339 status = htonl(NFS4ERR_BAD_HIGH_SLOT);
375 if (args->crsa_target_max_slots > fc_tbl->max_slots || 340 if (args->crsa_target_max_slots > fc_tbl->max_slots ||
376 args->crsa_target_max_slots < 1) 341 args->crsa_target_max_slots < 1)
377 goto out_putclient; 342 goto out;
378 343
379 status = htonl(NFS4_OK); 344 status = htonl(NFS4_OK);
380 if (args->crsa_target_max_slots == fc_tbl->max_slots) 345 if (args->crsa_target_max_slots == fc_tbl->max_slots)
381 goto out_putclient; 346 goto out;
382 347
383 fc_tbl->target_max_slots = args->crsa_target_max_slots; 348 fc_tbl->target_max_slots = args->crsa_target_max_slots;
384 nfs41_handle_recall_slot(clp); 349 nfs41_handle_recall_slot(cps->clp);
385out_putclient:
386 nfs_put_client(clp); /* balance nfs_find_client */
387out: 350out:
388 dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); 351 dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
389 return status; 352 return status;