mod fill;
pub use fill::{FillRing, WakableFillRing};
mod completion;
pub use completion::CompletionRing;
mod rx;
pub use rx::RxRing;
mod tx;
pub use tx::{TxRing, WakableTxRing};
use crate::error;
use std::sync::atomic::{AtomicU32, Ordering};
use crate::libc::rings as libc;
pub const XSK_RING_PROD_DEFAULT_NUM_DESCS: u32 = 2048;
pub const XSK_RING_CONS_DEFAULT_NUM_DESCS: u32 = 2048;
macro_rules! non_zero_and_power_of_2 {
($ctx:expr, $name:ident) => {{
let val = $ctx.$name;
if val == 0 {
return Err($crate::error::ConfigError {
name: stringify!($name),
kind: $crate::error::ConfigErrorKind::Zero,
}
.into());
} else if !val.is_power_of_two() {
return Err($crate::error::ConfigError {
name: stringify!($name),
kind: $crate::error::ConfigErrorKind::NonPowerOf2,
}
.into());
}
val
}};
}
macro_rules! zero_or_power_of_2 {
($ctx:expr, $name:ident) => {{
let val = $ctx.$name;
if val != 0 && !val.is_power_of_two() {
return Err($crate::error::ConfigError {
name: stringify!($name),
kind: $crate::error::ConfigErrorKind::NonPowerOf2,
}
.into());
}
val
}};
}
#[derive(Debug)]
pub enum Ring {
Fill,
Rx,
Completion,
Tx,
}
pub struct RingConfigBuilder {
pub rx_count: u32,
pub tx_count: u32,
pub fill_count: u32,
pub completion_count: u32,
}
impl Default for RingConfigBuilder {
fn default() -> Self {
Self {
fill_count: XSK_RING_PROD_DEFAULT_NUM_DESCS,
completion_count: XSK_RING_CONS_DEFAULT_NUM_DESCS,
rx_count: XSK_RING_CONS_DEFAULT_NUM_DESCS,
tx_count: XSK_RING_PROD_DEFAULT_NUM_DESCS,
}
}
}
impl RingConfigBuilder {
pub fn build(self) -> Result<RingConfig, error::Error> {
if self.rx_count == 0 && self.tx_count == 0 {
return Err(error::ConfigError {
name: "rx_count, tx_count",
kind: error::ConfigErrorKind::MustSendOrRecv,
}
.into());
}
let fill_count = non_zero_and_power_of_2!(self, fill_count);
let completion_count = non_zero_and_power_of_2!(self, completion_count);
let rx_count = zero_or_power_of_2!(self, rx_count);
let tx_count = zero_or_power_of_2!(self, tx_count);
Ok(RingConfig {
rx_count,
tx_count,
fill_count,
completion_count,
})
}
}
#[derive(Copy, Clone)]
pub struct RingConfig {
pub(crate) rx_count: u32,
pub(crate) tx_count: u32,
pub(crate) fill_count: u32,
pub(crate) completion_count: u32,
}
pub struct Rings {
pub fill_ring: FillRing,
pub rx_ring: Option<RxRing>,
pub completion_ring: CompletionRing,
pub tx_ring: Option<TxRing>,
}
pub struct WakableRings {
pub fill_ring: WakableFillRing,
pub rx_ring: Option<RxRing>,
pub completion_ring: CompletionRing,
pub tx_ring: Option<WakableTxRing>,
}
struct XskRing<T: 'static> {
producer: &'static AtomicU32,
consumer: &'static AtomicU32,
ring: &'static mut [T],
cached_produced: u32,
cached_consumed: u32,
count: u32,
mask: usize,
}
fn map_ring<T>(
socket: std::os::fd::RawFd,
count: u32,
offset: libc::RingPageOffsets,
offsets: &libc::xdp_ring_offset,
) -> std::io::Result<(crate::mmap::Mmap, XskRing<T>)> {
let mmap = crate::mmap::Mmap::map_ring(
offsets.desc as usize + (count as usize * std::mem::size_of::<T>()),
offset as u64,
socket,
)?;
let ring = unsafe {
let map = mmap.ptr;
let producer = AtomicU32::from_ptr(map.byte_offset(offsets.producer as _).cast());
let consumer = AtomicU32::from_ptr(map.byte_offset(offsets.consumer as _).cast());
let ring =
std::slice::from_raw_parts_mut(map.byte_offset(offsets.desc as _).cast(), count as _);
XskRing {
producer,
consumer,
count,
mask: count as usize - 1,
ring,
cached_produced: 0,
cached_consumed: 0,
}
};
Ok((mmap, ring))
}
struct XskProducer<T: 'static>(XskRing<T>);
impl<T> XskProducer<T> {
#[inline]
fn set(&mut self, i: usize, item: T) {
unsafe {
*self.0.ring.get_unchecked_mut(i & self.0.mask) = item;
}
}
#[inline]
fn reserve(&mut self, nb: u32) -> (usize, usize) {
if self.free(nb) < nb {
return (0, 0);
}
let idx = self.0.cached_produced;
self.0.cached_produced += nb;
(nb as _, idx as _)
}
#[inline]
fn free(&mut self, nb: u32) -> u32 {
let free_entries = self.0.cached_consumed - self.0.cached_produced;
if free_entries >= nb {
return free_entries;
}
self.0.cached_consumed = self.0.consumer.load(Ordering::Acquire);
self.0.cached_consumed += self.0.count;
self.0.cached_consumed - self.0.cached_produced
}
#[inline]
fn submit(&mut self, nb: u32) {
self.0.producer.fetch_add(nb, Ordering::Release);
}
}
struct XskConsumer<T: 'static>(XskRing<T>);
impl<T: Copy> XskConsumer<T> {
#[inline]
fn get(&self, i: usize) -> T {
unsafe { *self.0.ring.get_unchecked(i & self.0.mask) }
}
#[inline]
fn peek(&mut self, nb: u32) -> (usize, usize) {
let entries = self.available(nb);
if entries == 0 {
return (0, 0);
}
let consumed = self.0.cached_consumed;
self.0.cached_consumed += entries;
(entries as _, consumed as _)
}
#[inline]
fn available(&mut self, nb: u32) -> u32 {
let mut entries = self.0.cached_produced - self.0.cached_consumed;
if entries == 0 {
self.0.cached_produced = self.0.producer.load(Ordering::Acquire);
entries = self.0.cached_produced - self.0.cached_consumed;
}
std::cmp::min(entries, nb)
}
#[inline]
fn release(&mut self, nb: u32) {
self.0.consumer.fetch_add(nb, Ordering::Release);
}
}