1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//! # Mapping
//! Anonymous memory straight from the kernel, at addresses
//! that never move
use std::{
ptr,
sync::atomic::{AtomicUsize, Ordering},
};
/// The page size, or 0 before it has been asked for
static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0);
/// The size of a page, asking the kernel on first use
#[inline(always)]
pub(crate) fn page_size() -> usize {
let cached = PAGE_SIZE.load(Ordering::Relaxed);
if cached != 0 {
return cached;
}
let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) } as usize;
PAGE_SIZE.store(size, Ordering::Relaxed);
size
}
/// Rounds a length up to a whole number of pages
#[inline(always)]
pub(crate) fn round_up(len: usize) -> usize {
let page = page_size();
len.div_ceil(page) * page
}
/// Maps `len` bytes of zeroed, readable, writable memory
///
/// ## Returns
/// The base address, or null if the kernel refuses
pub(crate) fn alloc(len: usize) -> *mut u8 {
let len = round_up(len);
let base = unsafe {
libc::mmap(
ptr::null_mut(), // Let the kernel pick the address
len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_ANON | libc::MAP_PRIVATE, // Not backed by a file, not shared
-1, // No descriptor to back it with
0,
)
};
if base == libc::MAP_FAILED {
return ptr::null_mut();
}
base.cast::<u8>()
}
/// Gives a mapping back to the kernel
///
/// `len` must be the same length that was passed to `alloc`
pub(crate) fn free(base: *mut u8, len: usize) {
if base.is_null() {
return;
}
unsafe { libc::munmap(base.cast::<libc::c_void>(), round_up(len)) };
}
/// Gives the pages behind a mapping back without unmapping it
///
/// ## Returns
/// Whether the kernel took them
///
/// ## Behaviour
/// The range stays mapped, and reads as zeros once the kernel
/// has reclaimed it
///
/// ## Safety
/// Nothing in the range may be in use. The kernel can zero it
/// at any point after this, though a write before it does
/// cancels the reclaim for that page
pub(crate) unsafe fn release(base: *mut u8, len: usize) -> bool {
if base.is_null() || len == 0 {
return false;
}
unsafe { libc::madvise(base.cast::<libc::c_void>(), len, libc::MADV_FREE) == 0 }
}