From 232e62b0538f9fcb28789d9d41780eccc64613c1 Mon Sep 17 00:00:00 2001 From: Zhang Junyang Date: Mon, 3 Jun 2024 11:55:09 +0000 Subject: [PATCH] Redo segment with `Arc` rather than creating `SegmentHeadMeta` --- framework/aster-frame/src/mm/frame/segment.rs | 65 ++++++++++++------- framework/aster-frame/src/mm/page/meta.rs | 44 ++++--------- 2 files changed, 55 insertions(+), 54 deletions(-) diff --git a/framework/aster-frame/src/mm/frame/segment.rs b/framework/aster-frame/src/mm/frame/segment.rs index 367b21a26..b4591faa4 100644 --- a/framework/aster-frame/src/mm/frame/segment.rs +++ b/framework/aster-frame/src/mm/frame/segment.rs @@ -1,15 +1,12 @@ // SPDX-License-Identifier: MPL-2.0 +use alloc::sync::Arc; use core::ops::Range; use super::Frame; use crate::{ mm::{ - page::{ - allocator, - meta::{PageMeta, PageUsage, SegmentHeadMeta}, - Page, - }, + page::{meta::FrameMeta, Page}, HasPaddr, Paddr, VmIo, VmReader, VmWriter, PAGE_SIZE, }, Error, Result, @@ -34,10 +31,33 @@ use crate::{ /// ``` #[derive(Debug, Clone)] pub struct Segment { - head_page: Page, + inner: Arc, range: Range, } +/// This behaves like a `[Frame]` that owns a list of frame handles. +/// +/// The ownership is acheived by the reference counting mechanism of +/// frames. When constructing a `SegmentInner`, the frame handles are +/// forgotten. When dropping a `SegmentInner`, the frame handles are +/// restored and dropped. +#[derive(Debug)] +struct SegmentInner { + start: Paddr, + nframes: usize, +} + +impl Drop for SegmentInner { + fn drop(&mut self) { + for i in 0..self.nframes { + let pa_i = self.start + i * PAGE_SIZE; + // SAFETY: for each page there would be a forgotten handle + // when creating the `SegmentInner` object. + drop(unsafe { Page::::from_raw(pa_i) }); + } + } +} + impl HasPaddr for Segment { fn paddr(&self) -> Paddr { self.start_paddr() @@ -53,10 +73,16 @@ impl Segment { /// The given range of page frames must not have been allocated before, /// as part of either a `Frame` or `Segment`. pub(crate) unsafe fn new(paddr: Paddr, nframes: usize) -> Self { - let mut head = Page::::from_unused(paddr); - head.meta_mut().seg_len = (nframes * PAGE_SIZE) as u64; + for i in 0..nframes { + let pa_i = paddr + i * PAGE_SIZE; + let page = Page::::from_unused(pa_i); + core::mem::forget(page); + } Self { - head_page: head, + inner: Arc::new(SegmentInner { + start: paddr, + nframes, + }), range: 0..nframes, } } @@ -73,7 +99,7 @@ impl Segment { assert!(!adj_range.is_empty() && adj_range.end <= orig_range.end); Self { - head_page: self.head_page.clone(), + inner: self.inner.clone(), range: adj_range, } } @@ -99,7 +125,7 @@ impl Segment { } fn start_frame_index(&self) -> usize { - self.head_page.paddr() / PAGE_SIZE + self.range.start + self.inner.start / PAGE_SIZE + self.range.start } pub fn as_ptr(&self) -> *const u8 { @@ -149,20 +175,15 @@ impl VmIo for Segment { } } -impl PageMeta for SegmentHeadMeta { - const USAGE: PageUsage = PageUsage::SegmentHead; - - fn on_drop(page: &mut Page) { - let nframes = page.meta().seg_len as usize / PAGE_SIZE; - let start_index = page.paddr() / PAGE_SIZE; - unsafe { allocator::dealloc(start_index, nframes) }; - } -} - impl From for Segment { fn from(frame: Frame) -> Self { + let paddr = frame.paddr(); + core::mem::forget(frame); Self { - head_page: frame.page.into(), + inner: Arc::new(SegmentInner { + start: paddr, + nframes: 1, + }), range: 0..1, } } diff --git a/framework/aster-frame/src/mm/page/meta.rs b/framework/aster-frame/src/mm/page/meta.rs index a78f7d16d..fbe234522 100644 --- a/framework/aster-frame/src/mm/page/meta.rs +++ b/framework/aster-frame/src/mm/page/meta.rs @@ -70,8 +70,6 @@ pub enum PageUsage { /// The page is used as a frame, i.e., a page of untyped memory. Frame = 32, - /// The page is used as the head frame in a segment. - SegmentHead = 33, /// The page is used by a page table. PageTable = 64, @@ -85,7 +83,11 @@ pub enum PageUsage { pub(in crate::mm) struct MetaSlot { /// The metadata of the page. /// - /// The implementation may cast a `*const MetaSlot` to a `*const PageMeta`. + /// 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, @@ -94,7 +96,6 @@ pub(in crate::mm) struct MetaSlot { pub(super) union MetaSlotInner { frame: ManuallyDrop, - seg_head: ManuallyDrop, // Make sure the the generic parameters don't effect the memory layout. pt: ManuallyDrop>, } @@ -131,37 +132,16 @@ use private::Sealed; #[derive(Debug, Default)] #[repr(C)] -pub struct FrameMeta {} +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 {} -#[derive(Debug, Default)] -#[repr(C)] -pub struct SegmentHeadMeta { - /// Length of the segment in bytes. - pub(in crate::mm) seg_len: u64, -} - -impl Sealed for SegmentHeadMeta {} - -impl From> for Page { - fn from(page: Page) -> Self { - // FIXME: I intended to prevent a page simultaneously managed by a segment handle - // and a frame handle. However, `Vmo` holds a frame handle while block IO needs a - // segment handle from the same page. - // A segment cannot be mapped. So we have to introduce this enforcement soon: - // assert_eq!(page.count(), 1); - unsafe { - let mut head = Page::::from_raw(page.into_raw()); - (*head.ptr) - .usage - .store(PageUsage::SegmentHead as u8, Ordering::Relaxed); - head.meta_mut().seg_len = PAGE_SIZE as u64; - head - } - } -} - #[derive(Debug, Default)] #[repr(C)] pub struct PageTablePageMeta