aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/dcache.c66
-rw-r--r--fs/super.c2
-rw-r--r--include/linux/dcache.h2
3 files changed, 62 insertions, 8 deletions
diff --git a/fs/dcache.c b/fs/dcache.c
index 940d188e5d14..385f5dbc4b0c 100644
--- a/fs/dcache.c
+++ b/fs/dcache.c
@@ -382,6 +382,8 @@ static inline void prune_one_dentry(struct dentry * dentry)
382/** 382/**
383 * prune_dcache - shrink the dcache 383 * prune_dcache - shrink the dcache
384 * @count: number of entries to try and free 384 * @count: number of entries to try and free
385 * @sb: if given, ignore dentries for other superblocks
386 * which are being unmounted.
385 * 387 *
386 * Shrink the dcache. This is done when we need 388 * Shrink the dcache. This is done when we need
387 * more memory, or simply when we need to unmount 389 * more memory, or simply when we need to unmount
@@ -392,16 +394,29 @@ static inline void prune_one_dentry(struct dentry * dentry)
392 * all the dentries are in use. 394 * all the dentries are in use.
393 */ 395 */
394 396
395static void prune_dcache(int count) 397static void prune_dcache(int count, struct super_block *sb)
396{ 398{
397 spin_lock(&dcache_lock); 399 spin_lock(&dcache_lock);
398 for (; count ; count--) { 400 for (; count ; count--) {
399 struct dentry *dentry; 401 struct dentry *dentry;
400 struct list_head *tmp; 402 struct list_head *tmp;
403 struct rw_semaphore *s_umount;
401 404
402 cond_resched_lock(&dcache_lock); 405 cond_resched_lock(&dcache_lock);
403 406
404 tmp = dentry_unused.prev; 407 tmp = dentry_unused.prev;
408 if (unlikely(sb)) {
409 /* Try to find a dentry for this sb, but don't try
410 * too hard, if they aren't near the tail they will
411 * be moved down again soon
412 */
413 int skip = count;
414 while (skip && tmp != &dentry_unused &&
415 list_entry(tmp, struct dentry, d_lru)->d_sb != sb) {
416 skip--;
417 tmp = tmp->prev;
418 }
419 }
405 if (tmp == &dentry_unused) 420 if (tmp == &dentry_unused)
406 break; 421 break;
407 list_del_init(tmp); 422 list_del_init(tmp);
@@ -427,7 +442,45 @@ static void prune_dcache(int count)
427 spin_unlock(&dentry->d_lock); 442 spin_unlock(&dentry->d_lock);
428 continue; 443 continue;
429 } 444 }
430 prune_one_dentry(dentry); 445 /*
446 * If the dentry is not DCACHED_REFERENCED, it is time
447 * to remove it from the dcache, provided the super block is
448 * NULL (which means we are trying to reclaim memory)
449 * or this dentry belongs to the same super block that
450 * we want to shrink.
451 */
452 /*
453 * If this dentry is for "my" filesystem, then I can prune it
454 * without taking the s_umount lock (I already hold it).
455 */
456 if (sb && dentry->d_sb == sb) {
457 prune_one_dentry(dentry);
458 continue;
459 }
460 /*
461 * ...otherwise we need to be sure this filesystem isn't being
462 * unmounted, otherwise we could race with
463 * generic_shutdown_super(), and end up holding a reference to
464 * an inode while the filesystem is unmounted.
465 * So we try to get s_umount, and make sure s_root isn't NULL.
466 * (Take a local copy of s_umount to avoid a use-after-free of
467 * `dentry').
468 */
469 s_umount = &dentry->d_sb->s_umount;
470 if (down_read_trylock(s_umount)) {
471 if (dentry->d_sb->s_root != NULL) {
472 prune_one_dentry(dentry);
473 up_read(s_umount);
474 continue;
475 }
476 up_read(s_umount);
477 }
478 spin_unlock(&dentry->d_lock);
479 /* Cannot remove the first dentry, and it isn't appropriate
480 * to move it to the head of the list, so give up, and try
481 * later
482 */
483 break;
431 } 484 }
432 spin_unlock(&dcache_lock); 485 spin_unlock(&dcache_lock);
433} 486}
@@ -630,7 +683,7 @@ void shrink_dcache_parent(struct dentry * parent)
630 int found; 683 int found;
631 684
632 while ((found = select_parent(parent)) != 0) 685 while ((found = select_parent(parent)) != 0)
633 prune_dcache(found); 686 prune_dcache(found, parent->d_sb);
634} 687}
635 688
636/** 689/**
@@ -643,9 +696,10 @@ void shrink_dcache_parent(struct dentry * parent)
643 * done under dcache_lock. 696 * done under dcache_lock.
644 * 697 *
645 */ 698 */
646void shrink_dcache_anon(struct hlist_head *head) 699void shrink_dcache_anon(struct super_block *sb)
647{ 700{
648 struct hlist_node *lp; 701 struct hlist_node *lp;
702 struct hlist_head *head = &sb->s_anon;
649 int found; 703 int found;
650 do { 704 do {
651 found = 0; 705 found = 0;
@@ -668,7 +722,7 @@ void shrink_dcache_anon(struct hlist_head *head)
668 } 722 }
669 } 723 }
670 spin_unlock(&dcache_lock); 724 spin_unlock(&dcache_lock);
671 prune_dcache(found); 725 prune_dcache(found, sb);
672 } while(found); 726 } while(found);
673} 727}
674 728
@@ -689,7 +743,7 @@ static int shrink_dcache_memory(int nr, gfp_t gfp_mask)
689 if (nr) { 743 if (nr) {
690 if (!(gfp_mask & __GFP_FS)) 744 if (!(gfp_mask & __GFP_FS))
691 return -1; 745 return -1;
692 prune_dcache(nr); 746 prune_dcache(nr, NULL);
693 } 747 }
694 return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure; 748 return (dentry_stat.nr_unused / 100) * sysctl_vfs_cache_pressure;
695} 749}
diff --git a/fs/super.c b/fs/super.c
index a66f66bb8049..9d5c2add7228 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -231,7 +231,7 @@ void generic_shutdown_super(struct super_block *sb)
231 if (root) { 231 if (root) {
232 sb->s_root = NULL; 232 sb->s_root = NULL;
233 shrink_dcache_parent(root); 233 shrink_dcache_parent(root);
234 shrink_dcache_anon(&sb->s_anon); 234 shrink_dcache_anon(sb);
235 dput(root); 235 dput(root);
236 fsync_super(sb); 236 fsync_super(sb);
237 lock_super(sb); 237 lock_super(sb);
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 836325ee0931..46d0e079735d 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -217,7 +217,7 @@ extern struct dentry * d_alloc_anon(struct inode *);
217extern struct dentry * d_splice_alias(struct inode *, struct dentry *); 217extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
218extern void shrink_dcache_sb(struct super_block *); 218extern void shrink_dcache_sb(struct super_block *);
219extern void shrink_dcache_parent(struct dentry *); 219extern void shrink_dcache_parent(struct dentry *);
220extern void shrink_dcache_anon(struct hlist_head *); 220extern void shrink_dcache_anon(struct super_block *);
221extern int d_invalidate(struct dentry *); 221extern int d_invalidate(struct dentry *);
222 222
223/* only used at mount-time */ 223/* only used at mount-time */