Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 39368
b: refs/heads/master
c: c636ebd
h: refs/heads/master
v: v3
  • Loading branch information
David Howells authored and Linus Torvalds committed Oct 11, 2006
1 parent 27b5574 commit d08fa03
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 7 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 6ce315234aefcbc599dea390c15672156ebf9e7b
refs/heads/master: c636ebdb186bf37f98d3839f69293597723edb36
130 changes: 130 additions & 0 deletions trunk/fs/dcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,136 @@ void shrink_dcache_sb(struct super_block * sb)
spin_unlock(&dcache_lock);
}

/*
* destroy a single subtree of dentries for unmount
* - see the comments on shrink_dcache_for_umount() for a description of the
* locking
*/
static void shrink_dcache_for_umount_subtree(struct dentry *dentry)
{
struct dentry *parent;

BUG_ON(!IS_ROOT(dentry));

/* detach this root from the system */
spin_lock(&dcache_lock);
if (!list_empty(&dentry->d_lru)) {
dentry_stat.nr_unused--;
list_del_init(&dentry->d_lru);
}
__d_drop(dentry);
spin_unlock(&dcache_lock);

for (;;) {
/* descend to the first leaf in the current subtree */
while (!list_empty(&dentry->d_subdirs)) {
struct dentry *loop;

/* this is a branch with children - detach all of them
* from the system in one go */
spin_lock(&dcache_lock);
list_for_each_entry(loop, &dentry->d_subdirs,
d_u.d_child) {
if (!list_empty(&loop->d_lru)) {
dentry_stat.nr_unused--;
list_del_init(&loop->d_lru);
}

__d_drop(loop);
cond_resched_lock(&dcache_lock);
}
spin_unlock(&dcache_lock);

/* move to the first child */
dentry = list_entry(dentry->d_subdirs.next,
struct dentry, d_u.d_child);
}

/* consume the dentries from this leaf up through its parents
* until we find one with children or run out altogether */
do {
struct inode *inode;

if (atomic_read(&dentry->d_count) != 0) {
printk(KERN_ERR
"BUG: Dentry %p{i=%lx,n=%s}"
" still in use (%d)"
" [unmount of %s %s]\n",
dentry,
dentry->d_inode ?
dentry->d_inode->i_ino : 0UL,
dentry->d_name.name,
atomic_read(&dentry->d_count),
dentry->d_sb->s_type->name,
dentry->d_sb->s_id);
BUG();
}

parent = dentry->d_parent;
if (parent == dentry)
parent = NULL;
else
atomic_dec(&parent->d_count);

list_del(&dentry->d_u.d_child);
dentry_stat.nr_dentry--; /* For d_free, below */

inode = dentry->d_inode;
if (inode) {
dentry->d_inode = NULL;
list_del_init(&dentry->d_alias);
if (dentry->d_op && dentry->d_op->d_iput)
dentry->d_op->d_iput(dentry, inode);
else
iput(inode);
}

d_free(dentry);

/* finished when we fall off the top of the tree,
* otherwise we ascend to the parent and move to the
* next sibling if there is one */
if (!parent)
return;

dentry = parent;

} while (list_empty(&dentry->d_subdirs));

dentry = list_entry(dentry->d_subdirs.next,
struct dentry, d_u.d_child);
}
}

/*
* destroy the dentries attached to a superblock on unmounting
* - we don't need to use dentry->d_lock, and only need dcache_lock when
* removing the dentry from the system lists and hashes because:
* - the superblock is detached from all mountings and open files, so the
* dentry trees will not be rearranged by the VFS
* - s_umount is write-locked, so the memory pressure shrinker will ignore
* any dentries belonging to this superblock that it comes across
* - the filesystem itself is no longer permitted to rearrange the dentries
* in this superblock
*/
void shrink_dcache_for_umount(struct super_block *sb)
{
struct dentry *dentry;

if (down_read_trylock(&sb->s_umount))
BUG();

dentry = sb->s_root;
sb->s_root = NULL;
atomic_dec(&dentry->d_count);
shrink_dcache_for_umount_subtree(dentry);

while (!hlist_empty(&sb->s_anon)) {
dentry = hlist_entry(sb->s_anon.first, struct dentry, d_hash);
shrink_dcache_for_umount_subtree(dentry);
}
}

/*
* Search for at least 1 mount point in the dentry's subdirs.
* We descend to the next level whenever the d_subdirs
Expand Down
12 changes: 6 additions & 6 deletions trunk/fs/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -260,17 +260,17 @@ int fsync_super(struct super_block *sb)
* that need destruction out of superblock, call generic_shutdown_super()
* and release aforementioned objects. Note: dentries and inodes _are_
* taken care of and do not need specific handling.
*
* Upon calling this function, the filesystem may no longer alter or
* rearrange the set of dentries belonging to this super_block, nor may it
* change the attachments of dentries to inodes.
*/
void generic_shutdown_super(struct super_block *sb)
{
struct dentry *root = sb->s_root;
struct super_operations *sop = sb->s_op;

if (root) {
sb->s_root = NULL;
shrink_dcache_parent(root);
shrink_dcache_sb(sb);
dput(root);
if (sb->s_root) {
shrink_dcache_for_umount(sb);
fsync_super(sb);
lock_super(sb);
sb->s_flags &= ~MS_ACTIVE;
Expand Down
1 change: 1 addition & 0 deletions trunk/include/linux/dcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ extern struct dentry * d_alloc_anon(struct inode *);
extern struct dentry * d_splice_alias(struct inode *, struct dentry *);
extern void shrink_dcache_sb(struct super_block *);
extern void shrink_dcache_parent(struct dentry *);
extern void shrink_dcache_for_umount(struct super_block *);
extern int d_invalidate(struct dentry *);

/* only used at mount-time */
Expand Down

0 comments on commit d08fa03

Please sign in to comment.