| // 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<Flavour = ()> { |
| 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: MemoryBarrier>(_: 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: MemoryBarrier<Smp>>(_: 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: MemoryBarrier<Dma>>(_: T) { |
| T::run() |
| } |