| From fb2e3c9c2984fe5a073e12893132fd0b8dacfb72 Mon Sep 17 00:00:00 2001 |
| From: Jan Kara <jack@suse.cz> |
| Date: Fri, 1 Feb 2019 14:21:23 -0800 |
| Subject: fs/drop_caches.c: avoid softlockups in drop_pagecache_sb() |
| |
| [ Upstream commit c27d82f52f75fc9d8d9d40d120d2a96fdeeada5e ] |
| |
| When superblock has lots of inodes without any pagecache (like is the |
| case for /proc), drop_pagecache_sb() will iterate through all of them |
| without dropping sb->s_inode_list_lock which can lead to softlockups |
| (one of our customers hit this). |
| |
| Fix the problem by going to the slow path and doing cond_resched() in |
| case the process needs rescheduling. |
| |
| Link: http://lkml.kernel.org/r/20190114085343.15011-1-jack@suse.cz |
| Signed-off-by: Jan Kara <jack@suse.cz> |
| Acked-by: Michal Hocko <mhocko@suse.com> |
| Reviewed-by: Andrew Morton <akpm@linux-foundation.org> |
| Cc: Al Viro <viro@ZenIV.linux.org.uk> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/drop_caches.c | 8 +++++++- |
| 1 file changed, 7 insertions(+), 1 deletion(-) |
| |
| diff --git a/fs/drop_caches.c b/fs/drop_caches.c |
| index 82377017130f..d31b6c72b476 100644 |
| --- a/fs/drop_caches.c |
| +++ b/fs/drop_caches.c |
| @@ -21,8 +21,13 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused) |
| 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->i_state & (I_FREEING|I_WILL_FREE|I_NEW)) || |
| - (inode->i_mapping->nrpages == 0)) { |
| + (inode->i_mapping->nrpages == 0 && !need_resched())) { |
| spin_unlock(&inode->i_lock); |
| continue; |
| } |
| @@ -30,6 +35,7 @@ static void drop_pagecache_sb(struct super_block *sb, void *unused) |
| spin_unlock(&inode->i_lock); |
| spin_unlock(&sb->s_inode_list_lock); |
| |
| + cond_resched(); |
| invalidate_mapping_pages(inode->i_mapping, 0, -1); |
| iput(toput_inode); |
| toput_inode = inode; |
| -- |
| 2.19.1 |
| |