@@ -39,4 +39,27 @@ pub fn new(v: i32) -> Self {
pub fn fetch_add_relaxed(&self, i: i32) -> i32 {
arch::i32_fetch_add_relaxed(&self.0, i)
}
+
+ /// Subs `i` to the atomic variable with RELEASE ordering.
+ ///
+ /// Returns the old value before the sub.
+ ///
+ /// # Example
+ ///
+ /// ```rust
+ /// use kernel::sync::atomic::AtomicI32;
+ ///
+ /// let a = AtomicI32::new(1);
+ /// let b = a.fetch_sub_release(1);
+ /// let c = a.fetch_sub_release(2);
+ /// let d = a.fetch_sub_release(3);
+ /// let e = a.fetch_sub_release(core::i32::MIN);
+ ///
+ /// assert_eq!(b, 1);
+ /// assert_eq!(c, 0);
+ /// assert_eq!(d, -2);
+ /// ```
+ pub fn fetch_sub_release(&self, i: i32) -> i32 {
+ arch::i32_fetch_sub_release(&self.0, i)
+ }
}
@@ -24,3 +24,23 @@ pub(crate) fn i32_fetch_add_relaxed(v: &UnsafeCell<i32>, i: i32) -> i32 {
result
}
+
+pub(crate) fn i32_fetch_sub_release(v: &UnsafeCell<i32>, i: i32) -> i32 {
+ let mut result;
+ unsafe {
+ asm!(
+ "prfm pstl1strm, [{v}]",
+ "1: ldxr {result:w}, [{v}]",
+ "sub {val:w}, {result:w}, {i:w}",
+ "stlxr {tmp:w}, {val:w}, [{v}]",
+ "cbnz {tmp:w}, 1b",
+ result = out(reg) result,
+ tmp = out(reg) _,
+ val = out(reg) _,
+ v = in(reg) v.get(),
+ i = in(reg) i,
+ )
+ }
+
+ result
+}
@@ -41,3 +41,8 @@ pub(crate) fn i32_fetch_add_relaxed(v: &UnsafeCell<i32>, i: i32) -> i32 {
// SAFETY: `v.get()` points to a valid `i32`.
unsafe { i32_xadd(v.get(), i) }
}
+
+pub(crate) fn i32_fetch_sub_release(v: &UnsafeCell<i32>, i: i32) -> i32 {
+ // SAFETY: `v.get()` points to a valid `i32`.
+ unsafe { i32_xadd(v.get(), i.wrapping_neg()) }
+}
Signed-off-by: Boqun Feng <boqun.feng@gmail.com> --- rust/kernel/sync/atomic.rs | 23 +++++++++++++++++++++++ rust/kernel/sync/atomic/arch/arm64.rs | 20 ++++++++++++++++++++ rust/kernel/sync/atomic/arch/x86.rs | 5 +++++ 3 files changed, 48 insertions(+)