Refactor the APIs of VMO based on the new XArray

This commit is contained in:
Chen Chengjun
2025-04-30 10:09:20 +08:00
committed by Tate, Hongliang Tian
parent 1da723c0de
commit af2a7f7497
4 changed files with 299 additions and 241 deletions

View File

@ -5,45 +5,57 @@ use core::ops::Range;
use aster_rights::{Rights, TRights}; use aster_rights::{Rights, TRights};
use ostd::mm::{UFrame, VmIo}; use ostd::mm::{UFrame, VmIo};
use super::{CommitFlags, Vmo, VmoRightsOp}; use super::{CommitFlags, Vmo, VmoCommitError, VmoRightsOp};
use crate::prelude::*; use crate::prelude::*;
impl Vmo<Rights> { impl Vmo<Rights> {
/// Commits a page at specific offset /// Commits a page at specific offset.
pub fn commit_page(&self, offset: usize) -> Result<UFrame> {
self.check_rights(Rights::WRITE)?;
self.0.commit_page(offset)
}
/// Commits the pages specified in the range (in bytes).
/// ///
/// The range must be within the size of the VMO. /// If the commit operation needs to perform I/O, it will return a [`VmoCommitError::NeedIo`].
///
/// The start and end addresses will be rounded down and up to page boundaries.
/// ///
/// # Access rights /// # Access rights
/// ///
/// The method requires the Write right. /// The method requires the Write right.
pub fn commit(&self, range: Range<usize>) -> Result<()> { pub fn try_commit_page(&self, offset: usize) -> core::result::Result<UFrame, VmoCommitError> {
self.check_rights(Rights::WRITE)?; self.check_rights(Rights::WRITE)?;
self.0.operate_on_range( self.0.try_commit_page(offset)
&range, }
|commit_fn| commit_fn().map(|_| ()),
CommitFlags::empty(), /// Commits a page at a specific page index.
)?; ///
Ok(()) /// This method may involve I/O operations if the VMO needs to fetch
/// a page from the underlying page cache.
///
/// # Access rights
///
/// The method requires the Write right.
pub fn commit_on(&self, page_idx: usize, commit_flags: CommitFlags) -> Result<UFrame> {
self.check_rights(Rights::WRITE)?;
self.0.commit_on(page_idx, commit_flags)
} }
/// Traverses the indices within a specified range of a VMO sequentially. /// Traverses the indices within a specified range of a VMO sequentially.
///
/// For each index position, you have the option to commit the page as well as /// For each index position, you have the option to commit the page as well as
/// perform other operations. /// perform other operations.
pub(in crate::vm) fn operate_on_range<F>(&self, range: &Range<usize>, operate: F) -> Result<()> ///
/// Once a commit operation needs to perform I/O, it will return a [`VmoCommitError::NeedIo`].
///
/// # Access rights
///
/// The method requires the Write right.
pub(in crate::vm) fn try_operate_on_range<F>(
&self,
range: &Range<usize>,
operate: F,
) -> core::result::Result<(), VmoCommitError>
where where
F: FnMut(&mut dyn FnMut() -> Result<UFrame>) -> Result<()>, F: FnMut(
&mut dyn FnMut() -> core::result::Result<UFrame, VmoCommitError>,
) -> core::result::Result<(), VmoCommitError>,
{ {
self.check_rights(Rights::WRITE)?; self.check_rights(Rights::WRITE)?;
self.0 self.0.try_operate_on_range(range, operate)
.operate_on_range(range, operate, CommitFlags::empty())
} }
/// Decommits the pages specified in the range (in bytes). /// Decommits the pages specified in the range (in bytes).
@ -94,19 +106,6 @@ impl Vmo<Rights> {
Ok(Self(self.0.clone(), self.1)) Ok(Self(self.0.clone(), self.1))
} }
/// Creates a new VMO that replicates the original capability, initially representing
/// the same physical pages.
/// Changes to the permissions and commits/replacements of internal pages in the original VMO
/// and the new VMO will not affect each other.
///
/// # Access rights
///
/// The method requires the Dup right.
pub fn dup_independent(&self) -> Result<Self> {
self.check_rights(Rights::DUP | Rights::WRITE)?;
Ok(Vmo(Arc::new(super::Vmo_::clone(&self.0)), self.1))
}
/// Replaces the page at the `page_idx` in the VMO with the input `page`. /// Replaces the page at the `page_idx` in the VMO with the input `page`.
/// ///
/// # Access rights /// # Access rights

View File

@ -5,14 +5,18 @@
//! Virtual Memory Objects (VMOs). //! Virtual Memory Objects (VMOs).
use core::ops::Range; use core::{
ops::Range,
sync::atomic::{AtomicUsize, Ordering},
};
use align_ext::AlignExt; use align_ext::AlignExt;
use aster_rights::Rights; use aster_rights::Rights;
use ostd::{ use ostd::{
collections::xarray::{CursorMut, XArray},
mm::{FrameAllocOptions, UFrame, UntypedMem, VmReader, VmWriter}, mm::{FrameAllocOptions, UFrame, UntypedMem, VmReader, VmWriter},
task::disable_preempt,
}; };
use xarray::{Cursor, LockedXArray, XArray};
use crate::prelude::*; use crate::prelude::*;
@ -125,55 +129,47 @@ bitflags! {
} }
} }
/// `Pages` is the struct that manages the `UFrame`s stored in `Vmo_`. /// The error type used for commit operations of [`Vmo`].
pub(super) enum Pages { #[derive(Debug)]
/// `Pages` that cannot be resized. This kind of `Pages` will have a constant size. pub enum VmoCommitError {
Nonresizable(Mutex<XArray<UFrame>>, usize), /// Represents a general error raised during the commit operation.
/// `Pages` that can be resized and have a variable size. Err(Error),
Resizable(Mutex<(XArray<UFrame>, usize)>), /// Represents that the commit operation need to do I/O operation on the
/// wrapped index.
NeedIo(usize),
} }
impl Clone for Pages { impl From<Error> for VmoCommitError {
fn clone(&self) -> Self { fn from(e: Error) -> Self {
match self { VmoCommitError::Err(e)
Self::Nonresizable(_, _) => {
self.with(|pages, size| Self::Nonresizable(Mutex::new(pages.clone()), size))
}
Self::Resizable(_) => {
self.with(|pages, size| Self::Resizable(Mutex::new((pages.clone(), size))))
}
}
} }
} }
impl Pages { impl From<ostd::Error> for VmoCommitError {
fn with<R, F>(&self, func: F) -> R fn from(e: ostd::Error) -> Self {
where Error::from(e).into()
F: FnOnce(&mut XArray<UFrame>, usize) -> R,
{
match self {
Self::Nonresizable(pages, size) => func(&mut pages.lock(), *size),
Self::Resizable(pages) => {
let mut lock = pages.lock();
let size = lock.1;
func(&mut lock.0, size)
}
}
} }
} }
/// `Vmo_` is the structure that actually manages the content of VMO. /// `Vmo_` is the structure that actually manages the content of VMO.
///
/// Broadly speaking, there are two types of VMO: /// Broadly speaking, there are two types of VMO:
/// 1. File-backed VMO: the VMO backed by a file and resides in the `PageCache`, /// 1. File-backed VMO: the VMO backed by a file and resides in the page cache,
/// which includes a pager to provide it with actual pages. /// which includes a [`Pager`] to provide it with actual pages.
/// 2. Anonymous VMO: the VMO without a file backup, which does not have a pager. /// 2. Anonymous VMO: the VMO without a file backup, which does not have a `Pager`.
#[derive(Clone)]
pub(super) struct Vmo_ { pub(super) struct Vmo_ {
pager: Option<Arc<dyn Pager>>, pager: Option<Arc<dyn Pager>>,
/// Flags /// Flags
flags: VmoFlags, flags: VmoFlags,
/// The virtual pages where the VMO resides. /// The virtual pages where the VMO resides.
pages: Pages, pages: XArray<UFrame>,
/// The size of the VMO.
///
/// Note: This size may not necessarily match the size of the `pages`, but it is
/// required here that modifications to the size can only be made after locking
/// the [`XArray`] in the `pages` field. Therefore, the size read after locking the
/// `pages` will be the latest size.
size: AtomicUsize,
} }
impl Debug for Vmo_ { impl Debug for Vmo_ {
@ -202,111 +198,155 @@ impl CommitFlags {
impl Vmo_ { impl Vmo_ {
/// Prepares a new `UFrame` for the target index in pages, returns this new frame. /// Prepares a new `UFrame` for the target index in pages, returns this new frame.
fn prepare_page(&self, page_idx: usize) -> Result<UFrame> { ///
/// This operation may involve I/O operations if the VMO is backed by a pager.
fn prepare_page(&self, page_idx: usize, commit_flags: CommitFlags) -> Result<UFrame> {
match &self.pager { match &self.pager {
None => Ok(FrameAllocOptions::new().alloc_frame()?.into()), None => Ok(FrameAllocOptions::new().alloc_frame()?.into()),
Some(pager) => pager.commit_page(page_idx), Some(pager) => {
} if commit_flags.will_overwrite() {
}
/// Prepares a new `UFrame` for the target index in the VMO, returns this new frame.
fn prepare_overwrite(&self, page_idx: usize) -> Result<UFrame> {
if let Some(pager) = &self.pager {
pager.commit_overwrite(page_idx) pager.commit_overwrite(page_idx)
} else { } else {
Ok(FrameAllocOptions::new().alloc_frame()?.into()) pager.commit_page(page_idx)
}
}
} }
} }
fn commit_with_cursor( /// Commits a page at a specific page index.
&self, ///
cursor: &mut CursorMut<'_, UFrame>, /// This method may involve I/O operations if the VMO needs to fetch a page from
commit_flags: CommitFlags, /// the underlying page cache.
) -> Result<UFrame> { pub fn commit_on(&self, page_idx: usize, commit_flags: CommitFlags) -> Result<UFrame> {
let new_page = { let new_page = self.prepare_page(page_idx, commit_flags)?;
if let Some(committed_page) = cursor.load() {
// Fast path: return the page directly. let mut locked_pages = self.pages.lock();
return Ok(committed_page.clone()); if page_idx * PAGE_SIZE > self.size() {
} else if commit_flags.will_overwrite() { return_errno_with_message!(Errno::EINVAL, "the offset is outside the VMO");
// In this case, the page will be completely overwritten. }
self.prepare_overwrite(cursor.index() as usize)?
} else { let mut cursor = locked_pages.cursor_mut(page_idx as u64);
self.prepare_page(cursor.index() as usize)? if let Some(page) = cursor.load() {
return Ok(page.clone());
} }
};
cursor.store(new_page.clone()); cursor.store(new_page.clone());
Ok(new_page) Ok(new_page)
} }
/// Commits the page corresponding to the target offset in the VMO and return that page. fn try_commit_with_cursor(
/// If the current offset has already been committed, the page will be returned directly. &self,
pub fn commit_page(&self, offset: usize) -> Result<UFrame> { cursor: &mut Cursor<'_, UFrame>,
let page_idx = offset / PAGE_SIZE; ) -> core::result::Result<UFrame, VmoCommitError> {
self.pages.with(|pages, size| { if let Some(committed_page) = cursor.load() {
if offset >= size { return Ok(committed_page.clone());
return_errno_with_message!(Errno::EINVAL, "the offset is outside the VMO");
}
let mut cursor = pages.cursor_mut(page_idx as u64);
self.commit_with_cursor(&mut cursor, CommitFlags::empty())
})
} }
/// Decommits the page corresponding to the target offset in the VMO. if let Some(pager) = &self.pager {
fn decommit_page(&mut self, offset: usize) -> Result<()> { // FIXME: Here `Vmo` treat all instructions in `pager` as I/O instructions
// since it needs to take the inner `Mutex` lock and users also cannot hold a
// `SpinLock` to do such instructions. This workaround may introduce some performance
// issues. In the future we should solve the redundancy of `Vmo` and the pagecache
// make sure return such error when really needing I/Os.
return Err(VmoCommitError::NeedIo(cursor.index() as usize));
}
let frame = self.commit_on(cursor.index() as usize, CommitFlags::empty())?;
Ok(frame)
}
/// Commits the page corresponding to the target offset in the VMO.
///
/// If the commit operation needs to perform I/O, it will return a [`VmoCommitError::NeedIo`].
pub fn try_commit_page(&self, offset: usize) -> core::result::Result<UFrame, VmoCommitError> {
let page_idx = offset / PAGE_SIZE; let page_idx = offset / PAGE_SIZE;
self.pages.with(|pages, size| { if offset >= self.size() {
if offset >= size { return Err(VmoCommitError::Err(Error::with_message(
return_errno_with_message!(Errno::EINVAL, "the offset is outside the VMO"); Errno::EINVAL,
"the offset is outside the VMO",
)));
} }
let mut cursor = pages.cursor_mut(page_idx as u64);
if cursor.remove().is_some() let guard = disable_preempt();
&& let Some(pager) = &self.pager let mut cursor = self.pages.cursor(&guard, page_idx as u64);
{ self.try_commit_with_cursor(&mut cursor)
pager.decommit_page(page_idx)?;
}
Ok(())
})
} }
/// Traverses the indices within a specified range of a VMO sequentially. /// Traverses the indices within a specified range of a VMO sequentially.
///
/// For each index position, you have the option to commit the page as well as /// For each index position, you have the option to commit the page as well as
/// perform other operations. /// perform other operations.
pub fn operate_on_range<F>( ///
/// Once a commit operation needs to perform I/O, it will return a [`VmoCommitError::NeedIo`].
pub fn try_operate_on_range<F>(
&self, &self,
range: &Range<usize>, range: &Range<usize>,
mut operate: F, mut operate: F,
commit_flags: CommitFlags, ) -> core::result::Result<(), VmoCommitError>
) -> Result<()>
where where
F: FnMut(&mut dyn FnMut() -> Result<UFrame>) -> Result<()>, F: FnMut(
&mut dyn FnMut() -> core::result::Result<UFrame, VmoCommitError>,
) -> core::result::Result<(), VmoCommitError>,
{ {
self.pages.with(|pages, size| { if range.end > self.size() {
if range.end > size { return Err(VmoCommitError::Err(Error::with_message(
return_errno_with_message!(Errno::EINVAL, "operated range exceeds the vmo size"); Errno::EINVAL,
"operated range exceeds the vmo size",
)));
} }
let page_idx_range = get_page_idx_range(range); let page_idx_range = get_page_idx_range(range);
let mut cursor = pages.cursor_mut(page_idx_range.start as u64); let guard = disable_preempt();
let mut cursor = self.pages.cursor(&guard, page_idx_range.start as u64);
for page_idx in page_idx_range { for page_idx in page_idx_range {
let mut commit_fn = || self.commit_with_cursor(&mut cursor, commit_flags); let mut commit_fn = || self.try_commit_with_cursor(&mut cursor);
operate(&mut commit_fn)?; operate(&mut commit_fn)?;
cursor.next(); cursor.next();
} }
Ok(()) Ok(())
}) }
/// Traverses the indices within a specified range of a VMO sequentially.
///
/// For each index position, you have the option to commit the page as well as
/// perform other operations.
///
/// This method may involve I/O operations if the VMO needs to fetch a page from
/// the underlying page cache.
fn operate_on_range<F>(
&self,
mut range: Range<usize>,
mut operate: F,
commit_flags: CommitFlags,
) -> Result<()>
where
F: FnMut(
&mut dyn FnMut() -> core::result::Result<UFrame, VmoCommitError>,
) -> core::result::Result<(), VmoCommitError>,
{
'retry: loop {
let res = self.try_operate_on_range(&range, &mut operate);
match res {
Ok(_) => return Ok(()),
Err(VmoCommitError::Err(e)) => return Err(e),
Err(VmoCommitError::NeedIo(index)) => {
self.commit_on(index, commit_flags)?;
range.start = index * PAGE_SIZE;
continue 'retry;
}
}
}
} }
/// Decommits a range of pages in the VMO. /// Decommits a range of pages in the VMO.
pub fn decommit(&self, range: Range<usize>) -> Result<()> { pub fn decommit(&self, range: Range<usize>) -> Result<()> {
self.pages.with(|pages, size| { let locked_pages = self.pages.lock();
if range.end > size { if range.end > self.size() {
return_errno_with_message!(Errno::EINVAL, "operated range exceeds the vmo size"); return_errno_with_message!(Errno::EINVAL, "operated range exceeds the vmo size");
} }
self.decommit_pages(pages, range)?; self.decommit_pages(locked_pages, range)?;
Ok(()) Ok(())
})
} }
/// Reads the specified amount of buffer content starting from the target offset in the VMO. /// Reads the specified amount of buffer content starting from the target offset in the VMO.
@ -315,14 +355,19 @@ impl Vmo_ {
let read_range = offset..(offset + read_len); let read_range = offset..(offset + read_len);
let mut read_offset = offset % PAGE_SIZE; let mut read_offset = offset % PAGE_SIZE;
let read = move |commit_fn: &mut dyn FnMut() -> Result<UFrame>| { let read =
move |commit_fn: &mut dyn FnMut() -> core::result::Result<UFrame, VmoCommitError>| {
let frame = commit_fn()?; let frame = commit_fn()?;
frame.reader().skip(read_offset).read_fallible(writer)?; frame
.reader()
.skip(read_offset)
.read_fallible(writer)
.map_err(|e| VmoCommitError::from(e.0))?;
read_offset = 0; read_offset = 0;
Ok(()) Ok(())
}; };
self.operate_on_range(&read_range, read, CommitFlags::empty()) self.operate_on_range(read_range, read, CommitFlags::empty())
} }
/// Writes the specified amount of buffer content starting from the target offset in the VMO. /// Writes the specified amount of buffer content starting from the target offset in the VMO.
@ -330,31 +375,35 @@ impl Vmo_ {
let write_len = reader.remain(); let write_len = reader.remain();
let write_range = offset..(offset + write_len); let write_range = offset..(offset + write_len);
let mut write_offset = offset % PAGE_SIZE; let mut write_offset = offset % PAGE_SIZE;
let mut write =
let mut write = move |commit_fn: &mut dyn FnMut() -> Result<UFrame>| { move |commit_fn: &mut dyn FnMut() -> core::result::Result<UFrame, VmoCommitError>| {
let frame = commit_fn()?; let frame = commit_fn()?;
frame.writer().skip(write_offset).write_fallible(reader)?; frame
.writer()
.skip(write_offset)
.write_fallible(reader)
.map_err(|e| VmoCommitError::from(e.0))?;
write_offset = 0; write_offset = 0;
Ok(()) Ok(())
}; };
if write_range.len() < PAGE_SIZE { if write_range.len() < PAGE_SIZE {
self.operate_on_range(&write_range, write, CommitFlags::empty())?; self.operate_on_range(write_range.clone(), write, CommitFlags::empty())?;
} else { } else {
let temp = write_range.start + PAGE_SIZE - 1; let temp = write_range.start + PAGE_SIZE - 1;
let up_align_start = temp - temp % PAGE_SIZE; let up_align_start = temp - temp % PAGE_SIZE;
let down_align_end = write_range.end - write_range.end % PAGE_SIZE; let down_align_end = write_range.end - write_range.end % PAGE_SIZE;
if write_range.start != up_align_start { if write_range.start != up_align_start {
let head_range = write_range.start..up_align_start; let head_range = write_range.start..up_align_start;
self.operate_on_range(&head_range, &mut write, CommitFlags::empty())?; self.operate_on_range(head_range, &mut write, CommitFlags::empty())?;
} }
if up_align_start != down_align_end { if up_align_start != down_align_end {
let mid_range = up_align_start..down_align_end; let mid_range = up_align_start..down_align_end;
self.operate_on_range(&mid_range, &mut write, CommitFlags::WILL_OVERWRITE)?; self.operate_on_range(mid_range, &mut write, CommitFlags::WILL_OVERWRITE)?;
} }
if down_align_end != write_range.end { if down_align_end != write_range.end {
let tail_range = down_align_end..write_range.end; let tail_range = down_align_end..write_range.end;
self.operate_on_range(&tail_range, &mut write, CommitFlags::empty())?; self.operate_on_range(tail_range, &mut write, CommitFlags::empty())?;
} }
} }
@ -377,7 +426,7 @@ impl Vmo_ {
/// Returns the size of current VMO. /// Returns the size of current VMO.
pub fn size(&self) -> usize { pub fn size(&self) -> usize {
self.pages.with(|_, size| size) self.size.load(Ordering::Acquire)
} }
/// Resizes current VMO to target size. /// Resizes current VMO to target size.
@ -385,40 +434,54 @@ impl Vmo_ {
assert!(self.flags.contains(VmoFlags::RESIZABLE)); assert!(self.flags.contains(VmoFlags::RESIZABLE));
let new_size = new_size.align_up(PAGE_SIZE); let new_size = new_size.align_up(PAGE_SIZE);
let Pages::Resizable(ref pages) = self.pages else { let locked_pages = self.pages.lock();
return_errno_with_message!(Errno::EINVAL, "current VMO is not resizable");
};
let mut lock = pages.lock(); let old_size = self.size();
let old_size = lock.1;
if new_size == old_size { if new_size == old_size {
return Ok(()); return Ok(());
} }
self.size.store(new_size, Ordering::Release);
if new_size < old_size { if new_size < old_size {
self.decommit_pages(&mut lock.0, new_size..old_size)?; self.decommit_pages(locked_pages, new_size..old_size)?;
} }
lock.1 = new_size;
Ok(()) Ok(())
} }
fn decommit_pages(&self, pages: &mut XArray<UFrame>, range: Range<usize>) -> Result<()> { fn decommit_pages(
&self,
mut locked_pages: LockedXArray<UFrame>,
range: Range<usize>,
) -> Result<()> {
let page_idx_range = get_page_idx_range(&range); let page_idx_range = get_page_idx_range(&range);
let mut cursor = pages.cursor_mut(page_idx_range.start as u64); let mut cursor = locked_pages.cursor_mut(page_idx_range.start as u64);
let Some(pager) = &self.pager else {
for _ in page_idx_range {
cursor.remove();
cursor.next();
}
return Ok(());
};
let mut removed_page_idx = Vec::new();
for page_idx in page_idx_range { for page_idx in page_idx_range {
if cursor.remove().is_some() if cursor.remove().is_some() {
&& let Some(pager) = &self.pager removed_page_idx.push(page_idx);
{
pager.decommit_page(page_idx)?;
} }
cursor.next(); cursor.next();
} }
Ok(())
drop(cursor);
drop(locked_pages);
for page_idx in removed_page_idx {
pager.decommit_page(page_idx)?;
} }
/// Determines whether a page is committed. Ok(())
pub fn is_page_committed(&self, page_idx: usize) -> bool {
self.pages
.with(|pages, _| pages.load(page_idx as u64).is_some())
} }
/// Returns the flags of current VMO. /// Returns the flags of current VMO.
@ -427,13 +490,13 @@ impl Vmo_ {
} }
fn replace(&self, page: UFrame, page_idx: usize) -> Result<()> { fn replace(&self, page: UFrame, page_idx: usize) -> Result<()> {
self.pages.with(|pages, size| { let mut locked_pages = self.pages.lock();
if page_idx >= size / PAGE_SIZE { if page_idx >= self.size() / PAGE_SIZE {
return_errno_with_message!(Errno::EINVAL, "the page index is outside of the vmo"); return_errno_with_message!(Errno::EINVAL, "the page index is outside of the vmo");
} }
pages.store(page_idx as u64, page);
locked_pages.store(page_idx as u64, page);
Ok(()) Ok(())
})
} }
} }

View File

@ -4,14 +4,14 @@
//! Options for allocating root and child VMOs. //! Options for allocating root and child VMOs.
use core::sync::atomic::AtomicUsize;
use align_ext::AlignExt; use align_ext::AlignExt;
use aster_rights::{Rights, TRightSet, TRights}; use aster_rights::{Rights, TRightSet, TRights};
use ostd::{ use ostd::mm::{FrameAllocOptions, UFrame, USegment};
collections::xarray::XArray, use xarray::XArray;
mm::{FrameAllocOptions, UFrame, USegment},
};
use super::{Pager, Pages, Vmo, VmoFlags}; use super::{Pager, Vmo, VmoFlags};
use crate::{prelude::*, vm::vmo::Vmo_}; use crate::{prelude::*, vm::vmo::Vmo_};
/// Options for allocating a root VMO. /// Options for allocating a root VMO.
@ -122,18 +122,12 @@ impl<R: TRights> VmoOptions<TRightSet<R>> {
fn alloc_vmo_(size: usize, flags: VmoFlags, pager: Option<Arc<dyn Pager>>) -> Result<Vmo_> { fn alloc_vmo_(size: usize, flags: VmoFlags, pager: Option<Arc<dyn Pager>>) -> Result<Vmo_> {
let size = size.align_up(PAGE_SIZE); let size = size.align_up(PAGE_SIZE);
let pages = {
let pages = committed_pages_if_continuous(flags, size)?; let pages = committed_pages_if_continuous(flags, size)?;
if flags.contains(VmoFlags::RESIZABLE) {
Pages::Resizable(Mutex::new((pages, size)))
} else {
Pages::Nonresizable(Mutex::new(pages), size)
}
};
Ok(Vmo_ { Ok(Vmo_ {
pager, pager,
flags, flags,
pages, pages,
size: AtomicUsize::new(size),
}) })
} }
@ -142,13 +136,15 @@ fn committed_pages_if_continuous(flags: VmoFlags, size: usize) -> Result<XArray<
// if the vmo is continuous, we need to allocate frames for the vmo // if the vmo is continuous, we need to allocate frames for the vmo
let frames_num = size / PAGE_SIZE; let frames_num = size / PAGE_SIZE;
let segment: USegment = FrameAllocOptions::new().alloc_segment(frames_num)?.into(); let segment: USegment = FrameAllocOptions::new().alloc_segment(frames_num)?.into();
let mut committed_pages = XArray::new(); let committed_pages = XArray::new();
let mut cursor = committed_pages.cursor_mut(0); let mut locked_pages = committed_pages.lock();
let mut cursor = locked_pages.cursor_mut(0);
for frame in segment { for frame in segment {
cursor.store(frame); cursor.store(frame);
cursor.next(); cursor.next();
} }
drop(cursor); drop(cursor);
drop(locked_pages);
Ok(committed_pages) Ok(committed_pages)
} else { } else {
// otherwise, we wait for the page is read or write // otherwise, we wait for the page is read or write

View File

@ -6,45 +6,58 @@ use aster_rights::{Dup, Rights, TRightSet, TRights, Write};
use aster_rights_proc::require; use aster_rights_proc::require;
use ostd::mm::{UFrame, VmIo}; use ostd::mm::{UFrame, VmIo};
use super::{CommitFlags, Vmo, VmoRightsOp}; use super::{CommitFlags, Vmo, VmoCommitError, VmoRightsOp};
use crate::prelude::*; use crate::prelude::*;
impl<R: TRights> Vmo<TRightSet<R>> { impl<R: TRights> Vmo<TRightSet<R>> {
/// Commits a page at specific offset. /// Commits a page at specific offset.
pub fn commit_page(&self, offset: usize) -> Result<UFrame> {
self.check_rights(Rights::WRITE)?;
self.0.commit_page(offset)
}
/// Commits the pages specified in the range (in bytes).
/// ///
/// The range must be within the size of the VMO. /// If the commit operation needs to perform I/O, it will return a [`VmoCommitError::NeedIo`].
///
/// The start and end addresses will be rounded down and up to page boundaries.
/// ///
/// # Access rights /// # Access rights
/// ///
/// The method requires the Write right. /// The method requires the Write right.
#[require(R > Write)] #[require(R > Write)]
pub fn commit(&self, range: Range<usize>) -> Result<()> { pub fn try_commit_page(&self, offset: usize) -> core::result::Result<UFrame, VmoCommitError> {
self.0.operate_on_range( self.check_rights(Rights::WRITE)?;
&range, self.0.try_commit_page(offset)
|commit_fn| commit_fn().map(|_| ()), }
CommitFlags::empty(),
)?; /// Commits a page at a specific page index.
Ok(()) ///
/// This method may involve I/O operations if the VMO needs to fetch
/// a page from the underlying page cache.
///
/// # Access rights
///
/// The method requires the Write right.
#[require(R > Write)]
pub fn commit_on(&self, page_idx: usize, commit_flags: CommitFlags) -> Result<UFrame> {
self.0.commit_on(page_idx, commit_flags)
} }
/// Traverses the indices within a specified range of a VMO sequentially. /// Traverses the indices within a specified range of a VMO sequentially.
///
/// For each index position, you have the option to commit the page as well as /// For each index position, you have the option to commit the page as well as
/// perform other operations. /// perform other operations.
///
/// Once a commit operation needs to perform I/O, it will return a [`VmoCommitError::NeedIo`].
///
/// # Access rights
///
/// The method requires the Write right.
#[require(R > Write)] #[require(R > Write)]
pub(in crate::vm) fn operate_on_range<F>(&self, range: &Range<usize>, operate: F) -> Result<()> pub(in crate::vm) fn try_operate_on_range<F>(
&self,
range: &Range<usize>,
operate: F,
) -> core::result::Result<(), VmoCommitError>
where where
F: FnMut(&mut dyn FnMut() -> Result<UFrame>) -> Result<()>, F: FnMut(
&mut dyn FnMut() -> core::result::Result<UFrame, VmoCommitError>,
) -> core::result::Result<(), VmoCommitError>,
{ {
self.0 self.0.try_operate_on_range(range, operate)
.operate_on_range(range, operate, CommitFlags::empty())
} }
/// Decommits the pages specified in the range (in bytes). /// Decommits the pages specified in the range (in bytes).
@ -95,19 +108,6 @@ impl<R: TRights> Vmo<TRightSet<R>> {
Vmo(self.0.clone(), self.1) Vmo(self.0.clone(), self.1)
} }
/// Creates a new VMO that replicates the original capability, initially representing
/// the same physical pages.
/// Changes to the permissions and commits/replacements of internal pages in the original VMO
/// and the new VMO will not affect each other.
///
/// # Access rights
///
/// The method requires the Dup right.
#[require(R > Dup | Write)]
pub fn dup_independent(&self) -> Self {
Vmo(Arc::new(super::Vmo_::clone(&self.0)), self.1)
}
/// Replaces the page at the `page_idx` in the VMO with the input `page`. /// Replaces the page at the `page_idx` in the VMO with the input `page`.
/// ///
/// # Access rights /// # Access rights