use alloc::collections::VecDeque;
use spin::Once;
use crate::{
cpu::{CpuSet, PinCurrentCpu},
cpu_local,
sync::SpinLock,
trap::{self, IrqLine, TrapFrame},
};
pub fn inter_processor_call(targets: &CpuSet, f: fn()) {
let irq_guard = trap::disable_local();
let this_cpu_id = irq_guard.current_cpu();
let irq_num = INTER_PROCESSOR_CALL_IRQ.get().unwrap().num();
let mut call_on_self = false;
for cpu_id in targets.iter() {
if cpu_id == this_cpu_id {
call_on_self = true;
continue;
}
CALL_QUEUES.get_on_cpu(cpu_id).lock().push_back(f);
}
for cpu_id in targets.iter() {
if cpu_id == this_cpu_id {
continue;
}
unsafe {
crate::arch::irq::send_ipi(cpu_id, irq_num);
}
}
if call_on_self {
f();
}
}
static INTER_PROCESSOR_CALL_IRQ: Once<IrqLine> = Once::new();
cpu_local! {
static CALL_QUEUES: SpinLock<VecDeque<fn()>> = SpinLock::new(VecDeque::new());
}
fn do_inter_processor_call(_trapframe: &TrapFrame) {
let preempt_guard = trap::disable_local();
let cur_cpu = preempt_guard.current_cpu();
let mut queue = CALL_QUEUES.get_on_cpu(cur_cpu).lock();
while let Some(f) = queue.pop_front() {
log::trace!(
"Performing inter-processor call to {:#?} on CPU {}",
f,
cur_cpu
);
f();
}
}
pub(super) fn init() {
let mut irq = IrqLine::alloc().unwrap();
irq.on_active(do_inter_processor_call);
INTER_PROCESSOR_CALL_IRQ.call_once(|| irq);
}