mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Onur Özkan" <work@onurozkan.dev>
To: rust-for-linux@vger.kernel.org
Cc: linux-kernel@vger.kernel.org, lossin@kernel.org,
	lyude@redhat.com, ojeda@kernel.org, alex.gaynor@gmail.com,
	boqun.feng@gmail.com, gary@garyguo.net, a.hindborg@kernel.org,
	aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org,
	peterz@infradead.org, mingo@redhat.com, will@kernel.org,
	longman@redhat.com, felipe_life@live.com, daniel@sedlak.dev,
	bjorn3_gh@protonmail.com, daniel.almeida@collabora.com,
	"Onur Özkan" <work@onurozkan.dev>
Subject: [PATCH v6 3/7] rust: implement `WwMutex`, `WwAcquireCtx` and `WwMutexGuard`
Date: Wed,  3 Sep 2025 16:13:09 +0300	[thread overview]
Message-ID: <20250903131313.4365-4-work@onurozkan.dev> (raw)
In-Reply-To: <20250903131313.4365-1-work@onurozkan.dev>

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

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

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

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

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

 /// Create static [`WwClass`] instances.
 ///
@@ -134,3 +137,287 @@ pub fn new_wound_wait(name: &'static CStr) -> impl PinInit<Self> {
         Self::new(name, false)
     }
 }
+
+/// Groups multiple mutex acquisitions together for deadlock avoidance.
+///
+/// Must be used when acquiring multiple mutexes of the same class.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::sync::lock::ww_mutex::{WwClass, WwAcquireCtx, WwMutex};
+/// use kernel::c_str;
+/// use kernel::sync::Arc;
+/// use pin_init::stack_pin_init;
+///
+/// stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("my_class")));
+///
+/// // Create mutexes.
+/// let mutex1 = Arc::pin_init(WwMutex::new(1, &class), GFP_KERNEL)?;
+/// let mutex2 = Arc::pin_init(WwMutex::new(2, &class), GFP_KERNEL)?;
+///
+/// // Create acquire context for deadlock avoidance.
+/// let ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?;
+///
+/// // Acquire multiple locks safely.
+/// let guard1 = ctx.lock(&mutex1)?;
+/// let guard2 = ctx.lock(&mutex2)?;
+///
+/// // Mark acquisition phase as complete.
+/// ctx.done();
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[pin_data(PinnedDrop)]
+pub struct WwAcquireCtx<'a> {
+    #[pin]
+    inner: Opaque<bindings::ww_acquire_ctx>,
+    _p: PhantomData<&'a WwClass>,
+}
+
+impl<'ww_class> WwAcquireCtx<'ww_class> {
+    /// Initializes `Self` with calling C side `ww_acquire_init` inside.
+    pub fn new(ww_class: &'ww_class WwClass) -> impl PinInit<Self> {
+        let class = ww_class.inner.get();
+        pin_init!(WwAcquireCtx {
+            inner <- Opaque::ffi_init(|slot: *mut bindings::ww_acquire_ctx| {
+                // SAFETY: `ww_class` is valid for the lifetime `'ww_class` captured by `Self`.
+                unsafe { bindings::ww_acquire_init(slot, class) }
+            }),
+            _p: PhantomData
+        })
+    }
+
+    /// Marks the end of the acquire phase.
+    ///
+    /// After calling this function, no more mutexes can be acquired with this context.
+    pub fn done(&self) {
+        // SAFETY: The context is pinned and valid.
+        unsafe { bindings::ww_acquire_done(self.inner.get()) };
+    }
+
+    /// Locks the given mutex.
+    pub fn lock<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Result<WwMutexGuard<'a, T>> {
+        // SAFETY: The mutex is pinned and valid.
+        let ret = unsafe { bindings::ww_mutex_lock(ww_mutex.mutex.get(), self.inner.get()) };
+
+        to_result(ret)?;
+
+        Ok(WwMutexGuard::new(ww_mutex))
+    }
+
+    /// Similar to `lock`, but can be interrupted by signals.
+    pub fn lock_interruptible<'a, T>(
+        &'a self,
+        ww_mutex: &'a WwMutex<'a, T>,
+    ) -> Result<WwMutexGuard<'a, T>> {
+        // SAFETY: The mutex is pinned and valid.
+        let ret = unsafe {
+            bindings::ww_mutex_lock_interruptible(ww_mutex.mutex.get(), self.inner.get())
+        };
+
+        to_result(ret)?;
+
+        Ok(WwMutexGuard::new(ww_mutex))
+    }
+
+    /// Locks the given mutex using the slow path.
+    ///
+    /// This function should be used when `lock` fails (typically due to a potential deadlock).
+    pub fn lock_slow<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Result<WwMutexGuard<'a, T>> {
+        // SAFETY: The mutex is pinned and valid, and we're in the slow path.
+        unsafe { bindings::ww_mutex_lock_slow(ww_mutex.mutex.get(), self.inner.get()) };
+
+        Ok(WwMutexGuard::new(ww_mutex))
+    }
+
+    /// Similar to `lock_slow`, but can be interrupted by signals.
+    pub fn lock_slow_interruptible<'a, T>(
+        &'a self,
+        ww_mutex: &'a WwMutex<'a, T>,
+    ) -> Result<WwMutexGuard<'a, T>> {
+        // SAFETY: The mutex is pinned and valid, and we are in the slow path.
+        let ret = unsafe {
+            bindings::ww_mutex_lock_slow_interruptible(ww_mutex.mutex.get(), self.inner.get())
+        };
+
+        to_result(ret)?;
+
+        Ok(WwMutexGuard::new(ww_mutex))
+    }
+
+    /// Tries to lock the mutex without blocking.
+    ///
+    /// Unlike `lock`, no deadlock handling is performed.
+    pub fn try_lock<'a, T>(&'a self, ww_mutex: &'a WwMutex<'a, T>) -> Result<WwMutexGuard<'a, T>> {
+        // SAFETY: The mutex is pinned and valid.
+        let ret = unsafe { bindings::ww_mutex_trylock(ww_mutex.mutex.get(), self.inner.get()) };
+
+        if ret == 0 {
+            return Err(EBUSY);
+        } else {
+            to_result(ret)?;
+        }
+
+        Ok(WwMutexGuard::new(ww_mutex))
+    }
+}
+
+#[pinned_drop]
+impl PinnedDrop for WwAcquireCtx<'_> {
+    fn drop(self: Pin<&mut Self>) {
+        // SAFETY: The context is being dropped and is pinned.
+        unsafe { bindings::ww_acquire_fini(self.inner.get()) };
+    }
+}
+
+/// A wound/wait mutex backed with C side `ww_mutex`.
+///
+/// This is a mutual exclusion primitive that provides deadlock avoidance when
+/// acquiring multiple locks of the same class.
+///
+/// # Examples
+///
+/// ## Basic Usage
+///
+/// ```
+/// use kernel::c_str;
+/// use kernel::sync::Arc;
+/// use kernel::sync::lock::ww_mutex::{WwClass, WwAcquireCtx, WwMutex };
+/// use pin_init::stack_pin_init;
+///
+/// stack_pin_init!(let class = WwClass::new_wound_wait(c_str!("buffer_class")));
+/// let mutex = Arc::pin_init(WwMutex::new(42, &class), GFP_KERNEL)?;
+///
+/// let ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?;
+///
+/// let guard = ctx.lock(&mutex)?;
+/// assert_eq!(*guard, 42);
+///
+/// # Ok::<(), Error>(())
+/// ```
+///
+/// ## Multiple Locks
+///
+/// ```
+/// use kernel::c_str;
+/// use kernel::prelude::*;
+/// use kernel::sync::Arc;
+/// use kernel::sync::lock::ww_mutex::{WwClass, WwAcquireCtx, WwMutex};
+/// use pin_init::stack_pin_init;
+///
+/// stack_pin_init!(let class = WwClass::new_wait_die(c_str!("resource_class")));
+/// let mutex_a = Arc::pin_init(WwMutex::new("Resource A", &class), GFP_KERNEL)?;
+/// let mutex_b = Arc::pin_init(WwMutex::new("Resource B", &class), GFP_KERNEL)?;
+///
+/// let ctx = KBox::pin_init(WwAcquireCtx::new(&class), GFP_KERNEL)?;
+///
+/// // Try to acquire both locks.
+/// let guard_a = match ctx.lock(&mutex_a) {
+///     Ok(guard) => guard,
+///     Err(e) if e == EDEADLK => {
+///         // Deadlock detected, use slow path.
+///         ctx.lock_slow(&mutex_a)?
+///     }
+///     Err(e) => return Err(e),
+/// };
+///
+/// let guard_b = ctx.lock(&mutex_b)?;
+/// ctx.done();
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[pin_data]
+pub struct WwMutex<'a, T: ?Sized> {
+    _p: PhantomData<&'a WwClass>,
+    #[pin]
+    mutex: Opaque<bindings::ww_mutex>,
+    data: UnsafeCell<T>,
+}
+
+// SAFETY: [`WwMutex`] can be shared between threads.
+unsafe impl<T: ?Sized + Send> Send for WwMutex<'_, T> {}
+// SAFETY: [`WwMutex`] can be safely accessed from multiple threads concurrently.
+unsafe impl<T: ?Sized + Send + Sync> Sync for WwMutex<'_, T> {}
+
+impl<'ww_class, T> WwMutex<'ww_class, T> {
+    /// Creates `Self` with calling `ww_mutex_init` inside.
+    pub fn new(t: T, ww_class: &'ww_class WwClass) -> impl PinInit<Self> {
+        let class = ww_class.inner.get();
+        pin_init!(WwMutex {
+            mutex <- Opaque::ffi_init(|slot: *mut bindings::ww_mutex| {
+                // SAFETY: `ww_class` is valid for the lifetime `'ww_class` captured by `Self`.
+                unsafe { bindings::ww_mutex_init(slot, class) }
+            }),
+            data: UnsafeCell::new(t),
+            _p: PhantomData,
+        })
+    }
+}
+
+impl<T: ?Sized> WwMutex<'_, T> {
+    /// Returns a raw pointer to the inner mutex.
+    fn as_ptr(&self) -> *mut bindings::ww_mutex {
+        self.mutex.get()
+    }
+
+    /// Checks if the mutex is currently locked.
+    ///
+    /// Intended for internal tests only and should not be used
+    /// anywhere else.
+    #[cfg(CONFIG_KUNIT)]
+    fn is_locked(&self) -> bool {
+        // SAFETY: The mutex is pinned and valid.
+        unsafe { bindings::ww_mutex_is_locked(self.mutex.get()) }
+    }
+}
+
+/// A guard that provides exclusive access to the data protected
+/// by a [`WwMutex`].
+///
+/// # Invariants
+///
+/// The guard holds an exclusive lock on the associated [`WwMutex`]. The lock is held
+/// for the entire lifetime of this guard and is automatically released when the
+/// guard is dropped.
+#[must_use = "the lock unlocks immediately when the guard is unused"]
+pub struct WwMutexGuard<'a, T: ?Sized> {
+    mutex: &'a WwMutex<'a, T>,
+    _not_send: NotThreadSafe,
+}
+
+// SAFETY: [`WwMutexGuard`] can be shared between threads if the data can.
+unsafe impl<T: ?Sized + Sync> Sync for WwMutexGuard<'_, T> {}
+
+impl<'a, T: ?Sized> WwMutexGuard<'a, T> {
+    /// Creates a new guard for a locked mutex.
+    fn new(mutex: &'a WwMutex<'a, T>) -> Self {
+        Self {
+            mutex,
+            _not_send: NotThreadSafe,
+        }
+    }
+}
+
+impl<T: ?Sized> core::ops::Deref for WwMutexGuard<'_, T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        // SAFETY: We hold the lock, so we have exclusive access.
+        unsafe { &*self.mutex.data.get() }
+    }
+}
+
+impl<T: ?Sized> core::ops::DerefMut for WwMutexGuard<'_, T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        // SAFETY: We hold the lock, so we have exclusive access.
+        unsafe { &mut *self.mutex.data.get() }
+    }
+}
+
+impl<T: ?Sized> Drop for WwMutexGuard<'_, T> {
+    fn drop(&mut self) {
+        // SAFETY: We hold the lock and are about to release it.
+        unsafe { bindings::ww_mutex_unlock(self.mutex.as_ptr()) };
+    }
+}
--
2.50.0


  parent reply	other threads:[~2025-09-03 13:20 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-03 13:13 [PATCH v6 0/7] rust: add `ww_mutex` support Onur Özkan
2025-09-03 13:13 ` [PATCH v6 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
2025-09-03 13:46   ` Daniel Almeida
2025-09-03 13:13 ` [PATCH v6 2/7] rust: implement `WwClass` for ww_mutex support Onur Özkan
2025-09-03 16:06   ` Boqun Feng
2025-09-04  8:23     ` Onur Özkan
2025-09-03 13:13 ` Onur Özkan [this message]
2025-09-05 18:49   ` [PATCH v6 3/7] rust: implement `WwMutex`, `WwAcquireCtx` and `WwMutexGuard` Daniel Almeida
2025-09-05 19:03     ` Daniel Almeida
2025-09-06 11:38       ` Onur
2025-10-22 10:47       ` Onur Özkan
2025-09-06 11:35     ` Onur
2025-09-03 13:13 ` [PATCH v6 4/7] add KUnit coverage on Rust ww_mutex implementation Onur Özkan
2025-09-05 19:04   ` Daniel Almeida
2025-09-03 13:13 ` [PATCH v6 5/7] rust: ww_mutex: add context-free locking functions Onur Özkan
2025-09-05 19:14   ` Daniel Almeida
2025-09-06 11:20     ` Onur
2025-10-21 13:31       ` Daniel Almeida
2025-10-21 13:20     ` Onur Özkan
2025-09-03 13:13 ` [PATCH v6 6/7] rust: ww_mutex/exec: add high-level API Onur Özkan
2025-09-05 19:42   ` Daniel Almeida
2025-09-06 11:13     ` Onur
2025-09-06 15:04       ` Daniel Almeida
2025-09-07  8:20         ` Onur
2025-09-07  8:38           ` Onur
2025-10-21 19:36             ` Onur Özkan
2025-10-21 13:24     ` Onur Özkan
2025-10-21 14:04     ` Onur Özkan
2025-09-05 23:11   ` Elle Rhumsaa
2025-09-06 11:47     ` Onur Özkan
2025-09-03 13:13 ` [PATCH v6 7/7] add KUnit coverage on ww_mutex/exec implementation Onur Özkan
2025-09-05 23:12   ` Elle Rhumsaa
2025-10-16 19:47 ` [PATCH v6 0/7] rust: add `ww_mutex` support Lyude Paul
2025-10-17  5:03   ` Onur Özkan

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250903131313.4365-4-work@onurozkan.dev \
    --to=work@onurozkan.dev \
    --cc=a.hindborg@kernel.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=dakr@kernel.org \
    --cc=daniel.almeida@collabora.com \
    --cc=daniel@sedlak.dev \
    --cc=felipe_life@live.com \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=longman@redhat.com \
    --cc=lossin@kernel.org \
    --cc=lyude@redhat.com \
    --cc=mingo@redhat.com \
    --cc=ojeda@kernel.org \
    --cc=peterz@infradead.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    --cc=will@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®