diff options
Diffstat (limited to 'rust/kernel/irq/request.rs')
| -rw-r--r-- | rust/kernel/irq/request.rs | 507 | 
1 files changed, 507 insertions, 0 deletions
| diff --git a/rust/kernel/irq/request.rs b/rust/kernel/irq/request.rs new file mode 100644 index 000000000000..b150563fdef8 --- /dev/null +++ b/rust/kernel/irq/request.rs @@ -0,0 +1,507 @@ +// 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 { +    /// 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> 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::c_str; +/// 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_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 whose cookie is a pointer to `Self`. +#[pin_data] +pub struct Registration<T: Handler + 'static> { +    #[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 + 'static> 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 { +    /// 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> 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::c_str; +/// 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_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 whose cookie is a pointer to `Self`. +#[pin_data] +pub struct ThreadedRegistration<T: ThreadedHandler + 'static> { +    #[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 + 'static> 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 +} | 
