1use {
2 crate::{
3 device::{
4 DeviceQueue, RingConsumer, RingMmap, RingProducer, RxFillRing, TxCompletionRing,
5 XdpDesc, mmap_ring,
6 },
7 umem::{Frame, Umem},
8 },
9 libc::{
10 AF_XDP, SOCK_RAW, SOL_XDP, XDP_COPY, XDP_MMAP_OFFSETS, XDP_PGOFF_RX_RING,
11 XDP_PGOFF_TX_RING, XDP_RING_NEED_WAKEUP, XDP_RX_RING, XDP_TX_RING,
12 XDP_UMEM_COMPLETION_RING, XDP_UMEM_FILL_RING, XDP_UMEM_PGOFF_COMPLETION_RING,
13 XDP_UMEM_PGOFF_FILL_RING, XDP_USE_NEED_WAKEUP, XDP_ZEROCOPY, bind, getsockopt, sa_family_t,
14 sendto, setsockopt, sockaddr, sockaddr_xdp, socket, socklen_t, xdp_mmap_offsets,
15 xdp_umem_reg,
16 },
17 std::{
18 io,
19 marker::PhantomData,
20 mem,
21 os::fd::{AsFd, AsRawFd as _, BorrowedFd, FromRawFd as _, OwnedFd, RawFd},
22 ptr,
23 sync::atomic::Ordering,
24 },
25};
26
27pub struct Socket<U: Umem> {
28 fd: OwnedFd,
29 dev_queue: DeviceQueue,
30 umem: U,
31}
32
33impl<U: Umem> Socket<U> {
34 #[allow(clippy::type_complexity)]
35 pub fn new(
36 dev_queue: DeviceQueue,
37 mut umem: U,
38 zero_copy: bool,
39 rx_fill_ring_size: usize,
40 rx_ring_size: usize,
41 tx_completion_ring_size: usize,
42 tx_ring_size: usize,
43 ) -> Result<(Self, Rx<U::Frame>, Tx<U::Frame>), io::Error> {
44 unsafe {
45 let fd = socket(AF_XDP, SOCK_RAW, 0);
46 if fd < 0 {
47 return Err(Error::syscall(
48 "socket(AF_XDP, SOCK_RAW) failed",
49 io::Error::last_os_error(),
50 )
51 .into());
52 }
53 let fd = OwnedFd::from_raw_fd(fd);
54
55 let reg = xdp_umem_reg {
56 addr: umem.as_ptr() as u64,
57 len: umem.len() as u64,
58 chunk_size: umem.frame_size() as u32,
59 headroom: 0,
60 flags: 0,
61 tx_metadata_len: 0,
62 };
63
64 if setsockopt(
65 fd.as_raw_fd(),
66 libc::SOL_XDP,
67 libc::XDP_UMEM_REG,
68 ® as *const _ as *const libc::c_void,
69 mem::size_of::<xdp_umem_reg>() as libc::socklen_t,
70 ) < 0
71 {
72 return Err(Error::syscall(
73 "setsockopt(XDP_UMEM_REG) failed",
74 io::Error::last_os_error(),
75 )
76 .into());
77 }
78
79 for (ring, size) in [
80 (XDP_UMEM_COMPLETION_RING, tx_completion_ring_size),
81 (XDP_UMEM_FILL_RING, rx_fill_ring_size),
82 (XDP_TX_RING, tx_ring_size),
83 (XDP_RX_RING, rx_ring_size),
84 ] {
85 if ring == XDP_RX_RING && size == 0 {
86 continue;
88 }
89
90 if setsockopt(
91 fd.as_raw_fd(),
92 SOL_XDP,
93 ring,
94 &size as *const _ as *const libc::c_void,
95 mem::size_of::<u32>() as socklen_t,
96 ) < 0
97 {
98 return Err(Error::syscall(
99 format!("setsockopt(SOL_XDP, ring={ring}, size={size}) failed",),
100 io::Error::last_os_error(),
101 )
102 .into());
103 }
104 }
105
106 let mut offsets: xdp_mmap_offsets = mem::zeroed();
107 let mut optlen = mem::size_of::<xdp_mmap_offsets>() as socklen_t;
108 if getsockopt(
109 fd.as_raw_fd(),
110 SOL_XDP,
111 XDP_MMAP_OFFSETS,
112 &mut offsets as *mut _ as *mut libc::c_void,
113 &mut optlen,
114 ) < 0
115 {
116 return Err(Error::syscall(
117 "getsockopt(XDP_MMAP_OFFSETS) failed",
118 io::Error::last_os_error(),
119 )
120 .into());
121 }
122
123 let tx_completion_ring = TxCompletionRing::new(
124 mmap_ring(
125 fd.as_raw_fd(),
126 tx_completion_ring_size.saturating_mul(mem::size_of::<u64>()),
127 &offsets.cr,
128 XDP_UMEM_PGOFF_COMPLETION_RING,
129 )
130 .map_err(|source| Error::syscall("mmap completion ring failed", source))?,
131 tx_completion_ring_size as u32,
132 );
133
134 let mut rx_fill_ring = RxFillRing::new(
135 mmap_ring(
136 fd.as_raw_fd(),
137 rx_fill_ring_size.saturating_mul(mem::size_of::<u64>()),
138 &offsets.fr,
139 XDP_UMEM_PGOFF_FILL_RING,
140 )
141 .map_err(|source| Error::syscall("mmap fill ring failed", source))?,
142 rx_fill_ring_size as u32,
143 fd.as_raw_fd(),
144 );
145
146 if zero_copy {
147 for _ in 0..rx_fill_ring_size {
150 let Some(frame) = umem.reserve() else {
151 return Err(Error::InsufficientUmemFrames {
152 required: rx_fill_ring_size,
153 available: umem.available(),
154 }
155 .into());
156 };
157 rx_fill_ring
158 .write(frame)
159 .map_err(|source| Error::syscall("RX fill ring write failed", source))?;
160 }
161 rx_fill_ring.commit();
162 }
163
164 let tx_ring = Some(TxRing::new(
165 mmap_ring(
166 fd.as_raw_fd(),
167 tx_ring_size.saturating_mul(mem::size_of::<XdpDesc>()),
168 &offsets.tx,
169 XDP_PGOFF_TX_RING as u64,
170 )
171 .map_err(|source| Error::syscall("mmap tx ring failed", source))?,
172 tx_ring_size as u32,
173 fd.as_raw_fd(),
174 ));
175
176 let rx_ring = if rx_ring_size > 0 {
177 Some(RxRing::new(
178 mmap_ring(
179 fd.as_raw_fd(),
180 rx_ring_size.saturating_mul(mem::size_of::<XdpDesc>()),
181 &offsets.rx,
182 XDP_PGOFF_RX_RING as u64,
183 )
184 .map_err(|source| Error::syscall("mmap rx ring failed", source))?,
185 rx_ring_size as u32,
186 fd.as_raw_fd(),
187 ))
188 } else {
189 None
190 };
191
192 let sxdp = sockaddr_xdp {
193 sxdp_family: AF_XDP as sa_family_t,
194 sxdp_flags: XDP_USE_NEED_WAKEUP | if zero_copy { XDP_ZEROCOPY } else { XDP_COPY },
196 sxdp_ifindex: dev_queue.if_index(),
197 sxdp_queue_id: dev_queue.id().0 as u32,
198 sxdp_shared_umem_fd: 0,
199 };
200
201 if bind(
202 fd.as_raw_fd(),
203 &sxdp as *const _ as *const sockaddr,
204 mem::size_of::<sockaddr_xdp>() as socklen_t,
205 ) < 0
206 {
207 return Err(Error::syscall(
208 format!(
209 "bind(AF_XDP, ifindex={}, queue={}, flags=0x{:x}) failed",
210 sxdp.sxdp_ifindex, sxdp.sxdp_queue_id, sxdp.sxdp_flags
211 ),
212 io::Error::last_os_error(),
213 )
214 .into());
215 }
216
217 let tx = Tx {
218 completion: tx_completion_ring,
219 ring: tx_ring,
220 };
221 let rx = Rx {
222 fill: rx_fill_ring,
223 ring: rx_ring,
224 };
225 Ok((
226 Self {
227 fd,
228 dev_queue,
229 umem,
230 },
231 rx,
232 tx,
233 ))
234 }
235 }
236
237 pub fn tx(
238 queue: DeviceQueue,
239 umem: U,
240 zero_copy: bool,
241 completion_size: usize,
242 ring_size: usize,
243 ) -> Result<(Self, Tx<U::Frame>), io::Error> {
244 let (fill_size, rx_size) = if zero_copy {
245 let rx = queue
247 .ring_sizes()
248 .ok_or_else(|| io::Error::other("zero copy requires a set ring size"))?
249 .rx;
250 (rx, rx)
251 } else {
252 (1, 0)
254 };
255 let (socket, _, tx) = Self::new(
256 queue,
257 umem,
258 zero_copy,
259 fill_size,
260 rx_size,
261 completion_size,
262 ring_size,
263 )?;
264 Ok((socket, tx))
265 }
266
267 pub fn rx(
268 queue: DeviceQueue,
269 umem: U,
270 zero_copy: bool,
271 fill_size: usize,
272 ring_size: usize,
273 ) -> Result<(Self, Rx<U::Frame>), io::Error> {
274 let (socket, rx, _) = Self::new(queue, umem, zero_copy, fill_size, ring_size, 0, 0)?;
275 Ok((socket, rx))
276 }
277
278 pub fn queue(&self) -> &DeviceQueue {
279 &self.dev_queue
280 }
281
282 pub fn umem(&mut self) -> &mut U {
283 &mut self.umem
284 }
285}
286
287impl<U: Umem> AsFd for Socket<U> {
288 fn as_fd(&self) -> BorrowedFd<'_> {
289 self.fd.as_fd()
290 }
291}
292
293pub struct Tx<F: Frame> {
294 pub completion: TxCompletionRing,
295 pub ring: Option<TxRing<F>>,
296}
297
298pub struct Rx<F: Frame> {
299 pub fill: RxFillRing<F>,
300 pub ring: Option<RxRing>,
301}
302
303pub struct TxRing<F: Frame> {
304 mmap: RingMmap<XdpDesc>,
305 producer: RingProducer,
306 size: u32,
307 fd: RawFd,
308 _frame: PhantomData<F>,
309}
310
311#[derive(Debug)]
312pub struct RingFull<F: Frame>(pub F);
313
314impl<F: Frame> TxRing<F> {
315 fn new(mmap: RingMmap<XdpDesc>, size: u32, fd: RawFd) -> Self {
316 debug_assert!(size.is_power_of_two());
317 Self {
318 producer: RingProducer::new(mmap.producer, mmap.consumer, size),
319 mmap,
320 size,
321 fd,
322 _frame: PhantomData,
323 }
324 }
325
326 pub fn write(&mut self, frame: F, options: u32) -> Result<(), RingFull<F>> {
327 let Some(index) = self.producer.produce() else {
328 return Err(RingFull(frame));
329 };
330 let index = index & self.size.saturating_sub(1);
331 unsafe {
332 let desc = self.mmap.desc.add(index as usize);
333 desc.write(XdpDesc {
334 addr: frame.offset().0 as u64,
335 len: frame.len() as u32,
336 options,
337 });
338 }
339 Ok(())
340 }
341
342 pub fn needs_wakeup(&self) -> bool {
343 unsafe { (*self.mmap.flags).load(Ordering::Relaxed) & XDP_RING_NEED_WAKEUP != 0 }
344 }
345
346 pub fn wake(&self) -> Result<u64, io::Error> {
347 let result = unsafe { sendto(self.fd, ptr::null(), 0, libc::MSG_DONTWAIT, ptr::null(), 0) };
348 if result < 0 {
349 return Err(io::Error::last_os_error());
350 }
351 Ok(result as u64)
352 }
353
354 pub fn capacity(&self) -> usize {
355 self.size as usize
356 }
357
358 pub fn available(&self) -> usize {
359 self.producer.available() as usize
360 }
361
362 pub fn commit(&mut self) {
363 self.producer.commit();
364 }
365
366 pub fn sync(&mut self, commit: bool) {
367 self.producer.sync(commit);
368 }
369}
370
371pub struct RxRing {
372 #[allow(dead_code)]
373 mmap: RingMmap<XdpDesc>,
374 consumer: RingConsumer,
375 size: u32,
376 #[allow(dead_code)]
377 fd: RawFd,
378}
379
380impl RxRing {
381 fn new(mmap: RingMmap<XdpDesc>, size: u32, fd: RawFd) -> Self {
382 debug_assert!(size.is_power_of_two());
383 Self {
384 consumer: RingConsumer::new(mmap.producer, mmap.consumer),
385 mmap,
386 size,
387 fd,
388 }
389 }
390
391 pub fn capacity(&self) -> usize {
392 self.size as usize
393 }
394
395 pub fn available(&self) -> usize {
396 self.consumer.available() as usize
397 }
398
399 pub fn commit(&mut self) {
400 self.consumer.commit();
401 }
402
403 pub fn sync(&mut self, commit: bool) {
404 self.consumer.sync(commit);
405 }
406}
407
408#[derive(Debug, thiserror::Error)]
409enum Error {
410 #[error("{message}: {source}")]
411 Syscall {
412 message: String,
413 #[source]
414 source: io::Error,
415 },
416 #[error(
417 "insufficient UMEM frames for RX fill ring prefill: required={required}, \
418 available={available}"
419 )]
420 InsufficientUmemFrames { required: usize, available: usize },
421}
422
423impl Error {
424 fn syscall(message: impl Into<String>, source: io::Error) -> Self {
425 Self::Syscall {
426 message: message.into(),
427 source,
428 }
429 }
430}
431
432impl From<Error> for io::Error {
433 fn from(error: Error) -> io::Error {
434 io::Error::other(error)
435 }
436}