axipi/
lib.rs

1//! [ArceOS](https://github.com/arceos-org/arceos) Inter-Processor Interrupt (IPI) primitives.
2
3#![cfg_attr(not(test), no_std)]
4
5#[macro_use]
6extern crate log;
7extern crate alloc;
8
9use axhal::irq::{IPI_IRQ, IpiTarget};
10use axhal::percpu::this_cpu_id;
11use kspin::SpinNoIrq;
12use lazyinit::LazyInit;
13
14mod event;
15mod queue;
16
17pub use event::{Callback, MulticastCallback};
18use queue::IpiEventQueue;
19
20#[percpu::def_percpu]
21static IPI_EVENT_QUEUE: LazyInit<SpinNoIrq<IpiEventQueue>> = LazyInit::new();
22
23/// Initialize the per-CPU IPI event queue.
24pub fn init() {
25    IPI_EVENT_QUEUE.with_current(|ipi_queue| {
26        ipi_queue.init_once(SpinNoIrq::new(IpiEventQueue::default()));
27    });
28}
29
30/// Executes a callback on the specified destination CPU via IPI.
31pub fn run_on_cpu<T: Into<Callback>>(dest_cpu: usize, callback: T) {
32    info!("Send IPI event to CPU {}", dest_cpu);
33    if dest_cpu == this_cpu_id() {
34        // Execute callback on current CPU immediately
35        callback.into().call();
36    } else {
37        unsafe { IPI_EVENT_QUEUE.remote_ref_raw(dest_cpu) }
38            .lock()
39            .push(this_cpu_id(), callback.into());
40        axhal::irq::send_ipi(IPI_IRQ, IpiTarget::Other { cpu_id: dest_cpu });
41    }
42}
43
44/// Executes a callback on all other CPUs via IPI.
45pub fn run_on_each_cpu<T: Into<MulticastCallback>>(callback: T) {
46    info!("Send IPI event to all other CPUs");
47    let current_cpu_id = this_cpu_id();
48    let cpu_num = axconfig::plat::CPU_NUM;
49    let callback = callback.into();
50
51    // Execute callback on current CPU immediately
52    callback.clone().call();
53    // Push the callback to all other CPUs' IPI event queues
54    for cpu_id in 0..cpu_num {
55        if cpu_id != current_cpu_id {
56            unsafe { IPI_EVENT_QUEUE.remote_ref_raw(cpu_id) }
57                .lock()
58                .push(current_cpu_id, callback.clone().into_unicast());
59        }
60    }
61    // Send IPI to all other CPUs to trigger their callbacks
62    axhal::irq::send_ipi(
63        IPI_IRQ,
64        IpiTarget::AllExceptCurrent {
65            cpu_id: current_cpu_id,
66            cpu_num,
67        },
68    );
69}
70
71/// The handler for IPI events. It retrieves the events from the queue and calls the corresponding callbacks.
72pub fn ipi_handler() {
73    while let Some((src_cpu_id, callback)) = unsafe { IPI_EVENT_QUEUE.current_ref_mut_raw() }
74        .lock()
75        .pop_one()
76    {
77        debug!("Received IPI event from CPU {}", src_cpu_id);
78        callback.call();
79    }
80}