Started adding huge-page support in for `MappedFile<T>` and `file::memory::MemoryFile`. Also added huge-page support to `MappedFile<T>` via the `MapFlags` trait, which can also be used to add user-provided flags.

Added unit struct `Anonymous`, which can be used in `MappedFile` for creating anonymous mappings.

Fortune for mapped-file's current commit: Future blessing − 末吉
master
Avril 2 years ago
parent 8795f0a1a2
commit 92fe0166de
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -8,5 +8,4 @@ use super::*;
#[repr(transparent)]
pub struct MemoryFile(ManagedFD);
mod hugetlb;
//TODO: implement `memfd` (and its hugetlb interface, extracted to `hugetlb.rs`) from `utf8encode`.
//TODO: implement `memfd` from `utf8encode`.

@ -1,4 +0,0 @@
//! Huge-page interface for `MemoryFile`.
use super::*;
//TODO: implement `memfd`'s hugetlb interface from `utf8encode` here.

@ -1,7 +1,7 @@
//! All flags for controlling a `MappedFile<T>`.
use super::*;
use libc::c_int;
/// Permissions for the mapped pages.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Default)]
pub enum Perm
@ -23,6 +23,82 @@ pub enum Flags
Private,
}
impl Flags
{
/// Add these flags to another `MapFlags` provider's mask.
///
/// # Safety
/// The caller *should* ensure there are no conflicting flags present in the bitwise OR of `self` and `flags`'s respective masks; if there are, then `mmap()` may fail. This will not result in unexpected mapping behaviour, but will cause an error.
///
/// However, the caller **must** ensure there are no *overlapping* bits in the resulting mask, as that may produce a valid but unexpected combined mask.
///
/// # `hugetlb` support
/// For adding huge-page mapping flags to these, use `with_hugetlb()` instead.
#[inline]
pub unsafe fn chain_with(self, flags: impl MapFlags) -> impl MapFlags
{
struct Chained<T: ?Sized>(Flags, T);
unsafe impl<T: ?Sized> MapFlags for Chained<T>
where T: MapFlags
{
#[inline(always)]
fn get_mmap_flags(&self) -> c_int {
self.0.get_flags() | self.1.get_mmap_flags()
}
}
Chained(self, flags)
}
/// Add huge-page info to the mapping flags for this `MappedFile<T>` instance.
///
/// # Returns
/// An opaque type that combines the flags of `self` with those computed by `hugetlb`.
#[inline]
pub const fn with_hugetlb(self, hugetlb: HugePage) -> impl MapFlags + Send + Sync + 'static
{
#[derive(Debug)]
struct HugeTLBFlags(Flags, HugePage);
unsafe impl MapFlags for HugeTLBFlags
{
#[inline(always)]
fn get_mmap_flags(&self) -> c_int {
self.0.get_flags() | self.1.compute_huge().map(MapHugeFlag::get_mask).unwrap_or(0)
}
}
HugeTLBFlags(self, hugetlb)
}
}
/// Any type implementing this trait can be passed to `MappedFile<T>`'s `try_/new()` method to provide flags directly for `mmap()`.
/// Usually, the enum `Flags` should be used for this, but for HUGETLB configurations, or used-defined `MAP_FIXED` usages, it can be used on other types.
///
/// This trait is also implemented on `()`, which will just return `Flags::default()`'s implementation of it.
///
/// # Safety
/// This trait is marked `unsafe` as invalid memory mapping configurations can cause invalid or undefined behaviour that is unknown to `MappedFile<T>`.
pub unsafe trait MapFlags
{
fn get_mmap_flags(&self) -> c_int;
}
unsafe impl MapFlags for ()
{
#[inline]
fn get_mmap_flags(&self) -> c_int {
Flags::default().get_flags()
}
}
unsafe impl MapFlags for Flags
{
#[inline(always)]
fn get_mmap_flags(&self) -> c_int {
self.get_flags()
}
}
impl Flags
{
#[inline(always)]

@ -0,0 +1,159 @@
//! Huge-page interface for `MappedFile<T>` and `MemoryFile`.
use super::*;
use std::{
mem,
hash,
num::NonZeroUsize,
};
use libc::{
c_int,
MAP_HUGE_SHIFT,
};
/// Location in which the kernel exposes available huge-page sizes.
pub const HUGEPAGE_LOCATION: &'static str = "/sys/kernel/mm/hugepages/";
/// Represents a statically defined `MAP_HUGE_*` flag.
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
#[repr(transparent)]
pub struct MapHugeFlag(c_int);
impl Default for MapHugeFlag
{
#[inline]
fn default() -> Self {
Self(MAP_HUGE_SHIFT)
}
}
#[inline(always)]
//TODO: XXX: Check this implementation of `log2<usize>()`... It seems slightly wrong...
const fn log2(n: usize) -> usize
{
/*const fn num_bits<T>() -> usize {
mem::size_of::<T>() * (u8::BITS as usize)
}*/
usize::BITS as usize - n.leading_zeros() as usize - 1
}
impl MapHugeFlag
{
/// Create from a raw `MAP_HUGE_*` flag.
///
/// # Safety
/// The passed `flag` **must** be a valid bitmask representing a `MAP_HUGE_*` value **only**.
#[inline]
pub const unsafe fn from_mask_unchecked(flag: c_int) -> Self
{
Self(flag)
}
/// The kernel's default huge-page size.
pub const HUGE_DEFAULT: Self = Self(MAP_HUGE_SHIFT);
/// Predefined `MAP_HUGE_2MB` mask,
pub const HUGE_2MB: Self = Self(libc::MAP_HUGE_2MB);
/// Predefined `MAP_HUGE_1GB` mask,
pub const HUGE_1GB: Self = Self(libc::MAP_HUGE_1GB);
/// Calculate a `MAP_HUGE_*` flag from a size (in kB).
#[inline(always)]
pub const fn calculate(kilobytes: NonZeroUsize) -> Self
{
Self((log2(kilobytes.get()) << (MAP_HUGE_SHIFT as usize)) as c_int)
}
/// Get the `MAP_HUGE_*` mask.
#[inline(always)]
pub const fn get_mask(self) -> c_int
{
self.0
}
}
impl From<MapHugeFlag> for c_int
{
fn from(from: MapHugeFlag) -> Self
{
from.0
}
}
#[derive(Default, Clone, Copy)]
pub enum HugePage {
/// A staticly presented `MAP_HUGE_*` flag. See `MapHugeFlag` for details.
Static(MapHugeFlag),
/// A dynamically calculated `MAP_HUGE_*` flag from an arbitrary size *in kB*.
///
/// # Safety
/// The kernel must actually support huge-pages of this size.
Dynamic{ kilobytes: usize },
/// The smallest huge-page size on the system
#[default]
Smallest,
/// The largest huge-page size on the system
Largest,
/// Use a callback function to select the huge-page size (*in kB*) from an *ordered* (lowest to highest) enumeration of all available on the system.
//TODO: Remember to order the HUGEPAGE_LOCATION parsing results before passing them to this!
Selected(for<'r> fn (&'r [usize]) -> &'r usize),
}
impl hash::Hash for HugePage {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
mem::discriminant(self).hash(state);
match self {
Self::Static(hpf) => hpf.hash(state),
Self::Dynamic { kilobytes } => kilobytes.hash(state),
Self::Selected(func) => ptr::hash(func as *const _, state),
_ => (),
};
}
}
impl fmt::Debug for HugePage
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
f.debug_tuple("HugePage")
.field({
let v: &dyn fmt::Debug = match &self {
Self::Static(ref huge) => huge,
Self::Dynamic { ref kilobytes } => kilobytes,
Self::Smallest => &"<smallest>",
Self::Largest => &"<largest>",
Self::Selected(_) => &"<selector>",
};
v
})
.finish()
}
}
impl Eq for HugePage {}
impl PartialEq for HugePage
{
#[inline]
fn eq(&self, other: &Self) -> bool
{
match (self, other) {
(Self::Static(hpf), Self::Static(hpf2)) => hpf == hpf2,
(Self::Dynamic { kilobytes }, Self::Dynamic { kilobytes: kilobytes2 }) => kilobytes == kilobytes2,
(Self::Selected(func), Self::Selected(func2)) => ptr::eq(func, func2),
_ => mem::discriminant(self) == mem::discriminant(other),
}
}
}
impl HugePage
{
pub fn compute_huge(&self) -> Option<MapHugeFlag>
{
todo!("TODO: copy `utf8encode`'s `compute_huge_flag()` -> pub fn compute_flag(&self) -> Option<MapHugeFlag>;")
}
//TODO: ^
}
//TODO: implement `memfd`'s hugetlb interface from `utf8encode` here.

@ -23,8 +23,14 @@ use std::{
mod ffi;
use ffi::c_try;
pub mod hugetlb;
pub mod file;
use hugetlb::{
HugePage,
MapHugeFlag,
};
mod uniq;
use uniq::UniqueSlice;
@ -58,10 +64,10 @@ pub struct MappedFile<T>
map: MappedSlice,
}
//TODO: continue copying from the `TODO` line in `utf8encode/src/mmap.rs`
impl<T: AsRawFd> MappedFile<T> {
/// Map the file `file` to `len` bytes with memory protection as provided by `perm`, and
/// mapping flags provided by `flags`.
/// Map the file `file` to `len` bytes with memory protection as provided by `perm`, and mapping flags provided by `flags`.
/// # Mapping flags
/// The trait `MapFlags` is used to allow user-defined configurations of `mmap()`, but the `Flags` enum should usually be used for this, or `()`, which behaves the same as `Flags::default()`.
///
/// # Returns
/// If `mmap()` fails, then the current `errno` is returned alongside the `file` that was passed in, otherwise, a new mapping is
@ -69,7 +75,7 @@ impl<T: AsRawFd> MappedFile<T> {
///
/// # Panics
/// If `mmap()` succeeds, but returns an invalid address (e.g. 0)
pub fn try_new(file: T, len: usize, perm: Perm, flags: Flags) -> Result<Self, TryNewError<T>>
pub fn try_new(file: T, len: usize, perm: Perm, flags: impl flags::MapFlags) -> Result<Self, TryNewError<T>>
{
#[inline(never)]
#[cold]
@ -80,7 +86,7 @@ impl<T: AsRawFd> MappedFile<T> {
const NULL: *mut libc::c_void = ptr::null_mut();
let fd = file.as_raw_fd();
let slice = match unsafe {
mmap(ptr::null_mut(), len, perm.get_prot(), flags.get_flags(), fd, 0)
mmap(ptr::null_mut(), len, perm.get_prot(), flags.get_mmap_flags(), fd, 0)
} {
MAP_FAILED => return Err(TryNewError::wrap_last_error(file)),
NULL => _panic_invalid_address(),
@ -443,5 +449,21 @@ impl<T> ops::DerefMut for MappedFile<T>
self.as_slice_mut()
}
}
/// Used for anonymous mappings with `MappedFile`.
///
/// # Safety
/// The `AsRawFd` impl of this structure always returns `-1`. It should only be used with `MappedFile`, as this is an invlalid file descriptor in all other contexts.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
pub struct Anonymous;
impl AsRawFd for Anonymous
{
#[inline(always)]
fn as_raw_fd(&self) -> RawFd {
-1
}
}
//TODO: Continue copying from `utf8encode` at the //TODO (cont.) line

Loading…
Cancel
Save