#![allow(unsafe_code)]
use crate::descriptor::XdpDesc;
use crate::error::{Result, RingError};
use std::sync::atomic::{AtomicU32, Ordering};
pub const XDP_RING_NEED_WAKEUP: u32 = 0x1;
pub const MAX_SIMULATED_CAPACITY: u32 = 1 << 20;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RingType {
Fill,
Rx,
Tx,
Completion,
}
#[derive(Debug, Clone, Copy)]
pub struct RingOffsets {
pub producer: u64,
pub consumer: u64,
pub desc: u64,
pub flags: u64,
pub len: u64,
}
pub struct XskRing {
ring_type: RingType,
capacity: u32,
mask: u32,
storage: RingStorage,
}
enum RingStorage {
Simulated {
producer_idx: AtomicU32,
consumer_idx: AtomicU32,
descriptors: Vec<XdpDesc>,
},
Kernel(KernelRing),
}
struct KernelRing {
mmap_base: *mut u8,
mmap_len: usize,
producer: *mut AtomicU32,
consumer: *mut AtomicU32,
descs: *mut XdpDesc,
flags: Option<*const AtomicU32>,
}
impl Drop for KernelRing {
fn drop(&mut self) {
unsafe {
libc::munmap(self.mmap_base as *mut libc::c_void, self.mmap_len);
}
}
}
impl std::fmt::Debug for XskRing {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("XskRing")
.field("ring_type", &self.ring_type)
.field("capacity", &self.capacity)
.field("producer_idx", &self.producer_index())
.field("consumer_idx", &self.consumer_index())
.field("kernel_mode", &self.is_kernel_mode())
.finish()
}
}
unsafe impl Send for XskRing {}
unsafe impl Sync for XskRing {}
impl XskRing {
pub fn new(ring_type: RingType, capacity: u32) -> Result<Self> {
if capacity == 0 {
return Err(RingError::InvalidOffsets(
"ring capacity 必须 > 0".to_string(),
)
.into());
}
let actual_capacity = capacity
.max(16)
.checked_next_power_of_two()
.ok_or_else(|| {
RingError::InvalidOffsets(format!(
"ring capacity {capacity} 超出最大可表示容量 2^31"
))
})?;
if actual_capacity > MAX_SIMULATED_CAPACITY {
return Err(RingError::InvalidOffsets(format!(
"ring capacity {actual_capacity} 超出模拟模式上限 {MAX_SIMULATED_CAPACITY}"
))
.into());
}
let mask = actual_capacity - 1;
Ok(Self {
ring_type,
capacity: actual_capacity,
mask,
storage: RingStorage::Simulated {
producer_idx: AtomicU32::new(0),
consumer_idx: AtomicU32::new(0),
descriptors: vec![XdpDesc::zero(); actual_capacity as usize],
},
})
}
pub unsafe fn with_kernel_ring(
ring_type: RingType,
mmap_base: *mut u8,
offsets: RingOffsets,
) -> Result<Self> {
if mmap_base.is_null() {
return Err(RingError::InvalidOffsets("mmap_base 为空指针".to_string()).into());
}
let desc_area = offsets.len.checked_sub(offsets.desc).ok_or_else(|| {
RingError::InvalidOffsets(format!(
"desc 偏移 {} 超出 ring 长度 {}",
offsets.desc, offsets.len
))
})?;
let desc_size = std::mem::size_of::<XdpDesc>() as u64;
let capacity_u64 = desc_area / desc_size;
if capacity_u64 == 0 || capacity_u64 > u64::from(u32::MAX) {
return Err(RingError::InvalidOffsets(format!(
"描述符容量 {capacity_u64} 非法"
))
.into());
}
let capacity = capacity_u64 as u32;
if !capacity.is_power_of_two() {
return Err(RingError::InvalidOffsets(format!(
"容量 {capacity} 不是 2 的幂"
))
.into());
}
let word = std::mem::size_of::<u32>() as u64;
for (name, off) in [
("producer", offsets.producer),
("consumer", offsets.consumer),
] {
if off >= offsets.len || offsets.len - off < word {
return Err(RingError::InvalidOffsets(format!(
"{name} 偏移 {off} 越界(len={})",
offsets.len
))
.into());
}
}
if offsets.flags > 0 && (offsets.flags >= offsets.len || offsets.len - offsets.flags < word)
{
return Err(RingError::InvalidOffsets(format!(
"flags 偏移 {} 越界(len={})",
offsets.flags, offsets.len
))
.into());
}
let kernel = KernelRing {
mmap_base,
mmap_len: offsets.len as usize,
producer: unsafe { mmap_base.add(offsets.producer as usize) as *mut AtomicU32 },
consumer: unsafe { mmap_base.add(offsets.consumer as usize) as *mut AtomicU32 },
descs: unsafe { mmap_base.add(offsets.desc as usize) as *mut XdpDesc },
flags: if offsets.flags > 0 {
Some(unsafe { mmap_base.add(offsets.flags as usize) as *const AtomicU32 })
} else {
None
},
};
Ok(Self {
ring_type,
capacity,
mask: capacity - 1,
storage: RingStorage::Kernel(kernel),
})
}
#[inline]
fn ring_slot(base: u32, offset: u32, mask: u32) -> u32 {
base.wrapping_add(offset) & mask
}
#[inline]
fn producer_atomic(&self) -> &AtomicU32 {
match &self.storage {
RingStorage::Simulated { producer_idx, .. } => producer_idx,
RingStorage::Kernel(k) => unsafe { &*k.producer },
}
}
#[inline]
fn consumer_atomic(&self) -> &AtomicU32 {
match &self.storage {
RingStorage::Simulated { consumer_idx, .. } => consumer_idx,
RingStorage::Kernel(k) => unsafe { &*k.consumer },
}
}
#[inline]
fn read_desc(&self, slot: u32) -> XdpDesc {
match &self.storage {
RingStorage::Simulated { descriptors, .. } => descriptors[slot as usize],
RingStorage::Kernel(k) => unsafe { k.descs.add(slot as usize).read() },
}
}
#[inline]
fn write_desc(&mut self, slot: u32, desc: XdpDesc) {
match &mut self.storage {
RingStorage::Simulated { descriptors, .. } => descriptors[slot as usize] = desc,
RingStorage::Kernel(k) => unsafe { k.descs.add(slot as usize).write(desc) },
}
}
#[inline]
pub fn available_space(&self) -> u32 {
let producer = self.producer_atomic().load(Ordering::Relaxed);
let consumer = self.consumer_atomic().load(Ordering::Acquire);
self.capacity - (producer.wrapping_sub(consumer))
}
#[inline]
pub fn available_data(&self) -> u32 {
let producer = self.producer_atomic().load(Ordering::Acquire);
let consumer = self.consumer_atomic().load(Ordering::Relaxed);
producer.wrapping_sub(consumer)
}
pub fn enqueue_batch(&mut self, descriptors: &[XdpDesc]) -> Result<u32> {
if descriptors.is_empty() {
return Ok(0);
}
let available = self.available_space();
let to_enqueue = descriptors.len() as u32;
if to_enqueue > available {
return Err(RingError::BatchSizeExceeded {
requested: to_enqueue,
maximum: available,
}
.into());
}
let start_idx = self.producer_atomic().load(Ordering::Relaxed);
for (i, desc) in descriptors.iter().enumerate() {
let slot = Self::ring_slot(start_idx, i as u32, self.mask);
self.write_desc(slot, *desc);
}
self.producer_atomic()
.store(start_idx.wrapping_add(to_enqueue), Ordering::Release);
Ok(to_enqueue)
}
pub fn dequeue_batch(&mut self, max_batch: u32) -> Result<Vec<XdpDesc>> {
let available = self.available_data();
let to_dequeue = available.min(max_batch);
if to_dequeue == 0 {
return Ok(Vec::new());
}
let consumer = self.consumer_atomic().load(Ordering::Relaxed);
let mut result = Vec::with_capacity(to_dequeue as usize);
for i in 0..to_dequeue {
let slot = Self::ring_slot(consumer, i, self.mask);
result.push(self.read_desc(slot));
}
self.consumer_atomic()
.store(consumer.wrapping_add(to_dequeue), Ordering::Release);
Ok(result)
}
pub fn dequeue_batch_to(&mut self, buffer: &mut [XdpDesc]) -> Result<u32> {
let available = self.available_data();
let max = buffer.len() as u32;
let to_dequeue = available.min(max);
if to_dequeue == 0 {
return Ok(0);
}
let consumer = self.consumer_atomic().load(Ordering::Relaxed);
for (i, slot) in buffer.iter_mut().enumerate().take(to_dequeue as usize) {
let idx = Self::ring_slot(consumer, i as u32, self.mask);
*slot = self.read_desc(idx);
}
self.consumer_atomic()
.store(consumer.wrapping_add(to_dequeue), Ordering::Release);
Ok(to_dequeue)
}
pub fn enqueue_batch_from(&mut self, descriptors: &[XdpDesc]) -> Result<u32> {
self.enqueue_batch(descriptors)
}
pub fn revert_consumer(&mut self, count: u32) {
debug_assert!(
count <= self.consumer_index(),
"回退计数 {count} 超过当前消费者索引 {}",
self.consumer_index()
);
self.consumer_atomic().fetch_sub(count, Ordering::Release);
}
pub fn revert_producer(&mut self, count: u32) {
debug_assert!(
count <= self.producer_index(),
"回退计数 {count} 超过当前生产者索引 {}",
self.producer_index()
);
self.producer_atomic().fetch_sub(count, Ordering::Release);
}
#[inline]
pub fn capacity(&self) -> u32 {
self.capacity
}
#[inline]
pub fn ring_type(&self) -> RingType {
self.ring_type
}
#[inline]
pub fn producer_index(&self) -> u32 {
self.producer_atomic().load(Ordering::Relaxed)
}
#[inline]
pub fn consumer_index(&self) -> u32 {
self.consumer_atomic().load(Ordering::Relaxed)
}
#[inline]
pub fn is_kernel_mode(&self) -> bool {
matches!(self.storage, RingStorage::Kernel(_))
}
#[inline]
pub fn need_wakeup(&self) -> bool {
match &self.storage {
RingStorage::Kernel(k) => match k.flags {
Some(ptr) => unsafe {
let flags = (*ptr).load(Ordering::Acquire);
(flags & XDP_RING_NEED_WAKEUP) != 0
},
None => false,
},
RingStorage::Simulated { .. } => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_desc(addr: u64, len: u32) -> XdpDesc {
XdpDesc {
addr,
len,
options: 0,
}
}
#[test]
fn test_ring_creation() {
let ring = XskRing::new(RingType::Fill, 64).unwrap();
assert_eq!(ring.capacity(), 64);
assert_eq!(ring.ring_type(), RingType::Fill);
assert_eq!(ring.available_data(), 0);
assert_eq!(ring.available_space(), 64);
assert!(!ring.is_kernel_mode());
}
#[test]
fn test_ring_capacity_power_of_two() {
let ring = XskRing::new(RingType::Rx, 10).unwrap();
assert_eq!(ring.capacity(), 16);
}
#[test]
fn test_ring_enqueue_dequeue() {
let mut ring = XskRing::new(RingType::Tx, 16).unwrap();
let descs = [
make_desc(0, 64),
make_desc(4096, 128),
make_desc(8192, 256),
make_desc(12288, 512),
make_desc(16384, 1024),
];
let enqueued = ring.enqueue_batch(&descs).unwrap();
assert_eq!(enqueued, 5);
assert_eq!(ring.available_data(), 5);
assert_eq!(ring.available_space(), 11);
let dequeued = ring.dequeue_batch(3).unwrap();
assert_eq!(dequeued.len(), 3);
assert_eq!(dequeued[0].addr, 0);
assert_eq!(dequeued[1].addr, 4096);
assert_eq!(dequeued[2].addr, 8192);
assert_eq!(ring.available_data(), 2);
let dequeued = ring.dequeue_batch(10).unwrap();
assert_eq!(dequeued.len(), 2);
assert_eq!(ring.available_data(), 0);
}
#[test]
fn test_ring_full() {
let mut ring = XskRing::new(RingType::Completion, 16).unwrap();
let descs: Vec<XdpDesc> = (0..17).map(|i| make_desc(i * 4096, 0)).collect();
let result = ring.enqueue_batch(&descs);
assert!(result.is_err());
}
#[test]
fn test_ring_wrapping() {
let mut ring = XskRing::new(RingType::Fill, 16).unwrap();
let descs: Vec<XdpDesc> = (0..16).map(|i| make_desc(i * 4096, 0)).collect();
ring.enqueue_batch(&descs).unwrap();
ring.dequeue_batch(16).unwrap();
assert_eq!(ring.available_data(), 0);
assert_eq!(ring.available_space(), 16);
ring.enqueue_batch(&[make_desc(65536, 0), make_desc(69632, 0)]).unwrap();
assert_eq!(ring.available_data(), 2);
}
#[test]
fn test_ring_dequeue_to_buffer() {
let mut ring = XskRing::new(RingType::Rx, 8).unwrap();
let descs = [make_desc(1024, 100), make_desc(2048, 200), make_desc(3072, 300), make_desc(4096, 400)];
ring.enqueue_batch(&descs).unwrap();
let mut buffer = [XdpDesc::zero(); 3];
let count = ring.dequeue_batch_to(&mut buffer).unwrap();
assert_eq!(count, 3);
assert_eq!(buffer[0].addr, 1024);
assert_eq!(buffer[1].addr, 2048);
assert_eq!(buffer[2].addr, 3072);
}
#[test]
fn test_ring_revert() {
let mut ring = XskRing::new(RingType::Tx, 16).unwrap();
ring.enqueue_batch(&[make_desc(0, 0), make_desc(4096, 0), make_desc(8192, 0)])
.unwrap();
assert_eq!(ring.available_data(), 3);
ring.revert_producer(2);
assert_eq!(ring.available_data(), 1);
ring.dequeue_batch(1).unwrap();
assert_eq!(ring.available_data(), 0);
ring.revert_consumer(1);
assert_eq!(ring.available_data(), 1);
}
#[test]
fn test_ring_empty_dequeue() {
let mut ring = XskRing::new(RingType::Rx, 16).unwrap();
let result = ring.dequeue_batch(10).unwrap();
assert!(result.is_empty());
}
#[test]
fn test_ring_zero_length() {
let mut ring = XskRing::new(RingType::Fill, 16).unwrap();
let result = ring.enqueue_batch(&[]).unwrap();
assert_eq!(result, 0);
}
#[test]
fn test_ring_capacity_minimum_16() {
let ring = XskRing::new(RingType::Rx, 1).unwrap();
assert_eq!(ring.capacity(), 16);
assert!(ring.capacity().is_power_of_two());
}
#[test]
fn test_ring_capacity_exact_power_of_two() {
for cap in [16, 32, 64, 128, 256, 512, 1024, 2048, 4096] {
let ring = XskRing::new(RingType::Tx, cap).unwrap();
assert_eq!(ring.capacity(), cap);
assert!(ring.capacity().is_power_of_two());
}
}
#[test]
fn test_ring_capacity_rounds_up() {
let test_cases = vec![
(17, 32),
(33, 64),
(100, 128),
(255, 256),
(1000, 1024),
];
for (input, expected) in test_cases {
let ring = XskRing::new(RingType::Completion, input).unwrap();
assert_eq!(
ring.capacity(),
expected,
"Input {} should round up to {}",
input,
expected
);
assert!(ring.capacity().is_power_of_two());
}
}
#[test]
fn test_ring_new_fail_closed_on_invalid_capacity() {
assert!(XskRing::new(RingType::Rx, 0).is_err());
assert!(XskRing::new(RingType::Rx, u32::MAX).is_err());
assert!(XskRing::new(RingType::Rx, (1u32 << 31) + 1).is_err());
assert!(XskRing::new(RingType::Rx, 1u32 << 31).is_err());
assert!(XskRing::new(RingType::Rx, MAX_SIMULATED_CAPACITY + 1).is_err());
assert!(XskRing::new(RingType::Rx, MAX_SIMULATED_CAPACITY).is_ok());
}
#[test]
fn test_ring_empty_boundary() {
let mut ring = XskRing::new(RingType::Rx, 32).unwrap();
assert_eq!(ring.available_data(), 0);
assert_eq!(ring.available_space(), 32);
let result = ring.dequeue_batch(10).unwrap();
assert!(result.is_empty());
assert_eq!(ring.consumer_index(), 0);
}
#[test]
fn test_ring_full_boundary() {
let mut ring = XskRing::new(RingType::Tx, 16).unwrap();
let descs: Vec<XdpDesc> = (0..16).map(|i| make_desc(i * 4096, 64)).collect();
let enqueued = ring.enqueue_batch(&descs).unwrap();
assert_eq!(enqueued, 16);
assert_eq!(ring.available_data(), 16);
assert_eq!(ring.available_space(), 0);
let overflow = ring.enqueue_batch(&[make_desc(65536, 0)]);
assert!(overflow.is_err());
}
#[test]
fn test_ring_full_then_partial_consume() {
let mut ring = XskRing::new(RingType::Rx, 16).unwrap();
let descs: Vec<XdpDesc> = (0..16).map(|i| make_desc(i * 4096, 64)).collect();
ring.enqueue_batch(&descs).unwrap();
assert_eq!(ring.available_space(), 0);
let consumed = ring.dequeue_batch(5).unwrap();
assert_eq!(consumed.len(), 5);
assert_eq!(ring.available_space(), 5);
assert_eq!(ring.available_data(), 11);
}
#[test]
fn test_ring_index_wrapping_producer() {
let mut ring = XskRing::new(RingType::Fill, 16).unwrap();
for i in 0..16 {
ring.enqueue_batch(&[make_desc(i * 4096, 0)]).unwrap();
}
assert_eq!(ring.producer_index(), 16);
ring.dequeue_batch(8).unwrap();
assert_eq!(ring.consumer_index(), 8);
for i in 16..24 {
ring.enqueue_batch(&[make_desc(i * 4096, 0)]).unwrap();
}
assert_eq!(ring.producer_index(), 24);
assert_eq!(ring.available_data(), 16);
}
#[test]
fn test_ring_index_wrapping_consumer() {
let mut ring = XskRing::new(RingType::Rx, 16).unwrap();
for _ in 0..3 {
let descs: Vec<XdpDesc> = (0..16).map(|i| make_desc(i * 4096, 0)).collect();
ring.enqueue_batch(&descs).unwrap();
let consumed = ring.dequeue_batch(16).unwrap();
assert_eq!(consumed.len(), 16);
}
assert_eq!(ring.producer_index(), 48);
assert_eq!(ring.consumer_index(), 48);
assert_eq!(ring.available_data(), 0);
}
#[test]
fn test_ring_batch_exceeds_available_space() {
let mut ring = XskRing::new(RingType::Tx, 32).unwrap();
let descs: Vec<XdpDesc> = (0..10).map(|i| make_desc(i * 4096, 0)).collect();
ring.enqueue_batch(&descs).unwrap();
assert_eq!(ring.available_space(), 22);
let too_many: Vec<XdpDesc> = (0..30).map(|i| make_desc(i * 4096, 0)).collect();
let result = ring.enqueue_batch(&too_many);
assert!(result.is_err());
}
#[test]
fn test_ring_dequeue_batch_to_empty_buffer() {
let mut ring = XskRing::new(RingType::Rx, 16).unwrap();
let descs = [make_desc(1024, 100)];
ring.enqueue_batch(&descs).unwrap();
let mut buffer = [];
let count = ring.dequeue_batch_to(&mut buffer).unwrap();
assert_eq!(count, 0);
}
#[test]
fn test_ring_enqueue_batch_from_alias() {
let mut ring = XskRing::new(RingType::Fill, 16).unwrap();
let descs = [make_desc(4096, 128), make_desc(8192, 256)];
let count = ring.enqueue_batch_from(&descs).unwrap();
assert_eq!(count, 2);
assert_eq!(ring.available_data(), 2);
}
#[test]
fn test_ring_revert_producer_zero() {
let mut ring = XskRing::new(RingType::Tx, 16).unwrap();
ring.revert_producer(0);
assert_eq!(ring.producer_index(), 0);
assert_eq!(ring.available_data(), 0);
}
#[test]
fn test_ring_revert_consumer_zero() {
let mut ring = XskRing::new(RingType::Rx, 16).unwrap();
ring.revert_consumer(0);
assert_eq!(ring.consumer_index(), 0);
assert_eq!(ring.available_data(), 0);
}
#[test]
fn test_ring_types_all_work() {
let types = vec![
RingType::Fill,
RingType::Rx,
RingType::Tx,
RingType::Completion,
];
for ring_type in types {
let mut ring = XskRing::new(ring_type, 16).unwrap();
assert_eq!(ring.ring_type(), ring_type);
let desc = make_desc(0, 0);
ring.enqueue_batch(&[desc]).unwrap();
let result = ring.dequeue_batch(1).unwrap();
assert_eq!(result.len(), 1);
}
}
#[test]
fn test_ring_fifo_order() {
let mut ring = XskRing::new(RingType::Rx, 32).unwrap();
let descs: Vec<XdpDesc> = (0..10u32).map(|i| make_desc(i as u64 * 4096, i * 100)).collect();
ring.enqueue_batch(&descs).unwrap();
let dequeued = ring.dequeue_batch(10).unwrap();
for (i, desc) in dequeued.iter().enumerate() {
assert_eq!(desc.addr, i as u64 * 4096);
assert_eq!(desc.len, i as u32 * 100);
}
}
#[test]
fn test_need_wakeup_simulated_mode() {
let ring = XskRing::new(RingType::Tx, 64).unwrap();
assert!(!ring.need_wakeup());
let fill_ring = XskRing::new(RingType::Fill, 64).unwrap();
assert!(!fill_ring.need_wakeup());
}
#[test]
fn test_xdp_ring_need_wakeup_constant() {
assert_eq!(XDP_RING_NEED_WAKEUP, 0x1);
}
#[test]
fn test_ring_slot_wrapping_pure_logic() {
assert_eq!(XskRing::ring_slot(0, 0, 15), 0);
assert_eq!(XskRing::ring_slot(u32::MAX, 1, 15), 0); assert_eq!(XskRing::ring_slot(u32::MAX - 1, 3, 15), 1); assert_eq!(XskRing::ring_slot(u32::MAX - 2, 4, 15), 1); assert_eq!(XskRing::ring_slot(5, 3, 15), 8);
for base in [0u32, 1, 15, 16, u32::MAX - 7, u32::MAX] {
for off in [0u32, 1, 7, 16, 100] {
assert!(XskRing::ring_slot(base, off, 15) < 16);
}
}
}
#[test]
fn test_simulated_ring_index_wrap_at_u32_max() {
let mut ring = XskRing::new(RingType::Tx, 16).unwrap();
if let RingStorage::Simulated {
producer_idx,
consumer_idx,
..
} = &ring.storage
{
producer_idx.store(u32::MAX - 2, Ordering::SeqCst);
consumer_idx.store(u32::MAX - 2, Ordering::SeqCst);
} else {
panic!("new() 必须是模拟模式");
}
let descs: Vec<XdpDesc> = (0..4u32).map(|i| make_desc(u64::from(i) * 4096, i)).collect();
assert_eq!(ring.enqueue_batch(&descs).unwrap(), 4);
assert_eq!(ring.available_data(), 4);
assert_eq!(ring.producer_index(), 1);
let out = ring.dequeue_batch(4).unwrap();
assert_eq!(out.len(), 4);
for (i, d) in out.iter().enumerate() {
assert_eq!(d.addr, (i as u64) * 4096);
}
assert_eq!(ring.consumer_index(), 1);
assert_eq!(ring.available_data(), 0);
assert_eq!(ring.available_space(), 16);
}
fn fake_kernel_ring_layout(ring_size: u32, flags_value: u32) -> (*mut u8, RingOffsets) {
let desc_off = 64u64;
let len = desc_off + u64::from(ring_size) * std::mem::size_of::<XdpDesc>() as u64;
let map_len = len as usize;
let base = unsafe {
libc::mmap(
std::ptr::null_mut(),
map_len,
libc::PROT_READ | libc::PROT_WRITE,
libc::MAP_SHARED | libc::MAP_ANONYMOUS,
-1,
0,
)
};
assert_ne!(base, libc::MAP_FAILED, "匿名 mmap 失败");
let base = base as *mut u8;
unsafe {
(base.add(16) as *mut u32).write(flags_value);
}
(
base,
RingOffsets {
producer: 0,
consumer: 8,
desc: desc_off,
flags: 16,
len,
},
)
}
#[test]
fn test_kernel_mode_ring_enqueue_dequeue_via_mmap() {
let (base, offsets) = fake_kernel_ring_layout(4, 0);
let mut ring = unsafe { XskRing::with_kernel_ring(RingType::Tx, base, offsets) }
.expect("合法偏移必须成功");
assert!(ring.is_kernel_mode());
assert_eq!(ring.capacity(), 4);
assert_eq!(ring.available_space(), 4);
let descs = [make_desc(4096, 64), make_desc(8192, 128)];
assert_eq!(ring.enqueue_batch(&descs).unwrap(), 2);
unsafe {
assert_eq!((base as *const u32).read(), 2, "producer 索引应写入共享区");
let d0 = (base.add(64) as *const XdpDesc).read();
assert_eq!(d0.addr, 4096);
assert_eq!(d0.len, 64);
let d1 = (base.add(64 + 16) as *const XdpDesc).read();
assert_eq!(d1.addr, 8192);
}
let out = ring.dequeue_batch(4).unwrap();
assert_eq!(out.len(), 2);
assert_eq!(out[0].addr, 4096);
assert_eq!(out[1].addr, 8192);
unsafe {
assert_eq!((base.add(8) as *const u32).read(), 2, "consumer 索引应写入共享区");
}
}
#[test]
fn test_kernel_mode_ring_need_wakeup_flag() {
let (base, offsets) = fake_kernel_ring_layout(4, XDP_RING_NEED_WAKEUP);
let ring = unsafe { XskRing::with_kernel_ring(RingType::Fill, base, offsets) }
.expect("合法偏移必须成功");
assert!(ring.need_wakeup(), "flags 置位时 need_wakeup 必须为 true");
unsafe {
(base.add(16) as *mut u32).write(0u32);
}
assert!(!ring.need_wakeup());
}
#[test]
fn test_kernel_mode_ring_no_flags_field() {
let (base, mut offsets) = fake_kernel_ring_layout(4, 0);
offsets.flags = 0;
let ring = unsafe { XskRing::with_kernel_ring(RingType::Rx, base, offsets) }
.expect("合法偏移必须成功");
assert!(!ring.need_wakeup());
}
#[test]
fn test_kernel_mode_ring_rejects_invalid_offsets() {
let (base, offsets) = fake_kernel_ring_layout(4, 0);
let r = unsafe { XskRing::with_kernel_ring(RingType::Rx, std::ptr::null_mut(), offsets) };
assert!(r.is_err(), "空 mmap_base 必须拒绝");
let bad = RingOffsets { desc: 4096, ..offsets };
let r = unsafe { XskRing::with_kernel_ring(RingType::Rx, base, bad) };
assert!(r.is_err(), "desc > len 必须拒绝");
let bad = RingOffsets { len: offsets.desc + 3 * 16, ..offsets };
let r = unsafe { XskRing::with_kernel_ring(RingType::Rx, base, bad) };
assert!(r.is_err(), "非 2 的幂容量必须拒绝");
let bad = RingOffsets { producer: offsets.len, ..offsets };
let r = unsafe { XskRing::with_kernel_ring(RingType::Rx, base, bad) };
assert!(r.is_err(), "producer 越界必须拒绝");
let bad = RingOffsets { flags: offsets.len + 4, ..offsets };
let r = unsafe { XskRing::with_kernel_ring(RingType::Rx, base, bad) };
assert!(r.is_err(), "flags 越界必须拒绝");
unsafe {
libc::munmap(base as *mut libc::c_void, offsets.len as usize);
}
}
#[test]
fn test_kernel_mode_ring_wrap_across_u32_max() {
let (base, offsets) = fake_kernel_ring_layout(16, 0);
let mut ring = unsafe { XskRing::with_kernel_ring(RingType::Completion, base, offsets) }
.expect("合法偏移必须成功");
unsafe {
(base as *mut u32).write(u32::MAX - 1);
(base.add(8) as *mut u32).write(u32::MAX - 1);
}
assert_eq!(ring.available_data(), 0);
assert_eq!(ring.available_space(), 16);
let descs: Vec<XdpDesc> = (0..4u32).map(|i| make_desc(u64::from(i) * 4096, i)).collect();
assert_eq!(ring.enqueue_batch(&descs).unwrap(), 4);
assert_eq!(ring.producer_index(), 2);
assert_eq!(ring.available_data(), 4);
let out = ring.dequeue_batch(4).unwrap();
assert_eq!(out.len(), 4);
for (i, d) in out.iter().enumerate() {
assert_eq!(d.addr, (i as u64) * 4096, "跨回绕边界 FIFO 顺序必须保持");
}
assert_eq!(ring.consumer_index(), 2);
}
}