mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Beata Michalska <beata.michalska@arm.com>
To: "Onur Özkan" <work@onurozkan.dev>
Cc: rust-for-linux@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,
	daniel.almeida@collabora.com, thomas.hellstrom@linux.intel.com,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v13 6/7] rust: ww_mutex: implement DynLockSet
Date: Wed, 11 Nov 2026 20:55:29 +0100	[thread overview]
Message-ID: <avTIMTuuFnxHhS74@arm.com> (raw)
In-Reply-To: <20261020153112.299745-7-work@onurozkan.dev>

Hi Onur,

On Tue, Oct 20, 2026 at 06:29:42PM +0300, Onur Özkan wrote:
> DynLockSet is a high-level and safe API built on top of ww_mutex
> for dynamically discovered locks. It keeps acquired locks held,
> handles repeated objects and provides access to their data.
> 
> 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.
> 
> Signed-off-by: Onur Özkan <work@onurozkan.dev>
> ---
>  rust/kernel/sync/lock/ww_mutex.rs             |   4 +
>  .../sync/lock/ww_mutex/acquire_ctx_backoff.rs | 138 ++++++-
>  .../kernel/sync/lock/ww_mutex/dyn_lock_set.rs | 351 ++++++++++++++++++
>  3 files changed, 489 insertions(+), 4 deletions(-)
>  create mode 100644 rust/kernel/sync/lock/ww_mutex/dyn_lock_set.rs
> 
> diff --git a/rust/kernel/sync/lock/ww_mutex.rs b/rust/kernel/sync/lock/ww_mutex.rs
> index 2249214959b1c..fea2603d1bd1f 100644
> --- a/rust/kernel/sync/lock/ww_mutex.rs
> +++ b/rust/kernel/sync/lock/ww_mutex.rs
> @@ -7,6 +7,8 @@
>  //! [`AcquireCtx`] to track ordering and ensure forward progress.
>  //!
>  //! [`ww_transaction`] provides automatic deadlock recovery for ordinary guards.
> +//! Use [`DynLockSet::ww_transaction`] for dynamically discovered locks and
> +//! separate acquisition and work callbacks.
>  //!
>  //! See srctree/Documentation/locking/ww-mutex-design.rst for more details.
>  
> @@ -28,10 +30,12 @@
>      AcquireCtxBackoff, //
>  };
>  pub use class::Class;
> +pub use dyn_lock_set::DynLockSet;
>  
>  mod acquire_ctx;
>  mod acquire_ctx_backoff;
>  mod class;
> +mod dyn_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/acquire_ctx_backoff.rs b/rust/kernel/sync/lock/ww_mutex/acquire_ctx_backoff.rs
> index 71ad1cba37192..c6d213f74635f 100644
> --- a/rust/kernel/sync/lock/ww_mutex/acquire_ctx_backoff.rs
> +++ b/rust/kernel/sync/lock/ww_mutex/acquire_ctx_backoff.rs
> @@ -8,13 +8,17 @@
>      Mutex,
>      MutexGuard, //
>  };
> -use crate::prelude::*;
> +use crate::{
> +    bindings,
> +    prelude::*, //
> +};
>  use core::cell::Cell;
>  
>  /// An [`AcquireCtx`] that remembers the contended mutex.
>  ///
>  /// After [`Self::lock`] returns [`EDEADLK`], release all guards and call
> -/// [`Self::backoff`] before retrying. [`ww_transaction`] handles this retry loop.
> +/// [`Self::backoff`] before retrying. [`ww_transaction`] and
> +/// [`super::DynLockSet::ww_transaction`] handle this retry loop.
>  ///
>  /// Mutexes must outlive this wrapper so they remain valid for retry.
>  ///
> @@ -109,11 +113,36 @@ pub fn done(&self) {
>      }
>  
>      #[inline]
> -    fn assert_no_locks_held(&self) {
> +    pub(super) fn take_contended_guard(&self) -> Option<MutexGuard<'_, ()>> {
> +        self.backoff_guard.take()
> +    }
> +
> +    #[inline]
> +    pub(super) fn assert_no_locks_held(&self) {
>          // SAFETY: This initialized context belongs to the current task.
>          let acquired = unsafe { (*self.ctx.inner.get()).acquired };
>          assert_eq!(acquired, 0, "ww transaction still holds caller-owned locks");
>      }
> +
> +    #[inline]
> +    pub(super) fn clear_backoff(&self) {
> +        drop(self.backoff_guard.take());
> +        self.contended.set(None);
> +    }
> +
> +    pub(super) fn restart(&self) {
> +        self.clear_backoff();
> +        self.assert_no_locks_held();
> +        let ptr = self.ctx.inner.get();
> +        // SAFETY: This wrapper exclusively borrows the initialized, pinned context.
> +        // No acquired locks remain, so it can be finalized and reinitialized with
> +        // its static class.
> +        unsafe {
> +            let class = (*ptr).ww_class;
> +            bindings::ww_acquire_fini(ptr);
> +            bindings::ww_acquire_init(ptr, class);
> +        }
> +    }
>  }
>  
>  impl Drop for AcquireCtxBackoff<'_, '_> {
> @@ -164,7 +193,7 @@ pub fn ww_transaction<'a, R>(
>      retry(&ctx, || transaction(&ctx))
>  }
>  
> -fn retry<R>(
> +pub(super) fn retry<R>(
>      ctx: &AcquireCtxBackoff<'_, '_>,
>      mut transaction: impl FnMut() -> Result<R>,
>  ) -> Result<R> {
> @@ -178,6 +207,7 @@ fn retry<R>(
>  
>  #[kunit_tests(rust_kernel_ww_transaction)]
>  mod tests {
> +    use super::super::DynLockSet;
>      use super::*;
>      use crate::define_ww_class;
>      use pin_init::stack_pin_init;
> @@ -280,4 +310,104 @@ fn test_deadlock_without_contention() -> Result {
>          assert!(!mutex.is_locked());
>          Ok(())
>      }
> +
> +    #[test]
> +    fn test_dynamic_retry() -> Result {
> +        stack_pin_init!(let first = Mutex::new(10, &TEST_TRANSACTION_CLASS));
> +        stack_pin_init!(let second = Mutex::new(20, &TEST_TRANSACTION_CLASS));
> +        stack_pin_init!(let acquire_ctx = AcquireCtx::new(&TEST_TRANSACTION_CLASS));
> +        let ctx = AcquireCtxBackoff::new(acquire_ctx.as_mut());
> +        let mut set = DynLockSet::new(&ctx);
> +        let mut attempts = 0;
> +        let mut identity = None;
> +
> +        let result = set.ww_transaction(
> +            |set| {
> +                attempts += 1;
> +                let ptr = ctx.ctx.inner.get();
> +                // SAFETY: The pinned context is initialized and used only by
> +                // this task throughout the transaction.
> +                let stamp = unsafe { (*ptr).stamp };
> +                if attempts == 1 {
> +                    identity = Some((ptr, stamp));
> +                    set.lock(&first)?;
> +                    // SAFETY: `second` is initialized, pinned, and outlives the transaction.
> +                    let mutex = unsafe { Mutex::from_raw(second.inner.get()) };
> +                    ctx.contended.set(Some(mutex));
> +                    return Err(EDEADLK);
> +                }
> +
> +                assert_eq!(attempts, 2);
> +                assert_eq!(identity, Some((ptr, stamp)));
> +                assert!(!first.is_locked());
> +                // Backoff makes the contended mutex available before lock().
> +                assert_eq!(set.with_locked(&second, |v| *v)?, 20);
> +                set.lock(&second)?;
> +                set.lock(&first)
> +            },
> +            |set| {
> +                set.with_locked(&first, |v| *v += 1)?;
> +                set.with_locked(&second, |v| {
> +                    *v += 2;
> +                    *v
> +                })
> +            },
> +        )?;
> +
> +        assert_eq!(result, 22);
> +        assert_eq!(attempts, 2);
> +        assert!(!first.is_locked());
> +        assert!(!second.is_locked());
> +        Ok(())
> +    }
> +
> +    #[test]
> +    fn test_dynamic_error_cleanup() -> Result {
> +        stack_pin_init!(let first = Mutex::new(1, &TEST_TRANSACTION_CLASS));
> +        stack_pin_init!(let contended = Mutex::new(2, &TEST_TRANSACTION_CLASS));
> +        stack_pin_init!(let acquire_ctx = AcquireCtx::new(&TEST_TRANSACTION_CLASS));
> +        let ctx = AcquireCtxBackoff::new(acquire_ctx.as_mut());
> +        let mut set = DynLockSet::new(&ctx);
> +
> +        for swallow in [false, true] {
> +            let mut work_ran = false;
> +            let result = set.ww_transaction(
> +                |set| {
> +                    set.lock(&first)?;
> +                    // A swallowed or mapped EDEADLK must not poison the next call.
> +                    // SAFETY: `contended` is initialized, pinned, and outlives the transaction.
> +                    let mutex = unsafe { Mutex::from_raw(contended.inner.get()) };
> +                    ctx.contended.set(Some(mutex));
> +                    assert_eq!(set.lock(&first), Err(EDEADLK));
> +                    if swallow {
> +                        Ok(())
> +                    } else {
> +                        Err(EINVAL)
> +                    }
> +                },
> +                |set| {
> +                    work_ran = true;
> +                    ctx.done();
> +                    set.with_locked(&first, |v| *v += 1)
> +                },
> +            );
> +
> +            assert_eq!(result, if swallow { Ok(()) } else { Err(EINVAL) });
> +            assert_eq!(work_ran, swallow);
> +            assert!(ctx.contended.get().is_none());
> +            assert!(!first.is_locked());
> +            assert!(!contended.is_locked());
> +
> +            set.ww_transaction(
> +                |set| {
> +                    set.lock(&contended)?;
> +                    set.lock(&first)
> +                },
> +                |set| set.with_locked(&contended, |v| *v += 1),
> +            )?;
> +            assert!(!first.is_locked());
> +            assert!(!contended.is_locked());
> +        }
> +        Ok(())
> +    }
>  }
> diff --git a/rust/kernel/sync/lock/ww_mutex/dyn_lock_set.rs b/rust/kernel/sync/lock/ww_mutex/dyn_lock_set.rs
> new file mode 100644
> index 0000000000000..d82da83839ffb
> --- /dev/null
> +++ b/rust/kernel/sync/lock/ww_mutex/dyn_lock_set.rs
> @@ -0,0 +1,351 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Wound/wait locking for a set of objects discovered at runtime.
> +//!
> +//! [`DynLockSet`] keeps locks held while the caller inspects objects and finds
> +//! more locks to acquire. It handles repeated mutexes and provides access to
> +//! their protected data. [`DynLockSet::ww_transaction`] handles deadlock retries
> +//! and runs the work callback once all locks are held.
> +
> +use super::{
> +    acquire_ctx_backoff::retry,
> +    AcquireCtxBackoff,
> +    Mutex,
> +    MutexGuard, //
> +};
> +use crate::{
> +    bindings,
> +    prelude::*,
> +    types::NotThreadSafe, //
> +};
> +use core::ptr::NonNull;
> +
> +/// Used by [`DynLockSet`] 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 {
> +    #[inline]
> +    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()) };
> +    }
> +}
> +
> +/// A tracked set of [`Mutex`] locks acquired with the same [`AcquireCtxBackoff`].
> +///
> +/// Keeps locks held across loops and helper calls, handles repeated mutexes and
> +/// provides access to their data through [`Self::with_locked`].
> +pub struct DynLockSet<'ctx, 'm> {
> +    ctx: &'ctx AcquireCtxBackoff<'ctx, 'm>,
> +    taken: KVec<RawGuard>,
> +}
> +
> +impl<'ctx, 'm> DynLockSet<'ctx, 'm> {
> +    /// Creates an empty set using the given acquire context.
> +    #[inline]
> +    pub fn new(ctx: &'ctx AcquireCtxBackoff<'ctx, 'm>) -> Self {
> +        Self {
> +            ctx,
> +            taken: KVec::new(),
> +        }
> +    }
> +
> +    /// Attempts to lock the given [`Mutex`] and stores a guard for it.
> +    ///
> +    /// Returns success if the mutex is already held by this set, unless deadlock
> +    /// recovery is pending.
> +    pub fn lock<T>(&mut self, mutex: &'m Mutex<T>) -> Result {
> +        match self.ctx.lock(mutex) {
> +            Ok(guard) => self.add_guard(guard),
> +            Err(e) if e == EALREADY => {
> +                // The context may hold a guard owned outside this set.
> +                if self
> +                    .taken
> +                    .iter()
> +                    .any(|guard| guard.mutex_ptr.as_ptr() == mutex.inner.get())
> +                {
> +                    Ok(())
> +                } else {
> +                    Err(e)
> +                }
> +            }
> +            Err(e) => Err(e),
> +        }
> +    }
> +
> +    /// Acquires a dynamic set, retrying on deadlock then runs `work` once.
> +    ///
> +    /// Each call releases existing locks and starts a new transaction. Retries keep
> +    /// the same ticket. The set retains its allocation across retries and calls.
> +    ///
> +    /// `work` runs once after acquisition and must not acquire more locks. Its errors
> +    /// are returned without retrying. All locks and pending backoff state are released
> +    /// on return. The callbacks must not change the set's context or nest transactions
> +    /// on that context.
> +    ///
> +    /// # Panics
> +    ///
> +    /// Panics if the context holds caller-owned locks outside this set at a transaction
> +    /// boundary, including forgotten guards.
> +    ///
> +    /// # Examples
> +    ///
> +    /// ```
> +    /// use kernel::{
> +    ///     define_ww_class,
> +    ///     prelude::*,
> +    ///     sync::lock::ww_mutex::{
> +    ///         AcquireCtx,
> +    ///         AcquireCtxBackoff,
> +    ///         DynLockSet,
> +    ///         Mutex, //
> +    ///     }, //
> +    /// };
> +    /// use pin_init::stack_pin_init;
> +    ///
> +    /// define_ww_class!(CLASS);
> +    ///
> +    /// stack_pin_init!(let first = Mutex::new(1, &CLASS));
> +    /// stack_pin_init!(let second = Mutex::new(2, &CLASS));
> +    ///
> +    /// stack_pin_init!(let acquire_ctx = AcquireCtx::new(&CLASS));
> +    /// let ctx = AcquireCtxBackoff::new(acquire_ctx.as_mut());
> +    /// let mut set = DynLockSet::new(&ctx);
> +    ///
> +    /// set.ww_transaction(
> +    ///     |set| {
> +    ///         set.lock(&first)?;
> +    ///
> +    ///         if set.with_locked(&first, |value| *value > 0)? {
> +    ///             set.lock(&second)?;
> +    ///         }
> +    ///
> +    ///         Ok(())
> +    ///     },
> +    ///     |set| set.with_locked(&first, |value| *value += 1),
> +    /// )?;
> +    /// # Ok::<(), Error>(())
> +    /// ```
> +    pub fn ww_transaction<R>(
> +        &mut self,
> +        mut acquire: impl FnMut(&mut Self) -> Result,
> +        work: impl FnOnce(&mut Self) -> Result<R>,
> +    ) -> Result<R> {
So I have been pondering a bit on that one, in a context of drm_exec and
potentially having a Rust version of drm_gpuvm_exec_lock & co, and I'm having
some doubts really. The `drm_exec` implementation, apart from locking and
reserving fences, also plays with the `drm_gem_object` reference counting.
The `drm_exec` pairs each tracked lock with a GEM reference and releases both
during backoff or cleanup. Proposed implementation makes it rather cumbersome
to drop the reference when unlocking the mutex on a rollback (or backoff in
this series terminology). I assume that would require some sort of extension
to the `DynLockSet` ? Or am I missing smth ?

---
BR
Beata

> +        let ctx = self.ctx;
> +        self.clear();
> +        ctx.restart();
> +
> +        let result = retry(ctx, || {
> +            if let Some(guard) = self.ctx.take_contended_guard() {
> +                self.add_guard(guard)?;
> +            }
> +
> +            let result = acquire(self);
> +            if result.is_err() {
> +                self.clear();
> +            }
> +
> +            result
> +        });
> +
> +        let result = result.and_then(|()| work(self));
> +
> +        self.clear();
> +        ctx.clear_backoff();
> +        ctx.assert_no_locks_held();
> +
> +        result
> +    }
> +
> +    fn add_guard<T: ?Sized>(&mut self, guard: MutexGuard<'ctx, T>) -> Result {
> +        let raw_guard = RawGuard {
> +            // SAFETY: The guard holds this mutex which remains valid for `'ctx`.
> +            mutex_ptr: unsafe { NonNull::new_unchecked(guard.mutex.inner.get()) },
> +            _not_send: NotThreadSafe,
> +        };
> +
> +        // Transfer unlocking to `raw_guard` before the fallible push so failure
> +        // drops only one guard and unlocks the mutex exactly once.
> +        core::mem::forget(guard);
> +        self.taken.push(raw_guard, GFP_KERNEL)?;
> +
> +        Ok(())
> +    }
> +
> +    /// Executes `access` with a mutable reference to the data behind [`Mutex`].
> +    ///
> +    /// Fails with [`EINVAL`] if the [`Mutex`] was not locked in this set.
> +    pub fn with_locked<T: Unpin, Y>(
> +        &mut self,
> +        mutex: &'m Mutex<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 set.
> +            Err(EINVAL)
> +        }
> +    }
> +
> +    /// Releases all locks in the set.
> +    #[inline]
> +    pub fn clear(&mut self) {
> +        // `Drop` implementation of the `RawGuard` takes care of the unlocking.
> +        self.taken.clear();
> +    }
> +}
> +
> +impl Drop for DynLockSet<'_, '_> {
> +    #[inline]
> +    fn drop(&mut self) {
> +        self.clear();
> +    }
> +}
> +
> +#[kunit_tests(rust_kernel_dyn_lock_set)]
> +mod tests {
> +    use super::super::AcquireCtx;
> +    use super::*;
> +    use crate::{
> +        define_wd_class,
> +        define_ww_class, //
> +    };
> +    use pin_init::stack_pin_init;
> +
> +    define_ww_class!(TEST_WOUND_WAIT_CLASS);
> +    define_wd_class!(TEST_WAIT_DIE_CLASS);
> +
> +    #[test]
> +    fn test_lock_and_clear() -> Result {
> +        stack_pin_init!(let mutex = Mutex::new(10, &TEST_WOUND_WAIT_CLASS));
> +        stack_pin_init!(let acquire_ctx = AcquireCtx::new(&TEST_WOUND_WAIT_CLASS));
> +        let ctx = AcquireCtxBackoff::new(acquire_ctx.as_mut());
> +        let mut set = DynLockSet::new(&ctx);
> +
> +        assert_eq!(set.taken.capacity(), 0);
> +
> +        set.lock(&mutex)?;
> +        set.lock(&mutex)?;
> +        assert_eq!(set.taken.len(), 1);
> +
> +        set.with_locked(&mutex, |v| *v += 7)?;
> +        assert_eq!(set.with_locked(&mutex, |v| *v)?, 17);
> +
> +        let capacity = set.taken.capacity();
> +        assert!(capacity > 0);
> +
> +        set.clear();
> +        assert!(!mutex.is_locked());
> +        assert_eq!(set.taken.len(), 0);
> +        assert_eq!(set.taken.capacity(), capacity);
> +
> +        Ok(())
> +    }
> +
> +    #[test]
> +    fn test_drop_unlocks() -> Result {
> +        stack_pin_init!(let first = Mutex::new(1, &TEST_WOUND_WAIT_CLASS));
> +        stack_pin_init!(let second = Mutex::new(2, &TEST_WOUND_WAIT_CLASS));
> +        stack_pin_init!(let acquire_ctx = AcquireCtx::new(&TEST_WOUND_WAIT_CLASS));
> +        let ctx = AcquireCtxBackoff::new(acquire_ctx.as_mut());
> +
> +        {
> +            let mut set = DynLockSet::new(&ctx);
> +
> +            set.lock(&first)?;
> +            set.lock(&second)?;
> +        }
> +
> +        assert!(!first.is_locked());
> +        assert!(!second.is_locked());
> +
> +        Ok(())
> +    }
> +
> +    #[test]
> +    fn test_context_reuse() -> Result {
> +        stack_pin_init!(let mutex = Mutex::new(5, &TEST_WOUND_WAIT_CLASS));
> +        let mut acquire_ctx = KBox::pin_init(AcquireCtx::new(&TEST_WOUND_WAIT_CLASS), GFP_KERNEL)?;
> +
> +        {
> +            let ctx = AcquireCtxBackoff::new(acquire_ctx.as_mut());
> +            let mut set = DynLockSet::new(&ctx);
> +
> +            set.ww_transaction(
> +                |set| set.lock(&mutex),
> +                |set| set.with_locked(&mutex, |value| *value += 1),
> +            )?;
> +
> +            assert!(!mutex.is_locked());
> +        }
> +
> +        let guard = acquire_ctx.lock(&mutex)?;
> +        assert_eq!(*guard, 6);
> +
> +        Ok(())
> +    }
> +
> +    #[test]
> +    fn test_reject_external_guard() -> Result {
> +        stack_pin_init!(let mutex = Mutex::new(5, &TEST_WOUND_WAIT_CLASS));
> +        stack_pin_init!(let acquire_ctx = AcquireCtx::new(&TEST_WOUND_WAIT_CLASS));
> +        let ctx = AcquireCtxBackoff::new(acquire_ctx.as_mut());
> +        let guard = ctx.lock(&mutex)?;
> +        let mut set = DynLockSet::new(&ctx);
> +
> +        assert_eq!(set.lock(&mutex), Err(EALREADY));
> +        assert_eq!(set.with_locked(&mutex, |v| *v), Err(EINVAL));
> +
> +        set.clear();
> +        assert!(mutex.is_locked());
> +        assert_eq!(*guard, 5);
> +
> +        drop(guard);
> +        assert!(!mutex.is_locked());
> +
> +        Ok(())
> +    }
> +
> +    #[test]
> +    fn test_access_without_lock() -> Result {
> +        stack_pin_init!(let mutex = Mutex::new(5, &TEST_WOUND_WAIT_CLASS));
> +        stack_pin_init!(let acquire_ctx = AcquireCtx::new(&TEST_WOUND_WAIT_CLASS));
> +        let ctx = AcquireCtxBackoff::new(acquire_ctx.as_mut());
> +        let mut set = DynLockSet::new(&ctx);
> +
> +        assert_eq!(set.with_locked(&mutex, |v| *v), Err(EINVAL));
> +
> +        Ok(())
> +    }
> +
> +    #[test]
> +    fn test_class_mismatch() -> Result {
> +        stack_pin_init!(let mutex = Mutex::new(5, &TEST_WOUND_WAIT_CLASS));
> +        stack_pin_init!(let acquire_ctx = AcquireCtx::new(&TEST_WAIT_DIE_CLASS));
> +        let ctx = AcquireCtxBackoff::new(acquire_ctx.as_mut());
> +        let mut set = DynLockSet::new(&ctx);
> +
> +        assert_eq!(set.lock(&mutex), Err(EINVAL));
> +        assert!(!mutex.is_locked());
> +
> +        Ok(())
> +    }
> +}
> -- 
> 2.51.2
> 
> 

  reply	other threads:[~2026-11-11 19:55 UTC|newest]

Thread overview: 9+ 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 ` [PATCH v13 4/7] rust: ww_mutex: add Mutex, AcquireCtx and MutexGuard Onur Özkan
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-11-11 19:55   ` Beata Michalska [this message]
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=avTIMTuuFnxHhS74@arm.com \
    --to=beata.michalska@arm.com \
    --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 \
    --cc=work@onurozkan.dev \
    /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®