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: Trond Myklebust <Trond.Myklebust@netapp.com> Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
This commit is contained in:
		
					parent
					
						
							
								be1066bbcd
							
						
					
				
			
			
				commit
				
					
						8f649c3762
					
				
			
		
					 1 changed files with 28 additions and 14 deletions
				
			
		| 
						 | 
					@ -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);
 | 
				
			||||||
	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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue