diff options
-rw-r--r-- | fs/dcache.c | 130 | ||||
-rw-r--r-- | fs/super.c | 12 | ||||
-rw-r--r-- | include/linux/dcache.h | 1 |
3 files changed, 137 insertions, 6 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 2355bddad8de..2bac4ba1d1d3 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -549,6 +549,136 @@ repeat: | |||
549 | } | 549 | } |
550 | 550 | ||
551 | /* | 551 | /* |
552 | * destroy a single subtree of dentries for unmount | ||
553 | * - see the comments on shrink_dcache_for_umount() for a description of the | ||
554 | * locking | ||
555 | */ | ||
556 | static void shrink_dcache_for_umount_subtree(struct dentry *dentry) | ||
557 | { | ||
558 | struct dentry *parent; | ||
559 | |||
560 | BUG_ON(!IS_ROOT(dentry)); | ||
561 | |||
562 | /* detach this root from the system */ | ||
563 | spin_lock(&dcache_lock); | ||
564 | if (!list_empty(&dentry->d_lru)) { | ||
565 | dentry_stat.nr_unused--; | ||
566 | list_del_init(&dentry->d_lru); | ||
567 | } | ||
568 | __d_drop(dentry); | ||
569 | spin_unlock(&dcache_lock); | ||
570 | |||
571 | for (;;) { | ||
572 | /* descend to the first leaf in the current subtree */ | ||
573 | while (!list_empty(&dentry->d_subdirs)) { | ||
574 | struct dentry *loop; | ||
575 | |||
576 | /* this is a branch with children - detach all of them | ||
577 | * from the system in one go */ | ||
578 | spin_lock(&dcache_lock); | ||
579 | list_for_each_entry(loop, &dentry->d_subdirs, | ||
580 | d_u.d_child) { | ||
581 | if (!list_empty(&loop->d_lru)) { | ||
582 | dentry_stat.nr_unused--; | ||
583 | list_del_init(&loop->d_lru); | ||
584 | } | ||
585 | |||
586 | __d_drop(loop); | ||
587 | cond_resched_lock(&dcache_lock); | ||
588 | } | ||
589 | spin_unlock(&dcache_lock); | ||
590 | |||
591 | /* move to the first child */ | ||
592 | dentry = list_entry(dentry->d_subdirs.next, | ||
593 | struct dentry, d_u.d_child); | ||
594 | } | ||
595 | |||
596 | /* consume the dentries from this leaf up through its parents | ||
597 | * until we find one with children or run out altogether */ | ||
598 | do { | ||
599 | struct inode *inode; | ||
600 | |||
601 | if (atomic_read(&dentry->d_count) != 0) { | ||
602 | printk(KERN_ERR | ||
603 | "BUG: Dentry %p{i=%lx,n=%s}" | ||
604 | " still in use (%d)" | ||
605 | " [unmount of %s %s]\n", | ||
606 | dentry, | ||
607 | dentry->d_inode ? | ||
608 | dentry->d_inode->i_ino : 0UL, | ||
609 | dentry->d_name.name, | ||
610 | atomic_read(&dentry->d_count), | ||
611 | dentry->d_sb->s_type->name, | ||
612 | dentry->d_sb->s_id); | ||
613 | BUG(); | ||
614 | } | ||
615 | |||
616 | parent = dentry->d_parent; | ||
617 | if (parent == dentry) | ||
618 | parent = NULL; | ||
619 | else | ||
620 | atomic_dec(&parent->d_count); | ||
621 | |||
622 | list_del(&dentry->d_u.d_child); | ||
623 | dentry_stat.nr_dentry--; /* For d_free, below */ | ||
624 | |||
625 | inode = dentry->d_inode; | ||
626 | if (inode) { | ||
627 | dentry->d_inode = NULL; | ||
628 | list_del_init(&dentry->d_alias); | ||
629 | if (dentry->d_op && dentry->d_op->d_iput) | ||
630 | dentry->d_op->d_iput(dentry, inode); | ||
631 | else | ||
632 | iput(inode); | ||
633 | } | ||
634 | |||
635 | d_free(dentry); | ||
636 | |||
637 | /* finished when we fall off the top of the tree, | ||
638 | * otherwise we ascend to the parent and move to the | ||
639 | * next sibling if there is one */ | ||
640 | if (!parent) | ||
641 | return; | ||
642 | |||
643 | dentry = parent; | ||
644 | |||
645 | } while (list_empty(&dentry->d_subdirs)); | ||
646 | |||
647 | dentry = list_entry(dentry->d_subdirs.next, | ||
648 | struct dentry, d_u.d_child); | ||
649 | } | ||
650 | } | ||
651 | |||
652 | /* | ||
653 | * destroy the dentries attached to a superblock on unmounting | ||
654 | * - we don't need to use dentry->d_lock, and only need dcache_lock when | ||
655 | * removing the dentry from the system lists and hashes because: | ||
656 | * - the superblock is detached from all mountings and open files, so the | ||
657 | * dentry trees will not be rearranged by the VFS | ||
658 | * - s_umount is write-locked, so the memory pressure shrinker will ignore | ||
659 | * any dentries belonging to this superblock that it comes across | ||
660 | * - the filesystem itself is no longer permitted to rearrange the dentries | ||
661 | * in this superblock | ||
662 | */ | ||
663 | void shrink_dcache_for_umount(struct super_block *sb) | ||
664 | { | ||
665 | struct dentry *dentry; | ||
666 | |||
667 | if (down_read_trylock(&sb->s_umount)) | ||
668 | BUG(); | ||
669 | |||
670 | dentry = sb->s_root; | ||
671 | sb->s_root = NULL; | ||
672 | atomic_dec(&dentry->d_count); | ||
673 | shrink_dcache_for_umount_subtree(dentry); | ||
674 | |||
675 | while (!hlist_empty(&sb->s_anon)) { | ||
676 | dentry = hlist_entry(sb->s_anon.first, struct dentry, d_hash); | ||
677 | shrink_dcache_for_umount_subtree(dentry); | ||
678 | } | ||
679 | } | ||
680 | |||
681 | /* | ||
552 | * Search for at least 1 mount point in the dentry's subdirs. | 682 | * Search for at least 1 mount point in the dentry's subdirs. |
553 | * We descend to the next level whenever the d_subdirs | 683 | * We descend to the next level whenever the d_subdirs |
554 | * list is non-empty and continue searching. | 684 | * list is non-empty and continue searching. |
diff --git a/fs/super.c b/fs/super.c index aec99ddbe53f..47e554c12e76 100644 --- a/fs/super.c +++ b/fs/super.c | |||
@@ -260,17 +260,17 @@ int fsync_super(struct super_block *sb) | |||
260 | * that need destruction out of superblock, call generic_shutdown_super() | 260 | * that need destruction out of superblock, call generic_shutdown_super() |
261 | * and release aforementioned objects. Note: dentries and inodes _are_ | 261 | * and release aforementioned objects. Note: dentries and inodes _are_ |
262 | * taken care of and do not need specific handling. | 262 | * taken care of and do not need specific handling. |
263 | * | ||
264 | * Upon calling this function, the filesystem may no longer alter or | ||
265 | * rearrange the set of dentries belonging to this super_block, nor may it | ||
266 | * change the attachments of dentries to inodes. | ||
263 | */ | 267 | */ |
264 | void generic_shutdown_super(struct super_block *sb) | 268 | void generic_shutdown_super(struct super_block *sb) |
265 | { | 269 | { |
266 | struct dentry *root = sb->s_root; | ||
267 | struct super_operations *sop = sb->s_op; | 270 | struct super_operations *sop = sb->s_op; |
268 | 271 | ||
269 | if (root) { | 272 | if (sb->s_root) { |
270 | sb->s_root = NULL; | 273 | shrink_dcache_for_umount(sb); |
271 | shrink_dcache_parent(root); | ||
272 | shrink_dcache_sb(sb); | ||
273 | dput(root); | ||
274 | fsync_super(sb); | 274 | fsync_super(sb); |
275 | lock_super(sb); | 275 | lock_super(sb); |
276 | sb->s_flags &= ~MS_ACTIVE; | 276 | sb->s_flags &= ~MS_ACTIVE; |
diff --git a/include/linux/dcache.h b/include/linux/dcache.h index 44605be59409..63f64a9a5bf7 100644 --- a/include/linux/dcache.h +++ b/include/linux/dcache.h | |||
@@ -230,6 +230,7 @@ extern struct dentry * d_alloc_anon(struct inode *); | |||
230 | extern struct dentry * d_splice_alias(struct inode *, struct dentry *); | 230 | extern struct dentry * d_splice_alias(struct inode *, struct dentry *); |
231 | extern void shrink_dcache_sb(struct super_block *); | 231 | extern void shrink_dcache_sb(struct super_block *); |
232 | extern void shrink_dcache_parent(struct dentry *); | 232 | extern void shrink_dcache_parent(struct dentry *); |
233 | extern void shrink_dcache_for_umount(struct super_block *); | ||
233 | extern int d_invalidate(struct dentry *); | 234 | extern int d_invalidate(struct dentry *); |
234 | 235 | ||
235 | /* only used at mount-time */ | 236 | /* only used at mount-time */ |