aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2010-05-01 12:36:18 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2010-05-01 12:36:18 -0400
commit8f649c376254755f2261a693b3d48d09126218dc (patch)
tree818338eca6dea5c7e022a3a6df51c3b2aa9e54b7 /fs/nfs
parentbe1066bbcd443a65df312fdecea7e4959adedb45 (diff)
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>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/delegation.c42
1 files changed, 28 insertions, 14 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c
index 15671245c6ee..8d9ec494a944 100644
--- a/fs/nfs/delegation.c
+++ b/fs/nfs/delegation.c
@@ -129,21 +129,35 @@ again:
129 */ 129 */
130void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) 130void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res)
131{ 131{
132 struct nfs_delegation *delegation = NFS_I(inode)->delegation; 132 struct nfs_delegation *delegation;
133 struct rpc_cred *oldcred; 133 struct rpc_cred *oldcred = NULL;
134 134
135 if (delegation == NULL) 135 rcu_read_lock();
136 return; 136 delegation = rcu_dereference(NFS_I(inode)->delegation);
137 memcpy(delegation->stateid.data, res->delegation.data, 137 if (delegation != NULL) {
138 sizeof(delegation->stateid.data)); 138 spin_lock(&delegation->lock);
139 delegation->type = res->delegation_type; 139 if (delegation->inode != NULL) {
140 delegation->maxsize = res->maxsize; 140 memcpy(delegation->stateid.data, res->delegation.data,
141 oldcred = delegation->cred; 141 sizeof(delegation->stateid.data));
142 delegation->cred = get_rpccred(cred); 142 delegation->type = res->delegation_type;
143 clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); 143 delegation->maxsize = res->maxsize;
144 NFS_I(inode)->delegation_state = delegation->type; 144 oldcred = delegation->cred;
145 smp_wmb(); 145 delegation->cred = get_rpccred(cred);
146 put_rpccred(oldcred); 146 clear_bit(NFS_DELEGATION_NEED_RECLAIM,
147 &delegation->flags);
148 NFS_I(inode)->delegation_state = delegation->type;
149 spin_unlock(&delegation->lock);
150 put_rpccred(oldcred);
151 rcu_read_unlock();
152 } else {
153 /* We appear to have raced with a delegation return. */
154 spin_unlock(&delegation->lock);
155 rcu_read_unlock();
156 nfs_inode_set_delegation(inode, cred, res);
157 }
158 } else {
159 rcu_read_unlock();
160 }
147} 161}
148 162
149static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) 163static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync)