diff --git a/src/file/managed.rs b/src/file/managed.rs index 0ebeb7a..e645d7c 100644 --- a/src/file/managed.rs +++ b/src/file/managed.rs @@ -24,6 +24,38 @@ impl Clone for ManagedFD { } } +impl ManagedFD +{ + #[inline] + pub const unsafe fn take_unchecked(fd: RawFd) -> Self + { + Self(UnmanagedFD::new_unchecked(fd)) + } + + #[inline] + pub const fn take_raw(fd: RawFd) -> Self + { + assert!(fd>=0, "Invalid file descriptor"); + unsafe { + Self::take_unchecked(fd) + } + } + + #[inline] + pub const fn take(fd: UnmanagedFD) -> Self + { + Self(fd) + } + + #[inline] + pub fn detach(self) -> UnmanagedFD + { + let v = self.0.clone(); + std::mem::forget(self); + v + } +} + impl ops::Drop for ManagedFD { fn drop(&mut self) { diff --git a/src/file/memory.rs b/src/file/memory.rs index 8ab25df..2628ac1 100644 --- a/src/file/memory.rs +++ b/src/file/memory.rs @@ -4,17 +4,193 @@ //! Huge-pages can also be used for this memory. use super::*; use libc::{ + c_uint, memfd_create, MFD_CLOEXEC, MFD_HUGETLB, + + ftruncate, +}; +use std::{ + ffi::CStr, + borrow::{ + Borrow, + BorrowMut, + }, + ops, +}; +use hugetlb::{ + MapHugeFlag, + HugePage, +}; + +static UNNAMED: &'static CStr = unsafe { + CStr::from_bytes_with_nul_unchecked(b"\0") }; +const DEFAULT_FLAGS: c_uint = MFD_CLOEXEC; + +#[inline(always)] +//XXX: Is the static bound required here? +/// Create a raw, unmanaged, memory file with these flags and this name. +/// +/// # Safety +/// The reference obtained by `name` must not move as long as the `Ok()` result is alive. +pub unsafe fn create_raw(name: impl AsRef, flags: c_uint) -> io::Result +{ + UnmanagedFD::new_raw(memfd_create(name.as_ref().as_ptr(), flags)).ok_or_else(|| io::Error::last_os_error()) +} + /// A physical-memory backed file #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct MemoryFile(ManagedFD); +/// A named, physical-memory backed file +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct NamedMemoryFile(Box, MemoryFile); + +impl Borrow for NamedMemoryFile +{ + #[inline] + fn borrow(&self) -> &MemoryFile { + &self.1 + } +} +impl BorrowMut for NamedMemoryFile +{ + #[inline] + fn borrow_mut(&mut self) -> &mut MemoryFile { + &mut self.1 + } +} +impl ops::DerefMut for NamedMemoryFile +{ + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.1 + } +} +impl ops::Deref for NamedMemoryFile +{ + type Target = MemoryFile; + #[inline] + fn deref(&self) -> &Self::Target { + &self.1 + } +} + //TODO: impl `MemoryFile` (memfd_create() fd wrapper) +impl MemoryFile +{ + /// Create a new, empty, memory file with no name and no flags. + pub fn new() -> io::Result + { + let managed = unsafe { + match memfd_create(UNNAMED.as_ptr(), DEFAULT_FLAGS) { + -1 => return Err(io::Error::last_os_error()), + fd => ManagedFD::take_unchecked(fd), + } + }; + Ok(Self(managed)) + } + #[inline] + pub fn resize(&mut self, value: usize) -> io::Result<()> + { + if 0 == unsafe { ftruncate(self.as_raw_fd(), value.try_into().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?) } { + Ok(()) + } else { + Err(io::Error::last_os_error()) + } + } + + pub fn with_hugetlb(hugetlb: MapHugeFlag) -> io::Result + { + unsafe { create_raw(UNNAMED, DEFAULT_FLAGS | (hugetlb.get_mask() as c_uint)) } + .map(ManagedFD::take) + .map(Self) + } + + pub fn with_size(size: usize) -> io::Result + { + let mut this = Self(unsafe { create_raw(UNNAMED, DEFAULT_FLAGS) }.map(ManagedFD::take)?); + this.resize(size)?; + Ok(this) + } + + #[inline] + pub fn with_size_hugetlb(size: usize, hugetlb: MapHugeFlag) -> io::Result + { + let mut this = Self::with_hugetlb(hugetlb)?; + this.resize(size)?; + Ok(this) + } +} + +fn alloc_cstring(string: &str) -> std::ffi::CString +{ + #[cold] + fn _contains_nul(mut bytes: Vec) -> std::ffi::CString + { + // SAFETY: We know this will only be called if byte `0` is in `bytes` (**before** the final element) + let len = unsafe { + memchr::memchr(0, &bytes[..]).unwrap_unchecked() + }; + bytes.truncate(len); + // SAFETY: We have truncated the vector to end on the *first* instance of the `0` byte in `bytes`. + unsafe { + std::ffi::CString::from_vec_with_nul_unchecked(bytes) + } + } + let mut bytes = Vec::with_capacity(string.len()+1); + bytes.extend_from_slice(string.as_bytes()); + bytes.push(0); + match std::ffi::CString::from_vec_with_nul(bytes) { + Ok(v) => v, + Err(cn) => { + _contains_nul(cn.into_bytes()) + } + } +} + +impl NamedMemoryFile +{ + #[inline] + pub fn new(name: impl AsRef) -> io::Result + { + let name: Box = alloc_cstring(name.as_ref()).into(); + let managed = unsafe { + match memfd_create(name.as_ptr(), DEFAULT_FLAGS) { + -1 => return Err(io::Error::last_os_error()), + fd => ManagedFD::take_unchecked(fd), + } + }; + Ok(Self(name, MemoryFile(managed))) + } + + pub fn with_hugetlb(name: impl AsRef, hugetlb: MapHugeFlag) -> io::Result + { + let name: Box = alloc_cstring(name.as_ref()).into(); + let memfd = MemoryFile(unsafe { create_raw(&name, DEFAULT_FLAGS | (hugetlb.get_mask() as c_uint)) } + .map(ManagedFD::take)?); + Ok(Self(name, memfd)) + } + + pub fn with_size(name: impl AsRef, size: usize) -> io::Result + { + let name: Box = alloc_cstring(name.as_ref()).into(); + let mut this = MemoryFile(unsafe { create_raw(&name, DEFAULT_FLAGS) }.map(ManagedFD::take)?); + this.resize(size)?; + Ok(Self(name, this)) + } + + #[inline] + pub fn with_size_hugetlb(name: impl AsRef, size: usize, hugetlb: MapHugeFlag) -> io::Result + { + let mut this = Self::with_hugetlb(name, hugetlb)?; + this.resize(size)?; + Ok(this) + } +} impl AsRawFd for MemoryFile { diff --git a/src/file/unmanaged.rs b/src/file/unmanaged.rs index c94ae92..c6c7279 100644 --- a/src/file/unmanaged.rs +++ b/src/file/unmanaged.rs @@ -14,6 +14,15 @@ impl UnmanagedFD { Self(alias.as_raw_fd().into()) } + #[inline] + pub const fn new_raw(raw: RawFd) -> Option + { + match NonNegativeI32::new(raw) { + Some(x) => Some(Self(x)), + None => None, + } + } + #[inline] pub(super) const fn new_or_panic(raw: RawFd) -> Self { diff --git a/src/hugetlb.rs b/src/hugetlb.rs index 6771bd0..fb06a6c 100644 --- a/src/hugetlb.rs +++ b/src/hugetlb.rs @@ -6,6 +6,7 @@ use std::{ num::NonZeroUsize, fs, path::{Path, PathBuf}, + fmt, error, }; use libc::{ c_int, @@ -20,6 +21,32 @@ pub const HUGEPAGE_LOCATION: &'static str = "/sys/kernel/mm/hugepages/"; #[repr(transparent)] pub struct MapHugeFlag(c_int); +/// Error for when `HugePage::compute_huge()` fails. +#[derive(Debug)] +pub struct HugePageCalcErr(()); + +impl TryFrom for MapHugeFlag +{ + type Error = HugePageCalcErr; + + #[inline] + fn try_from(from: HugePage) -> Result + { + from.compute_huge().ok_or(HugePageCalcErr(())) + } +} + + +impl error::Error for HugePageCalcErr{} +impl fmt::Display for HugePageCalcErr +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + f.write_str("Invalid huge-page specification") + } +} + + impl Default for MapHugeFlag { #[inline]