linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 0/7] rust: add `ww_mutex` support
@ 2025-09-03 13:13 Onur Özkan
  2025-09-03 13:13 ` [PATCH v6 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
                   ` (6 more replies)
  0 siblings, 7 replies; 10+ messages in thread
From: Onur Özkan @ 2025-09-03 13:13 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary,
	a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
	longman, felipe_life, daniel, bjorn3_gh, daniel.almeida,
	Onur Özkan

Changes made in v6:
  - Added `unpinned_new` constructor for `WwClass` and updated global macros.
  - Changed all tests (and docs) to use Arc/KBox instead of `stack_pin_init`
    for `WwMutex` and `WwAcquireCtx`.
  - Added `LockKind` and `lock_common` helper to unify locking logic.
  - Added context-based and context-free locking functions for `WwMutex`.
  - Added `ww_mutex/exec` module, a high-level API with auto `EDEADLK`
    handling mechanism.

Onur Özkan (7):
  rust: add C wrappers for ww_mutex inline functions
  rust: implement `WwClass` for ww_mutex support
  rust: implement `WwMutex`, `WwAcquireCtx` and `WwMutexGuard`
  add KUnit coverage on Rust ww_mutex implementation
  rust: ww_mutex: add context-free locking functions
  rust: ww_mutex/exec: add high-level API
  add KUnit coverage on ww_mutex/exec implementation

 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      | 634 +++++++++++++++++++++++++
 rust/kernel/sync/lock/ww_mutex/exec.rs | 324 +++++++++++++
 6 files changed, 1000 insertions(+)
 create mode 100644 rust/helpers/ww_mutex.c
 create mode 100644 rust/kernel/sync/lock/ww_mutex.rs
 create mode 100644 rust/kernel/sync/lock/ww_mutex/exec.rs

--
2.50.0


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v6 1/7] rust: add C wrappers for ww_mutex inline functions
  2025-09-03 13:13 [PATCH v6 0/7] rust: add `ww_mutex` support Onur Özkan
@ 2025-09-03 13:13 ` Onur Özkan
  2025-09-03 13:46   ` Daniel Almeida
  2025-09-03 13:13 ` [PATCH v6 2/7] rust: implement `WwClass` for ww_mutex support Onur Özkan
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 10+ messages in thread
From: Onur Özkan @ 2025-09-03 13:13 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary,
	a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
	longman, felipe_life, daniel, bjorn3_gh, daniel.almeida,
	Onur Özkan

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. This patch provides C function wrappers
around these inline implementations, so bindgen can see them and generate
the corresponding Rust code.

Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
 rust/helpers/helpers.c  |  1 +
 rust/helpers/ww_mutex.c | 39 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 40 insertions(+)
 create mode 100644 rust/helpers/ww_mutex.c

diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 7cf7fe95e41d..4c789c5537b1 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -50,4 +50,5 @@
 #include "vmalloc.c"
 #include "wait.c"
 #include "workqueue.c"
+#include "ww_mutex.c"
 #include "xarray.c"
diff --git a/rust/helpers/ww_mutex.c b/rust/helpers/ww_mutex.c
new file mode 100644
index 000000000000..61a487653394
--- /dev/null
+++ b/rust/helpers/ww_mutex.c
@@ -0,0 +1,39 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/ww_mutex.h>
+
+void rust_helper_ww_mutex_init(struct ww_mutex *lock, struct ww_class *ww_class)
+{
+	ww_mutex_init(lock, ww_class);
+}
+
+void rust_helper_ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *ww_class)
+{
+	ww_acquire_init(ctx, ww_class);
+}
+
+void rust_helper_ww_acquire_done(struct ww_acquire_ctx *ctx)
+{
+	ww_acquire_done(ctx);
+}
+
+void rust_helper_ww_acquire_fini(struct ww_acquire_ctx *ctx)
+{
+	ww_acquire_fini(ctx);
+}
+
+void rust_helper_ww_mutex_lock_slow(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+{
+	ww_mutex_lock_slow(lock, ctx);
+}
+
+int rust_helper_ww_mutex_lock_slow_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+{
+	return ww_mutex_lock_slow_interruptible(lock, ctx);
+}
+
+bool rust_helper_ww_mutex_is_locked(struct ww_mutex *lock)
+{
+	return ww_mutex_is_locked(lock);
+}
+
--
2.50.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v6 2/7] rust: implement `WwClass` for ww_mutex support
  2025-09-03 13:13 [PATCH v6 0/7] rust: add `ww_mutex` support Onur Özkan
  2025-09-03 13:13 ` [PATCH v6 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
@ 2025-09-03 13:13 ` Onur Özkan
  2025-09-03 16:06   ` Boqun Feng
  2025-09-03 13:13 ` [PATCH v6 3/7] rust: implement `WwMutex`, `WwAcquireCtx` and `WwMutexGuard` Onur Özkan
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 10+ messages in thread
From: Onur Özkan @ 2025-09-03 13:13 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary,
	a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
	longman, felipe_life, daniel, bjorn3_gh, daniel.almeida,
	Onur Özkan

Adds the `WwClass` type, the first step in supporting
`ww_mutex` in Rust. `WwClass` represents a class of ww
mutexes used for deadlock avoidance for supporting both
wait-die and wound-wait semantics.

Also adds the `define_ww_class!` macro for safely declaring
static instances.

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

diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
index 27202beef90c..5b320c2b28c1 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..ca5b4525ace6
--- /dev/null
+++ b/rust/kernel/sync/lock/ww_mutex.rs
@@ -0,0 +1,136 @@
+// 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.
+//!
+//! C header: [`include/linux/ww_mutex.h`](srctree/include/linux/ww_mutex.h)
+//!
+//! For more information: <https://docs.kernel.org/locking/ww-mutex-design.html>
+
+use crate::bindings;
+use crate::prelude::*;
+use crate::types::Opaque;
+
+/// 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 =
+            // SAFETY: This is `static`, so address is fixed and won't move.
+            unsafe { $crate::sync::lock::ww_mutex::WwClass::unpinned_new($class_name, false) };
+    };
+    ($name:ident, wait_die, $class_name:expr) => {
+        static $name: $crate::sync::lock::ww_mutex::WwClass =
+            // SAFETY: This is `static`, so address is fixed and won't move.
+            unsafe { $crate::sync::lock::ww_mutex::WwClass::unpinned_new($class_name, true) };
+    };
+}
+
+/// A class used to group mutexes together for deadlock avoidance.
+///
+/// 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 {}
+
+impl WwClass {
+    /// Creates an unpinned [`WwClass`].
+    ///
+    /// # Safety
+    ///
+    /// Caller must guarantee that the returned value is not moved after creation.
+    pub const unsafe fn unpinned_new(name: &'static CStr, is_wait_die: bool) -> Self {
+        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,
+                // 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() },
+            }),
+        }
+    }
+
+    /// Creates a [`WwClass`].
+    ///
+    /// 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.
+    const fn new(name: &'static CStr, is_wait_die: bool) -> Self {
+        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,
+                // 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() },
+            }),
+        }
+    }
+
+    /// Creates wait-die [`WwClass`].
+    pub fn new_wait_die(name: &'static CStr) -> impl PinInit<Self> {
+        Self::new(name, true)
+    }
+
+    /// Creates wound-wait [`WwClass`].
+    pub fn new_wound_wait(name: &'static CStr) -> impl PinInit<Self> {
+        Self::new(name, false)
+    }
+}
--
2.50.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v6 3/7] rust: implement `WwMutex`, `WwAcquireCtx` and `WwMutexGuard`
  2025-09-03 13:13 [PATCH v6 0/7] rust: add `ww_mutex` support Onur Özkan
  2025-09-03 13:13 ` [PATCH v6 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
  2025-09-03 13:13 ` [PATCH v6 2/7] rust: implement `WwClass` for ww_mutex support Onur Özkan
@ 2025-09-03 13:13 ` Onur Özkan
  2025-09-03 13:13 ` [PATCH v6 4/7] add KUnit coverage on Rust ww_mutex implementation Onur Özkan
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Onur Özkan @ 2025-09-03 13:13 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary,
	a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
	longman, felipe_life, daniel, bjorn3_gh, daniel.almeida,
	Onur Özkan

Includes full locking API (lock, try_lock, slow path, interruptible variants)
and integration with kernel bindings.

Also adds the `EDEADLK` error code to support deadlock detection.

Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
 rust/kernel/error.rs              |   1 +
 rust/kernel/sync/lock/ww_mutex.rs | 289 +++++++++++++++++++++++++++++-
 2 files changed, 289 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index a41de293dcd1..560de6117094 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -64,6 +64,7 @@ macro_rules! declare_err {
     declare_err!(EPIPE, "Broken pipe.");
     declare_err!(EDOM, "Math argument out of domain of func.");
     declare_err!(ERANGE, "Math result not representable.");
+    declare_err!(EDEADLK, "Resource deadlock avoided.");
     declare_err!(EOVERFLOW, "Value too large for defined data type.");
     declare_err!(ETIMEDOUT, "Connection timed out.");
     declare_err!(ERESTARTSYS, "Restart the system call.");
diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_mutex.rs
index ca5b4525ace6..314360632953 100644
--- a/rust/kernel/sync/lock/ww_mutex.rs
+++ b/rust/kernel/sync/lock/ww_mutex.rs
@@ -10,8 +10,11 @@
 //! For more information: <https://docs.kernel.org/locking/ww-mutex-design.html>

 use crate::bindings;
+use crate::error::to_result;
 use crate::prelude::*;
-use crate::types::Opaque;
+use crate::types::{NotThreadSafe, Opaque};
+use core::cell::UnsafeCell;
+use core::marker::PhantomData;

 /// Create static [`WwClass`] instances.
 ///
@@ -134,3 +137,287 @@ pub fn new_wound_wait(name: &'static CStr) -> impl PinInit<Self> {
         Self::new(name, false)
     }
 }
+
+/// Groups multiple mutex acquisitions together for deadlock avoidance.
+///
+/// Must be used when acquiring multiple mutexes of the same class.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::sync::lock::ww_mutex::{WwClass, WwAcquireCtx, WwMutex};
+/// use kernel::c_str;
+/// use kernel::sync::Arc;
+/// use pin_init::stack_pin_init;
+///
+/// stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("my_class")));
+///
+/// // Create mutexes.
+/// let mutex1 = Arc::pin_init(WwMutex::new(1, &class), GFP_KERNEL)?;
+/// let mutex2 = Arc::pin_init(WwMutex::new(2, &class), GFP_KERNEL)?;
+///
+/// // Create acquire context for deadlock avoidance.
+/// let ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?;
+///
+/// // Acquire multiple locks safely.
+/// let guard1 = ctx.lock(&mutex1)?;
+/// let guard2 = ctx.lock(&mutex2)?;
+///
+/// // Mark acquisition phase as complete.
+/// ctx.done();
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[pin_data(PinnedDrop)]
+pub struct WwAcquireCtx<'a> {
+    #[pin]
+    inner: Opaque<bindings::ww_acquire_ctx>,
+    _p: PhantomData<&'a WwClass>,
+}
+
+impl<'ww_class> WwAcquireCtx<'ww_class> {
+    /// Initializes `Self` with calling C side `ww_acquire_init` inside.
+    pub fn new(ww_class: &'ww_class WwClass) -> impl PinInit<Self> {
+        let class = ww_class.inner.get();
+        pin_init!(WwAcquireCtx {
+            inner <- Opaque::ffi_init(|slot: *mut bindings::ww_acquire_ctx| {
+                // SAFETY: `ww_class` is valid for the lifetime `'ww_class` captured by `Self`.
+                unsafe { bindings::ww_acquire_init(slot, class) }
+            }),
+            _p: PhantomData
+        })
+    }
+
+    /// Marks the end of the acquire phase.
+    ///
+    /// After calling this function, no more mutexes can be acquired with this context.
+    pub fn done(&self) {
+        // SAFETY: The context is pinned and valid.
+        unsafe { bindings::ww_acquire_done(self.inner.get()) };
+    }
+
+    /// Locks the given mutex.
+    pub fn lock<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Result<WwMutexGuard<'a, T>> {
+        // SAFETY: The mutex is pinned and valid.
+        let ret = unsafe { bindings::ww_mutex_lock(ww_mutex.mutex.get(), self.inner.get()) };
+
+        to_result(ret)?;
+
+        Ok(WwMutexGuard::new(ww_mutex))
+    }
+
+    /// Similar to `lock`, but can be interrupted by signals.
+    pub fn lock_interruptible<'a, T>(
+        &'a self,
+        ww_mutex: &'a WwMutex<'a, T>,
+    ) -> Result<WwMutexGuard<'a, T>> {
+        // SAFETY: The mutex is pinned and valid.
+        let ret = unsafe {
+            bindings::ww_mutex_lock_interruptible(ww_mutex.mutex.get(), self.inner.get())
+        };
+
+        to_result(ret)?;
+
+        Ok(WwMutexGuard::new(ww_mutex))
+    }
+
+    /// Locks the given mutex using the slow path.
+    ///
+    /// This function should be used when `lock` fails (typically due to a potential deadlock).
+    pub fn lock_slow<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Result<WwMutexGuard<'a, T>> {
+        // SAFETY: The mutex is pinned and valid, and we're in the slow path.
+        unsafe { bindings::ww_mutex_lock_slow(ww_mutex.mutex.get(), self.inner.get()) };
+
+        Ok(WwMutexGuard::new(ww_mutex))
+    }
+
+    /// Similar to `lock_slow`, but can be interrupted by signals.
+    pub fn lock_slow_interruptible<'a, T>(
+        &'a self,
+        ww_mutex: &'a WwMutex<'a, T>,
+    ) -> Result<WwMutexGuard<'a, T>> {
+        // SAFETY: The mutex is pinned and valid, and we are in the slow path.
+        let ret = unsafe {
+            bindings::ww_mutex_lock_slow_interruptible(ww_mutex.mutex.get(), self.inner.get())
+        };
+
+        to_result(ret)?;
+
+        Ok(WwMutexGuard::new(ww_mutex))
+    }
+
+    /// Tries to lock the mutex without blocking.
+    ///
+    /// Unlike `lock`, no deadlock handling is performed.
+    pub fn try_lock<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Result<WwMutexGuard<'a, T>> {
+        // SAFETY: The mutex is pinned and valid.
+        let ret = unsafe { bindings::ww_mutex_trylock(ww_mutex.mutex.get(), self.inner.get()) };
+
+        if ret == 0 {
+            return Err(EBUSY);
+        } else {
+            to_result(ret)?;
+        }
+
+        Ok(WwMutexGuard::new(ww_mutex))
+    }
+}
+
+#[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()) };
+    }
+}
+
+/// 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::c_str;
+/// use kernel::sync::Arc;
+/// use kernel::sync::lock::ww_mutex::{WwClass, WwAcquireCtx, WwMutex };
+/// use pin_init::stack_pin_init;
+///
+/// stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("buffer_class")));
+/// let mutex = Arc::pin_init(WwMutex::new(42, &class), GFP_KERNEL)?;
+///
+/// let ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?;
+///
+/// let guard = ctx.lock(&mutex)?;
+/// assert_eq!(*guard, 42);
+///
+/// # Ok::<(), Error>(())
+/// ```
+///
+/// ## Multiple Locks
+///
+/// ```
+/// use kernel::c_str;
+/// use kernel::prelude::*;
+/// use kernel::sync::Arc;
+/// use kernel::sync::lock::ww_mutex::{WwClass, WwAcquireCtx, WwMutex};
+/// use pin_init::stack_pin_init;
+///
+/// stack_pin_init!(let class = WwClass::new_wait_die(c_str!("resource_class")));
+/// let mutex_a = Arc::pin_init(WwMutex::new("Resource A", &class), GFP_KERNEL)?;
+/// let mutex_b = Arc::pin_init(WwMutex::new("Resource B", &class), GFP_KERNEL)?;
+///
+/// let ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?;
+///
+/// // Try to acquire both locks.
+/// let guard_a = match ctx.lock(&mutex_a) {
+///     Ok(guard) => guard,
+///     Err(e) if e == EDEADLK => {
+///         // Deadlock detected, use slow path.
+///         ctx.lock_slow(&mutex_a)?
+///     }
+///     Err(e) => return Err(e),
+/// };
+///
+/// let guard_b = ctx.lock(&mutex_b)?;
+/// ctx.done();
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[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 + Send + 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 class = ww_class.inner.get();
+        pin_init!(WwMutex {
+            mutex <- Opaque::ffi_init(|slot: *mut bindings::ww_mutex| {
+                // SAFETY: `ww_class` is valid for the lifetime `'ww_class` captured by `Self`.
+                unsafe { bindings::ww_mutex_init(slot, class) }
+            }),
+            data: UnsafeCell::new(t),
+            _p: PhantomData,
+        })
+    }
+}
+
+impl<T: ?Sized> WwMutex<'_, T> {
+    /// Returns a raw pointer to the inner mutex.
+    fn as_ptr(&self) -> *mut bindings::ww_mutex {
+        self.mutex.get()
+    }
+
+    /// Checks if the mutex is currently locked.
+    ///
+    /// Intended for internal tests only and should not be used
+    /// anywhere else.
+    #[cfg(CONFIG_KUNIT)]
+    fn is_locked(&self) -> bool {
+        // SAFETY: The mutex is pinned and valid.
+        unsafe { bindings::ww_mutex_is_locked(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 shared between threads if the data can.
+unsafe impl<T: ?Sized + Sync> Sync for WwMutexGuard<'_, T> {}
+
+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.50.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v6 4/7] add KUnit coverage on Rust ww_mutex implementation
  2025-09-03 13:13 [PATCH v6 0/7] rust: add `ww_mutex` support Onur Özkan
                   ` (2 preceding siblings ...)
  2025-09-03 13:13 ` [PATCH v6 3/7] rust: implement `WwMutex`, `WwAcquireCtx` and `WwMutexGuard` Onur Özkan
@ 2025-09-03 13:13 ` Onur Özkan
  2025-09-03 13:13 ` [PATCH v6 5/7] rust: ww_mutex: add context-free locking functions Onur Özkan
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 10+ messages in thread
From: Onur Özkan @ 2025-09-03 13:13 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary,
	a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
	longman, felipe_life, daniel, bjorn3_gh, daniel.almeida,
	Onur Özkan

Adds coverage around the core `ww_mutex` functionality

Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
 rust/kernel/sync/lock/ww_mutex.rs | 127 ++++++++++++++++++++++++++++++
 1 file changed, 127 insertions(+)

diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_mutex.rs
index 314360632953..d289718d2c98 100644
--- a/rust/kernel/sync/lock/ww_mutex.rs
+++ b/rust/kernel/sync/lock/ww_mutex.rs
@@ -421,3 +421,130 @@ fn drop(&mut self) {
         unsafe { bindings::ww_mutex_unlock(self.mutex.as_ptr()) };
     }
 }
+
+#[kunit_tests(rust_kernel_ww_mutex)]
+mod tests {
+    use crate::c_str;
+    use crate::prelude::*;
+    use crate::sync::Arc;
+    use pin_init::stack_pin_init;
+
+    use super::*;
+
+    // A simple coverage on `define_ww_class` macro.
+    define_ww_class!(TEST_WOUND_WAIT_CLASS, wound_wait, c_str!("test_wound_wait"));
+    define_ww_class!(TEST_WAIT_DIE_CLASS, wait_die, c_str!("test_wait_die"));
+
+    #[test]
+    fn test_ww_mutex_basic_lock_unlock() -> Result {
+        stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("test_mutex_class")));
+
+        let mutex = Arc::pin_init(WwMutex::new(42, &class), GFP_KERNEL)?;
+
+        let ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?;
+
+        // Lock.
+        let guard = ctx.lock(&mutex)?;
+        assert_eq!(*guard, 42);
+
+        // Drop the lock.
+        drop(guard);
+
+        // Lock it again.
+        let mut guard = ctx.lock(&mutex)?;
+        *guard = 100;
+        assert_eq!(*guard, 100);
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_ww_mutex_trylock() -> Result {
+        stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("trylock_class")));
+
+        let mutex = Arc::pin_init(WwMutex::new(123, &class), GFP_KERNEL)?;
+
+        let ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?;
+
+        // `try_lock` on unlocked mutex should succeed.
+        let guard = ctx.try_lock(&mutex)?;
+        assert_eq!(*guard, 123);
+
+        // Now it should fail immediately as it's already locked.
+        assert!(ctx.try_lock(&mutex).is_err());
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_ww_mutex_is_locked() -> Result {
+        stack_pin_init!(let class = WwClass::new_wait_die(c_str!("locked_check_class")));
+
+        let mutex = Arc::pin_init(WwMutex::new("hello", &class), GFP_KERNEL)?;
+
+        let ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?;
+
+        // Should not be locked initially.
+        assert!(!mutex.is_locked());
+
+        let guard = ctx.lock(&mutex)?;
+        assert!(mutex.is_locked());
+
+        drop(guard);
+        assert!(!mutex.is_locked());
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_ww_acquire_context() -> Result {
+        stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("ctx_class")));
+
+        let mutex1 = Arc::pin_init(WwMutex::new(1, &class), GFP_KERNEL)?;
+        let mutex2 = Arc::pin_init(WwMutex::new(2, &class), GFP_KERNEL)?;
+
+        let ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?;
+
+        // Acquire multiple mutexes with the same context.
+        let guard1 = ctx.lock(&mutex1)?;
+        let guard2 = ctx.lock(&mutex2)?;
+
+        assert_eq!(*guard1, 1);
+        assert_eq!(*guard2, 2);
+
+        ctx.done();
+
+        // We shouldn't be able to lock once it's `done`.
+        assert!(ctx.lock(&mutex1).is_err());
+        assert!(ctx.lock(&mutex2).is_err());
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_with_global_classes() -> Result {
+        let wound_wait_mutex =
+            Arc::pin_init(WwMutex::new(100, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+        let wait_die_mutex = Arc::pin_init(WwMutex::new(200, &TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+
+        let ww_ctx = KBox::pin_init(WwAcquireCtx::new(&TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+        let wd_ctx = KBox::pin_init(WwAcquireCtx::new(&TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+
+        let ww_guard = ww_ctx.lock(&wound_wait_mutex)?;
+        let wd_guard = wd_ctx.lock(&wait_die_mutex)?;
+
+        assert_eq!(*ww_guard, 100);
+        assert_eq!(*wd_guard, 200);
+
+        assert!(wound_wait_mutex.is_locked());
+        assert!(wait_die_mutex.is_locked());
+
+        drop(ww_guard);
+        drop(wd_guard);
+
+        assert!(!wound_wait_mutex.is_locked());
+        assert!(!wait_die_mutex.is_locked());
+
+        Ok(())
+    }
+}
--
2.50.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v6 5/7] rust: ww_mutex: add context-free locking functions
  2025-09-03 13:13 [PATCH v6 0/7] rust: add `ww_mutex` support Onur Özkan
                   ` (3 preceding siblings ...)
  2025-09-03 13:13 ` [PATCH v6 4/7] add KUnit coverage on Rust ww_mutex implementation Onur Özkan
@ 2025-09-03 13:13 ` Onur Özkan
  2025-09-03 13:13 ` [PATCH v6 6/7] rust: ww_mutex/exec: add high-level API Onur Özkan
  2025-09-03 13:13 ` [PATCH v6 7/7] add KUnit coverage on ww_mutex/exec implementation Onur Özkan
  6 siblings, 0 replies; 10+ messages in thread
From: Onur Özkan @ 2025-09-03 13:13 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary,
	a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
	longman, felipe_life, daniel, bjorn3_gh, daniel.almeida,
	Onur Özkan

Adds new `WwMutex` functions (`lock`, `lock_interruptible`,
`lock_slow`, `lock_slow_interruptible` and `try_lock`) that
can be used without `WwAcquireCtx`. This provides simpler API
when deadlock avoidance grouping is not needed.

Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
 rust/kernel/sync/lock/ww_mutex.rs | 162 ++++++++++++++++++++++--------
 1 file changed, 122 insertions(+), 40 deletions(-)

diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_mutex.rs
index d289718d2c98..b415d6deae9b 100644
--- a/rust/kernel/sync/lock/ww_mutex.rs
+++ b/rust/kernel/sync/lock/ww_mutex.rs
@@ -138,6 +138,75 @@ pub fn new_wound_wait(name: &'static CStr) -> impl PinInit<Self> {
     }
 }

+/// Locking kinds used by [`lock_common`] to unify internal FFI locking logic.
+#[derive(Copy, Clone, Debug)]
+enum LockKind {
+    /// Blocks until lock is acquired.
+    Regular,
+    /// Blocks but can be interrupted by signals.
+    Interruptible,
+    /// Used in slow path after deadlock detection.
+    Slow,
+    /// Slow path but interruptible.
+    SlowInterruptible,
+    /// Does not block, returns immediately if busy.
+    Try,
+}
+
+/// Internal helper that unifies the different locking kinds.
+fn lock_common<'a, T: ?Sized>(
+    ww_mutex: &'a WwMutex<'a, T>,
+    ctx: Option<&WwAcquireCtx<'_>>,
+    kind: LockKind,
+) -> Result<WwMutexGuard<'a, T>> {
+    let ctx_ptr = ctx.map_or(core::ptr::null_mut(), |c| c.inner.get());
+
+    match kind {
+        LockKind::Regular => {
+            // SAFETY: `WwMutex` is always pinned. If `WwAcquireCtx` is `Some`, it is pinned,
+            // if `None`, it is set to `core::ptr::null_mut()`. Both cases are safe.
+            let ret = unsafe { bindings::ww_mutex_lock(ww_mutex.mutex.get(), ctx_ptr) };
+
+            to_result(ret)?;
+        }
+        LockKind::Interruptible => {
+            // SAFETY: `WwMutex` is always pinned. If `WwAcquireCtx` is `Some`, it is pinned,
+            // if `None`, it is set to `core::ptr::null_mut()`. Both cases are safe.
+            let ret =
+                unsafe { bindings::ww_mutex_lock_interruptible(ww_mutex.mutex.get(), ctx_ptr) };
+
+            to_result(ret)?;
+        }
+        LockKind::Slow => {
+            // SAFETY: `WwMutex` is always pinned. If `WwAcquireCtx` is `Some`, it is pinned,
+            // if `None`, it is set to `core::ptr::null_mut()`. Both cases are safe.
+            unsafe { bindings::ww_mutex_lock_slow(ww_mutex.mutex.get(), ctx_ptr) };
+        }
+        LockKind::SlowInterruptible => {
+            // SAFETY: `WwMutex` is always pinned. If `WwAcquireCtx` is `Some`, it is pinned,
+            // if `None`, it is set to `core::ptr::null_mut()`. Both cases are safe.
+            let ret = unsafe {
+                bindings::ww_mutex_lock_slow_interruptible(ww_mutex.mutex.get(), ctx_ptr)
+            };
+
+            to_result(ret)?;
+        }
+        LockKind::Try => {
+            // SAFETY: `WwMutex` is always pinned. If `WwAcquireCtx` is `Some`, it is pinned,
+            // if `None`, it is set to `core::ptr::null_mut()`. Both cases are safe.
+            let ret = unsafe { bindings::ww_mutex_trylock(ww_mutex.mutex.get(), ctx_ptr) };
+
+            if ret == 0 {
+                return Err(EBUSY);
+            } else {
+                to_result(ret)?;
+            }
+        }
+    };
+
+    Ok(WwMutexGuard::new(ww_mutex))
+}
+
 /// Groups multiple mutex acquisitions together for deadlock avoidance.
 ///
 /// Must be used when acquiring multiple mutexes of the same class.
@@ -196,14 +265,9 @@ pub fn done(&self) {
         unsafe { bindings::ww_acquire_done(self.inner.get()) };
     }

-    /// Locks the given mutex.
+    /// Locks the given mutex on this acquire context ([`WwAcquireCtx`]).
     pub fn lock<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Result<WwMutexGuard<'a, T>> {
-        // SAFETY: The mutex is pinned and valid.
-        let ret = unsafe { bindings::ww_mutex_lock(ww_mutex.mutex.get(), self.inner.get()) };
-
-        to_result(ret)?;
-
-        Ok(WwMutexGuard::new(ww_mutex))
+        lock_common(ww_mutex, Some(self), LockKind::Regular)
     }

     /// Similar to `lock`, but can be interrupted by signals.
@@ -211,24 +275,14 @@ pub fn lock_interruptible<'a, T>(
         &'a self,
         ww_mutex: &'a WwMutex<'a, T>,
     ) -> Result<WwMutexGuard<'a, T>> {
-        // SAFETY: The mutex is pinned and valid.
-        let ret = unsafe {
-            bindings::ww_mutex_lock_interruptible(ww_mutex.mutex.get(), self.inner.get())
-        };
-
-        to_result(ret)?;
-
-        Ok(WwMutexGuard::new(ww_mutex))
+        lock_common(ww_mutex, Some(self), LockKind::Interruptible)
     }

-    /// Locks the given mutex using the slow path.
+    /// Locks the given mutex on this acquire context ([`WwAcquireCtx`]) using the slow path.
     ///
     /// This function should be used when `lock` fails (typically due to a potential deadlock).
     pub fn lock_slow<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Result<WwMutexGuard<'a, T>> {
-        // SAFETY: The mutex is pinned and valid, and we're in the slow path.
-        unsafe { bindings::ww_mutex_lock_slow(ww_mutex.mutex.get(), self.inner.get()) };
-
-        Ok(WwMutexGuard::new(ww_mutex))
+        lock_common(ww_mutex, Some(self), LockKind::Slow)
     }

     /// Similar to `lock_slow`, but can be interrupted by signals.
@@ -236,30 +290,14 @@ pub fn lock_slow_interruptible<'a, T>(
         &'a self,
         ww_mutex: &'a WwMutex<'a, T>,
     ) -> Result<WwMutexGuard<'a, T>> {
-        // SAFETY: The mutex is pinned and valid, and we are in the slow path.
-        let ret = unsafe {
-            bindings::ww_mutex_lock_slow_interruptible(ww_mutex.mutex.get(), self.inner.get())
-        };
-
-        to_result(ret)?;
-
-        Ok(WwMutexGuard::new(ww_mutex))
+        lock_common(ww_mutex, Some(self), LockKind::SlowInterruptible)
     }

-    /// Tries to lock the mutex without blocking.
+    /// Tries to lock the mutex on this acquire context ([`WwAcquireCtx`]) without blocking.
     ///
     /// Unlike `lock`, no deadlock handling is performed.
     pub fn try_lock<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Result<WwMutexGuard<'a, T>> {
-        // SAFETY: The mutex is pinned and valid.
-        let ret = unsafe { bindings::ww_mutex_trylock(ww_mutex.mutex.get(), self.inner.get()) };
-
-        if ret == 0 {
-            return Err(EBUSY);
-        } else {
-            to_result(ret)?;
-        }
-
-        Ok(WwMutexGuard::new(ww_mutex))
+        lock_common(ww_mutex, Some(self), LockKind::Try)
     }
 }

@@ -355,7 +393,7 @@ pub fn new(t: T, ww_class: &'ww_class WwClass) -> impl PinInit<Self> {
     }
 }

-impl<T: ?Sized> WwMutex<'_, T> {
+impl<'ww_class, T: ?Sized> WwMutex<'ww_class, T> {
     /// Returns a raw pointer to the inner mutex.
     fn as_ptr(&self) -> *mut bindings::ww_mutex {
         self.mutex.get()
@@ -370,6 +408,35 @@ fn is_locked(&self) -> bool {
         // SAFETY: The mutex is pinned and valid.
         unsafe { bindings::ww_mutex_is_locked(self.mutex.get()) }
     }
+
+    /// Locks the given mutex without acquire context ([`WwAcquireCtx`]).
+    pub fn lock<'a>(&'a self) -> Result<WwMutexGuard<'a, T>> {
+        lock_common(self, None, LockKind::Regular)
+    }
+
+    /// Similar to `lock`, but can be interrupted by signals.
+    pub fn lock_interruptible<'a>(&'a self) -> Result<WwMutexGuard<'a, T>> {
+        lock_common(self, None, LockKind::Interruptible)
+    }
+
+    /// Locks the given mutex without acquire context ([`WwAcquireCtx`]) using the slow path.
+    ///
+    /// This function should be used when `lock` fails (typically due to a potential deadlock).
+    pub fn lock_slow<'a>(&'a self) -> Result<WwMutexGuard<'a, T>> {
+        lock_common(self, None, LockKind::Slow)
+    }
+
+    /// Similar to `lock_slow`, but can be interrupted by signals.
+    pub fn lock_slow_interruptible<'a>(&'a self) -> Result<WwMutexGuard<'a, T>> {
+        lock_common(self, None, LockKind::SlowInterruptible)
+    }
+
+    /// Tries to lock the mutex without acquire context ([`WwAcquireCtx`]) and without blocking.
+    ///
+    /// Unlike `lock`, no deadlock handling is performed.
+    pub fn try_lock<'a>(&'a self) -> Result<WwMutexGuard<'a, T>> {
+        lock_common(self, None, LockKind::Try)
+    }
 }

 /// A guard that provides exclusive access to the data protected
@@ -547,4 +614,19 @@ fn test_with_global_classes() -> Result {

         Ok(())
     }
+
+    #[test]
+    fn test_mutex_without_ctx() -> Result {
+        let mutex = Arc::pin_init(WwMutex::new(100, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+        let guard = mutex.lock()?;
+
+        assert_eq!(*guard, 100);
+        assert!(mutex.is_locked());
+
+        drop(guard);
+
+        assert!(!mutex.is_locked());
+
+        Ok(())
+    }
 }
--
2.50.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v6 6/7] rust: ww_mutex/exec: add high-level API
  2025-09-03 13:13 [PATCH v6 0/7] rust: add `ww_mutex` support Onur Özkan
                   ` (4 preceding siblings ...)
  2025-09-03 13:13 ` [PATCH v6 5/7] rust: ww_mutex: add context-free locking functions Onur Özkan
@ 2025-09-03 13:13 ` Onur Özkan
  2025-09-03 13:13 ` [PATCH v6 7/7] add KUnit coverage on ww_mutex/exec implementation Onur Özkan
  6 siblings, 0 replies; 10+ messages in thread
From: Onur Özkan @ 2025-09-03 13:13 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary,
	a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
	longman, felipe_life, daniel, bjorn3_gh, daniel.almeida,
	Onur Özkan

`ExecContext` is a helper built on top of ww_mutex
that provides a retrying interface for lock acquisition.
When `EDEADLK` is hit, it drops all held locks, resets
the acquire context and retries the given (by the user)
locking algorithm until it succeeds.

The API keeps track of acquired locks, cleans them up
automatically and allows data access to the protected
data through `with_locked()`. The `lock_all()` helper
allows implementing multi-mutex algorithms in a simpler
and less error-prone way while keeping the ww_mutex
semantics.

Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
 rust/kernel/sync/lock/ww_mutex.rs      |   2 +
 rust/kernel/sync/lock/ww_mutex/exec.rs | 176 +++++++++++++++++++++++++
 2 files changed, 178 insertions(+)
 create mode 100644 rust/kernel/sync/lock/ww_mutex/exec.rs

diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_mutex.rs
index b415d6deae9b..7de6578513e5 100644
--- a/rust/kernel/sync/lock/ww_mutex.rs
+++ b/rust/kernel/sync/lock/ww_mutex.rs
@@ -16,6 +16,8 @@
 use core::cell::UnsafeCell;
 use core::marker::PhantomData;

+pub mod exec;
+
 /// Create static [`WwClass`] instances.
 ///
 /// # Examples
diff --git a/rust/kernel/sync/lock/ww_mutex/exec.rs b/rust/kernel/sync/lock/ww_mutex/exec.rs
new file mode 100644
index 000000000000..2f1fc540f0b8
--- /dev/null
+++ b/rust/kernel/sync/lock/ww_mutex/exec.rs
@@ -0,0 +1,176 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! A high-level [`WwMutex`] execution helper.
+//!
+//! Provides a retrying lock mechanism on top of [`WwMutex`] and [`WwAcquireCtx`].
+//! It detects [`EDEADLK`] and handles it by rolling back and retrying the
+//! user-supplied locking algorithm until success.
+
+use crate::prelude::*;
+use crate::sync::lock::ww_mutex::{WwAcquireCtx, WwClass, WwMutex, WwMutexGuard};
+use core::ptr;
+
+/// High-level execution type for ww_mutex.
+///
+/// Tracks a series of locks acquired under a common [`WwAcquireCtx`].
+/// It ensures proper cleanup and retry mechanism on deadlocks and provides
+/// type-safe access to locked data via [`with_locked`].
+///
+/// Typical usage is through [`lock_all`], which retries a user-supplied
+/// locking algorithm until it succeeds without deadlock.
+pub struct ExecContext<'a> {
+    class: &'a WwClass,
+    acquire: Pin<KBox<WwAcquireCtx<'a>>>,
+    taken: KVec<WwMutexGuard<'a, ()>>,
+}
+
+impl<'a> Drop for ExecContext<'a> {
+    fn drop(&mut self) {
+        self.release_all_locks();
+    }
+}
+
+impl<'a> ExecContext<'a> {
+    /// Creates a new [`ExecContext`] for the given lock class.
+    ///
+    /// All locks taken through this context must belong to the same class.
+    ///
+    /// TODO: Add some safety mechanism to ensure classes are not different.
+    pub fn new(class: &'a WwClass) -> Result<Self> {
+        Ok(Self {
+            class,
+            acquire: KBox::pin_init(WwAcquireCtx::new(class), GFP_KERNEL)?,
+            taken: KVec::new(),
+        })
+    }
+
+    /// Attempts to lock a [`WwMutex`] and records the guard.
+    ///
+    /// Returns [`EDEADLK`] if lock ordering would cause a deadlock.
+    pub fn lock<T>(&mut self, mutex: &'a WwMutex<'a, T>) -> Result<()> {
+        let guard = self.acquire.lock(mutex)?;
+        // SAFETY: Type is erased for storage. Actual access uses `with_locked`
+        // which safely casts back.
+        let erased: WwMutexGuard<'a, ()> = unsafe { core::mem::transmute(guard) };
+        self.taken.push(erased, GFP_KERNEL)?;
+
+        Ok(())
+    }
+
+    /// Runs `locking_algorithm` until success with retrying on deadlock.
+    ///
+    /// `locking_algorithm` should attempt to acquire all needed locks.
+    /// If [`EDEADLK`] is detected, this function will roll back, reset
+    /// the context and retry automatically.
+    ///
+    /// Once all locks are acquired successfully, `on_all_locks_taken` is
+    /// invoked for exclusive access to the locked values. Afterwards, all
+    /// locks are released.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// use kernel::alloc::KBox;
+    /// use kernel::c_str;
+    /// use kernel::prelude::*;
+    /// use kernel::sync::Arc;
+    /// use kernel::sync::lock::ww_mutex;
+    /// use pin_init::stack_pin_init;
+    ///
+    /// stack_pin_init!(let class = ww_mutex::WwClass::new_wound_wait(c_str!("lock_all_example")));
+    ///
+    /// let mutex1 = Arc::pin_init(ww_mutex::WwMutex::new(0, &class), GFP_KERNEL)?;
+    /// let mutex2 = Arc::pin_init(ww_mutex::WwMutex::new(0, &class), GFP_KERNEL)?;
+    /// let mut ctx = KBox::pin_init(ww_mutex::exec::ExecContext::new(&class)?, GFP_KERNEL)?;
+    ///
+    /// ctx.lock_all(
+    ///     |ctx| {
+    ///         // Try to lock both mutexes.
+    ///         ctx.lock(&mutex1)?;
+    ///         ctx.lock(&mutex2)?;
+    ///
+    ///         Ok(())
+    ///     },
+    ///     |ctx| {
+    ///         // Safely mutate both values while holding the locks.
+    ///         ctx.with_locked(&mutex1, |v| *v += 1)?;
+    ///         ctx.with_locked(&mutex2, |v| *v += 1)?;
+    ///
+    ///         Ok(())
+    ///     },
+    /// )?;
+    ///
+    /// # Ok::<(), Error>(())
+    /// ```
+    pub fn lock_all<T, Y, Z>(
+        &mut self,
+        mut locking_algorithm: T,
+        mut on_all_locks_taken: Y,
+    ) -> Result<Z>
+    where
+        T: FnMut(&mut ExecContext<'a>) -> Result<()>,
+        Y: FnMut(&mut ExecContext<'a>) -> Result<Z>,
+    {
+        loop {
+            match locking_algorithm(self) {
+                Ok(()) => {
+                    // All locks in `locking_algorithm` succeeded.
+                    // The user can now safely use them in `on_all_locks_taken`.
+                    let res = on_all_locks_taken(self);
+                    self.release_all_locks();
+
+                    return res;
+                }
+                Err(e) if e == EDEADLK => {
+                    // Deadlock detected, retry from scratch.
+                    self.cleanup_on_deadlock()?;
+                    continue;
+                }
+                Err(e) => {
+                    return Err(e);
+                }
+            }
+        }
+    }
+
+    /// Executes `f` with a mutable reference to the data behind `mutex`.
+    ///
+    /// Fails with [`EINVAL`] if the mutex was not locked in this context.
+    pub fn with_locked<T, Y>(
+        &mut self,
+        mutex: &'a WwMutex<'a, T>,
+        f: impl FnOnce(&mut T) -> Y,
+    ) -> Result<Y> {
+        // Find the matching guard.
+        for guard in &mut self.taken {
+            if mutex.as_ptr() == guard.mutex.as_ptr() {
+                // SAFETY: We know this guard belongs to `mutex` and holds the lock.
+                let typed = unsafe { &mut *ptr::from_mut(guard).cast::<WwMutexGuard<'a, T>>() };
+                return Ok(f(&mut **typed));
+            }
+        }
+
+        // `mutex` isn't locked in this `ExecContext`.
+        Err(EINVAL)
+    }
+
+    /// Releases all currently held locks in this context.
+    ///
+    /// It is intended to be used for internal implementation only.
+    fn release_all_locks(&mut self) {
+        self.taken.clear();
+    }
+
+    /// Resets this context after a deadlock detection.
+    ///
+    /// Drops all held locks and reinitializes the [`WwAcquireCtx`].
+    ///
+    /// It is intended to be used for internal implementation only.
+    fn cleanup_on_deadlock(&mut self) -> Result {
+        self.release_all_locks();
+        // Re-init fresh `WwAcquireCtx`.
+        self.acquire = KBox::pin_init(WwAcquireCtx::new(self.class), GFP_KERNEL)?;
+
+        Ok(())
+    }
+}
--
2.50.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH v6 7/7] add KUnit coverage on ww_mutex/exec implementation
  2025-09-03 13:13 [PATCH v6 0/7] rust: add `ww_mutex` support Onur Özkan
                   ` (5 preceding siblings ...)
  2025-09-03 13:13 ` [PATCH v6 6/7] rust: ww_mutex/exec: add high-level API Onur Özkan
@ 2025-09-03 13:13 ` Onur Özkan
  6 siblings, 0 replies; 10+ messages in thread
From: Onur Özkan @ 2025-09-03 13:13 UTC (permalink / raw)
  To: rust-for-linux
  Cc: linux-kernel, lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary,
	a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
	longman, felipe_life, daniel, bjorn3_gh, daniel.almeida,
	Onur Özkan

Adds coverage for `ww_mutex/exec.rs` implementation.

Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
 rust/kernel/sync/lock/ww_mutex/exec.rs | 148 +++++++++++++++++++++++++
 1 file changed, 148 insertions(+)

diff --git a/rust/kernel/sync/lock/ww_mutex/exec.rs b/rust/kernel/sync/lock/ww_mutex/exec.rs
index 2f1fc540f0b8..543c5218232a 100644
--- a/rust/kernel/sync/lock/ww_mutex/exec.rs
+++ b/rust/kernel/sync/lock/ww_mutex/exec.rs
@@ -174,3 +174,151 @@ fn cleanup_on_deadlock(&mut self) -> Result {
         Ok(())
     }
 }
+
+#[kunit_tests(rust_kernel_ww_exec)]
+mod tests {
+    use crate::c_str;
+    use crate::prelude::*;
+    use crate::sync::Arc;
+    use pin_init::stack_pin_init;
+
+    use super::*;
+
+    #[test]
+    fn test_exec_context_basic_lock_unlock() -> Result {
+        stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("exec_ctx_basic")));
+
+        let mutex = Arc::pin_init(WwMutex::new(10, &class), GFP_KERNEL)?;
+        let mut ctx = KBox::pin_init(ExecContext::new(&class)?, GFP_KERNEL)?;
+
+        ctx.lock(&mutex)?;
+        ctx.with_locked(&mutex, |v| {
+            assert_eq!(*v, 10);
+        })?;
+
+        ctx.release_all_locks();
+        assert!(!mutex.is_locked());
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_exec_context_with_locked_mutates_data() -> Result {
+        stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("exec_ctx_with_locked")));
+
+        let mutex = Arc::pin_init(WwMutex::new(5, &class), GFP_KERNEL)?;
+        let mut ctx = KBox::pin_init(ExecContext::new(&class)?, GFP_KERNEL)?;
+
+        ctx.lock(&mutex)?;
+
+        ctx.with_locked(&mutex, |v| {
+            assert_eq!(*v, 5);
+            // Increment the value.
+            *v += 7;
+        })?;
+
+        ctx.with_locked(&mutex, |v| {
+            // Check that mutation took effect.
+            assert_eq!(*v, 12);
+        })?;
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_lock_all_success() -> Result {
+        stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("lock_all_ok")));
+
+        let mutex1 = Arc::pin_init(WwMutex::new(1, &class), GFP_KERNEL)?;
+        let mutex2 = Arc::pin_init(WwMutex::new(2, &class), GFP_KERNEL)?;
+        let mut ctx = KBox::pin_init(ExecContext::new(&class)?, GFP_KERNEL)?;
+
+        let res = ctx.lock_all(
+            |ctx| {
+                let _ = ctx.lock(&mutex1)?;
+                let _ = ctx.lock(&mutex2)?;
+                Ok(())
+            },
+            |ctx| {
+                ctx.with_locked(&mutex1, |v| *v += 10)?;
+                ctx.with_locked(&mutex2, |v| *v += 20)?;
+                Ok((
+                    ctx.with_locked(&mutex1, |v| *v)?,
+                    ctx.with_locked(&mutex2, |v| *v)?,
+                ))
+            },
+        )?;
+
+        assert_eq!(res, (11, 22));
+        assert!(!mutex1.is_locked());
+        assert!(!mutex2.is_locked());
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_with_different_input_type() -> Result {
+        stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("lock_all_ok")));
+
+        let mutex1 = Arc::pin_init(WwMutex::new(1, &class), GFP_KERNEL)?;
+        let mutex2 = Arc::pin_init(WwMutex::new("hello", &class), GFP_KERNEL)?;
+        let mut ctx = KBox::pin_init(ExecContext::new(&class)?, GFP_KERNEL)?;
+
+        ctx.lock_all(
+            |ctx| {
+                ctx.lock(&mutex1)?;
+                ctx.lock(&mutex2)?;
+                Ok(())
+            },
+            |ctx| {
+                ctx.with_locked(&mutex1, |v| assert_eq!(*v, 1))?;
+                ctx.with_locked(&mutex2, |v| assert_eq!(*v, "hello"))?;
+                Ok(())
+            },
+        )?;
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_lock_all_retries_on_deadlock() -> Result {
+        stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("lock_all_retry")));
+
+        let mutex = Arc::pin_init(WwMutex::new(99, &class), GFP_KERNEL)?;
+        let mut ctx = KBox::pin_init(ExecContext::new(&class)?, GFP_KERNEL)?;
+        let mut first_try = true;
+
+        let res = ctx.lock_all(
+            |ctx| {
+                if first_try {
+                    first_try = false;
+                    // Simulate deadlock on first attempt.
+                    return Err(EDEADLK);
+                }
+                ctx.lock(&mutex)
+            },
+            |ctx| {
+                ctx.with_locked(&mutex, |v| {
+                    *v += 1;
+                    *v
+                })
+            },
+        )?;
+
+        assert_eq!(res, 100);
+        Ok(())
+    }
+
+    #[test]
+    fn test_with_locked_on_unlocked_mutex() -> Result {
+        stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("with_unlocked_mutex")));
+
+        let mutex = Arc::pin_init(WwMutex::new(5, &class), GFP_KERNEL)?;
+        let mut ctx = KBox::pin_init(ExecContext::new(&class)?, GFP_KERNEL)?;
+
+        let ecode = ctx.with_locked(&mutex, |_v| {}).unwrap_err();
+        assert_eq!(EINVAL, ecode);
+
+        Ok(())
+    }
+}
--
2.50.0


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v6 1/7] rust: add C wrappers for ww_mutex inline functions
  2025-09-03 13:13 ` [PATCH v6 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
@ 2025-09-03 13:46   ` Daniel Almeida
  0 siblings, 0 replies; 10+ messages in thread
From: Daniel Almeida @ 2025-09-03 13:46 UTC (permalink / raw)
  To: Onur Özkan
  Cc: rust-for-linux, linux-kernel, lossin, lyude, ojeda, alex.gaynor,
	boqun.feng, gary, a.hindborg, aliceryhl, tmgross, dakr, peterz,
	mingo, will, longman, felipe_life, daniel, bjorn3_gh



> On 3 Sep 2025, at 10:13, Onur Özkan <work@onurozkan.dev> wrote:
> 
> 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. This patch provides C function wrappers
> around these inline implementations, so bindgen can see them and generate
> the corresponding Rust code.
> 
> Signed-off-by: Onur Özkan <work@onurozkan.dev>
> ---
> rust/helpers/helpers.c  |  1 +
> rust/helpers/ww_mutex.c | 39 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 40 insertions(+)
> create mode 100644 rust/helpers/ww_mutex.c
> 
> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> index 7cf7fe95e41d..4c789c5537b1 100644
> --- a/rust/helpers/helpers.c
> +++ b/rust/helpers/helpers.c
> @@ -50,4 +50,5 @@
> #include "vmalloc.c"
> #include "wait.c"
> #include "workqueue.c"
> +#include "ww_mutex.c"
> #include "xarray.c"
> diff --git a/rust/helpers/ww_mutex.c b/rust/helpers/ww_mutex.c
> new file mode 100644
> index 000000000000..61a487653394
> --- /dev/null
> +++ b/rust/helpers/ww_mutex.c
> @@ -0,0 +1,39 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/ww_mutex.h>
> +
> +void rust_helper_ww_mutex_init(struct ww_mutex *lock, struct ww_class *ww_class)
> +{
> + ww_mutex_init(lock, ww_class);
> +}
> +
> +void rust_helper_ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *ww_class)
> +{
> + ww_acquire_init(ctx, ww_class);
> +}
> +
> +void rust_helper_ww_acquire_done(struct ww_acquire_ctx *ctx)
> +{
> + ww_acquire_done(ctx);
> +}
> +
> +void rust_helper_ww_acquire_fini(struct ww_acquire_ctx *ctx)
> +{
> + ww_acquire_fini(ctx);
> +}
> +
> +void rust_helper_ww_mutex_lock_slow(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
> +{
> + ww_mutex_lock_slow(lock, ctx);
> +}
> +
> +int rust_helper_ww_mutex_lock_slow_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
> +{
> + return ww_mutex_lock_slow_interruptible(lock, ctx);
> +}
> +
> +bool rust_helper_ww_mutex_is_locked(struct ww_mutex *lock)
> +{
> + return ww_mutex_is_locked(lock);
> +}
> +
> --
> 2.50.0
> 

Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v6 2/7] rust: implement `WwClass` for ww_mutex support
  2025-09-03 13:13 ` [PATCH v6 2/7] rust: implement `WwClass` for ww_mutex support Onur Özkan
@ 2025-09-03 16:06   ` Boqun Feng
  0 siblings, 0 replies; 10+ messages in thread
From: Boqun Feng @ 2025-09-03 16:06 UTC (permalink / raw)
  To: Onur Özkan
  Cc: rust-for-linux, linux-kernel, lossin, lyude, ojeda, alex.gaynor,
	gary, a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
	longman, felipe_life, daniel, bjorn3_gh, daniel.almeida

On Wed, Sep 03, 2025 at 04:13:08PM +0300, Onur Özkan wrote:
> Adds the `WwClass` type, the first step in supporting
> `ww_mutex` in Rust. `WwClass` represents a class of ww
> mutexes used for deadlock avoidance for supporting both
> wait-die and wound-wait semantics.
> 
> Also adds the `define_ww_class!` macro for safely declaring
> static instances.
> 
> Signed-off-by: Onur Özkan <work@onurozkan.dev>
> ---
>  rust/kernel/sync/lock.rs          |   1 +
>  rust/kernel/sync/lock/ww_mutex.rs | 136 ++++++++++++++++++++++++++++++
>  2 files changed, 137 insertions(+)
>  create mode 100644 rust/kernel/sync/lock/ww_mutex.rs
> 
> diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
> index 27202beef90c..5b320c2b28c1 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..ca5b4525ace6
> --- /dev/null
> +++ b/rust/kernel/sync/lock/ww_mutex.rs
> @@ -0,0 +1,136 @@
> +// 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.
> +//!
> +//! C header: [`include/linux/ww_mutex.h`](srctree/include/linux/ww_mutex.h)
> +//!
> +//! For more information: <https://docs.kernel.org/locking/ww-mutex-design.html>
> +
> +use crate::bindings;
> +use crate::prelude::*;
> +use crate::types::Opaque;
> +
> +/// 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 =
> +            // SAFETY: This is `static`, so address is fixed and won't move.
> +            unsafe { $crate::sync::lock::ww_mutex::WwClass::unpinned_new($class_name, false) };
> +    };
> +    ($name:ident, wait_die, $class_name:expr) => {
> +        static $name: $crate::sync::lock::ww_mutex::WwClass =
> +            // SAFETY: This is `static`, so address is fixed and won't move.
> +            unsafe { $crate::sync::lock::ww_mutex::WwClass::unpinned_new($class_name, true) };
> +    };
> +}
> +
> +/// A class used to group mutexes together for deadlock avoidance.
> +///
> +/// 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 {}
> +
> +impl WwClass {
> +    /// Creates an unpinned [`WwClass`].
> +    ///
> +    /// # Safety
> +    ///
> +    /// Caller must guarantee that the returned value is not moved after creation.
> +    pub const unsafe fn unpinned_new(name: &'static CStr, is_wait_die: bool) -> Self {
> +        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,
> +                // 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>

Please don't use internet to reference the code in the same repo ;-) You
can just say "see __WW_CLASS_INITIALIZER() in include/linux/ww_mutex.h".

> +                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() },
> +            }),
> +        }
> +    }
> +
> +    /// Creates a [`WwClass`].
> +    ///
> +    /// 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.
> +    const fn new(name: &'static CStr, is_wait_die: bool) -> Self {
> +        WwClass {
> +            inner: Opaque::new(bindings::ww_class {

You cannot use Opaque::new() here, it'll move the object when new()
returns. You should change the function signature to a `... ->
PinInit<Self>`, and use pin_init!() macro + Opaque::ffi_init(), like:

    const fn new(...) -> PinInit<Self> {
        pin_init!{
	    Self {
	        inner <- Opaque::ffi_init(|class| {
		    ...
		});
	    }
	}
    }

Regards,
Boqun

> +                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() },
> +            }),
> +        }
> +    }
> +
> +    /// Creates wait-die [`WwClass`].
> +    pub fn new_wait_die(name: &'static CStr) -> impl PinInit<Self> {
> +        Self::new(name, true)
> +    }
> +
> +    /// Creates wound-wait [`WwClass`].
> +    pub fn new_wound_wait(name: &'static CStr) -> impl PinInit<Self> {
> +        Self::new(name, false)
> +    }
> +}
> --
> 2.50.0
> 

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2025-09-03 16:06 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-03 13:13 [PATCH v6 0/7] rust: add `ww_mutex` support Onur Özkan
2025-09-03 13:13 ` [PATCH v6 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
2025-09-03 13:46   ` Daniel Almeida
2025-09-03 13:13 ` [PATCH v6 2/7] rust: implement `WwClass` for ww_mutex support Onur Özkan
2025-09-03 16:06   ` Boqun Feng
2025-09-03 13:13 ` [PATCH v6 3/7] rust: implement `WwMutex`, `WwAcquireCtx` and `WwMutexGuard` Onur Özkan
2025-09-03 13:13 ` [PATCH v6 4/7] add KUnit coverage on Rust ww_mutex implementation Onur Özkan
2025-09-03 13:13 ` [PATCH v6 5/7] rust: ww_mutex: add context-free locking functions Onur Özkan
2025-09-03 13:13 ` [PATCH v6 6/7] rust: ww_mutex/exec: add high-level API Onur Özkan
2025-09-03 13:13 ` [PATCH v6 7/7] add KUnit coverage on ww_mutex/exec implementation Onur Özkan

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).