[PATCH 1/5] rust: irq: add anchor generic to IrqRequest and Registration

Danilo Krummrich <[email protected]>
Newsgroups dev.linux.lists.driver-core,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci,org.kernel.vger.rust-for-linux
Message-ID <[email protected]>
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 <[email protected]>
---
 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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.