[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250608-topics-tyr-request_irq-v4-3-81cb81fb8073@collabora.com>
Date: Sun, 08 Jun 2025 19:51:08 -0300
From: Daniel Almeida <daniel.almeida@...labora.com>
To: Miguel Ojeda <ojeda@...nel.org>, Alex Gaynor <alex.gaynor@...il.com>,
Boqun Feng <boqun.feng@...il.com>, Gary Guo <gary@...yguo.net>,
Björn Roy Baron <bjorn3_gh@...tonmail.com>,
Andreas Hindborg <a.hindborg@...nel.org>, Alice Ryhl <aliceryhl@...gle.com>,
Trevor Gross <tmgross@...ch.edu>, Danilo Krummrich <dakr@...nel.org>,
Greg Kroah-Hartman <gregkh@...uxfoundation.org>,
"Rafael J. Wysocki" <rafael@...nel.org>,
Thomas Gleixner <tglx@...utronix.de>, Benno Lossin <lossin@...nel.org>,
Bjorn Helgaas <bhelgaas@...gle.com>,
Krzysztof Wilczyński <kwilczynski@...nel.org>,
Benno Lossin <lossin@...nel.org>
Cc: linux-kernel@...r.kernel.org, rust-for-linux@...r.kernel.org,
linux-pci@...r.kernel.org, Daniel Almeida <daniel.almeida@...labora.com>
Subject: [PATCH v4 3/6] rust: irq: add support for non-threaded IRQs and
handlers
This patch adds support for non-threaded IRQs and handlers through
irq::Registration and the irq::Handler trait.
Registering an IRQ is defined as pub(crate) so that future patches can
define proper (safe) accessors for the IRQs exposed by a device. Said
accessors are bus-specific.
Signed-off-by: Daniel Almeida <daniel.almeida@...labora.com>
---
rust/bindings/bindings_helper.h | 1 +
rust/helpers/helpers.c | 1 +
rust/helpers/irq.c | 9 ++
rust/kernel/irq.rs | 5 +
rust/kernel/irq/request.rs | 259 ++++++++++++++++++++++++++++++++++++++++
5 files changed, 275 insertions(+)
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index bc494745f67b82e7a3a6f53055ece0fc3acf6e0d..32b95df509f1aec2d05035a1c49ec262d1ed7624 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -50,6 +50,7 @@
#include <linux/ethtool.h>
#include <linux/file.h>
#include <linux/firmware.h>
+#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/jiffies.h>
#include <linux/jump_label.h>
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 0f1b5d11598591bc62bb6439747211af164b76d6..25c927264835271f84d8c95d79f4ad6a381a7071 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -20,6 +20,7 @@
#include "dma.c"
#include "drm.c"
#include "err.c"
+#include "irq.c"
#include "fs.c"
#include "io.c"
#include "jump_label.c"
diff --git a/rust/helpers/irq.c b/rust/helpers/irq.c
new file mode 100644
index 0000000000000000000000000000000000000000..1faca428e2c047a656dec3171855c1508d67e60b
--- /dev/null
+++ b/rust/helpers/irq.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/interrupt.h>
+
+int rust_helper_request_irq(unsigned int irq, irq_handler_t handler,
+ unsigned long flags, const char *name, void *dev)
+{
+ return request_irq(irq, handler, flags, name, dev);
+}
diff --git a/rust/kernel/irq.rs b/rust/kernel/irq.rs
index 9abd9a6dc36f3e3ecc1f92ad7b0040176b56a079..650c9409a86ba25dfc2453cd10350f299de2450d 100644
--- a/rust/kernel/irq.rs
+++ b/rust/kernel/irq.rs
@@ -12,3 +12,8 @@
/// Flags to be used when registering IRQ handlers.
pub mod flags;
+
+/// IRQ allocation and handling.
+pub mod request;
+
+pub use request::{Handler, IrqReturn, Registration};
diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs
new file mode 100644
index 0000000000000000000000000000000000000000..e0bf8ca192e7b27c6dbfe611c3a6cc8df8153c35
--- /dev/null
+++ b/rust/kernel/irq/request.rs
@@ -0,0 +1,259 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright 2025 Collabora ltd.
+
+//! This module provides types like [`Registration`] and
+//! [`ThreadedRegistration`], which allow users to register handlers for a given
+//! IRQ line.
+
+use core::marker::PhantomPinned;
+
+use pin_init::pin_init_from_closure;
+
+use crate::alloc::Allocator;
+use crate::device::Bound;
+use crate::device::Device;
+use crate::devres::Devres;
+use crate::error::to_result;
+use crate::irq::flags::Flags;
+use crate::prelude::*;
+use crate::str::CStr;
+use crate::sync::Arc;
+
+/// The value that can be returned from an IrqHandler or a ThreadedIrqHandler.
+pub enum IrqReturn {
+ /// The interrupt was not from this device or was not handled.
+ None,
+
+ /// The interrupt was handled by this device.
+ Handled,
+}
+
+impl IrqReturn {
+ fn into_inner(self) -> u32 {
+ match self {
+ IrqReturn::None => bindings::irqreturn_IRQ_NONE,
+ IrqReturn::Handled => bindings::irqreturn_IRQ_HANDLED,
+ }
+ }
+}
+
+/// Callbacks for an IRQ handler.
+pub trait Handler: Sync {
+ /// The actual handler function. As usual, sleeps are not allowed in IRQ
+ /// context.
+ fn handle_irq(&self) -> IrqReturn;
+}
+
+impl<T: ?Sized + Handler + Send> Handler for Arc<T> {
+ fn handle_irq(&self) -> IrqReturn {
+ T::handle_irq(self)
+ }
+}
+
+impl<T: ?Sized + Handler, A: Allocator> Handler for Box<T, A> {
+ fn handle_irq(&self) -> IrqReturn {
+ T::handle_irq(self)
+ }
+}
+
+struct RegistrationInner {
+ irq: u32,
+ cookie: *mut kernel::ffi::c_void,
+}
+
+impl RegistrationInner {
+ fn synchronize(&self) {
+ // SAFETY:
+ // - `self.irq` is the same as the one passed to `request_{threaded}_irq`.
+ unsafe { bindings::synchronize_irq(self.irq) };
+ }
+}
+
+impl Drop for RegistrationInner {
+ fn drop(&mut self) {
+ // SAFETY:
+ // - `self.irq` is the same as the one passed to `request_{threaded}_irq`.
+ //
+ // - `cookie` was passed to `request_{threaded}_irq` as the cookie. It
+ // is guaranteed to be unique by the type system, since each call to
+ // `register` will return a different instance of `Registration`.
+ //
+ // - `&self` is `!Unpin` and was initializing using pin-init, so it
+ // occupied the same memory location for the entirety of its lifetime.
+ //
+ // Notice that this will block until all handlers finish executing,
+ // i.e.: at no point will &self be invalid while the handler is running.
+ unsafe { bindings::free_irq(self.irq, self.cookie) };
+ }
+}
+
+/// A registration of an IRQ handler for a given IRQ line.
+///
+/// # Examples
+///
+/// The following is an example of using `Registration`. It uses a
+/// [`AtomicU32`](core::sync::AtomicU32) to provide the interior mutability.
+///
+/// ```
+/// use core::sync::atomic::AtomicU32;
+/// use core::sync::atomic::Ordering;
+///
+/// use kernel::prelude::*;
+/// use kernel::device::Bound;
+/// use kernel::irq::flags;
+/// use kernel::irq::Registration;
+/// use kernel::irq::IrqReturn;
+/// use kernel::platform;
+/// use kernel::sync::Arc;
+/// use kernel::c_str;
+/// use kernel::alloc::flags::GFP_KERNEL;
+///
+/// // Declare a struct that will be passed in when the interrupt fires. The u32
+/// // merely serves as an example of some internal data.
+/// struct Data(AtomicU32);
+///
+/// // [`handle_irq`] takes &self. This example illustrates interior
+/// // mutability can be used when share the data between process context and IRQ
+/// // context.
+///
+/// type Handler = Data;
+///
+/// impl kernel::irq::request::Handler for Handler {
+/// // This is executing in IRQ context in some CPU. Other CPUs can still
+/// // try to access to data.
+/// fn handle_irq(&self) -> IrqReturn {
+/// self.0.fetch_add(1, Ordering::Relaxed);
+///
+/// IrqReturn::Handled
+/// }
+/// }
+///
+/// // This is running in process context.
+/// fn register_irq(handler: Handler, dev: &platform::Device<Bound>) -> Result<Arc<Registration<Handler>>> {
+/// let registration = dev.irq_by_index(0, flags::SHARED, c_str!("my-device"), handler)?;
+///
+/// // You can have as many references to the registration as you want, so
+/// // multiple parts of the driver can access it.
+/// let registration = Arc::pin_init(registration, GFP_KERNEL)?;
+///
+/// // The handler may be called immediately after the function above
+/// // returns, possibly in a different CPU.
+///
+/// {
+/// // The data can be accessed from the process context too.
+/// registration.handler().0.fetch_add(1, Ordering::Relaxed);
+/// }
+///
+/// Ok(registration)
+/// }
+///
+/// # Ok::<(), Error>(())
+///```
+///
+/// # Invariants
+///
+/// * We own an irq handler using `&self` as its private data.
+///
+#[pin_data]
+pub struct Registration<T: Handler + 'static> {
+ 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: Handler + 'static> Registration<T> {
+ /// Registers the IRQ handler with the system for the given IRQ number.
+ pub(crate) fn register<'a>(
+ dev: &'a Device<Bound>,
+ irq: u32,
+ flags: Flags,
+ name: &'static CStr,
+ handler: T,
+ ) -> impl PinInit<Self, Error> + 'a {
+ let closure = move |slot: *mut Self| {
+ // SAFETY: The slot passed to pin initializer is valid for writing.
+ unsafe {
+ slot.write(Self {
+ inner: Devres::new(
+ dev,
+ RegistrationInner {
+ irq,
+ cookie: slot.cast(),
+ },
+ GFP_KERNEL,
+ )?,
+ handler,
+ _pin: PhantomPinned,
+ })
+ };
+
+ // SAFETY:
+ // - The callbacks are valid for use with request_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.
+ let res = to_result(unsafe {
+ bindings::request_irq(
+ irq,
+ Some(handle_irq_callback::<T>),
+ flags.into_inner() as usize,
+ name.as_char_ptr(),
+ slot.cast(),
+ )
+ });
+
+ if res.is_err() {
+ // SAFETY: We are returning an error, so we can destroy the slot.
+ unsafe { core::ptr::drop_in_place(&raw mut (*slot).handler) };
+ }
+
+ res
+ };
+
+ // SAFETY:
+ // - if this returns Ok, then every field of `slot` is fully
+ // initialized.
+ // - if this returns an error, then the slot does not need to remain
+ // valid.
+ unsafe { pin_init_from_closure(closure) }
+ }
+
+ /// 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_irq`.
+unsafe extern "C" fn handle_irq_callback<T: Handler>(
+ _irq: i32,
+ ptr: *mut core::ffi::c_void,
+) -> core::ffi::c_uint {
+ // SAFETY: `ptr` is a pointer to Registration<T> set in `Registration::new`
+ let data = unsafe { &*(ptr as *const Registration<T>) };
+ T::handle_irq(&data.handler).into_inner()
+}
--
2.49.0
Powered by blists - more mailing lists