From: Daniel Almeida <daniel.almeida@collabora.com>
To: "Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"Rafael J. Wysocki" <rafael@kernel.org>,
"Thomas Gleixner" <tglx@linutronix.de>,
"Bjorn Helgaas" <bhelgaas@google.com>,
"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
"Benno Lossin" <lossin@kernel.org>
Cc: linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-pci@vger.kernel.org,
Joel Fernandes <joelagnelf@nvidia.com>,
Dirk Behme <dirk.behme@de.bosch.com>,
Daniel Almeida <daniel.almeida@collabora.com>
Subject: [PATCH v9 4/7] rust: irq: add support for threaded IRQs and handlers
Date: Mon, 11 Aug 2025 13:03:42 -0300 [thread overview]
Message-ID: <20250811-topics-tyr-request_irq2-v9-4-0485dcd9bcbf@collabora.com> (raw)
In-Reply-To: <20250811-topics-tyr-request_irq2-v9-0-0485dcd9bcbf@collabora.com>
This patch adds support for threaded IRQs and handlers through
irq::ThreadedRegistration and the irq::ThreadedHandler trait.
Threaded interrupts are more permissive in the sense that further
processing is possible in a kthread. This means that said execution takes
place outside of interrupt context, which is rather restrictive in many
ways.
Registering a threaded irq is dependent upon having an IrqRequest that
was previously allocated by a given device. This will be introduced in
subsequent patches.
Tested-by: Joel Fernandes <joelagnelf@nvidia.com>
Tested-by: Dirk Behme <dirk.behme@de.bosch.com>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
---
rust/kernel/irq.rs | 5 +-
rust/kernel/irq/request.rs | 228 ++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 230 insertions(+), 3 deletions(-)
diff --git a/rust/kernel/irq.rs b/rust/kernel/irq.rs
index c1019bc36ad1e7ae7dd3af8a8b5c14780bf70712..20abd40566559652eccd03f77ec873a16b6f48b0 100644
--- a/rust/kernel/irq.rs
+++ b/rust/kernel/irq.rs
@@ -18,4 +18,7 @@
pub use flags::Flags;
-pub use request::{Handler, IrqRequest, IrqReturn, Registration};
+pub use request::{
+ Handler, IrqRequest, IrqReturn, Registration, ThreadedHandler, ThreadedIrqReturn,
+ ThreadedRegistration,
+};
diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs
index 57e00ebf694d8e6e870d9ed57af7ee2ecf86ec05..4033df7d0dce1dbc9cc9b0b0f32fb9f8aa285d6b 100644
--- a/rust/kernel/irq/request.rs
+++ b/rust/kernel/irq/request.rs
@@ -1,8 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
// SPDX-FileCopyrightText: Copyright 2025 Collabora ltd.
-//! This module provides types like [`Registration`] which allow users to
-//! register handlers for a given IRQ line.
+//! This module provides types like [`Registration`] and
+//! [`ThreadedRegistration`], which allow users to register handlers for a given
+//! IRQ line.
use core::marker::PhantomPinned;
@@ -262,3 +263,226 @@ pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
let handler = unsafe { &*(ptr as *const T) };
T::handle(handler) as c_uint
}
+
+/// The value that can be returned from [`ThreadedHandler::handle`].
+#[repr(u32)]
+pub enum ThreadedIrqReturn {
+ /// The interrupt was not from this device or was not handled.
+ None = bindings::irqreturn_IRQ_NONE,
+
+ /// The interrupt was handled by this device.
+ Handled = bindings::irqreturn_IRQ_HANDLED,
+
+ /// The handler wants the handler thread to wake up.
+ WakeThread = bindings::irqreturn_IRQ_WAKE_THREAD,
+}
+
+/// Callbacks for a threaded IRQ handler.
+pub trait ThreadedHandler: Sync {
+ /// The hard IRQ handler.
+ ///
+ /// This is executed in interrupt context, hence all corresponding
+ /// limitations do apply. All work that does not necessarily need to be
+ /// executed from interrupt context, should be deferred to the threaded
+ /// handler, i.e. [`ThreadedHandler::handle_threaded`].
+ ///
+ /// The default implementation returns [`ThreadedIrqReturn::WakeThread`].
+ fn handle(&self) -> ThreadedIrqReturn {
+ ThreadedIrqReturn::WakeThread
+ }
+
+ /// The threaded IRQ handler.
+ ///
+ /// This is executed in process context. The kernel creates a dedicated
+ /// `kthread` for this purpose.
+ fn handle_threaded(&self) -> IrqReturn;
+}
+
+impl<T: ?Sized + ThreadedHandler + Send> ThreadedHandler for Arc<T> {
+ fn handle(&self) -> ThreadedIrqReturn {
+ T::handle(self)
+ }
+
+ fn handle_threaded(&self) -> IrqReturn {
+ T::handle_threaded(self)
+ }
+}
+
+impl<T: ?Sized + ThreadedHandler, A: Allocator> ThreadedHandler for Box<T, A> {
+ fn handle(&self) -> ThreadedIrqReturn {
+ T::handle(self)
+ }
+
+ fn handle_threaded(&self) -> IrqReturn {
+ T::handle_threaded(self)
+ }
+}
+
+/// A registration of a threaded IRQ handler for a given IRQ line.
+///
+/// Two callbacks are required: one to handle the IRQ, and one to handle any
+/// other work in a separate thread.
+///
+/// The thread handler is only called if the IRQ handler returns
+/// [`ThreadedIrqReturn::WakeThread`].
+///
+/// # Examples
+///
+/// The following is an example of using [`ThreadedRegistration`]. It uses a
+/// [`Mutex`](kernel::sync::Mutex) to provide interior mutability.
+///
+/// ```
+/// use kernel::c_str;
+/// use kernel::device::Bound;
+/// use kernel::irq::{
+/// self, Flags, IrqRequest, IrqReturn, ThreadedHandler, ThreadedIrqReturn,
+/// ThreadedRegistration,
+/// };
+/// use kernel::prelude::*;
+/// use kernel::sync::{Arc, Mutex};
+///
+/// // Declare a struct that will be passed in when the interrupt fires. The u32
+/// // merely serves as an example of some internal data.
+/// //
+/// // [`irq::ThreadedHandler::handle`] takes `&self`. This example
+/// // illustrates how interior mutability can be used when sharing the data
+/// // between process context and IRQ context.
+/// #[pin_data]
+/// struct Data {
+/// #[pin]
+/// value: Mutex<u32>,
+/// }
+///
+/// impl ThreadedHandler for Data {
+/// // This will run (in a separate kthread) if and only if
+/// // [`ThreadedHandler::handle`] returns [`WakeThread`], which it does by
+/// // default.
+/// fn handle_threaded(&self) -> IrqReturn {
+/// let mut data = self.value.lock();
+/// *data += 1;
+/// IrqReturn::Handled
+/// }
+/// }
+///
+/// // Registers a threaded IRQ handler for the given [`IrqRequest`].
+/// //
+/// // This is executing in process context and assumes that `request` was
+/// // previously acquired from a device.
+/// fn register_threaded_irq(
+/// handler: impl PinInit<Data, Error>,
+/// request: IrqRequest<'_>,
+/// ) -> Result<Arc<ThreadedRegistration<Data>>> {
+/// let registration =
+/// ThreadedRegistration::new(request, Flags::SHARED, c_str!("my_device"), handler);
+///
+/// let registration = Arc::pin_init(registration, GFP_KERNEL)?;
+///
+/// {
+/// // The data can be accessed from process context too.
+/// let mut data = registration.handler().value.lock();
+/// *data += 1;
+/// }
+///
+/// Ok(registration)
+/// }
+/// # Ok::<(), Error>(())
+/// ```
+///
+/// # Invariants
+///
+/// * We own an irq handler using `&T` as its private data.
+#[pin_data]
+pub struct ThreadedRegistration<T: ThreadedHandler + 'static> {
+ #[pin]
+ inner: Devres<RegistrationInner>,
+
+ #[pin]
+ handler: T,
+
+ /// Pinned because we need address stability so that we can pass a pointer
+ /// to the callback.
+ #[pin]
+ _pin: PhantomPinned,
+}
+
+impl<T: ThreadedHandler + 'static> ThreadedRegistration<T> {
+ /// Registers the IRQ handler with the system for the given IRQ number.
+ pub fn new<'a>(
+ request: IrqRequest<'a>,
+ flags: Flags,
+ name: &'static CStr,
+ handler: impl PinInit<T, Error> + 'a,
+ ) -> impl PinInit<Self, Error> + 'a {
+ try_pin_init!(&this in Self {
+ handler <- handler,
+ inner <- Devres::new(
+ request.dev,
+ try_pin_init!(RegistrationInner {
+ // SAFETY: `this` is a valid pointer to the `ThreadedRegistration` instance.
+ cookie: unsafe { &raw mut (*this.as_ptr()).handler }.cast(),
+ irq: {
+ // SAFETY:
+ // - The callbacks are valid for use with request_threaded_irq.
+ // - If this succeeds, the slot is guaranteed to be valid until the
+ // destructor of Self runs, which will deregister the callbacks
+ // before the memory location becomes invalid.
+ to_result(unsafe {
+ bindings::request_threaded_irq(
+ request.irq,
+ Some(handle_threaded_irq_callback::<T>),
+ Some(thread_fn_callback::<T>),
+ flags.into_inner(),
+ name.as_char_ptr(),
+ (&raw mut (*this.as_ptr()).handler).cast(),
+ )
+ })?;
+ request.irq
+ }
+ })
+ ),
+ _pin: PhantomPinned,
+ })
+ }
+
+ /// Returns a reference to the handler that was registered with the system.
+ pub fn handler(&self) -> &T {
+ &self.handler
+ }
+
+ /// Wait for pending IRQ handlers on other CPUs.
+ ///
+ /// This will attempt to access the inner [`Devres`] container.
+ pub fn try_synchronize(&self) -> Result {
+ let inner = self.inner.try_access().ok_or(ENODEV)?;
+ inner.synchronize();
+ Ok(())
+ }
+
+ /// Wait for pending IRQ handlers on other CPUs.
+ pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
+ let inner = self.inner.access(dev)?;
+ inner.synchronize();
+ Ok(())
+ }
+}
+
+/// # Safety
+///
+/// This function should be only used as the callback in `request_threaded_irq`.
+unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler>(
+ _irq: i32,
+ ptr: *mut c_void,
+) -> c_uint {
+ // SAFETY: `ptr` is a pointer to T set in `ThreadedRegistration::new`
+ let handler = unsafe { &*(ptr as *const T) };
+ T::handle(handler) as c_uint
+}
+
+/// # Safety
+///
+/// This function should be only used as the callback in `request_threaded_irq`.
+unsafe extern "C" fn thread_fn_callback<T: ThreadedHandler>(_irq: i32, ptr: *mut c_void) -> c_uint {
+ // SAFETY: `ptr` is a pointer to T set in `ThreadedRegistration::new`
+ let handler = unsafe { &*(ptr as *const T) };
+ T::handle_threaded(handler) as c_uint
+}
--
2.50.1
next prev parent reply other threads:[~2025-08-11 16:04 UTC|newest]
Thread overview: 27+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-11 16:03 [PATCH v9 0/7] rust: add support for request_irq Daniel Almeida
2025-08-11 16:03 ` [PATCH v9 1/7] rust: irq: add irq module Daniel Almeida
2025-08-15 12:02 ` Andreas Hindborg
2025-08-11 16:03 ` [PATCH v9 2/7] rust: irq: add flags module Daniel Almeida
2025-08-15 12:02 ` Andreas Hindborg
2025-08-15 12:21 ` Miguel Ojeda
2025-08-15 13:23 ` Daniel Almeida
2025-08-16 17:42 ` Miguel Ojeda
2025-08-11 16:03 ` [PATCH v9 3/7] rust: irq: add support for non-threaded IRQs and handlers Daniel Almeida
2025-08-18 8:14 ` Andreas Hindborg
2025-08-18 12:36 ` Daniel Almeida
2025-08-26 19:31 ` Daniel Almeida
2025-08-27 7:50 ` Andreas Hindborg
2025-08-27 7:55 ` Danilo Krummrich
2025-08-11 16:03 ` Daniel Almeida [this message]
2025-08-11 16:03 ` [PATCH v9 5/7] rust: platform: add irq accessors Daniel Almeida
2025-08-11 16:03 ` [PATCH v9 6/7] rust: pci: " Daniel Almeida
2025-08-11 16:03 ` [PATCH v9 7/7] rust: irq: add &Device<Bound> argument to irq callbacks Daniel Almeida
2025-08-11 17:00 ` Daniel Almeida
2025-08-11 17:11 ` Boqun Feng
2025-08-11 17:35 ` Danilo Krummrich
2025-08-11 17:30 ` Danilo Krummrich
2025-08-12 19:07 ` [PATCH v9 0/7] rust: add support for request_irq Danilo Krummrich
2025-08-12 19:17 ` Daniel Almeida
2025-08-12 19:21 ` Danilo Krummrich
2025-08-18 8:23 ` Andreas Hindborg
2025-08-18 8:27 ` Danilo Krummrich
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=20250811-topics-tyr-request_irq2-v9-4-0485dcd9bcbf@collabora.com \
--to=daniel.almeida@collabora.com \
--cc=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=bhelgaas@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=dakr@kernel.org \
--cc=dirk.behme@de.bosch.com \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=joelagnelf@nvidia.com \
--cc=kwilczynski@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=ojeda@kernel.org \
--cc=rafael@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tglx@linutronix.de \
--cc=tmgross@umich.edu \
/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;
as well as URLs for NNTP newsgroup(s).