use bytemuck::NoUninit;
use std::io::Result;
use crate::util::os::*;
use crate::vm::*;
use crate::{
util::{address::Address, VMThread},
vm::VMBinding,
};
#[derive(Debug)]
pub struct MmapError {
pub error_address: Address,
pub bytes: usize,
pub annotation: String,
pub error: std::io::Error,
}
impl MmapError {
pub fn new(
error_address: Address,
bytes: usize,
annotation: &MmapAnnotation<'_>,
error: std::io::Error,
) -> Self {
Self {
error_address,
bytes,
annotation: annotation.to_string(),
error,
}
}
}
impl std::fmt::Display for MmapError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"mmap {} (size {}, annotation {}) failed: {}",
self.error_address, self.bytes, self.annotation, self.error
)
}
}
impl std::error::Error for MmapError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.error)
}
}
pub type MmapResult<T> = std::result::Result<T, MmapError>;
pub trait OSMemory {
fn dzmmap(
start: Address,
size: usize,
strategy: MmapStrategy,
annotation: &MmapAnnotation<'_>,
) -> MmapResult<Address>;
fn dzmmap_anywhere(
size: usize,
align: usize,
strategy: MmapStrategy,
annotation: &MmapAnnotation<'_>,
) -> MmapResult<Address>;
fn dzmmap_preferred(
start: Address,
size: usize,
align: usize,
strategy: MmapStrategy,
annotation: &MmapAnnotation<'_>,
) -> MmapResult<Address>;
fn handle_mmap_error<VM: VMBinding>(mmap_error: MmapError, tls: VMThread) {
use crate::util::alloc::AllocationError;
use std::io::ErrorKind;
eprintln!(
"Failed to mmap from {} to {} (size {}), annotation {}",
mmap_error.error_address,
mmap_error.error_address.wrapping_add(mmap_error.bytes),
mmap_error.bytes,
mmap_error.annotation
);
eprintln!("{}", OS::get_process_memory_maps().unwrap());
let call_binding_oom = || {
trace!("Signal MmapOutOfMemory!");
VM::VMCollection::out_of_memory(tls, AllocationError::MmapOutOfMemory);
unreachable!()
};
match mmap_error.error.kind() {
ErrorKind::OutOfMemory => {
call_binding_oom();
}
ErrorKind::Other => {
if let Some(os_errno) = mmap_error.error.raw_os_error() {
if OS::is_mmap_oom(os_errno) {
call_binding_oom();
}
}
}
ErrorKind::AlreadyExists => {
panic!("Failed to mmap, the address is already mapped. Should MMTk quarantine the address range first?");
}
_ => {
if let Some(os_errno) = mmap_error.error.raw_os_error() {
if OS::is_mmap_oom(os_errno) {
call_binding_oom();
}
}
}
}
panic!("Unexpected mmap failure: {:?}", mmap_error.error)
}
fn is_mmap_oom(os_errno: i32) -> bool;
fn munmap(start: Address, size: usize) -> Result<()>;
fn set_memory_access(start: Address, size: usize, prot: MmapProtection) -> Result<()>;
fn panic_if_unmapped(start: Address, size: usize);
fn get_system_total_memory() -> Result<u64> {
use sysinfo::MemoryRefreshKind;
use sysinfo::{RefreshKind, System};
let sys = System::new_with_specifics(
RefreshKind::nothing().with_memory(MemoryRefreshKind::nothing().with_ram()),
);
Ok(sys.total_memory())
}
}
#[derive(Debug, Copy, Clone)]
pub struct MmapStrategy {
pub huge_page: HugePageSupport,
pub prot: MmapProtection,
pub replace: bool,
pub reserve: bool,
}
impl std::default::Default for MmapStrategy {
fn default() -> Self {
Self {
huge_page: HugePageSupport::No,
prot: MmapProtection::ReadWrite,
replace: false,
reserve: true,
}
}
}
impl MmapStrategy {
pub fn new(
huge_page: HugePageSupport,
prot: MmapProtection,
replace: bool,
reserve: bool,
) -> Self {
Self {
huge_page,
prot,
replace,
reserve,
}
}
pub fn huge_page(self, huge_page: HugePageSupport) -> Self {
Self { huge_page, ..self }
}
pub fn transparent_hugepages(self, enable: bool) -> Self {
let huge_page = if enable {
HugePageSupport::TransparentHugePages
} else {
HugePageSupport::No
};
Self { huge_page, ..self }
}
pub fn prot(self, prot: MmapProtection) -> Self {
Self { prot, ..self }
}
pub fn replace(self, replace: bool) -> Self {
Self { replace, ..self }
}
pub fn reserve(self, reserve: bool) -> Self {
Self { reserve, ..self }
}
#[cfg(test)] pub const INTERNAL_MEMORY: Self = Self::TEST;
#[cfg(not(test))]
pub const INTERNAL_MEMORY: Self = Self {
huge_page: HugePageSupport::No,
prot: MmapProtection::ReadWrite,
replace: false,
reserve: true,
};
pub const RAW_MEMORY_FREELIST: Self = Self {
huge_page: HugePageSupport::No,
prot: MmapProtection::ReadWrite,
replace: true,
reserve: true,
};
pub const QUARANTINE: Self = Self {
huge_page: HugePageSupport::No,
prot: MmapProtection::NoAccess,
replace: cfg!(test),
reserve: false,
};
#[cfg(test)]
pub const TEST: Self = Self {
huge_page: HugePageSupport::No,
prot: MmapProtection::ReadWrite,
replace: true,
reserve: true,
};
}
#[repr(i32)]
#[derive(Debug, Copy, Clone)]
pub enum MmapProtection {
ReadWrite,
ReadWriteExec,
NoAccess,
}
#[repr(u8)]
#[derive(Debug, Copy, Clone, NoUninit)]
pub enum HugePageSupport {
No,
TransparentHugePages,
}
pub enum MmapAnnotation<'a> {
Space {
name: &'a str,
},
SideMeta {
space: &'a str,
meta: &'a str,
},
Test {
file: &'a str,
line: u32,
},
Misc {
name: &'a str,
},
}
#[macro_export]
macro_rules! mmap_anno_test {
() => {
&$crate::util::os::MmapAnnotation::Test {
file: file!(),
line: line!(),
}
};
}
pub use mmap_anno_test;
impl std::fmt::Display for MmapAnnotation<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MmapAnnotation::Space { name } => write!(f, "mmtk:space:{name}"),
MmapAnnotation::SideMeta { space, meta } => write!(f, "mmtk:sidemeta:{space}:{meta}"),
MmapAnnotation::Test { file, line } => write!(f, "mmtk:test:{file}:{line}"),
MmapAnnotation::Misc { name } => write!(f, "mmtk:misc:{name}"),
}
}
}