ostd/irq/bottom_half.rs
1// SPDX-License-Identifier: MPL-2.0
2
3//! The bottom half of interrupt handling.
4
5use spin::Once;
6
7use super::{DisabledLocalIrqGuard, InterruptLevel, disable_local};
8use crate::task::disable_preempt;
9
10/// Registers a bottom half callback to be executed at interrupt level 1.
11///
12/// The callback takes a [`DisabledLocalIrqGuard`] as the first argument.
13/// This allows the callback to drop the guard
14/// in order to re-enable IRQs on the current CPU.
15/// The callback requires returning a `DisabledLocalIrqGuard`,
16/// thus ensuring that local IRQs are disabled by the end of the callback.
17/// The second argument is the IRQ number being processed.
18///
19/// The function may be called only once; subsequent calls take no effect.
20pub fn register_bottom_half_handler_l1(
21 func: fn(DisabledLocalIrqGuard, u8) -> DisabledLocalIrqGuard,
22) {
23 BOTTOM_HALF_HANDLER_L1.call_once(|| func);
24}
25
26/// Registers a bottom half callback to be executed at interrupt level 2.
27///
28/// Unlike the level 1 bottom half callback,
29/// the level 2 bottom half callback registered with this function
30/// cannot re-enable local IRQs.
31/// The function takes the IRQ number being processed as argument.
32///
33/// The function may be called only once; subsequent calls take no effect.
34pub fn register_bottom_half_handler_l2(func: fn(u8)) {
35 BOTTOM_HALF_HANDLER_L2.call_once(|| func);
36}
37
38static BOTTOM_HALF_HANDLER_L1: Once<fn(DisabledLocalIrqGuard, u8) -> DisabledLocalIrqGuard> =
39 Once::new();
40static BOTTOM_HALF_HANDLER_L2: Once<fn(u8)> = Once::new();
41
42pub(super) fn process(irq_num: u8) {
43 match InterruptLevel::current() {
44 InterruptLevel::L1(_) => process_l1(irq_num),
45 InterruptLevel::L2 => process_l2(irq_num),
46 _ => unreachable!("this function must have been call in interrupt context"),
47 }
48}
49
50fn process_l1(irq_num: u8) {
51 let Some(handler) = BOTTOM_HALF_HANDLER_L1.get() else {
52 return;
53 };
54
55 // We need to disable preemption when processing bottom half since
56 // the interrupt is enabled in this context.
57 // This needs to be done before enabling the local interrupts to
58 // avoid race conditions.
59 let preempt_guard = disable_preempt();
60 crate::arch::irq::enable_local();
61
62 // We need to ensure that local interrupts are disabled
63 // when the handler returns to prevent race conditions.
64 // See <https://github.com/asterinas/asterinas/pull/1623#discussion_r1964709636> for more details.
65 let irq_guard = disable_local();
66 let irq_guard = handler(irq_guard, irq_num);
67
68 // Interrupts should remain disabled when `process_bottom_half` returns,
69 // so we simply forget the guard.
70 core::mem::forget(irq_guard);
71 drop(preempt_guard);
72}
73
74fn process_l2(irq_num: u8) {
75 let Some(handler) = BOTTOM_HALF_HANDLER_L2.get() else {
76 return;
77 };
78
79 handler(irq_num);
80}