mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-22 08:53:29 +00:00
Allow specifying initial page metadata when allocating
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
077a9ac3ab
commit
819e8dacd5
@ -57,9 +57,10 @@ impl FrameAllocOptions {
|
|||||||
/// Allocates a collection of page frames according to the given options.
|
/// Allocates a collection of page frames according to the given options.
|
||||||
pub fn alloc(&self) -> Result<Vec<Frame>> {
|
pub fn alloc(&self) -> Result<Vec<Frame>> {
|
||||||
let pages = if self.is_contiguous {
|
let pages = if self.is_contiguous {
|
||||||
page::allocator::alloc(self.nframes * PAGE_SIZE).ok_or(Error::NoMemory)?
|
page::allocator::alloc(self.nframes * PAGE_SIZE, |_| FrameMeta::default())
|
||||||
|
.ok_or(Error::NoMemory)?
|
||||||
} else {
|
} else {
|
||||||
page::allocator::alloc_contiguous(self.nframes * PAGE_SIZE)
|
page::allocator::alloc_contiguous(self.nframes * PAGE_SIZE, |_| FrameMeta::default())
|
||||||
.ok_or(Error::NoMemory)?
|
.ok_or(Error::NoMemory)?
|
||||||
.into()
|
.into()
|
||||||
};
|
};
|
||||||
@ -79,7 +80,7 @@ impl FrameAllocOptions {
|
|||||||
return Err(Error::InvalidArgs);
|
return Err(Error::InvalidArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
let page = page::allocator::alloc_single().ok_or(Error::NoMemory)?;
|
let page = page::allocator::alloc_single(FrameMeta::default()).ok_or(Error::NoMemory)?;
|
||||||
let frame = Frame { page };
|
let frame = Frame { page };
|
||||||
if !self.uninit {
|
if !self.uninit {
|
||||||
frame.writer().fill(0);
|
frame.writer().fill(0);
|
||||||
@ -98,7 +99,7 @@ impl FrameAllocOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let segment: Segment =
|
let segment: Segment =
|
||||||
page::allocator::alloc_contiguous::<FrameMeta>(self.nframes * PAGE_SIZE)
|
page::allocator::alloc_contiguous(self.nframes * PAGE_SIZE, |_| FrameMeta::default())
|
||||||
.ok_or(Error::NoMemory)?
|
.ok_or(Error::NoMemory)?
|
||||||
.into();
|
.into();
|
||||||
if !self.uninit {
|
if !self.uninit {
|
||||||
|
@ -211,7 +211,7 @@ pub fn init_kernel_page_table(meta_pages: Vec<Page<MetaPageMeta>>) {
|
|||||||
};
|
};
|
||||||
let mut cursor = kpt.cursor_mut(&from).unwrap();
|
let mut cursor = kpt.cursor_mut(&from).unwrap();
|
||||||
for frame_paddr in to.step_by(PAGE_SIZE) {
|
for frame_paddr in to.step_by(PAGE_SIZE) {
|
||||||
let page = Page::<KernelMeta>::from_unused(frame_paddr);
|
let page = Page::<KernelMeta>::from_unused(frame_paddr, KernelMeta::default());
|
||||||
// SAFETY: we are doing mappings for the kernel.
|
// SAFETY: we are doing mappings for the kernel.
|
||||||
unsafe {
|
unsafe {
|
||||||
cursor.map(page.into(), prop);
|
cursor.map(page.into(), prop);
|
||||||
|
@ -13,7 +13,11 @@ use log::info;
|
|||||||
use spin::Once;
|
use spin::Once;
|
||||||
|
|
||||||
use super::{cont_pages::ContPages, meta::PageMeta, Page};
|
use super::{cont_pages::ContPages, meta::PageMeta, Page};
|
||||||
use crate::{boot::memory_region::MemoryRegionType, mm::PAGE_SIZE, sync::SpinLock};
|
use crate::{
|
||||||
|
boot::memory_region::MemoryRegionType,
|
||||||
|
mm::{Paddr, PAGE_SIZE},
|
||||||
|
sync::SpinLock,
|
||||||
|
};
|
||||||
|
|
||||||
/// FrameAllocator with a counter for allocated memory
|
/// FrameAllocator with a counter for allocated memory
|
||||||
pub(in crate::mm) struct CountingFrameAllocator {
|
pub(in crate::mm) struct CountingFrameAllocator {
|
||||||
@ -58,26 +62,37 @@ impl CountingFrameAllocator {
|
|||||||
pub(in crate::mm) static PAGE_ALLOCATOR: Once<SpinLock<CountingFrameAllocator>> = Once::new();
|
pub(in crate::mm) static PAGE_ALLOCATOR: Once<SpinLock<CountingFrameAllocator>> = Once::new();
|
||||||
|
|
||||||
/// Allocate a single page.
|
/// Allocate a single page.
|
||||||
pub(crate) fn alloc_single<M: PageMeta>() -> Option<Page<M>> {
|
///
|
||||||
|
/// The metadata of the page is initialized with the given metadata.
|
||||||
|
pub(crate) fn alloc_single<M: PageMeta>(metadata: M) -> Option<Page<M>> {
|
||||||
PAGE_ALLOCATOR.get().unwrap().lock().alloc(1).map(|idx| {
|
PAGE_ALLOCATOR.get().unwrap().lock().alloc(1).map(|idx| {
|
||||||
let paddr = idx * PAGE_SIZE;
|
let paddr = idx * PAGE_SIZE;
|
||||||
Page::<M>::from_unused(paddr)
|
Page::from_unused(paddr, metadata)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate a contiguous range of pages of a given length in bytes.
|
/// Allocate a contiguous range of pages of a given length in bytes.
|
||||||
///
|
///
|
||||||
|
/// The caller must provide a closure to initialize metadata for all the pages.
|
||||||
|
/// The closure receives the physical address of the page and returns the
|
||||||
|
/// metadata, which is similar to [`core::array::from_fn`].
|
||||||
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// The function panics if the length is not base-page-aligned.
|
/// The function panics if the length is not base-page-aligned.
|
||||||
pub(crate) fn alloc_contiguous<M: PageMeta>(len: usize) -> Option<ContPages<M>> {
|
pub(crate) fn alloc_contiguous<M: PageMeta, F>(len: usize, metadata_fn: F) -> Option<ContPages<M>>
|
||||||
|
where
|
||||||
|
F: FnMut(Paddr) -> M,
|
||||||
|
{
|
||||||
assert!(len % PAGE_SIZE == 0);
|
assert!(len % PAGE_SIZE == 0);
|
||||||
PAGE_ALLOCATOR
|
PAGE_ALLOCATOR
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.lock()
|
.lock()
|
||||||
.alloc(len / PAGE_SIZE)
|
.alloc(len / PAGE_SIZE)
|
||||||
.map(|start| ContPages::from_unused(start * PAGE_SIZE..start * PAGE_SIZE + len))
|
.map(|start| {
|
||||||
|
ContPages::from_unused(start * PAGE_SIZE..start * PAGE_SIZE + len, metadata_fn)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Allocate pages.
|
/// Allocate pages.
|
||||||
@ -85,17 +100,24 @@ pub(crate) fn alloc_contiguous<M: PageMeta>(len: usize) -> Option<ContPages<M>>
|
|||||||
/// The allocated pages are not guarenteed to be contiguous.
|
/// The allocated pages are not guarenteed to be contiguous.
|
||||||
/// The total length of the allocated pages is `len`.
|
/// The total length of the allocated pages is `len`.
|
||||||
///
|
///
|
||||||
|
/// The caller must provide a closure to initialize metadata for all the pages.
|
||||||
|
/// The closure receives the physical address of the page and returns the
|
||||||
|
/// metadata, which is similar to [`core::array::from_fn`].
|
||||||
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// The function panics if the length is not base-page-aligned.
|
/// The function panics if the length is not base-page-aligned.
|
||||||
pub(crate) fn alloc<M: PageMeta>(len: usize) -> Option<Vec<Page<M>>> {
|
pub(crate) fn alloc<M: PageMeta, F>(len: usize, mut metadata_fn: F) -> Option<Vec<Page<M>>>
|
||||||
|
where
|
||||||
|
F: FnMut(Paddr) -> M,
|
||||||
|
{
|
||||||
assert!(len % PAGE_SIZE == 0);
|
assert!(len % PAGE_SIZE == 0);
|
||||||
let nframes = len / PAGE_SIZE;
|
let nframes = len / PAGE_SIZE;
|
||||||
let mut allocator = PAGE_ALLOCATOR.get().unwrap().lock();
|
let mut allocator = PAGE_ALLOCATOR.get().unwrap().lock();
|
||||||
let mut vector = Vec::new();
|
let mut vector = Vec::new();
|
||||||
for _ in 0..nframes {
|
for _ in 0..nframes {
|
||||||
let paddr = allocator.alloc(1)? * PAGE_SIZE;
|
let paddr = allocator.alloc(1)? * PAGE_SIZE;
|
||||||
let page = Page::<M>::from_unused(paddr);
|
let page = Page::<M>::from_unused(paddr, metadata_fn(paddr));
|
||||||
vector.push(page);
|
vector.push(page);
|
||||||
}
|
}
|
||||||
Some(vector)
|
Some(vector)
|
||||||
|
@ -36,14 +36,21 @@ impl<M: PageMeta> Drop for ContPages<M> {
|
|||||||
impl<M: PageMeta> ContPages<M> {
|
impl<M: PageMeta> ContPages<M> {
|
||||||
/// Create a new `ContPages` from unused pages.
|
/// Create a new `ContPages` from unused pages.
|
||||||
///
|
///
|
||||||
|
/// The caller must provide a closure to initialize metadata for all the pages.
|
||||||
|
/// The closure receives the physical address of the page and returns the
|
||||||
|
/// metadata, which is similar to [`core::array::from_fn`].
|
||||||
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// The function panics if:
|
/// The function panics if:
|
||||||
/// - the physical address is invalid or not aligned;
|
/// - the physical address is invalid or not aligned;
|
||||||
/// - any of the pages are already in use.
|
/// - any of the pages are already in use.
|
||||||
pub fn from_unused(range: Range<Paddr>) -> Self {
|
pub fn from_unused<F>(range: Range<Paddr>, mut metadata_fn: F) -> Self
|
||||||
|
where
|
||||||
|
F: FnMut(Paddr) -> M,
|
||||||
|
{
|
||||||
for i in range.clone().step_by(PAGE_SIZE) {
|
for i in range.clone().step_by(PAGE_SIZE) {
|
||||||
let _ = ManuallyDrop::new(Page::<M>::from_unused(i));
|
let _ = ManuallyDrop::new(Page::<M>::from_unused(i, metadata_fn(i)));
|
||||||
}
|
}
|
||||||
Self {
|
Self {
|
||||||
range,
|
range,
|
||||||
|
@ -117,7 +117,7 @@ const_assert_eq!(size_of::<MetaSlot>(), 16);
|
|||||||
/// If a page type needs specific drop behavior, it should specify
|
/// If a page type needs specific drop behavior, it should specify
|
||||||
/// when implementing this trait. When we drop the last handle to
|
/// when implementing this trait. When we drop the last handle to
|
||||||
/// this page, the `on_drop` method will be called.
|
/// this page, the `on_drop` method will be called.
|
||||||
pub trait PageMeta: Default + Sync + private::Sealed + Sized {
|
pub trait PageMeta: Sync + private::Sealed + Sized {
|
||||||
const USAGE: PageUsage;
|
const USAGE: PageUsage;
|
||||||
|
|
||||||
fn on_drop(page: &mut Page<Self>);
|
fn on_drop(page: &mut Page<Self>);
|
||||||
@ -184,7 +184,7 @@ pub struct PageTablePageMetaInner {
|
|||||||
|
|
||||||
/// The metadata of any kinds of page table pages.
|
/// The metadata of any kinds of page table pages.
|
||||||
/// Make sure the the generic parameters don't effect the memory layout.
|
/// Make sure the the generic parameters don't effect the memory layout.
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct PageTablePageMeta<
|
pub struct PageTablePageMeta<
|
||||||
E: PageTableEntryTrait = PageTableEntry,
|
E: PageTableEntryTrait = PageTableEntry,
|
||||||
@ -194,7 +194,23 @@ pub struct PageTablePageMeta<
|
|||||||
{
|
{
|
||||||
pub lock: AtomicU8,
|
pub lock: AtomicU8,
|
||||||
pub inner: UnsafeCell<PageTablePageMetaInner>,
|
pub inner: UnsafeCell<PageTablePageMetaInner>,
|
||||||
_phantom: core::marker::PhantomData<(E, C)>,
|
_phantom: PhantomData<(E, C)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: PageTableEntryTrait, C: PagingConstsTrait> PageTablePageMeta<E, C>
|
||||||
|
where
|
||||||
|
[(); C::NR_LEVELS as usize]:,
|
||||||
|
{
|
||||||
|
pub fn new_locked(level: PagingLevel) -> Self {
|
||||||
|
Self {
|
||||||
|
lock: AtomicU8::new(1),
|
||||||
|
inner: UnsafeCell::new(PageTablePageMetaInner {
|
||||||
|
level,
|
||||||
|
nr_children: 0,
|
||||||
|
}),
|
||||||
|
_phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E: PageTableEntryTrait, C: PagingConstsTrait> Sealed for PageTablePageMeta<E, C> where
|
impl<E: PageTableEntryTrait, C: PagingConstsTrait> Sealed for PageTablePageMeta<E, C> where
|
||||||
@ -275,7 +291,7 @@ pub(crate) fn init() -> Vec<Page<MetaPageMeta>> {
|
|||||||
// Now the metadata pages are mapped, we can initialize the metadata.
|
// Now the metadata pages are mapped, we can initialize the metadata.
|
||||||
meta_pages
|
meta_pages
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(Page::<MetaPageMeta>::from_unused)
|
.map(|paddr| Page::<MetaPageMeta>::from_unused(paddr, MetaPageMeta::default()))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,12 +45,14 @@ unsafe impl<M: PageMeta> Sync for Page<M> {}
|
|||||||
impl<M: PageMeta> Page<M> {
|
impl<M: PageMeta> Page<M> {
|
||||||
/// Get a `Page` handle with a specific usage from a raw, unused page.
|
/// Get a `Page` handle with a specific usage from a raw, unused page.
|
||||||
///
|
///
|
||||||
|
/// The caller should provide the initial metadata of the page.
|
||||||
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
/// The function panics if:
|
/// The function panics if:
|
||||||
/// - the physical address is out of bound or not aligned;
|
/// - the physical address is out of bound or not aligned;
|
||||||
/// - the page is already in use.
|
/// - the page is already in use.
|
||||||
pub fn from_unused(paddr: Paddr) -> Self {
|
pub fn from_unused(paddr: Paddr, metadata: M) -> Self {
|
||||||
assert!(paddr % PAGE_SIZE == 0);
|
assert!(paddr % PAGE_SIZE == 0);
|
||||||
assert!(paddr < MAX_PADDR.load(Ordering::Relaxed) as Paddr);
|
assert!(paddr < MAX_PADDR.load(Ordering::Relaxed) as Paddr);
|
||||||
let vaddr = mapping::page_to_meta::<PagingConsts>(paddr);
|
let vaddr = mapping::page_to_meta::<PagingConsts>(paddr);
|
||||||
@ -72,7 +74,7 @@ impl<M: PageMeta> Page<M> {
|
|||||||
// SAFETY: The pointer points to the first byte of the `MetaSlot`
|
// SAFETY: The pointer points to the first byte of the `MetaSlot`
|
||||||
// structure, and layout ensured enoungh space for `M`. The original
|
// structure, and layout ensured enoungh space for `M`. The original
|
||||||
// value does not represent any object that's needed to be dropped.
|
// value does not represent any object that's needed to be dropped.
|
||||||
unsafe { (ptr as *mut M).write(M::default()) };
|
unsafe { (ptr as *mut M).write(metadata) };
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
ptr,
|
ptr,
|
||||||
|
@ -231,14 +231,8 @@ where
|
|||||||
/// set the lock bit for performance as it is exclusive and unlocking is an
|
/// set the lock bit for performance as it is exclusive and unlocking is an
|
||||||
/// extra unnecessary expensive operation.
|
/// extra unnecessary expensive operation.
|
||||||
pub(super) fn alloc(level: PagingLevel) -> Self {
|
pub(super) fn alloc(level: PagingLevel) -> Self {
|
||||||
let page = page::allocator::alloc_single::<PageTablePageMeta<E, C>>().unwrap();
|
let meta = PageTablePageMeta::new_locked(level);
|
||||||
|
let page = page::allocator::alloc_single::<PageTablePageMeta<E, C>>(meta).unwrap();
|
||||||
// The lock is initialized as held.
|
|
||||||
page.meta().lock.store(1, Ordering::Relaxed);
|
|
||||||
|
|
||||||
// SAFETY: here the page exclusively owned by the newly created handle.
|
|
||||||
let inner = unsafe { &mut *page.meta().inner.get() };
|
|
||||||
inner.level = level;
|
|
||||||
|
|
||||||
// Zero out the page table node.
|
// Zero out the page table node.
|
||||||
let ptr = paddr_to_vaddr(page.paddr()) as *mut u8;
|
let ptr = paddr_to_vaddr(page.paddr()) as *mut u8;
|
||||||
|
@ -33,7 +33,7 @@ fn test_tracked_map_unmap() {
|
|||||||
let pt = PageTable::<UserMode>::empty();
|
let pt = PageTable::<UserMode>::empty();
|
||||||
|
|
||||||
let from = PAGE_SIZE..PAGE_SIZE * 2;
|
let from = PAGE_SIZE..PAGE_SIZE * 2;
|
||||||
let page = allocator::alloc_single::<FrameMeta>().unwrap();
|
let page = allocator::alloc_single(FrameMeta::default()).unwrap();
|
||||||
let start_paddr = page.paddr();
|
let start_paddr = page.paddr();
|
||||||
let prop = PageProperty::new(PageFlags::RW, CachePolicy::Writeback);
|
let prop = PageProperty::new(PageFlags::RW, CachePolicy::Writeback);
|
||||||
unsafe { pt.cursor_mut(&from).unwrap().map(page.into(), prop) };
|
unsafe { pt.cursor_mut(&from).unwrap().map(page.into(), prop) };
|
||||||
@ -77,7 +77,7 @@ fn test_untracked_map_unmap() {
|
|||||||
fn test_user_copy_on_write() {
|
fn test_user_copy_on_write() {
|
||||||
let pt = PageTable::<UserMode>::empty();
|
let pt = PageTable::<UserMode>::empty();
|
||||||
let from = PAGE_SIZE..PAGE_SIZE * 2;
|
let from = PAGE_SIZE..PAGE_SIZE * 2;
|
||||||
let page = allocator::alloc_single::<FrameMeta>().unwrap();
|
let page = allocator::alloc_single(FrameMeta::default()).unwrap();
|
||||||
let start_paddr = page.paddr();
|
let start_paddr = page.paddr();
|
||||||
let prop = PageProperty::new(PageFlags::RW, CachePolicy::Writeback);
|
let prop = PageProperty::new(PageFlags::RW, CachePolicy::Writeback);
|
||||||
unsafe { pt.cursor_mut(&from).unwrap().map(page.clone().into(), prop) };
|
unsafe { pt.cursor_mut(&from).unwrap().map(page.clone().into(), prop) };
|
||||||
@ -133,7 +133,7 @@ fn test_base_protect_query() {
|
|||||||
|
|
||||||
let from_ppn = 1..1000;
|
let from_ppn = 1..1000;
|
||||||
let from = PAGE_SIZE * from_ppn.start..PAGE_SIZE * from_ppn.end;
|
let from = PAGE_SIZE * from_ppn.start..PAGE_SIZE * from_ppn.end;
|
||||||
let to = allocator::alloc::<FrameMeta>(999 * PAGE_SIZE).unwrap();
|
let to = allocator::alloc(999 * PAGE_SIZE, |_| FrameMeta::default()).unwrap();
|
||||||
let prop = PageProperty::new(PageFlags::RW, CachePolicy::Writeback);
|
let prop = PageProperty::new(PageFlags::RW, CachePolicy::Writeback);
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut cursor = pt.cursor_mut(&from).unwrap();
|
let mut cursor = pt.cursor_mut(&from).unwrap();
|
||||||
|
Reference in New Issue
Block a user