[PATCH RFC] btrfs: drop the ineffective barrier in should_cow_block()

FAN YE via B4 Relay <[email protected]>
Newsgroups org.kernel.vger.linux-btrfs,org.kernel.feeds.b4-sent,org.kernel.vger.linux-kernel
Message-ID <20260823-btrfs-drop-ineffective-barrier-v1-1-48854e8b847a@gmail.com>
From: FAN YE <[email protected]>

smp_mb__before_atomic() only orders against a non-value-returning atomic
RMW that follows it.  test_bit() is a plain load, so under the LKMM this
fence orders nothing here and nothing may rely on it.  It is not free
either: only six architectures override __smp_mb__before_atomic, so on
arm64 and most others it emits a real barrier -- dropping it removes one
dmb ish from each of the two inlined call sites.  The ordering the
comment asks for does not come from the fence in the first place; see
below.

It did not start out this way.  commit f1ebcc74d5b2 ("Btrfs: fix tree
corruption after multi-thread snapshots and inode_cache flush") added it
as a real smp_rmb(), paired with smp_wmb() around a plain int force_cow.
commit d1980131ca7f ("btrfs: update barrier in should_cow_block") swapped
it for smp_mb__before_atomic() when the field became a bit, noting in
its own log that "we should be fine in case the atomic barrier becomes a
no-op" -- and against a plain test_bit() that is what it became.
commit f07575bab632 ("btrfs: make the rule checking more readable for
should_cow_block()") only relocated it.  Drop it along with the comment
rather than promote it to a real barrier: whatever ordering is wanted
here would have to name what it pairs with today.  The two writer-side
comments in transaction.c only pointed here for that rationale, so they
go with it; the smp_mb__after_atomic() they annotated is left alone.

Assisted-by: Claude:claude-opus-5 sashiko(Sonnet-5) qwen
Signed-off-by: FAN YE <[email protected]>
---
Why the ordering does not depend on the fence: any writer that can reach
should_cow_block() for this root crosses fs_info->trans_lock in
join_transaction(), and can only do so after the committer's matching
unlock when it leaves TRANS_STATE_COMMIT_DOING in
btrfs_commit_transaction(), by which point create_pending_snapshots() --
where set_bit(FORCE_COW) lives -- has already run.  That release/acquire
pair orders the bit, fence or not.  BTRFS_ROOT_FORCE_COW has only these
three references tree-wide.

herd7 with the LKMM, one SB shape with only the fence varying: allowed with
this fence in front of a plain load, still allowed with the fence deleted,
forbidden with smp_mb() and forbidden with an RMW after the fence.  Built
with klitmus7 on x86_64 it hits 329608 times in 64000000 iterations, 329706
with the fence deleted, 0 with smp_mb().

arm64 defconfig+BTRFS_FS, gcc 13.3: ctree.o goes from 39 to 37 dmb ish, one
each from btrfs_cow_block() and btrfs_search_slot().  On x86_64 .text is
unchanged -- the only object differences are __LINE__ constants in the BUG
table shifting by the two deleted lines.  Compile-tested W=1 on both.

Sent as RFC for the one thing this does not settle: with the reader side
gone, the smp_mb__after_atomic() on each writer has nothing left to pair
with, and by the argument above trans_lock was doing the work anyway.  I
left both alone rather than guess -- if they are stale too that is a
separate patch, and I would rather have your reading first.
---
 fs/btrfs/ctree.c       | 2 --
 fs/btrfs/transaction.c | 2 --
 2 files changed, 4 deletions(-)

diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 8fe330d81b8f..fe74967d725a 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -625,8 +625,6 @@ static inline bool should_cow_block(struct btrfs_trans_handle *trans,
 	if (btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN))
 		return true;
 
-	/* Ensure we can see the FORCE_COW bit. */
-	smp_mb__before_atomic();
 	if (test_bit(BTRFS_ROOT_FORCE_COW, &root->state))
 		return true;
 
diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c
index bafc62cf5ebc..3fced11d7889 100644
--- a/fs/btrfs/transaction.c
+++ b/fs/btrfs/transaction.c
@@ -1531,7 +1531,6 @@ static noinline int commit_fs_roots(struct btrfs_trans_handle *trans)
 			if (unlikely(ret2))
 				return ret2;
 
-			/* see comments in should_cow_block() */
 			clear_bit(BTRFS_ROOT_FORCE_COW, &root->state);
 			smp_mb__after_atomic();
 
@@ -1819,7 +1818,6 @@ static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
 		btrfs_abort_transaction(trans, ret);
 		goto fail;
 	}
-	/* see comments in should_cow_block() */
 	set_bit(BTRFS_ROOT_FORCE_COW, &root->state);
 	smp_mb__after_atomic();
 

---
base-commit: 2709dd5ae32f0828f386327c76bba9f39f63a1c6
change-id: 20260823-btrfs-drop-ineffective-barrier-c3112e55b00b

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.