1#![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
23pub fn init() {
25 IPI_EVENT_QUEUE.with_current(|ipi_queue| {
26 ipi_queue.init_once(SpinNoIrq::new(IpiEventQueue::default()));
27 });
28}
29
30pub 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 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
44pub 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 callback.clone().call();
53 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 axhal::irq::send_ipi(
63 IPI_IRQ,
64 IpiTarget::AllExceptCurrent {
65 cpu_id: current_cpu_id,
66 cpu_num,
67 },
68 );
69}
70
71pub 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}