rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Benno Lossin" <lossin@kernel.org>
To: "Onur Özkan" <work@onurozkan.dev>,
	linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org
Cc: <ojeda@kernel.org>, <alex.gaynor@gmail.com>,
	<boqun.feng@gmail.com>, <gary@garyguo.net>,
	<a.hindborg@kernel.org>, <aliceryhl@google.com>,
	<tmgross@umich.edu>, <dakr@kernel.org>, <peterz@infradead.org>,
	<mingo@redhat.com>, <will@kernel.org>, <longman@redhat.com>,
	<felipe_life@live.com>, <daniel@sedlak.dev>,
	<bjorn3_gh@protonmail.com>
Subject: Re: [PATCH v5 2/3] implement ww_mutex abstraction for the Rust tree
Date: Sun, 22 Jun 2025 11:18:24 +0200	[thread overview]
Message-ID: <DASY7BECFRCT.332X5ZHZMV2W@kernel.org> (raw)
In-Reply-To: <20250621184454.8354-3-work@onurozkan.dev>

On Sat Jun 21, 2025 at 8:44 PM CEST, Onur Özkan wrote:
> Adds Rust bindings for the kernel's `ww_mutex` infrastructure to enable
> deadlock-free acquisition of multiple related locks.
>
> The patch abstracts `ww_mutex.h` header and wraps the existing
> C `ww_mutex` with three main types:
>     - `WwClass` for grouping related mutexes
>     - `WwAcquireCtx` for tracking lock acquisition context
>     - `WwMutex<T>` for the actual lock

Going to repeat my question from the previous version:

    I don't know the design of `struct ww_mutex`, but from the code below I
    gathered that it has some special error return values that signify that
    one should release other locks.
    
    Did anyone think about making a more Rusty API that would allow one to
    try to lock multiple mutexes at the same time (in a specified order) and
    if it fails, it would do the resetting automatically?

I'm not familiar with ww_mutex, so I can't tell if there is something
good that we could do.

> Signed-off-by: Onur Özkan <work@onurozkan.dev>
> ---
>  rust/kernel/error.rs              |   1 +
>  rust/kernel/sync/lock.rs          |   1 +
>  rust/kernel/sync/lock/ww_mutex.rs | 421 ++++++++++++++++++++++++++++++
>  3 files changed, 423 insertions(+)
>  create mode 100644 rust/kernel/sync/lock/ww_mutex.rs

Thanks for splitting the tests into its own patch, but I still think
it's useful to do the abstractions for `ww_class`, `ww_acquire_ctx` and
`ww_mutex` separately.

> +/// Create static [`WwClass`] instances.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::{c_str, define_ww_class};
> +///
> +/// define_ww_class!(WOUND_WAIT_GLOBAL_CLASS, wound_wait, c_str!("wound_wait_global_class"));
> +/// define_ww_class!(WAIT_DIE_GLOBAL_CLASS, wait_die, c_str!("wait_die_global_class"));
> +/// ```
> +#[macro_export]
> +macro_rules! define_ww_class {
> +    ($name:ident, wound_wait, $class_name:expr) => {
> +        static $name: $crate::sync::lock::ww_mutex::WwClass =
> +            { $crate::sync::lock::ww_mutex::WwClass::new($class_name, false) };
> +    };
> +    ($name:ident, wait_die, $class_name:expr) => {
> +        static $name: $crate::sync::lock::ww_mutex::WwClass =
> +            { $crate::sync::lock::ww_mutex::WwClass::new($class_name, true) };
> +    };
> +}

I really don't see the value in having a macro here. The user can just
declare the static themselves.

> +
> +/// A group of mutexes that can participate in deadlock avoidance together.

This isn't a group of mutexes, but rather a class that can be used to
group mutexes together.

> +///
> +/// All mutexes that might be acquired together should use the same class.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::sync::lock::ww_mutex::WwClass;
> +/// use kernel::c_str;
> +/// use pin_init::stack_pin_init;
> +///
> +/// stack_pin_init!(let _wait_die_class = WwClass::new_wait_die(c_str!("graphics_buffers")));
> +/// stack_pin_init!(let _wound_wait_class = WwClass::new_wound_wait(c_str!("memory_pools")));
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +#[pin_data]
> +pub struct WwClass {
> +    #[pin]
> +    inner: Opaque<bindings::ww_class>,
> +}
> +
> +// SAFETY: [`WwClass`] is set up once and never modified. It's fine to share it across threads.
> +unsafe impl Sync for WwClass {}
> +// SAFETY: Doesn't hold anything thread-specific. It's safe to send to other threads.
> +unsafe impl Send for WwClass {}
> +
> +macro_rules! ww_class_init_helper {
> +    ($name:expr, $is_wait_die:expr) => {
> +        Opaque::new(bindings::ww_class {
> +            stamp: bindings::atomic_long_t { counter: 0 },
> +            acquire_name: $name.as_char_ptr(),
> +            mutex_name: $name.as_char_ptr(),
> +            is_wait_die: $is_wait_die as u32,
> +            // TODO: Replace with `bindings::lock_class_key::default()` once stabilized for `const`.
> +            //
> +            // SAFETY: This is always zero-initialized when defined with `DEFINE_WD_CLASS`
> +            // globally on C side.
> +            //
> +            // Ref: <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/ww_mutex.h?h=v6.16-rc2#n85>
> +            acquire_key: unsafe { core::mem::zeroed() },
> +            // TODO: Replace with `bindings::lock_class_key::default()` once stabilized for `const`.
> +            //
> +            // SAFETY: This is always zero-initialized when defined with `DEFINE_WD_CLASS`
> +            // globally on C side.
> +            //
> +            // Ref: <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/include/linux/ww_mutex.h?h=v6.16-rc2#n85>
> +            mutex_key: unsafe { core::mem::zeroed() },
> +        })
> +    };
> +}

Is this really needed? Can't we do without this macro?

> +
> +impl WwClass {
> +    /// Creates a [`WwClass`].
> +    ///
> +    /// It's `pub` only so it can be used by the `define_ww_class!` macro.
> +    ///
> +    /// You should not use this function directly. Use the `define_ww_class!`
> +    /// macro or call [`WwClass::new_wait_die`] or [`WwClass::new_wound_wait`] instead.
> +    pub const fn new(name: &'static CStr, is_wait_die: bool) -> Self {

This doesn't work together with `#[pin_data]`, you shouldn't return it
by-value if it is supposed to be pin-initialized.

Is this type address sensitive? If yes, this function needs to be
`unsafe`, if no, then we can remove `#[pin_data]`.

> +        WwClass {
> +            inner: ww_class_init_helper!(name, is_wait_die),
> +        }
> +    }
> +
> +    /// Creates wait-die [`WwClass`].
> +    pub fn new_wait_die(name: &'static CStr) -> impl PinInit<Self> {
> +        pin_init!(WwClass {
> +            inner: ww_class_init_helper!(name, true),
> +        })

This can just be `new(name, true)` instead.

> +    }
> +
> +    /// Creates wound-wait [`WwClass`].
> +    pub fn new_wound_wait(name: &'static CStr) -> impl PinInit<Self> {
> +        pin_init!(WwClass {
> +            inner: ww_class_init_helper!(name, false),
> +        })

Ditto with `false`.

> +    }
> +}
> +
> +/// An acquire context is used to group multiple mutex acquisitions together
> +/// for deadlock avoidance. It must be used when acquiring multiple mutexes
> +/// of the same class.

The first paragraph will be displayed by rustdoc in several summary
views, so it should be a single short sentence.

> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::sync::lock::ww_mutex::{WwClass, WwAcquireCtx, WwMutex};
> +/// use kernel::c_str;
> +/// use kernel::alloc::KBox;
> +/// use pin_init::stack_pin_init;
> +///
> +/// stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("my_class")));
> +///
> +/// // Create mutexes.
> +/// stack_pin_init!(let mutex1 = WwMutex::new(1, &class));
> +/// stack_pin_init!(let mutex2 = WwMutex::new(2, &class));

I don't think it makes sense to have examples using mutexes that are on
the stack. Please use `Arc` instead.

> +///
> +/// // Create acquire context for deadlock avoidance.
> +/// let mut ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?;
> +///
> +/// // Acquire multiple locks safely.
> +/// let guard1 = mutex1.lock(Some(&ctx))?;
> +/// let guard2 = mutex2.lock(Some(&ctx))?;
> +///
> +/// // Mark acquisition phase as complete.
> +/// ctx.as_mut().done();
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +#[pin_data(PinnedDrop)]
> +pub struct WwAcquireCtx<'a> {
> +    #[pin]
> +    inner: Opaque<bindings::ww_acquire_ctx>,
> +    _p: PhantomData<&'a WwClass>,
> +}

> +// 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> {}
> +
> +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();

s/raw_ptr/class/

> +        pin_init!(WwMutex {
> +            mutex <- Opaque::ffi_init(|slot: *mut bindings::ww_mutex| {
> +                // SAFETY: The caller guarantees that `ww_class` remains valid.

The caller doesn't need to guarantees that (and in fact they don't),
because it's a reference that lives until `'ww_class` which is captured
by `Self`. So the borrow checker guarantees that the class remains
valid.

---
Cheers,
Benno

> +                unsafe { bindings::ww_mutex_init(slot, raw_ptr) }
> +            }),
> +            data: UnsafeCell::new(t),
> +            _p: PhantomData,
> +        })
> +    }
> +}

  reply	other threads:[~2025-06-22  9:18 UTC|newest]

Thread overview: 53+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-06-21 18:44 [PATCH v5 0/3] rust: add `ww_mutex` support Onur Özkan
2025-06-21 18:44 ` [PATCH v5 1/3] rust: add C wrappers for `ww_mutex` inline functions Onur Özkan
2025-06-21 18:44 ` [PATCH v5 2/3] implement ww_mutex abstraction for the Rust tree Onur Özkan
2025-06-22  9:18   ` Benno Lossin [this message]
2025-06-23 13:04     ` Boqun Feng
2025-06-23 13:44       ` Benno Lossin
2025-06-23 14:47         ` Boqun Feng
2025-06-23 15:14           ` Benno Lossin
2025-06-23 17:11             ` Boqun Feng
2025-06-23 23:22               ` Benno Lossin
2025-06-24  5:34                 ` Onur
2025-06-24  8:20                   ` Benno Lossin
2025-06-24 12:31                     ` Onur
2025-06-24 12:48                       ` Benno Lossin
2025-07-07 13:39             ` Onur
2025-07-07 15:31               ` Benno Lossin
2025-07-07 18:06                 ` Onur
2025-07-07 19:48                   ` Benno Lossin
2025-07-08 14:21                     ` Onur
2025-08-01 21:22                     ` Daniel Almeida
2025-08-02 10:42                       ` Benno Lossin
2025-08-02 13:41                         ` Miguel Ojeda
2025-08-02 14:15                         ` Daniel Almeida
2025-08-02 20:58                           ` Benno Lossin
2025-08-05 15:18                             ` Daniel Almeida
2025-08-05  9:08                           ` Onur Özkan
2025-08-05 12:41                             ` Daniel Almeida
2025-08-05 13:50                               ` Onur Özkan
2025-06-23 11:51   ` Alice Ryhl
2025-06-23 13:26   ` Boqun Feng
2025-06-23 18:17     ` Onur
2025-06-23 21:54       ` Boqun Feng
2025-06-21 18:44 ` [PATCH v5 3/3] add KUnit coverage on Rust `ww_mutex` implementation Onur Özkan
2025-06-22  9:16 ` [PATCH v5 0/3] rust: add `ww_mutex` support Benno Lossin
2025-07-24 13:53 ` Onur Özkan
2025-07-29 17:15   ` Benno Lossin
2025-07-30 10:24     ` Onur Özkan
2025-07-30 10:55       ` Benno Lossin
2025-08-05 16:22   ` Lyude Paul
2025-08-05 17:56     ` Daniel Almeida
2025-08-06  5:57     ` Onur Özkan
2025-08-06 17:37       ` Lyude Paul
2025-08-06 19:30         ` Benno Lossin
2025-08-14 11:13           ` Onur Özkan
2025-08-14 12:38             ` Daniel Almeida
2025-08-14 15:56               ` Onur
2025-08-14 18:22                 ` Daniel Almeida
2025-08-18 12:56                   ` Onur Özkan
2025-09-01 10:05                     ` Onur Özkan
2025-09-01 12:28                       ` Daniel Almeida
2025-09-02 16:53                   ` Onur
2025-09-03  6:24                     ` Onur
2025-09-03 13:04                       ` Daniel Almeida

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=DASY7BECFRCT.332X5ZHZMV2W@kernel.org \
    --to=lossin@kernel.org \
    --cc=a.hindborg@kernel.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=dakr@kernel.org \
    --cc=daniel@sedlak.dev \
    --cc=felipe_life@live.com \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=longman@redhat.com \
    --cc=mingo@redhat.com \
    --cc=ojeda@kernel.org \
    --cc=peterz@infradead.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    --cc=will@kernel.org \
    --cc=work@onurozkan.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).