// 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-model/ #![expect(private_bounds, reason = "sealed implementation")] /// Memory barrier orderings. /// /// The semantics of these orderings follows the [`LKMM`] definitions and rules. /// /// - [`Read`] provides ordering between preceding load operations and succeeding load operations. /// - [`Write`] provides ordering between preceding store operations and succeeding store /// operations. /// - [`Full`] provides ordering between all the preceding memory accesses and succeeding memory /// accesses. /// /// [`LKMM`]: srctree/tools/memory-model/ pub mod ordering { pub use crate::sync::atomic::ordering::Full; /// The annotation type for read-read barrier ordering. pub struct Read; /// The annotation type for write-write barrier ordering. pub struct Write; } pub use ordering::{ Full, Read, Write, // }; struct Smp; struct Dma; /// A compiler barrier. /// /// A barrier that prevents compiler from reordering memory accesses across the barrier. #[inline(always)] 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. unsafe { core::arch::asm!("") }; } trait MemoryBarrier { fn run(); } macro_rules! define_barrier { ($([$flavour:ident])? $ordering:ident, $binding:ident) => { impl MemoryBarrier$(<$flavour>)? for $ordering { #[inline] fn run() { // SAFETY: barrier methods are safe to call. unsafe { bindings::$binding() }; } } }; } define_barrier!(Full, mb); define_barrier!(Read, rmb); define_barrier!(Write, wmb); define_barrier!([Dma] Full, dma_mb); define_barrier!([Dma] Read, dma_rmb); define_barrier!([Dma] Write, dma_wmb); define_barrier!([Smp] Full, smp_mb); define_barrier!([Smp] Read, smp_rmb); define_barrier!([Smp] Write, smp_wmb); /// Memory barrier. /// /// A barrier that prevents compiler and CPU from reordering memory accesses across the barrier. /// /// The specific forms of reordering can be specified using the parameter. /// - `mb(Read)` provides a read-read barrier. /// - `mb(Write)` provides a write-write barrier. /// - `mb(Full)` provides a full barrier. /// /// # Examples /// /// ``` /// # use kernel::sync::barrier::*; /// mb(Read); /// mb(Write); /// mb(Full); /// ``` #[inline] #[doc(alias = "rmb")] #[doc(alias = "wmb")] pub fn mb(_: T) { T::run() } /// Memory barrier between CPUs. /// /// A barrier that prevents compiler and CPU from reordering memory accesses across the barrier. /// Does not prevent re-ordering with respect to other bus-mastering devices. /// /// See [`mb`] for usage. #[inline] #[doc(alias = "smp_rmb")] #[doc(alias = "smp_wmb")] pub fn smp_mb>(_: T) { if cfg!(CONFIG_SMP) { T::run() } else { barrier() } } /// Memory barrier between local CPU and bus-mastering devices. /// /// A barrier that prevents compiler and CPU from reordering memory accesses across the barrier. /// Does not prevent re-ordering with respect to other CPUs. /// /// See [`mb`] for usage. #[inline] #[doc(alias = "dma_rmb")] #[doc(alias = "dma_wmb")] pub fn dma_mb>(_: T) { T::run() }