use std::ptr::NonNull;
use std::{ffi::c_void, fs::File};
extern "C" {
fn slitter__page_size() -> i64;
fn slitter__reserve_region(size: usize, OUT_errno: *mut i32) -> Option<NonNull<c_void>>;
fn slitter__release_region(base: NonNull<c_void>, size: usize) -> i32;
fn slitter__allocate_region(base: NonNull<c_void>, size: usize) -> i32;
fn slitter__allocate_fd_region(
fd: i32,
offset: usize,
base: NonNull<c_void>,
size: usize,
) -> i32;
}
fn page_size_or_die() -> usize {
let ret = unsafe { slitter__page_size() };
if ret <= 0 {
panic!("Unable to find page_size: errno={}", -ret);
}
ret as usize
}
lazy_static::lazy_static! {
static ref PAGE_SIZE: usize = page_size_or_die();
}
#[inline]
pub fn page_size() -> usize {
*PAGE_SIZE
}
pub fn reserve_region(size: usize) -> Result<NonNull<c_void>, i32> {
let mut errno: i32 = 0;
assert!(
size > 0 && (size % page_size()) == 0,
"Bad region size={} page_size={}",
size,
page_size()
);
if let Some(base) = unsafe { slitter__reserve_region(size, &mut errno) } {
Ok(base)
} else {
Err(errno)
}
}
pub fn release_region(base: NonNull<c_void>, size: usize) -> Result<(), i32> {
if size == 0 {
return Ok(());
}
assert!(
(size % page_size()) == 0,
"Bad region size={} page_size={}",
size,
page_size()
);
let ret = unsafe { slitter__release_region(base, size) };
if ret == 0 {
Ok(())
} else {
Err(-ret)
}
}
pub fn allocate_region(base: NonNull<c_void>, size: usize) -> Result<(), i32> {
if size == 0 {
return Ok(());
}
assert!(
(size % page_size()) == 0,
"Bad region size={} page_size={}",
size,
page_size()
);
let ret = unsafe { slitter__allocate_region(base, size) };
if ret == 0 {
Ok(())
} else {
Err(-ret)
}
}
pub fn allocate_file_region(file: File, base: NonNull<c_void>, size: usize) -> Result<(), i32> {
use std::os::unix::io::FromRawFd;
use std::os::unix::io::IntoRawFd;
assert_eq!(
file.metadata().expect("has metadata").len(),
0,
"The file must be empty on entry"
);
if size == 0 {
return Ok(());
}
assert!(
(size % page_size()) == 0,
"Bad region size={} page_size={}",
size,
page_size()
);
file.set_len(size as u64).map_err(|_| 0)?;
let fd = file.into_raw_fd();
let ret = unsafe { slitter__allocate_fd_region(fd, 0, base, size) };
unsafe { File::from_raw_fd(fd) };
if ret == 0 {
Ok(())
} else {
Err(-ret)
}
}
#[test]
fn test_page_size() {
assert_ne!(page_size(), 0);
assert_eq!(page_size(), 4096);
}
#[test]
fn smoke_test() {
let region_size = 1usize << 21;
let mut base = reserve_region(3 * region_size).expect("reserve should succeed");
assert!(region_size > 3 * page_size());
release_region(base, region_size).expect("should release the bottom slop");
base = NonNull::new((base.as_ptr() as usize + region_size) as *mut c_void)
.expect("Should be non-null");
let top_slop = NonNull::new((base.as_ptr() as usize + region_size) as *mut c_void)
.expect("Should be non-null");
release_region(top_slop, region_size).expect("should release the top slop");
let bottom = base; let _guard = NonNull::new((base.as_ptr() as usize + page_size()) as *mut c_void)
.expect("Should be non-null");
let remainder = NonNull::new((base.as_ptr() as usize + 2 * page_size()) as *mut c_void)
.expect("Should be non-null");
allocate_region(bottom, page_size()).expect("should allocate bottom");
allocate_region(remainder, region_size - 2 * page_size()).expect("should allocate remainder");
release_region(base, region_size).expect("should release everything");
}