diff options
| -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 | ||
