mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-22 00:43:24 +00:00
Use pointer metadata for page metadata
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
cd22854f59
commit
60365a818a
@ -11,8 +11,9 @@ use crate::{
|
||||
arch::boot::smp::{bringup_all_aps, get_num_processors},
|
||||
cpu,
|
||||
mm::{
|
||||
kspace::KernelMeta,
|
||||
paddr_to_vaddr,
|
||||
page::{self, meta::KernelMeta, ContPages},
|
||||
page::{self, ContPages},
|
||||
PAGE_SIZE,
|
||||
},
|
||||
task::Task,
|
||||
|
@ -44,8 +44,9 @@ use spin::Once;
|
||||
use crate::{
|
||||
arch,
|
||||
mm::{
|
||||
kspace::KernelMeta,
|
||||
paddr_to_vaddr,
|
||||
page::{self, meta::KernelMeta, ContPages},
|
||||
page::{self, ContPages},
|
||||
PAGE_SIZE,
|
||||
},
|
||||
};
|
||||
|
@ -15,8 +15,10 @@
|
||||
#![feature(linkage)]
|
||||
#![feature(min_specialization)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(ptr_metadata)]
|
||||
#![feature(ptr_sub_ptr)]
|
||||
#![feature(sync_unsafe_cell)]
|
||||
#![feature(trait_upcasting)]
|
||||
// The `generic_const_exprs` feature is incomplete however required for the page table
|
||||
// const generic implementation. We are using this feature in a conservative manner.
|
||||
#![allow(incomplete_features)]
|
||||
|
@ -15,14 +15,17 @@ use core::mem::ManuallyDrop;
|
||||
|
||||
pub use segment::Segment;
|
||||
|
||||
use super::page::{
|
||||
meta::{FrameMeta, MetaSlot, PageMeta, PageUsage},
|
||||
DynPage, Page,
|
||||
use super::{
|
||||
page::{
|
||||
meta::{impl_page_meta, MetaSlot},
|
||||
DynPage, Page,
|
||||
},
|
||||
Infallible,
|
||||
};
|
||||
use crate::{
|
||||
mm::{
|
||||
io::{FallibleVmRead, FallibleVmWrite, VmIo, VmReader, VmWriter},
|
||||
paddr_to_vaddr, HasPaddr, Infallible, Paddr, PAGE_SIZE,
|
||||
paddr_to_vaddr, HasPaddr, Paddr, PAGE_SIZE,
|
||||
},
|
||||
Error, Result,
|
||||
};
|
||||
@ -178,14 +181,11 @@ impl VmIo for Frame {
|
||||
}
|
||||
}
|
||||
|
||||
impl PageMeta for FrameMeta {
|
||||
const USAGE: PageUsage = PageUsage::Frame;
|
||||
/// Metadata for a frame.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct FrameMeta {}
|
||||
|
||||
fn on_drop(_page: &mut Page<Self>) {
|
||||
// Nothing should be done so far since dropping the page would
|
||||
// have all taken care of.
|
||||
}
|
||||
}
|
||||
impl_page_meta!(FrameMeta);
|
||||
|
||||
// Here are implementations for `xarray`.
|
||||
|
||||
|
@ -4,10 +4,7 @@
|
||||
|
||||
use super::{Frame, Segment};
|
||||
use crate::{
|
||||
mm::{
|
||||
page::{self, meta::FrameMeta},
|
||||
PAGE_SIZE,
|
||||
},
|
||||
mm::{frame::FrameMeta, page, PAGE_SIZE},
|
||||
prelude::*,
|
||||
Error,
|
||||
};
|
||||
|
@ -6,8 +6,9 @@ use core::ops::Range;
|
||||
|
||||
use crate::{
|
||||
mm::{
|
||||
frame::FrameMeta,
|
||||
io::{FallibleVmRead, FallibleVmWrite},
|
||||
page::{meta::FrameMeta, ContPages},
|
||||
page::ContPages,
|
||||
Frame, HasPaddr, Infallible, Paddr, VmIo, VmReader, VmWriter,
|
||||
},
|
||||
Error, Result,
|
||||
|
@ -50,7 +50,7 @@ use spin::Once;
|
||||
use super::{
|
||||
nr_subpage_per_huge,
|
||||
page::{
|
||||
meta::{mapping, KernelMeta, MetaPageMeta},
|
||||
meta::{impl_page_meta, mapping, MetaPageMeta},
|
||||
Page,
|
||||
},
|
||||
page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags},
|
||||
@ -247,3 +247,9 @@ pub unsafe fn activate_kernel_page_table() {
|
||||
crate::mm::page_table::boot_pt::dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
/// The metadata of pages that contains the kernel itself.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct KernelMeta {}
|
||||
|
||||
impl_page_meta!(KernelMeta);
|
||||
|
@ -44,7 +44,7 @@ pub type PagingLevel = u8;
|
||||
|
||||
/// A minimal set of constants that determines the paging system.
|
||||
/// This provides an abstraction over most paging modes in common architectures.
|
||||
pub(crate) trait PagingConstsTrait: Clone + Debug + Default + Sync + 'static {
|
||||
pub(crate) trait PagingConstsTrait: Clone + Debug + Default + Send + Sync + 'static {
|
||||
/// The smallest page size.
|
||||
/// This is also the page size at level 1 page tables.
|
||||
const BASE_PAGE_SIZE: usize;
|
||||
|
@ -37,53 +37,31 @@ pub mod mapping {
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use core::{
|
||||
any::Any,
|
||||
cell::UnsafeCell,
|
||||
marker::PhantomData,
|
||||
mem::{size_of, ManuallyDrop},
|
||||
panic,
|
||||
sync::atomic::{AtomicU32, AtomicU8, Ordering},
|
||||
mem::size_of,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
use log::info;
|
||||
use num_derive::FromPrimitive;
|
||||
use static_assertions::const_assert_eq;
|
||||
|
||||
use super::{allocator, Page};
|
||||
use crate::{
|
||||
arch::mm::{PageTableEntry, PagingConsts},
|
||||
arch::mm::PagingConsts,
|
||||
mm::{
|
||||
paddr_to_vaddr, page_size,
|
||||
page_table::{boot_pt, PageTableEntryTrait},
|
||||
CachePolicy, Paddr, PageFlags, PageProperty, PagingConstsTrait, PagingLevel,
|
||||
PrivilegedPageFlags, Vaddr, PAGE_SIZE,
|
||||
paddr_to_vaddr, page_size, page_table::boot_pt, CachePolicy, Paddr, PageFlags,
|
||||
PageProperty, PrivilegedPageFlags, Vaddr, PAGE_SIZE,
|
||||
},
|
||||
};
|
||||
|
||||
/// Represents the usage of a page.
|
||||
#[repr(u8)]
|
||||
#[derive(Debug, FromPrimitive, PartialEq)]
|
||||
pub enum PageUsage {
|
||||
// The zero variant is reserved for the unused type. Only an unused page
|
||||
// can be designated for one of the other purposes.
|
||||
#[allow(dead_code)]
|
||||
Unused = 0,
|
||||
/// The page is reserved or unusable. The kernel should not touch it.
|
||||
#[allow(dead_code)]
|
||||
Reserved = 1,
|
||||
/// The maximum number of bytes of the metadata of a page.
|
||||
pub const PAGE_METADATA_MAX_SIZE: usize =
|
||||
META_SLOT_SIZE - size_of::<AtomicU32>() - size_of::<PageMetaVtablePtr>();
|
||||
/// The maximum alignment in bytes of the metadata of a page.
|
||||
pub const PAGE_METADATA_MAX_ALIGN: usize = align_of::<MetaSlot>();
|
||||
|
||||
/// The page is used as a frame, i.e., a page of untyped memory.
|
||||
Frame = 32,
|
||||
|
||||
/// The page is used by a page table.
|
||||
PageTable = 64,
|
||||
/// The page stores metadata of other pages.
|
||||
Meta = 65,
|
||||
/// The page stores the kernel such as kernel code, data, etc.
|
||||
Kernel = 66,
|
||||
|
||||
/// The page stores data for kernel stack.
|
||||
KernelStack = 67,
|
||||
}
|
||||
const META_SLOT_SIZE: usize = 64;
|
||||
|
||||
#[repr(C)]
|
||||
pub(in crate::mm) struct MetaSlot {
|
||||
@ -92,41 +70,58 @@ pub(in crate::mm) struct MetaSlot {
|
||||
/// It is placed at the beginning of a slot because:
|
||||
/// - the implementation can simply cast a `*const MetaSlot`
|
||||
/// to a `*const PageMeta` for manipulation;
|
||||
/// - the subsequent fields can utilize the end padding of the
|
||||
/// the inner union to save space.
|
||||
_inner: MetaSlotInner,
|
||||
/// To store [`PageUsage`].
|
||||
pub(super) usage: AtomicU8,
|
||||
/// - if the metadata need special alignment, we can provide
|
||||
/// at most `PAGE_METADATA_ALIGN` bytes of alignment;
|
||||
/// - the subsequent fields can utilize the padding of the
|
||||
/// reference count to save space.
|
||||
storage: UnsafeCell<[u8; PAGE_METADATA_MAX_SIZE]>,
|
||||
/// The reference count of the page.
|
||||
pub(super) ref_count: AtomicU32,
|
||||
/// The virtual table that indicates the type of the metadata.
|
||||
pub(super) vtable_ptr: UnsafeCell<PageMetaVtablePtr>,
|
||||
}
|
||||
|
||||
pub(super) union MetaSlotInner {
|
||||
_frame: ManuallyDrop<FrameMeta>,
|
||||
_pt: ManuallyDrop<PageTablePageMeta>,
|
||||
}
|
||||
type PageMetaVtablePtr = core::ptr::DynMetadata<dyn PageMeta>;
|
||||
|
||||
// Currently the sizes of the `MetaSlotInner` union variants are no larger
|
||||
// than 8 bytes and aligned to 8 bytes. So the size of `MetaSlot` is 16 bytes.
|
||||
//
|
||||
// Note that the size of `MetaSlot` should be a multiple of 8 bytes to prevent
|
||||
// cross-page accesses.
|
||||
const_assert_eq!(size_of::<MetaSlot>(), 16);
|
||||
const_assert_eq!(PAGE_SIZE % META_SLOT_SIZE, 0);
|
||||
const_assert_eq!(size_of::<MetaSlot>(), META_SLOT_SIZE);
|
||||
|
||||
/// All page metadata types must implemented this sealed trait,
|
||||
/// which ensures that each fields of `PageUsage` has one and only
|
||||
/// one metadata type corresponding to the usage purpose. Any user
|
||||
/// outside this module won't be able to add more metadata types
|
||||
/// and break assumptions made by this module.
|
||||
/// All page metadata types must implement this trait.
|
||||
///
|
||||
/// If a page type needs specific drop behavior, it should specify
|
||||
/// when implementing this trait. When we drop the last handle to
|
||||
/// this page, the `on_drop` method will be called.
|
||||
pub trait PageMeta: Sync + private::Sealed + Sized {
|
||||
const USAGE: PageUsage;
|
||||
|
||||
fn on_drop(page: &mut Page<Self>);
|
||||
/// this page, the `on_drop` method will be called. The `on_drop`
|
||||
/// method is called with the physical address of the page.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// 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 PageMeta: Any + Send + Sync + 'static {
|
||||
fn on_drop(&mut self, _paddr: Paddr) {}
|
||||
}
|
||||
|
||||
/// Makes a structure usable as a page metadata.
|
||||
///
|
||||
/// Directly implementing [`PageMeta`] is not safe since the size and alignment
|
||||
/// must be checked. This macro provides a safe way to implement the trait with
|
||||
/// compile-time checks.
|
||||
#[macro_export]
|
||||
macro_rules! impl_page_meta {
|
||||
($($t:ty),*) => {
|
||||
$(
|
||||
use static_assertions::const_assert;
|
||||
const_assert!(size_of::<$t>() <= $crate::mm::page::meta::PAGE_METADATA_MAX_SIZE);
|
||||
const_assert!(align_of::<$t>() <= $crate::mm::page::meta::PAGE_METADATA_MAX_ALIGN);
|
||||
// SAFETY: The size and alignment of the structure are checked.
|
||||
unsafe impl $crate::mm::page::meta::PageMeta for $t {}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
pub use impl_page_meta;
|
||||
|
||||
/// An internal routine in dropping implementations.
|
||||
///
|
||||
/// # Safety
|
||||
@ -134,154 +129,35 @@ pub trait PageMeta: Sync + private::Sealed + Sized {
|
||||
/// The caller should ensure that the pointer points to a page's metadata slot. The
|
||||
/// page should have a last handle to the page, and the page is about to be dropped,
|
||||
/// as the metadata slot after this operation becomes uninitialized.
|
||||
pub(super) unsafe fn drop_as_last<M: PageMeta>(ptr: *const MetaSlot) {
|
||||
pub(super) unsafe fn drop_last_in_place(ptr: *mut MetaSlot) {
|
||||
// This would be guaranteed as a safety requirement.
|
||||
debug_assert_eq!((*ptr).ref_count.load(Ordering::Relaxed), 0);
|
||||
|
||||
let paddr = mapping::meta_to_page::<PagingConsts>(ptr as Vaddr);
|
||||
|
||||
let meta_ptr: *mut dyn PageMeta = core::ptr::from_raw_parts_mut(ptr, *(*ptr).vtable_ptr.get());
|
||||
|
||||
// Let the custom dropper handle the drop.
|
||||
let mut page = Page::<M> {
|
||||
ptr,
|
||||
_marker: PhantomData,
|
||||
};
|
||||
M::on_drop(&mut page);
|
||||
let _ = ManuallyDrop::new(page);
|
||||
(*meta_ptr).on_drop(paddr);
|
||||
|
||||
// Drop the metadata.
|
||||
core::ptr::drop_in_place(ptr as *mut M);
|
||||
// No handles means no usage. This also releases the page as unused for further
|
||||
// calls to `Page::from_unused`.
|
||||
(*ptr).usage.store(0, Ordering::Release);
|
||||
core::ptr::drop_in_place(meta_ptr);
|
||||
|
||||
// Deallocate the page.
|
||||
// It would return the page to the allocator for further use. This would be done
|
||||
// after the release of the metadata to avoid re-allocation before the metadata
|
||||
// is reset.
|
||||
allocator::PAGE_ALLOCATOR.get().unwrap().lock().dealloc(
|
||||
mapping::meta_to_page::<PagingConsts>(ptr as Vaddr) / PAGE_SIZE,
|
||||
1,
|
||||
);
|
||||
allocator::PAGE_ALLOCATOR
|
||||
.get()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.dealloc(paddr / PAGE_SIZE, 1);
|
||||
}
|
||||
|
||||
mod private {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
// ======= Start of all the specific metadata structures definitions ==========
|
||||
|
||||
use private::Sealed;
|
||||
|
||||
/// The metadata of pages that holds metadata of pages.
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct FrameMeta {
|
||||
// If not doing so, the page table metadata would fit
|
||||
// in the front padding of meta slot and make it 12 bytes.
|
||||
// We make it 16 bytes. Further usage of frame metadata
|
||||
// is welcome to exploit this space.
|
||||
_unused_for_layout_padding: [u8; 8],
|
||||
}
|
||||
|
||||
impl Sealed for FrameMeta {}
|
||||
|
||||
/// The metadata of any kinds of page table pages.
|
||||
/// Make sure the the generic parameters don't effect the memory layout.
|
||||
#[derive(Debug)]
|
||||
#[repr(C)]
|
||||
pub(in crate::mm) struct PageTablePageMeta<
|
||||
E: PageTableEntryTrait = PageTableEntry,
|
||||
C: PagingConstsTrait = PagingConsts,
|
||||
> where
|
||||
[(); C::NR_LEVELS as usize]:,
|
||||
{
|
||||
/// The number of valid PTEs. It is mutable if the lock is held.
|
||||
pub nr_children: UnsafeCell<u16>,
|
||||
/// The level of the page table page. A page table page cannot be
|
||||
/// referenced by page tables of different levels.
|
||||
pub level: PagingLevel,
|
||||
/// Whether the pages mapped by the node is tracked.
|
||||
pub is_tracked: MapTrackingStatus,
|
||||
/// The lock for the page table page.
|
||||
pub lock: AtomicU8,
|
||||
_phantom: core::marker::PhantomData<(E, C)>,
|
||||
}
|
||||
|
||||
/// Describe if the physical address recorded in this page table refers to a
|
||||
/// page tracked by metadata.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub(in crate::mm) enum MapTrackingStatus {
|
||||
/// The page table node cannot contain references to any pages. It can only
|
||||
/// contain references to child page table nodes.
|
||||
NotApplicable,
|
||||
/// The mapped pages are not tracked by metadata. If any child page table
|
||||
/// nodes exist, they should also be tracked.
|
||||
Untracked,
|
||||
/// The mapped pages are tracked by metadata. If any child page table nodes
|
||||
/// exist, they should also be tracked.
|
||||
Tracked,
|
||||
}
|
||||
|
||||
impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageTablePageMeta<E, C>
|
||||
where
|
||||
[(); C::NR_LEVELS as usize]:,
|
||||
{
|
||||
pub fn new_locked(level: PagingLevel, is_tracked: MapTrackingStatus) -> Self {
|
||||
Self {
|
||||
nr_children: UnsafeCell::new(0),
|
||||
level,
|
||||
is_tracked,
|
||||
lock: AtomicU8::new(1),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: PageTableEntryTrait, C: PagingConstsTrait> Sealed for PageTablePageMeta<E, C> where
|
||||
[(); C::NR_LEVELS as usize]:
|
||||
{
|
||||
}
|
||||
|
||||
unsafe impl<E: PageTableEntryTrait, C: PagingConstsTrait> Send for PageTablePageMeta<E, C> where
|
||||
[(); C::NR_LEVELS as usize]:
|
||||
{
|
||||
}
|
||||
|
||||
unsafe impl<E: PageTableEntryTrait, C: PagingConstsTrait> Sync for PageTablePageMeta<E, C> where
|
||||
[(); C::NR_LEVELS as usize]:
|
||||
{
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct MetaPageMeta {}
|
||||
|
||||
impl Sealed for MetaPageMeta {}
|
||||
impl PageMeta for MetaPageMeta {
|
||||
const USAGE: PageUsage = PageUsage::Meta;
|
||||
fn on_drop(_page: &mut Page<Self>) {
|
||||
panic!("Meta pages are currently not allowed to be dropped");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct KernelMeta {}
|
||||
|
||||
impl Sealed for KernelMeta {}
|
||||
impl PageMeta for KernelMeta {
|
||||
const USAGE: PageUsage = PageUsage::Kernel;
|
||||
fn on_drop(_page: &mut Page<Self>) {
|
||||
panic!("Kernel pages are not allowed to be dropped");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[repr(C)]
|
||||
pub struct KernelStackMeta {}
|
||||
|
||||
impl Sealed for KernelStackMeta {}
|
||||
impl PageMeta for KernelStackMeta {
|
||||
const USAGE: PageUsage = PageUsage::KernelStack;
|
||||
fn on_drop(_page: &mut Page<Self>) {}
|
||||
}
|
||||
|
||||
// ======== End of all the specific metadata structures definitions ===========
|
||||
impl_page_meta!(MetaPageMeta);
|
||||
|
||||
/// Initializes the metadata of all physical pages.
|
||||
///
|
||||
|
@ -19,16 +19,16 @@ mod cont_pages;
|
||||
pub mod meta;
|
||||
|
||||
use core::{
|
||||
any::Any,
|
||||
marker::PhantomData,
|
||||
mem::ManuallyDrop,
|
||||
panic,
|
||||
sync::atomic::{AtomicU32, AtomicUsize, Ordering},
|
||||
};
|
||||
|
||||
pub use cont_pages::ContPages;
|
||||
use meta::{mapping, FrameMeta, MetaSlot, PageMeta, PageUsage};
|
||||
use meta::{mapping, MetaSlot, PageMeta, PAGE_METADATA_MAX_ALIGN, PAGE_METADATA_MAX_SIZE};
|
||||
|
||||
use super::{Frame, PagingLevel, PAGE_SIZE};
|
||||
use super::{frame::FrameMeta, Frame, PagingLevel, PAGE_SIZE};
|
||||
use crate::mm::{Paddr, PagingConsts, Vaddr};
|
||||
|
||||
static MAX_PADDR: AtomicUsize = AtomicUsize::new(0);
|
||||
@ -41,6 +41,7 @@ pub struct Page<M: PageMeta> {
|
||||
}
|
||||
|
||||
unsafe impl<M: PageMeta> Send for Page<M> {}
|
||||
|
||||
unsafe impl<M: PageMeta> Sync for Page<M> {}
|
||||
|
||||
impl<M: PageMeta> Page<M> {
|
||||
@ -56,20 +57,26 @@ impl<M: PageMeta> Page<M> {
|
||||
pub fn from_unused(paddr: Paddr, metadata: M) -> Self {
|
||||
assert!(paddr % PAGE_SIZE == 0);
|
||||
assert!(paddr < MAX_PADDR.load(Ordering::Relaxed) as Paddr);
|
||||
|
||||
// Checking unsafe preconditions of the `PageMeta` trait.
|
||||
debug_assert!(size_of::<M>() <= PAGE_METADATA_MAX_SIZE);
|
||||
debug_assert!(align_of::<M>() <= PAGE_METADATA_MAX_ALIGN);
|
||||
|
||||
let vaddr = mapping::page_to_meta::<PagingConsts>(paddr);
|
||||
let ptr = vaddr as *const MetaSlot;
|
||||
|
||||
// SAFETY: The aligned pointer points to an initialized `MetaSlot`.
|
||||
let usage = unsafe { &(*ptr).usage };
|
||||
// SAFETY: The aligned pointer points to an initialized `MetaSlot`.
|
||||
// SAFETY: The aligned pointer points to a initialized `MetaSlot`.
|
||||
let ref_count = unsafe { &(*ptr).ref_count };
|
||||
|
||||
usage
|
||||
.compare_exchange(0, M::USAGE as u8, Ordering::SeqCst, Ordering::Relaxed)
|
||||
.expect("page already in use when trying to get a new handle");
|
||||
ref_count
|
||||
.compare_exchange(0, 1, Ordering::Acquire, Ordering::Relaxed)
|
||||
.expect("Page already in use when trying to get a new handle");
|
||||
|
||||
let old_ref_count = ref_count.fetch_add(1, Ordering::Relaxed);
|
||||
debug_assert_eq!(old_ref_count, 0);
|
||||
// SAFETY: The aligned pointer points to a initialized `MetaSlot`.
|
||||
let vtable_ptr = unsafe { (*ptr).vtable_ptr.get() };
|
||||
|
||||
// SAFETY: The pointer is valid and we have the exclusive access.
|
||||
unsafe { vtable_ptr.write(core::ptr::metadata(&metadata as &dyn PageMeta)) };
|
||||
|
||||
// Initialize the metadata
|
||||
// SAFETY: The pointer points to the first byte of the `MetaSlot`
|
||||
@ -183,9 +190,10 @@ impl<M: PageMeta> Drop for Page<M> {
|
||||
// A fence is needed here with the same reasons stated in the implementation of
|
||||
// `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
|
||||
core::sync::atomic::fence(Ordering::Acquire);
|
||||
|
||||
// SAFETY: this is the last reference and is about to be dropped.
|
||||
unsafe {
|
||||
meta::drop_as_last::<M>(self.ptr);
|
||||
meta::drop_last_in_place(self.ptr as *mut MetaSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -229,6 +237,18 @@ impl DynPage {
|
||||
Self { ptr }
|
||||
}
|
||||
|
||||
/// Get the metadata of this page.
|
||||
pub fn meta(&self) -> &dyn Any {
|
||||
// SAFETY: The pointer is valid and no other writes will be done to it.
|
||||
let vtable_ptr = unsafe { *(*self.ptr).vtable_ptr.get() };
|
||||
|
||||
let meta_ptr: *const dyn PageMeta = core::ptr::from_raw_parts(self.ptr, vtable_ptr);
|
||||
|
||||
// SAFETY: The pointer is valid and the type is correct for the stored
|
||||
// metadata.
|
||||
(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::<PagingConsts>(self.ptr as Vaddr)
|
||||
@ -244,14 +264,6 @@ impl DynPage {
|
||||
PAGE_SIZE
|
||||
}
|
||||
|
||||
/// Get the usage of the page.
|
||||
pub fn usage(&self) -> PageUsage {
|
||||
// SAFETY: structure is safely created with a pointer that points
|
||||
// to initialized [`MetaSlot`] memory.
|
||||
let usage_raw = unsafe { (*self.ptr).usage.load(Ordering::Relaxed) };
|
||||
num::FromPrimitive::from_u8(usage_raw).unwrap()
|
||||
}
|
||||
|
||||
fn ref_count(&self) -> &AtomicU32 {
|
||||
unsafe { &(*self.ptr).ref_count }
|
||||
}
|
||||
@ -265,7 +277,7 @@ impl<M: PageMeta> TryFrom<DynPage> for Page<M> {
|
||||
/// 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: DynPage) -> Result<Self, Self::Error> {
|
||||
if dyn_page.usage() == M::USAGE {
|
||||
if dyn_page.meta().is::<M>() {
|
||||
let result = Page {
|
||||
ptr: dyn_page.ptr,
|
||||
_marker: PhantomData,
|
||||
@ -307,28 +319,10 @@ impl Drop for DynPage {
|
||||
// A fence is needed here with the same reasons stated in the implementation of
|
||||
// `Arc::drop`: <https://doc.rust-lang.org/std/sync/struct.Arc.html#method.drop>.
|
||||
core::sync::atomic::fence(Ordering::Acquire);
|
||||
// Drop the page and its metadata according to its usage.
|
||||
// SAFETY: all `drop_as_last` calls in match arms operates on a last, about to be
|
||||
// dropped page reference.
|
||||
|
||||
// SAFETY: this is the last reference and is about to be dropped.
|
||||
unsafe {
|
||||
match self.usage() {
|
||||
PageUsage::Frame => {
|
||||
meta::drop_as_last::<meta::FrameMeta>(self.ptr);
|
||||
}
|
||||
PageUsage::PageTable => {
|
||||
meta::drop_as_last::<meta::PageTablePageMeta>(self.ptr);
|
||||
}
|
||||
PageUsage::KernelStack => {
|
||||
meta::drop_as_last::<meta::KernelStackMeta>(self.ptr);
|
||||
}
|
||||
// The following pages don't have metadata and can't be dropped.
|
||||
PageUsage::Unused
|
||||
| PageUsage::Reserved
|
||||
| PageUsage::Kernel
|
||||
| PageUsage::Meta => {
|
||||
panic!("dropping a dynamic page with usage {:?}", self.usage());
|
||||
}
|
||||
}
|
||||
meta::drop_last_in_place(self.ptr as *mut MetaSlot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -70,15 +70,13 @@ use core::{any::TypeId, marker::PhantomData, mem::ManuallyDrop, ops::Range};
|
||||
use align_ext::AlignExt;
|
||||
|
||||
use super::{
|
||||
page_size, pte_index, Child, Entry, KernelMode, PageTable, PageTableEntryTrait, PageTableError,
|
||||
PageTableMode, PageTableNode, PagingConstsTrait, PagingLevel, RawPageTableNode, UserMode,
|
||||
page_size, pte_index, Child, Entry, KernelMode, MapTrackingStatus, PageTable,
|
||||
PageTableEntryTrait, PageTableError, PageTableMode, PageTableNode, PagingConstsTrait,
|
||||
PagingLevel, RawPageTableNode, UserMode,
|
||||
};
|
||||
use crate::{
|
||||
mm::{
|
||||
kspace::should_map_as_tracked,
|
||||
paddr_to_vaddr,
|
||||
page::{meta::MapTrackingStatus, DynPage},
|
||||
Paddr, PageProperty, Vaddr,
|
||||
kspace::should_map_as_tracked, paddr_to_vaddr, page::DynPage, Paddr, PageProperty, Vaddr,
|
||||
},
|
||||
task::{disable_preempt, DisabledPreemptGuard},
|
||||
};
|
||||
|
@ -3,8 +3,8 @@
|
||||
use core::{fmt::Debug, marker::PhantomData, ops::Range};
|
||||
|
||||
use super::{
|
||||
nr_subpage_per_huge, page::meta::MapTrackingStatus, page_prop::PageProperty, page_size, Paddr,
|
||||
PagingConstsTrait, PagingLevel, Vaddr,
|
||||
nr_subpage_per_huge, page_prop::PageProperty, page_size, Paddr, PagingConstsTrait, PagingLevel,
|
||||
Vaddr,
|
||||
};
|
||||
use crate::{
|
||||
arch::mm::{PageTableEntry, PagingConsts},
|
||||
@ -338,7 +338,9 @@ pub(super) unsafe fn page_walk<E: PageTableEntryTrait, C: PagingConstsTrait>(
|
||||
/// The interface for defining architecture-specific page table entries.
|
||||
///
|
||||
/// Note that a default PTE should be a PTE that points to nothing.
|
||||
pub trait PageTableEntryTrait: Clone + Copy + Debug + Default + Pod + Sized + Sync {
|
||||
pub trait PageTableEntryTrait:
|
||||
Clone + Copy + Debug + Default + Pod + Sized + Send + Sync + 'static
|
||||
{
|
||||
/// Create a set of new invalid page table flags that indicates an absent page.
|
||||
///
|
||||
/// Note that currently the implementation requires an all zero PTE to be an absent PTE.
|
||||
|
@ -4,11 +4,11 @@
|
||||
|
||||
use core::{mem::ManuallyDrop, panic};
|
||||
|
||||
use super::{PageTableEntryTrait, RawPageTableNode};
|
||||
use super::{MapTrackingStatus, PageTableEntryTrait, RawPageTableNode};
|
||||
use crate::{
|
||||
arch::mm::{PageTableEntry, PagingConsts},
|
||||
mm::{
|
||||
page::{inc_page_ref_count, meta::MapTrackingStatus, DynPage},
|
||||
page::{inc_page_ref_count, DynPage},
|
||||
page_prop::PageProperty,
|
||||
Paddr, PagingConstsTrait, PagingLevel,
|
||||
},
|
||||
|
@ -2,11 +2,8 @@
|
||||
|
||||
//! This module provides accessors to the page table entries in a node.
|
||||
|
||||
use super::{Child, PageTableEntryTrait, PageTableNode};
|
||||
use crate::mm::{
|
||||
nr_subpage_per_huge, page::meta::MapTrackingStatus, page_prop::PageProperty, page_size,
|
||||
PagingConstsTrait,
|
||||
};
|
||||
use super::{Child, MapTrackingStatus, PageTableEntryTrait, PageTableNode};
|
||||
use crate::mm::{nr_subpage_per_huge, page_prop::PageProperty, page_size, PagingConstsTrait};
|
||||
|
||||
/// A view of an entry in a page table node.
|
||||
///
|
||||
|
@ -28,7 +28,12 @@
|
||||
mod child;
|
||||
mod entry;
|
||||
|
||||
use core::{marker::PhantomData, mem::ManuallyDrop, sync::atomic::Ordering};
|
||||
use core::{
|
||||
cell::SyncUnsafeCell,
|
||||
marker::PhantomData,
|
||||
mem::ManuallyDrop,
|
||||
sync::atomic::{AtomicU8, Ordering},
|
||||
};
|
||||
|
||||
pub(in crate::mm) use self::{child::Child, entry::Entry};
|
||||
use super::{nr_subpage_per_huge, PageTableEntryTrait};
|
||||
@ -36,11 +41,7 @@ use crate::{
|
||||
arch::mm::{PageTableEntry, PagingConsts},
|
||||
mm::{
|
||||
paddr_to_vaddr,
|
||||
page::{
|
||||
self, inc_page_ref_count,
|
||||
meta::{MapTrackingStatus, PageMeta, PageTablePageMeta, PageUsage},
|
||||
DynPage, Page,
|
||||
},
|
||||
page::{self, inc_page_ref_count, meta::PageMeta, DynPage, Page},
|
||||
Paddr, PagingConstsTrait, PagingLevel, PAGE_SIZE,
|
||||
},
|
||||
};
|
||||
@ -352,23 +353,73 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageMeta for PageTablePageMeta<E, C>
|
||||
/// The metadata of any kinds of page table pages.
|
||||
/// Make sure the the generic parameters don't effect the memory layout.
|
||||
#[derive(Debug)]
|
||||
pub(in crate::mm) struct PageTablePageMeta<
|
||||
E: PageTableEntryTrait = PageTableEntry,
|
||||
C: PagingConstsTrait = PagingConsts,
|
||||
> where
|
||||
[(); C::NR_LEVELS as usize]:,
|
||||
{
|
||||
/// The number of valid PTEs. It is mutable if the lock is held.
|
||||
pub nr_children: SyncUnsafeCell<u16>,
|
||||
/// The level of the page table page. A page table page cannot be
|
||||
/// referenced by page tables of different levels.
|
||||
pub level: PagingLevel,
|
||||
/// The lock for the page table page.
|
||||
pub lock: AtomicU8,
|
||||
/// Whether the pages mapped by the node is tracked.
|
||||
pub is_tracked: MapTrackingStatus,
|
||||
_phantom: core::marker::PhantomData<(E, C)>,
|
||||
}
|
||||
|
||||
/// Describe if the physical address recorded in this page table refers to a
|
||||
/// page tracked by metadata.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub(in crate::mm) enum MapTrackingStatus {
|
||||
/// The page table node cannot contain references to any pages. It can only
|
||||
/// contain references to child page table nodes.
|
||||
NotApplicable,
|
||||
/// The mapped pages are not tracked by metadata. If any child page table
|
||||
/// nodes exist, they should also be tracked.
|
||||
Untracked,
|
||||
/// The mapped pages are tracked by metadata. If any child page table nodes
|
||||
/// exist, they should also be tracked.
|
||||
Tracked,
|
||||
}
|
||||
|
||||
impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageTablePageMeta<E, C>
|
||||
where
|
||||
[(); C::NR_LEVELS as usize]:,
|
||||
{
|
||||
const USAGE: PageUsage = PageUsage::PageTable;
|
||||
pub fn new_locked(level: PagingLevel, is_tracked: MapTrackingStatus) -> Self {
|
||||
Self {
|
||||
nr_children: SyncUnsafeCell::new(0),
|
||||
level,
|
||||
lock: AtomicU8::new(1),
|
||||
is_tracked,
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_drop(page: &mut Page<Self>) {
|
||||
// SAFETY: This is the last reference so we have an exclusive access.
|
||||
let nr_children = unsafe { *page.meta().nr_children.get() };
|
||||
// SAFETY: The layout of the `PageTablePageMeta` is ensured to be the same for
|
||||
// all possible generic parameters. And the layout fits the requirements.
|
||||
unsafe impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageMeta for PageTablePageMeta<E, C>
|
||||
where
|
||||
[(); C::NR_LEVELS as usize]:,
|
||||
{
|
||||
fn on_drop(&mut self, paddr: Paddr) {
|
||||
let nr_children = self.nr_children.get_mut();
|
||||
|
||||
if nr_children == 0 {
|
||||
if *nr_children == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let paddr = page.paddr();
|
||||
let level = page.meta().level;
|
||||
let is_tracked = page.meta().is_tracked;
|
||||
let level = self.level;
|
||||
let is_tracked = self.is_tracked;
|
||||
|
||||
// Drop the children.
|
||||
for i in 0..nr_subpage_per_huge::<C>() {
|
||||
|
@ -5,8 +5,9 @@ use core::mem::ManuallyDrop;
|
||||
use super::*;
|
||||
use crate::{
|
||||
mm::{
|
||||
frame::FrameMeta,
|
||||
kspace::LINEAR_MAPPING_BASE_VADDR,
|
||||
page::{allocator, meta::FrameMeta},
|
||||
page::allocator,
|
||||
page_prop::{CachePolicy, PageFlags},
|
||||
MAX_USERSPACE_VADDR,
|
||||
},
|
||||
|
@ -1,9 +1,10 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use crate::{
|
||||
impl_page_meta,
|
||||
mm::{
|
||||
kspace::kvirt_area::{KVirtArea, Tracked},
|
||||
page::{allocator, meta::KernelStackMeta},
|
||||
page::allocator,
|
||||
page_prop::{CachePolicy, PageFlags, PageProperty, PrivilegedPageFlags},
|
||||
PAGE_SIZE,
|
||||
},
|
||||
@ -34,6 +35,11 @@ pub struct KernelStack {
|
||||
has_guard_page: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct KernelStackMeta {}
|
||||
|
||||
impl_page_meta!(KernelStackMeta);
|
||||
|
||||
impl KernelStack {
|
||||
/// Generates a kernel stack with guard pages.
|
||||
/// 4 additional pages are allocated and regarded as guard pages, which should not be accessed.
|
||||
|
Reference in New Issue
Block a user