← Documents Documentation/networking/af_xdp.rst GitHub 원문 ↗

Linux 6.18.37 · Networking

AF_XDP

AF_XDP socket, UMEM, 네 종류 ring, XSKMAP, 공유·wakeup option과 multi-buffer 사용법을 설명합니다.

Source pathDocumentation/networking/af_xdp.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약·해설

af_xdp.rst:1-855

AF_XDP는 XDP가 선택한 ingress frame을 사용자 공간 UMEM으로 전달하고, RX·TX descriptor ring을 통해 고속으로 교환하는 socket address family입니다. 성능의 핵심은 packet data가 아니라 frame 소유권을 FILL·RX·TX·COMPLETION ring 사이에서 이동시키는 데 있습니다.

AF_XDP RX·TX 전체 경로
NIC RX queueXDP programXSKMAP 검증RX ring descriptor사용자 공간
사용자 공간FILL ring addrKernel RX bufferNIC RX queue
사용자 공간TX ring descriptorNIC TXCOMPLETION ring addr사용자 공간 재사용

XDP redirect에서 frame 재사용까지의 두 방향 흐름입니다.

AF_XDP ring 소유권
RingProducerConsumer내용
FILL사용자 공간KernelRX에 제공할 UMEM `addr`
RXKernel사용자 공간수신 `struct xdp_desc`
TX사용자 공간Kernel송신 `struct xdp_desc`
COMPLETIONKernel사용자 공간송신 완료 UMEM `addr`

각 ring의 producer·consumer와 frame 소유권 이동을 정리했습니다.

SPSC ring index 진행
`producer` indexdescriptor writewrite barrier`producer++`
`consumer` indexread barrierdescriptor read`consumer++`

Producer와 consumer는 서로 다른 index를 단독으로 갱신합니다.

공유 UMEM 구성 규칙
Socket 관계RX·TX ringFILL·COMPLETION pair분배
같은 netdev·queueSocket마다 별도공유 1쌍XDP program·XSKMAP
다른 queue 또는 netdevSocket마다 별도tuple마다 1쌍NIC steering
동시 접근SPSC 준수SPSC 준수사용자 동기화 필요

고유한 장치·queue tuple 수가 UMEM ring pair 수를 결정합니다.

need_wakeup 판단
FILL ring `need_wakeup=1`Buffer 공급`poll()`NIC RX 재개
TX ring `need_wakeup=1``poll()` 또는 `sendto()`TX 처리
`need_wakeup=0`System call 생략

Flag가 설정된 경우에만 system call로 kernel을 깨웁니다.

주요 AF_XDP option
Option단계역할
`XDP_COPY`bindCopy mode 강제
`XDP_ZEROCOPY`bindZero-copy mode 강제
`XDP_SHARED_UMEM`bindUMEM 공유
`XDP_USE_NEED_WAKEUP`bind필요할 때만 syscall
`XDP_USE_SG`bindMulti-buffer 허용
`XDP_UMEM_REG`setsockoptUMEM 등록·chunk·headroom
`SO_BINDTODEVICE`setsockoptInterface 고정
`XDP_STATISTICS`getsockoptDrop·invalid 통계

Bind flag와 socket option의 역할을 한눈에 정리했습니다.

Multi-buffer descriptor chain
Descriptor 0 `CONTD=1`Frame 0Descriptor 1 `CONTD=1`Frame 1Descriptor 2 `CONTD=0`마지막 frame·완전한 packet

`XDP_PKT_CONTD`는 다음 descriptor에서 패킷이 이어지는지를 표시합니다.

Multi-buffer mode 한계
Mode최대 frame 수초과·지원 확인
Copy`CONFIG_MAX_SKB_FRAGS + 1`전체 packet invalid·drop
Portable copy app18 이하 권장최소 config 17 기준
Zero-copyNIC hardware 한계`NETDEV_A_DEV_XDP_ZC_MAX_SEGS`

Copy와 zero-copy의 frame 수 제한이 다릅니다.

Traffic이 보이지 않을 때
Ingress trafficNIC queue 분배Bind한 queue인가?XSKMAP의 같은 queue socketRX ring
불일치`ethtool -L`로 queue 1개또는 `ethtool -N`으로 flow steering

Socket과 실제 ingress queue의 일치 여부를 먼저 확인합니다.

UMEM buffer 단일 소유권 규칙
동시 등록결과규칙
FILL + TX수신 중 송신해 packet 손상한 ring에만 둠
서로 다른 FILL ring여러 queue·device가 동시에 사용소유권 분리
COMPLETION 후 재사용송신 완료가 확인됨안전한 재활용

동일 buffer를 동시에 여러 ring에 넣으면 NIC read·write가 충돌합니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ======
4 AF_XDP
5 ======
6
7 Overview
8 ========
9
10 AF_XDP is an address family that is optimized for high performance
11 packet processing.
12
13 This document assumes that the reader is familiar with BPF and XDP. If
14 not, the Cilium project has an excellent reference guide at
15 http://cilium.readthedocs.io/en/latest/bpf/.
16
17 Using the XDP_REDIRECT action from an XDP program, the program can
18 redirect ingress frames to other XDP enabled netdevs, using the
19 bpf_redirect_map() function. AF_XDP sockets enable the possibility for
20 XDP programs to redirect frames to a memory buffer in a user-space
21 application.
22
23 An AF_XDP socket (XSK) is created with the normal socket()
24 syscall. Associated with each XSK are two rings: the RX ring and the
25 TX ring. A socket can receive packets on the RX ring and it can send
26 packets on the TX ring. These rings are registered and sized with the
27 setsockopts XDP_RX_RING and XDP_TX_RING, respectively. It is mandatory
28 to have at least one of these rings for each socket. An RX or TX
29 descriptor ring points to a data buffer in a memory area called a
30 UMEM. RX and TX can share the same UMEM so that a packet does not have
31 to be copied between RX and TX. Moreover, if a packet needs to be kept
32 for a while due to a possible retransmit, the descriptor that points
33 to that packet can be changed to point to another and reused right
34 away. This again avoids copying data.
35
36 The UMEM consists of a number of equally sized chunks. A descriptor in
37 one of the rings references a frame by referencing its addr. The addr
38 is simply an offset within the entire UMEM region. The user space
39 allocates memory for this UMEM using whatever means it feels is most
40 appropriate (malloc, mmap, huge pages, etc). This memory area is then
41 registered with the kernel using the new setsockopt XDP_UMEM_REG. The
42 UMEM also has two rings: the FILL ring and the COMPLETION ring. The
43 FILL ring is used by the application to send down addr for the kernel
44 to fill in with RX packet data. References to these frames will then
45 appear in the RX ring once each packet has been received. The
46 COMPLETION ring, on the other hand, contains frame addr that the
47 kernel has transmitted completely and can now be used again by user
48 space, for either TX or RX. Thus, the frame addrs appearing in the
49 COMPLETION ring are addrs that were previously transmitted using the
50 TX ring. In summary, the RX and FILL rings are used for the RX path
51 and the TX and COMPLETION rings are used for the TX path.
52
53 The socket is then finally bound with a bind() call to a device and a
54 specific queue id on that device, and it is not until bind is
55 completed that traffic starts to flow.
56
57 The UMEM can be shared between processes, if desired. If a process
58 wants to do this, it simply skips the registration of the UMEM and its
59 corresponding two rings, sets the XDP_SHARED_UMEM flag in the bind
60 call and submits the XSK of the process it would like to share UMEM
61 with as well as its own newly created XSK socket. The new process will
62 then receive frame addr references in its own RX ring that point to
63 this shared UMEM. Note that since the ring structures are
64 single-consumer / single-producer (for performance reasons), the new
65 process has to create its own socket with associated RX and TX rings,
66 since it cannot share this with the other process. This is also the
67 reason that there is only one set of FILL and COMPLETION rings per
68 UMEM. It is the responsibility of a single process to handle the UMEM.
69
70 How is then packets distributed from an XDP program to the XSKs? There
71 is a BPF map called XSKMAP (or BPF_MAP_TYPE_XSKMAP in full). The
72 user-space application can place an XSK at an arbitrary place in this
73 map. The XDP program can then redirect a packet to a specific index in
74 this map and at this point XDP validates that the XSK in that map was
75 indeed bound to that device and ring number. If not, the packet is
76 dropped. If the map is empty at that index, the packet is also
77 dropped. This also means that it is currently mandatory to have an XDP
78 program loaded (and one XSK in the XSKMAP) to be able to get any
79 traffic to user space through the XSK.
80
81 AF_XDP can operate in two different modes: XDP_SKB and XDP_DRV. If the
82 driver does not have support for XDP, or XDP_SKB is explicitly chosen
83 when loading the XDP program, XDP_SKB mode is employed that uses SKBs
84 together with the generic XDP support and copies out the data to user
85 space. A fallback mode that works for any network device. On the other
86 hand, if the driver has support for XDP, it will be used by the AF_XDP
87 code to provide better performance, but there is still a copy of the
88 data into user space.
89
90 Concepts
91 ========
92
93 In order to use an AF_XDP socket, a number of associated objects need
94 to be setup. These objects and their options are explained in the
95 following sections.
96
97 For an overview on how AF_XDP works, you can also take a look at the
98 Linux Plumbers paper from 2018 on the subject:
99 http://vger.kernel.org/lpc_net2018_talks/lpc18_paper_af_xdp_perf-v2.pdf. Do
100 NOT consult the paper from 2017 on "AF_PACKET v4", the first attempt
101 at AF_XDP. Nearly everything changed since then. Jonathan Corbet has
102 also written an excellent article on LWN, "Accelerating networking
103 with AF_XDP". It can be found at https://lwn.net/Articles/750845/.
104
105 UMEM
106 ----
107
108 UMEM is a region of virtual contiguous memory, divided into
109 equal-sized frames. An UMEM is associated to a netdev and a specific
110 queue id of that netdev. It is created and configured (chunk size,
111 headroom, start address and size) by using the XDP_UMEM_REG setsockopt
112 system call. A UMEM is bound to a netdev and queue id, via the bind()
113 system call.
114
115 An AF_XDP is socket linked to a single UMEM, but one UMEM can have
116 multiple AF_XDP sockets. To share an UMEM created via one socket A,
117 the next socket B can do this by setting the XDP_SHARED_UMEM flag in
118 struct sockaddr_xdp member sxdp_flags, and passing the file descriptor
119 of A to struct sockaddr_xdp member sxdp_shared_umem_fd.
120
121 The UMEM has two single-producer/single-consumer rings that are used
122 to transfer ownership of UMEM frames between the kernel and the
123 user-space application.
124
125 Rings
126 -----
127
128 There are a four different kind of rings: FILL, COMPLETION, RX and
129 TX. All rings are single-producer/single-consumer, so the user-space
130 application need explicit synchronization of multiple
131 processes/threads are reading/writing to them.
132
133 The UMEM uses two rings: FILL and COMPLETION. Each socket associated
134 with the UMEM must have an RX queue, TX queue or both. Say, that there
135 is a setup with four sockets (all doing TX and RX). Then there will be
136 one FILL ring, one COMPLETION ring, four TX rings and four RX rings.
137
138 The rings are head(producer)/tail(consumer) based rings. A producer
139 writes the data ring at the index pointed out by struct xdp_ring
140 producer member, and increasing the producer index. A consumer reads
141 the data ring at the index pointed out by struct xdp_ring consumer
142 member, and increasing the consumer index.
143
144 The rings are configured and created via the _RING setsockopt system
145 calls and mmapped to user-space using the appropriate offset to mmap()
146 (XDP_PGOFF_RX_RING, XDP_PGOFF_TX_RING, XDP_UMEM_PGOFF_FILL_RING and
147 XDP_UMEM_PGOFF_COMPLETION_RING).
148
149 The size of the rings need to be of size power of two.
150
151 UMEM Fill Ring
152 ~~~~~~~~~~~~~~
153
154 The FILL ring is used to transfer ownership of UMEM frames from
155 user-space to kernel-space. The UMEM addrs are passed in the ring. As
156 an example, if the UMEM is 64k and each chunk is 4k, then the UMEM has
157 16 chunks and can pass addrs between 0 and 64k.
158
159 Frames passed to the kernel are used for the ingress path (RX rings).
160
161 The user application produces UMEM addrs to this ring. Note that, if
162 running the application with aligned chunk mode, the kernel will mask
163 the incoming addr. E.g. for a chunk size of 2k, the log2(2048) LSB of
164 the addr will be masked off, meaning that 2048, 2050 and 3000 refers
165 to the same chunk. If the user application is run in the unaligned
166 chunks mode, then the incoming addr will be left untouched.
167
168
169 UMEM Completion Ring
170 ~~~~~~~~~~~~~~~~~~~~
171
172 The COMPLETION Ring is used transfer ownership of UMEM frames from
173 kernel-space to user-space. Just like the FILL ring, UMEM indices are
174 used.
175
176 Frames passed from the kernel to user-space are frames that has been
177 sent (TX ring) and can be used by user-space again.
178
179 The user application consumes UMEM addrs from this ring.
180
181
182 RX Ring
183 ~~~~~~~
184
185 The RX ring is the receiving side of a socket. Each entry in the ring
186 is a struct xdp_desc descriptor. The descriptor contains UMEM offset
187 (addr) and the length of the data (len).
188
189 If no frames have been passed to kernel via the FILL ring, no
190 descriptors will (or can) appear on the RX ring.
191
192 The user application consumes struct xdp_desc descriptors from this
193 ring.
194
195 TX Ring
196 ~~~~~~~
197
198 The TX ring is used to send frames. The struct xdp_desc descriptor is
199 filled (index, length and offset) and passed into the ring.
200
201 To start the transfer a sendmsg() system call is required. This might
202 be relaxed in the future.
203
204 The user application produces struct xdp_desc descriptors to this
205 ring.
206
207 Libbpf
208 ======
209
210 Libbpf is a helper library for eBPF and XDP that makes using these
211 technologies a lot simpler. It also contains specific helper functions
212 in tools/testing/selftests/bpf/xsk.h for facilitating the use of
213 AF_XDP. It contains two types of functions: those that can be used to
214 make the setup of AF_XDP socket easier and ones that can be used in the
215 data plane to access the rings safely and quickly.
216
217 We recommend that you use this library unless you have become a power
218 user. It will make your program a lot simpler.
219
220 XSKMAP / BPF_MAP_TYPE_XSKMAP
221 ============================
222
223 On XDP side there is a BPF map type BPF_MAP_TYPE_XSKMAP (XSKMAP) that
224 is used in conjunction with bpf_redirect_map() to pass the ingress
225 frame to a socket.
226
227 The user application inserts the socket into the map, via the bpf()
228 system call.
229
230 Note that if an XDP program tries to redirect to a socket that does
231 not match the queue configuration and netdev, the frame will be
232 dropped. E.g. an AF_XDP socket is bound to netdev eth0 and
233 queue 17. Only the XDP program executing for eth0 and queue 17 will
234 successfully pass data to the socket. Please refer to the sample
235 application (samples/bpf/) in for an example.
236
237 Configuration Flags and Socket Options
238 ======================================
239
240 These are the various configuration flags that can be used to control
241 and monitor the behavior of AF_XDP sockets.
242
243 XDP_COPY and XDP_ZEROCOPY bind flags
244 ------------------------------------
245
246 When you bind to a socket, the kernel will first try to use zero-copy
247 copy. If zero-copy is not supported, it will fall back on using copy
248 mode, i.e. copying all packets out to user space. But if you would
249 like to force a certain mode, you can use the following flags. If you
250 pass the XDP_COPY flag to the bind call, the kernel will force the
251 socket into copy mode. If it cannot use copy mode, the bind call will
252 fail with an error. Conversely, the XDP_ZEROCOPY flag will force the
253 socket into zero-copy mode or fail.
254
255 XDP_SHARED_UMEM bind flag
256 -------------------------
257
258 This flag enables you to bind multiple sockets to the same UMEM. It
259 works on the same queue id, between queue ids and between
260 netdevs/devices. In this mode, each socket has their own RX and TX
261 rings as usual, but you are going to have one or more FILL and
262 COMPLETION ring pairs. You have to create one of these pairs per
263 unique netdev and queue id tuple that you bind to.
264
265 Starting with the case were we would like to share a UMEM between
266 sockets bound to the same netdev and queue id. The UMEM (tied to the
267 fist socket created) will only have a single FILL ring and a single
268 COMPLETION ring as there is only on unique netdev,queue_id tuple that
269 we have bound to. To use this mode, create the first socket and bind
270 it in the normal way. Create a second socket and create an RX and a TX
271 ring, or at least one of them, but no FILL or COMPLETION rings as the
272 ones from the first socket will be used. In the bind call, set he
273 XDP_SHARED_UMEM option and provide the initial socket's fd in the
274 sxdp_shared_umem_fd field. You can attach an arbitrary number of extra
275 sockets this way.
276
277 What socket will then a packet arrive on? This is decided by the XDP
278 program. Put all the sockets in the XSK_MAP and just indicate which
279 index in the array you would like to send each packet to. A simple
280 round-robin example of distributing packets is shown below:
281
282 .. code-block:: c
283
284 #include <linux/bpf.h>
285 #include "bpf_helpers.h"
286
287 #define MAX_SOCKS 16
288
289 struct {
290 __uint(type, BPF_MAP_TYPE_XSKMAP);
291 __uint(max_entries, MAX_SOCKS);
292 __uint(key_size, sizeof(int));
293 __uint(value_size, sizeof(int));
294 } xsks_map SEC(".maps");
295
296 static unsigned int rr;
297
298 SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
299 {
300 rr = (rr + 1) & (MAX_SOCKS - 1);
301
302 return bpf_redirect_map(&xsks_map, rr, XDP_DROP);
303 }
304
305 Note, that since there is only a single set of FILL and COMPLETION
306 rings, and they are single producer, single consumer rings, you need
307 to make sure that multiple processes or threads do not use these rings
308 concurrently. There are no synchronization primitives in the
309 libbpf code that protects multiple users at this point in time.
310
311 Libbpf uses this mode if you create more than one socket tied to the
312 same UMEM. However, note that you need to supply the
313 XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD libbpf_flag with the
314 xsk_socket__create calls and load your own XDP program as there is no
315 built in one in libbpf that will route the traffic for you.
316
317 The second case is when you share a UMEM between sockets that are
318 bound to different queue ids and/or netdevs. In this case you have to
319 create one FILL ring and one COMPLETION ring for each unique
320 netdev,queue_id pair. Let us say you want to create two sockets bound
321 to two different queue ids on the same netdev. Create the first socket
322 and bind it in the normal way. Create a second socket and create an RX
323 and a TX ring, or at least one of them, and then one FILL and
324 COMPLETION ring for this socket. Then in the bind call, set he
325 XDP_SHARED_UMEM option and provide the initial socket's fd in the
326 sxdp_shared_umem_fd field as you registered the UMEM on that
327 socket. These two sockets will now share one and the same UMEM.
328
329 There is no need to supply an XDP program like the one in the previous
330 case where sockets were bound to the same queue id and
331 device. Instead, use the NIC's packet steering capabilities to steer
332 the packets to the right queue. In the previous example, there is only
333 one queue shared among sockets, so the NIC cannot do this steering. It
334 can only steer between queues.
335
336 In libbpf, you need to use the xsk_socket__create_shared() API as it
337 takes a reference to a FILL ring and a COMPLETION ring that will be
338 created for you and bound to the shared UMEM. You can use this
339 function for all the sockets you create, or you can use it for the
340 second and following ones and use xsk_socket__create() for the first
341 one. Both methods yield the same result.
342
343 Note that a UMEM can be shared between sockets on the same queue id
344 and device, as well as between queues on the same device and between
345 devices at the same time.
346
347 XDP_USE_NEED_WAKEUP bind flag
348 -----------------------------
349
350 This option adds support for a new flag called need_wakeup that is
351 present in the FILL ring and the TX ring, the rings for which user
352 space is a producer. When this option is set in the bind call, the
353 need_wakeup flag will be set if the kernel needs to be explicitly
354 woken up by a syscall to continue processing packets. If the flag is
355 zero, no syscall is needed.
356
357 If the flag is set on the FILL ring, the application needs to call
358 poll() to be able to continue to receive packets on the RX ring. This
359 can happen, for example, when the kernel has detected that there are no
360 more buffers on the FILL ring and no buffers left on the RX HW ring of
361 the NIC. In this case, interrupts are turned off as the NIC cannot
362 receive any packets (as there are no buffers to put them in), and the
363 need_wakeup flag is set so that user space can put buffers on the
364 FILL ring and then call poll() so that the kernel driver can put these
365 buffers on the HW ring and start to receive packets.
366
367 If the flag is set for the TX ring, it means that the application
368 needs to explicitly notify the kernel to send any packets put on the
369 TX ring. This can be accomplished either by a poll() call, as in the
370 RX path, or by calling sendto().
371
372 An example with the use of libbpf helpers would look like this for the
373 TX path:
374
375 .. code-block:: c
376
377 if (xsk_ring_prod__needs_wakeup(&my_tx_ring))
378 sendto(xsk_socket__fd(xsk_handle), NULL, 0, MSG_DONTWAIT, NULL, 0);
379
380 I.e., only use the syscall if the flag is set.
381
382 We recommend that you always enable this mode as it usually leads to
383 better performance especially if you run the application and the
384 driver on the same core, but also if you use different cores for the
385 application and the kernel driver, as it reduces the number of
386 syscalls needed for the TX path.
387
388 XDP_{RX|TX|UMEM_FILL|UMEM_COMPLETION}_RING setsockopts
389 ------------------------------------------------------
390
391 These setsockopts sets the number of descriptors that the RX, TX,
392 FILL, and COMPLETION rings respectively should have. It is mandatory
393 to set the size of at least one of the RX and TX rings. If you set
394 both, you will be able to both receive and send traffic from your
395 application, but if you only want to do one of them, you can save
396 resources by only setting up one of them. Both the FILL ring and the
397 COMPLETION ring are mandatory as you need to have a UMEM tied to your
398 socket. But if the XDP_SHARED_UMEM flag is used, any socket after the
399 first one does not have a UMEM and should in that case not have any
400 FILL or COMPLETION rings created as the ones from the shared UMEM will
401 be used. Note, that the rings are single-producer single-consumer, so
402 do not try to access them from multiple processes at the same
403 time. See the XDP_SHARED_UMEM section.
404
405 In libbpf, you can create Rx-only and Tx-only sockets by supplying
406 NULL to the rx and tx arguments, respectively, to the
407 xsk_socket__create function.
408
409 If you create a Tx-only socket, we recommend that you do not put any
410 packets on the fill ring. If you do this, drivers might think you are
411 going to receive something when you in fact will not, and this can
412 negatively impact performance.
413
414 XDP_UMEM_REG setsockopt
415 -----------------------
416
417 This setsockopt registers a UMEM to a socket. This is the area that
418 contain all the buffers that packet can reside in. The call takes a
419 pointer to the beginning of this area and the size of it. Moreover, it
420 also has parameter called chunk_size that is the size that the UMEM is
421 divided into. It can only be 2K or 4K at the moment. If you have an
422 UMEM area that is 128K and a chunk size of 2K, this means that you
423 will be able to hold a maximum of 128K / 2K = 64 packets in your UMEM
424 area and that your largest packet size can be 2K.
425
426 There is also an option to set the headroom of each single buffer in
427 the UMEM. If you set this to N bytes, it means that the packet will
428 start N bytes into the buffer leaving the first N bytes for the
429 application to use. The final option is the flags field, but it will
430 be dealt with in separate sections for each UMEM flag.
431
432 SO_BINDTODEVICE setsockopt
433 --------------------------
434
435 This is a generic SOL_SOCKET option that can be used to tie AF_XDP
436 socket to a particular network interface. It is useful when a socket
437 is created by a privileged process and passed to a non-privileged one.
438 Once the option is set, kernel will refuse attempts to bind that socket
439 to a different interface. Updating the value requires CAP_NET_RAW.
440
441 XDP_MAX_TX_SKB_BUDGET setsockopt
442 --------------------------------
443
444 This setsockopt sets the maximum number of descriptors that can be handled
445 and passed to the driver at one send syscall. It is applied in the copy
446 mode to allow application to tune the per-socket maximum iteration for
447 better throughput and less frequency of send syscall.
448 Allowed range is [32, xs->tx->nentries].
449
450 XDP_STATISTICS getsockopt
451 -------------------------
452
453 Gets drop statistics of a socket that can be useful for debug
454 purposes. The supported statistics are shown below:
455
456 .. code-block:: c
457
458 struct xdp_statistics {
459 __u64 rx_dropped; /* Dropped for reasons other than invalid desc */
460 __u64 rx_invalid_descs; /* Dropped due to invalid descriptor */
461 __u64 tx_invalid_descs; /* Dropped due to invalid descriptor */
462 };
463
464 XDP_OPTIONS getsockopt
465 ----------------------
466
467 Gets options from an XDP socket. The only one supported so far is
468 XDP_OPTIONS_ZEROCOPY which tells you if zero-copy is on or not.
469
470 Multi-Buffer Support
471 ====================
472
473 With multi-buffer support, programs using AF_XDP sockets can receive
474 and transmit packets consisting of multiple buffers both in copy and
475 zero-copy mode. For example, a packet can consist of two
476 frames/buffers, one with the header and the other one with the data,
477 or a 9K Ethernet jumbo frame can be constructed by chaining together
478 three 4K frames.
479
480 Some definitions:
481
482 * A packet consists of one or more frames
483
484 * A descriptor in one of the AF_XDP rings always refers to a single
485 frame. In the case the packet consists of a single frame, the
486 descriptor refers to the whole packet.
487
488 To enable multi-buffer support for an AF_XDP socket, use the new bind
489 flag XDP_USE_SG. If this is not provided, all multi-buffer packets
490 will be dropped just as before. Note that the XDP program loaded also
491 needs to be in multi-buffer mode. This can be accomplished by using
492 "xdp.frags" as the section name of the XDP program used.
493
494 To represent a packet consisting of multiple frames, a new flag called
495 XDP_PKT_CONTD is introduced in the options field of the Rx and Tx
496 descriptors. If it is true (1) the packet continues with the next
497 descriptor and if it is false (0) it means this is the last descriptor
498 of the packet. Why the reverse logic of end-of-packet (eop) flag found
499 in many NICs? Just to preserve compatibility with non-multi-buffer
500 applications that have this bit set to false for all packets on Rx,
501 and the apps set the options field to zero for Tx, as anything else
502 will be treated as an invalid descriptor.
503
504 These are the semantics for producing packets onto AF_XDP Tx ring
505 consisting of multiple frames:
506
507 * When an invalid descriptor is found, all the other
508 descriptors/frames of this packet are marked as invalid and not
509 completed. The next descriptor is treated as the start of a new
510 packet, even if this was not the intent (because we cannot guess
511 the intent). As before, if your program is producing invalid
512 descriptors you have a bug that must be fixed.
513
514 * Zero length descriptors are treated as invalid descriptors.
515
516 * For copy mode, the maximum supported number of frames in a packet is
517 equal to CONFIG_MAX_SKB_FRAGS + 1. If it is exceeded, all
518 descriptors accumulated so far are dropped and treated as
519 invalid. To produce an application that will work on any system
520 regardless of this config setting, limit the number of frags to 18,
521 as the minimum value of the config is 17.
522
523 * For zero-copy mode, the limit is up to what the NIC HW
524 supports. Usually at least five on the NICs we have checked. We
525 consciously chose to not enforce a rigid limit (such as
526 CONFIG_MAX_SKB_FRAGS + 1) for zero-copy mode, as it would have
527 resulted in copy actions under the hood to fit into what limit the
528 NIC supports. Kind of defeats the purpose of zero-copy mode. How to
529 probe for this limit is explained in the "probe for multi-buffer
530 support" section.
531
532 On the Rx path in copy-mode, the xsk core copies the XDP data into
533 multiple descriptors, if needed, and sets the XDP_PKT_CONTD flag as
534 detailed before. Zero-copy mode works the same, though the data is not
535 copied. When the application gets a descriptor with the XDP_PKT_CONTD
536 flag set to one, it means that the packet consists of multiple buffers
537 and it continues with the next buffer in the following
538 descriptor. When a descriptor with XDP_PKT_CONTD == 0 is received, it
539 means that this is the last buffer of the packet. AF_XDP guarantees
540 that only a complete packet (all frames in the packet) is sent to the
541 application. If there is not enough space in the AF_XDP Rx ring, all
542 frames of the packet will be dropped.
543
544 If application reads a batch of descriptors, using for example the libxdp
545 interfaces, it is not guaranteed that the batch will end with a full
546 packet. It might end in the middle of a packet and the rest of the
547 buffers of that packet will arrive at the beginning of the next batch,
548 since the libxdp interface does not read the whole ring (unless you
549 have an enormous batch size or a very small ring size).
550
551 An example program each for Rx and Tx multi-buffer support can be found
552 later in this document.
553
554 Usage
555 -----
556
557 In order to use AF_XDP sockets two parts are needed. The user-space
558 application and the XDP program. For a complete setup and usage example,
559 please refer to the xdp-project at
560 https://github.com/xdp-project/bpf-examples/tree/main/AF_XDP-example.
561
562 The XDP code sample is the following:
563
564 .. code-block:: c
565
566 SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
567 {
568 int index = ctx->rx_queue_index;
569
570 // A set entry here means that the corresponding queue_id
571 // has an active AF_XDP socket bound to it.
572 if (bpf_map_lookup_elem(&xsks_map, &index))
573 return bpf_redirect_map(&xsks_map, index, 0);
574
575 return XDP_PASS;
576 }
577
578 A simple but not so performance ring dequeue and enqueue could look
579 like this:
580
581 .. code-block:: c
582
583 // struct xdp_rxtx_ring {
584 // __u32 *producer;
585 // __u32 *consumer;
586 // struct xdp_desc *desc;
587 // };
588
589 // struct xdp_umem_ring {
590 // __u32 *producer;
591 // __u32 *consumer;
592 // __u64 *desc;
593 // };
594
595 // typedef struct xdp_rxtx_ring RING;
596 // typedef struct xdp_umem_ring RING;
597
598 // typedef struct xdp_desc RING_TYPE;
599 // typedef __u64 RING_TYPE;
600
601 int dequeue_one(RING *ring, RING_TYPE *item)
602 {
603 __u32 entries = *ring->producer - *ring->consumer;
604
605 if (entries == 0)
606 return -1;
607
608 // read-barrier!
609
610 *item = ring->desc[*ring->consumer & (RING_SIZE - 1)];
611 (*ring->consumer)++;
612 return 0;
613 }
614
615 int enqueue_one(RING *ring, const RING_TYPE *item)
616 {
617 u32 free_entries = RING_SIZE - (*ring->producer - *ring->consumer);
618
619 if (free_entries == 0)
620 return -1;
621
622 ring->desc[*ring->producer & (RING_SIZE - 1)] = *item;
623
624 // write-barrier!
625
626 (*ring->producer)++;
627 return 0;
628 }
629
630 But please use the libbpf functions as they are optimized and ready to
631 use. Will make your life easier.
632
633 Usage Multi-Buffer Rx
634 ---------------------
635
636 Here is a simple Rx path pseudo-code example (using libxdp interfaces
637 for simplicity). Error paths have been excluded to keep it short:
638
639 .. code-block:: c
640
641 void rx_packets(struct xsk_socket_info *xsk)
642 {
643 static bool new_packet = true;
644 u32 idx_rx = 0, idx_fq = 0;
645 static char *pkt;
646
647 int rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx);
648
649 xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);
650
651 for (int i = 0; i < rcvd; i++) {
652 struct xdp_desc *desc = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++);
653 char *frag = xsk_umem__get_data(xsk->umem->buffer, desc->addr);
654 bool eop = !(desc->options & XDP_PKT_CONTD);
655
656 if (new_packet)
657 pkt = frag;
658 else
659 add_frag_to_pkt(pkt, frag);
660
661 if (eop)
662 process_pkt(pkt);
663
664 new_packet = eop;
665
666 *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = desc->addr;
667 }
668
669 xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
670 xsk_ring_cons__release(&xsk->rx, rcvd);
671 }
672
673 Usage Multi-Buffer Tx
674 ---------------------
675
676 Here is an example Tx path pseudo-code (using libxdp interfaces for
677 simplicity) ignoring that the umem is finite in size, and that we
678 eventually will run out of packets to send. Also assumes pkts.addr
679 points to a valid location in the umem.
680
681 .. code-block:: c
682
683 void tx_packets(struct xsk_socket_info *xsk, struct pkt *pkts,
684 int batch_size)
685 {
686 u32 idx, i, pkt_nb = 0;
687
688 xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx);
689
690 for (i = 0; i < batch_size;) {
691 u64 addr = pkts[pkt_nb].addr;
692 u32 len = pkts[pkt_nb].size;
693
694 do {
695 struct xdp_desc *tx_desc;
696
697 tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i++);
698 tx_desc->addr = addr;
699
700 if (len > xsk_frame_size) {
701 tx_desc->len = xsk_frame_size;
702 tx_desc->options = XDP_PKT_CONTD;
703 } else {
704 tx_desc->len = len;
705 tx_desc->options = 0;
706 pkt_nb++;
707 }
708 len -= tx_desc->len;
709 addr += xsk_frame_size;
710
711 if (i == batch_size) {
712 /* Remember len, addr, pkt_nb for next iteration.
713 * Skipped for simplicity.
714 */
715 break;
716 }
717 } while (len);
718 }
719
720 xsk_ring_prod__submit(&xsk->tx, i);
721 }
722
723 Probing for Multi-Buffer Support
724 --------------------------------
725
726 To discover if a driver supports multi-buffer AF_XDP in SKB or DRV
727 mode, use the XDP_FEATURES feature of netlink in linux/netdev.h to
728 query for NETDEV_XDP_ACT_RX_SG support. This is the same flag as for
729 querying for XDP multi-buffer support. If XDP supports multi-buffer in
730 a driver, then AF_XDP will also support that in SKB and DRV mode.
731
732 To discover if a driver supports multi-buffer AF_XDP in zero-copy
733 mode, use XDP_FEATURES and first check the NETDEV_XDP_ACT_XSK_ZEROCOPY
734 flag. If it is set, it means that at least zero-copy is supported and
735 you should go and check the netlink attribute
736 NETDEV_A_DEV_XDP_ZC_MAX_SEGS in linux/netdev.h. An unsigned integer
737 value will be returned stating the max number of frags that are
738 supported by this device in zero-copy mode. These are the possible
739 return values:
740
741 1: Multi-buffer for zero-copy is not supported by this device, as max
742 one fragment supported means that multi-buffer is not possible.
743
744 >=2: Multi-buffer is supported in zero-copy mode for this device. The
745 returned number signifies the max number of frags supported.
746
747 For an example on how these are used through libbpf, please take a
748 look at tools/testing/selftests/bpf/xskxceiver.c.
749
750 Multi-Buffer Support for Zero-Copy Drivers
751 ------------------------------------------
752
753 Zero-copy drivers usually use the batched APIs for Rx and Tx
754 processing. Note that the Tx batch API guarantees that it will provide
755 a batch of Tx descriptors that ends with full packet at the end. This
756 to facilitate extending a zero-copy driver with multi-buffer support.
757
758 Sample application
759 ==================
760 There is a xdpsock benchmarking/test application that can be found at
761 https://github.com/xdp-project/bpf-examples/tree/main/AF_XDP-example
762 that demonstrates how to use AF_XDP sockets with private
763 UMEMs. Say that you would like your UDP traffic from port 4242 to end
764 up in queue 16, that we will enable AF_XDP on. Here, we use ethtool
765 for this::
766
767 ethtool -N p3p2 rx-flow-hash udp4 fn
768 ethtool -N p3p2 flow-type udp4 src-port 4242 dst-port 4242 \
769 action 16
770
771 Running the rxdrop benchmark in XDP_DRV mode can then be done
772 using::
773
774 samples/bpf/xdpsock -i p3p2 -q 16 -r -N
775
776 For XDP_SKB mode, use the switch "-S" instead of "-N" and all options
777 can be displayed with "-h", as usual.
778
779 This sample application uses libbpf to make the setup and usage of
780 AF_XDP simpler. If you want to know how the raw uapi of AF_XDP is
781 really used to make something more advanced, take a look at the libbpf
782 code in tools/testing/selftests/bpf/xsk.[ch].
783
784 FAQ
785 =======
786
787 Q: I am not seeing any traffic on the socket. What am I doing wrong?
788
789 A: When a netdev of a physical NIC is initialized, Linux usually
790 allocates one RX and TX queue pair per core. So on a 8 core system,
791 queue ids 0 to 7 will be allocated, one per core. In the AF_XDP
792 bind call or the xsk_socket__create libbpf function call, you
793 specify a specific queue id to bind to and it is only the traffic
794 towards that queue you are going to get on you socket. So in the
795 example above, if you bind to queue 0, you are NOT going to get any
796 traffic that is distributed to queues 1 through 7. If you are
797 lucky, you will see the traffic, but usually it will end up on one
798 of the queues you have not bound to.
799
800 There are a number of ways to solve the problem of getting the
801 traffic you want to the queue id you bound to. If you want to see
802 all the traffic, you can force the netdev to only have 1 queue, queue
803 id 0, and then bind to queue 0. You can use ethtool to do this::
804
805 sudo ethtool -L <interface> combined 1
806
807 If you want to only see part of the traffic, you can program the
808 NIC through ethtool to filter out your traffic to a single queue id
809 that you can bind your XDP socket to. Here is one example in which
810 UDP traffic to and from port 4242 are sent to queue 2::
811
812 sudo ethtool -N <interface> rx-flow-hash udp4 fn
813 sudo ethtool -N <interface> flow-type udp4 src-port 4242 dst-port \
814 4242 action 2
815
816 A number of other ways are possible all up to the capabilities of
817 the NIC you have.
818
819 Q: Can I use the XSKMAP to implement a switch between different umems
820 in copy mode?
821
822 A: The short answer is no, that is not supported at the moment. The
823 XSKMAP can only be used to switch traffic coming in on queue id X
824 to sockets bound to the same queue id X. The XSKMAP can contain
825 sockets bound to different queue ids, for example X and Y, but only
826 traffic goming in from queue id Y can be directed to sockets bound
827 to the same queue id Y. In zero-copy mode, you should use the
828 switch, or other distribution mechanism, in your NIC to direct
829 traffic to the correct queue id and socket.
830
831 Q: My packets are sometimes corrupted. What is wrong?
832
833 A: Care has to be taken not to feed the same buffer in the UMEM into
834 more than one ring at the same time. If you for example feed the
835 same buffer into the FILL ring and the TX ring at the same time, the
836 NIC might receive data into the buffer at the same time it is
837 sending it. This will cause some packets to become corrupted. Same
838 thing goes for feeding the same buffer into the FILL rings
839 belonging to different queue ids or netdevs bound with the
840 XDP_SHARED_UMEM flag.
841
842 Credits
843 =======
844
845 - Björn Töpel (AF_XDP core)
846 - Magnus Karlsson (AF_XDP core)
847 - Alexander Duyck
848 - Alexei Starovoitov
849 - Daniel Borkmann
850 - Jesper Dangaard Brouer
851 - John Fastabend
852 - Jonathan Corbet (LWN coverage)
853 - Michael S. Tsirkin
854 - Qi Z Zhang
855 - Willem de Bruijn
856

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

개요

1-89

`.. SPDX-License-Identifier: GPL-2.0`

AF_XDP

개요

AF_XDP는 고성능 패킷 처리에 최적화된 address family입니다.

이 문서는 독자가 BPF와 XDP에 익숙하다고 가정합니다. 그렇지 않다면 Cilium 프로젝트의 참고 안내서 `http://cilium.readthedocs.io/en/latest/bpf/`를 참조하십시오.

XDP 프로그램은 `XDP_REDIRECT` action과 `bpf_redirect_map()` 함수를 사용해 ingress frame을 다른 XDP 지원 netdev로 redirect할 수 있습니다. AF_XDP socket은 XDP 프로그램이 frame을 사용자 공간 애플리케이션의 memory buffer로 redirect할 수 있게 합니다.

AF_XDP socket(XSK)은 일반 `socket()` system call로 만듭니다. 각 XSK에는 RX ring과 TX ring이 연결됩니다. Socket은 RX ring으로 패킷을 받고 TX ring으로 패킷을 보냅니다. 두 ring은 각각 `XDP_RX_RING`, `XDP_TX_RING` setsockopt로 등록하고 크기를 정하며, 각 socket에는 이 둘 중 적어도 하나가 반드시 있어야 합니다.

RX 또는 TX descriptor ring은 UMEM이라는 memory 영역의 data buffer를 가리킵니다. RX와 TX는 같은 UMEM을 공유할 수 있어 RX와 TX 사이에서 패킷을 복사하지 않아도 됩니다. 재전송 가능성 때문에 패킷을 잠시 보관해야 할 때도 그 패킷을 가리키는 descriptor를 다른 frame을 가리키도록 바꿔 즉시 재사용할 수 있으므로 data copy를 다시 피할 수 있습니다.

UMEM은 크기가 같은 여러 chunk로 이루어집니다. Ring의 descriptor는 `addr`로 frame을 참조하며, 이 값은 전체 UMEM 영역 안의 offset입니다. 사용자 공간은 `malloc`, `mmap`, huge page 등 적절한 방법으로 UMEM memory를 할당하고 `XDP_UMEM_REG` setsockopt로 kernel에 등록합니다.

UMEM에도 FILL ring과 COMPLETION ring이 있습니다. 애플리케이션은 kernel이 RX 패킷 data를 채울 frame의 `addr`를 FILL ring으로 보냅니다. 패킷을 받으면 해당 frame 참조가 RX ring에 나타납니다. COMPLETION ring에는 kernel이 송신을 완전히 끝내 사용자 공간이 TX 또는 RX에 다시 쓸 수 있는 frame `addr`가 들어갑니다. 따라서 RX 경로는 FILL·RX ring을, TX 경로는 TX·COMPLETION ring을 사용합니다.

마지막으로 `bind()`를 호출해 socket을 장치의 특정 queue id에 묶습니다. `bind()`가 끝나야 traffic이 흐르기 시작합니다.

원한다면 여러 process가 UMEM을 공유할 수 있습니다. 새 process는 UMEM과 두 UMEM ring 등록을 생략하고 `bind` 호출에 `XDP_SHARED_UMEM` flag와 공유 대상 process의 XSK를 지정한 뒤 자신의 새 XSK를 전달합니다. 그러면 새 process의 RX ring에 공유 UMEM을 가리키는 frame `addr`가 들어옵니다.

Ring 구조는 성능을 위해 single-consumer/single-producer이므로 새 process는 다른 process와 RX·TX ring을 공유할 수 없고 자기 socket과 RX·TX ring을 만들어야 합니다. 같은 이유로 UMEM 하나에는 FILL·COMPLETION ring 집합이 하나뿐이며, 단일 process가 UMEM을 처리할 책임을 집니다.

XDP 프로그램에서 XSK로 패킷을 분배할 때는 `XSKMAP`, 정식 이름으로 `BPF_MAP_TYPE_XSKMAP`을 사용합니다. 사용자 공간 애플리케이션은 map의 임의 index에 XSK를 넣고, XDP 프로그램은 특정 index로 패킷을 redirect합니다. XDP는 그 XSK가 해당 장치와 queue 번호에 실제로 bind되었는지 검증합니다. 일치하지 않거나 해당 index가 비어 있으면 패킷을 drop합니다. 따라서 현재 XSK를 통해 사용자 공간으로 traffic을 받으려면 XDP 프로그램이 load되어 있고 XSKMAP에 적어도 하나의 XSK가 있어야 합니다.

AF_XDP는 `XDP_SKB`와 `XDP_DRV` 두 mode로 동작할 수 있습니다. Driver가 XDP를 지원하지 않거나 XDP 프로그램을 load할 때 `XDP_SKB`를 명시하면 generic XDP와 SKB를 사용하고 data를 사용자 공간으로 복사하는 fallback mode가 작동합니다. Driver가 XDP를 지원하면 AF_XDP가 이를 사용해 성능을 높이지만, 이 경우에도 사용자 공간으로의 data copy는 남아 있습니다.

.. SPDX-License-Identifier: GPL-2.0

======
AF_XDP
======

Overview
========

AF_XDP is an address family that is optimized for high performance
packet processing.

This document assumes that the reader is familiar with BPF and XDP. If
not, the Cilium project has an excellent reference guide at
http://cilium.readthedocs.io/en/latest/bpf/.

Using the XDP_REDIRECT action from an XDP program, the program can
redirect ingress frames to other XDP enabled netdevs, using the
bpf_redirect_map() function. AF_XDP sockets enable the possibility for
XDP programs to redirect frames to a memory buffer in a user-space
application.

An AF_XDP socket (XSK) is created with the normal socket()
syscall. Associated with each XSK are two rings: the RX ring and the
TX ring. A socket can receive packets on the RX ring and it can send
packets on the TX ring. These rings are registered and sized with the
setsockopts XDP_RX_RING and XDP_TX_RING, respectively. It is mandatory
to have at least one of these rings for each socket. An RX or TX
descriptor ring points to a data buffer in a memory area called a
UMEM. RX and TX can share the same UMEM so that a packet does not have
to be copied between RX and TX. Moreover, if a packet needs to be kept
for a while due to a possible retransmit, the descriptor that points
to that packet can be changed to point to another and reused right
away. This again avoids copying data.

The UMEM consists of a number of equally sized chunks. A descriptor in
one of the rings references a frame by referencing its addr. The addr
is simply an offset within the entire UMEM region. The user space
allocates memory for this UMEM using whatever means it feels is most
appropriate (malloc, mmap, huge pages, etc). This memory area is then
registered with the kernel using the new setsockopt XDP_UMEM_REG. The
UMEM also has two rings: the FILL ring and the COMPLETION ring. The
FILL ring is used by the application to send down addr for the kernel
to fill in with RX packet data. References to these frames will then
appear in the RX ring once each packet has been received. The
COMPLETION ring, on the other hand, contains frame addr that the
kernel has transmitted completely and can now be used again by user
space, for either TX or RX. Thus, the frame addrs appearing in the
COMPLETION ring are addrs that were previously transmitted using the
TX ring. In summary, the RX and FILL rings are used for the RX path
and the TX and COMPLETION rings are used for the TX path.

The socket is then finally bound with a bind() call to a device and a
specific queue id on that device, and it is not until bind is
completed that traffic starts to flow.

The UMEM can be shared between processes, if desired. If a process
wants to do this, it simply skips the registration of the UMEM and its
corresponding two rings, sets the XDP_SHARED_UMEM flag in the bind
call and submits the XSK of the process it would like to share UMEM
with as well as its own newly created XSK socket. The new process will
then receive frame addr references in its own RX ring that point to
this shared UMEM. Note that since the ring structures are
single-consumer / single-producer (for performance reasons), the new
process has to create its own socket with associated RX and TX rings,
since it cannot share this with the other process. This is also the
reason that there is only one set of FILL and COMPLETION rings per
UMEM. It is the responsibility of a single process to handle the UMEM.

How is then packets distributed from an XDP program to the XSKs? There
is a BPF map called XSKMAP (or BPF_MAP_TYPE_XSKMAP in full). The
user-space application can place an XSK at an arbitrary place in this
map. The XDP program can then redirect a packet to a specific index in
this map and at this point XDP validates that the XSK in that map was
indeed bound to that device and ring number. If not, the packet is
dropped. If the map is empty at that index, the packet is also
dropped. This also means that it is currently mandatory to have an XDP
program loaded (and one XSK in the XSKMAP) to be able to get any
traffic to user space through the XSK.

AF_XDP can operate in two different modes: XDP_SKB and XDP_DRV. If the
driver does not have support for XDP, or XDP_SKB is explicitly chosen
when loading the XDP program, XDP_SKB mode is employed that uses SKBs
together with the generic XDP support and copies out the data to user
space. A fallback mode that works for any network device. On the other
hand, if the driver has support for XDP, it will be used by the AF_XDP
code to provide better performance, but there is still a copy of the
data into user space.

개념

90-104

개념

AF_XDP socket을 사용하려면 여러 관련 object를 설정해야 합니다. 다음 절에서 각 object와 option을 설명합니다.

AF_XDP 동작의 전체 개요는 2018 Linux Plumbers 논문 `http://vger.kernel.org/lpc_net2018_talks/lpc18_paper_af_xdp_perf-v2.pdf`에서도 볼 수 있습니다. AF_XDP의 첫 시도였던 2017년 `AF_PACKET v4` 논문은 이후 거의 모든 내용이 바뀌었으므로 참조하지 마십시오. Jonathan Corbet의 LWN 기사 `Accelerating networking with AF_XDP`도 `https://lwn.net/Articles/750845/`에서 볼 수 있습니다.

Concepts
========

In order to use an AF_XDP socket, a number of associated objects need
to be setup. These objects and their options are explained in the
following sections.

For an overview on how AF_XDP works, you can also take a look at the
Linux Plumbers paper from 2018 on the subject:
http://vger.kernel.org/lpc_net2018_talks/lpc18_paper_af_xdp_perf-v2.pdf. Do
NOT consult the paper from 2017 on "AF_PACKET v4", the first attempt
at AF_XDP. Nearly everything changed since then. Jonathan Corbet has
also written an excellent article on LWN, "Accelerating networking
with AF_XDP". It can be found at https://lwn.net/Articles/750845/.

UMEM

105-124

UMEM

UMEM은 가상 주소상 연속인 memory 영역이며 크기가 같은 frame으로 나뉩니다. UMEM은 netdev와 그 netdev의 특정 queue id에 연결됩니다. `XDP_UMEM_REG` setsockopt system call로 chunk 크기, headroom, 시작 주소, 전체 크기를 정해 만들고 구성하며, `bind()` system call로 netdev와 queue id에 묶습니다.

AF_XDP socket 하나는 UMEM 하나에 연결되지만 UMEM 하나에는 여러 AF_XDP socket이 연결될 수 있습니다. Socket A로 만든 UMEM을 socket B에서 공유하려면 `struct sockaddr_xdp`의 `sxdp_flags` member에 `XDP_SHARED_UMEM`을 설정하고, A의 file descriptor를 `sxdp_shared_umem_fd` member로 전달합니다.

UMEM에는 kernel과 사용자 공간 애플리케이션 사이에서 UMEM frame 소유권을 넘기는 single-producer/single-consumer ring 두 개가 있습니다.

UMEM
----

UMEM is a region of virtual contiguous memory, divided into
equal-sized frames. An UMEM is associated to a netdev and a specific
queue id of that netdev. It is created and configured (chunk size,
headroom, start address and size) by using the XDP_UMEM_REG setsockopt
system call. A UMEM is bound to a netdev and queue id, via the bind()
system call.

An AF_XDP is socket linked to a single UMEM, but one UMEM can have
multiple AF_XDP sockets. To share an UMEM created via one socket A,
the next socket B can do this by setting the XDP_SHARED_UMEM flag in
struct sockaddr_xdp member sxdp_flags, and passing the file descriptor
of A to struct sockaddr_xdp member sxdp_shared_umem_fd.

The UMEM has two single-producer/single-consumer rings that are used
to transfer ownership of UMEM frames between the kernel and the
user-space application.

Ring 공통 구조

125-150

Ring

Ring은 FILL, COMPLETION, RX, TX 네 종류입니다. 모두 single-producer/single-consumer이므로 여러 process나 thread가 ring을 읽고 쓸 때 사용자 공간 애플리케이션이 명시적으로 동기화해야 합니다.

UMEM은 FILL·COMPLETION ring을 사용합니다. UMEM에 연결된 각 socket에는 RX queue, TX queue 또는 둘 다 있어야 합니다. 예를 들어 네 socket이 모두 TX와 RX를 수행하면 FILL ring 하나, COMPLETION ring 하나, TX ring 네 개, RX ring 네 개가 존재합니다.

Ring은 head(producer)/tail(consumer) index를 사용합니다. Producer는 `struct xdp_ring`의 `producer` member가 가리키는 index에 data를 쓰고 producer index를 증가시킵니다. Consumer는 `consumer` member가 가리키는 index에서 data를 읽고 consumer index를 증가시킵니다.

Ring은 `_RING` setsockopt system call로 구성·생성하고, `XDP_PGOFF_RX_RING`, `XDP_PGOFF_TX_RING`, `XDP_UMEM_PGOFF_FILL_RING`, `XDP_UMEM_PGOFF_COMPLETION_RING` offset을 사용해 `mmap()`으로 사용자 공간에 mapping합니다.

Ring 크기는 2의 거듭제곱이어야 합니다.

Rings
-----

There are a four different kind of rings: FILL, COMPLETION, RX and
TX. All rings are single-producer/single-consumer, so the user-space
application need explicit synchronization of multiple
processes/threads are reading/writing to them.

The UMEM uses two rings: FILL and COMPLETION. Each socket associated
with the UMEM must have an RX queue, TX queue or both. Say, that there
is a setup with four sockets (all doing TX and RX). Then there will be
one FILL ring, one COMPLETION ring, four TX rings and four RX rings.

The rings are head(producer)/tail(consumer) based rings. A producer
writes the data ring at the index pointed out by struct xdp_ring
producer member, and increasing the producer index. A consumer reads
the data ring at the index pointed out by struct xdp_ring consumer
member, and increasing the consumer index.

The rings are configured and created via the _RING setsockopt system
calls and mmapped to user-space using the appropriate offset to mmap()
(XDP_PGOFF_RX_RING, XDP_PGOFF_TX_RING, XDP_UMEM_PGOFF_FILL_RING and
XDP_UMEM_PGOFF_COMPLETION_RING).

The size of the rings need to be of size power of two.

UMEM FILL ring

151-168

UMEM FILL ring

FILL ring은 UMEM frame의 소유권을 사용자 공간에서 kernel 공간으로 넘기며 ring에는 UMEM `addr`를 넣습니다. 예를 들어 UMEM이 64 KiB이고 chunk가 4 KiB이면 UMEM에는 chunk 16개가 있고 0부터 64 KiB 사이의 `addr`를 전달할 수 있습니다.

Kernel에 넘긴 frame은 ingress 경로, 즉 RX ring에서 사용합니다.

사용자 애플리케이션이 이 ring에 UMEM `addr`를 생산합니다. Aligned chunk mode에서는 kernel이 들어온 `addr`를 mask합니다. Chunk 크기가 2 KiB라면 `log2(2048)`개의 최하위 bit를 지우므로 2048, 2050, 3000은 같은 chunk를 가리킵니다. Unaligned chunk mode에서는 들어온 `addr`를 바꾸지 않습니다.

UMEM Fill Ring
~~~~~~~~~~~~~~

The FILL ring is used to transfer ownership of UMEM frames from
user-space to kernel-space. The UMEM addrs are passed in the ring. As
an example, if the UMEM is 64k and each chunk is 4k, then the UMEM has
16 chunks and can pass addrs between 0 and 64k.

Frames passed to the kernel are used for the ingress path (RX rings).

The user application produces UMEM addrs to this ring. Note that, if
running the application with aligned chunk mode, the kernel will mask
the incoming addr.  E.g. for a chunk size of 2k, the log2(2048) LSB of
the addr will be masked off, meaning that 2048, 2050 and 3000 refers
to the same chunk. If the user application is run in the unaligned
chunks mode, then the incoming addr will be left untouched.

UMEM COMPLETION ring

169-181

UMEM COMPLETION ring

COMPLETION ring은 UMEM frame의 소유권을 kernel 공간에서 사용자 공간으로 넘깁니다. FILL ring과 마찬가지로 UMEM index를 사용합니다.

Kernel이 사용자 공간으로 넘기는 frame은 TX ring을 통해 송신을 끝내 사용자 공간이 다시 사용할 수 있는 frame입니다.

사용자 애플리케이션이 이 ring에서 UMEM `addr`를 소비합니다.

UMEM Completion Ring
~~~~~~~~~~~~~~~~~~~~

The COMPLETION Ring is used transfer ownership of UMEM frames from
kernel-space to user-space. Just like the FILL ring, UMEM indices are
used.

Frames passed from the kernel to user-space are frames that has been
sent (TX ring) and can be used by user-space again.

The user application consumes UMEM addrs from this ring.

RX ring

182-194

RX ring

RX ring은 socket의 수신 측입니다. 각 entry는 `struct xdp_desc` descriptor이며 UMEM offset인 `addr`와 data 길이인 `len`을 담습니다.

FILL ring을 통해 kernel에 frame을 넘기지 않았다면 RX ring에는 descriptor가 나타나지 않으며 나타날 수도 없습니다.

사용자 애플리케이션이 이 ring에서 `struct xdp_desc` descriptor를 소비합니다.

RX Ring
~~~~~~~

The RX ring is the receiving side of a socket. Each entry in the ring
is a struct xdp_desc descriptor. The descriptor contains UMEM offset
(addr) and the length of the data (len).

If no frames have been passed to kernel via the FILL ring, no
descriptors will (or can) appear on the RX ring.

The user application consumes struct xdp_desc descriptors from this
ring.

TX ring

195-206

TX ring

TX ring은 frame을 보내는 데 사용합니다. `struct xdp_desc` descriptor에 index, length, offset을 채워 ring에 넣습니다.

전송을 시작하려면 `sendmsg()` system call이 필요합니다. 향후 이 요구는 완화될 수 있습니다.

사용자 애플리케이션이 이 ring에 `struct xdp_desc` descriptor를 생산합니다.

TX Ring
~~~~~~~

The TX ring is used to send frames. The struct xdp_desc descriptor is
filled (index, length and offset) and passed into the ring.

To start the transfer a sendmsg() system call is required. This might
be relaxed in the future.

The user application produces struct xdp_desc descriptors to this
ring.

libbpf

207-219

libbpf

libbpf는 eBPF와 XDP를 더 쉽게 사용할 수 있게 하는 helper library입니다. AF_XDP 사용을 돕는 전용 helper 함수도 `tools/testing/selftests/bpf/xsk.h`에 들어 있습니다. 함수는 AF_XDP socket 설정을 쉽게 만드는 control-plane 함수와 ring을 안전하고 빠르게 접근하는 data-plane 함수 두 종류로 나뉩니다.

숙련된 power user가 아니라면 이 library를 사용하기를 권장합니다. 프로그램이 훨씬 단순해집니다.

Libbpf
======

Libbpf is a helper library for eBPF and XDP that makes using these
technologies a lot simpler. It also contains specific helper functions
in tools/testing/selftests/bpf/xsk.h for facilitating the use of
AF_XDP. It contains two types of functions: those that can be used to
make the setup of AF_XDP socket easier and ones that can be used in the
data plane to access the rings safely and quickly.

We recommend that you use this library unless you have become a power
user. It will make your program a lot simpler.

XSKMAP

220-236

XSKMAP / `BPF_MAP_TYPE_XSKMAP`

XDP 측에는 `bpf_redirect_map()`과 함께 사용해 ingress frame을 socket으로 전달하는 BPF map 형식 `BPF_MAP_TYPE_XSKMAP`, 즉 XSKMAP이 있습니다.

사용자 애플리케이션은 `bpf()` system call로 socket을 map에 넣습니다.

XDP 프로그램이 queue 구성과 netdev가 일치하지 않는 socket으로 redirect하려 하면 frame을 drop합니다. 예를 들어 AF_XDP socket이 netdev `eth0`의 queue 17에 bind되었다면 `eth0` queue 17에서 실행되는 XDP 프로그램만 그 socket으로 data를 성공적으로 보낼 수 있습니다. 예는 `samples/bpf/`의 sample application을 참조하십시오.

XSKMAP / BPF_MAP_TYPE_XSKMAP
============================

On XDP side there is a BPF map type BPF_MAP_TYPE_XSKMAP (XSKMAP) that
is used in conjunction with bpf_redirect_map() to pass the ingress
frame to a socket.

The user application inserts the socket into the map, via the bpf()
system call.

Note that if an XDP program tries to redirect to a socket that does
not match the queue configuration and netdev, the frame will be
dropped. E.g. an AF_XDP socket is bound to netdev eth0 and
queue 17. Only the XDP program executing for eth0 and queue 17 will
successfully pass data to the socket. Please refer to the sample
application (samples/bpf/) in for an example.

구성 flag와 copy mode

237-254

구성 flag와 socket option

다음 flag들은 AF_XDP socket 동작을 제어하고 감시하는 데 사용합니다.

`XDP_COPY`와 `XDP_ZEROCOPY` bind flag

Socket을 bind하면 kernel은 먼저 zero-copy를 시도하고, 지원하지 않으면 모든 패킷을 사용자 공간으로 복사하는 copy mode로 fallback합니다. 특정 mode를 강제하려면 flag를 사용합니다. `bind`에 `XDP_COPY`를 전달하면 copy mode를 강제하고 사용할 수 없으면 오류로 실패합니다. 반대로 `XDP_ZEROCOPY`는 zero-copy mode를 강제하고 사용할 수 없으면 실패합니다.

Configuration Flags and Socket Options
======================================

These are the various configuration flags that can be used to control
and monitor the behavior of AF_XDP sockets.

XDP_COPY and XDP_ZEROCOPY bind flags
------------------------------------

When you bind to a socket, the kernel will first try to use zero-copy
copy. If zero-copy is not supported, it will fall back on using copy
mode, i.e. copying all packets out to user space. But if you would
like to force a certain mode, you can use the following flags. If you
pass the XDP_COPY flag to the bind call, the kernel will force the
socket into copy mode. If it cannot use copy mode, the bind call will
fail with an error. Conversely, the XDP_ZEROCOPY flag will force the
socket into zero-copy mode or fail.

XDP_SHARED_UMEM

255-346

`XDP_SHARED_UMEM` bind flag

이 flag를 사용하면 여러 socket을 같은 UMEM에 bind할 수 있습니다. 같은 queue id, 서로 다른 queue id, 서로 다른 netdev·device 사이에서 모두 동작합니다. 각 socket은 평소처럼 자기 RX·TX ring을 가지지만 FILL·COMPLETION ring pair는 하나 이상 존재할 수 있습니다. Bind하는 고유한 `(netdev, queue_id)` tuple마다 이 pair를 하나 만들어야 합니다.

먼저 같은 netdev와 queue id에 bind한 socket끼리 UMEM을 공유하는 경우를 봅니다. 고유 `(netdev, queue_id)` tuple이 하나뿐이므로 최초 socket에 연결된 UMEM에는 FILL ring 하나와 COMPLETION ring 하나만 있습니다. 첫 socket은 일반 방식으로 만들고 bind합니다. 두 번째 socket에는 RX·TX ring 또는 적어도 둘 중 하나를 만들되, 첫 socket의 ring을 사용할 것이므로 FILL·COMPLETION ring은 만들지 않습니다. `bind`에서 `XDP_SHARED_UMEM`을 설정하고 최초 socket의 fd를 `sxdp_shared_umem_fd`에 넣습니다. 같은 방법으로 socket을 원하는 만큼 더 붙일 수 있습니다.

패킷이 어느 socket에 도착할지는 XDP 프로그램이 결정합니다. 모든 socket을 XSKMAP에 넣고 각 패킷을 보낼 array index를 지정합니다. 다음은 단순 round-robin 분배 예입니다.

.. code-block:: c

   #include <linux/bpf.h>
   #include "bpf_helpers.h"

   #define MAX_SOCKS 16

   struct {
       __uint(type, BPF_MAP_TYPE_XSKMAP);
       __uint(max_entries, MAX_SOCKS);
       __uint(key_size, sizeof(int));
       __uint(value_size, sizeof(int));
   } xsks_map SEC(".maps");

   static unsigned int rr;

   SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
   {
       rr = (rr + 1) & (MAX_SOCKS - 1);

       return bpf_redirect_map(&xsks_map, rr, XDP_DROP);
   }

Note, that since there is only a single set of FILL and COMPLETION

FILL·COMPLETION ring 집합은 하나뿐이고 single-producer/single-consumer이므로 여러 process나 thread가 이를 동시에 사용하지 않도록 해야 합니다. 현재 libbpf code에는 여러 사용자를 보호하는 synchronization primitive가 없습니다.

libbpf는 같은 UMEM에 socket을 둘 이상 만들면 이 mode를 사용합니다. 다만 traffic을 대신 route해 주는 내장 XDP 프로그램이 없으므로 `xsk_socket__create` 호출에 `XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD` libbpf flag를 주고 자체 XDP 프로그램을 load해야 합니다.

두 번째 경우는 서로 다른 queue id 또는 netdev에 bind한 socket들이 UMEM을 공유할 때입니다. 고유한 `(netdev, queue_id)` pair마다 FILL ring과 COMPLETION ring을 하나씩 만들어야 합니다. 같은 netdev의 서로 다른 queue id에 socket 두 개를 만들려면 첫 socket은 일반 방식으로 만들고 bind합니다. 두 번째 socket에는 RX·TX ring 중 적어도 하나와 그 socket용 FILL·COMPLETION ring을 만듭니다. UMEM은 첫 socket에 등록했으므로 `bind`에서 `XDP_SHARED_UMEM`을 설정하고 첫 socket fd를 `sxdp_shared_umem_fd`에 넣습니다. 두 socket은 이제 같은 UMEM을 공유합니다.

이 경우에는 같은 queue를 여러 socket이 공유할 때 사용한 XDP 분배 프로그램이 필요하지 않습니다. NIC의 packet steering 기능으로 패킷을 올바른 queue에 보냅니다. NIC는 queue 사이에서만 steering할 수 있으므로 하나의 queue를 여러 socket이 공유하는 앞선 경우에는 이 방법을 쓸 수 없습니다.

libbpf에서는 생성할 FILL·COMPLETION ring 참조를 받고 이를 공유 UMEM에 bind하는 `xsk_socket__create_shared()` API를 사용해야 합니다. 모든 socket에 이 함수를 쓰거나 첫 socket에는 `xsk_socket__create()`, 두 번째 이후에는 shared 함수를 써도 결과는 같습니다.

UMEM 하나는 같은 device와 queue id의 socket 사이, 같은 device의 여러 queue 사이, 서로 다른 device 사이에서 동시에 공유할 수 있습니다.

XDP_SHARED_UMEM bind flag
-------------------------

This flag enables you to bind multiple sockets to the same UMEM. It
works on the same queue id, between queue ids and between
netdevs/devices. In this mode, each socket has their own RX and TX
rings as usual, but you are going to have one or more FILL and
COMPLETION ring pairs. You have to create one of these pairs per
unique netdev and queue id tuple that you bind to.

Starting with the case were we would like to share a UMEM between
sockets bound to the same netdev and queue id. The UMEM (tied to the
fist socket created) will only have a single FILL ring and a single
COMPLETION ring as there is only on unique netdev,queue_id tuple that
we have bound to. To use this mode, create the first socket and bind
it in the normal way. Create a second socket and create an RX and a TX
ring, or at least one of them, but no FILL or COMPLETION rings as the
ones from the first socket will be used. In the bind call, set he
XDP_SHARED_UMEM option and provide the initial socket's fd in the
sxdp_shared_umem_fd field. You can attach an arbitrary number of extra
sockets this way.

What socket will then a packet arrive on? This is decided by the XDP
program. Put all the sockets in the XSK_MAP and just indicate which
index in the array you would like to send each packet to. A simple
round-robin example of distributing packets is shown below:

.. code-block:: c

   #include <linux/bpf.h>
   #include "bpf_helpers.h"

   #define MAX_SOCKS 16

   struct {
       __uint(type, BPF_MAP_TYPE_XSKMAP);
       __uint(max_entries, MAX_SOCKS);
       __uint(key_size, sizeof(int));
       __uint(value_size, sizeof(int));
   } xsks_map SEC(".maps");

   static unsigned int rr;

   SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
   {
       rr = (rr + 1) & (MAX_SOCKS - 1);

       return bpf_redirect_map(&xsks_map, rr, XDP_DROP);
   }

Note, that since there is only a single set of FILL and COMPLETION
rings, and they are single producer, single consumer rings, you need
to make sure that multiple processes or threads do not use these rings
concurrently. There are no synchronization primitives in the
libbpf code that protects multiple users at this point in time.

Libbpf uses this mode if you create more than one socket tied to the
same UMEM. However, note that you need to supply the
XSK_LIBBPF_FLAGS__INHIBIT_PROG_LOAD libbpf_flag with the
xsk_socket__create calls and load your own XDP program as there is no
built in one in libbpf that will route the traffic for you.

The second case is when you share a UMEM between sockets that are
bound to different queue ids and/or netdevs. In this case you have to
create one FILL ring and one COMPLETION ring for each unique
netdev,queue_id pair. Let us say you want to create two sockets bound
to two different queue ids on the same netdev. Create the first socket
and bind it in the normal way. Create a second socket and create an RX
and a TX ring, or at least one of them, and then one FILL and
COMPLETION ring for this socket. Then in the bind call, set he
XDP_SHARED_UMEM option and provide the initial socket's fd in the
sxdp_shared_umem_fd field as you registered the UMEM on that
socket. These two sockets will now share one and the same UMEM.

There is no need to supply an XDP program like the one in the previous
case where sockets were bound to the same queue id and
device. Instead, use the NIC's packet steering capabilities to steer
the packets to the right queue. In the previous example, there is only
one queue shared among sockets, so the NIC cannot do this steering. It
can only steer between queues.

In libbpf, you need to use the xsk_socket__create_shared() API as it
takes a reference to a FILL ring and a COMPLETION ring that will be
created for you and bound to the shared UMEM. You can use this
function for all the sockets you create, or you can use it for the
second and following ones and use xsk_socket__create() for the first
one. Both methods yield the same result.

Note that a UMEM can be shared between sockets on the same queue id
and device, as well as between queues on the same device and between
devices at the same time.

XDP_USE_NEED_WAKEUP

347-387

`XDP_USE_NEED_WAKEUP` bind flag

이 option은 사용자 공간이 producer인 FILL ring과 TX ring에 `need_wakeup` flag를 추가합니다. `bind`에서 option을 설정했을 때 kernel이 패킷 처리를 계속하려면 system call로 명시적으로 깨워야 하는 경우 flag가 설정됩니다. Flag가 0이면 system call이 필요하지 않습니다.

FILL ring의 flag가 설정되면 애플리케이션은 RX ring에서 계속 패킷을 받기 위해 `poll()`을 호출해야 합니다. 예를 들어 kernel이 FILL ring에 buffer가 없고 NIC RX hardware ring에도 buffer가 남지 않았음을 감지하면 NIC가 패킷을 받을 수 없으므로 interrupt를 끄고 `need_wakeup`을 설정합니다. 사용자 공간은 FILL ring에 buffer를 넣고 `poll()`을 호출해 kernel driver가 이를 hardware ring에 올리고 수신을 재개하게 합니다.

TX ring의 flag가 설정되면 애플리케이션이 TX ring에 넣은 패킷을 보내도록 kernel에 명시적으로 알려야 합니다. RX 경로처럼 `poll()`을 호출하거나 `sendto()`를 호출하면 됩니다.

libbpf helper를 사용하는 TX 경로 예는 다음과 같습니다.

.. code-block:: c

   if (xsk_ring_prod__needs_wakeup(&my_tx_ring))
       sendto(xsk_socket__fd(xsk_handle), NULL, 0, MSG_DONTWAIT, NULL, 0);

즉, flag가 설정된 경우에만 system call을 사용합니다.

보통 더 높은 성능을 내므로 이 mode를 항상 켜기를 권장합니다. 애플리케이션과 driver가 같은 core에서 실행될 때 특히 유리하고, 서로 다른 core를 사용하더라도 TX 경로의 system call 수를 줄입니다.

XDP_USE_NEED_WAKEUP bind flag
-----------------------------

This option adds support for a new flag called need_wakeup that is
present in the FILL ring and the TX ring, the rings for which user
space is a producer. When this option is set in the bind call, the
need_wakeup flag will be set if the kernel needs to be explicitly
woken up by a syscall to continue processing packets. If the flag is
zero, no syscall is needed.

If the flag is set on the FILL ring, the application needs to call
poll() to be able to continue to receive packets on the RX ring. This
can happen, for example, when the kernel has detected that there are no
more buffers on the FILL ring and no buffers left on the RX HW ring of
the NIC. In this case, interrupts are turned off as the NIC cannot
receive any packets (as there are no buffers to put them in), and the
need_wakeup flag is set so that user space can put buffers on the
FILL ring and then call poll() so that the kernel driver can put these
buffers on the HW ring and start to receive packets.

If the flag is set for the TX ring, it means that the application
needs to explicitly notify the kernel to send any packets put on the
TX ring. This can be accomplished either by a poll() call, as in the
RX path, or by calling sendto().

An example with the use of libbpf helpers would look like this for the
TX path:

.. code-block:: c

   if (xsk_ring_prod__needs_wakeup(&my_tx_ring))
       sendto(xsk_socket__fd(xsk_handle), NULL, 0, MSG_DONTWAIT, NULL, 0);

I.e., only use the syscall if the flag is set.

We recommend that you always enable this mode as it usually leads to
better performance especially if you run the application and the
driver on the same core, but also if you use different cores for the
application and the kernel driver, as it reduces the number of
syscalls needed for the TX path.

Ring setsockopt

388-413

`XDP_{RX|TX|UMEM_FILL|UMEM_COMPLETION}_RING` setsockopt

이 setsockopt들은 각각 RX, TX, FILL, COMPLETION ring의 descriptor 수를 정합니다. RX와 TX ring 중 적어도 하나의 크기는 반드시 설정해야 합니다. 둘 다 설정하면 애플리케이션에서 수신과 송신을 모두 할 수 있고, 한 방향만 필요하면 해당 ring만 만들어 자원을 절약할 수 있습니다.

Socket에는 UMEM이 연결되어야 하므로 FILL과 COMPLETION ring은 둘 다 필수입니다. 단, `XDP_SHARED_UMEM`을 사용하면 첫 socket 이후의 socket은 자체 UMEM이 없으므로 공유 UMEM의 ring을 사용하며 FILL·COMPLETION ring을 만들면 안 됩니다. Ring은 single-producer/single-consumer이므로 여러 process가 동시에 접근하면 안 됩니다.

libbpf에서는 `xsk_socket__create` 함수의 RX 또는 TX argument에 각각 `NULL`을 넘겨 RX-only 또는 TX-only socket을 만들 수 있습니다.

TX-only socket을 만들었다면 FILL ring에 패킷을 넣지 않기를 권장합니다. 넣으면 driver가 실제로는 수신하지 않을 애플리케이션이 수신할 것이라고 판단해 성능에 악영향을 줄 수 있습니다.

XDP_{RX|TX|UMEM_FILL|UMEM_COMPLETION}_RING setsockopts
------------------------------------------------------

These setsockopts sets the number of descriptors that the RX, TX,
FILL, and COMPLETION rings respectively should have. It is mandatory
to set the size of at least one of the RX and TX rings. If you set
both, you will be able to both receive and send traffic from your
application, but if you only want to do one of them, you can save
resources by only setting up one of them. Both the FILL ring and the
COMPLETION ring are mandatory as you need to have a UMEM tied to your
socket. But if the XDP_SHARED_UMEM flag is used, any socket after the
first one does not have a UMEM and should in that case not have any
FILL or COMPLETION rings created as the ones from the shared UMEM will
be used. Note, that the rings are single-producer single-consumer, so
do not try to access them from multiple processes at the same
time. See the XDP_SHARED_UMEM section.

In libbpf, you can create Rx-only and Tx-only sockets by supplying
NULL to the rx and tx arguments, respectively, to the
xsk_socket__create function.

If you create a Tx-only socket, we recommend that you do not put any
packets on the fill ring. If you do this, drivers might think you are
going to receive something when you in fact will not, and this can
negatively impact performance.

XDP_UMEM_REG

414-431

`XDP_UMEM_REG` setsockopt

이 setsockopt는 UMEM을 socket에 등록합니다. UMEM은 패킷이 머물 수 있는 모든 buffer를 담는 영역입니다. 호출에는 영역 시작 pointer와 전체 크기를 전달합니다. `chunk_size` parameter는 UMEM을 나누는 단위 크기이며 현재는 2 KiB 또는 4 KiB만 가능합니다. UMEM이 128 KiB이고 chunk가 2 KiB라면 최대 64개 패킷을 담을 수 있고 가장 큰 패킷 크기는 2 KiB입니다.

UMEM의 각 buffer에 headroom을 설정할 수도 있습니다. N byte로 설정하면 패킷은 buffer 시작에서 N byte 뒤에 놓이고 앞의 N byte는 애플리케이션이 사용할 수 있습니다. 마지막 option은 `flags` field이며 각 UMEM flag 절에서 따로 다룹니다.

XDP_UMEM_REG setsockopt
-----------------------

This setsockopt registers a UMEM to a socket. This is the area that
contain all the buffers that packet can reside in. The call takes a
pointer to the beginning of this area and the size of it. Moreover, it
also has parameter called chunk_size that is the size that the UMEM is
divided into. It can only be 2K or 4K at the moment. If you have an
UMEM area that is 128K and a chunk size of 2K, this means that you
will be able to hold a maximum of 128K / 2K = 64 packets in your UMEM
area and that your largest packet size can be 2K.

There is also an option to set the headroom of each single buffer in
the UMEM. If you set this to N bytes, it means that the packet will
start N bytes into the buffer leaving the first N bytes for the
application to use. The final option is the flags field, but it will
be dealt with in separate sections for each UMEM flag.

SO_BINDTODEVICE

432-440

`SO_BINDTODEVICE` setsockopt

특정 network interface에 AF_XDP socket을 고정하는 범용 `SOL_SOCKET` option입니다. Privileged process가 socket을 만든 뒤 non-privileged process에 넘길 때 유용합니다. Option을 설정하면 kernel은 그 socket을 다른 interface에 bind하려는 시도를 거부합니다. 값을 갱신하려면 `CAP_NET_RAW`가 필요합니다.

SO_BINDTODEVICE setsockopt
--------------------------

This is a generic SOL_SOCKET option that can be used to tie AF_XDP
socket to a particular network interface.  It is useful when a socket
is created by a privileged process and passed to a non-privileged one.
Once the option is set, kernel will refuse attempts to bind that socket
to a different interface.  Updating the value requires CAP_NET_RAW.

XDP_MAX_TX_SKB_BUDGET

441-449

`XDP_MAX_TX_SKB_BUDGET` setsockopt

한 번의 send system call에서 처리해 driver에 넘길 수 있는 최대 descriptor 수를 정합니다. Copy mode에서 애플리케이션이 socket별 최대 반복 횟수를 조정해 throughput을 높이고 send system call 빈도를 낮출 수 있게 합니다. 허용 범위는 `[32, xs->tx->nentries]`입니다.

XDP_MAX_TX_SKB_BUDGET setsockopt
--------------------------------

This setsockopt sets the maximum number of descriptors that can be handled
and passed to the driver at one send syscall. It is applied in the copy
mode to allow application to tune the per-socket maximum iteration for
better throughput and less frequency of send syscall.
Allowed range is [32, xs->tx->nentries].

XDP_STATISTICS

450-463

`XDP_STATISTICS` getsockopt

Debug에 유용한 socket drop 통계를 가져옵니다. 지원하는 통계는 다음과 같습니다.

.. code-block:: c

   struct xdp_statistics {
       __u64 rx_dropped; /* Dropped for reasons other than invalid desc */
       __u64 rx_invalid_descs; /* Dropped due to invalid descriptor */
       __u64 tx_invalid_descs; /* Dropped due to invalid descriptor */
   };
XDP_STATISTICS getsockopt
-------------------------

Gets drop statistics of a socket that can be useful for debug
purposes. The supported statistics are shown below:

.. code-block:: c

   struct xdp_statistics {
       __u64 rx_dropped; /* Dropped for reasons other than invalid desc */
       __u64 rx_invalid_descs; /* Dropped due to invalid descriptor */
       __u64 tx_invalid_descs; /* Dropped due to invalid descriptor */
   };

XDP_OPTIONS

464-469

`XDP_OPTIONS` getsockopt

XDP socket option을 가져옵니다. 현재 지원하는 것은 zero-copy가 켜졌는지 알려 주는 `XDP_OPTIONS_ZEROCOPY`뿐입니다.

XDP_OPTIONS getsockopt
----------------------

Gets options from an XDP socket. The only one supported so far is
XDP_OPTIONS_ZEROCOPY which tells you if zero-copy is on or not.

Multi-buffer 지원

470-553

Multi-buffer 지원

Multi-buffer 지원을 사용하면 AF_XDP socket 프로그램이 copy mode와 zero-copy mode 모두에서 여러 buffer로 이루어진 패킷을 송수신할 수 있습니다. 예를 들어 header가 든 frame과 data가 든 frame 두 개로 패킷을 만들거나, 4 KiB frame 세 개를 이어 9 KiB Ethernet jumbo frame을 만들 수 있습니다.

정의는 다음과 같습니다.

  • 패킷은 하나 이상의 frame으로 이루어집니다.
  • AF_XDP ring의 descriptor 하나는 언제나 frame 하나를 가리킵니다. 패킷이 frame 하나로 이루어졌다면 descriptor가 전체 패킷을 가리킵니다.

AF_XDP socket에서 multi-buffer 지원을 켜려면 새 bind flag `XDP_USE_SG`를 사용합니다. 제공하지 않으면 이전처럼 모든 multi-buffer 패킷을 drop합니다. Load된 XDP 프로그램도 multi-buffer mode여야 하며, XDP 프로그램 section 이름으로 `xdp.frags`를 사용하면 됩니다.

여러 frame으로 이루어진 패킷을 표현하기 위해 RX·TX descriptor의 `options` field에 `XDP_PKT_CONTD` flag가 도입되었습니다. 값이 1이면 패킷이 다음 descriptor에서 계속되고, 0이면 현재 descriptor가 마지막입니다. 많은 NIC의 end-of-packet(EOP) flag와 반대인 이유는 기존 non-multi-buffer 애플리케이션과 호환하기 위해서입니다. 기존 RX 애플리케이션은 이 bit가 모든 패킷에서 0이고 TX 애플리케이션은 `options`를 0으로 설정하며, 다른 값은 invalid descriptor로 처리되기 때문입니다.

여러 frame 패킷을 AF_XDP TX ring에 생산할 때의 의미는 다음과 같습니다.

  • Invalid descriptor를 만나면 같은 패킷의 다른 descriptor·frame도 모두 invalid로 표시하고 complete하지 않습니다. 다음 descriptor는 의도와 관계없이 새 패킷의 시작으로 취급합니다. 의도를 추측할 수 없기 때문입니다. Invalid descriptor를 생산한다면 프로그램 버그이므로 수정해야 합니다.
  • 길이가 0인 descriptor는 invalid descriptor로 처리합니다.
  • Copy mode에서 패킷 하나가 가질 수 있는 최대 frame 수는 `CONFIG_MAX_SKB_FRAGS + 1`입니다. 이를 넘으면 지금까지 모은 descriptor를 모두 drop하고 invalid로 처리합니다. 어떤 system에서도 동작하게 하려면 config 최소값이 17이므로 fragment 수를 18로 제한하십시오.
  • Zero-copy mode의 한계는 NIC hardware가 지원하는 수까지입니다. 확인한 NIC들은 보통 적어도 5개를 지원합니다. NIC 한계에 맞추려고 내부에서 copy를 발생시키면 zero-copy 목적을 훼손하므로 `CONFIG_MAX_SKB_FRAGS + 1` 같은 고정 한계를 일부러 강제하지 않습니다. 한계 조사 방법은 multi-buffer 지원 probe 절에서 설명합니다.

Copy-mode RX 경로에서 xsk core는 필요하면 XDP data를 여러 descriptor로 복사하고 `XDP_PKT_CONTD`를 설정합니다. Zero-copy mode도 data를 복사하지 않는다는 점을 제외하면 같습니다. 애플리케이션이 `XDP_PKT_CONTD == 1`인 descriptor를 받으면 패킷이 여러 buffer로 이루어져 다음 descriptor에서 이어짐을 뜻합니다. `XDP_PKT_CONTD == 0`이면 패킷의 마지막 buffer입니다.

AF_XDP는 완전한 패킷, 즉 그 패킷의 모든 frame만 애플리케이션에 보냅니다. AF_XDP RX ring에 공간이 부족하면 패킷의 모든 frame을 drop합니다.

애플리케이션이 libxdp interface 등으로 descriptor batch를 읽을 때 batch가 완전한 패킷에서 끝난다는 보장은 없습니다. Libxdp interface는 ring 전체를 읽지 않으므로 batch가 패킷 중간에서 끝나고 나머지 buffer가 다음 batch의 처음에 올 수 있습니다. Batch 크기가 매우 크거나 ring이 매우 작은 경우는 예외입니다.

RX와 TX multi-buffer 지원 예제 프로그램은 이 문서 뒤쪽에 각각 나옵니다.

Multi-Buffer Support
====================

With multi-buffer support, programs using AF_XDP sockets can receive
and transmit packets consisting of multiple buffers both in copy and
zero-copy mode. For example, a packet can consist of two
frames/buffers, one with the header and the other one with the data,
or a 9K Ethernet jumbo frame can be constructed by chaining together
three 4K frames.

Some definitions:

* A packet consists of one or more frames

* A descriptor in one of the AF_XDP rings always refers to a single
  frame. In the case the packet consists of a single frame, the
  descriptor refers to the whole packet.

To enable multi-buffer support for an AF_XDP socket, use the new bind
flag XDP_USE_SG. If this is not provided, all multi-buffer packets
will be dropped just as before. Note that the XDP program loaded also
needs to be in multi-buffer mode. This can be accomplished by using
"xdp.frags" as the section name of the XDP program used.

To represent a packet consisting of multiple frames, a new flag called
XDP_PKT_CONTD is introduced in the options field of the Rx and Tx
descriptors. If it is true (1) the packet continues with the next
descriptor and if it is false (0) it means this is the last descriptor
of the packet. Why the reverse logic of end-of-packet (eop) flag found
in many NICs? Just to preserve compatibility with non-multi-buffer
applications that have this bit set to false for all packets on Rx,
and the apps set the options field to zero for Tx, as anything else
will be treated as an invalid descriptor.

These are the semantics for producing packets onto AF_XDP Tx ring
consisting of multiple frames:

* When an invalid descriptor is found, all the other
  descriptors/frames of this packet are marked as invalid and not
  completed. The next descriptor is treated as the start of a new
  packet, even if this was not the intent (because we cannot guess
  the intent). As before, if your program is producing invalid
  descriptors you have a bug that must be fixed.

* Zero length descriptors are treated as invalid descriptors.

* For copy mode, the maximum supported number of frames in a packet is
  equal to CONFIG_MAX_SKB_FRAGS + 1. If it is exceeded, all
  descriptors accumulated so far are dropped and treated as
  invalid. To produce an application that will work on any system
  regardless of this config setting, limit the number of frags to 18,
  as the minimum value of the config is 17.

* For zero-copy mode, the limit is up to what the NIC HW
  supports. Usually at least five on the NICs we have checked. We
  consciously chose to not enforce a rigid limit (such as
  CONFIG_MAX_SKB_FRAGS + 1) for zero-copy mode, as it would have
  resulted in copy actions under the hood to fit into what limit the
  NIC supports. Kind of defeats the purpose of zero-copy mode. How to
  probe for this limit is explained in the "probe for multi-buffer
  support" section.

On the Rx path in copy-mode, the xsk core copies the XDP data into
multiple descriptors, if needed, and sets the XDP_PKT_CONTD flag as
detailed before. Zero-copy mode works the same, though the data is not
copied. When the application gets a descriptor with the XDP_PKT_CONTD
flag set to one, it means that the packet consists of multiple buffers
and it continues with the next buffer in the following
descriptor. When a descriptor with XDP_PKT_CONTD == 0 is received, it
means that this is the last buffer of the packet. AF_XDP guarantees
that only a complete packet (all frames in the packet) is sent to the
application. If there is not enough space in the AF_XDP Rx ring, all
frames of the packet will be dropped.

If application reads a batch of descriptors, using for example the libxdp
interfaces, it is not guaranteed that the batch will end with a full
packet. It might end in the middle of a packet and the rest of the
buffers of that packet will arrive at the beginning of the next batch,
since the libxdp interface does not read the whole ring (unless you
have an enormous batch size or a very small ring size).

An example program each for Rx and Tx multi-buffer support can be found
later in this document.

기본 사용법

554-632

사용법

AF_XDP socket을 사용하려면 사용자 공간 애플리케이션과 XDP 프로그램 두 부분이 필요합니다. 전체 설정·사용 예는 `https://github.com/xdp-project/bpf-examples/tree/main/AF_XDP-example`의 xdp-project를 참조하십시오.

다음 XDP code는 현재 RX queue index에 해당하는 XSKMAP entry가 있으면 그 socket으로 redirect하고, 없으면 `XDP_PASS`를 반환합니다.

.. code-block:: c

   SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
   {
       int index = ctx->rx_queue_index;

       // A set entry here means that the corresponding queue_id
       // has an active AF_XDP socket bound to it.
       if (bpf_map_lookup_elem(&xsks_map, &index))
           return bpf_redirect_map(&xsks_map, index, 0);

       return XDP_PASS;
   }

A simple but not so performance ring dequeue and enqueue could look

다음은 단순하지만 성능은 높지 않은 ring dequeue·enqueue 예입니다. Producer와 consumer 차이로 사용 entry와 빈 entry 수를 계산하고, ring 크기가 2의 거듭제곱이라는 점을 이용해 `index & (RING_SIZE - 1)`로 descriptor 위치를 구합니다. 실제 구현에서는 표시된 read·write barrier가 필요합니다.

.. code-block:: c

    // struct xdp_rxtx_ring {
    //     __u32 *producer;
    //     __u32 *consumer;
    //     struct xdp_desc *desc;
    // };

    // struct xdp_umem_ring {
    //     __u32 *producer;
    //     __u32 *consumer;
    //     __u64 *desc;
    // };

    // typedef struct xdp_rxtx_ring RING;
    // typedef struct xdp_umem_ring RING;

    // typedef struct xdp_desc RING_TYPE;
    // typedef __u64 RING_TYPE;

    int dequeue_one(RING *ring, RING_TYPE *item)
    {
        __u32 entries = *ring->producer - *ring->consumer;

        if (entries == 0)
            return -1;

        // read-barrier!

        *item = ring->desc[*ring->consumer & (RING_SIZE - 1)];
        (*ring->consumer)++;
        return 0;
    }

    int enqueue_one(RING *ring, const RING_TYPE *item)
    {
        u32 free_entries = RING_SIZE - (*ring->producer - *ring->consumer);

        if (free_entries == 0)
            return -1;

        ring->desc[*ring->producer & (RING_SIZE - 1)] = *item;

        // write-barrier!

        (*ring->producer)++;
        return 0;
    }

최적화되어 있고 바로 사용할 수 있는 libbpf 함수를 사용하십시오. 프로그램 작성이 더 쉬워집니다.

Usage
-----

In order to use AF_XDP sockets two parts are needed. The user-space
application and the XDP program. For a complete setup and usage example,
please refer to the xdp-project at
https://github.com/xdp-project/bpf-examples/tree/main/AF_XDP-example.

The XDP code sample is the following:

.. code-block:: c

   SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
   {
       int index = ctx->rx_queue_index;

       // A set entry here means that the corresponding queue_id
       // has an active AF_XDP socket bound to it.
       if (bpf_map_lookup_elem(&xsks_map, &index))
           return bpf_redirect_map(&xsks_map, index, 0);

       return XDP_PASS;
   }

A simple but not so performance ring dequeue and enqueue could look
like this:

.. code-block:: c

    // struct xdp_rxtx_ring {
    //     __u32 *producer;
    //     __u32 *consumer;
    //     struct xdp_desc *desc;
    // };

    // struct xdp_umem_ring {
    //     __u32 *producer;
    //     __u32 *consumer;
    //     __u64 *desc;
    // };

    // typedef struct xdp_rxtx_ring RING;
    // typedef struct xdp_umem_ring RING;

    // typedef struct xdp_desc RING_TYPE;
    // typedef __u64 RING_TYPE;

    int dequeue_one(RING *ring, RING_TYPE *item)
    {
        __u32 entries = *ring->producer - *ring->consumer;

        if (entries == 0)
            return -1;

        // read-barrier!

        *item = ring->desc[*ring->consumer & (RING_SIZE - 1)];
        (*ring->consumer)++;
        return 0;
    }

    int enqueue_one(RING *ring, const RING_TYPE *item)
    {
        u32 free_entries = RING_SIZE - (*ring->producer - *ring->consumer);

        if (free_entries == 0)
            return -1;

        ring->desc[*ring->producer & (RING_SIZE - 1)] = *item;

        // write-barrier!

        (*ring->producer)++;
        return 0;
    }

But please use the libbpf functions as they are optimized and ready to
use. Will make your life easier.

Multi-buffer RX 사용법

633-672

Multi-buffer RX 사용법

다음은 간결함을 위해 오류 경로를 제외하고 libxdp interface를 사용한 단순 RX 경로 pseudo-code입니다.

.. code-block:: c

    void rx_packets(struct xsk_socket_info *xsk)
    {
        static bool new_packet = true;
        u32 idx_rx = 0, idx_fq = 0;
        static char *pkt;

        int rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx);

        xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);

        for (int i = 0; i < rcvd; i++) {
            struct xdp_desc *desc = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++);
            char *frag = xsk_umem__get_data(xsk->umem->buffer, desc->addr);
            bool eop = !(desc->options & XDP_PKT_CONTD);

            if (new_packet)
                pkt = frag;
            else
                add_frag_to_pkt(pkt, frag);

            if (eop)
                process_pkt(pkt);

            new_packet = eop;

            *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = desc->addr;
        }

        xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
        xsk_ring_cons__release(&xsk->rx, rcvd);

예제는 RX descriptor를 순회하며 `XDP_PKT_CONTD`의 반대값으로 EOP를 판단합니다. 새 패킷이면 첫 fragment를 저장하고, 이어지는 fragment는 기존 패킷에 붙입니다. EOP에서 완성 패킷을 처리한 뒤 각 frame `addr`를 FILL ring에 되돌리고 RX descriptor를 release합니다.

Usage Multi-Buffer Rx
---------------------

Here is a simple Rx path pseudo-code example (using libxdp interfaces
for simplicity). Error paths have been excluded to keep it short:

.. code-block:: c

    void rx_packets(struct xsk_socket_info *xsk)
    {
        static bool new_packet = true;
        u32 idx_rx = 0, idx_fq = 0;
        static char *pkt;

        int rcvd = xsk_ring_cons__peek(&xsk->rx, opt_batch_size, &idx_rx);

        xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq);

        for (int i = 0; i < rcvd; i++) {
            struct xdp_desc *desc = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++);
            char *frag = xsk_umem__get_data(xsk->umem->buffer, desc->addr);
            bool eop = !(desc->options & XDP_PKT_CONTD);

            if (new_packet)
                pkt = frag;
            else
                add_frag_to_pkt(pkt, frag);

            if (eop)
                process_pkt(pkt);

            new_packet = eop;

            *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = desc->addr;
        }

        xsk_ring_prod__submit(&xsk->umem->fq, rcvd);
        xsk_ring_cons__release(&xsk->rx, rcvd);
    }

Multi-buffer TX 사용법

673-722

Multi-buffer TX 사용법

다음은 단순화를 위해 UMEM 크기가 유한해 결국 송신할 패킷이 고갈된다는 점을 무시하고, `pkts.addr`가 UMEM의 유효한 위치를 가리킨다고 가정한 libxdp TX 경로 pseudo-code입니다.

.. code-block:: c

    void tx_packets(struct xsk_socket_info *xsk, struct pkt *pkts,
                    int batch_size)
    {
        u32 idx, i, pkt_nb = 0;

        xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx);

        for (i = 0; i < batch_size;) {
            u64 addr = pkts[pkt_nb].addr;
            u32 len = pkts[pkt_nb].size;

            do {
                struct xdp_desc *tx_desc;

                tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i++);
                tx_desc->addr = addr;

                if (len > xsk_frame_size) {
                    tx_desc->len = xsk_frame_size;
                    tx_desc->options = XDP_PKT_CONTD;
                } else {
                    tx_desc->len = len;
                    tx_desc->options = 0;
                    pkt_nb++;
                }
                len -= tx_desc->len;
                addr += xsk_frame_size;

                if (i == batch_size) {
                    /* Remember len, addr, pkt_nb for next iteration.
                     * Skipped for simplicity.
                     */
                    break;
                }
            } while (len);
        }

        xsk_ring_prod__submit(&xsk->tx, i);

패킷 길이가 frame 크기보다 크면 현재 descriptor 길이를 frame 크기로 정하고 `XDP_PKT_CONTD`를 설정합니다. 마지막 frame에서는 남은 길이를 쓰고 `options = 0`으로 끝을 표시합니다. 예약한 batch가 패킷 중간에서 끝나면 다음 반복을 위해 `len`, `addr`, `pkt_nb`를 기억해야 하지만 예제에서는 생략합니다.

Usage Multi-Buffer Tx
---------------------

Here is an example Tx path pseudo-code (using libxdp interfaces for
simplicity) ignoring that the umem is finite in size, and that we
eventually will run out of packets to send. Also assumes pkts.addr
points to a valid location in the umem.

.. code-block:: c

    void tx_packets(struct xsk_socket_info *xsk, struct pkt *pkts,
                    int batch_size)
    {
        u32 idx, i, pkt_nb = 0;

        xsk_ring_prod__reserve(&xsk->tx, batch_size, &idx);

        for (i = 0; i < batch_size;) {
            u64 addr = pkts[pkt_nb].addr;
            u32 len = pkts[pkt_nb].size;

            do {
                struct xdp_desc *tx_desc;

                tx_desc = xsk_ring_prod__tx_desc(&xsk->tx, idx + i++);
                tx_desc->addr = addr;

                if (len > xsk_frame_size) {
                    tx_desc->len = xsk_frame_size;
                    tx_desc->options = XDP_PKT_CONTD;
                } else {
                    tx_desc->len = len;
                    tx_desc->options = 0;
                    pkt_nb++;
                }
                len -= tx_desc->len;
                addr += xsk_frame_size;

                if (i == batch_size) {
                    /* Remember len, addr, pkt_nb for next iteration.
                     * Skipped for simplicity.
                     */
                    break;
                }
            } while (len);
        }

        xsk_ring_prod__submit(&xsk->tx, i);
    }

Multi-buffer 지원 조사

723-749

Multi-buffer 지원 조사

Driver가 SKB 또는 DRV mode에서 multi-buffer AF_XDP를 지원하는지 알아보려면 `linux/netdev.h`의 netlink `XDP_FEATURES` feature를 사용해 `NETDEV_XDP_ACT_RX_SG` 지원을 질의합니다. XDP multi-buffer 지원을 질의할 때와 같은 flag입니다. Driver가 XDP multi-buffer를 지원하면 AF_XDP도 SKB·DRV mode에서 이를 지원합니다.

Zero-copy mode 지원 여부를 알아보려면 `XDP_FEATURES`에서 먼저 `NETDEV_XDP_ACT_XSK_ZEROCOPY` flag를 확인합니다. 설정되어 있으면 적어도 zero-copy를 지원하므로 `linux/netdev.h`의 netlink attribute `NETDEV_A_DEV_XDP_ZC_MAX_SEGS`를 확인합니다. 반환되는 unsigned integer는 이 device가 zero-copy mode에서 지원하는 최대 fragment 수입니다.

  • `1`: 최대 한 fragment만 지원하므로 이 device는 zero-copy multi-buffer를 지원하지 않습니다.
  • `>= 2`: 이 device는 zero-copy multi-buffer를 지원하며 반환값이 최대 fragment 수입니다.

libbpf를 통한 사용 예는 `tools/testing/selftests/bpf/xskxceiver.c`를 참조하십시오.

Probing for Multi-Buffer Support
--------------------------------

To discover if a driver supports multi-buffer AF_XDP in SKB or DRV
mode, use the XDP_FEATURES feature of netlink in linux/netdev.h to
query for NETDEV_XDP_ACT_RX_SG support. This is the same flag as for
querying for XDP multi-buffer support. If XDP supports multi-buffer in
a driver, then AF_XDP will also support that in SKB and DRV mode.

To discover if a driver supports multi-buffer AF_XDP in zero-copy
mode, use XDP_FEATURES and first check the NETDEV_XDP_ACT_XSK_ZEROCOPY
flag. If it is set, it means that at least zero-copy is supported and
you should go and check the netlink attribute
NETDEV_A_DEV_XDP_ZC_MAX_SEGS in linux/netdev.h. An unsigned integer
value will be returned stating the max number of frags that are
supported by this device in zero-copy mode. These are the possible
return values:

1: Multi-buffer for zero-copy is not supported by this device, as max
   one fragment supported means that multi-buffer is not possible.

>=2: Multi-buffer is supported in zero-copy mode for this device. The
     returned number signifies the max number of frags supported.

For an example on how these are used through libbpf, please take a
look at tools/testing/selftests/bpf/xskxceiver.c.

Zero-copy driver의 multi-buffer

750-757

Zero-copy driver의 multi-buffer 지원

Zero-copy driver는 보통 RX·TX 처리에 batch API를 사용합니다. TX batch API는 batch 마지막이 완전한 패킷으로 끝나는 TX descriptor batch를 제공한다고 보장합니다. 이는 zero-copy driver에 multi-buffer 지원을 쉽게 추가하기 위한 것입니다.

Multi-Buffer Support for Zero-Copy Drivers
------------------------------------------

Zero-copy drivers usually use the batched APIs for Rx and Tx
processing. Note that the Tx batch API guarantees that it will provide
a batch of Tx descriptors that ends with full packet at the end. This
to facilitate extending a zero-copy driver with multi-buffer support.

Sample application

758-783

Sample application

Private UMEM을 사용하는 AF_XDP socket 예를 보여 주는 `xdpsock` benchmark·test application은 `https://github.com/xdp-project/bpf-examples/tree/main/AF_XDP-example`에서 찾을 수 있습니다.

UDP port 4242 traffic을 AF_XDP를 켤 queue 16으로 보내려면 다음처럼 `ethtool`을 사용합니다.

for this::

      ethtool -N p3p2 rx-flow-hash udp4 fn
      ethtool -N p3p2 flow-type udp4 src-port 4242 dst-port 4242 \
          action 16

XDP_DRV mode에서 RX drop benchmark는 다음처럼 실행합니다.

using::

      samples/bpf/xdpsock -i p3p2 -q 16 -r -N

XDP_SKB mode에서는 `-N` 대신 `-S` switch를 사용합니다. 평소처럼 `-h`로 모든 option을 표시할 수 있습니다.

이 sample application은 AF_XDP 설정과 사용을 단순하게 만들기 위해 libbpf를 사용합니다. 더 고급 기능을 위해 AF_XDP raw UAPI가 실제로 어떻게 사용되는지 보려면 `tools/testing/selftests/bpf/xsk.[ch]`의 libbpf code를 참조하십시오.

Sample application
==================
There is a xdpsock benchmarking/test application that can be found at
https://github.com/xdp-project/bpf-examples/tree/main/AF_XDP-example
that demonstrates how to use AF_XDP sockets with private
UMEMs. Say that you would like your UDP traffic from port 4242 to end
up in queue 16, that we will enable AF_XDP on. Here, we use ethtool
for this::

      ethtool -N p3p2 rx-flow-hash udp4 fn
      ethtool -N p3p2 flow-type udp4 src-port 4242 dst-port 4242 \
          action 16

Running the rxdrop benchmark in XDP_DRV mode can then be done
using::

      samples/bpf/xdpsock -i p3p2 -q 16 -r -N

For XDP_SKB mode, use the switch "-S" instead of "-N" and all options
can be displayed with "-h", as usual.

This sample application uses libbpf to make the setup and usage of
AF_XDP simpler. If you want to know how the raw uapi of AF_XDP is
really used to make something more advanced, take a look at the libbpf
code in tools/testing/selftests/bpf/xsk.[ch].

FAQ

784-841

FAQ

Q: Socket에 traffic이 보이지 않습니다. 무엇이 잘못되었습니까?

A: 물리 NIC의 netdev를 초기화할 때 Linux는 보통 core마다 RX·TX queue pair 하나를 할당합니다. 8-core system이라면 core마다 하나씩 queue id 0~7을 만듭니다. AF_XDP `bind` 또는 libbpf `xsk_socket__create` 호출에서 특정 queue id를 지정하며, socket에는 그 queue로 향하는 traffic만 들어옵니다. Queue 0에 bind하면 queue 1~7로 분배된 traffic은 받지 못합니다. 운이 좋으면 traffic이 보일 수 있지만 보통 bind하지 않은 queue로 갑니다.

원하는 traffic을 bind한 queue id로 보내는 방법은 여러 가지입니다. 모든 traffic을 보려면 netdev가 queue 0 하나만 갖도록 강제한 뒤 queue 0에 bind할 수 있습니다.

   id 0, and then bind to queue 0. You can use ethtool to do this::

     sudo ethtool -L <interface> combined 1

Traffic 일부만 보려면 `ethtool`로 NIC를 설정해 원하는 traffic을 bind할 단일 queue로 filter할 수 있습니다. 다음 예는 port 4242로 오가는 UDP traffic을 queue 2로 보냅니다.

   UDP traffic to and from port 4242 are sent to queue 2::

     sudo ethtool -N <interface> rx-flow-hash udp4 fn
     sudo ethtool -N <interface> flow-type udp4 src-port 4242 dst-port \
     4242 action 2

그 밖의 방법은 NIC가 제공하는 기능에 따라 달라집니다.

Q: Copy mode에서 XSKMAP으로 서로 다른 UMEM 사이의 switch를 구현할 수 있습니까?

A: 현재는 지원하지 않습니다. XSKMAP은 queue id X로 들어온 traffic을 같은 queue id X에 bind된 socket으로만 전환할 수 있습니다. XSKMAP에 X와 Y처럼 서로 다른 queue id에 bind된 socket이 들어 있어도 queue id Y로 들어온 traffic만 같은 Y에 bind된 socket으로 보낼 수 있습니다. Zero-copy mode에서는 NIC의 switch 또는 다른 분배 메커니즘으로 traffic을 올바른 queue id와 socket에 보내야 합니다.

Q: 패킷이 가끔 손상됩니다. 무엇이 잘못되었습니까?

A: UMEM의 같은 buffer를 동시에 둘 이상의 ring에 넣지 않도록 주의해야 합니다. 같은 buffer를 FILL ring과 TX ring에 동시에 넣으면 NIC가 buffer로 data를 받는 동시에 그 buffer를 송신할 수 있어 패킷이 손상됩니다. `XDP_SHARED_UMEM`으로 bind한 서로 다른 queue id 또는 netdev의 FILL ring에 같은 buffer를 동시에 넣어도 같은 문제가 생깁니다.

FAQ
=======

Q: I am not seeing any traffic on the socket. What am I doing wrong?

A: When a netdev of a physical NIC is initialized, Linux usually
   allocates one RX and TX queue pair per core. So on a 8 core system,
   queue ids 0 to 7 will be allocated, one per core. In the AF_XDP
   bind call or the xsk_socket__create libbpf function call, you
   specify a specific queue id to bind to and it is only the traffic
   towards that queue you are going to get on you socket. So in the
   example above, if you bind to queue 0, you are NOT going to get any
   traffic that is distributed to queues 1 through 7. If you are
   lucky, you will see the traffic, but usually it will end up on one
   of the queues you have not bound to.

   There are a number of ways to solve the problem of getting the
   traffic you want to the queue id you bound to. If you want to see
   all the traffic, you can force the netdev to only have 1 queue, queue
   id 0, and then bind to queue 0. You can use ethtool to do this::

     sudo ethtool -L <interface> combined 1

   If you want to only see part of the traffic, you can program the
   NIC through ethtool to filter out your traffic to a single queue id
   that you can bind your XDP socket to. Here is one example in which
   UDP traffic to and from port 4242 are sent to queue 2::

     sudo ethtool -N <interface> rx-flow-hash udp4 fn
     sudo ethtool -N <interface> flow-type udp4 src-port 4242 dst-port \
     4242 action 2

   A number of other ways are possible all up to the capabilities of
   the NIC you have.

Q: Can I use the XSKMAP to implement a switch between different umems
   in copy mode?

A: The short answer is no, that is not supported at the moment. The
   XSKMAP can only be used to switch traffic coming in on queue id X
   to sockets bound to the same queue id X. The XSKMAP can contain
   sockets bound to different queue ids, for example X and Y, but only
   traffic goming in from queue id Y can be directed to sockets bound
   to the same queue id Y. In zero-copy mode, you should use the
   switch, or other distribution mechanism, in your NIC to direct
   traffic to the correct queue id and socket.

Q: My packets are sometimes corrupted. What is wrong?

A: Care has to be taken not to feed the same buffer in the UMEM into
   more than one ring at the same time. If you for example feed the
   same buffer into the FILL ring and the TX ring at the same time, the
   NIC might receive data into the buffer at the same time it is
   sending it. This will cause some packets to become corrupted. Same
   thing goes for feeding the same buffer into the FILL rings
   belonging to different queue ids or netdevs bound with the
   XDP_SHARED_UMEM flag.

기여자

842-855

기여자

  • Björn Töpel (AF_XDP core)
  • Magnus Karlsson (AF_XDP core)
  • Alexander Duyck
  • Alexei Starovoitov
  • Daniel Borkmann
  • Jesper Dangaard Brouer
  • John Fastabend
  • Jonathan Corbet (LWN coverage)
  • Michael S. Tsirkin
  • Qi Z Zhang
  • Willem de Bruijn
Credits
=======

- Björn Töpel (AF_XDP core)
- Magnus Karlsson (AF_XDP core)
- Alexander Duyck
- Alexei Starovoitov
- Daniel Borkmann
- Jesper Dangaard Brouer
- John Fastabend
- Jonathan Corbet (LWN coverage)
- Michael S. Tsirkin
- Qi Z Zhang
- Willem de Bruijn