xdp_socket/
kick.rs

1//! # XDP Socket Kernel Wakeup
2//!
3//! ## Purpose
4//!
5//! This file implements the `kick` method for the `Socket`. The purpose of this method
6//! is to notify the kernel that it needs to process packets in one of the XDP rings,
7//! especially when the `XDP_USE_NEED_WAKEUP` flag is in use.
8//!
9//! ## How it works
10//!
11//! The `kick` method checks if the `XDP_RING_NEED_WAKEUP` flag is set in the ring's
12//! flags field. If it is (or if the wakeup is manually enforced), it performs a syscall
13//! (`sendto` for TX, `recvfrom` for RX) with a zero-length buffer. This syscall does not
14//! transfer data but serves as a signal to wake up the kernel and prompt it to check the
15//! rings for new descriptors to process.
16//!
17//! ## Main components
18//!
19//! - `impl<const T:_Direction> Socket<T>`: An implementation block for the generic socket.
20//! - `kick()`: The public method that performs the wakeup call to the kernel.
21
22use std::{io, ptr};
23use std::sync::atomic::Ordering;
24
25use crate::socket::{_Direction, _RX, _TX, Socket};
26
27/// Implements the kernel wakeup logic for `Socket`.
28impl<const T: _Direction> Socket<T> {
29    /// Wakes up the kernel to process descriptors in the rings.
30    ///
31    /// This method is used to notify the kernel that it needs to process packets,
32    /// which is particularly important when the `XDP_USE_NEED_WAKEUP` flag is set
33    /// on the socket. It checks if the `XDP_RING_NEED_WAKEUP` flag is set in the
34    /// ring's flags field and, if so, performs a syscall to wake up the kernel.
35    ///
36    /// # How it works
37    ///
38    /// It performs a `sendto` (for TX) or `recvfrom` (for RX) syscall with a
39    /// zero-length buffer. This syscall does not transfer any data but acts as a
40    /// signal to the kernel.
41    ///
42    /// # Returns
43    ///
44    /// Returns `Ok(())` on success. On failure, it returns an `io::Error`, except
45    /// for certain non-critical errors like `EBUSY` or `EAGAIN`. A warning is
46    /// logged for `ENETDOWN`.
47    pub fn kick(&self) -> Result<(), io::Error> {
48        let need_wakeup = unsafe {
49                (*self.x_ring.mmap.flags).load(Ordering::Relaxed) & libc::XDP_RING_NEED_WAKEUP
50                    != 0
51            };
52
53        if need_wakeup {
54            let ret = unsafe {
55                match T {
56                    _TX => libc::sendto(
57                        self.raw_fd,
58                        ptr::null(),
59                        0,
60                        libc::MSG_DONTWAIT | libc::MSG_NOSIGNAL,
61                        ptr::null(),
62                        0,
63                    ),
64                    _RX => libc::recvfrom(
65                        self.raw_fd,
66                        ptr::null_mut(),
67                        0,
68                        libc::MSG_DONTWAIT | libc::MSG_NOSIGNAL,
69                        ptr::null_mut(),
70                        ptr::null_mut(),
71                    ),
72                }
73            };
74
75            if ret < 0 {
76                match io::Error::last_os_error().raw_os_error() {
77                    None | Some(libc::EBUSY | libc::ENOBUFS | libc::EAGAIN) => {}
78                    Some(libc::ENETDOWN) => {
79                        // TODO: better handling
80                        log::warn!("network interface is down, cannot wake up");
81                    }
82                    Some(e) => {
83                        return Err(io::Error::from_raw_os_error(e));
84                    }
85                }
86            }
87        }
88        Ok(())
89    }
90}