From: "Onur Özkan" <work@onurozkan.dev>
To: rust-for-linux@vger.kernel.org
Cc: 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, daniel.almeida@collabora.com,
thomas.hellstrom@linux.intel.com, linux-kernel@vger.kernel.org,
"Onur Özkan" <work@onurozkan.dev>
Subject: [PATCH v13 4/7] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard
Date: Tue, 20 Oct 2026 18:29:40 +0300 [thread overview]
Message-ID: <20261020153112.299745-5-work@onurozkan.dev> (raw)
In-Reply-To: <20261020153112.299745-1-work@onurozkan.dev>
Covers the low-level locking API (lock, try_lock and
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 | 179 ++++++++
2 files changed, 610 insertions(+)
create mode 100644 rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs
diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_mutex.rs
index 727c51cc73af8..91eaa6cf2a189 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::{
+ bindings,
+ container_of,
+ error::to_result,
+ prelude::*,
+ types::{
+ NotThreadSafe,
+ Opaque, //
+ }, //
+};
+use core::cell::UnsafeCell;
+
+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)?;
+///
+/// let guard = ctx.lock(&mutex)?;
+/// assert_eq!(*guard, 42);
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[pin_data]
+#[repr(C)]
+pub struct Mutex<T: ?Sized> {
+ #[pin]
+ inner: Opaque<bindings::ww_mutex>,
+ data: UnsafeCell<T>,
+}
+
+impl<T> Mutex<T> {
+ /// Initializes [`Mutex`] with the given `data` and [`Class`].
+ #[inline]
+ pub fn new(data: T, class: &'static 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 indefinitely.
+ unsafe { bindings::ww_mutex_init(slot, class_ptr) }
+ }),
+ data: UnsafeCell::new(data),
+ })
+ }
+}
+
+impl<T: ?Sized> Mutex<T> {
+ /// Checks if this [`Mutex`] is currently locked.
+ ///
+ /// The returned value is racy as another thread can acquire
+ /// or release the lock immediately after this call returns.
+ #[inline]
+ pub fn is_locked(&self) -> bool {
+ // SAFETY: It's safe to call `ww_mutex_is_locked` on
+ // a valid mutex.
+ unsafe { bindings::ww_mutex_is_locked(self.inner.get()) }
+ }
+
+ /// Locks this [`Mutex`] without [`AcquireCtx`].
+ #[inline]
+ pub fn lock(&self) -> Result<MutexGuard<'_, T>> {
+ lock(self, None)
+ }
+
+ /// Similar to [`Self::lock`], but can be interrupted by signals.
+ #[inline]
+ pub fn lock_interruptible(&self) -> Result<MutexGuard<'_, T>> {
+ lock_interruptible(self, None)
+ }
+
+ /// Tries to lock this [`Mutex`] with no [`AcquireCtx`] and without blocking.
+ ///
+ /// Returns `None` if the mutex could not be acquired.
+ #[must_use = "if unused, the lock will be immediately unlocked"]
+ #[inline]
+ pub fn try_lock(&self) -> Option<MutexGuard<'_, T>> {
+ try_lock(self, None)
+ }
+}
+
+impl Mutex<()> {
+ /// 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`.
+ /// - The `ww_class` associated with this mutex must be valid indefinitely.
+ #[inline]
+ 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` remains valid indefinitely.
+ //
+ // 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<T>,
+ _not_send: NotThreadSafe,
+}
+
+impl<'a, T: ?Sized> MutexGuard<'a, T> {
+ /// Creates a new guard for the given [`Mutex`].
+ #[inline]
+ fn new(mutex: &'a Mutex<T>) -> Self {
+ debug_assert!(mutex.is_locked());
+
+ // INVARIANT: Locking and `from_raw` ensure that the current task holds the mutex
+ // and transfer sole responsibility for unlocking it to this guard.
+ Self {
+ mutex,
+ _not_send: NotThreadSafe,
+ }
+ }
+}
+
+impl<'a> MutexGuard<'a, ()> {
+ /// Creates a [`MutexGuard`] from a raw pointer.
+ ///
+ /// 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 indefinitely.
+ /// - 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.
+ #[inline]
+ pub unsafe fn from_raw<'b>(ptr: *mut bindings::ww_mutex) -> 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 `ptr` is valid for the lifetime `'b` and its `ww_class` is valid indefinitely.
+ let mutex = unsafe { Mutex::from_raw(ptr) };
+
+ MutexGuard::new(mutex)
+ }
+}
+
+impl<T: ?Sized> core::ops::Deref for MutexGuard<'_, T> {
+ type Target = T;
+
+ #[inline]
+ 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> {
+ #[inline]
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ // SAFETY: self.mutex is locked, so we have exclusive access.
+ unsafe { &mut *self.mutex.data.get() }
+ }
+}
+
+impl<T: ?Sized> Drop for MutexGuard<'_, T> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: self.mutex is locked and are about to release it.
+ unsafe { bindings::ww_mutex_unlock(self.mutex.inner.get()) };
+ }
+}
+
+// SAFETY: `MutexGuard` can be shared between threads if the data can.
+unsafe impl<T: ?Sized + Sync> Sync for MutexGuard<'_, T> {}
+
+/// Locks a mutex with an optional acquire context.
+#[inline]
+fn lock<'a, T: ?Sized>(mutex: &'a Mutex<T>, ctx: Option<&AcquireCtx>) -> Result<MutexGuard<'a, T>> {
+ let ctx_ptr = match ctx {
+ Some(ctx) => {
+ ctx.validate_mutex(mutex)?;
+ ctx.inner.get()
+ }
+ None => core::ptr::null_mut(),
+ };
+
+ // SAFETY: `mutex` is pinned and valid. `ctx_ptr` is null or refers to a pinned context of
+ // the same class. Callers keep the context valid until the mutex is unlocked.
+ let ret = unsafe { bindings::ww_mutex_lock(mutex.inner.get(), ctx_ptr) };
+ to_result(ret)?;
+
+ Ok(MutexGuard::new(mutex))
+}
+
+/// Locks a mutex with an optional acquire context, allowing interruption by signals.
+#[inline]
+fn lock_interruptible<'a, T: ?Sized>(
+ mutex: &'a Mutex<T>,
+ ctx: Option<&AcquireCtx>,
+) -> Result<MutexGuard<'a, T>> {
+ let ctx_ptr = match ctx {
+ Some(ctx) => {
+ ctx.validate_mutex(mutex)?;
+ ctx.inner.get()
+ }
+ None => core::ptr::null_mut(),
+ };
+
+ // SAFETY: `mutex` is pinned and valid. `ctx_ptr` is null or refers to a pinned context of
+ // the same class. Callers keep the context valid until the mutex is unlocked.
+ let ret = unsafe { bindings::ww_mutex_lock_interruptible(mutex.inner.get(), ctx_ptr) };
+ to_result(ret)?;
+
+ Ok(MutexGuard::new(mutex))
+}
+
+/// Tries to lock a mutex with an optional acquire context without blocking.
+#[inline]
+fn try_lock<'a, T: ?Sized>(
+ mutex: &'a Mutex<T>,
+ ctx: Option<&AcquireCtx>,
+) -> Option<MutexGuard<'a, T>> {
+ let ctx_ptr = match ctx {
+ Some(ctx) => {
+ ctx.validate_mutex(mutex).ok()?;
+ ctx.inner.get()
+ }
+ None => core::ptr::null_mut(),
+ };
+
+ // SAFETY: `mutex` is pinned and valid. `ctx_ptr` is null or refers to a pinned context of
+ // the same class. Callers keep the context valid until the mutex is unlocked.
+ let ret = unsafe { bindings::ww_mutex_trylock(mutex.inner.get(), ctx_ptr) };
+
+ if ret != 0 {
+ Some(MutexGuard::new(mutex))
+ } else {
+ None
+ }
+}
+
+#[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)?;
+
+ 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).ok_or(EBUSY)?;
+ assert_eq!(*guard, 123);
+
+ // Now it should fail immediately as it's already locked.
+ assert!(ctx.try_lock(&mutex).is_none());
+
+ drop(guard);
+ assert!(ctx.try_lock(&mutex).is_some());
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_ww_mutex_class_mismatch() -> Result {
+ let mutex = Arc::pin_init(Mutex::new(123, &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let ctx = KBox::pin_init(AcquireCtx::new(&TEST_WAIT_DIE_CLASS), GFP_KERNEL)?;
+
+ assert_eq!(ctx.lock(&mutex).err(), Some(EINVAL));
+ assert_eq!(ctx.lock_interruptible(&mutex).err(), Some(EINVAL));
+ assert!(ctx.try_lock(&mutex).is_none());
+ assert!(!mutex.is_locked());
+
+ 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);
+
+ 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());
+ assert!(mutex.try_lock().is_none());
+
+ drop(guard);
+ assert!(!mutex.is_locked());
+
+ let guard = mutex.try_lock().ok_or(EBUSY)?;
+ assert_eq!(*guard, 100);
+ drop(guard);
+ assert!(!mutex.is_locked());
+
+ Ok(())
+ }
+
+ #[test]
+ fn test_guard_from_raw_unlocks_mutex() -> Result {
+ let mutex = Arc::pin_init(Mutex::new((), &TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
+ let guard = mutex.lock()?;
+
+ // Keep the mutex locked without leaving another guard responsible for unlocking it.
+ core::mem::forget(guard);
+
+ // SAFETY: `mutex` and its class remain valid for this test. The current task holds
+ // the mutex, and forgetting the previous guard lets this guard take over unlocking it.
+ let guard = unsafe { MutexGuard::from_raw(mutex.inner.get()) };
+ assert!(mutex.is_locked());
+
+ drop(guard);
+ assert!(!mutex.is_locked());
+
+ Ok(())
+ }
+}
diff --git a/rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs b/rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs
new file mode 100644
index 0000000000000..777d177f985ff
--- /dev/null
+++ b/rust/kernel/sync/lock/ww_mutex/acquire_ctx.rs
@@ -0,0 +1,179 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Provides [`AcquireCtx`] for managing multiple wound/wait
+//! mutexes from the same [`Class`].
+
+use super::{
+ lock,
+ lock_interruptible,
+ try_lock,
+ Class,
+ Mutex,
+ MutexGuard, //
+};
+use crate::{
+ bindings,
+ prelude::*,
+ types::Opaque, //
+};
+
+/// Groups multiple [`Mutex`]es for deadlock avoidance when acquired
+/// with the same [`Class`].
+///
+/// Dropping the context panics if any mutex acquired with it is still locked.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::{
+/// define_ww_class,
+/// sync::{
+/// lock::ww_mutex::{
+/// AcquireCtx,
+/// Class,
+/// Mutex, //
+/// },
+/// Arc,
+/// },
+/// };
+/// use pin_init::stack_pin_init;
+///
+/// define_ww_class!(SOME_WW_CLASS);
+///
+/// // Create mutexes.
+/// let mutex1 = Arc::pin_init(Mutex::new(1, &SOME_WW_CLASS), GFP_KERNEL)?;
+/// let mutex2 = Arc::pin_init(Mutex::new(2, &SOME_WW_CLASS), GFP_KERNEL)?;
+///
+/// // Create acquire context for deadlock avoidance.
+/// let ctx = KBox::pin_init(AcquireCtx::new(&SOME_WW_CLASS), GFP_KERNEL)?;
+///
+/// let guard1 = ctx.lock(&mutex1)?;
+/// let guard2 = ctx.lock(&mutex2)?;
+///
+/// // Mark acquisition phase as complete.
+/// ctx.done();
+///
+/// # Ok::<(), Error>(())
+/// ```
+#[pin_data(PinnedDrop)]
+#[repr(transparent)]
+pub struct AcquireCtx {
+ #[pin]
+ pub(super) inner: Opaque<bindings::ww_acquire_ctx>,
+}
+
+impl AcquireCtx {
+ /// Initializes a new [`AcquireCtx`] with the given [`Class`].
+ #[inline]
+ pub fn new(class: &'static 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 indefinitely.
+ unsafe { bindings::ww_acquire_init(slot, class_ptr) }
+ }),
+ })
+ }
+
+ /// Creates a [`AcquireCtx`] 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 an initialized
+ /// [`AcquireCtx`] belonging to the current task. The context must remain pinned
+ /// and valid for the lifetime `'a` and until all mutexes acquired with it are
+ /// unlocked, even if their guards are forgotten. It must not be finalized or
+ /// reinitialized during that time.
+ /// The associated `ww_class` must be valid indefinitely.
+ #[inline]
+ pub unsafe fn from_raw<'a>(ptr: *mut bindings::ww_acquire_ctx) -> &'a Self {
+ // SAFETY: By the safety contract, `ptr` is valid to construct `AcquireCtx`.
+ unsafe { &*ptr.cast() }
+ }
+
+ /// Marks the end of the acquire phase.
+ ///
+ /// Calling this function is optional. It is just useful to document
+ /// the code and clearly designated the acquire phase from actually
+ /// using the locked data structures.
+ ///
+ /// After calling this function, no more mutexes can be acquired with
+ /// this context.
+ #[inline]
+ pub fn done(&self) {
+ // SAFETY: `self.inner` contains a valid, initialized acquire context.
+ unsafe { bindings::ww_acquire_done(self.inner.get()) };
+ }
+
+ /// Locks the given [`Mutex`] on this [`AcquireCtx`].
+ #[inline]
+ pub fn lock<'a, T>(&'a self, mutex: &'a Mutex<T>) -> Result<MutexGuard<'a, T>> {
+ lock(mutex, Some(self))
+ }
+
+ /// Similar to [`Self::lock`], but can be interrupted by signals.
+ #[inline]
+ pub fn lock_interruptible<'a, T>(&'a self, mutex: &'a Mutex<T>) -> Result<MutexGuard<'a, T>> {
+ lock_interruptible(mutex, Some(self))
+ }
+
+ /// Tries to lock the [`Mutex`] on this [`AcquireCtx`] without blocking.
+ ///
+ /// Unlike [`Self::lock`], no deadlock handling is performed.
+ ///
+ /// Returns `None` if the mutex belongs to a different [`Class`] or could not
+ /// be acquired.
+ #[must_use = "if unused, the lock will be immediately unlocked"]
+ #[inline]
+ pub fn try_lock<'a, T>(&'a self, mutex: &'a Mutex<T>) -> Option<MutexGuard<'a, T>> {
+ try_lock(mutex, Some(self))
+ }
+
+ /// Checks the mutex's class and the acquired-lock count.
+ ///
+ /// Returns [`EINVAL`] if the [`Mutex`] has a different [`Class`] or
+ /// [`EOVERFLOW`] if the acquired-lock count cannot be incremented.
+ #[inline]
+ pub(super) fn validate_mutex<T: ?Sized>(&self, mutex: &Mutex<T>) -> Result {
+ let ctx_ptr = self.inner.get();
+ let mutex_ptr = mutex.inner.get();
+
+ // SAFETY: `ctx_ptr` is a valid pointer for the entire lifetime of `self`.
+ 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 };
+
+ // `self` and `mutex` must use the same class.
+ if ctx_class != mutex_class {
+ return Err(EINVAL);
+ }
+
+ // Check for overflow of the count checked by `AcquireCtx`'s destructor.
+ // SAFETY: `ctx_ptr` points to a valid context owned by the current task.
+ if unsafe { (*ctx_ptr).acquired } == u32::MAX {
+ return Err(EOVERFLOW);
+ }
+
+ Ok(())
+ }
+}
+
+#[pinned_drop]
+impl PinnedDrop for AcquireCtx {
+ #[inline]
+ fn drop(self: Pin<&mut Self>) {
+ let ptr = self.inner.get();
+ // SAFETY: `ptr` points to this initialized context, owned by the current task.
+ let acquired = unsafe { (*ptr).acquired };
+ assert_eq!(
+ acquired, 0,
+ "ww acquire context dropped with locked mutexes"
+ );
+
+ // SAFETY: The assertion above ensures that all acquired locks were released.
+ unsafe { bindings::ww_acquire_fini(ptr) };
+ }
+}
--
2.51.2
next prev parent reply other threads:[~2026-10-20 15:31 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-20 15:29 [PATCH v13 0/7] rust: add ww_mutex support Onur Özkan
2026-10-20 15:29 ` [PATCH v13 1/7] rust: add C wrappers for ww_mutex inline functions Onur Özkan
2026-10-20 15:29 ` [PATCH v13 2/7] ww_mutex: add ww_class field unconditionally Onur Özkan
2026-10-20 15:29 ` [PATCH v13 3/7] rust: implement Class for ww_class support Onur Özkan
2026-10-20 15:29 ` Onur Özkan [this message]
2026-10-20 15:29 ` [PATCH v13 5/7] rust: ww_mutex: add AcquireCtxBackoff Onur Özkan
2026-10-20 15:29 ` [PATCH v13 6/7] rust: ww_mutex: implement DynLockSet Onur Özkan
2026-10-20 15:29 ` [PATCH v13 7/7] MAINTAINERS: add Onur Özkan as WW MUTEX maintainer 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=20261020153112.299745-5-work@onurozkan.dev \
--to=work@onurozkan.dev \
--cc=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.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=thomas.hellstrom@linux.intel.com \
--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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.