Loading fs/dcache.c +85 −110 Original line number Diff line number Diff line Loading @@ -1074,116 +1074,6 @@ void shrink_dcache_sb(struct super_block *sb) } EXPORT_SYMBOL(shrink_dcache_sb); /* * 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)); for (;;) { /* descend to the first leaf in the current subtree */ while (!list_empty(&dentry->d_subdirs)) 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; /* * inform the fs that this dentry is about to be * unhashed and destroyed. */ if ((dentry->d_flags & DCACHE_OP_PRUNE) && !d_unhashed(dentry)) dentry->d_op->d_prune(dentry); dentry_lru_del(dentry); __d_shrink(dentry); if (dentry->d_lockref.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, dentry->d_lockref.count, dentry->d_sb->s_type->name, dentry->d_sb->s_id); BUG(); } if (IS_ROOT(dentry)) { parent = NULL; list_del(&dentry->d_u.d_child); } else { parent = dentry->d_parent; parent->d_lockref.count--; list_del(&dentry->d_u.d_child); } inode = dentry->d_inode; if (inode) { dentry->d_inode = NULL; hlist_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 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; dentry->d_lockref.count--; shrink_dcache_for_umount_subtree(dentry); while (!hlist_bl_empty(&sb->s_anon)) { dentry = hlist_bl_entry(hlist_bl_first(&sb->s_anon), struct dentry, d_hash); shrink_dcache_for_umount_subtree(dentry); } } /* * This tries to ascend one level of parenthood, but * we can race with renaming, so we need to re-check Loading Loading @@ -1478,6 +1368,91 @@ void shrink_dcache_parent(struct dentry *parent) } EXPORT_SYMBOL(shrink_dcache_parent); static enum d_walk_ret umount_collect(void *_data, struct dentry *dentry) { struct select_data *data = _data; enum d_walk_ret ret = D_WALK_CONTINUE; if (dentry->d_lockref.count) { dentry_lru_del(dentry); if (likely(!list_empty(&dentry->d_subdirs))) goto out; if (dentry == data->start && dentry->d_lockref.count == 1) goto out; 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, dentry->d_lockref.count, dentry->d_sb->s_type->name, dentry->d_sb->s_id); BUG(); } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) { /* * We can't use d_lru_shrink_move() because we * need to get the global LRU lock and do the * LRU accounting. */ if (dentry->d_flags & DCACHE_LRU_LIST) d_lru_del(dentry); d_shrink_add(dentry, &data->dispose); data->found++; ret = D_WALK_NORETRY; } out: if (data->found && need_resched()) ret = D_WALK_QUIT; return ret; } /* * destroy the dentries attached to a superblock on unmounting */ 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; for (;;) { struct select_data data; INIT_LIST_HEAD(&data.dispose); data.start = dentry; data.found = 0; d_walk(dentry, &data, umount_collect, NULL); if (!data.found) break; shrink_dentry_list(&data.dispose); cond_resched(); } d_drop(dentry); dput(dentry); while (!hlist_bl_empty(&sb->s_anon)) { struct select_data data; dentry = hlist_bl_entry(hlist_bl_first(&sb->s_anon), struct dentry, d_hash); INIT_LIST_HEAD(&data.dispose); data.start = NULL; data.found = 0; d_walk(dentry, &data, umount_collect, NULL); if (data.found) shrink_dentry_list(&data.dispose); cond_resched(); } } static enum d_walk_ret check_and_collect(void *_data, struct dentry *dentry) { struct select_data *data = _data; Loading Loading
fs/dcache.c +85 −110 Original line number Diff line number Diff line Loading @@ -1074,116 +1074,6 @@ void shrink_dcache_sb(struct super_block *sb) } EXPORT_SYMBOL(shrink_dcache_sb); /* * 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)); for (;;) { /* descend to the first leaf in the current subtree */ while (!list_empty(&dentry->d_subdirs)) 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; /* * inform the fs that this dentry is about to be * unhashed and destroyed. */ if ((dentry->d_flags & DCACHE_OP_PRUNE) && !d_unhashed(dentry)) dentry->d_op->d_prune(dentry); dentry_lru_del(dentry); __d_shrink(dentry); if (dentry->d_lockref.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, dentry->d_lockref.count, dentry->d_sb->s_type->name, dentry->d_sb->s_id); BUG(); } if (IS_ROOT(dentry)) { parent = NULL; list_del(&dentry->d_u.d_child); } else { parent = dentry->d_parent; parent->d_lockref.count--; list_del(&dentry->d_u.d_child); } inode = dentry->d_inode; if (inode) { dentry->d_inode = NULL; hlist_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 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; dentry->d_lockref.count--; shrink_dcache_for_umount_subtree(dentry); while (!hlist_bl_empty(&sb->s_anon)) { dentry = hlist_bl_entry(hlist_bl_first(&sb->s_anon), struct dentry, d_hash); shrink_dcache_for_umount_subtree(dentry); } } /* * This tries to ascend one level of parenthood, but * we can race with renaming, so we need to re-check Loading Loading @@ -1478,6 +1368,91 @@ void shrink_dcache_parent(struct dentry *parent) } EXPORT_SYMBOL(shrink_dcache_parent); static enum d_walk_ret umount_collect(void *_data, struct dentry *dentry) { struct select_data *data = _data; enum d_walk_ret ret = D_WALK_CONTINUE; if (dentry->d_lockref.count) { dentry_lru_del(dentry); if (likely(!list_empty(&dentry->d_subdirs))) goto out; if (dentry == data->start && dentry->d_lockref.count == 1) goto out; 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, dentry->d_lockref.count, dentry->d_sb->s_type->name, dentry->d_sb->s_id); BUG(); } else if (!(dentry->d_flags & DCACHE_SHRINK_LIST)) { /* * We can't use d_lru_shrink_move() because we * need to get the global LRU lock and do the * LRU accounting. */ if (dentry->d_flags & DCACHE_LRU_LIST) d_lru_del(dentry); d_shrink_add(dentry, &data->dispose); data->found++; ret = D_WALK_NORETRY; } out: if (data->found && need_resched()) ret = D_WALK_QUIT; return ret; } /* * destroy the dentries attached to a superblock on unmounting */ 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; for (;;) { struct select_data data; INIT_LIST_HEAD(&data.dispose); data.start = dentry; data.found = 0; d_walk(dentry, &data, umount_collect, NULL); if (!data.found) break; shrink_dentry_list(&data.dispose); cond_resched(); } d_drop(dentry); dput(dentry); while (!hlist_bl_empty(&sb->s_anon)) { struct select_data data; dentry = hlist_bl_entry(hlist_bl_first(&sb->s_anon), struct dentry, d_hash); INIT_LIST_HEAD(&data.dispose); data.start = NULL; data.found = 0; d_walk(dentry, &data, umount_collect, NULL); if (data.found) shrink_dentry_list(&data.dispose); cond_resched(); } } static enum d_walk_ret check_and_collect(void *_data, struct dentry *dentry) { struct select_data *data = _data; Loading