diff --git a/kernel/aster-nix/src/fs/utils/page_cache.rs b/kernel/aster-nix/src/fs/utils/page_cache.rs
index d741ed057..252349dff 100644
--- a/kernel/aster-nix/src/fs/utils/page_cache.rs
+++ b/kernel/aster-nix/src/fs/utils/page_cache.rs
@@ -198,6 +198,15 @@ impl Pager for PageCacheManager {
Ok(())
}
+
+ fn commit_overwrite(&self, idx: usize) -> Result {
+ 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)]
diff --git a/kernel/aster-nix/src/vm/vmo/dyn_cap.rs b/kernel/aster-nix/src/vm/vmo/dyn_cap.rs
index c02d5c596..15051aff5 100644
--- a/kernel/aster-nix/src/vm/vmo/dyn_cap.rs
+++ b/kernel/aster-nix/src/vm/vmo/dyn_cap.rs
@@ -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 {
/// The method requires the Write right.
pub fn commit(&self, range: Range) -> Result<()> {
self.check_rights(Rights::WRITE)?;
- self.0.commit_and_operate(&range, |_| {}, false)?;
+ self.0
+ .commit_and_operate(&range, |_| {}, CommitFlags::empty())?;
Ok(())
}
diff --git a/kernel/aster-nix/src/vm/vmo/mod.rs b/kernel/aster-nix/src/vm/vmo/mod.rs
index 1ec65bcd6..12f186c97 100644
--- a/kernel/aster-nix/src/vm/vmo/mod.rs
+++ b/kernel/aster-nix/src/vm/vmo/mod.rs
@@ -203,6 +203,27 @@ fn clone_page(page: &Frame) -> Result {
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 {
+ 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 {
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,
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
diff --git a/kernel/aster-nix/src/vm/vmo/pager.rs b/kernel/aster-nix/src/vm/vmo/pager.rs
index f6d7671ed..09870a231 100644
--- a/kernel/aster-nix/src/vm/vmo/pager.rs
+++ b/kernel/aster-nix/src/vm/vmo/pager.rs
@@ -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;
}
diff --git a/kernel/aster-nix/src/vm/vmo/static_cap.rs b/kernel/aster-nix/src/vm/vmo/static_cap.rs
index cc0b77d96..02d273408 100644
--- a/kernel/aster-nix/src/vm/vmo/static_cap.rs
+++ b/kernel/aster-nix/src/vm/vmo/static_cap.rs
@@ -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 Vmo> {
/// The method requires the Write right.
#[require(R > Write)]
pub fn commit(&self, range: Range) -> Result<()> {
- self.0.commit_and_operate(&range, |_| {}, false)?;
+ self.0
+ .commit_and_operate(&range, |_| {}, CommitFlags::empty())?;
Ok(())
}