summaryrefslogtreecommitdiff
path: root/rust/kernel/irq
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 10:42:18 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-19 10:42:18 -0700
commit59e6295fac26b8e85c1ea859cdd89fa1e47519d7 (patch)
tree7c071f8bab440eddbb569e84e9f31c80f8a4e529 /rust/kernel/irq
parentc6cf4441a3a05bb7273ed022f3e56c4fc591da08 (diff)
parent3b5ea0f078e1b72276e99c237f3dfc2fd72938bb (diff)
Merge tag 'driver-core-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core
Pull driver core updates from Danilo Krummrich: "container_of: - Apply typeof_member(), remove the local __mptr variable to eliminate variable shadowing warnings on nested container_of() calls, and remove unnecessary parentheses core: - Add driver name to probe debug print for initcall_debug - Avoid repeatedly printing the same 'Fixed dependency cycle' log - Unwind device_add() on attribute creation failure in attribute_container_add_class_device() - Remove statistics group if encryption group creation fails in transport_add_class_device() debugfs: - Fix lockdown check for mmap_prepare() - Warn if file creation failed due to uninitialized debugfs device property: - Implement fw_devlink support for software nodes by adding software_node_add_links(), which creates fwnode links from DEV_PROP_REF properties to enable automatic probe ordering. Add kunit-managed fwnode helpers and test coverage - Fix infinite loop in fwnode_for_each_child_node() when the secondary fwnode has more than one child. Add test cases - Fix out-of-bounds access in software_node_get_reference_args() when called with index -1 (UINT_MAX) - Refactor to use RAII approach with __free() - Add Bartosz Golaszewski as software node reviewer firmware loader: - Fix race where a sysfs fallback request can complete before being queued as pending, leading to a use-after-free on the next fallback request - Reject 0-size built-in firmware and fail the build on empty firmware files in CONFIG_EXTRA_FIRMWARE kobject: - Provide __KOBJ_ATTR() and __KOBJ_ATTR_RO/WO() initialization macros and allow the constification of kobject attributes, enabling them to reside in read-only memory platform: - Provide platform_device_set_of_node(), platform_device_set_fwnode(), and platform_device_set_of_node_from_dev() helpers that encapsulate firmware node reference counting for dynamically allocated platform devices Convert all in-tree users that manually assigned dev.of_node or dev.fwnode, fixing a pre-existing refcount bug in powermac. Switch to counting references of all firmware node types, not only OF nodes - Unify the release path for dynamically allocated platform devices by removing platform_device_release_full(). Amend the fwnode setter API contract to warn if a primary software node is overwritten. Add KUnit tests for correct software node removal on device unregistration Rust: - Auxiliary: - Add registration_data_with() closure-based API for invariant ForLt types - Debugfs: - Migrate BinaryWriter and BinaryReaderMut trait requirements from kernel::transmute traits to zerocopy traits - Device: - Add BoundInternal device context and InternalBoundContext trait for bus abstractions that need internal access to a bound device. - Make the lifetime on Core and CoreInternal invariant to prevent coercion to shorter lifetimes - Devres: - Fix race between concurrent revokers where the losing revoker could return before the winning revoker finished dropping the inner data, causing use-after-free. - Ensure revocation is complete before the device finishes unbinding by making the synchronization bidirectional. - Add DevresLt<F: ForLt>, a wrapper around Devres that shortens 'static back to the caller's borrow scope. Implement ForLt and CovariantForLt for Bar, IoMem, and ExclusiveIoMem - Driver: - Switch from index-based to pointer-based device ID info lookup, storing static references in driver_data. Centralize device ID handling in device_id.rs, removing the open-coded ACPI/OF matching logic and duplicate ID table from driver.rs - I/O: - Make I/O regions typed (with a dynamically-sized Region type for the existing untyped case), create view types representing subregions of a mapped I/O region, and add io_project!() for safely creating subviews. - Split Io into a base trait (IoBase) and an extension trait (Io) with a blanket implementation, preventing implementers from overriding provided methods that unsafe code relies on. - Add a SysMem backend for shared system memory with volatile access, and make Coherent implement Io via an I/O view type. Add IoSysMap as sum type of Mmio and SysMem. Add copying methods (memcpy_{from,to}io()) and read_val()/write_val() for typed access. - Replace dma_read!()/dma_write!() with io_read!()/io_write!() for primitives and copying methods for aggregates; drop the old macros. Convert nova-core to use I/O projection. - Fix internal shortcut rule dispatch in the register!() macro, remove unused rule arguments, and use path fragments for alias destinations - IRQ: - Make irq::Registration compatible with lifetime-bound drivers by removing the 'static bound on Handler/ThreadedHandler and replacing Devres<RegistrationInner> with direct request_irq()/free_irq() calls. Handlers can now directly own lifetime-bound device resources - PCI: - Convert IrqVectorRegistration to a lifetime-annotated owning type, giving drivers explicit control over the allocation lifetime. IrqVector embeds a resolved IrqRequest, making the conversion infallible. Remove the redundant request_irq()/request_threaded_irq() wrappers from pci::Device. - Add pci_irq_type() C helper and expose it via irq_type() on IrqVectorRegistration and IrqVector, returning PCI_IRQ_MSIX, PCI_IRQ_MSI, or PCI_IRQ_INTX. - Mark pci::Device refcount methods inline - Serdev: - Add Rust abstractions for the serial device bus, including serdev::Driver trait, serdev::Device wrapping struct serdev_device, and serdev::Adapter implementing RegistrationOps. Includes a sample driver. Markus Probst takes over as serdev maintainer for both C and Rust code - Misc: - Split ForLt into a base trait (providing the Of<'a> GAT) and an unsafe CovariantForLt subtrait guaranteeing covariance, enabling invariant types (e.g. those containing Mutex<&'bound T>) to participate in the ForLt abstraction. - Fix Coherent read past EOF returning -ERANGE instead of zero. - Fix firmware example UB by avoiding null-pointer ARef misc: - Avoid iattr allocation in kernfs listxattr by using kernfs_iattrs_noalloc(). - Unregister SoC bus on early device registration failure. - Remove unused DMA_FENCE_TRACE Kconfig symbol. - Fix /sys/module path in comment. - Refactor ISA bus init to remove nested blocks. - Remove redundant nodemask clears in numa_init(). - Add kernel-doc for fwnode_operations and sys_soc.h, mark internal property data as private for kernel-doc, and add property.h/fwnode.h to driver-api infrastructure docs. - Add MAINTAINERS entry for sys_soc.h" * tag 'driver-core-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core: (129 commits) rust: pci: expose the allocated interrupt type PCI: Add pci_irq_type() to query the allocated interrupt type rust: pci: remove request_irq() and request_threaded_irq() from Device rust: pci: resolve IRQ in index() and embed IrqRequest in IrqVector rust: pci: convert IrqVectorRegistration to a lifetime-managed owning type kernfs: avoid iattr allocation in listxattr rust: serdev: use ThisModule::as_ptr() instead of field access ACPI/IORT: use platform_device_set_fwnode() ACPI/APMT: use platform_device_set_fwnode() firmware_loader: do not queue completed sysfs fallback requests rust: pci: Mark Device refcount methods inline rust: irq: make Registration compatible with lifetime-bound drivers rust: net/phy: remove expansion from doc rust: dma: return zero for Coherent reads past EOF rust: io: register: use path fragment for alias destination rust: io: register: remove unused rule arguments rust: io: register: dispatch shortcut rules internally MAINTAINERS: add sys_soc.h to DRIVER CORE rust: debugfs: remove unsafe blocks from traits impl for Vec rust: debugfs: migrate debugfs traits requirements to zerocopy ...
Diffstat (limited to 'rust/kernel/irq')
-rw-r--r--rust/kernel/irq/request.rs432
1 files changed, 205 insertions, 227 deletions
diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs
index f425fe12f7c8..c1c6525a676a 100644
--- a/rust/kernel/irq/request.rs
+++ b/rust/kernel/irq/request.rs
@@ -5,16 +5,21 @@
//! [`ThreadedRegistration`], 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;
+use core::marker::{
+ PhantomData,
+ PhantomPinned, //
+};
+
+use crate::{
+ device::{
+ Bound,
+ Device, //
+ },
+ error::to_result,
+ irq::flags::Flags,
+ prelude::*,
+ str::CStr,
+};
/// The value that can be returned from a [`Handler`] or a [`ThreadedHandler`].
#[repr(u32)]
@@ -27,7 +32,7 @@ pub enum IrqReturn {
}
/// Callbacks for an IRQ handler.
-pub trait Handler: Sync + 'static {
+pub trait Handler: Sync {
/// The hard IRQ handler.
///
/// This is executed in interrupt context, hence all corresponding
@@ -36,73 +41,20 @@ pub trait Handler: Sync + 'static {
/// 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, device: &Device<Bound>) -> IrqReturn;
-}
-
-impl<T: ?Sized + Handler + Send> Handler for Arc<T> {
- fn handle(&self, device: &Device<Bound>) -> IrqReturn {
- T::handle(self, device)
- }
-}
-
-impl<T: ?Sized + Handler, A: Allocator + 'static> Handler for Box<T, A> {
- fn handle(&self, device: &Device<Bound>) -> IrqReturn {
- T::handle(self, device)
- }
+ fn handle(&self) -> IrqReturn;
}
-/// # 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.
+/// - `irq` has not been registered yet; this is consumed by [`Registration::new()`].
pub struct IrqRequest<'a> {
- dev: &'a Device<Bound>,
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>>,
}
impl<'a> IrqRequest<'a> {
@@ -111,12 +63,16 @@ impl<'a> IrqRequest<'a> {
/// # Safety
///
/// - `irq` should be a valid IRQ number for `dev`.
- pub(crate) unsafe fn new(dev: &'a Device<Bound>, irq: u32) -> Self {
+ pub(crate) unsafe fn new(_dev: &'a Device<Bound>, irq: u32) -> Self {
// INVARIANT: `irq` is a valid IRQ number for `dev`.
- IrqRequest { dev, irq }
+ IrqRequest {
+ irq,
+ _dev: PhantomData,
+ }
}
/// Returns the IRQ number of an [`IrqRequest`].
+ #[inline]
pub fn irq(&self) -> u32 {
self.irq
}
@@ -139,10 +95,18 @@ impl<'a> IrqRequest<'a> {
/// [`Completion::wait_for_completion()`]: kernel::sync::Completion::wait_for_completion
///
/// ```
-/// use kernel::device::{Bound, Device};
-/// use kernel::irq::{self, Flags, IrqRequest, IrqReturn, Registration};
-/// use kernel::prelude::*;
-/// use kernel::sync::{Arc, Completion};
+/// use core::pin::Pin;
+/// use kernel::{
+/// irq::{
+/// self,
+/// Flags,
+/// IrqRequest,
+/// IrqReturn,
+/// Registration,
+/// },
+/// prelude::*,
+/// sync::Completion,
+/// };
///
/// // Data shared between process and IRQ context.
/// #[pin_data]
@@ -153,7 +117,7 @@ impl<'a> IrqRequest<'a> {
///
/// impl irq::Handler for Data {
/// // Executed in IRQ context.
-/// fn handle(&self, _dev: &Device<Bound>) -> IrqReturn {
+/// fn handle(&self) -> IrqReturn {
/// self.completion.complete_all();
/// IrqReturn::Handled
/// }
@@ -163,12 +127,21 @@ impl<'a> IrqRequest<'a> {
/// //
/// // This runs in process context and assumes `request` was previously acquired from a device.
/// fn register_irq(
-/// handler: impl PinInit<Data, Error>,
/// request: IrqRequest<'_>,
-/// ) -> Result<Arc<Registration<Data>>> {
-/// let registration = Registration::new(request, Flags::SHARED, c"my_device", handler);
+/// ) -> Result<Pin<KBox<Registration<'_, Data>>>> {
+/// // SAFETY: The returned Registration is not leaked.
+/// let registration = unsafe {
+/// Registration::new(
+/// request,
+/// Flags::SHARED,
+/// c"my_device",
+/// try_pin_init!(Data {
+/// completion <- Completion::new(),
+/// }? Error),
+/// )
+/// };
///
-/// let registration = Arc::pin_init(registration, GFP_KERNEL)?;
+/// let registration = KBox::pin_init(registration, GFP_KERNEL)?;
///
/// registration.handler().completion.wait_for_completion();
///
@@ -179,11 +152,10 @@ impl<'a> IrqRequest<'a> {
///
/// # Invariants
///
-/// * We own an irq handler whose cookie is a pointer to `Self`.
-#[pin_data]
-pub struct Registration<T: Handler> {
- #[pin]
- inner: Devres<RegistrationInner>,
+/// * 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>,
#[pin]
handler: T,
@@ -194,44 +166,46 @@ pub struct Registration<T: Handler> {
_pin: PhantomPinned,
}
-impl<T: Handler> Registration<T> {
+impl<'a, T: Handler> Registration<'a, T> {
/// Registers the IRQ handler with the system for the given IRQ number.
- pub fn new<'a>(
+ ///
+ /// # Safety
+ ///
+ /// Callers must not `mem::forget()` the returned [`Registration`] or otherwise prevent its
+ /// [`Drop`] implementation from running.
+ pub unsafe fn new(
request: IrqRequest<'a>,
flags: Flags,
name: &'static CStr,
handler: impl PinInit<T, Error> + 'a,
- ) -> impl PinInit<Self, Error> + 'a {
+ ) -> impl PinInit<Self, Error> + 'a
+ where
+ T: 'a,
+ {
+ // INVARIANT: If initialization completes successfully, we own an IRQ handler registered
+ // via `request_irq` whose cookie is a pointer to `Self`.
try_pin_init!(&this in Self {
handler <- handler,
- inner <- Devres::new(
- request.dev,
- try_pin_init!(RegistrationInner {
- // INVARIANT: `this` is a valid pointer to the `Registration` instance
- cookie: this.as_ptr().cast::<c_void>(),
- 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::<T>),
- flags.into_inner(),
- name.as_char_ptr(),
- this.as_ptr().cast::<c_void>(),
- )
- })?;
- request.irq
- }
- })
- ),
+ request,
_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.
+ // - All fields are already initialized, so it's safe for the callback to be
+ // called immediately.
+ to_result(unsafe {
+ bindings::request_irq(
+ request.irq,
+ Some(handle_irq_callback::<T>),
+ flags.into_inner(),
+ name.as_char_ptr(),
+ this.as_ptr().cast::<c_void>(),
+ )
+ })?;
+ },
})
}
@@ -241,19 +215,25 @@ impl<T: Handler> Registration<T> {
}
/// 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(())
+ #[inline]
+ pub fn synchronize(&self) {
+ // SAFETY: `self.request.irq` is a valid registered IRQ number (type invariant).
+ unsafe { bindings::synchronize_irq(self.request.irq) };
}
+}
- /// 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(())
+#[pinned_drop]
+impl<T: Handler> PinnedDrop for Registration<'_, T> {
+ 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
+ // returns.
+ unsafe {
+ bindings::free_irq(
+ self.request.irq,
+ core::ptr::from_mut::<Self>(self.get_unchecked_mut()).cast::<c_void>(),
+ )
+ };
}
}
@@ -261,13 +241,11 @@ impl<T: Handler> Registration<T> {
///
/// 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 {
- // SAFETY: `ptr` is a pointer to `Registration<T>` set in `Registration::new`
- let registration = unsafe { &*(ptr as *const Registration<T>) };
- // 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() };
+ let ptr = ptr.cast_const().cast::<Registration<'_, T>>();
+ // SAFETY: `ptr` is a pointer to `Registration<'_, T>` set in `Registration::new()`.
+ let registration = unsafe { &*ptr };
- T::handle(&registration.handler, device) as c_uint
+ T::handle(&registration.handler) as c_uint
}
/// The value that can be returned from [`ThreadedHandler::handle`].
@@ -284,7 +262,7 @@ pub enum ThreadedIrqReturn {
}
/// Callbacks for a threaded IRQ handler.
-pub trait ThreadedHandler: Sync + 'static {
+pub trait ThreadedHandler: Sync {
/// The hard IRQ handler.
///
/// This is executed in interrupt context, hence all corresponding
@@ -293,8 +271,7 @@ pub trait ThreadedHandler: Sync + 'static {
/// handler, i.e. [`ThreadedHandler::handle_threaded`].
///
/// The default implementation returns [`ThreadedIrqReturn::WakeThread`].
- #[expect(unused_variables)]
- fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn {
+ fn handle(&self) -> ThreadedIrqReturn {
ThreadedIrqReturn::WakeThread
}
@@ -302,27 +279,7 @@ pub trait ThreadedHandler: Sync + 'static {
///
/// This is executed in process context. The kernel creates a dedicated
/// `kthread` for this purpose.
- fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn;
-}
-
-impl<T: ?Sized + ThreadedHandler + Send> ThreadedHandler for Arc<T> {
- fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn {
- T::handle(self, device)
- }
-
- fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn {
- T::handle_threaded(self, device)
- }
-}
-
-impl<T: ?Sized + ThreadedHandler, A: Allocator + 'static> ThreadedHandler for Box<T, A> {
- fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn {
- T::handle(self, device)
- }
-
- fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn {
- T::handle_threaded(self, device)
- }
+ fn handle_threaded(&self) -> IrqReturn;
}
/// A registration of a threaded IRQ handler for a given IRQ line.
@@ -339,13 +296,20 @@ impl<T: ?Sized + ThreadedHandler, A: Allocator + 'static> ThreadedHandler for Bo
/// [`Mutex`](kernel::sync::Mutex) to provide interior mutability.
///
/// ```
-/// use kernel::device::{Bound, Device};
-/// use kernel::irq::{
-/// self, Flags, IrqRequest, IrqReturn, ThreadedHandler, ThreadedIrqReturn,
-/// ThreadedRegistration,
+/// use core::pin::Pin;
+/// use kernel::{
+/// irq::{
+/// self,
+/// Flags,
+/// IrqRequest,
+/// IrqReturn,
+/// ThreadedHandler,
+/// ThreadedIrqReturn,
+/// ThreadedRegistration,
+/// },
+/// prelude::*,
+/// sync::Mutex,
/// };
-/// 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.
@@ -363,7 +327,7 @@ impl<T: ?Sized + ThreadedHandler, A: Allocator + 'static> ThreadedHandler for Bo
/// // This will run (in a separate kthread) if and only if
/// // [`ThreadedHandler::handle`] returns [`WakeThread`], which it does by
/// // default.
-/// fn handle_threaded(&self, _dev: &Device<Bound>) -> IrqReturn {
+/// fn handle_threaded(&self) -> IrqReturn {
/// let mut data = self.value.lock();
/// *data += 1;
/// IrqReturn::Handled
@@ -375,13 +339,21 @@ impl<T: ?Sized + ThreadedHandler, A: Allocator + 'static> ThreadedHandler for Bo
/// // This is executing in process context and assumes that `request` was
/// // previously acquired from a device.
/// fn register_threaded_irq(
-/// handler: impl PinInit<Data, Error>,
/// request: IrqRequest<'_>,
-/// ) -> Result<Arc<ThreadedRegistration<Data>>> {
-/// let registration =
-/// ThreadedRegistration::new(request, Flags::SHARED, c"my_device", handler);
+/// ) -> Result<Pin<KBox<ThreadedRegistration<'_, Data>>>> {
+/// // SAFETY: The returned Registration is not leaked.
+/// let registration = unsafe {
+/// ThreadedRegistration::new(
+/// request,
+/// Flags::SHARED,
+/// c"my_device",
+/// try_pin_init!(Data {
+/// value <- kernel::new_mutex!(0),
+/// }? Error),
+/// )
+/// };
///
-/// let registration = Arc::pin_init(registration, GFP_KERNEL)?;
+/// let registration = KBox::pin_init(registration, GFP_KERNEL)?;
///
/// {
/// // The data can be accessed from process context too.
@@ -396,11 +368,11 @@ impl<T: ?Sized + ThreadedHandler, A: Allocator + 'static> ThreadedHandler for Bo
///
/// # Invariants
///
-/// * We own an irq handler whose cookie is a pointer to `Self`.
-#[pin_data]
-pub struct ThreadedRegistration<T: ThreadedHandler> {
- #[pin]
- inner: Devres<RegistrationInner>,
+/// * 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>,
#[pin]
handler: T,
@@ -411,45 +383,47 @@ pub struct ThreadedRegistration<T: ThreadedHandler> {
_pin: PhantomPinned,
}
-impl<T: ThreadedHandler> ThreadedRegistration<T> {
+impl<'a, T: ThreadedHandler> ThreadedRegistration<'a, T> {
/// Registers the IRQ handler with the system for the given IRQ number.
- pub fn new<'a>(
+ ///
+ /// # Safety
+ ///
+ /// Callers must not `mem::forget()` the returned [`ThreadedRegistration`] or otherwise prevent
+ /// its [`Drop`] implementation from running.
+ pub unsafe fn new(
request: IrqRequest<'a>,
flags: Flags,
name: &'static CStr,
handler: impl PinInit<T, Error> + 'a,
- ) -> impl PinInit<Self, Error> + 'a {
+ ) -> impl PinInit<Self, Error> + 'a
+ where
+ T: 'a,
+ {
+ // INVARIANT: If initialization completes successfully, we own an IRQ handler registered
+ // via `request_threaded_irq` whose cookie is a pointer to `Self`.
try_pin_init!(&this in Self {
handler <- handler,
- inner <- Devres::new(
- request.dev,
- try_pin_init!(RegistrationInner {
- // INVARIANT: `this` is a valid pointer to the `ThreadedRegistration` instance.
- cookie: this.as_ptr().cast::<c_void>(),
- 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.
- // - 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,
- Some(handle_threaded_irq_callback::<T>),
- Some(thread_fn_callback::<T>),
- flags.into_inner(),
- name.as_char_ptr(),
- this.as_ptr().cast::<c_void>(),
- )
- })?;
- request.irq
- }
- })
- ),
+ request,
_pin: PhantomPinned,
+ _: {
+ // 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.
+ // - All fields are already initialized, so it's safe for the callbacks to be
+ // called immediately.
+ to_result(unsafe {
+ bindings::request_threaded_irq(
+ request.irq,
+ Some(handle_threaded_irq_callback::<T>),
+ Some(thread_fn_callback::<T>),
+ flags.into_inner(),
+ name.as_char_ptr(),
+ this.as_ptr().cast::<c_void>(),
+ )
+ })?;
+ },
})
}
@@ -459,19 +433,25 @@ impl<T: ThreadedHandler> ThreadedRegistration<T> {
}
/// 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(())
+ #[inline]
+ pub fn synchronize(&self) {
+ // SAFETY: `self.request.irq` is a valid registered IRQ number (type invariant).
+ unsafe { bindings::synchronize_irq(self.request.irq) };
}
+}
- /// 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(())
+#[pinned_drop]
+impl<T: ThreadedHandler> PinnedDrop for ThreadedRegistration<'_, T> {
+ 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
+ // this returns.
+ unsafe {
+ bindings::free_irq(
+ self.request.irq,
+ core::ptr::from_mut::<Self>(self.get_unchecked_mut()).cast::<c_void>(),
+ )
+ };
}
}
@@ -482,24 +462,22 @@ unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler>(
_irq: i32,
ptr: *mut c_void,
) -> c_uint {
- // SAFETY: `ptr` is a pointer to `ThreadedRegistration<T>` set in `ThreadedRegistration::new`
- let registration = unsafe { &*(ptr as *const ThreadedRegistration<T>) };
- // 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() };
+ let ptr = ptr.cast_const().cast::<ThreadedRegistration<'_, T>>();
+ // SAFETY: `ptr` is a pointer to `ThreadedRegistration<'_, T>` set in
+ // `ThreadedRegistration::new()`.
+ let registration = unsafe { &*ptr };
- T::handle(&registration.handler, device) as c_uint
+ T::handle(&registration.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<T: ThreadedHandler>(_irq: i32, ptr: *mut c_void) -> c_uint {
- // SAFETY: `ptr` is a pointer to `ThreadedRegistration<T>` set in `ThreadedRegistration::new`
- let registration = unsafe { &*(ptr as *const ThreadedRegistration<T>) };
- // 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() };
+ let ptr = ptr.cast_const().cast::<ThreadedRegistration<'_, T>>();
+ // SAFETY: `ptr` is a pointer to `ThreadedRegistration<'_, T>` set in
+ // `ThreadedRegistration::new()`.
+ let registration = unsafe { &*ptr };
- T::handle_threaded(&registration.handler, device) as c_uint
+ T::handle_threaded(&registration.handler) as c_uint
}