xdp_socket/
ring.rs

1//! # AF_XDP Ring Buffer Management
2//!
3//! ## Purpose
4//!
5//! This file defines the core data structures and logic for managing the various
6//! ring buffers used in AF_XDP sockets. These rings are the primary mechanism for
7//! communication between the userspace application and the kernel.
8//!
9//! ## How it works
10//!
11//! It provides a generic `Ring<T>` struct that encapsulates a memory-mapped ring buffer.
12//! This struct provides methods for atomically accessing and updating the producer and
13//! consumer indices, accessing descriptors, and checking ring flags. It also defines
14//! the `XdpDesc` struct for packet descriptors. The `RingType` enum helps manage the
15//! specifics of the four different rings (TX, RX, Fill, Completion), such as their
16//! memory map offsets and socket option names.
17//!
18//! ## Main components
19//!
20//! - `Ring<T>`: A generic struct representing a shared memory ring buffer.
21//! - `RingMmap<T>`: A struct holding the raw memory-mapped components of a ring.
22//! - `XdpDesc`: The descriptor structure for packets in the TX and RX rings, containing
23//!   address, length, and options.
24//! - `RingType`: An enum to differentiate between ring types and handle their specific
25//!   setup requirements.
26
27use crate::mmap::OwnedMmap;
28use std::sync::atomic::AtomicU32;
29use std::{io, mem::size_of, ptr, slice};
30
31/// The size of a single frame in the UMEM, typically 2KB or 4KB.
32pub const FRAME_SIZE: usize = 2048;
33/// The default number of frames to allocate for the UMEM.
34pub const FRAME_COUNT: usize = 4096;
35
36/// Holds the raw memory-mapped components of a ring buffer.
37///
38/// This struct contains raw pointers to the producer/consumer indices, the descriptor
39/// array, and flags within the memory-mapped region. It is managed by the `Ring` struct.
40pub struct RingMmap<T> {
41    /// The memory-mapped region owned by this struct.
42    pub mmap: OwnedMmap,
43    /// A pointer to the atomic producer index of the ring.
44    pub producer: *mut AtomicU32,
45    /// A pointer to the atomic consumer index of the ring.
46    pub consumer: *mut AtomicU32,
47    /// A pointer to the beginning of the descriptor array.
48    pub desc: *mut T,
49    /// A pointer to the atomic flags field of the ring.
50    pub flags: *mut AtomicU32,
51}
52
53
54impl<T> Default for RingMmap<T> {
55    fn default() -> Self {
56        RingMmap {
57            mmap: OwnedMmap(ptr::null_mut(), 0),
58            producer: ptr::null_mut(),
59            consumer: ptr::null_mut(),
60            desc: ptr::null_mut(),
61            flags: ptr::null_mut(),
62        }
63    }
64}
65
66/// An XDP descriptor, used in the TX and RX rings.
67///
68/// This struct corresponds to `struct xdp_desc` in the kernel and describes a
69/// single packet buffer in the UMEM.
70#[repr(C)]
71#[derive(Debug, Clone, Copy, Default)]
72pub struct XdpDesc {
73    /// The address of the packet data within the UMEM.
74    pub addr: u64,
75    /// The length of the packet data.
76    pub len: u32,
77    /// Options for the descriptor, currently unused.
78    pub options: u32,
79}
80
81impl XdpDesc {
82    /// Creates a new `XdpDesc`.
83    pub fn new(addr: u64, len: u32, options: u32) -> Self {
84        XdpDesc { addr, len, options }
85    }
86}
87
88/// A generic, safe wrapper for an AF_XDP ring buffer.
89///
90/// This struct provides safe methods to interact with a memory-mapped ring,
91/// handling atomic operations for producer/consumer indices and access to descriptors.
92#[derive(Default)]
93pub struct Ring<T> {
94    /// The memory-mapped components of the ring.
95    pub mmap: RingMmap<T>,
96    /// The number of descriptors the ring can hold.
97    pub len: usize,
98    /// A mask used for wrapping around the ring (len - 1).
99    pub mod_mask: u32,
100}
101
102impl<T> Ring<T>
103where
104    T: Copy,
105{
106    /// Returns the size of a single UMEM frame.
107    #[inline]
108    pub fn frame_size(&self) -> u64 {
109        FRAME_SIZE as u64
110    }
111
112    /// Memory-maps a ring from a file descriptor.
113    ///
114    /// # How it works
115    ///
116    /// This function calls the lower-level `mmap_ring` function to perform the `mmap`
117    /// syscall with the correct offsets for the given ring type. It then initializes
118    /// a `Ring` struct to manage the mapped memory.
119    pub fn mmap(
120        fd: i32,
121        len: usize,
122        ring_type: u64,
123        offsets: &libc::xdp_ring_offset,
124    ) -> Result<Self, io::Error> {
125        debug_assert!(len.is_power_of_two());
126        Ok(Ring {
127            mmap: mmap_ring(fd, len * size_of::<T>(), offsets, ring_type)?,
128            len,
129            mod_mask: len as u32 - 1,
130        })
131    }
132    /// Atomically reads the consumer index of the ring.
133    pub fn consumer(&self) -> u32 {
134        unsafe { (*self.mmap.consumer).load(std::sync::atomic::Ordering::Acquire) }
135    }
136    /// Atomically reads the producer index of the ring.
137    pub fn producer(&self) -> u32 {
138        unsafe { (*self.mmap.producer).load(std::sync::atomic::Ordering::Acquire) }
139    }
140    /// Atomically updates the producer index of the ring.
141    pub fn update_producer(&mut self, value: u32) {
142        unsafe {
143            (*self.mmap.producer).store(value, std::sync::atomic::Ordering::Release);
144        }
145    }
146    /// Atomically updates the consumer index of the ring.
147    pub fn update_consumer(&mut self, value: u32) {
148        unsafe {
149            (*self.mmap.consumer).store(value, std::sync::atomic::Ordering::Release);
150        }
151    }
152
153    /// Atomically reads the flags of the ring.
154    ///
155    /// Flags can indicate states like `XDP_RING_NEED_WAKEUP`.
156    pub fn flags(&self) -> u32 {
157        unsafe {
158            (*self.mmap.flags).load(std::sync::atomic::Ordering::Acquire)
159        }
160    }
161    /// Increments a value, wrapping it around the ring size.
162    pub fn increment(&self, value: &mut u32) -> u32 {
163        *value = (*value + 1) & (self.len - 1) as u32;
164        *value
165    }
166
167    /// Returns a mutable reference to the descriptor at a given index.
168    ///
169    /// # Panics
170    ///
171    /// This function will panic in debug builds if the index is out of bounds.
172    pub fn mut_desc_at(&mut self, index: u32) -> &mut T {
173        debug_assert!((index as usize) < self.len);
174        unsafe { &mut *self.mmap.desc.add(index as usize) }
175    }
176
177    /// Returns a copy of the descriptor at a given index.
178    ///
179    /// # Panics
180    ///
181    /// This function will panic in debug builds if the index is out of bounds.
182    pub fn desc_at(&self, index: u32) -> T {
183        debug_assert!((index as usize) < self.len);
184        unsafe { *self.mmap.desc.add(index as usize) }
185    }
186}
187
188impl Ring<u64> {
189    /// Fills the ring (typically the Fill Ring) with UMEM frame addresses.
190    ///
191    /// # Arguments
192    /// * `start_frame` - The starting frame number to begin filling from.
193    pub fn fill(&mut self, start_frame: u32) {
194        for i in 0..self.len as u32 {
195            let desc = self.mut_desc_at(i);
196            *desc = (i + start_frame) as u64 * FRAME_SIZE as u64;
197        }
198    }
199}
200
201impl Ring<XdpDesc> {
202    /// Fills the ring (typically the TX ring) with default `XdpDesc` values.
203    ///
204    /// This pre-populates the ring with descriptors pointing to corresponding
205    /// UMEM frames.
206    ///
207    /// # Arguments
208    /// * `start_frame` - The starting frame number to begin filling from.
209    pub fn fill(&mut self, start_frame: u32) {
210        for i in 0..self.len as u32 {
211            let desc = self.mut_desc_at(i);
212            *desc = XdpDesc {
213                addr: (i + start_frame) as u64 * FRAME_SIZE as u64,
214                len: 0,
215                options: 0,
216            }
217        }
218    }
219    /// Returns a mutable byte slice for a packet buffer in the UMEM.
220    ///
221    /// This function gets the descriptor at `index`, calculates the memory address
222    /// within the UMEM, and returns a mutable slice of `len` bytes. It also updates
223    /// the descriptor's length field.
224    ///
225    /// # Panics
226    ///
227    /// This function will panic in debug builds if the index or length are out of bounds.
228    pub(crate) fn mut_bytes_at(&mut self, ptr: *mut u8, index: u32, len: usize) -> &mut [u8] {
229        #[cfg(not(feature="no_safety_checks"))]
230        assert!(index < FRAME_COUNT as u32);
231        #[cfg(not(feature="no_safety_checks"))]
232        assert!((len as u32) < FRAME_SIZE as u32);
233
234        let desc = self.mut_desc_at(index);
235
236        #[cfg(not(feature="no_safety_checks"))]
237        assert!(FRAME_SIZE * FRAME_COUNT > desc.addr as usize + len);
238
239        unsafe {
240            let buf_ptr = ptr.offset(desc.addr as isize);
241            desc.len = len as u32;
242            slice::from_raw_parts_mut(buf_ptr, len)
243        }
244    }
245
246    /// Sets the descriptor at `index` to a specific length.
247    ///
248    /// The address is calculated based on the index and frame size.
249    pub fn set(&mut self, index: u32, len: u32) {
250        #[cfg(not(feature="no_safety_checks"))]
251        assert!(index < FRAME_COUNT as u32);
252        #[cfg(not(feature="no_safety_checks"))]
253        assert!(len < FRAME_SIZE as u32);
254
255        let desc = self.mut_desc_at(index);
256        *desc = XdpDesc {
257            addr: (index as u64 * FRAME_SIZE as u64),
258            len,
259            options: 0,
260        };
261    }
262}
263
264/// A low-level function to memory-map a single AF_XDP ring.
265///
266/// # How it works
267///
268/// It calculates the total size required for the mapping, including the area for
269/// producer/consumer indices and the descriptor array. It then calls `libc::mmap`
270/// with the appropriate file descriptor, size, and page offset for the given ring
271/// type. On success, it returns a `RingMmap` containing pointers to the relevant
272/// parts of the mapped region.
273pub fn mmap_ring<T>(
274    fd: i32,
275    size: usize,
276    offsets: &libc::xdp_ring_offset,
277    ring_type: u64,
278) -> Result<RingMmap<T>, io::Error> {
279    let map_size = (offsets.desc as usize).saturating_add(size);
280    let map_addr = unsafe {
281        libc::mmap(
282            ptr::null_mut(),
283            map_size,
284            libc::PROT_READ | libc::PROT_WRITE,
285            libc::MAP_SHARED | libc::MAP_POPULATE,
286            fd,
287            ring_type as i64,
288        )
289    };
290    if map_addr == libc::MAP_FAILED {
291        return Err(io::Error::last_os_error());
292    }
293    let producer = unsafe { map_addr.add(offsets.producer as usize) as *mut AtomicU32 };
294    let consumer = unsafe { map_addr.add(offsets.consumer as usize) as *mut AtomicU32 };
295    let desc = unsafe { map_addr.add(offsets.desc as usize) as *mut T };
296    let flags = unsafe { map_addr.add(offsets.flags as usize) as *mut AtomicU32 };
297    Ok(RingMmap {
298        mmap: OwnedMmap(map_addr, map_size),
299        producer,
300        consumer,
301        desc,
302        flags,
303    })
304}
305
306/// An enum representing the four types of AF_XDP rings.
307#[derive(Copy, Clone, Debug, PartialEq)]
308pub enum RingType {
309    /// The Transmit (TX) ring, for sending packets.
310    Tx,
311    /// The Receive (RX) ring, for receiving packets.
312    Rx,
313    /// The Fill ring, for providing the kernel with free UMEM frames.
314    Fill,
315    /// The Completion ring, for retrieving used UMEM frames from the kernel.
316    Completion,
317}
318
319impl RingType {
320    fn as_index(&self) -> libc::c_int {
321        match self {
322            RingType::Tx => libc::XDP_TX_RING,
323            RingType::Rx => libc::XDP_RX_RING,
324            RingType::Fill => libc::XDP_UMEM_FILL_RING,
325            RingType::Completion => libc::XDP_UMEM_COMPLETION_RING,
326        }
327    }
328
329    fn as_offset(&self) -> u64 {
330        match self {
331            RingType::Tx => libc::XDP_PGOFF_TX_RING as u64,
332            RingType::Rx => libc::XDP_PGOFF_RX_RING as u64,
333            RingType::Fill => libc::XDP_UMEM_PGOFF_FILL_RING,
334            RingType::Completion => libc::XDP_UMEM_PGOFF_COMPLETION_RING,
335        }
336    }
337
338    /// Sets the size of a specific ring via `setsockopt`.
339    ///
340    /// # Arguments
341    /// * `raw_fd` - The raw file descriptor of the XDP socket.
342    /// * `ring_size` - The number of descriptors for the ring.
343    pub fn set_size(self, raw_fd: libc::c_int, mut ring_size: usize) -> io::Result<()> {
344        if ring_size == 0 && (self == RingType::Fill || self == RingType::Completion) {
345            ring_size = 1 // Fill and Completion rings must have at least one entry
346        }
347        unsafe {
348            if libc::setsockopt(
349                raw_fd,
350                libc::SOL_XDP,
351                self.as_index() as libc::c_int,
352                &ring_size as *const _ as *const libc::c_void,
353                size_of::<u32>() as libc::socklen_t,
354            ) < 0
355            {
356                return Err(io::Error::last_os_error());
357            }
358        }
359        Ok(())
360    }
361    /// Memory-maps a ring of a specific type.
362    ///
363    /// This is a convenience method that selects the correct offsets from `xdp_mmap_offsets`
364    /// based on the `RingType` and then calls the generic `Ring::mmap` function.
365    ///
366    /// # Arguments
367    /// * `raw_fd` - The raw file descriptor of the XDP socket.
368    /// * `offsets` - The struct containing the memory map offsets for all rings.
369    /// * `ring_size` - The number of descriptors for the ring.
370    pub fn mmap<T: Copy>(
371        self,
372        raw_fd: libc::c_int,
373        offsets: &libc::xdp_mmap_offsets,
374        ring_size: usize,
375    ) -> io::Result<Ring<T>> {
376        let ring_offs = match self {
377            RingType::Tx => &offsets.tx,
378            RingType::Rx => &offsets.rx,
379            RingType::Fill => &offsets.fr,
380            _ => &offsets.cr,
381        };
382        Ring::<T>::mmap(raw_fd, ring_size, self.as_offset(), ring_offs)
383    }
384}