|
@@ -1,5 +1,12 @@
|
|
|
pub(super) mod def;
|
|
|
|
|
|
+use alloc::vec::Vec;
|
|
|
+use core::{
|
|
|
+ iter,
|
|
|
+ sync::atomic::{AtomicUsize, Ordering},
|
|
|
+};
|
|
|
+
|
|
|
+use archop::Azy;
|
|
|
use array_macro::array;
|
|
|
use collection_ex::RangeMap;
|
|
|
use spin::Mutex;
|
|
@@ -7,7 +14,7 @@ use spin::Mutex;
|
|
|
pub use self::def::{ExVec, ALLOC_VEC};
|
|
|
use super::apic::{Polarity, TriggerMode, LAPIC_ID};
|
|
|
use crate::{
|
|
|
- cpu::{arch::seg::ndt::USR_CODE_X64, intr::IntrHandler, time::Instant, Lazy},
|
|
|
+ cpu::{arch::seg::ndt::USR_CODE_X64, intr::IntrHandler, time::Instant},
|
|
|
dev::ioapic,
|
|
|
mem::space::PageFaultErrCode,
|
|
|
sched::{
|
|
@@ -16,27 +23,34 @@ use crate::{
|
|
|
},
|
|
|
};
|
|
|
|
|
|
-#[thread_local]
|
|
|
-pub static MANAGER: Lazy<Manager> = Lazy::new(|| Manager::new(unsafe { crate::cpu::id() }));
|
|
|
+static MANAGER: Azy<Vec<Manager>> = Azy::new(|| {
|
|
|
+ iter::repeat_with(Default::default)
|
|
|
+ .take(crate::cpu::count())
|
|
|
+ .collect()
|
|
|
+});
|
|
|
|
|
|
pub struct Manager {
|
|
|
- cpu: usize,
|
|
|
map: Mutex<RangeMap<u8, ()>>,
|
|
|
slots: [Mutex<Option<(IntrHandler, *mut u8)>>; u8::MAX as usize + 1],
|
|
|
+ count: AtomicUsize,
|
|
|
}
|
|
|
|
|
|
+unsafe impl Sync for Manager {}
|
|
|
+unsafe impl Send for Manager {}
|
|
|
+
|
|
|
impl Manager {
|
|
|
- pub fn new(cpu: usize) -> Self {
|
|
|
+ pub fn new() -> Self {
|
|
|
Manager {
|
|
|
- cpu,
|
|
|
map: Mutex::new(RangeMap::new(ALLOC_VEC)),
|
|
|
slots: array![_ => Mutex::new(None); 256],
|
|
|
+ count: AtomicUsize::new(0),
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pub fn invoke(&self, vec: u8) {
|
|
|
+ pub fn invoke(vec: u8) {
|
|
|
PREEMPT.scope(|| {
|
|
|
- if let Some((handler, arg)) = *self.slots[vec as usize].lock() {
|
|
|
+ let manager = &MANAGER[unsafe { crate::cpu::id() }];
|
|
|
+ if let Some((handler, arg)) = *manager.slots[vec as usize].lock() {
|
|
|
handler(arg);
|
|
|
} else {
|
|
|
log::trace!("Unhandled interrupt #{:?}", vec);
|
|
@@ -44,67 +58,101 @@ impl Manager {
|
|
|
})
|
|
|
}
|
|
|
|
|
|
- pub fn register(&self, gsi: u32, handler: Option<(IntrHandler, *mut u8)>) -> sv_call::Result {
|
|
|
+ pub fn select_cpu() -> usize {
|
|
|
+ MANAGER
|
|
|
+ .iter()
|
|
|
+ .enumerate()
|
|
|
+ .fold((usize::MAX, usize::MAX), |(acc, iacc), (index, manager)| {
|
|
|
+ let value = manager.count.load(Ordering::Acquire);
|
|
|
+ if value < acc {
|
|
|
+ (value, index)
|
|
|
+ } else {
|
|
|
+ (acc, iacc)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .1
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn register(gsi: u32, cpu: usize, handler: (IntrHandler, *mut u8)) -> sv_call::Result {
|
|
|
let _pree = PREEMPT.lock();
|
|
|
let mut ioapic = ioapic::chip().lock();
|
|
|
let entry = ioapic.get_entry(gsi)?;
|
|
|
|
|
|
- let in_use = ALLOC_VEC.contains(&entry.vec());
|
|
|
-
|
|
|
- let self_apic_id = *LAPIC_ID.read().get(&self.cpu).ok_or(sv_call::EINVAL)?;
|
|
|
- let apic_id = entry.dest_id();
|
|
|
- if in_use && self_apic_id != apic_id {
|
|
|
+ if ALLOC_VEC.contains(&entry.vec()) {
|
|
|
return Err(sv_call::EEXIST);
|
|
|
}
|
|
|
|
|
|
- let vec = in_use.then_some(entry.vec());
|
|
|
-
|
|
|
- if let Some(handler) = handler {
|
|
|
- let mut map = self.map.lock();
|
|
|
- let vec = if let Some(vec) = vec {
|
|
|
- map.try_insert_with(
|
|
|
- vec..(vec + 1),
|
|
|
- || Ok::<_, sv_call::Error>(((), ())),
|
|
|
- sv_call::EEXIST,
|
|
|
- )?;
|
|
|
- vec
|
|
|
- } else {
|
|
|
- map.allocate_with(1, |_| Ok::<_, sv_call::Error>(((), ())), sv_call::ENOMEM)?
|
|
|
- .0
|
|
|
- };
|
|
|
-
|
|
|
- *self.slots[vec as usize].lock() = Some(handler);
|
|
|
- unsafe { ioapic.config_dest(gsi, vec, self_apic_id) }?;
|
|
|
- } else if let Some(vec) = vec {
|
|
|
- *self.slots[vec as usize].lock() = None;
|
|
|
- unsafe { ioapic.deconfig(gsi) }?;
|
|
|
+ let apic_id = *LAPIC_ID.read().get(&cpu).ok_or(sv_call::EINVAL)?;
|
|
|
+ let manager = MANAGER.get(cpu).ok_or(sv_call::ENODEV)?;
|
|
|
+
|
|
|
+ let vec = manager.map.lock().allocate_with::<_, sv_call::Error>(
|
|
|
+ 1,
|
|
|
+ |_| {
|
|
|
+ manager.count.fetch_add(1, Ordering::SeqCst);
|
|
|
+ Ok(())
|
|
|
+ },
|
|
|
+ sv_call::ENOMEM,
|
|
|
+ )?;
|
|
|
+
|
|
|
+ *manager.slots[vec as usize].lock() = Some(handler);
|
|
|
+ unsafe { ioapic.config_dest(gsi, vec, apic_id) }?;
|
|
|
+
|
|
|
+ Ok(())
|
|
|
+ }
|
|
|
+
|
|
|
+ pub fn deregister(gsi: u32, cpu: usize) -> sv_call::Result {
|
|
|
+ let _pree = PREEMPT.lock();
|
|
|
+ let mut ioapic = ioapic::chip().lock();
|
|
|
+ let entry = ioapic.get_entry(gsi)?;
|
|
|
+
|
|
|
+ let vec = entry.vec();
|
|
|
+
|
|
|
+ if !ALLOC_VEC.contains(&vec) {
|
|
|
+ return Err(sv_call::ENOENT);
|
|
|
}
|
|
|
+ let manager = MANAGER.get(cpu).ok_or(sv_call::ENODEV)?;
|
|
|
+
|
|
|
+ *manager.slots[vec as usize].lock() = None;
|
|
|
+ unsafe { ioapic.deconfig(gsi) }?;
|
|
|
+
|
|
|
+ {
|
|
|
+ let mut lock = manager.map.lock();
|
|
|
+ manager.count.fetch_sub(1, Ordering::SeqCst);
|
|
|
+ lock.remove(vec);
|
|
|
+ }
|
|
|
+
|
|
|
Ok(())
|
|
|
}
|
|
|
|
|
|
#[inline]
|
|
|
- pub fn config(&self, gsi: u32, trig_mode: TriggerMode, polarity: Polarity) -> sv_call::Result {
|
|
|
+ pub fn config(gsi: u32, trig_mode: TriggerMode, polarity: Polarity) -> sv_call::Result {
|
|
|
PREEMPT.scope(|| unsafe { ioapic::chip().lock().config(gsi, trig_mode, polarity) })
|
|
|
}
|
|
|
|
|
|
#[inline]
|
|
|
- pub fn mask(&self, gsi: u32, masked: bool) -> sv_call::Result {
|
|
|
+ pub fn mask(gsi: u32, masked: bool) -> sv_call::Result {
|
|
|
PREEMPT.scope(|| unsafe { ioapic::chip().lock().mask(gsi, masked) })
|
|
|
}
|
|
|
|
|
|
#[inline]
|
|
|
- pub fn eoi(&self, gsi: u32) -> sv_call::Result {
|
|
|
+ pub fn eoi(gsi: u32) -> sv_call::Result {
|
|
|
PREEMPT.scope(|| unsafe { ioapic::chip().lock().eoi(gsi) })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+impl Default for Manager {
|
|
|
+ fn default() -> Self {
|
|
|
+ Self::new()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/// # Safety
|
|
|
///
|
|
|
/// This function must only be called from its assembly routine `rout_XX`.
|
|
|
#[no_mangle]
|
|
|
unsafe extern "C" fn common_interrupt(frame: *mut Frame) {
|
|
|
let vec = unsafe { &*frame }.errc_vec as u8;
|
|
|
- MANAGER.invoke(vec);
|
|
|
+ Manager::invoke(vec);
|
|
|
super::apic::lapic(|lapic| lapic.eoi());
|
|
|
crate::sched::SCHED.tick(Instant::now());
|
|
|
}
|
|
@@ -158,5 +206,5 @@ unsafe fn exception(frame_ptr: *mut Frame, vec: def::ExVec) {
|
|
|
|
|
|
#[inline]
|
|
|
pub(super) fn init() {
|
|
|
- Lazy::force(&MANAGER);
|
|
|
+ Azy::force(&MANAGER);
|
|
|
}
|