diff options
author | Christoph Hellwig <hch@infradead.org> | 2010-10-10 05:36:25 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2010-10-25 21:26:13 -0400 |
commit | 3049cfe24ef3872ba74f90630356722cf988b80d (patch) | |
tree | 18c7574640a8f95106facd56cb03a74b73d60f42 /fs | |
parent | 265ac90230257e9c035e4b0c63a0c11c5336e93c (diff) |
fs: split __shrink_dcache_sb
Currently __shrink_dcache_sb has an extremly awkward calling convention
because it tries to please very different callers. Split out the
main loop into a shrink_dentry_list helper, which gets called directly
from shrink_dcache_sb for the cases where all dentries need to be pruned,
or from __shrink_dcache_sb for pruning only a certain number of dentries.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/dcache.c | 127 |
1 files changed, 67 insertions, 60 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 1a976d4efbe1..e987ad576a39 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -459,66 +459,20 @@ static void prune_one_dentry(struct dentry * dentry) | |||
459 | } | 459 | } |
460 | } | 460 | } |
461 | 461 | ||
462 | /* | 462 | static void shrink_dentry_list(struct list_head *list) |
463 | * Shrink the dentry LRU on a given superblock. | ||
464 | * @sb : superblock to shrink dentry LRU. | ||
465 | * @count: If count is NULL, we prune all dentries on superblock. | ||
466 | * @flags: If flags is non-zero, we need to do special processing based on | ||
467 | * which flags are set. This means we don't need to maintain multiple | ||
468 | * similar copies of this loop. | ||
469 | */ | ||
470 | static void __shrink_dcache_sb(struct super_block *sb, int *count, int flags) | ||
471 | { | 463 | { |
472 | LIST_HEAD(referenced); | ||
473 | LIST_HEAD(tmp); | ||
474 | struct dentry *dentry; | 464 | struct dentry *dentry; |
475 | int cnt = 0; | ||
476 | 465 | ||
477 | BUG_ON(!sb); | 466 | while (!list_empty(list)) { |
478 | BUG_ON((flags & DCACHE_REFERENCED) && count == NULL); | 467 | dentry = list_entry(list->prev, struct dentry, d_lru); |
479 | spin_lock(&dcache_lock); | ||
480 | if (count != NULL) | ||
481 | /* called from prune_dcache() and shrink_dcache_parent() */ | ||
482 | cnt = *count; | ||
483 | restart: | ||
484 | if (count == NULL) | ||
485 | list_splice_init(&sb->s_dentry_lru, &tmp); | ||
486 | else { | ||
487 | while (!list_empty(&sb->s_dentry_lru)) { | ||
488 | dentry = list_entry(sb->s_dentry_lru.prev, | ||
489 | struct dentry, d_lru); | ||
490 | BUG_ON(dentry->d_sb != sb); | ||
491 | |||
492 | spin_lock(&dentry->d_lock); | ||
493 | /* | ||
494 | * If we are honouring the DCACHE_REFERENCED flag and | ||
495 | * the dentry has this flag set, don't free it. Clear | ||
496 | * the flag and put it back on the LRU. | ||
497 | */ | ||
498 | if ((flags & DCACHE_REFERENCED) | ||
499 | && (dentry->d_flags & DCACHE_REFERENCED)) { | ||
500 | dentry->d_flags &= ~DCACHE_REFERENCED; | ||
501 | list_move(&dentry->d_lru, &referenced); | ||
502 | spin_unlock(&dentry->d_lock); | ||
503 | } else { | ||
504 | list_move_tail(&dentry->d_lru, &tmp); | ||
505 | spin_unlock(&dentry->d_lock); | ||
506 | cnt--; | ||
507 | if (!cnt) | ||
508 | break; | ||
509 | } | ||
510 | cond_resched_lock(&dcache_lock); | ||
511 | } | ||
512 | } | ||
513 | while (!list_empty(&tmp)) { | ||
514 | dentry = list_entry(tmp.prev, struct dentry, d_lru); | ||
515 | dentry_lru_del_init(dentry); | 468 | dentry_lru_del_init(dentry); |
516 | spin_lock(&dentry->d_lock); | 469 | |
517 | /* | 470 | /* |
518 | * We found an inuse dentry which was not removed from | 471 | * We found an inuse dentry which was not removed from |
519 | * the LRU because of laziness during lookup. Do not free | 472 | * the LRU because of laziness during lookup. Do not free |
520 | * it - just keep it off the LRU list. | 473 | * it - just keep it off the LRU list. |
521 | */ | 474 | */ |
475 | spin_lock(&dentry->d_lock); | ||
522 | if (atomic_read(&dentry->d_count)) { | 476 | if (atomic_read(&dentry->d_count)) { |
523 | spin_unlock(&dentry->d_lock); | 477 | spin_unlock(&dentry->d_lock); |
524 | continue; | 478 | continue; |
@@ -527,13 +481,60 @@ restart: | |||
527 | /* dentry->d_lock was dropped in prune_one_dentry() */ | 481 | /* dentry->d_lock was dropped in prune_one_dentry() */ |
528 | cond_resched_lock(&dcache_lock); | 482 | cond_resched_lock(&dcache_lock); |
529 | } | 483 | } |
530 | if (count == NULL && !list_empty(&sb->s_dentry_lru)) | 484 | } |
531 | goto restart; | 485 | |
532 | if (count != NULL) | 486 | /** |
533 | *count = cnt; | 487 | * __shrink_dcache_sb - shrink the dentry LRU on a given superblock |
488 | * @sb: superblock to shrink dentry LRU. | ||
489 | * @count: number of entries to prune | ||
490 | * @flags: flags to control the dentry processing | ||
491 | * | ||
492 | * If flags contains DCACHE_REFERENCED reference dentries will not be pruned. | ||
493 | */ | ||
494 | static void __shrink_dcache_sb(struct super_block *sb, int *count, int flags) | ||
495 | { | ||
496 | /* called from prune_dcache() and shrink_dcache_parent() */ | ||
497 | struct dentry *dentry; | ||
498 | LIST_HEAD(referenced); | ||
499 | LIST_HEAD(tmp); | ||
500 | int cnt = *count; | ||
501 | |||
502 | spin_lock(&dcache_lock); | ||
503 | while (!list_empty(&sb->s_dentry_lru)) { | ||
504 | dentry = list_entry(sb->s_dentry_lru.prev, | ||
505 | struct dentry, d_lru); | ||
506 | BUG_ON(dentry->d_sb != sb); | ||
507 | |||
508 | /* | ||
509 | * If we are honouring the DCACHE_REFERENCED flag and the | ||
510 | * dentry has this flag set, don't free it. Clear the flag | ||
511 | * and put it back on the LRU. | ||
512 | */ | ||
513 | if (flags & DCACHE_REFERENCED) { | ||
514 | spin_lock(&dentry->d_lock); | ||
515 | if (dentry->d_flags & DCACHE_REFERENCED) { | ||
516 | dentry->d_flags &= ~DCACHE_REFERENCED; | ||
517 | list_move(&dentry->d_lru, &referenced); | ||
518 | spin_unlock(&dentry->d_lock); | ||
519 | cond_resched_lock(&dcache_lock); | ||
520 | continue; | ||
521 | } | ||
522 | spin_unlock(&dentry->d_lock); | ||
523 | } | ||
524 | |||
525 | list_move_tail(&dentry->d_lru, &tmp); | ||
526 | if (!--cnt) | ||
527 | break; | ||
528 | cond_resched_lock(&dcache_lock); | ||
529 | } | ||
530 | |||
531 | *count = cnt; | ||
532 | shrink_dentry_list(&tmp); | ||
533 | |||
534 | if (!list_empty(&referenced)) | 534 | if (!list_empty(&referenced)) |
535 | list_splice(&referenced, &sb->s_dentry_lru); | 535 | list_splice(&referenced, &sb->s_dentry_lru); |
536 | spin_unlock(&dcache_lock); | 536 | spin_unlock(&dcache_lock); |
537 | |||
537 | } | 538 | } |
538 | 539 | ||
539 | /** | 540 | /** |
@@ -619,13 +620,19 @@ static void prune_dcache(int count) | |||
619 | * shrink_dcache_sb - shrink dcache for a superblock | 620 | * shrink_dcache_sb - shrink dcache for a superblock |
620 | * @sb: superblock | 621 | * @sb: superblock |
621 | * | 622 | * |
622 | * Shrink the dcache for the specified super block. This | 623 | * Shrink the dcache for the specified super block. This is used to free |
623 | * is used to free the dcache before unmounting a file | 624 | * the dcache before unmounting a file system. |
624 | * system | ||
625 | */ | 625 | */ |
626 | void shrink_dcache_sb(struct super_block * sb) | 626 | void shrink_dcache_sb(struct super_block *sb) |
627 | { | 627 | { |
628 | __shrink_dcache_sb(sb, NULL, 0); | 628 | LIST_HEAD(tmp); |
629 | |||
630 | spin_lock(&dcache_lock); | ||
631 | while (!list_empty(&sb->s_dentry_lru)) { | ||
632 | list_splice_init(&sb->s_dentry_lru, &tmp); | ||
633 | shrink_dentry_list(&tmp); | ||
634 | } | ||
635 | spin_unlock(&dcache_lock); | ||
629 | } | 636 | } |
630 | EXPORT_SYMBOL(shrink_dcache_sb); | 637 | EXPORT_SYMBOL(shrink_dcache_sb); |
631 | 638 | ||