use crate::error::{DescriptorError, Result};
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DescriptorType {
DataFrame,
ControlMessage,
Reserved,
}
#[repr(C, align(8))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct XdpDesc {
pub addr: u64,
pub len: u32,
pub options: u32,
}
const _: () = assert!(
core::mem::size_of::<XdpDesc>() == 16,
"XdpDesc 必须与内核 xdp_desc 同为 16 字节"
);
impl XdpDesc {
#[inline]
pub const fn zero() -> Self {
Self {
addr: 0,
len: 0,
options: 0,
}
}
#[inline]
pub fn is_zero(&self) -> bool {
self.addr == 0 && self.len == 0 && self.options == 0
}
#[inline]
pub fn as_u64_slice(&self) -> [u64; 2] {
[self.addr, (self.len as u64) | ((self.options as u64) << 32)]
}
#[inline]
pub fn from_u64_slice(raw: [u64; 2]) -> Self {
Self {
addr: raw[0],
len: (raw[1] as u32),
options: (raw[1] >> 32) as u32,
}
}
}
impl Default for XdpDesc {
fn default() -> Self {
Self::zero()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Descriptor {
xdp: XdpDesc,
index: u32,
generation: u16,
owner: u16,
desc_type: DescriptorType,
}
pub const MAX_FRAME_INDEX: u64 = (1 << 20) - 1;
impl Descriptor {
#[inline]
pub fn new(
xdp: XdpDesc,
index: u32,
owner: u16,
generation: u16,
desc_type: DescriptorType,
) -> Self {
Self {
xdp,
index,
owner,
generation,
desc_type,
}
}
#[inline]
pub fn from_xdp(xdp: XdpDesc, owner: u16, generation: u16, frame_shift: u32) -> Result<Self> {
let raw_index = xdp.addr >> frame_shift;
if raw_index > MAX_FRAME_INDEX {
return Err(DescriptorError::OutOfRange {
descriptor: xdp.addr,
max_valid: MAX_FRAME_INDEX,
}
.into());
}
let index = u32::try_from(raw_index).map_err(|_| DescriptorError::OutOfRange {
descriptor: xdp.addr,
max_valid: MAX_FRAME_INDEX,
})?;
Ok(Self {
xdp,
index,
owner,
generation,
desc_type: DescriptorType::DataFrame,
})
}
#[inline]
pub fn xdp_desc(&self) -> &XdpDesc {
&self.xdp
}
#[inline]
pub fn addr(&self) -> u64 {
self.xdp.addr
}
#[inline]
pub fn len(&self) -> u32 {
self.xdp.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.xdp.len == 0
}
#[inline]
pub fn options(&self) -> u32 {
self.xdp.options
}
#[inline]
pub fn index(&self) -> u32 {
self.index
}
#[inline]
pub fn owner(&self) -> u16 {
self.owner
}
#[inline]
pub fn generation(&self) -> u16 {
self.generation
}
#[inline]
pub fn descriptor_type(&self) -> DescriptorType {
self.desc_type
}
#[inline]
pub fn set_owner(&mut self, new_owner: u16) {
self.owner = new_owner;
}
#[inline]
pub fn set_generation(&mut self, new_generation: u16) {
self.generation = new_generation;
}
#[inline]
pub fn is_zero(&self) -> bool {
self.xdp.is_zero()
}
#[inline]
pub fn to_raw(&self) -> [u64; 2] {
self.xdp.as_u64_slice()
}
}
impl Default for Descriptor {
fn default() -> Self {
Self {
xdp: XdpDesc::zero(),
index: 0,
generation: 0,
owner: 0,
desc_type: DescriptorType::Reserved,
}
}
}
pub struct DescriptorEngine {
max_frame_index: u64,
frame_shift: u32,
current_generation: AtomicU64,
allocated_count: AtomicU64,
freed_count: AtomicU64,
active_bitmap: Vec<AtomicU64>,
}
impl std::fmt::Debug for DescriptorEngine {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DescriptorEngine")
.field("max_frame_index", &self.max_frame_index)
.field("current_generation", &self.current_generation)
.field("allocated_count", &self.allocated_count)
.field("freed_count", &self.freed_count)
.finish()
}
}
impl DescriptorEngine {
pub fn new(max_frames: u64) -> Result<Self> {
Self::with_frame_shift(max_frames, 12)
}
pub fn with_frame_shift(max_frames: u64, frame_shift: u32) -> Result<Self> {
if !(11..=15).contains(&frame_shift) {
return Err(DescriptorError::InvalidFrameShift(frame_shift).into());
}
if max_frames > MAX_FRAME_INDEX + 1 {
return Err(DescriptorError::InvalidCapacity(max_frames).into());
}
Ok(Self::new_unchecked(max_frames, frame_shift))
}
fn new_unchecked(max_frames: u64, frame_shift: u32) -> Self {
let bitmap_words = max_frames.div_ceil(64);
let active_bitmap = (0..bitmap_words)
.map(|_| AtomicU64::new(0))
.collect();
Self {
max_frame_index: max_frames.saturating_sub(1),
frame_shift,
current_generation: AtomicU64::new(1),
allocated_count: AtomicU64::new(0),
freed_count: AtomicU64::new(0),
active_bitmap,
}
}
#[inline]
pub fn frame_shift(&self) -> u32 {
self.frame_shift
}
pub fn allocate(&self, frame_index: u32, owner: u16) -> Result<Descriptor> {
if u64::from(frame_index) > self.max_frame_index {
return Err(DescriptorError::OutOfRange {
descriptor: u64::from(frame_index) << self.frame_shift,
max_valid: self.max_frame_index,
}
.into());
}
if self.get_bitmap(frame_index) {
return Err(DescriptorError::AlreadyAllocated(u64::from(frame_index)).into());
}
let generation = self.current_generation.load(Ordering::Relaxed) as u16;
self.set_bitmap(frame_index, true);
self.allocated_count.fetch_add(1, Ordering::Relaxed);
let xdp = XdpDesc {
addr: u64::from(frame_index) << self.frame_shift,
len: 0,
options: 0,
};
Ok(Descriptor::new(
xdp,
frame_index,
owner,
generation,
DescriptorType::DataFrame,
))
}
pub fn release(&self, desc: &Descriptor) -> Result<()> {
let index = desc.index();
if !self.get_bitmap(index) {
return Err(DescriptorError::AlreadyFreed(desc.addr()).into());
}
self.set_bitmap(index, false);
self.freed_count.fetch_add(1, Ordering::Relaxed);
Ok(())
}
pub fn rollback_allocate(&self, frame_index: u32) -> bool {
if u64::from(frame_index) > self.max_frame_index {
return false;
}
if !self.get_bitmap(frame_index) {
return false;
}
self.set_bitmap(frame_index, false);
let _ = self.allocated_count.fetch_sub(1, Ordering::Relaxed);
true
}
pub fn verify(&self, desc: &Descriptor, expected_owner: u16) -> Result<()> {
let index = desc.index();
if (index as u64) > self.max_frame_index {
return Err(DescriptorError::OutOfRange {
descriptor: desc.addr(),
max_valid: self.max_frame_index,
}
.into());
}
if !self.get_bitmap(index) {
return Err(DescriptorError::AlreadyFreed(desc.addr()).into());
}
if desc.owner() != expected_owner {
return Err(DescriptorError::OwnershipMismatch {
expected: expected_owner as u32,
actual: desc.owner() as u32,
}
.into());
}
Ok(())
}
pub fn transfer_ownership(
&self,
desc: &Descriptor,
old_owner: u16,
new_owner: u16,
) -> Result<Descriptor> {
self.verify(desc, old_owner)?;
let next = self.current_generation.fetch_add(1, Ordering::Relaxed).wrapping_add(1);
let new_generation = u16::try_from(next).map_err(|_| {
DescriptorError::TransactionFailed(format!("代际号 {next} 超出 u16 范围"))
})?;
let mut new_desc = *desc;
new_desc.set_owner(new_owner);
new_desc.set_generation(new_generation);
Ok(new_desc)
}
#[inline]
pub fn is_allocated(&self, index: u32) -> bool {
self.get_bitmap(index)
}
#[inline]
pub fn bump_generation(&self) -> Result<u64> {
let next = self
.current_generation
.fetch_add(1, Ordering::Relaxed)
.wrapping_add(1);
if next > u64::from(u16::MAX) {
return Err(DescriptorError::TransactionFailed(format!(
"代际号 {next} 超出 u16 范围"
))
.into());
}
Ok(next)
}
#[inline]
pub fn conservation_stats(&self) -> (u64, u64) {
(
self.allocated_count.load(Ordering::Relaxed),
self.freed_count.load(Ordering::Relaxed),
)
}
pub fn verify_conservation(&self) -> bool {
let (allocated, freed) = self.conservation_stats();
let active = self.count_active();
allocated == freed + active
}
fn count_active(&self) -> u64 {
let mut count = 0u64;
for word in &self.active_bitmap {
count += word.load(Ordering::Relaxed).count_ones() as u64;
}
count
}
fn set_bitmap(&self, index: u32, value: bool) {
let word_idx = (index / 64) as usize;
let bit_idx = index % 64;
if word_idx < self.active_bitmap.len() {
if value {
self.active_bitmap[word_idx].fetch_or(1u64 << bit_idx, Ordering::Relaxed);
} else {
self.active_bitmap[word_idx].fetch_and(!(1u64 << bit_idx), Ordering::Relaxed);
}
}
}
fn get_bitmap(&self, index: u32) -> bool {
let word_idx = (index / 64) as usize;
let bit_idx = index % 64;
if word_idx < self.active_bitmap.len() {
(self.active_bitmap[word_idx].load(Ordering::Relaxed) >> bit_idx) & 1 == 1
} else {
false
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::LinuxError;
#[test]
fn test_xdp_desc_zero() {
let desc = XdpDesc::zero();
assert!(desc.is_zero());
assert_eq!(desc.addr, 0);
assert_eq!(desc.len, 0);
assert_eq!(desc.options, 0);
}
#[test]
fn test_xdp_desc_roundtrip() {
let desc = XdpDesc {
addr: 0x1000,
len: 1500,
options: 0x42,
};
let raw = desc.as_u64_slice();
let recovered = XdpDesc::from_u64_slice(raw);
assert_eq!(recovered, desc);
}
#[test]
fn test_descriptor_creation() {
let xdp = XdpDesc {
addr: 4096,
len: 1500,
options: 0,
};
let desc = Descriptor::new(xdp, 1, 1, 1, DescriptorType::DataFrame);
assert_eq!(desc.index(), 1);
assert_eq!(desc.addr(), 4096);
assert_eq!(desc.len(), 1500);
assert_eq!(desc.owner(), 1);
assert_eq!(desc.generation(), 1);
assert_eq!(desc.descriptor_type(), DescriptorType::DataFrame);
}
#[test]
fn test_descriptor_modify() {
let xdp = XdpDesc::zero();
let mut desc = Descriptor::new(xdp, 0, 1, 1, DescriptorType::DataFrame);
desc.set_owner(2);
assert_eq!(desc.owner(), 2);
desc.set_generation(5);
assert_eq!(desc.generation(), 5);
}
#[test]
fn test_descriptor_from_xdp() {
let xdp = XdpDesc {
addr: 8192, len: 64,
options: 0,
};
let desc = Descriptor::from_xdp(xdp, 3, 42, 12).unwrap();
assert_eq!(desc.index(), 2);
assert_eq!(desc.addr(), 8192);
assert_eq!(desc.owner(), 3);
assert_eq!(desc.generation(), 42);
}
#[test]
fn test_descriptor_from_xdp_out_of_range_rejected() {
let xdp = XdpDesc {
addr: (1u64 << 20) << 12,
len: 64,
options: 0,
};
let result = Descriptor::from_xdp(xdp, 1, 1, 12);
assert!(matches!(
result,
Err(LinuxError::Descriptor(DescriptorError::OutOfRange { .. }))
));
}
#[test]
fn test_descriptor_from_xdp_high_bits_truncation_rejected() {
let xdp = XdpDesc {
addr: (1u64 << 32) << 12,
len: 0,
options: 0,
};
assert!(Descriptor::from_xdp(xdp, 1, 1, 12).is_err());
}
#[test]
fn test_descriptor_from_xdp_max_valid_index() {
let xdp = XdpDesc {
addr: MAX_FRAME_INDEX << 12,
len: 0,
options: 0,
};
let desc = Descriptor::from_xdp(xdp, 1, 1, 12).unwrap();
assert_eq!(desc.index() as u64, MAX_FRAME_INDEX);
}
#[test]
fn test_descriptor_from_xdp_non_default_frame_shift() {
let xdp = XdpDesc {
addr: 4096, len: 64,
options: 0,
};
let desc = Descriptor::from_xdp(xdp, 1, 1, 11).unwrap();
assert_eq!(desc.index(), 2);
let xdp2 = XdpDesc {
addr: 65536,
len: 64,
options: 0,
};
let desc2 = Descriptor::from_xdp(xdp2, 1, 1, 15).unwrap();
assert_eq!(desc2.index(), 2);
}
#[test]
fn test_descriptor_engine_allocate_release() {
let engine = DescriptorEngine::new(1024).unwrap();
let desc = engine.allocate(0, 1).unwrap();
assert_eq!(desc.index(), 0);
assert_eq!(desc.owner(), 1);
assert_eq!(desc.addr(), 0);
engine.verify(&desc, 1).unwrap();
engine.release(&desc).unwrap();
let result = engine.verify(&desc, 1);
assert!(result.is_err());
}
#[test]
fn test_descriptor_engine_zero_rejected() {
let engine = DescriptorEngine::new(1024).unwrap();
let zero = Descriptor::default();
let result = engine.verify(&zero, 1);
assert!(result.is_err());
}
#[test]
fn test_descriptor_ownership_transfer() {
let engine = DescriptorEngine::new(1024).unwrap();
let desc = engine.allocate(0, 1).unwrap();
let new_desc = engine.transfer_ownership(&desc, 1, 2).unwrap();
assert_eq!(new_desc.owner(), 2);
assert_eq!(new_desc.index(), 0);
}
#[test]
fn test_descriptor_conservation() {
let engine = DescriptorEngine::new(1024).unwrap();
let desc1 = engine.allocate(0, 1).unwrap();
let _desc2 = engine.allocate(1, 1).unwrap();
engine.release(&desc1).unwrap();
assert!(engine.verify_conservation());
let (allocated, freed) = engine.conservation_stats();
assert_eq!(allocated, 2);
assert_eq!(freed, 1);
}
#[test]
fn test_descriptor_out_of_range() {
let engine = DescriptorEngine::new(100).unwrap();
let xdp = XdpDesc {
addr: (2000u64) << 12,
len: 0,
options: 0,
};
let desc = Descriptor::new(xdp, 2000, 1, 1, DescriptorType::DataFrame);
let result = engine.verify(&desc, 1);
assert!(result.is_err());
}
#[test]
fn test_already_freed_rejected() {
let engine = DescriptorEngine::new(1024).unwrap();
let desc = engine.allocate(5, 1).unwrap();
engine.release(&desc).unwrap();
let result = engine.release(&desc);
assert!(result.is_err());
}
#[test]
fn test_ownership_mismatch() {
let engine = DescriptorEngine::new(1024).unwrap();
let desc = engine.allocate(10, 2).unwrap();
let result = engine.verify(&desc, 1);
assert!(result.is_err());
}
#[test]
fn test_frame_index_boundary_zero() {
let engine = DescriptorEngine::new(1024).unwrap();
let desc = engine.allocate(0, 1).unwrap();
assert_eq!(desc.index(), 0);
assert_eq!(desc.addr(), 0);
engine.verify(&desc, 1).unwrap();
}
#[test]
fn test_frame_index_boundary_one() {
let engine = DescriptorEngine::new(1024).unwrap();
let desc = engine.allocate(1, 1).unwrap();
assert_eq!(desc.index(), 1);
assert_eq!(desc.addr(), 4096);
engine.verify(&desc, 1).unwrap();
}
#[test]
fn test_frame_index_20bit_max() {
let max_frames = 0x100000u64; let engine = DescriptorEngine::new(max_frames).unwrap();
let max_index = (max_frames - 1) as u32;
let desc = engine.allocate(max_index, 1).unwrap();
assert_eq!(desc.index(), max_index);
assert_eq!(desc.addr(), (max_index as u64) << 12);
engine.verify(&desc, 1).unwrap();
}
#[test]
fn test_frame_index_out_of_range() {
let engine = DescriptorEngine::new(100).unwrap();
let xdp = XdpDesc {
addr: (200u64) << 12,
len: 0,
options: 0,
};
let desc = Descriptor::new(xdp, 200, 1, 1, DescriptorType::DataFrame);
let result = engine.verify(&desc, 1);
assert!(result.is_err());
}
#[test]
fn test_descriptor_default_is_zero() {
let desc = Descriptor::default();
assert!(desc.is_zero());
assert_eq!(desc.index(), 0);
assert_eq!(desc.owner(), 0);
assert_eq!(desc.generation(), 0);
assert_eq!(desc.descriptor_type(), DescriptorType::Reserved);
}
#[test]
fn test_xdp_desc_default_is_zero() {
let desc = XdpDesc::default();
assert!(desc.is_zero());
assert_eq!(desc.addr, 0);
assert_eq!(desc.len, 0);
assert_eq!(desc.options, 0);
}
#[test]
fn test_xdp_desc_not_zero_with_addr_only() {
let desc = XdpDesc {
addr: 4096,
len: 0,
options: 0,
};
assert!(!desc.is_zero());
}
#[test]
fn test_xdp_desc_not_zero_with_len_only() {
let desc = XdpDesc {
addr: 0,
len: 100,
options: 0,
};
assert!(!desc.is_zero());
}
#[test]
fn test_xdp_desc_not_zero_with_options_only() {
let desc = XdpDesc {
addr: 0,
len: 0,
options: 1,
};
assert!(!desc.is_zero());
}
#[test]
fn test_descriptor_to_raw_roundtrip() {
let xdp = XdpDesc {
addr: 0x12345678,
len: 1500,
options: 0xDEAD,
};
let desc = Descriptor::new(xdp, 0, 1, 2, DescriptorType::DataFrame);
let raw = desc.to_raw();
let recovered = XdpDesc::from_u64_slice(raw);
assert_eq!(recovered, xdp);
}
#[test]
fn test_generation_bump() {
let engine = DescriptorEngine::new(1024).unwrap();
let initial = engine.bump_generation().unwrap();
assert!(initial > 1);
let next = engine.bump_generation().unwrap();
assert_eq!(next, initial + 1);
}
#[test]
fn test_generation_bump_overflow_fail_closed() {
let engine = DescriptorEngine::new(1024).unwrap();
engine
.current_generation
.store(u64::from(u16::MAX), Ordering::Relaxed);
let result = engine.bump_generation();
assert!(result.is_err(), "代际号越界必须 Fail-Closed");
}
#[test]
fn test_transfer_ownership_increments_generation() {
let engine = DescriptorEngine::new(1024).unwrap();
let desc = engine.allocate(0, 1).unwrap();
let original_gen = desc.generation();
let new_desc = engine.transfer_ownership(&desc, 1, 2).unwrap();
assert_eq!(new_desc.owner(), 2);
assert_eq!(new_desc.index(), 0);
assert!(new_desc.generation() > original_gen);
}
#[test]
fn test_transfer_ownership_wrong_old_owner_fails() {
let engine = DescriptorEngine::new(1024).unwrap();
let desc = engine.allocate(0, 1).unwrap();
let result = engine.transfer_ownership(&desc, 99, 2);
assert!(result.is_err());
}
#[test]
fn test_transfer_ownership_generation_overflow_fails() {
let engine = DescriptorEngine::new(1024).unwrap();
let desc = engine.allocate(0, 1).unwrap();
engine.current_generation.store(u64::from(u16::MAX), Ordering::Relaxed);
let result = engine.transfer_ownership(&desc, 1, 2);
assert!(result.is_err(), "代际号越界必须 Fail-Closed");
}
#[test]
fn test_multiple_allocations_and_releases() {
let engine = DescriptorEngine::new(1024).unwrap();
let mut descs = Vec::new();
for i in 0..100 {
let desc = engine.allocate(i, (i % 10) as u16).unwrap();
descs.push(desc);
}
assert!(engine.verify_conservation());
let (allocated, freed) = engine.conservation_stats();
assert_eq!(allocated, 100);
assert_eq!(freed, 0);
for desc in &descs[..50] {
engine.release(desc).unwrap();
}
assert!(engine.verify_conservation());
let (allocated, freed) = engine.conservation_stats();
assert_eq!(allocated, 100);
assert_eq!(freed, 50);
for desc in &descs[50..] {
engine.release(desc).unwrap();
}
assert!(engine.verify_conservation());
let (allocated, freed) = engine.conservation_stats();
assert_eq!(allocated, 100);
assert_eq!(freed, 100);
}
#[test]
fn test_already_in_use_check() {
let engine = DescriptorEngine::new(1024).unwrap();
let desc = engine.allocate(42, 1).unwrap();
engine.verify(&desc, 1).unwrap();
assert!(engine.is_allocated(42));
let result = engine.allocate(42, 2);
assert!(
matches!(
result,
Err(LinuxError::Descriptor(DescriptorError::AlreadyAllocated(42)))
),
"已分配帧重复分配必须返回 AlreadyAllocated,实际 {result:?}"
);
let (allocated, freed) = engine.conservation_stats();
assert_eq!(allocated, 1);
assert_eq!(freed, 0);
assert!(engine.verify_conservation());
}
#[test]
fn test_engine_capacity_fail_closed_over_20bit() {
assert!(DescriptorEngine::new((1u64 << 20) + 1).is_err());
assert!(DescriptorEngine::new(u64::MAX).is_err());
assert!(DescriptorEngine::with_frame_shift((1u64 << 20) + 1, 12).is_err());
assert!(DescriptorEngine::new(1u64 << 20).is_ok());
assert!(DescriptorEngine::with_frame_shift(1024, 16).is_err());
}
#[test]
fn test_descriptor_engine_debug_format() {
let engine = DescriptorEngine::new(256).unwrap();
let debug = format!("{:?}", engine);
assert!(debug.contains("DescriptorEngine"));
assert!(debug.contains("max_frame_index"));
assert!(debug.contains("current_generation"));
}
#[test]
fn test_descriptor_type_variants() {
let types = vec![
DescriptorType::DataFrame,
DescriptorType::ControlMessage,
DescriptorType::Reserved,
];
for desc_type in types {
let xdp = XdpDesc::zero();
let desc = Descriptor::new(xdp, 0, 0, 0, desc_type);
assert_eq!(desc.descriptor_type(), desc_type);
}
}
#[test]
fn test_large_descriptor_engine() {
let engine = DescriptorEngine::new(65536).unwrap();
let desc = engine.allocate(65535, 1).unwrap();
assert_eq!(desc.index(), 65535);
engine.verify(&desc, 1).unwrap();
engine.release(&desc).unwrap();
assert!(engine.verify_conservation());
}
}