lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <DAQL7NBDD68Q.1ZS3PUR0AGN0R@kernel.org>
Date: Thu, 19 Jun 2025 16:42:15 +0200
From: "Benno Lossin" <lossin@...nel.org>
To: Onur Özkan <work@...rozkan.dev>,
 <rust-for-linux@...r.kernel.org>, <linux-kernel@...r.kernel.org>
Cc: <peterz@...radead.org>, <mingo@...hat.com>, <will@...nel.org>,
 <boqun.feng@...il.com>, <longman@...hat.com>, <ojeda@...nel.org>,
 <alex.gaynor@...il.com>, <gary@...yguo.net>, <bjorn3_gh@...tonmail.com>,
 <a.hindborg@...nel.org>, <aliceryhl@...gle.com>, <tmgross@...ch.edu>,
 <dakr@...nel.org>, <thatslyude@...il.com>
Subject: Re: [PATCH V3] implement `ww_mutex` abstraction for the Rust tree

On Thu Jun 19, 2025 at 4:06 PM CEST, Onur Özkan wrote:
> From: onur-ozkan <work@...rozkan.dev>

Can you double-check your name in your git config? This doesn't match
the Signed-off-by below.

> Adds Rust bindings for the kernel's `ww_mutex` infrastructure to enable
> deadlock-free acquisition of multiple related locks.
>
> The implementation 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
>
> Some of the kernel's `ww_mutex` functions are implemented as `static inline`,
> so they are inaccessible from Rust as bindgen can't generate code on them.
> The `rust/helpers/ww_mutex.c` file provides C function wrappers around these inline
> implementations, so bindgen can see them and generate the corresponding Rust code.
>
> Link: https://rust-for-linux.zulipchat.com/#narrow/channel/291566-Library/topic/Writing.20up.20wrappers.20for.20ww_mutex.3F/with/524269974
> Suggested-by: thatslyude@...il.com
> Signed-off-by: Onur Özkan <work@...rozkan.dev>
> ---
>  rust/helpers/helpers.c            |   1 +
>  rust/helpers/ww_mutex.c           |  39 +++
>  rust/kernel/error.rs              |   1 +
>  rust/kernel/sync/lock.rs          |   1 +
>  rust/kernel/sync/lock/ww_mutex.rs | 556 ++++++++++++++++++++++++++++++
>  5 files changed, 598 insertions(+)
>  create mode 100644 rust/helpers/ww_mutex.c
>  create mode 100644 rust/kernel/sync/lock/ww_mutex.rs

Can you split this patch into multiple smaller ones? For example all the
tests can be done separately as well as the abstractions for `ww_class`,
`ww_acquire_ctx` and `ww_mutex`.

Thanks.

> diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
> index 3dee3139fcd4..94d8014b236b 100644
> --- a/rust/kernel/error.rs
> +++ b/rust/kernel/error.rs
> @@ -84,6 +84,7 @@ macro_rules! declare_err {
>      declare_err!(EIOCBQUEUED, "iocb queued, will get completion event.");
>      declare_err!(ERECALLCONFLICT, "Conflict with recalled state.");
>      declare_err!(ENOGRACE, "NFS file lock reclaim refused.");
> +    declare_err!(EDEADLK, "Resource deadlock avoided.");
>  }
>
>  /// Generic integer kernel error.
> diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
> index e82fa5be289c..8824ebc81084 100644
> --- a/rust/kernel/sync/lock.rs
> +++ b/rust/kernel/sync/lock.rs
> @@ -15,6 +15,7 @@
>
>  pub mod mutex;
>  pub mod spinlock;
> +pub mod ww_mutex;
>
>  pub(super) mod global;
>  pub use global::{GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedBy};
> diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_mutex.rs
> new file mode 100644
> index 000000000000..888db286fc4b
> --- /dev/null
> +++ b/rust/kernel/sync/lock/ww_mutex.rs
> @@ -0,0 +1,556 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! A kernel Wound/Wait Mutex.
> +//!
> +//! This module provides Rust abstractions for the Linux kernel's `ww_mutex` implementation,
> +//! which provides deadlock avoidance through a wait-wound or wait-die algorithm.
> +
> +use crate::error::{to_result, Result};
> +use crate::prelude::EBUSY;
> +use crate::{bindings, str::CStr, types::Opaque};
> +use core::marker::PhantomData;
> +use core::{cell::UnsafeCell, pin::Pin};
> +use macros::kunit_tests;
> +use pin_init::{pin_data, pin_init, pinned_drop, PinInit};
> +
> +/// A helper macro for creating static `WwClass` instances.

s/A helper macro for creating/Create/

> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::c_str;
> +/// use kernel::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 {

What's the reason for this being a macro?

> +    ($name:ident, wound_wait, $class_name:expr) => {
> +        static $name: $crate::sync::lock::ww_mutex::WwClass = {
> +            $crate::sync::lock::ww_mutex::WwClass {
> +                inner: $crate::types::Opaque::new($crate::bindings::ww_class {
> +                    stamp: $crate::bindings::atomic_long_t { counter: 0 },
> +                    acquire_name: $class_name.as_char_ptr(),
> +                    mutex_name: $class_name.as_char_ptr(),
> +                    is_wait_die: 0,
> +                    // 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://github.com/torvalds/linux/blob/master/include/linux/ww_mutex.h#L85-L89
> +                    acquire_key: unsafe { core::mem::zeroed() },

This (and the others) can use [1] instead of `unsafe`. That series will
most likely land in v6.17.

[1]: https://lore.kernel.org/all/20250523145125.523275-1-lossin@kernel.org

> +                    // 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://github.com/torvalds/linux/blob/master/include/linux/ww_mutex.h#L85-L89
> +                    mutex_key: unsafe { core::mem::zeroed() },
> +                }),
> +            }
> +        };
> +    };
> +    ($name:ident, wait_die, $class_name:expr) => {
> +        static $name: $crate::sync::lock::ww_mutex::WwClass = {
> +            $crate::sync::lock::ww_mutex::WwClass {
> +                inner: $crate::types::Opaque::new($crate::bindings::ww_class {
> +                    stamp: $crate::bindings::atomic_long_t { counter: 0 },
> +                    acquire_name: $class_name.as_char_ptr(),
> +                    mutex_name: $class_name.as_char_ptr(),
> +                    is_wait_die: 1,
> +                    // 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://github.com/torvalds/linux/blob/master/include/linux/ww_mutex.h#L85-L89
> +                    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://github.com/torvalds/linux/blob/master/include/linux/ww_mutex.h#L85-L89
> +                    mutex_key: unsafe { core::mem::zeroed() },
> +                }),
> +            }
> +        };
> +    };
> +}
> +
> +/// Implementation of C side `ww_class`.

This isn't informative at all. The names already match, so I wouldn't
have thought otherwise.

> +///
> +/// Represents a group of mutexes that can participate in deadlock avoidance 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 {
> +    /// Wrapper of the underlying C `ww_class`.
> +    ///
> +    /// You should not construct this type manually. Use the `define_ww_class` macro
> +    /// or call `WwClass::new_wait_die` or `WwClass::new_wound_wait` instead.
> +    #[pin]
> +    pub inner: Opaque<bindings::ww_class>,

Why `pub`? Abstractions normally don't expose `Opaque` wrappers for
bindings. Especially because this type is marked `#[pin_data]` this
seems wrong, because this would allow people to construct it in a
non-pinned state & also non-initialized state.

> +}
> +
> +// SAFETY: `WwClass` can be safely accessed from multiple threads concurrently.

Why? This is supposed to justify that.

> +unsafe impl Sync for WwClass {}
> +// SAFETY: `WwClass` can be shared between threads.
> +unsafe impl Send for WwClass {}
> +
> +impl WwClass {
> +    fn new(name: &'static CStr, is_wait_die: bool) -> impl PinInit<Self> {
> +        pin_init!(WwClass {
> +            inner: 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,
> +                acquire_key: bindings::lock_class_key::default(),
> +                mutex_key: bindings::lock_class_key::default(),
> +            })
> +        })
> +    }
> +
> +    /// Creates wait-die `WwClass` that wraps C side `ww_class`.
> +    pub fn new_wait_die(name: &'static CStr) -> impl PinInit<Self> {
> +        Self::new(name, true)
> +    }
> +
> +    /// Creates wound-wait `WwClass` that wraps C side `ww_class`.
> +    pub fn new_wound_wait(name: &'static CStr) -> impl PinInit<Self> {
> +        Self::new(name, false)
> +    }
> +}
> +
> +/// Implementation of C side `ww_acquire_ctx`.

This also isn't informative.

> +///
> +/// 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.
> +///

[...]

> +/// A wound/wait mutex backed with C side `ww_mutex`.
> +///
> +/// This is a mutual exclusion primitive that provides deadlock avoidance when
> +/// acquiring multiple locks of the same class.
> +///
> +/// # Examples
> +///
> +/// ## Basic Usage
> +///
> +/// ```
> +/// use kernel::sync::lock::ww_mutex::{WwClass, WwMutex};
> +/// use kernel::c_str;
> +/// use pin_init::stack_pin_init;
> +///
> +/// stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("buffer_class")));
> +/// stack_pin_init!(let mutex = WwMutex::new(42, &class));
> +///
> +/// // Simple lock without context
> +/// let guard = mutex.as_ref().lock(None).unwrap();
> +/// assert_eq!(*guard, 42);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +///
> +/// ## Multiple Locks with KBox
> +///
> +/// ```
> +/// use kernel::sync::lock::ww_mutex::{WwClass, WwAcquireCtx, WwMutex};
> +/// use kernel::alloc::KBox;
> +/// use kernel::c_str;
> +/// use kernel::error::code::*;
> +///
> +/// let class = KBox::pin_init(WwClass::new_wait_die(c_str!("resource_class")), GFP_KERNEL).unwrap();
> +/// let mutex_a = KBox::pin_init(WwMutex::new("Resource A", &class), GFP_KERNEL).unwrap();
> +/// let mutex_b = KBox::pin_init(WwMutex::new("Resource B", &class), GFP_KERNEL).unwrap();

Storing mutexes in `KBox` doesn't really make sense there might be
special cases, but for this example, we should use `Arc` instead.

> +///
> +/// let mut ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL).unwrap();
> +///
> +/// // Try to acquire both locks
> +/// let guard_a = match mutex_a.as_ref().lock(Some(&ctx)) {
> +///     Ok(guard) => guard,
> +///     Err(e) if e == EDEADLK => {
> +///         // Deadlock detected, use slow path
> +///         mutex_a.as_ref().lock_slow(&ctx).unwrap()
> +///     }
> +///     Err(e) => return Err(e),
> +/// };
> +///
> +/// let guard_b = mutex_b.as_ref().lock(Some(&ctx)).unwrap();
> +/// ctx.as_mut().done();
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +#[pin_data]
> +pub struct WwMutex<'a, T: ?Sized> {
> +    _p: PhantomData<&'a WwClass>,
> +    #[pin]
> +    mutex: Opaque<bindings::ww_mutex>,
> +    #[pin]
> +    data: UnsafeCell<T>,

You marked the data as `#[pin]`, but `lock()` gives access to
`WwMutexGuard` which implements `DerefMut`, circumventing the pinning.
So either remove the `#[pin]` on `data`, or only return
`Pin<WwMutexGuard>`.

> +}
> +
> +// 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();
> +        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,
> +        })
> +    }
> +}
> +
> +impl<T: ?Sized> WwMutex<'_, T> {
> +    /// Locks the mutex with the given acquire context.
> +    pub fn lock<'a>(
> +        self: Pin<&'a Self>,

This receiver type is pretty annoying, because `Arc<T>` and
`Pin<Box<T>>` deref to `&T` and thus you don't get `Pin<&T>`. Just use
`&self` instead, if your type is only constructible using `impl
PinInit<Self>`, then it's guaranteed that all instances of the type are
pinned.

> +        ctx: Option<&WwAcquireCtx<'_>>,
> +    ) -> Result<WwMutexGuard<'a, T>> {
> +        // SAFETY: The mutex is pinned and valid.
> +        let ret = unsafe {
> +            bindings::ww_mutex_lock(
> +                self.mutex.get(),
> +                ctx.map_or(core::ptr::null_mut(), |c| c.as_ptr()),
> +            )
> +        };
> +
> +        to_result(ret)?;
> +
> +        Ok(WwMutexGuard::new(self))
> +    }

[...]

> +/// A guard that provides exclusive access to the data protected by a
> +// [`WwMutex`] (a.k.a `ww_mutex` on the C side).
> +pub struct WwMutexGuard<'a, T: ?Sized> {
> +    mutex: Pin<&'a WwMutex<'a, T>>,
> +}
> +
> +// SAFETY: `WwMutexGuard` can be transferred across thread boundaries if the data can.
> +unsafe impl<T: ?Sized + Send> Send for WwMutexGuard<'_, T> {}
> +
> +// SAFETY: `WwMutexGuard` can be shared between threads if the data can.
> +unsafe impl<T: ?Sized + Send + Sync> Sync for WwMutexGuard<'_, T> {}
> +
> +impl<'a, T: ?Sized> WwMutexGuard<'a, T> {
> +    /// Creates a new guard for a locked mutex.
> +    fn new(mutex: Pin<&'a WwMutex<'a, T>>) -> Self {
> +        Self { mutex }
> +    }
> +}
> +
> +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.

This needs a type invariant on `Self`.

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

What happens when this guard is forgotten and the lock is never
released?

---
Cheers,
Benno

> +    }
> +}

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ