Browse Source

添加per cpu变量支持 (#327)

LoGin 1 year ago
parent
commit
c3dad0011d

+ 4 - 3
kernel/src/arch/x86_64/cpu.rs

@@ -1,12 +1,13 @@
 use core::arch::asm;
 
-use super::asm::current::current_pcb;
+use x86::cpuid::{cpuid, CpuIdResult};
 
 /// @brief 获取当前cpu的apic id
 #[inline]
 pub fn current_cpu_id() -> u32 {
-    // TODO: apic重构后,使用apic id来设置这里
-    current_pcb().cpu_id as u32
+    let cpuid_res: CpuIdResult = cpuid!(0x1);
+    let cpu_id = (cpuid_res.ebx >> 24) & 0xff;
+    return cpu_id;
 }
 
 /// @brief 通过pause指令,让cpu休息一会儿。降低空转功耗

+ 1 - 1
kernel/src/filesystem/vfs/mod.rs

@@ -162,7 +162,7 @@ pub trait IndexNode: Any + Sync + Send + Debug {
     }
 
     /// @brief 重新设置文件的大小
-    /// 
+    ///
     /// 如果文件大小增加,则文件内容不变,但是文件的空洞部分会被填充为0
     /// 如果文件大小减小,则文件内容会被截断
     ///

+ 1 - 0
kernel/src/mm/mod.rs

@@ -28,6 +28,7 @@ pub mod kernel_mapper;
 pub mod mmio_buddy;
 pub mod no_init;
 pub mod page;
+pub mod percpu;
 pub mod syscall;
 pub mod ucontext;
 

+ 90 - 0
kernel/src/mm/percpu.rs

@@ -0,0 +1,90 @@
+use core::sync::atomic::AtomicUsize;
+
+use alloc::vec::Vec;
+
+use crate::{
+    include::bindings::bindings::smp_get_total_cpu, libs::lazy_init::Lazy,
+    smp::core::smp_get_processor_id,
+};
+
+/// 系统中的CPU数量
+///
+/// todo: 待smp模块重构后,从smp模块获取CPU数量。
+/// 目前由于smp模块初始化时机较晚,导致大部分内核模块无法在早期初始化PerCpu变量。
+const CPU_NUM: AtomicUsize = AtomicUsize::new(PerCpu::MAX_CPU_NUM);
+
+#[derive(Debug)]
+pub struct PerCpu;
+
+impl PerCpu {
+    pub const MAX_CPU_NUM: usize = 128;
+    /// # 初始化PerCpu
+    ///
+    /// 该函数应该在内核初始化时调用一次。
+    ///
+    /// 该函数会调用`smp_get_total_cpu()`获取CPU数量,然后将其存储在`CPU_NUM`中。
+    #[allow(dead_code)]
+    pub fn init() {
+        if CPU_NUM.load(core::sync::atomic::Ordering::SeqCst) != 0 {
+            panic!("PerCpu::init() called twice");
+        }
+        let cpus = unsafe { smp_get_total_cpu() } as usize;
+        assert!(cpus > 0, "PerCpu::init(): smp_get_total_cpu() returned 0");
+        CPU_NUM.store(cpus, core::sync::atomic::Ordering::SeqCst);
+    }
+}
+
+/// PerCpu变量
+///
+/// 该结构体的每个实例都是线程安全的,因为每个CPU都有自己的变量。
+///
+/// 一种简单的使用方法是:使用该结构体提供的`define_lazy`方法定义一个全局变量,
+/// 然后在内核初始化时调用`init`、`new`方法去初始化它。
+///
+/// 当然,由于Lazy<T>有运行时开销,所以也可以直接全局声明一个Option,
+/// 然后手动初始化然后赋值到Option中。(这样需要在初始化的时候,手动确保并发安全)
+#[derive(Debug)]
+#[allow(dead_code)]
+pub struct PerCpuVar<T> {
+    inner: Vec<T>,
+}
+
+#[allow(dead_code)]
+impl<T> PerCpuVar<T> {
+    /// # 初始化PerCpu变量
+    ///
+    /// ## 参数
+    ///
+    /// - `data` - 每个CPU的数据的初始值。 传入的Vec的长度必须等于CPU的数量,否则返回None。
+    pub fn new(data: Vec<T>) -> Option<Self> {
+        let cpu_num = CPU_NUM.load(core::sync::atomic::Ordering::SeqCst);
+        if cpu_num == 0 {
+            panic!("PerCpu::init() not called");
+        }
+
+        if data.len() != cpu_num {
+            return None;
+        }
+
+        return Some(Self { inner: data });
+    }
+
+    /// 定义一个Lazy的PerCpu变量,稍后再初始化
+    pub const fn define_lazy() -> Lazy<Self> {
+        Lazy::<Self>::new()
+    }
+
+    pub fn get(&self) -> &T {
+        let cpu_id = smp_get_processor_id();
+        &self.inner[cpu_id as usize]
+    }
+
+    pub fn get_mut(&mut self) -> &mut T {
+        let cpu_id = smp_get_processor_id();
+        &mut self.inner[cpu_id as usize]
+    }
+}
+
+/// PerCpu变量是线程安全的,因为每个CPU都有自己的变量。
+unsafe impl<T> Sync for PerCpuVar<T> {}
+unsafe impl<T> Send for PerCpuVar<T> {}

+ 1 - 1
kernel/src/mm/syscall.rs

@@ -78,7 +78,7 @@ impl Syscall {
         if new_addr == address_space.brk {
             return Ok(address_space.brk);
         }
-        
+
         unsafe {
             address_space
                 .set_brk(VirtAddr::new(page_align_up(new_addr.data())))