Commit 8f649c37 authored by Trond Myklebust's avatar Trond Myklebust

NFSv4: Fix the locking in nfs_inode_reclaim_delegation()

Ensure that we correctly rcu-dereference the delegation itself, and that we
protect against removal while we're changing the contents.
Signed-off-by: default avatarTrond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
parent be1066bb
...@@ -129,21 +129,35 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s ...@@ -129,21 +129,35 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s
*/ */
void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
{ {
struct nfs_delegation *delegation = NFS_I(inode)->delegation; struct nfs_delegation *delegation;
struct rpc_cred *oldcred; struct rpc_cred *oldcred = NULL;
if (delegation == NULL) rcu_read_lock();
return; delegation = rcu_dereference(NFS_I(inode)->delegation);
memcpy(delegation->stateid.data, res->delegation.data, if (delegation != NULL) {
sizeof(delegation->stateid.data)); spin_lock(&delegation->lock);
delegation->type = res->delegation_type; if (delegation->inode != NULL) {
delegation->maxsize = res->maxsize; memcpy(delegation->stateid.data, res->delegation.data,
oldcred = delegation->cred; sizeof(delegation->stateid.data));
delegation->cred = get_rpccred(cred); delegation->type = res->delegation_type;
clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); delegation->maxsize = res->maxsize;
NFS_I(inode)->delegation_state = delegation->type; oldcred = delegation->cred;
smp_wmb(); delegation->cred = get_rpccred(cred);
put_rpccred(oldcred); clear_bit(NFS_DELEGATION_NEED_RECLAIM,
&delegation->flags);
NFS_I(inode)->delegation_state = delegation->type;
spin_unlock(&delegation->lock);
put_rpccred(oldcred);
rcu_read_unlock();
} else {
/* We appear to have raced with a delegation return. */
spin_unlock(&delegation->lock);
rcu_read_unlock();
nfs_inode_set_delegation(inode, cred, res);
}
} else {
rcu_read_unlock();
}
} }
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)
......
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