| From 6d4506285f10f411bb0e7eb48366a1b296bf2d4c Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Fri, 6 Dec 2019 10:55:59 -0600 |
| Subject: fs: call fsnotify_sb_delete after evict_inodes |
| |
| From: Eric Sandeen <sandeen@redhat.com> |
| |
| [ Upstream commit 1edc8eb2e93130e36ac74ac9c80913815a57d413 ] |
| |
| When a filesystem is unmounted, we currently call fsnotify_sb_delete() |
| before evict_inodes(), which means that fsnotify_unmount_inodes() |
| must iterate over all inodes on the superblock looking for any inodes |
| with watches. This is inefficient and can lead to livelocks as it |
| iterates over many unwatched inodes. |
| |
| At this point, SB_ACTIVE is gone and dropping refcount to zero kicks |
| the inode out out immediately, so anything processed by |
| fsnotify_sb_delete / fsnotify_unmount_inodes gets evicted in that loop. |
| |
| After that, the call to evict_inodes will evict everything else with a |
| zero refcount. |
| |
| This should speed things up overall, and avoid livelocks in |
| fsnotify_unmount_inodes(). |
| |
| Signed-off-by: Eric Sandeen <sandeen@redhat.com> |
| Reviewed-by: Jan Kara <jack@suse.cz> |
| Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| fs/notify/fsnotify.c | 3 +++ |
| fs/super.c | 4 +++- |
| 2 files changed, 6 insertions(+), 1 deletion(-) |
| |
| diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c |
| index ac9eb273e28c..f44e39c68328 100644 |
| --- a/fs/notify/fsnotify.c |
| +++ b/fs/notify/fsnotify.c |
| @@ -57,6 +57,9 @@ static void fsnotify_unmount_inodes(struct super_block *sb) |
| * doing an __iget/iput with SB_ACTIVE clear would actually |
| * evict all inodes with zero i_count from icache which is |
| * unnecessarily violent and may in fact be illegal to do. |
| + * However, we should have been called /after/ evict_inodes |
| + * removed all zero refcount inodes, in any case. Test to |
| + * be sure. |
| */ |
| if (!atomic_read(&inode->i_count)) { |
| spin_unlock(&inode->i_lock); |
| diff --git a/fs/super.c b/fs/super.c |
| index cfadab2cbf35..cd352530eca9 100644 |
| --- a/fs/super.c |
| +++ b/fs/super.c |
| @@ -448,10 +448,12 @@ void generic_shutdown_super(struct super_block *sb) |
| sync_filesystem(sb); |
| sb->s_flags &= ~SB_ACTIVE; |
| |
| - fsnotify_sb_delete(sb); |
| cgroup_writeback_umount(); |
| |
| + /* evict all inodes with zero refcount */ |
| evict_inodes(sb); |
| + /* only nonzero refcount inodes can have marks */ |
| + fsnotify_sb_delete(sb); |
| |
| if (sb->s_dio_done_wq) { |
| destroy_workqueue(sb->s_dio_done_wq); |
| -- |
| 2.20.1 |
| |