1#![allow(unsafe_code)]
19
20use crate::descriptor::{Descriptor, DescriptorEngine, XdpDesc};
21use crate::error::{LinuxError, Result, UmemError, XskError};
22use crate::ring::{RingOffsets, RingType, XskRing};
23use crate::umem::UmemManager;
24use std::sync::atomic::{AtomicBool, Ordering};
25use std::sync::Arc;
26
27const PREFILL_CHUNK: usize = 64;
29
30fn build_umem_reg(addr: *const u8, len: usize, chunk_size: u32, headroom: u32) -> libc::xdp_umem_reg {
39 libc::xdp_umem_reg {
40 addr: addr as u64,
41 len: len as u64,
42 chunk_size,
43 headroom,
44 flags: 0,
45 tx_metadata_len: 0,
46 }
47}
48
49fn to_ring_offsets(k: &libc::xdp_ring_offset, ring_size: u32) -> RingOffsets {
54 RingOffsets {
55 producer: k.producer,
56 consumer: k.consumer,
57 desc: k.desc,
58 flags: k.flags,
59 len: k.desc + u64::from(ring_size) * std::mem::size_of::<XdpDesc>() as u64,
60 }
61}
62
63#[derive(Debug)]
71struct RingMmapGuard {
72 entries: Vec<RingMmapEntry>,
74}
75
76#[derive(Debug)]
78struct RingMmapEntry {
79 base: *mut u8,
81 len: usize,
83 disarmed: bool,
85}
86
87impl RingMmapGuard {
88 fn new() -> Self {
90 Self {
91 entries: Vec::new(),
92 }
93 }
94
95 fn register(&mut self, base: *mut u8, len: usize) -> usize {
97 self.entries.push(RingMmapEntry {
98 base,
99 len,
100 disarmed: false,
101 });
102 self.entries.len() - 1
103 }
104
105 fn disarm(&mut self, idx: usize) {
109 if let Some(entry) = self.entries.get_mut(idx) {
110 entry.disarmed = true;
111 }
112 }
113}
114
115impl Drop for RingMmapGuard {
116 fn drop(&mut self) {
117 for entry in &self.entries {
118 if entry.disarmed || entry.base.is_null() {
119 continue;
120 }
121 guard_unmap(entry.base, entry.len);
123 }
124 }
125}
126
127#[cfg(not(test))]
135fn guard_unmap(base: *mut u8, len: usize) {
136 unsafe {
140 libc::munmap(base as *mut libc::c_void, len);
141 }
142}
143
144#[cfg(test)]
146fn guard_unmap(base: *mut u8, len: usize) {
147 if GUARD_TEST_STUB.with(|s| s.get()) {
148 GUARD_UNMAP_LOG.with(|l| l.borrow_mut().push((base as usize, len)));
149 return;
150 }
151 unsafe {
153 libc::munmap(base as *mut libc::c_void, len);
154 }
155}
156
157#[cfg(test)]
158thread_local! {
159 static GUARD_TEST_STUB: std::cell::Cell<bool> = const { std::cell::Cell::new(false) };
161 static GUARD_UNMAP_LOG: std::cell::RefCell<Vec<(usize, usize)>> = const { std::cell::RefCell::new(Vec::new()) };
163}
164
165#[derive(Debug, Clone)]
167pub struct XskConfig {
168 pub ifindex: u32,
170 pub queue_id: u32,
172 pub zero_copy: bool,
174 pub fill_ring_size: u32,
176 pub rx_ring_size: u32,
178 pub tx_ring_size: u32,
180 pub completion_ring_size: u32,
182 pub shared_umem: bool,
184 pub frame_size: u32,
186 pub headroom: u32,
188 pub so_rcvbuf: i32,
190 pub so_sndbuf: i32,
192 pub prefill_chunk: usize,
194}
195
196impl Default for XskConfig {
197 fn default() -> Self {
198 Self {
199 ifindex: 0,
200 queue_id: 0,
201 zero_copy: true,
202 fill_ring_size: 2048,
203 rx_ring_size: 2048,
204 tx_ring_size: 2048,
205 completion_ring_size: 2048,
206 shared_umem: false,
207 frame_size: 4096,
208 headroom: 0,
209 so_rcvbuf: 1024 * 1024,
210 so_sndbuf: 1024 * 1024,
211 prefill_chunk: 64,
212 }
213 }
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq)]
218pub enum XskState {
219 Created,
221 Configured,
223 Bound,
225 Closed,
227}
228
229pub struct XskSocket {
234 fd: Option<i32>,
236 config: XskConfig,
238 state: XskState,
240 umem: Option<Arc<UmemManager>>,
242 fill_ring: XskRing,
244 rx_ring: XskRing,
246 tx_ring: XskRing,
248 completion_ring: XskRing,
250 descriptor_engine: DescriptorEngine,
252 prefilled_frames: u32,
254 active: AtomicBool,
256}
257
258impl std::fmt::Debug for XskSocket {
259 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
260 f.debug_struct("XskSocket")
261 .field("fd", &self.fd)
262 .field("config", &self.config)
263 .field("state", &self.state)
264 .field("active", &self.active)
265 .finish()
266 }
267}
268
269impl XskSocket {
270 pub fn new(config: XskConfig) -> Result<Self> {
279 let fill_ring_size = config.fill_ring_size;
280 let rx_ring_size = config.rx_ring_size;
281 let tx_ring_size = config.tx_ring_size;
282 let completion_ring_size = config.completion_ring_size;
283
284 let total_descriptors = u64::from(fill_ring_size)
286 + u64::from(rx_ring_size)
287 + u64::from(tx_ring_size)
288 + u64::from(completion_ring_size);
289 let descriptor_engine = DescriptorEngine::new(total_descriptors)?;
291
292 Ok(Self {
293 fd: None,
294 config,
295 state: XskState::Created,
296 umem: None,
297 fill_ring: XskRing::new(RingType::Fill, fill_ring_size)?,
298 rx_ring: XskRing::new(RingType::Rx, rx_ring_size)?,
299 tx_ring: XskRing::new(RingType::Tx, tx_ring_size)?,
300 completion_ring: XskRing::new(RingType::Completion, completion_ring_size)?,
301 descriptor_engine,
302 prefilled_frames: 0,
303 active: AtomicBool::new(false),
304 })
305 }
306
307 pub fn new_real(config: XskConfig, umem: Arc<UmemManager>) -> Result<Self> {
322 let mut socket = Self::new(config)?;
323 socket.create_socket()?;
324 socket.configure()?;
325 socket.bind(umem)?;
326 debug_assert!(socket.is_kernel_mode());
327 Ok(socket)
328 }
329
330 pub fn create_socket(&mut self) -> Result<()> {
332 if self.state != XskState::Created {
335 return Err(XskError::InvalidState(format!(
336 "create_socket expects state=Created, actual state={:?}",
337 self.state
338 ))
339 .into());
340 }
341
342 let fd = unsafe { libc::socket(libc::AF_XDP, libc::SOCK_RAW, 0) };
347 if fd < 0 {
348 let err = std::io::Error::last_os_error();
349 return Err(XskError::SocketCreate(format!("socket() failed: {}", err)).into());
350 }
351
352 self.fd = Some(fd);
353 self.state = XskState::Configured;
354
355 Ok(())
356 }
357
358 pub fn configure(&mut self) -> Result<()> {
360 let fd = self.get_fd()?;
361
362 let rcvbuf: i32 = self.config.so_rcvbuf;
365 let ret = unsafe {
366 libc::setsockopt(
367 fd,
368 libc::SOL_SOCKET,
369 libc::SO_RCVBUF,
370 &rcvbuf as *const i32 as *const libc::c_void,
371 std::mem::size_of::<i32>() as libc::socklen_t,
372 )
373 };
374 if ret < 0 {
375 let err = std::io::Error::last_os_error();
376 return Err(XskError::SocketOption(format!("SO_RCVBUF failed: {}", err)).into());
377 }
378
379 let sndbuf: i32 = self.config.so_sndbuf;
382 let ret = unsafe {
383 libc::setsockopt(
384 fd,
385 libc::SOL_SOCKET,
386 libc::SO_SNDBUF,
387 &sndbuf as *const i32 as *const libc::c_void,
388 std::mem::size_of::<i32>() as libc::socklen_t,
389 )
390 };
391 if ret < 0 {
392 let err = std::io::Error::last_os_error();
393 return Err(XskError::SocketOption(format!("SO_SNDBUF failed: {}", err)).into());
394 }
395
396 Ok(())
397 }
398
399 fn set_ring_size(&self, optname: libc::c_int, size: u32) -> Result<()> {
404 let fd = self.get_fd()?;
405 let ret = unsafe {
408 libc::setsockopt(
409 fd,
410 libc::SOL_XDP,
411 optname,
412 &size as *const u32 as *const libc::c_void,
413 std::mem::size_of::<u32>() as libc::socklen_t,
414 )
415 };
416 if ret < 0 {
417 let err = std::io::Error::last_os_error();
418 return Err(XskError::SocketOption(format!(
419 "ring size setsockopt(opt={optname}, size={size}) failed: {err}"
420 ))
421 .into());
422 }
423 Ok(())
424 }
425
426 fn get_mmap_offsets(fd: i32) -> Result<libc::xdp_mmap_offsets> {
428 let mut offsets: libc::xdp_mmap_offsets = unsafe { std::mem::zeroed() };
432 let mut optlen = std::mem::size_of::<libc::xdp_mmap_offsets>() as libc::socklen_t;
433 let ret = unsafe {
435 libc::getsockopt(
436 fd,
437 libc::SOL_XDP,
438 libc::XDP_MMAP_OFFSETS,
439 &mut offsets as *mut libc::xdp_mmap_offsets as *mut libc::c_void,
440 &mut optlen,
441 )
442 };
443 if ret < 0 {
444 let err = std::io::Error::last_os_error();
445 return Err(XskError::SocketOption(format!(
446 "getsockopt(XDP_MMAP_OFFSETS) failed: {err}"
447 ))
448 .into());
449 }
450 Ok(offsets)
451 }
452
453 fn mmap_kernel_ring(fd: i32, pgoff: libc::off_t, length: usize, name: &str) -> Result<*mut u8> {
459 let addr = unsafe {
464 libc::mmap(
465 std::ptr::null_mut(),
466 length,
467 libc::PROT_READ | libc::PROT_WRITE,
468 libc::MAP_SHARED,
469 fd,
470 pgoff,
471 )
472 };
473 if addr == libc::MAP_FAILED {
474 let err = std::io::Error::last_os_error();
475 return Err(XskError::SocketOption(format!(
476 "{name} ring mmap(pgoff={pgoff:#x}, len={length}) failed: {err}"
477 ))
478 .into());
479 }
480 Ok(addr as *mut u8)
481 }
482
483 pub fn bind(&mut self, umem: Arc<UmemManager>) -> Result<()> {
498 if !umem.is_initialized() || umem.as_ptr().is_null() {
502 return Err(LinuxError::Umem(UmemError::NotCreated));
503 }
504 if self.config.frame_size == 0 {
505 return Err(XskError::SocketOption(
506 "frame_size 必须 > 0(XDP_UMEM_MIN_CHUNK_SIZE=2048)".to_string(),
507 )
508 .into());
509 }
510 if !self.config.frame_size.is_power_of_two() {
512 return Err(XskError::SocketOption(format!(
513 "frame_size={} 必须是 2 的幂(XDP_UMEM chunk_size 契约)",
514 self.config.frame_size
515 ))
516 .into());
517 }
518 let frame_size = self.config.frame_size;
521 let umem_frames = (umem.size() / frame_size as usize).min(u32::MAX as usize) as u32;
522 let new_engine =
525 DescriptorEngine::with_frame_shift(u64::from(umem_frames), frame_size.trailing_zeros())?;
526
527 let fd = self.get_fd()?;
528
529 let reg = build_umem_reg(
532 umem.as_ptr(),
533 umem.size(),
534 self.config.frame_size,
535 self.config.headroom,
536 );
537 let ret = unsafe {
540 libc::setsockopt(
541 fd,
542 libc::SOL_XDP,
543 libc::XDP_UMEM_REG,
544 ® as *const libc::xdp_umem_reg as *const libc::c_void,
545 std::mem::size_of::<libc::xdp_umem_reg>() as libc::socklen_t,
546 )
547 };
548 if ret < 0 {
549 let err = std::io::Error::last_os_error();
550 return Err(XskError::SocketOption(format!("XDP_UMEM_REG failed: {err}")).into());
551 }
552
553 self.set_ring_size(libc::XDP_UMEM_FILL_RING, self.config.fill_ring_size)?;
555 self.set_ring_size(libc::XDP_UMEM_COMPLETION_RING, self.config.completion_ring_size)?;
556 self.set_ring_size(libc::XDP_RX_RING, self.config.rx_ring_size)?;
557 self.set_ring_size(libc::XDP_TX_RING, self.config.tx_ring_size)?;
558
559 let offsets = Self::get_mmap_offsets(fd)?;
561
562 let rx_off = to_ring_offsets(&offsets.rx, self.config.rx_ring_size);
567 let tx_off = to_ring_offsets(&offsets.tx, self.config.tx_ring_size);
568 let fr_off = to_ring_offsets(&offsets.fr, self.config.fill_ring_size);
569 let cr_off = to_ring_offsets(&offsets.cr, self.config.completion_ring_size);
570
571 let mut mmap_guard = RingMmapGuard::new();
572 let rx_base = Self::mmap_kernel_ring(
573 fd,
574 libc::XDP_PGOFF_RX_RING,
575 rx_off.len as usize,
576 "RX",
577 )?;
578 let rx_slot = mmap_guard.register(rx_base, rx_off.len as usize);
579 let tx_base = Self::mmap_kernel_ring(
580 fd,
581 libc::XDP_PGOFF_TX_RING,
582 tx_off.len as usize,
583 "TX",
584 )?;
585 let tx_slot = mmap_guard.register(tx_base, tx_off.len as usize);
586 let fr_base = Self::mmap_kernel_ring(
587 fd,
588 libc::XDP_UMEM_PGOFF_FILL_RING as libc::off_t,
589 fr_off.len as usize,
590 "FILL",
591 )?;
592 let fr_slot = mmap_guard.register(fr_base, fr_off.len as usize);
593 let cr_base = Self::mmap_kernel_ring(
594 fd,
595 libc::XDP_UMEM_PGOFF_COMPLETION_RING as libc::off_t,
596 cr_off.len as usize,
597 "COMPLETION",
598 )?;
599 let cr_slot = mmap_guard.register(cr_base, cr_off.len as usize);
600
601 let rx_ring = unsafe { XskRing::with_kernel_ring(RingType::Rx, rx_base, rx_off) }?;
606 mmap_guard.disarm(rx_slot);
607 let tx_ring = unsafe { XskRing::with_kernel_ring(RingType::Tx, tx_base, tx_off) }?;
608 mmap_guard.disarm(tx_slot);
609 let fill_ring = unsafe { XskRing::with_kernel_ring(RingType::Fill, fr_base, fr_off) }?;
610 mmap_guard.disarm(fr_slot);
611 let completion_ring =
612 unsafe { XskRing::with_kernel_ring(RingType::Completion, cr_base, cr_off) }?;
613 mmap_guard.disarm(cr_slot);
614
615 let mut addr: libc::sockaddr_xdp = unsafe { std::mem::zeroed() };
620 addr.sxdp_family = libc::AF_XDP as u16;
621 addr.sxdp_ifindex = self.config.ifindex;
622 addr.sxdp_queue_id = self.config.queue_id;
623 let mut flags: u16 = if self.config.zero_copy {
627 libc::XDP_ZEROCOPY
628 } else {
629 libc::XDP_COPY
630 };
631 if self.config.shared_umem {
632 flags |= libc::XDP_SHARED_UMEM;
633 }
634 addr.sxdp_flags = flags;
635
636 let addr_ptr = &addr as *const libc::sockaddr_xdp as *const libc::sockaddr;
637 let addr_len = std::mem::size_of::<libc::sockaddr_xdp>() as libc::socklen_t;
638
639 let ret = unsafe { libc::bind(fd, addr_ptr, addr_len) };
642 if ret < 0 {
643 let err = std::io::Error::last_os_error();
644 return Err(XskError::BindFailed(format!(
645 "bind() failed: {err}(WSL2/无驱动支持环境属预期失败,绝不降级模拟)"
646 ))
647 .into());
648 }
649
650 self.fill_ring = fill_ring;
652 self.rx_ring = rx_ring;
653 self.tx_ring = tx_ring;
654 self.completion_ring = completion_ring;
655
656 self.descriptor_engine = new_engine;
660 self.prefilled_frames = 0;
661
662 self.umem = Some(umem);
663 self.state = XskState::Bound;
664 self.active.store(true, Ordering::SeqCst);
665
666 self.prefill_fill_ring()?;
668
669 Ok(())
670 }
671
672 pub fn prefill_fill_ring(&mut self) -> Result<u32> {
681 let frame_size = self.config.frame_size;
682 if frame_size == 0 {
683 return Err(XskError::SocketOption("frame_size 必须 > 0".to_string()).into());
684 }
685 let umem = match &self.umem {
686 Some(u) => Arc::clone(u),
687 None => return Ok(0), };
689
690 let umem_frames = (umem.size() / frame_size as usize).min(u32::MAX as usize) as u32;
693 let target = self.fill_ring.capacity().min(umem_frames);
694 let owner = self.config.queue_id as u16;
695
696 let start = self.prefilled_frames.min(target);
699 let mut filled: u32 = 0;
700 let chunk_size = self.config.prefill_chunk.clamp(1, PREFILL_CHUNK);
702 let mut chunk = [XdpDesc::zero(); PREFILL_CHUNK];
703 while start.saturating_add(filled) < target {
704 let frame_base = start.saturating_add(filled);
705 let batch = ((target - frame_base) as usize).min(chunk_size);
707 for (j, slot) in chunk.iter_mut().enumerate().take(batch) {
708 let frame_index = frame_base + j as u32;
710 let _ = self.descriptor_engine.allocate(frame_index, owner)?;
712 *slot = XdpDesc {
713 addr: u64::from(frame_index) * u64::from(frame_size),
714 len: 0,
715 options: 0,
716 };
717 }
718 let n = self.fill_ring.enqueue_batch(&chunk[..batch])?;
719 filled = filled.saturating_add(n);
720 if n as usize != batch {
721 break; }
723 }
724 self.prefilled_frames = start.saturating_add(filled);
725
726 if filled > 0 && self.fill_ring.need_wakeup() {
729 self.notify_fill()?;
730 }
731 Ok(filled)
732 }
733
734 #[inline]
736 pub fn is_kernel_mode(&self) -> bool {
737 self.rx_ring.is_kernel_mode()
738 }
739
740 pub fn close(&mut self) -> Result<()> {
742 self.active.store(false, Ordering::SeqCst);
743
744 if let Some(fd) = self.fd.take() {
747 let ret = unsafe { libc::close(fd) };
750 if ret < 0 {
751 self.state = XskState::Closed;
754 let err = std::io::Error::last_os_error();
755 return Err(XskError::CloseFailed(format!("close() failed: {}", err)).into());
756 }
757 }
758
759 self.state = XskState::Closed;
760
761 Ok(())
762 }
763
764 #[inline]
766 pub fn fd(&self) -> Option<i32> {
767 self.fd
768 }
769
770 #[inline]
772 pub fn state(&self) -> XskState {
773 self.state
774 }
775
776 #[inline]
778 pub fn is_active(&self) -> bool {
779 self.active.load(Ordering::SeqCst)
780 }
781
782 #[inline]
784 pub fn fill_ring(&self) -> &XskRing {
785 &self.fill_ring
786 }
787
788 #[inline]
790 pub fn fill_ring_mut(&mut self) -> &mut XskRing {
791 &mut self.fill_ring
792 }
793
794 #[inline]
796 pub fn rx_ring(&self) -> &XskRing {
797 &self.rx_ring
798 }
799
800 #[inline]
802 pub fn rx_ring_mut(&mut self) -> &mut XskRing {
803 &mut self.rx_ring
804 }
805
806 #[inline]
808 pub fn tx_ring(&self) -> &XskRing {
809 &self.tx_ring
810 }
811
812 #[inline]
814 pub fn tx_ring_mut(&mut self) -> &mut XskRing {
815 &mut self.tx_ring
816 }
817
818 #[inline]
820 pub fn completion_ring(&self) -> &XskRing {
821 &self.completion_ring
822 }
823
824 #[inline]
826 pub fn completion_ring_mut(&mut self) -> &mut XskRing {
827 &mut self.completion_ring
828 }
829
830 #[inline]
832 pub fn descriptor_engine(&self) -> &DescriptorEngine {
833 &self.descriptor_engine
834 }
835
836 pub fn fill_descriptors(&mut self, count: u32) -> Result<u32> {
846 let mut descs = Vec::with_capacity(count as usize);
847 let mut allocated_indices = Vec::with_capacity(count as usize);
848 for i in 0..count {
849 if self.descriptor_engine.is_allocated(i) {
854 continue;
855 }
856 let desc = self
857 .descriptor_engine
858 .allocate(i, self.config.queue_id as u16)?;
859 descs.push(*desc.xdp_desc());
860 allocated_indices.push(i);
861 }
862 let filled = match self.fill_ring.enqueue_batch(&descs) {
863 Ok(n) => n,
864 Err(e) => {
865 for &idx in &allocated_indices {
869 self.descriptor_engine.rollback_allocate(idx);
870 }
871 return Err(e);
872 }
873 };
874 if self.fill_ring.need_wakeup() {
876 self.notify_fill()?;
877 }
878 Ok(filled)
879 }
880
881 pub fn receive_batch(&mut self, max_batch: u32) -> Result<Vec<Descriptor>> {
889 let raw_descs = self.rx_ring.dequeue_batch(max_batch)?;
890 let mut descriptors = Vec::with_capacity(raw_descs.len());
891
892 for raw_xdp in raw_descs {
893 let desc = Descriptor::from_xdp(
897 raw_xdp,
898 self.config.queue_id as u16,
899 0,
900 self.descriptor_engine.frame_shift(),
901 )?;
902 self.descriptor_engine
903 .verify(&desc, self.config.queue_id as u16)?;
904 descriptors.push(desc);
905 }
906
907 Ok(descriptors)
908 }
909
910 pub fn receive_batch_to(&mut self, buffer: &mut [Descriptor]) -> Result<u32> {
918 let max_batch = buffer.len() as u32;
919 let raw_descs = self.rx_ring.dequeue_batch(max_batch)?;
920 let count = raw_descs.len() as u32;
921
922 for (i, raw_xdp) in raw_descs.iter().enumerate() {
923 let desc = Descriptor::from_xdp(
924 *raw_xdp,
925 self.config.queue_id as u16,
926 0,
927 self.descriptor_engine.frame_shift(),
928 )?;
929 self.descriptor_engine
930 .verify(&desc, self.config.queue_id as u16)?;
931 buffer[i] = desc;
932 }
933
934 Ok(count)
935 }
936
937 pub fn send_batch(&mut self, descriptors: &[Descriptor]) -> Result<u32> {
945 let mut raw_descs: Vec<XdpDesc> = Vec::with_capacity(descriptors.len());
946
947 for desc in descriptors {
948 self.descriptor_engine
949 .verify(desc, self.config.queue_id as u16)?;
950 raw_descs.push(*desc.xdp_desc());
951 }
952
953 let sent = self.tx_ring.enqueue_batch(&raw_descs)?;
954 if self.tx_ring.need_wakeup() {
956 self.notify_tx()?;
957 }
958 Ok(sent)
959 }
960
961 pub fn send_xdp_batch(&mut self, descriptors: &[XdpDesc]) -> Result<u32> {
969 for raw_xdp in descriptors {
973 let desc = Descriptor::from_xdp(
974 *raw_xdp,
975 self.config.queue_id as u16,
976 0,
977 self.descriptor_engine.frame_shift(),
978 )?;
979 self.descriptor_engine
980 .verify(&desc, self.config.queue_id as u16)?;
981 }
982 let sent = self.tx_ring.enqueue_batch(descriptors)?;
983 if self.tx_ring.need_wakeup() {
984 self.notify_tx()?;
985 }
986 Ok(sent)
987 }
988
989 #[inline]
996 pub fn notify_tx(&self) -> Result<()> {
997 let fd = match self.fd {
998 Some(f) if f >= 0 => f,
999 _ => return Ok(()), };
1001 let ret = unsafe {
1004 libc::sendto(
1005 fd,
1006 std::ptr::null(),
1007 0,
1008 libc::MSG_DONTWAIT,
1009 std::ptr::null(),
1010 0,
1011 )
1012 };
1013 if ret < 0 {
1014 let err = std::io::Error::last_os_error();
1015 if matches!(
1017 err.raw_os_error(),
1018 Some(libc::EAGAIN) | Some(libc::EINTR)
1019 ) {
1020 return Ok(());
1021 }
1022 return Err(
1023 XskError::NotifyFailed(format!("notify_tx sendto failed: {err}")).into(),
1024 );
1025 }
1026 Ok(())
1027 }
1028
1029 #[inline]
1038 pub fn notify_fill(&self) -> Result<()> {
1039 let fd = match self.fd {
1040 Some(f) if f >= 0 => f,
1041 _ => return Ok(()),
1042 };
1043 let ret = unsafe {
1046 libc::sendto(
1047 fd,
1048 std::ptr::null(),
1049 0,
1050 libc::MSG_DONTWAIT,
1051 std::ptr::null(),
1052 0,
1053 )
1054 };
1055 if ret < 0 {
1056 let err = std::io::Error::last_os_error();
1057 if matches!(
1059 err.raw_os_error(),
1060 Some(libc::EAGAIN) | Some(libc::EINTR)
1061 ) {
1062 return Ok(());
1063 }
1064 return Err(
1065 XskError::NotifyFailed(format!("notify_fill sendto failed: {err}")).into(),
1066 );
1067 }
1068 Ok(())
1069 }
1070
1071 pub fn recycle_completed(&mut self, max_batch: u32) -> Result<u32> {
1079 let raw_descs = self.completion_ring.dequeue_batch(max_batch)?;
1080 let count = raw_descs.len() as u32;
1081
1082 for raw_xdp in &raw_descs {
1083 let desc = Descriptor::from_xdp(
1085 *raw_xdp,
1086 self.config.queue_id as u16,
1087 0,
1088 self.descriptor_engine.frame_shift(),
1089 )?;
1090 self.descriptor_engine.release(&desc)?;
1091 }
1092
1093 Ok(count)
1094 }
1095
1096 pub fn receive_and_refill(&mut self, max_batch: u32) -> Result<Vec<Descriptor>> {
1104 let received = self.receive_batch(max_batch)?;
1105
1106 if !received.is_empty() {
1107 self.fill_descriptors(received.len() as u32)?;
1108 }
1109
1110 Ok(received)
1111 }
1112
1113 pub fn umem(&self) -> Option<&Arc<UmemManager>> {
1115 self.umem.as_ref()
1116 }
1117
1118 pub fn get_fd(&self) -> Result<i32> {
1126 self.fd.ok_or_else(|| XskError::NotBound.into())
1127 }
1128
1129 #[inline]
1131 pub fn queue_id(&self) -> u32 {
1132 self.config.queue_id
1133 }
1134}
1135
1136impl Drop for XskSocket {
1137 fn drop(&mut self) {
1138 let _ = self.close();
1139 }
1140}
1141
1142#[cfg(test)]
1143mod tests {
1144 use super::*;
1145
1146 #[test]
1147 fn test_xsk_config_default() {
1148 let config = XskConfig::default();
1149 assert_eq!(config.ifindex, 0);
1150 assert_eq!(config.queue_id, 0);
1151 assert!(config.zero_copy);
1152 }
1153
1154 #[test]
1155 fn test_xsk_socket_creation() {
1156 let config = XskConfig::default();
1157 let socket = XskSocket::new(config);
1158 assert!(socket.is_ok());
1159 let socket = socket.unwrap();
1160 assert_eq!(socket.state(), XskState::Created);
1161 assert!(!socket.is_active());
1162 }
1163
1164 #[test]
1165 fn test_xsk_socket_create_and_configure() {
1166 let config = XskConfig::default();
1167 let mut socket = XskSocket::new(config).unwrap();
1168
1169 let result = socket.create_socket();
1170 if result.is_ok() {
1171 let result = socket.configure();
1172 assert!(result.is_ok());
1173 }
1174 }
1175
1176 #[test]
1177 fn test_xsk_ring_operations() {
1178 let config = XskConfig::default();
1179 let socket = XskSocket::new(config).unwrap();
1180
1181 assert_eq!(socket.fill_ring().capacity(), 2048);
1182 assert_eq!(socket.rx_ring().capacity(), 2048);
1183 assert_eq!(socket.tx_ring().capacity(), 2048);
1184 assert_eq!(socket.completion_ring().capacity(), 2048);
1185 }
1186
1187 #[test]
1188 fn test_xsk_descriptor_engine() {
1189 let config = XskConfig::default();
1190 let socket = XskSocket::new(config).unwrap();
1191
1192 let engine = socket.descriptor_engine();
1193 let desc = engine.allocate(0, 1).unwrap();
1194 assert_eq!(desc.index(), 0);
1195 assert_eq!(desc.addr(), 0);
1196
1197 engine.verify(&desc, 1).unwrap();
1198 engine.release(&desc).unwrap();
1199
1200 assert!(engine.verify_conservation());
1201 }
1202
1203 #[test]
1204 fn test_xsk_fill_and_receive() {
1205 let config = XskConfig::default();
1206 let mut socket = XskSocket::new(config).unwrap();
1207
1208 let filled = socket.fill_descriptors(4).unwrap();
1210 assert_eq!(filled, 4);
1211 assert_eq!(socket.fill_ring().available_data(), 4);
1212
1213 let descs = socket.fill_ring_mut().dequeue_batch(4).unwrap();
1215 assert_eq!(descs.len(), 4);
1216 socket.rx_ring_mut().enqueue_batch(&descs).unwrap();
1217
1218 let received = socket.receive_batch(4).unwrap();
1220 assert_eq!(received.len(), 4);
1221
1222 for desc in &received {
1223 assert_eq!(desc.addr(), (desc.index() as u64) << 12);
1224 }
1225 }
1226
1227 #[test]
1228 fn test_xsk_send_and_complete() {
1229 let config = XskConfig::default();
1230 let mut socket = XskSocket::new(config).unwrap();
1231
1232 let d0 = socket.descriptor_engine().allocate(100, 0).unwrap();
1234 let d1 = socket.descriptor_engine().allocate(101, 0).unwrap();
1235
1236 let descs = [d0, d1];
1237 let sent = socket.send_batch(&descs).unwrap();
1238 assert_eq!(sent, 2);
1239
1240 let tx_descs = socket.tx_ring_mut().dequeue_batch(2).unwrap();
1242 socket.completion_ring_mut().enqueue_batch(&tx_descs).unwrap();
1243
1244 let recycled = socket.recycle_completed(2).unwrap();
1246 assert_eq!(recycled, 2);
1247
1248 assert!(socket.descriptor_engine().verify_conservation());
1249 }
1250
1251 #[test]
1252 fn test_xsk_receive_batch_to_buffer() {
1253 let config = XskConfig::default();
1254 let mut socket = XskSocket::new(config).unwrap();
1255
1256 socket.fill_descriptors(3).unwrap();
1258 let descs = socket.fill_ring_mut().dequeue_batch(3).unwrap();
1259 socket.rx_ring_mut().enqueue_batch(&descs).unwrap();
1260
1261 let mut buffer = [Descriptor::default(); 4];
1263 let count = socket.receive_batch_to(&mut buffer).unwrap();
1264 assert_eq!(count, 3);
1265 }
1266
1267 #[test]
1268 fn test_xsk_config_custom_values() {
1269 let config = XskConfig {
1270 ifindex: 5,
1271 queue_id: 3,
1272 zero_copy: false,
1273 fill_ring_size: 512,
1274 rx_ring_size: 1024,
1275 tx_ring_size: 256,
1276 completion_ring_size: 128,
1277 shared_umem: true,
1278 frame_size: 4096,
1279 headroom: 0,
1280 so_rcvbuf: 1024 * 1024,
1281 so_sndbuf: 1024 * 1024,
1282 prefill_chunk: 64,
1283 };
1284 assert_eq!(config.ifindex, 5);
1285 assert_eq!(config.queue_id, 3);
1286 assert!(!config.zero_copy);
1287 assert_eq!(config.fill_ring_size, 512);
1288 assert_eq!(config.rx_ring_size, 1024);
1289 assert_eq!(config.tx_ring_size, 256);
1290 assert_eq!(config.completion_ring_size, 128);
1291 assert!(config.shared_umem);
1292 }
1293
1294 #[test]
1295 fn test_xsk_config_clone() {
1296 let config = XskConfig {
1297 ifindex: 42,
1298 queue_id: 7,
1299 ..Default::default()
1300 };
1301 let cloned = config.clone();
1302 assert_eq!(cloned.ifindex, 42);
1303 assert_eq!(cloned.queue_id, 7);
1304 }
1305
1306 #[test]
1307 fn test_xsk_state_transitions() {
1308 let config = XskConfig::default();
1309 let mut socket = XskSocket::new(config).unwrap();
1310 assert_eq!(socket.state(), XskState::Created);
1311 assert!(!socket.is_active());
1312
1313 let result = socket.create_socket();
1314 if result.is_ok() {
1315 assert_eq!(socket.state(), XskState::Configured);
1316 }
1317 }
1318
1319 #[test]
1320 fn test_xsk_socket_debug_format() {
1321 let config = XskConfig::default();
1322 let socket = XskSocket::new(config).unwrap();
1323 let debug = format!("{:?}", socket);
1324 assert!(debug.contains("XskSocket"));
1325 assert!(debug.contains("state"));
1326 assert!(debug.contains("active"));
1327 }
1328
1329 #[test]
1330 fn test_xsk_ring_sizes_from_config() {
1331 let config = XskConfig {
1332 fill_ring_size: 256,
1333 rx_ring_size: 512,
1334 tx_ring_size: 1024,
1335 completion_ring_size: 2048,
1336 ..Default::default()
1337 };
1338 let socket = XskSocket::new(config).unwrap();
1339
1340 assert_eq!(socket.fill_ring().capacity(), 256);
1341 assert_eq!(socket.rx_ring().capacity(), 512);
1342 assert_eq!(socket.tx_ring().capacity(), 1024);
1343 assert_eq!(socket.completion_ring().capacity(), 2048);
1344 }
1345
1346 #[test]
1347 fn test_xsk_queue_id_config() {
1348 let config = XskConfig {
1349 queue_id: 42,
1350 ..Default::default()
1351 };
1352 let socket = XskSocket::new(config).unwrap();
1353 let desc = socket.descriptor_engine().allocate(0, 42).unwrap();
1354 assert_eq!(desc.owner(), 42);
1355 }
1356
1357 #[test]
1358 fn test_xsk_zero_copy_config() {
1359 let config = XskConfig {
1360 zero_copy: true,
1361 ..Default::default()
1362 };
1363 assert!(config.zero_copy);
1364
1365 let config = XskConfig {
1366 zero_copy: false,
1367 ..Default::default()
1368 };
1369 assert!(!config.zero_copy);
1370 }
1371
1372 #[test]
1373 fn test_xsk_send_xdp_batch() {
1374 let config = XskConfig::default();
1375 let mut socket = XskSocket::new(config).unwrap();
1376
1377 let _ = socket.descriptor_engine().allocate(0, 42).unwrap();
1381 let _ = socket.descriptor_engine().allocate(1, 42).unwrap();
1382
1383 let xdp_descs = [
1384 XdpDesc {
1385 addr: 0, len: 100,
1387 options: 0,
1388 },
1389 XdpDesc {
1390 addr: 4096, len: 200,
1392 options: 0,
1393 },
1394 ];
1395
1396 let sent = socket.send_xdp_batch(&xdp_descs).unwrap();
1397 assert_eq!(sent, 2);
1398 assert_eq!(socket.tx_ring().available_data(), 2);
1399 }
1400
1401 #[test]
1402 fn test_xsk_receive_and_refill() {
1403 let config = XskConfig::default();
1404 let mut socket = XskSocket::new(config).unwrap();
1405
1406 socket.fill_descriptors(4).unwrap();
1407 let descs = socket.fill_ring_mut().dequeue_batch(4).unwrap();
1408 socket.rx_ring_mut().enqueue_batch(&descs).unwrap();
1409
1410 let received = socket.receive_and_refill(4).unwrap();
1411 assert_eq!(received.len(), 4);
1412 }
1413
1414 #[test]
1415 fn test_xsk_fill_descriptors_zero_count() {
1416 let config = XskConfig::default();
1417 let mut socket = XskSocket::new(config).unwrap();
1418
1419 let filled = socket.fill_descriptors(0).unwrap();
1420 assert_eq!(filled, 0);
1421 }
1422
1423 #[test]
1424 fn test_xsk_receive_empty_rx_ring() {
1425 let config = XskConfig::default();
1426 let mut socket = XskSocket::new(config).unwrap();
1427
1428 let received = socket.receive_batch(10).unwrap();
1429 assert!(received.is_empty());
1430 }
1431
1432 #[test]
1433 fn test_xsk_recycle_empty_completion_ring() {
1434 let config = XskConfig::default();
1435 let mut socket = XskSocket::new(config).unwrap();
1436
1437 let recycled = socket.recycle_completed(10).unwrap();
1438 assert_eq!(recycled, 0);
1439 }
1440
1441 #[test]
1442 fn test_xsk_state_variants() {
1443 let states = vec![
1444 XskState::Created,
1445 XskState::Configured,
1446 XskState::Bound,
1447 XskState::Closed,
1448 ];
1449 for state in states {
1450 let debug = format!("{:?}", state);
1451 assert!(!debug.is_empty());
1452 }
1453 }
1454
1455 #[test]
1456 fn test_xsk_socket_fd_none_in_simulation() {
1457 let config = XskConfig::default();
1458 let socket = XskSocket::new(config).unwrap();
1459 assert!(socket.fd().is_none());
1460 }
1461
1462 #[test]
1463 fn test_xsk_umem_none_initially() {
1464 let config = XskConfig::default();
1465 let socket = XskSocket::new(config).unwrap();
1466 assert!(socket.umem().is_none());
1467 }
1468
1469 #[test]
1470 fn test_xsk_conservation_after_send_and_recycle() {
1471 let config = XskConfig::default();
1472 let mut socket = XskSocket::new(config).unwrap();
1473
1474 let d0 = socket.descriptor_engine().allocate(10, 0).unwrap();
1475 let d1 = socket.descriptor_engine().allocate(11, 0).unwrap();
1476
1477 assert!(socket.descriptor_engine().verify_conservation());
1478
1479 let descs = [d0, d1];
1480 socket.send_batch(&descs).unwrap();
1481
1482 let tx_descs = socket.tx_ring_mut().dequeue_batch(2).unwrap();
1483 socket.completion_ring_mut().enqueue_batch(&tx_descs).unwrap();
1484
1485 socket.recycle_completed(2).unwrap();
1486
1487 assert!(socket.descriptor_engine().verify_conservation());
1488 }
1489
1490 #[test]
1491 fn test_build_umem_reg_pure_logic() {
1492 let addr = 0x7f00_0000usize as *const u8;
1494 let reg = build_umem_reg(addr, 2 * 1024 * 1024, 4096, 256);
1495 assert_eq!(reg.addr, 0x7f00_0000u64);
1496 assert_eq!(reg.len, 2 * 1024 * 1024u64);
1497 assert_eq!(reg.chunk_size, 4096);
1498 assert_eq!(reg.headroom, 256);
1499 assert_eq!(reg.flags, 0, "保留标志必须置 0");
1500 assert_eq!(reg.tx_metadata_len, 0, "不启用 TX metadata");
1501
1502 let reg0 = build_umem_reg(std::ptr::null(), 4096, 2048, 0);
1504 assert_eq!(reg0.addr, 0);
1505 assert_eq!(reg0.headroom, 0);
1506 }
1507
1508 #[test]
1509 fn test_to_ring_offsets_pure_logic() {
1510 let k = libc::xdp_ring_offset {
1512 producer: 0,
1513 consumer: 64,
1514 desc: 128,
1515 flags: 192,
1516 };
1517 let off = to_ring_offsets(&k, 256);
1518 assert_eq!(off.producer, 0);
1519 assert_eq!(off.consumer, 64);
1520 assert_eq!(off.desc, 128);
1521 assert_eq!(off.flags, 192);
1522 assert_eq!(
1523 off.len,
1524 128 + 256 * std::mem::size_of::<XdpDesc>() as u64,
1525 "mmap 长度必须覆盖 desc 偏移 + 全部描述符"
1526 );
1527 assert_eq!(std::mem::size_of::<XdpDesc>(), 16);
1529 }
1530
1531 #[test]
1532 fn test_xsk_new_is_simulation_mode() {
1533 let socket = XskSocket::new(XskConfig::default()).unwrap();
1535 assert!(!socket.is_kernel_mode());
1536 assert!(!socket.rx_ring().is_kernel_mode());
1537 assert!(!socket.fill_ring().is_kernel_mode());
1538 assert!(socket.fd().is_none());
1539 }
1540
1541 #[test]
1542 fn test_xsk_bind_rejects_uncreated_umem() {
1543 let mut socket = match XskSocket::new(XskConfig::default()) {
1545 Ok(s) => s,
1546 Err(_) => return,
1547 };
1548 if socket.create_socket().is_err() {
1549 return; }
1551 let umem = Arc::new(
1552 UmemManager::new(crate::umem::UmemConfig {
1553 size: 4096 * 16,
1554 hugepage: false,
1555 locked: false,
1556 shared: false,
1557 })
1558 .unwrap(),
1559 );
1560 let result = socket.bind(umem);
1562 assert!(result.is_err(), "UMEM 未 mmap 时 bind 必须失败");
1563 assert!(!socket.is_kernel_mode(), "失败后不得进入内核模式(无混合状态)");
1564 }
1565
1566 #[test]
1567 fn test_xsk_new_real_fail_closed_on_invalid_ifindex() {
1568 let mut umem = UmemManager::new(crate::umem::UmemConfig {
1570 size: 4096 * 64,
1571 hugepage: false,
1572 locked: false,
1573 shared: false,
1574 })
1575 .unwrap();
1576 umem.create().unwrap();
1577 let config = XskConfig {
1578 ifindex: 0, queue_id: 0,
1580 zero_copy: false,
1581 fill_ring_size: 64,
1582 rx_ring_size: 64,
1583 tx_ring_size: 64,
1584 completion_ring_size: 64,
1585 shared_umem: false,
1586 frame_size: 4096,
1587 headroom: 0,
1588 so_rcvbuf: 1024 * 1024,
1589 so_sndbuf: 1024 * 1024,
1590 prefill_chunk: 64,
1591 };
1592 let result = XskSocket::new_real(config, Arc::new(umem));
1593 match result {
1594 Ok(s) => {
1595 assert!(s.is_kernel_mode());
1597 assert_eq!(s.state(), XskState::Bound);
1598 }
1599 Err(e) => {
1600 let msg = format!("{e}");
1602 assert!(!msg.is_empty());
1603 }
1604 }
1605 }
1606
1607 #[test]
1608 fn test_prefill_fill_ring_kernel_mode_logic() {
1609 let config = XskConfig {
1612 fill_ring_size: 16,
1613 frame_size: 4096,
1614 ..Default::default()
1615 };
1616 let mut socket = XskSocket::new(config).unwrap();
1617
1618 let mut umem = UmemManager::new(crate::umem::UmemConfig {
1620 size: 4096 * 8,
1621 hugepage: false,
1622 locked: false,
1623 shared: false,
1624 })
1625 .unwrap();
1626 umem.create().unwrap();
1627 socket.umem = Some(Arc::new(umem));
1628
1629 let desc_off = 64u64;
1631 let ring_size = 16u32;
1632 let len = desc_off + u64::from(ring_size) * 16;
1633 let base = unsafe {
1635 libc::mmap(
1636 std::ptr::null_mut(),
1637 len as usize,
1638 libc::PROT_READ | libc::PROT_WRITE,
1639 libc::MAP_SHARED | libc::MAP_ANONYMOUS,
1640 -1,
1641 0,
1642 )
1643 };
1644 assert_ne!(base, libc::MAP_FAILED);
1645 let base = base as *mut u8;
1646 let offsets = crate::ring::RingOffsets {
1647 producer: 0,
1648 consumer: 8,
1649 desc: desc_off,
1650 flags: 16,
1651 len,
1652 };
1653 socket.fill_ring = unsafe {
1655 XskRing::with_kernel_ring(RingType::Fill, base, offsets)
1656 }
1657 .unwrap();
1658
1659 let filled = socket.prefill_fill_ring().unwrap();
1661 assert_eq!(filled, 8);
1662
1663 unsafe {
1666 assert_eq!((base as *const u32).read(), 8);
1667 for i in 0..8u64 {
1668 let d = (base.add(desc_off as usize) as *const XdpDesc)
1669 .add(i as usize)
1670 .read();
1671 assert_eq!(d.addr, i * 4096, "帧地址必须按 frame_size 步进");
1672 assert_eq!(d.len, 0);
1673 }
1674 }
1675 assert!(socket.descriptor_engine().verify_conservation());
1677 }
1678
1679 #[test]
1680 fn test_prefill_fill_ring_simulation_mode_no_umem() {
1681 let mut socket = XskSocket::new(XskConfig::default()).unwrap();
1683 assert_eq!(socket.prefill_fill_ring().unwrap(), 0);
1684 }
1685
1686 fn anon_mmap_page() -> *mut u8 {
1690 let base = unsafe {
1692 libc::mmap(
1693 std::ptr::null_mut(),
1694 4096,
1695 libc::PROT_READ | libc::PROT_WRITE,
1696 libc::MAP_SHARED | libc::MAP_ANONYMOUS,
1697 -1,
1698 0,
1699 )
1700 };
1701 assert_ne!(base, libc::MAP_FAILED, "匿名 mmap 失败");
1702 base as *mut u8
1703 }
1704
1705 fn page_is_mapped(addr: *const u8) -> bool {
1707 let mut vec = [0u8; 1];
1708 let ret = unsafe { libc::mincore(addr as *mut libc::c_void, 4096, vec.as_mut_ptr()) };
1711 ret == 0
1712 }
1713
1714 #[test]
1715 fn test_ring_mmap_guard_disarm_path_keeps_mapping() {
1716 let base = anon_mmap_page();
1718 {
1719 let mut guard = RingMmapGuard::new();
1720 let slot = guard.register(base, 4096);
1721 guard.disarm(slot);
1722 } unsafe {
1725 base.write(0xAB);
1726 assert_eq!(base.read(), 0xAB, "disarm 后映射必须仍可访问");
1727 }
1728 assert!(page_is_mapped(base), "disarm 的映射不得被 munmap");
1729 unsafe {
1732 libc::munmap(base as *mut libc::c_void, 4096);
1733 }
1734 }
1735
1736 #[test]
1737 fn test_ring_mmap_guard_failure_path_munmaps_all() {
1738 GUARD_TEST_STUB.with(|s| s.set(true));
1741 GUARD_UNMAP_LOG.with(|l| l.borrow_mut().clear());
1742 let b1 = anon_mmap_page();
1743 let b2 = anon_mmap_page();
1744 {
1745 let mut guard = RingMmapGuard::new();
1746 guard.register(b1, 4096);
1747 guard.register(b2, 4096);
1748 }
1750 let log = GUARD_UNMAP_LOG.with(|l| l.borrow().clone());
1751 assert_eq!(
1752 log,
1753 vec![(b1 as usize, 4096), (b2 as usize, 4096)],
1754 "失败路径遗留映射必须全部按登记顺序回收"
1755 );
1756 GUARD_TEST_STUB.with(|s| s.set(false));
1757 GUARD_UNMAP_LOG.with(|l| l.borrow_mut().clear());
1758 unsafe {
1760 libc::munmap(b1 as *mut libc::c_void, 4096);
1761 libc::munmap(b2 as *mut libc::c_void, 4096);
1762 }
1763 }
1764
1765 #[test]
1766 fn test_ring_mmap_guard_partial_disarm_only_munmaps_armed() {
1767 GUARD_TEST_STUB.with(|s| s.set(true));
1769 GUARD_UNMAP_LOG.with(|l| l.borrow_mut().clear());
1770 let b1 = anon_mmap_page();
1771 let b2 = anon_mmap_page();
1772 let b3 = anon_mmap_page();
1773 {
1774 let mut guard = RingMmapGuard::new();
1775 let s1 = guard.register(b1, 4096);
1776 guard.register(b2, 4096);
1777 guard.register(b3, 4096);
1778 guard.disarm(s1);
1779 }
1780 let log = GUARD_UNMAP_LOG.with(|l| l.borrow().clone());
1781 assert_eq!(
1782 log,
1783 vec![(b2 as usize, 4096), (b3 as usize, 4096)],
1784 "仅未移交(armed)映射 b2/b3 须被回收,已移交 b1 不得回收"
1785 );
1786 GUARD_TEST_STUB.with(|s| s.set(false));
1787 GUARD_UNMAP_LOG.with(|l| l.borrow_mut().clear());
1788 unsafe {
1790 libc::munmap(b1 as *mut libc::c_void, 4096);
1791 libc::munmap(b2 as *mut libc::c_void, 4096);
1792 libc::munmap(b3 as *mut libc::c_void, 4096);
1793 }
1794 }
1795
1796 #[test]
1797 fn test_xsk_fill_descriptors_no_double_register() {
1798 let config = XskConfig::default();
1801 let mut socket = XskSocket::new(config).unwrap();
1802
1803 let filled = socket.fill_descriptors(4).unwrap();
1804 assert_eq!(filled, 4);
1805 let (allocated, _) = socket.descriptor_engine().conservation_stats();
1806 assert_eq!(allocated, 4);
1807
1808 let again = socket.fill_descriptors(4).unwrap();
1809 assert_eq!(again, 0, "已分配帧不得重复登记");
1810 let (allocated_after, _) = socket.descriptor_engine().conservation_stats();
1811 assert_eq!(allocated_after, 4, "重复填充不得双计数");
1812 assert!(socket.descriptor_engine().verify_conservation());
1813 }
1814}