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}