From: Danilo Krummrich <dakr@kernel.org>
To: bhelgaas@google.com, dakr@kernel.org, kwilczynski@kernel.org,
aliceryhl@google.com, daniel.almeida@collabora.com,
ojeda@kernel.org, boqun@kernel.org, gary@garyguo.net,
bjorn3_gh@protonmail.com, lossin@kernel.org,
a.hindborg@kernel.org, tmgross@umich.edu, tamird@kernel.org,
acourbot@nvidia.com, work@onurozkan.dev, jhubbard@nvidia.com,
ttabi@nvidia.com, apopple@nvidia.com, ecourtney@nvidia.com,
shashanks@nvidia.com, zhiw@nvidia.com
Cc: driver-core@lists.linux.dev, linux-pci@vger.kernel.org,
rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH 1/5] rust: irq: add anchor generic to IrqRequest and Registration
Date: Tue, 11 Aug 2026 00:47:40 +0200 [thread overview]
Message-ID: <20260810224800.2314458-2-dakr@kernel.org> (raw)
In-Reply-To: <20260810224800.2314458-1-dakr@kernel.org>
Add an IrqRequestAnchor marker trait and a generic parameter A
(defaulting to ()) on IrqRequest, Registration, and
ThreadedRegistration.
Bus-specific IRQ sources can implement IrqRequestAnchor for a reference
to their allocation type, so that the Registration stores a real borrow
of the allocation and prevents it from being dropped while the handler
is live.
The default anchor () keeps all existing behavior unchanged; platform
devices are unaffected.
Add a new constructor IrqRequest::new_anchored() that accepts the anchor
value. The existing IrqRequest::new() continues to produce
IrqRequest<'a, ()>.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
rust/kernel/irq.rs | 10 ++++-
rust/kernel/irq/request.rs | 86 ++++++++++++++++++++++++++++----------
2 files changed, 71 insertions(+), 25 deletions(-)
diff --git a/rust/kernel/irq.rs b/rust/kernel/irq.rs
index 09ef1e7f853c..dd3cc6969ebf 100644
--- a/rust/kernel/irq.rs
+++ b/rust/kernel/irq.rs
@@ -19,6 +19,12 @@
pub use flags::Flags;
pub use request::{
- Handler, IrqRequest, IrqReturn, Registration, ThreadedHandler, ThreadedIrqReturn,
- ThreadedRegistration,
+ Handler,
+ IrqRequest,
+ IrqRequestAnchor,
+ IrqReturn,
+ Registration,
+ ThreadedHandler,
+ ThreadedIrqReturn,
+ ThreadedRegistration, //
};
diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs
index c1c6525a676a..760381a07816 100644
--- a/rust/kernel/irq/request.rs
+++ b/rust/kernel/irq/request.rs
@@ -44,17 +44,31 @@ pub trait Handler: Sync {
fn handle(&self) -> IrqReturn;
}
+/// Marker trait for the anchor stored in an [`IrqRequest`].
+///
+/// Bus-specific IRQ sources can implement this for a reference to their allocation type, so that
+/// the [`Registration`] that stores the request keeps the allocation alive. The default `()`
+/// imposes no constraint beyond the device lifetime.
+pub trait IrqRequestAnchor: Send + Sync {}
+
+impl IrqRequestAnchor for () {}
+
/// A request for an IRQ line for a given device.
///
+/// The anchor `A` is stored in the [`Registration`] built from this request. When `A` is a
+/// reference to a bus-specific allocation, the borrow prevents the allocation from being dropped
+/// before the handler is freed.
+///
/// # Invariants
///
/// - `ìrq` is the number of an interrupt source of `dev`.
/// - `irq` has not been registered yet; this is consumed by [`Registration::new()`].
-pub struct IrqRequest<'a> {
+pub struct IrqRequest<'a, A: IrqRequestAnchor = ()> {
irq: u32,
/// Proves the device is bound at registration time and ties `'a` to the device's bound
/// lifetime, ensuring the [`Registration`] cannot outlive it.
_dev: PhantomData<&'a Device<Bound>>,
+ anchor: A,
}
impl<'a> IrqRequest<'a> {
@@ -63,14 +77,34 @@ impl<'a> IrqRequest<'a> {
/// # Safety
///
/// - `irq` should be a valid IRQ number for `dev`.
+ #[inline]
pub(crate) unsafe fn new(_dev: &'a Device<Bound>, irq: u32) -> Self {
+ // SAFETY: Caller guarantees `irq` is valid for `dev`.
+ unsafe { Self::new_anchored(_dev, irq, ()) }
+ }
+}
+
+impl<'a, A: IrqRequestAnchor> IrqRequest<'a, A> {
+ /// Creates a new IRQ request with an anchor to a bus-specific resource allocation.
+ ///
+ /// # Safety
+ ///
+ /// `irq` must be a valid IRQ number for `dev`.
+ #[inline]
+ pub(crate) unsafe fn new_anchored(_dev: &'a Device<Bound>, irq: u32, anchor: A) -> Self {
// INVARIANT: `irq` is a valid IRQ number for `dev`.
IrqRequest {
irq,
_dev: PhantomData,
+ anchor,
}
}
+ /// Returns a reference to the anchor.
+ pub fn anchor(&self) -> &A {
+ &self.anchor
+ }
+
/// Returns the IRQ number of an [`IrqRequest`].
#[inline]
pub fn irq(&self) -> u32 {
@@ -154,8 +188,8 @@ pub fn irq(&self) -> u32 {
///
/// * We own an irq handler registered via `request_irq` whose cookie is a pointer to `Self`.
#[pin_data(PinnedDrop)]
-pub struct Registration<'a, T: Handler> {
- request: IrqRequest<'a>,
+pub struct Registration<'a, T: Handler, A: IrqRequestAnchor = ()> {
+ request: IrqRequest<'a, A>,
#[pin]
handler: T,
@@ -166,7 +200,7 @@ pub struct Registration<'a, T: Handler> {
_pin: PhantomPinned,
}
-impl<'a, T: Handler> Registration<'a, T> {
+impl<'a, T: Handler, A: IrqRequestAnchor + 'a> Registration<'a, T, A> {
/// Registers the IRQ handler with the system for the given IRQ number.
///
/// # Safety
@@ -174,7 +208,7 @@ impl<'a, T: Handler> Registration<'a, T> {
/// Callers must not `mem::forget()` the returned [`Registration`] or otherwise prevent its
/// [`Drop`] implementation from running.
pub unsafe fn new(
- request: IrqRequest<'a>,
+ request: IrqRequest<'a, A>,
flags: Flags,
name: &'static CStr,
handler: impl PinInit<T, Error> + 'a,
@@ -199,7 +233,7 @@ pub unsafe fn new(
to_result(unsafe {
bindings::request_irq(
request.irq,
- Some(handle_irq_callback::<T>),
+ Some(handle_irq_callback::<T, A>),
flags.into_inner(),
name.as_char_ptr(),
this.as_ptr().cast::<c_void>(),
@@ -223,7 +257,7 @@ pub fn synchronize(&self) {
}
#[pinned_drop]
-impl<T: Handler> PinnedDrop for Registration<'_, T> {
+impl<T: Handler, A: IrqRequestAnchor> PinnedDrop for Registration<'_, T, A> {
fn drop(self: Pin<&mut Self>) {
// SAFETY: The cookie was set to a pointer to `Self` in `Registration::new()`. This blocks
// until all in-flight handlers complete, so no references to `self` remain after this
@@ -240,9 +274,12 @@ fn drop(self: Pin<&mut Self>) {
/// # 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 c_void) -> c_uint {
- let ptr = ptr.cast_const().cast::<Registration<'_, T>>();
- // SAFETY: `ptr` is a pointer to `Registration<'_, T>` set in `Registration::new()`.
+unsafe extern "C" fn handle_irq_callback<T: Handler, A: IrqRequestAnchor>(
+ _irq: i32,
+ ptr: *mut c_void,
+) -> c_uint {
+ let ptr = ptr.cast_const().cast::<Registration<'_, T, A>>();
+ // SAFETY: `ptr` is a pointer to `Registration<'_, T, A>` set in `Registration::new()`.
let registration = unsafe { &*ptr };
T::handle(®istration.handler) as c_uint
@@ -371,8 +408,8 @@ fn handle(&self) -> ThreadedIrqReturn {
/// * We own an irq handler registered via `request_threaded_irq` whose cookie is a pointer to
/// `Self`.
#[pin_data(PinnedDrop)]
-pub struct ThreadedRegistration<'a, T: ThreadedHandler> {
- request: IrqRequest<'a>,
+pub struct ThreadedRegistration<'a, T: ThreadedHandler, A: IrqRequestAnchor = ()> {
+ request: IrqRequest<'a, A>,
#[pin]
handler: T,
@@ -383,7 +420,7 @@ pub struct ThreadedRegistration<'a, T: ThreadedHandler> {
_pin: PhantomPinned,
}
-impl<'a, T: ThreadedHandler> ThreadedRegistration<'a, T> {
+impl<'a, T: ThreadedHandler, A: IrqRequestAnchor + 'a> ThreadedRegistration<'a, T, A> {
/// Registers the IRQ handler with the system for the given IRQ number.
///
/// # Safety
@@ -391,7 +428,7 @@ impl<'a, T: ThreadedHandler> ThreadedRegistration<'a, T> {
/// Callers must not `mem::forget()` the returned [`ThreadedRegistration`] or otherwise prevent
/// its [`Drop`] implementation from running.
pub unsafe fn new(
- request: IrqRequest<'a>,
+ request: IrqRequest<'a, A>,
flags: Flags,
name: &'static CStr,
handler: impl PinInit<T, Error> + 'a,
@@ -416,8 +453,8 @@ pub unsafe fn new(
to_result(unsafe {
bindings::request_threaded_irq(
request.irq,
- Some(handle_threaded_irq_callback::<T>),
- Some(thread_fn_callback::<T>),
+ Some(handle_threaded_irq_callback::<T, A>),
+ Some(thread_fn_callback::<T, A>),
flags.into_inner(),
name.as_char_ptr(),
this.as_ptr().cast::<c_void>(),
@@ -441,7 +478,7 @@ pub fn synchronize(&self) {
}
#[pinned_drop]
-impl<T: ThreadedHandler> PinnedDrop for ThreadedRegistration<'_, T> {
+impl<T: ThreadedHandler, A: IrqRequestAnchor> PinnedDrop for ThreadedRegistration<'_, T, A> {
fn drop(self: Pin<&mut Self>) {
// SAFETY: The cookie was set to a pointer to `Self` in `ThreadedRegistration::new()`. This
// blocks until all in-flight handlers complete, so no references to `self` remain after
@@ -458,12 +495,12 @@ fn drop(self: Pin<&mut Self>) {
/// # Safety
///
/// This function should be only used as the callback in `request_threaded_irq`.
-unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler>(
+unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler, A: IrqRequestAnchor>(
_irq: i32,
ptr: *mut c_void,
) -> c_uint {
- let ptr = ptr.cast_const().cast::<ThreadedRegistration<'_, T>>();
- // SAFETY: `ptr` is a pointer to `ThreadedRegistration<'_, T>` set in
+ let ptr = ptr.cast_const().cast::<ThreadedRegistration<'_, T, A>>();
+ // SAFETY: `ptr` is a pointer to `ThreadedRegistration<'_, T, A>` set in
// `ThreadedRegistration::new()`.
let registration = unsafe { &*ptr };
@@ -473,9 +510,12 @@ fn drop(self: Pin<&mut Self>) {
/// # 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 {
- let ptr = ptr.cast_const().cast::<ThreadedRegistration<'_, T>>();
- // SAFETY: `ptr` is a pointer to `ThreadedRegistration<'_, T>` set in
+unsafe extern "C" fn thread_fn_callback<T: ThreadedHandler, A: IrqRequestAnchor>(
+ _irq: i32,
+ ptr: *mut c_void,
+) -> c_uint {
+ let ptr = ptr.cast_const().cast::<ThreadedRegistration<'_, T, A>>();
+ // SAFETY: `ptr` is a pointer to `ThreadedRegistration<'_, T, A>` set in
// `ThreadedRegistration::new()`.
let registration = unsafe { &*ptr };
--
2.55.0
next prev parent reply other threads:[~2026-08-10 22:50 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-10 22:47 [PATCH 0/5] Rework PCI IRQ vector code Danilo Krummrich
2026-08-10 22:47 ` Danilo Krummrich [this message]
2026-08-10 22:47 ` [PATCH 2/5] rust: pci: convert IrqVectorRegistration to a lifetime-managed owning type Danilo Krummrich
2026-08-10 22:47 ` [PATCH 3/5] rust: pci: remove IrqVector and resolve IrqRequest directly Danilo Krummrich
2026-08-10 22:47 ` [PATCH 4/5] PCI: add pci_irq_type() to query the allocated interrupt type Danilo Krummrich
2026-08-10 22:47 ` [PATCH 5/5] rust: pci: expose " 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=20260810224800.2314458-2-dakr@kernel.org \
--to=dakr@kernel.org \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=aliceryhl@google.com \
--cc=apopple@nvidia.com \
--cc=bhelgaas@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=driver-core@lists.linux.dev \
--cc=ecourtney@nvidia.com \
--cc=gary@garyguo.net \
--cc=jhubbard@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=rust-for-linux@vger.kernel.org \
--cc=shashanks@nvidia.com \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=ttabi@nvidia.com \
--cc=work@onurozkan.dev \
--cc=zhiw@nvidia.com \
/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