use crate::{
Packet,
error::{ConfigError, Error},
libc::{self, InternalXdpFlags, xdp::xdp_desc},
};
use std::collections::VecDeque;
#[derive(Copy, Clone)]
pub enum FrameSize {
TwoK,
FourK,
}
impl TryFrom<FrameSize> for u32 {
type Error = ConfigError;
fn try_from(value: FrameSize) -> Result<Self, Self::Error> {
let ret = match value {
FrameSize::TwoK => 2048,
FrameSize::FourK => 4096,
};
Ok(ret)
}
}
pub struct Umem {
pub(crate) mmap: crate::mmap::Mmap,
available: VecDeque<u64>,
pub(crate) frame_size: usize,
frame_mask: u64,
pub(crate) head_room: usize,
pub(crate) options: InternalXdpFlags::Enum,
}
impl Umem {
pub fn map(cfg: UmemCfg) -> std::io::Result<Self> {
let mmap = crate::mmap::Mmap::map_umem(cfg.frame_count as usize * cfg.frame_size as usize)?;
let mut available = VecDeque::with_capacity(cfg.frame_count as _);
let frame_size = cfg.frame_size as u64;
available.extend((0..cfg.frame_count as u64).map(|i| i * frame_size));
Ok(Self {
mmap,
available,
frame_size: cfg.frame_size as usize - libc::xdp::XDP_PACKET_HEADROOM as usize,
frame_mask: cfg.frame_mask,
head_room: cfg.head_room as _,
options: cfg.options,
})
}
#[inline]
pub fn capacity(&self) -> usize {
self.available.capacity()
}
#[inline]
pub fn outstanding(&self) -> usize {
self.available.capacity() - self.available.len()
}
#[inline]
pub fn allocatable(&self) -> usize {
self.available.len()
}
#[inline]
pub(crate) unsafe fn packet(&self, desc: xdp_desc) -> Packet {
unsafe {
let data = self
.mmap
.ptr
.byte_offset((desc.addr - self.head_room as u64) as _);
Packet {
data,
capacity: self.frame_size,
head: self.head_room,
tail: self.head_room + desc.len as usize,
base: self.mmap.ptr,
options: desc.options | self.options,
}
}
}
#[inline]
pub unsafe fn alloc(&mut self) -> Option<Packet> {
let addr = self.available.pop_front()?;
unsafe {
let data = self
.mmap
.ptr
.byte_offset((addr + libc::xdp::XDP_PACKET_HEADROOM) as _);
Some(Packet {
data,
capacity: self.frame_size,
head: self.head_room,
tail: self.head_room,
base: self.mmap.ptr,
options: self.options,
})
}
}
#[inline]
pub(crate) fn free_addr(&mut self, address: u64) {
self.available.push_front(address & self.frame_mask);
}
#[inline]
pub fn free_packet(&mut self, packet: Packet) {
debug_assert_eq!(
packet.base, self.mmap.ptr,
"the packet was not allocated from this Umem"
);
self.free_addr(
unsafe {
packet
.data
.byte_offset(packet.head as _)
.offset_from(packet.base) as _
},
);
}
#[inline]
pub(crate) fn free_get_timestamp(&mut self, address: u64) -> u64 {
use libc::xdp::xsk_tx_metadata;
let align_offset = address % self.frame_size as u64;
let timestamp = if align_offset >= std::mem::size_of::<xsk_tx_metadata>() as u64 {
unsafe {
let tx_meta = std::ptr::read_unaligned(
self.mmap
.ptr
.byte_offset((address - std::mem::size_of::<xsk_tx_metadata>() as u64) as _)
.cast::<xsk_tx_metadata>(),
);
tx_meta.offload.completion
}
} else {
0
};
self.free_addr(address);
timestamp
}
#[inline]
pub(crate) fn available(&mut self) -> &mut VecDeque<u64> {
&mut self.available
}
}
pub struct UmemCfgBuilder {
pub frame_size: FrameSize,
pub head_room: u32,
pub frame_count: u32,
pub tx_checksum: bool,
pub tx_timestamp: bool,
#[cfg(debug_assertions)]
pub software_checksum: bool,
}
impl Default for UmemCfgBuilder {
fn default() -> Self {
Self {
frame_size: FrameSize::FourK, head_room: 0,
frame_count: 8 * 1024,
tx_checksum: false,
tx_timestamp: false,
#[cfg(debug_assertions)]
software_checksum: false,
}
}
}
impl UmemCfgBuilder {
pub fn new(tx_flags: crate::nic::XdpTxMetadata) -> Self {
Self {
tx_checksum: tx_flags.checksum(),
tx_timestamp: tx_flags.timestamp(),
..Default::default()
}
}
pub fn build(self) -> Result<UmemCfg, Error> {
let frame_size = self.frame_size.try_into()?;
let frame_mask = !(frame_size as u64 - 1);
let head_room = within_range!(
self,
head_room,
0..(frame_size - libc::xdp::XDP_PACKET_HEADROOM as u32) as _
);
let frame_count = within_range!(self, frame_count, 1..u32::MAX as _);
let total_size = frame_count as usize * frame_size as usize;
if total_size > isize::MAX as usize {
return Err(Error::Cfg(crate::error::ConfigError {
name: "frame_count * frame_size",
kind: crate::error::ConfigErrorKind::OutOfRange {
size: total_size,
range: frame_size as usize..isize::MAX as usize,
},
}));
}
let mut options = 0;
if self.tx_checksum {
options |= InternalXdpFlags::SUPPORTS_CHECKSUM_OFFLOAD;
}
if self.tx_timestamp {
options |= InternalXdpFlags::SUPPORTS_TIMESTAMP;
}
#[cfg(debug_assertions)]
if self.software_checksum {
options |= InternalXdpFlags::USE_SOFTWARE_OFFLOAD;
}
Ok(UmemCfg {
frame_size,
frame_mask,
frame_count,
head_room,
options,
})
}
}
#[derive(Copy, Clone)]
pub struct UmemCfg {
frame_size: u32,
frame_mask: u64,
frame_count: u32,
head_room: u32,
options: InternalXdpFlags::Enum,
}