| From 18df5b9e9be5fc47e22f3d22e5896e58c9fba522 Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Mon, 28 Jun 2021 19:35:47 -0700 |
| Subject: writeback, cgroup: increment isw_nr_in_flight before grabbing an |
| inode |
| |
| From: Roman Gushchin <guro@fb.com> |
| |
| [ Upstream commit 8826ee4fe75051f8cbfa5d4a9aa70565938e724c ] |
| |
| isw_nr_in_flight is used to determine whether the inode switch queue |
| should be flushed from the umount path. Currently it's increased after |
| grabbing an inode and even scheduling the switch work. It means the |
| umount path can walk past cleanup_offline_cgwb() with active inode |
| references, which can result in a "Busy inodes after unmount." message and |
| use-after-free issues (with inode->i_sb which gets freed). |
| |
| Fix it by incrementing isw_nr_in_flight before doing anything with the |
| inode and decrementing in the case when switching wasn't scheduled. |
| |
| The problem hasn't yet been seen in the real life and was discovered by |
| Jan Kara by looking into the code. |
| |
| Link: https://lkml.kernel.org/r/20210608230225.2078447-4-guro@fb.com |
| Signed-off-by: Roman Gushchin <guro@fb.com> |
| Suggested-by: Jan Kara <jack@suse.com> |
| Reviewed-by: Jan Kara <jack@suse.cz> |
| Cc: Alexander Viro <viro@zeniv.linux.org.uk> |
| Cc: Dave Chinner <dchinner@redhat.com> |
| Cc: Dennis Zhou <dennis@kernel.org> |
| Cc: Tejun Heo <tj@kernel.org> |
| Cc: Jens Axboe <axboe@kernel.dk> |
| 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/fs-writeback.c | 5 +++-- |
| 1 file changed, 3 insertions(+), 2 deletions(-) |
| |
| diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c |
| index 0d0f014b09ec..afda7a7263b7 100644 |
| --- a/fs/fs-writeback.c |
| +++ b/fs/fs-writeback.c |
| @@ -505,6 +505,8 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id) |
| if (!isw) |
| return; |
| |
| + atomic_inc(&isw_nr_in_flight); |
| + |
| /* find and pin the new wb */ |
| rcu_read_lock(); |
| memcg_css = css_from_id(new_wb_id, &memory_cgrp_subsys); |
| @@ -535,11 +537,10 @@ static void inode_switch_wbs(struct inode *inode, int new_wb_id) |
| * Let's continue after I_WB_SWITCH is guaranteed to be visible. |
| */ |
| call_rcu(&isw->rcu_head, inode_switch_wbs_rcu_fn); |
| - |
| - atomic_inc(&isw_nr_in_flight); |
| return; |
| |
| out_free: |
| + atomic_dec(&isw_nr_in_flight); |
| if (isw->new_wb) |
| wb_put(isw->new_wb); |
| kfree(isw); |
| -- |
| 2.30.2 |
| |