All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Alice Ryhl" <aliceryhl@google.com>
Cc: linux-rt-devel@lists.linux.dev, linux-kernel@vger.kernel.org,
	ojeda@kernel.org, gary@garyguo.net
Subject: Re: [PATCH 6.18.y 6/6] rust: sync: Introduce SpinLockIrq::lock_with() and friends
Date: Thu, 15 Oct 2026 08:30:49 +0000	[thread overview]
Message-ID: <sashiko-outbox-172495@kernel.org> (raw)
In-Reply-To: <20261015-spinlock-irq-6-18-v1-6-72c58975994e@google.com>

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] locking: lock_with() panics on PREEMPT_RT and deadlocks with CondVar
- [Medium] rust: sync: lockdep_assert_irqs_disabled() fails on PREEMPT_RT in kunit
--

commit 5ae395d5694f43cafc4f5157f39a1c85552be6b1
Author: Lyude Paul <lyude@redhat.com>

rust: sync: Introduce SpinLockIrq::lock_with() and friends

This commit adds lock_with() and try_lock_with() methods to SpinLockIrq.
These methods allow acquiring a spinlock without changing the interrupt
state by passing a LocalInterruptDisabled token. The goal is to avoid
overhead when it is already known that local interrupts are disabled.

Link: https://patch.msgid.link/20260807070218.27144-19-boqun@kernel.org

> diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs
> index e566f223c6494..b3a0d7a3de04c 100644
> --- a/rust/kernel/sync/lock/spinlock.rs
> +++ b/rust/kernel/sync/lock/spinlock.rs
> @@ -208,7 +221,47 @@ macro_rules! new_spinlock_irq {
>  /// # Ok::<(), Error>(())
>  /// ```
>  ///
> +/// The next example demonstrates locking a [`SpinLockIrq`] using [`lock_with()`] in a function
> +/// which can only be called when local processor interrupts are already disabled.
> +///
> +/// ```
> +/// use kernel::sync::{new_spinlock_irq, SpinLockIrq};
> +/// use kernel::interrupt::*;
> +///
> +/// struct Inner {
> +///     a: u32,
> +/// }
> +///
> +/// #[pin_data]
> +/// struct Example {
> +///     #[pin]
> +///     inner: SpinLockIrq<Inner>,
> +/// }
> +///
> +/// impl Example {
> +///     fn new() -> impl PinInit<Self> {
> +///         pin_init!(Self {
> +///             inner <- new_spinlock_irq!(Inner { a: 20 }),
> +///         })
> +///     }
> +/// }
> +///
> +/// // Accessing an `Example` from a function that can only be called in no-interrupt contexts.
> +/// fn noirq_work(e: &Example, interrupt_disabled: &LocalInterruptDisabled) {
> +///     // Because we know interrupts are disabled from interrupt_disable, we can skip toggling
> +///     // interrupt state using lock_with() and the provided token
> +///     assert_eq!(e.inner.lock_with(interrupt_disabled).a, 20);
> +/// }

[Severity: High]
Does this code demonstrate a pattern that will crash a preempt-rt kernel?

If the guard returned by lock_with() is passed to a CondVar, the condition
variable will drop the spinlock and sleep. Because the LocalInterruptDisabled
token remains in scope, the thread will invoke the scheduler with hard IRQs
physically disabled, causing a deadlock and panic.

[ ... ]

> @@ -273,6 +326,43 @@ unsafe fn assert_is_held(ptr: *mut Self::State) {
>      }
>  }
>  
> +impl<T: ?Sized> Lock<T, SpinLockIrqBackend> {
> +    /// Casts the lock as a `Lock<T, SpinLockBackend>`.
> +    #[inline]
> +    fn as_lock_in_interrupt<'a>(&'a self, _context: &'a LocalInterruptDisabled) -> &'a SpinLock<T> {
> +        // SAFETY:
> +        // - `Lock<T, SpinLockBackend>` and `Lock<T, SpinLockIrqBackend>` both have identical data
> +        //   layouts.
> +        // - As long as local interrupts are disabled (which is proven to be true by _context), it
> +        //   is safe to treat a lock with SpinLockIrqBackend as a SpinLockBackend lock.
> +        unsafe { core::mem::transmute(self) }
> +    }
> +
> +    /// Acquires the lock without modifying local interrupt state.
> +    ///
> +    /// This function should be used in place of the more expensive [`Lock::lock()`] function when
> +    /// possible for [`SpinLockIrq`] locks.
> +    #[inline]
> +    pub fn lock_with<'a>(&'a self, context: &'a LocalInterruptDisabled) -> SpinLockGuard<'a, T> {
> +        self.as_lock_in_interrupt(context).lock()
> +    }

[Severity: High]
Can acquiring a spinlock here while hardware interrupts are explicitly held
disabled by the provided context token violate wait-context rules?

On preempt-rt, spinlock_t is mapped to an rt_mutex which sleeps on contention.
Acquiring a sleeping lock while hardware interrupts are explicitly disabled
triggers a panic. Regular SpinLockIrq::lock() avoids this because
spin_lock_irq() conditionally omits hard IRQ disablement on RT.

> +
> +    /// Tries to acquire the lock without modifying local interrupt state.
> +    ///
> +    /// This function should be used in place of the more expensive [`Lock::try_lock()`] function
> +    /// when possible for [`SpinLockIrq`] locks.
> +    ///
> +    /// Returns a guard that can be used to access the data protected by the lock if successful.
> +    #[must_use = "if unused, the lock will be immediately unlocked"]
> +    #[inline]
> +    pub fn try_lock_with<'a>(
> +        &'a self,
> +        context: &'a LocalInterruptDisabled,
> +    ) -> Option<SpinLockGuard<'a, T>> {
> +        self.as_lock_in_interrupt(context).try_lock()
> +    }

[Severity: High]
Is it safe to try acquiring a sleepable spinlock_t with interrupts disabled,
or does this also trigger a wait-context violation on preempt-rt?


While reviewing the file, I also noticed an issue in the tests:

rust/kernel/sync/lock/spinlock.rs:WorkItem::run() {
    ...
        // Make sure the interrupts actually turned off
        // SAFETY: It's always safe to call `lockdep_assert_irqs_disabled()`
        unsafe { bindings::lockdep_assert_irqs_disabled() };
    ...
}

[Severity: Medium]
Will this lockdep assertion incorrectly assume that SpinLockIrq::lock() disables
hardware interrupts across all configurations?

On preempt-rt it does not, which triggers a lockdep warning when running the
rust_spinlock_irq_condvar kunit test.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261015-spinlock-irq-6-18-v1-0-72c58975994e@google.com?part=6

      reply	other threads:[~2026-10-15  8:30 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-15  8:16 [PATCH 6.18.y 0/6] Backport spin_lock_irq_disable() to 6.18.y Alice Ryhl
2026-10-15  8:16 ` [PATCH 6.18.y 1/6] irq: Add refcounted local interrupt disabling Alice Ryhl
2026-10-15  8:16 ` [PATCH 6.18.y 2/6] rust: Introduce interrupt module Alice Ryhl
2026-10-15  8:31   ` sashiko-bot
2026-10-15  8:16 ` [PATCH 6.18.y 3/6] rust: helper: Add spin_{un,}lock_irq_{enable,disable}() helpers Alice Ryhl
2026-10-15  8:16 ` [PATCH 6.18.y 4/6] rust: sync: Use super::* in spinlock.rs Alice Ryhl
2026-10-15  8:16 ` [PATCH 6.18.y 5/6] rust: sync: Add SpinLockIrq Alice Ryhl
2026-10-15  8:30   ` sashiko-bot
2026-10-15  8:16 ` [PATCH 6.18.y 6/6] rust: sync: Introduce SpinLockIrq::lock_with() and friends Alice Ryhl
2026-10-15  8:30   ` sashiko-bot [this message]

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=sashiko-outbox-172495@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=aliceryhl@google.com \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rt-devel@lists.linux.dev \
    --cc=ojeda@kernel.org \
    --cc=sashiko-reviews@lists.linux.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 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.