diff options
author | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
---|---|---|
committer | Andrea Bastoni <bastoni@cs.unc.edu> | 2010-05-30 19:16:45 -0400 |
commit | ada47b5fe13d89735805b566185f4885f5a3f750 (patch) | |
tree | 644b88f8a71896307d71438e9b3af49126ffb22b /fs/nfs/delegation.c | |
parent | 43e98717ad40a4ae64545b5ba047c7b86aa44f4f (diff) | |
parent | 3280f21d43ee541f97f8cda5792150d2dbec20d5 (diff) |
Merge branch 'wip-2.6.34' into old-private-masterarchived-private-master
Diffstat (limited to 'fs/nfs/delegation.c')
-rw-r--r-- | fs/nfs/delegation.c | 164 |
1 files changed, 105 insertions, 59 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 6dd48a4405b4..ea61d26e7871 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
@@ -10,6 +10,7 @@ | |||
10 | #include <linux/kthread.h> | 10 | #include <linux/kthread.h> |
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/sched.h> | 12 | #include <linux/sched.h> |
13 | #include <linux/slab.h> | ||
13 | #include <linux/smp_lock.h> | 14 | #include <linux/smp_lock.h> |
14 | #include <linux/spinlock.h> | 15 | #include <linux/spinlock.h> |
15 | 16 | ||
@@ -23,6 +24,8 @@ | |||
23 | 24 | ||
24 | static void nfs_do_free_delegation(struct nfs_delegation *delegation) | 25 | static void nfs_do_free_delegation(struct nfs_delegation *delegation) |
25 | { | 26 | { |
27 | if (delegation->cred) | ||
28 | put_rpccred(delegation->cred); | ||
26 | kfree(delegation); | 29 | kfree(delegation); |
27 | } | 30 | } |
28 | 31 | ||
@@ -35,13 +38,7 @@ static void nfs_free_delegation_callback(struct rcu_head *head) | |||
35 | 38 | ||
36 | static void nfs_free_delegation(struct nfs_delegation *delegation) | 39 | static void nfs_free_delegation(struct nfs_delegation *delegation) |
37 | { | 40 | { |
38 | struct rpc_cred *cred; | ||
39 | |||
40 | cred = rcu_dereference(delegation->cred); | ||
41 | rcu_assign_pointer(delegation->cred, NULL); | ||
42 | call_rcu(&delegation->rcu, nfs_free_delegation_callback); | 41 | call_rcu(&delegation->rcu, nfs_free_delegation_callback); |
43 | if (cred) | ||
44 | put_rpccred(cred); | ||
45 | } | 42 | } |
46 | 43 | ||
47 | void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) | 44 | void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) |
@@ -92,7 +89,7 @@ out: | |||
92 | return status; | 89 | return status; |
93 | } | 90 | } |
94 | 91 | ||
95 | static void nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid) | 92 | static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *stateid) |
96 | { | 93 | { |
97 | struct nfs_inode *nfsi = NFS_I(inode); | 94 | struct nfs_inode *nfsi = NFS_I(inode); |
98 | struct nfs_open_context *ctx; | 95 | struct nfs_open_context *ctx; |
@@ -116,10 +113,11 @@ again: | |||
116 | err = nfs_delegation_claim_locks(ctx, state); | 113 | err = nfs_delegation_claim_locks(ctx, state); |
117 | put_nfs_open_context(ctx); | 114 | put_nfs_open_context(ctx); |
118 | if (err != 0) | 115 | if (err != 0) |
119 | return; | 116 | return err; |
120 | goto again; | 117 | goto again; |
121 | } | 118 | } |
122 | spin_unlock(&inode->i_lock); | 119 | spin_unlock(&inode->i_lock); |
120 | return 0; | ||
123 | } | 121 | } |
124 | 122 | ||
125 | /* | 123 | /* |
@@ -127,21 +125,35 @@ again: | |||
127 | */ | 125 | */ |
128 | 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) |
129 | { | 127 | { |
130 | struct nfs_delegation *delegation = NFS_I(inode)->delegation; | 128 | struct nfs_delegation *delegation; |
131 | struct rpc_cred *oldcred; | 129 | struct rpc_cred *oldcred = NULL; |
132 | 130 | ||
133 | if (delegation == NULL) | 131 | rcu_read_lock(); |
134 | return; | 132 | delegation = rcu_dereference(NFS_I(inode)->delegation); |
135 | memcpy(delegation->stateid.data, res->delegation.data, | 133 | if (delegation != NULL) { |
136 | sizeof(delegation->stateid.data)); | 134 | spin_lock(&delegation->lock); |
137 | delegation->type = res->delegation_type; | 135 | if (delegation->inode != NULL) { |
138 | delegation->maxsize = res->maxsize; | 136 | memcpy(delegation->stateid.data, res->delegation.data, |
139 | oldcred = delegation->cred; | 137 | sizeof(delegation->stateid.data)); |
140 | delegation->cred = get_rpccred(cred); | 138 | delegation->type = res->delegation_type; |
141 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); | 139 | delegation->maxsize = res->maxsize; |
142 | NFS_I(inode)->delegation_state = delegation->type; | 140 | oldcred = delegation->cred; |
143 | smp_wmb(); | 141 | delegation->cred = get_rpccred(cred); |
144 | 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 | } | ||
145 | } | 157 | } |
146 | 158 | ||
147 | 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) |
@@ -164,9 +176,13 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation | |||
164 | return inode; | 176 | return inode; |
165 | } | 177 | } |
166 | 178 | ||
167 | 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) | ||
168 | { | 182 | { |
169 | 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)); | ||
170 | 186 | ||
171 | if (delegation == NULL) | 187 | if (delegation == NULL) |
172 | goto nomatch; | 188 | goto nomatch; |
@@ -193,7 +209,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
193 | { | 209 | { |
194 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | 210 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; |
195 | struct nfs_inode *nfsi = NFS_I(inode); | 211 | struct nfs_inode *nfsi = NFS_I(inode); |
196 | struct nfs_delegation *delegation; | 212 | struct nfs_delegation *delegation, *old_delegation; |
197 | struct nfs_delegation *freeme = NULL; | 213 | struct nfs_delegation *freeme = NULL; |
198 | int status = 0; | 214 | int status = 0; |
199 | 215 | ||
@@ -211,10 +227,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
211 | spin_lock_init(&delegation->lock); | 227 | spin_lock_init(&delegation->lock); |
212 | 228 | ||
213 | spin_lock(&clp->cl_lock); | 229 | spin_lock(&clp->cl_lock); |
214 | if (rcu_dereference(nfsi->delegation) != NULL) { | 230 | old_delegation = rcu_dereference_protected(nfsi->delegation, |
215 | if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, | 231 | lockdep_is_held(&clp->cl_lock)); |
216 | sizeof(delegation->stateid)) == 0 && | 232 | if (old_delegation != NULL) { |
217 | 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) { | ||
218 | goto out; | 236 | goto out; |
219 | } | 237 | } |
220 | /* | 238 | /* |
@@ -224,12 +242,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
224 | dfprintk(FILE, "%s: server %s handed out " | 242 | dfprintk(FILE, "%s: server %s handed out " |
225 | "a duplicate delegation!\n", | 243 | "a duplicate delegation!\n", |
226 | __func__, clp->cl_hostname); | 244 | __func__, clp->cl_hostname); |
227 | if (delegation->type <= nfsi->delegation->type) { | 245 | if (delegation->type <= old_delegation->type) { |
228 | freeme = delegation; | 246 | freeme = delegation; |
229 | delegation = NULL; | 247 | delegation = NULL; |
230 | goto out; | 248 | goto out; |
231 | } | 249 | } |
232 | freeme = nfs_detach_delegation_locked(nfsi, NULL); | 250 | freeme = nfs_detach_delegation_locked(nfsi, NULL, clp); |
233 | } | 251 | } |
234 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); | 252 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); |
235 | nfsi->delegation_state = delegation->type; | 253 | nfsi->delegation_state = delegation->type; |
@@ -261,30 +279,34 @@ static void nfs_msync_inode(struct inode *inode) | |||
261 | /* | 279 | /* |
262 | * Basic procedure for returning a delegation to the server | 280 | * Basic procedure for returning a delegation to the server |
263 | */ | 281 | */ |
264 | static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation) | 282 | static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) |
265 | { | 283 | { |
266 | struct nfs_inode *nfsi = NFS_I(inode); | 284 | struct nfs_inode *nfsi = NFS_I(inode); |
285 | int err; | ||
267 | 286 | ||
268 | nfs_msync_inode(inode); | ||
269 | /* | 287 | /* |
270 | * Guard against new delegated open/lock/unlock calls and against | 288 | * Guard against new delegated open/lock/unlock calls and against |
271 | * state recovery | 289 | * state recovery |
272 | */ | 290 | */ |
273 | down_write(&nfsi->rwsem); | 291 | down_write(&nfsi->rwsem); |
274 | nfs_delegation_claim_opens(inode, &delegation->stateid); | 292 | err = nfs_delegation_claim_opens(inode, &delegation->stateid); |
275 | up_write(&nfsi->rwsem); | 293 | up_write(&nfsi->rwsem); |
276 | nfs_msync_inode(inode); | 294 | if (err) |
295 | goto out; | ||
277 | 296 | ||
278 | return nfs_do_return_delegation(inode, delegation, 1); | 297 | err = nfs_do_return_delegation(inode, delegation, issync); |
298 | out: | ||
299 | return err; | ||
279 | } | 300 | } |
280 | 301 | ||
281 | /* | 302 | /* |
282 | * Return all delegations that have been marked for return | 303 | * Return all delegations that have been marked for return |
283 | */ | 304 | */ |
284 | void nfs_client_return_marked_delegations(struct nfs_client *clp) | 305 | int nfs_client_return_marked_delegations(struct nfs_client *clp) |
285 | { | 306 | { |
286 | struct nfs_delegation *delegation; | 307 | struct nfs_delegation *delegation; |
287 | struct inode *inode; | 308 | struct inode *inode; |
309 | int err = 0; | ||
288 | 310 | ||
289 | restart: | 311 | restart: |
290 | rcu_read_lock(); | 312 | rcu_read_lock(); |
@@ -295,15 +317,21 @@ restart: | |||
295 | if (inode == NULL) | 317 | if (inode == NULL) |
296 | continue; | 318 | continue; |
297 | spin_lock(&clp->cl_lock); | 319 | spin_lock(&clp->cl_lock); |
298 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | 320 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); |
299 | spin_unlock(&clp->cl_lock); | 321 | spin_unlock(&clp->cl_lock); |
300 | rcu_read_unlock(); | 322 | rcu_read_unlock(); |
301 | if (delegation != NULL) | 323 | if (delegation != NULL) { |
302 | __nfs_inode_return_delegation(inode, delegation); | 324 | filemap_flush(inode->i_mapping); |
325 | err = __nfs_inode_return_delegation(inode, delegation, 0); | ||
326 | } | ||
303 | iput(inode); | 327 | iput(inode); |
304 | goto restart; | 328 | if (!err) |
329 | goto restart; | ||
330 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); | ||
331 | return err; | ||
305 | } | 332 | } |
306 | rcu_read_unlock(); | 333 | rcu_read_unlock(); |
334 | return 0; | ||
307 | } | 335 | } |
308 | 336 | ||
309 | /* | 337 | /* |
@@ -318,9 +346,9 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode) | |||
318 | struct nfs_inode *nfsi = NFS_I(inode); | 346 | struct nfs_inode *nfsi = NFS_I(inode); |
319 | struct nfs_delegation *delegation; | 347 | struct nfs_delegation *delegation; |
320 | 348 | ||
321 | if (rcu_dereference(nfsi->delegation) != NULL) { | 349 | if (rcu_access_pointer(nfsi->delegation) != NULL) { |
322 | spin_lock(&clp->cl_lock); | 350 | spin_lock(&clp->cl_lock); |
323 | delegation = nfs_detach_delegation_locked(nfsi, NULL); | 351 | delegation = nfs_detach_delegation_locked(nfsi, NULL, clp); |
324 | spin_unlock(&clp->cl_lock); | 352 | spin_unlock(&clp->cl_lock); |
325 | if (delegation != NULL) | 353 | if (delegation != NULL) |
326 | nfs_do_return_delegation(inode, delegation, 0); | 354 | nfs_do_return_delegation(inode, delegation, 0); |
@@ -334,12 +362,14 @@ int nfs_inode_return_delegation(struct inode *inode) | |||
334 | struct nfs_delegation *delegation; | 362 | struct nfs_delegation *delegation; |
335 | int err = 0; | 363 | int err = 0; |
336 | 364 | ||
337 | if (rcu_dereference(nfsi->delegation) != NULL) { | 365 | if (rcu_access_pointer(nfsi->delegation) != NULL) { |
338 | spin_lock(&clp->cl_lock); | 366 | spin_lock(&clp->cl_lock); |
339 | delegation = nfs_detach_delegation_locked(nfsi, NULL); | 367 | delegation = nfs_detach_delegation_locked(nfsi, NULL, clp); |
340 | spin_unlock(&clp->cl_lock); | 368 | spin_unlock(&clp->cl_lock); |
341 | if (delegation != NULL) | 369 | if (delegation != NULL) { |
342 | err = __nfs_inode_return_delegation(inode, delegation); | 370 | nfs_msync_inode(inode); |
371 | err = __nfs_inode_return_delegation(inode, delegation, 1); | ||
372 | } | ||
343 | } | 373 | } |
344 | return err; | 374 | return err; |
345 | } | 375 | } |
@@ -368,33 +398,47 @@ void nfs_super_return_all_delegations(struct super_block *sb) | |||
368 | spin_unlock(&delegation->lock); | 398 | spin_unlock(&delegation->lock); |
369 | } | 399 | } |
370 | rcu_read_unlock(); | 400 | rcu_read_unlock(); |
371 | nfs_client_return_marked_delegations(clp); | 401 | if (nfs_client_return_marked_delegations(clp) != 0) |
402 | nfs4_schedule_state_manager(clp); | ||
372 | } | 403 | } |
373 | 404 | ||
374 | static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) | 405 | static |
406 | void nfs_client_mark_return_all_delegation_types(struct nfs_client *clp, fmode_t flags) | ||
375 | { | 407 | { |
376 | struct nfs_delegation *delegation; | 408 | struct nfs_delegation *delegation; |
377 | 409 | ||
378 | rcu_read_lock(); | 410 | rcu_read_lock(); |
379 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 411 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
380 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); | 412 | if ((delegation->type == (FMODE_READ|FMODE_WRITE)) && !(flags & FMODE_WRITE)) |
381 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); | 413 | continue; |
414 | if (delegation->type & flags) | ||
415 | nfs_mark_return_delegation(clp, delegation); | ||
382 | } | 416 | } |
383 | rcu_read_unlock(); | 417 | rcu_read_unlock(); |
384 | } | 418 | } |
385 | 419 | ||
420 | static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) | ||
421 | { | ||
422 | nfs_client_mark_return_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); | ||
423 | } | ||
424 | |||
386 | static void nfs_delegation_run_state_manager(struct nfs_client *clp) | 425 | static void nfs_delegation_run_state_manager(struct nfs_client *clp) |
387 | { | 426 | { |
388 | if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) | 427 | if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) |
389 | nfs4_schedule_state_manager(clp); | 428 | nfs4_schedule_state_manager(clp); |
390 | } | 429 | } |
391 | 430 | ||
392 | void nfs_expire_all_delegations(struct nfs_client *clp) | 431 | void nfs_expire_all_delegation_types(struct nfs_client *clp, fmode_t flags) |
393 | { | 432 | { |
394 | nfs_client_mark_return_all_delegations(clp); | 433 | nfs_client_mark_return_all_delegation_types(clp, flags); |
395 | nfs_delegation_run_state_manager(clp); | 434 | nfs_delegation_run_state_manager(clp); |
396 | } | 435 | } |
397 | 436 | ||
437 | void nfs_expire_all_delegations(struct nfs_client *clp) | ||
438 | { | ||
439 | nfs_expire_all_delegation_types(clp, FMODE_READ|FMODE_WRITE); | ||
440 | } | ||
441 | |||
398 | /* | 442 | /* |
399 | * Return all delegations following an NFS4ERR_CB_PATH_DOWN error. | 443 | * Return all delegations following an NFS4ERR_CB_PATH_DOWN error. |
400 | */ | 444 | */ |
@@ -413,8 +457,7 @@ static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *c | |||
413 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 457 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
414 | if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) | 458 | if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) |
415 | continue; | 459 | continue; |
416 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); | 460 | nfs_mark_return_delegation(clp, delegation); |
417 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); | ||
418 | } | 461 | } |
419 | rcu_read_unlock(); | 462 | rcu_read_unlock(); |
420 | } | 463 | } |
@@ -428,18 +471,21 @@ void nfs_expire_unreferenced_delegations(struct nfs_client *clp) | |||
428 | /* | 471 | /* |
429 | * Asynchronous delegation recall! | 472 | * Asynchronous delegation recall! |
430 | */ | 473 | */ |
431 | int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid) | 474 | int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid, |
475 | int (*validate_stateid)(struct nfs_delegation *delegation, | ||
476 | const nfs4_stateid *stateid)) | ||
432 | { | 477 | { |
433 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | 478 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; |
434 | struct nfs_delegation *delegation; | 479 | struct nfs_delegation *delegation; |
435 | 480 | ||
436 | rcu_read_lock(); | 481 | rcu_read_lock(); |
437 | delegation = rcu_dereference(NFS_I(inode)->delegation); | 482 | delegation = rcu_dereference(NFS_I(inode)->delegation); |
438 | if (delegation == NULL || memcmp(delegation->stateid.data, stateid->data, | 483 | |
439 | sizeof(delegation->stateid.data)) != 0) { | 484 | if (!validate_stateid(delegation, stateid)) { |
440 | rcu_read_unlock(); | 485 | rcu_read_unlock(); |
441 | return -ENOENT; | 486 | return -ENOENT; |
442 | } | 487 | } |
488 | |||
443 | nfs_mark_return_delegation(clp, delegation); | 489 | nfs_mark_return_delegation(clp, delegation); |
444 | rcu_read_unlock(); | 490 | rcu_read_unlock(); |
445 | nfs_delegation_run_state_manager(clp); | 491 | nfs_delegation_run_state_manager(clp); |
@@ -496,7 +542,7 @@ restart: | |||
496 | if (inode == NULL) | 542 | if (inode == NULL) |
497 | continue; | 543 | continue; |
498 | spin_lock(&clp->cl_lock); | 544 | spin_lock(&clp->cl_lock); |
499 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | 545 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); |
500 | spin_unlock(&clp->cl_lock); | 546 | spin_unlock(&clp->cl_lock); |
501 | rcu_read_unlock(); | 547 | rcu_read_unlock(); |
502 | if (delegation != NULL) | 548 | if (delegation != NULL) |