diff options
Diffstat (limited to 'fs/nfs/delegation.c')
-rw-r--r-- | fs/nfs/delegation.c | 260 |
1 files changed, 132 insertions, 128 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index cc563cfa6940..968225a88015 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
@@ -43,6 +43,27 @@ static void nfs_free_delegation(struct nfs_delegation *delegation) | |||
43 | put_rpccred(cred); | 43 | put_rpccred(cred); |
44 | } | 44 | } |
45 | 45 | ||
46 | void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) | ||
47 | { | ||
48 | set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags); | ||
49 | } | ||
50 | |||
51 | int nfs_have_delegation(struct inode *inode, fmode_t flags) | ||
52 | { | ||
53 | struct nfs_delegation *delegation; | ||
54 | int ret = 0; | ||
55 | |||
56 | flags &= FMODE_READ|FMODE_WRITE; | ||
57 | rcu_read_lock(); | ||
58 | delegation = rcu_dereference(NFS_I(inode)->delegation); | ||
59 | if (delegation != NULL && (delegation->type & flags) == flags) { | ||
60 | nfs_mark_delegation_referenced(delegation); | ||
61 | ret = 1; | ||
62 | } | ||
63 | rcu_read_unlock(); | ||
64 | return ret; | ||
65 | } | ||
66 | |||
46 | static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state) | 67 | static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state) |
47 | { | 68 | { |
48 | struct inode *inode = state->inode; | 69 | struct inode *inode = state->inode; |
@@ -119,7 +140,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st | |||
119 | delegation->maxsize = res->maxsize; | 140 | delegation->maxsize = res->maxsize; |
120 | oldcred = delegation->cred; | 141 | oldcred = delegation->cred; |
121 | delegation->cred = get_rpccred(cred); | 142 | delegation->cred = get_rpccred(cred); |
122 | delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM; | 143 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); |
123 | NFS_I(inode)->delegation_state = delegation->type; | 144 | NFS_I(inode)->delegation_state = delegation->type; |
124 | smp_wmb(); | 145 | smp_wmb(); |
125 | put_rpccred(oldcred); | 146 | put_rpccred(oldcred); |
@@ -134,19 +155,35 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation * | |||
134 | return res; | 155 | return res; |
135 | } | 156 | } |
136 | 157 | ||
158 | static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation) | ||
159 | { | ||
160 | struct inode *inode = NULL; | ||
161 | |||
162 | spin_lock(&delegation->lock); | ||
163 | if (delegation->inode != NULL) | ||
164 | inode = igrab(delegation->inode); | ||
165 | spin_unlock(&delegation->lock); | ||
166 | return inode; | ||
167 | } | ||
168 | |||
137 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) | 169 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) |
138 | { | 170 | { |
139 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); | 171 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); |
140 | 172 | ||
141 | if (delegation == NULL) | 173 | if (delegation == NULL) |
142 | goto nomatch; | 174 | goto nomatch; |
175 | spin_lock(&delegation->lock); | ||
143 | if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, | 176 | if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, |
144 | sizeof(delegation->stateid.data)) != 0) | 177 | sizeof(delegation->stateid.data)) != 0) |
145 | goto nomatch; | 178 | goto nomatch_unlock; |
146 | list_del_rcu(&delegation->super_list); | 179 | list_del_rcu(&delegation->super_list); |
180 | delegation->inode = NULL; | ||
147 | nfsi->delegation_state = 0; | 181 | nfsi->delegation_state = 0; |
148 | rcu_assign_pointer(nfsi->delegation, NULL); | 182 | rcu_assign_pointer(nfsi->delegation, NULL); |
183 | spin_unlock(&delegation->lock); | ||
149 | return delegation; | 184 | return delegation; |
185 | nomatch_unlock: | ||
186 | spin_unlock(&delegation->lock); | ||
150 | nomatch: | 187 | nomatch: |
151 | return NULL; | 188 | return NULL; |
152 | } | 189 | } |
@@ -172,6 +209,8 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
172 | delegation->change_attr = nfsi->change_attr; | 209 | delegation->change_attr = nfsi->change_attr; |
173 | delegation->cred = get_rpccred(cred); | 210 | delegation->cred = get_rpccred(cred); |
174 | delegation->inode = inode; | 211 | delegation->inode = inode; |
212 | delegation->flags = 1<<NFS_DELEGATION_REFERENCED; | ||
213 | spin_lock_init(&delegation->lock); | ||
175 | 214 | ||
176 | spin_lock(&clp->cl_lock); | 215 | spin_lock(&clp->cl_lock); |
177 | if (rcu_dereference(nfsi->delegation) != NULL) { | 216 | if (rcu_dereference(nfsi->delegation) != NULL) { |
@@ -226,22 +265,47 @@ static void nfs_msync_inode(struct inode *inode) | |||
226 | */ | 265 | */ |
227 | static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation) | 266 | static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation) |
228 | { | 267 | { |
229 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | ||
230 | struct nfs_inode *nfsi = NFS_I(inode); | 268 | struct nfs_inode *nfsi = NFS_I(inode); |
231 | 269 | ||
232 | nfs_msync_inode(inode); | 270 | nfs_msync_inode(inode); |
233 | down_read(&clp->cl_sem); | ||
234 | /* Guard against new delegated open calls */ | 271 | /* Guard against new delegated open calls */ |
235 | down_write(&nfsi->rwsem); | 272 | down_write(&nfsi->rwsem); |
236 | nfs_delegation_claim_opens(inode, &delegation->stateid); | 273 | nfs_delegation_claim_opens(inode, &delegation->stateid); |
237 | up_write(&nfsi->rwsem); | 274 | up_write(&nfsi->rwsem); |
238 | up_read(&clp->cl_sem); | ||
239 | nfs_msync_inode(inode); | 275 | nfs_msync_inode(inode); |
240 | 276 | ||
241 | return nfs_do_return_delegation(inode, delegation, 1); | 277 | return nfs_do_return_delegation(inode, delegation, 1); |
242 | } | 278 | } |
243 | 279 | ||
244 | /* | 280 | /* |
281 | * Return all delegations that have been marked for return | ||
282 | */ | ||
283 | void nfs_client_return_marked_delegations(struct nfs_client *clp) | ||
284 | { | ||
285 | struct nfs_delegation *delegation; | ||
286 | struct inode *inode; | ||
287 | |||
288 | restart: | ||
289 | rcu_read_lock(); | ||
290 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | ||
291 | if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags)) | ||
292 | continue; | ||
293 | inode = nfs_delegation_grab_inode(delegation); | ||
294 | if (inode == NULL) | ||
295 | continue; | ||
296 | spin_lock(&clp->cl_lock); | ||
297 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | ||
298 | spin_unlock(&clp->cl_lock); | ||
299 | rcu_read_unlock(); | ||
300 | if (delegation != NULL) | ||
301 | __nfs_inode_return_delegation(inode, delegation); | ||
302 | iput(inode); | ||
303 | goto restart; | ||
304 | } | ||
305 | rcu_read_unlock(); | ||
306 | } | ||
307 | |||
308 | /* | ||
245 | * This function returns the delegation without reclaiming opens | 309 | * This function returns the delegation without reclaiming opens |
246 | * or protecting against delegation reclaims. | 310 | * or protecting against delegation reclaims. |
247 | * It is therefore really only safe to be called from | 311 | * It is therefore really only safe to be called from |
@@ -279,83 +343,55 @@ int nfs_inode_return_delegation(struct inode *inode) | |||
279 | return err; | 343 | return err; |
280 | } | 344 | } |
281 | 345 | ||
346 | static void nfs_mark_return_delegation(struct nfs_client *clp, struct nfs_delegation *delegation) | ||
347 | { | ||
348 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); | ||
349 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); | ||
350 | } | ||
351 | |||
282 | /* | 352 | /* |
283 | * Return all delegations associated to a super block | 353 | * Return all delegations associated to a super block |
284 | */ | 354 | */ |
285 | void nfs_return_all_delegations(struct super_block *sb) | 355 | void nfs_super_return_all_delegations(struct super_block *sb) |
286 | { | 356 | { |
287 | struct nfs_client *clp = NFS_SB(sb)->nfs_client; | 357 | struct nfs_client *clp = NFS_SB(sb)->nfs_client; |
288 | struct nfs_delegation *delegation; | 358 | struct nfs_delegation *delegation; |
289 | struct inode *inode; | ||
290 | 359 | ||
291 | if (clp == NULL) | 360 | if (clp == NULL) |
292 | return; | 361 | return; |
293 | restart: | ||
294 | rcu_read_lock(); | 362 | rcu_read_lock(); |
295 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 363 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
296 | if (delegation->inode->i_sb != sb) | 364 | spin_lock(&delegation->lock); |
297 | continue; | 365 | if (delegation->inode != NULL && delegation->inode->i_sb == sb) |
298 | inode = igrab(delegation->inode); | 366 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); |
299 | if (inode == NULL) | 367 | spin_unlock(&delegation->lock); |
300 | continue; | ||
301 | spin_lock(&clp->cl_lock); | ||
302 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | ||
303 | spin_unlock(&clp->cl_lock); | ||
304 | rcu_read_unlock(); | ||
305 | if (delegation != NULL) | ||
306 | __nfs_inode_return_delegation(inode, delegation); | ||
307 | iput(inode); | ||
308 | goto restart; | ||
309 | } | 368 | } |
310 | rcu_read_unlock(); | 369 | rcu_read_unlock(); |
370 | nfs_client_return_marked_delegations(clp); | ||
311 | } | 371 | } |
312 | 372 | ||
313 | static int nfs_do_expire_all_delegations(void *ptr) | 373 | static void nfs_client_mark_return_all_delegations(struct nfs_client *clp) |
314 | { | 374 | { |
315 | struct nfs_client *clp = ptr; | ||
316 | struct nfs_delegation *delegation; | 375 | struct nfs_delegation *delegation; |
317 | struct inode *inode; | ||
318 | 376 | ||
319 | allow_signal(SIGKILL); | ||
320 | restart: | ||
321 | if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0) | ||
322 | goto out; | ||
323 | if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) | ||
324 | goto out; | ||
325 | rcu_read_lock(); | 377 | rcu_read_lock(); |
326 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 378 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
327 | inode = igrab(delegation->inode); | 379 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); |
328 | if (inode == NULL) | 380 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); |
329 | continue; | ||
330 | spin_lock(&clp->cl_lock); | ||
331 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | ||
332 | spin_unlock(&clp->cl_lock); | ||
333 | rcu_read_unlock(); | ||
334 | if (delegation) | ||
335 | __nfs_inode_return_delegation(inode, delegation); | ||
336 | iput(inode); | ||
337 | goto restart; | ||
338 | } | 381 | } |
339 | rcu_read_unlock(); | 382 | rcu_read_unlock(); |
340 | out: | 383 | } |
341 | nfs_put_client(clp); | 384 | |
342 | module_put_and_exit(0); | 385 | static void nfs_delegation_run_state_manager(struct nfs_client *clp) |
386 | { | ||
387 | if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) | ||
388 | nfs4_schedule_state_manager(clp); | ||
343 | } | 389 | } |
344 | 390 | ||
345 | void nfs_expire_all_delegations(struct nfs_client *clp) | 391 | void nfs_expire_all_delegations(struct nfs_client *clp) |
346 | { | 392 | { |
347 | struct task_struct *task; | 393 | nfs_client_mark_return_all_delegations(clp); |
348 | 394 | nfs_delegation_run_state_manager(clp); | |
349 | __module_get(THIS_MODULE); | ||
350 | atomic_inc(&clp->cl_count); | ||
351 | task = kthread_run(nfs_do_expire_all_delegations, clp, | ||
352 | "%s-delegreturn", | ||
353 | rpc_peeraddr2str(clp->cl_rpcclient, | ||
354 | RPC_DISPLAY_ADDR)); | ||
355 | if (!IS_ERR(task)) | ||
356 | return; | ||
357 | nfs_put_client(clp); | ||
358 | module_put(THIS_MODULE); | ||
359 | } | 395 | } |
360 | 396 | ||
361 | /* | 397 | /* |
@@ -363,68 +399,29 @@ void nfs_expire_all_delegations(struct nfs_client *clp) | |||
363 | */ | 399 | */ |
364 | void nfs_handle_cb_pathdown(struct nfs_client *clp) | 400 | void nfs_handle_cb_pathdown(struct nfs_client *clp) |
365 | { | 401 | { |
366 | struct nfs_delegation *delegation; | ||
367 | struct inode *inode; | ||
368 | |||
369 | if (clp == NULL) | 402 | if (clp == NULL) |
370 | return; | 403 | return; |
371 | restart: | 404 | nfs_client_mark_return_all_delegations(clp); |
405 | } | ||
406 | |||
407 | static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *clp) | ||
408 | { | ||
409 | struct nfs_delegation *delegation; | ||
410 | |||
372 | rcu_read_lock(); | 411 | rcu_read_lock(); |
373 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 412 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
374 | inode = igrab(delegation->inode); | 413 | if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags)) |
375 | if (inode == NULL) | ||
376 | continue; | 414 | continue; |
377 | spin_lock(&clp->cl_lock); | 415 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); |
378 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | 416 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); |
379 | spin_unlock(&clp->cl_lock); | ||
380 | rcu_read_unlock(); | ||
381 | if (delegation != NULL) | ||
382 | __nfs_inode_return_delegation(inode, delegation); | ||
383 | iput(inode); | ||
384 | goto restart; | ||
385 | } | 417 | } |
386 | rcu_read_unlock(); | 418 | rcu_read_unlock(); |
387 | } | 419 | } |
388 | 420 | ||
389 | struct recall_threadargs { | 421 | void nfs_expire_unreferenced_delegations(struct nfs_client *clp) |
390 | struct inode *inode; | ||
391 | struct nfs_client *clp; | ||
392 | const nfs4_stateid *stateid; | ||
393 | |||
394 | struct completion started; | ||
395 | int result; | ||
396 | }; | ||
397 | |||
398 | static int recall_thread(void *data) | ||
399 | { | 422 | { |
400 | struct recall_threadargs *args = (struct recall_threadargs *)data; | 423 | nfs_client_mark_return_unreferenced_delegations(clp); |
401 | struct inode *inode = igrab(args->inode); | 424 | nfs_delegation_run_state_manager(clp); |
402 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | ||
403 | struct nfs_inode *nfsi = NFS_I(inode); | ||
404 | struct nfs_delegation *delegation; | ||
405 | |||
406 | daemonize("nfsv4-delegreturn"); | ||
407 | |||
408 | nfs_msync_inode(inode); | ||
409 | down_read(&clp->cl_sem); | ||
410 | down_write(&nfsi->rwsem); | ||
411 | spin_lock(&clp->cl_lock); | ||
412 | delegation = nfs_detach_delegation_locked(nfsi, args->stateid); | ||
413 | if (delegation != NULL) | ||
414 | args->result = 0; | ||
415 | else | ||
416 | args->result = -ENOENT; | ||
417 | spin_unlock(&clp->cl_lock); | ||
418 | complete(&args->started); | ||
419 | nfs_delegation_claim_opens(inode, args->stateid); | ||
420 | up_write(&nfsi->rwsem); | ||
421 | up_read(&clp->cl_sem); | ||
422 | nfs_msync_inode(inode); | ||
423 | |||
424 | if (delegation != NULL) | ||
425 | nfs_do_return_delegation(inode, delegation, 1); | ||
426 | iput(inode); | ||
427 | module_put_and_exit(0); | ||
428 | } | 425 | } |
429 | 426 | ||
430 | /* | 427 | /* |
@@ -432,22 +429,20 @@ static int recall_thread(void *data) | |||
432 | */ | 429 | */ |
433 | int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid) | 430 | int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid) |
434 | { | 431 | { |
435 | struct recall_threadargs data = { | 432 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; |
436 | .inode = inode, | 433 | struct nfs_delegation *delegation; |
437 | .stateid = stateid, | ||
438 | }; | ||
439 | int status; | ||
440 | 434 | ||
441 | init_completion(&data.started); | 435 | rcu_read_lock(); |
442 | __module_get(THIS_MODULE); | 436 | delegation = rcu_dereference(NFS_I(inode)->delegation); |
443 | status = kernel_thread(recall_thread, &data, CLONE_KERNEL); | 437 | if (delegation == NULL || memcmp(delegation->stateid.data, stateid->data, |
444 | if (status < 0) | 438 | sizeof(delegation->stateid.data)) != 0) { |
445 | goto out_module_put; | 439 | rcu_read_unlock(); |
446 | wait_for_completion(&data.started); | 440 | return -ENOENT; |
447 | return data.result; | 441 | } |
448 | out_module_put: | 442 | nfs_mark_return_delegation(clp, delegation); |
449 | module_put(THIS_MODULE); | 443 | rcu_read_unlock(); |
450 | return status; | 444 | nfs_delegation_run_state_manager(clp); |
445 | return 0; | ||
451 | } | 446 | } |
452 | 447 | ||
453 | /* | 448 | /* |
@@ -459,10 +454,14 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs | |||
459 | struct inode *res = NULL; | 454 | struct inode *res = NULL; |
460 | rcu_read_lock(); | 455 | rcu_read_lock(); |
461 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 456 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
462 | if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { | 457 | spin_lock(&delegation->lock); |
458 | if (delegation->inode != NULL && | ||
459 | nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { | ||
463 | res = igrab(delegation->inode); | 460 | res = igrab(delegation->inode); |
464 | break; | ||
465 | } | 461 | } |
462 | spin_unlock(&delegation->lock); | ||
463 | if (res != NULL) | ||
464 | break; | ||
466 | } | 465 | } |
467 | rcu_read_unlock(); | 466 | rcu_read_unlock(); |
468 | return res; | 467 | return res; |
@@ -476,7 +475,7 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp) | |||
476 | struct nfs_delegation *delegation; | 475 | struct nfs_delegation *delegation; |
477 | rcu_read_lock(); | 476 | rcu_read_lock(); |
478 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) | 477 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) |
479 | delegation->flags |= NFS_DELEGATION_NEED_RECLAIM; | 478 | set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); |
480 | rcu_read_unlock(); | 479 | rcu_read_unlock(); |
481 | } | 480 | } |
482 | 481 | ||
@@ -486,17 +485,22 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp) | |||
486 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp) | 485 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp) |
487 | { | 486 | { |
488 | struct nfs_delegation *delegation; | 487 | struct nfs_delegation *delegation; |
488 | struct inode *inode; | ||
489 | restart: | 489 | restart: |
490 | rcu_read_lock(); | 490 | rcu_read_lock(); |
491 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | 491 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
492 | if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) | 492 | if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0) |
493 | continue; | ||
494 | inode = nfs_delegation_grab_inode(delegation); | ||
495 | if (inode == NULL) | ||
493 | continue; | 496 | continue; |
494 | spin_lock(&clp->cl_lock); | 497 | spin_lock(&clp->cl_lock); |
495 | delegation = nfs_detach_delegation_locked(NFS_I(delegation->inode), NULL); | 498 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); |
496 | spin_unlock(&clp->cl_lock); | 499 | spin_unlock(&clp->cl_lock); |
497 | rcu_read_unlock(); | 500 | rcu_read_unlock(); |
498 | if (delegation != NULL) | 501 | if (delegation != NULL) |
499 | nfs_free_delegation(delegation); | 502 | nfs_free_delegation(delegation); |
503 | iput(inode); | ||
500 | goto restart; | 504 | goto restart; |
501 | } | 505 | } |
502 | rcu_read_unlock(); | 506 | rcu_read_unlock(); |