#![cfg_attr(not(test), no_std)]
#[macro_use]
extern crate log;
extern crate alloc;
use axhal::irq::{IPI_IRQ, IpiTarget};
use axhal::percpu::this_cpu_id;
use kspin::SpinNoIrq;
use lazyinit::LazyInit;
mod event;
mod queue;
pub use event::{Callback, MulticastCallback};
use queue::IpiEventQueue;
#[percpu::def_percpu]
static IPI_EVENT_QUEUE: LazyInit<SpinNoIrq<IpiEventQueue>> = LazyInit::new();
pub fn init() {
IPI_EVENT_QUEUE.with_current(|ipi_queue| {
ipi_queue.init_once(SpinNoIrq::new(IpiEventQueue::default()));
});
}
pub fn run_on_cpu<T: Into<Callback>>(dest_cpu: usize, callback: T) {
info!("Send IPI event to CPU {dest_cpu}");
if dest_cpu == this_cpu_id() {
callback.into().call();
} else {
unsafe { IPI_EVENT_QUEUE.remote_ref_raw(dest_cpu) }
.lock()
.push(this_cpu_id(), callback.into());
axhal::irq::send_ipi(IPI_IRQ, IpiTarget::Other { cpu_id: dest_cpu });
}
}
pub fn run_on_each_cpu<T: Into<MulticastCallback>>(callback: T) {
info!("Send IPI event to all other CPUs");
let current_cpu_id = this_cpu_id();
let cpu_num = axconfig::plat::CPU_NUM;
let callback = callback.into();
callback.clone().call();
for cpu_id in 0..cpu_num {
if cpu_id != current_cpu_id {
unsafe { IPI_EVENT_QUEUE.remote_ref_raw(cpu_id) }
.lock()
.push(current_cpu_id, callback.clone().into_unicast());
}
}
axhal::irq::send_ipi(
IPI_IRQ,
IpiTarget::AllExceptCurrent {
cpu_id: current_cpu_id,
cpu_num,
},
);
}
pub fn ipi_handler() {
while let Some((src_cpu_id, callback)) = unsafe { IPI_EVENT_QUEUE.current_ref_mut_raw() }
.lock()
.pop_one()
{
debug!("Received IPI event from CPU {src_cpu_id}");
callback.call();
}
}