use crate::{
catnip::runtime::memory::mempool::MemoryPool,
inetstack::protocols::MAX_HEADER_SIZE,
runtime::{
fail::Fail,
libdpdk::{
rte_mbuf,
rte_mempool,
},
memory::DemiBuffer,
types::{
demi_sgarray_t,
demi_sgaseg_t,
},
},
};
use ::anyhow::Error;
use ::libc::c_void;
use ::std::{
ffi::CString,
mem,
ptr::{
self,
NonNull,
},
};
pub use crate::catnip::runtime::memory::config::MemoryConfig;
#[derive(Debug)]
pub struct MemoryManager {
config: MemoryConfig,
body_pool: MemoryPool,
}
impl MemoryManager {
pub fn new(max_body_size: usize) -> Result<Self, Error> {
let config: MemoryConfig = MemoryConfig::new(Some(max_body_size), None, None);
let body_pool: MemoryPool = MemoryPool::new(
CString::new("body_pool")?,
config.get_max_body_size(),
config.get_body_pool_size(),
config.get_cache_size(),
)?;
Ok(Self { config, body_pool })
}
pub fn into_sgarray(&self, buf: DemiBuffer) -> Result<demi_sgarray_t, Fail> {
let data: *const u8 = buf.as_ptr();
let sga_seg: demi_sgaseg_t = demi_sgaseg_t {
sgaseg_buf: data as *mut c_void,
sgaseg_len: buf.len() as u32,
};
Ok(demi_sgarray_t {
sga_buf: buf.into_raw().as_ptr() as *mut c_void,
sga_numsegs: 1,
sga_segs: [sga_seg],
sga_addr: unsafe { mem::zeroed() },
})
}
pub fn alloc_body_mbuf(&self) -> Result<DemiBuffer, Fail> {
let mbuf_ptr: *mut rte_mbuf = self.body_pool.alloc_mbuf(None)?;
Ok(unsafe { DemiBuffer::from_mbuf(mbuf_ptr) })
}
pub fn alloc_sgarray(&self, size: usize) -> Result<demi_sgarray_t, Fail> {
if size == 0 {
let cause: String = format!("cannot allocate a zero-sized buffer");
error!("sgaalloc(): {}", cause);
return Err(Fail::new(libc::EINVAL, &cause));
}
if size > u16::MAX as usize {
return Err(Fail::new(libc::EINVAL, "size too large for a single demi_sgaseg_t"));
}
let buf: DemiBuffer = if size <= self.config.get_max_body_size() {
let mbuf_ptr: *mut rte_mbuf = self.body_pool.alloc_mbuf(Some(size))?;
unsafe { DemiBuffer::from_mbuf(mbuf_ptr) }
} else {
DemiBuffer::new_with_headroom(size as u16, MAX_HEADER_SIZE as u16)
};
let data: *const u8 = buf.as_ptr();
let sga_seg: demi_sgaseg_t = demi_sgaseg_t {
sgaseg_buf: data as *mut c_void,
sgaseg_len: size as u32,
};
Ok(demi_sgarray_t {
sga_buf: buf.into_raw().as_ptr() as *mut c_void,
sga_numsegs: 1,
sga_segs: [sga_seg],
sga_addr: unsafe { mem::zeroed() },
})
}
pub fn free_sgarray(&self, sga: demi_sgarray_t) -> Result<(), Fail> {
if sga.sga_numsegs != 1 {
return Err(Fail::new(libc::EINVAL, "demi_sgarray_t has invalid segment count"));
}
if sga.sga_buf == ptr::null_mut() {
return Err(Fail::new(libc::EINVAL, "demi_sgarray_t has invalid DemiBuffer token"));
}
let token: NonNull<u8> = unsafe { NonNull::new_unchecked(sga.sga_buf as *mut u8) };
let buf: DemiBuffer = unsafe { DemiBuffer::from_raw(token) };
drop(buf);
Ok(())
}
pub fn clone_sgarray(&self, sga: &demi_sgarray_t) -> Result<DemiBuffer, Fail> {
if sga.sga_numsegs != 1 {
return Err(Fail::new(libc::EINVAL, "demi_sgarray_t has invalid segment count"));
}
if sga.sga_buf == ptr::null_mut() {
return Err(Fail::new(libc::EINVAL, "demi_sgarray_t has invalid DemiBuffer token"));
}
let token: NonNull<u8> = unsafe { NonNull::new_unchecked(sga.sga_buf as *mut u8) };
let buf: DemiBuffer = unsafe { DemiBuffer::from_raw(token) };
let mut clone: DemiBuffer = buf.clone();
mem::forget(buf);
let sga_data: *const u8 = sga.sga_segs[0].sgaseg_buf as *const u8;
let sga_len: usize = sga.sga_segs[0].sgaseg_len as usize;
let clone_data: *const u8 = clone.as_ptr();
let mut clone_len: usize = clone.len();
if sga_data != clone_data || sga_len != clone_len {
if sga_data < clone_data || sga_data.addr() + sga_len > clone_data.addr() + clone_len {
return Err(Fail::new(
libc::EINVAL,
"demi_sgarray_t describes data outside backing buffer's allocated region",
));
}
let adjustment_amount: usize = sga_data.addr() - clone_data.addr();
clone.adjust(adjustment_amount)?;
clone_len -= adjustment_amount;
debug_assert_eq!(clone_len, clone.len());
let trim_amount: usize = clone_len - sga_len;
clone.trim(trim_amount)?;
}
Ok(clone)
}
pub fn body_pool(&self) -> *mut rte_mempool {
self.body_pool.into_raw()
}
}