Support mmap shared anonymous memory

This commit is contained in:
Jianfeng Jiang
2023-12-04 07:32:38 +00:00
committed by Tate, Hongliang Tian
parent 89b21ba3b4
commit ea25157790
12 changed files with 148 additions and 10 deletions

View File

@ -52,6 +52,8 @@ fn do_sys_mmap(
addr, len, vm_perms, option, fd, offset addr, len, vm_perms, option, fd, offset
); );
check_option(&option)?;
let len = len.align_up(PAGE_SIZE); let len = len.align_up(PAGE_SIZE);
if offset % PAGE_SIZE != 0 { if offset % PAGE_SIZE != 0 {
@ -78,6 +80,11 @@ fn do_sys_mmap(
// TODO: support MAP_32BIT. MAP_32BIT requires the map range to be below 2GB // TODO: support MAP_32BIT. MAP_32BIT requires the map range to be below 2GB
warn!("MAP_32BIT is not supported"); warn!("MAP_32BIT is not supported");
} }
if option.typ() == MMapType::Shared {
options = options.is_shared(true);
}
options options
}; };
let map_addr = vm_map_options.build()?; let map_addr = vm_map_options.build()?;
@ -121,6 +128,14 @@ fn alloc_filebacked_vmo(
} }
} }
fn check_option(option: &MMapOptions) -> Result<()> {
if option.typ() == MMapType::File {
return_errno_with_message!(Errno::EINVAL, "Invalid mmap type");
}
Ok(())
}
// Definition of MMap flags, conforming to the linux mmap interface: // Definition of MMap flags, conforming to the linux mmap interface:
// https://man7.org/linux/man-pages/man2/mmap.2.html // https://man7.org/linux/man-pages/man2/mmap.2.html
// //

View File

@ -153,7 +153,7 @@ impl Vmar<Rights> {
/// The method requires the Read right. /// The method requires the Read right.
pub fn fork_from(vmar: &Vmar) -> Result<Self> { pub fn fork_from(vmar: &Vmar) -> Result<Self> {
vmar.check_rights(Rights::READ)?; vmar.check_rights(Rights::READ)?;
let vmar_ = vmar.0.new_cow_root()?; let vmar_ = vmar.0.new_fork_root()?;
Ok(Vmar(vmar_, Rights::all())) Ok(Vmar(vmar_, Rights::all()))
} }
} }

View File

@ -706,16 +706,16 @@ impl Vmar_ {
Ok(()) Ok(())
} }
pub(super) fn new_cow_root(self: &Arc<Self>) -> Result<Arc<Self>> { pub(super) fn new_fork_root(self: &Arc<Self>) -> Result<Arc<Self>> {
if self.parent.upgrade().is_some() { if self.parent.upgrade().is_some() {
return_errno_with_message!(Errno::EINVAL, "can only dup cow vmar for root vmar"); return_errno_with_message!(Errno::EINVAL, "can only dup cow vmar for root vmar");
} }
self.new_cow(None) self.new_fork(None)
} }
/// Create a new vmar by creating cow child for all mapped vmos. /// Create a new vmar by creating cow child for all mapped vmos.
fn new_cow(&self, parent: Option<&Arc<Vmar_>>) -> Result<Arc<Self>> { fn new_fork(&self, parent: Option<&Arc<Vmar_>>) -> Result<Arc<Self>> {
let new_vmar_ = { let new_vmar_ = {
let vmar_inner = VmarInner::new(); let vmar_inner = VmarInner::new();
// If this is not a root `Vmar`, we clone the `VmSpace` from parent. // If this is not a root `Vmar`, we clone the `VmSpace` from parent.
@ -742,7 +742,7 @@ impl Vmar_ {
// Clone child vmars. // Clone child vmars.
for (child_vmar_base, child_vmar_) in &inner.child_vmar_s { for (child_vmar_base, child_vmar_) in &inner.child_vmar_s {
let new_child_vmar = child_vmar_.new_cow(Some(&new_vmar_))?; let new_child_vmar = child_vmar_.new_fork(Some(&new_vmar_))?;
new_vmar_ new_vmar_
.inner .inner
.lock() .lock()
@ -752,7 +752,7 @@ impl Vmar_ {
// Clone vm mappings. // Clone vm mappings.
for (vm_mapping_base, vm_mapping) in &inner.vm_mappings { for (vm_mapping_base, vm_mapping) in &inner.vm_mappings {
let new_mapping = Arc::new(vm_mapping.new_cow(&new_vmar_)?); let new_mapping = Arc::new(vm_mapping.new_fork(&new_vmar_)?);
new_vmar_ new_vmar_
.inner .inner
.lock() .lock()

View File

@ -160,7 +160,7 @@ impl<R: TRights> Vmar<TRightSet<R>> {
/// The method requires the Read right. /// The method requires the Read right.
pub fn fork_from<R1>(vmar: &Vmar<R1>) -> Result<Self> { pub fn fork_from<R1>(vmar: &Vmar<R1>) -> Result<Self> {
vmar.check_rights(Rights::READ)?; vmar.check_rights(Rights::READ)?;
let vmar_ = vmar.0.new_cow_root()?; let vmar_ = vmar.0.new_fork_root()?;
Ok(Vmar(vmar_, TRightSet(R::new()))) Ok(Vmar(vmar_, TRightSet(R::new())))
} }

View File

@ -24,6 +24,10 @@ pub struct VmMapping {
parent: Weak<Vmar_>, parent: Weak<Vmar_>,
/// The mapped vmo. The mapped vmo is with dynamic capability. /// The mapped vmo. The mapped vmo is with dynamic capability.
vmo: Vmo<Rights>, vmo: Vmo<Rights>,
/// Whether the mapping is shared among processes
/// TODO: support file-backed shared mappings.
/// only anonyous memory can be mapped shared now.
is_shared: bool,
} }
impl VmMapping { impl VmMapping {
@ -34,6 +38,7 @@ impl VmMapping {
inner: Mutex::new(inner), inner: Mutex::new(inner),
parent: self.parent.clone(), parent: self.parent.clone(),
vmo, vmo,
is_shared: self.is_shared,
}) })
} }
} }
@ -73,6 +78,7 @@ impl VmMapping {
offset, offset,
align, align,
can_overwrite, can_overwrite,
is_shared,
} = option; } = option;
let Vmar(parent_vmar, _) = parent; let Vmar(parent_vmar, _) = parent;
let vmo_size = vmo.size(); let vmo_size = vmo.size();
@ -102,6 +108,7 @@ impl VmMapping {
inner: Mutex::new(vm_mapping_inner), inner: Mutex::new(vm_mapping_inner),
parent: Arc::downgrade(&parent_vmar), parent: Arc::downgrade(&parent_vmar),
vmo: vmo.to_dyn(), vmo: vmo.to_dyn(),
is_shared,
}) })
} }
@ -274,13 +281,17 @@ impl VmMapping {
Ok(()) Ok(())
} }
pub(super) fn new_cow(&self, new_parent: &Arc<Vmar_>) -> Result<VmMapping> { pub(super) fn new_fork(&self, new_parent: &Arc<Vmar_>) -> Result<VmMapping> {
let VmMapping { inner, vmo, .. } = self; let VmMapping { inner, vmo, .. } = self;
let child_vmo = { let child_vmo = {
let parent_vmo = vmo.dup().unwrap(); let parent_vmo = vmo.dup().unwrap();
let vmo_size = parent_vmo.size(); let vmo_size = parent_vmo.size();
VmoChildOptions::new_cow(parent_vmo, 0..vmo_size).alloc()? if self.is_shared {
VmoChildOptions::new_slice_rights(parent_vmo, 0..vmo_size).alloc()?
} else {
VmoChildOptions::new_cow(parent_vmo, 0..vmo_size).alloc()?
}
}; };
let new_inner = { let new_inner = {
@ -299,6 +310,7 @@ impl VmMapping {
inner: Mutex::new(new_inner), inner: Mutex::new(new_inner),
parent: Arc::downgrade(new_parent), parent: Arc::downgrade(new_parent),
vmo: child_vmo, vmo: child_vmo,
is_shared: self.is_shared,
}) })
} }
@ -614,6 +626,8 @@ pub struct VmarMapOptions<R1, R2> {
offset: Option<usize>, offset: Option<usize>,
align: usize, align: usize,
can_overwrite: bool, can_overwrite: bool,
// Whether the mapping is mapped with `MAP_SHARED`
is_shared: bool,
} }
impl<R1, R2> VmarMapOptions<R1, R2> { impl<R1, R2> VmarMapOptions<R1, R2> {
@ -634,6 +648,7 @@ impl<R1, R2> VmarMapOptions<R1, R2> {
offset: None, offset: None,
align: PAGE_SIZE, align: PAGE_SIZE,
can_overwrite: false, can_overwrite: false,
is_shared: false,
} }
} }
@ -703,6 +718,17 @@ impl<R1, R2> VmarMapOptions<R1, R2> {
self self
} }
/// Sets whether the mapping can be shared with other process.
///
/// The default value is false.
///
/// If this value is set to true, the mapping will be shared with child
/// process (by creating slice child vmo) when forking.
pub fn is_shared(mut self, is_shared: bool) -> Self {
self.is_shared = is_shared;
self
}
/// Creates the mapping. /// Creates the mapping.
/// ///
/// All options will be checked at this point. /// All options will be checked at this point.

View File

@ -571,4 +571,17 @@ mod test {
vmo.resize(PAGE_SIZE).unwrap(); vmo.resize(PAGE_SIZE).unwrap();
assert_eq!(vmo.read_val::<u8>(10).unwrap(), 42); assert_eq!(vmo.read_val::<u8>(10).unwrap(), 42);
} }
#[ktest]
fn resize_cow() {
let vmo = VmoOptions::<Full>::new(10 * PAGE_SIZE)
.flags(VmoFlags::RESIZABLE)
.alloc()
.unwrap();
let cow_child = VmoChildOptions::new_cow(vmo, 0..PAGE_SIZE).alloc().unwrap();
cow_child.resize(2 * PAGE_SIZE).unwrap();
assert_eq!(cow_child.size(), 2 * PAGE_SIZE);
}
} }

View File

@ -19,6 +19,7 @@ TEST_APPS := \
hello_c \ hello_c \
hello_pie \ hello_pie \
hello_world \ hello_world \
mmap \
mongoose \ mongoose \
network \ network \
pthread \ pthread \

View File

@ -0,0 +1,5 @@
# SPDX-License-Identifier: MPL-2.0
include ../test_common.mk
EXTRA_C_FLAGS :=

View File

@ -0,0 +1,59 @@
// SPDX-License-Identifier: MPL-2.0
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/wait.h>
int main()
{
int *shared_memory;
int value = 123;
// Create an anonymous memory region for sharing
shared_memory = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (shared_memory == MAP_FAILED) {
perror("mmap failed");
exit(1);
}
// Create a child process
pid_t pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
} else if (pid == 0) {
// Child process
// Modify the value in the shared memory in the child process
*shared_memory = value;
printf("Child process: Value in shared memory: %d\n",
*shared_memory);
} else {
// Parent process
// Wait for the child process to finish
wait(NULL);
printf("Parent process: Value in shared memory: %d\n",
*shared_memory);
if (*shared_memory != value) {
perror("shared memory contains invalid value");
exit(1);
}
// Unmap the memory
if (munmap(shared_memory, sizeof(int)) == -1) {
perror("munmap failed");
exit(1);
}
}
return 0;
}

View File

@ -8,7 +8,7 @@ SCRIPT_DIR=/regression
cd ${SCRIPT_DIR}/.. cd ${SCRIPT_DIR}/..
echo "Start process test......" echo "Start process test......"
tests="hello_world/hello_world fork/fork execve/execve fork_c/fork signal_c/signal_test pthread/pthread_test hello_pie/hello pty/open_pty getpid/getpid eventfd2/eventfd2" tests="hello_world/hello_world fork/fork execve/execve fork_c/fork signal_c/signal_test pthread/pthread_test hello_pie/hello pty/open_pty getpid/getpid eventfd2/eventfd2 mmap/map_shared_anon"
for testcase in ${tests} for testcase in ${tests}
do do
echo "Running test ${testcase}......" echo "Running test ${testcase}......"

View File

@ -17,6 +17,7 @@ TESTS ?= \
link_test \ link_test \
lseek_test \ lseek_test \
mkdir_test \ mkdir_test \
mmap_test \
open_create_test \ open_create_test \
open_test \ open_test \
pty_test \ pty_test \

View File

@ -0,0 +1,18 @@
MMapFileTest*
MMapNoFixtureTest*
ReadWriteSharedPrivate*
MMapDeathTest.TruncateAfterCOWBreak
MMapTest.FixedAlignment
MMapTest.InvalidLength
MMapTest.ProtWriteOnlyReadable
MMapTest.MprotectNotPageAligned
MMapTest.MprotectHugeLength
MMapTest.HugeLength
MMapTest.ExceedLimit*
MMapTest.NoExceedLimitAS
MMapTest.AccessCOWInvalidatesCachedSegments
MMapTest.MapPipe
MMapTest.MapDevZero*
MMapTest.MapCharDevice
MMapTest.MapDirectory
MMapTest.PrivateAndShared