[PATCH v2 0/5] btrfs: data integrity fixes
From: Josef Bacik
Date: Tue Nov 03 2026 - 10:24:55 EST
v1: https://lore.kernel.org/all/20261030-b4-btrfs-data-integrity-v1-0-f4d4196731e6@xxxxxxxxxxxxxx/
v1->v2:
- Rework patch 3, now "btrfs: wait for ordered extent IO in fast fsync".
BTRFS_INODE_COW_WRITE_ERROR is set after the end IO handler clears
writeback, so a fast fsync could still miss it, for plain write errors
as well. Fast fsync now waits for BTRFS_ORDERED_IO_DONE on the ordered
extents it collected and checks BTRFS_ORDERED_IOERR, and the inode flag
is gone (Sashiko, Qu, Filipe).
- Patch 4: use DEBUG_WARN() instead of ASSERT() for the check that can no
longer be true (Filipe).
- Collect tags.
--- Original email (v1) ---
Hello,
I've been doing some model checking of our trickier paths with Claude's
help, and these are the fixes for the bugs it turned up that actually
lose data, or tell userspace that something is safe when it isn't.
Every one of them was reproduced in a VM on an unpatched kernel and goes
away with the fix, though the fsync and log sync ones needed a delay
injected to hit the window.
The worst one is the fstrim one. If you run fstrim while a device
replace is running we'll discard everything that has already been copied
to the target device, because the target's alloc_state is empty until
the replace finishes, so the whole device looks unallocated. The
replace then finishes happily, the source device goes away, and you're
left with zeroes where your metadata used to be. This has been around
since FITRIM learned to trim unallocated space back in 4.3.
The split bio one is similarly nasty. If the last piece of a split bio
to complete is one of the split bios and it failed, we drop the error on
the floor, and a buffered read returns success with whatever was sitting
in the page.
The rest are in our error handling. A delalloc failure in the middle of
a folio leaves blocks dirty that no longer have an ordered extent, a
fast fsync after an error that happened before we submitted the bio can
log extents that were never written (which log replay then happily puts
in place of the data that was there before), and btrfs_sync_log() can
return success for a log transaction that never got committed.
I have a cleanup series for btrfs_sync_log coming, as I had to spend a
lot of time grokking that fix, which is a good indication that the code
is too complicated.
They're all independent of each other and tagged for stable. This is
against for-next, and it also applies cleanly on top of Qu's
extent-by-extent writeback series. Thanks,
Josef
---
Changes in v2:
- Link to v1: https://patch.msgid.link/20261030-b4-btrfs-data-integrity-v1-0-f4d4196731e6@xxxxxxxxxxxxxx
---
Josef Bacik (5):
btrfs: don't drop the error of the last split bio
btrfs: clear dirty blocks after a delalloc failure
btrfs: wait for ordered extent IO in fast fsync
btrfs: don't report success for a skipped log sync
btrfs: don't trim the target of a device replace
fs/btrfs/bio.c | 9 ++++++---
fs/btrfs/btrfs_inode.h | 10 ----------
fs/btrfs/extent-tree.c | 8 ++++++++
fs/btrfs/extent_io.c | 10 ++++++++++
fs/btrfs/file.c | 52 +++++++++++++++++++++++++++++++++++++++----------
fs/btrfs/ordered-data.c | 31 -----------------------------
fs/btrfs/tree-log.c | 20 +++++++++++++++++--
7 files changed, 84 insertions(+), 56 deletions(-)
---
base-commit: 2789b2c034d7c0c8687355921a38c5ac41a565fa
change-id: 20261029-b4-btrfs-data-integrity-eb753b57300f