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 @@ again: ...@@ -129,21 +129,35 @@ again:
*/ */
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);
if (delegation != NULL) {
spin_lock(&delegation->lock);
if (delegation->inode != NULL) {
memcpy(delegation->stateid.data, res->delegation.data, memcpy(delegation->stateid.data, res->delegation.data,
sizeof(delegation->stateid.data)); sizeof(delegation->stateid.data));
delegation->type = res->delegation_type; delegation->type = res->delegation_type;
delegation->maxsize = res->maxsize; delegation->maxsize = res->maxsize;
oldcred = delegation->cred; oldcred = delegation->cred;
delegation->cred = get_rpccred(cred); delegation->cred = get_rpccred(cred);
clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); clear_bit(NFS_DELEGATION_NEED_RECLAIM,
&delegation->flags);
NFS_I(inode)->delegation_state = delegation->type; NFS_I(inode)->delegation_state = delegation->type;
smp_wmb(); spin_unlock(&delegation->lock);
put_rpccred(oldcred); 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