diff options
Diffstat (limited to 'fs/nfs/delegation.c')
| -rw-r--r-- | fs/nfs/delegation.c | 90 |
1 files changed, 64 insertions, 26 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 7f3f60641344..a6ad68865880 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
| @@ -85,25 +85,30 @@ static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_ | |||
| 85 | { | 85 | { |
| 86 | struct inode *inode = state->inode; | 86 | struct inode *inode = state->inode; |
| 87 | struct file_lock *fl; | 87 | struct file_lock *fl; |
| 88 | struct file_lock_context *flctx = inode->i_flctx; | ||
| 89 | struct list_head *list; | ||
| 88 | int status = 0; | 90 | int status = 0; |
| 89 | 91 | ||
| 90 | if (inode->i_flock == NULL) | 92 | if (flctx == NULL) |
| 91 | goto out; | 93 | goto out; |
| 92 | 94 | ||
| 93 | /* Protect inode->i_flock using the i_lock */ | 95 | list = &flctx->flc_posix; |
| 94 | spin_lock(&inode->i_lock); | 96 | spin_lock(&flctx->flc_lock); |
| 95 | for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { | 97 | restart: |
| 96 | if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) | 98 | list_for_each_entry(fl, list, fl_list) { |
| 97 | continue; | ||
| 98 | if (nfs_file_open_context(fl->fl_file) != ctx) | 99 | if (nfs_file_open_context(fl->fl_file) != ctx) |
| 99 | continue; | 100 | continue; |
| 100 | spin_unlock(&inode->i_lock); | 101 | spin_unlock(&flctx->flc_lock); |
| 101 | status = nfs4_lock_delegation_recall(fl, state, stateid); | 102 | status = nfs4_lock_delegation_recall(fl, state, stateid); |
| 102 | if (status < 0) | 103 | if (status < 0) |
| 103 | goto out; | 104 | goto out; |
| 104 | spin_lock(&inode->i_lock); | 105 | spin_lock(&flctx->flc_lock); |
| 105 | } | 106 | } |
| 106 | spin_unlock(&inode->i_lock); | 107 | if (list == &flctx->flc_posix) { |
| 108 | list = &flctx->flc_flock; | ||
| 109 | goto restart; | ||
| 110 | } | ||
| 111 | spin_unlock(&flctx->flc_lock); | ||
| 107 | out: | 112 | out: |
| 108 | return status; | 113 | return status; |
| 109 | } | 114 | } |
| @@ -175,10 +180,9 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, | |||
| 175 | delegation->cred = get_rpccred(cred); | 180 | delegation->cred = get_rpccred(cred); |
| 176 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, | 181 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, |
| 177 | &delegation->flags); | 182 | &delegation->flags); |
| 178 | NFS_I(inode)->delegation_state = delegation->type; | ||
| 179 | spin_unlock(&delegation->lock); | 183 | spin_unlock(&delegation->lock); |
| 180 | put_rpccred(oldcred); | ||
| 181 | rcu_read_unlock(); | 184 | rcu_read_unlock(); |
| 185 | put_rpccred(oldcred); | ||
| 182 | trace_nfs4_reclaim_delegation(inode, res->delegation_type); | 186 | trace_nfs4_reclaim_delegation(inode, res->delegation_type); |
| 183 | } else { | 187 | } else { |
| 184 | /* We appear to have raced with a delegation return. */ | 188 | /* We appear to have raced with a delegation return. */ |
| @@ -270,7 +274,6 @@ nfs_detach_delegation_locked(struct nfs_inode *nfsi, | |||
| 270 | set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); | 274 | set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); |
| 271 | list_del_rcu(&delegation->super_list); | 275 | list_del_rcu(&delegation->super_list); |
| 272 | delegation->inode = NULL; | 276 | delegation->inode = NULL; |
| 273 | nfsi->delegation_state = 0; | ||
| 274 | rcu_assign_pointer(nfsi->delegation, NULL); | 277 | rcu_assign_pointer(nfsi->delegation, NULL); |
| 275 | spin_unlock(&delegation->lock); | 278 | spin_unlock(&delegation->lock); |
| 276 | return delegation; | 279 | return delegation; |
| @@ -301,6 +304,17 @@ nfs_inode_detach_delegation(struct inode *inode) | |||
| 301 | return nfs_detach_delegation(nfsi, delegation, server); | 304 | return nfs_detach_delegation(nfsi, delegation, server); |
| 302 | } | 305 | } |
| 303 | 306 | ||
| 307 | static void | ||
| 308 | nfs_update_inplace_delegation(struct nfs_delegation *delegation, | ||
| 309 | const struct nfs_delegation *update) | ||
| 310 | { | ||
| 311 | if (nfs4_stateid_is_newer(&update->stateid, &delegation->stateid)) { | ||
| 312 | delegation->stateid.seqid = update->stateid.seqid; | ||
| 313 | smp_wmb(); | ||
| 314 | delegation->type = update->type; | ||
| 315 | } | ||
| 316 | } | ||
| 317 | |||
| 304 | /** | 318 | /** |
| 305 | * nfs_inode_set_delegation - set up a delegation on an inode | 319 | * nfs_inode_set_delegation - set up a delegation on an inode |
| 306 | * @inode: inode to which delegation applies | 320 | * @inode: inode to which delegation applies |
| @@ -334,9 +348,11 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
| 334 | old_delegation = rcu_dereference_protected(nfsi->delegation, | 348 | old_delegation = rcu_dereference_protected(nfsi->delegation, |
| 335 | lockdep_is_held(&clp->cl_lock)); | 349 | lockdep_is_held(&clp->cl_lock)); |
| 336 | if (old_delegation != NULL) { | 350 | if (old_delegation != NULL) { |
| 337 | if (nfs4_stateid_match(&delegation->stateid, | 351 | /* Is this an update of the existing delegation? */ |
| 338 | &old_delegation->stateid) && | 352 | if (nfs4_stateid_match_other(&old_delegation->stateid, |
| 339 | delegation->type == old_delegation->type) { | 353 | &delegation->stateid)) { |
| 354 | nfs_update_inplace_delegation(old_delegation, | ||
| 355 | delegation); | ||
| 340 | goto out; | 356 | goto out; |
| 341 | } | 357 | } |
| 342 | /* | 358 | /* |
| @@ -354,13 +370,15 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
| 354 | delegation = NULL; | 370 | delegation = NULL; |
| 355 | goto out; | 371 | goto out; |
| 356 | } | 372 | } |
| 357 | freeme = nfs_detach_delegation_locked(nfsi, | 373 | if (test_and_set_bit(NFS_DELEGATION_RETURNING, |
| 374 | &old_delegation->flags)) | ||
| 375 | goto out; | ||
| 376 | freeme = nfs_detach_delegation_locked(nfsi, | ||
| 358 | old_delegation, clp); | 377 | old_delegation, clp); |
| 359 | if (freeme == NULL) | 378 | if (freeme == NULL) |
| 360 | goto out; | 379 | goto out; |
| 361 | } | 380 | } |
| 362 | list_add_rcu(&delegation->super_list, &server->delegations); | 381 | list_add_rcu(&delegation->super_list, &server->delegations); |
| 363 | nfsi->delegation_state = delegation->type; | ||
| 364 | rcu_assign_pointer(nfsi->delegation, delegation); | 382 | rcu_assign_pointer(nfsi->delegation, delegation); |
| 365 | delegation = NULL; | 383 | delegation = NULL; |
| 366 | 384 | ||
| @@ -418,6 +436,8 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation) | |||
| 418 | { | 436 | { |
| 419 | bool ret = false; | 437 | bool ret = false; |
| 420 | 438 | ||
| 439 | if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) | ||
| 440 | goto out; | ||
| 421 | if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) | 441 | if (test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) |
| 422 | ret = true; | 442 | ret = true; |
| 423 | if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) { | 443 | if (test_and_clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags) && !ret) { |
| @@ -429,6 +449,7 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation) | |||
| 429 | ret = true; | 449 | ret = true; |
| 430 | spin_unlock(&delegation->lock); | 450 | spin_unlock(&delegation->lock); |
| 431 | } | 451 | } |
| 452 | out: | ||
| 432 | return ret; | 453 | return ret; |
| 433 | } | 454 | } |
| 434 | 455 | ||
| @@ -456,14 +477,20 @@ restart: | |||
| 456 | super_list) { | 477 | super_list) { |
| 457 | if (!nfs_delegation_need_return(delegation)) | 478 | if (!nfs_delegation_need_return(delegation)) |
| 458 | continue; | 479 | continue; |
| 459 | inode = nfs_delegation_grab_inode(delegation); | 480 | if (!nfs_sb_active(server->super)) |
| 460 | if (inode == NULL) | ||
| 461 | continue; | 481 | continue; |
| 482 | inode = nfs_delegation_grab_inode(delegation); | ||
| 483 | if (inode == NULL) { | ||
| 484 | rcu_read_unlock(); | ||
| 485 | nfs_sb_deactive(server->super); | ||
| 486 | goto restart; | ||
| 487 | } | ||
| 462 | delegation = nfs_start_delegation_return_locked(NFS_I(inode)); | 488 | delegation = nfs_start_delegation_return_locked(NFS_I(inode)); |
| 463 | rcu_read_unlock(); | 489 | rcu_read_unlock(); |
| 464 | 490 | ||
| 465 | err = nfs_end_delegation_return(inode, delegation, 0); | 491 | err = nfs_end_delegation_return(inode, delegation, 0); |
| 466 | iput(inode); | 492 | iput(inode); |
| 493 | nfs_sb_deactive(server->super); | ||
| 467 | if (!err) | 494 | if (!err) |
| 468 | goto restart; | 495 | goto restart; |
| 469 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); | 496 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); |
| @@ -794,19 +821,30 @@ restart: | |||
| 794 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { | 821 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { |
| 795 | list_for_each_entry_rcu(delegation, &server->delegations, | 822 | list_for_each_entry_rcu(delegation, &server->delegations, |
| 796 | super_list) { | 823 | super_list) { |
| 824 | if (test_bit(NFS_DELEGATION_RETURNING, | ||
| 825 | &delegation->flags)) | ||
| 826 | continue; | ||
| 797 | if (test_bit(NFS_DELEGATION_NEED_RECLAIM, | 827 | if (test_bit(NFS_DELEGATION_NEED_RECLAIM, |
| 798 | &delegation->flags) == 0) | 828 | &delegation->flags) == 0) |
| 799 | continue; | 829 | continue; |
| 800 | inode = nfs_delegation_grab_inode(delegation); | 830 | if (!nfs_sb_active(server->super)) |
| 801 | if (inode == NULL) | ||
| 802 | continue; | 831 | continue; |
| 803 | delegation = nfs_detach_delegation(NFS_I(inode), | 832 | inode = nfs_delegation_grab_inode(delegation); |
| 804 | delegation, server); | 833 | if (inode == NULL) { |
| 834 | rcu_read_unlock(); | ||
| 835 | nfs_sb_deactive(server->super); | ||
| 836 | goto restart; | ||
| 837 | } | ||
| 838 | delegation = nfs_start_delegation_return_locked(NFS_I(inode)); | ||
| 805 | rcu_read_unlock(); | 839 | rcu_read_unlock(); |
| 806 | 840 | if (delegation != NULL) { | |
| 807 | if (delegation != NULL) | 841 | delegation = nfs_detach_delegation(NFS_I(inode), |
| 808 | nfs_free_delegation(delegation); | 842 | delegation, server); |
| 843 | if (delegation != NULL) | ||
| 844 | nfs_free_delegation(delegation); | ||
| 845 | } | ||
| 809 | iput(inode); | 846 | iput(inode); |
| 847 | nfs_sb_deactive(server->super); | ||
| 810 | goto restart; | 848 | goto restart; |
| 811 | } | 849 | } |
| 812 | } | 850 | } |
