All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 6.18.y 0/6] Backport spin_lock_irq_disable() to 6.18.y
@ 2026-10-15  8:16 Alice Ryhl
  2026-10-15  8:16 ` [PATCH 6.18.y 1/6] irq: Add refcounted local interrupt disabling Alice Ryhl
                   ` (5 more replies)
  0 siblings, 6 replies; 10+ messages in thread
From: Alice Ryhl @ 2026-10-15  8:16 UTC (permalink / raw)
  To: stable, Greg Kroah-Hartman, Sasha Levin, Boqun Feng,
	Peter Zijlstra, Gary Guo
  Cc: Boqun Feng, Lyude Paul, Ingo Molnar, Will Deacon, Waiman Long,
	Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
	Miguel Ojeda, Alex Gaynor, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	linux-rt-devel, rust-for-linux, Alice Ryhl

This series backports the new spin_lock_irq_disable() API, as well as
the new SpinLockIrq API for Rust to 6.18.y. This means that any upstream
code using these APIs can be backported cleanly to 6.18.y without
needing adjustment to work using the old APIs.

The first commit uses a different implementation than upstream commit
e901c1510e24 ("irq,spin_lock: Add counted interrupt
disabling/enabling"). In mainline, `local_interrupt_enable()` works by
carving `HARDIRQ_DISABLE_BITS` out of `preempt_count`, which requires
reworking the `preempt_count` layout and arch-specific accessors across
x86, arm64, and s390. Instead of backporting those invasive changes to
6.18.y, patch 1 implements the same refcounted interrupt disabling API
using a dedicated per-CPU nesting counter and saved irqflags word. This
is slightly less efficient, but is much less invasive. Patches 2-6 are
clean cherry-picks of the upstream patches.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
Alice Ryhl (1):
      irq: Add refcounted local interrupt disabling

Boqun Feng (1):
      rust: helper: Add spin_{un,}lock_irq_{enable,disable}() helpers

Lyude Paul (4):
      rust: Introduce interrupt module
      rust: sync: Use super::* in spinlock.rs
      rust: sync: Add SpinLockIrq
      rust: sync: Introduce SpinLockIrq::lock_with() and friends

 include/linux/interrupt_rc.h      |  26 +++
 include/linux/spinlock.h          |  61 +++++++
 kernel/softirq.c                  |  63 ++++++++
 rust/helpers/helpers.c            |   1 +
 rust/helpers/interrupt.c          |  13 ++
 rust/helpers/spinlock.c           |  15 ++
 rust/helpers/sync.c               |   5 +
 rust/kernel/interrupt.rs          |  89 +++++++++++
 rust/kernel/lib.rs                |   1 +
 rust/kernel/sync.rs               |   9 +-
 rust/kernel/sync/lock/global.rs   |   3 +
 rust/kernel/sync/lock/spinlock.rs | 329 +++++++++++++++++++++++++++++++++++++-
 12 files changed, 609 insertions(+), 6 deletions(-)
---
base-commit: 17ad2612c11a748b50d32eefc681344b26fea946
change-id: 20261013-spinlock-irq-6-18-9720cdb14be6

Best regards,
-- 
Alice Ryhl <aliceryhl@google.com>


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 6.18.y 1/6] irq: Add refcounted local interrupt disabling
  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 ` Alice Ryhl
  2026-10-15  8:16 ` [PATCH 6.18.y 2/6] rust: Introduce interrupt module Alice Ryhl
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Alice Ryhl @ 2026-10-15  8:16 UTC (permalink / raw)
  To: stable, Greg Kroah-Hartman, Sasha Levin, Boqun Feng,
	Peter Zijlstra, Gary Guo
  Cc: Boqun Feng, Lyude Paul, Ingo Molnar, Will Deacon, Waiman Long,
	Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
	Miguel Ojeda, Alex Gaynor, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	linux-rt-devel, rust-for-linux, Alice Ryhl

Rust's SpinLockIrq abstraction requires refcounted local interrupt
disabling so that nested critical sections can be released in any order.
Upstream commit e901c1510e24 ("irq,spin_lock: Add counted interrupt
disabling/enabling") implements this by carving HARDIRQ_DISABLE_BITS out
of preempt_count, which requires invasive architecture-specific changes
across x86, arm64, and s390. Thus, for 6.18, implement the same API
using a dedicated per-CPU nesting count and saved irqflags word instead.

Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 include/linux/interrupt_rc.h | 26 ++++++++++++++++++
 include/linux/spinlock.h     | 61 ++++++++++++++++++++++++++++++++++++++++++
 kernel/softirq.c             | 63 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 150 insertions(+)

diff --git a/include/linux/interrupt_rc.h b/include/linux/interrupt_rc.h
new file mode 100644
index 000000000000..64564bfd715a
--- /dev/null
+++ b/include/linux/interrupt_rc.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_INTERRUPT_RC_H
+#define __LINUX_INTERRUPT_RC_H
+
+/*
+ * Refcounted local processor interrupt management.
+ *
+ * local_interrupt_disable() disables interrupts on the local CPU and may be
+ * nested; interrupts are only restored once the outermost
+ * local_interrupt_enable() runs.
+ *
+ * Unlike local_irq_save()/local_irq_restore(), the saved interrupt state is
+ * held in per-CPU state rather than by the caller, so the regions do not have
+ * to be released in acquisition order. That property is what allows Rust's
+ * SpinLockIrq guards - which are ordinary values and may be dropped in any
+ * order - to be built on top of this.
+ *
+ * Since the implementation depends on local_irq_save()/local_irq_restore(),
+ * this lives in its own header to make it easier to include without hitting
+ * circular header dependencies.
+ */
+
+void local_interrupt_disable(void);
+void local_interrupt_enable(void);
+
+#endif /* !__LINUX_INTERRUPT_RC_H */
diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h
index d3561c4a080e..bb067c652fbd 100644
--- a/include/linux/spinlock.h
+++ b/include/linux/spinlock.h
@@ -57,6 +57,7 @@
 #include <linux/linkage.h>
 #include <linux/compiler.h>
 #include <linux/irqflags.h>
+#include <linux/interrupt_rc.h>
 #include <linux/thread_info.h>
 #include <linux/stringify.h>
 #include <linux/bottom_half.h>
@@ -455,6 +456,66 @@ static __always_inline int spin_is_contended(spinlock_t *lock)
 # include <linux/spinlock_rt.h>
 #endif /* CONFIG_PREEMPT_RT */
 
+/*
+ * spin_lock_irq_disable() and friends behave like spin_lock_irq(), except that
+ * the interrupt disabling is refcounted (see <linux/interrupt_rc.h>).
+ *
+ * Two differences from spin_lock_irq() follow from that:
+ *
+ *  - Nesting works: an inner critical section does not re-enable interrupts on
+ *    exit while an outer one is still held.
+ *  - If interrupts were already disabled on entry to the outermost section,
+ *    they stay disabled on exit, as with spin_lock_irqsave().
+ *
+ * Unlike spin_lock_irqsave() the saved state lives in per-CPU state rather
+ * than in a caller-provided variable, so sections need not be released in
+ * acquisition order. These exist for Rust's SpinLockIrq, whose guards are
+ * ordinary values and may be dropped in any order.
+ */
+#ifndef CONFIG_PREEMPT_RT
+static __always_inline void spin_lock_irq_disable(spinlock_t *lock)
+{
+	local_interrupt_disable();
+	spin_lock(lock);
+}
+
+static __always_inline void spin_unlock_irq_enable(spinlock_t *lock)
+{
+	spin_unlock(lock);
+	local_interrupt_enable();
+}
+
+static __always_inline int spin_trylock_irq_disable(spinlock_t *lock)
+{
+	local_interrupt_disable();
+
+	if (spin_trylock(lock))
+		return 1;
+
+	local_interrupt_enable();
+	return 0;
+}
+#else /* CONFIG_PREEMPT_RT */
+/*
+ * On PREEMPT_RT spinlock_t is a sleeping lock and spin_lock_irq() does not
+ * disable interrupts at all; mirror that behaviour here.
+ */
+static __always_inline void spin_lock_irq_disable(spinlock_t *lock)
+{
+	spin_lock(lock);
+}
+
+static __always_inline void spin_unlock_irq_enable(spinlock_t *lock)
+{
+	spin_unlock(lock);
+}
+
+static __always_inline int spin_trylock_irq_disable(spinlock_t *lock)
+{
+	return spin_trylock(lock);
+}
+#endif /* CONFIG_PREEMPT_RT */
+
 /*
  * Does a critical section need to be broken due to another
  * task waiting?: (technically does not depend on CONFIG_PREEMPTION,
diff --git a/kernel/softirq.c b/kernel/softirq.c
index 77198911b8dd..3d17cf5ff790 100644
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -12,6 +12,7 @@
 #include <linux/export.h>
 #include <linux/kernel_stat.h>
 #include <linux/interrupt.h>
+#include <linux/interrupt_rc.h>
 #include <linux/init.h>
 #include <linux/local_lock.h>
 #include <linux/mm.h>
@@ -88,6 +89,68 @@ EXPORT_PER_CPU_SYMBOL_GPL(hardirqs_enabled);
 EXPORT_PER_CPU_SYMBOL_GPL(hardirq_context);
 #endif
 
+/*
+ * Refcounted local interrupt disabling; see <linux/interrupt_rc.h>.
+ *
+ * @local_interrupt_disable_count is the nesting depth on this CPU and
+ * @local_interrupt_disable_flags holds the irqflags captured by the outermost
+ * local_interrupt_disable().
+ *
+ * Both are only ever accessed by the owning CPU with interrupts disabled, so
+ * no atomics are needed: local_interrupt_disable() disables interrupts before
+ * touching the count, and a non-zero count implies interrupts are disabled.
+ */
+static DEFINE_PER_CPU(unsigned int, local_interrupt_disable_count);
+static DEFINE_PER_CPU(unsigned long, local_interrupt_disable_flags);
+
+void local_interrupt_disable(void)
+{
+	unsigned int count;
+	unsigned long flags;
+
+	WARN_ON_ONCE(in_nmi());
+
+	/*
+	 * Disable interrupts before touching the count, so that an interrupt
+	 * handler on this CPU cannot observe or modify a half-updated state.
+	 */
+	local_irq_save(flags);
+
+	count = raw_cpu_read(local_interrupt_disable_count);
+	if (count == 0) {
+		preempt_disable();
+		raw_cpu_write(local_interrupt_disable_flags, flags);
+	}
+
+	raw_cpu_write(local_interrupt_disable_count, count + 1);
+}
+EXPORT_SYMBOL_GPL(local_interrupt_disable);
+
+void local_interrupt_enable(void)
+{
+	unsigned int count = raw_cpu_read(local_interrupt_disable_count);
+
+	if (WARN_ON_ONCE(count == 0))
+		return;
+
+	count--;
+	raw_cpu_write(local_interrupt_disable_count, count);
+
+	if (count == 0) {
+		unsigned long flags = raw_cpu_read(local_interrupt_disable_flags);
+
+		/*
+		 * Restore rather than unconditionally enable: the outermost
+		 * local_interrupt_disable() may itself have run with
+		 * interrupts already disabled, in which case they must stay
+		 * disabled here.
+		 */
+		local_irq_restore(flags);
+		preempt_enable();
+	}
+}
+EXPORT_SYMBOL_GPL(local_interrupt_enable);
+
 /*
  * SOFTIRQ_OFFSET usage:
  *

-- 
2.56.0.385.gd3acb90ef8-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6.18.y 2/6] rust: Introduce interrupt module
  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 ` 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
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 10+ messages in thread
From: Alice Ryhl @ 2026-10-15  8:16 UTC (permalink / raw)
  To: stable, Greg Kroah-Hartman, Sasha Levin, Boqun Feng,
	Peter Zijlstra, Gary Guo
  Cc: Boqun Feng, Lyude Paul, Ingo Molnar, Will Deacon, Waiman Long,
	Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
	Miguel Ojeda, Alex Gaynor, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	linux-rt-devel, rust-for-linux, Alice Ryhl

From: Lyude Paul <lyude@redhat.com>

commit c1f0451ec748f7a58a4e50bcb8f75bd89718be78 upstream.

This introduces a module for dealing with interrupt-disabled contexts,
including the ability to enable and disable interrupts along with the
ability to annotate functions as expecting that IRQs are already
disabled on the local CPU.

Signed-off-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Boqun Feng <boqun@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Benno Lossin <lossin@kernel.org>
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Link: https://patch.msgid.link/20260807070218.27144-15-boqun@kernel.org
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 rust/helpers/helpers.c   |  1 +
 rust/helpers/interrupt.c | 13 +++++++
 rust/helpers/sync.c      |  5 +++
 rust/kernel/interrupt.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs       |  1 +
 5 files changed, 109 insertions(+)

diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 551da6c9b506..01ffade6c038 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -29,6 +29,7 @@
 #include "err.c"
 #include "irq.c"
 #include "fs.c"
+#include "interrupt.c"
 #include "io.c"
 #include "jump_label.c"
 #include "kunit.c"
diff --git a/rust/helpers/interrupt.c b/rust/helpers/interrupt.c
new file mode 100644
index 000000000000..69595498620f
--- /dev/null
+++ b/rust/helpers/interrupt.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/spinlock.h>
+
+__rust_helper void rust_helper_local_interrupt_disable(void)
+{
+	local_interrupt_disable();
+}
+
+__rust_helper void rust_helper_local_interrupt_enable(void)
+{
+	local_interrupt_enable();
+}
diff --git a/rust/helpers/sync.c b/rust/helpers/sync.c
index ff7e68b48810..3bc5930c39c5 100644
--- a/rust/helpers/sync.c
+++ b/rust/helpers/sync.c
@@ -11,3 +11,8 @@ void rust_helper_lockdep_unregister_key(struct lock_class_key *k)
 {
 	lockdep_unregister_key(k);
 }
+
+__rust_helper void rust_helper_lockdep_assert_irqs_disabled(void)
+{
+	lockdep_assert_irqs_disabled();
+}
diff --git a/rust/kernel/interrupt.rs b/rust/kernel/interrupt.rs
new file mode 100644
index 000000000000..a880ec3b8538
--- /dev/null
+++ b/rust/kernel/interrupt.rs
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Interrupt controls
+//!
+//! This module allows Rust code to annotate areas of code where local processor interrupts should
+//! be disabled, along with actually disabling local processor interrupts.
+//!
+//! # ⚠️ Warning! ⚠️
+//!
+//! The usage of this module can be more complicated than meets the eye, especially surrounding
+//! [preemptible kernels]. It's recommended to take care when using the functions and types defined
+//! here and familiarize yourself with the various documentation we have before using them, along
+//! with the various documents we link to here.
+//!
+//! # Reading material
+//!
+//! - [Software interrupts and realtime (LWN)](https://lwn.net/Articles/520076)
+//!
+//! [preemptible kernels]: https://www.kernel.org/doc/html/latest/locking/preempt-locking.html
+
+use crate::types::NotThreadSafe;
+
+/// A guard that represents local processor interrupt disablement on preemptible kernels.
+///
+/// [`LocalInterruptDisabled`] is a guard type that represents that local processor interrupts have
+/// been disabled on a preemptible kernel.
+///
+/// Certain functions take an immutable reference of [`LocalInterruptDisabled`] in order to require
+/// that they may only be run in local-interrupt-disabled contexts on preemptible kernels.
+///
+/// This is a marker type; it has no size, and is simply used as a compile-time guarantee that local
+/// processor interrupts are disabled on preemptible kernels. Note that no guarantees about the
+/// state of interrupts are made by this type on non-preemptible kernels.
+///
+/// # Invariants
+///
+/// Local processor interrupts are disabled on preemptible kernels for as long as an object of this
+/// type exists.
+pub struct LocalInterruptDisabled(NotThreadSafe);
+
+/// Disable local processor interrupts on a preemptible kernel.
+///
+/// This function disables local processor interrupts on a preemptible kernel, and returns a
+/// [`LocalInterruptDisabled`] token as proof of this. On non-preemptible kernels, this function is
+/// a no-op.
+///
+/// **Usage of this function is discouraged** unless you are absolutely sure you know what you are
+/// doing, as kernel interfaces for Rust that deal with interrupt state will typically handle local
+/// processor interrupt state management on their own and managing this by hand is quite error
+/// prone.
+#[inline]
+pub fn local_interrupt_disable() -> LocalInterruptDisabled {
+    // SAFETY: It's always safe to call `local_interrupt_disable()`.
+    unsafe { bindings::local_interrupt_disable() };
+
+    LocalInterruptDisabled(NotThreadSafe)
+}
+
+impl Drop for LocalInterruptDisabled {
+    #[inline]
+    fn drop(&mut self) {
+        // SAFETY: Per type invariants, a `local_interrupt_disable()` must be called to create this
+        // object, hence calling the corresponding `local_interrupt_enable()` is safe.
+        unsafe { bindings::local_interrupt_enable() };
+    }
+}
+
+impl LocalInterruptDisabled {
+    /// Assume that local processor interrupts are disabled on preemptible kernels.
+    ///
+    /// This can be used for annotating code that is known to be run in contexts where local
+    /// processor interrupts are disabled on preemptible kernels. It makes no changes to the local
+    /// interrupt state on its own.
+    ///
+    /// # Safety
+    ///
+    /// For the whole life `'a`, local interrupts must be disabled on preemptible kernels. This
+    /// could be a context like, for example, an interrupt handler.
+    #[inline]
+    pub unsafe fn assume_disabled<'a>() -> &'a LocalInterruptDisabled {
+        const ASSUME_DISABLED: &LocalInterruptDisabled = &LocalInterruptDisabled(NotThreadSafe);
+
+        // Confirm they're actually disabled if lockdep is available
+        // SAFETY: It's always safe to call `lockdep_assert_irqs_disabled()`.
+        unsafe { bindings::lockdep_assert_irqs_disabled() };
+
+        ASSUME_DISABLED
+    }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 3dd7bebe7888..75a466d9319b 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -96,6 +96,7 @@
 pub mod fs;
 pub mod id_pool;
 pub mod init;
+pub mod interrupt;
 pub mod io;
 pub mod ioctl;
 pub mod iov;

-- 
2.56.0.385.gd3acb90ef8-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6.18.y 3/6] rust: helper: Add spin_{un,}lock_irq_{enable,disable}() helpers
  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:16 ` Alice Ryhl
  2026-10-15  8:16 ` [PATCH 6.18.y 4/6] rust: sync: Use super::* in spinlock.rs Alice Ryhl
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 10+ messages in thread
From: Alice Ryhl @ 2026-10-15  8:16 UTC (permalink / raw)
  To: stable, Greg Kroah-Hartman, Sasha Levin, Boqun Feng,
	Peter Zijlstra, Gary Guo
  Cc: Boqun Feng, Lyude Paul, Ingo Molnar, Will Deacon, Waiman Long,
	Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
	Miguel Ojeda, Alex Gaynor, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	linux-rt-devel, rust-for-linux, Alice Ryhl

From: Boqun Feng <boqun@kernel.org>

commit 78c998b04ef53492f3502051349e55615681e00f upstream.

spin_lock_irq_disable() and spin_unlock_irq_enable() are inline
functions, to use them in Rust helpers are introduced. This is for
interrupt disabling lock abstraction in Rust.

Signed-off-by: Boqun Feng <boqun@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Andreas Hindborg <a.hindborg@kernel.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Link: https://patch.msgid.link/20260807070218.27144-16-boqun@kernel.org
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 rust/helpers/spinlock.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/rust/helpers/spinlock.c b/rust/helpers/spinlock.c
index 42c4bf01a23e..7929d59bca89 100644
--- a/rust/helpers/spinlock.c
+++ b/rust/helpers/spinlock.c
@@ -35,3 +35,18 @@ void rust_helper_spin_assert_is_held(spinlock_t *lock)
 {
 	lockdep_assert_held(lock);
 }
+
+__rust_helper void rust_helper_spin_lock_irq_disable(spinlock_t *lock)
+{
+	spin_lock_irq_disable(lock);
+}
+
+__rust_helper void rust_helper_spin_unlock_irq_enable(spinlock_t *lock)
+{
+	spin_unlock_irq_enable(lock);
+}
+
+__rust_helper int rust_helper_spin_trylock_irq_disable(spinlock_t *lock)
+{
+	return spin_trylock_irq_disable(lock);
+}

-- 
2.56.0.385.gd3acb90ef8-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6.18.y 4/6] rust: sync: Use super::* in spinlock.rs
  2026-10-15  8:16 [PATCH 6.18.y 0/6] Backport spin_lock_irq_disable() to 6.18.y Alice Ryhl
                   ` (2 preceding siblings ...)
  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 ` Alice Ryhl
  2026-10-15  8:16 ` [PATCH 6.18.y 5/6] rust: sync: Add SpinLockIrq Alice Ryhl
  2026-10-15  8:16 ` [PATCH 6.18.y 6/6] rust: sync: Introduce SpinLockIrq::lock_with() and friends Alice Ryhl
  5 siblings, 0 replies; 10+ messages in thread
From: Alice Ryhl @ 2026-10-15  8:16 UTC (permalink / raw)
  To: stable, Greg Kroah-Hartman, Sasha Levin, Boqun Feng,
	Peter Zijlstra, Gary Guo
  Cc: Boqun Feng, Lyude Paul, Ingo Molnar, Will Deacon, Waiman Long,
	Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
	Miguel Ojeda, Alex Gaynor, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	linux-rt-devel, rust-for-linux, Alice Ryhl

From: Lyude Paul <lyude@redhat.com>

commit df9165ab5254acb2035919c6e0511de799c1e8c1 upstream.

No functional changes.

Signed-off-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Boqun Feng <boqun@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20260807070218.27144-17-boqun@kernel.org
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 rust/kernel/sync/lock/spinlock.rs | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs
index d7be38ccbdc7..78432a598a45 100644
--- a/rust/kernel/sync/lock/spinlock.rs
+++ b/rust/kernel/sync/lock/spinlock.rs
@@ -3,6 +3,7 @@
 //! A kernel spinlock.
 //!
 //! This module allows Rust code to use the kernel's `spinlock_t`.
+use super::*;
 
 /// Creates a [`SpinLock`] initialiser with the given name and a newly-created lock class.
 ///
@@ -82,7 +83,7 @@ macro_rules! new_spinlock {
 /// ```
 ///
 /// [`spinlock_t`]: srctree/include/linux/spinlock.h
-pub type SpinLock<T> = super::Lock<T, SpinLockBackend>;
+pub type SpinLock<T> = Lock<T, SpinLockBackend>;
 
 /// A kernel `spinlock_t` lock backend.
 pub struct SpinLockBackend;
@@ -91,13 +92,11 @@ macro_rules! new_spinlock {
 ///
 /// This is simply a type alias for a [`Guard`] returned from locking a [`SpinLock`]. It will unlock
 /// the [`SpinLock`] upon being dropped.
-///
-/// [`Guard`]: super::Guard
-pub type SpinLockGuard<'a, T> = super::Guard<'a, T, SpinLockBackend>;
+pub type SpinLockGuard<'a, T> = Guard<'a, T, SpinLockBackend>;
 
 // SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion. `relock` uses the
 // default implementation that always calls the same locking method.
-unsafe impl super::Backend for SpinLockBackend {
+unsafe impl Backend for SpinLockBackend {
     type State = bindings::spinlock_t;
     type GuardState = ();
 

-- 
2.56.0.385.gd3acb90ef8-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6.18.y 5/6] rust: sync: Add SpinLockIrq
  2026-10-15  8:16 [PATCH 6.18.y 0/6] Backport spin_lock_irq_disable() to 6.18.y Alice Ryhl
                   ` (3 preceding siblings ...)
  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 ` 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
  5 siblings, 1 reply; 10+ messages in thread
From: Alice Ryhl @ 2026-10-15  8:16 UTC (permalink / raw)
  To: stable, Greg Kroah-Hartman, Sasha Levin, Boqun Feng,
	Peter Zijlstra, Gary Guo
  Cc: Boqun Feng, Lyude Paul, Ingo Molnar, Will Deacon, Waiman Long,
	Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
	Miguel Ojeda, Alex Gaynor, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	linux-rt-devel, rust-for-linux, Alice Ryhl

From: Lyude Paul <lyude@redhat.com>

commit 5967f4df55521de596cbe34e0c0c96e962ef3f2e upstream.

A variant of `SpinLock` that ensures interrupts are disabled in the
critical section. `lock()` will ensure that either interrupts are
already disabled or disable them. `unlock()` will reverse the respective
operation.

[Boqun: Port to use spin_lock_irq_disable() and
spin_unlock_irq_enable()]

Signed-off-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Boqun Feng <boqun@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Link: https://patch.msgid.link/20260807070218.27144-18-boqun@kernel.org
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 rust/kernel/sync.rs               |   9 +-
 rust/kernel/sync/lock/global.rs   |   3 +
 rust/kernel/sync/lock/spinlock.rs | 230 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 241 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
index cf5b638a097d..cf3c361bab15 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -26,7 +26,14 @@
 pub use condvar::{new_condvar, CondVar, CondVarTimeoutResult};
 pub use lock::global::{global_lock, GlobalGuard, GlobalLock, GlobalLockBackend, GlobalLockedBy};
 pub use lock::mutex::{new_mutex, Mutex, MutexGuard};
-pub use lock::spinlock::{new_spinlock, SpinLock, SpinLockGuard};
+pub use lock::spinlock::{
+    new_spinlock,
+    new_spinlock_irq,
+    SpinLock,
+    SpinLockGuard,
+    SpinLockIrq,
+    SpinLockIrqGuard, //
+};
 pub use locked_by::LockedBy;
 pub use refcount::Refcount;
 
diff --git a/rust/kernel/sync/lock/global.rs b/rust/kernel/sync/lock/global.rs
index d65f94b5caf2..47e200b750c1 100644
--- a/rust/kernel/sync/lock/global.rs
+++ b/rust/kernel/sync/lock/global.rs
@@ -299,4 +299,7 @@ macro_rules! global_lock_inner {
     (backend SpinLock) => {
         $crate::sync::lock::spinlock::SpinLockBackend
     };
+    (backend SpinLockIrq) => {
+        $crate::sync::lock::spinlock::SpinLockIrqBackend
+    };
 }
diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs
index 78432a598a45..e566f223c649 100644
--- a/rust/kernel/sync/lock/spinlock.rs
+++ b/rust/kernel/sync/lock/spinlock.rs
@@ -4,6 +4,7 @@
 //!
 //! This module allows Rust code to use the kernel's `spinlock_t`.
 use super::*;
+use crate::prelude::*;
 
 /// Creates a [`SpinLock`] initialiser with the given name and a newly-created lock class.
 ///
@@ -138,3 +139,232 @@ unsafe fn assert_is_held(ptr: *mut Self::State) {
         unsafe { bindings::spin_assert_is_held(ptr) }
     }
 }
+
+/// Creates a [`SpinLockIrq`] initialiser with the given name and a newly-created lock class.
+///
+/// It uses the name if one is given, otherwise it generates one based on the file name and line
+/// number.
+#[macro_export]
+macro_rules! new_spinlock_irq {
+    ($inner:expr $(, $name:literal)? $(,)?) => {
+        $crate::sync::SpinLockIrq::new(
+            $inner, $crate::optional_name!($($name)?), $crate::static_lock_class!())
+    };
+}
+pub use new_spinlock_irq;
+
+/// A variant of `SpinLock` that ensures interrupts are disabled in the critical section.
+///
+/// For more info on spinlocks, see [`SpinLock`]. For more information on interrupts,
+/// [see the interrupt module](kernel::interrupt).
+///
+/// # Examples
+///
+/// The following example shows how to declare, allocate initialise and access a struct (`Example`)
+/// that contains an inner struct (`Inner`) that is protected by a spinlock that requires local
+/// processor interrupts to be disabled.
+///
+/// ```
+/// use kernel::sync::{new_spinlock_irq, SpinLockIrq};
+///
+/// struct Inner {
+///     a: u32,
+///     b: u32,
+/// }
+///
+/// #[pin_data]
+/// struct Example {
+///     #[pin]
+///     c: SpinLockIrq<Inner>,
+///     #[pin]
+///     d: SpinLockIrq<Inner>,
+/// }
+///
+/// impl Example {
+///     fn new() -> impl PinInit<Self> {
+///         pin_init!(Self {
+///             c <- new_spinlock_irq!(Inner { a: 0, b: 10 }),
+///             d <- new_spinlock_irq!(Inner { a: 20, b: 30 }),
+///         })
+///     }
+/// }
+///
+/// // Allocate a boxed `Example`
+/// let e = KBox::pin_init(Example::new(), GFP_KERNEL)?;
+///
+/// // Accessing an `Example` from a context where interrupts may not be disabled already.
+/// let c_guard = e.c.lock(); // interrupts are disabled now, +1 interrupt disable refcount
+/// let d_guard = e.d.lock(); // no interrupt state change, +1 interrupt disable refcount
+///
+/// assert_eq!(c_guard.a, 0);
+/// assert_eq!(c_guard.b, 10);
+/// assert_eq!(d_guard.a, 20);
+/// assert_eq!(d_guard.b, 30);
+///
+/// drop(c_guard); // Dropping c_guard will not re-enable interrupts just yet, since d_guard is
+///                // still in scope.
+/// drop(d_guard); // Last interrupt disable reference dropped here, so interrupts are re-enabled
+///                // now
+/// # Ok::<(), Error>(())
+/// ```
+///
+/// [`lock()`]: SpinLockIrq::lock
+pub type SpinLockIrq<T> = super::Lock<T, SpinLockIrqBackend>;
+
+/// A kernel `spinlock_t` lock backend that can only be acquired in interrupt disabled contexts.
+pub struct SpinLockIrqBackend;
+
+/// A [`Guard`] acquired from locking a [`SpinLockIrq`] using [`lock()`].
+///
+/// This is simply a type alias for a [`Guard`] returned from locking a [`SpinLockIrq`] using
+/// [`lock()`]. It will unlock the [`SpinLockIrq`] and decrement the local processor's interrupt
+/// disablement refcount upon being dropped.
+///
+/// [`lock()`]: SpinLockIrq::lock
+pub type SpinLockIrqGuard<'a, T> = Guard<'a, T, SpinLockIrqBackend>;
+
+// SAFETY: The underlying kernel `spinlock_t` object ensures mutual exclusion. `relock` uses the
+// default implementation that always calls the same locking method.
+unsafe impl Backend for SpinLockIrqBackend {
+    type State = bindings::spinlock_t;
+    type GuardState = ();
+
+    #[inline]
+    unsafe fn init(
+        ptr: *mut Self::State,
+        name: *const crate::ffi::c_char,
+        key: *mut bindings::lock_class_key,
+    ) {
+        // SAFETY: The safety requirements ensure that `ptr` is valid for writes, and `name` and
+        // `key` are valid for read indefinitely.
+        unsafe { bindings::__spin_lock_init(ptr, name, key) }
+    }
+
+    #[inline]
+    unsafe fn lock(ptr: *mut Self::State) -> Self::GuardState {
+        // SAFETY: The safety requirements of this function ensure that `ptr` points to valid
+        // memory, and that it has been initialised before.
+        unsafe { bindings::spin_lock_irq_disable(ptr) }
+    }
+
+    #[inline]
+    unsafe fn unlock(ptr: *mut Self::State, _guard_state: &Self::GuardState) {
+        // SAFETY: The safety requirements of this function ensure that `ptr` is valid and that the
+        // caller is the owner of the spinlock.
+        unsafe { bindings::spin_unlock_irq_enable(ptr) }
+    }
+
+    #[inline]
+    unsafe fn try_lock(ptr: *mut Self::State) -> Option<Self::GuardState> {
+        // SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use.
+        let result = unsafe { bindings::spin_trylock_irq_disable(ptr) };
+
+        if result != 0 {
+            Some(())
+        } else {
+            None
+        }
+    }
+
+    #[inline]
+    unsafe fn assert_is_held(ptr: *mut Self::State) {
+        // SAFETY: The `ptr` pointer is guaranteed to be valid and initialized before use.
+        unsafe { bindings::spin_assert_is_held(ptr) }
+    }
+}
+
+#[kunit_tests(rust_spinlock_irq_condvar)]
+mod tests {
+    use super::*;
+    use crate::{
+        sync::*,
+        workqueue::{
+            self,
+            impl_has_work,
+            new_work,
+            Work,
+            WorkItem, //
+        },
+    };
+
+    struct TestState {
+        value: u32,
+        waiter_ready: bool,
+    }
+
+    #[pin_data]
+    struct Test {
+        #[pin]
+        state: SpinLockIrq<TestState>,
+
+        #[pin]
+        state_changed: CondVar,
+
+        #[pin]
+        waiter_state_changed: CondVar,
+
+        #[pin]
+        wait_work: Work<Self>,
+    }
+
+    impl_has_work! {
+        impl HasWork<Self> for Test { self.wait_work }
+    }
+
+    impl Test {
+        pub(crate) fn new() -> Result<Arc<Self>> {
+            Arc::try_pin_init(
+                try_pin_init!(
+                    Self {
+                        state <- new_spinlock_irq!(TestState {
+                            value: 1,
+                            waiter_ready: false
+                        }),
+                        state_changed <- new_condvar!(),
+                        waiter_state_changed <- new_condvar!(),
+                        wait_work <- new_work!("IrqCondvarTest::wait_work")
+                    }
+                ),
+                GFP_KERNEL,
+            )
+        }
+    }
+
+    impl WorkItem for Test {
+        type Pointer = Arc<Self>;
+
+        fn run(this: Arc<Self>) {
+            // Wait for the test to be ready to wait for us
+            let mut state = this.state.lock();
+
+            // Make sure the interrupts actually turned off
+            // SAFETY: It's always safe to call `lockdep_assert_irqs_disabled()`
+            unsafe { bindings::lockdep_assert_irqs_disabled() };
+
+            while !state.waiter_ready {
+                this.waiter_state_changed.wait(&mut state);
+            }
+
+            // Deliver the exciting value update our test has been waiting for
+            state.value += 1;
+            this.state_changed.notify_sync();
+        }
+    }
+
+    #[test]
+    fn spinlock_irq_condvar() -> Result {
+        let testdata = Test::new()?;
+
+        let _ = workqueue::system().enqueue(testdata.clone());
+
+        // Let the updater know when we're ready to wait
+        let mut state = testdata.state.lock();
+        state.waiter_ready = true;
+        testdata.waiter_state_changed.notify_sync();
+
+        // Wait for the exciting value update
+        testdata.state_changed.wait(&mut state);
+        assert_eq!(state.value, 2);
+        Ok(())
+    }
+}

-- 
2.56.0.385.gd3acb90ef8-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6.18.y 6/6] rust: sync: Introduce SpinLockIrq::lock_with() and friends
  2026-10-15  8:16 [PATCH 6.18.y 0/6] Backport spin_lock_irq_disable() to 6.18.y Alice Ryhl
                   ` (4 preceding siblings ...)
  2026-10-15  8:16 ` [PATCH 6.18.y 5/6] rust: sync: Add SpinLockIrq Alice Ryhl
@ 2026-10-15  8:16 ` Alice Ryhl
  2026-10-15  8:30   ` sashiko-bot
  5 siblings, 1 reply; 10+ messages in thread
From: Alice Ryhl @ 2026-10-15  8:16 UTC (permalink / raw)
  To: stable, Greg Kroah-Hartman, Sasha Levin, Boqun Feng,
	Peter Zijlstra, Gary Guo
  Cc: Boqun Feng, Lyude Paul, Ingo Molnar, Will Deacon, Waiman Long,
	Sebastian Andrzej Siewior, Clark Williams, Steven Rostedt,
	Miguel Ojeda, Alex Gaynor, Björn Roy Baron, Benno Lossin,
	Andreas Hindborg, Trevor Gross, Danilo Krummrich, linux-kernel,
	linux-rt-devel, rust-for-linux, Alice Ryhl

From: Lyude Paul <lyude@redhat.com>

commit ec4fad7c2bdc80c62fa73f799b77ad299f73dcc3 upstream.

`SpinLockIrq` and `SpinLock` use the exact same underlying C structure,
with the only real difference being that the former uses the
irq_disable() and irq_enable() variants for locking/unlocking. These
variants can introduce some minor overhead in contexts where we already
know that local processor interrupts are disabled, and as such we want a
way to be able to skip modifying processor interrupt state in said
contexts in order to avoid some overhead - just like the current C API
allows us to do.

In order to do this, we add some special functions for SpinLockIrq:
lock_with() and try_lock_with(), which allow acquiring the lock without
changing the interrupt state - as long as the caller can provide a
LocalInterruptDisabled reference to prove that local processor
interrupts have been disabled.

In some hacked-together benchmarks we ran, most of the time this did
actually seem to lead to a noticeable difference in overhead:

  From an aarch64 VM running on a MacBook M4:
    lock() when irq is disabled, 100 times cost Delta { nanos: 500 }
    lock_with() when irq is disabled, 100 times cost Delta { nanos: 292 }
    lock() when irq is enabled, 100 times cost Delta { nanos: 834 }

    lock() when irq is disabled, 100 times cost Delta { nanos: 459 }
    lock_with() when irq is disabled, 100 times cost Delta { nanos: 291 }
    lock() when irq is enabled, 100 times cost Delta { nanos: 709 }

  From an x86_64 VM (qemu/kvm) running on a i7-13700H
    lock() when irq is disabled, 100 times cost Delta { nanos: 1002 }
    lock_with() when irq is disabled, 100 times cost Delta { nanos: 729 }
    lock() when irq is enabled, 100 times cost Delta { nanos: 1516 }

    lock() when irq is disabled, 100 times cost Delta { nanos: 754 }
    lock_with() when irq is disabled, 100 times cost Delta { nanos: 966 }
    lock() when irq is enabled, 100 times cost Delta { nanos: 1227 }

    (note that there were some runs on x86_64 where lock() on irq
    disabled vs. lock_with() on irq disabled had equivalent benchmarks,
    but it very much appeared to be a minority of test runs.)

While it's not clear how this affects real-world workloads yet, let's
add this for the time being so we can find out.

This makes it so that a `SpinLockIrq` will work like a `SpinLock` if
interrupts are disabled. So a function:

        (&'a SpinLockIrq, &'a LocalInterruptDisabled) -> Guard<'a, .., SpinLockBackend>

makes sense. Note that due to `Guard` and `LocalInterruptDisabled`
having the same lifetime, interrupts cannot be enabled while the Guard
exists.

Signed-off-by: Lyude Paul <lyude@redhat.com>
Signed-off-by: Boqun Feng <boqun@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Reviewed-by: Gary Guo <gary@garyguo.net>
Link: https://patch.msgid.link/20260807070218.27144-19-boqun@kernel.org
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
 rust/kernel/sync/lock/spinlock.rs | 92 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 91 insertions(+), 1 deletion(-)

diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs
index e566f223c649..b3a0d7a3de04 100644
--- a/rust/kernel/sync/lock/spinlock.rs
+++ b/rust/kernel/sync/lock/spinlock.rs
@@ -4,7 +4,10 @@
 //!
 //! This module allows Rust code to use the kernel's `spinlock_t`.
 use super::*;
-use crate::prelude::*;
+use crate::{
+    interrupt::LocalInterruptDisabled,
+    prelude::*, //
+};
 
 /// Creates a [`SpinLock`] initialiser with the given name and a newly-created lock class.
 ///
@@ -155,6 +158,16 @@ macro_rules! new_spinlock_irq {
 
 /// A variant of `SpinLock` that ensures interrupts are disabled in the critical section.
 ///
+/// This lock can be acquired in two ways:
+///
+/// - Using [`lock()`] like any other type of lock, in which case the bindings will modify the
+///   interrupt state to ensure that local processor interrupts remain disabled for at least as
+///   long as the [`SpinLockIrqGuard`] exists.
+/// - Using [`lock_with()`] in contexts where a [`LocalInterruptDisabled`] token is present and
+///   local processor interrupts are already known to be disabled, in which case the local
+///   interrupt state will not be touched. This method should be preferred if a
+///   [`LocalInterruptDisabled`] token is present in the scope.
+///
 /// For more info on spinlocks, see [`SpinLock`]. For more information on interrupts,
 /// [see the interrupt module](kernel::interrupt).
 ///
@@ -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);
+/// }
+///
+/// # let e = KBox::pin_init(Example::new(), GFP_KERNEL)?;
+/// # let interrupt_guard = local_interrupt_disable();
+/// # noirq_work(&e, &interrupt_guard);
+/// #
+/// # Ok::<(), Error>(())
+/// ```
+///
 /// [`lock()`]: SpinLockIrq::lock
+/// [`lock_with()`]: SpinLockIrq::lock_with
 pub type SpinLockIrq<T> = super::Lock<T, SpinLockIrqBackend>;
 
 /// A kernel `spinlock_t` lock backend that can only be acquired in interrupt disabled contexts.
@@ -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()
+    }
+
+    /// 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()
+    }
+}
+
 #[kunit_tests(rust_spinlock_irq_condvar)]
 mod tests {
     use super::*;

-- 
2.56.0.385.gd3acb90ef8-goog


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 6.18.y 5/6] rust: sync: Add SpinLockIrq
  2026-10-15  8:16 ` [PATCH 6.18.y 5/6] rust: sync: Add SpinLockIrq Alice Ryhl
@ 2026-10-15  8:30   ` sashiko-bot
  0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-10-15  8:30 UTC (permalink / raw)
  To: Alice Ryhl; +Cc: linux-rt-devel, linux-kernel, ojeda, gary

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] workqueue: missing flush_work in spinlock_irq test causes module-unload UAF
- [Medium] rust: sync: lockdep_assert_irqs_disabled() in test fails on PREEMPT_RT
- [Low] rust: sync: missing loop around CondVar::wait in spinlock_irq_condvar test
- [Low] rust: sync: misleading SpinLockIrqBackend documentation
--

commit 53307f4fda1c118612a5cc4ab386ba1ddc8eda26
Author: Lyude Paul <lyude@redhat.com>

rust: sync: Add SpinLockIrq

A variant of `SpinLock` that ensures interrupts are disabled in the
critical section. `lock()` will ensure that either interrupts are
already disabled or disable them. `unlock()` will reverse the respective
operation.

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

> diff --git a/rust/kernel/sync/lock/spinlock.rs b/rust/kernel/sync/lock/spinlock.rs
> --- a/rust/kernel/sync/lock/spinlock.rs
> +++ b/rust/kernel/sync/lock/spinlock.rs
[ ... ]
> +/// A kernel `spinlock_t` lock backend that can only be acquired in interrupt disabled contexts.
> +pub struct SpinLockIrqBackend;

[Severity: Low]
Does this comment accurately reflect the backend's behavior?

The backend implementation calls bindings::spin_lock_irq_disable(ptr),
which disables interrupts itself if they aren't already disabled.

Could this doc string be updated to clarify that it disables interrupts,
rather than requiring them to be disabled beforehand?

[ ... ]
> +    impl WorkItem for Test {
> +        type Pointer = Arc<Self>;
> +
> +        fn run(this: Arc<Self>) {
> +            // Wait for the test to be ready to wait for us
> +            let mut state = this.state.lock();
> +
> +            // 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 test fail on preempt-rt configurations?

On preempt-rt, spin_lock_irq_disable() is implemented as a sleepable lock
and intentionally does not disable hardware interrupts. This means
bindings::lockdep_assert_irqs_disabled() will evaluate to true and trigger
a warning or panic depending on configuration.

Could this check be adjusted or guarded so the test doesn't unconditionally
fail on preempt-rt kernels?

[ ... ]
> +    #[test]
> +    fn spinlock_irq_condvar() -> Result {
> +        let testdata = Test::new()?;
> +
> +        let _ = workqueue::system().enqueue(testdata.clone());
> +
> +        // Let the updater know when we're ready to wait
> +        let mut state = testdata.state.lock();
> +        state.waiter_ready = true;
> +        testdata.waiter_state_changed.notify_sync();
> +
> +        // Wait for the exciting value update
> +        testdata.state_changed.wait(&mut state);

[Severity: Low]
Is this wait vulnerable to spurious wakeups?

Condition variables in the kernel waitqueue can wake up spuriously.
If a spurious wakeup occurs before the worker thread increments the value,
the test will proceed to the assert and panic since the value is still 1.

Should this wait be wrapped in a while loop checking state.value,
similar to how the worker thread checks state.waiter_ready?

> +        assert_eq!(state.value, 2);
> +        Ok(())
> +    }

[Severity: High]
Does this test leave background work running, potentially causing a
use-after-free during module unload?

The test enqueues a work item to workqueue::system() and waits for
state_changed.notify_sync(). Once notified, this main thread completes and
returns, which allows the kunit test to finish.

If the module is unloaded immediately after the test completes, the worker
thread executing Test::run() might still be finishing its final steps or
returning to the C workqueue executor. This would cause the kernel to
execute unmapped memory.

Should there be a flush_work() or similar synchronization barrier here
before returning from the test?

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

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 6.18.y 6/6] rust: sync: Introduce SpinLockIrq::lock_with() and friends
  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
  0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-10-15  8:30 UTC (permalink / raw)
  To: Alice Ryhl; +Cc: linux-rt-devel, linux-kernel, ojeda, gary

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

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH 6.18.y 2/6] rust: Introduce interrupt module
  2026-10-15  8:16 ` [PATCH 6.18.y 2/6] rust: Introduce interrupt module Alice Ryhl
@ 2026-10-15  8:31   ` sashiko-bot
  0 siblings, 0 replies; 10+ messages in thread
From: sashiko-bot @ 2026-10-15  8:31 UTC (permalink / raw)
  To: Alice Ryhl; +Cc: linux-rt-devel, linux-kernel, ojeda, gary

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Medium] rust: docs falsely claim local_interrupt_disable is no-op on !PREEMPT kernels
- [Medium] rust: interrupt: missing #[must_use] on LocalInterruptDisabled guard
- [Low] rust: missing // INVARIANT: comment for LocalInterruptDisabled
- [Low] rust: redundant FFI helper for out-of-line local_interrupt_disable()
--

commit d5f70a73a2c90b91e9114a20f641c084d5e70a96
Author: Lyude Paul <lyude@redhat.com>
rust: Introduce interrupt module

This introduces a module for dealing with interrupt-disabled contexts,
including the ability to enable and disable interrupts along with the
ability to annotate functions as expecting that IRQs are already
disabled on the local CPU.

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

> diff --git a/rust/helpers/interrupt.c b/rust/helpers/interrupt.c
> new file mode 100644
> index 0000000000000..69595498620f5
> --- /dev/null
> +++ b/rust/helpers/interrupt.c
> @@ -0,0 +1,13 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +#include <linux/spinlock.h>
> +
> +__rust_helper void rust_helper_local_interrupt_disable(void)
> +{
> +	local_interrupt_disable();
> +}
> +
> +__rust_helper void rust_helper_local_interrupt_enable(void)
> +{
> +	local_interrupt_enable();
> +}

[Severity: Low]
This isn't a bug, but are these C FFI wrappers necessary? According to
the subsystem rules, rust/helpers/ should only be used to export inline
functions or function macros.

Looking at kernel/softirq.c, local_interrupt_disable() and
local_interrupt_enable() are unconditionally implemented as out-of-line
functions across all configurations. Could we expose them directly via
bindgen by including <linux/interrupt_rc.h> in
rust/bindings/bindings_helper.h instead of adding redundant boilerplate?

[ ... ]

> diff --git a/rust/kernel/interrupt.rs b/rust/kernel/interrupt.rs
> new file mode 100644
> index 0000000000000..a880ec3b8538a
> --- /dev/null
> +++ b/rust/kernel/interrupt.rs

[ ... ]

> +/// # Invariants
> +///
> +/// Local processor interrupts are disabled on preemptible kernels for as long as an object of this
> +/// type exists.
> +pub struct LocalInterruptDisabled(NotThreadSafe);

[Severity: Medium]
Should this struct be marked with #[must_use]?

Because LocalInterruptDisabled implements Drop to call
local_interrupt_enable(), if a caller forgets to bind the returned guard to
a variable:

    local_interrupt_disable();

the guard will be dropped instantly at the semicolon. This would
unintentionally re-enable interrupts immediately, leaving the intended
critical section unprotected against interrupts.

> +
> +/// Disable local processor interrupts on a preemptible kernel.
> +///
> +/// This function disables local processor interrupts on a preemptible kernel, and returns a
> +/// [`LocalInterruptDisabled`] token as proof of this. On non-preemptible kernels, this function is
> +/// a no-op.

[Severity: Medium]
Does this accurately describe the behavior on non-preemptible kernels?

Looking at the C implementation in kernel/softirq.c:local_interrupt_disable(),
it unconditionally calls local_irq_save(flags). This always disables
hardware interrupts regardless of CONFIG_PREEMPT or CONFIG_PREEMPT_RT.

Could this documentation mislead developers into assuming the function
has zero cost on non-preemptible kernels, while it actually degrades
interrupt latency?

> +///
> +/// **Usage of this function is discouraged** unless you are absolutely sure you know what you are
> +/// doing, as kernel interfaces for Rust that deal with interrupt state will typically handle local
> +/// processor interrupt state management on their own and managing this by hand is quite error
> +/// prone.
> +#[inline]
> +pub fn local_interrupt_disable() -> LocalInterruptDisabled {
> +    // SAFETY: It's always safe to call `local_interrupt_disable()`.
> +    unsafe { bindings::local_interrupt_disable() };
> +
> +    LocalInterruptDisabled(NotThreadSafe)

[Severity: Low]
This isn't a bug, but should this construction have an // INVARIANT: comment?

The subsystem guidelines mandate that when a struct with an # Invariants
documentation section is constructed, the code should have an // INVARIANT:
comment explaining why the invariants are satisfied.

[ ... ]

> +impl LocalInterruptDisabled {
> +    /// Assume that local processor interrupts are disabled on preemptible kernels.
> +    ///
> +    /// This can be used for annotating code that is known to be run in contexts where local
> +    /// processor interrupts are disabled on preemptible kernels. It makes no changes to the local
> +    /// interrupt state on its own.
> +    ///
> +    /// # Safety
> +    ///
> +    /// For the whole life `'a`, local interrupts must be disabled on preemptible kernels. This
> +    /// could be a context like, for example, an interrupt handler.
> +    #[inline]
> +    pub unsafe fn assume_disabled<'a>() -> &'a LocalInterruptDisabled {
> +        const ASSUME_DISABLED: &LocalInterruptDisabled = &LocalInterruptDisabled(NotThreadSafe);

[Severity: Low]
Similarly, should this construction also include an // INVARIANT: comment?

> +
> +        // Confirm they're actually disabled if lockdep is available
> +        // SAFETY: It's always safe to call `lockdep_assert_irqs_disabled()`.
> +        unsafe { bindings::lockdep_assert_irqs_disabled() };
> +
> +        ASSUME_DISABLED
> +    }
> +}

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

^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-10-15  8:31 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 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.