* [PATCH v10 0/7] rust: add ww_mutex support
@ 2026-01-03 7:35 Onur Özkan
2026-01-03 7:35 ` [PATCH v10 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
` (7 more replies)
0 siblings, 8 replies; 12+ messages in thread
From: Onur Özkan @ 2026-01-03 7:35 UTC (permalink / raw)
To: 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, Onur Özkan
Changes in v10:
- Added missing reviewed-by tags.
- Fixed git conflict resolution leftovers [1].
[1]: https://lore.kernel.org/all/202601030632.SukxzGNs-lkp@intel.com/
Changes in v9:
- Added `__rust_helper` prefixes to `ww_mutex.c`.
- Reworked on Class to remove pinned initialization functions
and added documentation suggesting its use as a global variable
as this is its only usage on the C side.
- Added myself as a maintainer of WW Mutex.
- Various minor updates (commit description updates, minor code
rearrangements, etc.).
Changes in v8:
- Various minor updates (e.g., doc updates, removing unnecessary
lifetime annotations, etc.).
- Changed C implementation to always provide ww_class field in
ww_mutex and ww_acquire_ctx types so we can access it from Rust
easily.
- Locking functions are now safe to call.
- Added class validation logic to ww_mutex::lock_common.
Changes in v7:
- Split Class and AcquireCtx into separate modules.
- Removed "Ww" prefixes from type names.
- Renamed exec.rs -> lock_set.rs and ExecContext -> LockSet.
- Removed transmute logic from LockSet (formerly ExecContext).
- Improved various doc-comments.
- Marked certain AcquireCtx functions as unsafe.
- Added from_raw constructors for Mutex, MutexGuard, AcquireCtx
and Class.
- LockSet::cleanup_on_deadlock no longer triggers reallocations when
reinitializing AcquireCtx.
- Incorporated various minor improvements suggested on the v6 series.
Changes in v6:
- Added `unpinned_new` constructor for `WwClass` and updated
global macros.
- Changed all tests (and docs) to use Arc/KBox instead of
`stack_pin_init` for `WwMutex` and `WwAcquireCtx`.
- Added `LockKind` and `lock_common` helper to unify locking logic.
- Added context-based and context-free locking functions for `WwMutex`.
- Added `ww_mutex/exec` module, a high-level API with auto `EDEADLK`
handling mechanism.
Changes in v5:
- Addressed documentation review notes.
- Removed `unwrap()`s in examples and KUnit tests.
Onur Özkan (7):
rust: add C wrappers for ww_mutex inline functions
ww_mutex: add ww_class field unconditionally
rust: error: add EDEADLK
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 | 45 ++
rust/kernel/error.rs | 1 +
rust/kernel/sync/lock.rs | 1 +
rust/kernel/sync/lock/ww_mutex.rs | 444 ++++++++++++++++++
rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs | 172 +++++++
rust/kernel/sync/lock/ww_mutex/class.rs | 139 ++++++
rust/kernel/sync/lock/ww_mutex/lock_set.rs | 370 +++++++++++++++
10 files changed, 1177 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
--
2.51.2
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v10 1/7] rust: add C wrappers for ww_mutex inline functions
2026-01-03 7:35 [PATCH v10 0/7] rust: add ww_mutex support Onur Özkan
@ 2026-01-03 7:35 ` Onur Özkan
2026-02-03 13:45 ` Daniel Almeida
2026-01-03 7:35 ` [PATCH v10 2/7] ww_mutex: add ww_class field unconditionally Onur Özkan
` (6 subsequent siblings)
7 siblings, 1 reply; 12+ messages in thread
From: Onur Özkan @ 2026-01-03 7:35 UTC (permalink / raw)
To: 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, Onur Özkan
Some of the kernel's `ww_mutex` functions are implemented as
`static inline`, so they are inaccessible from Rust as bindgen
can't generate code on them. This patch provides C function wrappers
around these inline implementations, so bindgen can see them and
generate the corresponding Rust code.
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
MAINTAINERS | 1 +
rust/helpers/helpers.c | 1 +
rust/helpers/ww_mutex.c | 45 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 47 insertions(+)
create mode 100644 rust/helpers/ww_mutex.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 765ad2daa218..d245b7b38b21 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14728,6 +14728,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 79c72762ad9c..a95fb3abaab3 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -61,4 +61,5 @@
#include "vmalloc.c"
#include "wait.c"
#include "workqueue.c"
+#include "ww_mutex.c"
#include "xarray.c"
diff --git a/rust/helpers/ww_mutex.c b/rust/helpers/ww_mutex.c
new file mode 100644
index 000000000000..2ea19aafbd4e
--- /dev/null
+++ b/rust/helpers/ww_mutex.c
@@ -0,0 +1,45 @@
+// 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
+void rust_helper_ww_mutex_lock_slow(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+{
+ ww_mutex_lock_slow(lock, ctx);
+}
+
+__rust_helper
+int rust_helper_ww_mutex_lock_slow_interruptible(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
+{
+ return ww_mutex_lock_slow_interruptible(lock, ctx);
+}
+
+__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 related [flat|nested] 12+ messages in thread
* [PATCH v10 2/7] ww_mutex: add ww_class field unconditionally
2026-01-03 7:35 [PATCH v10 0/7] rust: add ww_mutex support Onur Özkan
2026-01-03 7:35 ` [PATCH v10 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
@ 2026-01-03 7:35 ` Onur Özkan
2026-01-03 7:35 ` [PATCH v10 3/7] rust: error: add EDEADLK Onur Özkan
` (5 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Onur Özkan @ 2026-01-03 7:35 UTC (permalink / raw)
To: 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, Onur Özkan
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, simplify AcquireCtx reinitialization logic and
validate Mutex and AcquireCtx class pairing in much simpler way.
Link: https://lore.kernel.org/all/ECC0425A-8B18-4626-8EA8-2F843C45E0A1@collabora.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 45ff6f7a872b..c9835035b6d6 100644
--- a/include/linux/ww_mutex.h
+++ b/include/linux/ww_mutex.h
@@ -47,9 +47,7 @@ struct ww_class {
struct ww_mutex {
struct WW_MUTEX_BASE base;
struct ww_acquire_ctx *ctx;
-#ifdef DEBUG_WW_MUTEXES
struct ww_class *ww_class;
-#endif
};
struct ww_acquire_ctx {
@@ -58,9 +56,9 @@ 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
@@ -110,9 +108,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
}
/**
@@ -147,8 +143,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 related [flat|nested] 12+ messages in thread
* [PATCH v10 3/7] rust: error: add EDEADLK
2026-01-03 7:35 [PATCH v10 0/7] rust: add ww_mutex support Onur Özkan
2026-01-03 7:35 ` [PATCH v10 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
2026-01-03 7:35 ` [PATCH v10 2/7] ww_mutex: add ww_class field unconditionally Onur Özkan
@ 2026-01-03 7:35 ` Onur Özkan
2026-01-03 7:35 ` [PATCH v10 4/7] rust: implement Class for ww_class support Onur Özkan
` (4 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Onur Özkan @ 2026-01-03 7:35 UTC (permalink / raw)
To: 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, Onur Özkan
This is needed for the ww_mutex implementation so
we can handle EDEADLK on lock attempts.
Signed-off-by: Onur Özkan <work@onurozkan.dev>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Lyude Paul <lyude@redhat.com>
---
rust/kernel/error.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 258b12afdcba..c2b95db23abc 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -66,6 +66,7 @@ macro_rules! declare_err {
declare_err!(EPIPE, "Broken pipe.");
declare_err!(EDOM, "Math argument out of domain of func.");
declare_err!(ERANGE, "Math result not representable.");
+ declare_err!(EDEADLK, "Resource deadlock avoided.");
declare_err!(EOVERFLOW, "Value too large for defined data type.");
declare_err!(ETIMEDOUT, "Connection timed out.");
declare_err!(ERESTARTSYS, "Restart the system call.");
--
2.51.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v10 4/7] rust: implement Class for ww_class support
2026-01-03 7:35 [PATCH v10 0/7] rust: add ww_mutex support Onur Özkan
` (2 preceding siblings ...)
2026-01-03 7:35 ` [PATCH v10 3/7] rust: error: add EDEADLK Onur Özkan
@ 2026-01-03 7:35 ` Onur Özkan
2026-01-03 7:35 ` [PATCH v10 5/7] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard Onur Özkan
` (3 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Onur Özkan @ 2026-01-03 7:35 UTC (permalink / raw)
To: 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, Onur Özkan
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_class macro for safely declaring
static instances.
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
rust/kernel/sync/lock.rs | 1 +
rust/kernel/sync/lock/ww_mutex.rs | 7 ++
rust/kernel/sync/lock/ww_mutex/class.rs | 139 ++++++++++++++++++++++++
3 files changed, 147 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 46a57d1fc309..ef7f4a023177 100644
--- a/rust/kernel/sync/lock.rs
+++ b/rust/kernel/sync/lock.rs
@@ -15,6 +15,7 @@
pub mod mutex;
pub mod spinlock;
+pub mod ww_mutex;
pub(super) mod global;
pub use global::{GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedBy};
diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_mutex.rs
new file mode 100644
index 000000000000..727c51cc73af
--- /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 000000000000..d57478f33659
--- /dev/null
+++ b/rust/kernel/sync/lock/ww_mutex/class.rs
@@ -0,0 +1,139 @@
+// 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;
+use crate::prelude::*;
+use crate::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::stringify!($name)),
+ 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::stringify!($name)),
+ 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_ww_class, define_wd_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(name: &'static CStr, is_wait_die: bool) -> Self {
+ Class {
+ inner: Opaque::new(bindings::ww_class {
+ stamp: bindings::atomic_long_t { counter: 0 },
+ acquire_name: name.as_ptr().cast(),
+ mutex_name: name.as_ptr().cast(),
+ 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`.
+ 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.
+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 related [flat|nested] 12+ messages in thread
* [PATCH v10 5/7] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard
2026-01-03 7:35 [PATCH v10 0/7] rust: add ww_mutex support Onur Özkan
` (3 preceding siblings ...)
2026-01-03 7:35 ` [PATCH v10 4/7] rust: implement Class for ww_class support Onur Özkan
@ 2026-01-03 7:35 ` Onur Özkan
2026-01-03 7:35 ` [PATCH v10 6/7] rust: ww_mutex: implement LockSet Onur Özkan
` (2 subsequent siblings)
7 siblings, 0 replies; 12+ messages in thread
From: Onur Özkan @ 2026-01-03 7:35 UTC (permalink / raw)
To: 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, Onur Özkan
Covers the entire low-level locking API (lock, try_lock,
slow path, interruptible variants) and integration with
kernel bindings.
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
rust/kernel/sync/lock/ww_mutex.rs | 431 ++++++++++++++++++
rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs | 172 +++++++
2 files changed, 603 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 727c51cc73af..e36a0204a00e 100644
--- a/rust/kernel/sync/lock/ww_mutex.rs
+++ b/rust/kernel/sync/lock/ww_mutex.rs
@@ -1,7 +1,438 @@
// 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::error::to_result;
+use crate::prelude::*;
+use crate::types::{NotThreadSafe, Opaque};
+use crate::{bindings, container_of};
+
+use core::cell::UnsafeCell;
+use core::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;
+/// use kernel::sync::Arc;
+/// use kernel::sync::lock::ww_mutex::{AcquireCtx, Class, Mutex};
+/// 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)?;
+///
+/// let guard = 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.
+ 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)
+ }
+
+ /// Locks this [`Mutex`] without [`AcquireCtx`] using the slow path.
+ ///
+ /// This function should be used when [`Self::lock`] fails (typically due
+ /// to a potential deadlock).
+ pub fn lock_slow(&self) -> Result<MutexGuard<'_, T>> {
+ lock_common(self, None, LockKind::Slow)
+ }
+
+ /// Similar to [`Self::lock_slow`], but can be interrupted by signals.
+ pub fn lock_slow_interruptible(&self) -> Result<MutexGuard<'_, T>> {
+ lock_common(self, None, LockKind::SlowInterruptible)
+ }
+
+ /// 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` can be shared across threads if the protected
+// data `T` can be.
+unsafe impl<T: ?Sized + Send + Sync> 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());
+
+ 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`.
+ 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> {
+ 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,
+ /// Used in slow path after deadlock detection.
+ Slow,
+ /// Slow path but interruptible.
+ SlowInterruptible,
+ /// Does not block, returns immediately if busy.
+ Try,
+}
+
+/// Internal helper that unifies the different locking kinds.
+///
+/// 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::Slow => {
+ // 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.
+ unsafe { bindings::ww_mutex_lock_slow(mutex_ptr, ctx_ptr) };
+ }
+ LockKind::SlowInterruptible => {
+ // 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_slow_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 crate::prelude::*;
+ use crate::sync::Arc;
+ use crate::{define_wd_class, define_ww_class};
+
+ use super::*;
+
+ 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)?;
+
+ let guard = ctx.lock(&mutex)?;
+ assert_eq!(*guard, 42);
+
+ // Drop the lock.
+ drop(guard);
+
+ let mut guard = 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.
+ let guard = ctx.try_lock(&mutex)?;
+ assert_eq!(*guard, 123);
+
+ // Now it should fail immediately as it's already locked.
+ assert!(ctx.try_lock(&mutex).is_err());
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_ww_mutex_is_locked() -> Result {
+ 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());
+
+ let guard = 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.
+ let guard1 = ctx.lock(&mutex1)?;
+ let guard2 = ctx.lock(&mutex2)?;
+
+ assert_eq!(*guard1, 1);
+ assert_eq!(*guard2, 2);
+
+ // SAFETY: It's called exactly once here and nowhere else.
+ unsafe { ctx.done() };
+
+ // We shouldn't be able to lock once it's `done`.
+ assert!(ctx.lock(&mutex1).is_err());
+ assert!(ctx.lock(&mutex2).is_err());
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_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_with_unlocked_mutex() -> Result {
+ let mutex = Arc::pin_init(Mutex::new((), &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+
+ assert!(!mutex.is_locked());
+
+ // SAFETY: `mutex` remains valid for the duration of this test.
+ match unsafe { MutexGuard::from_raw(mutex.inner.get()) } {
+ // Should fail with `EINVAL` because the mutex is not locked.
+ Err(e) => assert_eq!(e, EINVAL),
+ _ => unreachable!(),
+ };
+
+ 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 000000000000..4b773c76bd2c
--- /dev/null
+++ b/rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Provides [`AcquireCtx`] for managing multiple wound/wait
+//! mutexes from the same [`Class`].
+
+use crate::bindings;
+use crate::prelude::*;
+use crate::types::Opaque;
+
+use core::marker::PhantomData;
+
+use super::{lock_common, Class, LockKind, Mutex, MutexGuard};
+
+/// Groups multiple [`Mutex`]es for deadlock avoidance when acquired
+/// with the same [`Class`].
+///
+/// # Examples
+///
+/// ```
+/// use kernel::sync::lock::ww_mutex::{Class, AcquireCtx, Mutex};
+/// use kernel::define_ww_class;
+/// use kernel::sync::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)?;
+///
+/// let guard1 = ctx.lock(&mutex1)?;
+/// let guard2 = ctx.lock(&mutex2)?;
+///
+/// // Mark acquisition phase as complete.
+/// // SAFETY: It's called exactly once here and nowhere else.
+/// unsafe { 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.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that this function is called only once
+ /// and after calling it, no further mutexes are acquired using
+ /// this context.
+ pub unsafe fn done(&self) {
+ // SAFETY: By the safety contract, the caller guarantees that this
+ // function is called only once.
+ unsafe { bindings::ww_acquire_done(self.inner.get()) };
+ }
+
+ /// Re-initializes the [`AcquireCtx`].
+ ///
+ /// Must be called after releasing all locks when [`EDEADLK`] occurs.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure no locks are held in this [`AcquireCtx`].
+ pub unsafe fn reinit(self: Pin<&mut Self>) {
+ let ctx = self.inner.get();
+
+ // SAFETY: `ww_class` is always a valid pointer in properly initialized
+ // `AcquireCtx`.
+ let class_ptr = unsafe { (*ctx).ww_class };
+
+ // SAFETY:
+ // - Lifetime of any guard (which hold an immutable borrow of `self`) cannot overlap
+ // with the execution of this function. This enforces that all locks acquired via
+ // this context have been released.
+ //
+ // - `ctx` is guaranteed to be initialized because `ww_acquire_fini`
+ // can only be called from the `Drop` implementation.
+ //
+ // - `ww_acquire_fini` is safe to call on an initialized context.
+ unsafe { bindings::ww_acquire_fini(ctx) };
+
+ // SAFETY: `ww_acquire_init` is safe to call with valid pointers
+ // to initialize an uninitialized context.
+ unsafe { bindings::ww_acquire_init(ctx, class_ptr) };
+ }
+
+ /// Locks the given [`Mutex`] on this [`AcquireCtx`].
+ pub 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.
+ pub fn lock_interruptible<'a, T>(
+ &'a self,
+ mutex: &'a Mutex<'a, T>,
+ ) -> Result<MutexGuard<'a, T>> {
+ lock_common(mutex, Some(self), LockKind::Interruptible)
+ }
+
+ /// Locks the given [`Mutex`] on this [`AcquireCtx`] using the slow path.
+ ///
+ /// This function should be used when [`Self::lock`] fails (typically due
+ /// to a potential deadlock).
+ pub fn lock_slow<'a, T>(&'a self, mutex: &'a Mutex<'a, T>) -> Result<MutexGuard<'a, T>> {
+ lock_common(mutex, Some(self), LockKind::Slow)
+ }
+
+ /// Similar to [`Self::lock_slow`], but can be interrupted by signals.
+ pub fn lock_slow_interruptible<'a, T>(
+ &'a self,
+ mutex: &'a Mutex<'a, T>,
+ ) -> Result<MutexGuard<'a, T>> {
+ lock_common(mutex, Some(self), LockKind::SlowInterruptible)
+ }
+
+ /// Tries to lock the [`Mutex`] on this [`AcquireCtx`] without blocking.
+ ///
+ /// Unlike [`Self::lock`], no deadlock handling is performed.
+ pub 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: Given the lifetime bounds we know no locks are held,
+ // so calling `ww_acquire_fini` is safe.
+ unsafe { bindings::ww_acquire_fini(self.inner.get()) };
+ }
+}
--
2.51.2
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v10 6/7] rust: ww_mutex: implement LockSet
2026-01-03 7:35 [PATCH v10 0/7] rust: add ww_mutex support Onur Özkan
` (4 preceding siblings ...)
2026-01-03 7:35 ` [PATCH v10 5/7] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard Onur Özkan
@ 2026-01-03 7:35 ` Onur Özkan
2026-01-03 14:28 ` kernel test robot
2026-01-03 7:35 ` [PATCH v10 7/7] MAINTAINERS: add Onur Özkan as WW MUTEX maintainer Onur Özkan
2026-07-10 12:52 ` [PATCH v10 0/7] rust: add ww_mutex support Alice Ryhl
7 siblings, 1 reply; 12+ messages in thread
From: Onur Özkan @ 2026-01-03 7:35 UTC (permalink / raw)
To: 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, Onur Özkan
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, resets
the acquire context and retries the given (by the user)
locking algorithm 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 | 370 +++++++++++++++++++++
2 files changed, 376 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 e36a0204a00e..0c12dfc05f22 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::error::to_result;
@@ -18,9 +22,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 000000000000..db5ca4fe7cb4
--- /dev/null
+++ b/rust/kernel/sync/lock/ww_mutex/lock_set.rs
@@ -0,0 +1,370 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Provides [`LockSet`] which automatically detects [`EDEADLK`],
+//! releases all locks, resets the state and retries the user
+//! supplied locking algorithm until success.
+
+use super::{AcquireCtx, Class, Mutex};
+use crate::bindings;
+use crate::prelude::*;
+use crate::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>,
+}
+
+/// 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 {
+ 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(),
+ })
+ }
+
+ /// 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(),
+ }
+ }
+
+ /// Attempts to lock the given [`Mutex`] and stores a guard for it.
+ pub fn lock<T>(&mut self, mutex: &'a Mutex<'a, T>) -> Result {
+ let guard = self.acquire_ctx.lock(mutex)?;
+
+ self.taken.push(
+ 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,
+ },
+ GFP_KERNEL,
+ )?;
+
+ // Avoid unlocking here; `release_all_locks` (also run by `Drop`)
+ // performs the unlock for `LockSet`.
+ core::mem::forget(guard);
+
+ Ok(())
+ }
+
+ /// Runs `locking_algorithm` until success with retrying on deadlock.
+ ///
+ /// `locking_algorithm` should attempt to acquire all needed locks.
+ /// If [`EDEADLK`] is detected, this function will roll back, reset
+ /// the context and retry automatically.
+ ///
+ /// Once all locks are acquired successfully, `on_all_locks_taken` is
+ /// invoked for exclusive access to the locked values. Afterwards, all
+ /// locks are released.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use kernel::alloc::KBox;
+ /// use kernel::define_ww_class;
+ /// use kernel::prelude::*;
+ /// use kernel::sync::Arc;
+ /// use kernel::sync::lock::ww_mutex::{LockSet, Mutex};
+ /// 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 => {
+ // Deadlock detected, retry from scratch.
+ 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();
+ }
+
+ /// Resets this [`LockSet`] after a deadlock detection.
+ ///
+ /// Drops all held locks and reinitializes the [`AcquireCtx`].
+ ///
+ /// It is intended to be used for internal implementation only.
+ fn cleanup_on_deadlock(&mut self) {
+ self.release_all_locks();
+
+ // SAFETY: We released all the locks just above.
+ unsafe { self.acquire_ctx.as_mut().reinit() };
+ }
+}
+
+#[kunit_tests(rust_kernel_lock_set)]
+mod tests {
+ use crate::prelude::*;
+ use crate::sync::Arc;
+ use crate::{define_wd_class, define_ww_class};
+
+ use super::*;
+
+ 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.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| {
+ let _ = lock_set.lock(&mutex1)?;
+ let _ = 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 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 res = lock_set.lock_all(
+ // `locking_algorithm` closure
+ |lock_set| {
+ if first_try {
+ first_try = false;
+ // Simulate deadlock on first attempt.
+ return Err(EDEADLK);
+ }
+ lock_set.lock(&mutex)
+ },
+ // `on_all_locks_taken` closure
+ |lock_set| {
+ lock_set.with_locked(&mutex, |v| {
+ *v += 1;
+ *v
+ })
+ },
+ )?;
+
+ assert_eq!(res, 100);
+ 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 related [flat|nested] 12+ messages in thread
* [PATCH v10 7/7] MAINTAINERS: add Onur Özkan as WW MUTEX maintainer
2026-01-03 7:35 [PATCH v10 0/7] rust: add ww_mutex support Onur Özkan
` (5 preceding siblings ...)
2026-01-03 7:35 ` [PATCH v10 6/7] rust: ww_mutex: implement LockSet Onur Özkan
@ 2026-01-03 7:35 ` Onur Özkan
2026-07-10 12:52 ` [PATCH v10 0/7] rust: add ww_mutex support Alice Ryhl
7 siblings, 0 replies; 12+ messages in thread
From: Onur Özkan @ 2026-01-03 7:35 UTC (permalink / raw)
To: 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, Onur Özkan
Add myself as a WW MUTEX maintainer to reflect ongoing
work and future maintenance of WW MUTEX.
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
MAINTAINERS | 1 +
1 file changed, 1 insertion(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index d245b7b38b21..10eeeaf0ccb6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -14711,6 +14711,7 @@ M: Peter Zijlstra <peterz@infradead.org>
M: Ingo Molnar <mingo@redhat.com>
M: Will Deacon <will@kernel.org>
M: Boqun Feng <boqun.feng@gmail.com> (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 related [flat|nested] 12+ messages in thread
* Re: [PATCH v10 6/7] rust: ww_mutex: implement LockSet
2026-01-03 7:35 ` [PATCH v10 6/7] rust: ww_mutex: implement LockSet Onur Özkan
@ 2026-01-03 14:28 ` kernel test robot
0 siblings, 0 replies; 12+ messages in thread
From: kernel test robot @ 2026-01-03 14:28 UTC (permalink / raw)
To: Onur Özkan, rust-for-linux
Cc: oe-kbuild-all, 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, Onur Özkan
Hi Onur,
kernel test robot noticed the following build warnings:
[auto build test WARNING on tip/locking/core]
[also build test WARNING on rust/rust-next linus/master v6.19-rc3 next-20251219]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Onur-zkan/rust-add-C-wrappers-for-ww_mutex-inline-functions/20260103-153912
base: tip/locking/core
patch link: https://lore.kernel.org/r/20260103073554.34855-7-work%40onurozkan.dev
patch subject: [PATCH v10 6/7] rust: ww_mutex: implement LockSet
config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20260103/202601031513.bVzSj6aK-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
rustc: rustc 1.88.0 (6b00bc388 2025-06-23)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260103/202601031513.bVzSj6aK-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601031513.bVzSj6aK-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> warning: this let-binding has unit value
--> rust/kernel/sync/lock/ww_mutex/lock_set.rs:273:17
|
273 | let _ = lock_set.lock(&mutex1)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `lock_set.lock(&mutex1)?;`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
= note: `-W clippy::let-unit-value` implied by `-W clippy::all`
= help: to override `-W clippy::all` add `#[allow(clippy::let_unit_value)]`
--
>> warning: matching over `()` is more explicit
--> rust/kernel/sync/lock/ww_mutex/lock_set.rs:273:21
|
273 | let _ = lock_set.lock(&mutex1)?;
| ^ help: use `()` instead of `_`: `()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns
= note: requested on the command line with `-W clippy::ignored-unit-patterns`
--
>> warning: this let-binding has unit value
--> rust/kernel/sync/lock/ww_mutex/lock_set.rs:274:17
|
274 | let _ = lock_set.lock(&mutex2)?;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `lock_set.lock(&mutex2)?;`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_unit_value
--
>> warning: matching over `()` is more explicit
--> rust/kernel/sync/lock/ww_mutex/lock_set.rs:274:21
|
274 | let _ = lock_set.lock(&mutex2)?;
| ^ help: use `()` instead of `_`: `()`
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ignored_unit_patterns
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v10 1/7] rust: add C wrappers for ww_mutex inline functions
2026-01-03 7:35 ` [PATCH v10 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
@ 2026-02-03 13:45 ` Daniel Almeida
2026-02-03 15:02 ` Onur Özkan
0 siblings, 1 reply; 12+ messages in thread
From: Daniel Almeida @ 2026-02-03 13:45 UTC (permalink / raw)
To: Onur Özkan
Cc: rust-for-linux, lossin, lyude, ojeda, alex.gaynor, boqun.feng,
gary, a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
longman, felipe_life, daniel, thomas.hellstrom, linux-kernel
Hi Onur,
Sorry for the delay, I’m finally getting back to this series.
> On 3 Jan 2026, at 04:35, Onur Özkan <work@onurozkan.dev> wrote:
>
> Some of the kernel's `ww_mutex` functions are implemented as
> `static inline`, so they are inaccessible from Rust as bindgen
> can't generate code on them. This patch provides C function wrappers
> around these inline implementations, so bindgen can see them and
> generate the corresponding Rust code.
>
> Signed-off-by: Onur Özkan <work@onurozkan.dev>
My comment remains the same as v8 [0]: did you forget to pick up tags? There’s an r-b here in v7 [1].
Or perhaps I’m missing something?
— Daniel
[0]: https://lore.kernel.org/rust-for-linux/2F879792-3C12-4C8F-8EF1-122F44EF2B19@collabora.com/
[1]: https://lore.kernel.org/rust-for-linux/34A2E775-A47E-4D4E-A292-E12489F07987@collabora.com/
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v10 1/7] rust: add C wrappers for ww_mutex inline functions
2026-02-03 13:45 ` Daniel Almeida
@ 2026-02-03 15:02 ` Onur Özkan
0 siblings, 0 replies; 12+ messages in thread
From: Onur Özkan @ 2026-02-03 15:02 UTC (permalink / raw)
To: Daniel Almeida
Cc: rust-for-linux, lossin, lyude, ojeda, alex.gaynor, boqun.feng,
gary, a.hindborg, aliceryhl, tmgross, dakr, peterz, mingo, will,
longman, felipe_life, daniel, thomas.hellstrom, linux-kernel
On Tue, 3 Feb 2026 10:45:12 -0300
Daniel Almeida <daniel.almeida@collabora.com> wrote:
> Hi Onur,
>
> Sorry for the delay, I’m finally getting back to this series.
>
No worries, thanks for looking on this.
>
> > On 3 Jan 2026, at 04:35, Onur Özkan <work@onurozkan.dev> wrote:
> >
> > Some of the kernel's `ww_mutex` functions are implemented as
> > `static inline`, so they are inaccessible from Rust as bindgen
> > can't generate code on them. This patch provides C function wrappers
> > around these inline implementations, so bindgen can see them and
> > generate the corresponding Rust code.
> >
> > Signed-off-by: Onur Özkan <work@onurozkan.dev>
>
> My comment remains the same as v8 [0]: did you forget to pick up
> tags? There’s an r-b here in v7 [1].
>
> Or perhaps I’m missing something?
>
I didn't forget, the patch in this file is changed since that (see
__rust_helper prefixes).
Regards,
Onur
> — Daniel
>
> [0]:
> https://lore.kernel.org/rust-for-linux/2F879792-3C12-4C8F-8EF1-122F44EF2B19@collabora.com/
> [1]:
> https://lore.kernel.org/rust-for-linux/34A2E775-A47E-4D4E-A292-E12489F07987@collabora.com/
>
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v10 0/7] rust: add ww_mutex support
2026-01-03 7:35 [PATCH v10 0/7] rust: add ww_mutex support Onur Özkan
` (6 preceding siblings ...)
2026-01-03 7:35 ` [PATCH v10 7/7] MAINTAINERS: add Onur Özkan as WW MUTEX maintainer Onur Özkan
@ 2026-07-10 12:52 ` Alice Ryhl
7 siblings, 0 replies; 12+ messages in thread
From: Alice Ryhl @ 2026-07-10 12:52 UTC (permalink / raw)
To: Onur Özkan
Cc: rust-for-linux, lossin, lyude, ojeda, alex.gaynor, boqun.feng,
gary, a.hindborg, tmgross, dakr, peterz, mingo, will, longman,
felipe_life, daniel, daniel.almeida, thomas.hellstrom,
linux-kernel
On Sat, Jan 03, 2026 at 10:35:47AM +0300, Onur Özkan wrote:
>
> MAINTAINERS | 2 +
> include/linux/ww_mutex.h | 8 +-
> rust/helpers/helpers.c | 1 +
> rust/helpers/ww_mutex.c | 45 ++
> rust/kernel/error.rs | 1 +
> rust/kernel/sync/lock.rs | 1 +
> rust/kernel/sync/lock/ww_mutex.rs | 444 ++++++++++++++++++
> rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs | 172 +++++++
> rust/kernel/sync/lock/ww_mutex/class.rs | 139 ++++++
> rust/kernel/sync/lock/ww_mutex/lock_set.rs | 370 +++++++++++++++
> 10 files changed, 1177 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
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-07-10 12:52 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-03 7:35 [PATCH v10 0/7] rust: add ww_mutex support Onur Özkan
2026-01-03 7:35 ` [PATCH v10 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
2026-02-03 13:45 ` Daniel Almeida
2026-02-03 15:02 ` Onur Özkan
2026-01-03 7:35 ` [PATCH v10 2/7] ww_mutex: add ww_class field unconditionally Onur Özkan
2026-01-03 7:35 ` [PATCH v10 3/7] rust: error: add EDEADLK Onur Özkan
2026-01-03 7:35 ` [PATCH v10 4/7] rust: implement Class for ww_class support Onur Özkan
2026-01-03 7:35 ` [PATCH v10 5/7] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard Onur Özkan
2026-01-03 7:35 ` [PATCH v10 6/7] rust: ww_mutex: implement LockSet Onur Özkan
2026-01-03 14:28 ` kernel test robot
2026-01-03 7:35 ` [PATCH v10 7/7] MAINTAINERS: add Onur Özkan as WW MUTEX maintainer Onur Özkan
2026-07-10 12:52 ` [PATCH v10 0/7] rust: add ww_mutex support Alice Ryhl
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.