blob: 57ecadc5840397a3118abcdce41ced7ec8892ab2 [file] [log] [blame]
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +02001// SPDX-License-Identifier: GPL-2.0
Björn Töpeldac091492018-05-18 14:00:21 +02002/* Copyright(c) 2017 - 2018 Intel Corporation. */
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +02003
4#include <assert.h>
5#include <errno.h>
6#include <getopt.h>
7#include <libgen.h>
8#include <linux/bpf.h>
9#include <linux/if_link.h>
10#include <linux/if_xdp.h>
11#include <linux/if_ether.h>
12#include <net/if.h>
13#include <signal.h>
14#include <stdbool.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#include <net/ethernet.h>
19#include <sys/resource.h>
20#include <sys/socket.h>
21#include <sys/mman.h>
22#include <time.h>
23#include <unistd.h>
24#include <pthread.h>
25#include <locale.h>
26#include <sys/types.h>
27#include <poll.h>
28
Jakub Kicinski67481822018-07-26 14:32:21 -070029#include "bpf/libbpf.h"
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +020030#include "bpf_util.h"
Jakub Kicinski2bf3e2e2018-05-14 22:35:02 -070031#include <bpf/bpf.h>
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +020032
33#include "xdpsock.h"
34
35#ifndef SOL_XDP
36#define SOL_XDP 283
37#endif
38
39#ifndef AF_XDP
40#define AF_XDP 44
41#endif
42
43#ifndef PF_XDP
44#define PF_XDP AF_XDP
45#endif
46
47#define NUM_FRAMES 131072
48#define FRAME_HEADROOM 0
Björn Töpela412ef52018-06-04 13:57:14 +020049#define FRAME_SHIFT 11
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +020050#define FRAME_SIZE 2048
51#define NUM_DESCS 1024
52#define BATCH_SIZE 16
53
54#define FQ_NUM_DESCS 1024
55#define CQ_NUM_DESCS 1024
56
57#define DEBUG_HEXDUMP 0
58
Björn Töpela412ef52018-06-04 13:57:14 +020059typedef __u64 u64;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +020060typedef __u32 u32;
61
62static unsigned long prev_time;
63
64enum benchmark_type {
65 BENCH_RXDROP = 0,
66 BENCH_TXONLY = 1,
67 BENCH_L2FWD = 2,
68};
69
70static enum benchmark_type opt_bench = BENCH_RXDROP;
71static u32 opt_xdp_flags;
72static const char *opt_if = "";
73static int opt_ifindex;
74static int opt_queue;
75static int opt_poll;
76static int opt_shared_packet_buffer;
77static int opt_interval = 1;
Björn Töpel9f5232c2018-06-04 14:06:01 +020078static u32 opt_xdp_bind_flags;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +020079
80struct xdp_umem_uqueue {
81 u32 cached_prod;
82 u32 cached_cons;
83 u32 mask;
84 u32 size;
Björn Töpel1c4917d2018-05-22 09:35:00 +020085 u32 *producer;
86 u32 *consumer;
Björn Töpela412ef52018-06-04 13:57:14 +020087 u64 *ring;
Björn Töpel1c4917d2018-05-22 09:35:00 +020088 void *map;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +020089};
90
91struct xdp_umem {
Björn Töpela412ef52018-06-04 13:57:14 +020092 char *frames;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +020093 struct xdp_umem_uqueue fq;
94 struct xdp_umem_uqueue cq;
95 int fd;
96};
97
98struct xdp_uqueue {
99 u32 cached_prod;
100 u32 cached_cons;
101 u32 mask;
102 u32 size;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200103 u32 *producer;
104 u32 *consumer;
105 struct xdp_desc *ring;
106 void *map;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200107};
108
109struct xdpsock {
110 struct xdp_uqueue rx;
111 struct xdp_uqueue tx;
112 int sfd;
113 struct xdp_umem *umem;
114 u32 outstanding_tx;
115 unsigned long rx_npkts;
116 unsigned long tx_npkts;
117 unsigned long prev_rx_npkts;
118 unsigned long prev_tx_npkts;
119};
120
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200121static int num_socks;
122struct xdpsock *xsks[MAX_SOCKS];
123
124static unsigned long get_nsecs(void)
125{
126 struct timespec ts;
127
128 clock_gettime(CLOCK_MONOTONIC, &ts);
129 return ts.tv_sec * 1000000000UL + ts.tv_nsec;
130}
131
132static void dump_stats(void);
133
134#define lassert(expr) \
135 do { \
136 if (!(expr)) { \
137 fprintf(stderr, "%s:%s:%i: Assertion failed: " \
138 #expr ": errno: %d/\"%s\"\n", \
139 __FILE__, __func__, __LINE__, \
140 errno, strerror(errno)); \
141 dump_stats(); \
142 exit(EXIT_FAILURE); \
143 } \
144 } while (0)
145
146#define barrier() __asm__ __volatile__("": : :"memory")
Brian Brooks598135e2018-07-25 16:08:19 -0500147#ifdef __aarch64__
148#define u_smp_rmb() __asm__ __volatile__("dmb ishld": : :"memory")
149#define u_smp_wmb() __asm__ __volatile__("dmb ishst": : :"memory")
150#else
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200151#define u_smp_rmb() barrier()
152#define u_smp_wmb() barrier()
Brian Brooks598135e2018-07-25 16:08:19 -0500153#endif
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200154#define likely(x) __builtin_expect(!!(x), 1)
155#define unlikely(x) __builtin_expect(!!(x), 0)
156
157static const char pkt_data[] =
158 "\x3c\xfd\xfe\x9e\x7f\x71\xec\xb1\xd7\x98\x3a\xc0\x08\x00\x45\x00"
159 "\x00\x2e\x00\x00\x00\x00\x40\x11\x88\x97\x05\x08\x07\x08\xc8\x14"
160 "\x1e\x04\x10\x92\x10\x92\x00\x1a\x6d\xa3\x34\x33\x1f\x69\x40\x6b"
161 "\x54\x59\xb6\x14\x2d\x11\x44\xbf\xaf\xd9\xbe\xaa";
162
163static inline u32 umem_nb_free(struct xdp_umem_uqueue *q, u32 nb)
164{
Magnus Karlssona65ea682018-06-04 13:57:15 +0200165 u32 free_entries = q->cached_cons - q->cached_prod;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200166
167 if (free_entries >= nb)
168 return free_entries;
169
170 /* Refresh the local tail pointer */
Magnus Karlssona65ea682018-06-04 13:57:15 +0200171 q->cached_cons = *q->consumer + q->size;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200172
Magnus Karlssona65ea682018-06-04 13:57:15 +0200173 return q->cached_cons - q->cached_prod;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200174}
175
176static inline u32 xq_nb_free(struct xdp_uqueue *q, u32 ndescs)
177{
178 u32 free_entries = q->cached_cons - q->cached_prod;
179
180 if (free_entries >= ndescs)
181 return free_entries;
182
183 /* Refresh the local tail pointer */
Björn Töpel1c4917d2018-05-22 09:35:00 +0200184 q->cached_cons = *q->consumer + q->size;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200185 return q->cached_cons - q->cached_prod;
186}
187
188static inline u32 umem_nb_avail(struct xdp_umem_uqueue *q, u32 nb)
189{
190 u32 entries = q->cached_prod - q->cached_cons;
191
192 if (entries == 0) {
Björn Töpel1c4917d2018-05-22 09:35:00 +0200193 q->cached_prod = *q->producer;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200194 entries = q->cached_prod - q->cached_cons;
195 }
196
197 return (entries > nb) ? nb : entries;
198}
199
200static inline u32 xq_nb_avail(struct xdp_uqueue *q, u32 ndescs)
201{
202 u32 entries = q->cached_prod - q->cached_cons;
203
204 if (entries == 0) {
Björn Töpel1c4917d2018-05-22 09:35:00 +0200205 q->cached_prod = *q->producer;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200206 entries = q->cached_prod - q->cached_cons;
207 }
208
209 return (entries > ndescs) ? ndescs : entries;
210}
211
212static inline int umem_fill_to_kernel_ex(struct xdp_umem_uqueue *fq,
213 struct xdp_desc *d,
214 size_t nb)
215{
216 u32 i;
217
218 if (umem_nb_free(fq, nb) < nb)
219 return -ENOSPC;
220
221 for (i = 0; i < nb; i++) {
222 u32 idx = fq->cached_prod++ & fq->mask;
223
Björn Töpela412ef52018-06-04 13:57:14 +0200224 fq->ring[idx] = d[i].addr;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200225 }
226
227 u_smp_wmb();
228
Björn Töpel1c4917d2018-05-22 09:35:00 +0200229 *fq->producer = fq->cached_prod;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200230
231 return 0;
232}
233
Björn Töpela412ef52018-06-04 13:57:14 +0200234static inline int umem_fill_to_kernel(struct xdp_umem_uqueue *fq, u64 *d,
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200235 size_t nb)
236{
237 u32 i;
238
239 if (umem_nb_free(fq, nb) < nb)
240 return -ENOSPC;
241
242 for (i = 0; i < nb; i++) {
243 u32 idx = fq->cached_prod++ & fq->mask;
244
Björn Töpel1c4917d2018-05-22 09:35:00 +0200245 fq->ring[idx] = d[i];
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200246 }
247
248 u_smp_wmb();
249
Björn Töpel1c4917d2018-05-22 09:35:00 +0200250 *fq->producer = fq->cached_prod;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200251
252 return 0;
253}
254
255static inline size_t umem_complete_from_kernel(struct xdp_umem_uqueue *cq,
Björn Töpela412ef52018-06-04 13:57:14 +0200256 u64 *d, size_t nb)
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200257{
258 u32 idx, i, entries = umem_nb_avail(cq, nb);
259
260 u_smp_rmb();
261
262 for (i = 0; i < entries; i++) {
263 idx = cq->cached_cons++ & cq->mask;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200264 d[i] = cq->ring[idx];
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200265 }
266
267 if (entries > 0) {
268 u_smp_wmb();
269
Björn Töpel1c4917d2018-05-22 09:35:00 +0200270 *cq->consumer = cq->cached_cons;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200271 }
272
273 return entries;
274}
275
Björn Töpela412ef52018-06-04 13:57:14 +0200276static inline void *xq_get_data(struct xdpsock *xsk, u64 addr)
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200277{
Björn Töpela412ef52018-06-04 13:57:14 +0200278 return &xsk->umem->frames[addr];
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200279}
280
281static inline int xq_enq(struct xdp_uqueue *uq,
282 const struct xdp_desc *descs,
283 unsigned int ndescs)
284{
Björn Töpel1c4917d2018-05-22 09:35:00 +0200285 struct xdp_desc *r = uq->ring;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200286 unsigned int i;
287
288 if (xq_nb_free(uq, ndescs) < ndescs)
289 return -ENOSPC;
290
291 for (i = 0; i < ndescs; i++) {
292 u32 idx = uq->cached_prod++ & uq->mask;
293
Björn Töpela412ef52018-06-04 13:57:14 +0200294 r[idx].addr = descs[i].addr;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200295 r[idx].len = descs[i].len;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200296 }
297
298 u_smp_wmb();
299
Björn Töpel1c4917d2018-05-22 09:35:00 +0200300 *uq->producer = uq->cached_prod;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200301 return 0;
302}
303
304static inline int xq_enq_tx_only(struct xdp_uqueue *uq,
Björn Töpela412ef52018-06-04 13:57:14 +0200305 unsigned int id, unsigned int ndescs)
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200306{
Björn Töpel1c4917d2018-05-22 09:35:00 +0200307 struct xdp_desc *r = uq->ring;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200308 unsigned int i;
309
310 if (xq_nb_free(uq, ndescs) < ndescs)
311 return -ENOSPC;
312
313 for (i = 0; i < ndescs; i++) {
314 u32 idx = uq->cached_prod++ & uq->mask;
315
Björn Töpela412ef52018-06-04 13:57:14 +0200316 r[idx].addr = (id + i) << FRAME_SHIFT;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200317 r[idx].len = sizeof(pkt_data) - 1;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200318 }
319
320 u_smp_wmb();
321
Björn Töpel1c4917d2018-05-22 09:35:00 +0200322 *uq->producer = uq->cached_prod;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200323 return 0;
324}
325
326static inline int xq_deq(struct xdp_uqueue *uq,
327 struct xdp_desc *descs,
328 int ndescs)
329{
Björn Töpel1c4917d2018-05-22 09:35:00 +0200330 struct xdp_desc *r = uq->ring;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200331 unsigned int idx;
332 int i, entries;
333
334 entries = xq_nb_avail(uq, ndescs);
335
336 u_smp_rmb();
337
338 for (i = 0; i < entries; i++) {
339 idx = uq->cached_cons++ & uq->mask;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200340 descs[i] = r[idx];
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200341 }
342
343 if (entries > 0) {
344 u_smp_wmb();
345
Björn Töpel1c4917d2018-05-22 09:35:00 +0200346 *uq->consumer = uq->cached_cons;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200347 }
348
349 return entries;
350}
351
352static void swap_mac_addresses(void *data)
353{
354 struct ether_header *eth = (struct ether_header *)data;
355 struct ether_addr *src_addr = (struct ether_addr *)&eth->ether_shost;
356 struct ether_addr *dst_addr = (struct ether_addr *)&eth->ether_dhost;
357 struct ether_addr tmp;
358
359 tmp = *src_addr;
360 *src_addr = *dst_addr;
361 *dst_addr = tmp;
362}
363
Björn Töpela412ef52018-06-04 13:57:14 +0200364static void hex_dump(void *pkt, size_t length, u64 addr)
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200365{
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200366 const unsigned char *address = (unsigned char *)pkt;
367 const unsigned char *line = address;
368 size_t line_size = 32;
369 unsigned char c;
Björn Töpela412ef52018-06-04 13:57:14 +0200370 char buf[32];
371 int i = 0;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200372
Björn Töpela412ef52018-06-04 13:57:14 +0200373 if (!DEBUG_HEXDUMP)
374 return;
375
376 sprintf(buf, "addr=%llu", addr);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200377 printf("length = %zu\n", length);
Björn Töpela412ef52018-06-04 13:57:14 +0200378 printf("%s | ", buf);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200379 while (length-- > 0) {
380 printf("%02X ", *address++);
381 if (!(++i % line_size) || (length == 0 && i % line_size)) {
382 if (length == 0) {
383 while (i++ % line_size)
384 printf("__ ");
385 }
386 printf(" | "); /* right close */
387 while (line < address) {
388 c = *line++;
389 printf("%c", (c < 33 || c == 255) ? 0x2E : c);
390 }
391 printf("\n");
392 if (length > 0)
Björn Töpela412ef52018-06-04 13:57:14 +0200393 printf("%s | ", buf);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200394 }
395 }
396 printf("\n");
397}
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200398
399static size_t gen_eth_frame(char *frame)
400{
401 memcpy(frame, pkt_data, sizeof(pkt_data) - 1);
402 return sizeof(pkt_data) - 1;
403}
404
405static struct xdp_umem *xdp_umem_configure(int sfd)
406{
407 int fq_size = FQ_NUM_DESCS, cq_size = CQ_NUM_DESCS;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200408 struct xdp_mmap_offsets off;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200409 struct xdp_umem_reg mr;
410 struct xdp_umem *umem;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200411 socklen_t optlen;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200412 void *bufs;
413
414 umem = calloc(1, sizeof(*umem));
415 lassert(umem);
416
417 lassert(posix_memalign(&bufs, getpagesize(), /* PAGE_SIZE aligned */
418 NUM_FRAMES * FRAME_SIZE) == 0);
419
420 mr.addr = (__u64)bufs;
421 mr.len = NUM_FRAMES * FRAME_SIZE;
Björn Töpela412ef52018-06-04 13:57:14 +0200422 mr.chunk_size = FRAME_SIZE;
423 mr.headroom = FRAME_HEADROOM;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200424
425 lassert(setsockopt(sfd, SOL_XDP, XDP_UMEM_REG, &mr, sizeof(mr)) == 0);
426 lassert(setsockopt(sfd, SOL_XDP, XDP_UMEM_FILL_RING, &fq_size,
427 sizeof(int)) == 0);
428 lassert(setsockopt(sfd, SOL_XDP, XDP_UMEM_COMPLETION_RING, &cq_size,
429 sizeof(int)) == 0);
430
Björn Töpel1c4917d2018-05-22 09:35:00 +0200431 optlen = sizeof(off);
432 lassert(getsockopt(sfd, SOL_XDP, XDP_MMAP_OFFSETS, &off,
433 &optlen) == 0);
434
435 umem->fq.map = mmap(0, off.fr.desc +
Björn Töpela412ef52018-06-04 13:57:14 +0200436 FQ_NUM_DESCS * sizeof(u64),
Björn Töpel1c4917d2018-05-22 09:35:00 +0200437 PROT_READ | PROT_WRITE,
438 MAP_SHARED | MAP_POPULATE, sfd,
439 XDP_UMEM_PGOFF_FILL_RING);
440 lassert(umem->fq.map != MAP_FAILED);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200441
442 umem->fq.mask = FQ_NUM_DESCS - 1;
443 umem->fq.size = FQ_NUM_DESCS;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200444 umem->fq.producer = umem->fq.map + off.fr.producer;
445 umem->fq.consumer = umem->fq.map + off.fr.consumer;
446 umem->fq.ring = umem->fq.map + off.fr.desc;
Magnus Karlssona65ea682018-06-04 13:57:15 +0200447 umem->fq.cached_cons = FQ_NUM_DESCS;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200448
Björn Töpel1c4917d2018-05-22 09:35:00 +0200449 umem->cq.map = mmap(0, off.cr.desc +
Björn Töpela412ef52018-06-04 13:57:14 +0200450 CQ_NUM_DESCS * sizeof(u64),
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200451 PROT_READ | PROT_WRITE,
452 MAP_SHARED | MAP_POPULATE, sfd,
453 XDP_UMEM_PGOFF_COMPLETION_RING);
Björn Töpel1c4917d2018-05-22 09:35:00 +0200454 lassert(umem->cq.map != MAP_FAILED);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200455
456 umem->cq.mask = CQ_NUM_DESCS - 1;
457 umem->cq.size = CQ_NUM_DESCS;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200458 umem->cq.producer = umem->cq.map + off.cr.producer;
459 umem->cq.consumer = umem->cq.map + off.cr.consumer;
460 umem->cq.ring = umem->cq.map + off.cr.desc;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200461
Björn Töpela412ef52018-06-04 13:57:14 +0200462 umem->frames = bufs;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200463 umem->fd = sfd;
464
465 if (opt_bench == BENCH_TXONLY) {
466 int i;
467
Björn Töpela412ef52018-06-04 13:57:14 +0200468 for (i = 0; i < NUM_FRAMES * FRAME_SIZE; i += FRAME_SIZE)
469 (void)gen_eth_frame(&umem->frames[i]);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200470 }
471
472 return umem;
473}
474
475static struct xdpsock *xsk_configure(struct xdp_umem *umem)
476{
477 struct sockaddr_xdp sxdp = {};
Björn Töpel1c4917d2018-05-22 09:35:00 +0200478 struct xdp_mmap_offsets off;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200479 int sfd, ndescs = NUM_DESCS;
480 struct xdpsock *xsk;
481 bool shared = true;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200482 socklen_t optlen;
Björn Töpela412ef52018-06-04 13:57:14 +0200483 u64 i;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200484
485 sfd = socket(PF_XDP, SOCK_RAW, 0);
486 lassert(sfd >= 0);
487
488 xsk = calloc(1, sizeof(*xsk));
489 lassert(xsk);
490
491 xsk->sfd = sfd;
492 xsk->outstanding_tx = 0;
493
494 if (!umem) {
495 shared = false;
496 xsk->umem = xdp_umem_configure(sfd);
497 } else {
498 xsk->umem = umem;
499 }
500
501 lassert(setsockopt(sfd, SOL_XDP, XDP_RX_RING,
502 &ndescs, sizeof(int)) == 0);
503 lassert(setsockopt(sfd, SOL_XDP, XDP_TX_RING,
504 &ndescs, sizeof(int)) == 0);
Björn Töpel1c4917d2018-05-22 09:35:00 +0200505 optlen = sizeof(off);
506 lassert(getsockopt(sfd, SOL_XDP, XDP_MMAP_OFFSETS, &off,
507 &optlen) == 0);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200508
509 /* Rx */
Björn Töpel1c4917d2018-05-22 09:35:00 +0200510 xsk->rx.map = mmap(NULL,
511 off.rx.desc +
512 NUM_DESCS * sizeof(struct xdp_desc),
513 PROT_READ | PROT_WRITE,
514 MAP_SHARED | MAP_POPULATE, sfd,
515 XDP_PGOFF_RX_RING);
516 lassert(xsk->rx.map != MAP_FAILED);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200517
518 if (!shared) {
Björn Töpela412ef52018-06-04 13:57:14 +0200519 for (i = 0; i < NUM_DESCS * FRAME_SIZE; i += FRAME_SIZE)
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200520 lassert(umem_fill_to_kernel(&xsk->umem->fq, &i, 1)
521 == 0);
522 }
523
524 /* Tx */
Björn Töpel1c4917d2018-05-22 09:35:00 +0200525 xsk->tx.map = mmap(NULL,
526 off.tx.desc +
527 NUM_DESCS * sizeof(struct xdp_desc),
528 PROT_READ | PROT_WRITE,
529 MAP_SHARED | MAP_POPULATE, sfd,
530 XDP_PGOFF_TX_RING);
531 lassert(xsk->tx.map != MAP_FAILED);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200532
533 xsk->rx.mask = NUM_DESCS - 1;
534 xsk->rx.size = NUM_DESCS;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200535 xsk->rx.producer = xsk->rx.map + off.rx.producer;
536 xsk->rx.consumer = xsk->rx.map + off.rx.consumer;
537 xsk->rx.ring = xsk->rx.map + off.rx.desc;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200538
539 xsk->tx.mask = NUM_DESCS - 1;
540 xsk->tx.size = NUM_DESCS;
Björn Töpel1c4917d2018-05-22 09:35:00 +0200541 xsk->tx.producer = xsk->tx.map + off.tx.producer;
542 xsk->tx.consumer = xsk->tx.map + off.tx.consumer;
543 xsk->tx.ring = xsk->tx.map + off.tx.desc;
Magnus Karlssona65ea682018-06-04 13:57:15 +0200544 xsk->tx.cached_cons = NUM_DESCS;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200545
546 sxdp.sxdp_family = PF_XDP;
547 sxdp.sxdp_ifindex = opt_ifindex;
548 sxdp.sxdp_queue_id = opt_queue;
Björn Töpel9f5232c2018-06-04 14:06:01 +0200549
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200550 if (shared) {
551 sxdp.sxdp_flags = XDP_SHARED_UMEM;
552 sxdp.sxdp_shared_umem_fd = umem->fd;
Björn Töpel9f5232c2018-06-04 14:06:01 +0200553 } else {
554 sxdp.sxdp_flags = opt_xdp_bind_flags;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200555 }
556
557 lassert(bind(sfd, (struct sockaddr *)&sxdp, sizeof(sxdp)) == 0);
558
559 return xsk;
560}
561
562static void print_benchmark(bool running)
563{
564 const char *bench_str = "INVALID";
565
566 if (opt_bench == BENCH_RXDROP)
567 bench_str = "rxdrop";
568 else if (opt_bench == BENCH_TXONLY)
569 bench_str = "txonly";
570 else if (opt_bench == BENCH_L2FWD)
571 bench_str = "l2fwd";
572
573 printf("%s:%d %s ", opt_if, opt_queue, bench_str);
574 if (opt_xdp_flags & XDP_FLAGS_SKB_MODE)
575 printf("xdp-skb ");
576 else if (opt_xdp_flags & XDP_FLAGS_DRV_MODE)
577 printf("xdp-drv ");
578 else
579 printf(" ");
580
581 if (opt_poll)
582 printf("poll() ");
583
584 if (running) {
585 printf("running...");
586 fflush(stdout);
587 }
588}
589
590static void dump_stats(void)
591{
592 unsigned long now = get_nsecs();
593 long dt = now - prev_time;
594 int i;
595
596 prev_time = now;
597
Prashant Bhole11c3f512018-08-31 10:00:49 +0900598 for (i = 0; i < num_socks && xsks[i]; i++) {
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200599 char *fmt = "%-15s %'-11.0f %'-11lu\n";
600 double rx_pps, tx_pps;
601
602 rx_pps = (xsks[i]->rx_npkts - xsks[i]->prev_rx_npkts) *
603 1000000000. / dt;
604 tx_pps = (xsks[i]->tx_npkts - xsks[i]->prev_tx_npkts) *
605 1000000000. / dt;
606
607 printf("\n sock%d@", i);
608 print_benchmark(false);
609 printf("\n");
610
611 printf("%-15s %-11s %-11s %-11.2f\n", "", "pps", "pkts",
612 dt / 1000000000.);
613 printf(fmt, "rx", rx_pps, xsks[i]->rx_npkts);
614 printf(fmt, "tx", tx_pps, xsks[i]->tx_npkts);
615
616 xsks[i]->prev_rx_npkts = xsks[i]->rx_npkts;
617 xsks[i]->prev_tx_npkts = xsks[i]->tx_npkts;
618 }
619}
620
621static void *poller(void *arg)
622{
623 (void)arg;
624 for (;;) {
625 sleep(opt_interval);
626 dump_stats();
627 }
628
629 return NULL;
630}
631
632static void int_exit(int sig)
633{
634 (void)sig;
635 dump_stats();
636 bpf_set_link_xdp_fd(opt_ifindex, -1, opt_xdp_flags);
637 exit(EXIT_SUCCESS);
638}
639
640static struct option long_options[] = {
641 {"rxdrop", no_argument, 0, 'r'},
642 {"txonly", no_argument, 0, 't'},
643 {"l2fwd", no_argument, 0, 'l'},
644 {"interface", required_argument, 0, 'i'},
645 {"queue", required_argument, 0, 'q'},
646 {"poll", no_argument, 0, 'p'},
647 {"shared-buffer", no_argument, 0, 's'},
648 {"xdp-skb", no_argument, 0, 'S'},
649 {"xdp-native", no_argument, 0, 'N'},
650 {"interval", required_argument, 0, 'n'},
Björn Töpel58c50ae2018-08-28 14:44:35 +0200651 {"zero-copy", no_argument, 0, 'z'},
652 {"copy", no_argument, 0, 'c'},
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200653 {0, 0, 0, 0}
654};
655
656static void usage(const char *prog)
657{
658 const char *str =
659 " Usage: %s [OPTIONS]\n"
660 " Options:\n"
661 " -r, --rxdrop Discard all incoming packets (default)\n"
662 " -t, --txonly Only send packets\n"
663 " -l, --l2fwd MAC swap L2 forwarding\n"
664 " -i, --interface=n Run on interface n\n"
665 " -q, --queue=n Use queue n (default 0)\n"
666 " -p, --poll Use poll syscall\n"
667 " -s, --shared-buffer Use shared packet buffer\n"
668 " -S, --xdp-skb=n Use XDP skb-mod\n"
669 " -N, --xdp-native=n Enfore XDP native mode\n"
670 " -n, --interval=n Specify statistics update interval (default 1 sec).\n"
Björn Töpel58c50ae2018-08-28 14:44:35 +0200671 " -z, --zero-copy Force zero-copy mode.\n"
672 " -c, --copy Force copy mode.\n"
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200673 "\n";
674 fprintf(stderr, str, prog);
675 exit(EXIT_FAILURE);
676}
677
678static void parse_command_line(int argc, char **argv)
679{
680 int option_index, c;
681
682 opterr = 0;
683
684 for (;;) {
Björn Töpel58c50ae2018-08-28 14:44:35 +0200685 c = getopt_long(argc, argv, "rtli:q:psSNn:cz", long_options,
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200686 &option_index);
687 if (c == -1)
688 break;
689
690 switch (c) {
691 case 'r':
692 opt_bench = BENCH_RXDROP;
693 break;
694 case 't':
695 opt_bench = BENCH_TXONLY;
696 break;
697 case 'l':
698 opt_bench = BENCH_L2FWD;
699 break;
700 case 'i':
701 opt_if = optarg;
702 break;
703 case 'q':
704 opt_queue = atoi(optarg);
705 break;
706 case 's':
707 opt_shared_packet_buffer = 1;
708 break;
709 case 'p':
710 opt_poll = 1;
711 break;
712 case 'S':
713 opt_xdp_flags |= XDP_FLAGS_SKB_MODE;
Björn Töpel9f5232c2018-06-04 14:06:01 +0200714 opt_xdp_bind_flags |= XDP_COPY;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200715 break;
716 case 'N':
717 opt_xdp_flags |= XDP_FLAGS_DRV_MODE;
718 break;
719 case 'n':
720 opt_interval = atoi(optarg);
721 break;
Björn Töpel58c50ae2018-08-28 14:44:35 +0200722 case 'z':
723 opt_xdp_bind_flags |= XDP_ZEROCOPY;
724 break;
725 case 'c':
726 opt_xdp_bind_flags |= XDP_COPY;
727 break;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200728 default:
729 usage(basename(argv[0]));
730 }
731 }
732
733 opt_ifindex = if_nametoindex(opt_if);
734 if (!opt_ifindex) {
735 fprintf(stderr, "ERROR: interface \"%s\" does not exist\n",
736 opt_if);
737 usage(basename(argv[0]));
738 }
739}
740
741static void kick_tx(int fd)
742{
743 int ret;
744
745 ret = sendto(fd, NULL, 0, MSG_DONTWAIT, NULL, 0);
Magnus Karlssonc03079c2018-06-29 09:48:19 +0200746 if (ret >= 0 || errno == ENOBUFS || errno == EAGAIN || errno == EBUSY)
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200747 return;
748 lassert(0);
749}
750
751static inline void complete_tx_l2fwd(struct xdpsock *xsk)
752{
Björn Töpela412ef52018-06-04 13:57:14 +0200753 u64 descs[BATCH_SIZE];
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200754 unsigned int rcvd;
755 size_t ndescs;
756
757 if (!xsk->outstanding_tx)
758 return;
759
760 kick_tx(xsk->sfd);
761 ndescs = (xsk->outstanding_tx > BATCH_SIZE) ? BATCH_SIZE :
762 xsk->outstanding_tx;
763
764 /* re-add completed Tx buffers */
765 rcvd = umem_complete_from_kernel(&xsk->umem->cq, descs, ndescs);
766 if (rcvd > 0) {
767 umem_fill_to_kernel(&xsk->umem->fq, descs, rcvd);
768 xsk->outstanding_tx -= rcvd;
769 xsk->tx_npkts += rcvd;
770 }
771}
772
773static inline void complete_tx_only(struct xdpsock *xsk)
774{
Björn Töpela412ef52018-06-04 13:57:14 +0200775 u64 descs[BATCH_SIZE];
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200776 unsigned int rcvd;
777
778 if (!xsk->outstanding_tx)
779 return;
780
781 kick_tx(xsk->sfd);
782
783 rcvd = umem_complete_from_kernel(&xsk->umem->cq, descs, BATCH_SIZE);
784 if (rcvd > 0) {
785 xsk->outstanding_tx -= rcvd;
786 xsk->tx_npkts += rcvd;
787 }
788}
789
790static void rx_drop(struct xdpsock *xsk)
791{
792 struct xdp_desc descs[BATCH_SIZE];
793 unsigned int rcvd, i;
794
795 rcvd = xq_deq(&xsk->rx, descs, BATCH_SIZE);
796 if (!rcvd)
797 return;
798
799 for (i = 0; i < rcvd; i++) {
Björn Töpela412ef52018-06-04 13:57:14 +0200800 char *pkt = xq_get_data(xsk, descs[i].addr);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200801
Björn Töpela412ef52018-06-04 13:57:14 +0200802 hex_dump(pkt, descs[i].len, descs[i].addr);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200803 }
804
805 xsk->rx_npkts += rcvd;
806
807 umem_fill_to_kernel_ex(&xsk->umem->fq, descs, rcvd);
808}
809
810static void rx_drop_all(void)
811{
812 struct pollfd fds[MAX_SOCKS + 1];
813 int i, ret, timeout, nfds = 1;
814
815 memset(fds, 0, sizeof(fds));
816
817 for (i = 0; i < num_socks; i++) {
818 fds[i].fd = xsks[i]->sfd;
819 fds[i].events = POLLIN;
820 timeout = 1000; /* 1sn */
821 }
822
823 for (;;) {
824 if (opt_poll) {
825 ret = poll(fds, nfds, timeout);
826 if (ret <= 0)
827 continue;
828 }
829
830 for (i = 0; i < num_socks; i++)
831 rx_drop(xsks[i]);
832 }
833}
834
835static void tx_only(struct xdpsock *xsk)
836{
837 int timeout, ret, nfds = 1;
838 struct pollfd fds[nfds + 1];
839 unsigned int idx = 0;
840
841 memset(fds, 0, sizeof(fds));
842 fds[0].fd = xsk->sfd;
843 fds[0].events = POLLOUT;
844 timeout = 1000; /* 1sn */
845
846 for (;;) {
847 if (opt_poll) {
848 ret = poll(fds, nfds, timeout);
849 if (ret <= 0)
850 continue;
851
852 if (fds[0].fd != xsk->sfd ||
853 !(fds[0].revents & POLLOUT))
854 continue;
855 }
856
857 if (xq_nb_free(&xsk->tx, BATCH_SIZE) >= BATCH_SIZE) {
858 lassert(xq_enq_tx_only(&xsk->tx, idx, BATCH_SIZE) == 0);
859
860 xsk->outstanding_tx += BATCH_SIZE;
861 idx += BATCH_SIZE;
862 idx %= NUM_FRAMES;
863 }
864
865 complete_tx_only(xsk);
866 }
867}
868
869static void l2fwd(struct xdpsock *xsk)
870{
871 for (;;) {
872 struct xdp_desc descs[BATCH_SIZE];
873 unsigned int rcvd, i;
874 int ret;
875
876 for (;;) {
877 complete_tx_l2fwd(xsk);
878
879 rcvd = xq_deq(&xsk->rx, descs, BATCH_SIZE);
880 if (rcvd > 0)
881 break;
882 }
883
884 for (i = 0; i < rcvd; i++) {
Björn Töpela412ef52018-06-04 13:57:14 +0200885 char *pkt = xq_get_data(xsk, descs[i].addr);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200886
887 swap_mac_addresses(pkt);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200888
Björn Töpela412ef52018-06-04 13:57:14 +0200889 hex_dump(pkt, descs[i].len, descs[i].addr);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200890 }
891
892 xsk->rx_npkts += rcvd;
893
894 ret = xq_enq(&xsk->tx, descs, rcvd);
895 lassert(ret == 0);
896 xsk->outstanding_tx += rcvd;
897 }
898}
899
900int main(int argc, char **argv)
901{
902 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
Jakub Kicinski67481822018-07-26 14:32:21 -0700903 struct bpf_prog_load_attr prog_load_attr = {
904 .prog_type = BPF_PROG_TYPE_XDP,
905 };
906 int prog_fd, qidconf_map, xsks_map;
907 struct bpf_object *obj;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200908 char xdp_filename[256];
Jakub Kicinski67481822018-07-26 14:32:21 -0700909 struct bpf_map *map;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200910 int i, ret, key = 0;
911 pthread_t pt;
912
913 parse_command_line(argc, argv);
914
915 if (setrlimit(RLIMIT_MEMLOCK, &r)) {
916 fprintf(stderr, "ERROR: setrlimit(RLIMIT_MEMLOCK) \"%s\"\n",
917 strerror(errno));
918 exit(EXIT_FAILURE);
919 }
920
921 snprintf(xdp_filename, sizeof(xdp_filename), "%s_kern.o", argv[0]);
Jakub Kicinski67481822018-07-26 14:32:21 -0700922 prog_load_attr.file = xdp_filename;
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200923
Jakub Kicinski67481822018-07-26 14:32:21 -0700924 if (bpf_prog_load_xattr(&prog_load_attr, &obj, &prog_fd))
925 exit(EXIT_FAILURE);
926 if (prog_fd < 0) {
927 fprintf(stderr, "ERROR: no program found: %s\n",
928 strerror(prog_fd));
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200929 exit(EXIT_FAILURE);
930 }
931
Jakub Kicinski67481822018-07-26 14:32:21 -0700932 map = bpf_object__find_map_by_name(obj, "qidconf_map");
933 qidconf_map = bpf_map__fd(map);
934 if (qidconf_map < 0) {
935 fprintf(stderr, "ERROR: no qidconf map found: %s\n",
936 strerror(qidconf_map));
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200937 exit(EXIT_FAILURE);
938 }
939
Jakub Kicinski67481822018-07-26 14:32:21 -0700940 map = bpf_object__find_map_by_name(obj, "xsks_map");
941 xsks_map = bpf_map__fd(map);
942 if (xsks_map < 0) {
943 fprintf(stderr, "ERROR: no xsks map found: %s\n",
944 strerror(xsks_map));
945 exit(EXIT_FAILURE);
946 }
947
948 if (bpf_set_link_xdp_fd(opt_ifindex, prog_fd, opt_xdp_flags) < 0) {
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200949 fprintf(stderr, "ERROR: link set xdp fd failed\n");
950 exit(EXIT_FAILURE);
951 }
952
Jakub Kicinski67481822018-07-26 14:32:21 -0700953 ret = bpf_map_update_elem(qidconf_map, &key, &opt_queue, 0);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200954 if (ret) {
955 fprintf(stderr, "ERROR: bpf_map_update_elem qidconf\n");
956 exit(EXIT_FAILURE);
957 }
958
959 /* Create sockets... */
960 xsks[num_socks++] = xsk_configure(NULL);
961
962#if RR_LB
963 for (i = 0; i < MAX_SOCKS - 1; i++)
964 xsks[num_socks++] = xsk_configure(xsks[0]->umem);
965#endif
966
967 /* ...and insert them into the map. */
968 for (i = 0; i < num_socks; i++) {
969 key = i;
Jakub Kicinski67481822018-07-26 14:32:21 -0700970 ret = bpf_map_update_elem(xsks_map, &key, &xsks[i]->sfd, 0);
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +0200971 if (ret) {
972 fprintf(stderr, "ERROR: bpf_map_update_elem %d\n", i);
973 exit(EXIT_FAILURE);
974 }
975 }
976
977 signal(SIGINT, int_exit);
978 signal(SIGTERM, int_exit);
979 signal(SIGABRT, int_exit);
980
981 setlocale(LC_ALL, "");
982
983 ret = pthread_create(&pt, NULL, poller, NULL);
984 lassert(ret == 0);
985
986 prev_time = get_nsecs();
987
988 if (opt_bench == BENCH_RXDROP)
989 rx_drop_all();
990 else if (opt_bench == BENCH_TXONLY)
991 tx_only(xsks[0]);
992 else
993 l2fwd(xsks[0]);
994
995 return 0;
996}