kernel/irq/request.rs
1// SPDX-License-Identifier: GPL-2.0
2// SPDX-FileCopyrightText: Copyright 2025 Collabora ltd.
3
4//! This module provides types like [`Registration`] and
5//! [`ThreadedRegistration`], which allow users to register handlers for a given
6//! IRQ line.
7
8use core::marker::PhantomPinned;
9
10use crate::alloc::Allocator;
11use crate::device::{Bound, Device};
12use crate::devres::Devres;
13use crate::error::to_result;
14use crate::irq::flags::Flags;
15use crate::prelude::*;
16use crate::str::CStr;
17use crate::sync::Arc;
18
19/// The value that can be returned from a [`Handler`] or a [`ThreadedHandler`].
20#[repr(u32)]
21pub enum IrqReturn {
22 /// The interrupt was not from this device or was not handled.
23 None = bindings::irqreturn_IRQ_NONE,
24
25 /// The interrupt was handled by this device.
26 Handled = bindings::irqreturn_IRQ_HANDLED,
27}
28
29/// Callbacks for an IRQ handler.
30pub trait Handler: Sync {
31 /// The hard IRQ handler.
32 ///
33 /// This is executed in interrupt context, hence all corresponding
34 /// limitations do apply.
35 ///
36 /// All work that does not necessarily need to be executed from
37 /// interrupt context, should be deferred to a threaded handler.
38 /// See also [`ThreadedRegistration`].
39 fn handle(&self, device: &Device<Bound>) -> IrqReturn;
40}
41
42impl<T: ?Sized + Handler + Send> Handler for Arc<T> {
43 fn handle(&self, device: &Device<Bound>) -> IrqReturn {
44 T::handle(self, device)
45 }
46}
47
48impl<T: ?Sized + Handler, A: Allocator> Handler for Box<T, A> {
49 fn handle(&self, device: &Device<Bound>) -> IrqReturn {
50 T::handle(self, device)
51 }
52}
53
54/// # Invariants
55///
56/// - `self.irq` is the same as the one passed to `request_{threaded}_irq`.
57/// - `cookie` was passed to `request_{threaded}_irq` as the cookie. It is guaranteed to be unique
58/// by the type system, since each call to `new` will return a different instance of
59/// `Registration`.
60#[pin_data(PinnedDrop)]
61struct RegistrationInner {
62 irq: u32,
63 cookie: *mut c_void,
64}
65
66impl RegistrationInner {
67 fn synchronize(&self) {
68 // SAFETY: safe as per the invariants of `RegistrationInner`
69 unsafe { bindings::synchronize_irq(self.irq) };
70 }
71}
72
73#[pinned_drop]
74impl PinnedDrop for RegistrationInner {
75 fn drop(self: Pin<&mut Self>) {
76 // SAFETY:
77 //
78 // Safe as per the invariants of `RegistrationInner` and:
79 //
80 // - The containing struct is `!Unpin` and was initialized using
81 // pin-init, so it occupied the same memory location for the entirety of
82 // its lifetime.
83 //
84 // Notice that this will block until all handlers finish executing,
85 // i.e.: at no point will &self be invalid while the handler is running.
86 unsafe { bindings::free_irq(self.irq, self.cookie) };
87 }
88}
89
90// SAFETY: We only use `inner` on drop, which called at most once with no
91// concurrent access.
92unsafe impl Sync for RegistrationInner {}
93
94// SAFETY: It is safe to send `RegistrationInner` across threads.
95unsafe impl Send for RegistrationInner {}
96
97/// A request for an IRQ line for a given device.
98///
99/// # Invariants
100///
101/// - `ìrq` is the number of an interrupt source of `dev`.
102/// - `irq` has not been registered yet.
103pub struct IrqRequest<'a> {
104 dev: &'a Device<Bound>,
105 irq: u32,
106}
107
108impl<'a> IrqRequest<'a> {
109 /// Creates a new IRQ request for the given device and IRQ number.
110 ///
111 /// # Safety
112 ///
113 /// - `irq` should be a valid IRQ number for `dev`.
114 pub(crate) unsafe fn new(dev: &'a Device<Bound>, irq: u32) -> Self {
115 // INVARIANT: `irq` is a valid IRQ number for `dev`.
116 IrqRequest { dev, irq }
117 }
118
119 /// Returns the IRQ number of an [`IrqRequest`].
120 pub fn irq(&self) -> u32 {
121 self.irq
122 }
123}
124
125/// A registration of an IRQ handler for a given IRQ line.
126///
127/// # Examples
128///
129/// The following is an example of using `Registration`. It uses a
130/// [`Completion`] to coordinate between the IRQ
131/// handler and process context. [`Completion`] uses interior mutability, so the
132/// handler can signal with [`Completion::complete_all()`] and the process
133/// context can wait with [`Completion::wait_for_completion()`] even though
134/// there is no way to get a mutable reference to the any of the fields in
135/// `Data`.
136///
137/// [`Completion`]: kernel::sync::Completion
138/// [`Completion::complete_all()`]: kernel::sync::Completion::complete_all
139/// [`Completion::wait_for_completion()`]: kernel::sync::Completion::wait_for_completion
140///
141/// ```
142/// use kernel::device::{Bound, Device};
143/// use kernel::irq::{self, Flags, IrqRequest, IrqReturn, Registration};
144/// use kernel::prelude::*;
145/// use kernel::sync::{Arc, Completion};
146///
147/// // Data shared between process and IRQ context.
148/// #[pin_data]
149/// struct Data {
150/// #[pin]
151/// completion: Completion,
152/// }
153///
154/// impl irq::Handler for Data {
155/// // Executed in IRQ context.
156/// fn handle(&self, _dev: &Device<Bound>) -> IrqReturn {
157/// self.completion.complete_all();
158/// IrqReturn::Handled
159/// }
160/// }
161///
162/// // Registers an IRQ handler for the given IrqRequest.
163/// //
164/// // This runs in process context and assumes `request` was previously acquired from a device.
165/// fn register_irq(
166/// handler: impl PinInit<Data, Error>,
167/// request: IrqRequest<'_>,
168/// ) -> Result<Arc<Registration<Data>>> {
169/// let registration = Registration::new(request, Flags::SHARED, c"my_device", handler);
170///
171/// let registration = Arc::pin_init(registration, GFP_KERNEL)?;
172///
173/// registration.handler().completion.wait_for_completion();
174///
175/// Ok(registration)
176/// }
177/// # Ok::<(), Error>(())
178/// ```
179///
180/// # Invariants
181///
182/// * We own an irq handler whose cookie is a pointer to `Self`.
183#[pin_data]
184pub struct Registration<T: Handler + 'static> {
185 #[pin]
186 inner: Devres<RegistrationInner>,
187
188 #[pin]
189 handler: T,
190
191 /// Pinned because we need address stability so that we can pass a pointer
192 /// to the callback.
193 #[pin]
194 _pin: PhantomPinned,
195}
196
197impl<T: Handler + 'static> Registration<T> {
198 /// Registers the IRQ handler with the system for the given IRQ number.
199 pub fn new<'a>(
200 request: IrqRequest<'a>,
201 flags: Flags,
202 name: &'static CStr,
203 handler: impl PinInit<T, Error> + 'a,
204 ) -> impl PinInit<Self, Error> + 'a {
205 try_pin_init!(&this in Self {
206 handler <- handler,
207 inner <- Devres::new(
208 request.dev,
209 try_pin_init!(RegistrationInner {
210 // INVARIANT: `this` is a valid pointer to the `Registration` instance
211 cookie: this.as_ptr().cast::<c_void>(),
212 irq: {
213 // SAFETY:
214 // - The callbacks are valid for use with request_irq.
215 // - If this succeeds, the slot is guaranteed to be valid until the
216 // destructor of Self runs, which will deregister the callbacks
217 // before the memory location becomes invalid.
218 // - When request_irq is called, everything that handle_irq_callback will
219 // touch has already been initialized, so it's safe for the callback to
220 // be called immediately.
221 to_result(unsafe {
222 bindings::request_irq(
223 request.irq,
224 Some(handle_irq_callback::<T>),
225 flags.into_inner(),
226 name.as_char_ptr(),
227 this.as_ptr().cast::<c_void>(),
228 )
229 })?;
230 request.irq
231 }
232 })
233 ),
234 _pin: PhantomPinned,
235 })
236 }
237
238 /// Returns a reference to the handler that was registered with the system.
239 pub fn handler(&self) -> &T {
240 &self.handler
241 }
242
243 /// Wait for pending IRQ handlers on other CPUs.
244 ///
245 /// This will attempt to access the inner [`Devres`] container.
246 pub fn try_synchronize(&self) -> Result {
247 let inner = self.inner.try_access().ok_or(ENODEV)?;
248 inner.synchronize();
249 Ok(())
250 }
251
252 /// Wait for pending IRQ handlers on other CPUs.
253 pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
254 let inner = self.inner.access(dev)?;
255 inner.synchronize();
256 Ok(())
257 }
258}
259
260/// # Safety
261///
262/// This function should be only used as the callback in `request_irq`.
263unsafe extern "C" fn handle_irq_callback<T: Handler>(_irq: i32, ptr: *mut c_void) -> c_uint {
264 // SAFETY: `ptr` is a pointer to `Registration<T>` set in `Registration::new`
265 let registration = unsafe { &*(ptr as *const Registration<T>) };
266 // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq
267 // callback is running implies that the device has not yet been unbound.
268 let device = unsafe { registration.inner.device().as_bound() };
269
270 T::handle(®istration.handler, device) as c_uint
271}
272
273/// The value that can be returned from [`ThreadedHandler::handle`].
274#[repr(u32)]
275pub enum ThreadedIrqReturn {
276 /// The interrupt was not from this device or was not handled.
277 None = bindings::irqreturn_IRQ_NONE,
278
279 /// The interrupt was handled by this device.
280 Handled = bindings::irqreturn_IRQ_HANDLED,
281
282 /// The handler wants the handler thread to wake up.
283 WakeThread = bindings::irqreturn_IRQ_WAKE_THREAD,
284}
285
286/// Callbacks for a threaded IRQ handler.
287pub trait ThreadedHandler: Sync {
288 /// The hard IRQ handler.
289 ///
290 /// This is executed in interrupt context, hence all corresponding
291 /// limitations do apply. All work that does not necessarily need to be
292 /// executed from interrupt context, should be deferred to the threaded
293 /// handler, i.e. [`ThreadedHandler::handle_threaded`].
294 ///
295 /// The default implementation returns [`ThreadedIrqReturn::WakeThread`].
296 #[expect(unused_variables)]
297 fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn {
298 ThreadedIrqReturn::WakeThread
299 }
300
301 /// The threaded IRQ handler.
302 ///
303 /// This is executed in process context. The kernel creates a dedicated
304 /// `kthread` for this purpose.
305 fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn;
306}
307
308impl<T: ?Sized + ThreadedHandler + Send> ThreadedHandler for Arc<T> {
309 fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn {
310 T::handle(self, device)
311 }
312
313 fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn {
314 T::handle_threaded(self, device)
315 }
316}
317
318impl<T: ?Sized + ThreadedHandler, A: Allocator> ThreadedHandler for Box<T, A> {
319 fn handle(&self, device: &Device<Bound>) -> ThreadedIrqReturn {
320 T::handle(self, device)
321 }
322
323 fn handle_threaded(&self, device: &Device<Bound>) -> IrqReturn {
324 T::handle_threaded(self, device)
325 }
326}
327
328/// A registration of a threaded IRQ handler for a given IRQ line.
329///
330/// Two callbacks are required: one to handle the IRQ, and one to handle any
331/// other work in a separate thread.
332///
333/// The thread handler is only called if the IRQ handler returns
334/// [`ThreadedIrqReturn::WakeThread`].
335///
336/// # Examples
337///
338/// The following is an example of using [`ThreadedRegistration`]. It uses a
339/// [`Mutex`](kernel::sync::Mutex) to provide interior mutability.
340///
341/// ```
342/// use kernel::device::{Bound, Device};
343/// use kernel::irq::{
344/// self, Flags, IrqRequest, IrqReturn, ThreadedHandler, ThreadedIrqReturn,
345/// ThreadedRegistration,
346/// };
347/// use kernel::prelude::*;
348/// use kernel::sync::{Arc, Mutex};
349///
350/// // Declare a struct that will be passed in when the interrupt fires. The u32
351/// // merely serves as an example of some internal data.
352/// //
353/// // [`irq::ThreadedHandler::handle`] takes `&self`. This example
354/// // illustrates how interior mutability can be used when sharing the data
355/// // between process context and IRQ context.
356/// #[pin_data]
357/// struct Data {
358/// #[pin]
359/// value: Mutex<u32>,
360/// }
361///
362/// impl ThreadedHandler for Data {
363/// // This will run (in a separate kthread) if and only if
364/// // [`ThreadedHandler::handle`] returns [`WakeThread`], which it does by
365/// // default.
366/// fn handle_threaded(&self, _dev: &Device<Bound>) -> IrqReturn {
367/// let mut data = self.value.lock();
368/// *data += 1;
369/// IrqReturn::Handled
370/// }
371/// }
372///
373/// // Registers a threaded IRQ handler for the given [`IrqRequest`].
374/// //
375/// // This is executing in process context and assumes that `request` was
376/// // previously acquired from a device.
377/// fn register_threaded_irq(
378/// handler: impl PinInit<Data, Error>,
379/// request: IrqRequest<'_>,
380/// ) -> Result<Arc<ThreadedRegistration<Data>>> {
381/// let registration =
382/// ThreadedRegistration::new(request, Flags::SHARED, c"my_device", handler);
383///
384/// let registration = Arc::pin_init(registration, GFP_KERNEL)?;
385///
386/// {
387/// // The data can be accessed from process context too.
388/// let mut data = registration.handler().value.lock();
389/// *data += 1;
390/// }
391///
392/// Ok(registration)
393/// }
394/// # Ok::<(), Error>(())
395/// ```
396///
397/// # Invariants
398///
399/// * We own an irq handler whose cookie is a pointer to `Self`.
400#[pin_data]
401pub struct ThreadedRegistration<T: ThreadedHandler + 'static> {
402 #[pin]
403 inner: Devres<RegistrationInner>,
404
405 #[pin]
406 handler: T,
407
408 /// Pinned because we need address stability so that we can pass a pointer
409 /// to the callback.
410 #[pin]
411 _pin: PhantomPinned,
412}
413
414impl<T: ThreadedHandler + 'static> ThreadedRegistration<T> {
415 /// Registers the IRQ handler with the system for the given IRQ number.
416 pub fn new<'a>(
417 request: IrqRequest<'a>,
418 flags: Flags,
419 name: &'static CStr,
420 handler: impl PinInit<T, Error> + 'a,
421 ) -> impl PinInit<Self, Error> + 'a {
422 try_pin_init!(&this in Self {
423 handler <- handler,
424 inner <- Devres::new(
425 request.dev,
426 try_pin_init!(RegistrationInner {
427 // INVARIANT: `this` is a valid pointer to the `ThreadedRegistration` instance.
428 cookie: this.as_ptr().cast::<c_void>(),
429 irq: {
430 // SAFETY:
431 // - The callbacks are valid for use with request_threaded_irq.
432 // - If this succeeds, the slot is guaranteed to be valid until the
433 // destructor of Self runs, which will deregister the callbacks
434 // before the memory location becomes invalid.
435 // - When request_threaded_irq is called, everything that the two callbacks
436 // will touch has already been initialized, so it's safe for the
437 // callbacks to be called immediately.
438 to_result(unsafe {
439 bindings::request_threaded_irq(
440 request.irq,
441 Some(handle_threaded_irq_callback::<T>),
442 Some(thread_fn_callback::<T>),
443 flags.into_inner(),
444 name.as_char_ptr(),
445 this.as_ptr().cast::<c_void>(),
446 )
447 })?;
448 request.irq
449 }
450 })
451 ),
452 _pin: PhantomPinned,
453 })
454 }
455
456 /// Returns a reference to the handler that was registered with the system.
457 pub fn handler(&self) -> &T {
458 &self.handler
459 }
460
461 /// Wait for pending IRQ handlers on other CPUs.
462 ///
463 /// This will attempt to access the inner [`Devres`] container.
464 pub fn try_synchronize(&self) -> Result {
465 let inner = self.inner.try_access().ok_or(ENODEV)?;
466 inner.synchronize();
467 Ok(())
468 }
469
470 /// Wait for pending IRQ handlers on other CPUs.
471 pub fn synchronize(&self, dev: &Device<Bound>) -> Result {
472 let inner = self.inner.access(dev)?;
473 inner.synchronize();
474 Ok(())
475 }
476}
477
478/// # Safety
479///
480/// This function should be only used as the callback in `request_threaded_irq`.
481unsafe extern "C" fn handle_threaded_irq_callback<T: ThreadedHandler>(
482 _irq: i32,
483 ptr: *mut c_void,
484) -> c_uint {
485 // SAFETY: `ptr` is a pointer to `ThreadedRegistration<T>` set in `ThreadedRegistration::new`
486 let registration = unsafe { &*(ptr as *const ThreadedRegistration<T>) };
487 // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq
488 // callback is running implies that the device has not yet been unbound.
489 let device = unsafe { registration.inner.device().as_bound() };
490
491 T::handle(®istration.handler, device) as c_uint
492}
493
494/// # Safety
495///
496/// This function should be only used as the callback in `request_threaded_irq`.
497unsafe extern "C" fn thread_fn_callback<T: ThreadedHandler>(_irq: i32, ptr: *mut c_void) -> c_uint {
498 // SAFETY: `ptr` is a pointer to `ThreadedRegistration<T>` set in `ThreadedRegistration::new`
499 let registration = unsafe { &*(ptr as *const ThreadedRegistration<T>) };
500 // SAFETY: The irq callback is removed before the device is unbound, so the fact that the irq
501 // callback is running implies that the device has not yet been unbound.
502 let device = unsafe { registration.inner.device().as_bound() };
503
504 T::handle_threaded(®istration.handler, device) as c_uint
505}