Skip to contentSkip to conversation

rust: add ww_mutex support

PATCH Superseded Onur Özkan 1 participant 7 messages Oct 11

Subsystems (3)

v11 of 13 Superseded Latest is v13 →

lore
Share & tools
lore

Onur Özkan

author reviewer
#0 lore raw

v11 cover rust: add ww_mutex support

This series adds Rust abstractions for ww_mutex, including Class, Mutex, AcquireCtx and MutexGuard. LockSet handles deadlock recovery for callers that need to acquire multiple locks.

Changes in v11:
- Rebased and dropped the EDEADLK patch, which is already covered by the base tree.
- Removed the slow-path locking APIs and helpers and AcquireCtx::reinit().
- Relaxed the Mutex Sync bound and fixed the MutexGuard::from_raw() safety contract.
- Made AcquireCtx locking methods unsafe to account for leaked guards, and made AcquireCtx::done() safe.
- Fixed the LockSet double unlock on allocation failure. Deadlock recovery now waits for and holds the contended lock before retrying.
- Made use statements vertical.

Previous version:
https://lore.kernel.org/all/[email protected]/

Onur Özkan (6):
rust: add C wrappers for ww_mutex inline functions
ww_mutex: add ww_class field unconditionally
rust: implement Class for ww_class support
rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard
rust: ww_mutex: implement LockSet
MAINTAINERS: add Onur Özkan as WW MUTEX maintainer

 MAINTAINERS                                   |   2 +
 include/linux/ww_mutex.h                      |   8 +-
 rust/helpers/helpers.c                        |   1 +
 rust/helpers/ww_mutex.c                       |  33 ++
 rust/kernel/sync/lock.rs                      |   1 +
 rust/kernel/sync/lock/ww_mutex.rs             | 443 +++++++++++++++++
 rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs | 148 ++++++
 rust/kernel/sync/lock/ww_mutex/class.rs       | 144 ++++++
 rust/kernel/sync/lock/ww_mutex/lock_set.rs    | 448 ++++++++++++++++++
 9 files changed, 1222 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

base-commit: 47324d3a5b3abd781295044d01d92d09f184e872

Signature · 1 line
2.51.2
author reviewer
#1 lore raw

PATCH v11 1/6 rust: add C wrappers for ww_mutex inline functions

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.

---
 MAINTAINERS             |  1 +
 rust/helpers/helpers.c  |  1 +
 rust/helpers/ww_mutex.c | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 35 insertions(+)
 create mode 100644 rust/helpers/ww_mutex.c
MAINTAINERS +1 -0
index 47946f1c507b..198bb740f7cc 100644
@@ -15283,6 +15283,7 @@ F:	kernel/locking/
 F:	lib/locking*.[ch]
 F:	rust/helpers/mutex.c
 F:	rust/helpers/spinlock.c
+F:	rust/helpers/ww_mutex.c
 F:	rust/kernel/sync/lock.rs
 F:	rust/kernel/sync/lock/
 F:	rust/kernel/sync/locked_by.rs
rust/helpers/helpers.c +1 -0
index 440fb7638e3c..0287064c82c1 100644
@@ -105,4 +105,5 @@
 #include "vmalloc.c"
 #include "wait.c"
 #include "workqueue.c"
+#include "ww_mutex.c"
 #include "xarray.c"
rust/helpers/ww_mutex.c new file +33 -0
new file mode 100644
index 000000000000..e7aa65ac5874
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/ww_mutex.h>
+
+__rust_helper
+void rust_helper_ww_mutex_init(struct ww_mutex *lock, struct ww_class *ww_class)
+{
+	ww_mutex_init(lock, ww_class);
+}
+
+__rust_helper
+void rust_helper_ww_acquire_init(struct ww_acquire_ctx *ctx, struct ww_class *ww_class)
+{
+	ww_acquire_init(ctx, ww_class);
+}
+
+__rust_helper
+void rust_helper_ww_acquire_done(struct ww_acquire_ctx *ctx)
+{
+	ww_acquire_done(ctx);
+}
+
+__rust_helper
+void rust_helper_ww_acquire_fini(struct ww_acquire_ctx *ctx)
+{
+	ww_acquire_fini(ctx);
+}
+
+__rust_helper
+bool rust_helper_ww_mutex_is_locked(struct ww_mutex *lock)
+{
+	return ww_mutex_is_locked(lock);
+}
Signature · 1 line
2.51.2
author reviewer
#2 lore raw

PATCH v11 2/6 ww_mutex: add ww_class field unconditionally

In reply to 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 and validate Mutex and AcquireCtx class pairing in much simpler way.

---
 include/linux/ww_mutex.h | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)
include/linux/ww_mutex.h +2 -6
index 0c95ead5a297..c955824aa5d4 100644
@@ -48,9 +48,7 @@ struct ww_class {
 context_lock_struct(ww_mutex) {
 	struct WW_MUTEX_BASE base;
 	struct ww_acquire_ctx *ctx;
-#ifdef DEBUG_WW_MUTEXES
 	struct ww_class *ww_class;
-#endif
 };
 
 context_lock_struct(ww_acquire_ctx) {
@@ -59,9 +57,9 @@ context_lock_struct(ww_acquire_ctx) {
 	unsigned int acquired;
 	unsigned short wounded;
 	unsigned short is_wait_die;
+	struct ww_class *ww_class;
 #ifdef DEBUG_WW_MUTEXES
 	unsigned int done_acquire;
-	struct ww_class *ww_class;
 	void *contending_lock;
 #endif
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
@@ -111,9 +109,7 @@ static inline void ww_mutex_init(struct ww_mutex *lock,
 {
 	ww_mutex_base_init(&lock->base, ww_class->mutex_name, &ww_class->mutex_key);
 	lock->ctx = NULL;
-#ifdef DEBUG_WW_MUTEXES
 	lock->ww_class = ww_class;
-#endif
 }
 
 /**
@@ -149,8 +145,8 @@ static inline void ww_acquire_init(struct ww_acquire_ctx *ctx,
 	ctx->acquired = 0;
 	ctx->wounded = false;
 	ctx->is_wait_die = ww_class->is_wait_die;
-#ifdef DEBUG_WW_MUTEXES
 	ctx->ww_class = ww_class;
+#ifdef DEBUG_WW_MUTEXES
 	ctx->done_acquire = 0;
 	ctx->contending_lock = NULL;
 #endif
Signature · 1 line
2.51.2
author reviewer
#3 lore raw

PATCH v11 3/6 rust: implement Class for ww_class support

In reply to 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_ww_class and define_wd_class macros for safely declaring static instances.

---
 rust/kernel/sync/lock.rs                |   1 +
 rust/kernel/sync/lock/ww_mutex.rs       |   7 ++
 rust/kernel/sync/lock/ww_mutex/class.rs | 144 ++++++++++++++++++++++++
 3 files changed, 152 insertions(+)
 create mode 100644 rust/kernel/sync/lock/ww_mutex.rs
 create mode 100644 rust/kernel/sync/lock/ww_mutex/class.rs
rust/kernel/sync/lock.rs +1 -0
index 10b6b5e9b024..f509250822c5 100644
@@ -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};
rust/kernel/sync/lock/ww_mutex.rs new file +7 -0
new file mode 100644
index 000000000000..727c51cc73af
@@ -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;
rust/kernel/sync/lock/ww_mutex/class.rs new file +144 -0

144 lines not on this page · raw message

Signature · 1 line
2.51.2
author reviewer
#4 lore raw

PATCH v11 4/6 rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard

In reply to Onur Özkan

Covers the low-level locking API (lock, try_lock and interruptible variants) and integration with kernel bindings.

---
 rust/kernel/sync/lock/ww_mutex.rs             | 430 ++++++++++++++++++
 rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs | 148 ++++++
 2 files changed, 578 insertions(+)
 create mode 100644 rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs
rust/kernel/sync/lock/ww_mutex.rs +430 -0
index 727c51cc73af..1626ec811a83 100644
@@ -1,7 +1,437 @@
 // SPDX-License-Identifier: GPL-2.0
 
 //! Rust abstractions for the kernel's wound-wait locking primitives.
+//!
+//! It is designed to avoid deadlocks when locking multiple [`Mutex`]es
+//! that belong to the same [`Class`]. Each lock acquisition uses an
+//! [`AcquireCtx`] to track ordering and ensure forward progress.
+//!
+//! See srctree/Documentation/locking/ww-mutex-design.rst for more details.
 
+use crate::{
+    bindings,
+    container_of,
+    error::to_result,
+    prelude::*,
+    types::{
+        NotThreadSafe,
+        Opaque, //
+    }, //
+};
+use core::{
+    cell::UnsafeCell,
+    marker::PhantomData, //
+};
+
+pub use acquire_ctx::AcquireCtx;
 pub use class::Class;
 
+mod acquire_ctx;
 mod class;
+
+/// A wound-wait (ww) mutex that is powered with deadlock avoidance
+/// when acquiring multiple locks of the same [`Class`].
+///
+/// Each mutex belongs to a [`Class`], which the wound-wait algorithm
+/// uses to figure out the order of acquisition and prevent deadlocks.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::{
+///     define_ww_class,
+///     sync::{
+///         lock::ww_mutex::{
+///             AcquireCtx,
+///             Class,
+///             Mutex, //
+///         },
+///         Arc,
+///     },
+/// };
+/// use pin_init::stack_pin_init;
+///
+/// define_ww_class!(SOME_WW_CLASS);
+///
+/// let mutex = Arc::pin_init(Mutex::new(42, &SOME_WW_CLASS), GFP_KERNEL)?;
+/// let ctx = KBox::pin_init(AcquireCtx::new(&SOME_WW_CLASS), GFP_KERNEL)?;
+///
+/// // SAFETY: The guard is dropped before `ctx`.
+/// let guard = unsafe { ctx.lock(&mutex) }?;
+/// assert_eq!(*guard, 42);
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[pin_data]
+#[repr(C)]
+pub struct Mutex<'a, T: ?Sized> {
+    _p: PhantomData<&'a Class>,
+    #[pin]
+    inner: Opaque<bindings::ww_mutex>,
+    data: UnsafeCell<T>,
+}
+
+impl<'class, T> Mutex<'class, T> {
+    /// Initializes [`Mutex`] with the given `data` and [`Class`].
+    pub fn new(data: T, class: &'class Class) -> impl PinInit<Self> {
+        let class_ptr = class.inner.get();
+        pin_init!(Mutex {
+            inner <- Opaque::ffi_init(|slot: *mut bindings::ww_mutex| {
+                // SAFETY: `class` is valid for the lifetime `'class` captured by `Self`.
+                unsafe { bindings::ww_mutex_init(slot, class_ptr) }
+            }),
+            data: UnsafeCell::new(data),
+            _p: PhantomData
+        })
+    }
+}
+
+impl<'class, T: ?Sized> Mutex<'class, T> {
+    /// Checks if this [`Mutex`] is currently locked.
+    ///
+    /// The returned value is racy as another thread can acquire
+    /// or release the lock immediately after this call returns.
+    pub fn is_locked(&self) -> bool {
+        // SAFETY: It's safe to call `ww_mutex_is_locked` on
+        // a valid mutex.
+        unsafe { bindings::ww_mutex_is_locked(self.inner.get()) }
+    }
+
+    /// Locks this [`Mutex`] without [`AcquireCtx`].
+    pub fn lock(&self) -> Result<MutexGuard<'_, T>> {
+        lock_common(self, None, LockKind::Regular)
+    }
+
+    /// Similar to [`Self::lock`], but can be interrupted by signals.
+    pub fn lock_interruptible(&self) -> Result<MutexGuard<'_, T>> {
+        lock_common(self, None, LockKind::Interruptible)
+    }
+
+    /// Tries to lock this [`Mutex`] with no [`AcquireCtx`] and without blocking.
+    ///
+    /// Unlike [`Self::lock`], no deadlock handling is performed.
+    pub fn try_lock(&self) -> Result<MutexGuard<'_, T>> {
+        lock_common(self, None, LockKind::Try)
+    }
+}
+
+impl<'class> Mutex<'class, ()> {
+    /// Creates a [`Mutex`] from a raw pointer.
+    ///
+    /// This function is intended for interoperability with C code.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that:
+    ///
+    /// - `ptr` is a valid pointer to a `ww_mutex`.
+    /// - `ptr` must remain valid for the lifetime `'a`.
+    /// - ww_class associated with this mutex must be valid for
+    ///   the lifetime `'class`.
+    pub unsafe fn from_raw<'a>(ptr: *mut bindings::ww_mutex) -> &'a Self {
+        // SAFETY: By the safety contract, the caller guarantees that `ptr`
+        // points to a valid `ww_mutex` which is the `inner` field of `Mutex`,
+        // that it remains valid for the lifetime `'a` and the associated
+        // ww_class outlives `'class`.
+        //
+        // Because [`Mutex`] is `#[repr(C)]`, the `inner` field sits at a
+        // stable offset that `container_of!` can safely rely on.
+        unsafe { &*container_of!(Opaque::cast_from(ptr), Self, inner) }
+    }
+}
+
+// SAFETY: `Mutex` can be sent to another thread if the protected
+// data `T` can be.
+unsafe impl<T: ?Sized + Send> Send for Mutex<'_, T> {}
+
+// SAFETY: `Mutex` serialises access to the protected data, so it is
+// `Sync` as long as the data is `Send`.
+unsafe impl<T: ?Sized + Send> Sync for Mutex<'_, T> {}
+
+/// A guard that provides exclusive access to the data protected
+/// by a [`Mutex`].
+///
+/// # Invariants
+///
+/// The guard holds an exclusive lock on the associated [`Mutex`]. The lock is held
+/// for the entire lifetime of this guard and is automatically released when the
+/// guard is dropped.
+#[must_use = "the lock unlocks immediately when the guard is unused"]
+pub struct MutexGuard<'a, T: ?Sized> {
+    mutex: &'a Mutex<'a, T>,
+    _not_send: NotThreadSafe,
+}
+
+impl<'a, T: ?Sized> MutexGuard<'a, T> {
+    /// Creates a new guard for the given [`Mutex`].
+    fn new(mutex: &'a Mutex<'a, T>) -> Self {
+        assert!(mutex.is_locked());
+
+        Self {
+            mutex,
+            _not_send: NotThreadSafe,
+        }
+    }
+}
+
+impl<'a> MutexGuard<'a, ()> {
+    /// Creates a [`MutexGuard`] from a raw pointer.
+    ///
+    /// If the given pointer refers to a mutex that is not locked,
+    /// returns [`EINVAL`].
+    ///
+    /// This function is intended for interoperability with C code.
+    ///
+    /// # Safety
+    ///
+    /// The caller must ensure that:
+    ///
+    /// - `ptr` is a valid pointer to a `ww_mutex`.
+    /// - `ptr` must remain valid for the lifetime `'b`.
+    /// - The `ww_class` associated with the `ww_mutex` must be valid for the lifetime `'b`.
+    /// - The current task holds the mutex.
+    /// - The caller transfers responsibility for unlocking the mutex to the returned guard
+    ///   and must not unlock it separately (including through another guard).
+    /// - Any associated acquire context remains valid until the mutex is unlocked,
+    ///   even if the returned guard is forgotten. The context must not be finalized or
+    ///   reinitialized before then.
+    pub unsafe fn from_raw<'b>(ptr: *mut bindings::ww_mutex) -> Result<MutexGuard<'b, ()>> {
+        // SAFETY: By this function's safety contract, the caller guarantees that `ptr` points to a
+        // valid `ww_mutex` which is the `inner` field of a `Mutex`. The caller also guarantees
+        // that both `ptr` and the associated `ww_class` are valid for the lifetime `'b`.
+        let mutex = unsafe { Mutex::from_raw(ptr) };
+
+        if !mutex.is_locked() {
+            return Err(EINVAL);
+        }
+
+        Ok(MutexGuard::new(mutex))
+    }
+}
+
+impl<T: ?Sized> core::ops::Deref for MutexGuard<'_, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: self.mutex is locked, so we have exclusive access.
+        unsafe { &*self.mutex.data.get() }
+    }
+}
+
+impl<T: ?Sized + Unpin> core::ops::DerefMut for MutexGuard<'_, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        // SAFETY: self.mutex is locked, so we have exclusive access.
+        unsafe { &mut *self.mutex.data.get() }
+    }
+}
+
+impl<T: ?Sized> Drop for MutexGuard<'_, T> {
+    fn drop(&mut self) {
+        // SAFETY: self.mutex is locked and are about to release it.
+        unsafe { bindings::ww_mutex_unlock(self.mutex.inner.get()) };
+    }
+}
+
+// SAFETY: `MutexGuard` can be shared between threads if the data can.
+unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
+
+/// Locking kinds used by [`lock_common`] to unify the internal
+/// locking logic.
+///
+/// It's best not to expose this type (and [`lock_common`]) to the
+/// kernel, as it allows internal API changes without worrying
+/// about breaking external compatibility.
+#[derive(Copy, Clone, Debug)]
+enum LockKind {
+    /// Blocks until lock is acquired.
+    Regular,
+    /// Blocks but can be interrupted by signals.
+    Interruptible,
+    /// Does not block, returns immediately if busy.
+    Try,
+}
+
+/// Internal helper that unifies the different locking kinds.
+///
+/// Returns [`EINVAL`] if the [`Mutex`] has a different [`Class`].
+fn lock_common<'a, T: ?Sized>(
+    mutex: &'a Mutex<'a, T>,
+    ctx: Option<&AcquireCtx<'_>>,
+    kind: LockKind,
+) -> Result<MutexGuard<'a, T>> {
+    let mutex_ptr = mutex.inner.get();
+
+    let ctx_ptr = match ctx {
+        Some(acquire_ctx) => {
+            let ctx_ptr = acquire_ctx.inner.get();
+
+            // SAFETY: `ctx_ptr` is a valid pointer for the entire
+            // lifetime of `ctx`.
+            let ctx_class = unsafe { (*ctx_ptr).ww_class };
+
+            // SAFETY: `mutex_ptr` is a valid pointer for the entire
+            // lifetime of `mutex`.
+            let mutex_class = unsafe { (*mutex_ptr).ww_class };
+
+            // `ctx` and `mutex` must use the same class.
+            if ctx_class != mutex_class {
+                return Err(EINVAL);
+            }
+
+            ctx_ptr
+        }
+        None => core::ptr::null_mut(),
+    };
+
+    match kind {
+        LockKind::Regular => {
+            // SAFETY: `Mutex` is always pinned. If `AcquireCtx` is `Some`, it is pinned,
+            // if `None`, it is set to `core::ptr::null_mut()`. Both cases are safe.
+            let ret = unsafe { bindings::ww_mutex_lock(mutex_ptr, ctx_ptr) };
+
+            to_result(ret)?;
+        }
+        LockKind::Interruptible => {
+            // SAFETY: `Mutex` is always pinned. If `AcquireCtx` is `Some`, it is pinned,
+            // if `None`, it is set to `core::ptr::null_mut()`. Both cases are safe.
+            let ret = unsafe { bindings::ww_mutex_lock_interruptible(mutex_ptr, ctx_ptr) };
+
+            to_result(ret)?;
+        }
+        LockKind::Try => {
+            // SAFETY: `Mutex` is always pinned. If `AcquireCtx` is `Some`, it is pinned,
+            // if `None`, it is set to `core::ptr::null_mut()`. Both cases are safe.
+            let ret = unsafe { bindings::ww_mutex_trylock(mutex_ptr, ctx_ptr) };
+
+            if ret == 0 {
+                return Err(EBUSY);
+            } else {
+                to_result(ret)?;
+            }
+        }
+    };
+
+    Ok(MutexGuard::new(mutex))
+}
+
+#[kunit_tests(rust_kernel_ww_mutex)]
+mod tests {
+    use super::*;
+    use crate::{
+        define_wd_class,
+        define_ww_class,
+        sync::Arc, //
+    };
+
+    define_ww_class!(TEST_WOUND_WAIT_CLASS);
+    define_wd_class!(TEST_WAIT_DIE_CLASS);
+
+    #[test]
+    fn test_ww_mutex_basic_lock_unlock() -> Result {
+        let mutex = Arc::pin_init(Mutex::new(42, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+        let ctx = KBox::pin_init(AcquireCtx::new(&TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+
+        // SAFETY: The guard is dropped before `ctx`.
+        let guard = unsafe { ctx.lock(&mutex) }?;
+        assert_eq!(*guard, 42);
+
+        // Drop the lock.
+        drop(guard);
+
+        // SAFETY: The guard is dropped before `ctx`.
+        let mut guard = unsafe { ctx.lock(&mutex) }?;
+        *guard = 100;
+        assert_eq!(*guard, 100);
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_ww_mutex_trylock() -> Result {
+        let mutex = Arc::pin_init(Mutex::new(123, &TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+        let ctx = KBox::pin_init(AcquireCtx::new(&TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+
+        // `try_lock` on unlocked mutex should succeed.
+        // SAFETY: The guard is dropped before `ctx`.
+        let guard = unsafe { ctx.try_lock(&mutex) }?;
+        assert_eq!(*guard, 123);
+
+        // Now it should fail immediately as it's already locked.
+        // SAFETY: Any returned guard is dropped immediately, before `ctx`.
+        assert!(unsafe { ctx.try_lock(&mutex) }.is_err());
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_ww_mutex_is_locked() -> Result {
+        let mutex = Arc::pin_init(Mutex::new("hello", &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+        let ctx = KBox::pin_init(AcquireCtx::new(&TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+
+        // Should not be locked initially.
+        assert!(!mutex.is_locked());
+
+        // SAFETY: The guard is dropped before `ctx`.
+        let guard = unsafe { ctx.lock(&mutex) }?;
+        assert!(mutex.is_locked());
+
+        drop(guard);
+        assert!(!mutex.is_locked());
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_ww_acquire_context_done() -> Result {
+        let mutex1 = Arc::pin_init(Mutex::new(1, &TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+        let mutex2 = Arc::pin_init(Mutex::new(2, &TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+        let ctx = KBox::pin_init(AcquireCtx::new(&TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+
+        // Acquire multiple mutexes with the same context.
+        // SAFETY: The guard is dropped before `ctx`.
+        let guard1 = unsafe { ctx.lock(&mutex1) }?;
+        // SAFETY: The guard is dropped before `ctx`.
+        let guard2 = unsafe { ctx.lock(&mutex2) }?;
+
+        assert_eq!(*guard1, 1);
+        assert_eq!(*guard2, 2);
+
+        ctx.done();
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_mutex_without_ctx() -> Result {
+        let mutex = Arc::pin_init(Mutex::new(100, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+        let guard = mutex.lock()?;
+
+        assert_eq!(*guard, 100);
+        assert!(mutex.is_locked());
+
+        drop(guard);
+
+        assert!(!mutex.is_locked());
+
+        Ok(())
+    }
+
+    #[test]
+    fn test_guard_from_raw_unlocks_mutex() -> Result {
+        let mutex = Arc::pin_init(Mutex::new((), &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+        let guard = mutex.lock()?;
+
+        // Keep the mutex locked without leaving another guard responsible for unlocking it.
+        core::mem::forget(guard);
+
+        // SAFETY: `mutex` and its class remain valid for this test. The current task holds
+        // the mutex, and forgetting the previous guard lets this guard take over unlocking it.
+        let guard = unsafe { MutexGuard::from_raw(mutex.inner.get()) }?;
+        assert!(mutex.is_locked());
+
+        drop(guard);
+        assert!(!mutex.is_locked());
+
+        Ok(())
+    }
+}
rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs new file +148 -0

148 lines not on this page · raw message

Signature · 1 line
2.51.2
author reviewer
#5 lore raw

PATCH v11 5/6 rust: ww_mutex: implement LockSet

In reply to 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 and waits for the contended mutex. It then retries the given (by the user) locking algorithm with that mutex held and the same acquire context until it succeeds.

---
 rust/kernel/sync/lock/ww_mutex.rs          |   6 +
 rust/kernel/sync/lock/ww_mutex/lock_set.rs | 448 +++++++++++++++++++++
 2 files changed, 454 insertions(+)
 create mode 100644 rust/kernel/sync/lock/ww_mutex/lock_set.rs
rust/kernel/sync/lock/ww_mutex.rs +6 -0
index 1626ec811a83..99b5a32d033d 100644
@@ -6,6 +6,10 @@
 //! that belong to the same [`Class`]. Each lock acquisition uses an
 //! [`AcquireCtx`] to track ordering and ensure forward progress.
 //!
+//! It is recommended to use [`LockSet`] as it provides safe high-level
+//! interface that automatically handles deadlocks, retries and context
+//! management.
+//!
 //! See srctree/Documentation/locking/ww-mutex-design.rst for more details.
 
 use crate::{
@@ -25,9 +29,11 @@
 
 pub use acquire_ctx::AcquireCtx;
 pub use class::Class;
+pub use lock_set::LockSet;
 
 mod acquire_ctx;
 mod class;
+mod lock_set;
 
 /// A wound-wait (ww) mutex that is powered with deadlock avoidance
 /// when acquiring multiple locks of the same [`Class`].
rust/kernel/sync/lock/ww_mutex/lock_set.rs new file +448 -0

448 lines not on this page · raw message

Signature · 1 line
2.51.2
author reviewer
#6 lore raw

PATCH v11 6/6 MAINTAINERS: add Onur Özkan as WW MUTEX maintainer

In reply to Onur Özkan

Add myself as a WW MUTEX maintainer to reflect ongoing work and future maintenance of WW MUTEX.

---
 MAINTAINERS | 1 +
 1 file changed, 1 insertion(+)
MAINTAINERS +1 -0
index 198bb740f7cc..ff41d34f76f7 100644
@@ -15265,6 +15265,7 @@ M:	Peter Zijlstra <[email protected]>
 M:	Ingo Molnar <[email protected]>
 M:	Will Deacon <[email protected]>
 M:	Boqun Feng <[email protected]> (LOCKDEP & RUST)
+M:	Onur Özkan <[email protected]> (WW MUTEX)
 R:	Waiman Long <[email protected]>
 L:	[email protected]
 S:	Maintained
Signature · 1 line
2.51.2