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>
This commit is contained in:
parent
0c116cadd9
commit
869f9dfa4d
@ -193,7 +193,11 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
|
|||||||
{
|
{
|
||||||
int res = 0;
|
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);
|
nfs_free_delegation(delegation);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@ -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_client *clp = NFS_SERVER(inode)->nfs_client;
|
||||||
struct nfs_inode *nfsi = NFS_I(inode);
|
struct nfs_inode *nfsi = NFS_I(inode);
|
||||||
int err;
|
int err = 0;
|
||||||
|
|
||||||
if (delegation == NULL)
|
if (delegation == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
do {
|
do {
|
||||||
|
if (test_bit(NFS_DELEGATION_REVOKED, &delegation->flags))
|
||||||
|
break;
|
||||||
err = nfs_delegation_claim_opens(inode, &delegation->stateid);
|
err = nfs_delegation_claim_opens(inode, &delegation->stateid);
|
||||||
if (!issync || err != -EAGAIN)
|
if (!issync || err != -EAGAIN)
|
||||||
break;
|
break;
|
||||||
@ -605,10 +611,23 @@ static void nfs_client_mark_return_unused_delegation_types(struct nfs_client *cl
|
|||||||
rcu_read_unlock();
|
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)
|
void nfs_remove_bad_delegation(struct inode *inode)
|
||||||
{
|
{
|
||||||
struct nfs_delegation *delegation;
|
struct nfs_delegation *delegation;
|
||||||
|
|
||||||
|
nfs_revoke_delegation(inode);
|
||||||
delegation = nfs_inode_detach_delegation(inode);
|
delegation = nfs_inode_detach_delegation(inode);
|
||||||
if (delegation) {
|
if (delegation) {
|
||||||
nfs_inode_find_state_and_recover(inode, &delegation->stateid);
|
nfs_inode_find_state_and_recover(inode, &delegation->stateid);
|
||||||
|
@ -31,6 +31,7 @@ enum {
|
|||||||
NFS_DELEGATION_RETURN_IF_CLOSED,
|
NFS_DELEGATION_RETURN_IF_CLOSED,
|
||||||
NFS_DELEGATION_REFERENCED,
|
NFS_DELEGATION_REFERENCED,
|
||||||
NFS_DELEGATION_RETURNING,
|
NFS_DELEGATION_RETURNING,
|
||||||
|
NFS_DELEGATION_REVOKED,
|
||||||
};
|
};
|
||||||
|
|
||||||
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
|
int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
|
||||||
|
@ -1654,7 +1654,7 @@ static int nfs4_handle_delegation_recall_error(struct nfs_server *server, struct
|
|||||||
nfs_inode_find_state_and_recover(state->inode,
|
nfs_inode_find_state_and_recover(state->inode,
|
||||||
stateid);
|
stateid);
|
||||||
nfs4_schedule_stateid_recovery(server, state);
|
nfs4_schedule_stateid_recovery(server, state);
|
||||||
return 0;
|
return -EAGAIN;
|
||||||
case -NFS4ERR_DELAY:
|
case -NFS4ERR_DELAY:
|
||||||
case -NFS4ERR_GRACE:
|
case -NFS4ERR_GRACE:
|
||||||
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
set_bit(NFS_DELEGATED_STATE, &state->flags);
|
||||||
|
Loading…
Reference in New Issue
Block a user