1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(C) 2019 Marvell International Ltd. 3 */ 4 5 #include "l2fwd_event.h" 6 #include "l2fwd_poll.h" 7 8 /* display usage */ 9 static void 10 l2fwd_event_usage(const char *prgname) 11 { 12 printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n" 13 " -p PORTMASK: hexadecimal bitmask of ports to configure\n" 14 " -q NQ: number of queue (=ports) per lcore (default is 1)\n" 15 " -T PERIOD: statistics will be refreshed each PERIOD seconds " 16 " (0 to disable, 10 default, 86400 maximum)\n" 17 " --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n" 18 " When enabled:\n" 19 " - The source MAC address is replaced by the TX port MAC address\n" 20 " - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n" 21 " --mode: Packet transfer mode for I/O, poll or eventdev\n" 22 " Default mode = eventdev\n" 23 " --eventq-sched: Event queue schedule type, ordered, atomic or parallel.\n" 24 " Default: atomic\n" 25 " Valid only if --mode=eventdev\n\n", 26 prgname); 27 } 28 29 static int 30 l2fwd_event_parse_portmask(const char *portmask) 31 { 32 char *end = NULL; 33 unsigned long pm; 34 35 /* parse hexadecimal string */ 36 pm = strtoul(portmask, &end, 16); 37 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) 38 return -1; 39 40 if (pm == 0) 41 return -1; 42 43 return pm; 44 } 45 46 static unsigned int 47 l2fwd_event_parse_nqueue(const char *q_arg) 48 { 49 char *end = NULL; 50 unsigned long n; 51 52 /* parse hexadecimal string */ 53 n = strtoul(q_arg, &end, 10); 54 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) 55 return 0; 56 if (n == 0) 57 return 0; 58 if (n >= MAX_RX_QUEUE_PER_LCORE) 59 return 0; 60 61 return n; 62 } 63 64 static int 65 l2fwd_event_parse_timer_period(const char *q_arg) 66 { 67 char *end = NULL; 68 int n; 69 70 /* parse number string */ 71 n = strtol(q_arg, &end, 10); 72 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) 73 return -1; 74 if (n >= MAX_TIMER_PERIOD) 75 return -1; 76 77 return n; 78 } 79 80 static void 81 l2fwd_event_parse_mode(const char *optarg, 82 struct l2fwd_resources *rsrc) 83 { 84 if (!strncmp(optarg, "poll", 4)) 85 rsrc->event_mode = false; 86 else if (!strncmp(optarg, "eventdev", 8)) 87 rsrc->event_mode = true; 88 } 89 90 static void 91 l2fwd_event_parse_eventq_sched(const char *optarg, 92 struct l2fwd_resources *rsrc) 93 { 94 if (!strncmp(optarg, "ordered", 7)) 95 rsrc->sched_type = RTE_SCHED_TYPE_ORDERED; 96 else if (!strncmp(optarg, "atomic", 6)) 97 rsrc->sched_type = RTE_SCHED_TYPE_ATOMIC; 98 else if (!strncmp(optarg, "parallel", 8)) 99 rsrc->sched_type = RTE_SCHED_TYPE_PARALLEL; 100 } 101 102 static const char short_options[] = 103 "p:" /* portmask */ 104 "q:" /* number of queues */ 105 "T:" /* timer period */ 106 ; 107 108 #define CMD_LINE_OPT_MAC_UPDATING "mac-updating" 109 #define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating" 110 #define CMD_LINE_OPT_MODE "mode" 111 #define CMD_LINE_OPT_EVENTQ_SCHED "eventq-sched" 112 113 enum { 114 /* long options mapped to a short option */ 115 116 /* first long only option value must be >= 256, so that we won't 117 * conflict with short options 118 */ 119 CMD_LINE_OPT_MIN_NUM = 256, 120 CMD_LINE_OPT_MODE_NUM, 121 CMD_LINE_OPT_EVENTQ_SCHED_NUM, 122 }; 123 124 /* Parse the argument given in the command line of the application */ 125 static int 126 l2fwd_event_parse_args(int argc, char **argv, 127 struct l2fwd_resources *rsrc) 128 { 129 int mac_updating = 1; 130 struct option lgopts[] = { 131 { CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1}, 132 { CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0}, 133 { CMD_LINE_OPT_MODE, required_argument, NULL, 134 CMD_LINE_OPT_MODE_NUM}, 135 { CMD_LINE_OPT_EVENTQ_SCHED, required_argument, NULL, 136 CMD_LINE_OPT_EVENTQ_SCHED_NUM}, 137 {NULL, 0, 0, 0} 138 }; 139 int opt, ret, timer_secs; 140 char *prgname = argv[0]; 141 char **argvopt; 142 int option_index; 143 144 argvopt = argv; 145 while ((opt = getopt_long(argc, argvopt, short_options, 146 lgopts, &option_index)) != EOF) { 147 148 switch (opt) { 149 /* portmask */ 150 case 'p': 151 rsrc->enabled_port_mask = 152 l2fwd_event_parse_portmask(optarg); 153 if (rsrc->enabled_port_mask == 0) { 154 printf("invalid portmask\n"); 155 l2fwd_event_usage(prgname); 156 return -1; 157 } 158 break; 159 160 /* nqueue */ 161 case 'q': 162 rsrc->rx_queue_per_lcore = 163 l2fwd_event_parse_nqueue(optarg); 164 if (rsrc->rx_queue_per_lcore == 0) { 165 printf("invalid queue number\n"); 166 l2fwd_event_usage(prgname); 167 return -1; 168 } 169 break; 170 171 /* timer period */ 172 case 'T': 173 timer_secs = l2fwd_event_parse_timer_period(optarg); 174 if (timer_secs < 0) { 175 printf("invalid timer period\n"); 176 l2fwd_event_usage(prgname); 177 return -1; 178 } 179 rsrc->timer_period = timer_secs; 180 /* convert to number of cycles */ 181 rsrc->timer_period *= rte_get_timer_hz(); 182 break; 183 184 case CMD_LINE_OPT_MODE_NUM: 185 l2fwd_event_parse_mode(optarg, rsrc); 186 break; 187 188 case CMD_LINE_OPT_EVENTQ_SCHED_NUM: 189 l2fwd_event_parse_eventq_sched(optarg, rsrc); 190 break; 191 192 /* long options */ 193 case 0: 194 break; 195 196 default: 197 l2fwd_event_usage(prgname); 198 return -1; 199 } 200 } 201 202 rsrc->mac_updating = mac_updating; 203 204 if (optind >= 0) 205 argv[optind-1] = prgname; 206 207 ret = optind-1; 208 optind = 1; /* reset getopt lib */ 209 return ret; 210 } 211 212 static int 213 l2fwd_launch_one_lcore(void *args) 214 { 215 struct l2fwd_resources *rsrc = args; 216 struct l2fwd_poll_resources *poll_rsrc = rsrc->poll_rsrc; 217 struct l2fwd_event_resources *evt_rsrc = rsrc->evt_rsrc; 218 219 if (rsrc->event_mode) 220 evt_rsrc->ops.l2fwd_event_loop(rsrc); 221 else 222 poll_rsrc->poll_main_loop(rsrc); 223 224 return 0; 225 } 226 227 /* Check the link status of all ports in up to 9s, and print them finally */ 228 static void 229 check_all_ports_link_status(struct l2fwd_resources *rsrc, 230 uint32_t port_mask) 231 { 232 #define CHECK_INTERVAL 100 /* 100ms */ 233 #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ 234 uint16_t port_id; 235 uint8_t count, all_ports_up, print_flag = 0; 236 struct rte_eth_link link; 237 int ret; 238 239 printf("\nChecking link status..."); 240 fflush(stdout); 241 for (count = 0; count <= MAX_CHECK_TIME; count++) { 242 if (rsrc->force_quit) 243 return; 244 all_ports_up = 1; 245 RTE_ETH_FOREACH_DEV(port_id) { 246 if (rsrc->force_quit) 247 return; 248 if ((port_mask & (1 << port_id)) == 0) 249 continue; 250 memset(&link, 0, sizeof(link)); 251 ret = rte_eth_link_get_nowait(port_id, &link); 252 if (ret < 0) { 253 all_ports_up = 0; 254 if (print_flag == 1) 255 printf("Port %u link get failed: %s\n", 256 port_id, rte_strerror(-ret)); 257 continue; 258 } 259 /* print link status if flag set */ 260 if (print_flag == 1) { 261 if (link.link_status) 262 printf( 263 "Port%d Link Up. Speed %u Mbps - %s\n", 264 port_id, link.link_speed, 265 (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? 266 ("full-duplex") : ("half-duplex\n")); 267 else 268 printf("Port %d Link Down\n", port_id); 269 continue; 270 } 271 /* clear all_ports_up flag if any link down */ 272 if (link.link_status == ETH_LINK_DOWN) { 273 all_ports_up = 0; 274 break; 275 } 276 } 277 /* after finally printing all link status, get out */ 278 if (print_flag == 1) 279 break; 280 281 if (all_ports_up == 0) { 282 printf("."); 283 fflush(stdout); 284 rte_delay_ms(CHECK_INTERVAL); 285 } 286 287 /* set the print_flag if all ports up or timeout */ 288 if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) { 289 print_flag = 1; 290 printf("done\n"); 291 } 292 } 293 } 294 295 /* Print out statistics on packets dropped */ 296 static void 297 print_stats(struct l2fwd_resources *rsrc) 298 { 299 uint64_t total_packets_dropped, total_packets_tx, total_packets_rx; 300 uint32_t port_id; 301 302 total_packets_dropped = 0; 303 total_packets_tx = 0; 304 total_packets_rx = 0; 305 306 const char clr[] = {27, '[', '2', 'J', '\0' }; 307 const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0' }; 308 309 /* Clear screen and move to top left */ 310 printf("%s%s", clr, topLeft); 311 312 printf("\nPort statistics ===================================="); 313 314 for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) { 315 /* skip disabled ports */ 316 if ((rsrc->enabled_port_mask & (1 << port_id)) == 0) 317 continue; 318 printf("\nStatistics for port %u ------------------------------" 319 "\nPackets sent: %29"PRIu64 320 "\nPackets received: %25"PRIu64 321 "\nPackets dropped: %26"PRIu64, 322 port_id, 323 rsrc->port_stats[port_id].tx, 324 rsrc->port_stats[port_id].rx, 325 rsrc->port_stats[port_id].dropped); 326 327 total_packets_dropped += 328 rsrc->port_stats[port_id].dropped; 329 total_packets_tx += rsrc->port_stats[port_id].tx; 330 total_packets_rx += rsrc->port_stats[port_id].rx; 331 } 332 333 if (rsrc->event_mode) { 334 struct l2fwd_event_resources *evt_rsrc = rsrc->evt_rsrc; 335 struct rte_event_eth_rx_adapter_stats rx_adptr_stats; 336 struct rte_event_eth_tx_adapter_stats tx_adptr_stats; 337 int ret, i; 338 339 for (i = 0; i < evt_rsrc->rx_adptr.nb_rx_adptr; i++) { 340 ret = rte_event_eth_rx_adapter_stats_get( 341 evt_rsrc->rx_adptr.rx_adptr[i], 342 &rx_adptr_stats); 343 if (ret < 0) 344 continue; 345 printf("\nRx adapter[%d] statistics====================" 346 "\nReceive queue poll count: %17"PRIu64 347 "\nReceived packet count: %20"PRIu64 348 "\nEventdev enqueue count: %19"PRIu64 349 "\nEventdev enqueue retry count: %13"PRIu64 350 "\nReceived packet dropped count: %12"PRIu64 351 "\nRx enqueue start timestamp: %15"PRIu64 352 "\nRx enqueue block cycles: %18"PRIu64 353 "\nRx enqueue unblock timestamp: %13"PRIu64, 354 evt_rsrc->rx_adptr.rx_adptr[i], 355 rx_adptr_stats.rx_poll_count, 356 rx_adptr_stats.rx_packets, 357 rx_adptr_stats.rx_enq_count, 358 rx_adptr_stats.rx_enq_retry, 359 rx_adptr_stats.rx_dropped, 360 rx_adptr_stats.rx_enq_start_ts, 361 rx_adptr_stats.rx_enq_block_cycles, 362 rx_adptr_stats.rx_enq_end_ts); 363 } 364 for (i = 0; i < evt_rsrc->tx_adptr.nb_tx_adptr; i++) { 365 ret = rte_event_eth_tx_adapter_stats_get( 366 evt_rsrc->tx_adptr.tx_adptr[i], 367 &tx_adptr_stats); 368 if (ret < 0) 369 continue; 370 printf("\nTx adapter[%d] statistics====================" 371 "\nNumber of transmit retries: %15"PRIu64 372 "\nNumber of packets transmitted: %12"PRIu64 373 "\nNumber of packets dropped: %16"PRIu64, 374 evt_rsrc->tx_adptr.tx_adptr[i], 375 tx_adptr_stats.tx_retry, 376 tx_adptr_stats.tx_packets, 377 tx_adptr_stats.tx_dropped); 378 } 379 } 380 printf("\nAggregate lcore statistics =========================" 381 "\nTotal packets sent: %23"PRIu64 382 "\nTotal packets received: %19"PRIu64 383 "\nTotal packets dropped: %20"PRIu64, 384 total_packets_tx, 385 total_packets_rx, 386 total_packets_dropped); 387 printf("\n====================================================\n"); 388 } 389 390 static void 391 l2fwd_event_print_stats(struct l2fwd_resources *rsrc) 392 { 393 uint64_t prev_tsc = 0, diff_tsc, cur_tsc, timer_tsc = 0; 394 const uint64_t timer_period = rsrc->timer_period; 395 396 while (!rsrc->force_quit) { 397 /* if timer is enabled */ 398 if (timer_period > 0) { 399 cur_tsc = rte_rdtsc(); 400 diff_tsc = cur_tsc - prev_tsc; 401 402 /* advance the timer */ 403 timer_tsc += diff_tsc; 404 405 /* if timer has reached its timeout */ 406 if (unlikely(timer_tsc >= timer_period)) { 407 print_stats(rsrc); 408 /* reset the timer */ 409 timer_tsc = 0; 410 } 411 prev_tsc = cur_tsc; 412 } 413 } 414 } 415 416 417 static void 418 signal_handler(int signum) 419 { 420 struct l2fwd_resources *rsrc = l2fwd_get_rsrc(); 421 if (signum == SIGINT || signum == SIGTERM) { 422 printf("\n\nSignal %d received, preparing to exit...\n", 423 signum); 424 rsrc->force_quit = true; 425 } 426 } 427 428 int 429 main(int argc, char **argv) 430 { 431 struct l2fwd_resources *rsrc; 432 uint16_t nb_ports_available = 0; 433 uint32_t nb_ports_in_mask = 0; 434 uint16_t port_id, last_port; 435 uint32_t nb_mbufs; 436 uint16_t nb_ports; 437 int i, ret; 438 439 /* init EAL */ 440 ret = rte_eal_init(argc, argv); 441 if (ret < 0) 442 rte_panic("Invalid EAL arguments\n"); 443 argc -= ret; 444 argv += ret; 445 446 rsrc = l2fwd_get_rsrc(); 447 448 signal(SIGINT, signal_handler); 449 signal(SIGTERM, signal_handler); 450 451 /* parse application arguments (after the EAL ones) */ 452 ret = l2fwd_event_parse_args(argc, argv, rsrc); 453 if (ret < 0) 454 rte_panic("Invalid L2FWD arguments\n"); 455 456 printf("MAC updating %s\n", rsrc->mac_updating ? "enabled" : 457 "disabled"); 458 459 nb_ports = rte_eth_dev_count_avail(); 460 if (nb_ports == 0) 461 rte_panic("No Ethernet ports - bye\n"); 462 463 /* check port mask to possible port mask */ 464 if (rsrc->enabled_port_mask & ~((1 << nb_ports) - 1)) 465 rte_panic("Invalid portmask; possible (0x%x)\n", 466 (1 << nb_ports) - 1); 467 468 /* reset l2fwd_dst_ports */ 469 for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) 470 rsrc->dst_ports[port_id] = 0; 471 last_port = 0; 472 473 /* 474 * Each logical core is assigned a dedicated TX queue on each port. 475 */ 476 RTE_ETH_FOREACH_DEV(port_id) { 477 /* skip ports that are not enabled */ 478 if ((rsrc->enabled_port_mask & (1 << port_id)) == 0) 479 continue; 480 481 if (nb_ports_in_mask % 2) { 482 rsrc->dst_ports[port_id] = last_port; 483 rsrc->dst_ports[last_port] = port_id; 484 } else { 485 last_port = port_id; 486 } 487 488 nb_ports_in_mask++; 489 } 490 if (nb_ports_in_mask % 2) { 491 printf("Notice: odd number of ports in portmask.\n"); 492 rsrc->dst_ports[last_port] = last_port; 493 } 494 495 nb_mbufs = RTE_MAX(nb_ports * (RTE_TEST_RX_DESC_DEFAULT + 496 RTE_TEST_TX_DESC_DEFAULT + 497 MAX_PKT_BURST + rte_lcore_count() * 498 MEMPOOL_CACHE_SIZE), 8192U); 499 500 /* create the mbuf pool */ 501 rsrc->pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", 502 nb_mbufs, MEMPOOL_CACHE_SIZE, 0, 503 RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); 504 if (rsrc->pktmbuf_pool == NULL) 505 rte_panic("Cannot init mbuf pool\n"); 506 507 nb_ports_available = l2fwd_event_init_ports(rsrc); 508 if (!nb_ports_available) 509 rte_panic("All available ports are disabled. Please set portmask.\n"); 510 511 /* Configure eventdev parameters if required */ 512 if (rsrc->event_mode) 513 l2fwd_event_resource_setup(rsrc); 514 else 515 l2fwd_poll_resource_setup(rsrc); 516 517 /* initialize port stats */ 518 memset(&rsrc->port_stats, 0, 519 sizeof(struct l2fwd_port_statistics)); 520 521 /* All settings are done. Now enable eth devices */ 522 RTE_ETH_FOREACH_DEV(port_id) { 523 /* skip ports that are not enabled */ 524 if ((rsrc->enabled_port_mask & 525 (1 << port_id)) == 0) 526 continue; 527 528 ret = rte_eth_dev_start(port_id); 529 if (ret < 0) 530 rte_panic("rte_eth_dev_start:err=%d, port=%u\n", ret, 531 port_id); 532 } 533 534 if (rsrc->event_mode) 535 l2fwd_event_service_setup(rsrc); 536 537 check_all_ports_link_status(rsrc, rsrc->enabled_port_mask); 538 539 /* launch per-lcore init on every lcore */ 540 rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, rsrc, 541 SKIP_MASTER); 542 l2fwd_event_print_stats(rsrc); 543 if (rsrc->event_mode) { 544 struct l2fwd_event_resources *evt_rsrc = 545 rsrc->evt_rsrc; 546 for (i = 0; i < evt_rsrc->rx_adptr.nb_rx_adptr; i++) 547 rte_event_eth_rx_adapter_stop( 548 evt_rsrc->rx_adptr.rx_adptr[i]); 549 for (i = 0; i < evt_rsrc->tx_adptr.nb_tx_adptr; i++) 550 rte_event_eth_tx_adapter_stop( 551 evt_rsrc->tx_adptr.tx_adptr[i]); 552 553 RTE_ETH_FOREACH_DEV(port_id) { 554 if ((rsrc->enabled_port_mask & 555 (1 << port_id)) == 0) 556 continue; 557 rte_eth_dev_stop(port_id); 558 } 559 560 rte_eal_mp_wait_lcore(); 561 RTE_ETH_FOREACH_DEV(port_id) { 562 if ((rsrc->enabled_port_mask & 563 (1 << port_id)) == 0) 564 continue; 565 rte_eth_dev_close(port_id); 566 } 567 568 rte_event_dev_stop(evt_rsrc->event_d_id); 569 rte_event_dev_close(evt_rsrc->event_d_id); 570 571 } else { 572 rte_eal_mp_wait_lcore(); 573 574 RTE_ETH_FOREACH_DEV(port_id) { 575 if ((rsrc->enabled_port_mask & 576 (1 << port_id)) == 0) 577 continue; 578 printf("Closing port %d...", port_id); 579 rte_eth_dev_stop(port_id); 580 rte_eth_dev_close(port_id); 581 printf(" Done\n"); 582 } 583 } 584 printf("Bye...\n"); 585 586 return 0; 587 } 588