blob: 60a882a2296ca0e6a3cd8dd90b53ddf6059dedaf [file] [log] [blame]
Magnus Karlssonb4b8faa2018-05-02 13:01:36 +02001// SPDX-License-Identifier: GPL-2.0
Björn Töpeldac09142018-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
29#include "bpf_load.h"
30#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
49#define FRAME_SIZE 2048
50#define NUM_DESCS 1024
51#define BATCH_SIZE 16
52
53#define FQ_NUM_DESCS 1024
54#define CQ_NUM_DESCS 1024
55
56#define DEBUG_HEXDUMP 0
57
58typedef __u32 u32;
59
60static unsigned long prev_time;
61
62enum benchmark_type {
63 BENCH_RXDROP = 0,
64 BENCH_TXONLY = 1,
65 BENCH_L2FWD = 2,
66};
67
68static enum benchmark_type opt_bench = BENCH_RXDROP;
69static u32 opt_xdp_flags;
70static const char *opt_if = "";
71static int opt_ifindex;
72static int opt_queue;
73static int opt_poll;
74static int opt_shared_packet_buffer;
75static int opt_interval = 1;
76
77struct xdp_umem_uqueue {
78 u32 cached_prod;
79 u32 cached_cons;
80 u32 mask;
81 u32 size;
82 struct xdp_umem_ring *ring;
83};
84
85struct xdp_umem {
86 char (*frames)[FRAME_SIZE];
87 struct xdp_umem_uqueue fq;
88 struct xdp_umem_uqueue cq;
89 int fd;
90};
91
92struct xdp_uqueue {
93 u32 cached_prod;
94 u32 cached_cons;
95 u32 mask;
96 u32 size;
97 struct xdp_rxtx_ring *ring;
98};
99
100struct xdpsock {
101 struct xdp_uqueue rx;
102 struct xdp_uqueue tx;
103 int sfd;
104 struct xdp_umem *umem;
105 u32 outstanding_tx;
106 unsigned long rx_npkts;
107 unsigned long tx_npkts;
108 unsigned long prev_rx_npkts;
109 unsigned long prev_tx_npkts;
110};
111
112#define MAX_SOCKS 4
113static int num_socks;
114struct xdpsock *xsks[MAX_SOCKS];
115
116static unsigned long get_nsecs(void)
117{
118 struct timespec ts;
119
120 clock_gettime(CLOCK_MONOTONIC, &ts);
121 return ts.tv_sec * 1000000000UL + ts.tv_nsec;
122}
123
124static void dump_stats(void);
125
126#define lassert(expr) \
127 do { \
128 if (!(expr)) { \
129 fprintf(stderr, "%s:%s:%i: Assertion failed: " \
130 #expr ": errno: %d/\"%s\"\n", \
131 __FILE__, __func__, __LINE__, \
132 errno, strerror(errno)); \
133 dump_stats(); \
134 exit(EXIT_FAILURE); \
135 } \
136 } while (0)
137
138#define barrier() __asm__ __volatile__("": : :"memory")
139#define u_smp_rmb() barrier()
140#define u_smp_wmb() barrier()
141#define likely(x) __builtin_expect(!!(x), 1)
142#define unlikely(x) __builtin_expect(!!(x), 0)
143
144static const char pkt_data[] =
145 "\x3c\xfd\xfe\x9e\x7f\x71\xec\xb1\xd7\x98\x3a\xc0\x08\x00\x45\x00"
146 "\x00\x2e\x00\x00\x00\x00\x40\x11\x88\x97\x05\x08\x07\x08\xc8\x14"
147 "\x1e\x04\x10\x92\x10\x92\x00\x1a\x6d\xa3\x34\x33\x1f\x69\x40\x6b"
148 "\x54\x59\xb6\x14\x2d\x11\x44\xbf\xaf\xd9\xbe\xaa";
149
150static inline u32 umem_nb_free(struct xdp_umem_uqueue *q, u32 nb)
151{
152 u32 free_entries = q->size - (q->cached_prod - q->cached_cons);
153
154 if (free_entries >= nb)
155 return free_entries;
156
157 /* Refresh the local tail pointer */
158 q->cached_cons = q->ring->ptrs.consumer;
159
160 return q->size - (q->cached_prod - q->cached_cons);
161}
162
163static inline u32 xq_nb_free(struct xdp_uqueue *q, u32 ndescs)
164{
165 u32 free_entries = q->cached_cons - q->cached_prod;
166
167 if (free_entries >= ndescs)
168 return free_entries;
169
170 /* Refresh the local tail pointer */
171 q->cached_cons = q->ring->ptrs.consumer + q->size;
172 return q->cached_cons - q->cached_prod;
173}
174
175static inline u32 umem_nb_avail(struct xdp_umem_uqueue *q, u32 nb)
176{
177 u32 entries = q->cached_prod - q->cached_cons;
178
179 if (entries == 0) {
180 q->cached_prod = q->ring->ptrs.producer;
181 entries = q->cached_prod - q->cached_cons;
182 }
183
184 return (entries > nb) ? nb : entries;
185}
186
187static inline u32 xq_nb_avail(struct xdp_uqueue *q, u32 ndescs)
188{
189 u32 entries = q->cached_prod - q->cached_cons;
190
191 if (entries == 0) {
192 q->cached_prod = q->ring->ptrs.producer;
193 entries = q->cached_prod - q->cached_cons;
194 }
195
196 return (entries > ndescs) ? ndescs : entries;
197}
198
199static inline int umem_fill_to_kernel_ex(struct xdp_umem_uqueue *fq,
200 struct xdp_desc *d,
201 size_t nb)
202{
203 u32 i;
204
205 if (umem_nb_free(fq, nb) < nb)
206 return -ENOSPC;
207
208 for (i = 0; i < nb; i++) {
209 u32 idx = fq->cached_prod++ & fq->mask;
210
211 fq->ring->desc[idx] = d[i].idx;
212 }
213
214 u_smp_wmb();
215
216 fq->ring->ptrs.producer = fq->cached_prod;
217
218 return 0;
219}
220
221static inline int umem_fill_to_kernel(struct xdp_umem_uqueue *fq, u32 *d,
222 size_t nb)
223{
224 u32 i;
225
226 if (umem_nb_free(fq, nb) < nb)
227 return -ENOSPC;
228
229 for (i = 0; i < nb; i++) {
230 u32 idx = fq->cached_prod++ & fq->mask;
231
232 fq->ring->desc[idx] = d[i];
233 }
234
235 u_smp_wmb();
236
237 fq->ring->ptrs.producer = fq->cached_prod;
238
239 return 0;
240}
241
242static inline size_t umem_complete_from_kernel(struct xdp_umem_uqueue *cq,
243 u32 *d, size_t nb)
244{
245 u32 idx, i, entries = umem_nb_avail(cq, nb);
246
247 u_smp_rmb();
248
249 for (i = 0; i < entries; i++) {
250 idx = cq->cached_cons++ & cq->mask;
251 d[i] = cq->ring->desc[idx];
252 }
253
254 if (entries > 0) {
255 u_smp_wmb();
256
257 cq->ring->ptrs.consumer = cq->cached_cons;
258 }
259
260 return entries;
261}
262
263static inline void *xq_get_data(struct xdpsock *xsk, __u32 idx, __u32 off)
264{
265 lassert(idx < NUM_FRAMES);
266 return &xsk->umem->frames[idx][off];
267}
268
269static inline int xq_enq(struct xdp_uqueue *uq,
270 const struct xdp_desc *descs,
271 unsigned int ndescs)
272{
273 struct xdp_rxtx_ring *r = uq->ring;
274 unsigned int i;
275
276 if (xq_nb_free(uq, ndescs) < ndescs)
277 return -ENOSPC;
278
279 for (i = 0; i < ndescs; i++) {
280 u32 idx = uq->cached_prod++ & uq->mask;
281
282 r->desc[idx].idx = descs[i].idx;
283 r->desc[idx].len = descs[i].len;
284 r->desc[idx].offset = descs[i].offset;
285 }
286
287 u_smp_wmb();
288
289 r->ptrs.producer = uq->cached_prod;
290 return 0;
291}
292
293static inline int xq_enq_tx_only(struct xdp_uqueue *uq,
294 __u32 idx, unsigned int ndescs)
295{
296 struct xdp_rxtx_ring *q = uq->ring;
297 unsigned int i;
298
299 if (xq_nb_free(uq, ndescs) < ndescs)
300 return -ENOSPC;
301
302 for (i = 0; i < ndescs; i++) {
303 u32 idx = uq->cached_prod++ & uq->mask;
304
305 q->desc[idx].idx = idx + i;
306 q->desc[idx].len = sizeof(pkt_data) - 1;
307 q->desc[idx].offset = 0;
308 }
309
310 u_smp_wmb();
311
312 q->ptrs.producer = uq->cached_prod;
313 return 0;
314}
315
316static inline int xq_deq(struct xdp_uqueue *uq,
317 struct xdp_desc *descs,
318 int ndescs)
319{
320 struct xdp_rxtx_ring *r = uq->ring;
321 unsigned int idx;
322 int i, entries;
323
324 entries = xq_nb_avail(uq, ndescs);
325
326 u_smp_rmb();
327
328 for (i = 0; i < entries; i++) {
329 idx = uq->cached_cons++ & uq->mask;
330 descs[i] = r->desc[idx];
331 }
332
333 if (entries > 0) {
334 u_smp_wmb();
335
336 r->ptrs.consumer = uq->cached_cons;
337 }
338
339 return entries;
340}
341
342static void swap_mac_addresses(void *data)
343{
344 struct ether_header *eth = (struct ether_header *)data;
345 struct ether_addr *src_addr = (struct ether_addr *)&eth->ether_shost;
346 struct ether_addr *dst_addr = (struct ether_addr *)&eth->ether_dhost;
347 struct ether_addr tmp;
348
349 tmp = *src_addr;
350 *src_addr = *dst_addr;
351 *dst_addr = tmp;
352}
353
354#if DEBUG_HEXDUMP
355static void hex_dump(void *pkt, size_t length, const char *prefix)
356{
357 int i = 0;
358 const unsigned char *address = (unsigned char *)pkt;
359 const unsigned char *line = address;
360 size_t line_size = 32;
361 unsigned char c;
362
363 printf("length = %zu\n", length);
364 printf("%s | ", prefix);
365 while (length-- > 0) {
366 printf("%02X ", *address++);
367 if (!(++i % line_size) || (length == 0 && i % line_size)) {
368 if (length == 0) {
369 while (i++ % line_size)
370 printf("__ ");
371 }
372 printf(" | "); /* right close */
373 while (line < address) {
374 c = *line++;
375 printf("%c", (c < 33 || c == 255) ? 0x2E : c);
376 }
377 printf("\n");
378 if (length > 0)
379 printf("%s | ", prefix);
380 }
381 }
382 printf("\n");
383}
384#endif
385
386static size_t gen_eth_frame(char *frame)
387{
388 memcpy(frame, pkt_data, sizeof(pkt_data) - 1);
389 return sizeof(pkt_data) - 1;
390}
391
392static struct xdp_umem *xdp_umem_configure(int sfd)
393{
394 int fq_size = FQ_NUM_DESCS, cq_size = CQ_NUM_DESCS;
395 struct xdp_umem_reg mr;
396 struct xdp_umem *umem;
397 void *bufs;
398
399 umem = calloc(1, sizeof(*umem));
400 lassert(umem);
401
402 lassert(posix_memalign(&bufs, getpagesize(), /* PAGE_SIZE aligned */
403 NUM_FRAMES * FRAME_SIZE) == 0);
404
405 mr.addr = (__u64)bufs;
406 mr.len = NUM_FRAMES * FRAME_SIZE;
407 mr.frame_size = FRAME_SIZE;
408 mr.frame_headroom = FRAME_HEADROOM;
409
410 lassert(setsockopt(sfd, SOL_XDP, XDP_UMEM_REG, &mr, sizeof(mr)) == 0);
411 lassert(setsockopt(sfd, SOL_XDP, XDP_UMEM_FILL_RING, &fq_size,
412 sizeof(int)) == 0);
413 lassert(setsockopt(sfd, SOL_XDP, XDP_UMEM_COMPLETION_RING, &cq_size,
414 sizeof(int)) == 0);
415
416 umem->fq.ring = mmap(0, sizeof(struct xdp_umem_ring) +
417 FQ_NUM_DESCS * sizeof(u32),
418 PROT_READ | PROT_WRITE,
419 MAP_SHARED | MAP_POPULATE, sfd,
420 XDP_UMEM_PGOFF_FILL_RING);
421 lassert(umem->fq.ring != MAP_FAILED);
422
423 umem->fq.mask = FQ_NUM_DESCS - 1;
424 umem->fq.size = FQ_NUM_DESCS;
425
426 umem->cq.ring = mmap(0, sizeof(struct xdp_umem_ring) +
427 CQ_NUM_DESCS * sizeof(u32),
428 PROT_READ | PROT_WRITE,
429 MAP_SHARED | MAP_POPULATE, sfd,
430 XDP_UMEM_PGOFF_COMPLETION_RING);
431 lassert(umem->cq.ring != MAP_FAILED);
432
433 umem->cq.mask = CQ_NUM_DESCS - 1;
434 umem->cq.size = CQ_NUM_DESCS;
435
436 umem->frames = (char (*)[FRAME_SIZE])bufs;
437 umem->fd = sfd;
438
439 if (opt_bench == BENCH_TXONLY) {
440 int i;
441
442 for (i = 0; i < NUM_FRAMES; i++)
443 (void)gen_eth_frame(&umem->frames[i][0]);
444 }
445
446 return umem;
447}
448
449static struct xdpsock *xsk_configure(struct xdp_umem *umem)
450{
451 struct sockaddr_xdp sxdp = {};
452 int sfd, ndescs = NUM_DESCS;
453 struct xdpsock *xsk;
454 bool shared = true;
455 u32 i;
456
457 sfd = socket(PF_XDP, SOCK_RAW, 0);
458 lassert(sfd >= 0);
459
460 xsk = calloc(1, sizeof(*xsk));
461 lassert(xsk);
462
463 xsk->sfd = sfd;
464 xsk->outstanding_tx = 0;
465
466 if (!umem) {
467 shared = false;
468 xsk->umem = xdp_umem_configure(sfd);
469 } else {
470 xsk->umem = umem;
471 }
472
473 lassert(setsockopt(sfd, SOL_XDP, XDP_RX_RING,
474 &ndescs, sizeof(int)) == 0);
475 lassert(setsockopt(sfd, SOL_XDP, XDP_TX_RING,
476 &ndescs, sizeof(int)) == 0);
477
478 /* Rx */
479 xsk->rx.ring = mmap(NULL,
480 sizeof(struct xdp_ring) +
481 NUM_DESCS * sizeof(struct xdp_desc),
482 PROT_READ | PROT_WRITE,
483 MAP_SHARED | MAP_POPULATE, sfd,
484 XDP_PGOFF_RX_RING);
485 lassert(xsk->rx.ring != MAP_FAILED);
486
487 if (!shared) {
488 for (i = 0; i < NUM_DESCS / 2; i++)
489 lassert(umem_fill_to_kernel(&xsk->umem->fq, &i, 1)
490 == 0);
491 }
492
493 /* Tx */
494 xsk->tx.ring = mmap(NULL,
495 sizeof(struct xdp_ring) +
496 NUM_DESCS * sizeof(struct xdp_desc),
497 PROT_READ | PROT_WRITE,
498 MAP_SHARED | MAP_POPULATE, sfd,
499 XDP_PGOFF_TX_RING);
500 lassert(xsk->tx.ring != MAP_FAILED);
501
502 xsk->rx.mask = NUM_DESCS - 1;
503 xsk->rx.size = NUM_DESCS;
504
505 xsk->tx.mask = NUM_DESCS - 1;
506 xsk->tx.size = NUM_DESCS;
507
508 sxdp.sxdp_family = PF_XDP;
509 sxdp.sxdp_ifindex = opt_ifindex;
510 sxdp.sxdp_queue_id = opt_queue;
511 if (shared) {
512 sxdp.sxdp_flags = XDP_SHARED_UMEM;
513 sxdp.sxdp_shared_umem_fd = umem->fd;
514 }
515
516 lassert(bind(sfd, (struct sockaddr *)&sxdp, sizeof(sxdp)) == 0);
517
518 return xsk;
519}
520
521static void print_benchmark(bool running)
522{
523 const char *bench_str = "INVALID";
524
525 if (opt_bench == BENCH_RXDROP)
526 bench_str = "rxdrop";
527 else if (opt_bench == BENCH_TXONLY)
528 bench_str = "txonly";
529 else if (opt_bench == BENCH_L2FWD)
530 bench_str = "l2fwd";
531
532 printf("%s:%d %s ", opt_if, opt_queue, bench_str);
533 if (opt_xdp_flags & XDP_FLAGS_SKB_MODE)
534 printf("xdp-skb ");
535 else if (opt_xdp_flags & XDP_FLAGS_DRV_MODE)
536 printf("xdp-drv ");
537 else
538 printf(" ");
539
540 if (opt_poll)
541 printf("poll() ");
542
543 if (running) {
544 printf("running...");
545 fflush(stdout);
546 }
547}
548
549static void dump_stats(void)
550{
551 unsigned long now = get_nsecs();
552 long dt = now - prev_time;
553 int i;
554
555 prev_time = now;
556
557 for (i = 0; i < num_socks; i++) {
558 char *fmt = "%-15s %'-11.0f %'-11lu\n";
559 double rx_pps, tx_pps;
560
561 rx_pps = (xsks[i]->rx_npkts - xsks[i]->prev_rx_npkts) *
562 1000000000. / dt;
563 tx_pps = (xsks[i]->tx_npkts - xsks[i]->prev_tx_npkts) *
564 1000000000. / dt;
565
566 printf("\n sock%d@", i);
567 print_benchmark(false);
568 printf("\n");
569
570 printf("%-15s %-11s %-11s %-11.2f\n", "", "pps", "pkts",
571 dt / 1000000000.);
572 printf(fmt, "rx", rx_pps, xsks[i]->rx_npkts);
573 printf(fmt, "tx", tx_pps, xsks[i]->tx_npkts);
574
575 xsks[i]->prev_rx_npkts = xsks[i]->rx_npkts;
576 xsks[i]->prev_tx_npkts = xsks[i]->tx_npkts;
577 }
578}
579
580static void *poller(void *arg)
581{
582 (void)arg;
583 for (;;) {
584 sleep(opt_interval);
585 dump_stats();
586 }
587
588 return NULL;
589}
590
591static void int_exit(int sig)
592{
593 (void)sig;
594 dump_stats();
595 bpf_set_link_xdp_fd(opt_ifindex, -1, opt_xdp_flags);
596 exit(EXIT_SUCCESS);
597}
598
599static struct option long_options[] = {
600 {"rxdrop", no_argument, 0, 'r'},
601 {"txonly", no_argument, 0, 't'},
602 {"l2fwd", no_argument, 0, 'l'},
603 {"interface", required_argument, 0, 'i'},
604 {"queue", required_argument, 0, 'q'},
605 {"poll", no_argument, 0, 'p'},
606 {"shared-buffer", no_argument, 0, 's'},
607 {"xdp-skb", no_argument, 0, 'S'},
608 {"xdp-native", no_argument, 0, 'N'},
609 {"interval", required_argument, 0, 'n'},
610 {0, 0, 0, 0}
611};
612
613static void usage(const char *prog)
614{
615 const char *str =
616 " Usage: %s [OPTIONS]\n"
617 " Options:\n"
618 " -r, --rxdrop Discard all incoming packets (default)\n"
619 " -t, --txonly Only send packets\n"
620 " -l, --l2fwd MAC swap L2 forwarding\n"
621 " -i, --interface=n Run on interface n\n"
622 " -q, --queue=n Use queue n (default 0)\n"
623 " -p, --poll Use poll syscall\n"
624 " -s, --shared-buffer Use shared packet buffer\n"
625 " -S, --xdp-skb=n Use XDP skb-mod\n"
626 " -N, --xdp-native=n Enfore XDP native mode\n"
627 " -n, --interval=n Specify statistics update interval (default 1 sec).\n"
628 "\n";
629 fprintf(stderr, str, prog);
630 exit(EXIT_FAILURE);
631}
632
633static void parse_command_line(int argc, char **argv)
634{
635 int option_index, c;
636
637 opterr = 0;
638
639 for (;;) {
640 c = getopt_long(argc, argv, "rtli:q:psSNn:", long_options,
641 &option_index);
642 if (c == -1)
643 break;
644
645 switch (c) {
646 case 'r':
647 opt_bench = BENCH_RXDROP;
648 break;
649 case 't':
650 opt_bench = BENCH_TXONLY;
651 break;
652 case 'l':
653 opt_bench = BENCH_L2FWD;
654 break;
655 case 'i':
656 opt_if = optarg;
657 break;
658 case 'q':
659 opt_queue = atoi(optarg);
660 break;
661 case 's':
662 opt_shared_packet_buffer = 1;
663 break;
664 case 'p':
665 opt_poll = 1;
666 break;
667 case 'S':
668 opt_xdp_flags |= XDP_FLAGS_SKB_MODE;
669 break;
670 case 'N':
671 opt_xdp_flags |= XDP_FLAGS_DRV_MODE;
672 break;
673 case 'n':
674 opt_interval = atoi(optarg);
675 break;
676 default:
677 usage(basename(argv[0]));
678 }
679 }
680
681 opt_ifindex = if_nametoindex(opt_if);
682 if (!opt_ifindex) {
683 fprintf(stderr, "ERROR: interface \"%s\" does not exist\n",
684 opt_if);
685 usage(basename(argv[0]));
686 }
687}
688
689static void kick_tx(int fd)
690{
691 int ret;
692
693 ret = sendto(fd, NULL, 0, MSG_DONTWAIT, NULL, 0);
694 if (ret >= 0 || errno == ENOBUFS || errno == EAGAIN)
695 return;
696 lassert(0);
697}
698
699static inline void complete_tx_l2fwd(struct xdpsock *xsk)
700{
701 u32 descs[BATCH_SIZE];
702 unsigned int rcvd;
703 size_t ndescs;
704
705 if (!xsk->outstanding_tx)
706 return;
707
708 kick_tx(xsk->sfd);
709 ndescs = (xsk->outstanding_tx > BATCH_SIZE) ? BATCH_SIZE :
710 xsk->outstanding_tx;
711
712 /* re-add completed Tx buffers */
713 rcvd = umem_complete_from_kernel(&xsk->umem->cq, descs, ndescs);
714 if (rcvd > 0) {
715 umem_fill_to_kernel(&xsk->umem->fq, descs, rcvd);
716 xsk->outstanding_tx -= rcvd;
717 xsk->tx_npkts += rcvd;
718 }
719}
720
721static inline void complete_tx_only(struct xdpsock *xsk)
722{
723 u32 descs[BATCH_SIZE];
724 unsigned int rcvd;
725
726 if (!xsk->outstanding_tx)
727 return;
728
729 kick_tx(xsk->sfd);
730
731 rcvd = umem_complete_from_kernel(&xsk->umem->cq, descs, BATCH_SIZE);
732 if (rcvd > 0) {
733 xsk->outstanding_tx -= rcvd;
734 xsk->tx_npkts += rcvd;
735 }
736}
737
738static void rx_drop(struct xdpsock *xsk)
739{
740 struct xdp_desc descs[BATCH_SIZE];
741 unsigned int rcvd, i;
742
743 rcvd = xq_deq(&xsk->rx, descs, BATCH_SIZE);
744 if (!rcvd)
745 return;
746
747 for (i = 0; i < rcvd; i++) {
748 u32 idx = descs[i].idx;
749
750 lassert(idx < NUM_FRAMES);
751#if DEBUG_HEXDUMP
752 char *pkt;
753 char buf[32];
754
755 pkt = xq_get_data(xsk, idx, descs[i].offset);
756 sprintf(buf, "idx=%d", idx);
757 hex_dump(pkt, descs[i].len, buf);
758#endif
759 }
760
761 xsk->rx_npkts += rcvd;
762
763 umem_fill_to_kernel_ex(&xsk->umem->fq, descs, rcvd);
764}
765
766static void rx_drop_all(void)
767{
768 struct pollfd fds[MAX_SOCKS + 1];
769 int i, ret, timeout, nfds = 1;
770
771 memset(fds, 0, sizeof(fds));
772
773 for (i = 0; i < num_socks; i++) {
774 fds[i].fd = xsks[i]->sfd;
775 fds[i].events = POLLIN;
776 timeout = 1000; /* 1sn */
777 }
778
779 for (;;) {
780 if (opt_poll) {
781 ret = poll(fds, nfds, timeout);
782 if (ret <= 0)
783 continue;
784 }
785
786 for (i = 0; i < num_socks; i++)
787 rx_drop(xsks[i]);
788 }
789}
790
791static void tx_only(struct xdpsock *xsk)
792{
793 int timeout, ret, nfds = 1;
794 struct pollfd fds[nfds + 1];
795 unsigned int idx = 0;
796
797 memset(fds, 0, sizeof(fds));
798 fds[0].fd = xsk->sfd;
799 fds[0].events = POLLOUT;
800 timeout = 1000; /* 1sn */
801
802 for (;;) {
803 if (opt_poll) {
804 ret = poll(fds, nfds, timeout);
805 if (ret <= 0)
806 continue;
807
808 if (fds[0].fd != xsk->sfd ||
809 !(fds[0].revents & POLLOUT))
810 continue;
811 }
812
813 if (xq_nb_free(&xsk->tx, BATCH_SIZE) >= BATCH_SIZE) {
814 lassert(xq_enq_tx_only(&xsk->tx, idx, BATCH_SIZE) == 0);
815
816 xsk->outstanding_tx += BATCH_SIZE;
817 idx += BATCH_SIZE;
818 idx %= NUM_FRAMES;
819 }
820
821 complete_tx_only(xsk);
822 }
823}
824
825static void l2fwd(struct xdpsock *xsk)
826{
827 for (;;) {
828 struct xdp_desc descs[BATCH_SIZE];
829 unsigned int rcvd, i;
830 int ret;
831
832 for (;;) {
833 complete_tx_l2fwd(xsk);
834
835 rcvd = xq_deq(&xsk->rx, descs, BATCH_SIZE);
836 if (rcvd > 0)
837 break;
838 }
839
840 for (i = 0; i < rcvd; i++) {
841 char *pkt = xq_get_data(xsk, descs[i].idx,
842 descs[i].offset);
843
844 swap_mac_addresses(pkt);
845#if DEBUG_HEXDUMP
846 char buf[32];
847 u32 idx = descs[i].idx;
848
849 sprintf(buf, "idx=%d", idx);
850 hex_dump(pkt, descs[i].len, buf);
851#endif
852 }
853
854 xsk->rx_npkts += rcvd;
855
856 ret = xq_enq(&xsk->tx, descs, rcvd);
857 lassert(ret == 0);
858 xsk->outstanding_tx += rcvd;
859 }
860}
861
862int main(int argc, char **argv)
863{
864 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
865 char xdp_filename[256];
866 int i, ret, key = 0;
867 pthread_t pt;
868
869 parse_command_line(argc, argv);
870
871 if (setrlimit(RLIMIT_MEMLOCK, &r)) {
872 fprintf(stderr, "ERROR: setrlimit(RLIMIT_MEMLOCK) \"%s\"\n",
873 strerror(errno));
874 exit(EXIT_FAILURE);
875 }
876
877 snprintf(xdp_filename, sizeof(xdp_filename), "%s_kern.o", argv[0]);
878
879 if (load_bpf_file(xdp_filename)) {
880 fprintf(stderr, "ERROR: load_bpf_file %s\n", bpf_log_buf);
881 exit(EXIT_FAILURE);
882 }
883
884 if (!prog_fd[0]) {
885 fprintf(stderr, "ERROR: load_bpf_file: \"%s\"\n",
886 strerror(errno));
887 exit(EXIT_FAILURE);
888 }
889
890 if (bpf_set_link_xdp_fd(opt_ifindex, prog_fd[0], opt_xdp_flags) < 0) {
891 fprintf(stderr, "ERROR: link set xdp fd failed\n");
892 exit(EXIT_FAILURE);
893 }
894
895 ret = bpf_map_update_elem(map_fd[0], &key, &opt_queue, 0);
896 if (ret) {
897 fprintf(stderr, "ERROR: bpf_map_update_elem qidconf\n");
898 exit(EXIT_FAILURE);
899 }
900
901 /* Create sockets... */
902 xsks[num_socks++] = xsk_configure(NULL);
903
904#if RR_LB
905 for (i = 0; i < MAX_SOCKS - 1; i++)
906 xsks[num_socks++] = xsk_configure(xsks[0]->umem);
907#endif
908
909 /* ...and insert them into the map. */
910 for (i = 0; i < num_socks; i++) {
911 key = i;
912 ret = bpf_map_update_elem(map_fd[1], &key, &xsks[i]->sfd, 0);
913 if (ret) {
914 fprintf(stderr, "ERROR: bpf_map_update_elem %d\n", i);
915 exit(EXIT_FAILURE);
916 }
917 }
918
919 signal(SIGINT, int_exit);
920 signal(SIGTERM, int_exit);
921 signal(SIGABRT, int_exit);
922
923 setlocale(LC_ALL, "");
924
925 ret = pthread_create(&pt, NULL, poller, NULL);
926 lassert(ret == 0);
927
928 prev_time = get_nsecs();
929
930 if (opt_bench == BENCH_RXDROP)
931 rx_drop_all();
932 else if (opt_bench == BENCH_TXONLY)
933 tx_only(xsks[0]);
934 else
935 l2fwd(xsks[0]);
936
937 return 0;
938}