From 45ec3ecc706657319c8728753fb0411c4c938023 Mon Sep 17 00:00:00 2001 From: 4lDO2 <4lDO2@protonmail.com> Date: Mon, 26 Jun 2023 14:08:33 +0200 Subject: [PATCH] Track Allocated and CoW External the same way. --- src/context/memory.rs | 193 ++++++++++++++---------------------------- src/memory/mod.rs | 15 +++- 2 files changed, 76 insertions(+), 132 deletions(-) diff --git a/src/context/memory.rs b/src/context/memory.rs index bfe9a61a..768baf80 100644 --- a/src/context/memory.rs +++ b/src/context/memory.rs @@ -83,15 +83,12 @@ impl AddrSpace { for (grant_base, grant_info) in self.grants.iter() { let new_grant = match grant_info.provider { - Provider::PhysBorrowed { ref base } => Grant::physmap(base.clone(), PageSpan::new(grant_base, grant_info.page_count), grant_info.flags, new_mapper, ())?, + Provider::PhysBorrowed { base } => Grant::physmap(base.clone(), PageSpan::new(grant_base, grant_info.page_count), grant_info.flags, new_mapper, ())?, Provider::Allocated => Grant::cow(Arc::clone(&self_arc), grant_base, grant_base, grant_info.page_count, grant_info.flags, this_mapper, new_mapper, &mut this_flusher, ())?, // MAP_SHARED grants are retained by reference, across address space clones (across // forks on monolithic kernels). - Provider::External { cow: false, ref address_space, ref src_base } => Grant::borrow_grant(Arc::clone(&address_space), grant_base, grant_base, grant_info, new_mapper, (), false)?, - - // MAP_PRIVATE grants, in this case indirect ones, are CoW. - Provider::External { cow: true, ref address_space, ref src_base } => todo!(), + Provider::External { ref address_space, src_base } => Grant::borrow_grant(Arc::clone(&address_space), grant_base, grant_base, grant_info, new_mapper, (), false)?, Provider::Fmap { ref desc } => todo!(), }; @@ -540,8 +537,7 @@ pub enum Provider { /// The memory is borrowed directly from another address space. /// /// All grants in the specified range must be of type Allocated. - // TODO: Vec? - External { address_space: Arc<RwLock<AddrSpace>>, src_base: Page, cow: bool }, + External { address_space: Arc<RwLock<AddrSpace>>, src_base: Page }, /// The memory is borrowed from another address space, but managed by a scheme via fmap. // TODO: This is probably a very heavy way to keep track of fmap'd files, perhaps move to the // ~~context~~ address space? @@ -622,7 +618,6 @@ impl Grant { provider: Provider::External { src_base, address_space: src_address_space_lock, - cow: false, } }, }) @@ -673,13 +668,12 @@ impl Grant { Provider::Allocated => Provider::External { src_base, address_space: Arc::clone(&src_address_space_lock), - cow: false, }, Provider::PhysBorrowed { base: src_phys_base } => Provider::PhysBorrowed { base: src_phys_base.next_by(offset_from_src_base), }, Provider::Fmap { .. } => todo!(), - Provider::External { ref address_space, src_base, cow } => Provider::External { address_space: Arc::clone(address_space), src_base, cow }, + Provider::External { ref address_space, src_base } => Provider::External { address_space: Arc::clone(address_space), src_base }, } }, }); @@ -705,14 +699,17 @@ impl Grant { let dst_page = dst_base.next_by(page_idx).start_address(); let Some((_old_flags, src_phys, flush)) = (unsafe { src_mapper.remap_with(src_page.start_address(), |flags| flags.write(false)) }) else { - // Page is not mapped, let the page fault handler take care of that. + // Page is not mapped, let the page fault handler take care of that (initializing + // it to zero). + // // TODO: If eager, allocate zeroed page if writable, or use *the* zeroed page (also - // for read-only). + // for read-only)? continue; }; let src_frame = Frame::containing_address(src_phys); let src_page_info = get_page_info(src_frame).expect("allocated page was not present in the global page array"); + src_page_info.add_ref(true); let Some(map_result) = (unsafe { dst_mapper.map_phys(dst_page, src_frame.start_address(), flags.write(false)) }) else { break; @@ -727,7 +724,7 @@ impl Grant { page_count, flags, mapped: true, - provider: Provider::External { src_base, address_space: src_address_space_lock, cow: true }, + provider: Provider::Allocated, }, }) } @@ -762,8 +759,6 @@ impl Grant { pub fn unmap(mut self, mapper: &mut PageMapper, mut flusher: impl Flusher<RmmA>) -> UnmapResult { assert!(self.info.mapped); - let is_cow = matches!(self.info.provider, Provider::External { cow: true, .. }); - for page in self.span().pages() { // Lazy mappings do not need to be unmapped. let Some((phys, _, flush)) = (unsafe { mapper.unmap_phys(page.start_address(), true) }) else { @@ -773,9 +768,9 @@ impl Grant { match self.info.provider { Provider::Allocated | Provider::External { .. } => { - get_page_info(frame) + /*get_page_info(frame) .expect("allocated frame did not have an associated PageInfo") - .remove_ref(is_cow); + .remove_ref(is_cow);*/ } _ => (), } @@ -823,10 +818,9 @@ impl Grant { page_count: span.count, provider: match self.info.provider { Provider::Fmap { .. } => todo!(), - Provider::External { ref address_space, ref src_base, cow } => Provider::External { + Provider::External { ref address_space, src_base } => Provider::External { address_space: Arc::clone(address_space), - src_base: src_base.clone(), - cow, + src_base, }, Provider::Allocated { .. } => Provider::Allocated, Provider::PhysBorrowed { ref base } => Provider::PhysBorrowed { base: base.clone() }, @@ -852,13 +846,12 @@ impl Grant { Provider::Fmap { .. } => todo!(), Provider::Allocated => Provider::Allocated, - Provider::External { ref address_space, ref src_base, cow } => Provider::External { + Provider::External { ref address_space, src_base } => Provider::External { address_space: Arc::clone(address_space), - src_base: src_base.clone(), - cow, + src_base, }, - Provider::PhysBorrowed { ref base } => Provider::PhysBorrowed { base: base.next_by(this_span.count) }, + Provider::PhysBorrowed { base } => Provider::PhysBorrowed { base: base.next_by(this_span.count) }, } }, }); @@ -881,9 +874,8 @@ impl GrantInfo { let is_downgrade = (self.flags.has_write() || !flags.contains(MapFlags::PROT_WRITE)) && (self.flags.has_execute() || !flags.contains(MapFlags::PROT_EXEC)); match self.provider { - Provider::Allocated { .. } | Provider::External { cow: true, .. } => true, - Provider::PhysBorrowed { .. } | Provider::External { cow: false, .. } => is_downgrade, - Provider::Fmap { .. } => is_downgrade, + Provider::Allocated => true, + _ => is_downgrade, } } @@ -1019,7 +1011,7 @@ fn cow(dst_mapper: &mut PageMapper, page: Page, old_frame: Frame, info: &PageInf unsafe { copy_frame_to_frame_directly(new_frame, old_frame); } - info.refcount.fetch_sub(1, Ordering::SeqCst); + info.remove_ref(true); Ok(new_frame) } @@ -1028,6 +1020,7 @@ fn init_frame() -> Result<Frame, PfError> { let new_frame = crate::memory::allocate_frames(1).ok_or(PfError::Oom)?; let page_info = get_page_info(new_frame).expect("all allocated frames need an associated page info"); page_info.refcount.store(1, Ordering::Relaxed); + page_info.cow_refcount.store(1, Ordering::Relaxed); Ok(new_frame) } @@ -1042,17 +1035,6 @@ fn map_zeroed(mapper: &mut PageMapper, page: Page, page_flags: PageFlags<RmmA>, Ok(new_frame) } -/* -pub fn make_exclusive(mapper: &mut PageMapper, page: Page, old_frame_opt: Option<Frame>) -> Result<Frame, PfError> { - //let (_, _, flush) = mapper.remap_with_full(page.start_address(), |old_phys, old_flags| (new_phys, old_flags.write(true))).expect("TODO: handle error?"); - - let new_page = page_slot.insert(PageInfo::try_new_exclusive().map_err(|_| PfError::Oom)?); - - - Ok(new_page.phys.clone()) -} -*/ - pub unsafe fn copy_frame_to_frame_directly(dst: Frame, src: Frame) { // Optimized exact-page-size copy function? let dst = unsafe { RmmA::phys_to_virt(dst.start_address()).data() as *mut u8 }; @@ -1104,111 +1086,62 @@ pub fn try_correcting_page_tables(faulting_page: Page, access: AccessMode) -> Re // correct posix_madvise information, allocating 4 contiguous pages and mapping them together, // might be a useful future optimization. - let frame = { - match grant_info.provider { - Provider::Allocated if access == AccessMode::Write => { - /*match pages[pages_from_grant_start].as_ref().and_then(PageInfo::try_get_exclusively) { - Some(exclusively_owned) => exclusively_owned.phys.clone(), - // TODO: Option::get_or_try_insert? - None => make_exclusive(&mut pages[pages_from_grant_start])?, - }*/ - match faulting_pageinfo_opt { - Some((_, None)) => unreachable!("allocated page needs frame to be valid"), - Some((frame, Some(info))) => if info.refcount.load(Ordering::SeqCst) == 1 { - frame - } else { - cow(&mut addr_space.table.utable, faulting_page, frame, info, grant_flags)? - }, - _ => map_zeroed(&mut addr_space.table.utable, faulting_page, grant_flags, true)?, - } - } - // TODO: the zeroed page first, readonly? - Provider::Allocated => { - match faulting_pageinfo_opt { - Some((_, None)) => unreachable!("allocated page needs frame to be valid"), - Some((frame, Some(page_info))) => frame, - None => { - map_zeroed(&mut addr_space.table.utable, faulting_page, grant_flags, false)? - } - } - } - Provider::PhysBorrowed { ref base } => { - base.next_by(pages_from_grant_start) - } - Provider::External { cow: external_is_cow, address_space: ref foreign_address_space, ref src_base } => { - /* - let pages = match pages { - Some(pgs) => pgs, - None => pages.insert(try_box_slice_new(|| None, grant_info.page_count).map_err(|_| PfError::Oom)?), - }; - */ - - let guard = foreign_address_space.read(); - let src_page = src_base.next_by(pages_from_grant_start); - - //if let Some(page_info) = &pages[pages_from_grant_start] { - if let Some((phys, _)) = guard.table.utable.translate(src_page.start_address()) { - let src_frame = Frame::containing_address(phys); - let info = get_page_info(src_frame).expect("all allocated frames need a PageInfo"); + let mut allow_writable = true; - if external_is_cow && access == AccessMode::Write { - cow(&mut addr_space.table.utable, src_page, src_frame, info, grant_flags)? - } else { - src_frame - } + let frame = match grant_info.provider { + Provider::Allocated if access == AccessMode::Write => { + match faulting_pageinfo_opt { + Some((_, None)) => unreachable!("allocated page needs frame to be valid"), + Some((frame, Some(info))) => if info.cow_refcount.load(Ordering::SeqCst) == 1 { + frame } else { - map_zeroed(&mut addr_space.table.utable, src_page, grant_flags, access == AccessMode::Write)? - } - - - /* - let Some((owner_base, owner_grant)) = guard.grants.contains(src_page) else { - log::error!("Foreign grant did not exist at specified offset."); - return Err(PfError::NonfatalInternalError); - }; - - // TODO: Would nested grants provide any benefit? - // TODO: Use recursion? - let Provider::Allocated = owner_grant.provider else { - log::error!("Chained grants!"); - return Err(PfError::NonfatalInternalError); - }; - let owner_pages_from_grant_start = src_page.offset_from(owner_base); - - let page_info = match &owner_pages[owner_pages_from_grant_start] { - Some(page) => page.ref_clone(cow), - None => { - let mut guard = RwLockUpgradableGuard::upgrade(guard); - - let (owner_base_again, owner_grant) = guard.grants.contains_mut(src_page) - .expect("grant cannot disappear without write lock having existed"); - - debug_assert_eq!(owner_base, owner_base_again); + cow(&mut addr_space.table.utable, faulting_page, frame, info, grant_flags)? + }, + _ => map_zeroed(&mut addr_space.table.utable, faulting_page, grant_flags, true)?, + } + } + Provider::Allocated => { + match faulting_pageinfo_opt { + Some((_, None)) => unreachable!("allocated page needs frame to be valid"), + Some((frame, Some(page_info))) => { + allow_writable = page_info.cow_refcount.load(Ordering::SeqCst) == 1; - let Provider::Allocated = owner_grant.provider else { - unreachable!("cannot have changed in the meantime"); - }; - debug_assert!(owner_pages[owner_pages_from_grant_start].is_none()); + frame + } - owner_pages[owner_pages_from_grant_start].insert(PageInfo::try_new_exclusive().map_err(|_| PfError::Oom)?).ref_clone(cow) - } - }; + None => { + // TODO: the zeroed page first, readonly? + map_zeroed(&mut addr_space.table.utable, faulting_page, grant_flags, false)? + } + } + } + Provider::PhysBorrowed { base } => { + base.next_by(pages_from_grant_start) + } + Provider::External { address_space: ref foreign_address_space, src_base } => { + let guard = foreign_address_space.read(); + let src_page = src_base.next_by(pages_from_grant_start); - let frame = page_info.phys.clone(); + if let Some((phys, _)) = guard.table.utable.translate(src_page.start_address()) { + let src_frame = Frame::containing_address(phys); - pages[pages_from_grant_start] = Some(page_info); + let info = get_page_info(src_frame).expect("all allocated frames need a PageInfo"); + info.add_ref(false); - frame - */ + src_frame + } else { + // TODO: Should this be called? + map_zeroed(&mut addr_space.table.utable, src_page, grant_flags, access == AccessMode::Write)? } - Provider::Fmap { ref desc } => todo!(), } + Provider::Fmap { ref desc } => todo!(), }; if super::context_id().into() == 3 { //log::info!("Correcting {:?} => {:?} (base {:?} info {:?})", faulting_page, frame, grant_base, grant_info); } - let Some(flush) = (unsafe { addr_space.table.utable.map_phys(faulting_page.start_address(), frame.start_address(), grant_flags) }) else { + let new_flags = grant_flags.write(grant_flags.has_write() && allow_writable); + let Some(flush) = (unsafe { addr_space.table.utable.map_phys(faulting_page.start_address(), frame.start_address(), new_flags) }) else { // TODO return Err(PfError::Oom); }; diff --git a/src/memory/mod.rs b/src/memory/mod.rs index 1754c55c..ac071138 100644 --- a/src/memory/mod.rs +++ b/src/memory/mod.rs @@ -8,12 +8,13 @@ use core::sync::atomic::{AtomicUsize, Ordering}; use crate::arch::rmm::LockedAllocator; use crate::common::try_box_slice_new; +use crate::context::memory::AddrSpace; pub use crate::paging::{PAGE_SIZE, PhysicalAddress}; use crate::rmm::areas; use alloc::boxed::Box; use alloc::collections::BTreeMap; -use alloc::sync::Arc; +use alloc::sync::{Arc, Weak}; use alloc::vec::Vec; use rmm::{ FrameAllocator, @@ -189,13 +190,15 @@ impl Drop for RaiiFrame { } } +#[derive(Debug)] pub struct PageInfo { pub refcount: AtomicUsize, pub cow_refcount: AtomicUsize, // TODO: AtomicFlags? pub flags: FrameFlags, - _padding: usize, + pub _padding: usize, } + bitflags::bitflags! { pub struct FrameFlags: usize { const NONE = 0; @@ -254,6 +257,14 @@ impl PageInfo { _padding: 0, } } + pub fn add_ref(&self, cow: bool) { + if cow { + self.cow_refcount.fetch_add(1, Ordering::Relaxed); + } + self.refcount.fetch_add(1, Ordering::Relaxed); + + core::sync::atomic::fence(Ordering::Release); + } pub fn remove_ref(&self, cow: bool) { if cow { self.cow_refcount.fetch_sub(1, Ordering::Relaxed); -- GitLab