[PATCH 07/11] ext4: Convert group-count barrier protocol to acquire/release

From: Jinjie Ruan

Date: Tue Aug 25 2026 - 06:03:38 EST


During an online resize, reading s_groups_count and s_blockfile_groups
requires strict ordering against subsequent initialized block group
metadata.

Replace the historical smp_wmb()/smp_rmb() pairs with smp_store_release()
and smp_load_acquire(). This formalizes the publish-subscribe pattern
and allows weakly-ordered architectures (e.g., arm64) to utilize native
STLR/LDAR instructions instead of full DMB fences.

Update the documentation in resize.c and ext4.h accordingly.

Assisted-by: Gemini:Gemini-3.1
Assisted-by: DeepSeek:DeepSeek-V3
Signed-off-by: Jinjie Ruan <ruanjinjie@xxxxxxxxxx>
---
fs/ext4/ext4.h | 10 +++-------
fs/ext4/mballoc.c | 6 ++----
fs/ext4/resize.c | 19 +++++++++++--------
3 files changed, 16 insertions(+), 19 deletions(-)

diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
index 724a27e8be61..d70b9cb09155 100644
--- a/fs/ext4/ext4.h
+++ b/fs/ext4/ext4.h
@@ -3542,16 +3542,12 @@ static inline void ext4_isize_set(struct ext4_inode *raw_inode, loff_t i_size)
}

/*
- * Reading s_groups_count requires using smp_rmb() afterwards. See
- * the locking protocol documented in the comments of ext4_group_add()
- * in resize.c
+ * Reading s_groups_count uses acquire semantics.
*/
static inline ext4_group_t ext4_get_groups_count(struct super_block *sb)
{
- ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count;
-
- smp_rmb();
- return ngroups;
+ /* Pairs with smp_store_release() in ext4_update_super() */
+ return smp_load_acquire(&EXT4_SB(sb)->s_groups_count);
}

static inline ext4_group_t ext4_flex_group(struct ext4_sb_info *sbi,
diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
index 06171a11db12..a15285224cdf 100644
--- a/fs/ext4/mballoc.c
+++ b/fs/ext4/mballoc.c
@@ -899,10 +899,8 @@ static ext4_group_t ext4_get_allocation_groups_count(

/* non-extent files are limited to low blocks/groups */
if (!(ext4_test_inode_flag(ac->ac_inode, EXT4_INODE_EXTENTS)))
- ngroups = EXT4_SB(ac->ac_sb)->s_blockfile_groups;
-
- /* Pairs with smp_wmb() in ext4_update_super() */
- smp_rmb();
+ /* Pairs with smp_store_release() in ext4_update_super() */
+ ngroups = smp_load_acquire(&EXT4_SB(ac->ac_sb)->s_blockfile_groups);

return ngroups;
}
diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c
index 2c5b851c552a..5e3ad8393cf8 100644
--- a/fs/ext4/resize.c
+++ b/fs/ext4/resize.c
@@ -1462,11 +1462,11 @@ static void ext4_update_super(struct super_block *sb,
*
* The precise rules we use are:
*
- * * Writers must perform a smp_wmb() after updating all
- * dependent data and before modifying the groups count
+ * * Writers must use a release store when updating the groups count
+ * after all dependent data has been updated
*
- * * Readers must perform an smp_rmb() after reading the groups
- * count and before reading any dependent data.
+ * * Readers must use an acquire load when reading the groups
+ * count before reading any dependent data.
*
* NB. These rules can be relaxed when checking the group count
* while freeing data, as we can only allocate from a block
@@ -1474,12 +1474,15 @@ static void ext4_update_super(struct super_block *sb,
* only then free after serialising in turn against that
* allocation.
*/
- smp_wmb();

/* Update the global fs size fields */
- sbi->s_groups_count += flex_gd->count;
- sbi->s_blockfile_groups = min(sbi->s_groups_count,
- (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb)));
+ /* Pairs with smp_load_acquire() in ext4_get_groups_count() */
+ smp_store_release(&sbi->s_groups_count,
+ sbi->s_groups_count + flex_gd->count);
+ /* Pairs with smp_load_acquire() in ext4_get_allocation_groups_count() */
+ smp_store_release(&sbi->s_blockfile_groups,
+ min(sbi->s_groups_count,
+ (EXT4_MAX_BLOCK_FILE_PHYS / EXT4_BLOCKS_PER_GROUP(sb))));

/* Update the reserved block counts only once the new group is
* active. */
--
2.34.1