mirror of
https://github.com/asterinas/asterinas.git
synced 2025-06-21 08:16:32 +00:00
Support mmap shared anonymous memory
This commit is contained in:
committed by
Tate, Hongliang Tian
parent
89b21ba3b4
commit
ea25157790
@ -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
|
||||||
//
|
//
|
||||||
|
@ -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()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
@ -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())))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 \
|
||||||
|
5
regression/apps/mmap/Makefile
Normal file
5
regression/apps/mmap/Makefile
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
include ../test_common.mk
|
||||||
|
|
||||||
|
EXTRA_C_FLAGS :=
|
59
regression/apps/mmap/map_shared_anon.c
Normal file
59
regression/apps/mmap/map_shared_anon.c
Normal 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;
|
||||||
|
}
|
@ -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}......"
|
||||||
|
@ -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 \
|
||||||
|
18
regression/syscall_test/blocklists/mmap_test
Normal file
18
regression/syscall_test/blocklists/mmap_test
Normal 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
|
Reference in New Issue
Block a user