Browse Source

实现页面反向映射 (#670)

* 实现页面反向映射

* 完善PAGE_MANAGER初始化时机 && 封装lock函数 && 删掉过时注释
Jomo 11 months ago
parent
commit
56cc4dbe27

+ 6 - 2
kernel/src/driver/net/dma.rs

@@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags;
 use crate::arch::MMArch;
 
 use crate::mm::kernel_mapper::KernelMapper;
-use crate::mm::page::PageFlags;
+use crate::mm::page::{page_manager_lock_irasave, PageFlags};
 use crate::mm::{
     allocator::page_frame::{
         allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame,
@@ -57,7 +57,11 @@ pub unsafe fn dma_dealloc(paddr: usize, vaddr: NonNull<u8>, pages: usize) -> i32
     flusher.flush();
 
     unsafe {
-        deallocate_page_frames(PhysPageFrame::new(PhysAddr::new(paddr)), page_count);
+        deallocate_page_frames(
+            PhysPageFrame::new(PhysAddr::new(paddr)),
+            page_count,
+            &mut page_manager_lock_irasave(),
+        );
     }
     return 0;
 }

+ 6 - 2
kernel/src/driver/virtio/virtio_impl.rs

@@ -3,7 +3,7 @@ use crate::arch::mm::kernel_page_flags;
 use crate::arch::MMArch;
 
 use crate::mm::kernel_mapper::KernelMapper;
-use crate::mm::page::PageFlags;
+use crate::mm::page::{page_manager_lock_irasave, PageFlags};
 use crate::mm::{
     allocator::page_frame::{
         allocate_page_frames, deallocate_page_frames, PageFrameCount, PhysPageFrame,
@@ -68,7 +68,11 @@ unsafe impl Hal for HalImpl {
         flusher.flush();
 
         unsafe {
-            deallocate_page_frames(PhysPageFrame::new(PhysAddr::new(paddr)), page_count);
+            deallocate_page_frames(
+                PhysPageFrame::new(PhysAddr::new(paddr)),
+                page_count,
+                &mut page_manager_lock_irasave(),
+            );
         }
         return 0;
     }

+ 14 - 1
kernel/src/mm/allocator/page_frame.rs

@@ -5,6 +5,7 @@ use core::{
 
 use crate::{
     arch::{mm::LockedFrameAllocator, MMArch},
+    libs::spinlock::SpinLockGuard,
     mm::{MemoryManagementArch, PhysAddr, VirtAddr},
 };
 
@@ -348,8 +349,20 @@ pub unsafe fn allocate_page_frames(count: PageFrameCount) -> Option<(PhysAddr, P
 ///
 /// @param frame 要释放的第一个页帧
 /// @param count 要释放的页帧数量 (必须是2的n次幂)
-pub unsafe fn deallocate_page_frames(frame: PhysPageFrame, count: PageFrameCount) {
+pub unsafe fn deallocate_page_frames(
+    frame: PhysPageFrame,
+    count: PageFrameCount,
+    page_manager: &mut SpinLockGuard<'_, crate::mm::page::PageManager>,
+) {
     unsafe {
         LockedFrameAllocator.free(frame.phys_address(), count);
     }
+
+    // 将已回收的物理页面对应的Page从PAGE_MANAGER中删去
+    let mut frame = frame;
+    for _ in 0..count.data() {
+        let paddr = frame.phys_address();
+        page_manager.remove_page(&paddr);
+        frame = frame.next();
+    }
 }

+ 7 - 2
kernel/src/mm/init.rs

@@ -1,8 +1,11 @@
 use core::{fmt::Write, sync::atomic::Ordering};
 
 use crate::{
-    arch::MMArch, driver::serial::serial8250::send_to_default_serial8250_port,
-    filesystem::procfs::kmsg::kmsg_init, libs::printk::PrintkWriter, mm::mmio_buddy::mmio_init,
+    arch::MMArch,
+    driver::serial::serial8250::send_to_default_serial8250_port,
+    filesystem::procfs::kmsg::kmsg_init,
+    libs::printk::PrintkWriter,
+    mm::{mmio_buddy::mmio_init, page::page_manager_init},
 };
 
 use super::MemoryManagementArch;
@@ -44,6 +47,8 @@ pub unsafe fn mm_init() {
     mmio_init();
     // enable KMSG
     kmsg_init();
+    // enable PAGE_MANAGER
+    page_manager_init();
 
     MM_INIT
         .compare_exchange(

+ 89 - 2
kernel/src/mm/page.rs

@@ -6,17 +6,102 @@ use core::{
     sync::atomic::{compiler_fence, Ordering},
 };
 
+use alloc::sync::Arc;
+use hashbrown::{HashMap, HashSet};
+
 use crate::{
     arch::{interrupt::ipi::send_ipi, MMArch},
     exception::ipi::{IpiKind, IpiTarget},
     kerror, kwarn,
+    libs::spinlock::{SpinLock, SpinLockGuard},
 };
 
 use super::{
-    allocator::page_frame::FrameAllocator, syscall::ProtFlags, MemoryManagementArch, PageTableKind,
-    PhysAddr, VirtAddr,
+    allocator::page_frame::FrameAllocator, syscall::ProtFlags, ucontext::LockedVMA,
+    MemoryManagementArch, PageTableKind, PhysAddr, VirtAddr,
 };
 
+/// 全局物理页信息管理器
+pub static mut PAGE_MANAGER: Option<SpinLock<PageManager>> = None;
+
+/// 初始化PAGE_MANAGER
+pub fn page_manager_init() {
+    kinfo!("page_manager_init");
+    let page_manager = SpinLock::new(PageManager::new());
+
+    compiler_fence(Ordering::SeqCst);
+    unsafe { PAGE_MANAGER = Some(page_manager) };
+    compiler_fence(Ordering::SeqCst);
+
+    kinfo!("page_manager_init done");
+}
+
+pub fn page_manager_lock_irasave() -> SpinLockGuard<'static, PageManager> {
+    unsafe { PAGE_MANAGER.as_ref().unwrap().lock_irqsave() }
+}
+
+// 物理页管理器
+pub struct PageManager {
+    phys2page: HashMap<PhysAddr, Page>,
+}
+
+impl PageManager {
+    pub fn new() -> Self {
+        Self {
+            phys2page: HashMap::new(),
+        }
+    }
+
+    pub fn get_mut(&mut self, paddr: &PhysAddr) -> &mut Page {
+        self.phys2page.get_mut(paddr).unwrap()
+    }
+
+    pub fn insert(&mut self, paddr: PhysAddr, page: Page) {
+        self.phys2page.insert(paddr, page);
+    }
+
+    pub fn remove_page(&mut self, paddr: &PhysAddr) {
+        self.phys2page.remove(paddr);
+    }
+}
+
+/// 物理页面信息
+pub struct Page {
+    /// 映射计数
+    map_count: usize,
+    /// 是否为共享页
+    shared: bool,
+    /// 映射到当前page的VMA
+    anon_vma: HashSet<Arc<LockedVMA>>,
+}
+
+impl Page {
+    pub fn new(shared: bool) -> Self {
+        Self {
+            map_count: 0,
+            shared,
+            anon_vma: HashSet::new(),
+        }
+    }
+
+    /// 将vma加入anon_vma
+    pub fn insert_vma(&mut self, vma: Arc<LockedVMA>) {
+        self.anon_vma.insert(vma);
+        self.map_count += 1;
+    }
+
+    /// 将vma从anon_vma中删去
+    pub fn remove_vma(&mut self, vma: &LockedVMA) {
+        self.anon_vma.remove(vma);
+        self.map_count -= 1;
+    }
+
+    /// 判断当前物理页是否能被回
+    pub fn can_deallocate(&self) -> bool {
+        self.map_count == 0 && !self.shared
+    }
+}
+
 #[derive(Debug)]
 pub struct PageTable<Arch> {
     /// 当前页表表示的虚拟地址空间的起始地址
@@ -591,6 +676,8 @@ impl<Arch: MemoryManagementArch, F: FrameAllocator> PageMapper<Arch, F> {
         compiler_fence(Ordering::SeqCst);
         let phys: PhysAddr = self.frame_allocator.allocate_one()?;
         compiler_fence(Ordering::SeqCst);
+
+        page_manager_lock_irasave().insert(phys, Page::new(false));
         return self.map_phys(virt, phys, flags);
     }
 

+ 100 - 39
kernel/src/mm/ucontext.rs

@@ -14,6 +14,7 @@ use alloc::{
     vec::Vec,
 };
 use hashbrown::HashSet;
+use ida::IdAllocator;
 use system_error::SystemError;
 
 use crate::{
@@ -24,6 +25,7 @@ use crate::{
         rwlock::{RwLock, RwLockWriteGuard},
         spinlock::{SpinLock, SpinLockGuard},
     },
+    mm::page::page_manager_lock_irasave,
     process::ProcessManager,
     syscall::user_access::{UserBufferReader, UserBufferWriter},
 };
@@ -50,6 +52,9 @@ use super::{
 //   protection by setting the value to 0.
 pub const DEFAULT_MMAP_MIN_ADDR: usize = 65536;
 
+/// LockedVMA的id分配器
+static LOCKEDVMA_ID_ALLOCATOR: IdAllocator = IdAllocator::new(0, usize::MAX);
+
 #[derive(Debug)]
 pub struct AddressSpace {
     inner: RwLock<InnerAddressSpace>,
@@ -464,7 +469,7 @@ impl InnerAddressSpace {
             let r = r.lock().region;
             let r = self.mappings.remove_vma(&r).unwrap();
             let intersection = r.lock().region().intersect(&to_unmap).unwrap();
-            let split_result = r.extract(intersection).unwrap();
+            let split_result = r.extract(intersection, &self.user_mapper.utable).unwrap();
 
             // TODO: 当引入后备页映射后,这里需要增加通知文件的逻辑
 
@@ -519,7 +524,9 @@ impl InnerAddressSpace {
             let r = self.mappings.remove_vma(&r).unwrap();
 
             let intersection = r.lock().region().intersect(&region).unwrap();
-            let split_result = r.extract(intersection).expect("Failed to extract VMA");
+            let split_result = r
+                .extract(intersection, mapper)
+                .expect("Failed to extract VMA");
 
             if let Some(before) = split_result.prev {
                 self.mappings.insert_vma(before);
@@ -663,6 +670,7 @@ impl Drop for UserMapper {
             deallocate_page_frames(
                 PhysPageFrame::new(self.utable.table().phys()),
                 PageFrameCount::new(1),
+                &mut page_manager_lock_irasave(),
             )
         };
     }
@@ -870,17 +878,21 @@ impl Default for UserMappings {
 ///
 /// 备注:进行性能测试,看看SpinLock和RwLock哪个更快。
 #[derive(Debug)]
-pub struct LockedVMA(SpinLock<VMA>);
+pub struct LockedVMA {
+    /// 用于计算哈希值,避免总是获取vma锁来计算哈希值
+    id: usize,
+    vma: SpinLock<VMA>,
+}
 
 impl core::hash::Hash for LockedVMA {
     fn hash<H: Hasher>(&self, state: &mut H) {
-        self.0.lock().hash(state);
+        self.id.hash(state);
     }
 }
 
 impl PartialEq for LockedVMA {
     fn eq(&self, other: &Self) -> bool {
-        self.0.lock().eq(&other.0.lock())
+        self.id.eq(&other.id)
     }
 }
 
@@ -889,13 +901,16 @@ impl Eq for LockedVMA {}
 #[allow(dead_code)]
 impl LockedVMA {
     pub fn new(vma: VMA) -> Arc<Self> {
-        let r = Arc::new(Self(SpinLock::new(vma)));
-        r.0.lock().self_ref = Arc::downgrade(&r);
+        let r = Arc::new(Self {
+            id: LOCKEDVMA_ID_ALLOCATOR.alloc().unwrap(),
+            vma: SpinLock::new(vma),
+        });
+        r.vma.lock().self_ref = Arc::downgrade(&r);
         return r;
     }
 
     pub fn lock(&self) -> SpinLockGuard<VMA> {
-        return self.0.lock();
+        return self.vma.lock();
     }
 
     /// 调整当前VMA的页面的标志位
@@ -933,19 +948,28 @@ impl LockedVMA {
 
         let mut guard = self.lock();
         assert!(guard.mapped);
+
+        // 获取物理页的anon_vma的守卫
+        let mut anon_vma_guard: SpinLockGuard<'_, crate::mm::page::PageManager> =
+            page_manager_lock_irasave();
         for page in guard.region.pages() {
             let (paddr, _, flush) = unsafe { mapper.unmap_phys(page.virt_address(), true) }
                 .expect("Failed to unmap, beacuse of some page is not mapped");
 
-            // todo: 获取物理页的anon_vma的守卫
+            // 从anon_vma中删除当前VMA
+            let page = anon_vma_guard.get_mut(&paddr);
+            page.remove_vma(self);
 
-            // todo: 从anon_vma中删除当前VMA
-
-            // todo: 如果物理页的anon_vma链表长度为0,则释放物理页.
-
-            // 目前由于还没有实现共享页,所以直接释放物理页也没问题。
-            // 但是在实现共享页之后,就不能直接释放物理页了,需要在anon_vma链表长度为0的时候才能释放物理页
-            unsafe { deallocate_page_frames(PhysPageFrame::new(paddr), PageFrameCount::new(1)) };
+            // 如果物理页的anon_vma链表长度为0并且不是共享页,则释放物理页.
+            if page.can_deallocate() {
+                unsafe {
+                    deallocate_page_frames(
+                        PhysPageFrame::new(paddr),
+                        PageFrameCount::new(1),
+                        &mut anon_vma_guard,
+                    )
+                };
+            }
 
             flusher.consume(flush);
         }
@@ -953,7 +977,7 @@ impl LockedVMA {
     }
 
     pub fn mapped(&self) -> bool {
-        return self.0.lock().mapped;
+        return self.vma.lock().mapped;
     }
 
     /// 将当前VMA进行切分,切分成3个VMA,分别是:
@@ -961,7 +985,7 @@ impl LockedVMA {
     /// 1. 前面的VMA,如果没有则为None
     /// 2. 中间的VMA,也就是传入的Region
     /// 3. 后面的VMA,如果没有则为None
-    pub fn extract(&self, region: VirtRegion) -> Option<VMASplitResult> {
+    pub fn extract(&self, region: VirtRegion, utable: &PageMapper) -> Option<VMASplitResult> {
         assert!(region.start().check_aligned(MMArch::PAGE_SIZE));
         assert!(region.end().check_aligned(MMArch::PAGE_SIZE));
 
@@ -1005,9 +1029,29 @@ impl LockedVMA {
             vma
         });
 
-        guard.region = region;
+        // 重新设置before、after这两个VMA里面的物理页的anon_vma
+        let mut anon_vma_guard = page_manager_lock_irasave();
+        if let Some(before) = before.clone() {
+            let virt_iter = before.lock().region.iter_pages();
+            for frame in virt_iter {
+                let paddr = utable.translate(frame.virt_address()).unwrap().0;
+                let page = anon_vma_guard.get_mut(&paddr);
+                page.insert_vma(before.clone());
+                page.remove_vma(self);
+            }
+        }
+
+        if let Some(after) = after.clone() {
+            let virt_iter = after.lock().region.iter_pages();
+            for frame in virt_iter {
+                let paddr = utable.translate(frame.virt_address()).unwrap().0;
+                let page = anon_vma_guard.get_mut(&paddr);
+                page.insert_vma(after.clone());
+                page.remove_vma(self);
+            }
+        }
 
-        // TODO: 重新设置before、after这两个VMA里面的物理页的anon_vma
+        guard.region = region;
 
         return Some(VMASplitResult::new(
             before,
@@ -1017,6 +1061,12 @@ impl LockedVMA {
     }
 }
 
+impl Drop for LockedVMA {
+    fn drop(&mut self) {
+        LOCKEDVMA_ID_ALLOCATOR.free(self.id);
+    }
+}
+
 /// VMA切分结果
 pub struct VMASplitResult {
     pub prev: Option<Arc<LockedVMA>>,
@@ -1194,27 +1244,22 @@ impl VMA {
         mapper: &mut PageMapper,
         mut flusher: impl Flusher<MMArch>,
     ) -> Result<Arc<LockedVMA>, SystemError> {
-        {
-            let mut cur_phy = phys;
-            let mut cur_dest = destination;
-
-            for _ in 0..count.data() {
-                // 将物理页帧映射到虚拟页帧
-                let r = unsafe {
-                    mapper.map_phys(cur_dest.virt_address(), cur_phy.phys_address(), flags)
-                }
-                .expect("Failed to map phys, may be OOM error");
+        let mut cur_phy = phys;
+        let mut cur_dest = destination;
 
-                // todo: 增加OOM处理
+        for _ in 0..count.data() {
+            // 将物理页帧映射到虚拟页帧
+            let r =
+                unsafe { mapper.map_phys(cur_dest.virt_address(), cur_phy.phys_address(), flags) }
+                    .expect("Failed to map phys, may be OOM error");
 
-                // todo: 将VMA加入到anon_vma中
+            // todo: 增加OOM处理
 
-                // 刷新TLB
-                flusher.consume(r);
+            // 刷新TLB
+            flusher.consume(r);
 
-                cur_phy = cur_phy.next();
-                cur_dest = cur_dest.next();
-            }
+            cur_phy = cur_phy.next();
+            cur_dest = cur_dest.next();
         }
 
         let r: Arc<LockedVMA> = LockedVMA::new(VMA {
@@ -1226,6 +1271,17 @@ impl VMA {
             self_ref: Weak::default(),
             provider: Provider::Allocated,
         });
+
+        // 将VMA加入到anon_vma中
+        let mut anon_vma_guard = page_manager_lock_irasave();
+        cur_phy = phys;
+        for _ in 0..count.data() {
+            let paddr = cur_phy.phys_address();
+            let page = anon_vma_guard.get_mut(&paddr);
+            page.insert_vma(r.clone());
+            cur_phy = cur_phy.next();
+        }
+
         return Ok(r);
     }
 
@@ -1258,7 +1314,6 @@ impl VMA {
             // );
             let r = unsafe { mapper.map(cur_dest.virt_address(), flags) }
                 .expect("Failed to map zero, may be OOM error");
-            // todo: 将VMA加入到anon_vma中
             // todo: 增加OOM处理
 
             // 稍后再刷新TLB,这里取消刷新
@@ -1280,12 +1335,18 @@ impl VMA {
         drop(flusher);
         // kdebug!("VMA::zeroed: flusher dropped");
 
-        // 清空这些内存
+        // 清空这些内存并将VMA加入到anon_vma中
+        let mut anon_vma_guard = page_manager_lock_irasave();
         let virt_iter: VirtPageFrameIter =
             VirtPageFrameIter::new(destination, destination.add(page_count));
         for frame in virt_iter {
             let paddr = mapper.translate(frame.virt_address()).unwrap().0;
 
+            // 将VMA加入到anon_vma
+            let page = anon_vma_guard.get_mut(&paddr);
+            page.insert_vma(r.clone());
+
+            // 清空内存
             unsafe {
                 let vaddr = MMArch::phys_2_virt(paddr).unwrap();
                 MMArch::write_bytes(vaddr, 0, MMArch::PAGE_SIZE);