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}