Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.3 713 lines 16 kB view raw
1// SPDX-License-Identifier: GPL-2.0 2/* Copyright(c) 2017 - 2018 Intel Corporation. */ 3 4#include <asm/barrier.h> 5#include <errno.h> 6#include <getopt.h> 7#include <libgen.h> 8#include <linux/bpf.h> 9#include <linux/compiler.h> 10#include <linux/if_link.h> 11#include <linux/if_xdp.h> 12#include <linux/if_ether.h> 13#include <locale.h> 14#include <net/ethernet.h> 15#include <net/if.h> 16#include <poll.h> 17#include <pthread.h> 18#include <signal.h> 19#include <stdbool.h> 20#include <stdio.h> 21#include <stdlib.h> 22#include <string.h> 23#include <sys/mman.h> 24#include <sys/resource.h> 25#include <sys/socket.h> 26#include <sys/types.h> 27#include <time.h> 28#include <unistd.h> 29 30#include "libbpf.h" 31#include "xsk.h" 32#include <bpf/bpf.h> 33 34#ifndef SOL_XDP 35#define SOL_XDP 283 36#endif 37 38#ifndef AF_XDP 39#define AF_XDP 44 40#endif 41 42#ifndef PF_XDP 43#define PF_XDP AF_XDP 44#endif 45 46#define NUM_FRAMES (4 * 1024) 47#define BATCH_SIZE 64 48 49#define DEBUG_HEXDUMP 0 50#define MAX_SOCKS 8 51 52typedef __u64 u64; 53typedef __u32 u32; 54 55static unsigned long prev_time; 56 57enum benchmark_type { 58 BENCH_RXDROP = 0, 59 BENCH_TXONLY = 1, 60 BENCH_L2FWD = 2, 61}; 62 63static enum benchmark_type opt_bench = BENCH_RXDROP; 64static u32 opt_xdp_flags = XDP_FLAGS_UPDATE_IF_NOEXIST; 65static const char *opt_if = ""; 66static int opt_ifindex; 67static int opt_queue; 68static int opt_poll; 69static int opt_interval = 1; 70static u32 opt_xdp_bind_flags; 71static int opt_xsk_frame_size = XSK_UMEM__DEFAULT_FRAME_SIZE; 72static __u32 prog_id; 73 74struct xsk_umem_info { 75 struct xsk_ring_prod fq; 76 struct xsk_ring_cons cq; 77 struct xsk_umem *umem; 78 void *buffer; 79}; 80 81struct xsk_socket_info { 82 struct xsk_ring_cons rx; 83 struct xsk_ring_prod tx; 84 struct xsk_umem_info *umem; 85 struct xsk_socket *xsk; 86 unsigned long rx_npkts; 87 unsigned long tx_npkts; 88 unsigned long prev_rx_npkts; 89 unsigned long prev_tx_npkts; 90 u32 outstanding_tx; 91}; 92 93static int num_socks; 94struct xsk_socket_info *xsks[MAX_SOCKS]; 95 96static unsigned long get_nsecs(void) 97{ 98 struct timespec ts; 99 100 clock_gettime(CLOCK_MONOTONIC, &ts); 101 return ts.tv_sec * 1000000000UL + ts.tv_nsec; 102} 103 104static void print_benchmark(bool running) 105{ 106 const char *bench_str = "INVALID"; 107 108 if (opt_bench == BENCH_RXDROP) 109 bench_str = "rxdrop"; 110 else if (opt_bench == BENCH_TXONLY) 111 bench_str = "txonly"; 112 else if (opt_bench == BENCH_L2FWD) 113 bench_str = "l2fwd"; 114 115 printf("%s:%d %s ", opt_if, opt_queue, bench_str); 116 if (opt_xdp_flags & XDP_FLAGS_SKB_MODE) 117 printf("xdp-skb "); 118 else if (opt_xdp_flags & XDP_FLAGS_DRV_MODE) 119 printf("xdp-drv "); 120 else 121 printf(" "); 122 123 if (opt_poll) 124 printf("poll() "); 125 126 if (running) { 127 printf("running..."); 128 fflush(stdout); 129 } 130} 131 132static void dump_stats(void) 133{ 134 unsigned long now = get_nsecs(); 135 long dt = now - prev_time; 136 int i; 137 138 prev_time = now; 139 140 for (i = 0; i < num_socks && xsks[i]; i++) { 141 char *fmt = "%-15s %'-11.0f %'-11lu\n"; 142 double rx_pps, tx_pps; 143 144 rx_pps = (xsks[i]->rx_npkts - xsks[i]->prev_rx_npkts) * 145 1000000000. / dt; 146 tx_pps = (xsks[i]->tx_npkts - xsks[i]->prev_tx_npkts) * 147 1000000000. / dt; 148 149 printf("\n sock%d@", i); 150 print_benchmark(false); 151 printf("\n"); 152 153 printf("%-15s %-11s %-11s %-11.2f\n", "", "pps", "pkts", 154 dt / 1000000000.); 155 printf(fmt, "rx", rx_pps, xsks[i]->rx_npkts); 156 printf(fmt, "tx", tx_pps, xsks[i]->tx_npkts); 157 158 xsks[i]->prev_rx_npkts = xsks[i]->rx_npkts; 159 xsks[i]->prev_tx_npkts = xsks[i]->tx_npkts; 160 } 161} 162 163static void *poller(void *arg) 164{ 165 (void)arg; 166 for (;;) { 167 sleep(opt_interval); 168 dump_stats(); 169 } 170 171 return NULL; 172} 173 174static void remove_xdp_program(void) 175{ 176 __u32 curr_prog_id = 0; 177 178 if (bpf_get_link_xdp_id(opt_ifindex, &curr_prog_id, opt_xdp_flags)) { 179 printf("bpf_get_link_xdp_id failed\n"); 180 exit(EXIT_FAILURE); 181 } 182 if (prog_id == curr_prog_id) 183 bpf_set_link_xdp_fd(opt_ifindex, -1, opt_xdp_flags); 184 else if (!curr_prog_id) 185 printf("couldn't find a prog id on a given interface\n"); 186 else 187 printf("program on interface changed, not removing\n"); 188} 189 190static void int_exit(int sig) 191{ 192 struct xsk_umem *umem = xsks[0]->umem->umem; 193 194 (void)sig; 195 196 dump_stats(); 197 xsk_socket__delete(xsks[0]->xsk); 198 (void)xsk_umem__delete(umem); 199 remove_xdp_program(); 200 201 exit(EXIT_SUCCESS); 202} 203 204static void __exit_with_error(int error, const char *file, const char *func, 205 int line) 206{ 207 fprintf(stderr, "%s:%s:%i: errno: %d/\"%s\"\n", file, func, 208 line, error, strerror(error)); 209 dump_stats(); 210 remove_xdp_program(); 211 exit(EXIT_FAILURE); 212} 213 214#define exit_with_error(error) __exit_with_error(error, __FILE__, __func__, \ 215 __LINE__) 216 217static const char pkt_data[] = 218 "\x3c\xfd\xfe\x9e\x7f\x71\xec\xb1\xd7\x98\x3a\xc0\x08\x00\x45\x00" 219 "\x00\x2e\x00\x00\x00\x00\x40\x11\x88\x97\x05\x08\x07\x08\xc8\x14" 220 "\x1e\x04\x10\x92\x10\x92\x00\x1a\x6d\xa3\x34\x33\x1f\x69\x40\x6b" 221 "\x54\x59\xb6\x14\x2d\x11\x44\xbf\xaf\xd9\xbe\xaa"; 222 223static void swap_mac_addresses(void *data) 224{ 225 struct ether_header *eth = (struct ether_header *)data; 226 struct ether_addr *src_addr = (struct ether_addr *)&eth->ether_shost; 227 struct ether_addr *dst_addr = (struct ether_addr *)&eth->ether_dhost; 228 struct ether_addr tmp; 229 230 tmp = *src_addr; 231 *src_addr = *dst_addr; 232 *dst_addr = tmp; 233} 234 235static void hex_dump(void *pkt, size_t length, u64 addr) 236{ 237 const unsigned char *address = (unsigned char *)pkt; 238 const unsigned char *line = address; 239 size_t line_size = 32; 240 unsigned char c; 241 char buf[32]; 242 int i = 0; 243 244 if (!DEBUG_HEXDUMP) 245 return; 246 247 sprintf(buf, "addr=%llu", addr); 248 printf("length = %zu\n", length); 249 printf("%s | ", buf); 250 while (length-- > 0) { 251 printf("%02X ", *address++); 252 if (!(++i % line_size) || (length == 0 && i % line_size)) { 253 if (length == 0) { 254 while (i++ % line_size) 255 printf("__ "); 256 } 257 printf(" | "); /* right close */ 258 while (line < address) { 259 c = *line++; 260 printf("%c", (c < 33 || c == 255) ? 0x2E : c); 261 } 262 printf("\n"); 263 if (length > 0) 264 printf("%s | ", buf); 265 } 266 } 267 printf("\n"); 268} 269 270static size_t gen_eth_frame(struct xsk_umem_info *umem, u64 addr) 271{ 272 memcpy(xsk_umem__get_data(umem->buffer, addr), pkt_data, 273 sizeof(pkt_data) - 1); 274 return sizeof(pkt_data) - 1; 275} 276 277static struct xsk_umem_info *xsk_configure_umem(void *buffer, u64 size) 278{ 279 struct xsk_umem_info *umem; 280 struct xsk_umem_config cfg = { 281 .fill_size = XSK_RING_PROD__DEFAULT_NUM_DESCS, 282 .comp_size = XSK_RING_CONS__DEFAULT_NUM_DESCS, 283 .frame_size = opt_xsk_frame_size, 284 .frame_headroom = XSK_UMEM__DEFAULT_FRAME_HEADROOM, 285 }; 286 int ret; 287 288 umem = calloc(1, sizeof(*umem)); 289 if (!umem) 290 exit_with_error(errno); 291 292 ret = xsk_umem__create(&umem->umem, buffer, size, &umem->fq, &umem->cq, 293 &cfg); 294 if (ret) 295 exit_with_error(-ret); 296 297 umem->buffer = buffer; 298 return umem; 299} 300 301static struct xsk_socket_info *xsk_configure_socket(struct xsk_umem_info *umem) 302{ 303 struct xsk_socket_config cfg; 304 struct xsk_socket_info *xsk; 305 int ret; 306 u32 idx; 307 int i; 308 309 xsk = calloc(1, sizeof(*xsk)); 310 if (!xsk) 311 exit_with_error(errno); 312 313 xsk->umem = umem; 314 cfg.rx_size = XSK_RING_CONS__DEFAULT_NUM_DESCS; 315 cfg.tx_size = XSK_RING_PROD__DEFAULT_NUM_DESCS; 316 cfg.libbpf_flags = 0; 317 cfg.xdp_flags = opt_xdp_flags; 318 cfg.bind_flags = opt_xdp_bind_flags; 319 ret = xsk_socket__create(&xsk->xsk, opt_if, opt_queue, umem->umem, 320 &xsk->rx, &xsk->tx, &cfg); 321 if (ret) 322 exit_with_error(-ret); 323 324 ret = bpf_get_link_xdp_id(opt_ifindex, &prog_id, opt_xdp_flags); 325 if (ret) 326 exit_with_error(-ret); 327 328 ret = xsk_ring_prod__reserve(&xsk->umem->fq, 329 XSK_RING_PROD__DEFAULT_NUM_DESCS, 330 &idx); 331 if (ret != XSK_RING_PROD__DEFAULT_NUM_DESCS) 332 exit_with_error(-ret); 333 for (i = 0; i < XSK_RING_PROD__DEFAULT_NUM_DESCS; i++) 334 *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx++) = 335 i * opt_xsk_frame_size; 336 xsk_ring_prod__submit(&xsk->umem->fq, 337 XSK_RING_PROD__DEFAULT_NUM_DESCS); 338 339 return xsk; 340} 341 342static struct option long_options[] = { 343 {"rxdrop", no_argument, 0, 'r'}, 344 {"txonly", no_argument, 0, 't'}, 345 {"l2fwd", no_argument, 0, 'l'}, 346 {"interface", required_argument, 0, 'i'}, 347 {"queue", required_argument, 0, 'q'}, 348 {"poll", no_argument, 0, 'p'}, 349 {"xdp-skb", no_argument, 0, 'S'}, 350 {"xdp-native", no_argument, 0, 'N'}, 351 {"interval", required_argument, 0, 'n'}, 352 {"zero-copy", no_argument, 0, 'z'}, 353 {"copy", no_argument, 0, 'c'}, 354 {"frame-size", required_argument, 0, 'f'}, 355 {0, 0, 0, 0} 356}; 357 358static void usage(const char *prog) 359{ 360 const char *str = 361 " Usage: %s [OPTIONS]\n" 362 " Options:\n" 363 " -r, --rxdrop Discard all incoming packets (default)\n" 364 " -t, --txonly Only send packets\n" 365 " -l, --l2fwd MAC swap L2 forwarding\n" 366 " -i, --interface=n Run on interface n\n" 367 " -q, --queue=n Use queue n (default 0)\n" 368 " -p, --poll Use poll syscall\n" 369 " -S, --xdp-skb=n Use XDP skb-mod\n" 370 " -N, --xdp-native=n Enfore XDP native mode\n" 371 " -n, --interval=n Specify statistics update interval (default 1 sec).\n" 372 " -z, --zero-copy Force zero-copy mode.\n" 373 " -c, --copy Force copy mode.\n" 374 " -f, --frame-size=n Set the frame size (must be a power of two, default is %d).\n" 375 "\n"; 376 fprintf(stderr, str, prog, XSK_UMEM__DEFAULT_FRAME_SIZE); 377 exit(EXIT_FAILURE); 378} 379 380static void parse_command_line(int argc, char **argv) 381{ 382 int option_index, c; 383 384 opterr = 0; 385 386 for (;;) { 387 c = getopt_long(argc, argv, "Frtli:q:psSNn:czf:", long_options, 388 &option_index); 389 if (c == -1) 390 break; 391 392 switch (c) { 393 case 'r': 394 opt_bench = BENCH_RXDROP; 395 break; 396 case 't': 397 opt_bench = BENCH_TXONLY; 398 break; 399 case 'l': 400 opt_bench = BENCH_L2FWD; 401 break; 402 case 'i': 403 opt_if = optarg; 404 break; 405 case 'q': 406 opt_queue = atoi(optarg); 407 break; 408 case 'p': 409 opt_poll = 1; 410 break; 411 case 'S': 412 opt_xdp_flags |= XDP_FLAGS_SKB_MODE; 413 opt_xdp_bind_flags |= XDP_COPY; 414 break; 415 case 'N': 416 opt_xdp_flags |= XDP_FLAGS_DRV_MODE; 417 break; 418 case 'n': 419 opt_interval = atoi(optarg); 420 break; 421 case 'z': 422 opt_xdp_bind_flags |= XDP_ZEROCOPY; 423 break; 424 case 'c': 425 opt_xdp_bind_flags |= XDP_COPY; 426 break; 427 case 'F': 428 opt_xdp_flags &= ~XDP_FLAGS_UPDATE_IF_NOEXIST; 429 break; 430 case 'f': 431 opt_xsk_frame_size = atoi(optarg); 432 break; 433 default: 434 usage(basename(argv[0])); 435 } 436 } 437 438 opt_ifindex = if_nametoindex(opt_if); 439 if (!opt_ifindex) { 440 fprintf(stderr, "ERROR: interface \"%s\" does not exist\n", 441 opt_if); 442 usage(basename(argv[0])); 443 } 444 445 if (opt_xsk_frame_size & (opt_xsk_frame_size - 1)) { 446 fprintf(stderr, "--frame-size=%d is not a power of two\n", 447 opt_xsk_frame_size); 448 usage(basename(argv[0])); 449 } 450} 451 452static void kick_tx(struct xsk_socket_info *xsk) 453{ 454 int ret; 455 456 ret = sendto(xsk_socket__fd(xsk->xsk), NULL, 0, MSG_DONTWAIT, NULL, 0); 457 if (ret >= 0 || errno == ENOBUFS || errno == EAGAIN || errno == EBUSY) 458 return; 459 exit_with_error(errno); 460} 461 462static inline void complete_tx_l2fwd(struct xsk_socket_info *xsk) 463{ 464 u32 idx_cq = 0, idx_fq = 0; 465 unsigned int rcvd; 466 size_t ndescs; 467 468 if (!xsk->outstanding_tx) 469 return; 470 471 kick_tx(xsk); 472 ndescs = (xsk->outstanding_tx > BATCH_SIZE) ? BATCH_SIZE : 473 xsk->outstanding_tx; 474 475 /* re-add completed Tx buffers */ 476 rcvd = xsk_ring_cons__peek(&xsk->umem->cq, ndescs, &idx_cq); 477 if (rcvd > 0) { 478 unsigned int i; 479 int ret; 480 481 ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq); 482 while (ret != rcvd) { 483 if (ret < 0) 484 exit_with_error(-ret); 485 ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, 486 &idx_fq); 487 } 488 for (i = 0; i < rcvd; i++) 489 *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = 490 *xsk_ring_cons__comp_addr(&xsk->umem->cq, 491 idx_cq++); 492 493 xsk_ring_prod__submit(&xsk->umem->fq, rcvd); 494 xsk_ring_cons__release(&xsk->umem->cq, rcvd); 495 xsk->outstanding_tx -= rcvd; 496 xsk->tx_npkts += rcvd; 497 } 498} 499 500static inline void complete_tx_only(struct xsk_socket_info *xsk) 501{ 502 unsigned int rcvd; 503 u32 idx; 504 505 if (!xsk->outstanding_tx) 506 return; 507 508 kick_tx(xsk); 509 510 rcvd = xsk_ring_cons__peek(&xsk->umem->cq, BATCH_SIZE, &idx); 511 if (rcvd > 0) { 512 xsk_ring_cons__release(&xsk->umem->cq, rcvd); 513 xsk->outstanding_tx -= rcvd; 514 xsk->tx_npkts += rcvd; 515 } 516} 517 518static void rx_drop(struct xsk_socket_info *xsk) 519{ 520 unsigned int rcvd, i; 521 u32 idx_rx = 0, idx_fq = 0; 522 int ret; 523 524 rcvd = xsk_ring_cons__peek(&xsk->rx, BATCH_SIZE, &idx_rx); 525 if (!rcvd) 526 return; 527 528 ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq); 529 while (ret != rcvd) { 530 if (ret < 0) 531 exit_with_error(-ret); 532 ret = xsk_ring_prod__reserve(&xsk->umem->fq, rcvd, &idx_fq); 533 } 534 535 for (i = 0; i < rcvd; i++) { 536 u64 addr = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx)->addr; 537 u32 len = xsk_ring_cons__rx_desc(&xsk->rx, idx_rx++)->len; 538 char *pkt = xsk_umem__get_data(xsk->umem->buffer, addr); 539 540 hex_dump(pkt, len, addr); 541 *xsk_ring_prod__fill_addr(&xsk->umem->fq, idx_fq++) = addr; 542 } 543 544 xsk_ring_prod__submit(&xsk->umem->fq, rcvd); 545 xsk_ring_cons__release(&xsk->rx, rcvd); 546 xsk->rx_npkts += rcvd; 547} 548 549static void rx_drop_all(void) 550{ 551 struct pollfd fds[MAX_SOCKS + 1]; 552 int i, ret, timeout, nfds = 1; 553 554 memset(fds, 0, sizeof(fds)); 555 556 for (i = 0; i < num_socks; i++) { 557 fds[i].fd = xsk_socket__fd(xsks[i]->xsk); 558 fds[i].events = POLLIN; 559 timeout = 1000; /* 1sn */ 560 } 561 562 for (;;) { 563 if (opt_poll) { 564 ret = poll(fds, nfds, timeout); 565 if (ret <= 0) 566 continue; 567 } 568 569 for (i = 0; i < num_socks; i++) 570 rx_drop(xsks[i]); 571 } 572} 573 574static void tx_only(struct xsk_socket_info *xsk) 575{ 576 int timeout, ret, nfds = 1; 577 struct pollfd fds[nfds + 1]; 578 u32 idx, frame_nb = 0; 579 580 memset(fds, 0, sizeof(fds)); 581 fds[0].fd = xsk_socket__fd(xsk->xsk); 582 fds[0].events = POLLOUT; 583 timeout = 1000; /* 1sn */ 584 585 for (;;) { 586 if (opt_poll) { 587 ret = poll(fds, nfds, timeout); 588 if (ret <= 0) 589 continue; 590 591 if (!(fds[0].revents & POLLOUT)) 592 continue; 593 } 594 595 if (xsk_ring_prod__reserve(&xsk->tx, BATCH_SIZE, &idx) == 596 BATCH_SIZE) { 597 unsigned int i; 598 599 for (i = 0; i < BATCH_SIZE; i++) { 600 xsk_ring_prod__tx_desc(&xsk->tx, idx + i)->addr 601 = (frame_nb + i) * opt_xsk_frame_size; 602 xsk_ring_prod__tx_desc(&xsk->tx, idx + i)->len = 603 sizeof(pkt_data) - 1; 604 } 605 606 xsk_ring_prod__submit(&xsk->tx, BATCH_SIZE); 607 xsk->outstanding_tx += BATCH_SIZE; 608 frame_nb += BATCH_SIZE; 609 frame_nb %= NUM_FRAMES; 610 } 611 612 complete_tx_only(xsk); 613 } 614} 615 616static void l2fwd(struct xsk_socket_info *xsk) 617{ 618 for (;;) { 619 unsigned int rcvd, i; 620 u32 idx_rx = 0, idx_tx = 0; 621 int ret; 622 623 for (;;) { 624 complete_tx_l2fwd(xsk); 625 626 rcvd = xsk_ring_cons__peek(&xsk->rx, BATCH_SIZE, 627 &idx_rx); 628 if (rcvd > 0) 629 break; 630 } 631 632 ret = xsk_ring_prod__reserve(&xsk->tx, rcvd, &idx_tx); 633 while (ret != rcvd) { 634 if (ret < 0) 635 exit_with_error(-ret); 636 ret = xsk_ring_prod__reserve(&xsk->tx, rcvd, &idx_tx); 637 } 638 639 for (i = 0; i < rcvd; i++) { 640 u64 addr = xsk_ring_cons__rx_desc(&xsk->rx, 641 idx_rx)->addr; 642 u32 len = xsk_ring_cons__rx_desc(&xsk->rx, 643 idx_rx++)->len; 644 char *pkt = xsk_umem__get_data(xsk->umem->buffer, addr); 645 646 swap_mac_addresses(pkt); 647 648 hex_dump(pkt, len, addr); 649 xsk_ring_prod__tx_desc(&xsk->tx, idx_tx)->addr = addr; 650 xsk_ring_prod__tx_desc(&xsk->tx, idx_tx++)->len = len; 651 } 652 653 xsk_ring_prod__submit(&xsk->tx, rcvd); 654 xsk_ring_cons__release(&xsk->rx, rcvd); 655 656 xsk->rx_npkts += rcvd; 657 xsk->outstanding_tx += rcvd; 658 } 659} 660 661int main(int argc, char **argv) 662{ 663 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY}; 664 struct xsk_umem_info *umem; 665 pthread_t pt; 666 void *bufs; 667 int ret; 668 669 parse_command_line(argc, argv); 670 671 if (setrlimit(RLIMIT_MEMLOCK, &r)) { 672 fprintf(stderr, "ERROR: setrlimit(RLIMIT_MEMLOCK) \"%s\"\n", 673 strerror(errno)); 674 exit(EXIT_FAILURE); 675 } 676 677 ret = posix_memalign(&bufs, getpagesize(), /* PAGE_SIZE aligned */ 678 NUM_FRAMES * opt_xsk_frame_size); 679 if (ret) 680 exit_with_error(ret); 681 682 /* Create sockets... */ 683 umem = xsk_configure_umem(bufs, NUM_FRAMES * opt_xsk_frame_size); 684 xsks[num_socks++] = xsk_configure_socket(umem); 685 686 if (opt_bench == BENCH_TXONLY) { 687 int i; 688 689 for (i = 0; i < NUM_FRAMES; i++) 690 (void)gen_eth_frame(umem, i * opt_xsk_frame_size); 691 } 692 693 signal(SIGINT, int_exit); 694 signal(SIGTERM, int_exit); 695 signal(SIGABRT, int_exit); 696 697 setlocale(LC_ALL, ""); 698 699 ret = pthread_create(&pt, NULL, poller, NULL); 700 if (ret) 701 exit_with_error(ret); 702 703 prev_time = get_nsecs(); 704 705 if (opt_bench == BENCH_RXDROP) 706 rx_drop_all(); 707 else if (opt_bench == BENCH_TXONLY) 708 tx_only(xsks[0]); 709 else 710 l2fwd(xsks[0]); 711 712 return 0; 713}