NFSv4: Fix a potential state reclaim deadlock
If the server reboots while we are engaged in a delegation return, and
there is a pNFS layout with return-on-close set, then the current code
can end up deadlocking in pnfs_roc() when nfs_inode_set_delegation()
tries to return the old delegation.
Now that delegreturn actually uses its own copy of the stateid, it
should be safe to just always update the delegation stateid in place.
Fixes: 078000d02d
("pNFS: We want return-on-close to complete when evicting the inode")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
This commit is contained in:
parent
cf0d7e7f45
commit
1ba04394e0
|
@ -228,8 +228,7 @@ again:
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
|
void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
|
||||||
fmode_t type,
|
fmode_t type, const nfs4_stateid *stateid,
|
||||||
const nfs4_stateid *stateid,
|
|
||||||
unsigned long pagemod_limit)
|
unsigned long pagemod_limit)
|
||||||
{
|
{
|
||||||
struct nfs_delegation *delegation;
|
struct nfs_delegation *delegation;
|
||||||
|
@ -239,25 +238,24 @@ void nfs_inode_reclaim_delegation(struct inode *inode, const struct cred *cred,
|
||||||
delegation = rcu_dereference(NFS_I(inode)->delegation);
|
delegation = rcu_dereference(NFS_I(inode)->delegation);
|
||||||
if (delegation != NULL) {
|
if (delegation != NULL) {
|
||||||
spin_lock(&delegation->lock);
|
spin_lock(&delegation->lock);
|
||||||
if (nfs4_is_valid_delegation(delegation, 0)) {
|
nfs4_stateid_copy(&delegation->stateid, stateid);
|
||||||
nfs4_stateid_copy(&delegation->stateid, stateid);
|
delegation->type = type;
|
||||||
delegation->type = type;
|
delegation->pagemod_limit = pagemod_limit;
|
||||||
delegation->pagemod_limit = pagemod_limit;
|
oldcred = delegation->cred;
|
||||||
oldcred = delegation->cred;
|
delegation->cred = get_cred(cred);
|
||||||
delegation->cred = get_cred(cred);
|
clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
|
||||||
clear_bit(NFS_DELEGATION_NEED_RECLAIM,
|
if (test_and_clear_bit(NFS_DELEGATION_REVOKED,
|
||||||
&delegation->flags);
|
&delegation->flags))
|
||||||
spin_unlock(&delegation->lock);
|
atomic_long_inc(&nfs_active_delegations);
|
||||||
rcu_read_unlock();
|
|
||||||
put_cred(oldcred);
|
|
||||||
trace_nfs4_reclaim_delegation(inode, type);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* We appear to have raced with a delegation return. */
|
|
||||||
spin_unlock(&delegation->lock);
|
spin_unlock(&delegation->lock);
|
||||||
|
rcu_read_unlock();
|
||||||
|
put_cred(oldcred);
|
||||||
|
trace_nfs4_reclaim_delegation(inode, type);
|
||||||
|
} else {
|
||||||
|
rcu_read_unlock();
|
||||||
|
nfs_inode_set_delegation(inode, cred, type, stateid,
|
||||||
|
pagemod_limit);
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
|
||||||
nfs_inode_set_delegation(inode, cred, type, stateid, pagemod_limit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
|
static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)
|
||||||
|
|
Loading…
Reference in New Issue