Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506// SPDX-License-Identifier: GPL-2.0// SPDX-FileCopyrightText: Copyright 2025 Collabora ltd.
//! This module provides types like [`Registration`] and//! [`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;
/// 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 + 'static { /// 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, 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) }}
/// # 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<Bound>, 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`. pub(crate) unsafe fn new(dev: &'a Device<Bound>, 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::device::{Bound, Device};/// 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, _dev: &Device<Bound>) -> 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<Data, Error>,/// request: IrqRequest<'_>,/// ) -> Result<Arc<Registration<Data>>> {/// let registration = Registration::new(request, Flags::SHARED, c"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 whose cookie is a pointer to `Self`.#[pin_data]pub struct Registration<T: Handler> { #[pin] inner: Devres<RegistrationInner>,
#[pin] handler: T,
/// Pinned because we need address stability so that we can pass a pointer /// to the callback. #[pin] _pin: PhantomPinned,}
impl<T: Handler> Registration<T> { /// 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<T, Error> + 'a, ) -> impl PinInit<Self, Error> + 'a { 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 } }) ), _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<Bound>) -> 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<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() };
T::handle(®istration.handler, device) 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 + 'static { /// 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`]. #[expect(unused_variables)] fn handle(&self, device: &Device<Bound>) -> 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, 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) }}
/// 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::device::{Bound, Device};/// 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<u32>,/// }////// 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, _dev: &Device<Bound>) -> 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<Data, Error>,/// request: IrqRequest<'_>,/// ) -> Result<Arc<ThreadedRegistration<Data>>> {/// let registration =/// ThreadedRegistration::new(request, Flags::SHARED, c"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 whose cookie is a pointer to `Self`.#[pin_data]pub struct ThreadedRegistration<T: ThreadedHandler> { #[pin] inner: Devres<RegistrationInner>,
#[pin] handler: T,
/// Pinned because we need address stability so that we can pass a pointer /// to the callback. #[pin] _pin: PhantomPinned,}
impl<T: ThreadedHandler> ThreadedRegistration<T> { /// 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<T, Error> + 'a, ) -> impl PinInit<Self, Error> + 'a { 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 } }) ), _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<Bound>) -> 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<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() };
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<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() };
T::handle_threaded(®istration.handler, device) as c_uint}