diff options
| author | Jan Kara <jack@suse.cz> | 2025-09-12 12:38:36 +0200 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2025-10-19 16:34:06 +0200 |
| commit | bd408c334f3a17d8c8dd3e35ada0e8a75a17f5ca (patch) | |
| tree | a0a888bc76d5a6170c32e9753ea0cdc80b0a8913 /fs | |
| parent | 4bdabd52ca1bcdccd66027bea5e8e650b5dade62 (diff) | |
| download | linux-bd408c334f3a17d8c8dd3e35ada0e8a75a17f5ca.tar.gz linux-bd408c334f3a17d8c8dd3e35ada0e8a75a17f5ca.tar.bz2 linux-bd408c334f3a17d8c8dd3e35ada0e8a75a17f5ca.zip | |
writeback: Avoid softlockup when switching many inodes
[ Upstream commit 66c14dccd810d42ec5c73bb8a9177489dfd62278 ]
process_inode_switch_wbs_work() can be switching over 100 inodes to a
different cgroup. Since switching an inode requires counting all dirty &
under-writeback pages in the address space of each inode, this can take
a significant amount of time. Add a possibility to reschedule after
processing each inode to avoid softlockups.
Acked-by: Tejun Heo <tj@kernel.org>
Signed-off-by: Jan Kara <jack@suse.cz>
Signed-off-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Sasha Levin <sashal@kernel.org>
Diffstat (limited to 'fs')
| -rw-r--r-- | fs/fs-writeback.c | 11 |
1 files changed, 10 insertions, 1 deletions
diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 4ae226778d64..eff778dc0386 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -503,6 +503,7 @@ static void inode_switch_wbs_work_fn(struct work_struct *work) */ down_read(&bdi->wb_switch_rwsem); + inodep = isw->inodes; /* * By the time control reaches here, RCU grace period has passed * since I_WB_SWITCH assertion and all wb stat update transactions @@ -513,6 +514,7 @@ static void inode_switch_wbs_work_fn(struct work_struct *work) * gives us exclusion against all wb related operations on @inode * including IO list manipulations and stat updates. */ +relock: if (old_wb < new_wb) { spin_lock(&old_wb->list_lock); spin_lock_nested(&new_wb->list_lock, SINGLE_DEPTH_NESTING); @@ -521,10 +523,17 @@ static void inode_switch_wbs_work_fn(struct work_struct *work) spin_lock_nested(&old_wb->list_lock, SINGLE_DEPTH_NESTING); } - for (inodep = isw->inodes; *inodep; inodep++) { + while (*inodep) { WARN_ON_ONCE((*inodep)->i_wb != old_wb); if (inode_do_switch_wbs(*inodep, old_wb, new_wb)) nr_switched++; + inodep++; + if (*inodep && need_resched()) { + spin_unlock(&new_wb->list_lock); + spin_unlock(&old_wb->list_lock); + cond_resched(); + goto relock; + } } spin_unlock(&new_wb->list_lock); |
