[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <aFlV9ky2RKrYnrJX@Mac.home>
Date: Mon, 23 Jun 2025 06:26:14 -0700
From: Boqun Feng <boqun.feng@...il.com>
To: Onur Özkan <work@...rozkan.dev>
Cc: linux-kernel@...r.kernel.org, rust-for-linux@...r.kernel.org,
ojeda@...nel.org, alex.gaynor@...il.com, gary@...yguo.net,
lossin@...nel.org, a.hindborg@...nel.org, aliceryhl@...gle.com,
tmgross@...ch.edu, dakr@...nel.org, peterz@...radead.org,
mingo@...hat.com, will@...nel.org, longman@...hat.com,
felipe_life@...e.com, daniel@...lak.dev, bjorn3_gh@...tonmail.com
Subject: Re: [PATCH v5 2/3] implement ww_mutex abstraction for the Rust tree
On Sat, Jun 21, 2025 at 09:44:53PM +0300, Onur Özkan wrote:
[...]
> +#[pin_data(PinnedDrop)]
> +pub struct WwAcquireCtx<'a> {
> + #[pin]
> + inner: Opaque<bindings::ww_acquire_ctx>,
> + _p: PhantomData<&'a WwClass>,
> +}
> +
> +// SAFETY: Used in controlled ways during lock acquisition. No race risk.
> +unsafe impl Sync for WwAcquireCtx<'_> {}
> +// SAFETY: Doesn't rely on thread-local state. Safe to move between threads.
> +unsafe impl Send for WwAcquireCtx<'_> {}
> +
I don't think `WwAcquireCtx` is `Send`, if you look at C code when
LOCKDEP is enabled, `ww_acquire_init()` calls a few `mutex_acquire()`
and expects `ww_acquire_fini()` to call the corresponding
`mutex_release()`, and these two have to be on the same task. Also I
don't think there is a need for sending `WwAcquireCtx` to another
thread.
Besides, the `Sync` of `WwAcquireCtx` also doesn't make sense, I would
drop it if there is no real usage for now.
> +impl<'ctx> WwAcquireCtx<'ctx> {
> + /// Initializes `Self` with calling C side `ww_acquire_init` inside.
> + pub fn new<'class: 'ctx>(ww_class: &'class WwClass) -> impl PinInit<Self> {
> + let raw_ptr = ww_class.inner.get();
> + pin_init!(WwAcquireCtx {
> + inner <- Opaque::ffi_init(|slot: *mut bindings::ww_acquire_ctx| {
> + // SAFETY: The caller guarantees that `ww_class` remains valid.
> + unsafe { bindings::ww_acquire_init(slot, raw_ptr) }
> + }),
> + _p: PhantomData
> + })
> + }
> +
> + /// Marks the end of the acquire phase with C side `ww_acquire_done`.
> + ///
> + /// After calling this function, no more mutexes can be acquired with this context.
> + pub fn done(self: Pin<&mut Self>) {
> + // SAFETY: The context is pinned and valid.
> + unsafe { bindings::ww_acquire_done(self.inner.get()) };
> + }
> +
> + /// Returns a raw pointer to the inner `ww_acquire_ctx`.
> + fn as_ptr(&self) -> *mut bindings::ww_acquire_ctx {
> + self.inner.get()
> + }
> +}
> +
> +#[pinned_drop]
> +impl PinnedDrop for WwAcquireCtx<'_> {
> + fn drop(self: Pin<&mut Self>) {
> + // SAFETY: The context is being dropped and is pinned.
> + unsafe { bindings::ww_acquire_fini(self.inner.get()) };
> + }
> +}
> +
[...]
> +#[pin_data]
> +pub struct WwMutex<'a, T: ?Sized> {
> + _p: PhantomData<&'a WwClass>,
> + #[pin]
> + mutex: Opaque<bindings::ww_mutex>,
> + data: UnsafeCell<T>,
> +}
> +
> +// SAFETY: [`WwMutex`] can be shared between threads.
> +unsafe impl<T: ?Sized + Send> Send for WwMutex<'_, T> {}
> +// SAFETY: [`WwMutex`] can be safely accessed from multiple threads concurrently.
> +unsafe impl<T: ?Sized + Sync> Sync for WwMutex<'_, T> {}
I believe this requires `T` being `Send` as well, because if `&WwMutex`
is shared between threads, that means any thread can access `&mut T`
when the lock acquired.
> +
> +impl<'ww_class, T> WwMutex<'ww_class, T> {
> + /// Creates `Self` with calling `ww_mutex_init` inside.
> + pub fn new(t: T, ww_class: &'ww_class WwClass) -> impl PinInit<Self> {
> + let raw_ptr = ww_class.inner.get();
> + pin_init!(WwMutex {
> + mutex <- Opaque::ffi_init(|slot: *mut bindings::ww_mutex| {
> + // SAFETY: The caller guarantees that `ww_class` remains valid.
> + unsafe { bindings::ww_mutex_init(slot, raw_ptr) }
> + }),
> + data: UnsafeCell::new(t),
> + _p: PhantomData,
> + })
> + }
> +}
> +
[...]
> + /// Checks if the mutex is currently locked.
> + pub fn is_locked(&self) -> bool {
Did I miss a reply from you regarding:
https://lore.kernel.org/rust-for-linux/aFReIdlPPg4MmaYX@tardis.local/
no public is_lock() please. Do an assert_is_locked() instead. We need to
avoid users from abusing this.
> + // SAFETY: The mutex is pinned and valid.
> + unsafe { bindings::ww_mutex_is_locked(self.mutex.get()) }
> + }
> +
> + /// Returns a raw pointer to the inner mutex.
> + fn as_ptr(&self) -> *mut bindings::ww_mutex {
> + self.mutex.get()
> + }
> +}
> +
> +/// A guard that provides exclusive access to the data protected
> +/// by a [`WwMutex`].
> +///
> +/// # Invariants
> +///
> +/// The guard holds an exclusive lock on the associated [`WwMutex`]. The lock is held
> +/// for the entire lifetime of this guard and is automatically released when the
> +/// guard is dropped.
> +#[must_use = "the lock unlocks immediately when the guard is unused"]
> +pub struct WwMutexGuard<'a, T: ?Sized> {
> + mutex: &'a WwMutex<'a, T>,
> + _not_send: NotThreadSafe,
> +}
> +
> +// SAFETY: [`WwMutexGuard`] can be transferred across thread boundaries if the data can.
> +unsafe impl<T: ?Sized + Send> Send for WwMutexGuard<'_, T> {}
Nope, ww_mutex is still a mutex, you cannot acquire the lock in one task
and release the lock on another task.
> +
> +// SAFETY: [`WwMutexGuard`] can be shared between threads if the data can.
> +unsafe impl<T: ?Sized + Send + Sync> Sync for WwMutexGuard<'_, T> {}
You don't need the `Send` here? A `&WwMutexGuard` doesn't provide the
access to `&mut T`, so being `Sync` suffices.
Regards,
Boqun
> +
> +impl<'a, T: ?Sized> WwMutexGuard<'a, T> {
> + /// Creates a new guard for a locked mutex.
> + fn new(mutex: &'a WwMutex<'a, T>) -> Self {
> + Self {
> + mutex,
> + _not_send: NotThreadSafe,
> + }
> + }
> +}
> +
> +impl<T: ?Sized> core::ops::Deref for WwMutexGuard<'_, T> {
> + type Target = T;
> +
> + fn deref(&self) -> &Self::Target {
> + // SAFETY: We hold the lock, so we have exclusive access.
> + unsafe { &*self.mutex.data.get() }
> + }
> +}
> +
> +impl<T: ?Sized> core::ops::DerefMut for WwMutexGuard<'_, T> {
> + fn deref_mut(&mut self) -> &mut Self::Target {
> + // SAFETY: We hold the lock, so we have exclusive access.
> + unsafe { &mut *self.mutex.data.get() }
> + }
> +}
> +
> +impl<T: ?Sized> Drop for WwMutexGuard<'_, T> {
> + fn drop(&mut self) {
> + // SAFETY: We hold the lock and are about to release it.
> + unsafe { bindings::ww_mutex_unlock(self.mutex.as_ptr()) };
> + }
> +}
> --
> 2.49.0
>
Powered by blists - more mailing lists