aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorTrond Myklebust <trond.myklebust@primarydata.com>2014-11-10 18:43:56 -0500
committerTrond Myklebust <trond.myklebust@primarydata.com>2014-11-12 17:19:04 -0500
commit869f9dfa4d6d57b79e0afc3af14772c2a023eeb1 (patch)
tree48adcdfacd4ac26a9faf36b8d31f9d2ed9f1217c /fs/nfs
parent0c116cadd94b16b30b1dd90d38b2784d9b39b01a (diff)
NFSv4: Fix races between nfs_remove_bad_delegation() and delegation return
Any attempt to call nfs_remove_bad_delegation() while a delegation is being returned is currently a no-op. This means that we can end up looping forever in nfs_end_delegation_return() if something causes the delegation to be revoked. This patch adds a mechanism whereby the state recovery code can communicate to the delegation return code that the delegation is no longer valid and that it should not be used when reclaiming state. It also changes the return value for nfs4_handle_delegation_recall_error() to ensure that nfs_end_delegation_return() does not reattempt the lock reclaim before state recovery is done. http://lkml.kernel.org/r/CAN-5tyHwG=Cn2Q9KsHWadewjpTTy_K26ee+UnSvHvG4192p-Xw@mail.gmail.com Cc: stable@vger.kernel.org Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/delegation.c23
-rw-r--r--fs/nfs/delegation.h1
-rw-r--r--fs/nfs/nfs4proc.c2
3 files changed, 23 insertions, 3 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 5853f53db732..e5f473d13e24 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -193,7 +193,11 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
193{ 193{
194 int res = 0; 194 int res = 0;
195 195
196 res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync); 196 if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
197 res = nfs4_proc_delegreturn(inode,
198 delegation->cred,
199 &delegation->stateid,
200 issync);
197 nfs_free_delegation(delegation); 201 nfs_free_delegation(delegation);
198 return res; 202 return res;
199} 203}
@@ -380,11 +384,13 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
380{ 384{
381 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; 385 struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
382 struct nfs_inode *nfsi = NFS_I(inode); 386 struct nfs_inode *nfsi = NFS_I(inode);
383 int err; 387 int err = 0;
384 388
385 if (delegation == NULL) 389 if (delegation == NULL)
386 return 0; 390 return 0;
387 do { 391 do {
392 if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
393 break;
388 err = nfs_delegation_claim_opens(inode, &delegation->stateid); 394 err = nfs_delegation_claim_opens(inode, &delegation->stateid);
389 if (!issync || err != -EAGAIN) 395 if (!issync || err != -EAGAIN)
390 break; 396 break;
@@ -605,10 +611,23 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
605 rcu_read_unlock(); 611 rcu_read_unlock();
606} 612}
607 613
614static void nfs_revoke_delegation(struct inode *inode)
615{
616 struct nfs_delegation *delegation;
617 rcu_read_lock();
618 delegation = rcu_dereference(NFS_I(inode)->delegation);
619 if (delegation != NULL) {
620 set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
621 nfs_mark_return_delegation(NFS_SERVER(inode), delegation);
622 }
623 rcu_read_unlock();
624}
625
608void nfs_remove_bad_delegation(struct inode *inode) 626void nfs_remove_bad_delegation(struct inode *inode)
609{ 627{
610 struct nfs_delegation *delegation; 628 struct nfs_delegation *delegation;
611 629
630 nfs_revoke_delegation(inode);
612 delegation = nfs_inode_detach_delegation(inode); 631 delegation = nfs_inode_detach_delegation(inode);
613 if (delegation) { 632 if (delegation) {
614 nfs_inode_find_state_and_recover(inode, &delegation->stateid); 633 nfs_inode_find_state_and_recover(inode, &delegation->stateid);
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h
index 5c1cce39297f..e3c20a3ccc93 100644
--- a/fs/nfs/delegation.h
+++ b/fs/nfs/delegation.h
@@ -31,6 +31,7 @@ enum {
31 NFS_DELEGATION_RETURN_IF_CLOSED, 31 NFS_DELEGATION_RETURN_IF_CLOSED,
32 NFS_DELEGATION_REFERENCED, 32 NFS_DELEGATION_REFERENCED,
33 NFS_DELEGATION_RETURNING, 33 NFS_DELEGATION_RETURNING,
34 NFS_DELEGATION_REVOKED,
34}; 35};
35 36
36int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); 37int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 3b98fe752ef8..4b7166f4e1cf 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -1654,7 +1654,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
1654 nfs_inode_find_state_and_recover(state->inode, 1654 nfs_inode_find_state_and_recover(state->inode,
1655 stateid); 1655 stateid);
1656 nfs4_schedule_stateid_recovery(server, state); 1656 nfs4_schedule_stateid_recovery(server, state);
1657 return 0; 1657 return -EAGAIN;
1658 case -NFS4ERR_DELAY: 1658 case -NFS4ERR_DELAY:
1659 case -NFS4ERR_GRACE: 1659 case -NFS4ERR_GRACE:
1660 set_bit(NFS_DELEGATED_STATE, &state->flags); 1660 set_bit(NFS_DELEGATED_STATE, &state->flags);