mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-15 08:16:47 +00:00
Optimize write_bytes for Vmo
This commit is contained in:
parent
75e9382d57
commit
d0e95776cb
@ -198,6 +198,15 @@ impl Pager for PageCacheManager {
|
|||||||
|
|
||||||
Ok(())
|
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)]
|
#[derive(Debug)]
|
||||||
|
@ -7,7 +7,7 @@ use aster_rights::{Rights, TRights};
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
options::{VmoCowChild, VmoSliceChild},
|
options::{VmoCowChild, VmoSliceChild},
|
||||||
Vmo, VmoChildOptions, VmoRightsOp,
|
CommitFlags, Vmo, VmoChildOptions, VmoRightsOp,
|
||||||
};
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@ -84,7 +84,8 @@ impl Vmo<Rights> {
|
|||||||
/// The method requires the Write right.
|
/// The method requires the Write right.
|
||||||
pub fn commit(&self, range: Range<usize>) -> Result<()> {
|
pub fn commit(&self, range: Range<usize>) -> Result<()> {
|
||||||
self.check_rights(Rights::WRITE)?;
|
self.check_rights(Rights::WRITE)?;
|
||||||
self.0.commit_and_operate(&range, |_| {}, false)?;
|
self.0
|
||||||
|
.commit_and_operate(&range, |_| {}, CommitFlags::empty())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,6 +203,27 @@ fn clone_page(page: &Frame) -> Result<Frame> {
|
|||||||
Ok(new_page)
|
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_ {
|
impl Vmo_ {
|
||||||
/// Prepare a new `Frame` for the target index in pages, returning the new page as well as
|
/// 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.
|
/// whether this page needs to be marked as exclusive.
|
||||||
@ -218,7 +239,7 @@ impl Vmo_ {
|
|||||||
&self,
|
&self,
|
||||||
page_idx: usize,
|
page_idx: usize,
|
||||||
is_cow_vmo: bool,
|
is_cow_vmo: bool,
|
||||||
will_write: bool,
|
commit_flags: CommitFlags,
|
||||||
) -> Result<(Frame, bool)> {
|
) -> Result<(Frame, bool)> {
|
||||||
let (page, should_mark_exclusive) = match &self.pager {
|
let (page, should_mark_exclusive) = match &self.pager {
|
||||||
None => {
|
None => {
|
||||||
@ -232,7 +253,7 @@ impl Vmo_ {
|
|||||||
// VMO requires COW and the prepared page is about to undergo a write operation.
|
// 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
|
// 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.
|
// 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 {
|
if trigger_cow {
|
||||||
// Condition 3.
|
// Condition 3.
|
||||||
(clone_page(&page)?, true)
|
(clone_page(&page)?, true)
|
||||||
@ -245,11 +266,25 @@ impl Vmo_ {
|
|||||||
Ok((page, should_mark_exclusive))
|
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(
|
fn commit_with_cursor(
|
||||||
&self,
|
&self,
|
||||||
cursor: &mut CursorMut<'_, Frame, VmoMark>,
|
cursor: &mut CursorMut<'_, Frame, VmoMark>,
|
||||||
is_cow_vmo: bool,
|
is_cow_vmo: bool,
|
||||||
will_write: bool,
|
commit_flags: CommitFlags,
|
||||||
) -> Result<Frame> {
|
) -> Result<Frame> {
|
||||||
let (new_page, is_exclusive) = {
|
let (new_page, is_exclusive) = {
|
||||||
let is_exclusive = cursor.is_marked(VmoMark::ExclusivePage);
|
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 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,
|
// the current VMO requires copy-on-write, there is an impending write operation to the page,
|
||||||
// and the page is not exclusive.
|
// 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 {
|
if !trigger_cow {
|
||||||
// Fast path: return the page directly.
|
// Fast path: return the page directly.
|
||||||
return Ok(committed_page.clone());
|
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 {
|
} 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| {
|
self.pages.with(|pages, size| {
|
||||||
let is_cow_vmo = pages.is_marked(VmoMark::CowVmo);
|
let is_cow_vmo = pages.is_marked(VmoMark::CowVmo);
|
||||||
let mut cursor = pages.cursor_mut(page_idx as u64);
|
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,
|
&self,
|
||||||
range: &Range<usize>,
|
range: &Range<usize>,
|
||||||
mut operate: F,
|
mut operate: F,
|
||||||
will_write: bool,
|
commit_flags: CommitFlags,
|
||||||
) -> Result<()>
|
) -> Result<()>
|
||||||
where
|
where
|
||||||
F: FnMut(Frame),
|
F: FnMut(Frame),
|
||||||
@ -328,7 +379,7 @@ impl Vmo_ {
|
|||||||
let mut cursor = pages.cursor_mut(page_idx_range.start as u64);
|
let mut cursor = pages.cursor_mut(page_idx_range.start as u64);
|
||||||
for page_idx in page_idx_range {
|
for page_idx in page_idx_range {
|
||||||
let committed_page =
|
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);
|
operate(committed_page);
|
||||||
cursor.next();
|
cursor.next();
|
||||||
}
|
}
|
||||||
@ -356,7 +407,7 @@ impl Vmo_ {
|
|||||||
read_offset = 0;
|
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.
|
/// 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 write_offset = offset % PAGE_SIZE;
|
||||||
let mut buf_reader: VmReader = buf.into();
|
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);
|
page.writer().skip(write_offset).write(&mut buf_reader);
|
||||||
write_offset = 0;
|
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();
|
let is_cow_vmo = self.is_cow_vmo();
|
||||||
if let Some(pager) = &self.pager
|
if let Some(pager) = &self.pager
|
||||||
|
@ -50,4 +50,9 @@ pub trait Pager: Send + Sync {
|
|||||||
/// such an assumption for its correctness; instead, it should simply ignore the
|
/// such an assumption for its correctness; instead, it should simply ignore the
|
||||||
/// call or return an error.
|
/// call or return an error.
|
||||||
fn decommit_page(&self, idx: usize) -> Result<()>;
|
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>;
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ use aster_rights_proc::require;
|
|||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
options::{VmoCowChild, VmoSliceChild},
|
options::{VmoCowChild, VmoSliceChild},
|
||||||
Vmo, VmoChildOptions, VmoRightsOp,
|
CommitFlags, Vmo, VmoChildOptions, VmoRightsOp,
|
||||||
};
|
};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
@ -84,7 +84,8 @@ impl<R: TRights> Vmo<TRightSet<R>> {
|
|||||||
/// 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 commit(&self, range: Range<usize>) -> Result<()> {
|
||||||
self.0.commit_and_operate(&range, |_| {}, false)?;
|
self.0
|
||||||
|
.commit_and_operate(&range, |_| {}, CommitFlags::empty())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user