Linux real-time development
 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: 11+ 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]
2026-10-15 19:36 ` [PATCH 6.18.y 0/6] Backport spin_lock_irq_disable() to 6.18.y Sasha Levin

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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox