* [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
` (6 more replies)
0 siblings, 7 replies; 11+ 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] 11+ 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
` (5 subsequent siblings)
6 siblings, 1 reply; 11+ 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] 11+ 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
` (4 subsequent siblings)
6 siblings, 0 replies; 11+ 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] 11+ 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
` (3 subsequent siblings)
6 siblings, 0 replies; 11+ 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] 11+ 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
` (2 subsequent siblings)
6 siblings, 0 replies; 11+ 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] 11+ 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
2026-01-03 7:35 ` [PATCH v10 7/7] MAINTAINERS: add Onur Özkan as WW MUTEX maintainer Onur Özkan
6 siblings, 0 replies; 11+ 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] 11+ 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
6 siblings, 1 reply; 11+ 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] 11+ 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
6 siblings, 0 replies; 11+ 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] 11+ 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; 11+ 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] 11+ 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; 11+ 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] 11+ 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; 11+ 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] 11+ messages in thread
end of thread, other threads:[~2026-02-03 15:08 UTC | newest]
Thread overview: 11+ 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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox