(partial-merge from (probably) now-defunct branch `hugetlb`.)

Fortune for collect's current commit: Half blessing − 半吉
exec
Avril 2 years ago
commit b47a27c60f
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -284,6 +284,7 @@ impl RawFile
/// # Note
/// This does not *extend* the file's capacity, it is instead similar to `fs::File::set_len()`.
#[cfg_attr(feature="logging", instrument(err))]
#[inline]
pub fn allocate_size(&mut self, size: u64) -> io::Result<()>
{
use libc::{ fallocate, off_t};
@ -296,6 +297,25 @@ impl RawFile
}
}
/// Sets the size of this file.
///
/// The only real difference is that this will work on a `hugetlbfs` file, whereas `allocate_size()` will not.
/// # Note
/// This is essentially `fs::File::set_len()`.
#[cfg_attr(feature="logging", instrument(err))]
#[inline]
pub fn truncate_size(&mut self, size: u64) -> io::Result<()>
{
use libc::{ ftruncate, off_t};
if_trace!(trace!("attempting ftruncate({}, {size}) (max offset: {})", self.0.get(), off_t::MAX));
match unsafe { ftruncate(self.0.get(), if cfg!(debug_assertions) {
size.try_into().map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "Offset larger than max offset size"))?
} else { size as off_t }) } {
-1 => Err(io::Error::last_os_error()),
_ => Ok(())
}
}
/// Open a new in-memory (W+R) file with an optional name and a fixed size.
#[cfg_attr(feature="logging", instrument(err))]
pub fn open_mem(name: Option<&str>, len: usize) -> Result<Self, error::MemfileError>

@ -40,7 +40,7 @@ const CHECKED_MASK_CREATION: bool = if cfg!(feature="hugepage-checked-masks") ||
/// For most use-cases, `get_masks()` should be fine.
#[cfg_attr(feature="logging", instrument(err, skip_all, fields(path = ?path.as_ref())))]
#[inline]
pub fn get_masks_in<P>(path: P) -> eyre::Result<impl Iterator<Item=eyre::Result<Mask>> + 'static>
pub fn get_masks_in<P>(path: P) -> eyre::Result<impl Iterator<Item=eyre::Result<SizedMask>> + 'static>
where P: AsRef<Path>
{
let path = path.as_ref();
@ -69,8 +69,11 @@ where P: AsRef<Path>
.with_section(|| ok.header("Bytes were"))
.with_section(move || format!("{path:?}").header("Checked path was"))
.with_section(root_path_section.clone()))
.and_then(|mask| Ok(SizedMask{mask, size: ok.try_into().wrap_err("Size was larger than `u64`")?}))
// .map(|mask| -> eyre::Result<_> { Ok(SizedMask{mask, size: ok.try_into()?}) })
} else {
Ok(Mask::new(ok))
Ok(SizedMask{ mask: Mask::new(ok), size: ok as u64 })
}
},
Ok((None, path)) => Err(eyre!("Failed to extract bytes from path"))
@ -84,52 +87,157 @@ where P: AsRef<Path>
/// Find all `Mask`s on this system.
#[cfg_attr(feature="logging", instrument(level="trace"))]
#[inline]
pub fn get_masks() -> eyre::Result<impl Iterator<Item=eyre::Result<Mask>> + 'static>
pub fn get_masks() -> eyre::Result<impl Iterator<Item=eyre::Result<SizedMask>> + 'static>
{
get_masks_in(HUGEPAGE_SIZES_LOCATION)
}
/// A huge-page mask that can be bitwise OR'd with `HUGETLB_MASK`.
/// A huge-page mask that can be bitwise OR'd with `HUGETLB_MASK`, but retains the size of that huge-page.
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
#[repr(transparent)]
pub struct Mask(c_uint);
pub struct SizedMask
{
mask: Mask,
size: u64,
}
impl fmt::Display for Mask
impl SizedMask
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
pub const fn size(&self) -> u64
{
write!(f, "{}", self.raw())
self.size
}
#[inline]
pub const fn as_mask(&self) -> &Mask
{
&self.mask
}
}
impl fmt::LowerHex for Mask
impl std::borrow::Borrow<Mask> for SizedMask
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "0x{:x}", self.raw())
}
fn borrow(&self) -> &Mask {
&self.mask
}
}
impl fmt::UpperHex for Mask
impl std::ops::Deref for SizedMask
{
type Target = Mask;
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "0x{:X}", self.raw())
}
fn deref(&self) -> &Self::Target {
&self.mask
}
}
impl fmt::Binary for Mask
impl From<SizedMask> for Mask
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
fn from(from: SizedMask) -> Self
{
write!(f, "0b{:b}", self.raw())
from.mask
}
}
/// A huge-page mask that can be bitwise OR'd with `HUGETLB_MASK`.
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
#[repr(transparent)]
pub struct Mask(c_uint);
/// `Mask` and `SizedMask` trait impls
const _:() = {
macro_rules! mask_impls {
($name:ident) => {
impl fmt::Display for $name
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "{}", self.raw())
}
}
impl fmt::LowerHex for $name
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "0x{:x}", self.raw())
}
}
impl fmt::UpperHex for $name
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "0x{:X}", self.raw())
}
}
impl fmt::Binary for $name
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "0b{:b}", self.raw())
}
}
// Comparisons
impl PartialEq<c_uint> for $name
{
#[inline]
fn eq(&self, &other: &c_uint) -> bool
{
self.mask() == other
}
}
impl PartialEq<c_int> for $name
{
#[inline]
fn eq(&self, &other: &c_int) -> bool
{
self.raw() == other
}
}
};
}
mask_impls!(Mask);
mask_impls!(SizedMask);
impl ops::BitOr<c_uint> for Mask
{
type Output= c_uint;
#[inline]
fn bitor(self, rhs: c_uint) -> Self::Output {
self.mask() | rhs
}
}
impl ops::BitOr for Mask
{
type Output= Self;
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl ops::BitOrAssign for Mask
{
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
};
#[inline]
const fn log2_usize(x: usize) -> usize {
const BITS: usize = std::mem::size_of::<usize>() * 8usize; //XXX Is this okay to be hardcoded? I can't find CHAR_BIT in core, so...
@ -256,49 +364,6 @@ impl TryFrom<usize> for Mask
}
}
impl ops::BitOr<c_uint> for Mask
{
type Output= c_uint;
#[inline]
fn bitor(self, rhs: c_uint) -> Self::Output {
self.mask() | rhs
}
}
impl ops::BitOr for Mask
{
type Output= Self;
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
Self(self.0 | rhs.0)
}
}
impl ops::BitOrAssign for Mask
{
#[inline]
fn bitor_assign(&mut self, rhs: Self) {
self.0 |= rhs.0;
}
}
impl PartialEq<c_uint> for Mask
{
#[inline]
fn eq(&self, &other: &c_uint) -> bool
{
self.mask() == other
}
}
impl PartialEq<c_int> for Mask
{
#[inline]
fn eq(&self, &other: &c_int) -> bool
{
self.raw() == other
}
}
//TODO: add test `.memfd_create_wrapper{,_flags}()` usage, too with some `MAP_HUGE_` constants as sizes
/// Take a directory path and try to parse the hugepage size from it.
@ -539,45 +604,76 @@ mod tests
(masks > 0).then(|| drop(println!("Found {masks} masks on system"))).ok_or(eyre!("Found no masks"))
}
//#[test] TODO: XXX: AAAAA: system does not support huge-page memfd_create() allocations!?!?!?!?
// TODO: Or am I missing something here? Does it pre-allocate? What is this?
fn memfd_create_wrapper() -> eyre::Result<()>
#[test]
fn hugetlb_truncate_succeeds() -> eyre::Result<()>
{
//crate::init()?;
/// XXX: Temporary alias until we have a owning `mmap()`'d fd data-structure that impl's `From<impl IntoRawFd>`
type MappedFile = fs::File;
use std::ffi::CString;
let name = CString::new(Vec::from_iter(b"memfd_create_wrapper() test".into_iter().copied())).unwrap();
let mask = super::get_masks()?.next().ok_or(eyre!("No masks found"))?.wrap_err("Failed to extract mask")?;
eprintln!("Using mask: {mask:x} ({mask:b})");
let create = mask.memfd_create_wrapper_flags();
let buf = {
let mut buf = vec![0; name.as_bytes_with_nul().len()];
println!("Allocated {} bytes for buffer", buf.len());
let mut file: fs::File = {
let mut file = unsafe {super::RawFile::from_raw_fd( libc::memfd_create(name.as_ptr(), super::MEMFD_CREATE_FLAGS | mask.mask()) ) };//.wrap_err(eyre!("Failed to create file"))?;
println!("Created file {file:?}");
file.allocate_size(buf.len() as u64).wrap_err(eyre!("fallocate() failed"))?;
println!("Set file-size to {}", buf.len());
file
}.into();
use std::io::{Read, Write, Seek};
println!("Writing {} bytes {:?}...", name.as_bytes_with_nul().len(), name.as_bytes_with_nul());
file.write_all(name.as_bytes_with_nul()).wrap_err(eyre!("Writing failed"))?;
println!("Seeking back to 0...");
file.seek(std::io::SeekFrom::Start(0)).wrap_err(eyre!("Seeking failed"))?;
println!("Reading {} bytes...", buf.len());
file.read_exact(&mut buf[..]).wrap_err(eyre!("Reading failed"))?;
let file: MappedFile = {
let mut file = create(name.as_ptr(), super::MEMFD_CREATE_FLAGS).wrap_err(eyre!("Failed to create file"))?;
println!("Created file {file:?}, attempting ftruncate({})", mask.size());
// XXX: Note: `fallocate()` fails on hugetlb files, but `ftruncate()` does not.
file.truncate_size(mask.size()).wrap_err(eyre!("ftruncate() failed"))?;
println!("Set file-size to {}", mask.size());
file
}.into();
drop(file); //TODO: `mmap()` file to `mask.size()`.
Ok(())
}
#[test]
#[should_panic]
// TODO: `write()` syscall is not allowed here. Try to come up with an example that uses `splice()` and `send_file()`.
fn hugetlb_write_fails()
{
fn _hugetlb_write_fails() -> eyre::Result<()> {
//crate::init()?;
println!("Read {} bytes into: {:?}", buf.len(), buf);
buf
};
assert_eq!(CString::from_vec_with_nul(buf).expect("Invalid contents read"), name);
use std::ffi::CString;
let name = CString::new(Vec::from_iter(b"memfd_create_wrapper() test".into_iter().copied())).unwrap();
let mask = super::get_masks()?.next().ok_or(eyre!("No masks found"))?.wrap_err("Failed to extract mask")?;
eprintln!("Using mask: {mask:x} ({mask:b})");
let create = mask.memfd_create_wrapper_flags();
let buf = {
let mut buf = vec![0; name.as_bytes_with_nul().len()];
println!("Allocated {} bytes for buffer", buf.len());
let mut file: fs::File = {
let mut file = create(name.as_ptr(), super::MEMFD_CREATE_FLAGS)/*unsafe {super::RawFile::from_raw_fd( libc::memfd_create(name.as_ptr(), super::MEMFD_CREATE_FLAGS | mask.mask()) ) };*/.wrap_err(eyre!("Failed to create file"))?;
println!("Created file {file:?}, truncating to length of mask: {}", mask.size());
// XXX: Note: `fallocate()` fails on hugetlb files, but `ftruncate()` does not.
file.truncate_size(mask.size()).wrap_err(eyre!("ftruncate() failed"))?;
println!("Set file-size to {}", buf.len());
file
}.into();
use std::io::{Read, Write, Seek};
println!("Writing {} bytes {:?}...", name.as_bytes_with_nul().len(), name.as_bytes_with_nul());
file.write_all(name.as_bytes_with_nul()).wrap_err(eyre!("Writing failed"))?;
println!("Seeking back to 0...");
file.seek(std::io::SeekFrom::Start(0)).wrap_err(eyre!("Seeking failed"))?;
println!("Reading {} bytes...", buf.len());
file.read_exact(&mut buf[..]).wrap_err(eyre!("Reading failed"))?;
println!("Read {} bytes into: {:?}", buf.len(), buf);
buf
};
assert_eq!(CString::from_vec_with_nul(buf).expect("Invalid contents read"), name);
Ok(())
Ok(())
}
_hugetlb_write_fails().unwrap();
}
}
}

Loading…
Cancel
Save