* [PATCH v12 0/6] rust: add ww_mutex support
@ 2026-10-12 10:26 Onur Özkan
2026-10-12 10:26 ` [PATCH v12 1/6] rust: add C wrappers for ww_mutex inline functions Onur Özkan
` (5 more replies)
0 siblings, 6 replies; 13+ messages in thread
From: Onur Özkan @ 2026-10-12 10:26 UTC (permalink / raw)
To: rust-for-linux
Cc: Onur Özkan, lossin, lyude, ojeda, alex.gaynor, boqun.feng,
gary, a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
longman, felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
This series adds Rust abstractions for ww_mutex, including Class, Mutex,
AcquireCtx and MutexGuard. LockSet handles deadlock recovery for callers
that need to acquire multiple locks.
Changes in v12 (sashiko fixes):
- Give Rust-defined classes separate _acquire and _mutex lockdep names
to match the C initializer.
- Correct the Class Sync safety comment and add an invariant comment
when constructing MutexGuard.
- Add #[inline] to Class::from_raw(), Mutex::is_locked(),
AcquireCtx::done(), MutexGuard::drop() and RawGuard::drop().
Built x86_64 kernels with and without PREEMPT_RT. rustfmtcheck, Clippy and
rustdoc passed. All 14 ww_mutex/LockSet KUnit tests passed on both builds
with CONFIG_PROVE_LOCKING and CONFIG_DEBUG_WW_MUTEX_SLOWPATH enabled.
Previous version:
https://lore.kernel.org/all/cover.1791743500.git.work@onurozkan.dev/
Onur Özkan (6):
rust: add C wrappers for ww_mutex inline functions
ww_mutex: add ww_class field unconditionally
rust: implement Class for ww_class support
rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard
rust: ww_mutex: implement LockSet
MAINTAINERS: add Onur Özkan as WW MUTEX maintainer
MAINTAINERS | 2 +
include/linux/ww_mutex.h | 8 +-
rust/helpers/helpers.c | 1 +
rust/helpers/ww_mutex.c | 33 ++
rust/kernel/sync/lock.rs | 1 +
rust/kernel/sync/lock/ww_mutex.rs | 447 +++++++++++++++++
rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs | 149 ++++++
rust/kernel/sync/lock/ww_mutex/class.rs | 151 ++++++
rust/kernel/sync/lock/ww_mutex/lock_set.rs | 449 ++++++++++++++++++
9 files changed, 1235 insertions(+), 6 deletions(-)
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/acquire_ctx.rs
create mode 100644 rust/kernel/sync/lock/ww_mutex/class.rs
create mode 100644 rust/kernel/sync/lock/ww_mutex/lock_set.rs
Range-diff against v11:
1: 4942155a8873e = 1: 4942155a8873e rust: add C wrappers for ww_mutex inline functions
2: 168394bb52a6c = 2: 168394bb52a6c ww_mutex: add ww_class field unconditionally
3: 003bc88697dc8 ! 3: ba8ea8e2e2b50 rust: implement Class for ww_class support
@@ rust/kernel/sync/lock/ww_mutex/class.rs (new)
+ // SAFETY: This is `static`, so address is fixed and won't move.
+ unsafe {
+ $crate::sync::lock::ww_mutex::Class::new_unpinned(
-+ $crate::c_str!(::core::stringify!($name)),
++ $crate::c_str!(::core::concat!(::core::stringify!($name), "_acquire")),
++ $crate::c_str!(::core::concat!(::core::stringify!($name), "_mutex")),
+ false,
+ )
+ };
@@ rust/kernel/sync/lock/ww_mutex/class.rs (new)
+ // SAFETY: This is `static`, so address is fixed and won't move.
+ unsafe {
+ $crate::sync::lock::ww_mutex::Class::new_unpinned(
-+ $crate::c_str!(::core::stringify!($name)),
++ $crate::c_str!(::core::concat!(::core::stringify!($name), "_acquire")),
++ $crate::c_str!(::core::concat!(::core::stringify!($name), "_mutex")),
+ true,
+ )
+ };
@@ rust/kernel/sync/lock/ww_mutex/class.rs (new)
+ ///
+ /// Caller must guarantee that the returned value must be pinned before
+ /// its first use.
-+ pub const unsafe fn new_unpinned(name: &'static CStr, is_wait_die: bool) -> Self {
++ pub const unsafe fn new_unpinned(
++ acquire_name: &'static CStr,
++ mutex_name: &'static CStr,
++ is_wait_die: bool,
++ ) -> Self {
+ Class {
+ inner: Opaque::new(bindings::ww_class {
+ stamp: bindings::atomic_long_t { counter: 0 },
-+ acquire_name: crate::str::as_char_ptr_in_const_context(name),
-+ mutex_name: crate::str::as_char_ptr_in_const_context(name),
++ acquire_name: crate::str::as_char_ptr_in_const_context(acquire_name),
++ mutex_name: crate::str::as_char_ptr_in_const_context(mutex_name),
+ is_wait_die: is_wait_die as u32,
+ // TODO: Replace with `bindings::lock_class_key::default()` once
+ // stabilized for `const`.
@@ rust/kernel/sync/lock/ww_mutex/class.rs (new)
+ ///
+ /// The caller must ensure that `ptr` points to the `inner` field of
+ /// [`Class`] and that it remains valid for the lifetime `'a`.
++ #[inline]
+ pub const unsafe fn from_raw<'a>(ptr: *mut bindings::ww_class) -> &'a Self {
+ // SAFETY: By the safety contract, `ptr` is valid to construct `Class`.
+ unsafe { &*ptr.cast() }
+ }
+}
+
-+// SAFETY: [`Class`] is set up once and never modified. It's fine to share it across threads.
++// SAFETY: [`Class`] is immutable except for its atomic stamp. It's fine to share it across threads.
+unsafe impl Sync for Class {}
+// SAFETY: Doesn't hold anything thread-specific. It's safe to send to other threads.
+unsafe impl Send for Class {}
4: 293fbbc0bcc0f ! 4: 36bddd73ca296 rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard
@@ rust/kernel/sync/lock/ww_mutex.rs
+ ///
+ /// The returned value is racy as another thread can acquire
+ /// or release the lock immediately after this call returns.
++ #[inline]
+ pub fn is_locked(&self) -> bool {
+ // SAFETY: It's safe to call `ww_mutex_is_locked` on
+ // a valid mutex.
@@ rust/kernel/sync/lock/ww_mutex.rs
+ fn new(mutex: &'a Mutex<'a, T>) -> Self {
+ assert!(mutex.is_locked());
+
++ // INVARIANT: `lock_common` and `from_raw` ensure that the current task holds the mutex
++ // and transfer sole responsibility for unlocking it to this guard.
+ Self {
+ mutex,
+ _not_send: NotThreadSafe,
@@ rust/kernel/sync/lock/ww_mutex.rs
+}
+
+impl<T: ?Sized> Drop for MutexGuard<'_, T> {
++ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: self.mutex is locked and are about to release it.
+ unsafe { bindings::ww_mutex_unlock(self.mutex.inner.get()) };
@@ rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs (new)
+ ///
+ /// After calling this function, no more mutexes can be acquired with
+ /// this context.
++ #[inline]
+ pub fn done(&self) {
+ // SAFETY: `self.inner` contains a valid, initialized acquire context.
+ unsafe { bindings::ww_acquire_done(self.inner.get()) };
5: d085c15ba9da4 ! 5: 8a3e94391fcee rust: ww_mutex: implement LockSet
@@ rust/kernel/sync/lock/ww_mutex/lock_set.rs (new)
+}
+
+impl Drop for RawGuard {
++ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: `mutex_ptr` originates from a locked `Mutex` and remains
+ // valid for the lifetime of this guard, so unlocking here is sound.
6: 8f0e93bd1781e = 6: 2b7b779f72f77 MAINTAINERS: add Onur Özkan as WW MUTEX maintainer
base-commit: 47324d3a5b3abd781295044d01d92d09f184e872
--
2.51.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v12 1/6] rust: add C wrappers for ww_mutex inline functions
2026-10-12 10:26 [PATCH v12 0/6] rust: add ww_mutex support Onur Özkan
@ 2026-10-12 10:26 ` Onur Özkan
2026-10-12 11:41 ` Gary Guo
2026-10-12 10:26 ` [PATCH v12 2/6] ww_mutex: add ww_class field unconditionally Onur Özkan
` (4 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Onur Özkan @ 2026-10-12 10:26 UTC (permalink / raw)
To: rust-for-linux
Cc: Onur Özkan, lossin, lyude, ojeda, alex.gaynor, boqun.feng,
gary, a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
longman, felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
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.
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
MAINTAINERS | 1 +
rust/helpers/helpers.c | 1 +
rust/helpers/ww_mutex.c | 33 +++++++++++++++++++++++++++++++++
3 files changed, 35 insertions(+)
create mode 100644 rust/helpers/ww_mutex.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 47946f1c507b9..198bb740f7cc2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15283,6 +15283,7 @@ F: kernel/locking/
F: lib/locking*.[ch]
F: rust/helpers/mutex.c
F: rust/helpers/spinlock.c
+F: rust/helpers/ww_mutex.c
F: rust/kernel/sync/lock.rs
F: rust/kernel/sync/lock/
F: rust/kernel/sync/locked_by.rs
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 440fb7638e3cb..0287064c82c18 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -105,4 +105,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 0000000000000..e7aa65ac58745
--- /dev/null
+++ b/rust/helpers/ww_mutex.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/ww_mutex.h>
+
+__rust_helper
+void rust_helper_ww_mutex_init(struct ww_mutex *lock, struct ww_class *ww_class)
+{
+ ww_mutex_init(lock, ww_class);
+}
+
+__rust_helper
+void rust_helper_ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *ww_class)
+{
+ ww_acquire_init(ctx, ww_class);
+}
+
+__rust_helper
+void rust_helper_ww_acquire_done(struct ww_acquire_ctx *ctx)
+{
+ ww_acquire_done(ctx);
+}
+
+__rust_helper
+void rust_helper_ww_acquire_fini(struct ww_acquire_ctx *ctx)
+{
+ ww_acquire_fini(ctx);
+}
+
+__rust_helper
+bool rust_helper_ww_mutex_is_locked(struct ww_mutex *lock)
+{
+ return ww_mutex_is_locked(lock);
+}
--
2.51.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v12 2/6] ww_mutex: add ww_class field unconditionally
2026-10-12 10:26 [PATCH v12 0/6] rust: add ww_mutex support Onur Özkan
2026-10-12 10:26 ` [PATCH v12 1/6] rust: add C wrappers for ww_mutex inline functions Onur Özkan
@ 2026-10-12 10:26 ` Onur Özkan
2026-10-12 11:42 ` Gary Guo
2026-10-12 10:26 ` [PATCH v12 3/6] rust: implement Class for ww_class support Onur Özkan
` (3 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Onur Özkan @ 2026-10-12 10:26 UTC (permalink / raw)
To: rust-for-linux
Cc: Onur Özkan, lossin, lyude, ojeda, alex.gaynor, boqun.feng,
gary, a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
longman, felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
Removes DEBUG_WW_MUTEXES guard around the ww_class field
in struct ww_mutex and struct ww_acquire_ctx to always
initialize it.
Keeping the class pointer available unconditionally allows
Rust abstractions to keep from_raw() simple on Mutex and
AcquireCtx and validate Mutex and AcquireCtx class pairing
in much simpler way.
Link: https://lore.kernel.org/all/ECC0425A-8B18-4626-8EA8-2F843C45E0A1@collabora.com/
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
include/linux/ww_mutex.h | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/include/linux/ww_mutex.h b/include/linux/ww_mutex.h
index 0c95ead5a2977..c955824aa5d48 100644
--- a/include/linux/ww_mutex.h
+++ b/include/linux/ww_mutex.h
@@ -48,9 +48,7 @@ struct ww_class {
context_lock_struct(ww_mutex) {
struct WW_MUTEX_BASE base;
struct ww_acquire_ctx *ctx;
-#ifdef DEBUG_WW_MUTEXES
struct ww_class *ww_class;
-#endif
};
context_lock_struct(ww_acquire_ctx) {
@@ -59,9 +57,9 @@ context_lock_struct(ww_acquire_ctx) {
unsigned int acquired;
unsigned short wounded;
unsigned short is_wait_die;
+ struct ww_class *ww_class;
#ifdef DEBUG_WW_MUTEXES
unsigned int done_acquire;
- struct ww_class *ww_class;
void *contending_lock;
#endif
#ifdef CONFIG_DEBUG_LOCK_ALLOC
@@ -111,9 +109,7 @@ static inline void ww_mutex_init(struct ww_mutex *lock,
{
ww_mutex_base_init(&lock->base, ww_class->mutex_name, &ww_class->mutex_key);
lock->ctx = NULL;
-#ifdef DEBUG_WW_MUTEXES
lock->ww_class = ww_class;
-#endif
}
/**
@@ -149,8 +145,8 @@ static inline void ww_acquire_init(struct ww_acquire_ctx *ctx,
ctx->acquired = 0;
ctx->wounded = false;
ctx->is_wait_die = ww_class->is_wait_die;
-#ifdef DEBUG_WW_MUTEXES
ctx->ww_class = ww_class;
+#ifdef DEBUG_WW_MUTEXES
ctx->done_acquire = 0;
ctx->contending_lock = NULL;
#endif
--
2.51.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v12 3/6] rust: implement Class for ww_class support
2026-10-12 10:26 [PATCH v12 0/6] rust: add ww_mutex support Onur Özkan
2026-10-12 10:26 ` [PATCH v12 1/6] rust: add C wrappers for ww_mutex inline functions Onur Özkan
2026-10-12 10:26 ` [PATCH v12 2/6] ww_mutex: add ww_class field unconditionally Onur Özkan
@ 2026-10-12 10:26 ` Onur Özkan
2026-10-12 11:03 ` Gary Guo
2026-10-12 10:26 ` [PATCH v12 4/6] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard Onur Özkan
` (2 subsequent siblings)
5 siblings, 1 reply; 13+ messages in thread
From: Onur Özkan @ 2026-10-12 10:26 UTC (permalink / raw)
To: rust-for-linux
Cc: Onur Özkan, lossin, lyude, ojeda, alex.gaynor, boqun.feng,
gary, a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
longman, felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
Adds the Class type, the first step in supporting ww_mutex in Rust.
Class represents ww_class, used for deadlock avoidance for supporting
both wait-die and wound-wait semantics.
Also adds the define_ww_class and define_wd_class macros for safely
declaring static instances.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
rust/kernel/sync/lock.rs | 1 +
rust/kernel/sync/lock/ww_mutex.rs | 7 ++
rust/kernel/sync/lock/ww_mutex/class.rs | 151 ++++++++++++++++++++++++
3 files changed, 159 insertions(+)
create mode 100644 rust/kernel/sync/lock/ww_mutex.rs
create mode 100644 rust/kernel/sync/lock/ww_mutex/class.rs
diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
index 10b6b5e9b024f..f509250822c55 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 0000000000000..727c51cc73af8
--- /dev/null
+++ b/rust/kernel/sync/lock/ww_mutex.rs
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust abstractions for the kernel's wound-wait locking primitives.
+
+pub use class::Class;
+
+mod class;
diff --git a/rust/kernel/sync/lock/ww_mutex/class.rs b/rust/kernel/sync/lock/ww_mutex/class.rs
new file mode 100644
index 0000000000000..58a4467552700
--- /dev/null
+++ b/rust/kernel/sync/lock/ww_mutex/class.rs
@@ -0,0 +1,151 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Provides [`Class`] to group wound/wait mutexes to be acquired together
+//! and specifies which deadlock avoidance algorithm to use (e.g., wound-wait
+//! or wait-die).
+//!
+//! The [`define_ww_class!`] and [`define_wd_class!`] macros provide safe
+//! ways to create classes.
+
+use crate::{
+ bindings,
+ prelude::*,
+ types::Opaque, //
+};
+
+/// Defines a static wound-wait [`Class`].
+///
+/// # Examples
+///
+/// ```
+/// use kernel::define_ww_class;
+///
+/// define_ww_class!(SOME_WW_CLASS);
+/// ```
+#[macro_export]
+macro_rules! define_ww_class {
+ ($name:ident) => {
+ static $name: $crate::sync::lock::ww_mutex::Class =
+ // SAFETY: This is `static`, so address is fixed and won't move.
+ unsafe {
+ $crate::sync::lock::ww_mutex::Class::new_unpinned(
+ $crate::c_str!(::core::concat!(::core::stringify!($name), "_acquire")),
+ $crate::c_str!(::core::concat!(::core::stringify!($name), "_mutex")),
+ false,
+ )
+ };
+ };
+}
+
+/// Defines a static wait-die [`Class`].
+///
+/// # Examples
+///
+/// ```
+/// use kernel::define_wd_class;
+///
+/// define_wd_class!(SOME_WD_CLASS);
+/// ```
+#[macro_export]
+macro_rules! define_wd_class {
+ ($name:ident) => {
+ static $name: $crate::sync::lock::ww_mutex::Class =
+ // SAFETY: This is `static`, so address is fixed and won't move.
+ unsafe {
+ $crate::sync::lock::ww_mutex::Class::new_unpinned(
+ $crate::c_str!(::core::concat!(::core::stringify!($name), "_acquire")),
+ $crate::c_str!(::core::concat!(::core::stringify!($name), "_mutex")),
+ true,
+ )
+ };
+ };
+}
+
+/// Used to group mutexes together for deadlock avoidance.
+///
+/// All mutexes that might be acquired together should use the same class.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::{
+/// define_wd_class,
+/// define_ww_class, //
+/// };
+///
+/// define_ww_class!(SOME_WW_CLASS);
+/// define_wd_class!(SOME_WD_CLASS);
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[pin_data]
+#[repr(transparent)]
+pub struct Class {
+ #[pin]
+ pub(super) inner: Opaque<bindings::ww_class>,
+}
+
+impl Class {
+ /// Creates an unpinned [`Class`].
+ ///
+ /// You should prefer using [`define_ww_class!`] and [`define_wd_class!`]
+ /// macros. This function is `pub` only so that those macros can use it.
+ /// The alternative would be to expose the private fields of [`Class`]
+ /// which is less desirable.
+ ///
+ /// # Safety
+ ///
+ /// Caller must guarantee that the returned value must be pinned before
+ /// its first use.
+ pub const unsafe fn new_unpinned(
+ acquire_name: &'static CStr,
+ mutex_name: &'static CStr,
+ is_wait_die: bool,
+ ) -> Self {
+ Class {
+ inner: Opaque::new(bindings::ww_class {
+ stamp: bindings::atomic_long_t { counter: 0 },
+ acquire_name: crate::str::as_char_ptr_in_const_context(acquire_name),
+ mutex_name: crate::str::as_char_ptr_in_const_context(mutex_name),
+ 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.
+ //
+ // For reference, 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.
+ //
+ // For reference, see __WW_CLASS_INITIALIZER() in
+ // "include/linux/ww_mutex.h".
+ mutex_key: unsafe { core::mem::zeroed() },
+ }),
+ }
+ }
+
+ /// Creates a [`Class`] from a raw pointer.
+ ///
+ /// This function is intended for interoperability with C code.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `ptr` points to the `inner` field of
+ /// [`Class`] and that it remains valid for the lifetime `'a`.
+ #[inline]
+ pub const unsafe fn from_raw<'a>(ptr: *mut bindings::ww_class) -> &'a Self {
+ // SAFETY: By the safety contract, `ptr` is valid to construct `Class`.
+ unsafe { &*ptr.cast() }
+ }
+}
+
+// SAFETY: [`Class`] is immutable except for its atomic stamp. It's fine to share it across threads.
+unsafe impl Sync for Class {}
+// SAFETY: Doesn't hold anything thread-specific. It's safe to send to other threads.
+unsafe impl Send for Class {}
--
2.51.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v12 4/6] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard
2026-10-12 10:26 [PATCH v12 0/6] rust: add ww_mutex support Onur Özkan
` (2 preceding siblings ...)
2026-10-12 10:26 ` [PATCH v12 3/6] rust: implement Class for ww_class support Onur Özkan
@ 2026-10-12 10:26 ` Onur Özkan
2026-10-12 11:40 ` Gary Guo
2026-10-12 10:26 ` [PATCH v12 5/6] rust: ww_mutex: implement LockSet Onur Özkan
2026-10-12 10:26 ` [PATCH v12 6/6] MAINTAINERS: add Onur Özkan as WW MUTEX maintainer Onur Özkan
5 siblings, 1 reply; 13+ messages in thread
From: Onur Özkan @ 2026-10-12 10:26 UTC (permalink / raw)
To: rust-for-linux
Cc: Onur Özkan, lossin, lyude, ojeda, alex.gaynor, boqun.feng,
gary, a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
longman, felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
Covers the low-level locking API (lock, try_lock and
interruptible variants) and integration with kernel bindings.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
rust/kernel/sync/lock/ww_mutex.rs | 434 ++++++++++++++++++
rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs | 149 ++++++
2 files changed, 583 insertions(+)
create mode 100644 rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs
diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_mutex.rs
index 727c51cc73af8..2423f89a7e762 100644
--- a/rust/kernel/sync/lock/ww_mutex.rs
+++ b/rust/kernel/sync/lock/ww_mutex.rs
@@ -1,7 +1,441 @@
// SPDX-License-Identifier: GPL-2.0
//! Rust abstractions for the kernel's wound-wait locking primitives.
+//!
+//! It is designed to avoid deadlocks when locking multiple [`Mutex`]es
+//! that belong to the same [`Class`]. Each lock acquisition uses an
+//! [`AcquireCtx`] to track ordering and ensure forward progress.
+//!
+//! See srctree/Documentation/locking/ww-mutex-design.rst for more details.
+use crate::{
+ bindings,
+ container_of,
+ error::to_result,
+ prelude::*,
+ types::{
+ NotThreadSafe,
+ Opaque, //
+ }, //
+};
+use core::{
+ cell::UnsafeCell,
+ marker::PhantomData, //
+};
+
+pub use acquire_ctx::AcquireCtx;
pub use class::Class;
+mod acquire_ctx;
mod class;
+
+/// A wound-wait (ww) mutex that is powered with deadlock avoidance
+/// when acquiring multiple locks of the same [`Class`].
+///
+/// Each mutex belongs to a [`Class`], which the wound-wait algorithm
+/// uses to figure out the order of acquisition and prevent deadlocks.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::{
+/// define_ww_class,
+/// sync::{
+/// lock::ww_mutex::{
+/// AcquireCtx,
+/// Class,
+/// Mutex, //
+/// },
+/// Arc,
+/// },
+/// };
+/// use pin_init::stack_pin_init;
+///
+/// define_ww_class!(SOME_WW_CLASS);
+///
+/// let mutex = Arc::pin_init(Mutex::new(42, &SOME_WW_CLASS), GFP_KERNEL)?;
+/// let ctx = KBox::pin_init(AcquireCtx::new(&SOME_WW_CLASS), GFP_KERNEL)?;
+///
+/// // SAFETY: The guard is dropped before `ctx`.
+/// let guard = unsafe { ctx.lock(&mutex) }?;
+/// assert_eq!(*guard, 42);
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[pin_data]
+#[repr(C)]
+pub struct Mutex<'a, T: ?Sized> {
+ _p: PhantomData<&'a Class>,
+ #[pin]
+ inner: Opaque<bindings::ww_mutex>,
+ data: UnsafeCell<T>,
+}
+
+impl<'class, T> Mutex<'class, T> {
+ /// Initializes [`Mutex`] with the given `data` and [`Class`].
+ pub fn new(data: T, class: &'class Class) -> impl PinInit<Self> {
+ let class_ptr = class.inner.get();
+ pin_init!(Mutex {
+ inner <- Opaque::ffi_init(|slot: *mut bindings::ww_mutex| {
+ // SAFETY: `class` is valid for the lifetime `'class` captured by `Self`.
+ unsafe { bindings::ww_mutex_init(slot, class_ptr) }
+ }),
+ data: UnsafeCell::new(data),
+ _p: PhantomData
+ })
+ }
+}
+
+impl<'class, T: ?Sized> Mutex<'class, T> {
+ /// Checks if this [`Mutex`] is currently locked.
+ ///
+ /// The returned value is racy as another thread can acquire
+ /// or release the lock immediately after this call returns.
+ #[inline]
+ pub fn is_locked(&self) -> bool {
+ // SAFETY: It's safe to call `ww_mutex_is_locked` on
+ // a valid mutex.
+ unsafe { bindings::ww_mutex_is_locked(self.inner.get()) }
+ }
+
+ /// Locks this [`Mutex`] without [`AcquireCtx`].
+ pub fn lock(&self) -> Result<MutexGuard<'_, T>> {
+ lock_common(self, None, LockKind::Regular)
+ }
+
+ /// Similar to [`Self::lock`], but can be interrupted by signals.
+ pub fn lock_interruptible(&self) -> Result<MutexGuard<'_, T>> {
+ lock_common(self, None, LockKind::Interruptible)
+ }
+
+ /// Tries to lock this [`Mutex`] with no [`AcquireCtx`] and without blocking.
+ ///
+ /// Unlike [`Self::lock`], no deadlock handling is performed.
+ pub fn try_lock(&self) -> Result<MutexGuard<'_, T>> {
+ lock_common(self, None, LockKind::Try)
+ }
+}
+
+impl<'class> Mutex<'class, ()> {
+ /// Creates a [`Mutex`] from a raw pointer.
+ ///
+ /// This function is intended for interoperability with C code.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that:
+ ///
+ /// - `ptr` is a valid pointer to a `ww_mutex`.
+ /// - `ptr` must remain valid for the lifetime `'a`.
+ /// - ww_class associated with this mutex must be valid for
+ /// the lifetime `'class`.
+ pub unsafe fn from_raw<'a>(ptr: *mut bindings::ww_mutex) -> &'a Self {
+ // SAFETY: By the safety contract, the caller guarantees that `ptr`
+ // points to a valid `ww_mutex` which is the `inner` field of `Mutex`,
+ // that it remains valid for the lifetime `'a` and the associated
+ // ww_class outlives `'class`.
+ //
+ // Because [`Mutex`] is `#[repr(C)]`, the `inner` field sits at a
+ // stable offset that `container_of!` can safely rely on.
+ unsafe { &*container_of!(Opaque::cast_from(ptr), Self, inner) }
+ }
+}
+
+// SAFETY: `Mutex` can be sent to another thread if the protected
+// data `T` can be.
+unsafe impl<T: ?Sized + Send> Send for Mutex<'_, T> {}
+
+// SAFETY: `Mutex` serialises access to the protected data, so it is
+// `Sync` as long as the data is `Send`.
+unsafe impl<T: ?Sized + Send> Sync for Mutex<'_, T> {}
+
+/// A guard that provides exclusive access to the data protected
+/// by a [`Mutex`].
+///
+/// # Invariants
+///
+/// The guard holds an exclusive lock on the associated [`Mutex`]. 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 MutexGuard<'a, T: ?Sized> {
+ mutex: &'a Mutex<'a, T>,
+ _not_send: NotThreadSafe,
+}
+
+impl<'a, T: ?Sized> MutexGuard<'a, T> {
+ /// Creates a new guard for the given [`Mutex`].
+ fn new(mutex: &'a Mutex<'a, T>) -> Self {
+ assert!(mutex.is_locked());
+
+ // INVARIANT: `lock_common` and `from_raw` ensure that the current task holds the mutex
+ // and transfer sole responsibility for unlocking it to this guard.
+ Self {
+ mutex,
+ _not_send: NotThreadSafe,
+ }
+ }
+}
+
+impl<'a> MutexGuard<'a, ()> {
+ /// Creates a [`MutexGuard`] from a raw pointer.
+ ///
+ /// If the given pointer refers to a mutex that is not locked,
+ /// returns [`EINVAL`].
+ ///
+ /// This function is intended for interoperability with C code.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that:
+ ///
+ /// - `ptr` is a valid pointer to a `ww_mutex`.
+ /// - `ptr` must remain valid for the lifetime `'b`.
+ /// - The `ww_class` associated with the `ww_mutex` must be valid for the lifetime `'b`.
+ /// - The current task holds the mutex.
+ /// - The caller transfers responsibility for unlocking the mutex to the returned guard
+ /// and must not unlock it separately (including through another guard).
+ /// - Any associated acquire context remains valid until the mutex is unlocked,
+ /// even if the returned guard is forgotten. The context must not be finalized or
+ /// reinitialized before then.
+ pub unsafe fn from_raw<'b>(ptr: *mut bindings::ww_mutex) -> Result<MutexGuard<'b, ()>> {
+ // SAFETY: By this function's safety contract, the caller guarantees that `ptr` points to a
+ // valid `ww_mutex` which is the `inner` field of a `Mutex`. The caller also guarantees
+ // that both `ptr` and the associated `ww_class` are valid for the lifetime `'b`.
+ let mutex = unsafe { Mutex::from_raw(ptr) };
+
+ if !mutex.is_locked() {
+ return Err(EINVAL);
+ }
+
+ Ok(MutexGuard::new(mutex))
+ }
+}
+
+impl<T: ?Sized> core::ops::Deref for MutexGuard<'_, T> {
+ type Target = T;
+
+ fn deref(&self) -> &Self::Target {
+ // SAFETY: self.mutex is locked, so we have exclusive access.
+ unsafe { &*self.mutex.data.get() }
+ }
+}
+
+impl<T: ?Sized + Unpin> core::ops::DerefMut for MutexGuard<'_, T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ // SAFETY: self.mutex is locked, so we have exclusive access.
+ unsafe { &mut *self.mutex.data.get() }
+ }
+}
+
+impl<T: ?Sized> Drop for MutexGuard<'_, T> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: self.mutex is locked and are about to release it.
+ unsafe { bindings::ww_mutex_unlock(self.mutex.inner.get()) };
+ }
+}
+
+// SAFETY: `MutexGuard` can be shared between threads if the data can.
+unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
+
+/// Locking kinds used by [`lock_common`] to unify the internal
+/// locking logic.
+///
+/// It's best not to expose this type (and [`lock_common`]) to the
+/// kernel, as it allows internal API changes without worrying
+/// about breaking external compatibility.
+#[derive(Copy, Clone, Debug)]
+enum LockKind {
+ /// Blocks until lock is acquired.
+ Regular,
+ /// Blocks but can be interrupted by signals.
+ Interruptible,
+ /// Does not block, returns immediately if busy.
+ Try,
+}
+
+/// Internal helper that unifies the different locking kinds.
+///
+/// Returns [`EINVAL`] if the [`Mutex`] has a different [`Class`].
+fn lock_common<'a, T: ?Sized>(
+ mutex: &'a Mutex<'a, T>,
+ ctx: Option<&AcquireCtx<'_>>,
+ kind: LockKind,
+) -> Result<MutexGuard<'a, T>> {
+ let mutex_ptr = mutex.inner.get();
+
+ let ctx_ptr = match ctx {
+ Some(acquire_ctx) => {
+ let ctx_ptr = acquire_ctx.inner.get();
+
+ // SAFETY: `ctx_ptr` is a valid pointer for the entire
+ // lifetime of `ctx`.
+ let ctx_class = unsafe { (*ctx_ptr).ww_class };
+
+ // SAFETY: `mutex_ptr` is a valid pointer for the entire
+ // lifetime of `mutex`.
+ let mutex_class = unsafe { (*mutex_ptr).ww_class };
+
+ // `ctx` and `mutex` must use the same class.
+ if ctx_class != mutex_class {
+ return Err(EINVAL);
+ }
+
+ ctx_ptr
+ }
+ None => core::ptr::null_mut(),
+ };
+
+ match kind {
+ LockKind::Regular => {
+ // SAFETY: `Mutex` is always pinned. If `AcquireCtx` 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(mutex_ptr, ctx_ptr) };
+
+ to_result(ret)?;
+ }
+ LockKind::Interruptible => {
+ // SAFETY: `Mutex` is always pinned. If `AcquireCtx` 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(mutex_ptr, ctx_ptr) };
+
+ to_result(ret)?;
+ }
+ LockKind::Try => {
+ // SAFETY: `Mutex` is always pinned. If `AcquireCtx` 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(mutex_ptr, ctx_ptr) };
+
+ if ret == 0 {
+ return Err(EBUSY);
+ } else {
+ to_result(ret)?;
+ }
+ }
+ };
+
+ Ok(MutexGuard::new(mutex))
+}
+
+#[kunit_tests(rust_kernel_ww_mutex)]
+mod tests {
+ use super::*;
+ use crate::{
+ define_wd_class,
+ define_ww_class,
+ sync::Arc, //
+ };
+
+ define_ww_class!(TEST_WOUND_WAIT_CLASS);
+ define_wd_class!(TEST_WAIT_DIE_CLASS);
+
+ #[test]
+ fn test_ww_mutex_basic_lock_unlock() -> Result {
+ let mutex = Arc::pin_init(Mutex::new(42, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let ctx = KBox::pin_init(AcquireCtx::new(&TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+
+ // SAFETY: The guard is dropped before `ctx`.
+ let guard = unsafe { ctx.lock(&mutex) }?;
+ assert_eq!(*guard, 42);
+
+ // Drop the lock.
+ drop(guard);
+
+ // SAFETY: The guard is dropped before `ctx`.
+ let mut guard = unsafe { ctx.lock(&mutex) }?;
+ *guard = 100;
+ assert_eq!(*guard, 100);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_ww_mutex_trylock() -> Result {
+ let mutex = Arc::pin_init(Mutex::new(123, &TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+ let ctx = KBox::pin_init(AcquireCtx::new(&TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+
+ // `try_lock` on unlocked mutex should succeed.
+ // SAFETY: The guard is dropped before `ctx`.
+ let guard = unsafe { ctx.try_lock(&mutex) }?;
+ assert_eq!(*guard, 123);
+
+ // Now it should fail immediately as it's already locked.
+ // SAFETY: Any returned guard is dropped immediately, before `ctx`.
+ assert!(unsafe { ctx.try_lock(&mutex) }.is_err());
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_ww_mutex_is_locked() -> Result {
+ let mutex = Arc::pin_init(Mutex::new("hello", &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let ctx = KBox::pin_init(AcquireCtx::new(&TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+
+ // Should not be locked initially.
+ assert!(!mutex.is_locked());
+
+ // SAFETY: The guard is dropped before `ctx`.
+ let guard = unsafe { ctx.lock(&mutex) }?;
+ assert!(mutex.is_locked());
+
+ drop(guard);
+ assert!(!mutex.is_locked());
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_ww_acquire_context_done() -> Result {
+ let mutex1 = Arc::pin_init(Mutex::new(1, &TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+ let mutex2 = Arc::pin_init(Mutex::new(2, &TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+ let ctx = KBox::pin_init(AcquireCtx::new(&TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+
+ // Acquire multiple mutexes with the same context.
+ // SAFETY: The guard is dropped before `ctx`.
+ let guard1 = unsafe { ctx.lock(&mutex1) }?;
+ // SAFETY: The guard is dropped before `ctx`.
+ let guard2 = unsafe { ctx.lock(&mutex2) }?;
+
+ assert_eq!(*guard1, 1);
+ assert_eq!(*guard2, 2);
+
+ ctx.done();
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_mutex_without_ctx() -> Result {
+ let mutex = Arc::pin_init(Mutex::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(())
+ }
+
+ #[test]
+ fn test_guard_from_raw_unlocks_mutex() -> Result {
+ let mutex = Arc::pin_init(Mutex::new((), &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let guard = mutex.lock()?;
+
+ // Keep the mutex locked without leaving another guard responsible for unlocking it.
+ core::mem::forget(guard);
+
+ // SAFETY: `mutex` and its class remain valid for this test. The current task holds
+ // the mutex, and forgetting the previous guard lets this guard take over unlocking it.
+ let guard = unsafe { MutexGuard::from_raw(mutex.inner.get()) }?;
+ assert!(mutex.is_locked());
+
+ drop(guard);
+ assert!(!mutex.is_locked());
+
+ Ok(())
+ }
+}
diff --git a/rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs b/rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs
new file mode 100644
index 0000000000000..18f4654e7df26
--- /dev/null
+++ b/rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Provides [`AcquireCtx`] for managing multiple wound/wait
+//! mutexes from the same [`Class`].
+
+use super::{
+ lock_common,
+ Class,
+ LockKind,
+ Mutex,
+ MutexGuard, //
+};
+use crate::{
+ bindings,
+ prelude::*,
+ types::Opaque, //
+};
+use core::marker::PhantomData;
+
+/// Groups multiple [`Mutex`]es for deadlock avoidance when acquired
+/// with the same [`Class`].
+///
+/// # Examples
+///
+/// ```
+/// use kernel::{
+/// define_ww_class,
+/// sync::{
+/// lock::ww_mutex::{
+/// AcquireCtx,
+/// Class,
+/// Mutex, //
+/// },
+/// Arc,
+/// },
+/// };
+/// use pin_init::stack_pin_init;
+///
+/// define_ww_class!(SOME_WW_CLASS);
+///
+/// // Create mutexes.
+/// let mutex1 = Arc::pin_init(Mutex::new(1, &SOME_WW_CLASS), GFP_KERNEL)?;
+/// let mutex2 = Arc::pin_init(Mutex::new(2, &SOME_WW_CLASS), GFP_KERNEL)?;
+///
+/// // Create acquire context for deadlock avoidance.
+/// let ctx = KBox::pin_init(AcquireCtx::new(&SOME_WW_CLASS), GFP_KERNEL)?;
+///
+/// // SAFETY: The guard is dropped before `ctx`.
+/// let guard1 = unsafe { ctx.lock(&mutex1) }?;
+/// // SAFETY: The guard is dropped before `ctx`.
+/// let guard2 = unsafe { ctx.lock(&mutex2) }?;
+///
+/// // Mark acquisition phase as complete.
+/// ctx.done();
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[pin_data(PinnedDrop)]
+#[repr(transparent)]
+pub struct AcquireCtx<'a> {
+ #[pin]
+ pub(super) inner: Opaque<bindings::ww_acquire_ctx>,
+ _p: PhantomData<&'a Class>,
+}
+
+impl<'class> AcquireCtx<'class> {
+ /// Initializes a new [`AcquireCtx`] with the given [`Class`].
+ pub fn new(class: &'class Class) -> impl PinInit<Self> {
+ let class_ptr = class.inner.get();
+ pin_init!(AcquireCtx {
+ inner <- Opaque::ffi_init(|slot: *mut bindings::ww_acquire_ctx| {
+ // SAFETY: `class` is valid for the lifetime `'class` captured
+ // by `AcquireCtx`.
+ unsafe { bindings::ww_acquire_init(slot, class_ptr) }
+ }),
+ _p: PhantomData
+ })
+ }
+
+ /// Creates a [`AcquireCtx`] from a raw pointer.
+ ///
+ /// This function is intended for interoperability with C code.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `ptr` is a valid pointer to the `inner` field
+ /// of [`AcquireCtx`] and that it remains valid for the lifetime `'a`.
+ pub unsafe fn from_raw<'a>(ptr: *mut bindings::ww_acquire_ctx) -> &'a Self {
+ // SAFETY: By the safety contract, `ptr` is valid to construct `AcquireCtx`.
+ unsafe { &*ptr.cast() }
+ }
+
+ /// Marks the end of the acquire phase.
+ ///
+ /// Calling this function is optional. It is just useful to document
+ /// the code and clearly designated the acquire phase from actually
+ /// using the locked data structures.
+ ///
+ /// After calling this function, no more mutexes can be acquired with
+ /// this context.
+ #[inline]
+ pub fn done(&self) {
+ // SAFETY: `self.inner` contains a valid, initialized acquire context.
+ unsafe { bindings::ww_acquire_done(self.inner.get()) };
+ }
+
+ /// Locks the given [`Mutex`] on this [`AcquireCtx`].
+ ///
+ /// # Safety
+ ///
+ /// On success, the caller must keep this context valid until the mutex is unlocked,
+ /// even if the returned guard is forgotten. The context must not be finalized or
+ /// reinitialized while the mutex remains locked.
+ pub unsafe fn lock<'a, T>(&'a self, mutex: &'a Mutex<'a, T>) -> Result<MutexGuard<'a, T>> {
+ lock_common(mutex, Some(self), LockKind::Regular)
+ }
+
+ /// Similar to [`Self::lock`], but can be interrupted by signals.
+ ///
+ /// # Safety
+ ///
+ /// The same requirements as [`Self::lock`] apply.
+ pub unsafe fn lock_interruptible<'a, T>(
+ &'a self,
+ mutex: &'a Mutex<'a, T>,
+ ) -> Result<MutexGuard<'a, T>> {
+ lock_common(mutex, Some(self), LockKind::Interruptible)
+ }
+
+ /// Tries to lock the [`Mutex`] on this [`AcquireCtx`] without blocking.
+ ///
+ /// Unlike [`Self::lock`], no deadlock handling is performed.
+ ///
+ /// # Safety
+ ///
+ /// The same requirements as [`Self::lock`] apply.
+ pub unsafe fn try_lock<'a, T>(&'a self, mutex: &'a Mutex<'a, T>) -> Result<MutexGuard<'a, T>> {
+ lock_common(mutex, Some(self), LockKind::Try)
+ }
+}
+
+#[pinned_drop]
+impl PinnedDrop for AcquireCtx<'_> {
+ fn drop(self: Pin<&mut Self>) {
+ // SAFETY: The locking methods require callers to release all acquired locks
+ // before this context is dropped.
+ unsafe { bindings::ww_acquire_fini(self.inner.get()) };
+ }
+}
--
2.51.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v12 5/6] rust: ww_mutex: implement LockSet
2026-10-12 10:26 [PATCH v12 0/6] rust: add ww_mutex support Onur Özkan
` (3 preceding siblings ...)
2026-10-12 10:26 ` [PATCH v12 4/6] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard Onur Özkan
@ 2026-10-12 10:26 ` Onur Özkan
2026-10-12 12:02 ` Gary Guo
2026-10-12 10:26 ` [PATCH v12 6/6] MAINTAINERS: add Onur Özkan as WW MUTEX maintainer Onur Özkan
5 siblings, 1 reply; 13+ messages in thread
From: Onur Özkan @ 2026-10-12 10:26 UTC (permalink / raw)
To: rust-for-linux
Cc: Onur Özkan, lossin, lyude, ojeda, alex.gaynor, boqun.feng,
gary, a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
longman, felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
LockSet is a high-level and safe API built on top of
ww_mutex which provides a safe and easy to use API
while keeping the ww_mutex semantics.
When EDEADLK is hit it drops all held locks and waits for the
contended mutex. It then retries the given (by the user)
locking algorithm with that mutex held and the same acquire
context until it succeeds.
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
rust/kernel/sync/lock/ww_mutex.rs | 6 +
rust/kernel/sync/lock/ww_mutex/lock_set.rs | 449 +++++++++++++++++++++
2 files changed, 455 insertions(+)
create mode 100644 rust/kernel/sync/lock/ww_mutex/lock_set.rs
diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_mutex.rs
index 2423f89a7e762..17054d0807f65 100644
--- a/rust/kernel/sync/lock/ww_mutex.rs
+++ b/rust/kernel/sync/lock/ww_mutex.rs
@@ -6,6 +6,10 @@
//! that belong to the same [`Class`]. Each lock acquisition uses an
//! [`AcquireCtx`] to track ordering and ensure forward progress.
//!
+//! It is recommended to use [`LockSet`] as it provides safe high-level
+//! interface that automatically handles deadlocks, retries and context
+//! management.
+//!
//! See srctree/Documentation/locking/ww-mutex-design.rst for more details.
use crate::{
@@ -25,9 +29,11 @@
pub use acquire_ctx::AcquireCtx;
pub use class::Class;
+pub use lock_set::LockSet;
mod acquire_ctx;
mod class;
+mod lock_set;
/// A wound-wait (ww) mutex that is powered with deadlock avoidance
/// when acquiring multiple locks of the same [`Class`].
diff --git a/rust/kernel/sync/lock/ww_mutex/lock_set.rs b/rust/kernel/sync/lock/ww_mutex/lock_set.rs
new file mode 100644
index 0000000000000..141a3305892ef
--- /dev/null
+++ b/rust/kernel/sync/lock/ww_mutex/lock_set.rs
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Provides [`LockSet`] which automatically detects [`EDEADLK`],
+//! releases all locks, waits for the contended mutex and retries the user
+//! supplied locking algorithm with the same acquire context.
+
+use super::{
+ AcquireCtx,
+ Class,
+ Mutex, //
+};
+use crate::{
+ bindings,
+ prelude::*,
+ types::NotThreadSafe, //
+};
+use core::ptr::NonNull;
+
+/// A tracked set of [`Mutex`] locks acquired under the same [`Class`].
+///
+/// It ensures proper cleanup and retry mechanism on deadlocks and provides
+/// safe access to locked data via [`LockSet::with_locked`].
+///
+/// Typical usage is through [`LockSet::lock_all`], which retries a
+/// user supplied locking algorithm until it succeeds without deadlock.
+pub struct LockSet<'a> {
+ acquire_ctx: Pin<KBox<AcquireCtx<'a>>>,
+ taken: KVec<RawGuard>,
+ // Set by `lock()` on `EDEADLK`; the mutex remains valid for `'a`.
+ contended: Option<NonNull<bindings::ww_mutex>>,
+}
+
+/// Used by [`LockSet`] to track acquired locks.
+///
+/// This type is strictly crate-private and must never be exposed
+/// outside this crate.
+struct RawGuard {
+ mutex_ptr: NonNull<bindings::ww_mutex>,
+ _not_send: NotThreadSafe,
+}
+
+impl Drop for RawGuard {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: `mutex_ptr` originates from a locked `Mutex` and remains
+ // valid for the lifetime of this guard, so unlocking here is sound.
+ unsafe { bindings::ww_mutex_unlock(self.mutex_ptr.as_ptr()) };
+ }
+}
+
+impl<'a> Drop for LockSet<'a> {
+ fn drop(&mut self) {
+ self.release_all_locks();
+ }
+}
+
+impl<'a> LockSet<'a> {
+ /// Creates a new [`LockSet`] with the given [`Class`].
+ ///
+ /// All locks taken through this [`LockSet`] must belong to the
+ /// same [`Class`].
+ pub fn new(class: &'a Class) -> Result<Self> {
+ Ok(Self {
+ acquire_ctx: KBox::pin_init(AcquireCtx::new(class), GFP_KERNEL)?,
+ taken: KVec::new(),
+ contended: None,
+ })
+ }
+
+ /// Creates a new [`LockSet`] using an existing [`AcquireCtx`].
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `acquire_ctx` is properly initialized
+ /// and holds no [`Mutex`]es.
+ pub unsafe fn new_with_acquire_ctx(acquire_ctx: Pin<KBox<AcquireCtx<'a>>>) -> Self {
+ Self {
+ acquire_ctx,
+ taken: KVec::new(),
+ contended: None,
+ }
+ }
+
+ /// Attempts to lock the given [`Mutex`] and stores a guard for it.
+ ///
+ /// Returns success if the mutex is already held by this set.
+ pub fn lock<T>(&mut self, mutex: &'a Mutex<'a, T>) -> Result {
+ // Preserve the first contended mutex until deadlock recovery has completed.
+ if self.contended.is_some() {
+ return Err(EDEADLK);
+ }
+
+ // SAFETY: All tracked locks are released before the context is dropped.
+ // Forgetting the set leaks the context.
+ let guard = match unsafe { self.acquire_ctx.lock(mutex) } {
+ Ok(guard) => guard,
+ Err(e) if e == EDEADLK => {
+ self.contended = NonNull::new(mutex.inner.get());
+ return Err(e);
+ }
+ // The mutex acquired during backoff is encountered again on retry.
+ Err(e) if e == EALREADY => return Ok(()),
+ Err(e) => return Err(e),
+ };
+
+ let raw_guard = RawGuard {
+ // SAFETY: We just locked it above so it's a valid pointer.
+ mutex_ptr: unsafe { NonNull::new_unchecked(guard.mutex.inner.get()) },
+ _not_send: NotThreadSafe,
+ };
+
+ // Transfer unlocking to `raw_guard` before the fallible push, so failure
+ // drops only one guard and unlocks the mutex exactly once.
+ core::mem::forget(guard);
+ self.taken.push(raw_guard, GFP_KERNEL)?;
+
+ Ok(())
+ }
+
+ /// Runs `locking_algorithm` until success with retrying on deadlock.
+ ///
+ /// `locking_algorithm` must acquire all needed locks and immediately propagate
+ /// any error from [`Self::lock`] unchanged (e.g. using `?`).
+ /// If [`Self::lock`] returns [`EDEADLK`], this function releases all held locks
+ /// and waits to acquire the contended mutex. It then retries with that mutex
+ /// held, preserving the acquire context's original ticket.
+ ///
+ /// Once all locks are acquired successfully, `on_all_locks_taken` is
+ /// invoked for exclusive access to the locked values. It must not acquire
+ /// additional locks through this set. Afterwards, all locks are released.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use kernel::{
+ /// alloc::KBox,
+ /// define_ww_class,
+ /// prelude::*,
+ /// sync::{
+ /// lock::ww_mutex::{
+ /// LockSet,
+ /// Mutex, //
+ /// },
+ /// Arc,
+ /// },
+ /// };
+ /// use pin_init::stack_pin_init;
+ ///
+ /// define_ww_class!(SOME_WOUND_WAIT_CLASS);
+ ///
+ /// let mutex1 = Arc::pin_init(Mutex::new(0, &SOME_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ /// let mutex2 = Arc::pin_init(Mutex::new(0, &SOME_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ /// let mut lock_set = KBox::pin_init(LockSet::new(&SOME_WOUND_WAIT_CLASS)?, GFP_KERNEL)?;
+ ///
+ /// lock_set.lock_all(
+ /// // `locking_algorithm` closure
+ /// |lock_set| {
+ /// lock_set.lock(&mutex1)?;
+ /// lock_set.lock(&mutex2)?;
+ ///
+ /// Ok(())
+ /// },
+ /// // `on_all_locks_taken` closure
+ /// |lock_set| {
+ /// // Safely mutate both values while holding the locks.
+ /// lock_set.with_locked(&mutex1, |v| *v += 1)?;
+ /// lock_set.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 LockSet<'a>) -> Result,
+ Y: FnMut(&mut LockSet<'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 => {
+ self.cleanup_on_deadlock()?;
+ continue;
+ }
+ Err(e) => {
+ self.release_all_locks();
+ return Err(e);
+ }
+ }
+ }
+ }
+
+ /// Executes `access` with a mutable reference to the data behind [`Mutex`].
+ ///
+ /// Fails with [`EINVAL`] if the [`Mutex`] was not locked in this [`LockSet`].
+ pub fn with_locked<T: Unpin, Y>(
+ &mut self,
+ mutex: &'a Mutex<'a, T>,
+ access: impl for<'b> FnOnce(&'b mut T) -> Y,
+ ) -> Result<Y> {
+ let mutex_ptr = mutex.inner.get();
+
+ if self
+ .taken
+ .iter()
+ .any(|guard| guard.mutex_ptr.as_ptr() == mutex_ptr)
+ {
+ // SAFETY: We hold the lock corresponding to `mutex`, so we have
+ // exclusive access to its protected data.
+ let value = unsafe { &mut *mutex.data.get() };
+ Ok(access(value))
+ } else {
+ // `mutex` isn't locked in this `LockSet`.
+ Err(EINVAL)
+ }
+ }
+
+ /// Releases all currently held locks in this [`LockSet`].
+ fn release_all_locks(&mut self) {
+ // `Drop` implementation of the `RawGuard` takes care of the unlocking.
+ self.taken.clear();
+ }
+
+ /// Releases all locks and acquires the contended mutex with the same context.
+ fn cleanup_on_deadlock(&mut self) -> Result {
+ self.release_all_locks();
+
+ // A callback may return `EDEADLK` without a failed lock acquisition.
+ let contended = self.contended.take().ok_or(EDEADLK)?;
+
+ // SAFETY: `lock()` recorded this pointer from a mutex borrowed for `'a`,
+ // so both the mutex and its class remain valid for that lifetime.
+ let mutex = unsafe { Mutex::from_raw(contended.as_ptr()) };
+
+ // With no locks held, regular locking waits for this mutex to become available.
+ self.lock(mutex)
+ }
+}
+
+#[kunit_tests(rust_kernel_lock_set)]
+mod tests {
+ use super::*;
+ use crate::{
+ define_wd_class,
+ define_ww_class,
+ sync::Arc, //
+ };
+
+ define_ww_class!(TEST_WOUND_WAIT_CLASS);
+ define_wd_class!(TEST_WAIT_DIE_CLASS);
+
+ #[test]
+ fn test_lock_set_basic_lock_unlock() -> Result {
+ let mutex = Arc::pin_init(Mutex::new(10, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let mut lock_set = KBox::pin_init(LockSet::new(&TEST_WOUND_WAIT_CLASS)?, GFP_KERNEL)?;
+
+ lock_set.lock(&mutex)?;
+ lock_set.lock(&mutex)?;
+ assert_eq!(lock_set.taken.len(), 1);
+
+ lock_set.with_locked(&mutex, |v| {
+ assert_eq!(*v, 10);
+ })?;
+
+ lock_set.release_all_locks();
+ assert!(!mutex.is_locked());
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_lock_set_with_locked_mutates_data() -> Result {
+ let mutex = Arc::pin_init(Mutex::new(5, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let mut lock_set = KBox::pin_init(LockSet::new(&TEST_WOUND_WAIT_CLASS)?, GFP_KERNEL)?;
+
+ lock_set.lock(&mutex)?;
+
+ lock_set.with_locked(&mutex, |v| {
+ assert_eq!(*v, 5);
+ // Increment the value.
+ *v += 7;
+ })?;
+
+ lock_set.with_locked(&mutex, |v| {
+ // Check that mutation took effect.
+ assert_eq!(*v, 12);
+ })?;
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_lock_all_success() -> Result {
+ let mutex1 = Arc::pin_init(Mutex::new(1, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let mutex2 = Arc::pin_init(Mutex::new(2, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let mut lock_set = KBox::pin_init(LockSet::new(&TEST_WOUND_WAIT_CLASS)?, GFP_KERNEL)?;
+
+ let res = lock_set.lock_all(
+ // `locking_algorithm` closure
+ |lock_set| {
+ lock_set.lock(&mutex1)?;
+ lock_set.lock(&mutex2)?;
+ Ok(())
+ },
+ // `on_all_locks_taken` closure
+ |lock_set| {
+ lock_set.with_locked(&mutex1, |v| *v += 10)?;
+ lock_set.with_locked(&mutex2, |v| *v += 20)?;
+ Ok((
+ lock_set.with_locked(&mutex1, |v| *v)?,
+ lock_set.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 {
+ let mutex1 = Arc::pin_init(Mutex::new(1, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let mutex2 = Arc::pin_init(Mutex::new("hello", &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let mut lock_set = KBox::pin_init(LockSet::new(&TEST_WOUND_WAIT_CLASS)?, GFP_KERNEL)?;
+
+ lock_set.lock_all(
+ // `locking_algorithm` closure
+ |lock_set| {
+ lock_set.lock(&mutex1)?;
+ lock_set.lock(&mutex2)?;
+
+ Ok(())
+ },
+ // `on_all_locks_taken` closure
+ |lock_set| {
+ lock_set.with_locked(&mutex1, |v| assert_eq!(*v, 1))?;
+ lock_set.with_locked(&mutex2, |v| assert_eq!(*v, "hello"))?;
+ Ok(())
+ },
+ )?;
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_lock_all_retries_on_deadlock() -> Result {
+ let first = Arc::pin_init(Mutex::new(1, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let mutex = Arc::pin_init(Mutex::new(99, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let mut lock_set = KBox::pin_init(LockSet::new(&TEST_WOUND_WAIT_CLASS)?, GFP_KERNEL)?;
+ let mut first_try = true;
+ let ctx = lock_set.acquire_ctx.inner.get();
+ // SAFETY: The set owns this initialized context throughout the test.
+ let stamp = unsafe { (*ctx).stamp };
+
+ let res = lock_set.lock_all(
+ // `locking_algorithm` closure
+ |lock_set| {
+ if first_try {
+ first_try = false;
+ lock_set.lock(&first)?;
+ // Simulate a failed acquisition with its contended mutex recorded.
+ lock_set.contended = NonNull::new(mutex.inner.get());
+ return Err(EDEADLK);
+ }
+
+ assert!(!first.is_locked());
+ // Backoff must acquire the contended mutex before retrying this callback.
+ lock_set.with_locked(&mutex, |v| assert_eq!(*v, 99))?;
+ lock_set.lock(&mutex)?;
+ assert_eq!(lock_set.taken.len(), 1);
+ Ok(())
+ },
+ // `on_all_locks_taken` closure
+ |lock_set| {
+ lock_set.with_locked(&mutex, |v| {
+ *v += 1;
+ *v
+ })
+ },
+ )?;
+
+ assert_eq!(res, 100);
+ assert!(!first.is_locked());
+ assert!(!mutex.is_locked());
+ // SAFETY: The context is still owned by the set and remains initialized.
+ assert_eq!(unsafe { (*ctx).stamp }, stamp);
+ Ok(())
+ }
+
+ #[test]
+ fn test_lock_all_deadlock_without_contended_mutex() -> Result {
+ let mutex = Arc::pin_init(Mutex::new(1, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let mut lock_set = KBox::pin_init(LockSet::new(&TEST_WOUND_WAIT_CLASS)?, GFP_KERNEL)?;
+ let mut attempts = 0;
+
+ let res = lock_set.lock_all(
+ |lock_set| {
+ attempts += 1;
+ lock_set.lock(&mutex)?;
+ // Return a different error on an unexpected retry to avoid an endless loop.
+ Err(if attempts == 1 { EDEADLK } else { EINVAL })
+ },
+ |_| Ok(()),
+ );
+
+ assert_eq!(res, Err(EDEADLK));
+ assert_eq!(attempts, 1);
+ assert!(!mutex.is_locked());
+ Ok(())
+ }
+
+ #[test]
+ fn test_with_locked_on_unlocked_mutex() -> Result {
+ let mutex = Arc::pin_init(Mutex::new(5, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let mut lock_set = KBox::pin_init(LockSet::new(&TEST_WOUND_WAIT_CLASS)?, GFP_KERNEL)?;
+
+ let ecode = lock_set.with_locked(&mutex, |_v| {}).unwrap_err();
+ assert_eq!(EINVAL, ecode);
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_with_different_classes() -> Result {
+ let mutex = Arc::pin_init(Mutex::new(5, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let mut lock_set = KBox::pin_init(LockSet::new(&TEST_WAIT_DIE_CLASS)?, GFP_KERNEL)?;
+
+ let ecode = lock_set.lock(&mutex).unwrap_err();
+ assert_eq!(EINVAL, ecode);
+
+ Ok(())
+ }
+}
--
2.51.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v12 6/6] MAINTAINERS: add Onur Özkan as WW MUTEX maintainer
2026-10-12 10:26 [PATCH v12 0/6] rust: add ww_mutex support Onur Özkan
` (4 preceding siblings ...)
2026-10-12 10:26 ` [PATCH v12 5/6] rust: ww_mutex: implement LockSet Onur Özkan
@ 2026-10-12 10:26 ` Onur Özkan
5 siblings, 0 replies; 13+ messages in thread
From: Onur Özkan @ 2026-10-12 10:26 UTC (permalink / raw)
To: rust-for-linux
Cc: Onur Özkan, lossin, lyude, ojeda, alex.gaynor, boqun.feng,
gary, a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
longman, felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
Add myself as a WW MUTEX maintainer to reflect ongoing work and future
maintenance of WW MUTEX.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 198bb740f7cc2..ff41d34f76f70 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -15265,6 +15265,7 @@ M: Peter Zijlstra <peterz@infradead.org>
M: Ingo Molnar <mingo@redhat.com>
M: Will Deacon <will@kernel.org>
M: Boqun Feng <boqun@kernel.org> (LOCKDEP & RUST)
+M: Onur Özkan <work@onurozkan.dev> (WW MUTEX)
R: Waiman Long <longman@redhat.com>
L: linux-kernel@vger.kernel.org
S: Maintained
--
2.51.2
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v12 3/6] rust: implement Class for ww_class support
2026-10-12 10:26 ` [PATCH v12 3/6] rust: implement Class for ww_class support Onur Özkan
@ 2026-10-12 11:03 ` Gary Guo
2026-10-12 11:26 ` Onur Özkan
0 siblings, 1 reply; 13+ messages in thread
From: Gary Guo @ 2026-10-12 11:03 UTC (permalink / raw)
To: Onur Özkan, rust-for-linux
Cc: lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary, a.hindborg,
aliceryhl, tmgross, dakr, peterz, mingo, will, longman,
felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
On Mon Oct 12, 2026 at 11:26 AM BST, Onur Özkan wrote:
> Adds the Class type, the first step in supporting ww_mutex in Rust.
> Class represents ww_class, used for deadlock avoidance for supporting
> both wait-die and wound-wait semantics.
>
> Also adds the define_ww_class and define_wd_class macros for safely
> declaring static instances.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
> Signed-off-by: Onur Özkan <work@onurozkan.dev>
Reviewed-by: Gary Guo <gary@garyguo.net>
With some nits below.
> ---
> rust/kernel/sync/lock.rs | 1 +
> rust/kernel/sync/lock/ww_mutex.rs | 7 ++
> rust/kernel/sync/lock/ww_mutex/class.rs | 151 ++++++++++++++++++++++++
> 3 files changed, 159 insertions(+)
> create mode 100644 rust/kernel/sync/lock/ww_mutex.rs
> create mode 100644 rust/kernel/sync/lock/ww_mutex/class.rs
>
> diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
> index 10b6b5e9b024f..f509250822c55 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 0000000000000..727c51cc73af8
> --- /dev/null
> +++ b/rust/kernel/sync/lock/ww_mutex.rs
> @@ -0,0 +1,7 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Rust abstractions for the kernel's wound-wait locking primitives.
> +
> +pub use class::Class;
> +
> +mod class;
> diff --git a/rust/kernel/sync/lock/ww_mutex/class.rs b/rust/kernel/sync/lock/ww_mutex/class.rs
> new file mode 100644
> index 0000000000000..58a4467552700
> --- /dev/null
> +++ b/rust/kernel/sync/lock/ww_mutex/class.rs
> @@ -0,0 +1,151 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Provides [`Class`] to group wound/wait mutexes to be acquired together
> +//! and specifies which deadlock avoidance algorithm to use (e.g., wound-wait
> +//! or wait-die).
> +//!
> +//! The [`define_ww_class!`] and [`define_wd_class!`] macros provide safe
> +//! ways to create classes.
> +
> +use crate::{
> + bindings,
> + prelude::*,
> + types::Opaque, //
> +};
> +
> +/// Defines a static wound-wait [`Class`].
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::define_ww_class;
> +///
> +/// define_ww_class!(SOME_WW_CLASS);
> +/// ```
> +#[macro_export]
> +macro_rules! define_ww_class {
> + ($name:ident) => {
> + static $name: $crate::sync::lock::ww_mutex::Class =
> + // SAFETY: This is `static`, so address is fixed and won't move.
> + unsafe {
> + $crate::sync::lock::ww_mutex::Class::new_unpinned(
> + $crate::c_str!(::core::concat!(::core::stringify!($name), "_acquire")),
> + $crate::c_str!(::core::concat!(::core::stringify!($name), "_mutex")),
> + false,
> + )
> + };
> + };
> +}
> +
> +/// Defines a static wait-die [`Class`].
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::define_wd_class;
> +///
> +/// define_wd_class!(SOME_WD_CLASS);
> +/// ```
> +#[macro_export]
> +macro_rules! define_wd_class {
> + ($name:ident) => {
> + static $name: $crate::sync::lock::ww_mutex::Class =
> + // SAFETY: This is `static`, so address is fixed and won't move.
> + unsafe {
> + $crate::sync::lock::ww_mutex::Class::new_unpinned(
> + $crate::c_str!(::core::concat!(::core::stringify!($name), "_acquire")),
> + $crate::c_str!(::core::concat!(::core::stringify!($name), "_mutex")),
> + true,
> + )
> + };
> + };
> +}
> +
> +/// Used to group mutexes together for deadlock avoidance.
> +///
> +/// All mutexes that might be acquired together should use the same class.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::{
> +/// define_wd_class,
> +/// define_ww_class, //
> +/// };
> +///
> +/// define_ww_class!(SOME_WW_CLASS);
> +/// define_wd_class!(SOME_WD_CLASS);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +#[pin_data]
> +#[repr(transparent)]
> +pub struct Class {
> + #[pin]
> + pub(super) inner: Opaque<bindings::ww_class>,
> +}
> +
> +impl Class {
> + /// Creates an unpinned [`Class`].
> + ///
> + /// You should prefer using [`define_ww_class!`] and [`define_wd_class!`]
> + /// macros. This function is `pub` only so that those macros can use it.
> + /// The alternative would be to expose the private fields of [`Class`]
> + /// which is less desirable.
Replace the paragraph with `#[doc(hidden)]`?
> + ///
> + /// # Safety
> + ///
> + /// Caller must guarantee that the returned value must be pinned before
> + /// its first use.
> + pub const unsafe fn new_unpinned(
> + acquire_name: &'static CStr,
> + mutex_name: &'static CStr,
> + is_wait_die: bool,
> + ) -> Self {
> + Class {
> + inner: Opaque::new(bindings::ww_class {
> + stamp: bindings::atomic_long_t { counter: 0 },
> + acquire_name: crate::str::as_char_ptr_in_const_context(acquire_name),
> + mutex_name: crate::str::as_char_ptr_in_const_context(mutex_name),
> + 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.
> + //
> + // For reference, 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.
> + //
> + // For reference, see __WW_CLASS_INITIALIZER() in
> + // "include/linux/ww_mutex.h".
> + mutex_key: unsafe { core::mem::zeroed() },
Does `..pin_init::zeroed()` work in this case?
Best,
Gary
> + }),
> + }
> + }
> +
> + /// Creates a [`Class`] from a raw pointer.
> + ///
> + /// This function is intended for interoperability with C code.
> + ///
> + /// # Safety
> + ///
> + /// The caller must ensure that `ptr` points to the `inner` field of
> + /// [`Class`] and that it remains valid for the lifetime `'a`.
> + #[inline]
> + pub const unsafe fn from_raw<'a>(ptr: *mut bindings::ww_class) -> &'a Self {
> + // SAFETY: By the safety contract, `ptr` is valid to construct `Class`.
> + unsafe { &*ptr.cast() }
> + }
> +}
> +
> +// SAFETY: [`Class`] is immutable except for its atomic stamp. It's fine to share it across threads.
> +unsafe impl Sync for Class {}
> +// SAFETY: Doesn't hold anything thread-specific. It's safe to send to other threads.
> +unsafe impl Send for Class {}
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v12 3/6] rust: implement Class for ww_class support
2026-10-12 11:03 ` Gary Guo
@ 2026-10-12 11:26 ` Onur Özkan
0 siblings, 0 replies; 13+ messages in thread
From: Onur Özkan @ 2026-10-12 11:26 UTC (permalink / raw)
To: Gary Guo
Cc: rust-for-linux, lossin, lyude, ojeda, alex.gaynor, boqun.feng,
a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
longman, felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
On Mon, 12 Oct 2026 12:03:04 +0100
Gary Guo <gary@garyguo.net> wrote:
> On Mon Oct 12, 2026 at 11:26 AM BST, Onur Özkan wrote:
> > Adds the Class type, the first step in supporting ww_mutex in Rust.
> > Class represents ww_class, used for deadlock avoidance for supporting
> > both wait-die and wound-wait semantics.
> >
> > Also adds the define_ww_class and define_wd_class macros for safely
> > declaring static instances.
> >
> > Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> > Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
> > Signed-off-by: Onur Özkan <work@onurozkan.dev>
>
> Reviewed-by: Gary Guo <gary@garyguo.net>
>
> With some nits below.
>
> > ---
> > rust/kernel/sync/lock.rs | 1 +
> > rust/kernel/sync/lock/ww_mutex.rs | 7 ++
> > rust/kernel/sync/lock/ww_mutex/class.rs | 151 ++++++++++++++++++++++++
> > 3 files changed, 159 insertions(+)
> > create mode 100644 rust/kernel/sync/lock/ww_mutex.rs
> > create mode 100644 rust/kernel/sync/lock/ww_mutex/class.rs
> >
> > diff --git a/rust/kernel/sync/lock.rs b/rust/kernel/sync/lock.rs
> > index 10b6b5e9b024f..f509250822c55 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 0000000000000..727c51cc73af8
> > --- /dev/null
> > +++ b/rust/kernel/sync/lock/ww_mutex.rs
> > @@ -0,0 +1,7 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +//! Rust abstractions for the kernel's wound-wait locking primitives.
> > +
> > +pub use class::Class;
> > +
> > +mod class;
> > diff --git a/rust/kernel/sync/lock/ww_mutex/class.rs b/rust/kernel/sync/lock/ww_mutex/class.rs
> > new file mode 100644
> > index 0000000000000..58a4467552700
> > --- /dev/null
> > +++ b/rust/kernel/sync/lock/ww_mutex/class.rs
> > @@ -0,0 +1,151 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +
> > +//! Provides [`Class`] to group wound/wait mutexes to be acquired together
> > +//! and specifies which deadlock avoidance algorithm to use (e.g., wound-wait
> > +//! or wait-die).
> > +//!
> > +//! The [`define_ww_class!`] and [`define_wd_class!`] macros provide safe
> > +//! ways to create classes.
> > +
> > +use crate::{
> > + bindings,
> > + prelude::*,
> > + types::Opaque, //
> > +};
> > +
> > +/// Defines a static wound-wait [`Class`].
> > +///
> > +/// # Examples
> > +///
> > +/// ```
> > +/// use kernel::define_ww_class;
> > +///
> > +/// define_ww_class!(SOME_WW_CLASS);
> > +/// ```
> > +#[macro_export]
> > +macro_rules! define_ww_class {
> > + ($name:ident) => {
> > + static $name: $crate::sync::lock::ww_mutex::Class =
> > + // SAFETY: This is `static`, so address is fixed and won't move.
> > + unsafe {
> > + $crate::sync::lock::ww_mutex::Class::new_unpinned(
> > + $crate::c_str!(::core::concat!(::core::stringify!($name), "_acquire")),
> > + $crate::c_str!(::core::concat!(::core::stringify!($name), "_mutex")),
> > + false,
> > + )
> > + };
> > + };
> > +}
> > +
> > +/// Defines a static wait-die [`Class`].
> > +///
> > +/// # Examples
> > +///
> > +/// ```
> > +/// use kernel::define_wd_class;
> > +///
> > +/// define_wd_class!(SOME_WD_CLASS);
> > +/// ```
> > +#[macro_export]
> > +macro_rules! define_wd_class {
> > + ($name:ident) => {
> > + static $name: $crate::sync::lock::ww_mutex::Class =
> > + // SAFETY: This is `static`, so address is fixed and won't move.
> > + unsafe {
> > + $crate::sync::lock::ww_mutex::Class::new_unpinned(
> > + $crate::c_str!(::core::concat!(::core::stringify!($name), "_acquire")),
> > + $crate::c_str!(::core::concat!(::core::stringify!($name), "_mutex")),
> > + true,
> > + )
> > + };
> > + };
> > +}
> > +
> > +/// Used to group mutexes together for deadlock avoidance.
> > +///
> > +/// All mutexes that might be acquired together should use the same class.
> > +///
> > +/// # Examples
> > +///
> > +/// ```
> > +/// use kernel::{
> > +/// define_wd_class,
> > +/// define_ww_class, //
> > +/// };
> > +///
> > +/// define_ww_class!(SOME_WW_CLASS);
> > +/// define_wd_class!(SOME_WD_CLASS);
> > +///
> > +/// # Ok::<(), Error>(())
> > +/// ```
> > +#[pin_data]
> > +#[repr(transparent)]
> > +pub struct Class {
> > + #[pin]
> > + pub(super) inner: Opaque<bindings::ww_class>,
> > +}
> > +
> > +impl Class {
> > + /// Creates an unpinned [`Class`].
> > + ///
> > + /// You should prefer using [`define_ww_class!`] and [`define_wd_class!`]
> > + /// macros. This function is `pub` only so that those macros can use it.
> > + /// The alternative would be to expose the private fields of [`Class`]
> > + /// which is less desirable.
>
> Replace the paragraph with `#[doc(hidden)]`?
Good idea.
>
> > + ///
> > + /// # Safety
> > + ///
> > + /// Caller must guarantee that the returned value must be pinned before
> > + /// its first use.
> > + pub const unsafe fn new_unpinned(
> > + acquire_name: &'static CStr,
> > + mutex_name: &'static CStr,
> > + is_wait_die: bool,
> > + ) -> Self {
> > + Class {
> > + inner: Opaque::new(bindings::ww_class {
> > + stamp: bindings::atomic_long_t { counter: 0 },
> > + acquire_name: crate::str::as_char_ptr_in_const_context(acquire_name),
> > + mutex_name: crate::str::as_char_ptr_in_const_context(mutex_name),
> > + 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.
> > + //
> > + // For reference, 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.
> > + //
> > + // For reference, see __WW_CLASS_INITIALIZER() in
> > + // "include/linux/ww_mutex.h".
> > + mutex_key: unsafe { core::mem::zeroed() },
>
> Does `..pin_init::zeroed()` work in this case?
It does.
Thanks,
Onur
>
> Best,
> Gary
>
> > + }),
> > + }
> > + }
> > +
> > + /// Creates a [`Class`] from a raw pointer.
> > + ///
> > + /// This function is intended for interoperability with C code.
> > + ///
> > + /// # Safety
> > + ///
> > + /// The caller must ensure that `ptr` points to the `inner` field of
> > + /// [`Class`] and that it remains valid for the lifetime `'a`.
> > + #[inline]
> > + pub const unsafe fn from_raw<'a>(ptr: *mut bindings::ww_class) -> &'a Self {
> > + // SAFETY: By the safety contract, `ptr` is valid to construct `Class`.
> > + unsafe { &*ptr.cast() }
> > + }
> > +}
> > +
> > +// SAFETY: [`Class`] is immutable except for its atomic stamp. It's fine to share it across threads.
> > +unsafe impl Sync for Class {}
> > +// SAFETY: Doesn't hold anything thread-specific. It's safe to send to other threads.
> > +unsafe impl Send for Class {}
>
>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v12 4/6] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard
2026-10-12 10:26 ` [PATCH v12 4/6] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard Onur Özkan
@ 2026-10-12 11:40 ` Gary Guo
0 siblings, 0 replies; 13+ messages in thread
From: Gary Guo @ 2026-10-12 11:40 UTC (permalink / raw)
To: Onur Özkan, rust-for-linux
Cc: lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary, a.hindborg,
aliceryhl, tmgross, dakr, peterz, mingo, will, longman,
felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
On Mon Oct 12, 2026 at 11:26 AM BST, Onur Özkan wrote:
> Covers the low-level locking API (lock, try_lock and
> interruptible variants) and integration with kernel bindings.
>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Onur Özkan <work@onurozkan.dev>
> ---
> rust/kernel/sync/lock/ww_mutex.rs | 434 ++++++++++++++++++
> rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs | 149 ++++++
> 2 files changed, 583 insertions(+)
> create mode 100644 rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs
>
> diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_mutex.rs
> index 727c51cc73af8..2423f89a7e762 100644
> --- a/rust/kernel/sync/lock/ww_mutex.rs
> +++ b/rust/kernel/sync/lock/ww_mutex.rs
> @@ -1,7 +1,441 @@
> // SPDX-License-Identifier: GPL-2.0
>
> //! Rust abstractions for the kernel's wound-wait locking primitives.
> +//!
> +//! It is designed to avoid deadlocks when locking multiple [`Mutex`]es
> +//! that belong to the same [`Class`]. Each lock acquisition uses an
> +//! [`AcquireCtx`] to track ordering and ensure forward progress.
> +//!
> +//! See srctree/Documentation/locking/ww-mutex-design.rst for more details.
>
> +use crate::{
> + bindings,
> + container_of,
> + error::to_result,
> + prelude::*,
> + types::{
> + NotThreadSafe,
> + Opaque, //
> + }, //
> +};
> +use core::{
> + cell::UnsafeCell,
> + marker::PhantomData, //
> +};
> +
> +pub use acquire_ctx::AcquireCtx;
> pub use class::Class;
>
> +mod acquire_ctx;
> mod class;
> +
> +/// A wound-wait (ww) mutex that is powered with deadlock avoidance
> +/// when acquiring multiple locks of the same [`Class`].
> +///
> +/// Each mutex belongs to a [`Class`], which the wound-wait algorithm
> +/// uses to figure out the order of acquisition and prevent deadlocks.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::{
> +/// define_ww_class,
> +/// sync::{
> +/// lock::ww_mutex::{
> +/// AcquireCtx,
> +/// Class,
> +/// Mutex, //
> +/// },
> +/// Arc,
> +/// },
> +/// };
> +/// use pin_init::stack_pin_init;
> +///
> +/// define_ww_class!(SOME_WW_CLASS);
> +///
> +/// let mutex = Arc::pin_init(Mutex::new(42, &SOME_WW_CLASS), GFP_KERNEL)?;
> +/// let ctx = KBox::pin_init(AcquireCtx::new(&SOME_WW_CLASS), GFP_KERNEL)?;
> +///
> +/// // SAFETY: The guard is dropped before `ctx`.
> +/// let guard = unsafe { ctx.lock(&mutex) }?;
> +/// assert_eq!(*guard, 42);
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +#[pin_data]
> +#[repr(C)]
> +pub struct Mutex<'a, T: ?Sized> {
> + _p: PhantomData<&'a Class>,
Do you expect any users that have non-static class?
So far all in-kernel user of ww mutex has a static class.
> + #[pin]
> + inner: Opaque<bindings::ww_mutex>,
> + data: UnsafeCell<T>,
> +}
> +
> +impl<'class, T> Mutex<'class, T> {
> + /// Initializes [`Mutex`] with the given `data` and [`Class`].
> + pub fn new(data: T, class: &'class Class) -> impl PinInit<Self> {
> + let class_ptr = class.inner.get();
> + pin_init!(Mutex {
> + inner <- Opaque::ffi_init(|slot: *mut bindings::ww_mutex| {
> + // SAFETY: `class` is valid for the lifetime `'class` captured by `Self`.
> + unsafe { bindings::ww_mutex_init(slot, class_ptr) }
> + }),
> + data: UnsafeCell::new(data),
> + _p: PhantomData
> + })
> + }
> +}
> +
> +impl<'class, T: ?Sized> Mutex<'class, T> {
> + /// Checks if this [`Mutex`] is currently locked.
> + ///
> + /// The returned value is racy as another thread can acquire
> + /// or release the lock immediately after this call returns.
> + #[inline]
> + pub fn is_locked(&self) -> bool {
> + // SAFETY: It's safe to call `ww_mutex_is_locked` on
> + // a valid mutex.
> + unsafe { bindings::ww_mutex_is_locked(self.inner.get()) }
> + }
> +
> + /// Locks this [`Mutex`] without [`AcquireCtx`].
> + pub fn lock(&self) -> Result<MutexGuard<'_, T>> {
> + lock_common(self, None, LockKind::Regular)
> + }
> +
> + /// Similar to [`Self::lock`], but can be interrupted by signals.
> + pub fn lock_interruptible(&self) -> Result<MutexGuard<'_, T>> {
> + lock_common(self, None, LockKind::Interruptible)
> + }
> +
> + /// Tries to lock this [`Mutex`] with no [`AcquireCtx`] and without blocking.
> + ///
> + /// Unlike [`Self::lock`], no deadlock handling is performed.
> + pub fn try_lock(&self) -> Result<MutexGuard<'_, T>> {
`try_lock` failure should return `Option` instead.
> + lock_common(self, None, LockKind::Try)
> + }
> +}
> +
> +impl<'class> Mutex<'class, ()> {
> + /// Creates a [`Mutex`] from a raw pointer.
> + ///
> + /// This function is intended for interoperability with C code.
> + ///
> + /// # Safety
> + ///
> + /// The caller must ensure that:
> + ///
> + /// - `ptr` is a valid pointer to a `ww_mutex`.
> + /// - `ptr` must remain valid for the lifetime `'a`.
> + /// - ww_class associated with this mutex must be valid for
> + /// the lifetime `'class`.
> + pub unsafe fn from_raw<'a>(ptr: *mut bindings::ww_mutex) -> &'a Self {
> + // SAFETY: By the safety contract, the caller guarantees that `ptr`
> + // points to a valid `ww_mutex` which is the `inner` field of `Mutex`,
> + // that it remains valid for the lifetime `'a` and the associated
> + // ww_class outlives `'class`.
> + //
> + // Because [`Mutex`] is `#[repr(C)]`, the `inner` field sits at a
> + // stable offset that `container_of!` can safely rely on.
> + unsafe { &*container_of!(Opaque::cast_from(ptr), Self, inner) }
> + }
> +}
> +
> +// SAFETY: `Mutex` can be sent to another thread if the protected
> +// data `T` can be.
> +unsafe impl<T: ?Sized + Send> Send for Mutex<'_, T> {}
> +
> +// SAFETY: `Mutex` serialises access to the protected data, so it is
> +// `Sync` as long as the data is `Send`.
> +unsafe impl<T: ?Sized + Send> Sync for Mutex<'_, T> {}
> +
> +/// A guard that provides exclusive access to the data protected
> +/// by a [`Mutex`].
> +///
> +/// # Invariants
> +///
> +/// The guard holds an exclusive lock on the associated [`Mutex`]. 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 MutexGuard<'a, T: ?Sized> {
> + mutex: &'a Mutex<'a, T>,
> + _not_send: NotThreadSafe,
> +}
> +
> +impl<'a, T: ?Sized> MutexGuard<'a, T> {
> + /// Creates a new guard for the given [`Mutex`].
> + fn new(mutex: &'a Mutex<'a, T>) -> Self {
> + assert!(mutex.is_locked());
> +
> + // INVARIANT: `lock_common` and `from_raw` ensure that the current task holds the mutex
> + // and transfer sole responsibility for unlocking it to this guard.
> + Self {
> + mutex,
> + _not_send: NotThreadSafe,
> + }
> + }
> +}
> +
> +impl<'a> MutexGuard<'a, ()> {
> + /// Creates a [`MutexGuard`] from a raw pointer.
> + ///
> + /// If the given pointer refers to a mutex that is not locked,
> + /// returns [`EINVAL`].
> + ///
> + /// This function is intended for interoperability with C code.
> + ///
> + /// # Safety
> + ///
> + /// The caller must ensure that:
> + ///
> + /// - `ptr` is a valid pointer to a `ww_mutex`.
> + /// - `ptr` must remain valid for the lifetime `'b`.
> + /// - The `ww_class` associated with the `ww_mutex` must be valid for the lifetime `'b`.
> + /// - The current task holds the mutex.
> + /// - The caller transfers responsibility for unlocking the mutex to the returned guard
> + /// and must not unlock it separately (including through another guard).
> + /// - Any associated acquire context remains valid until the mutex is unlocked,
> + /// even if the returned guard is forgotten. The context must not be finalized or
> + /// reinitialized before then.
> + pub unsafe fn from_raw<'b>(ptr: *mut bindings::ww_mutex) -> Result<MutexGuard<'b, ()>> {
> + // SAFETY: By this function's safety contract, the caller guarantees that `ptr` points to a
> + // valid `ww_mutex` which is the `inner` field of a `Mutex`. The caller also guarantees
> + // that both `ptr` and the associated `ww_class` are valid for the lifetime `'b`.
> + let mutex = unsafe { Mutex::from_raw(ptr) };
> +
> + if !mutex.is_locked() {
> + return Err(EINVAL);
> + }
Why is this checked despite being an unsafe function. Do you expect users to
call this without knowing the lock is already locked? I think that's unlikely.
Perhaps make this a debug_assert instead?
> +
> + Ok(MutexGuard::new(mutex))
> + }
> +}
> +
> +impl<T: ?Sized> core::ops::Deref for MutexGuard<'_, T> {
> + type Target = T;
> +
`#[inline]` for this and others.
> + fn deref(&self) -> &Self::Target {
> + // SAFETY: self.mutex is locked, so we have exclusive access.
> + unsafe { &*self.mutex.data.get() }
> + }
> +}
> +
> +impl<T: ?Sized + Unpin> core::ops::DerefMut for MutexGuard<'_, T> {
> + fn deref_mut(&mut self) -> &mut Self::Target {
> + // SAFETY: self.mutex is locked, so we have exclusive access.
> + unsafe { &mut *self.mutex.data.get() }
> + }
> +}
> +
> +impl<T: ?Sized> Drop for MutexGuard<'_, T> {
> + #[inline]
> + fn drop(&mut self) {
> + // SAFETY: self.mutex is locked and are about to release it.
> + unsafe { bindings::ww_mutex_unlock(self.mutex.inner.get()) };
> + }
> +}
> +
> +// SAFETY: `MutexGuard` can be shared between threads if the data can.
> +unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
> +
> +/// Locking kinds used by [`lock_common`] to unify the internal
> +/// locking logic.
> +///
> +/// It's best not to expose this type (and [`lock_common`]) to the
> +/// kernel, as it allows internal API changes without worrying
> +/// about breaking external compatibility.
> +#[derive(Copy, Clone, Debug)]
> +enum LockKind {
> + /// Blocks until lock is acquired.
> + Regular,
> + /// Blocks but can be interrupted by signals.
> + Interruptible,
> + /// Does not block, returns immediately if busy.
> + Try,
> +}
> +
> +/// Internal helper that unifies the different locking kinds.
> +///
> +/// Returns [`EINVAL`] if the [`Mutex`] has a different [`Class`].
> +fn lock_common<'a, T: ?Sized>(
> + mutex: &'a Mutex<'a, T>,
> + ctx: Option<&AcquireCtx<'_>>,
> + kind: LockKind,
> +) -> Result<MutexGuard<'a, T>> {
I think we should have 3 helpers, to avoid having to branch on `kind` which is
compile-tiome known information.
The only part between the 3 kinds that is shared is the ctx_ptr computation. I
think it should be extracted to its own helper, and be used for 3 types of
locking.
> + let mutex_ptr = mutex.inner.get();
> +
> + let ctx_ptr = match ctx {
> + Some(acquire_ctx) => {
> + let ctx_ptr = acquire_ctx.inner.get();
> +
> + // SAFETY: `ctx_ptr` is a valid pointer for the entire
> + // lifetime of `ctx`.
> + let ctx_class = unsafe { (*ctx_ptr).ww_class };
> +
> + // SAFETY: `mutex_ptr` is a valid pointer for the entire
> + // lifetime of `mutex`.
> + let mutex_class = unsafe { (*mutex_ptr).ww_class };
> +
> + // `ctx` and `mutex` must use the same class.
> + if ctx_class != mutex_class {
> + return Err(EINVAL);
> + }
This is supposed to be a error condition?
> +
> + ctx_ptr
> + }
> + None => core::ptr::null_mut(),
> + };
> +
> + match kind {
> + LockKind::Regular => {
> + // SAFETY: `Mutex` is always pinned. If `AcquireCtx` 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(mutex_ptr, ctx_ptr) };
> +
> + to_result(ret)?;
> + }
> + LockKind::Interruptible => {
> + // SAFETY: `Mutex` is always pinned. If `AcquireCtx` 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(mutex_ptr, ctx_ptr) };
> +
> + to_result(ret)?;
> + }
> + LockKind::Try => {
> + // SAFETY: `Mutex` is always pinned. If `AcquireCtx` 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(mutex_ptr, ctx_ptr) };
> +
> + if ret == 0 {
> + return Err(EBUSY);
> + } else {
> + to_result(ret)?;
> + }
> + }
> + };
> +
> + Ok(MutexGuard::new(mutex))
> +}
>
> [snip]
>
> diff --git a/rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs b/rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs
> new file mode 100644
> index 0000000000000..18f4654e7df26
> --- /dev/null
> +++ b/rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs
> @@ -0,0 +1,149 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Provides [`AcquireCtx`] for managing multiple wound/wait
> +//! mutexes from the same [`Class`].
> +
> +use super::{
> + lock_common,
> + Class,
> + LockKind,
> + Mutex,
> + MutexGuard, //
> +};
> +use crate::{
> + bindings,
> + prelude::*,
> + types::Opaque, //
> +};
> +use core::marker::PhantomData;
> +
> +/// Groups multiple [`Mutex`]es for deadlock avoidance when acquired
> +/// with the same [`Class`].
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::{
> +/// define_ww_class,
> +/// sync::{
> +/// lock::ww_mutex::{
> +/// AcquireCtx,
> +/// Class,
> +/// Mutex, //
> +/// },
> +/// Arc,
> +/// },
> +/// };
> +/// use pin_init::stack_pin_init;
> +///
> +/// define_ww_class!(SOME_WW_CLASS);
> +///
> +/// // Create mutexes.
> +/// let mutex1 = Arc::pin_init(Mutex::new(1, &SOME_WW_CLASS), GFP_KERNEL)?;
> +/// let mutex2 = Arc::pin_init(Mutex::new(2, &SOME_WW_CLASS), GFP_KERNEL)?;
> +///
> +/// // Create acquire context for deadlock avoidance.
> +/// let ctx = KBox::pin_init(AcquireCtx::new(&SOME_WW_CLASS), GFP_KERNEL)?;
> +///
> +/// // SAFETY: The guard is dropped before `ctx`.
> +/// let guard1 = unsafe { ctx.lock(&mutex1) }?;
> +/// // SAFETY: The guard is dropped before `ctx`.
> +/// let guard2 = unsafe { ctx.lock(&mutex2) }?;
> +///
> +/// // Mark acquisition phase as complete.
> +/// ctx.done();
> +///
> +/// # Ok::<(), Error>(())
> +/// ```
> +#[pin_data(PinnedDrop)]
> +#[repr(transparent)]
> +pub struct AcquireCtx<'a> {
> + #[pin]
> + pub(super) inner: Opaque<bindings::ww_acquire_ctx>,
> + _p: PhantomData<&'a Class>,
> +}
> +
> +impl<'class> AcquireCtx<'class> {
> + /// Initializes a new [`AcquireCtx`] with the given [`Class`].
> + pub fn new(class: &'class Class) -> impl PinInit<Self> {
> + let class_ptr = class.inner.get();
> + pin_init!(AcquireCtx {
> + inner <- Opaque::ffi_init(|slot: *mut bindings::ww_acquire_ctx| {
> + // SAFETY: `class` is valid for the lifetime `'class` captured
> + // by `AcquireCtx`.
> + unsafe { bindings::ww_acquire_init(slot, class_ptr) }
> + }),
> + _p: PhantomData
> + })
> + }
I wonder if a better API would be
class.new_context()
vs.
AcquireCtx::new(&class)
?
> +
> + /// Creates a [`AcquireCtx`] from a raw pointer.
> + ///
> + /// This function is intended for interoperability with C code.
> + ///
> + /// # Safety
> + ///
> + /// The caller must ensure that `ptr` is a valid pointer to the `inner` field
> + /// of [`AcquireCtx`] and that it remains valid for the lifetime `'a`.
> + pub unsafe fn from_raw<'a>(ptr: *mut bindings::ww_acquire_ctx) -> &'a Self {
> + // SAFETY: By the safety contract, `ptr` is valid to construct `AcquireCtx`.
> + unsafe { &*ptr.cast() }
> + }
> +
> + /// Marks the end of the acquire phase.
> + ///
> + /// Calling this function is optional. It is just useful to document
> + /// the code and clearly designated the acquire phase from actually
> + /// using the locked data structures.
> + ///
> + /// After calling this function, no more mutexes can be acquired with
> + /// this context.
> + #[inline]
> + pub fn done(&self) {
> + // SAFETY: `self.inner` contains a valid, initialized acquire context.
> + unsafe { bindings::ww_acquire_done(self.inner.get()) };
> + }
> +
> + /// Locks the given [`Mutex`] on this [`AcquireCtx`].
> + ///
> + /// # Safety
> + ///
> + /// On success, the caller must keep this context valid until the mutex is unlocked,
> + /// even if the returned guard is forgotten. The context must not be finalized or
> + /// reinitialized while the mutex remains locked.
> + pub unsafe fn lock<'a, T>(&'a self, mutex: &'a Mutex<'a, T>) -> Result<MutexGuard<'a, T>> {
> + lock_common(mutex, Some(self), LockKind::Regular)
> + }
> +
> + /// Similar to [`Self::lock`], but can be interrupted by signals.
> + ///
> + /// # Safety
> + ///
> + /// The same requirements as [`Self::lock`] apply.
> + pub unsafe fn lock_interruptible<'a, T>(
> + &'a self,
> + mutex: &'a Mutex<'a, T>,
> + ) -> Result<MutexGuard<'a, T>> {
> + lock_common(mutex, Some(self), LockKind::Interruptible)
> + }
> +
> + /// Tries to lock the [`Mutex`] on this [`AcquireCtx`] without blocking.
> + ///
> + /// Unlike [`Self::lock`], no deadlock handling is performed.
> + ///
> + /// # Safety
> + ///
> + /// The same requirements as [`Self::lock`] apply.
> + pub unsafe fn try_lock<'a, T>(&'a self, mutex: &'a Mutex<'a, T>) -> Result<MutexGuard<'a, T>> {
> + lock_common(mutex, Some(self), LockKind::Try)
> + }
> +}
> +
> +#[pinned_drop]
> +impl PinnedDrop for AcquireCtx<'_> {
> + fn drop(self: Pin<&mut Self>) {
I wonder if we can just do
let acquired = unsafe { (*self.inner.get()).acquired };
assert!(!acquired);
to get rid of the `unsafe`. Somewhat similar to what we do with srcu. Of course,
ultimately we want to use `!Leak` for this and can remove the check.
Thoughts?
Best,
Gary
> + // SAFETY: The locking methods require callers to release all acquired locks
> + // before this context is dropped.
> + unsafe { bindings::ww_acquire_fini(self.inner.get()) };
> + }
> +}
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v12 1/6] rust: add C wrappers for ww_mutex inline functions
2026-10-12 10:26 ` [PATCH v12 1/6] rust: add C wrappers for ww_mutex inline functions Onur Özkan
@ 2026-10-12 11:41 ` Gary Guo
0 siblings, 0 replies; 13+ messages in thread
From: Gary Guo @ 2026-10-12 11:41 UTC (permalink / raw)
To: Onur Özkan, rust-for-linux
Cc: lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary, a.hindborg,
aliceryhl, tmgross, dakr, peterz, mingo, will, longman,
felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
On Mon Oct 12, 2026 at 11:26 AM BST, Onur Özkan 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.
>
> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Reviewed-by: Lyude Paul <lyude@redhat.com>
> Signed-off-by: Onur Özkan <work@onurozkan.dev>
Reviewed-by: Gary Guo <gary@garyguo.net>
> ---
> MAINTAINERS | 1 +
> rust/helpers/helpers.c | 1 +
> rust/helpers/ww_mutex.c | 33 +++++++++++++++++++++++++++++++++
> 3 files changed, 35 insertions(+)
> create mode 100644 rust/helpers/ww_mutex.c
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v12 2/6] ww_mutex: add ww_class field unconditionally
2026-10-12 10:26 ` [PATCH v12 2/6] ww_mutex: add ww_class field unconditionally Onur Özkan
@ 2026-10-12 11:42 ` Gary Guo
0 siblings, 0 replies; 13+ messages in thread
From: Gary Guo @ 2026-10-12 11:42 UTC (permalink / raw)
To: Onur Özkan, rust-for-linux
Cc: lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary, a.hindborg,
aliceryhl, tmgross, dakr, peterz, mingo, will, longman,
felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
On Mon Oct 12, 2026 at 11:26 AM BST, Onur Özkan wrote:
> Removes DEBUG_WW_MUTEXES guard around the ww_class field
> in struct ww_mutex and struct ww_acquire_ctx to always
> initialize it.
>
> Keeping the class pointer available unconditionally allows
> Rust abstractions to keep from_raw() simple on Mutex and
> AcquireCtx and validate Mutex and AcquireCtx class pairing
> in much simpler way.
Is a mismatch a memory safety issue or just a bug?
Best,
Gary
>
> Link: https://lore.kernel.org/all/ECC0425A-8B18-4626-8EA8-2F843C45E0A1@collabora.com/
> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
> Reviewed-by: Alice Ryhl <aliceryhl@google.com>
> Signed-off-by: Onur Özkan <work@onurozkan.dev>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v12 5/6] rust: ww_mutex: implement LockSet
2026-10-12 10:26 ` [PATCH v12 5/6] rust: ww_mutex: implement LockSet Onur Özkan
@ 2026-10-12 12:02 ` Gary Guo
0 siblings, 0 replies; 13+ messages in thread
From: Gary Guo @ 2026-10-12 12:02 UTC (permalink / raw)
To: Onur Özkan, rust-for-linux
Cc: lossin, lyude, ojeda, alex.gaynor, boqun.feng, gary, a.hindborg,
aliceryhl, tmgross, dakr, peterz, mingo, will, longman,
felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
On Mon Oct 12, 2026 at 11:26 AM BST, Onur Özkan wrote:
> LockSet is a high-level and safe API built on top of
> ww_mutex which provides a safe and easy to use API
> while keeping the ww_mutex semantics.
>
> When EDEADLK is hit it drops all held locks and waits for the
> contended mutex. It then retries the given (by the user)
> locking algorithm with that mutex held and the same acquire
> context until it succeeds.
Do we really need this, or this can be achieved by simple retry loops?
fn retry_if_deadlock<R>(f: impl FnOnce() -> Result<R>) -> Result<R> {
loop {
match f() {
Err(EDEADLK) => (),
e => return e,
}
}
}
stack_pin_init!(let acq_ctx = AcquireCtx::new(&class));
retry_if_deadlock(|| {
let mut gaurd1 = acq_ctx.lock(&mutex1)?;
let mut guard2 = acq_ctx.lock(&mutex2)?;
acq_ctx.done();
*guard1 += 1;
*guard2 += 1;
});
I think the only difference is that the `LockSet` acquires the contending mutex
first. However, I imagine we can solve this by having a wrapped `AcquireCtx`
that provides that.
Best,
Gary
>
> Signed-off-by: Onur Özkan <work@onurozkan.dev>
> ---
> rust/kernel/sync/lock/ww_mutex.rs | 6 +
> rust/kernel/sync/lock/ww_mutex/lock_set.rs | 449 +++++++++++++++++++++
> 2 files changed, 455 insertions(+)
> create mode 100644 rust/kernel/sync/lock/ww_mutex/lock_set.rs
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-10-12 12:02 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-12 10:26 [PATCH v12 0/6] rust: add ww_mutex support Onur Özkan
2026-10-12 10:26 ` [PATCH v12 1/6] rust: add C wrappers for ww_mutex inline functions Onur Özkan
2026-10-12 11:41 ` Gary Guo
2026-10-12 10:26 ` [PATCH v12 2/6] ww_mutex: add ww_class field unconditionally Onur Özkan
2026-10-12 11:42 ` Gary Guo
2026-10-12 10:26 ` [PATCH v12 3/6] rust: implement Class for ww_class support Onur Özkan
2026-10-12 11:03 ` Gary Guo
2026-10-12 11:26 ` Onur Özkan
2026-10-12 10:26 ` [PATCH v12 4/6] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard Onur Özkan
2026-10-12 11:40 ` Gary Guo
2026-10-12 10:26 ` [PATCH v12 5/6] rust: ww_mutex: implement LockSet Onur Özkan
2026-10-12 12:02 ` Gary Guo
2026-10-12 10:26 ` [PATCH v12 6/6] MAINTAINERS: add Onur Özkan as WW MUTEX maintainer Onur Özkan
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®