Commit 8c75593c authored by Trond Myklebust's avatar Trond Myklebust Committed by Anna Schumaker

NFSv4: Ensure the delegation is pinned in nfs_do_return_delegation()

The call to nfs_do_return_delegation() needs to be taken without
any RCU locks. Add a refcount to make sure the delegation remains
pinned in memory until we're done.

Fixes: ee05f456 ("NFSv4: Fix races between open and delegreturn")
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: default avatarAnna Schumaker <Anna.Schumaker@Netapp.com>
parent cd1b659d
...@@ -47,10 +47,22 @@ static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation) ...@@ -47,10 +47,22 @@ static void nfs_mark_delegation_revoked(struct nfs_delegation *delegation)
} }
} }
static struct nfs_delegation *nfs_get_delegation(struct nfs_delegation *delegation)
{
refcount_inc(&delegation->refcount);
return delegation;
}
static void nfs_put_delegation(struct nfs_delegation *delegation)
{
if (refcount_dec_and_test(&delegation->refcount))
__nfs_free_delegation(delegation);
}
static void nfs_free_delegation(struct nfs_delegation *delegation) static void nfs_free_delegation(struct nfs_delegation *delegation)
{ {
nfs_mark_delegation_revoked(delegation); nfs_mark_delegation_revoked(delegation);
__nfs_free_delegation(delegation); nfs_put_delegation(delegation);
} }
/** /**
...@@ -275,8 +287,10 @@ nfs_start_delegation_return_locked(struct nfs_inode *nfsi) ...@@ -275,8 +287,10 @@ nfs_start_delegation_return_locked(struct nfs_inode *nfsi)
if (delegation == NULL) if (delegation == NULL)
goto out; goto out;
spin_lock(&delegation->lock); spin_lock(&delegation->lock);
if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
ret = delegation; /* Refcount matched in nfs_end_delegation_return() */
ret = nfs_get_delegation(delegation);
}
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
if (ret) if (ret)
nfs_clear_verifier_delegated(&nfsi->vfs_inode); nfs_clear_verifier_delegated(&nfsi->vfs_inode);
...@@ -397,6 +411,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred, ...@@ -397,6 +411,7 @@ int nfs_inode_set_delegation(struct inode *inode, const struct cred *cred,
if (delegation == NULL) if (delegation == NULL)
return -ENOMEM; return -ENOMEM;
nfs4_stateid_copy(&delegation->stateid, stateid); nfs4_stateid_copy(&delegation->stateid, stateid);
refcount_set(&delegation->refcount, 1);
delegation->type = type; delegation->type = type;
delegation->pagemod_limit = pagemod_limit; delegation->pagemod_limit = pagemod_limit;
delegation->change_attr = inode_peek_iversion_raw(inode); delegation->change_attr = inode_peek_iversion_raw(inode);
...@@ -496,6 +511,8 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation ...@@ -496,6 +511,8 @@ static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation
err = nfs_do_return_delegation(inode, delegation, issync); err = nfs_do_return_delegation(inode, delegation, issync);
out: out:
/* Refcount matched in nfs_start_delegation_return_locked() */
nfs_put_delegation(delegation);
return err; return err;
} }
...@@ -690,7 +707,8 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode) ...@@ -690,7 +707,8 @@ void nfs4_inode_return_delegation_on_close(struct inode *inode)
list_empty(&NFS_I(inode)->open_files) && list_empty(&NFS_I(inode)->open_files) &&
!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { !test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) {
clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags); clear_bit(NFS_DELEGATION_RETURN_IF_CLOSED, &delegation->flags);
ret = delegation; /* Refcount matched in nfs_end_delegation_return() */
ret = nfs_get_delegation(delegation);
} }
spin_unlock(&delegation->lock); spin_unlock(&delegation->lock);
if (ret) if (ret)
...@@ -1094,10 +1112,11 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp) ...@@ -1094,10 +1112,11 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
delegation = nfs_start_delegation_return_locked(NFS_I(inode)); delegation = nfs_start_delegation_return_locked(NFS_I(inode));
rcu_read_unlock(); rcu_read_unlock();
if (delegation != NULL) { if (delegation != NULL) {
delegation = nfs_detach_delegation(NFS_I(inode), if (nfs_detach_delegation(NFS_I(inode), delegation,
delegation, server); server) != NULL)
if (delegation != NULL)
nfs_free_delegation(delegation); nfs_free_delegation(delegation);
/* Match nfs_start_delegation_return_locked */
nfs_put_delegation(delegation);
} }
iput(inode); iput(inode);
nfs_sb_deactive(server->super); nfs_sb_deactive(server->super);
......
...@@ -22,6 +22,7 @@ struct nfs_delegation { ...@@ -22,6 +22,7 @@ struct nfs_delegation {
unsigned long pagemod_limit; unsigned long pagemod_limit;
__u64 change_attr; __u64 change_attr;
unsigned long flags; unsigned long flags;
refcount_t refcount;
spinlock_t lock; spinlock_t lock;
struct rcu_head rcu; struct rcu_head rcu;
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment