1use alloc::{boxed::Box, collections::VecDeque};
9
10use spin::Once;
11
12use crate::{
13 arch::{irq::HwCpuId, trap::TrapFrame},
14 cpu::{CpuSet, PinCurrentCpu},
15 cpu_local, irq,
16 sync::SpinLock,
17 util::id_set::Id,
18};
19
20pub fn inter_processor_call(targets: &CpuSet, f: fn()) {
35 let irq_guard = irq::disable_local();
36 let this_cpu_id = irq_guard.current_cpu();
37
38 let ipi_data = IPI_GLOBAL_DATA.get().unwrap();
39
40 let mut call_on_self = false;
41 for cpu_id in targets.iter() {
42 if cpu_id == this_cpu_id {
43 call_on_self = true;
44 continue;
45 }
46 CALL_QUEUES.get_on_cpu(cpu_id).lock().push_back(f);
47 }
48 for cpu_id in targets.iter() {
49 if cpu_id == this_cpu_id {
50 continue;
51 }
52 let hw_cpu_id = ipi_data.hw_cpu_ids[cpu_id.as_usize()];
53 crate::arch::irq::send_ipi(hw_cpu_id, &irq_guard as _);
54 }
55 if call_on_self {
56 f();
58 }
59}
60
61struct IpiGlobalData {
62 hw_cpu_ids: Box<[HwCpuId]>,
63}
64
65static IPI_GLOBAL_DATA: Once<IpiGlobalData> = Once::new();
66
67cpu_local! {
68 static CALL_QUEUES: SpinLock<VecDeque<fn()>> = SpinLock::new(VecDeque::new());
69}
70
71pub(crate) unsafe fn do_inter_processor_call(_trapframe: &TrapFrame) {
78 let this_cpu_id = crate::cpu::CpuId::current_racy();
80
81 let mut queue = CALL_QUEUES.get_on_cpu(this_cpu_id).lock();
82 while let Some(f) = queue.pop_front() {
83 log::trace!(
84 "Performing inter-processor call to {:#?} on CPU {:#?}",
85 f,
86 this_cpu_id,
87 );
88 f();
89 }
90}
91
92pub(super) fn init() {
93 IPI_GLOBAL_DATA.call_once(|| {
94 let hw_cpu_ids = crate::boot::smp::construct_hw_cpu_id_mapping();
95
96 IpiGlobalData { hw_cpu_ids }
97 });
98}