From: Chao Yu <chao@kernel.org>
To: jaegeuk@kernel.org
Cc: linux-f2fs-devel@lists.sourceforge.net,
linux-kernel@vger.kernel.org, Chao Yu <chao@kernel.org>,
stable@kernel.org
Subject: [PATCH 3/7] f2fs: fix to return -EIO from f2fs_write_checkpoint() once cp_error is set
Date: Thu, 22 Oct 2026 12:52:40 +0000 [thread overview]
Message-ID: <20261022125244.2205275-3-chao@kernel.org> (raw)
In-Reply-To: <20261022125244.2205275-1-chao@kernel.org>
From: Chao Yu <chao@kernel.org>
f2fs_write_checkpoint() skips a CP_SYNC/CP_FASTBOOT/CP_DISCARD request
w/ err = 0 if the superblock is not dirty, and this check is done before
the f2fs_cp_error() check.
However, do_checkpoint() clears SBI_IS_DIRTY unconditionally, even when
the checkpoint fails due to cp_error (e.g. shutdown), and only returns
-EIO at the end. So a checkpoint request which was queued before cp_error
was set, and waits on cp_global_sem or in the checkpoint merge queue, will
see a clean superblock once the previous checkpoint failed, and returns 0
w/o doing checkpoint.
For a file which relies on checkpoint to persist its fsynced data, e.g. a
compressed file (CP_COMPRESSED), f2fs_do_sync_file() -> f2fs_sync_fs()
then returns 0, and fsync() reports false success, whereas its data was
neither covered by a checkpoint nor by fsync node blocks: after recovery,
the fsynced data is rolled back to an old version.
Note that commit e41de21d78ec ("f2fs: fix to return -EIO from
f2fs_sync_fs() if cp_error is set") only covers the case cp_error is set
before f2fs_sync_fs() is called, not the case it is set while the request
is pending.
This was reproduced by the f2fs consistency test (verify_consistency.sh
--init zoned -S 64 --mode consistency, w/ compress_algorithm=lz4 and
default checkpoint_merge, under fault injection): fsync_verify.py randomly
overwrites 4K blocks w/ a generation number and fsyncs them, the test
injects "f2fs_io shutdown 2" in the middle, then verifies the fsynced
generation after fsck and remount:
FAIL: file 2 block 510: rolled back: gen 17 < durable gen 18
FAIL: file 2 block 561: rolled back: gen 22 < durable gen 23
FAIL: file 2 block 955: rolled back: gen 12 < durable gen 13
FAIL: file 2 block 3986: rolled back: gen 23 < durable gen 24
w/ debug logs (trace_printk) added in f2fs, the ftrace log of the
failing compressed file (ino 438) shows:
python3-346519 2267.558508: FSV SYNC-BEGIN f=2 fdatasync n=4
python3-346519 2267.558519: ino=438 FSYNC enter datasync=1 dirty=4 cpver=14c8edcb
f2fs_ckpt 2267.559876: CP end reason=4 err=0 cpver=14c8edcb
python3-346519 2267.570549: ino=438 DOWRITE OPU idx=510 old=a21bc1 new=ae4af3 hdr[f=2 b=510 g=18]
python3-346519 2267.571478: ino=438 WCACHE exit ret=0 nwritten=4 dirty_left=0
bash 2267.571810: iteration 4: injecting shutdown 2
f2fs_ckpt 2267.573403: CP start reason=4 cpver=14c8edcb dirty=1
python3-346519 2267.579576: ino=438 FSYNC wait_range ret=0 dirty_left=0 append=1
f2fs_ckpt 2267.589393: CP end reason=4 err=-5 cpver=14c8edcc cp_error=1
f2fs_ckpt 2267.589413: CP start reason=4 cpver=14c8edcc dirty=0
f2fs_ckpt 2267.589414: CP SKIPPED (sb not dirty) reason=4
f2fs_ckpt 2267.589415: CP end reason=4 err=0 cpver=14c8edcc cp_error=1
python3-346516 2267.589431: SYNC_FS sync=1 err=-5
python3-346519 2267.589503: SYNC_FS sync=1 err=0 <--- false success
python3-346519 2267.589507: ino=438 FSYNC cp_reason=2 sync_fs ret=0
python3-346519 2267.589510: ino=438 FSYNC exit ret=0
python3-346519 2267.589567: FSV SYNC-OK f=2 D=510:18,561:23,955:13,3986:24
[shutdown -> umount -> fsck -> remount, recovery from cpver 14c8edcb]
kworker 2370.028185: ino=438 READ-END idx=510 hdr[f=2 b=510 g=17]
- the checkpoint which should cover the data written at 2267.570549
failed w/ -EIO due to shutdown, and cleared SBI_IS_DIRTY.
- the next merged checkpoint request was skipped as the superblock looked
clean, and returned 0, so fdatasync() of ino 438 returned 0.
- after recovery from the last valid checkpoint (14c8edcb), block 510
has gen 17 instead of the fsynced gen 18.
Let's check cp_error before the "superblock is not dirty" check in
f2fs_write_checkpoint(), so that a pending checkpoint request returns
-EIO once cp_error is set.
Cc: stable@kernel.org
Fixes: c34f42e2cb2d ("f2fs: report error of do_checkpoint")
Signed-off-by: Chao Yu <chao@kernel.org>
---
fs/f2fs/checkpoint.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
index 5c5b45d3c67b..819073671f3d 100644
--- a/fs/f2fs/checkpoint.c
+++ b/fs/f2fs/checkpoint.c
@@ -1945,14 +1945,19 @@ int f2fs_write_checkpoint(struct f2fs_sb_info *sbi, struct cp_control *cpc)
stat_cp_time(cpc, CP_TIME_LOCK);
- if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) &&
- ((cpc->reason & CP_FASTBOOT) || (cpc->reason & CP_SYNC) ||
- ((cpc->reason & CP_DISCARD) && !sbi->discard_blks)))
- goto out;
+ /*
+ * Check cp_error first: a failed checkpoint has cleared SBI_IS_DIRTY
+ * in do_checkpoint(), so a following request would be skipped below
+ * and report false success.
+ */
if (unlikely(f2fs_cp_error(sbi))) {
err = -EIO;
goto out;
}
+ if (!is_sbi_flag_set(sbi, SBI_IS_DIRTY) &&
+ ((cpc->reason & CP_FASTBOOT) || (cpc->reason & CP_SYNC) ||
+ ((cpc->reason & CP_DISCARD) && !sbi->discard_blks)))
+ goto out;
trace_f2fs_write_checkpoint(sbi->sb, cpc->reason, CP_PHASE_START_BLOCK_OPS);
--
2.49.0
next prev parent reply other threads:[~2026-10-22 12:53 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-22 12:52 [PATCH 1/7] f2fs: fix to persist i_size extended inside last block for fsync Chao Yu
2026-10-22 12:52 ` [PATCH 2/7] f2fs: fix to return -EIO from f2fs_sync_fs() if cp_error is set Chao Yu
2026-10-22 12:52 ` Chao Yu [this message]
2026-10-22 12:52 ` [PATCH 4/7] f2fs: fix to truncate stale meta cache entry on in-place update Chao Yu
2026-10-22 12:52 ` [PATCH 5/7] f2fs: fix to truncate meta cache entry in F2FS_IOC_SEC_TRIM_FILE Chao Yu
2026-10-22 12:52 ` [PATCH 6/7] f2fs: fix to serialize truncation of COW blocks w/ GC Chao Yu
2026-10-22 12:52 ` [PATCH 7/7] f2fs: cache: fix to initialize f2fs_writeback info at mount time Chao Yu
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261022125244.2205275-3-chao@kernel.org \
--to=chao@kernel.org \
--cc=jaegeuk@kernel.org \
--cc=linux-f2fs-devel@lists.sourceforge.net \
--cc=linux-kernel@vger.kernel.org \
--cc=stable@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®