Rust for Linux List
 help / color / mirror / Atom feed
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(&registration.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


  reply	other threads:[~2026-08-10 22:50 UTC|newest]

Thread overview: 8+ 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-11 11:44   ` Gary Guo
2026-08-11 16:44     ` 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