Skip to content

Commit 6021a45

Browse files
fbqjannau
authored andcommitted
rust: sync: atomic: Add atomic {cmp,}xchg operations
xchg() and cmpxchg() are basic operations on atomic. Provide these based on C APIs. Note that cmpxchg() use the similar function signature as compare_exchange() in Rust std: returning a `Result`, `Ok(old)` means the operation succeeds and `Err(old)` means the operation fails. 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-6-boqun.feng@gmail.com/
1 parent 064b552 commit 6021a45

1 file changed

Lines changed: 167 additions & 1 deletion

File tree

rust/kernel/sync/atomic.rs

Lines changed: 167 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ pub use internal::AtomicImpl;
2525
pub use ordering::{Acquire, Full, Relaxed, Release};
2626

2727
use crate::build_error;
28-
use internal::{AtomicBasicOps, AtomicRepr};
28+
use internal::{AtomicBasicOps, AtomicExchangeOps, AtomicRepr};
2929
use ordering::OrderingType;
3030

3131
/// A memory location which can be safely modified from multiple execution contexts.
@@ -293,3 +293,169 @@ where
293293
}
294294
}
295295
}
296+
297+
impl<T: AtomicType> Atomic<T>
298+
where
299+
T::Repr: AtomicExchangeOps,
300+
{
301+
/// Atomic exchange.
302+
///
303+
/// Atomically updates `*self` to `v` and returns the old value of `*self`.
304+
///
305+
/// # Examples
306+
///
307+
/// ```
308+
/// use kernel::sync::atomic::{Atomic, Acquire, Relaxed};
309+
///
310+
/// let x = Atomic::new(42);
311+
///
312+
/// assert_eq!(42, x.xchg(52, Acquire));
313+
/// assert_eq!(52, x.load(Relaxed));
314+
/// ```
315+
#[doc(alias("atomic_xchg", "atomic64_xchg", "swap"))]
316+
#[inline(always)]
317+
pub fn xchg<Ordering: ordering::Ordering>(&self, v: T, _: Ordering) -> T {
318+
let v = into_repr(v);
319+
320+
// INVARIANT: `self.0` is a valid `T` after `atomic_xchg*()` because `v` is transmutable to
321+
// `T`.
322+
let ret = {
323+
match Ordering::TYPE {
324+
OrderingType::Full => T::Repr::atomic_xchg(&self.0, v),
325+
OrderingType::Acquire => T::Repr::atomic_xchg_acquire(&self.0, v),
326+
OrderingType::Release => T::Repr::atomic_xchg_release(&self.0, v),
327+
OrderingType::Relaxed => T::Repr::atomic_xchg_relaxed(&self.0, v),
328+
}
329+
};
330+
331+
// SAFETY: `ret` comes from reading `*self`, which is a valid `T` per type invariants.
332+
unsafe { from_repr(ret) }
333+
}
334+
335+
/// Atomic compare and exchange.
336+
///
337+
/// If `*self` == `old`, atomically updates `*self` to `new`. Otherwise, `*self` is not
338+
/// modified.
339+
///
340+
/// Compare: The comparison is done via the byte level comparison between `*self` and `old`.
341+
///
342+
/// Ordering: When succeeds, provides the corresponding ordering as the `Ordering` type
343+
/// parameter indicates, and a failed one doesn't provide any ordering, the load part of a
344+
/// failed cmpxchg is a [`Relaxed`] load.
345+
///
346+
/// Returns `Ok(value)` if cmpxchg succeeds, and `value` is guaranteed to be equal to `old`,
347+
/// otherwise returns `Err(value)`, and `value` is the current value of `*self`.
348+
///
349+
/// # Examples
350+
///
351+
/// ```
352+
/// use kernel::sync::atomic::{Atomic, Full, Relaxed};
353+
///
354+
/// let x = Atomic::new(42);
355+
///
356+
/// // Checks whether cmpxchg succeeded.
357+
/// let success = x.cmpxchg(52, 64, Relaxed).is_ok();
358+
/// # assert!(!success);
359+
///
360+
/// // Checks whether cmpxchg failed.
361+
/// let failure = x.cmpxchg(52, 64, Relaxed).is_err();
362+
/// # assert!(failure);
363+
///
364+
/// // Uses the old value if failed, probably re-try cmpxchg.
365+
/// match x.cmpxchg(52, 64, Relaxed) {
366+
/// Ok(_) => { },
367+
/// Err(old) => {
368+
/// // do something with `old`.
369+
/// # assert_eq!(old, 42);
370+
/// }
371+
/// }
372+
///
373+
/// // Uses the latest value regardlessly, same as atomic_cmpxchg() in C.
374+
/// let latest = x.cmpxchg(42, 64, Full).unwrap_or_else(|old| old);
375+
/// # assert_eq!(42, latest);
376+
/// assert_eq!(64, x.load(Relaxed));
377+
/// ```
378+
///
379+
/// [`Relaxed`]: ordering::Relaxed
380+
#[doc(alias(
381+
"atomic_cmpxchg",
382+
"atomic64_cmpxchg",
383+
"atomic_try_cmpxchg",
384+
"atomic64_try_cmpxchg",
385+
"compare_exchange"
386+
))]
387+
#[inline(always)]
388+
pub fn cmpxchg<Ordering: ordering::Ordering>(
389+
&self,
390+
mut old: T,
391+
new: T,
392+
o: Ordering,
393+
) -> Result<T, T> {
394+
// Note on code generation:
395+
//
396+
// try_cmpxchg() is used to implement cmpxchg(), and if the helper functions are inlined,
397+
// the compiler is able to figure out that branch is not needed if the users don't care
398+
// about whether the operation succeeds or not. One exception is on x86, due to commit
399+
// 44fe84459faf ("locking/atomic: Fix atomic_try_cmpxchg() semantics"), the
400+
// atomic_try_cmpxchg() on x86 has a branch even if the caller doesn't care about the
401+
// success of cmpxchg and only wants to use the old value. For example, for code like:
402+
//
403+
// let latest = x.cmpxchg(42, 64, Full).unwrap_or_else(|old| old);
404+
//
405+
// It will still generate code:
406+
//
407+
// movl $0x40, %ecx
408+
// movl $0x34, %eax
409+
// lock
410+
// cmpxchgl %ecx, 0x4(%rsp)
411+
// jne 1f
412+
// 2:
413+
// ...
414+
// 1: movl %eax, %ecx
415+
// jmp 2b
416+
//
417+
// This might be "fixed" by introducing a try_cmpxchg_exclusive() that knows the "*old"
418+
// location in the C function is always safe to write.
419+
if self.try_cmpxchg(&mut old, new, o) {
420+
Ok(old)
421+
} else {
422+
Err(old)
423+
}
424+
}
425+
426+
/// Atomic compare and exchange and returns whether the operation succeeds.
427+
///
428+
/// If `*self` == `old`, atomically updates `*self` to `new`. Otherwise, `*self` is not
429+
/// modified, `*old` is updated to the current value of `*self`.
430+
///
431+
/// "Compare" and "Ordering" part are the same as [`Atomic::cmpxchg()`].
432+
///
433+
/// Returns `true` means the cmpxchg succeeds otherwise returns `false`.
434+
#[inline(always)]
435+
fn try_cmpxchg<Ordering: ordering::Ordering>(&self, old: &mut T, new: T, _: Ordering) -> bool {
436+
let mut tmp = into_repr(*old);
437+
let new = into_repr(new);
438+
439+
// INVARIANT: `self.0` is a valid `T` after `atomic_try_cmpxchg*()` because `new` is
440+
// transmutable to `T`.
441+
let ret = {
442+
match Ordering::TYPE {
443+
OrderingType::Full => T::Repr::atomic_try_cmpxchg(&self.0, &mut tmp, new),
444+
OrderingType::Acquire => {
445+
T::Repr::atomic_try_cmpxchg_acquire(&self.0, &mut tmp, new)
446+
}
447+
OrderingType::Release => {
448+
T::Repr::atomic_try_cmpxchg_release(&self.0, &mut tmp, new)
449+
}
450+
OrderingType::Relaxed => {
451+
T::Repr::atomic_try_cmpxchg_relaxed(&self.0, &mut tmp, new)
452+
}
453+
}
454+
};
455+
456+
// SAFETY: `tmp` comes from reading `*self`, which is a valid `T` per type invariants.
457+
*old = unsafe { from_repr(tmp) };
458+
459+
ret
460+
}
461+
}

0 commit comments

Comments
 (0)