Optimize write_bytes for Vmo

This commit is contained in:
Sun12551 2024-06-05 20:11:47 +08:00 committed by Tate, Hongliang Tian
parent 75e9382d57
commit d0e95776cb
5 changed files with 101 additions and 16 deletions

View File

@ -198,6 +198,15 @@ impl Pager for PageCacheManager {
Ok(())
}
fn commit_overwrite(&self, idx: usize) -> Result<Frame> {
if let Some(page) = self.pages.lock().get(&idx) {
return Ok(page.frame.clone());
}
let page = Page::alloc_zero()?;
Ok(self.pages.lock().get_or_insert(idx, || page).frame.clone())
}
}
#[derive(Debug)]

View File

@ -7,7 +7,7 @@ use aster_rights::{Rights, TRights};
use super::{
options::{VmoCowChild, VmoSliceChild},
Vmo, VmoChildOptions, VmoRightsOp,
CommitFlags, Vmo, VmoChildOptions, VmoRightsOp,
};
use crate::prelude::*;
@ -84,7 +84,8 @@ impl Vmo<Rights> {
/// The method requires the Write right.
pub fn commit(&self, range: Range<usize>) -> Result<()> {
self.check_rights(Rights::WRITE)?;
self.0.commit_and_operate(&range, |_| {}, false)?;
self.0
.commit_and_operate(&range, |_| {}, CommitFlags::empty())?;
Ok(())
}

View File

@ -203,6 +203,27 @@ fn clone_page(page: &Frame) -> Result<Frame> {
Ok(new_page)
}
bitflags! {
/// Commit Flags.
pub struct CommitFlags: u8 {
/// Set this flag if the page will be written soon.
const WILL_WRITE = 1;
/// Set this flag if the page will be completely overwritten.
/// This flag contains the WILL_WRITE flag.
const WILL_OVERWRITE = 3;
}
}
impl CommitFlags {
pub fn will_write(&self) -> bool {
self.contains(Self::WILL_WRITE)
}
pub fn will_overwrite(&self) -> bool {
self.contains(Self::WILL_OVERWRITE)
}
}
impl Vmo_ {
/// Prepare a new `Frame` for the target index in pages, returning the new page as well as
/// whether this page needs to be marked as exclusive.
@ -218,7 +239,7 @@ impl Vmo_ {
&self,
page_idx: usize,
is_cow_vmo: bool,
will_write: bool,
commit_flags: CommitFlags,
) -> Result<(Frame, bool)> {
let (page, should_mark_exclusive) = match &self.pager {
None => {
@ -232,7 +253,7 @@ impl Vmo_ {
// VMO requires COW and the prepared page is about to undergo a write operation.
// At this point, the `Frame` obtained from the pager needs to be cloned to
// avoid subsequent modifications affecting the content of the `Frame` in the pager.
let trigger_cow = is_cow_vmo && will_write;
let trigger_cow = is_cow_vmo && commit_flags.will_write();
if trigger_cow {
// Condition 3.
(clone_page(&page)?, true)
@ -245,11 +266,25 @@ impl Vmo_ {
Ok((page, should_mark_exclusive))
}
/// Prepare a new `Frame` for the target index in pages, returning the new page.
/// This function is only used when the new `Frame` will be completely overwritten
/// and we do not care about the content on the page.
fn prepare_overwrite(&self, page_idx: usize, is_cow_vmo: bool) -> Result<Frame> {
let page = if let Some(pager) = &self.pager
&& !is_cow_vmo
{
pager.commit_overwrite(page_idx)?
} else {
FrameAllocOptions::new(1).alloc_single()?
};
Ok(page)
}
fn commit_with_cursor(
&self,
cursor: &mut CursorMut<'_, Frame, VmoMark>,
is_cow_vmo: bool,
will_write: bool,
commit_flags: CommitFlags,
) -> Result<Frame> {
let (new_page, is_exclusive) = {
let is_exclusive = cursor.is_marked(VmoMark::ExclusivePage);
@ -257,15 +292,26 @@ impl Vmo_ {
// The necessary and sufficient condition for triggering the COW mechanism is that
// the current VMO requires copy-on-write, there is an impending write operation to the page,
// and the page is not exclusive.
let trigger_cow = is_cow_vmo && will_write && !is_exclusive;
let trigger_cow = is_cow_vmo && commit_flags.will_write() && !is_exclusive;
if !trigger_cow {
// Fast path: return the page directly.
return Ok(committed_page.clone());
}
(clone_page(&committed_page)?, true)
if commit_flags.will_overwrite() {
(FrameAllocOptions::new(1).alloc_single()?, true)
} else {
(clone_page(&committed_page)?, true)
}
} else if commit_flags.will_overwrite() {
// In this case, the page will be completely overwritten. The page only needs to
// be marked as `ExclusivePage` when the current VMO is a cow VMO.
(
self.prepare_overwrite(cursor.index() as usize, is_cow_vmo)?,
is_cow_vmo,
)
} else {
self.prepare_page(cursor.index() as usize, is_cow_vmo, will_write)?
self.prepare_page(cursor.index() as usize, is_cow_vmo, commit_flags)?
}
};
@ -284,7 +330,12 @@ impl Vmo_ {
self.pages.with(|pages, size| {
let is_cow_vmo = pages.is_marked(VmoMark::CowVmo);
let mut cursor = pages.cursor_mut(page_idx as u64);
self.commit_with_cursor(&mut cursor, is_cow_vmo, will_write)
let commit_flags = if will_write {
CommitFlags::WILL_WRITE
} else {
CommitFlags::empty()
};
self.commit_with_cursor(&mut cursor, is_cow_vmo, commit_flags)
})
}
@ -310,7 +361,7 @@ impl Vmo_ {
&self,
range: &Range<usize>,
mut operate: F,
will_write: bool,
commit_flags: CommitFlags,
) -> Result<()>
where
F: FnMut(Frame),
@ -328,7 +379,7 @@ impl Vmo_ {
let mut cursor = pages.cursor_mut(page_idx_range.start as u64);
for page_idx in page_idx_range {
let committed_page =
self.commit_with_cursor(&mut cursor, is_cow_vmo, will_write)?;
self.commit_with_cursor(&mut cursor, is_cow_vmo, commit_flags)?;
operate(committed_page);
cursor.next();
}
@ -356,7 +407,7 @@ impl Vmo_ {
read_offset = 0;
};
self.commit_and_operate(&read_range, read, false)
self.commit_and_operate(&read_range, read, CommitFlags::empty())
}
/// Write the specified amount of buffer content starting from the target offset in the VMO.
@ -366,12 +417,30 @@ impl Vmo_ {
let mut write_offset = offset % PAGE_SIZE;
let mut buf_reader: VmReader = buf.into();
let write = move |page: Frame| {
let mut write = move |page: Frame| {
page.writer().skip(write_offset).write(&mut buf_reader);
write_offset = 0;
};
self.commit_and_operate(&write_range, write, true)?;
if write_range.len() < PAGE_SIZE {
self.commit_and_operate(&write_range, write, CommitFlags::WILL_WRITE)?;
} else {
let temp = write_range.start + PAGE_SIZE - 1;
let up_align_start = temp - temp % PAGE_SIZE;
let down_align_end = write_range.end - write_range.end % PAGE_SIZE;
if write_range.start != up_align_start {
let head_range = write_range.start..up_align_start;
self.commit_and_operate(&head_range, &mut write, CommitFlags::WILL_WRITE)?;
}
if up_align_start != down_align_end {
let mid_range = up_align_start..down_align_end;
self.commit_and_operate(&mid_range, &mut write, CommitFlags::WILL_OVERWRITE)?;
}
if down_align_end != write_range.end {
let tail_range = down_align_end..write_range.end;
self.commit_and_operate(&tail_range, &mut write, CommitFlags::WILL_WRITE)?;
}
}
let is_cow_vmo = self.is_cow_vmo();
if let Some(pager) = &self.pager

View File

@ -50,4 +50,9 @@ pub trait Pager: Send + Sync {
/// such an assumption for its correctness; instead, it should simply ignore the
/// call or return an error.
fn decommit_page(&self, idx: usize) -> Result<()>;
/// Ask the pager to provide a frame at a specified index.
/// Notify the pager that the frame will be fully overwritten soon, so pager can
/// choose not to initialize it.
fn commit_overwrite(&self, idx: usize) -> Result<Frame>;
}

View File

@ -8,7 +8,7 @@ use aster_rights_proc::require;
use super::{
options::{VmoCowChild, VmoSliceChild},
Vmo, VmoChildOptions, VmoRightsOp,
CommitFlags, Vmo, VmoChildOptions, VmoRightsOp,
};
use crate::prelude::*;
@ -84,7 +84,8 @@ impl<R: TRights> Vmo<TRightSet<R>> {
/// The method requires the Write right.
#[require(R > Write)]
pub fn commit(&self, range: Range<usize>) -> Result<()> {
self.0.commit_and_operate(&range, |_| {}, false)?;
self.0
.commit_and_operate(&range, |_| {}, CommitFlags::empty())?;
Ok(())
}