xref: /linux/rust/kernel/sync/atomic/internal.rs (revision 88b489385bfe3713497a63c0dcf4dd7852cf4568)
1*2387fb2aSBoqun Feng // SPDX-License-Identifier: GPL-2.0
2*2387fb2aSBoqun Feng 
3*2387fb2aSBoqun Feng //! Atomic internal implementations.
4*2387fb2aSBoqun Feng //!
5*2387fb2aSBoqun Feng //! Provides 1:1 mapping to the C atomic operations.
6*2387fb2aSBoqun Feng 
7*2387fb2aSBoqun Feng use crate::bindings;
8*2387fb2aSBoqun Feng use crate::macros::paste;
9*2387fb2aSBoqun Feng use core::cell::UnsafeCell;
10*2387fb2aSBoqun Feng 
11*2387fb2aSBoqun Feng mod private {
12*2387fb2aSBoqun Feng     /// Sealed trait marker to disable customized impls on atomic implementation traits.
13*2387fb2aSBoqun Feng     pub trait Sealed {}
14*2387fb2aSBoqun Feng }
15*2387fb2aSBoqun Feng 
16*2387fb2aSBoqun Feng // `i32` and `i64` are only supported atomic implementations.
17*2387fb2aSBoqun Feng impl private::Sealed for i32 {}
18*2387fb2aSBoqun Feng impl private::Sealed for i64 {}
19*2387fb2aSBoqun Feng 
20*2387fb2aSBoqun Feng /// A marker trait for types that implement atomic operations with C side primitives.
21*2387fb2aSBoqun Feng ///
22*2387fb2aSBoqun Feng /// This trait is sealed, and only types that have directly mapping to the C side atomics should
23*2387fb2aSBoqun Feng /// impl this:
24*2387fb2aSBoqun Feng ///
25*2387fb2aSBoqun Feng /// - `i32` maps to `atomic_t`.
26*2387fb2aSBoqun Feng /// - `i64` maps to `atomic64_t`.
27*2387fb2aSBoqun Feng pub trait AtomicImpl: Sized + Send + Copy + private::Sealed {
28*2387fb2aSBoqun Feng     /// The type of the delta in arithmetic or logical operations.
29*2387fb2aSBoqun Feng     ///
30*2387fb2aSBoqun Feng     /// For example, in `atomic_add(ptr, v)`, it's the type of `v`. Usually it's the same type of
31*2387fb2aSBoqun Feng     /// [`Self`], but it may be different for the atomic pointer type.
32*2387fb2aSBoqun Feng     type Delta;
33*2387fb2aSBoqun Feng }
34*2387fb2aSBoqun Feng 
35*2387fb2aSBoqun Feng // `atomic_t` implements atomic operations on `i32`.
36*2387fb2aSBoqun Feng impl AtomicImpl for i32 {
37*2387fb2aSBoqun Feng     type Delta = Self;
38*2387fb2aSBoqun Feng }
39*2387fb2aSBoqun Feng 
40*2387fb2aSBoqun Feng // `atomic64_t` implements atomic operations on `i64`.
41*2387fb2aSBoqun Feng impl AtomicImpl for i64 {
42*2387fb2aSBoqun Feng     type Delta = Self;
43*2387fb2aSBoqun Feng }
44*2387fb2aSBoqun Feng 
45*2387fb2aSBoqun Feng /// Atomic representation.
46*2387fb2aSBoqun Feng #[repr(transparent)]
47*2387fb2aSBoqun Feng pub struct AtomicRepr<T: AtomicImpl>(UnsafeCell<T>);
48*2387fb2aSBoqun Feng 
49*2387fb2aSBoqun Feng impl<T: AtomicImpl> AtomicRepr<T> {
50*2387fb2aSBoqun Feng     /// Creates a new atomic representation `T`.
new(v: T) -> Self51*2387fb2aSBoqun Feng     pub const fn new(v: T) -> Self {
52*2387fb2aSBoqun Feng         Self(UnsafeCell::new(v))
53*2387fb2aSBoqun Feng     }
54*2387fb2aSBoqun Feng 
55*2387fb2aSBoqun Feng     /// Returns a pointer to the underlying `T`.
56*2387fb2aSBoqun Feng     ///
57*2387fb2aSBoqun Feng     /// # Guarantees
58*2387fb2aSBoqun Feng     ///
59*2387fb2aSBoqun Feng     /// The returned pointer is valid and properly aligned (i.e. aligned to [`align_of::<T>()`]).
as_ptr(&self) -> *mut T60*2387fb2aSBoqun Feng     pub const fn as_ptr(&self) -> *mut T {
61*2387fb2aSBoqun Feng         // GUARANTEE: `self.0` is an `UnsafeCell<T>`, therefore the pointer returned by `.get()`
62*2387fb2aSBoqun Feng         // must be valid and properly aligned.
63*2387fb2aSBoqun Feng         self.0.get()
64*2387fb2aSBoqun Feng     }
65*2387fb2aSBoqun Feng }
66*2387fb2aSBoqun Feng 
67*2387fb2aSBoqun Feng // This macro generates the function signature with given argument list and return type.
68*2387fb2aSBoqun Feng macro_rules! declare_atomic_method {
69*2387fb2aSBoqun Feng     (
70*2387fb2aSBoqun Feng         $(#[doc=$doc:expr])*
71*2387fb2aSBoqun Feng         $func:ident($($arg:ident : $arg_type:ty),*) $(-> $ret:ty)?
72*2387fb2aSBoqun Feng     ) => {
73*2387fb2aSBoqun Feng         paste!(
74*2387fb2aSBoqun Feng             $(#[doc = $doc])*
75*2387fb2aSBoqun Feng             fn [< atomic_ $func >]($($arg: $arg_type,)*) $(-> $ret)?;
76*2387fb2aSBoqun Feng         );
77*2387fb2aSBoqun Feng     };
78*2387fb2aSBoqun Feng     (
79*2387fb2aSBoqun Feng         $(#[doc=$doc:expr])*
80*2387fb2aSBoqun Feng         $func:ident [$variant:ident $($rest:ident)*]($($arg_sig:tt)*) $(-> $ret:ty)?
81*2387fb2aSBoqun Feng     ) => {
82*2387fb2aSBoqun Feng         paste!(
83*2387fb2aSBoqun Feng             declare_atomic_method!(
84*2387fb2aSBoqun Feng                 $(#[doc = $doc])*
85*2387fb2aSBoqun Feng                 [< $func _ $variant >]($($arg_sig)*) $(-> $ret)?
86*2387fb2aSBoqun Feng             );
87*2387fb2aSBoqun Feng         );
88*2387fb2aSBoqun Feng 
89*2387fb2aSBoqun Feng         declare_atomic_method!(
90*2387fb2aSBoqun Feng             $(#[doc = $doc])*
91*2387fb2aSBoqun Feng             $func [$($rest)*]($($arg_sig)*) $(-> $ret)?
92*2387fb2aSBoqun Feng         );
93*2387fb2aSBoqun Feng     };
94*2387fb2aSBoqun Feng     (
95*2387fb2aSBoqun Feng         $(#[doc=$doc:expr])*
96*2387fb2aSBoqun Feng         $func:ident []($($arg_sig:tt)*) $(-> $ret:ty)?
97*2387fb2aSBoqun Feng     ) => {
98*2387fb2aSBoqun Feng         declare_atomic_method!(
99*2387fb2aSBoqun Feng             $(#[doc = $doc])*
100*2387fb2aSBoqun Feng             $func($($arg_sig)*) $(-> $ret)?
101*2387fb2aSBoqun Feng         );
102*2387fb2aSBoqun Feng     }
103*2387fb2aSBoqun Feng }
104*2387fb2aSBoqun Feng 
105*2387fb2aSBoqun Feng // This macro generates the function implementation with given argument list and return type, and it
106*2387fb2aSBoqun Feng // will replace "call(...)" expression with "$ctype _ $func" to call the real C function.
107*2387fb2aSBoqun Feng macro_rules! impl_atomic_method {
108*2387fb2aSBoqun Feng     (
109*2387fb2aSBoqun Feng         ($ctype:ident) $func:ident($($arg:ident: $arg_type:ty),*) $(-> $ret:ty)? {
110*2387fb2aSBoqun Feng             $unsafe:tt { call($($c_arg:expr),*) }
111*2387fb2aSBoqun Feng         }
112*2387fb2aSBoqun Feng     ) => {
113*2387fb2aSBoqun Feng         paste!(
114*2387fb2aSBoqun Feng             #[inline(always)]
115*2387fb2aSBoqun Feng             fn [< atomic_ $func >]($($arg: $arg_type,)*) $(-> $ret)? {
116*2387fb2aSBoqun Feng                 // TODO: Ideally we want to use the SAFETY comments written at the macro invocation
117*2387fb2aSBoqun Feng                 // (e.g. in `declare_and_impl_atomic_methods!()`, however, since SAFETY comments
118*2387fb2aSBoqun Feng                 // are just comments, and they are not passed to macros as tokens, therefore we
119*2387fb2aSBoqun Feng                 // cannot use them here. One potential improvement is that if we support using
120*2387fb2aSBoqun Feng                 // attributes as an alternative for SAFETY comments, then we can use that for macro
121*2387fb2aSBoqun Feng                 // generating code.
122*2387fb2aSBoqun Feng                 //
123*2387fb2aSBoqun Feng                 // SAFETY: specified on macro invocation.
124*2387fb2aSBoqun Feng                 $unsafe { bindings::[< $ctype _ $func >]($($c_arg,)*) }
125*2387fb2aSBoqun Feng             }
126*2387fb2aSBoqun Feng         );
127*2387fb2aSBoqun Feng     };
128*2387fb2aSBoqun Feng     (
129*2387fb2aSBoqun Feng         ($ctype:ident) $func:ident[$variant:ident $($rest:ident)*]($($arg_sig:tt)*) $(-> $ret:ty)? {
130*2387fb2aSBoqun Feng             $unsafe:tt { call($($arg:tt)*) }
131*2387fb2aSBoqun Feng         }
132*2387fb2aSBoqun Feng     ) => {
133*2387fb2aSBoqun Feng         paste!(
134*2387fb2aSBoqun Feng             impl_atomic_method!(
135*2387fb2aSBoqun Feng                 ($ctype) [< $func _ $variant >]($($arg_sig)*) $( -> $ret)? {
136*2387fb2aSBoqun Feng                     $unsafe { call($($arg)*) }
137*2387fb2aSBoqun Feng             }
138*2387fb2aSBoqun Feng             );
139*2387fb2aSBoqun Feng         );
140*2387fb2aSBoqun Feng         impl_atomic_method!(
141*2387fb2aSBoqun Feng             ($ctype) $func [$($rest)*]($($arg_sig)*) $( -> $ret)? {
142*2387fb2aSBoqun Feng                 $unsafe { call($($arg)*) }
143*2387fb2aSBoqun Feng             }
144*2387fb2aSBoqun Feng         );
145*2387fb2aSBoqun Feng     };
146*2387fb2aSBoqun Feng     (
147*2387fb2aSBoqun Feng         ($ctype:ident) $func:ident[]($($arg_sig:tt)*) $( -> $ret:ty)? {
148*2387fb2aSBoqun Feng             $unsafe:tt { call($($arg:tt)*) }
149*2387fb2aSBoqun Feng         }
150*2387fb2aSBoqun Feng     ) => {
151*2387fb2aSBoqun Feng         impl_atomic_method!(
152*2387fb2aSBoqun Feng             ($ctype) $func($($arg_sig)*) $(-> $ret)? {
153*2387fb2aSBoqun Feng                 $unsafe { call($($arg)*) }
154*2387fb2aSBoqun Feng             }
155*2387fb2aSBoqun Feng         );
156*2387fb2aSBoqun Feng     }
157*2387fb2aSBoqun Feng }
158*2387fb2aSBoqun Feng 
159*2387fb2aSBoqun Feng // Delcares $ops trait with methods and implements the trait for `i32` and `i64`.
160*2387fb2aSBoqun Feng macro_rules! declare_and_impl_atomic_methods {
161*2387fb2aSBoqun Feng     ($(#[$attr:meta])* $pub:vis trait $ops:ident {
162*2387fb2aSBoqun Feng         $(
163*2387fb2aSBoqun Feng             $(#[doc=$doc:expr])*
164*2387fb2aSBoqun Feng             fn $func:ident [$($variant:ident),*]($($arg_sig:tt)*) $( -> $ret:ty)? {
165*2387fb2aSBoqun Feng                 $unsafe:tt { bindings::#call($($arg:tt)*) }
166*2387fb2aSBoqun Feng             }
167*2387fb2aSBoqun Feng         )*
168*2387fb2aSBoqun Feng     }) => {
169*2387fb2aSBoqun Feng         $(#[$attr])*
170*2387fb2aSBoqun Feng         $pub trait $ops: AtomicImpl {
171*2387fb2aSBoqun Feng             $(
172*2387fb2aSBoqun Feng                 declare_atomic_method!(
173*2387fb2aSBoqun Feng                     $(#[doc=$doc])*
174*2387fb2aSBoqun Feng                     $func[$($variant)*]($($arg_sig)*) $(-> $ret)?
175*2387fb2aSBoqun Feng                 );
176*2387fb2aSBoqun Feng             )*
177*2387fb2aSBoqun Feng         }
178*2387fb2aSBoqun Feng 
179*2387fb2aSBoqun Feng         impl $ops for i32 {
180*2387fb2aSBoqun Feng             $(
181*2387fb2aSBoqun Feng                 impl_atomic_method!(
182*2387fb2aSBoqun Feng                     (atomic) $func[$($variant)*]($($arg_sig)*) $(-> $ret)? {
183*2387fb2aSBoqun Feng                         $unsafe { call($($arg)*) }
184*2387fb2aSBoqun Feng                     }
185*2387fb2aSBoqun Feng                 );
186*2387fb2aSBoqun Feng             )*
187*2387fb2aSBoqun Feng         }
188*2387fb2aSBoqun Feng 
189*2387fb2aSBoqun Feng         impl $ops for i64 {
190*2387fb2aSBoqun Feng             $(
191*2387fb2aSBoqun Feng                 impl_atomic_method!(
192*2387fb2aSBoqun Feng                     (atomic64) $func[$($variant)*]($($arg_sig)*) $(-> $ret)? {
193*2387fb2aSBoqun Feng                         $unsafe { call($($arg)*) }
194*2387fb2aSBoqun Feng                     }
195*2387fb2aSBoqun Feng                 );
196*2387fb2aSBoqun Feng             )*
197*2387fb2aSBoqun Feng         }
198*2387fb2aSBoqun Feng     }
199*2387fb2aSBoqun Feng }
200*2387fb2aSBoqun Feng 
201*2387fb2aSBoqun Feng declare_and_impl_atomic_methods!(
202*2387fb2aSBoqun Feng     /// Basic atomic operations
203*2387fb2aSBoqun Feng     pub trait AtomicBasicOps {
204*2387fb2aSBoqun Feng         /// Atomic read (load).
205*2387fb2aSBoqun Feng         fn read[acquire](a: &AtomicRepr<Self>) -> Self {
206*2387fb2aSBoqun Feng             // SAFETY: `a.as_ptr()` is valid and properly aligned.
207*2387fb2aSBoqun Feng             unsafe { bindings::#call(a.as_ptr().cast()) }
208*2387fb2aSBoqun Feng         }
209*2387fb2aSBoqun Feng 
210*2387fb2aSBoqun Feng         /// Atomic set (store).
211*2387fb2aSBoqun Feng         fn set[release](a: &AtomicRepr<Self>, v: Self) {
212*2387fb2aSBoqun Feng             // SAFETY: `a.as_ptr()` is valid and properly aligned.
213*2387fb2aSBoqun Feng             unsafe { bindings::#call(a.as_ptr().cast(), v) }
214*2387fb2aSBoqun Feng         }
215*2387fb2aSBoqun Feng     }
216*2387fb2aSBoqun Feng );
217*2387fb2aSBoqun Feng 
218*2387fb2aSBoqun Feng declare_and_impl_atomic_methods!(
219*2387fb2aSBoqun Feng     /// Exchange and compare-and-exchange atomic operations
220*2387fb2aSBoqun Feng     pub trait AtomicExchangeOps {
221*2387fb2aSBoqun Feng         /// Atomic exchange.
222*2387fb2aSBoqun Feng         ///
223*2387fb2aSBoqun Feng         /// Atomically updates `*a` to `v` and returns the old value.
224*2387fb2aSBoqun Feng         fn xchg[acquire, release, relaxed](a: &AtomicRepr<Self>, v: Self) -> Self {
225*2387fb2aSBoqun Feng             // SAFETY: `a.as_ptr()` is valid and properly aligned.
226*2387fb2aSBoqun Feng             unsafe { bindings::#call(a.as_ptr().cast(), v) }
227*2387fb2aSBoqun Feng         }
228*2387fb2aSBoqun Feng 
229*2387fb2aSBoqun Feng         /// Atomic compare and exchange.
230*2387fb2aSBoqun Feng         ///
231*2387fb2aSBoqun Feng         /// If `*a` == `*old`, atomically updates `*a` to `new`. Otherwise, `*a` is not
232*2387fb2aSBoqun Feng         /// modified, `*old` is updated to the current value of `*a`.
233*2387fb2aSBoqun Feng         ///
234*2387fb2aSBoqun Feng         /// Return `true` if the update of `*a` occurred, `false` otherwise.
235*2387fb2aSBoqun Feng         fn try_cmpxchg[acquire, release, relaxed](
236*2387fb2aSBoqun Feng             a: &AtomicRepr<Self>, old: &mut Self, new: Self
237*2387fb2aSBoqun Feng         ) -> bool {
238*2387fb2aSBoqun Feng             // SAFETY: `a.as_ptr()` is valid and properly aligned. `core::ptr::from_mut(old)`
239*2387fb2aSBoqun Feng             // is valid and properly aligned.
240*2387fb2aSBoqun Feng             unsafe { bindings::#call(a.as_ptr().cast(), core::ptr::from_mut(old), new) }
241*2387fb2aSBoqun Feng         }
242*2387fb2aSBoqun Feng     }
243*2387fb2aSBoqun Feng );
244*2387fb2aSBoqun Feng 
245*2387fb2aSBoqun Feng declare_and_impl_atomic_methods!(
246*2387fb2aSBoqun Feng     /// Atomic arithmetic operations
247*2387fb2aSBoqun Feng     pub trait AtomicArithmeticOps {
248*2387fb2aSBoqun Feng         /// Atomic add (wrapping).
249*2387fb2aSBoqun Feng         ///
250*2387fb2aSBoqun Feng         /// Atomically updates `*a` to `(*a).wrapping_add(v)`.
251*2387fb2aSBoqun Feng         fn add[](a: &AtomicRepr<Self>, v: Self::Delta) {
252*2387fb2aSBoqun Feng             // SAFETY: `a.as_ptr()` is valid and properly aligned.
253*2387fb2aSBoqun Feng             unsafe { bindings::#call(v, a.as_ptr().cast()) }
254*2387fb2aSBoqun Feng         }
255*2387fb2aSBoqun Feng 
256*2387fb2aSBoqun Feng         /// Atomic fetch and add (wrapping).
257*2387fb2aSBoqun Feng         ///
258*2387fb2aSBoqun Feng         /// Atomically updates `*a` to `(*a).wrapping_add(v)`, and returns the value of `*a`
259*2387fb2aSBoqun Feng         /// before the update.
260*2387fb2aSBoqun Feng         fn fetch_add[acquire, release, relaxed](a: &AtomicRepr<Self>, v: Self::Delta) -> Self {
261*2387fb2aSBoqun Feng             // SAFETY: `a.as_ptr()` is valid and properly aligned.
262*2387fb2aSBoqun Feng             unsafe { bindings::#call(v, a.as_ptr().cast()) }
263*2387fb2aSBoqun Feng         }
264*2387fb2aSBoqun Feng     }
265*2387fb2aSBoqun Feng );
266