From 0851d34a8cc3a0a43acd79a5c4980d45c6471aab Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Mon, 11 Aug 2025 13:03:41 -0300 Subject: 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 dependent upon having a IrqRequest that was previously allocated by a given device. This will be introduced in subsequent patches. Tested-by: Joel Fernandes Tested-by: Dirk Behme Reviewed-by: Alice Ryhl Signed-off-by: Daniel Almeida Link: https://lore.kernel.org/r/20250811-topics-tyr-request_irq2-v9-3-0485dcd9bcbf@collabora.com [ Remove expect(dead_code) from Flags::into_inner(), add expect(dead_code) to IrqRequest::new(), fix intra-doc links. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/irq/request.rs | 265 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 265 insertions(+) create mode 100644 rust/kernel/irq/request.rs (limited to 'rust/kernel/irq/request.rs') diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs new file mode 100644 index 000000000000..c585811cfdfb --- /dev/null +++ b/rust/kernel/irq/request.rs @@ -0,0 +1,265 @@ +// 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. + +use core::marker::PhantomPinned; + +use crate::alloc::Allocator; +use crate::device::{Bound, 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 a [`Handler`] or a `ThreadedHandler`. +#[repr(u32)] +pub enum IrqReturn { + /// 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, +} + +/// Callbacks for an IRQ handler. +pub trait Handler: 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 a threaded handler. + /// See also `ThreadedRegistration`. + fn handle(&self) -> IrqReturn; +} + +impl Handler for Arc { + fn handle(&self) -> IrqReturn { + T::handle(self) + } +} + +impl Handler for Box { + fn handle(&self) -> IrqReturn { + T::handle(self) + } +} + +/// # Invariants +/// +/// - `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 `new` will return a different instance of +/// `Registration`. +#[pin_data(PinnedDrop)] +struct RegistrationInner { + irq: u32, + cookie: *mut c_void, +} + +impl RegistrationInner { + fn synchronize(&self) { + // SAFETY: safe as per the invariants of `RegistrationInner` + unsafe { bindings::synchronize_irq(self.irq) }; + } +} + +#[pinned_drop] +impl PinnedDrop for RegistrationInner { + fn drop(self: Pin<&mut Self>) { + // SAFETY: + // + // Safe as per the invariants of `RegistrationInner` and: + // + // - The containing struct is `!Unpin` and was initialized 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) }; + } +} + +// SAFETY: We only use `inner` on drop, which called at most once with no +// concurrent access. +unsafe impl Sync for RegistrationInner {} + +// SAFETY: It is safe to send `RegistrationInner` across threads. +unsafe impl Send for RegistrationInner {} + +/// A request for an IRQ line for a given device. +/// +/// # Invariants +/// +/// - `ìrq` is the number of an interrupt source of `dev`. +/// - `irq` has not been registered yet. +pub struct IrqRequest<'a> { + dev: &'a Device, + irq: u32, +} + +impl<'a> IrqRequest<'a> { + /// Creates a new IRQ request for the given device and IRQ number. + /// + /// # Safety + /// + /// - `irq` should be a valid IRQ number for `dev`. + #[expect(dead_code)] + pub(crate) unsafe fn new(dev: &'a Device, irq: u32) -> Self { + // INVARIANT: `irq` is a valid IRQ number for `dev`. + IrqRequest { dev, irq } + } + + /// Returns the IRQ number of an [`IrqRequest`]. + pub fn irq(&self) -> u32 { + self.irq + } +} + +/// A registration of an IRQ handler for a given IRQ line. +/// +/// # Examples +/// +/// The following is an example of using `Registration`. It uses a +/// [`Completion`] to coordinate between the IRQ +/// handler and process context. [`Completion`] uses interior mutability, so the +/// handler can signal with [`Completion::complete_all()`] and the process +/// context can wait with [`Completion::wait_for_completion()`] even though +/// there is no way to get a mutable reference to the any of the fields in +/// `Data`. +/// +/// [`Completion`]: kernel::sync::Completion +/// [`Completion::complete_all()`]: kernel::sync::Completion::complete_all +/// [`Completion::wait_for_completion()`]: kernel::sync::Completion::wait_for_completion +/// +/// ``` +/// use kernel::c_str; +/// use kernel::device::Bound; +/// use kernel::irq::{self, Flags, IrqRequest, IrqReturn, Registration}; +/// use kernel::prelude::*; +/// use kernel::sync::{Arc, Completion}; +/// +/// // Data shared between process and IRQ context. +/// #[pin_data] +/// struct Data { +/// #[pin] +/// completion: Completion, +/// } +/// +/// impl irq::Handler for Data { +/// // Executed in IRQ context. +/// fn handle(&self) -> IrqReturn { +/// self.completion.complete_all(); +/// IrqReturn::Handled +/// } +/// } +/// +/// // Registers an IRQ handler for the given IrqRequest. +/// // +/// // This runs in process context and assumes `request` was previously acquired from a device. +/// fn register_irq( +/// handler: impl PinInit, +/// request: IrqRequest<'_>, +/// ) -> Result>> { +/// let registration = Registration::new(request, Flags::SHARED, c_str!("my_device"), handler); +/// +/// let registration = Arc::pin_init(registration, GFP_KERNEL)?; +/// +/// registration.handler().completion.wait_for_completion(); +/// +/// Ok(registration) +/// } +/// # Ok::<(), Error>(()) +/// ``` +/// +/// # Invariants +/// +/// * We own an irq handler using `&self.handler` as its private data. +#[pin_data] +pub struct Registration { + #[pin] + inner: Devres, + + #[pin] + handler: T, + + /// Pinned because we need address stability so that we can pass a pointer + /// to the callback. + #[pin] + _pin: PhantomPinned, +} + +impl Registration { + /// 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 + 'a, + ) -> impl PinInit + '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 `Registration` instance + cookie: unsafe { &raw mut (*this.as_ptr()).handler }.cast(), + irq: { + // 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. + to_result(unsafe { + bindings::request_irq( + request.irq, + Some(handle_irq_callback::), + 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) -> 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(_irq: i32, ptr: *mut c_void) -> c_uint { + // SAFETY: `ptr` is a pointer to T set in `Registration::new` + let handler = unsafe { &*(ptr as *const T) }; + T::handle(handler) as c_uint +} -- cgit v1.2.3 From 135d40523244dcad3c64eb2ce131cf018db5cff4 Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Mon, 11 Aug 2025 13:03:42 -0300 Subject: rust: irq: add support for threaded IRQs and handlers 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 Tested-by: Dirk Behme Reviewed-by: Alice Ryhl Signed-off-by: Daniel Almeida Link: https://lore.kernel.org/r/20250811-topics-tyr-request_irq2-v9-4-0485dcd9bcbf@collabora.com [ Add now available intra-doc links back in. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/irq.rs | 5 +- rust/kernel/irq/request.rs | 232 ++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 232 insertions(+), 5 deletions(-) (limited to 'rust/kernel/irq/request.rs') diff --git a/rust/kernel/irq.rs b/rust/kernel/irq.rs index c1019bc36ad1..20abd4056655 100644 --- a/rust/kernel/irq.rs +++ b/rust/kernel/irq.rs @@ -18,4 +18,7 @@ mod request; 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 c585811cfdfb..7a956566f503 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; @@ -15,7 +16,7 @@ use crate::prelude::*; use crate::str::CStr; use crate::sync::Arc; -/// The value that can be returned from a [`Handler`] or a `ThreadedHandler`. +/// The value that can be returned from a [`Handler`] or a [`ThreadedHandler`]. #[repr(u32)] pub enum IrqReturn { /// The interrupt was not from this device or was not handled. @@ -34,7 +35,7 @@ pub trait Handler: Sync { /// /// All work that does not necessarily need to be executed from /// interrupt context, should be deferred to a threaded handler. - /// See also `ThreadedRegistration`. + /// See also [`ThreadedRegistration`]. fn handle(&self) -> IrqReturn; } @@ -263,3 +264,226 @@ unsafe extern "C" fn handle_irq_callback(_irq: i32, ptr: *mut c_void 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 ThreadedHandler for Arc { + fn handle(&self) -> ThreadedIrqReturn { + T::handle(self) + } + + fn handle_threaded(&self) -> IrqReturn { + T::handle_threaded(self) + } +} + +impl ThreadedHandler for Box { + 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, +/// } +/// +/// 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, +/// request: IrqRequest<'_>, +/// ) -> Result>> { +/// 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 { + #[pin] + inner: Devres, + + #[pin] + handler: T, + + /// Pinned because we need address stability so that we can pass a pointer + /// to the callback. + #[pin] + _pin: PhantomPinned, +} + +impl ThreadedRegistration { + /// 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 + 'a, + ) -> impl PinInit + '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::), + Some(thread_fn_callback::), + 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) -> 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( + _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(_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 +} -- cgit v1.2.3 From 17e70f0c549f4a19da7d681d60b552901833f8f3 Mon Sep 17 00:00:00 2001 From: Daniel Almeida Date: Mon, 11 Aug 2025 13:03:43 -0300 Subject: rust: platform: add irq accessors These accessors can be used to retrieve a irq::Registration and irq::ThreadedRegistration from a platform device by index or name. Alternatively, drivers can retrieve an IrqRequest from a bound platform device for later use. These accessors ensure that only valid IRQ lines can ever be registered. Reviewed-by: Alice Ryhl Tested-by: Joel Fernandes Tested-by: Dirk Behme Signed-off-by: Daniel Almeida Link: https://lore.kernel.org/r/20250811-topics-tyr-request_irq2-v9-5-0485dcd9bcbf@collabora.com [ Remove expect(dead_code) from IrqRequest::new(), re-format macros and macro invocations to not exceed 100 characters line length. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/irq/request.rs | 1 - rust/kernel/platform.rs | 176 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 176 insertions(+), 1 deletion(-) (limited to 'rust/kernel/irq/request.rs') diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs index 7a956566f503..4033df7d0dce 100644 --- a/rust/kernel/irq/request.rs +++ b/rust/kernel/irq/request.rs @@ -111,7 +111,6 @@ impl<'a> IrqRequest<'a> { /// # Safety /// /// - `irq` should be a valid IRQ number for `dev`. - #[expect(dead_code)] pub(crate) unsafe fn new(dev: &'a Device, irq: u32) -> Self { // INVARIANT: `irq` is a valid IRQ number for `dev`. IrqRequest { dev, irq } diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 8f028c76f9fa..ce2bb4d4e882 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -10,6 +10,7 @@ use crate::{ driver, error::{from_result, to_result, Result}, io::{mem::IoRequest, Resource}, + irq::{self, IrqRequest}, of, prelude::*, types::Opaque, @@ -284,6 +285,181 @@ impl Device { } } +macro_rules! define_irq_accessor_by_index { + ( + $(#[$meta:meta])* $fn_name:ident, + $request_fn:ident, + $reg_type:ident, + $handler_trait:ident + ) => { + $(#[$meta])* + pub fn $fn_name<'a, T: irq::$handler_trait + 'static>( + &'a self, + flags: irq::Flags, + index: u32, + name: &'static CStr, + handler: impl PinInit + 'a, + ) -> Result, Error> + 'a> { + let request = self.$request_fn(index)?; + + Ok(irq::$reg_type::::new( + request, + flags, + name, + handler, + )) + } + }; +} + +macro_rules! define_irq_accessor_by_name { + ( + $(#[$meta:meta])* $fn_name:ident, + $request_fn:ident, + $reg_type:ident, + $handler_trait:ident + ) => { + $(#[$meta])* + pub fn $fn_name<'a, T: irq::$handler_trait + 'static>( + &'a self, + flags: irq::Flags, + irq_name: &CStr, + name: &'static CStr, + handler: impl PinInit + 'a, + ) -> Result, Error> + 'a> { + let request = self.$request_fn(irq_name)?; + + Ok(irq::$reg_type::::new( + request, + flags, + name, + handler, + )) + } + }; +} + +impl Device { + /// Returns an [`IrqRequest`] for the IRQ at the given index, if any. + pub fn irq_by_index(&self, index: u32) -> Result> { + // SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`. + let irq = unsafe { bindings::platform_get_irq(self.as_raw(), index) }; + + if irq < 0 { + return Err(Error::from_errno(irq)); + } + + // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`. + Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) }) + } + + /// Returns an [`IrqRequest`] for the IRQ at the given index, but does not + /// print an error if the IRQ cannot be obtained. + pub fn optional_irq_by_index(&self, index: u32) -> Result> { + // SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`. + let irq = unsafe { bindings::platform_get_irq_optional(self.as_raw(), index) }; + + if irq < 0 { + return Err(Error::from_errno(irq)); + } + + // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`. + Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) }) + } + + /// Returns an [`IrqRequest`] for the IRQ with the given name, if any. + pub fn irq_by_name(&self, name: &CStr) -> Result> { + // SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`. + let irq = unsafe { bindings::platform_get_irq_byname(self.as_raw(), name.as_char_ptr()) }; + + if irq < 0 { + return Err(Error::from_errno(irq)); + } + + // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`. + Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) }) + } + + /// Returns an [`IrqRequest`] for the IRQ with the given name, but does not + /// print an error if the IRQ cannot be obtained. + pub fn optional_irq_by_name(&self, name: &CStr) -> Result> { + // SAFETY: `self.as_raw` returns a valid pointer to a `struct platform_device`. + let irq = unsafe { + bindings::platform_get_irq_byname_optional(self.as_raw(), name.as_char_ptr()) + }; + + if irq < 0 { + return Err(Error::from_errno(irq)); + } + + // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`. + Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) }) + } + + define_irq_accessor_by_index!( + /// Returns a [`irq::Registration`] for the IRQ at the given index. + request_irq_by_index, + irq_by_index, + Registration, + Handler + ); + define_irq_accessor_by_name!( + /// Returns a [`irq::Registration`] for the IRQ with the given name. + request_irq_by_name, + irq_by_name, + Registration, + Handler + ); + define_irq_accessor_by_index!( + /// Does the same as [`Self::request_irq_by_index`], except that it does + /// not print an error message if the IRQ cannot be obtained. + request_optional_irq_by_index, + optional_irq_by_index, + Registration, + Handler + ); + define_irq_accessor_by_name!( + /// Does the same as [`Self::request_irq_by_name`], except that it does + /// not print an error message if the IRQ cannot be obtained. + request_optional_irq_by_name, + optional_irq_by_name, + Registration, + Handler + ); + + define_irq_accessor_by_index!( + /// Returns a [`irq::ThreadedRegistration`] for the IRQ at the given index. + request_threaded_irq_by_index, + irq_by_index, + ThreadedRegistration, + ThreadedHandler + ); + define_irq_accessor_by_name!( + /// Returns a [`irq::ThreadedRegistration`] for the IRQ with the given name. + request_threaded_irq_by_name, + irq_by_name, + ThreadedRegistration, + ThreadedHandler + ); + define_irq_accessor_by_index!( + /// Does the same as [`Self::request_threaded_irq_by_index`], except + /// that it does not print an error message if the IRQ cannot be + /// obtained. + request_optional_threaded_irq_by_index, + optional_irq_by_index, + ThreadedRegistration, + ThreadedHandler + ); + define_irq_accessor_by_name!( + /// Does the same as [`Self::request_threaded_irq_by_name`], except that + /// it does not print an error message if the IRQ cannot be obtained. + request_optional_threaded_irq_by_name, + optional_irq_by_name, + ThreadedRegistration, + ThreadedHandler + ); +} + // SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic // argument. kernel::impl_device_context_deref!(unsafe { Device }); -- cgit v1.2.3 From 29e16fcd67ee5b1d0417a657294cf96fdf2f8df9 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Mon, 11 Aug 2025 13:03:45 -0300 Subject: rust: irq: add &Device argument to irq callbacks When working with a bus device, many operations are only possible while the device is still bound. The &Device type represents a proof in the type system that you are in a scope where the device is guaranteed to still be bound. Since we deregister irq callbacks when unbinding a device, if an irq callback is running, that implies that the device has not yet been unbound. To allow drivers to take advantage of that, add an additional argument to irq callbacks. Signed-off-by: Alice Ryhl Reviewed-by: Boqun Feng Signed-off-by: Daniel Almeida Link: https://lore.kernel.org/r/20250811-topics-tyr-request_irq2-v9-7-0485dcd9bcbf@collabora.com Signed-off-by: Danilo Krummrich --- rust/kernel/irq/request.rs | 95 +++++++++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 38 deletions(-) (limited to 'rust/kernel/irq/request.rs') diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs index 4033df7d0dce..b150563fdef8 100644 --- a/rust/kernel/irq/request.rs +++ b/rust/kernel/irq/request.rs @@ -36,18 +36,18 @@ pub trait Handler: Sync { /// All work that does not necessarily need to be executed from /// interrupt context, should be deferred to a threaded handler. /// See also [`ThreadedRegistration`]. - fn handle(&self) -> IrqReturn; + fn handle(&self, device: &Device) -> IrqReturn; } impl Handler for Arc { - fn handle(&self) -> IrqReturn { - T::handle(self) + fn handle(&self, device: &Device) -> IrqReturn { + T::handle(self, device) } } impl Handler for Box { - fn handle(&self) -> IrqReturn { - T::handle(self) + fn handle(&self, device: &Device) -> IrqReturn { + T::handle(self, device) } } @@ -140,7 +140,7 @@ impl<'a> IrqRequest<'a> { /// /// ``` /// use kernel::c_str; -/// use kernel::device::Bound; +/// use kernel::device::{Bound, Device}; /// use kernel::irq::{self, Flags, IrqRequest, IrqReturn, Registration}; /// use kernel::prelude::*; /// use kernel::sync::{Arc, Completion}; @@ -154,7 +154,7 @@ impl<'a> IrqRequest<'a> { /// /// impl irq::Handler for Data { /// // Executed in IRQ context. -/// fn handle(&self) -> IrqReturn { +/// fn handle(&self, _dev: &Device) -> IrqReturn { /// self.completion.complete_all(); /// IrqReturn::Handled /// } @@ -180,7 +180,7 @@ impl<'a> IrqRequest<'a> { /// /// # Invariants /// -/// * We own an irq handler using `&self.handler` as its private data. +/// * We own an irq handler whose cookie is a pointer to `Self`. #[pin_data] pub struct Registration { #[pin] @@ -208,21 +208,24 @@ impl Registration { inner <- Devres::new( request.dev, try_pin_init!(RegistrationInner { - // SAFETY: `this` is a valid pointer to the `Registration` instance - cookie: unsafe { &raw mut (*this.as_ptr()).handler }.cast(), + // INVARIANT: `this` is a valid pointer to the `Registration` instance + cookie: this.as_ptr().cast::(), irq: { // 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. + // - When request_irq is called, everything that handle_irq_callback will + // touch has already been initialized, so it's safe for the callback to + // be called immediately. to_result(unsafe { bindings::request_irq( request.irq, Some(handle_irq_callback::), flags.into_inner(), name.as_char_ptr(), - (&raw mut (*this.as_ptr()).handler).cast(), + this.as_ptr().cast::(), ) })?; request.irq @@ -259,9 +262,13 @@ impl Registration { /// /// This function should be only used as the callback in `request_irq`. unsafe extern "C" fn handle_irq_callback(_irq: i32, ptr: *mut c_void) -> c_uint { - // SAFETY: `ptr` is a pointer to T set in `Registration::new` - let handler = unsafe { &*(ptr as *const T) }; - T::handle(handler) as c_uint + // SAFETY: `ptr` is a pointer to `Registration` set in `Registration::new` + let registration = unsafe { &*(ptr as *const Registration) }; + // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq + // callback is running implies that the device has not yet been unbound. + let device = unsafe { registration.inner.device().as_bound() }; + + T::handle(®istration.handler, device) as c_uint } /// The value that can be returned from [`ThreadedHandler::handle`]. @@ -287,7 +294,8 @@ pub trait ThreadedHandler: Sync { /// handler, i.e. [`ThreadedHandler::handle_threaded`]. /// /// The default implementation returns [`ThreadedIrqReturn::WakeThread`]. - fn handle(&self) -> ThreadedIrqReturn { + #[expect(unused_variables)] + fn handle(&self, device: &Device) -> ThreadedIrqReturn { ThreadedIrqReturn::WakeThread } @@ -295,26 +303,26 @@ pub trait ThreadedHandler: Sync { /// /// This is executed in process context. The kernel creates a dedicated /// `kthread` for this purpose. - fn handle_threaded(&self) -> IrqReturn; + fn handle_threaded(&self, device: &Device) -> IrqReturn; } impl ThreadedHandler for Arc { - fn handle(&self) -> ThreadedIrqReturn { - T::handle(self) + fn handle(&self, device: &Device) -> ThreadedIrqReturn { + T::handle(self, device) } - fn handle_threaded(&self) -> IrqReturn { - T::handle_threaded(self) + fn handle_threaded(&self, device: &Device) -> IrqReturn { + T::handle_threaded(self, device) } } impl ThreadedHandler for Box { - fn handle(&self) -> ThreadedIrqReturn { - T::handle(self) + fn handle(&self, device: &Device) -> ThreadedIrqReturn { + T::handle(self, device) } - fn handle_threaded(&self) -> IrqReturn { - T::handle_threaded(self) + fn handle_threaded(&self, device: &Device) -> IrqReturn { + T::handle_threaded(self, device) } } @@ -333,7 +341,7 @@ impl ThreadedHandler for Box { /// /// ``` /// use kernel::c_str; -/// use kernel::device::Bound; +/// use kernel::device::{Bound, Device}; /// use kernel::irq::{ /// self, Flags, IrqRequest, IrqReturn, ThreadedHandler, ThreadedIrqReturn, /// ThreadedRegistration, @@ -357,7 +365,7 @@ impl ThreadedHandler for Box { /// // 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 { +/// fn handle_threaded(&self, _dev: &Device) -> IrqReturn { /// let mut data = self.value.lock(); /// *data += 1; /// IrqReturn::Handled @@ -390,7 +398,7 @@ impl ThreadedHandler for Box { /// /// # Invariants /// -/// * We own an irq handler using `&T` as its private data. +/// * We own an irq handler whose cookie is a pointer to `Self`. #[pin_data] pub struct ThreadedRegistration { #[pin] @@ -418,14 +426,17 @@ impl ThreadedRegistration { 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(), + // INVARIANT: `this` is a valid pointer to the `ThreadedRegistration` instance. + cookie: this.as_ptr().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. + // destructor of Self runs, which will deregister the callbacks + // before the memory location becomes invalid. + // - When request_threaded_irq is called, everything that the two callbacks + // will touch has already been initialized, so it's safe for the + // callbacks to be called immediately. to_result(unsafe { bindings::request_threaded_irq( request.irq, @@ -433,7 +444,7 @@ impl ThreadedRegistration { Some(thread_fn_callback::), flags.into_inner(), name.as_char_ptr(), - (&raw mut (*this.as_ptr()).handler).cast(), + this.as_ptr().cast::(), ) })?; request.irq @@ -473,16 +484,24 @@ unsafe extern "C" fn handle_threaded_irq_callback( _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: `ptr` is a pointer to `ThreadedRegistration` set in `ThreadedRegistration::new` + let registration = unsafe { &*(ptr as *const ThreadedRegistration) }; + // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq + // callback is running implies that the device has not yet been unbound. + let device = unsafe { registration.inner.device().as_bound() }; + + T::handle(®istration.handler, device) as c_uint } /// # Safety /// /// This function should be only used as the callback in `request_threaded_irq`. unsafe extern "C" fn thread_fn_callback(_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 + // SAFETY: `ptr` is a pointer to `ThreadedRegistration` set in `ThreadedRegistration::new` + let registration = unsafe { &*(ptr as *const ThreadedRegistration) }; + // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq + // callback is running implies that the device has not yet been unbound. + let device = unsafe { registration.inner.device().as_bound() }; + + T::handle_threaded(®istration.handler, device) as c_uint } -- cgit v1.2.3