diff options
author | Andy Adamson <andros@netapp.com> | 2011-01-05 21:04:32 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-01-06 14:46:24 -0500 |
commit | c36fca52f5e4594ffd0ff175b328966b0d393184 (patch) | |
tree | 6d771744cc49f0edc0d2b6b2f9fe919163002346 /fs/nfs/callback_proc.c | |
parent | 2c2618c6f29c41a0a966f14f05c8bf45fcabb750 (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.c | 167 |
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 * | |||
55 | out_iput: | 57 | out_iput: |
56 | rcu_read_unlock(); | 58 | rcu_read_unlock(); |
57 | iput(inode); | 59 | iput(inode); |
58 | out_putclient: | ||
59 | nfs_put_client(clp); | ||
60 | out: | 60 | out: |
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); | ||
101 | out: | 95 | out: |
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 | ||
311 | out_putclient: | ||
312 | nfs_put_client(clp); | ||
313 | out: | 278 | out: |
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); |
351 | out: | 317 | out: |
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); |
385 | out_putclient: | ||
386 | nfs_put_client(clp); /* balance nfs_find_client */ | ||
387 | out: | 350 | out: |
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; |