Don't copy on write if this is the only reference

This commit is contained in:
Zhang Junyang
2024-09-01 12:36:43 +08:00
committed by Tate, Hongliang Tian
parent f13e5d12c1
commit bec2c97637
3 changed files with 38 additions and 1 deletions

View File

@ -241,7 +241,14 @@ impl VmMapping {
return Ok(());
}
if self.is_shared {
// If the forked child or parent immediately unmaps the page after
// the fork without accessing it, we are the only reference to the
// frame. We can directly map the frame as writable without
// copying. In this case, the reference count of the frame is 2 (
// one for the mapping and one for the frame handle itself).
let only_reference = frame.reference_count() == 2;
if self.is_shared || only_reference {
cursor.protect(PAGE_SIZE, |p| p.flags |= PageFlags::W);
} else {
let new_frame = duplicate_frame(&frame)?;

View File

@ -80,6 +80,21 @@ impl Frame {
core::ptr::copy_nonoverlapping(src.as_ptr(), self.as_mut_ptr(), self.size());
}
}
/// Get the reference count of the frame.
///
/// It returns the number of all references to the page, including all the
/// existing page handles ([`Frame`]) and all the mappings in the page
/// table that points to the page.
///
/// # Safety
///
/// The function is safe to call, but using it requires extra care. The
/// reference count can be changed by other threads at any time including
/// potentially between calling this method and acting on the result.
pub fn reference_count(&self) -> u32 {
self.page.reference_count()
}
}
impl From<Page<FrameMeta>> for Frame {

View File

@ -164,6 +164,21 @@ impl<M: PageMeta> Page<M> {
unsafe { &*(self.ptr as *const M) }
}
/// Get the reference count of the page.
///
/// It returns the number of all references to the page, including all the
/// existing page handles ([`Page`], [`DynPage`]), and all the mappings in the
/// page table that points to the page.
///
/// # Safety
///
/// The function is safe to call, but using it requires extra care. The
/// reference count can be changed by other threads at any time including
/// potentially between calling this method and acting on the result.
pub fn reference_count(&self) -> u32 {
self.ref_count().load(Ordering::Relaxed)
}
fn ref_count(&self) -> &AtomicU32 {
unsafe { &(*self.ptr).ref_count }
}