* [PATCH 00/11] Convert barrier pairs to acquire/release for better performance
@ 2026-08-25 9:54 Jinjie Ruan
2026-08-25 9:54 ` [PATCH 01/11] user_namespace: Use acquire/release for nr_extents synchronization Jinjie Ruan
` (10 more replies)
0 siblings, 11 replies; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
Hi,
This series converts some existing smp_wmb()/smp_rmb() barrier pairs to
smp_store_release()/smp_load_acquire() across various subsystems.
Background
==========
Many architectures support load acquire and store release instructions
which can replace explicit memory barriers and save cycles. As noted
in the ARM architecture reference [1]:
"Weaker ordering requirements that are imposed by Load-Acquire and
Store-Release instructions allow for micro-architectural
optimizations, which could reduce some of the performance impacts
that are otherwise imposed by an explicit memory barrier.
If the ordering requirement is satisfied using either a Load-Acquire
or Store-Release, then it would be preferable to use these
instructions instead of a DMB."
On arm64, a typical seqcount [2] read loop requires 13 cycles with DMB
barriers. Replacing the read barrier with smp_load_acquire() reduces
this to 8 cycles on an Ampere Altra.
We also observed significant barrier overhead while profiling Unxibench
syscall test on arm64: a single getuid() call is ~8ns slower than on
a comparable x86 system, with the dominant cost in map_id_up()'s smp_rmb(),
which is a DMB ISHLD on arm64. Converting it to smp_load_acquire() allows
the use of LDAR, eliminating the measurable overhead.
This motivated a broader search for existing barrier pairs that can
be converted to the lighter acquire/release semantics.
Changes
=======
Each patch in this series targets a specific barrier pair where the
publish/subscribe pattern is already present:
- Writers populate data, then publish a flag/count/pointer via
smp_store_release()
- Readers load the flag/count/pointer via smp_load_acquire(), then
consume the data
This preserves the existing memory ordering guarantees while allowing
architectures with native acquire/release instructions (e.g. arm64's
STLR/LDAR) to avoid the cost of full one-way barriers (DMB ISHST/ISHLD).
On architectures without native support, the generated code is
generally no worse than the explicit barrier pair.
The conversions are mechanical and no functional change is intended.
[1]: https://support.arm.com/documentation/102336/0100/Load-Acquire-and-Store-Release-instructions
[2]: https://github.com/torvalds/linux/commit/d0dd066a0fa26d55c19ace9e89dedd9504c5bcba
Jinjie Ruan (11):
user_namespace: Use acquire/release for nr_extents synchronization
lib/vsprintf: Use acquire/release for ptr_key publication
fs: aio: Use acquire/release for ring->tail publication
fs: Use acquire/release for fdtable resize synchronization
pidfs: Use test_bit_acquire() for attr flag tests
super: Use acquire for SB_BORN check in super_cache_count()
ext4: Convert group-count barrier protocol to acquire/release
soreuseport: publish num_socks with acquire/release
net: sched: act_gact: use acquire/release for tcfg_ptype
8021q: publish vlan_devices_arrays entries with acquire/release
can: isotp: publish tx.state with smp_store_release()
fs/aio.c | 12 +++++-------
fs/ext4/ext4.h | 10 +++-------
fs/ext4/mballoc.c | 6 ++----
fs/ext4/resize.c | 19 +++++++++++--------
fs/file.c | 10 ++++------
fs/pidfs.c | 6 ++----
fs/super.c | 7 +++----
kernel/user_namespace.c | 24 +++++++++++++-----------
lib/vsprintf.c | 11 ++++-------
net/8021q/vlan.c | 6 ++----
net/8021q/vlan.h | 8 +++-----
net/can/isotp.c | 4 ++--
net/core/sock_reuseport.c | 20 ++++++++------------
net/sched/act_gact.c | 12 ++++--------
14 files changed, 66 insertions(+), 89 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 01/11] user_namespace: Use acquire/release for nr_extents synchronization
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
@ 2026-08-25 9:54 ` Jinjie Ruan
2026-08-25 9:54 ` [PATCH 02/11] lib/vsprintf: Use acquire/release for ptr_key publication Jinjie Ruan
` (9 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
Replace smp_wmb()/smp_rmb() with smp_store_release()/smp_load_acquire()
when publishing and consuming `nr_extents`. This expresses the
publish/subscribe pattern more clearly and allows architectures with
native acquire/release instructions (e.g. arm64's STLR/LDAR) to avoid
the cost of full one-way barriers (DMB ISHST/ISHLD).
No functional change intended.
Assisted-by: DeepSeek:DeepSeek-V3
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
kernel/user_namespace.c | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 0bed462e9b2a..576b667a8813 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -317,9 +317,9 @@ map_id_range_down_base(unsigned extents, struct uid_gid_map *map, u32 id, u32 co
static u32 map_id_range_down(struct uid_gid_map *map, u32 id, u32 count)
{
+ /* Pairs with smp_store_release() in map_write(). */
+ unsigned int extents = smp_load_acquire(&map->nr_extents);
struct uid_gid_extent *extent;
- unsigned extents = map->nr_extents;
- smp_rmb();
if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
extent = map_id_range_down_base(extents, map, id, count);
@@ -383,9 +383,9 @@ map_id_range_up_max(unsigned extents, struct uid_gid_map *map, u32 id, u32 count
u32 map_id_range_up(struct uid_gid_map *map, u32 id, u32 count)
{
+ /* Pairs with smp_store_release() in map_write(). */
+ unsigned int extents = smp_load_acquire(&map->nr_extents);
struct uid_gid_extent *extent;
- unsigned extents = map->nr_extents;
- smp_rmb();
if (extents <= UID_GID_MAP_MAX_BASE_EXTENTS)
extent = map_id_range_up_base(extents, map, id, count);
@@ -676,9 +676,9 @@ static int projid_m_show(struct seq_file *seq, void *v)
static void *m_start(struct seq_file *seq, loff_t *ppos,
struct uid_gid_map *map)
{
+ /* Pairs with smp_store_release() in map_write(). */
+ unsigned int extents = smp_load_acquire(&map->nr_extents);
loff_t pos = *ppos;
- unsigned extents = map->nr_extents;
- smp_rmb();
if (pos >= extents)
return NULL;
@@ -967,9 +967,11 @@ static ssize_t map_write(struct file *file, const char __user *buf,
* desired behavior is to see the values of the extents that
* were written before the count of the extents.
*
- * To achieve this smp_wmb() is used on guarantee the write
- * order and smp_rmb() is guaranteed that we don't have crazy
- * architectures returning stale data.
+ * The nr_extents field is the publish point for the extent
+ * data. Writers use smp_store_release() to ensure all extent
+ * data is visible before nr_extents is updated. Readers use
+ * smp_load_acquire() to ensure they see a consistent view of
+ * the extent data when reading nr_extents.
*/
mutex_lock(&userns_state_mutex);
@@ -1098,8 +1100,8 @@ static ssize_t map_write(struct file *file, const char __user *buf,
map->forward = new_map.forward;
map->reverse = new_map.reverse;
}
- smp_wmb();
- map->nr_extents = new_map.nr_extents;
+ /* Pairs with smp_load_acquire() in map_id_range_{up,down}() and m_start(). */
+ smp_store_release(&map->nr_extents, new_map.nr_extents);
*ppos = count;
ret = count;
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 02/11] lib/vsprintf: Use acquire/release for ptr_key publication
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
2026-08-25 9:54 ` [PATCH 01/11] user_namespace: Use acquire/release for nr_extents synchronization Jinjie Ruan
@ 2026-08-25 9:54 ` Jinjie Ruan
2026-08-25 9:54 ` [PATCH 03/11] fs: aio: Use acquire/release for ring->tail publication Jinjie Ruan
` (8 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
Replace the smp_wmb() + WRITE_ONCE() and READ_ONCE() + smp_rmb() barrier
pair with smp_store_release()/smp_load_acquire() on filled_random_ptr_key.
This expresses the publish/subscribe pattern more clearly and allows
architectures with native acquire/release instructions (e.g. arm64's
STLR/LDAR) to avoid the cost of full one-way barriers (DMB ISHST/ISHLD).
No functional change intended.
Assisted-by: DeepSeek:DeepSeek-V3
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
lib/vsprintf.c | 11 ++++-------
1 file changed, 4 insertions(+), 7 deletions(-)
diff --git a/lib/vsprintf.c b/lib/vsprintf.c
index e285e8bc4712..d6f235e143b7 100644
--- a/lib/vsprintf.c
+++ b/lib/vsprintf.c
@@ -762,9 +762,8 @@ static int fill_ptr_key(struct notifier_block *nb, unsigned long action, void *d
{
get_random_bytes(&ptr_key, sizeof(ptr_key));
- /* Pairs with smp_rmb() before reading ptr_key. */
- smp_wmb();
- WRITE_ONCE(filled_random_ptr_key, true);
+ /* Pairs with smp_load_acquire() before reading ptr_key. */
+ smp_store_release(&filled_random_ptr_key, true);
return NOTIFY_DONE;
}
@@ -781,12 +780,10 @@ static inline int __ptr_to_hashval(const void *ptr, unsigned long *hashval_out)
{
unsigned long hashval;
- if (!READ_ONCE(filled_random_ptr_key))
+ /* Pairs with smp_store_release() after writing ptr_key. */
+ if (!smp_load_acquire(&filled_random_ptr_key))
return -EBUSY;
- /* Pairs with smp_wmb() after writing ptr_key. */
- smp_rmb();
-
#ifdef CONFIG_64BIT
hashval = (unsigned long)siphash_1u64((u64)ptr, &ptr_key);
/*
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 03/11] fs: aio: Use acquire/release for ring->tail publication
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
2026-08-25 9:54 ` [PATCH 01/11] user_namespace: Use acquire/release for nr_extents synchronization Jinjie Ruan
2026-08-25 9:54 ` [PATCH 02/11] lib/vsprintf: Use acquire/release for ptr_key publication Jinjie Ruan
@ 2026-08-25 9:54 ` Jinjie Ruan
2026-08-25 9:54 ` [PATCH 04/11] fs: Use acquire/release for fdtable resize synchronization Jinjie Ruan
` (7 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
Replace the smp_wmb() + WRITE_ONCE(ring->tail) and READ_ONCE(ring->tail)
+ smp_rmb() barrier pair with smp_store_release()/smp_load_acquire()
on `ring->tail`.
This expresses the publish/subscribe pattern more clearly and allows
architectures with native acquire/release instructions (e.g. arm64's
STLR/LDAR) to avoid the cost of full one-way barriers (DMB ISHST/ISHLD).
The release ensures event data written before updating ring->tail is
visible to readers that observe the new tail value via acquire, which
is exactly the ordering the barrier pair provided.
No functional change intended.
Assisted-by: DeepSeek:DeepSeek-V3
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
fs/aio.c | 12 +++++-------
1 file changed, 5 insertions(+), 7 deletions(-)
diff --git a/fs/aio.c b/fs/aio.c
index d78acc69f487..4413c82688cc 100644
--- a/fs/aio.c
+++ b/fs/aio.c
@@ -1206,13 +1206,12 @@ static void aio_complete(struct aio_kiocb *iocb)
/* after flagging the request as done, we
* must never even look at it again
*/
- smp_wmb(); /* make event visible before updating tail */
-
- ctx->tail = tail;
+ WRITE_ONCE(ctx->tail, tail);
ring = folio_address(ctx->ring_folios[0]);
head = ring->head;
- ring->tail = tail;
+ /* Make event visible before updating tail */
+ smp_store_release(&ring->tail, tail);
flush_dcache_folio(ctx->ring_folios[0]);
ctx->completed_events++;
@@ -1288,13 +1287,12 @@ static long aio_read_events_ring(struct kioctx *ctx,
/* Access to ->ring_folios here is protected by ctx->ring_lock. */
ring = folio_address(ctx->ring_folios[0]);
head = ring->head;
- tail = ring->tail;
-
/*
* Ensure that once we've read the current tail pointer, that
* we also see the events that were stored up to the tail.
+ * Pairs with smp_store_release() in aio_complete().
*/
- smp_rmb();
+ tail = smp_load_acquire(&ring->tail);
pr_debug("h%u t%u m%u\n", head, tail, ctx->nr_events);
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 04/11] fs: Use acquire/release for fdtable resize synchronization
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
` (2 preceding siblings ...)
2026-08-25 9:54 ` [PATCH 03/11] fs: aio: Use acquire/release for ring->tail publication Jinjie Ruan
@ 2026-08-25 9:54 ` Jinjie Ruan
2026-08-25 9:54 ` [PATCH 05/11] pidfs: Use test_bit_acquire() for attr flag tests Jinjie Ruan
` (6 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
Replace the smp_wmb()/smp_rmb() barrier pair with
smp_store_release()/smp_load_acquire() on `files->resize_in_progress`.
The flag is the publish point for fdtable expansion: writers clear it
via release after rcu_assign_pointer(), readers check it via acquire
before rcu_dereference_sched(). Observing it clear guarantees the new
fdt pointer is visible.
This expresses the pattern more clearly and allows cheaper one-way
barriers on weakly-ordered architectures (e.g. arm64 STLR/LDAR vs
DMB ISHST/ISHLD).
Assisted-by: DeepSeek:DeepSeek-V3
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
fs/file.c | 10 ++++------
1 file changed, 4 insertions(+), 6 deletions(-)
diff --git a/fs/file.c b/fs/file.c
index 628ca07dc4b1..86c035d459f2 100644
--- a/fs/file.c
+++ b/fs/file.c
@@ -273,8 +273,6 @@ static int expand_fdtable(struct files_struct *files, unsigned int nr)
rcu_assign_pointer(files->fdt, new_fdt);
if (cur_fdt != &files->fdtab)
call_rcu(&cur_fdt->rcu, free_fdtable_rcu);
- /* coupled with smp_rmb() in fd_install() */
- smp_wmb();
return 0;
}
@@ -313,7 +311,8 @@ static int expand_files(struct files_struct *files, unsigned int nr)
/* All good, so we try */
files->resize_in_progress = true;
error = expand_fdtable(files, nr);
- files->resize_in_progress = false;
+ /* coupled with smp_load_acquire() in fd_install() */
+ smp_store_release(&files->resize_in_progress, false);
wake_up_all(&files->resize_wait);
return error;
@@ -685,13 +684,12 @@ void fd_install(unsigned int fd, struct file *file)
return;
rcu_read_lock_sched();
- if (unlikely(files->resize_in_progress)) {
+ /* coupled with smp_store_release() in expand_files() */
+ if (unlikely(smp_load_acquire(&files->resize_in_progress))) {
rcu_read_unlock_sched();
fd_install_slowpath(fd, file);
return;
}
- /* coupled with smp_wmb() in expand_fdtable() */
- smp_rmb();
fdt = rcu_dereference_sched(files->fdt);
VFS_BUG_ON(rcu_access_pointer(fdt->fd[fd]) != NULL);
rcu_assign_pointer(fdt->fd[fd], file);
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 05/11] pidfs: Use test_bit_acquire() for attr flag tests
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
` (3 preceding siblings ...)
2026-08-25 9:54 ` [PATCH 04/11] fs: Use acquire/release for fdtable resize synchronization Jinjie Ruan
@ 2026-08-25 9:54 ` Jinjie Ruan
2026-08-25 9:54 ` [PATCH 06/11] super: Use acquire for SB_BORN check in super_cache_count() Jinjie Ruan
` (5 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
Replace test_bit() + smp_rmb() with test_bit_acquire() for
PIDFS_ATTR_BIT_EXIT and PIDFS_ATTR_BIT_COREDUMP tests in pidfd_info().
The acquire semantics ensure that if the flag is observed set, the
associated attr fields written before smp_wmb() + set_bit() are
also visible.
Writers keep smp_wmb() + set_bit() since no release bitop exists.
Assisted-by: DeepSeek:DeepSeek-V3
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
fs/pidfs.c | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/fs/pidfs.c b/fs/pidfs.c
index a6a643f15d08..2cd0fab73180 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -393,8 +393,7 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
attr = READ_ONCE(pid->attr);
if (mask & PIDFD_INFO_EXIT) {
- if (test_bit(PIDFS_ATTR_BIT_EXIT, &attr->attr_mask)) {
- smp_rmb();
+ if (test_bit_acquire(PIDFS_ATTR_BIT_EXIT, &attr->attr_mask)) {
kinfo.mask |= PIDFD_INFO_EXIT;
#ifdef CONFIG_CGROUPS
kinfo.cgroupid = attr->cgroupid;
@@ -405,8 +404,7 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
}
if (mask & PIDFD_INFO_COREDUMP) {
- if (test_bit(PIDFS_ATTR_BIT_COREDUMP, &attr->attr_mask)) {
- smp_rmb();
+ if (test_bit_acquire(PIDFS_ATTR_BIT_COREDUMP, &attr->attr_mask)) {
kinfo.mask |= PIDFD_INFO_COREDUMP | PIDFD_INFO_COREDUMP_SIGNAL | PIDFD_INFO_COREDUMP_CODE;
kinfo.coredump_mask = attr->coredump_mask;
kinfo.coredump_signal = attr->coredump_signal;
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 06/11] super: Use acquire for SB_BORN check in super_cache_count()
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
` (4 preceding siblings ...)
2026-08-25 9:54 ` [PATCH 05/11] pidfs: Use test_bit_acquire() for attr flag tests Jinjie Ruan
@ 2026-08-25 9:54 ` Jinjie Ruan
2026-08-25 9:54 ` [PATCH 07/11] ext4: Convert group-count barrier protocol to acquire/release Jinjie Ruan
` (4 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
The active SB_BORN check in super_cache_count() pairs with the
smp_store_release() in super_wake() when publishing a newly initialized
superblock.
Replace the historical independent smp_rmb() barrier with an acquire load
on sb->s_flags. This expresses the publish-subscribe pattern more clearly
and allows weakly-ordered architectures like arm64 to utilize efficient
native instructions (e.g., LDAR) instead of a full one-way barrier.
Also update the outdated comment to correctly point to super_wake().
Assisted-by: Gemini:Gemini-3.1
Assisted-by: DeepSeek:DeepSeek-V3
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
fs/super.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/fs/super.c b/fs/super.c
index 05e443173038..5860daea6d40 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -267,12 +267,11 @@ static unsigned long super_cache_count(struct shrinker *shrink,
* However, if we are currently mounting the superblock, the underlying
* filesystem might be in a state of partial construction and hence it
* is dangerous to access it. super_trylock_shared() uses a SB_BORN check
- * to avoid this situation, so do the same here. The memory barrier is
- * matched with the one in mount_fs() as we don't hold locks here.
+ * to avoid this situation, so do the same here. The acquire is matched
+ * with the smp_store_release() in super_wake() as we don't hold locks here.
*/
- if (!(sb->s_flags & SB_BORN))
+ if (!(smp_load_acquire(&sb->s_flags) & SB_BORN))
return 0;
- smp_rmb();
if (sb->s_op && sb->s_op->nr_cached_objects &&
super_fs_objects_eligible(sc))
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 07/11] ext4: Convert group-count barrier protocol to acquire/release
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
` (5 preceding siblings ...)
2026-08-25 9:54 ` [PATCH 06/11] super: Use acquire for SB_BORN check in super_cache_count() Jinjie Ruan
@ 2026-08-25 9:54 ` Jinjie Ruan
2026-08-25 9:54 ` [PATCH 08/11] soreuseport: publish num_socks with acquire/release Jinjie Ruan
` (3 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
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@huawei.com>
---
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
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 08/11] soreuseport: publish num_socks with acquire/release
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
` (6 preceding siblings ...)
2026-08-25 9:54 ` [PATCH 07/11] ext4: Convert group-count barrier protocol to acquire/release Jinjie Ruan
@ 2026-08-25 9:54 ` Jinjie Ruan
2026-08-25 9:54 ` [PATCH 09/11] net: sched: act_gact: use acquire/release for tcfg_ptype Jinjie Ruan
` (2 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
Replace the smp_wmb()/smp_rmb() barrier pair with
smp_store_release()/smp_load_acquire() on reuse->num_socks.
Writers publish socks[] updates via release before incrementing
or decrementing the count; readers acquire the count before
accessing socks[], ensuring they observe a consistent view.
The detach path gains proper ordering between the socks[] write and
the decrement, which was previously unordered.
No functional change intended.
Assisted-by: DeepSeek:DeepSeek-V3
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
net/core/sock_reuseport.c | 20 ++++++++------------
1 file changed, 8 insertions(+), 12 deletions(-)
diff --git a/net/core/sock_reuseport.c b/net/core/sock_reuseport.c
index 29948cb44b7d..6d3c511d1def 100644
--- a/net/core/sock_reuseport.c
+++ b/net/core/sock_reuseport.c
@@ -125,9 +125,8 @@ static void __reuseport_add_sock(struct sock *sk,
struct sock_reuseport *reuse)
{
reuse->socks[reuse->num_socks] = sk;
- /* paired with smp_rmb() in reuseport_(select|migrate)_sock() */
- smp_wmb();
- reuse->num_socks++;
+ /* paired with smp_load_acquire() in reuseport_(select|migrate)_sock() */
+ smp_store_release(&reuse->num_socks, reuse->num_socks + 1);
reuseport_get_incoming_cpu(sk, reuse);
}
@@ -140,7 +139,8 @@ static bool __reuseport_detach_sock(struct sock *sk,
return false;
reuse->socks[i] = reuse->socks[reuse->num_socks - 1];
- reuse->num_socks--;
+ /* paired with smp_load_acquire() in reuseport_(select|migrate)_sock() */
+ smp_store_release(&reuse->num_socks, reuse->num_socks - 1);
reuseport_put_incoming_cpu(sk, reuse);
return true;
@@ -583,11 +583,9 @@ struct sock *reuseport_select_sock(struct sock *sk,
goto out;
prog = rcu_dereference(reuse->prog);
- socks = READ_ONCE(reuse->num_socks);
+ /* paired with smp_store_release() in __reuseport_add_sock() */
+ socks = smp_load_acquire(&reuse->num_socks);
if (likely(socks)) {
- /* paired with smp_wmb() in __reuseport_add_sock() */
- smp_rmb();
-
if (!prog || !skb)
goto select_by_hash;
@@ -634,13 +632,11 @@ struct sock *reuseport_migrate_sock(struct sock *sk,
if (!reuse)
goto out;
- socks = READ_ONCE(reuse->num_socks);
+ /* paired with smp_store_release() in __reuseport_add_sock() */
+ socks = smp_load_acquire(&reuse->num_socks);
if (unlikely(!socks))
goto failure;
- /* paired with smp_wmb() in __reuseport_add_sock() */
- smp_rmb();
-
hash = migrating_sk->sk_hash;
prog = rcu_dereference(reuse->prog);
if (!prog || prog->expected_attach_type != BPF_SK_REUSEPORT_SELECT_OR_MIGRATE) {
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 09/11] net: sched: act_gact: use acquire/release for tcfg_ptype
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
` (7 preceding siblings ...)
2026-08-25 9:54 ` [PATCH 08/11] soreuseport: publish num_socks with acquire/release Jinjie Ruan
@ 2026-08-25 9:54 ` Jinjie Ruan
2026-08-25 9:54 ` [PATCH 10/11] 8021q: publish vlan_devices_arrays entries with acquire/release Jinjie Ruan
2026-08-25 9:54 ` [PATCH 11/11] can: isotp: publish tx.state with smp_store_release() Jinjie Ruan
10 siblings, 0 replies; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
Replace the smp_wmb()/smp_rmb() barrier pair with
smp_store_release()/smp_load_acquire() on gact->tcfg_ptype.
tcf_gact_init() publishes tcfg_ptype via release after writing
tcfg_pval/tcfg_paction; tcf_gact_act() acquires it before
dispatching to gact_net_rand()/gact_determ(), ensuring the probability
parameters are visible.
No functional change intended.
Assisted-by: DeepSeek:DeepSeek-V3
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
net/sched/act_gact.c | 12 ++++--------
1 file changed, 4 insertions(+), 8 deletions(-)
diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c
index 565860cccba6..d4f39f98e2cf 100644
--- a/net/sched/act_gact.c
+++ b/net/sched/act_gact.c
@@ -25,7 +25,6 @@ static struct tc_action_ops act_gact_ops;
#ifdef CONFIG_GACT_PROB
static int gact_net_rand(struct tcf_gact *gact)
{
- smp_rmb(); /* coupled with smp_wmb() in tcf_gact_init() */
if (get_random_u32_below(gact->tcfg_pval))
return gact->tcf_action;
return gact->tcfg_paction;
@@ -35,7 +34,6 @@ static int gact_determ(struct tcf_gact *gact)
{
u32 pack = atomic_inc_return(&gact->packets);
- smp_rmb(); /* coupled with smp_wmb() in tcf_gact_init() */
if (pack % gact->tcfg_pval)
return gact->tcf_action;
return gact->tcfg_paction;
@@ -133,11 +131,8 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla,
if (p_parm) {
gact->tcfg_paction = p_parm->paction;
gact->tcfg_pval = max_t(u16, 1, p_parm->pval);
- /* Make sure tcfg_pval is written before tcfg_ptype
- * coupled with smp_rmb() in gact_net_rand() & gact_determ()
- */
- smp_wmb();
- gact->tcfg_ptype = p_parm->ptype;
+ /* Pairs with smp_load_acquire() in tcf_gact_act(). */
+ smp_store_release(&gact->tcfg_ptype, p_parm->ptype);
}
#endif
spin_unlock_bh(&gact->tcf_lock);
@@ -160,7 +155,8 @@ TC_INDIRECT_SCOPE int tcf_gact_act(struct sk_buff *skb,
#ifdef CONFIG_GACT_PROB
{
- u32 ptype = READ_ONCE(gact->tcfg_ptype);
+ /* Pairs with smp_store_release() in tcf_gact_init() */
+ u32 ptype = smp_load_acquire(&gact->tcfg_ptype);
if (ptype)
action = gact_rand[ptype](gact);
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 10/11] 8021q: publish vlan_devices_arrays entries with acquire/release
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
` (8 preceding siblings ...)
2026-08-25 9:54 ` [PATCH 09/11] net: sched: act_gact: use acquire/release for tcfg_ptype Jinjie Ruan
@ 2026-08-25 9:54 ` Jinjie Ruan
2026-08-25 9:54 ` [PATCH 11/11] can: isotp: publish tx.state with smp_store_release() Jinjie Ruan
10 siblings, 0 replies; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
Replace the smp_wmb()/smp_rmb() barrier pair with
smp_store_release()/smp_load_acquire() on
vg->vlan_devices_arrays[pidx][vidx].
vlan_group_prealloc_vid() publishes the array pointer via release;
__vlan_group_get_device() acquires it before accessing the array,
ensuring the allocated entries are visible.
No functional change intended.
Assisted-by: DeepSeek:DeepSeek-V3
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
net/8021q/vlan.c | 6 ++----
net/8021q/vlan.h | 8 +++-----
2 files changed, 5 insertions(+), 9 deletions(-)
diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c
index 2d2efb877975..e34ada0cceac 100644
--- a/net/8021q/vlan.c
+++ b/net/8021q/vlan.c
@@ -70,10 +70,8 @@ static int vlan_group_prealloc_vid(struct vlan_group *vg,
if (array == NULL)
return -ENOBUFS;
- /* paired with smp_rmb() in __vlan_group_get_device() */
- smp_wmb();
-
- vg->vlan_devices_arrays[pidx][vidx] = array;
+ /* paired with smp_load_acquire() in __vlan_group_get_device() */
+ smp_store_release(&vg->vlan_devices_arrays[pidx][vidx], array);
return 0;
}
diff --git a/net/8021q/vlan.h b/net/8021q/vlan.h
index c41caaf94095..6db7172bdaac 100644
--- a/net/8021q/vlan.h
+++ b/net/8021q/vlan.h
@@ -56,11 +56,9 @@ static inline struct net_device *__vlan_group_get_device(struct vlan_group *vg,
{
struct net_device **array;
- array = vg->vlan_devices_arrays[pidx]
- [vlan_id / VLAN_GROUP_ARRAY_PART_LEN];
-
- /* paired with smp_wmb() in vlan_group_prealloc_vid() */
- smp_rmb();
+ /* Pairs with smp_store_release() in vlan_group_prealloc_vid() */
+ array = smp_load_acquire(&vg->vlan_devices_arrays[pidx]
+ [vlan_id / VLAN_GROUP_ARRAY_PART_LEN]);
return array ? array[vlan_id % VLAN_GROUP_ARRAY_PART_LEN] : NULL;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH 11/11] can: isotp: publish tx.state with smp_store_release()
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
` (9 preceding siblings ...)
2026-08-25 9:54 ` [PATCH 10/11] 8021q: publish vlan_devices_arrays entries with acquire/release Jinjie Ruan
@ 2026-08-25 9:54 ` Jinjie Ruan
2026-08-25 11:46 ` Oliver Hartkopp
10 siblings, 1 reply; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-25 9:54 UTC (permalink / raw)
To: viro, brauner, jack, bcrl, tytso, adilger.kernel, libaokun,
ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, socketcan, mkl, kuniyu, willemb, jhs, jiri,
kees, cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
Cc: ruanjinjie
The writer already pairs with the smp_load_acquire() readers
in isotp_tx_timeout()/isotp_tx_gen_done(); convert
the smp_wmb() + WRITE_ONCE() into a release store.
Assisted-by: DeepSeek:DeepSeek-V3
Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
---
net/can/isotp.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/net/can/isotp.c b/net/can/isotp.c
index 155530aedce2..11b653ba7c10 100644
--- a/net/can/isotp.c
+++ b/net/can/isotp.c
@@ -1156,8 +1156,8 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
my_gen = isotp_inc_tx_gen(READ_ONCE(so->tx_gen));
isotp_set_tx_result(so, my_gen, ECOMM); /* prevent stale slot matching */
WRITE_ONCE(so->tx_gen, my_gen);
- smp_wmb(); /* see smp_load_acquire() in isotp_tx_[timeout|gen_done] */
- WRITE_ONCE(so->tx.state, ISOTP_SENDING);
+ /* Pairs with smp_load_acquire() in isotp_tx_[timeout|gen_done] */
+ smp_store_release(&so->tx.state, ISOTP_SENDING);
WRITE_ONCE(so->cfecho, 0);
spin_unlock_bh(&so->rx_lock);
--
2.34.1
^ permalink raw reply related [flat|nested] 15+ messages in thread
* Re: [PATCH 11/11] can: isotp: publish tx.state with smp_store_release()
2026-08-25 9:54 ` [PATCH 11/11] can: isotp: publish tx.state with smp_store_release() Jinjie Ruan
@ 2026-08-25 11:46 ` Oliver Hartkopp
2026-08-26 3:33 ` Jinjie Ruan
0 siblings, 1 reply; 15+ messages in thread
From: Oliver Hartkopp @ 2026-08-25 11:46 UTC (permalink / raw)
To: Jinjie Ruan, viro, brauner, jack, bcrl, tytso, adilger.kernel,
libaokun, ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, mkl, kuniyu, willemb, jhs, jiri, kees,
cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
On 25.08.26 11:54, Jinjie Ruan wrote:
> The writer already pairs with the smp_load_acquire() readers
> in isotp_tx_timeout()/isotp_tx_gen_done(); convert
> the smp_wmb() + WRITE_ONCE() into a release store.
>
> Assisted-by: DeepSeek:DeepSeek-V3
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> ---
> net/can/isotp.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/net/can/isotp.c b/net/can/isotp.c
> index 155530aedce2..11b653ba7c10 100644
> --- a/net/can/isotp.c
> +++ b/net/can/isotp.c
> @@ -1156,8 +1156,8 @@ static int isotp_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
> my_gen = isotp_inc_tx_gen(READ_ONCE(so->tx_gen));
> isotp_set_tx_result(so, my_gen, ECOMM); /* prevent stale slot matching */
> WRITE_ONCE(so->tx_gen, my_gen);
> - smp_wmb(); /* see smp_load_acquire() in isotp_tx_[timeout|gen_done] */
> - WRITE_ONCE(so->tx.state, ISOTP_SENDING);
> + /* Pairs with smp_load_acquire() in isotp_tx_[timeout|gen_done] */
> + smp_store_release(&so->tx.state, ISOTP_SENDING);
> WRITE_ONCE(so->cfecho, 0);
> spin_unlock_bh(&so->rx_lock);
>
Hi Jinjie,
thank you for the patch, but I think this breaks the barrier logic.
The original smp_wmb() ensures that so->tx_gen is visible before both
subsequent writes (so->tx.state and so->cfecho).
By converting only the first write into smp_store_release(), the
WRITE_ONCE(so->cfecho, 0) is no longer protected. The compiler or CPU
could reorder and execute the cfecho write before the release store of
so->tx.state, introducing a race condition with the concurrent readers.
Best regards,
Oliver
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 11/11] can: isotp: publish tx.state with smp_store_release()
2026-08-25 11:46 ` Oliver Hartkopp
@ 2026-08-26 3:33 ` Jinjie Ruan
2026-08-26 8:04 ` Oliver Hartkopp
0 siblings, 1 reply; 15+ messages in thread
From: Jinjie Ruan @ 2026-08-26 3:33 UTC (permalink / raw)
To: Oliver Hartkopp, viro, brauner, jack, bcrl, tytso, adilger.kernel,
libaokun, ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, mkl, kuniyu, willemb, jhs, jiri, kees,
cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
在 2026/8/25 19:46, Oliver Hartkopp 写道:
>
>
> On 25.08.26 11:54, Jinjie Ruan wrote:
>> The writer already pairs with the smp_load_acquire() readers
>> in isotp_tx_timeout()/isotp_tx_gen_done(); convert
>> the smp_wmb() + WRITE_ONCE() into a release store.
>>
>> Assisted-by: DeepSeek:DeepSeek-V3
>> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
>> ---
>> net/can/isotp.c | 4 ++--
>> 1 file changed, 2 insertions(+), 2 deletions(-)
>>
>> diff --git a/net/can/isotp.c b/net/can/isotp.c
>> index 155530aedce2..11b653ba7c10 100644
>> --- a/net/can/isotp.c
>> +++ b/net/can/isotp.c
>> @@ -1156,8 +1156,8 @@ static int isotp_sendmsg(struct socket *sock,
>> struct msghdr *msg, size_t size)
>> my_gen = isotp_inc_tx_gen(READ_ONCE(so->tx_gen));
>> isotp_set_tx_result(so, my_gen, ECOMM); /* prevent stale slot
>> matching */
>> WRITE_ONCE(so->tx_gen, my_gen);
>> - smp_wmb(); /* see smp_load_acquire() in isotp_tx_[timeout|
>> gen_done] */
>> - WRITE_ONCE(so->tx.state, ISOTP_SENDING);
>> + /* Pairs with smp_load_acquire() in isotp_tx_[timeout|gen_done] */
>> + smp_store_release(&so->tx.state, ISOTP_SENDING);
>> WRITE_ONCE(so->cfecho, 0);
>> spin_unlock_bh(&so->rx_lock);
>>
>
> Hi Jinjie,
>
Hi Oliver,
> thank you for the patch, but I think this breaks the barrier logic.
> The original smp_wmb() ensures that so->tx_gen is visible before both
> subsequent writes (so->tx.state and so->cfecho).
Right!
>
> By converting only the first write into smp_store_release(), the
> WRITE_ONCE(so->cfecho, 0) is no longer protected. The compiler or CPU
> could reorder and execute the cfecho write before the release store of
Indeed, that's true.
> so->tx.state, introducing a race condition with the concurrent readers.
My rough understanding is as follows:
All lock-free readers fall into two disjoint sets:
- `isotp_tx_timeout()` and `isotp_tx_gen_done()`: read only `tx.state`
(acquire) and `tx_gen`
- `isotp_txfr_timer_handler()` the timer path of `isotp_send_cframe()`,
and the post-claim path of `isotp_sendmsg()` touch `cfecho` but never
`tx_gen`.
So no lock-free reader observes both `tx_gen` and `cfecho`.
`isotp_rcv_echo()` is the only function reading both, and it runs under
`so->rx_lock`, which serializes it with the claim.
So the ordering the `smp_wmb()` provided on top of the new release store
`tx_gen` before `cfecho` — is unobservable to every reader.
Moreover, `tx.state` and `cfecho` were never ordered against each other
by the original barrier: both followed the `smp_wmb()`, so the `(state,
cfecho)` visibility seen by the lock-free timer readers is bit-for-bit
identical before and after this change.
So the release store preserves the one ordering that matters: a reader
observing `ISOTP_SENDING` sees the new `tx_gen`.
Best regards,
Jinjie
>
> Best regards,
> Oliver
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 11/11] can: isotp: publish tx.state with smp_store_release()
2026-08-26 3:33 ` Jinjie Ruan
@ 2026-08-26 8:04 ` Oliver Hartkopp
0 siblings, 0 replies; 15+ messages in thread
From: Oliver Hartkopp @ 2026-08-26 8:04 UTC (permalink / raw)
To: Jinjie Ruan, viro, brauner, jack, bcrl, tytso, adilger.kernel,
libaokun, ojaswin, ritesh.list, yi.zhang, pmladek, rostedt,
andriy.shevchenko, linux, senozhatsky, akpm, davem, edumazet,
kuba, pabeni, horms, mkl, kuniyu, willemb, jhs, jiri, kees,
cyphar, tglx, liuhangbin, sdf, nb, linux-fsdevel, linux-aio,
linux-kernel, linux-ext4, netdev, linux-can
On 26.08.26 05:33, Jinjie Ruan wrote:
>
>
> 在 2026/8/25 19:46, Oliver Hartkopp 写道:
>>
>>
>> On 25.08.26 11:54, Jinjie Ruan wrote:
>>> The writer already pairs with the smp_load_acquire() readers
>>> in isotp_tx_timeout()/isotp_tx_gen_done(); convert
>>> the smp_wmb() + WRITE_ONCE() into a release store.
>>>
>>> Assisted-by: DeepSeek:DeepSeek-V3
>>> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
>>> ---
>>> net/can/isotp.c | 4 ++--
>>> 1 file changed, 2 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/net/can/isotp.c b/net/can/isotp.c
>>> index 155530aedce2..11b653ba7c10 100644
>>> --- a/net/can/isotp.c
>>> +++ b/net/can/isotp.c
>>> @@ -1156,8 +1156,8 @@ static int isotp_sendmsg(struct socket *sock,
>>> struct msghdr *msg, size_t size)
>>> my_gen = isotp_inc_tx_gen(READ_ONCE(so->tx_gen));
>>> isotp_set_tx_result(so, my_gen, ECOMM); /* prevent stale slot
>>> matching */
>>> WRITE_ONCE(so->tx_gen, my_gen);
>>> - smp_wmb(); /* see smp_load_acquire() in isotp_tx_[timeout|
>>> gen_done] */
>>> - WRITE_ONCE(so->tx.state, ISOTP_SENDING);
>>> + /* Pairs with smp_load_acquire() in isotp_tx_[timeout|gen_done] */
>>> + smp_store_release(&so->tx.state, ISOTP_SENDING);
>>> WRITE_ONCE(so->cfecho, 0);
>>> spin_unlock_bh(&so->rx_lock);
>>>
>>
>> Hi Jinjie,
>>
>
> Hi Oliver,
>
>> thank you for the patch, but I think this breaks the barrier logic.
>> The original smp_wmb() ensures that so->tx_gen is visible before both
>> subsequent writes (so->tx.state and so->cfecho).
>
> Right!
>
>>
>> By converting only the first write into smp_store_release(), the
>> WRITE_ONCE(so->cfecho, 0) is no longer protected. The compiler or CPU
>> could reorder and execute the cfecho write before the release store of
>
> Indeed, that's true.
>
>> so->tx.state, introducing a race condition with the concurrent readers.
>
> My rough understanding is as follows:
>
> All lock-free readers fall into two disjoint sets:
>
> - `isotp_tx_timeout()` and `isotp_tx_gen_done()`: read only `tx.state`
> (acquire) and `tx_gen`
>
> - `isotp_txfr_timer_handler()` the timer path of `isotp_send_cframe()`,
> and the post-claim path of `isotp_sendmsg()` touch `cfecho` but never
> `tx_gen`.
>
> So no lock-free reader observes both `tx_gen` and `cfecho`.
>
> `isotp_rcv_echo()` is the only function reading both, and it runs under
> `so->rx_lock`, which serializes it with the claim.
>
> So the ordering the `smp_wmb()` provided on top of the new release store
> `tx_gen` before `cfecho` — is unobservable to every reader.
>
> Moreover, `tx.state` and `cfecho` were never ordered against each other
> by the original barrier: both followed the `smp_wmb()`, so the `(state,
> cfecho)` visibility seen by the lock-free timer readers is bit-for-bit
> identical before and after this change.
>
> So the release store preserves the one ordering that matters: a reader
> observing `ISOTP_SENDING` sees the new `tx_gen`.
>
> Best regards,
> Jinjie
Thanks for your explanation, but this assumption is too narrow and
misses how so->cfecho interacts with the rest of the ISO-TP state
machine, especially under concurrent TX and RX traffic.
Even if isotp_tx_timeout() and isotp_tx_gen_done() don't read cfecho,
other functions do. For example, cfecho is heavily involved in the RX
path—such as in isotp_rcv_cf()—where incoming Consecutive Frames are
validated against the current transmission state.
By converting the code to smp_store_release(), you only guarantee that
the write to so->tx_gen happens before so->tx.state. However, you
completely lose the ordering guarantee for WRITE_ONCE(so->cfecho, 0).
The compiler or CPU is now free to reorder and execute the cfecho clear
before the smp_store_release().
If a concurrent CAN frame arrives and triggers the RX path exactly at
this microsecond, it could see an updated, cleared cfecho value while
tx.state is still in its old state, or vice versa. This breaks the
atomicity of the state transition in isotp_sendmsg().
The original smp_wmb() acts as a clear fence: it ensures that both
subsequent writes (tx.state and cfecho) become visible to all concurrent
readers strictly after the new tx_gen generation is visible.
We cannot weaken this guarantee. It makes the lockless design extremely
fragile and prone to hard-to-debug race conditions.
Therefore:
Nacked-by: Oliver Hartkopp socketcan@hartkopp.net
Best regards,
Oliver
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-08-26 8:04 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-25 9:54 [PATCH 00/11] Convert barrier pairs to acquire/release for better performance Jinjie Ruan
2026-08-25 9:54 ` [PATCH 01/11] user_namespace: Use acquire/release for nr_extents synchronization Jinjie Ruan
2026-08-25 9:54 ` [PATCH 02/11] lib/vsprintf: Use acquire/release for ptr_key publication Jinjie Ruan
2026-08-25 9:54 ` [PATCH 03/11] fs: aio: Use acquire/release for ring->tail publication Jinjie Ruan
2026-08-25 9:54 ` [PATCH 04/11] fs: Use acquire/release for fdtable resize synchronization Jinjie Ruan
2026-08-25 9:54 ` [PATCH 05/11] pidfs: Use test_bit_acquire() for attr flag tests Jinjie Ruan
2026-08-25 9:54 ` [PATCH 06/11] super: Use acquire for SB_BORN check in super_cache_count() Jinjie Ruan
2026-08-25 9:54 ` [PATCH 07/11] ext4: Convert group-count barrier protocol to acquire/release Jinjie Ruan
2026-08-25 9:54 ` [PATCH 08/11] soreuseport: publish num_socks with acquire/release Jinjie Ruan
2026-08-25 9:54 ` [PATCH 09/11] net: sched: act_gact: use acquire/release for tcfg_ptype Jinjie Ruan
2026-08-25 9:54 ` [PATCH 10/11] 8021q: publish vlan_devices_arrays entries with acquire/release Jinjie Ruan
2026-08-25 9:54 ` [PATCH 11/11] can: isotp: publish tx.state with smp_store_release() Jinjie Ruan
2026-08-25 11:46 ` Oliver Hartkopp
2026-08-26 3:33 ` Jinjie Ruan
2026-08-26 8:04 ` Oliver Hartkopp
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox