#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum XdpMode {
#[default]
Copy,
ZeroCopy,
Auto,
}
impl XdpMode {
pub fn may_zero_copy(self) -> bool {
matches!(self, XdpMode::ZeroCopy | XdpMode::Auto)
}
}
#[derive(Debug, Clone)]
pub struct XdpConfig {
pub program: String,
pub filter_udp_port: u16,
pub attach_mode: String,
}
impl Default for XdpConfig {
fn default() -> Self {
Self {
program: "redirect".into(),
filter_udp_port: 0,
attach_mode: "native".into(),
}
}
}
#[derive(Debug, Clone)]
pub struct UmemConfig {
pub frame_size: usize,
pub frame_count: usize,
}
impl Default for UmemConfig {
fn default() -> Self {
Self {
frame_size: 2048,
frame_count: 4096,
}
}
}
#[derive(Debug, Clone)]
pub struct AfXdpConfig {
pub interface: String,
pub queue_id: u32,
pub mode: XdpMode,
pub poll_budget: usize,
pub xdp: XdpConfig,
pub umem: UmemConfig,
}
impl Default for AfXdpConfig {
fn default() -> Self {
Self {
interface: "eth0".into(),
queue_id: 0,
mode: XdpMode::Copy,
poll_budget: 64,
xdp: XdpConfig::default(),
umem: UmemConfig::default(),
}
}
}
#[derive(Debug, Clone)]
pub struct SimNetConfig {
pub payload_size: usize,
pub batch_size: usize,
pub idle_rate: f32,
pub drop_rate: f32,
pub udp_dst_port: u16,
}
impl Default for SimNetConfig {
fn default() -> Self {
Self {
payload_size: 8,
batch_size: 32,
idle_rate: 0.0,
drop_rate: 0.0,
udp_dst_port: 9000,
}
}
}
impl SimNetConfig {
pub fn validate(&self) -> crate::core::Result<()> {
if self.batch_size == 0 {
return Err(crate::core::Error::config("batch_size must be > 0"));
}
if !(0.0..1.0).contains(&self.idle_rate) {
return Err(crate::core::Error::config(
"idle_rate must be in [0.0, 1.0)",
));
}
if !(0.0..1.0).contains(&self.drop_rate) {
return Err(crate::core::Error::config(
"drop_rate must be in [0.0, 1.0)",
));
}
Ok(())
}
}
impl UmemConfig {
pub fn validate(&self) -> crate::core::Result<()> {
if self.frame_size == 0 || !self.frame_size.is_power_of_two() {
return Err(crate::core::Error::config(
"umem frame_size must be a non-zero power of two",
));
}
if self.frame_count == 0 || !self.frame_count.is_power_of_two() {
return Err(crate::core::Error::config(
"umem frame_count must be a non-zero power of two",
));
}
Ok(())
}
}
impl AfXdpConfig {
pub fn validate(&self) -> crate::core::Result<()> {
if self.interface.is_empty() {
return Err(crate::core::Error::config("interface must not be empty"));
}
if self.poll_budget == 0 {
return Err(crate::core::Error::config("poll_budget must be > 0"));
}
self.umem.validate()?;
Ok(())
}
}
impl DpdkConfig {
pub fn validate(&self) -> crate::core::Result<()> {
if self.pci_addr.is_empty() {
return Err(crate::core::Error::config("pci_addr must not be empty"));
}
if self.burst_size == 0 {
return Err(crate::core::Error::config("burst_size must be > 0"));
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct DpdkConfig {
pub pci_addr: String,
pub core_mask: String,
pub hugepage_count: usize,
pub rx_queue_id: u16,
pub burst_size: u16,
}
impl Default for DpdkConfig {
fn default() -> Self {
Self {
pci_addr: String::new(),
core_mask: "0x1".into(),
hugepage_count: 512,
rx_queue_id: 0,
burst_size: 32,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn xdp_mode_and_defaults() {
assert!(!XdpMode::Copy.may_zero_copy());
assert!(XdpMode::ZeroCopy.may_zero_copy());
assert!(XdpMode::Auto.may_zero_copy());
assert_eq!(XdpConfig::default().program, "redirect");
assert_eq!(UmemConfig::default().frame_size, 2048);
assert_eq!(AfXdpConfig::default().interface, "eth0");
assert_eq!(SimNetConfig::default().udp_dst_port, 9000);
}
#[test]
fn validates_umem_and_af_xdp() {
UmemConfig::default().validate().unwrap();
assert!(
UmemConfig {
frame_size: 3,
frame_count: 4
}
.validate()
.is_err()
);
assert!(
UmemConfig {
frame_size: 2048,
frame_count: 0
}
.validate()
.is_err()
);
AfXdpConfig::default().validate().unwrap();
assert!(
AfXdpConfig {
interface: String::new(),
..AfXdpConfig::default()
}
.validate()
.is_err()
);
assert!(
AfXdpConfig {
poll_budget: 0,
..AfXdpConfig::default()
}
.validate()
.is_err()
);
}
#[test]
fn validates_sim_and_dpdk() {
SimNetConfig::default().validate().unwrap();
assert!(
SimNetConfig {
batch_size: 0,
..SimNetConfig::default()
}
.validate()
.is_err()
);
assert!(
SimNetConfig {
idle_rate: 1.0,
..SimNetConfig::default()
}
.validate()
.is_err()
);
assert!(
SimNetConfig {
drop_rate: -0.1,
..SimNetConfig::default()
}
.validate()
.is_err()
);
assert!(DpdkConfig::default().validate().is_err());
DpdkConfig {
pci_addr: "0000:00:00.0".into(),
..DpdkConfig::default()
}
.validate()
.unwrap();
assert!(
DpdkConfig {
pci_addr: "x".into(),
burst_size: 0,
..DpdkConfig::default()
}
.validate()
.is_err()
);
}
}