Kaynağa Gözat

Tweak CPU selection in creating interrupts

徐启航 1 yıl önce
ebeveyn
işleme
29a3b4a859

+ 26 - 27
h2o/kernel/src/cpu/intr/imp.rs

@@ -3,7 +3,7 @@ use alloc::sync::Arc;
 use crossbeam_queue::ArrayQueue;
 use sv_call::Feature;
 
-use super::arch::MANAGER;
+use super::arch::Manager;
 use crate::{
     cpu::time::Instant,
     dev::Resource,
@@ -15,6 +15,7 @@ const MAX_TIMES: usize = 100;
 #[derive(Debug)]
 pub struct Interrupt {
     gsi: u32,
+    cpu: usize,
     last_time: ArrayQueue<Instant>,
     level_triggered: bool,
     event_data: EventData,
@@ -27,7 +28,7 @@ impl Event for Interrupt {
 
     fn wait(&self, waiter: Arc<dyn crate::sched::Waiter>) {
         if self.level_triggered {
-            MANAGER.mask(self.gsi, false).unwrap();
+            Manager::mask(self.gsi, false).unwrap();
         }
         self.wait_impl(waiter);
     }
@@ -38,19 +39,25 @@ impl Event for Interrupt {
         let signal = self.notify_impl(clear, set);
 
         if self.level_triggered {
-            MANAGER.mask(self.gsi, true).unwrap();
+            Manager::mask(self.gsi, true).unwrap();
         }
-        MANAGER.eoi(self.gsi).unwrap();
+        Manager::eoi(self.gsi).unwrap();
         signal
     }
 }
 
 impl Interrupt {
     #[inline]
-    pub fn new(res: &Resource<u32>, gsi: u32, level_triggered: bool) -> sv_call::Result<Arc<Self>> {
+    pub fn new(
+        res: &Resource<u32>,
+        gsi: u32,
+        cpu: usize,
+        level_triggered: bool,
+    ) -> sv_call::Result<Arc<Self>> {
         if res.magic_eq(super::gsi_resource()) && res.range().contains(&gsi) {
             Ok(Arc::try_new(Interrupt {
                 gsi,
+                cpu,
                 last_time: ArrayQueue::new(MAX_TIMES),
                 level_triggered,
                 event_data: EventData::new(0),
@@ -71,6 +78,13 @@ impl Interrupt {
     }
 }
 
+impl Drop for Interrupt {
+    fn drop(&mut self) {
+        self.cancel();
+        let _ = Manager::deregister(self.gsi, self.cpu);
+    }
+}
+
 unsafe impl DefaultFeature for Interrupt {
     fn default_features() -> Feature {
         Feature::SEND | Feature::WAIT
@@ -89,10 +103,7 @@ mod syscall {
 
     use super::*;
     use crate::{
-        cpu::{
-            arch::apic::{Polarity, TriggerMode},
-            intr::arch::MANAGER,
-        },
+        cpu::arch::apic::{Polarity, TriggerMode},
         sched::SCHED,
         syscall::{Out, UserPtr},
     };
@@ -111,18 +122,17 @@ mod syscall {
             Polarity::Low
         };
 
+        let cpu = Manager::select_cpu();
+
         let intr = SCHED.with_current(|cur| {
             let handles = cur.space().handles();
             let res = handles.get::<Resource<u32>>(res)?;
-            Interrupt::new(&res, gsi, level_triggered)
+            Interrupt::new(&res, gsi, cpu, level_triggered)
         })?;
 
-        MANAGER.config(gsi, trig_mode, polarity)?;
-        MANAGER.register(
-            gsi,
-            Some((handler, (&*intr as *const Interrupt) as *mut u8)),
-        )?;
-        MANAGER.mask(gsi, false)?;
+        Manager::config(gsi, trig_mode, polarity)?;
+        Manager::register(gsi, cpu, (handler, (&*intr as *const Interrupt) as *mut u8))?;
+        Manager::mask(gsi, false)?;
 
         let event = Arc::downgrade(&intr) as _;
         SCHED.with_current(|cur| unsafe { cur.space().handles().insert_raw(intr, Some(event)) })
@@ -139,15 +149,4 @@ mod syscall {
             last_time.write(unsafe { data.raw() })
         })
     }
-
-    #[syscall]
-    fn intr_drop(hdl: Handle) -> Result {
-        hdl.check_null()?;
-        SCHED.with_current(|cur| {
-            let intr = cur.space().handles().remove::<Interrupt>(hdl)?;
-            intr.cancel();
-            MANAGER.register(intr.gsi, None)?;
-            Ok(())
-        })
-    }
 }

+ 88 - 40
h2o/kernel/src/cpu/x86_64/intr.rs

@@ -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);
 }

+ 0 - 10
h2o/kernel/syscall/interrupt.json

@@ -34,16 +34,6 @@
                     "ty": "*mut ()"
                 }
             ]
-        },
-        {
-            "name": "sv_intr_drop",
-            "returns": "()",
-            "args": [
-                {
-                    "name": "hdl",
-                    "ty": "Handle"
-                }
-            ]
         }
     ]
 }

+ 4 - 9
h2o/libs/collection_ex/src/range_map.rs

@@ -29,15 +29,10 @@ impl<K, V> RangeMap<K, V> {
         &self.range
     }
 
-    pub fn allocate_with<F, E, R>(
-        &mut self,
-        size: K,
-        value: F,
-        no_fit: impl Into<E>,
-    ) -> Result<(K, R), E>
+    pub fn allocate_with<F, E>(&mut self, size: K, value: F, no_fit: impl Into<E>) -> Result<K, E>
     where
         K: Ord + Sub<Output = K> + Add<Output = K> + Copy,
-        F: FnOnce(Range<K>) -> Result<(V, R), E>,
+        F: FnOnce(Range<K>) -> Result<V, E>,
     {
         let mut range = None;
 
@@ -56,9 +51,9 @@ impl<K, V> RangeMap<K, V> {
 
         if let Some(range) = range {
             let start = range.start;
-            let (value, ret) = value(range.clone())?;
+            let value = value(range.clone())?;
             self.inner.entry(start).or_insert((range, value));
-            Ok((start, ret))
+            Ok(start)
         } else {
             Err(no_fit.into())
         }

+ 1 - 10
src/lib/h2o_rs/src/dev/intr.rs

@@ -8,6 +8,7 @@ use crate::{error::Result, obj::Object, time::Instant};
 #[derive(Debug)]
 pub struct Interrupt(sv_call::Handle);
 crate::impl_obj!(Interrupt, SV_INTERRUPT);
+crate::impl_obj!(@DROP, Interrupt);
 
 impl Interrupt {
     pub fn acquire(res: &GsiRes, gsi: u32, config: IntrConfig) -> Result<Interrupt> {
@@ -41,16 +42,6 @@ impl Interrupt {
     }
 }
 
-impl Drop for Interrupt {
-    fn drop(&mut self) {
-        unsafe {
-            sv_call::sv_intr_drop(unsafe { self.raw() })
-                .into_res()
-                .expect("Failed to drop an interrupt");
-        }
-    }
-}
-
 pub struct PackIntrWait {
     pub ins: u128,
     pub syscall: Syscall,