Skip to main content

agave_xdp/
device.rs

1use {
2    crate::{
3        netlink::MacAddress,
4        route::Router,
5        umem::{Frame, FrameOffset},
6    },
7    libc::{
8        AF_INET, IF_NAMESIZE, SIOCETHTOOL, SIOCGIFADDR, SIOCGIFHWADDR, SOCK_DGRAM, SYS_ioctl,
9        ifreq, mmap, munmap, socket, syscall, xdp_ring_offset,
10    },
11    std::{
12        ffi::{CStr, CString, c_char},
13        fs,
14        io::{self, ErrorKind},
15        marker::PhantomData,
16        mem,
17        net::Ipv4Addr,
18        os::fd::{AsRawFd as _, FromRawFd as _, OwnedFd, RawFd},
19        ptr, slice,
20        sync::atomic::{AtomicU32, Ordering},
21    },
22};
23
24#[derive(Copy, Clone, Debug)]
25pub struct QueueId(pub u64);
26
27pub struct NetworkDevice {
28    if_index: u32,
29    if_name: String,
30}
31
32impl NetworkDevice {
33    pub fn new(name: impl Into<String>) -> Result<Self, io::Error> {
34        let if_name = name.into();
35        let if_name_c = CString::new(if_name.as_bytes())
36            .map_err(|_| io::Error::new(ErrorKind::InvalidInput, "Invalid interface name"))?;
37
38        let if_index = unsafe { libc::if_nametoindex(if_name_c.as_ptr()) };
39
40        if if_index == 0 {
41            return Err(io::Error::last_os_error());
42        }
43
44        Ok(Self { if_index, if_name })
45    }
46
47    pub fn new_from_index(if_index: u32) -> Result<Self, io::Error> {
48        let mut buf = [0u8; 1024];
49        let ret = unsafe { libc::if_indextoname(if_index, buf.as_mut_ptr() as *mut c_char) };
50        if ret.is_null() {
51            return Err(io::Error::last_os_error());
52        }
53
54        let cstr = unsafe { CStr::from_ptr(ret) };
55        let if_name = String::from_utf8_lossy(cstr.to_bytes()).to_string();
56
57        Ok(Self { if_index, if_name })
58    }
59
60    pub fn new_from_default_route() -> Result<Self, io::Error> {
61        let router = Router::new()?;
62        let default_route = router.default().unwrap();
63        NetworkDevice::new_from_index(default_route.if_index)
64    }
65
66    pub fn name(&self) -> &str {
67        &self.if_name
68    }
69
70    pub fn if_index(&self) -> u32 {
71        self.if_index
72    }
73
74    pub fn mac_addr(&self) -> Result<MacAddress, io::Error> {
75        let fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
76        if fd < 0 {
77            return Err(io::Error::last_os_error());
78        }
79        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
80
81        let mut req: ifreq = unsafe { mem::zeroed() };
82        let if_name = CString::new(self.if_name.as_bytes()).unwrap();
83
84        let if_name_bytes = if_name.as_bytes_with_nul();
85        let len = std::cmp::min(if_name_bytes.len(), IF_NAMESIZE);
86        unsafe {
87            std::ptr::copy_nonoverlapping(
88                if_name_bytes.as_ptr() as *const c_char,
89                req.ifr_name.as_mut_ptr(),
90                len,
91            );
92        }
93
94        let result = unsafe { syscall(SYS_ioctl, fd.as_raw_fd(), SIOCGIFHWADDR, &mut req) };
95        if result < 0 {
96            return Err(io::Error::last_os_error());
97        }
98
99        Ok(MacAddress(
100            unsafe {
101                slice::from_raw_parts(req.ifr_ifru.ifru_hwaddr.sa_data.as_ptr() as *const u8, 6)
102            }
103            .try_into()
104            .unwrap(),
105        ))
106    }
107
108    pub fn ipv4_addr(&self) -> Result<Ipv4Addr, io::Error> {
109        let fd = unsafe { libc::socket(libc::AF_INET, libc::SOCK_DGRAM, 0) };
110        if fd < 0 {
111            return Err(io::Error::last_os_error());
112        }
113        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
114
115        let mut req: ifreq = unsafe { mem::zeroed() };
116        let if_name = CString::new(self.if_name.as_bytes()).unwrap();
117
118        let if_name_bytes = if_name.as_bytes_with_nul();
119        let len = std::cmp::min(if_name_bytes.len(), IF_NAMESIZE);
120        unsafe {
121            std::ptr::copy_nonoverlapping(
122                if_name_bytes.as_ptr() as *const c_char,
123                req.ifr_name.as_mut_ptr(),
124                len,
125            );
126        }
127
128        let result = unsafe { syscall(SYS_ioctl, fd.as_raw_fd(), SIOCGIFADDR, &mut req) };
129        if result < 0 {
130            return Err(io::Error::last_os_error());
131        }
132
133        let addr = unsafe {
134            let addr_ptr = &req.ifr_ifru.ifru_addr as *const libc::sockaddr;
135            let sin_addr = (*(addr_ptr as *const libc::sockaddr_in)).sin_addr;
136            Ipv4Addr::from(sin_addr.s_addr.to_ne_bytes())
137        };
138        Ok(addr)
139    }
140
141    pub fn driver(&self) -> io::Result<String> {
142        let path = format!("/sys/class/net/{}/device/driver", self.if_name);
143
144        let path = fs::read_link(path).map_err(|e| {
145            io::Error::new(
146                e.kind(),
147                format!(
148                    "Failed to read driver link for interface {}: {}",
149                    self.if_name, e
150                ),
151            )
152        })?;
153
154        Ok(path.file_name().unwrap().to_str().unwrap().into())
155    }
156
157    pub fn open_queue(&self, queue_id: QueueId) -> Result<DeviceQueue, io::Error> {
158        let ring_sizes = Self::ring_sizes(&self.if_name).ok();
159        Ok(DeviceQueue::new(self.if_index, queue_id, ring_sizes))
160    }
161
162    pub fn ring_sizes(if_name: &str) -> Result<RingSizes, io::Error> {
163        const ETHTOOL_GRINGPARAM: u32 = 0x00000010;
164
165        #[repr(C)]
166        struct EthtoolRingParam {
167            cmd: u32,
168            rx_max_pending: u32,
169            rx_mini_max_pending: u32,
170            rx_jumbo_max_pending: u32,
171            tx_max_pending: u32,
172            rx_pending: u32,
173            rx_mini_pending: u32,
174            rx_jumbo_pending: u32,
175            tx_pending: u32,
176        }
177
178        let fd = unsafe { socket(AF_INET, SOCK_DGRAM, 0) };
179        if fd < 0 {
180            return Err(io::Error::last_os_error());
181        }
182        let fd = unsafe { OwnedFd::from_raw_fd(fd) };
183
184        let mut rp: EthtoolRingParam = unsafe { mem::zeroed() };
185        rp.cmd = ETHTOOL_GRINGPARAM;
186
187        let mut ifr: ifreq = unsafe { mem::zeroed() };
188        unsafe {
189            ptr::copy_nonoverlapping(
190                if_name.as_ptr() as *const c_char,
191                ifr.ifr_name.as_mut_ptr(),
192                if_name.len().min(IF_NAMESIZE),
193            );
194        }
195        ifr.ifr_name[IF_NAMESIZE - 1] = 0;
196        ifr.ifr_ifru.ifru_data = &mut rp as *mut _ as *mut c_char;
197
198        let res = unsafe { syscall(SYS_ioctl, fd.as_raw_fd(), SIOCETHTOOL, &ifr) };
199        if res < 0 {
200            return Err(io::Error::last_os_error());
201        }
202
203        Ok(RingSizes {
204            rx: rp.rx_pending as usize,
205            tx: rp.tx_pending as usize,
206        })
207    }
208}
209
210#[derive(Debug, Clone, Copy, PartialEq, Eq)]
211pub struct RingSizes {
212    pub rx: usize,
213    pub tx: usize,
214}
215
216impl Default for RingSizes {
217    fn default() -> Self {
218        // These are reasonable defaults for devices which don't have a set ring size. Values must
219        // be a power of two.
220        Self { rx: 1024, tx: 1024 }
221    }
222}
223
224pub struct DeviceQueue {
225    if_index: u32,
226    queue_id: QueueId,
227    ring_sizes: Option<RingSizes>,
228    completion: Option<TxCompletionRing>,
229}
230
231impl DeviceQueue {
232    pub fn new(if_index: u32, queue_id: QueueId, ring_sizes: Option<RingSizes>) -> Self {
233        Self {
234            if_index,
235            queue_id,
236            ring_sizes,
237            completion: None,
238        }
239    }
240
241    pub fn if_index(&self) -> u32 {
242        self.if_index
243    }
244
245    pub fn id(&self) -> QueueId {
246        self.queue_id
247    }
248
249    pub fn tx_completion(&mut self) -> Option<&TxCompletionRing> {
250        self.completion.as_ref()
251    }
252
253    pub fn ring_sizes(&self) -> Option<RingSizes> {
254        self.ring_sizes
255    }
256}
257
258pub(crate) struct RingConsumer {
259    producer: *mut AtomicU32,
260    cached_producer: u32,
261    consumer: *mut AtomicU32,
262    cached_consumer: u32,
263}
264
265///Safety: Instances of `RingConsumer` MUST only be resident on one thread at a time
266unsafe impl Send for RingConsumer {}
267
268impl RingConsumer {
269    pub fn new(producer: *mut AtomicU32, consumer: *mut AtomicU32) -> Self {
270        Self {
271            producer,
272            cached_producer: unsafe { (*producer).load(Ordering::Acquire) },
273            consumer,
274            cached_consumer: unsafe { (*consumer).load(Ordering::Relaxed) },
275        }
276    }
277
278    pub fn available(&self) -> u32 {
279        self.cached_producer.wrapping_sub(self.cached_consumer)
280    }
281
282    pub fn consume(&mut self) -> Option<u32> {
283        if self.cached_consumer == self.cached_producer {
284            return None;
285        }
286
287        let index = self.cached_consumer;
288        self.cached_consumer = self.cached_consumer.wrapping_add(1);
289        Some(index)
290    }
291
292    pub fn commit(&mut self) {
293        unsafe { (*self.consumer).store(self.cached_consumer, Ordering::Release) };
294    }
295
296    pub fn sync(&mut self, commit: bool) {
297        if commit {
298            self.commit();
299        }
300        self.cached_producer = unsafe { (*self.producer).load(Ordering::Acquire) };
301    }
302}
303
304pub(crate) struct RingProducer {
305    producer: *mut AtomicU32,
306    cached_producer: u32,
307    consumer: *mut AtomicU32,
308    cached_consumer: u32,
309    size: u32,
310}
311
312///Safety: Instances of `RingProducer` MUST only be resident on one thread at a time
313unsafe impl Send for RingProducer {}
314
315impl RingProducer {
316    pub fn new(producer: *mut AtomicU32, consumer: *mut AtomicU32, size: u32) -> Self {
317        Self {
318            producer,
319            cached_producer: unsafe { (*producer).load(Ordering::Relaxed) },
320            consumer,
321            cached_consumer: unsafe { (*consumer).load(Ordering::Acquire) },
322            size,
323        }
324    }
325
326    pub fn available(&self) -> u32 {
327        self.size
328            .saturating_sub(self.cached_producer.wrapping_sub(self.cached_consumer))
329    }
330
331    pub fn produce(&mut self) -> Option<u32> {
332        if self.available() == 0 {
333            return None;
334        }
335
336        let index = self.cached_producer;
337        self.cached_producer = self.cached_producer.wrapping_add(1);
338        Some(index)
339    }
340
341    pub fn commit(&mut self) {
342        unsafe { (*self.producer).store(self.cached_producer, Ordering::Release) };
343    }
344
345    pub fn sync(&mut self, commit: bool) {
346        if commit {
347            self.commit();
348        }
349        self.cached_consumer = unsafe { (*self.consumer).load(Ordering::Acquire) };
350    }
351}
352
353#[repr(C)]
354#[derive(Debug, Clone)]
355pub(crate) struct XdpDesc {
356    pub(crate) addr: u64,
357    pub(crate) len: u32,
358    pub(crate) options: u32,
359}
360
361pub struct TxCompletionRing {
362    mmap: RingMmap<u64>,
363    consumer: RingConsumer,
364    size: u32,
365}
366
367impl TxCompletionRing {
368    pub(crate) fn new(mmap: RingMmap<u64>, size: u32) -> Self {
369        debug_assert!(size.is_power_of_two());
370        Self {
371            consumer: RingConsumer::new(mmap.producer, mmap.consumer),
372            mmap,
373            size,
374        }
375    }
376
377    pub fn read(&mut self) -> Option<FrameOffset> {
378        let index = self.consumer.consume()? & self.size.saturating_sub(1);
379        let index = unsafe { *self.mmap.desc.add(index as usize) } as usize;
380        Some(FrameOffset(index))
381    }
382
383    pub fn commit(&mut self) {
384        self.consumer.commit();
385    }
386
387    pub fn sync(&mut self, commit: bool) {
388        self.consumer.sync(commit);
389    }
390}
391
392pub struct RxFillRing<F: Frame> {
393    mmap: RingMmap<u64>,
394    producer: RingProducer,
395    size: u32,
396    _fd: RawFd,
397    _frame: PhantomData<F>,
398}
399
400impl<F: Frame> RxFillRing<F> {
401    pub(crate) fn new(mmap: RingMmap<u64>, size: u32, fd: RawFd) -> Self {
402        debug_assert!(size.is_power_of_two());
403        Self {
404            producer: RingProducer::new(mmap.producer, mmap.consumer, size),
405            mmap,
406            size,
407            _fd: fd,
408            _frame: PhantomData,
409        }
410    }
411
412    pub fn write(&mut self, frame: F) -> Result<(), io::Error> {
413        let Some(index) = self.producer.produce() else {
414            return Err(ErrorKind::StorageFull.into());
415        };
416        let index = index & self.size.saturating_sub(1);
417        let desc = unsafe { self.mmap.desc.add(index as usize) };
418        // Safety: index is within the ring so the pointer is valid
419        unsafe {
420            desc.write(frame.offset().0 as u64);
421        }
422
423        Ok(())
424    }
425
426    pub fn commit(&mut self) {
427        self.producer.commit();
428    }
429
430    pub fn sync(&mut self, commit: bool) {
431        self.producer.sync(commit);
432    }
433}
434
435pub struct RingMmap<T> {
436    pub mmap: *const u8,
437    pub mmap_len: usize,
438    pub producer: *mut AtomicU32,
439    pub consumer: *mut AtomicU32,
440    pub desc: *mut T,
441    pub flags: *mut AtomicU32,
442}
443
444///Safety: Instances of `RingMmap<T>` MUST only be resident on one thread at a time
445unsafe impl<T> Send for RingMmap<T> {}
446
447impl<T> Drop for RingMmap<T> {
448    fn drop(&mut self) {
449        unsafe {
450            munmap(self.mmap as *mut _, self.mmap_len);
451        }
452    }
453}
454
455pub(crate) unsafe fn mmap_ring<T>(
456    fd: i32,
457    size: usize,
458    offsets: &xdp_ring_offset,
459    ring_type: u64,
460) -> Result<RingMmap<T>, io::Error> {
461    let map_size = (offsets.desc as usize).saturating_add(size);
462    // Safety: just a libc wrapper. We pass a valid size and file descriptor.
463    let map_addr = unsafe {
464        mmap(
465            ptr::null_mut(),
466            map_size,
467            libc::PROT_READ | libc::PROT_WRITE,
468            libc::MAP_SHARED | libc::MAP_POPULATE,
469            fd,
470            ring_type as i64,
471        )
472    };
473    if ptr::eq(map_addr, libc::MAP_FAILED) {
474        return Err(io::Error::last_os_error());
475    }
476    // Safety: manual pointer arithmetic. We are sure that the given offsets
477    // don't exceed the bounds.
478    unsafe {
479        let producer = map_addr.add(offsets.producer as usize) as *mut AtomicU32;
480        let consumer = map_addr.add(offsets.consumer as usize) as *mut AtomicU32;
481        let desc = map_addr.add(offsets.desc as usize) as *mut T;
482        let flags = map_addr.add(offsets.flags as usize) as *mut AtomicU32;
483        // V1
484        // let flags = map_addr.add(offsets.consumer as usize + mem::size_of::<u32>()) as *mut AtomicU32;
485        Ok(RingMmap {
486            mmap: map_addr as *const u8,
487            mmap_len: map_size,
488            producer,
489            consumer,
490            desc,
491            flags,
492        })
493    }
494}
495
496#[cfg(test)]
497mod test {
498    use super::*;
499
500    #[test]
501    fn test_ring_producer() {
502        let mut producer = AtomicU32::new(0);
503        let mut consumer = AtomicU32::new(0);
504        let size = 16;
505        let mut ring = RingProducer::new(&mut producer as *mut _, &mut consumer as *mut _, size);
506        assert_eq!(ring.available(), size);
507
508        for i in 0..size {
509            assert_eq!(ring.produce(), Some(i));
510            assert_eq!(ring.available(), size - i - 1);
511        }
512        assert_eq!(ring.produce(), None);
513
514        consumer.store(1, Ordering::Release);
515        assert_eq!(ring.produce(), None);
516        ring.commit();
517        assert_eq!(ring.produce(), None);
518        ring.sync(true);
519        assert_eq!(ring.produce(), Some(16));
520        assert_eq!(ring.produce(), None);
521
522        consumer.store(2, Ordering::Release);
523        ring.sync(true);
524        assert_eq!(ring.produce(), Some(17));
525    }
526
527    #[test]
528    fn test_ring_producer_wrap_around() {
529        let size = 16;
530        let mut producer = AtomicU32::new(u32::MAX - 1);
531        let mut consumer = AtomicU32::new(u32::MAX - size - 1);
532        let mut ring = RingProducer::new(&mut producer as *mut _, &mut consumer as *mut _, size);
533        assert_eq!(ring.available(), 0);
534
535        consumer.fetch_add(1, Ordering::Release);
536        ring.sync(true);
537        assert_eq!(ring.produce(), Some(u32::MAX - 1));
538        consumer.fetch_add(1, Ordering::Release);
539        ring.sync(true);
540        assert_eq!(ring.produce(), Some(u32::MAX));
541        consumer.fetch_add(1, Ordering::Release);
542        ring.sync(true);
543        assert_eq!(ring.produce(), Some(0));
544        consumer.fetch_add(1, Ordering::Release);
545        ring.sync(true);
546        assert_eq!(ring.produce(), Some(1));
547    }
548
549    #[test]
550    fn test_ring_consumer() {
551        let mut producer = AtomicU32::new(0);
552        let mut consumer = AtomicU32::new(0);
553        let size = 16;
554        let mut ring = RingConsumer::new(&mut producer as *mut _, &mut consumer as *mut _);
555        assert_eq!(ring.available(), 0);
556
557        producer.store(1, Ordering::Release);
558        assert_eq!(ring.available(), 0);
559        ring.sync(true);
560        assert_eq!(ring.available(), 1);
561
562        producer.store(size, Ordering::Release);
563        ring.sync(true);
564
565        for i in 0..size {
566            assert_eq!(ring.consume(), Some(i));
567            assert_eq!(ring.available(), size - i - 1);
568        }
569        assert_eq!(ring.consume(), None);
570    }
571
572    #[test]
573    fn test_ring_consumer_wrap_around() {
574        let mut producer = AtomicU32::new(u32::MAX - 1);
575        let mut consumer = AtomicU32::new(u32::MAX - 1);
576        let mut ring = RingConsumer::new(&mut producer as *mut _, &mut consumer as *mut _);
577        assert_eq!(ring.available(), 0);
578        assert_eq!(ring.consume(), None);
579
580        producer.fetch_add(1, Ordering::Release);
581        ring.sync(true);
582        assert_eq!(ring.consume(), Some(u32::MAX - 1));
583
584        producer.store(0, Ordering::Release);
585        ring.sync(true);
586        assert_eq!(ring.available(), 1);
587        assert_eq!(ring.consume(), Some(u32::MAX));
588
589        producer.fetch_add(1, Ordering::Release);
590        ring.sync(true);
591        assert_eq!(ring.consume(), Some(0));
592
593        producer.fetch_add(1, Ordering::Release);
594        ring.sync(true);
595        assert_eq!(ring.consume(), Some(1));
596    }
597}