diff options
Diffstat (limited to 'fs/nfs/delegation.c')
-rw-r--r-- | fs/nfs/delegation.c | 393 |
1 files changed, 269 insertions, 124 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index b9c3c43cea1d..dd25c2aec375 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
@@ -11,7 +11,6 @@ | |||
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
13 | #include <linux/slab.h> | 13 | #include <linux/slab.h> |
14 | #include <linux/smp_lock.h> | ||
15 | #include <linux/spinlock.h> | 14 | #include <linux/spinlock.h> |
16 | 15 | ||
17 | #include <linux/nfs4.h> | 16 | #include <linux/nfs4.h> |
@@ -22,30 +21,32 @@ | |||
22 | #include "delegation.h" | 21 | #include "delegation.h" |
23 | #include "internal.h" | 22 | #include "internal.h" |
24 | 23 | ||
25 | static void nfs_do_free_delegation(struct nfs_delegation *delegation) | ||
26 | { | ||
27 | if (delegation->cred) | ||
28 | put_rpccred(delegation->cred); | ||
29 | kfree(delegation); | ||
30 | } | ||
31 | |||
32 | static void nfs_free_delegation_callback(struct rcu_head *head) | ||
33 | { | ||
34 | struct nfs_delegation *delegation = container_of(head, struct nfs_delegation, rcu); | ||
35 | |||
36 | nfs_do_free_delegation(delegation); | ||
37 | } | ||
38 | |||
39 | static void nfs_free_delegation(struct nfs_delegation *delegation) | 24 | static void nfs_free_delegation(struct nfs_delegation *delegation) |
40 | { | 25 | { |
41 | call_rcu(&delegation->rcu, nfs_free_delegation_callback); | 26 | if (delegation->cred) { |
27 | put_rpccred(delegation->cred); | ||
28 | delegation->cred = NULL; | ||
29 | } | ||
30 | kfree_rcu(delegation, rcu); | ||
42 | } | 31 | } |
43 | 32 | ||
33 | /** | ||
34 | * nfs_mark_delegation_referenced - set delegation's REFERENCED flag | ||
35 | * @delegation: delegation to process | ||
36 | * | ||
37 | */ | ||
44 | void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) | 38 | void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) |
45 | { | 39 | { |
46 | set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); | 40 | set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); |
47 | } | 41 | } |
48 | 42 | ||
43 | /** | ||
44 | * nfs_have_delegation - check if inode has a delegation | ||
45 | * @inode: inode to check | ||
46 | * @flags: delegation types to check for | ||
47 | * | ||
48 | * Returns one if inode has the indicated delegation, otherwise zero. | ||
49 | */ | ||
49 | int nfs_have_delegation(struct inode *inode, fmode_t flags) | 50 | int nfs_have_delegation(struct inode *inode, fmode_t flags) |
50 | { | 51 | { |
51 | struct nfs_delegation *delegation; | 52 | struct nfs_delegation *delegation; |
@@ -71,20 +72,20 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_ | |||
71 | if (inode->i_flock == NULL) | 72 | if (inode->i_flock == NULL) |
72 | goto out; | 73 | goto out; |
73 | 74 | ||
74 | /* Protect inode->i_flock using the BKL */ | 75 | /* Protect inode->i_flock using the file locks lock */ |
75 | lock_kernel(); | 76 | lock_flocks(); |
76 | for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { | 77 | for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { |
77 | if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) | 78 | if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) |
78 | continue; | 79 | continue; |
79 | if (nfs_file_open_context(fl->fl_file) != ctx) | 80 | if (nfs_file_open_context(fl->fl_file) != ctx) |
80 | continue; | 81 | continue; |
81 | unlock_kernel(); | 82 | unlock_flocks(); |
82 | status = nfs4_lock_delegation_recall(state, fl); | 83 | status = nfs4_lock_delegation_recall(state, fl); |
83 | if (status < 0) | 84 | if (status < 0) |
84 | goto out; | 85 | goto out; |
85 | lock_kernel(); | 86 | lock_flocks(); |
86 | } | 87 | } |
87 | unlock_kernel(); | 88 | unlock_flocks(); |
88 | out: | 89 | out: |
89 | return status; | 90 | return status; |
90 | } | 91 | } |
@@ -120,10 +121,15 @@ again: | |||
120 | return 0; | 121 | return 0; |
121 | } | 122 | } |
122 | 123 | ||
123 | /* | 124 | /** |
124 | * Set up a delegation on an inode | 125 | * nfs_inode_reclaim_delegation - process a delegation reclaim request |
126 | * @inode: inode to process | ||
127 | * @cred: credential to use for request | ||
128 | * @res: new delegation state from server | ||
129 | * | ||
125 | */ | 130 | */ |
126 | void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) | 131 | void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, |
132 | struct nfs_openres *res) | ||
127 | { | 133 | { |
128 | struct nfs_delegation *delegation; | 134 | struct nfs_delegation *delegation; |
129 | struct rpc_cred *oldcred = NULL; | 135 | struct rpc_cred *oldcred = NULL; |
@@ -176,38 +182,52 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation | |||
176 | return inode; | 182 | return inode; |
177 | } | 183 | } |
178 | 184 | ||
179 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, | 185 | static struct nfs_delegation * |
180 | const nfs4_stateid *stateid, | 186 | nfs_detach_delegation_locked(struct nfs_inode *nfsi, |
181 | struct nfs_client *clp) | 187 | struct nfs_server *server) |
182 | { | 188 | { |
183 | struct nfs_delegation *delegation = | 189 | struct nfs_delegation *delegation = |
184 | rcu_dereference_protected(nfsi->delegation, | 190 | rcu_dereference_protected(nfsi->delegation, |
185 | lockdep_is_held(&clp->cl_lock)); | 191 | lockdep_is_held(&server->nfs_client->cl_lock)); |
186 | 192 | ||
187 | if (delegation == NULL) | 193 | if (delegation == NULL) |
188 | goto nomatch; | 194 | goto nomatch; |
195 | |||
189 | spin_lock(&delegation->lock); | 196 | spin_lock(&delegation->lock); |
190 | if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, | ||
191 | sizeof(delegation->stateid.data)) != 0) | ||
192 | goto nomatch_unlock; | ||
193 | list_del_rcu(&delegation->super_list); | 197 | list_del_rcu(&delegation->super_list); |
194 | delegation->inode = NULL; | 198 | delegation->inode = NULL; |
195 | nfsi->delegation_state = 0; | 199 | nfsi->delegation_state = 0; |
196 | rcu_assign_pointer(nfsi->delegation, NULL); | 200 | rcu_assign_pointer(nfsi->delegation, NULL); |
197 | spin_unlock(&delegation->lock); | 201 | spin_unlock(&delegation->lock); |
198 | return delegation; | 202 | return delegation; |
199 | nomatch_unlock: | ||
200 | spin_unlock(&delegation->lock); | ||
201 | nomatch: | 203 | nomatch: |
202 | return NULL; | 204 | return NULL; |
203 | } | 205 | } |
204 | 206 | ||
205 | /* | 207 | static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, |
206 | * Set up a delegation on an inode | 208 | struct nfs_server *server) |
209 | { | ||
210 | struct nfs_client *clp = server->nfs_client; | ||
211 | struct nfs_delegation *delegation; | ||
212 | |||
213 | spin_lock(&clp->cl_lock); | ||
214 | delegation = nfs_detach_delegation_locked(nfsi, server); | ||
215 | spin_unlock(&clp->cl_lock); | ||
216 | return delegation; | ||
217 | } | ||
218 | |||
219 | /** | ||
220 | * nfs_inode_set_delegation - set up a delegation on an inode | ||
221 | * @inode: inode to which delegation applies | ||
222 | * @cred: cred to use for subsequent delegation processing | ||
223 | * @res: new delegation state from server | ||
224 | * | ||
225 | * Returns zero on success, or a negative errno value. | ||
207 | */ | 226 | */ |
208 | int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) | 227 | int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) |
209 | { | 228 | { |
210 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | 229 | struct nfs_server *server = NFS_SERVER(inode); |
230 | struct nfs_client *clp = server->nfs_client; | ||
211 | struct nfs_inode *nfsi = NFS_I(inode); | 231 | struct nfs_inode *nfsi = NFS_I(inode); |
212 | struct nfs_delegation *delegation, *old_delegation; | 232 | struct nfs_delegation *delegation, *old_delegation; |
213 | struct nfs_delegation *freeme = NULL; | 233 | struct nfs_delegation *freeme = NULL; |
@@ -228,7 +248,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
228 | 248 | ||
229 | spin_lock(&clp->cl_lock); | 249 | spin_lock(&clp->cl_lock); |
230 | old_delegation = rcu_dereference_protected(nfsi->delegation, | 250 | old_delegation = rcu_dereference_protected(nfsi->delegation, |
231 | lockdep_is_held(&clp->cl_lock)); | 251 | lockdep_is_held(&clp->cl_lock)); |
232 | if (old_delegation != NULL) { | 252 | if (old_delegation != NULL) { |
233 | if (memcmp(&delegation->stateid, &old_delegation->stateid, | 253 | if (memcmp(&delegation->stateid, &old_delegation->stateid, |
234 | sizeof(old_delegation->stateid)) == 0 && | 254 | sizeof(old_delegation->stateid)) == 0 && |
@@ -247,9 +267,9 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
247 | delegation = NULL; | 267 | delegation = NULL; |
248 | goto out; | 268 | goto out; |
249 | } | 269 | } |
250 | freeme = nfs_detach_delegation_locked(nfsi, NULL, clp); | 270 | freeme = nfs_detach_delegation_locked(nfsi, server); |
251 | } | 271 | } |
252 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); | 272 | list_add_rcu(&delegation->super_list, &server->delegations); |
253 | nfsi->delegation_state = delegation->type; | 273 | nfsi->delegation_state = delegation->type; |
254 | rcu_assign_pointer(nfsi->delegation, delegation); | 274 | rcu_assign_pointer(nfsi->delegation, delegation); |
255 | delegation = NULL; | 275 | delegation = NULL; |
@@ -291,73 +311,85 @@ out: | |||
291 | return err; | 311 | return err; |
292 | } | 312 | } |
293 | 313 | ||
294 | /* | 314 | /** |
295 | * Return all delegations that have been marked for return | 315 | * nfs_client_return_marked_delegations - return previously marked delegations |
316 | * @clp: nfs_client to process | ||
317 | * | ||
318 | * Returns zero on success, or a negative errno value. | ||
296 | */ | 319 | */ |
297 | int nfs_client_return_marked_delegations(struct nfs_client *clp) | 320 | int nfs_client_return_marked_delegations(struct nfs_client *clp) |
298 | { | 321 | { |
299 | struct nfs_delegation *delegation; | 322 | struct nfs_delegation *delegation; |
323 | struct nfs_server *server; | ||
300 | struct inode *inode; | 324 | struct inode *inode; |
301 | int err = 0; | 325 | int err = 0; |
302 | 326 | ||
303 | restart: | 327 | restart: |
304 | rcu_read_lock(); | 328 | rcu_read_lock(); |
305 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 329 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { |
306 | if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) | 330 | list_for_each_entry_rcu(delegation, &server->delegations, |
307 | continue; | 331 | super_list) { |
308 | inode = nfs_delegation_grab_inode(delegation); | 332 | if (!test_and_clear_bit(NFS_DELEGATION_RETURN, |
309 | if (inode == NULL) | 333 | &delegation->flags)) |
310 | continue; | 334 | continue; |
311 | spin_lock(&clp->cl_lock); | 335 | inode = nfs_delegation_grab_inode(delegation); |
312 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); | 336 | if (inode == NULL) |
313 | spin_unlock(&clp->cl_lock); | 337 | continue; |
314 | rcu_read_unlock(); | 338 | delegation = nfs_detach_delegation(NFS_I(inode), |
315 | if (delegation != NULL) { | 339 | server); |
316 | filemap_flush(inode->i_mapping); | 340 | rcu_read_unlock(); |
317 | err = __nfs_inode_return_delegation(inode, delegation, 0); | 341 | |
342 | if (delegation != NULL) { | ||
343 | filemap_flush(inode->i_mapping); | ||
344 | err = __nfs_inode_return_delegation(inode, | ||
345 | delegation, 0); | ||
346 | } | ||
347 | iput(inode); | ||
348 | if (!err) | ||
349 | goto restart; | ||
350 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); | ||
351 | return err; | ||
318 | } | 352 | } |
319 | iput(inode); | ||
320 | if (!err) | ||
321 | goto restart; | ||
322 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); | ||
323 | return err; | ||
324 | } | 353 | } |
325 | rcu_read_unlock(); | 354 | rcu_read_unlock(); |
326 | return 0; | 355 | return 0; |
327 | } | 356 | } |
328 | 357 | ||
329 | /* | 358 | /** |
330 | * This function returns the delegation without reclaiming opens | 359 | * nfs_inode_return_delegation_noreclaim - return delegation, don't reclaim opens |
331 | * or protecting against delegation reclaims. | 360 | * @inode: inode to process |
332 | * It is therefore really only safe to be called from | 361 | * |
333 | * nfs4_clear_inode() | 362 | * Does not protect against delegation reclaims, therefore really only safe |
363 | * to be called from nfs4_clear_inode(). | ||
334 | */ | 364 | */ |
335 | void nfs_inode_return_delegation_noreclaim(struct inode *inode) | 365 | void nfs_inode_return_delegation_noreclaim(struct inode *inode) |
336 | { | 366 | { |
337 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | 367 | struct nfs_server *server = NFS_SERVER(inode); |
338 | struct nfs_inode *nfsi = NFS_I(inode); | 368 | struct nfs_inode *nfsi = NFS_I(inode); |
339 | struct nfs_delegation *delegation; | 369 | struct nfs_delegation *delegation; |
340 | 370 | ||
341 | if (rcu_access_pointer(nfsi->delegation) != NULL) { | 371 | if (rcu_access_pointer(nfsi->delegation) != NULL) { |
342 | spin_lock(&clp->cl_lock); | 372 | delegation = nfs_detach_delegation(nfsi, server); |
343 | delegation = nfs_detach_delegation_locked(nfsi, NULL, clp); | ||
344 | spin_unlock(&clp->cl_lock); | ||
345 | if (delegation != NULL) | 373 | if (delegation != NULL) |
346 | nfs_do_return_delegation(inode, delegation, 0); | 374 | nfs_do_return_delegation(inode, delegation, 0); |
347 | } | 375 | } |
348 | } | 376 | } |
349 | 377 | ||
378 | /** | ||
379 | * nfs_inode_return_delegation - synchronously return a delegation | ||
380 | * @inode: inode to process | ||
381 | * | ||
382 | * Returns zero on success, or a negative errno value. | ||
383 | */ | ||
350 | int nfs_inode_return_delegation(struct inode *inode) | 384 | int nfs_inode_return_delegation(struct inode *inode) |
351 | { | 385 | { |
352 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | 386 | struct nfs_server *server = NFS_SERVER(inode); |
353 | struct nfs_inode *nfsi = NFS_I(inode); | 387 | struct nfs_inode *nfsi = NFS_I(inode); |
354 | struct nfs_delegation *delegation; | 388 | struct nfs_delegation *delegation; |
355 | int err = 0; | 389 | int err = 0; |
356 | 390 | ||
357 | if (rcu_access_pointer(nfsi->delegation) != NULL) { | 391 | if (rcu_access_pointer(nfsi->delegation) != NULL) { |
358 | spin_lock(&clp->cl_lock); | 392 | delegation = nfs_detach_delegation(nfsi, server); |
359 | delegation = nfs_detach_delegation_locked(nfsi, NULL, clp); | ||
360 | spin_unlock(&clp->cl_lock); | ||
361 | if (delegation != NULL) { | 393 | if (delegation != NULL) { |
362 | nfs_wb_all(inode); | 394 | nfs_wb_all(inode); |
363 | err = __nfs_inode_return_delegation(inode, delegation, 1); | 395 | err = __nfs_inode_return_delegation(inode, delegation, 1); |
@@ -366,46 +398,61 @@ int nfs_inode_return_delegation(struct inode *inode) | |||
366 | return err; | 398 | return err; |
367 | } | 399 | } |
368 | 400 | ||
369 | static void nfs_mark_return_delegation(struct nfs_client *clp, struct nfs_delegation *delegation) | 401 | static void nfs_mark_return_delegation(struct nfs_delegation *delegation) |
370 | { | 402 | { |
403 | struct nfs_client *clp = NFS_SERVER(delegation->inode)->nfs_client; | ||
404 | |||
371 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); | 405 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); |
372 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); | 406 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); |
373 | } | 407 | } |
374 | 408 | ||
375 | /* | 409 | /** |
376 | * Return all delegations associated to a super block | 410 | * nfs_super_return_all_delegations - return delegations for one superblock |
411 | * @sb: sb to process | ||
412 | * | ||
377 | */ | 413 | */ |
378 | void nfs_super_return_all_delegations(struct super_block *sb) | 414 | void nfs_super_return_all_delegations(struct super_block *sb) |
379 | { | 415 | { |
380 | struct nfs_client *clp = NFS_SB(sb)->nfs_client; | 416 | struct nfs_server *server = NFS_SB(sb); |
417 | struct nfs_client *clp = server->nfs_client; | ||
381 | struct nfs_delegation *delegation; | 418 | struct nfs_delegation *delegation; |
382 | 419 | ||
383 | if (clp == NULL) | 420 | if (clp == NULL) |
384 | return; | 421 | return; |
422 | |||
385 | rcu_read_lock(); | 423 | rcu_read_lock(); |
386 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 424 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) { |
387 | spin_lock(&delegation->lock); | 425 | spin_lock(&delegation->lock); |
388 | if (delegation->inode != NULL && delegation->inode->i_sb == sb) | 426 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); |
389 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); | ||
390 | spin_unlock(&delegation->lock); | 427 | spin_unlock(&delegation->lock); |
391 | } | 428 | } |
392 | rcu_read_unlock(); | 429 | rcu_read_unlock(); |
430 | |||
393 | if (nfs_client_return_marked_delegations(clp) != 0) | 431 | if (nfs_client_return_marked_delegations(clp) != 0) |
394 | nfs4_schedule_state_manager(clp); | 432 | nfs4_schedule_state_manager(clp); |
395 | } | 433 | } |
396 | 434 | ||
397 | static | 435 | static void nfs_mark_return_all_delegation_types(struct nfs_server *server, |
398 | void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, fmode_t flags) | 436 | fmode_t flags) |
399 | { | 437 | { |
400 | struct nfs_delegation *delegation; | 438 | struct nfs_delegation *delegation; |
401 | 439 | ||
402 | rcu_read_lock(); | 440 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) { |
403 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | ||
404 | if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) | 441 | if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) |
405 | continue; | 442 | continue; |
406 | if (delegation->type & flags) | 443 | if (delegation->type & flags) |
407 | nfs_mark_return_delegation(clp, delegation); | 444 | nfs_mark_return_delegation(delegation); |
408 | } | 445 | } |
446 | } | ||
447 | |||
448 | static void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, | ||
449 | fmode_t flags) | ||
450 | { | ||
451 | struct nfs_server *server; | ||
452 | |||
453 | rcu_read_lock(); | ||
454 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) | ||
455 | nfs_mark_return_all_delegation_types(server, flags); | ||
409 | rcu_read_unlock(); | 456 | rcu_read_unlock(); |
410 | } | 457 | } |
411 | 458 | ||
@@ -420,19 +467,32 @@ static void nfs_delegation_run_state_manager(struct nfs_client *clp) | |||
420 | nfs4_schedule_state_manager(clp); | 467 | nfs4_schedule_state_manager(clp); |
421 | } | 468 | } |
422 | 469 | ||
470 | /** | ||
471 | * nfs_expire_all_delegation_types | ||
472 | * @clp: client to process | ||
473 | * @flags: delegation types to expire | ||
474 | * | ||
475 | */ | ||
423 | void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags) | 476 | void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags) |
424 | { | 477 | { |
425 | nfs_client_mark_return_all_delegation_types(clp, flags); | 478 | nfs_client_mark_return_all_delegation_types(clp, flags); |
426 | nfs_delegation_run_state_manager(clp); | 479 | nfs_delegation_run_state_manager(clp); |
427 | } | 480 | } |
428 | 481 | ||
482 | /** | ||
483 | * nfs_expire_all_delegations | ||
484 | * @clp: client to process | ||
485 | * | ||
486 | */ | ||
429 | void nfs_expire_all_delegations(struct nfs_client *clp) | 487 | void nfs_expire_all_delegations(struct nfs_client *clp) |
430 | { | 488 | { |
431 | nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); | 489 | nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); |
432 | } | 490 | } |
433 | 491 | ||
434 | /* | 492 | /** |
435 | * Return all delegations following an NFS4ERR_CB_PATH_DOWN error. | 493 | * nfs_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN |
494 | * @clp: client to process | ||
495 | * | ||
436 | */ | 496 | */ |
437 | void nfs_handle_cb_pathdown(struct nfs_client *clp) | 497 | void nfs_handle_cb_pathdown(struct nfs_client *clp) |
438 | { | 498 | { |
@@ -441,29 +501,43 @@ void nfs_handle_cb_pathdown(struct nfs_client *clp) | |||
441 | nfs_client_mark_return_all_delegations(clp); | 501 | nfs_client_mark_return_all_delegations(clp); |
442 | } | 502 | } |
443 | 503 | ||
444 | static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *clp) | 504 | static void nfs_mark_return_unreferenced_delegations(struct nfs_server *server) |
445 | { | 505 | { |
446 | struct nfs_delegation *delegation; | 506 | struct nfs_delegation *delegation; |
447 | 507 | ||
448 | rcu_read_lock(); | 508 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) { |
449 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | ||
450 | if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) | 509 | if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) |
451 | continue; | 510 | continue; |
452 | nfs_mark_return_delegation(clp, delegation); | 511 | nfs_mark_return_delegation(delegation); |
453 | } | 512 | } |
454 | rcu_read_unlock(); | ||
455 | } | 513 | } |
456 | 514 | ||
515 | /** | ||
516 | * nfs_expire_unreferenced_delegations - Eliminate unused delegations | ||
517 | * @clp: nfs_client to process | ||
518 | * | ||
519 | */ | ||
457 | void nfs_expire_unreferenced_delegations(struct nfs_client *clp) | 520 | void nfs_expire_unreferenced_delegations(struct nfs_client *clp) |
458 | { | 521 | { |
459 | nfs_client_mark_return_unreferenced_delegations(clp); | 522 | struct nfs_server *server; |
523 | |||
524 | rcu_read_lock(); | ||
525 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) | ||
526 | nfs_mark_return_unreferenced_delegations(server); | ||
527 | rcu_read_unlock(); | ||
528 | |||
460 | nfs_delegation_run_state_manager(clp); | 529 | nfs_delegation_run_state_manager(clp); |
461 | } | 530 | } |
462 | 531 | ||
463 | /* | 532 | /** |
464 | * Asynchronous delegation recall! | 533 | * nfs_async_inode_return_delegation - asynchronously return a delegation |
534 | * @inode: inode to process | ||
535 | * @stateid: state ID information from CB_RECALL arguments | ||
536 | * | ||
537 | * Returns zero on success, or a negative errno value. | ||
465 | */ | 538 | */ |
466 | int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid) | 539 | int nfs_async_inode_return_delegation(struct inode *inode, |
540 | const nfs4_stateid *stateid) | ||
467 | { | 541 | { |
468 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | 542 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; |
469 | struct nfs_delegation *delegation; | 543 | struct nfs_delegation *delegation; |
@@ -475,22 +549,21 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s | |||
475 | rcu_read_unlock(); | 549 | rcu_read_unlock(); |
476 | return -ENOENT; | 550 | return -ENOENT; |
477 | } | 551 | } |
478 | 552 | nfs_mark_return_delegation(delegation); | |
479 | nfs_mark_return_delegation(clp, delegation); | ||
480 | rcu_read_unlock(); | 553 | rcu_read_unlock(); |
554 | |||
481 | nfs_delegation_run_state_manager(clp); | 555 | nfs_delegation_run_state_manager(clp); |
482 | return 0; | 556 | return 0; |
483 | } | 557 | } |
484 | 558 | ||
485 | /* | 559 | static struct inode * |
486 | * Retrieve the inode associated with a delegation | 560 | nfs_delegation_find_inode_server(struct nfs_server *server, |
487 | */ | 561 | const struct nfs_fh *fhandle) |
488 | struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle) | ||
489 | { | 562 | { |
490 | struct nfs_delegation *delegation; | 563 | struct nfs_delegation *delegation; |
491 | struct inode *res = NULL; | 564 | struct inode *res = NULL; |
492 | rcu_read_lock(); | 565 | |
493 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 566 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) { |
494 | spin_lock(&delegation->lock); | 567 | spin_lock(&delegation->lock); |
495 | if (delegation->inode != NULL && | 568 | if (delegation->inode != NULL && |
496 | nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { | 569 | nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { |
@@ -500,49 +573,121 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs | |||
500 | if (res != NULL) | 573 | if (res != NULL) |
501 | break; | 574 | break; |
502 | } | 575 | } |
576 | return res; | ||
577 | } | ||
578 | |||
579 | /** | ||
580 | * nfs_delegation_find_inode - retrieve the inode associated with a delegation | ||
581 | * @clp: client state handle | ||
582 | * @fhandle: filehandle from a delegation recall | ||
583 | * | ||
584 | * Returns pointer to inode matching "fhandle," or NULL if a matching inode | ||
585 | * cannot be found. | ||
586 | */ | ||
587 | struct inode *nfs_delegation_find_inode(struct nfs_client *clp, | ||
588 | const struct nfs_fh *fhandle) | ||
589 | { | ||
590 | struct nfs_server *server; | ||
591 | struct inode *res = NULL; | ||
592 | |||
593 | rcu_read_lock(); | ||
594 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { | ||
595 | res = nfs_delegation_find_inode_server(server, fhandle); | ||
596 | if (res != NULL) | ||
597 | break; | ||
598 | } | ||
503 | rcu_read_unlock(); | 599 | rcu_read_unlock(); |
504 | return res; | 600 | return res; |
505 | } | 601 | } |
506 | 602 | ||
507 | /* | 603 | static void nfs_delegation_mark_reclaim_server(struct nfs_server *server) |
508 | * Mark all delegations as needing to be reclaimed | 604 | { |
605 | struct nfs_delegation *delegation; | ||
606 | |||
607 | list_for_each_entry_rcu(delegation, &server->delegations, super_list) | ||
608 | set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); | ||
609 | } | ||
610 | |||
611 | /** | ||
612 | * nfs_delegation_mark_reclaim - mark all delegations as needing to be reclaimed | ||
613 | * @clp: nfs_client to process | ||
614 | * | ||
509 | */ | 615 | */ |
510 | void nfs_delegation_mark_reclaim(struct nfs_client *clp) | 616 | void nfs_delegation_mark_reclaim(struct nfs_client *clp) |
511 | { | 617 | { |
512 | struct nfs_delegation *delegation; | 618 | struct nfs_server *server; |
619 | |||
513 | rcu_read_lock(); | 620 | rcu_read_lock(); |
514 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) | 621 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) |
515 | set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); | 622 | nfs_delegation_mark_reclaim_server(server); |
516 | rcu_read_unlock(); | 623 | rcu_read_unlock(); |
517 | } | 624 | } |
518 | 625 | ||
519 | /* | 626 | /** |
520 | * Reap all unclaimed delegations after reboot recovery is done | 627 | * nfs_delegation_reap_unclaimed - reap unclaimed delegations after reboot recovery is done |
628 | * @clp: nfs_client to process | ||
629 | * | ||
521 | */ | 630 | */ |
522 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp) | 631 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp) |
523 | { | 632 | { |
524 | struct nfs_delegation *delegation; | 633 | struct nfs_delegation *delegation; |
634 | struct nfs_server *server; | ||
525 | struct inode *inode; | 635 | struct inode *inode; |
636 | |||
526 | restart: | 637 | restart: |
527 | rcu_read_lock(); | 638 | rcu_read_lock(); |
528 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 639 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { |
529 | if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0) | 640 | list_for_each_entry_rcu(delegation, &server->delegations, |
530 | continue; | 641 | super_list) { |
531 | inode = nfs_delegation_grab_inode(delegation); | 642 | if (test_bit(NFS_DELEGATION_NEED_RECLAIM, |
532 | if (inode == NULL) | 643 | &delegation->flags) == 0) |
533 | continue; | 644 | continue; |
534 | spin_lock(&clp->cl_lock); | 645 | inode = nfs_delegation_grab_inode(delegation); |
535 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); | 646 | if (inode == NULL) |
536 | spin_unlock(&clp->cl_lock); | 647 | continue; |
537 | rcu_read_unlock(); | 648 | delegation = nfs_detach_delegation(NFS_I(inode), |
538 | if (delegation != NULL) | 649 | server); |
539 | nfs_free_delegation(delegation); | 650 | rcu_read_unlock(); |
540 | iput(inode); | 651 | |
541 | goto restart; | 652 | if (delegation != NULL) |
653 | nfs_free_delegation(delegation); | ||
654 | iput(inode); | ||
655 | goto restart; | ||
656 | } | ||
542 | } | 657 | } |
543 | rcu_read_unlock(); | 658 | rcu_read_unlock(); |
544 | } | 659 | } |
545 | 660 | ||
661 | /** | ||
662 | * nfs_delegations_present - check for existence of delegations | ||
663 | * @clp: client state handle | ||
664 | * | ||
665 | * Returns one if there are any nfs_delegation structures attached | ||
666 | * to this nfs_client. | ||
667 | */ | ||
668 | int nfs_delegations_present(struct nfs_client *clp) | ||
669 | { | ||
670 | struct nfs_server *server; | ||
671 | int ret = 0; | ||
672 | |||
673 | rcu_read_lock(); | ||
674 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) | ||
675 | if (!list_empty(&server->delegations)) { | ||
676 | ret = 1; | ||
677 | break; | ||
678 | } | ||
679 | rcu_read_unlock(); | ||
680 | return ret; | ||
681 | } | ||
682 | |||
683 | /** | ||
684 | * nfs4_copy_delegation_stateid - Copy inode's state ID information | ||
685 | * @dst: stateid data structure to fill in | ||
686 | * @inode: inode to check | ||
687 | * | ||
688 | * Returns one and fills in "dst->data" * if inode had a delegation, | ||
689 | * otherwise zero is returned. | ||
690 | */ | ||
546 | int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode) | 691 | int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode) |
547 | { | 692 | { |
548 | struct nfs_inode *nfsi = NFS_I(inode); | 693 | struct nfs_inode *nfsi = NFS_I(inode); |