Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Configurable Page Size Policy to MmapRegion #127

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,22 @@ let buf = &mut [0u8; 5];
let result = guest_memory_mmap.write(buf, addr);
```

#### Page Size Policy

For regions backed by a call to `mmap`, the user may specify the desired page size
and behavior expected by the operating system. The current options include:

- `BasePages`: The standard page size provided by the operating system.
- `TransparentHugepages`: (Implemented only for Unix-like systems) Hints to the operating
system that base pages can be combined transparently into larger page sizes. Concretely,
mappings with this policy will invoke `madvise` with the `MADV_HUGEPAGE` flag.
- `ExplicitHugepages`: Requests that the entire mapping be explicitly mapped to a pre-reserved
pool of hugepages. Concretely, mappings with this policy will include the `MAP_HUGETLB` flag
in the call to `mmap`. **NOTE:** If the operating system has no available hugepages (e.g. on Linux,
if `cat /proc/sys/vm/nr_hugepages` reads 0), then the mapping, or attempts to dereference addresses
within the region, may fail. It is the responsibility of the VMM to ensure that hugepages are
available for use before constructing a mapping with this policy.

### Utilities and Helpers

The following utilities and helper traits/macros are imported from the
Expand Down
53 changes: 49 additions & 4 deletions src/mmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,17 @@ pub enum Error {
UnsortedMemoryRegions,
}

/// Page configuration types for controlling allocation size and behavior
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum PageSizePolicy {
/// Base pages are the smallest page-size unit available on the system.
BasePages,
/// Transparent hugepages, if available, are managed by the host operating system.
TransparentHugepages,
/// Explicit hugepages swear a lot. Especially if the addresses aren't aligned.
ExplicitHugepages,
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Expand Down Expand Up @@ -426,7 +437,21 @@ impl GuestMemoryMmap {
///
/// Valid memory regions are specified as a slice of (Address, Size) tuples sorted by Address.
pub fn from_ranges(ranges: &[(GuestAddress, usize)]) -> result::Result<Self, Error> {
Self::from_ranges_with_files(ranges.iter().map(|r| (r.0, r.1, None)))
Self::from_ranges_with_options(
ranges
.iter()
.map(|r| (r.0, r.1, PageSizePolicy::BasePages, None)),
)
}

/// Creates a container and allocates anonymous memory for guest memory regions.
///
/// Valid memory regions are specified as asequence of (Address, Size, PageSizePolicy)
/// tuples sorted by Address.
pub fn from_ranges_with_policy(
ranges: &[(GuestAddress, usize, PageSizePolicy)],
) -> result::Result<Self, Error> {
Self::from_ranges_with_options(ranges.iter().map(|r| (r.0, r.1, r.2, None)))
}

/// Creates a container and allocates anonymous memory for guest memory regions.
Expand All @@ -437,18 +462,38 @@ impl GuestMemoryMmap {
where
A: Borrow<(GuestAddress, usize, Option<FileOffset>)>,
T: IntoIterator<Item = A>,
{
Self::from_ranges_with_options(ranges.into_iter().map(|r| {
(
r.borrow().0,
r.borrow().1,
PageSizePolicy::BasePages,
r.borrow().2.clone(),
)
}))
}

/// Creates a container and allocates anonymous memory for guest memory regions.
///
/// Valid memory regions are specified as a sequence of (Address, Size, Option<FileOffset>)
/// tuples sorted by Address.
pub fn from_ranges_with_options<A, T>(ranges: T) -> result::Result<Self, Error>
where
A: Borrow<(GuestAddress, usize, PageSizePolicy, Option<FileOffset>)>,
T: IntoIterator<Item = A>,
{
Self::from_regions(
ranges
.into_iter()
.map(|x| {
let guest_base = x.borrow().0;
let size = x.borrow().1;
let policy = x.borrow().2;

if let Some(ref f_off) = x.borrow().2 {
MmapRegion::from_file(f_off.clone(), size)
if let Some(ref f_off) = x.borrow().3 {
MmapRegion::from_file_with_policy(f_off.clone(), size, policy)
} else {
MmapRegion::new(size)
MmapRegion::with_policy(size, policy)
}
.map_err(Error::MmapRegion)
.and_then(|r| GuestRegionMmap::new(r, guest_base))
Expand Down
56 changes: 55 additions & 1 deletion src/mmap_unix.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::ptr::null_mut;
use std::result;

use crate::guest_memory::FileOffset;
use crate::mmap::{check_file_offset, AsSlice};
use crate::mmap::{check_file_offset, AsSlice, PageSizePolicy};
use crate::volatile_memory::{self, compute_offset, VolatileMemory, VolatileSlice};

/// Error conditions that may arise when creating a new `MmapRegion` object.
Expand Down Expand Up @@ -90,6 +90,7 @@ pub struct MmapRegion {
prot: i32,
flags: i32,
owned: bool,
policy: PageSizePolicy,
}

// Send and Sync aren't automatically inherited for the raw address pointer.
Expand All @@ -105,11 +106,21 @@ impl MmapRegion {
/// # Arguments
/// * `size` - The size of the memory region in bytes.
pub fn new(size: usize) -> Result<Self> {
Self::with_policy(size, PageSizePolicy::BasePages)
}

/// Creates a shared anonymous mapping of `size` bytes.
///
/// # Arguments
/// * `size` - The size of the memory region in bytes.
/// * `policy` - The page size policy of the memory region.
pub fn with_policy(size: usize, policy: PageSizePolicy) -> Result<Self> {
Self::build(
None,
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_ANONYMOUS | libc::MAP_NORESERVE | libc::MAP_PRIVATE,
policy,
)
}

Expand All @@ -120,11 +131,27 @@ impl MmapRegion {
/// referred to by `file_offset.file`.
/// * `size` - The size of the memory region in bytes.
pub fn from_file(file_offset: FileOffset, size: usize) -> Result<Self> {
Self::from_file_with_policy(file_offset, size, PageSizePolicy::BasePages)
}

/// Creates a shared file mapping of `size` bytes.
///
/// # Arguments
/// * `file_offset` - The mapping will be created at offset `file_offset.start` in the file
/// referred to by `file_offset.file`.
/// * `size` - The size of the memory region in bytes.
/// * `policy` - The page size policy of the memory region.
pub fn from_file_with_policy(
file_offset: FileOffset,
size: usize,
policy: PageSizePolicy,
) -> Result<Self> {
Self::build(
Some(file_offset),
size,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_NORESERVE | libc::MAP_SHARED,
policy,
)
}

Expand All @@ -143,6 +170,7 @@ impl MmapRegion {
size: usize,
prot: i32,
flags: i32,
policy: PageSizePolicy,
) -> Result<Self> {
// Forbid MAP_FIXED, as it doesn't make sense in this context, and is pretty dangerous
// in general.
Expand All @@ -157,6 +185,13 @@ impl MmapRegion {
(-1, 0)
};

// Support explicit (pre-reserved) hugepages if requested
let flags = if policy == PageSizePolicy::ExplicitHugepages {
flags | libc::MAP_HUGETLB
} else {
flags
};

// This is safe because we're not allowing MAP_FIXED, and invalid parameters cannot break
// Rust safety guarantees (things may change if we're mapping /dev/mem or some wacky file).
let addr = unsafe { libc::mmap(null_mut(), size, prot, flags, fd, offset as libc::off_t) };
Expand All @@ -165,13 +200,21 @@ impl MmapRegion {
return Err(Error::Mmap(io::Error::last_os_error()));
}

// Support transparent hugepages if requested
if policy == PageSizePolicy::TransparentHugepages {
unsafe {
libc::madvise(addr, size, libc::MADV_HUGEPAGE);
};
}

Ok(Self {
addr: addr as *mut u8,
size,
file_offset,
prot,
flags,
owned: true,
policy,
})
}

Expand Down Expand Up @@ -213,6 +256,7 @@ impl MmapRegion {
prot,
flags,
owned: false,
policy: PageSizePolicy::BasePages,
})
}

Expand Down Expand Up @@ -248,6 +292,11 @@ impl MmapRegion {
self.owned
}

/// Returns information regarding the page size policy backing this region.
pub fn policy(&self) -> PageSizePolicy {
self.policy
}

/// Checks whether this region and `other` are backed by overlapping
/// [`FileOffset`](struct.FileOffset.html) objects.
///
Expand Down Expand Up @@ -387,6 +436,7 @@ mod tests {
size,
prot,
flags,
PageSizePolicy::BasePages,
);
assert_eq!(format!("{:?}", r.unwrap_err()), "InvalidOffsetLength");

Expand All @@ -396,6 +446,7 @@ mod tests {
size,
prot,
flags,
PageSizePolicy::BasePages,
);
assert_eq!(format!("{:?}", r.unwrap_err()), "MappingPastEof");

Expand All @@ -405,6 +456,7 @@ mod tests {
size,
prot,
flags | libc::MAP_FIXED,
PageSizePolicy::BasePages,
);
assert_eq!(format!("{:?}", r.unwrap_err()), "MapFixed");

Expand All @@ -417,6 +469,7 @@ mod tests {
size,
prot,
flags,
PageSizePolicy::BasePages,
);
assert_eq!(r.unwrap_err().raw_os_error(), libc::EINVAL);

Expand All @@ -426,6 +479,7 @@ mod tests {
size,
prot,
flags,
PageSizePolicy::BasePages,
)
.unwrap();

Expand Down
26 changes: 25 additions & 1 deletion src/mmap_windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use libc::{c_void, size_t};
use winapi::um::errhandlingapi::GetLastError;

use crate::guest_memory::FileOffset;
use crate::mmap::AsSlice;
use crate::mmap::{AsSlice, PageSizePolicy};
use crate::volatile_memory::{self, compute_offset, VolatileMemory, VolatileSlice};

#[allow(non_snake_case)]
Expand Down Expand Up @@ -89,6 +89,15 @@ impl MmapRegion {
/// # Arguments
/// * `size` - The size of the memory region in bytes.
pub fn new(size: usize) -> io::Result<Self> {
Self::with_policy(size, PageSizePolicy::BasePages)
}

/// Creates a shared anonymous mapping of `size` bytes.
///
/// # Arguments
/// * `size` - The size of the memory region in bytes.
/// * `policy` - Unimplemented on Windows platforms.
pub fn with_policy(size: usize, _policy: PageSizePolicy) -> io::Result<Self> {
if (size == 0) || (size > MM_HIGHEST_VAD_ADDRESS as usize) {
return Err(io::Error::from_raw_os_error(libc::EINVAL));
}
Expand All @@ -112,6 +121,21 @@ impl MmapRegion {
/// referred to by `file_offset.file`.
/// * `size` - The size of the memory region in bytes.
pub fn from_file(file_offset: FileOffset, size: usize) -> io::Result<Self> {
Self::from_file_with_policy(file_offset, size, PageSizePolicy::BasePages)
}

/// Creates a shared file mapping of `size` bytes.
///
/// # Arguments
/// * `file_offset` - The mapping will be created at offset `file_offset.start` in the file
/// referred to by `file_offset.file`.
/// * `size` - The size of the memory region in bytes.
/// * `policy` - Unimplemented on Windows platforms.
pub fn from_file_with_policy(
file_offset: FileOffset,
size: usize,
_policy: PageSizePolicy,
) -> io::Result<Self> {
let handle = file_offset.file().as_raw_handle();
if handle == INVALID_HANDLE_VALUE {
return Err(io::Error::from_raw_os_error(libc::EBADF));
Expand Down