Skip to content

Commit 90e90f5

Browse files
committed
implement shared mmap for anonymous maps
1 parent b5730b7 commit 90e90f5

4 files changed

Lines changed: 99 additions & 11 deletions

File tree

libkernel/src/memory/proc_vm/memory_map/mod.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,25 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
9393

9494
/// Maps a region of memory.
9595
pub fn mmap(
96+
&mut self,
97+
requested_address: AddressRequest,
98+
len: usize,
99+
perms: VMAPermissions,
100+
kind: VMAreaKind,
101+
name: String,
102+
) -> Result<VA> {
103+
self.mmap_with_options(requested_address, len, perms, kind, name, false)
104+
}
105+
106+
/// Maps a region of memory with explicit VMA options.
107+
pub fn mmap_with_options(
96108
&mut self,
97109
requested_address: AddressRequest,
98110
mut len: usize,
99111
perms: VMAPermissions,
100112
kind: VMAreaKind,
101113
name: String,
114+
shared: bool,
102115
) -> Result<VA> {
103116
if len == 0 {
104117
return Err(KernelError::InvalidValue);
@@ -150,6 +163,7 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
150163
let mut new_vma = VMArea::new(region, kind, perms);
151164

152165
new_vma.set_name(name);
166+
new_vma.set_shared(shared);
153167

154168
self.insert_and_merge(new_vma);
155169

@@ -494,8 +508,9 @@ impl<AS: UserAddressSpace> MemoryMap<AS> {
494508
for vma in new_vmas.values() {
495509
let mut pte_perms = PtePermissions::from(vma.permissions);
496510

497-
// Mark all writable pages as CoW.
498-
if pte_perms.is_write() {
511+
// Shared mappings stay shared across fork; private writable
512+
// mappings become CoW.
513+
if !vma.is_shared() && pte_perms.is_write() {
499514
pte_perms = pte_perms.into_cow();
500515
}
501516

libkernel/src/memory/proc_vm/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,7 @@ mod tests {
194194
kind: VMAreaKind::Anon, // Simplification for test
195195
permissions: VMAPermissions::rx(),
196196
name: String::new(),
197+
shared: false,
197198
};
198199

199200
ProcessVM::from_vma(text_vma).unwrap()
@@ -350,6 +351,7 @@ mod tests {
350351
kind: VMAreaKind::Anon,
351352
permissions: VMAPermissions::ro(),
352353
name: String::new(),
354+
shared: false,
353355
};
354356
vm.mm.insert_and_merge(obstacle_vma);
355357
assert_eq!(vm.mm.vma_count(), 2);

libkernel/src/memory/proc_vm/vmarea.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ pub struct VMArea {
186186
pub(super) name: String,
187187
pub(super) kind: VMAreaKind,
188188
pub(super) permissions: VMAPermissions,
189+
pub(super) shared: bool,
189190
}
190191

191192
impl VMArea {
@@ -201,6 +202,7 @@ impl VMArea {
201202
kind,
202203
permissions,
203204
name: String::new(),
205+
shared: false,
204206
}
205207
}
206208

@@ -209,6 +211,11 @@ impl VMArea {
209211
self.name = s.as_ref().to_string();
210212
}
211213

214+
/// Marks this VMA as a shared mapping.
215+
pub fn set_shared(&mut self, shared: bool) {
216+
self.shared = shared;
217+
}
218+
212219
/// Creates a file-backed `VMArea` directly from an ELF program header.
213220
///
214221
/// This is a convenience function used by the ELF loader. It parses the
@@ -264,6 +271,7 @@ impl VMArea {
264271
}),
265272
permissions,
266273
name: String::new(),
274+
shared: false,
267275
}
268276
}
269277

@@ -397,7 +405,7 @@ impl VMArea {
397405
/// Merging is possible if permissions are identical and the backing storage
398406
/// is of a compatible and contiguous nature.
399407
pub(super) fn can_merge_with(&self, other: &VMArea) -> bool {
400-
if self.permissions != other.permissions {
408+
if self.permissions != other.permissions || self.shared != other.shared {
401409
return false;
402410
}
403411

@@ -435,6 +443,11 @@ impl VMArea {
435443
matches!(self.kind, VMAreaKind::File(_))
436444
}
437445

446+
/// Returns whether this VMA was created as a shared mapping.
447+
pub fn is_shared(&self) -> bool {
448+
self.shared
449+
}
450+
438451
/// Shrink this VMA's region to `new_region`, recalculating file offsets,
439452
/// for file mappings.
440453
#[must_use]

src/memory/mmap.rs

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
use core::sync::atomic::{AtomicUsize, Ordering};
22

3-
use crate::{process::fd_table::Fd, sched::syscall_ctx::ProcessCtx};
3+
use crate::{memory::page::ClaimedPage, process::fd_table::Fd, sched::syscall_ctx::ProcessCtx};
44
use alloc::string::{String, ToString};
55
use libkernel::{
6-
error::{KernelError, Result},
6+
error::{KernelError, MapError, Result},
77
memory::{
8+
PAGE_MASK, PAGE_SIZE,
89
address::VA,
10+
paging::permissions::PtePermissions,
911
proc_vm::{
12+
address_space::UserAddressSpace,
1013
memory_map::AddressRequest,
1114
vmarea::{VMAPermissions, VMAreaKind},
1215
},
@@ -45,6 +48,47 @@ fn prot_to_perms(prot: u64) -> VMAPermissions {
4548
/// # Returns
4649
/// A `Result` containing the starting address of the new mapping on success,
4750
/// or a `KernelError` on failure.
51+
fn align_mmap_len(len: usize) -> usize {
52+
if len & PAGE_MASK != 0 {
53+
(len & !PAGE_MASK) + PAGE_SIZE
54+
} else {
55+
len
56+
}
57+
}
58+
59+
async fn populate_shared_anon_mapping(
60+
ctx: &ProcessCtx,
61+
start: VA,
62+
len: usize,
63+
permissions: VMAPermissions,
64+
) -> Result<()> {
65+
let region = VirtMemoryRegion::new(start, align_mmap_len(len));
66+
67+
for page_va in region.iter_pages() {
68+
let page = ClaimedPage::alloc_zeroed()?;
69+
let pfn = page.pa().to_pfn();
70+
71+
match ctx
72+
.shared()
73+
.vm
74+
.lock_save_irq()
75+
.mm_mut()
76+
.address_space_mut()
77+
.map_page(pfn, page_va, PtePermissions::from(permissions))
78+
{
79+
Ok(()) => {
80+
page.leak();
81+
}
82+
Err(KernelError::MappingError(MapError::AlreadyMapped)) => {
83+
drop(page);
84+
}
85+
Err(e) => return Err(e),
86+
}
87+
}
88+
89+
Ok(())
90+
}
91+
4892
pub async fn sys_mmap(
4993
ctx: &ProcessCtx,
5094
addr: u64,
@@ -58,13 +102,15 @@ pub async fn sys_mmap(
58102
return Err(KernelError::InvalidValue);
59103
}
60104

61-
// Ensure mapping sharability has been specified:
62-
if (flags & (MAP_SHARED | MAP_PRIVATE)) == 0 {
105+
let mapping_kind = flags & (MAP_SHARED | MAP_PRIVATE);
106+
if mapping_kind == 0 || mapping_kind == (MAP_SHARED | MAP_PRIVATE) {
63107
return Err(KernelError::InvalidValue);
64108
}
65109

66-
// TODO: Shared Mappings.
67-
if (flags & MAP_SHARED) != 0 {
110+
let is_shared = (flags & MAP_SHARED) != 0;
111+
let is_anon = (flags & (MAP_ANON | MAP_ANONYMOUS)) != 0;
112+
113+
if is_shared && !is_anon {
68114
return Err(KernelError::NotSupported);
69115
}
70116

@@ -87,7 +133,10 @@ pub async fn sys_mmap(
87133

88134
let requested_len = len as usize;
89135

90-
let (kind, name) = if (flags & (MAP_ANON | MAP_ANONYMOUS)) != 0 {
136+
let (kind, name) = if is_anon {
137+
if offset != 0 {
138+
return Err(KernelError::InvalidValue);
139+
}
91140
(VMAreaKind::Anon, String::new())
92141
} else {
93142
// File-backed mapping: require a valid fd and use the provided offset.
@@ -128,14 +177,23 @@ pub async fn sys_mmap(
128177

129178
// Lock the task and call the core memory manager to perform the mapping.
130179
let proc_vm = ctx.shared().vm.shared_vm();
131-
let new_mapping_addr = proc_vm.lock_save_irq().mm_mut().mmap(
180+
let new_mapping_addr = proc_vm.lock_save_irq().mm_mut().mmap_with_options(
132181
address_request,
133182
requested_len,
134183
permissions,
135184
kind,
136185
name,
186+
is_shared,
137187
)?;
138188

189+
if is_shared
190+
&& let Err(e) =
191+
populate_shared_anon_mapping(ctx, new_mapping_addr, requested_len, permissions).await
192+
{
193+
let _ = sys_munmap(ctx, new_mapping_addr, align_mmap_len(requested_len)).await;
194+
return Err(e);
195+
}
196+
139197
Ok(new_mapping_addr.value())
140198
}
141199

0 commit comments

Comments
 (0)