Memory barriers are building blocks for concurrent code, hence provide
a minimal set of them.
The compiler barrier, barrier(), is implemented in inline asm instead of
using core::sync::atomic::compiler_fence() because memory models are
different: kernel's atomics are implemented in inline asm therefore the
compiler barrier should be implemented in inline asm as well. Also it's
currently only public to the kernel crate until there's a reasonable
driver usage.
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
---
rust/helpers/barrier.c | 18 ++++++++++
rust/helpers/helpers.c | 1 +
rust/kernel/sync.rs | 1 +
rust/kernel/sync/barrier.rs | 67 +++++++++++++++++++++++++++++++++++++
4 files changed, 87 insertions(+)
create mode 100644 rust/helpers/barrier.c
create mode 100644 rust/kernel/sync/barrier.rs
diff --git a/rust/helpers/barrier.c b/rust/helpers/barrier.c
new file mode 100644
index 000000000000..cdf28ce8e511
--- /dev/null
+++ b/rust/helpers/barrier.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <asm/barrier.h>
+
+void rust_helper_smp_mb(void)
+{
+ smp_mb();
+}
+
+void rust_helper_smp_wmb(void)
+{
+ smp_wmb();
+}
+
+void rust_helper_smp_rmb(void)
+{
+ smp_rmb();
+}
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 83e89f6a68fb..8ddfc8f84e87 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -9,6 +9,7 @@
#include "atomic.c"
#include "auxiliary.c"
+#include "barrier.c"
#include "blk.c"
#include "bug.c"
#include "build_assert.c"
diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
index b620027e0641..c7c0e552bafe 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -11,6 +11,7 @@
mod arc;
pub mod atomic;
+pub mod barrier;
mod condvar;
pub mod lock;
mod locked_by;
diff --git a/rust/kernel/sync/barrier.rs b/rust/kernel/sync/barrier.rs
new file mode 100644
index 000000000000..36a5c70e6716
--- /dev/null
+++ b/rust/kernel/sync/barrier.rs
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Memory barriers.
+//!
+//! These primitives have the same semantics as their C counterparts: and the precise definitions of
+//! semantics can be found at [`LKMM`].
+//!
+//! [`LKMM`]: srctree/tools/memory-mode/
+
+/// A compiler barrier.
+///
+/// An explicic compiler barrier function that prevents the compiler from moving the memory
+/// accesses either side of it to the other side.
+pub(crate) fn barrier() {
+ // By default, Rust inline asms are treated as being able to access any memory or flags, hence
+ // it suffices as a compiler barrier.
+ //
+ // SAFETY: An empty asm block should be safe.
+ unsafe {
+ core::arch::asm!("");
+ }
+}
+
+/// A full memory barrier.
+///
+/// A barrier function that prevents both the compiler and the CPU from moving the memory accesses
+/// either side of it to the other side.
+pub fn smp_mb() {
+ if cfg!(CONFIG_SMP) {
+ // SAFETY: `smp_mb()` is safe to call.
+ unsafe {
+ bindings::smp_mb();
+ }
+ } else {
+ barrier();
+ }
+}
+
+/// A write-write memory barrier.
+///
+/// A barrier function that prevents both the compiler and the CPU from moving the memory write
+/// accesses either side of it to the other side.
+pub fn smp_wmb() {
+ if cfg!(CONFIG_SMP) {
+ // SAFETY: `smp_wmb()` is safe to call.
+ unsafe {
+ bindings::smp_wmb();
+ }
+ } else {
+ barrier();
+ }
+}
+
+/// A read-read memory barrier.
+///
+/// A barrier function that prevents both the compiler and the CPU from moving the memory read
+/// accesses either side of it to the other side.
+pub fn smp_rmb() {
+ if cfg!(CONFIG_SMP) {
+ // SAFETY: `smp_rmb()` is safe to call.
+ unsafe {
+ bindings::smp_rmb();
+ }
+ } else {
+ barrier();
+ }
+}
--
2.39.5 (Apple Git-154)
"Boqun Feng" <boqun.feng@gmail.com> writes: > Memory barriers are building blocks for concurrent code, hence provide > a minimal set of them. > > The compiler barrier, barrier(), is implemented in inline asm instead of > using core::sync::atomic::compiler_fence() because memory models are > different: kernel's atomics are implemented in inline asm therefore the > compiler barrier should be implemented in inline asm as well. Also it's > currently only public to the kernel crate until there's a reasonable > driver usage. > > Signed-off-by: Boqun Feng <boqun.feng@gmail.com> > --- > rust/helpers/barrier.c | 18 ++++++++++ > rust/helpers/helpers.c | 1 + > rust/kernel/sync.rs | 1 + > rust/kernel/sync/barrier.rs | 67 +++++++++++++++++++++++++++++++++++++ > 4 files changed, 87 insertions(+) > create mode 100644 rust/helpers/barrier.c > create mode 100644 rust/kernel/sync/barrier.rs > > diff --git a/rust/helpers/barrier.c b/rust/helpers/barrier.c > new file mode 100644 > index 000000000000..cdf28ce8e511 > --- /dev/null > +++ b/rust/helpers/barrier.c > @@ -0,0 +1,18 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include <asm/barrier.h> > + > +void rust_helper_smp_mb(void) > +{ > + smp_mb(); > +} > + > +void rust_helper_smp_wmb(void) > +{ > + smp_wmb(); > +} > + > +void rust_helper_smp_rmb(void) > +{ > + smp_rmb(); > +} > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c > index 83e89f6a68fb..8ddfc8f84e87 100644 > --- a/rust/helpers/helpers.c > +++ b/rust/helpers/helpers.c > @@ -9,6 +9,7 @@ > > #include "atomic.c" > #include "auxiliary.c" > +#include "barrier.c" > #include "blk.c" > #include "bug.c" > #include "build_assert.c" > diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs > index b620027e0641..c7c0e552bafe 100644 > --- a/rust/kernel/sync.rs > +++ b/rust/kernel/sync.rs > @@ -11,6 +11,7 @@ > > mod arc; > pub mod atomic; > +pub mod barrier; > mod condvar; > pub mod lock; > mod locked_by; > diff --git a/rust/kernel/sync/barrier.rs b/rust/kernel/sync/barrier.rs > new file mode 100644 > index 000000000000..36a5c70e6716 > --- /dev/null > +++ b/rust/kernel/sync/barrier.rs > @@ -0,0 +1,67 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +//! Memory barriers. > +//! > +//! These primitives have the same semantics as their C counterparts: and the precise definitions of > +//! semantics can be found at [`LKMM`]. > +//! > +//! [`LKMM`]: srctree/tools/memory-mode/ Typo in link target. > + > +/// A compiler barrier. > +/// > +/// An explicic compiler barrier function that prevents the compiler from moving the memory > +/// accesses either side of it to the other side. Typo in "explicit". How about: A compiler barrier. Prevents the compiler from reordering memory access instructions across the barrier. > +pub(crate) fn barrier() { > + // By default, Rust inline asms are treated as being able to access any memory or flags, hence > + // it suffices as a compiler barrier. > + // > + // SAFETY: An empty asm block should be safe. > + unsafe { > + core::arch::asm!(""); > + } > +} > + > +/// A full memory barrier. > +/// > +/// A barrier function that prevents both the compiler and the CPU from moving the memory accesses > +/// either side of it to the other side. A barrier that prevents compiler and CPU from reordering memory access instructions across the barrier. > +pub fn smp_mb() { > + if cfg!(CONFIG_SMP) { > + // SAFETY: `smp_mb()` is safe to call. > + unsafe { > + bindings::smp_mb(); > + } > + } else { > + barrier(); > + } > +} > + > +/// A write-write memory barrier. > +/// > +/// A barrier function that prevents both the compiler and the CPU from moving the memory write > +/// accesses either side of it to the other side. A barrier that prevents compiler and CPU from reordering memory write instructions across the barrier. > +pub fn smp_wmb() { > + if cfg!(CONFIG_SMP) { > + // SAFETY: `smp_wmb()` is safe to call. > + unsafe { > + bindings::smp_wmb(); > + } > + } else { > + barrier(); > + } > +} > + > +/// A read-read memory barrier. > +/// > +/// A barrier function that prevents both the compiler and the CPU from moving the memory read > +/// accesses either side of it to the other side. A barrier that prevents compiler and CPU from reordering memory read instructions across the barrier. > +pub fn smp_rmb() { > + if cfg!(CONFIG_SMP) { > + // SAFETY: `smp_rmb()` is safe to call. > + unsafe { > + bindings::smp_rmb(); > + } > + } else { > + barrier(); > + } > +} Best regards, Andreas Hindborg
On Thu, Jun 26, 2025 at 03:36:25PM +0200, Andreas Hindborg wrote: > "Boqun Feng" <boqun.feng@gmail.com> writes: [...] > > +//! [`LKMM`]: srctree/tools/memory-mode/ > > Typo in link target. > > > + > > +/// A compiler barrier. > > +/// > > +/// An explicic compiler barrier function that prevents the compiler from moving the memory > > +/// accesses either side of it to the other side. > > Typo in "explicit". > Fixed. > How about: > > A compiler barrier. Prevents the compiler from reordering > memory access instructions across the barrier. > > > > +pub(crate) fn barrier() { > > + // By default, Rust inline asms are treated as being able to access any memory or flags, hence > > + // it suffices as a compiler barrier. > > + // > > + // SAFETY: An empty asm block should be safe. > > + unsafe { > > + core::arch::asm!(""); > > + } > > +} > > + > > +/// A full memory barrier. > > +/// > > +/// A barrier function that prevents both the compiler and the CPU from moving the memory accesses > > +/// either side of it to the other side. > > > A barrier that prevents compiler and CPU from reordering memory access > instructions across the barrier. > > > +pub fn smp_mb() { > > + if cfg!(CONFIG_SMP) { > > + // SAFETY: `smp_mb()` is safe to call. > > + unsafe { > > + bindings::smp_mb(); > > + } > > + } else { > > + barrier(); > > + } > > +} > > + > > +/// A write-write memory barrier. > > +/// > > +/// A barrier function that prevents both the compiler and the CPU from moving the memory write > > +/// accesses either side of it to the other side. > > A barrier that prevents compiler and CPU from reordering memory write > instructions across the barrier. > > > +pub fn smp_wmb() { > > + if cfg!(CONFIG_SMP) { > > + // SAFETY: `smp_wmb()` is safe to call. > > + unsafe { > > + bindings::smp_wmb(); > > + } > > + } else { > > + barrier(); > > + } > > +} > > + > > +/// A read-read memory barrier. > > +/// > > +/// A barrier function that prevents both the compiler and the CPU from moving the memory read > > +/// accesses either side of it to the other side. > > A barrier that prevents compiler and CPU from reordering memory read > instructions across the barrier. > These are good wording, except that I will use "memory (read/write) accesses" instead of "memory (read/write) instructions" because: 1) "instructions" are at lower level than the language, and memory barriers function are provided as synchonization primitives, so I feel we should describe memory barrier effects at language level, i.e. mention how it would interact with objects and accesses to them. 2) There are instructions can do read and write in one instruction, it might be unclear when we say "prevents reordering an instruction" whether both parts are included, for example: r1 = atomic_add(x, 1); // <- this can be one instruction. smp_rmb(); r2 = atomic_read(y); people may think because the smp_rmb() prevents read instructions reordering, and atomic_add() is one instruction in this case, smp_rmb() can prevents the write part of that instruction from reordering, but that's not the case. So I will do: A barrier that prevents compiler and CPU from reordering memory read accesses across the barrier. Regards, Boqun > > +pub fn smp_rmb() { > > + if cfg!(CONFIG_SMP) { > > + // SAFETY: `smp_rmb()` is safe to call. > > + unsafe { > > + bindings::smp_rmb(); > > + } > > + } else { > > + barrier(); > > + } > > +} > > > Best regards, > Andreas Hindborg > >
"Boqun Feng" <boqun.feng@gmail.com> writes: > On Thu, Jun 26, 2025 at 03:36:25PM +0200, Andreas Hindborg wrote: >> "Boqun Feng" <boqun.feng@gmail.com> writes: > [...] >> > +//! [`LKMM`]: srctree/tools/memory-mode/ >> >> Typo in link target. >> >> > + >> > +/// A compiler barrier. >> > +/// >> > +/// An explicic compiler barrier function that prevents the compiler from moving the memory >> > +/// accesses either side of it to the other side. >> >> Typo in "explicit". >> > > Fixed. > >> How about: >> >> A compiler barrier. Prevents the compiler from reordering >> memory access instructions across the barrier. >> >> >> > +pub(crate) fn barrier() { >> > + // By default, Rust inline asms are treated as being able to access any memory or flags, hence >> > + // it suffices as a compiler barrier. >> > + // >> > + // SAFETY: An empty asm block should be safe. >> > + unsafe { >> > + core::arch::asm!(""); >> > + } >> > +} >> > + >> > +/// A full memory barrier. >> > +/// >> > +/// A barrier function that prevents both the compiler and the CPU from moving the memory accesses >> > +/// either side of it to the other side. >> >> >> A barrier that prevents compiler and CPU from reordering memory access >> instructions across the barrier. >> >> > +pub fn smp_mb() { >> > + if cfg!(CONFIG_SMP) { >> > + // SAFETY: `smp_mb()` is safe to call. >> > + unsafe { >> > + bindings::smp_mb(); >> > + } >> > + } else { >> > + barrier(); >> > + } >> > +} >> > + >> > +/// A write-write memory barrier. >> > +/// >> > +/// A barrier function that prevents both the compiler and the CPU from moving the memory write >> > +/// accesses either side of it to the other side. >> >> A barrier that prevents compiler and CPU from reordering memory write >> instructions across the barrier. >> >> > +pub fn smp_wmb() { >> > + if cfg!(CONFIG_SMP) { >> > + // SAFETY: `smp_wmb()` is safe to call. >> > + unsafe { >> > + bindings::smp_wmb(); >> > + } >> > + } else { >> > + barrier(); >> > + } >> > +} >> > + >> > +/// A read-read memory barrier. >> > +/// >> > +/// A barrier function that prevents both the compiler and the CPU from moving the memory read >> > +/// accesses either side of it to the other side. >> >> A barrier that prevents compiler and CPU from reordering memory read >> instructions across the barrier. >> > > These are good wording, except that I will use "memory (read/write) > accesses" instead of "memory (read/write) instructions" because: > > 1) "instructions" are at lower level than the language, and memory > barriers function are provided as synchonization primitives, so I > feel we should describe memory barrier effects at language level, > i.e. mention how it would interact with objects and accesses to them. > > 2) There are instructions can do read and write in one instruction, it > might be unclear when we say "prevents reordering an instruction" > whether both parts are included, for example: > > r1 = atomic_add(x, 1); // <- this can be one instruction. > smp_rmb(); > r2 = atomic_read(y); > > people may think because the smp_rmb() prevents read instructions > reordering, and atomic_add() is one instruction in this case, > smp_rmb() can prevents the write part of that instruction from > reordering, but that's not the case. > > > So I will do: > > A barrier that prevents compiler and CPU from reordering memory read > accesses across the barrier. Right, that makes sense 👍 Best regards, Andreas Hindborg
© 2016 - 2025 Red Hat, Inc.