diff --git a/src/context/memory.rs b/src/context/memory.rs index c23d8dfa98fc8fbbe34ebfd765bb1ad1ce081e30..68ebe472fa37b46baa52f69bfd4582eb00f8bcbf 100644 --- a/src/context/memory.rs +++ b/src/context/memory.rs @@ -205,11 +205,16 @@ impl AddrSpace { } } pub fn mmap(&mut self, page: Option<Page>, page_count: NonZeroUsize, flags: MapFlags, map: impl FnOnce(Page, PageFlags<RmmA>, &mut PageMapper, &mut dyn Flusher<RmmA>) -> Result<Grant>) -> Result<Page> { + self.mmap_multiple(page, page_count, flags, move |page, flags, mapper, flusher| Ok(Some(map(page, flags, mapper, flusher)?))) + } + pub fn mmap_multiple<I: IntoIterator<Item = Grant>>(&mut self, page: Option<Page>, page_count: NonZeroUsize, flags: MapFlags, map: impl FnOnce(Page, PageFlags<RmmA>, &mut PageMapper, &mut dyn Flusher<RmmA>) -> Result<I>) -> Result<Page> { // Finally, the end of all "T0DO: Abstract with other grant creation"! let selected_span = self.grants.find_free_at(self.mmap_min, page, page_count.get(), flags)?; // TODO: Threads share address spaces, so not only the inactive flusher should be sending - // out IPIs. + // out IPIs. IPIs will only be sent when downgrading mappings (i.e. when a stale TLB entry + // will not be corrected by a page fault), and will furthermore require proper + // synchronization. let (mut active, mut inactive); let flusher = if self.is_current() { active = PageFlushAll::new(); @@ -219,7 +224,10 @@ impl AddrSpace { &mut inactive as &mut dyn Flusher<RmmA> }; - self.grants.insert(map(selected_span.base, page_flags(flags), &mut self.table.utable, flusher)?); + let iter = map(selected_span.base, page_flags(flags), &mut self.table.utable, flusher)?; + for grant in iter { + self.grants.insert(grant); + } Ok(selected_span.base) } @@ -401,6 +409,7 @@ impl UserGrants { return Err(Error::new(EEXIST)); } if flags.contains(MapFlags::MAP_FIXED) { + // TODO: find_free_at -> Result<(PageSpan, needs_to_unmap: PageSpan)> return Err(Error::new(EOPNOTSUPP)); } else { // TODO: Find grant close to requested address? @@ -703,7 +712,13 @@ impl Grant { }) } - // TODO: Return multiple grants. + // TODO: Do not return Vec, return an iterator perhaps? Referencing the source address space? + + /// Borrow all pages in the range `[src_base, src_base+page_count)` from `src_address_space`, + /// mapping them into `[dst_base, dst_base+page_count)`. While the pages are borrowed, + /// subsequent mappings/mprotects/etc. will not be visible in the destination address space; + /// the *pages present at that time* are borrowed, rather than the source range permanently, by + /// reference. pub fn borrow( src_address_space_lock: Arc<RwLock<AddrSpace>>, src_address_space: &AddrSpace, @@ -714,7 +729,7 @@ impl Grant { dst_mapper: &mut PageMapper, dst_flusher: impl Flusher<RmmA>, eager: bool, - ) -> Result<Grant, Enomem> { + ) -> Result<Vec<Grant>, Enomem> { /* if eager { for page in PageSpan::new(src_base, page_count) { @@ -723,20 +738,40 @@ impl Grant { } */ - Ok(Grant { - base: dst_base, - info: GrantInfo { - page_count, - flags, - mapped: true, - provider: Provider::External { - src_base, - address_space: src_address_space_lock, - cow: false, - pages: None, - } - }, - }) + let mut dst_grants = Vec::with_capacity(1); + + let src_span = PageSpan::new(src_base, page_count); + + for (src_base, src_grant) in src_address_space.grants.conflicts(src_span) { + let grant_span = PageSpan::new(src_base, src_grant.page_count); + + let common_span = src_span.intersection(grant_span); + let offset_from_src_base = common_span.base.offset_from(src_base); + + dst_grants.push(Grant { + base: dst_base.next_by(offset_from_src_base), + info: GrantInfo { + page_count: common_span.count, + flags, + mapped: true, + provider: match src_grant.provider { + Provider::Allocated { ref pages } => Provider::External { + src_base, + address_space: Arc::clone(&src_address_space_lock), + cow: false, + pages: None, + }, + 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, ref pages } => Provider::External { address_space: Arc::clone(address_space), src_base, cow, pages: pages.as_ref().map(|pages| pages.iter().map(|pg| pg.as_ref().map(|pg| pg.ref_clone(false))).collect::<Vec<_>>().into()) }, + } + }, + }); + } + + Ok(dst_grants) } // TODO: This is limited to one page. Should it be (if some magic new proc: API is introduced)? pub fn cow( diff --git a/src/scheme/proc.rs b/src/scheme/proc.rs index 1fe4026c6707461e0ca3f2ac98f5f59d3e1c0a66..52c3fba431fa24f2fea6046b60262a94fbf55cc6 100644 --- a/src/scheme/proc.rs +++ b/src/scheme/proc.rs @@ -687,9 +687,6 @@ impl KernelScheme for ProcScheme { if Arc::ptr_eq(addrspace, dst_addr_space) { return Err(Error::new(EBUSY)); } - /* - // Limit to transferring/borrowing at most one grant, or part of a grant (splitting - // will be mandatory if grants are coalesced). let (requested_dst_page, page_count) = crate::syscall::validate_region(map.address, map.size)?; let (src_page, _) = crate::syscall::validate_region(map.offset, map.size)?; @@ -718,7 +715,7 @@ impl KernelScheme for ProcScheme { let src_mapper = &mut src_addr_space.table.utable; let src_page_count = NonZeroUsize::new(src_grant_span.count).ok_or(Error::new(EINVAL))?; - let result_page = if consume { + let result_base = if consume { let grant = src_addr_space.grants.remove(src_grant_span.base).expect("grant cannot disappear"); let (before, middle, after) = grant.extract(src_grant_span).expect("called intersect(), must succeed"); @@ -727,12 +724,10 @@ impl KernelScheme for ProcScheme { dst_addr_space.mmap(requested_dst_page, src_page_count, map.flags, |dst_page, _flags, dst_mapper, dst_flusher| Grant::transfer(middle, dst_page, src_mapper, dst_mapper, InactiveFlusher::new(), dst_flusher))? } else { - dst_addr_space.mmap(requested_dst_page, src_page_count, map.flags, |dst_page, flags, dst_mapper, flusher| Ok(Grant::borrow(src_grant_span.base, dst_page, src_grant_span.count, flags, None, src_mapper, dst_mapper, flusher)?))? + dst_addr_space.mmap_multiple(requested_dst_page, src_page_count, map.flags, |dst_page, flags, dst_mapper, flusher| Ok(Grant::borrow(Arc::clone(addrspace), src_addr_space, src_grant_span.base, dst_page, src_grant_span.count, flags, dst_mapper, flusher, true)?))? }; - */ - //Ok(result_page.start_address().data()) - todo!() + Ok(result_base.start_address().data()) } _ => Err(Error::new(EBADF)), } diff --git a/src/scheme/user.rs b/src/scheme/user.rs index 2a0b19cbf4c0bb58b2f4d7f010ebc2953e944967..d46013b6ab62e8e83e32963180e8d3f0ba9cb3dc 100644 --- a/src/scheme/user.rs +++ b/src/scheme/user.rs @@ -261,7 +261,7 @@ impl UserInner { let (_middle_part_of_buf, tail_part_of_buf) = middle_tail_part_of_buf.split_at(middle_page_count * PAGE_SIZE).expect("split must succeed"); if let Some(middle_page_count) = NonZeroUsize::new(middle_page_count) { - dst_space.mmap(Some(first_middle_dst_page), middle_page_count, map_flags, move |dst_page, page_flags, mapper, flusher| { + dst_space.mmap_multiple(Some(first_middle_dst_page), middle_page_count, map_flags, move |dst_page, page_flags, mapper, flusher| { Ok(Grant::borrow(Arc::clone(&cur_space_lock), &mut *cur_space_lock.write(), first_middle_src_page, dst_page, middle_page_count.get(), page_flags, mapper, flusher, true)?) })?; }