Skip to content

Commit

Permalink
NFSv4: Fix races between nfs_remove_bad_delegation() and delegation r…
Browse files Browse the repository at this point in the history
…eturn

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>
  • Loading branch information
Trond Myklebust committed Nov 12, 2014
1 parent 0c116ca commit 869f9df
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 3 deletions.
23 changes: 21 additions & 2 deletions fs/nfs/delegation.c
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,11 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
{
int res = 0;

res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid, issync);
if (!test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
res = nfs4_proc_delegreturn(inode,
delegation->cred,
&delegation->stateid,
issync);
nfs_free_delegation(delegation);
return res;
}
Expand Down Expand Up @@ -380,11 +384,13 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
{
struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
struct nfs_inode *nfsi = NFS_I(inode);
int err;
int err = 0;

if (delegation == NULL)
return 0;
do {
if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
break;
err = nfs_delegation_claim_opens(inode, &delegation->stateid);
if (!issync || err != -EAGAIN)
break;
Expand Down Expand Up @@ -605,10 +611,23 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
rcu_read_unlock();
}

static void nfs_revoke_delegation(struct inode *inode)
{
struct nfs_delegation *delegation;
rcu_read_lock();
delegation = rcu_dereference(NFS_I(inode)->delegation);
if (delegation != NULL) {
set_bit(NFS_DELEGATION_REVOKED, &delegation->flags);
nfs_mark_return_delegation(NFS_SERVER(inode), delegation);
}
rcu_read_unlock();
}

void nfs_remove_bad_delegation(struct inode *inode)
{
struct nfs_delegation *delegation;

nfs_revoke_delegation(inode);
delegation = nfs_inode_detach_delegation(inode);
if (delegation) {
nfs_inode_find_state_and_recover(inode, &delegation->stateid);
Expand Down
1 change: 1 addition & 0 deletions fs/nfs/delegation.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ enum {
NFS_DELEGATION_RETURN_IF_CLOSED,
NFS_DELEGATION_REFERENCED,
NFS_DELEGATION_RETURNING,
NFS_DELEGATION_REVOKED,
};

int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
Expand Down
2 changes: 1 addition & 1 deletion fs/nfs/nfs4proc.c
Original file line number Diff line number Diff line change
Expand Up @@ -1654,7 +1654,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
nfs_inode_find_state_and_recover(state->inode,
stateid);
nfs4_schedule_stateid_recovery(server, state);
return 0;
return -EAGAIN;
case -NFS4ERR_DELAY:
case -NFS4ERR_GRACE:
set_bit(NFS_DELEGATED_STATE, &state->flags);
Expand Down

0 comments on commit 869f9df

Please sign in to comment.