use alloc::{boxed::Box, collections::VecDeque};
use spin::Once;
use crate::{
arch::{irq::HwCpuId, trap::TrapFrame},
cpu::{CpuSet, PinCurrentCpu},
cpu_local, irq,
sync::SpinLock,
util::id_set::Id,
};
pub fn inter_processor_call(targets: &CpuSet, f: fn()) {
let ipi_sender = IPI_SENDER.get().unwrap();
ipi_sender.inter_processor_call(targets, f);
}
pub(crate) struct IpiSender {
hw_cpu_ids: Box<[HwCpuId]>,
}
pub(crate) static IPI_SENDER: Once<IpiSender> = Once::new();
impl IpiSender {
pub(crate) fn inter_processor_call(&self, targets: &CpuSet, f: fn()) {
let irq_guard = irq::disable_local();
let this_cpu_id = irq_guard.current_cpu();
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;
}
let hw_cpu_id = self.hw_cpu_ids[cpu_id.as_usize()];
crate::arch::irq::send_ipi(hw_cpu_id, &irq_guard as _);
}
if call_on_self {
f();
}
}
}
cpu_local! {
static CALL_QUEUES: SpinLock<VecDeque<fn()>> = SpinLock::new(VecDeque::new());
}
pub(crate) unsafe fn do_inter_processor_call(_trapframe: &TrapFrame) {
let this_cpu_id = crate::cpu::CpuId::current_racy();
let mut queue = CALL_QUEUES.get_on_cpu(this_cpu_id).lock();
while let Some(f) = queue.pop_front() {
crate::debug!(
"Performing inter-processor call to {:#?} on CPU {:#?}",
f,
this_cpu_id,
);
f();
}
}
pub(super) fn init() {
IPI_SENDER.call_once(|| {
let hw_cpu_ids = crate::boot::smp::construct_hw_cpu_id_mapping();
IpiSender { hw_cpu_ids }
});
}