Re: [PATCH v5 05/10] rust: sync: atomic: Add atomic {cmp,}xchg operations

Boqun Feng <[email protected]>
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]>
On Sat, Jun 21, 2025 at 12:37:53PM +0100, Gary Guo wrote:
[...]
> > +    /// Atomic compare and exchange.
> > +    ///
> > +    /// Compare: The comparison is done via the byte level comparison between the atomic variables
> > +    /// with the `old` value.
> > +    ///
> > +    /// Ordering: When succeeds, provides the corresponding ordering as the `Ordering` type
> > +    /// parameter indicates, and a failed one doesn't provide any ordering, the read part of a
> > +    /// failed cmpxchg should be treated as a relaxed read.
> > +    ///
> > +    /// Returns `Ok(value)` if cmpxchg succeeds, and `value` is guaranteed to be equal to `old`,
> > +    /// otherwise returns `Err(value)`, and `value` is the value of the atomic variable when
> > +    /// cmpxchg was happening.
> > +    ///
> > +    /// # Examples
> > +    ///
> > +    /// ```rust
> > +    /// use kernel::sync::atomic::{Atomic, Full, Relaxed};
> > +    ///
> > +    /// let x = Atomic::new(42);
> > +    ///
> > +    /// // Checks whether cmpxchg succeeded.
> > +    /// let success = x.cmpxchg(52, 64, Relaxed).is_ok();
> > +    /// # assert!(!success);
> > +    ///
> > +    /// // Checks whether cmpxchg failed.
> > +    /// let failure = x.cmpxchg(52, 64, Relaxed).is_err();
> > +    /// # assert!(failure);
> > +    ///
> > +    /// // Uses the old value if failed, probably re-try cmpxchg.
> > +    /// match x.cmpxchg(52, 64, Relaxed) {
> > +    ///     Ok(_) => { },
> > +    ///     Err(old) => {
> > +    ///         // do something with `old`.
> > +    ///         # assert_eq!(old, 42);
> > +    ///     }
> > +    /// }
> > +    ///
> > +    /// // Uses the latest value regardlessly, same as atomic_cmpxchg() in C.
> > +    /// let latest = x.cmpxchg(42, 64, Full).unwrap_or_else(|old| old);
> > +    /// # assert_eq!(42, latest);
> > +    /// assert_eq!(64, x.load(Relaxed));
> > +    /// ```
> > +    #[doc(alias(
> > +        "atomic_cmpxchg",
> > +        "atomic64_cmpxchg",
> > +        "atomic_try_cmpxchg",
> > +        "atomic64_try_cmpxchg"
> > +    ))]
> > +    #[inline(always)]
> > +    pub fn cmpxchg<Ordering: All>(&self, mut old: T, new: T, o: Ordering) -> Result<T, T> {
> > +        // Note on code generation:
> > +        //
> > +        // try_cmpxchg() is used to implement cmpxchg(), and if the helper functions are inlined,
> > +        // the compiler is able to figure out that branch is not needed if the users don't care
> > +        // about whether the operation succeeds or not. One exception is on x86, due to commit
> > +        // 44fe84459faf ("locking/atomic: Fix atomic_try_cmpxchg() semantics"), the
> > +        // atomic_try_cmpxchg() on x86 has a branch even if the caller doesn't care about the
> > +        // success of cmpxchg and only wants to use the old value. For example, for code like:
> > +        //
> > +        //     let latest = x.cmpxchg(42, 64, Full).unwrap_or_else(|old| old);
> > +        //
> > +        // It will still generate code:
> > +        //
> > +        //     movl    $0x40, %ecx
> > +        //     movl    $0x34, %eax
> > +        //     lock
> > +        //     cmpxchgl        %ecx, 0x4(%rsp)
> > +        //     jne     1f
> > +        //     2:
> > +        //     ...
> > +        //     1:  movl    %eax, %ecx
> > +        //     jmp 2b
> > +        //
> > +        // This might be "fixed" by introducing a try_cmpxchg_exclusive() that knows the "*old"
> > +        // location in the C function is always safe to write.
> > +        if self.try_cmpxchg(&mut old, new, o) {
> > +            Ok(old)
> > +        } else {
> > +            Err(old)
> > +        }
> > +    }
> > +
> > +    /// Atomic compare and exchange and returns whether the operation succeeds.
> > +    ///
> > +    /// "Compare" and "Ordering" part are the same as [`Atomic::cmpxchg()`].
> > +    ///
> > +    /// Returns `true` means the cmpxchg succeeds otherwise returns `false` with `old` updated to
> > +    /// the value of the atomic variable when cmpxchg was happening.
> > +    #[inline(always)]
> > +    fn try_cmpxchg<Ordering: All>(&self, old: &mut T, new: T, _: Ordering) -> bool {
> > +        let old = (old as *mut T).cast::<T::Repr>();
> > +        let new = T::into_repr(new);
> > +        let a = self.0.get().cast::<T::Repr>();
> > +
> > +        // SAFETY:
> > +        // - For calling the atomic_try_cmpchg*() function:
> > +        //   - `self.as_ptr()` is a valid pointer, and per the safety requirement of `AllowAtomic`,
> > +        //      a `*mut T` is a valid `*mut T::Repr`. Therefore `a` is a valid pointer,
> > +        //   - per the type invariants, the following atomic operation won't cause data races.
> > +        //   - `old` is a valid pointer to write because it comes from a mutable reference.
> > +        // - For extra safety requirement of usage on pointers returned by `self.as_ptr():
> > +        //   - atomic operations are used here.
> > +        unsafe {
> > +            match Ordering::TYPE {
> > +                OrderingType::Full => T::Repr::atomic_try_cmpxchg(a, old, new),
> > +                OrderingType::Acquire => T::Repr::atomic_try_cmpxchg_acquire(a, old, new),
> > +                OrderingType::Release => T::Repr::atomic_try_cmpxchg_release(a, old, new),
> > +                OrderingType::Relaxed => T::Repr::atomic_try_cmpxchg_relaxed(a, old, new),
> > +            }
> > +        }
> 
> Again this function is only using `T::into_repr`, bypassing
> `T::from_repr` and just use pointer casting.
> 
> BTW, any reason that this is a separate function, and it couldn't just
> be in `cmpxchg` function?
> 

It's a non-public function, I feel it's easier to see that Rust's
cmpxchg() is implemented via a try_cmpxchg() that is a wrapper of
`atomic_try_cmpxchg*()`.

Regards,
Boqun

> 
> > +    }
> > +}
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.