Index: linux-2.6.17/fs/inode.c =================================================================== --- linux-2.6.17.orig/fs/inode.c 2006-06-17 18:49:35.000000000 -0700 +++ linux-2.6.17/fs/inode.c 2006-06-19 19:28:54.005732436 -0700 @@ -84,16 +84,6 @@ static struct hlist_head *inode_hashtabl DEFINE_SPINLOCK(inode_lock); /* - * iprune_mutex provides exclusion between the kswapd or try_to_free_pages - * icache shrinking path, and the umount path. Without this exclusion, - * by the time prune_icache calls iput for the inode whose pages it has - * been invalidating, or by the time it calls clear_inode & destroy_inode - * from its final dispose_list, the struct super_block they refer to - * (for inode->i_sb->s_op) may already have been freed and reused. - */ -static DEFINE_MUTEX(iprune_mutex); - -/* * Statistics gathering.. */ struct inodes_stat_t inodes_stat; @@ -351,15 +341,14 @@ int invalidate_inodes(struct super_block int busy; LIST_HEAD(throw_away); - mutex_lock(&iprune_mutex); + kmem_cache_reclaim_get(inode_cachep); spin_lock(&inode_lock); inotify_unmount_inodes(&sb->s_inodes); busy = invalidate_list(&sb->s_inodes, &throw_away); spin_unlock(&inode_lock); dispose_list(&throw_away); - mutex_unlock(&iprune_mutex); - + kmem_cache_reclaim_put(inode_cachep); return busy; } @@ -400,70 +389,6 @@ static int can_unuse(struct inode *inode } /* - * Scan `goal' inodes on the unused list for freeable ones. They are moved to - * a temporary list and then are freed outside inode_lock by dispose_list(). - * - * Any inodes which are pinned purely because of attached pagecache have their - * pagecache removed. We expect the final iput() on that inode to add it to - * the front of the inode_unused list. So look for it there and if the - * inode is still freeable, proceed. The right inode is found 99.9% of the - * time in testing on a 4-way. - * - * If the inode has metadata buffers attached to mapping->private_list then - * try to remove them. - */ -static void prune_icache(int nr_to_scan) -{ - LIST_HEAD(freeable); - int nr_pruned = 0; - int nr_scanned; - unsigned long reap = 0; - - mutex_lock(&iprune_mutex); - spin_lock(&inode_lock); - for (nr_scanned = 0; nr_scanned < nr_to_scan; nr_scanned++) { - struct inode *inode; - - if (list_empty(&inode_unused)) - break; - - inode = list_entry(inode_unused.prev, struct inode, i_list); - - if (inode->i_state || atomic_read(&inode->i_count)) { - list_move(&inode->i_list, &inode_unused); - continue; - } - if (inode_has_buffers(inode) || inode->i_data.nrpages) { - __iget(inode); - spin_unlock(&inode_lock); - if (remove_inode_buffers(inode)) - reap += invalidate_inode_pages(&inode->i_data); - iput(inode); - spin_lock(&inode_lock); - - if (inode != list_entry(inode_unused.next, - struct inode, i_list)) - continue; /* wrong inode or list_empty */ - if (!can_unuse(inode)) - continue; - } - list_move(&inode->i_list, &freeable); - inode->i_state |= I_FREEING; - nr_pruned++; - } - inodes_stat.nr_unused -= nr_pruned; - spin_unlock(&inode_lock); - - dispose_list(&freeable); - mutex_unlock(&iprune_mutex); - - if (current_is_kswapd()) - mod_page_state(kswapd_inodesteal, reap); - else - mod_page_state(pginodesteal, reap); -} - -/* * shrink_icache_memory() will attempt to reclaim some unused inodes. Here, * "unused" means that no dentries are referring to the inodes: the files are * not open and the dcache references to those inodes have already been @@ -482,7 +407,7 @@ static int shrink_icache_memory(int nr, */ if (!(gfp_mask & __GFP_FS)) return -1; - prune_icache(nr); + kmem_cache_reclaim(inode_cachep, nr); } return (inodes_stat.nr_unused / 100) * sysctl_vfs_cache_pressure; } @@ -1365,6 +1290,53 @@ void __init inode_init_early(void) INIT_HLIST_HEAD(&inode_hashtable[loop]); } +void inode_dtor(void *p, kmem_cache_t *cachep, unsigned long flags) +{ + struct inode *inode = p; + + if (!(flags & SLAB_DTOR_FREE)) + return; + + spin_lock(&inode_lock); + /* Do not free a busy inode */ + if (inode->i_state || atomic_read(&inode->i_count)) + goto unlock; + + if (inode_has_buffers(inode) || inode->i_data.nrpages) { + __iget(inode); + spin_unlock(&inode_lock); + if (remove_inode_buffers(inode)) + invalidate_inode_pages(&inode->i_data); + iput(inode); + spin_lock(&inode_lock); + if (atomic_read(&inode->i_count) || !can_unuse(inode)) + goto unlock; + + } + inode->i_state |= I_FREEING; + inodes_stat.nr_unused--; + spin_unlock(&inode_lock); + + if (inode->i_data.nrpages) + truncate_inode_pages(&inode->i_data, 0); + clear_inode(inode); + spin_lock(&inode_lock); + hlist_del_init(&inode->i_hash); + list_del_init(&inode->i_sb_list); + spin_unlock(&inode_lock); + + wake_up_inode(inode); + destroy_inode(inode); + spin_lock(&inode_lock); + inodes_stat.nr_inodes--; + if (current_is_kswapd()) + inc_page_state(kswapd_inodesteal); + else + inc_page_state(pginodesteal); +unlock: + spin_unlock(&inode_lock); +} + void __init inode_init(unsigned long mempages) { int loop; @@ -1376,7 +1348,7 @@ void __init inode_init(unsigned long mem (SLAB_RECLAIM_ACCOUNT|SLAB_PANIC| SLAB_MEM_SPREAD), init_once, - NULL); + inode_dtor); set_shrinker(DEFAULT_SEEKS, shrink_icache_memory); /* Hash may have been set up in inode_init_early */