[PATCH] f2fs: wake up foreground GC waiters when the GC thread skips a round
From: Wenjie Qi
Date: Mon Oct 12 2026 - 06:52:45 EST
With GC_MERGE, f2fs_balance_fs() queues a foreground caller on fggc_wq,
wakes the GC thread, and sleeps without a timeout or condition recheck.
The root cause is that a foreground writer, the GC thread, and fsfreeze
can race as follows:
foreground writer GC thread fsfreeze
- f2fs_create
- f2fs_balance_fs
- prepare_to_wait(&fggc_wq)
- wake_up(&gc_wait_queue_head)
- io_schedule()
waits on fggc_wq - freeze_super()
- frozen =
SB_FREEZE_WRITE
- sb_wait_write()
waits for writer
- gc_thread_func()
- waitqueue_active(fggc_wq)
returns true
- foreground = true
- frozen >= SB_FREEZE_WRITE
- continue
no wake_up_all(&fggc_wq)
The writer keeps its superblock write reference while sleeping. fsfreeze
waits for that writer, while the GC thread loops because the queued waiter
keeps waitqueue_active(fggc_wq) true.
Wake foreground waiters on each early skip. A skipped foreground-GC round
must not leave its caller blocked.
Fixes: 5911d2d1d1a3 ("f2fs: introduce gc_merge mount option")
Signed-off-by: Wenjie Qi <qiwenjie@xxxxxxxxxx>
---
Reproduced on a 512 MiB F2FS image mounted with gc_merge, filled and
fragmented under concurrent inode creation. The kernel reported:
INFO: task sh:1381 blocked in I/O wait for more than 20 seconds
Call Trace:
io_schedule
f2fs_balance_fs
f2fs_create
The SysRq task dump showed fsfreeze in freeze_super.part.0. An NMI
backtrace reported:
CPU: 2 PID: 229 Comm: f2fs_gc-254:0
Call Trace:
gc_thread_func
The f2fs_gc kthread also consumed 301 ticks in three seconds at
USER_HZ=100.
---
fs/f2fs/gc.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
index 926e6976bc2..086670f9df2 100644
--- a/fs/f2fs/gc.c
+++ b/fs/f2fs/gc.c
@@ -66,6 +66,8 @@ static int gc_thread_func(void *data)
if (f2fs_readonly(sbi->sb)) {
stat_other_skip_bggc_count(sbi);
+ if (foreground)
+ wake_up_all(&gc_th->fggc_wq);
continue;
}
if (kthread_should_stop())
@@ -74,6 +76,8 @@ static int gc_thread_func(void *data)
if (sbi->sb->s_writers.frozen >= SB_FREEZE_WRITE) {
increase_sleep_time(gc_th, &wait_ms);
stat_other_skip_bggc_count(sbi);
+ if (foreground)
+ wake_up_all(&gc_th->fggc_wq);
continue;
}
@@ -83,6 +87,8 @@ static int gc_thread_func(void *data)
if (!sb_start_write_trylock(sbi->sb)) {
stat_other_skip_bggc_count(sbi);
+ if (foreground)
+ wake_up_all(&gc_th->fggc_wq);
continue;
}
--
2.43.0