diff options
Diffstat (limited to 'fs/nfs/delegation.c')
| -rw-r--r-- | fs/nfs/delegation.c | 87 | 
1 files changed, 52 insertions, 35 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 2563bebc4c6..ea61d26e787 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c @@ -10,6 +10,7 @@  #include <linux/kthread.h>  #include <linux/module.h>  #include <linux/sched.h> +#include <linux/slab.h>  #include <linux/smp_lock.h>  #include <linux/spinlock.h> @@ -23,6 +24,8 @@  static void nfs_do_free_delegation(struct nfs_delegation *delegation)  { +	if (delegation->cred) +		put_rpccred(delegation->cred);  	kfree(delegation);  } @@ -35,13 +38,7 @@ static void nfs_free_delegation_callback(struct rcu_head *head)  static void nfs_free_delegation(struct nfs_delegation *delegation)  { -	struct rpc_cred *cred; - -	cred = rcu_dereference(delegation->cred); -	rcu_assign_pointer(delegation->cred, NULL);  	call_rcu(&delegation->rcu, nfs_free_delegation_callback); -	if (cred) -		put_rpccred(cred);  }  void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) @@ -128,21 +125,35 @@ again:   */  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 rpc_cred *oldcred; +	struct nfs_delegation *delegation; +	struct rpc_cred *oldcred = NULL; -	if (delegation == NULL) -		return; -	memcpy(delegation->stateid.data, res->delegation.data, -			sizeof(delegation->stateid.data)); -	delegation->type = res->delegation_type; -	delegation->maxsize = res->maxsize; -	oldcred = delegation->cred; -	delegation->cred = get_rpccred(cred); -	clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); -	NFS_I(inode)->delegation_state = delegation->type; -	smp_wmb(); -	put_rpccred(oldcred); +	rcu_read_lock(); +	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, +			       sizeof(delegation->stateid.data)); +			delegation->type = res->delegation_type; +			delegation->maxsize = res->maxsize; +			oldcred = delegation->cred; +			delegation->cred = get_rpccred(cred); +			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) @@ -165,9 +176,13 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation  	return inode;  } -static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) +static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, +							   const nfs4_stateid *stateid, +							   struct nfs_client *clp)  { -	struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); +	struct nfs_delegation *delegation = +		rcu_dereference_protected(nfsi->delegation, +					  lockdep_is_held(&clp->cl_lock));  	if (delegation == NULL)  		goto nomatch; @@ -194,7 +209,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct  {  	struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;  	struct nfs_inode *nfsi = NFS_I(inode); -	struct nfs_delegation *delegation; +	struct nfs_delegation *delegation, *old_delegation;  	struct nfs_delegation *freeme = NULL;  	int status = 0; @@ -212,10 +227,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct  	spin_lock_init(&delegation->lock);  	spin_lock(&clp->cl_lock); -	if (rcu_dereference(nfsi->delegation) != NULL) { -		if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, -					sizeof(delegation->stateid)) == 0 && -				delegation->type == nfsi->delegation->type) { +	old_delegation = rcu_dereference_protected(nfsi->delegation, +						   lockdep_is_held(&clp->cl_lock)); +	if (old_delegation != NULL) { +		if (memcmp(&delegation->stateid, &old_delegation->stateid, +					sizeof(old_delegation->stateid)) == 0 && +				delegation->type == old_delegation->type) {  			goto out;  		}  		/* @@ -225,12 +242,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct  		dfprintk(FILE, "%s: server %s handed out "  				"a duplicate delegation!\n",  				__func__, clp->cl_hostname); -		if (delegation->type <= nfsi->delegation->type) { +		if (delegation->type <= old_delegation->type) {  			freeme = delegation;  			delegation = NULL;  			goto out;  		} -		freeme = nfs_detach_delegation_locked(nfsi, NULL); +		freeme = nfs_detach_delegation_locked(nfsi, NULL, clp);  	}  	list_add_rcu(&delegation->super_list, &clp->cl_delegations);  	nfsi->delegation_state = delegation->type; @@ -300,7 +317,7 @@ restart:  		if (inode == NULL)  			continue;  		spin_lock(&clp->cl_lock); -		delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); +		delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp);  		spin_unlock(&clp->cl_lock);  		rcu_read_unlock();  		if (delegation != NULL) { @@ -329,9 +346,9 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode)  	struct nfs_inode *nfsi = NFS_I(inode);  	struct nfs_delegation *delegation; -	if (rcu_dereference(nfsi->delegation) != NULL) { +	if (rcu_access_pointer(nfsi->delegation) != NULL) {  		spin_lock(&clp->cl_lock); -		delegation = nfs_detach_delegation_locked(nfsi, NULL); +		delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);  		spin_unlock(&clp->cl_lock);  		if (delegation != NULL)  			nfs_do_return_delegation(inode, delegation, 0); @@ -345,9 +362,9 @@ int nfs_inode_return_delegation(struct inode *inode)  	struct nfs_delegation *delegation;  	int err = 0; -	if (rcu_dereference(nfsi->delegation) != NULL) { +	if (rcu_access_pointer(nfsi->delegation) != NULL) {  		spin_lock(&clp->cl_lock); -		delegation = nfs_detach_delegation_locked(nfsi, NULL); +		delegation = nfs_detach_delegation_locked(nfsi, NULL, clp);  		spin_unlock(&clp->cl_lock);  		if (delegation != NULL) {  			nfs_msync_inode(inode); @@ -525,7 +542,7 @@ restart:  		if (inode == NULL)  			continue;  		spin_lock(&clp->cl_lock); -		delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); +		delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp);  		spin_unlock(&clp->cl_lock);  		rcu_read_unlock();  		if (delegation != NULL)  |