parent
2adbc4dcd9
commit
75919dce41
@ -1,82 +1,40 @@
|
|||||||
// Interface for inter-processor interrupt.
|
/// Interface for inter-processor interrupt.
|
||||||
// This module wraps inter-processor interrupt into a broadcast-calling style.
|
/// This module wraps inter-processor interrupt into a broadcast-calling style.
|
||||||
|
|
||||||
use crate::consts::KERNEL_OFFSET;
|
use crate::consts::KERNEL_OFFSET;
|
||||||
use crate::sync::{Semaphore, SpinLock as Mutex};
|
use alloc::boxed::{Box, FnBox};
|
||||||
use alloc::boxed::Box;
|
|
||||||
use alloc::sync::Arc;
|
use alloc::sync::Arc;
|
||||||
use apic::{LocalApic, XApic, LAPIC_ADDR};
|
use apic::{LocalApic, XApic, LAPIC_ADDR};
|
||||||
use lazy_static::*;
|
use core::sync::atomic::{spin_loop_hint, AtomicU8, Ordering};
|
||||||
use rcore_memory::Page;
|
|
||||||
use x86_64::instructions::tlb;
|
|
||||||
use x86_64::VirtAddr;
|
|
||||||
|
|
||||||
struct IPIInvoke<'a, A>(&'a (Fn(&A) -> ()), &'a A);
|
pub type IPIEventItem = Box<FnBox()>;
|
||||||
|
|
||||||
lazy_static! {
|
|
||||||
static ref IPI_INVOKE_LOCK: Mutex<()> = Mutex::new(());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait InvokeEventHandle {
|
|
||||||
fn call(&self);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct InvokeEvent<A: 'static> {
|
|
||||||
function: fn(&A) -> (),
|
|
||||||
argument: Arc<A>,
|
|
||||||
done_semaphore: Arc<Semaphore>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A> InvokeEventHandle for InvokeEvent<A> {
|
|
||||||
fn call(&self) {
|
|
||||||
let arg_ref = self.argument.as_ref();
|
|
||||||
(self.function)(arg_ref);
|
|
||||||
self.done_semaphore.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type IPIEventItem = Box<InvokeEventHandle>;
|
|
||||||
|
|
||||||
// TODO: something fishy is going on here...
|
|
||||||
// In fact, the argument lives as long as the Arc.
|
|
||||||
fn create_item<A: 'static>(f: fn(&A) -> (), arg: &Arc<A>, sem: &Arc<Semaphore>) -> IPIEventItem {
|
|
||||||
Box::new(InvokeEvent {
|
|
||||||
function: f,
|
|
||||||
argument: arg.clone(),
|
|
||||||
done_semaphore: sem.clone(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
unsafe fn get_apic() -> XApic {
|
unsafe fn get_apic() -> XApic {
|
||||||
let mut lapic = unsafe { XApic::new(KERNEL_OFFSET + LAPIC_ADDR) };
|
let mut lapic = unsafe { XApic::new(KERNEL_OFFSET + LAPIC_ADDR) };
|
||||||
lapic
|
lapic
|
||||||
}
|
}
|
||||||
pub fn invoke_on_allcpu<A: 'static>(f: fn(&A) -> (), arg: A, wait: bool) {
|
|
||||||
|
pub fn invoke_on_allcpu(f: impl Fn() + 'static, wait: bool) {
|
||||||
// Step 1: initialize
|
// Step 1: initialize
|
||||||
use super::interrupt::consts::IPIFuncCall;
|
use super::interrupt::consts::IPIFuncCall;
|
||||||
let mut apic = unsafe { get_apic() };
|
let mut apic = unsafe { get_apic() };
|
||||||
let sem = Arc::new(Semaphore::new(0));
|
let func = Arc::new(f);
|
||||||
let arcarg = Arc::new(arg);
|
let cpu_count = super::gdt::Cpu::iter().count();
|
||||||
let mut cpu_count = 0;
|
let rest_count = Arc::new(AtomicU8::new(cpu_count as u8));
|
||||||
// Step 2: invoke
|
// Step 2: invoke
|
||||||
super::gdt::Cpu::foreach(|cpu| {
|
for cpu in super::gdt::Cpu::iter() {
|
||||||
let id = cpu.get_id();
|
let func_clone = func.clone();
|
||||||
cpu_count += 1;
|
let rest_clone = rest_count.clone();
|
||||||
cpu.notify_event(create_item(f, &arcarg, &sem));
|
cpu.notify_event(Box::new(move || {
|
||||||
apic.send_ipi(id as u8, IPIFuncCall);
|
func_clone();
|
||||||
});
|
rest_clone.fetch_sub(1, Ordering::Relaxed);
|
||||||
if wait {
|
}));
|
||||||
for _ in 0..cpu_count {
|
apic.send_ipi(cpu.id() as u8, IPIFuncCall);
|
||||||
sem.acquire();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if wait {
|
||||||
|
// spin if remote invocation do not complete
|
||||||
|
while rest_count.load(Ordering::Relaxed) != 0 {
|
||||||
|
spin_loop_hint();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Examples of such cases.
|
|
||||||
|
|
||||||
pub fn tlb_shootdown(tuple: &(usize, usize)) {
|
|
||||||
// debug!("CPU {}: remote tlb flush {:x?}", super::cpu::id(), tuple);
|
|
||||||
let (start_addr, end_addr) = *tuple;
|
|
||||||
for p in Page::range_of(start_addr, end_addr) {
|
|
||||||
tlb::flush(VirtAddr::new(p.start_address() as u64));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in new issue