* [PATCH] f2fs: write a checkpoint in fsync after the warm node log is moved
@ 2026-11-15 21:35 Daeho Jeong
0 siblings, 0 replies; only message in thread
From: Daeho Jeong @ 2026-11-15 21:35 UTC (permalink / raw)
To: linux-kernel, linux-f2fs-devel, kernel-team; +Cc: Daeho Jeong, stable
From: Daeho Jeong <daehojeong@google.com>
Roll-forward recovery finds fsynced node blocks by following the chain in
the warm node log. It starts from the next free block of the log recorded
in the last checkpoint, and each node block has the address of the next
one in its footer. f2fs_allocate_data_block() fills the footer after it
moves the log to a new segment, so the chain is kept when a segment is
full.
f2fs_allocate_segment_for_resize() moves the log out of the range to be
removed in the middle of a segment. No node block is allocated there, so
the last node block in the old segment still points to the next block of
the old segment, and nothing points to the new one. The node blocks
written after that cannot be found by recovery until the next checkpoint.
For example, with F2FS_IOC_RESIZE_FS:
- fsync(A) writes a node block to segment X, which is in the range.
- resize moves the warm node log from segment X to segment Y.
- fsync(B) writes a node block to segment Y and returns 0.
- after a sudden power-off, recovery follows the chain in segment X,
recovers A and stops there. B is lost.
Checkpoint is blocked during the first GC pass of resize, so this lasts
until the pass is done. f2fs_ioc_reserve_dev_alias() moves the logs in
the same way.
Set SBI_NEED_CP when the warm node log is moved, so that fsync writes a
checkpoint instead of relying on the chain. The next checkpoint clears
the flag and starts a new chain from the new segment. Such an fsync waits
for the first GC pass of resize, since checkpoint is blocked until then.
An fsync which has already passed need_do_checkpoint() can still write
its node blocks after the log is moved. So check the flag again after
the node blocks are written, and write a checkpoint in that case, too.
Test: four tasks create and fsync files while F2FS_IOC_RESIZE_FS shrinks
the filesystem, and the machine is rebooted by sysrq-b 1.5 seconds after
the log is moved. Without this patch, all the files whose fsync returned
after the move were lost (454 and 437 files in two runs). With this
patch, no fsync returned in that window and no file with a returned
fsync was lost. The second check was tested with a debug patch which
delays an fsync between need_do_checkpoint() and its node writes.
Fixes: b4b10061ef98 ("f2fs: refactor resize_fs to avoid meta updates in progress")
Cc: stable@vger.kernel.org
Signed-off-by: Daeho Jeong <daehojeong@google.com>
---
fs/f2fs/file.c | 8 ++++++++
fs/f2fs/segment.c | 10 +++++++++-
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
index 05061254c3b2..61ed0ad5d29d 100644
--- a/fs/f2fs/file.c
+++ b/fs/f2fs/file.c
@@ -466,6 +466,14 @@ static int f2fs_do_sync_file(struct file *file, loff_t start, loff_t end,
goto sync_nodes;
}
+ /*
+ * The warm node log can be moved after need_do_checkpoint() above.
+ * Then the node blocks written here are out of the chain, so write
+ * a checkpoint.
+ */
+ if (unlikely(is_sbi_flag_set(sbi, SBI_NEED_CP)))
+ goto go_write;
+
/*
* If it's atomic_write, it's just fine to keep write ordering. So
* here we don't need to wait for node write completion, since we use
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c
index 31ac93a93021..e77a01376288 100644
--- a/fs/f2fs/segment.c
+++ b/fs/f2fs/segment.c
@@ -3488,9 +3488,17 @@ int f2fs_allocate_segment_for_resize(struct f2fs_sb_info *sbi, int type,
unlock:
up_write(&SIT_I(sbi)->sentry_lock);
- if (segno != curseg->segno)
+ if (segno != curseg->segno) {
f2fs_notice(sbi, "For resize: curseg of type %d: %u ==> %u",
type, segno, curseg->segno);
+ /*
+ * The chain of fsynced node blocks is cut here, so roll-forward
+ * recovery cannot find what is fsynced from now on. Let fsync
+ * write a checkpoint until the next checkpoint is done.
+ */
+ if (type == CURSEG_WARM_NODE)
+ set_sbi_flag(sbi, SBI_NEED_CP);
+ }
mutex_unlock(&curseg->curseg_mutex);
f2fs_up_read(&SM_I(sbi)->curseg_lock);
--
2.56.0.611.g1030f0b940-goog
^ permalink raw reply [flat|nested] only message in thread
only message in thread, other threads:[~2026-11-15 21:35 UTC | newest]
Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-15 21:35 [PATCH] f2fs: write a checkpoint in fsync after the warm node log is moved Daeho Jeong
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®