[PATCH] btrfs: dev-replace: fix missing barrier before waking bio counter waiters

FAN YE <[email protected]>
Newsgroups org.kernel.feeds.b4-sent,org.kernel.vger.linux-btrfs,org.kernel.vger.linux-kernel
Message-ID <20260822-btrfs-dev-replace-bio-counter-barrier-v1-1-e718ae7d25e5@gmail.com>
btrfs_bio_counter_sub() gates the wakeup on cond_wake_up_nomb(), whose
bare waitqueue_active() requires a full barrier from the preceding code.
percpu_counter_sub() does not provide one, its fast path is a plain
per-cpu update, so the waitqueue_active() load can be hoisted over the
counter update.  btrfs_rm_dev_replace_blocked() waits for
percpu_counter_sum() to reach zero and can then be left sleeping by the
very decrement that reaches it.  It stays asleep until some unrelated
later decrement wakes it, with BTRFS_FS_STATE_DEV_REPLACING set the whole
time, so every bio submitter on the filesystem blocks as well.
Use cond_wake_up(), which has the barrier.

Fixes: 4245215d6a8d ("Btrfs, raid56: fix use-after-free problem in the final device replace procedure on raid56")
Assisted-by: Claude:claude-opus-5
Signed-off-by: FAN YE <[email protected]>
---
Two independent checks.  A litmus test of the same shape: herd7 with the LKMM
allows the interleaving and forbids it with smp_mb(); built with klitmus7 it
hits 349326 times in 64000000 iterations on x86_64, 0 times with the barrier.
With that interleaving staged in the dev-replace path in a VM (the last
in-flight bio released while the finishing waiter is queueing), the wakeup is
lost in 3/3 runs and the filesystem stalls for 24.5s until an unrelated
decrement arrives, 0/3 with this patch.  Gating the barrier on
BTRFS_FS_STATE_DEV_REPLACING to keep it off the common path does not work:
that test_bit() is unordered as well, and hoisting it over the decrement drops
the wakeup in 3/3 runs.  Compile-tested (W=1, x86_64 defconfig +
CONFIG_BTRFS_FS=y).
---
 fs/btrfs/dev-replace.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/btrfs/dev-replace.c b/fs/btrfs/dev-replace.c
index dc0834f920c3..b2791480e263 100644
--- a/fs/btrfs/dev-replace.c
+++ b/fs/btrfs/dev-replace.c
@@ -1353,7 +1353,7 @@ bool __pure btrfs_dev_replace_is_ongoing(struct btrfs_dev_replace *dev_replace)
 void btrfs_bio_counter_sub(struct btrfs_fs_info *fs_info, s64 amount)
 {
 	percpu_counter_sub(&fs_info->dev_replace.bio_counter, amount);
-	cond_wake_up_nomb(&fs_info->dev_replace.replace_wait);
+	cond_wake_up(&fs_info->dev_replace.replace_wait);
 }
 
 void btrfs_bio_counter_inc_blocked(struct btrfs_fs_info *fs_info)

---
base-commit: 26260251022fbc2f248a3d747a9b2b961b18d2d8
change-id: 20260822-btrfs-dev-replace-bio-counter-barrier-4a20eb35187a

Best regards,
--  
FAN YE <[email protected]>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.