Releasing a super_block requires walking all inodes for the given superblock and releasing them. Currently, inodes are found on one of four lists: - global list inode_in_use - global list inode_unused - per-sb ->s_dirty - per-sb ->s_io The second list, inode_unused can potentially be quite large. Unfortunately, it cannot be made per-sb as it is the global LRU list used for inode cache reduction under memory pressure. When unmounting a single filesystem, profiling shows dramatic time spent walking inode_unused. This because very noticeble when one is unmounting a decently sized tree of filesystems. The proposed solution is to create a new list per-sb, that contains all inodes allocated. It is maintained under the inode_lock for the sake of simplicity, but this may prove unneccesary, and may be better done with another global or per-sb lock. Signed-off-by: Mike Waychison Index: linux-2.6.10-quilt/fs/inode.c =================================================================== --- linux-2.6.10-quilt.orig/fs/inode.c 2005-01-07 14:08:45.000000000 -0800 +++ linux-2.6.10-quilt/fs/inode.c 2005-01-07 14:29:16.000000000 -0800 @@ -115,6 +115,10 @@ static struct inode *alloc_inode(struct struct address_space * const mapping = &inode->i_data; inode->i_sb = sb; + spin_lock(&inode_lock); + list_add_tail(&inode->i_all_inodes, &sb->s_all_inodes); + spin_unlock(&inode_lock); + inode->i_blkbits = sb->s_blocksize_bits; inode->i_flags = 0; atomic_set(&inode->i_count, 1); @@ -172,6 +176,9 @@ static struct inode *alloc_inode(struct void destroy_inode(struct inode *inode) { + spin_lock(&inode_lock); + list_del(&inode->i_all_inodes); + spin_unlock(&inode_lock); if (inode_has_buffers(inode)) BUG(); security_inode_free(inode); @@ -296,7 +303,7 @@ static void dispose_list(struct list_hea /* * Invalidate all inodes for a device. */ -static int invalidate_list(struct list_head *head, struct super_block * sb, struct list_head * dispose) +static int invalidate_list(struct list_head *head, struct list_head * dispose) { struct list_head *next; int busy = 0, count = 0; @@ -309,9 +316,7 @@ static int invalidate_list(struct list_h next = next->next; if (tmp == head) break; - inode = list_entry(tmp, struct inode, i_list); - if (inode->i_sb != sb) - continue; + inode = list_entry(tmp, struct inode, i_all_inodes); invalidate_inode_buffers(inode); if (!atomic_read(&inode->i_count)) { hlist_del_init(&inode->i_hash); @@ -350,10 +355,7 @@ int invalidate_inodes(struct super_block down(&iprune_sem); spin_lock(&inode_lock); - busy = invalidate_list(&inode_in_use, sb, &throw_away); - busy |= invalidate_list(&inode_unused, sb, &throw_away); - busy |= invalidate_list(&sb->s_dirty, sb, &throw_away); - busy |= invalidate_list(&sb->s_io, sb, &throw_away); + busy = invalidate_list(&sb->s_all_inodes, &throw_away); spin_unlock(&inode_lock); dispose_list(&throw_away); Index: linux-2.6.10-quilt/fs/super.c =================================================================== --- linux-2.6.10-quilt.orig/fs/super.c 2005-01-07 14:08:50.000000000 -0800 +++ linux-2.6.10-quilt/fs/super.c 2005-01-07 14:13:30.000000000 -0800 @@ -69,6 +69,7 @@ static struct super_block *alloc_super(v INIT_LIST_HEAD(&s->s_io); INIT_LIST_HEAD(&s->s_files); INIT_LIST_HEAD(&s->s_instances); + INIT_LIST_HEAD(&s->s_all_inodes); INIT_HLIST_HEAD(&s->s_anon); init_rwsem(&s->s_umount); sema_init(&s->s_lock, 1); Index: linux-2.6.10-quilt/include/linux/fs.h =================================================================== --- linux-2.6.10-quilt.orig/include/linux/fs.h 2005-01-07 14:08:49.000000000 -0800 +++ linux-2.6.10-quilt/include/linux/fs.h 2005-01-07 14:29:43.000000000 -0800 @@ -459,6 +459,7 @@ struct inode { #ifdef CONFIG_QUOTA struct dquot *i_dquot[MAXQUOTAS]; #endif + struct list_head i_all_inodes; /* These three should probably be a union */ struct list_head i_devices; struct pipe_inode_info *i_pipe; @@ -776,6 +777,7 @@ struct super_block { void *s_security; struct xattr_handler **s_xattr; + struct list_head s_all_inodes; /* goes through i_all_inodes */ struct list_head s_dirty; /* dirty inodes */ struct list_head s_io; /* parked for writeback */ struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */