From 56cc4dbe27e132aac5c61b8bd4f4ec9a223b49ee Mon Sep 17 00:00:00 2001 From: Jomo <2512364506@qq.com> Date: Sun, 31 Mar 2024 16:33:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E9=A1=B5=E9=9D=A2=E5=8F=8D?= =?UTF-8?q?=E5=90=91=E6=98=A0=E5=B0=84=20(#670)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 实现页面反向映射 * 完善PAGE_MANAGER初始化时机 && 封装lock函数 && 删掉过时注释 --- kernel/src/driver/net/dma.rs | 8 +- kernel/src/driver/virtio/virtio_impl.rs | 8 +- kernel/src/mm/allocator/page_frame.rs | 15 ++- kernel/src/mm/init.rs | 9 +- kernel/src/mm/page.rs | 91 +++++++++++++++- kernel/src/mm/ucontext.rs | 139 +++++++++++++++++------- 6 files changed, 222 insertions(+), 48 deletions(-) diff --git a/kernel/src/driver/net/dma.rs b/kernel/src/driver/net/dma.rs index 8d74c335..657e3da8 100644 --- a/kernel/src/driver/net/dma.rs +++ b/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, 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; } diff --git a/kernel/src/driver/virtio/virtio_impl.rs b/kernel/src/driver/virtio/virtio_impl.rs index 8d63dde2..dab879af 100644 --- a/kernel/src/driver/virtio/virtio_impl.rs +++ b/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; } diff --git a/kernel/src/mm/allocator/page_frame.rs b/kernel/src/mm/allocator/page_frame.rs index c4652af9..9a7fd291 100644 --- a/kernel/src/mm/allocator/page_frame.rs +++ b/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(); + } } diff --git a/kernel/src/mm/init.rs b/kernel/src/mm/init.rs index b1b79fc9..515c2b35 100644 --- a/kernel/src/mm/init.rs +++ b/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( diff --git a/kernel/src/mm/page.rs b/kernel/src/mm/page.rs index 8edc4e70..2cf0b8ae 100644 --- a/kernel/src/mm/page.rs +++ b/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> = 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, +} + +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>, +} + +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) { + 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 { /// 当前页表表示的虚拟地址空间的起始地址 @@ -591,6 +676,8 @@ impl PageMapper { 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); } diff --git a/kernel/src/mm/ucontext.rs b/kernel/src/mm/ucontext.rs index a44f3162..1b000d8e 100644 --- a/kernel/src/mm/ucontext.rs +++ b/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, @@ -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(®ion).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); +pub struct LockedVMA { + /// 用于计算哈希值,避免总是获取vma锁来计算哈希值 + id: usize, + vma: SpinLock, +} impl core::hash::Hash for LockedVMA { fn hash(&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 { - 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 { - 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 { + pub fn extract(&self, region: VirtRegion, utable: &PageMapper) -> Option { 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); + } + } - // TODO: 重新设置before、after这两个VMA里面的物理页的anon_vma + 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); + } + } + + 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>, @@ -1194,27 +1244,22 @@ impl VMA { mapper: &mut PageMapper, mut flusher: impl Flusher, ) -> Result, SystemError> { - { - let mut cur_phy = phys; - let mut cur_dest = destination; + 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"); + 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: 增加OOM处理 + // todo: 增加OOM处理 - // todo: 将VMA加入到anon_vma中 + // 刷新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::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);