[vm] Avoid early munmap of shm
This commit is contained in:
parent
ad317e61f6
commit
2e608cdf47
@ -4,7 +4,8 @@ use crate::fs::FileMode;
|
|||||||
use crate::process::{do_getegid, do_geteuid, gid_t, uid_t, ThreadRef};
|
use crate::process::{do_getegid, do_geteuid, gid_t, uid_t, ThreadRef};
|
||||||
use crate::time::{do_gettimeofday, time_t};
|
use crate::time::{do_gettimeofday, time_t};
|
||||||
use crate::vm::{
|
use crate::vm::{
|
||||||
ChunkRef, VMInitializer, VMMapOptionsBuilder, VMPerms, VMRange, USER_SPACE_VM_MANAGER,
|
ChunkRef, MunmapChunkFlag, VMInitializer, VMMapOptionsBuilder, VMPerms, VMRange,
|
||||||
|
USER_SPACE_VM_MANAGER,
|
||||||
};
|
};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
@ -212,7 +213,7 @@ impl Drop for ShmSegment {
|
|||||||
assert!(self.process_set.len() == 0);
|
assert!(self.process_set.len() == 0);
|
||||||
USER_SPACE_VM_MANAGER
|
USER_SPACE_VM_MANAGER
|
||||||
.internal()
|
.internal()
|
||||||
.munmap_chunk(&self.chunk, None, false);
|
.munmap_chunk(&self.chunk, None, MunmapChunkFlag::Default);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +80,7 @@ pub use self::chunk::{ChunkRef, ChunkType};
|
|||||||
pub use self::process_vm::{MMapFlags, MRemapFlags, MSyncFlags, ProcessVM, ProcessVMBuilder};
|
pub use self::process_vm::{MMapFlags, MRemapFlags, MSyncFlags, ProcessVM, ProcessVMBuilder};
|
||||||
pub use self::user_space_vm::USER_SPACE_VM_MANAGER;
|
pub use self::user_space_vm::USER_SPACE_VM_MANAGER;
|
||||||
pub use self::vm_area::VMArea;
|
pub use self::vm_area::VMArea;
|
||||||
|
pub use self::vm_manager::MunmapChunkFlag;
|
||||||
pub use self::vm_perms::VMPerms;
|
pub use self::vm_perms::VMPerms;
|
||||||
pub use self::vm_range::VMRange;
|
pub use self::vm_range::VMRange;
|
||||||
pub use self::vm_util::{VMInitializer, VMMapOptionsBuilder};
|
pub use self::vm_util::{VMInitializer, VMMapOptionsBuilder};
|
||||||
|
@ -3,6 +3,7 @@ use super::*;
|
|||||||
use super::chunk::*;
|
use super::chunk::*;
|
||||||
use super::user_space_vm::USER_SPACE_VM_MANAGER;
|
use super::user_space_vm::USER_SPACE_VM_MANAGER;
|
||||||
use super::vm_area::VMArea;
|
use super::vm_area::VMArea;
|
||||||
|
use super::vm_manager::MunmapChunkFlag;
|
||||||
use super::vm_perms::VMPerms;
|
use super::vm_perms::VMPerms;
|
||||||
use super::vm_util::{
|
use super::vm_util::{
|
||||||
FileBacked, VMInitializer, VMMapAddr, VMMapOptions, VMMapOptionsBuilder, VMRemapOptions,
|
FileBacked, VMInitializer, VMMapAddr, VMMapOptions, VMMapOptionsBuilder, VMRemapOptions,
|
||||||
@ -214,7 +215,7 @@ impl<'a, 'b> ProcessVMBuilder<'a, 'b> {
|
|||||||
chunks.iter().for_each(|chunk| {
|
chunks.iter().for_each(|chunk| {
|
||||||
USER_SPACE_VM_MANAGER
|
USER_SPACE_VM_MANAGER
|
||||||
.internal()
|
.internal()
|
||||||
.munmap_chunk(chunk, None, false);
|
.munmap_chunk(chunk, None, MunmapChunkFlag::Default);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -312,9 +313,11 @@ impl Drop for ProcessVM {
|
|||||||
mem_chunks
|
mem_chunks
|
||||||
.drain_filter(|chunk| chunk.is_single_vma())
|
.drain_filter(|chunk| chunk.is_single_vma())
|
||||||
.for_each(|chunk| {
|
.for_each(|chunk| {
|
||||||
USER_SPACE_VM_MANAGER
|
USER_SPACE_VM_MANAGER.internal().munmap_chunk(
|
||||||
.internal()
|
&chunk,
|
||||||
.munmap_chunk(&chunk, None, false);
|
None,
|
||||||
|
MunmapChunkFlag::OnProcessExit,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
assert!(mem_chunks.len() == 0);
|
assert!(mem_chunks.len() == 0);
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
//! Shared memory manager. (POSIX)
|
//! Shared memory manager. (POSIX)
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use super::vm_manager::InternalVMManager;
|
use super::vm_manager::{InternalVMManager, MunmapChunkFlag};
|
||||||
use super::vm_util::VMMapOptions;
|
use super::vm_util::VMMapOptions;
|
||||||
use crate::process::ThreadStatus;
|
use crate::process::ThreadStatus;
|
||||||
|
|
||||||
use rcore_fs::vfs::{FileType, Metadata};
|
use rcore_fs::vfs::{FileType, Metadata};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Weak};
|
use std::sync::{Arc, Weak};
|
||||||
|
|
||||||
type InodeId = usize;
|
type InodeId = usize;
|
||||||
@ -120,8 +120,8 @@ impl ShmManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Self::apply_new_perms_if_higher(&mut shared_vma, *options.perms());
|
Self::apply_new_perms_if_higher(&mut shared_vma, *options.perms());
|
||||||
|
shared_vma.attach_shared_process(current_pid)?;
|
||||||
if !contained {
|
if !contained {
|
||||||
shared_vma.attach_shared_process(current_pid)?;
|
|
||||||
current.vm().add_mem_chunk(shared_chunk.clone());
|
current.vm().add_mem_chunk(shared_chunk.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +132,7 @@ impl ShmManager {
|
|||||||
&mut self,
|
&mut self,
|
||||||
chunk: &ChunkRef,
|
chunk: &ChunkRef,
|
||||||
unmap_range: &VMRange,
|
unmap_range: &VMRange,
|
||||||
force_unmap: bool,
|
flag: MunmapChunkFlag,
|
||||||
) -> Result<MunmapSharedResult> {
|
) -> Result<MunmapSharedResult> {
|
||||||
debug_assert!(chunk.is_shared());
|
debug_assert!(chunk.is_shared());
|
||||||
let mut shared_vma = Self::vma_of(chunk);
|
let mut shared_vma = Self::vma_of(chunk);
|
||||||
@ -141,7 +141,9 @@ impl ShmManager {
|
|||||||
let partial_unmap = !unmap_range.is_superset_of(shared_range);
|
let partial_unmap = !unmap_range.is_superset_of(shared_range);
|
||||||
|
|
||||||
// Fails when force unmap a partial of shared chunk which is still shared by other process
|
// Fails when force unmap a partial of shared chunk which is still shared by other process
|
||||||
if force_unmap && (partial_unmap || !shared_vma.exclusive_by(current_pid)) {
|
if flag == MunmapChunkFlag::Force
|
||||||
|
&& (partial_unmap || !shared_vma.exclusive_by(current_pid))
|
||||||
|
{
|
||||||
return_errno!(EINVAL, "force unmap shared chunk failed");
|
return_errno!(EINVAL, "force unmap shared chunk failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +152,11 @@ impl ShmManager {
|
|||||||
return Ok(MunmapSharedResult::StillInUse);
|
return Ok(MunmapSharedResult::StillInUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
if shared_vma.detach_shared_process(current_pid)? {
|
let force_detach = match flag {
|
||||||
|
MunmapChunkFlag::Default => false,
|
||||||
|
MunmapChunkFlag::Force | MunmapChunkFlag::OnProcessExit => true,
|
||||||
|
};
|
||||||
|
if shared_vma.detach_shared_process(current_pid, force_detach)? {
|
||||||
self.shared_chunks.remove(&Self::inode_id_of(&shared_vma));
|
self.shared_chunks.remove(&Self::inode_id_of(&shared_vma));
|
||||||
Ok(MunmapSharedResult::Freeable)
|
Ok(MunmapSharedResult::Freeable)
|
||||||
} else {
|
} else {
|
||||||
|
@ -6,7 +6,6 @@ use super::vm_util::FileBacked;
|
|||||||
|
|
||||||
use intrusive_collections::rbtree::{Link, RBTree};
|
use intrusive_collections::rbtree::{Link, RBTree};
|
||||||
use intrusive_collections::{intrusive_adapter, KeyAdapter};
|
use intrusive_collections::{intrusive_adapter, KeyAdapter};
|
||||||
use std::collections::HashSet;
|
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
@ -19,8 +18,11 @@ pub struct VMArea {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum VMAccess {
|
pub enum VMAccess {
|
||||||
|
/// Can only be accessed by one single process
|
||||||
Private(pid_t),
|
Private(pid_t),
|
||||||
Shared(HashSet<pid_t>),
|
/// Can be accessed by multi processes, also a reference counter
|
||||||
|
/// to record sharings within each process(like thread)
|
||||||
|
Shared(HashMap<pid_t, u32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VMArea {
|
impl VMArea {
|
||||||
@ -88,14 +90,18 @@ impl VMArea {
|
|||||||
pub fn belong_to(&self, target_pid: pid_t) -> bool {
|
pub fn belong_to(&self, target_pid: pid_t) -> bool {
|
||||||
match &self.access {
|
match &self.access {
|
||||||
VMAccess::Private(pid) => *pid == target_pid,
|
VMAccess::Private(pid) => *pid == target_pid,
|
||||||
VMAccess::Shared(pid_set) => pid_set.contains(&target_pid),
|
VMAccess::Shared(pid_table) => pid_table.contains_key(&target_pid),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exclusive_by(&self, target_pid: pid_t) -> bool {
|
pub fn exclusive_by(&self, target_pid: pid_t) -> bool {
|
||||||
match &self.access {
|
match &self.access {
|
||||||
VMAccess::Private(pid) => *pid == target_pid,
|
VMAccess::Private(pid) => *pid == target_pid,
|
||||||
VMAccess::Shared(pid_set) => pid_set.len() == 1 && pid_set.contains(&target_pid),
|
VMAccess::Shared(pid_table) => {
|
||||||
|
pid_table.len() == 1
|
||||||
|
&& pid_table.contains_key(&target_pid)
|
||||||
|
&& *pid_table.get(&target_pid).unwrap() == 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -264,7 +270,7 @@ impl VMArea {
|
|||||||
|
|
||||||
pub fn mark_shared(&mut self) {
|
pub fn mark_shared(&mut self) {
|
||||||
let access = match self.access {
|
let access = match self.access {
|
||||||
VMAccess::Private(pid) => VMAccess::Shared(HashSet::from([pid])),
|
VMAccess::Private(pid) => VMAccess::Shared(HashMap::from([(pid, 1)])),
|
||||||
VMAccess::Shared(_) => {
|
VMAccess::Shared(_) => {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -272,29 +278,31 @@ impl VMArea {
|
|||||||
self.access = access;
|
self.access = access;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn shared_process_set(&self) -> Result<&HashSet<pid_t>> {
|
|
||||||
match &self.access {
|
|
||||||
VMAccess::Private(_) => Err(errno!(EINVAL, "not a shared vma")),
|
|
||||||
VMAccess::Shared(pid_set) => Ok(pid_set),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn attach_shared_process(&mut self, pid: pid_t) -> Result<()> {
|
pub fn attach_shared_process(&mut self, pid: pid_t) -> Result<()> {
|
||||||
match &mut self.access {
|
match &mut self.access {
|
||||||
VMAccess::Private(_) => Err(errno!(EINVAL, "not a shared vma")),
|
VMAccess::Private(_) => Err(errno!(EINVAL, "not a shared vma")),
|
||||||
VMAccess::Shared(pid_set) => {
|
VMAccess::Shared(pid_table) => {
|
||||||
pid_set.insert(pid);
|
if let Some(mut ref_ctr) = pid_table.get_mut(&pid) {
|
||||||
|
*ref_ctr += 1;
|
||||||
|
} else {
|
||||||
|
let _ = pid_table.insert(pid, 1);
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn detach_shared_process(&mut self, pid: pid_t) -> Result<bool> {
|
pub fn detach_shared_process(&mut self, pid: pid_t, force_detach: bool) -> Result<bool> {
|
||||||
match &mut self.access {
|
match &mut self.access {
|
||||||
VMAccess::Private(_) => Err(errno!(EINVAL, "not a shared vma")),
|
VMAccess::Private(_) => Err(errno!(EINVAL, "not a shared vma")),
|
||||||
VMAccess::Shared(pid_set) => {
|
VMAccess::Shared(pid_table) => {
|
||||||
pid_set.remove(&pid);
|
if let Some(mut ref_ctr) = pid_table.get_mut(&pid) {
|
||||||
Ok(pid_set.is_empty())
|
*ref_ctr -= 1;
|
||||||
|
if *ref_ctr == 0 || force_detach {
|
||||||
|
let _ = pid_table.remove(&pid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(pid_table.is_empty())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,9 +224,11 @@ impl VMManager {
|
|||||||
|
|
||||||
for chunk in overlapping_chunks.iter() {
|
for chunk in overlapping_chunks.iter() {
|
||||||
match chunk.internal() {
|
match chunk.internal() {
|
||||||
ChunkType::SingleVMA(_) => {
|
ChunkType::SingleVMA(_) => internal_manager.munmap_chunk(
|
||||||
internal_manager.munmap_chunk(chunk, Some(&munmap_range), false)?
|
chunk,
|
||||||
}
|
Some(&munmap_range),
|
||||||
|
MunmapChunkFlag::Default,
|
||||||
|
)?,
|
||||||
ChunkType::MultiVMA(manager) => manager
|
ChunkType::MultiVMA(manager) => manager
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -262,7 +264,7 @@ impl VMManager {
|
|||||||
return internal_manager.munmap_chunk(
|
return internal_manager.munmap_chunk(
|
||||||
&overlapping_chunk,
|
&overlapping_chunk,
|
||||||
Some(&munmap_range),
|
Some(&munmap_range),
|
||||||
false,
|
MunmapChunkFlag::Default,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
warn!("no overlapping chunks anymore");
|
warn!("no overlapping chunks anymore");
|
||||||
@ -512,7 +514,7 @@ impl VMManager {
|
|||||||
let mut mem_chunks = thread.vm().mem_chunks().write().unwrap();
|
let mut mem_chunks = thread.vm().mem_chunks().write().unwrap();
|
||||||
|
|
||||||
mem_chunks.iter().for_each(|chunk| {
|
mem_chunks.iter().for_each(|chunk| {
|
||||||
internal_manager.munmap_chunk(&chunk, None, false);
|
internal_manager.munmap_chunk(&chunk, None, MunmapChunkFlag::OnProcessExit);
|
||||||
});
|
});
|
||||||
mem_chunks.clear();
|
mem_chunks.clear();
|
||||||
|
|
||||||
@ -588,7 +590,7 @@ impl InternalVMManager {
|
|||||||
&mut self,
|
&mut self,
|
||||||
chunk: &ChunkRef,
|
chunk: &ChunkRef,
|
||||||
munmap_range: Option<&VMRange>,
|
munmap_range: Option<&VMRange>,
|
||||||
force_unmap: bool,
|
flag: MunmapChunkFlag,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
trace!(
|
trace!(
|
||||||
"munmap_chunk range = {:?}, munmap_range = {:?}",
|
"munmap_chunk range = {:?}, munmap_range = {:?}",
|
||||||
@ -619,11 +621,11 @@ impl InternalVMManager {
|
|||||||
|
|
||||||
if chunk.is_shared() {
|
if chunk.is_shared() {
|
||||||
trace!(
|
trace!(
|
||||||
"munmap_shared_chunk, chunk_range: {:?}, munmap_range = {:?}",
|
"munmap_shared_chunk, chunk_range = {:?}, munmap_range = {:?}",
|
||||||
chunk.range(),
|
chunk.range(),
|
||||||
munmap_range,
|
munmap_range,
|
||||||
);
|
);
|
||||||
return self.munmap_shared_chunk(chunk, munmap_range, force_unmap);
|
return self.munmap_shared_chunk(chunk, munmap_range, flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Either the munmap range is a subset of the chunk range or the munmap range is
|
// Either the munmap range is a subset of the chunk range or the munmap range is
|
||||||
@ -743,7 +745,7 @@ impl InternalVMManager {
|
|||||||
&mut self,
|
&mut self,
|
||||||
chunk: &ChunkRef,
|
chunk: &ChunkRef,
|
||||||
munmap_range: &VMRange,
|
munmap_range: &VMRange,
|
||||||
force_unmap: bool,
|
flag: MunmapChunkFlag,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if !chunk.is_shared() {
|
if !chunk.is_shared() {
|
||||||
return_errno!(EINVAL, "not a shared chunk");
|
return_errno!(EINVAL, "not a shared chunk");
|
||||||
@ -754,7 +756,7 @@ impl InternalVMManager {
|
|||||||
|
|
||||||
if self
|
if self
|
||||||
.shm_manager
|
.shm_manager
|
||||||
.munmap_shared_chunk(chunk, munmap_range, force_unmap)?
|
.munmap_shared_chunk(chunk, munmap_range, flag)?
|
||||||
== MunmapSharedResult::Freeable
|
== MunmapSharedResult::Freeable
|
||||||
{
|
{
|
||||||
let vma = chunk.get_vma_for_single_vma_chunk();
|
let vma = chunk.get_vma_for_single_vma_chunk();
|
||||||
@ -1025,7 +1027,7 @@ impl InternalVMManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Munmap the corresponding single vma chunk
|
// Munmap the corresponding single vma chunk
|
||||||
self.munmap_chunk(&chunk, Some(&target_range), true)?;
|
self.munmap_chunk(&chunk, Some(&target_range), MunmapChunkFlag::Force)?;
|
||||||
}
|
}
|
||||||
VMMapAddr::Any => unreachable!(),
|
VMMapAddr::Any => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -1147,6 +1149,17 @@ impl InternalVMManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Flags used by `munmap_chunk()` and `munmap_shared_chunk()`.
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum MunmapChunkFlag {
|
||||||
|
/// Indicates normal behavior when munamp a shared chunk
|
||||||
|
Default,
|
||||||
|
/// Indicates the shared chunk must be freed entirely
|
||||||
|
Force,
|
||||||
|
/// Indicates the shared chunk must detach current process totally
|
||||||
|
OnProcessExit,
|
||||||
|
}
|
||||||
|
|
||||||
impl VMRemapParser for InternalVMManager {
|
impl VMRemapParser for InternalVMManager {
|
||||||
fn is_free_range(&self, request_range: &VMRange) -> bool {
|
fn is_free_range(&self, request_range: &VMRange) -> bool {
|
||||||
self.free_manager.is_free_range(request_range)
|
self.free_manager.is_free_range(request_range)
|
||||||
|
Loading…
Reference in New Issue
Block a user