Skip to content

Commit a023a97

Browse files
fbqjannau
authored andcommitted
rust: sync: atomic: Add the framework of arithmetic operations
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 `AtomicType` 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 `AtomicType` describing which types have and can do atomic arithemtic operations. One trait `AtomicAdd` is added, and only add() and fetch_add() are added. The rest will be added in the future. Signed-off-by: Boqun Feng <boqun.feng@gmail.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Reviewed-by: Alice Ryhl <aliceryhl@google.com> Reviewed-by: Benno Lossin <lossin@kernel.org> Reviewed-by: Elle Rhumsaa <elle@weathered-steel.dev> Link: https://lore.kernel.org/all/20250719030827.61357-7-boqun.feng@gmail.com/
1 parent 6021a45 commit a023a97

2 files changed

Lines changed: 106 additions & 2 deletions

File tree

rust/kernel/sync/atomic.rs

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
//!
1717
//! [`LKMM`]: srctree/tools/memory-model/
1818
19-
#[allow(dead_code, unreachable_pub)]
2019
mod internal;
2120
pub mod ordering;
2221
mod predefine;
@@ -25,7 +24,7 @@ pub use internal::AtomicImpl;
2524
pub use ordering::{Acquire, Full, Relaxed, Release};
2625

2726
use crate::build_error;
28-
use internal::{AtomicBasicOps, AtomicExchangeOps, AtomicRepr};
27+
use internal::{AtomicArithmeticOps, AtomicBasicOps, AtomicExchangeOps, AtomicRepr};
2928
use ordering::OrderingType;
3029

3130
/// A memory location which can be safely modified from multiple execution contexts.
@@ -112,6 +111,19 @@ pub unsafe trait AtomicType: Sized + Send + Copy {
112111
type Repr: AtomicImpl;
113112
}
114113

114+
/// Types that support atomic add operations.
115+
///
116+
/// # Safety
117+
///
118+
// TODO: Properly defines `wrapping_add` in the following comment.
119+
/// `wrapping_add` any value of type `Self::Repr::Delta` obtained by [`Self::rhs_into_delta()`] to
120+
/// any value of type `Self::Repr` obtained through transmuting a value of type `Self` to must
121+
/// yield a value with a bit pattern also valid for `Self`.
122+
pub unsafe trait AtomicAdd<Rhs = Self>: AtomicType {
123+
/// Converts `Rhs` into the `Delta` type of the atomic implementation.
124+
fn rhs_into_delta(rhs: Rhs) -> <Self::Repr as AtomicImpl>::Delta;
125+
}
126+
115127
#[inline(always)]
116128
const fn into_repr<T: AtomicType>(v: T) -> T::Repr {
117129
// SAFETY: Per the safety requirement of `AtomicType`, `T` is round-trip transmutable to
@@ -459,3 +471,81 @@ where
459471
ret
460472
}
461473
}
474+
475+
impl<T: AtomicType> Atomic<T>
476+
where
477+
T::Repr: AtomicArithmeticOps,
478+
{
479+
/// Atomic add.
480+
///
481+
/// Atomically updates `*self` to `(*self).wrapping_add(v)`.
482+
///
483+
/// # Examples
484+
///
485+
/// ```
486+
/// use kernel::sync::atomic::{Atomic, Relaxed};
487+
///
488+
/// let x = Atomic::new(42);
489+
///
490+
/// assert_eq!(42, x.load(Relaxed));
491+
///
492+
/// x.add(12, Relaxed);
493+
///
494+
/// assert_eq!(54, x.load(Relaxed));
495+
/// ```
496+
#[inline(always)]
497+
pub fn add<Rhs>(&self, v: Rhs, _: ordering::Relaxed)
498+
where
499+
T: AtomicAdd<Rhs>,
500+
{
501+
let v = T::rhs_into_delta(v);
502+
503+
// INVARIANT: `self.0` is a valid `T` after `atomic_add()` due to safety requirement of
504+
// `AtomicAdd`.
505+
T::Repr::atomic_add(&self.0, v);
506+
}
507+
508+
/// Atomic fetch and add.
509+
///
510+
/// Atomically updates `*self` to `(*self).wrapping_add(v)`, and returns the value of `*self`
511+
/// before the update.
512+
///
513+
/// # Examples
514+
///
515+
/// ```
516+
/// use kernel::sync::atomic::{Atomic, Acquire, Full, Relaxed};
517+
///
518+
/// let x = Atomic::new(42);
519+
///
520+
/// assert_eq!(42, x.load(Relaxed));
521+
///
522+
/// assert_eq!(54, { x.fetch_add(12, Acquire); x.load(Relaxed) });
523+
///
524+
/// let x = Atomic::new(42);
525+
///
526+
/// assert_eq!(42, x.load(Relaxed));
527+
///
528+
/// assert_eq!(54, { x.fetch_add(12, Full); x.load(Relaxed) } );
529+
/// ```
530+
#[inline(always)]
531+
pub fn fetch_add<Rhs, Ordering: ordering::Ordering>(&self, v: Rhs, _: Ordering) -> T
532+
where
533+
T: AtomicAdd<Rhs>,
534+
{
535+
let v = T::rhs_into_delta(v);
536+
537+
// INVARIANT: `self.0` is a valid `T` after `atomic_fetch_add*()` due to safety requirement
538+
// of `AtomicAdd`.
539+
let ret = {
540+
match Ordering::TYPE {
541+
OrderingType::Full => T::Repr::atomic_fetch_add(&self.0, v),
542+
OrderingType::Acquire => T::Repr::atomic_fetch_add_acquire(&self.0, v),
543+
OrderingType::Release => T::Repr::atomic_fetch_add_release(&self.0, v),
544+
OrderingType::Relaxed => T::Repr::atomic_fetch_add_relaxed(&self.0, v),
545+
}
546+
};
547+
548+
// SAFETY: `ret` comes from reading `self.0`, which is a valid `T` per type invariants.
549+
unsafe { from_repr(ret) }
550+
}
551+
}

rust/kernel/sync/atomic/predefine.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,22 @@ unsafe impl super::AtomicType for i32 {
88
type Repr = i32;
99
}
1010

11+
// SAFETY: The wrapping add result of two `i32`s is a valid `i32`.
12+
unsafe impl super::AtomicAdd<i32> for i32 {
13+
fn rhs_into_delta(rhs: i32) -> i32 {
14+
rhs
15+
}
16+
}
17+
1118
// SAFETY: `i64` has the same size and alignment with itself, and is round-trip transmutable to
1219
// itself.
1320
unsafe impl super::AtomicType for i64 {
1421
type Repr = i64;
1522
}
23+
24+
// SAFETY: The wrapping add result of two `i64`s is a valid `i64`.
25+
unsafe impl super::AtomicAdd<i64> for i64 {
26+
fn rhs_into_delta(rhs: i64) -> i64 {
27+
rhs
28+
}
29+
}

0 commit comments

Comments
 (0)