Use pointer metadata for page metadata

This commit is contained in:
Zhang Junyang
2024-10-03 15:41:56 +08:00
committed by Tate, Hongliang Tian
parent cd22854f59
commit 60365a818a
17 changed files with 223 additions and 290 deletions

View File

@ -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,

View File

@ -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,
},
};

View File

@ -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)]

View File

@ -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`.

View File

@ -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,
};

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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.
///

View File

@ -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);
}
}
}

View File

@ -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},
};

View File

@ -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.

View File

@ -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,
},

View File

@ -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.
///

View File

@ -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>() {

View File

@ -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,
},

View File

@ -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.