Use sb_for_each_inodes() in evict_inodes() to avoid rescanning referenced inodes after each reschedule. Collect eligible inodes during the walk and dispose of them after it completes. Convert drop_pagecache_sb() as well, replacing its deferred toput_inode reference with the iterator's position tracking. Signed-off-by: Julian Sun --- fs/drop_caches.c | 44 +++++++++++++++++++------------------------ fs/inode.c | 49 +++++++++++++----------------------------------- 2 files changed, 32 insertions(+), 61 deletions(-) diff --git a/fs/drop_caches.c b/fs/drop_caches.c index 49f56a598ecb..0d475a5ff8cf 100644 --- a/fs/drop_caches.c +++ b/fs/drop_caches.c @@ -16,36 +16,30 @@ /* A global variable is a bit ugly, but it keeps the code simple */ static int sysctl_drop_caches; -static void drop_pagecache_sb(struct super_block *sb, void *unused) +static int drop_pagecache_inode_iter_cb(struct inode *inode, void *unused) { - struct inode *inode, *toput_inode = NULL; + struct super_block *sb = inode->i_sb; - spin_lock(&sb->s_inode_list_lock); - list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - spin_lock(&inode->i_lock); - /* - * We must skip inodes in unusual state. We may also skip - * inodes without pages but we deliberately won't in case - * we need to reschedule to avoid softlockups. - */ - if ((inode_state_read(inode) & (I_FREEING | I_WILL_FREE | I_NEW)) || - (mapping_empty(inode->i_mapping) && !need_resched())) { - spin_unlock(&inode->i_lock); - continue; - } - __iget(inode); + if (mapping_empty(inode->i_mapping)) { spin_unlock(&inode->i_lock); - spin_unlock(&sb->s_inode_list_lock); - - invalidate_mapping_pages(inode->i_mapping, 0, -1); - iput(toput_inode); - toput_inode = inode; - - cond_resched(); - spin_lock(&sb->s_inode_list_lock); + return 0; } + + __iget(inode); + spin_unlock(&inode->i_lock); spin_unlock(&sb->s_inode_list_lock); - iput(toput_inode); + + invalidate_mapping_pages(inode->i_mapping, 0, -1); + iput(inode); + + spin_lock(&sb->s_inode_list_lock); + + return 0; +} + +static void drop_pagecache_sb(struct super_block *sb, void *unused) +{ + sb_for_each_inodes(sb, INODE_ITER_NORMAL, drop_pagecache_inode_iter_cb, NULL); } static int drop_caches_sysctl_handler(const struct ctl_table *table, int write, diff --git a/fs/inode.c b/fs/inode.c index b4279063a5dd..07a5f48641af 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -959,6 +959,17 @@ static void dispose_list(struct list_head *head) } } +static int evict_inodes_inode_iter_cb(struct inode *inode, void *data) +{ + struct list_head *dispose = (struct list_head *)data; + + inode_state_set(inode, I_FREEING); + inode_lru_list_del(inode); + spin_unlock(&inode->i_lock); + list_add(&inode->i_lru, dispose); + return 0; +} + /** * evict_inodes - evict all evictable inodes for a superblock * @sb: superblock to operate on @@ -970,44 +981,10 @@ static void dispose_list(struct list_head *head) */ void evict_inodes(struct super_block *sb) { - struct inode *inode; LIST_HEAD(dispose); + unsigned int flags = INODE_ITER_NORMAL | INODE_ITER_UNUSED; -again: - spin_lock(&sb->s_inode_list_lock); - list_for_each_entry(inode, &sb->s_inodes, i_sb_list) { - if (icount_read_once(inode)) - continue; - - spin_lock(&inode->i_lock); - if (icount_read(inode)) { - spin_unlock(&inode->i_lock); - continue; - } - if (inode_state_read(inode) & (I_NEW | I_FREEING | I_WILL_FREE)) { - spin_unlock(&inode->i_lock); - continue; - } - - inode_state_set(inode, I_FREEING); - inode_lru_list_del(inode); - spin_unlock(&inode->i_lock); - list_add(&inode->i_lru, &dispose); - - /* - * We can have a ton of inodes to evict at unmount time given - * enough memory, check to see if we need to go to sleep for a - * bit so we don't livelock. - */ - if (need_resched()) { - spin_unlock(&sb->s_inode_list_lock); - cond_resched(); - dispose_list(&dispose); - goto again; - } - } - spin_unlock(&sb->s_inode_list_lock); - + sb_for_each_inodes(sb, flags, evict_inodes_inode_iter_cb, &dispose); dispose_list(&dispose); } EXPORT_SYMBOL_GPL(evict_inodes); -- 2.39.5