[PATCH v7 6/9] rust: sync: atomic: Add the framework of arithmetic operations
Boqun Feng <[email protected]> Sun, 13 Jul 2025 22:36:53 -0700
| Newsgroups | dev.linux.lists.lkmm,org.kernel.vger.linux-arch,org.kernel.vger.linux-kernel,org.kernel.vger.rust-for-linux |
|---|---|
| Message-ID | <[email protected]> |
One important set of atomic operations is the arithmetic operations, i.e. add(), sub(), fetch_add(), add_return(), etc. However it may not make senses for all the types that `AllowAtomic` to have arithmetic operations, for example a `Foo(u32)` may not have a reasonable add() or sub(), plus subword types (`u8` and `u16`) currently don't have atomic arithmetic operations even on C side and might not have them in the future in Rust (because they are usually suboptimal on a few architecures). Therefore the plan is to add a few subtraits of `AllowAtomic` describing which types have and can do atomic arithemtic operations. One trait `AllowAtomicAdd` is added, and only add() and fetch_add() are added. The rest will be added in the future. Reviewed-by: Alice Ryhl <[email protected]> Signed-off-by: Boqun Feng <[email protected]> --- rust/kernel/sync/atomic.rs | 14 ++++ rust/kernel/sync/atomic/generic.rs | 111 ++++++++++++++++++++++++++++- 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/rust/kernel/sync/atomic.rs b/rust/kernel/sync/atomic.rs index c5193c1c90fe..54f5b4618337 100644 --- a/rust/kernel/sync/atomic.rs +++ b/rust/kernel/sync/atomic.rs @@ -29,8 +29,22 @@ unsafe impl generic::AllowAtomic for i32 { type Repr = i32; } +// SAFETY: The wrapping add result of two `i32`s is a valid `i32`. +unsafe impl generic::AllowAtomicAdd<i32> for i32 { + fn rhs_into_delta(rhs: i32) -> i32 { + rhs + } +} + // SAFETY: `i64` has the same size and alignment with itself, and is round-trip transmutable to // itself. unsafe impl generic::AllowAtomic for i64 { type Repr = i64; } + +// SAFETY: The wrapping add result of two `i64`s is a valid `i64`. +unsafe impl generic::AllowAtomicAdd<i64> for i64 { + fn rhs_into_delta(rhs: i64) -> i64 { + rhs + } +} diff --git a/rust/kernel/sync/atomic/generic.rs b/rust/kernel/sync/atomic/generic.rs index 4e45d594d8ef..9e2394017202 100644 --- a/rust/kernel/sync/atomic/generic.rs +++ b/rust/kernel/sync/atomic/generic.rs @@ -2,7 +2,7 @@ //! Generic atomic primitives. -use super::ops::{AtomicHasBasicOps, AtomicHasXchgOps, AtomicImpl}; +use super::ops::{AtomicHasArithmeticOps, AtomicHasBasicOps, AtomicHasXchgOps, AtomicImpl}; use super::{ordering, ordering::OrderingType}; use crate::build_error; use core::cell::UnsafeCell; @@ -104,6 +104,18 @@ const fn into_repr<T: AllowAtomic>(v: T) -> T::Repr { unsafe { core::mem::transmute_copy(&r) } } +/// Types that support atomic add operations. +/// +/// # Safety +/// +/// Wrapping adding any value of type `Self::Repr::Delta` obtained by [`Self::rhs_into_delta()`] to +/// any value of type `Self::Repr` obtained through transmuting a value of type `Self` to must +/// yield a value with a bit pattern also valid for `Self`. +pub unsafe trait AllowAtomicAdd<Rhs = Self>: AllowAtomic { + /// Converts `Rhs` into the `Delta` type of the atomic implementation. + fn rhs_into_delta(rhs: Rhs) -> <Self::Repr as AtomicImpl>::Delta; +} + impl<T: AllowAtomic> Atomic<T> { /// Creates a new atomic `T`. pub const fn new(v: T) -> Self { @@ -462,3 +474,100 @@ fn try_cmpxchg<Ordering: ordering::Any>(&self, old: &mut T, new: T, _: Ordering) ret } } + +impl<T: AllowAtomic> Atomic<T> +where + T::Repr: AtomicHasArithmeticOps, +{ + /// Atomic add. + /// + /// Atomically updates `*self` to `(*self).wrapping_add(v)`. + /// + /// # Examples + /// + /// ``` + /// use kernel::sync::atomic::{Atomic, Relaxed}; + /// + /// let x = Atomic::new(42); + /// + /// assert_eq!(42, x.load(Relaxed)); + /// + /// x.add(12, Relaxed); + /// + /// assert_eq!(54, x.load(Relaxed)); + /// ``` + #[inline(always)] + pub fn add<Rhs, Ordering: ordering::RelaxedOnly>(&self, v: Rhs, _: Ordering) + where + T: AllowAtomicAdd<Rhs>, + { + let v = T::rhs_into_delta(v); + // CAST: Per the safety requirement of `AllowAtomic`, a valid pointer of `T` is a valid + // pointer of `T::Repr` for reads and valid for writes of values transmutable to `T`. + let a = self.as_ptr().cast::<T::Repr>(); + + // `*self` remains valid after `atomic_add()` because of the safety requirement of + // `AllowAtomicAdd`. + // + // SAFETY: + // - `a` is aligned to `align_of::<T::Repr>()` because of the safety requirement of + // `AllowAtomic` and the guarantee of `Atomic::as_ptr()`. + // - `a` is a valid pointer per the CAST justification above. + unsafe { + T::Repr::atomic_add(a, v); + } + } + + /// Atomic fetch and add. + /// + /// Atomically updates `*self` to `(*self).wrapping_add(v)`, and returns the value of `*self` + /// before the update. + /// + /// # Examples + /// + /// ``` + /// use kernel::sync::atomic::{Atomic, Acquire, Full, Relaxed}; + /// + /// let x = Atomic::new(42); + /// + /// assert_eq!(42, x.load(Relaxed)); + /// + /// assert_eq!(54, { x.fetch_add(12, Acquire); x.load(Relaxed) }); + /// + /// let x = Atomic::new(42); + /// + /// assert_eq!(42, x.load(Relaxed)); + /// + /// assert_eq!(54, { x.fetch_add(12, Full); x.load(Relaxed) } ); + /// ``` + #[inline(always)] + pub fn fetch_add<Rhs, Ordering: ordering::Any>(&self, v: Rhs, _: Ordering) -> T + where + T: AllowAtomicAdd<Rhs>, + { + let v = T::rhs_into_delta(v); + // CAST: Per the safety requirement of `AllowAtomic`, a valid pointer of `T` is a valid + // pointer of `T::Repr` for reads and valid for writes of values transmutable to `T`. + let a = self.as_ptr().cast::<T::Repr>(); + + // `*self` remains valid after `atomic_fetch_add*()` because of the safety requirement of + // `AllowAtomicAdd`. + // + // SAFETY: + // - `a` is aligned to `align_of::<T::Repr>()` because of the safety requirement of + // `AllowAtomic` and the guarantee of `Atomic::as_ptr()`. + // - `a` is a valid pointer per the CAST justification above. + let ret = unsafe { + match Ordering::TYPE { + OrderingType::Full => T::Repr::atomic_fetch_add(a, v), + OrderingType::Acquire => T::Repr::atomic_fetch_add_acquire(a, v), + OrderingType::Release => T::Repr::atomic_fetch_add_release(a, v), + OrderingType::Relaxed => T::Repr::atomic_fetch_add_relaxed(a, v), + } + }; + + // SAFETY: `ret` comes from reading `a` which was derived from `self.as_ptr()` which points + // at a valid `T`. + unsafe { from_repr(ret) } + } +} -- 2.39.5 (Apple Git-154)