From 14308f81b6ac57707f625f597407f7efd90806cd Mon Sep 17 00:00:00 2001 From: Zhang Junyang Date: Tue, 24 Dec 2024 14:28:28 +0800 Subject: [PATCH] Remove `AnyFrame` --- ostd/src/mm/frame/meta.rs | 3 +- ostd/src/mm/frame/mod.rs | 269 ++++++++++----------------- ostd/src/mm/frame/untyped/mod.rs | 12 +- ostd/src/mm/kspace/kvirt_area.rs | 4 +- ostd/src/mm/page_table/cursor.rs | 16 +- ostd/src/mm/page_table/node/child.rs | 8 +- ostd/src/mm/page_table/node/mod.rs | 4 +- ostd/src/mm/tlb.rs | 11 +- 8 files changed, 131 insertions(+), 196 deletions(-) diff --git a/ostd/src/mm/frame/meta.rs b/ostd/src/mm/frame/meta.rs index 8992e61c1..6446d9cdc 100644 --- a/ostd/src/mm/frame/meta.rs +++ b/ostd/src/mm/frame/meta.rs @@ -38,6 +38,7 @@ pub(crate) mod mapping { use core::{ any::Any, cell::UnsafeCell, + fmt::Debug, mem::{size_of, MaybeUninit}, sync::atomic::{AtomicU32, Ordering}, }; @@ -113,7 +114,7 @@ const_assert_eq!(size_of::(), META_SLOT_SIZE); /// The implemented structure must have a size less than or equal to /// [`PAGE_METADATA_MAX_SIZE`] and an alignment less than or equal to /// [`PAGE_METADATA_MAX_ALIGN`]. -pub unsafe trait FrameMeta: Any + Send + Sync + 'static { +pub unsafe trait FrameMeta: Any + Send + Sync + Debug + 'static { /// Called when the last handle to the page is dropped. fn on_drop(&mut self, _paddr: Paddr) {} } diff --git a/ostd/src/mm/frame/mod.rs b/ostd/src/mm/frame/mod.rs index 1bd329c86..003965c80 100644 --- a/ostd/src/mm/frame/mod.rs +++ b/ostd/src/mm/frame/mod.rs @@ -20,10 +20,9 @@ mod segment; pub mod untyped; use core::{ - any::Any, marker::PhantomData, mem::ManuallyDrop, - sync::atomic::{AtomicUsize, Ordering}, + sync::atomic::{AtomicU32, AtomicUsize, Ordering}, }; use meta::{ @@ -39,14 +38,15 @@ static MAX_PADDR: AtomicUsize = AtomicUsize::new(0); /// A page with a statically-known usage, whose metadata is represented by `M`. #[derive(Debug)] -pub struct Frame { +#[repr(transparent)] +pub struct Frame { pub(super) ptr: *const MetaSlot, pub(super) _marker: PhantomData, } -unsafe impl Send for Frame {} +unsafe impl Send for Frame {} -unsafe impl Sync for Frame {} +unsafe impl Sync for Frame {} impl Frame { /// Get a `Frame` handle with a specific usage from a raw, unused page. @@ -102,6 +102,78 @@ impl Frame { } } + /// Get the metadata of this page. + pub fn meta(&self) -> &M { + // SAFETY: `self.ptr` points to the metadata storage which is valid to + // be immutably borrowed as `M` because the type is correct, it lives + // under the given lifetime, and no one will mutably borrow the page + // metadata after initialization. + unsafe { &*self.ptr.cast() } + } +} + +impl Frame { + /// Get the physical address. + pub fn paddr(&self) -> Paddr { + mapping::meta_to_page::(self.ptr as Vaddr) + } + + /// Get the paging level of this page. + /// + /// This is the level of the page table entry that maps the frame, + /// which determines the size of the frame. + /// + /// Currently, the level is always 1, which means the frame is a regular + /// page frame. + pub const fn level(&self) -> PagingLevel { + 1 + } + + /// Size of this page in bytes. + pub const fn size(&self) -> usize { + PAGE_SIZE + } + + /// Get the dyncamically-typed metadata of this frame. + /// + /// If the type is known at compile time, use [`Frame::meta`] instead. + pub fn dyn_meta(&self) -> &dyn FrameMeta { + let slot = self.slot(); + + // SAFETY: The page metadata is valid to be borrowed immutably, since it will never be + // borrowed mutably after initialization. + let vtable_ptr = unsafe { &*slot.vtable_ptr.get() }; + + // SAFETY: The page metadata is initialized and valid. + let vtable_ptr = *unsafe { vtable_ptr.assume_init_ref() }; + + let meta_ptr: *const dyn FrameMeta = core::ptr::from_raw_parts(self.ptr, vtable_ptr); + + // SAFETY: `self.ptr` points to the metadata storage which is valid to be immutably + // borrowed under `vtable_ptr` because the vtable is correct, it lives under the given + // lifetime, and no one will mutably borrow the page metadata after initialization. + unsafe { &*meta_ptr } + } + + /// Get the reference count of the page. + /// + /// It returns the number of all references to the page, including all the + /// existing page handles ([`Frame`], [`Frame`]), and all the mappings in the + /// page table that points to the page. + /// + /// # Safety + /// + /// The function is safe to call, but using it requires extra care. The + /// reference count can be changed by other threads at any time including + /// potentially between calling this method and acting on the result. + pub fn reference_count(&self) -> u32 { + self.ref_count().load(Ordering::Relaxed) + } + + fn ref_count(&self) -> &AtomicU32 { + unsafe { &(*self.ptr).ref_count } + } + /// Forget the handle to the page. /// /// This will result in the page being leaked without calling the custom dropper. @@ -138,58 +210,14 @@ impl Frame { } } - /// Get the physical address. - pub fn paddr(&self) -> Paddr { - mapping::meta_to_page::(self.ptr as Vaddr) - } - - /// Get the paging level of this page. - /// - /// This is the level of the page table entry that maps the frame, - /// which determines the size of the frame. - /// - /// Currently, the level is always 1, which means the frame is a regular - /// page frame. - pub const fn level(&self) -> PagingLevel { - 1 - } - - /// Size of this page in bytes. - pub const fn size(&self) -> usize { - PAGE_SIZE - } - - /// Get the metadata of this page. - pub fn meta(&self) -> &M { - // SAFETY: `self.ptr` points to the metadata storage which is valid to be immutably - // borrowed as `M` because the type is correct, it lives under the given lifetime, and no - // one will mutably borrow the page metadata after initialization. - unsafe { &*self.ptr.cast() } - } - - /// Get the reference count of the page. - /// - /// It returns the number of all references to the page, including all the - /// existing page handles ([`Frame`], [`AnyFrame`]), and all the mappings in the - /// page table that points to the page. - /// - /// # Safety - /// - /// The function is safe to call, but using it requires extra care. The - /// reference count can be changed by other threads at any time including - /// potentially between calling this method and acting on the result. - pub fn reference_count(&self) -> u32 { - self.slot().ref_count.load(Ordering::Relaxed) - } - fn slot(&self) -> &MetaSlot { - // SAFETY: `ptr` points to a valid `MetaSlot` that will never be mutably borrowed, so taking an - // immutable reference to it is always safe. + // SAFETY: `ptr` points to a valid `MetaSlot` that will never be + // mutably borrowed, so taking an immutable reference to it is safe. unsafe { &*self.ptr } } } -impl Clone for Frame { +impl Clone for Frame { fn clone(&self) -> Self { // SAFETY: We have already held a reference to the page. unsafe { self.slot().inc_ref_count() }; @@ -201,7 +229,7 @@ impl Clone for Frame { } } -impl Drop for Frame { +impl Drop for Frame { fn drop(&mut self) { let last_ref_cnt = self.slot().ref_count.fetch_sub(1, Ordering::Release); debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED); @@ -219,147 +247,44 @@ impl Drop for Frame { } } -/// A page with a dynamically-known usage. -/// -/// It can also be used when the user don't care about the usage of the page. -#[derive(Debug)] -pub struct AnyFrame { - ptr: *const MetaSlot, -} +impl TryFrom> for Frame { + type Error = Frame; -unsafe impl Send for AnyFrame {} -unsafe impl Sync for AnyFrame {} - -impl AnyFrame { - /// Forget the handle to the page. - /// - /// This is the same as [`Frame::into_raw`]. - /// - /// This will result in the page being leaked without calling the custom dropper. - /// - /// A physical address to the page is returned in case the page needs to be - /// restored using [`Self::from_raw`] later. - pub(in crate::mm) fn into_raw(self) -> Paddr { - let paddr = self.paddr(); - core::mem::forget(self); - paddr - } - - /// Restore a forgotten page from a physical address. - /// - /// # Safety - /// - /// The safety concerns are the same as [`Frame::from_raw`]. - pub(in crate::mm) unsafe fn from_raw(paddr: Paddr) -> Self { - let vaddr = mapping::page_to_meta::(paddr); - let ptr = vaddr as *const MetaSlot; - - Self { ptr } - } - - /// Get the metadata of this page. - pub fn meta(&self) -> &dyn Any { - let slot = self.slot(); - - // SAFETY: The page metadata is valid to be borrowed immutably, since it will never be - // borrowed mutably after initialization. - let vtable_ptr = unsafe { &*slot.vtable_ptr.get() }; - - // SAFETY: The page metadata is initialized and valid. - let vtable_ptr = *unsafe { vtable_ptr.assume_init_ref() }; - - let meta_ptr: *const dyn FrameMeta = core::ptr::from_raw_parts(self.ptr, vtable_ptr); - - // SAFETY: `self.ptr` points to the metadata storage which is valid to be immutably - // borrowed under `vtable_ptr` because the vtable is correct, it lives under the given - // lifetime, and no one will mutably borrow the page metadata after initialization. - (unsafe { &*meta_ptr }) as &dyn Any - } - - /// Get the physical address of the start of the page - pub fn paddr(&self) -> Paddr { - mapping::meta_to_page::(self.ptr as Vaddr) - } - - /// Get the paging level of this page. - pub fn level(&self) -> PagingLevel { - 1 - } - - /// Size of this page in bytes. - pub fn size(&self) -> usize { - PAGE_SIZE - } - - fn slot(&self) -> &MetaSlot { - // SAFETY: `ptr` points to a valid `MetaSlot` that will never be mutably borrowed, so taking an - // immutable reference to it is always safe. - unsafe { &*self.ptr } - } -} - -impl TryFrom for Frame { - type Error = AnyFrame; - - /// Try converting a [`AnyFrame`] into the statically-typed [`Frame`]. + /// Try converting a [`Frame`] into the statically-typed [`Frame`]. /// /// If the usage of the page is not the same as the expected usage, it will /// return the dynamic page itself as is. - fn try_from(dyn_page: AnyFrame) -> Result { - if dyn_page.meta().is::() { + fn try_from(dyn_frame: Frame) -> Result { + if (dyn_frame.dyn_meta() as &dyn core::any::Any).is::() { let result = Frame { - ptr: dyn_page.ptr, + ptr: dyn_frame.ptr, _marker: PhantomData, }; - let _ = ManuallyDrop::new(dyn_page); + let _ = ManuallyDrop::new(dyn_frame); Ok(result) } else { - Err(dyn_page) + Err(dyn_frame) } } } -impl From> for AnyFrame { - fn from(page: Frame) -> Self { - let result = Self { ptr: page.ptr }; - let _ = ManuallyDrop::new(page); +impl From> for Frame { + fn from(frame: Frame) -> Self { + let result = Self { + ptr: frame.ptr, + _marker: PhantomData, + }; + let _ = ManuallyDrop::new(frame); result } } -impl From for AnyFrame { +impl From for Frame { fn from(frame: UntypedFrame) -> Self { Frame::::from(frame).into() } } -impl Clone for AnyFrame { - fn clone(&self) -> Self { - // SAFETY: We have already held a reference to the page. - unsafe { self.slot().inc_ref_count() }; - - Self { ptr: self.ptr } - } -} - -impl Drop for AnyFrame { - fn drop(&mut self) { - let last_ref_cnt = self.slot().ref_count.fetch_sub(1, Ordering::Release); - debug_assert!(last_ref_cnt != 0 && last_ref_cnt != REF_COUNT_UNUSED); - - if last_ref_cnt == 1 { - // A fence is needed here with the same reasons stated in the implementation of - // `Arc::drop`: . - core::sync::atomic::fence(Ordering::Acquire); - - // SAFETY: this is the last reference and is about to be dropped. - unsafe { - meta::drop_last_in_place(self.ptr as *mut MetaSlot); - } - } - } -} - /// Increases the reference count of the page by one. /// /// # Safety diff --git a/ostd/src/mm/frame/untyped/mod.rs b/ostd/src/mm/frame/untyped/mod.rs index a929250d3..7760b260a 100644 --- a/ostd/src/mm/frame/untyped/mod.rs +++ b/ostd/src/mm/frame/untyped/mod.rs @@ -16,8 +16,8 @@ use core::mem::ManuallyDrop; pub use segment::UntypedSegment; use super::{ - meta::{impl_frame_meta_for, MetaSlot}, - AnyFrame, Frame, + meta::{impl_frame_meta_for, FrameMeta, MetaSlot}, + Frame, }; use crate::{ mm::{ @@ -100,14 +100,14 @@ impl From> for UntypedFrame { } } -impl TryFrom for UntypedFrame { - type Error = AnyFrame; +impl TryFrom> for UntypedFrame { + type Error = Frame; - /// Try converting a [`AnyFrame`] into the statically-typed [`UntypedFrame`]. + /// Try converting a [`Frame`] into the statically-typed [`UntypedFrame`]. /// /// If the dynamic page is not used as an untyped page frame, it will /// return the dynamic page itself as is. - fn try_from(page: AnyFrame) -> core::result::Result { + fn try_from(page: Frame) -> core::result::Result { page.try_into().map(|p: Frame| p.into()) } } diff --git a/ostd/src/mm/kspace/kvirt_area.rs b/ostd/src/mm/kspace/kvirt_area.rs index 8e80b1252..338d35bb3 100644 --- a/ostd/src/mm/kspace/kvirt_area.rs +++ b/ostd/src/mm/kspace/kvirt_area.rs @@ -11,7 +11,7 @@ use super::{KERNEL_PAGE_TABLE, TRACKED_MAPPED_PAGES_RANGE, VMALLOC_VADDR_RANGE}; use crate::{ cpu::CpuSet, mm::{ - frame::{meta::FrameMeta, AnyFrame, Frame}, + frame::{meta::FrameMeta, Frame}, page_prop::PageProperty, page_table::PageTableItem, tlb::{TlbFlushOp, TlbFlusher, FLUSH_ALL_RANGE_THRESHOLD}, @@ -232,7 +232,7 @@ impl KVirtArea { /// /// This function returns None if the address is not mapped (`NotMapped`), /// while panics if the address is mapped to a `MappedUntracked` or `PageTableNode` page. - pub fn get_page(&self, addr: Vaddr) -> Option { + pub fn get_page(&self, addr: Vaddr) -> Option> { let query_result = self.query_page(addr); match query_result { PageTableItem::Mapped { diff --git a/ostd/src/mm/page_table/cursor.rs b/ostd/src/mm/page_table/cursor.rs index 79d74b362..ebed0e60e 100644 --- a/ostd/src/mm/page_table/cursor.rs +++ b/ostd/src/mm/page_table/cursor.rs @@ -76,7 +76,9 @@ use super::{ }; use crate::{ mm::{ - frame::AnyFrame, kspace::should_map_as_tracked, paddr_to_vaddr, Paddr, PageProperty, Vaddr, + frame::{meta::FrameMeta, Frame}, + kspace::should_map_as_tracked, + paddr_to_vaddr, Paddr, PageProperty, Vaddr, }, task::{disable_preempt, DisabledPreemptGuard}, }; @@ -89,7 +91,7 @@ pub enum PageTableItem { }, Mapped { va: Vaddr, - page: AnyFrame, + page: Frame, prop: PageProperty, }, #[allow(dead_code)] @@ -400,9 +402,9 @@ where self.0.query() } - /// Maps the range starting from the current address to a [`AnyFrame`]. + /// Maps the range starting from the current address to a [`Frame`]. /// - /// It returns the previously mapped [`AnyFrame`] if that exists. + /// It returns the previously mapped [`Frame`] if that exists. /// /// # Panics /// @@ -415,7 +417,11 @@ where /// /// The caller should ensure that the virtual range being mapped does /// not affect kernel's memory safety. - pub unsafe fn map(&mut self, page: AnyFrame, prop: PageProperty) -> Option { + pub unsafe fn map( + &mut self, + page: Frame, + prop: PageProperty, + ) -> Option> { let end = self.0.va + page.size(); assert!(end <= self.0.barrier_va.end); diff --git a/ostd/src/mm/page_table/node/child.rs b/ostd/src/mm/page_table/node/child.rs index 88cec5a07..2d046731f 100644 --- a/ostd/src/mm/page_table/node/child.rs +++ b/ostd/src/mm/page_table/node/child.rs @@ -8,7 +8,7 @@ use super::{MapTrackingStatus, PageTableEntryTrait, RawPageTableNode}; use crate::{ arch::mm::{PageTableEntry, PagingConsts}, mm::{ - frame::{inc_page_ref_count, AnyFrame}, + frame::{inc_page_ref_count, meta::FrameMeta, Frame}, page_prop::PageProperty, Paddr, PagingConstsTrait, PagingLevel, }, @@ -27,7 +27,7 @@ pub(in crate::mm) enum Child< [(); C::NR_LEVELS as usize]:, { PageTable(RawPageTableNode), - Frame(AnyFrame, PageProperty), + Frame(Frame, PageProperty), /// Pages not tracked by handles. Untracked(Paddr, PagingLevel, PageProperty), None, @@ -119,7 +119,7 @@ where match is_tracked { MapTrackingStatus::Tracked => { // SAFETY: The physical address points to a valid page. - let page = unsafe { AnyFrame::from_raw(paddr) }; + let page = unsafe { Frame::::from_raw(paddr) }; Child::Frame(page, pte.prop()) } MapTrackingStatus::Untracked => Child::Untracked(paddr, level, pte.prop()), @@ -162,7 +162,7 @@ where // the reference to the page. unsafe { inc_page_ref_count(paddr) }; // SAFETY: The physical address points to a valid page. - let page = unsafe { AnyFrame::from_raw(paddr) }; + let page = unsafe { Frame::::from_raw(paddr) }; Child::Frame(page, pte.prop()) } MapTrackingStatus::Untracked => Child::Untracked(paddr, level, pte.prop()), diff --git a/ostd/src/mm/page_table/node/mod.rs b/ostd/src/mm/page_table/node/mod.rs index b6766edc5..20dfdadc0 100644 --- a/ostd/src/mm/page_table/node/mod.rs +++ b/ostd/src/mm/page_table/node/mod.rs @@ -40,7 +40,7 @@ use super::{nr_subpage_per_huge, PageTableEntryTrait}; use crate::{ arch::mm::{PageTableEntry, PagingConsts}, mm::{ - frame::{self, inc_page_ref_count, meta::FrameMeta, AnyFrame, Frame}, + frame::{self, inc_page_ref_count, meta::FrameMeta, Frame}, paddr_to_vaddr, Paddr, PagingConstsTrait, PagingLevel, PAGE_SIZE, }, }; @@ -442,7 +442,7 @@ where } else if is_tracked == MapTrackingStatus::Tracked { // SAFETY: The PTE points to a tracked page. The ownership // of the child is transferred to the child then dropped. - drop(unsafe { AnyFrame::from_raw(paddr) }); + drop(unsafe { Frame::::from_raw(paddr) }); } } } diff --git a/ostd/src/mm/tlb.rs b/ostd/src/mm/tlb.rs index 545e9471a..1cc386fdc 100644 --- a/ostd/src/mm/tlb.rs +++ b/ostd/src/mm/tlb.rs @@ -5,7 +5,10 @@ use alloc::vec::Vec; use core::ops::Range; -use super::{frame::AnyFrame, Vaddr, PAGE_SIZE}; +use super::{ + frame::{meta::FrameMeta, Frame}, + Vaddr, PAGE_SIZE, +}; use crate::{ cpu::{CpuSet, PinCurrentCpu}, cpu_local, @@ -77,7 +80,7 @@ impl TlbFlusher { /// flushed. Otherwise if the page is recycled for other purposes, the user /// space program can still access the page through the TLB entries. This /// method is designed to be used in such cases. - pub fn issue_tlb_flush_with(&self, op: TlbFlushOp, drop_after_flush: AnyFrame) { + pub fn issue_tlb_flush_with(&self, op: TlbFlushOp, drop_after_flush: Frame) { self.issue_tlb_flush_(op, Some(drop_after_flush)); } @@ -91,7 +94,7 @@ impl TlbFlusher { self.need_self_flush } - fn issue_tlb_flush_(&self, op: TlbFlushOp, drop_after_flush: Option) { + fn issue_tlb_flush_(&self, op: TlbFlushOp, drop_after_flush: Option>) { let op = op.optimize_for_large_range(); // Fast path for single CPU cases. @@ -156,7 +159,7 @@ impl TlbFlushOp { // Lock ordering: lock FLUSH_OPS before PAGE_KEEPER. cpu_local! { static FLUSH_OPS: SpinLock = SpinLock::new(OpsStack::new()); - static PAGE_KEEPER: SpinLock, LocalIrqDisabled> = SpinLock::new(Vec::new()); + static PAGE_KEEPER: SpinLock>, LocalIrqDisabled> = SpinLock::new(Vec::new()); } fn do_remote_flush() {