diff options
Diffstat (limited to 'fs/nfs/delegation.c')
-rw-r--r-- | fs/nfs/delegation.c | 86 |
1 files changed, 51 insertions, 35 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 15671245c6ee..ea61d26e7871 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
@@ -24,6 +24,8 @@ | |||
24 | 24 | ||
25 | static void nfs_do_free_delegation(struct nfs_delegation *delegation) | 25 | static void nfs_do_free_delegation(struct nfs_delegation *delegation) |
26 | { | 26 | { |
27 | if (delegation->cred) | ||
28 | put_rpccred(delegation->cred); | ||
27 | kfree(delegation); | 29 | kfree(delegation); |
28 | } | 30 | } |
29 | 31 | ||
@@ -36,13 +38,7 @@ static void nfs_free_delegation_callback(struct rcu_head *head) | |||
36 | 38 | ||
37 | static void nfs_free_delegation(struct nfs_delegation *delegation) | 39 | static void nfs_free_delegation(struct nfs_delegation *delegation) |
38 | { | 40 | { |
39 | struct rpc_cred *cred; | ||
40 | |||
41 | cred = rcu_dereference(delegation->cred); | ||
42 | rcu_assign_pointer(delegation->cred, NULL); | ||
43 | call_rcu(&delegation->rcu, nfs_free_delegation_callback); | 41 | call_rcu(&delegation->rcu, nfs_free_delegation_callback); |
44 | if (cred) | ||
45 | put_rpccred(cred); | ||
46 | } | 42 | } |
47 | 43 | ||
48 | void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) | 44 | void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) |
@@ -129,21 +125,35 @@ again: | |||
129 | */ | 125 | */ |
130 | void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) | 126 | void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) |
131 | { | 127 | { |
132 | struct nfs_delegation *delegation = NFS_I(inode)->delegation; | 128 | struct nfs_delegation *delegation; |
133 | struct rpc_cred *oldcred; | 129 | struct rpc_cred *oldcred = NULL; |
134 | 130 | ||
135 | if (delegation == NULL) | 131 | rcu_read_lock(); |
136 | return; | 132 | delegation = rcu_dereference(NFS_I(inode)->delegation); |
137 | memcpy(delegation->stateid.data, res->delegation.data, | 133 | if (delegation != NULL) { |
138 | sizeof(delegation->stateid.data)); | 134 | spin_lock(&delegation->lock); |
139 | delegation->type = res->delegation_type; | 135 | if (delegation->inode != NULL) { |
140 | delegation->maxsize = res->maxsize; | 136 | memcpy(delegation->stateid.data, res->delegation.data, |
141 | oldcred = delegation->cred; | 137 | sizeof(delegation->stateid.data)); |
142 | delegation->cred = get_rpccred(cred); | 138 | delegation->type = res->delegation_type; |
143 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); | 139 | delegation->maxsize = res->maxsize; |
144 | NFS_I(inode)->delegation_state = delegation->type; | 140 | oldcred = delegation->cred; |
145 | smp_wmb(); | 141 | delegation->cred = get_rpccred(cred); |
146 | put_rpccred(oldcred); | 142 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, |
143 | &delegation->flags); | ||
144 | NFS_I(inode)->delegation_state = delegation->type; | ||
145 | spin_unlock(&delegation->lock); | ||
146 | put_rpccred(oldcred); | ||
147 | rcu_read_unlock(); | ||
148 | } else { | ||
149 | /* We appear to have raced with a delegation return. */ | ||
150 | spin_unlock(&delegation->lock); | ||
151 | rcu_read_unlock(); | ||
152 | nfs_inode_set_delegation(inode, cred, res); | ||
153 | } | ||
154 | } else { | ||
155 | rcu_read_unlock(); | ||
156 | } | ||
147 | } | 157 | } |
148 | 158 | ||
149 | static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) | 159 | static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) |
@@ -166,9 +176,13 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation | |||
166 | return inode; | 176 | return inode; |
167 | } | 177 | } |
168 | 178 | ||
169 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) | 179 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, |
180 | const nfs4_stateid *stateid, | ||
181 | struct nfs_client *clp) | ||
170 | { | 182 | { |
171 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); | 183 | struct nfs_delegation *delegation = |
184 | rcu_dereference_protected(nfsi->delegation, | ||
185 | lockdep_is_held(&clp->cl_lock)); | ||
172 | 186 | ||
173 | if (delegation == NULL) | 187 | if (delegation == NULL) |
174 | goto nomatch; | 188 | goto nomatch; |
@@ -195,7 +209,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
195 | { | 209 | { |
196 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | 210 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; |
197 | struct nfs_inode *nfsi = NFS_I(inode); | 211 | struct nfs_inode *nfsi = NFS_I(inode); |
198 | struct nfs_delegation *delegation; | 212 | struct nfs_delegation *delegation, *old_delegation; |
199 | struct nfs_delegation *freeme = NULL; | 213 | struct nfs_delegation *freeme = NULL; |
200 | int status = 0; | 214 | int status = 0; |
201 | 215 | ||
@@ -213,10 +227,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
213 | spin_lock_init(&delegation->lock); | 227 | spin_lock_init(&delegation->lock); |
214 | 228 | ||
215 | spin_lock(&clp->cl_lock); | 229 | spin_lock(&clp->cl_lock); |
216 | if (rcu_dereference(nfsi->delegation) != NULL) { | 230 | old_delegation = rcu_dereference_protected(nfsi->delegation, |
217 | if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, | 231 | lockdep_is_held(&clp->cl_lock)); |
218 | sizeof(delegation->stateid)) == 0 && | 232 | if (old_delegation != NULL) { |
219 | delegation->type == nfsi->delegation->type) { | 233 | if (memcmp(&delegation->stateid, &old_delegation->stateid, |
234 | sizeof(old_delegation->stateid)) == 0 && | ||
235 | delegation->type == old_delegation->type) { | ||
220 | goto out; | 236 | goto out; |
221 | } | 237 | } |
222 | /* | 238 | /* |
@@ -226,12 +242,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
226 | dfprintk(FILE, "%s: server %s handed out " | 242 | dfprintk(FILE, "%s: server %s handed out " |
227 | "a duplicate delegation!\n", | 243 | "a duplicate delegation!\n", |
228 | __func__, clp->cl_hostname); | 244 | __func__, clp->cl_hostname); |
229 | if (delegation->type <= nfsi->delegation->type) { | 245 | if (delegation->type <= old_delegation->type) { |
230 | freeme = delegation; | 246 | freeme = delegation; |
231 | delegation = NULL; | 247 | delegation = NULL; |
232 | goto out; | 248 | goto out; |
233 | } | 249 | } |
234 | freeme = nfs_detach_delegation_locked(nfsi, NULL); | 250 | freeme = nfs_detach_delegation_locked(nfsi, NULL, clp); |
235 | } | 251 | } |
236 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); | 252 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); |
237 | nfsi->delegation_state = delegation->type; | 253 | nfsi->delegation_state = delegation->type; |
@@ -301,7 +317,7 @@ restart: | |||
301 | if (inode == NULL) | 317 | if (inode == NULL) |
302 | continue; | 318 | continue; |
303 | spin_lock(&clp->cl_lock); | 319 | spin_lock(&clp->cl_lock); |
304 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | 320 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); |
305 | spin_unlock(&clp->cl_lock); | 321 | spin_unlock(&clp->cl_lock); |
306 | rcu_read_unlock(); | 322 | rcu_read_unlock(); |
307 | if (delegation != NULL) { | 323 | if (delegation != NULL) { |
@@ -330,9 +346,9 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode) | |||
330 | struct nfs_inode *nfsi = NFS_I(inode); | 346 | struct nfs_inode *nfsi = NFS_I(inode); |
331 | struct nfs_delegation *delegation; | 347 | struct nfs_delegation *delegation; |
332 | 348 | ||
333 | if (rcu_dereference(nfsi->delegation) != NULL) { | 349 | if (rcu_access_pointer(nfsi->delegation) != NULL) { |
334 | spin_lock(&clp->cl_lock); | 350 | spin_lock(&clp->cl_lock); |
335 | delegation = nfs_detach_delegation_locked(nfsi, NULL); | 351 | delegation = nfs_detach_delegation_locked(nfsi, NULL, clp); |
336 | spin_unlock(&clp->cl_lock); | 352 | spin_unlock(&clp->cl_lock); |
337 | if (delegation != NULL) | 353 | if (delegation != NULL) |
338 | nfs_do_return_delegation(inode, delegation, 0); | 354 | nfs_do_return_delegation(inode, delegation, 0); |
@@ -346,9 +362,9 @@ int nfs_inode_return_delegation(struct inode *inode) | |||
346 | struct nfs_delegation *delegation; | 362 | struct nfs_delegation *delegation; |
347 | int err = 0; | 363 | int err = 0; |
348 | 364 | ||
349 | if (rcu_dereference(nfsi->delegation) != NULL) { | 365 | if (rcu_access_pointer(nfsi->delegation) != NULL) { |
350 | spin_lock(&clp->cl_lock); | 366 | spin_lock(&clp->cl_lock); |
351 | delegation = nfs_detach_delegation_locked(nfsi, NULL); | 367 | delegation = nfs_detach_delegation_locked(nfsi, NULL, clp); |
352 | spin_unlock(&clp->cl_lock); | 368 | spin_unlock(&clp->cl_lock); |
353 | if (delegation != NULL) { | 369 | if (delegation != NULL) { |
354 | nfs_msync_inode(inode); | 370 | nfs_msync_inode(inode); |
@@ -526,7 +542,7 @@ restart: | |||
526 | if (inode == NULL) | 542 | if (inode == NULL) |
527 | continue; | 543 | continue; |
528 | spin_lock(&clp->cl_lock); | 544 | spin_lock(&clp->cl_lock); |
529 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | 545 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); |
530 | spin_unlock(&clp->cl_lock); | 546 | spin_unlock(&clp->cl_lock); |
531 | rcu_read_unlock(); | 547 | rcu_read_unlock(); |
532 | if (delegation != NULL) | 548 | if (delegation != NULL) |