diff --git a/src/file/memory.rs b/src/file/memory.rs index 480806d..59016f1 100644 --- a/src/file/memory.rs +++ b/src/file/memory.rs @@ -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`. diff --git a/src/file/memory/hugetlb.rs b/src/file/memory/hugetlb.rs deleted file mode 100644 index 6074de6..0000000 --- a/src/file/memory/hugetlb.rs +++ /dev/null @@ -1,4 +0,0 @@ -//! Huge-page interface for `MemoryFile`. -use super::*; - -//TODO: implement `memfd`'s hugetlb interface from `utf8encode` here. diff --git a/src/flags.rs b/src/flags.rs index 61a0f4f..bcb8132 100644 --- a/src/flags.rs +++ b/src/flags.rs @@ -1,7 +1,7 @@ //! All flags for controlling a `MappedFile`. +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(Flags, T); + + unsafe impl MapFlags for Chained + 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` 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`'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`. +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)] diff --git a/src/hugetlb.rs b/src/hugetlb.rs new file mode 100644 index 0000000..c22ddfb --- /dev/null +++ b/src/hugetlb.rs @@ -0,0 +1,159 @@ +//! Huge-page interface for `MappedFile` 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()`... It seems slightly wrong... +const fn log2(n: usize) -> usize +{ + /*const fn num_bits() -> usize { + mem::size_of::() * (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 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(&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 => &"", + Self::Largest => &"", + Self::Selected(_) => &"", + }; + 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 + { + todo!("TODO: copy `utf8encode`'s `compute_huge_flag()` -> pub fn compute_flag(&self) -> Option;") + } + //TODO: ^ +} + +//TODO: implement `memfd`'s hugetlb interface from `utf8encode` here. diff --git a/src/lib.rs b/src/lib.rs index 13ba068..41b0e36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 map: MappedSlice, } -//TODO: continue copying from the `TODO` line in `utf8encode/src/mmap.rs` impl MappedFile { - /// 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 MappedFile { /// /// # 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> + pub fn try_new(file: T, len: usize, perm: Perm, flags: impl flags::MapFlags) -> Result> { #[inline(never)] #[cold] @@ -80,7 +86,7 @@ impl MappedFile { 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 ops::DerefMut for MappedFile 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