1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(C) 2019 Marvell International Ltd. 3 */ 4 5 #include "l2fwd_poll.h" 6 7 /* display usage */ 8 static void 9 l2fwd_event_usage(const char *prgname) 10 { 11 printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n" 12 " -p PORTMASK: hexadecimal bitmask of ports to configure\n" 13 " -q NQ: number of queue (=ports) per lcore (default is 1)\n" 14 " -T PERIOD: statistics will be refreshed each PERIOD seconds " 15 " (0 to disable, 10 default, 86400 maximum)\n" 16 " --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n" 17 " When enabled:\n" 18 " - The source MAC address is replaced by the TX port MAC address\n" 19 " - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n", 20 prgname); 21 } 22 23 static int 24 l2fwd_event_parse_portmask(const char *portmask) 25 { 26 char *end = NULL; 27 unsigned long pm; 28 29 /* parse hexadecimal string */ 30 pm = strtoul(portmask, &end, 16); 31 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) 32 return -1; 33 34 if (pm == 0) 35 return -1; 36 37 return pm; 38 } 39 40 static unsigned int 41 l2fwd_event_parse_nqueue(const char *q_arg) 42 { 43 char *end = NULL; 44 unsigned long n; 45 46 /* parse hexadecimal string */ 47 n = strtoul(q_arg, &end, 10); 48 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) 49 return 0; 50 if (n == 0) 51 return 0; 52 if (n >= MAX_RX_QUEUE_PER_LCORE) 53 return 0; 54 55 return n; 56 } 57 58 static int 59 l2fwd_event_parse_timer_period(const char *q_arg) 60 { 61 char *end = NULL; 62 int n; 63 64 /* parse number string */ 65 n = strtol(q_arg, &end, 10); 66 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) 67 return -1; 68 if (n >= MAX_TIMER_PERIOD) 69 return -1; 70 71 return n; 72 } 73 74 static const char short_options[] = 75 "p:" /* portmask */ 76 "q:" /* number of queues */ 77 "T:" /* timer period */ 78 ; 79 80 #define CMD_LINE_OPT_MAC_UPDATING "mac-updating" 81 #define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating" 82 83 enum { 84 /* long options mapped to a short option */ 85 86 /* first long only option value must be >= 256, so that we won't 87 * conflict with short options 88 */ 89 CMD_LINE_OPT_MIN_NUM = 256, 90 }; 91 92 /* Parse the argument given in the command line of the application */ 93 static int 94 l2fwd_event_parse_args(int argc, char **argv, 95 struct l2fwd_resources *rsrc) 96 { 97 int mac_updating = 1; 98 struct option lgopts[] = { 99 { CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1}, 100 { CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0}, 101 {NULL, 0, 0, 0} 102 }; 103 int opt, ret, timer_secs; 104 char *prgname = argv[0]; 105 char **argvopt; 106 int option_index; 107 108 argvopt = argv; 109 while ((opt = getopt_long(argc, argvopt, short_options, 110 lgopts, &option_index)) != EOF) { 111 112 switch (opt) { 113 /* portmask */ 114 case 'p': 115 rsrc->enabled_port_mask = 116 l2fwd_event_parse_portmask(optarg); 117 if (rsrc->enabled_port_mask == 0) { 118 printf("invalid portmask\n"); 119 l2fwd_event_usage(prgname); 120 return -1; 121 } 122 break; 123 124 /* nqueue */ 125 case 'q': 126 rsrc->rx_queue_per_lcore = 127 l2fwd_event_parse_nqueue(optarg); 128 if (rsrc->rx_queue_per_lcore == 0) { 129 printf("invalid queue number\n"); 130 l2fwd_event_usage(prgname); 131 return -1; 132 } 133 break; 134 135 /* timer period */ 136 case 'T': 137 timer_secs = l2fwd_event_parse_timer_period(optarg); 138 if (timer_secs < 0) { 139 printf("invalid timer period\n"); 140 l2fwd_event_usage(prgname); 141 return -1; 142 } 143 rsrc->timer_period = timer_secs; 144 /* convert to number of cycles */ 145 rsrc->timer_period *= rte_get_timer_hz(); 146 break; 147 148 /* long options */ 149 case 0: 150 break; 151 152 default: 153 l2fwd_event_usage(prgname); 154 return -1; 155 } 156 } 157 158 rsrc->mac_updating = mac_updating; 159 160 if (optind >= 0) 161 argv[optind-1] = prgname; 162 163 ret = optind-1; 164 optind = 1; /* reset getopt lib */ 165 return ret; 166 } 167 168 static int 169 l2fwd_launch_one_lcore(void *args) 170 { 171 struct l2fwd_resources *rsrc = args; 172 struct l2fwd_poll_resources *poll_rsrc = rsrc->poll_rsrc; 173 174 poll_rsrc->poll_main_loop(rsrc); 175 176 return 0; 177 } 178 179 /* Check the link status of all ports in up to 9s, and print them finally */ 180 static void 181 check_all_ports_link_status(struct l2fwd_resources *rsrc, 182 uint32_t port_mask) 183 { 184 #define CHECK_INTERVAL 100 /* 100ms */ 185 #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ 186 uint16_t port_id; 187 uint8_t count, all_ports_up, print_flag = 0; 188 struct rte_eth_link link; 189 190 printf("\nChecking link status..."); 191 fflush(stdout); 192 for (count = 0; count <= MAX_CHECK_TIME; count++) { 193 if (rsrc->force_quit) 194 return; 195 all_ports_up = 1; 196 RTE_ETH_FOREACH_DEV(port_id) { 197 if (rsrc->force_quit) 198 return; 199 if ((port_mask & (1 << port_id)) == 0) 200 continue; 201 memset(&link, 0, sizeof(link)); 202 rte_eth_link_get_nowait(port_id, &link); 203 /* print link status if flag set */ 204 if (print_flag == 1) { 205 if (link.link_status) 206 printf( 207 "Port%d Link Up. Speed %u Mbps - %s\n", 208 port_id, link.link_speed, 209 (link.link_duplex == ETH_LINK_FULL_DUPLEX) ? 210 ("full-duplex") : ("half-duplex\n")); 211 else 212 printf("Port %d Link Down\n", port_id); 213 continue; 214 } 215 /* clear all_ports_up flag if any link down */ 216 if (link.link_status == ETH_LINK_DOWN) { 217 all_ports_up = 0; 218 break; 219 } 220 } 221 /* after finally printing all link status, get out */ 222 if (print_flag == 1) 223 break; 224 225 if (all_ports_up == 0) { 226 printf("."); 227 fflush(stdout); 228 rte_delay_ms(CHECK_INTERVAL); 229 } 230 231 /* set the print_flag if all ports up or timeout */ 232 if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) { 233 print_flag = 1; 234 printf("done\n"); 235 } 236 } 237 } 238 239 /* Print out statistics on packets dropped */ 240 static void 241 print_stats(struct l2fwd_resources *rsrc) 242 { 243 uint64_t total_packets_dropped, total_packets_tx, total_packets_rx; 244 uint32_t port_id; 245 246 total_packets_dropped = 0; 247 total_packets_tx = 0; 248 total_packets_rx = 0; 249 250 const char clr[] = {27, '[', '2', 'J', '\0' }; 251 const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0' }; 252 253 /* Clear screen and move to top left */ 254 printf("%s%s", clr, topLeft); 255 256 printf("\nPort statistics ===================================="); 257 258 for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) { 259 /* skip disabled ports */ 260 if ((rsrc->enabled_port_mask & (1 << port_id)) == 0) 261 continue; 262 printf("\nStatistics for port %u ------------------------------" 263 "\nPackets sent: %24"PRIu64 264 "\nPackets received: %20"PRIu64 265 "\nPackets dropped: %21"PRIu64, 266 port_id, 267 rsrc->port_stats[port_id].tx, 268 rsrc->port_stats[port_id].rx, 269 rsrc->port_stats[port_id].dropped); 270 271 total_packets_dropped += 272 rsrc->port_stats[port_id].dropped; 273 total_packets_tx += rsrc->port_stats[port_id].tx; 274 total_packets_rx += rsrc->port_stats[port_id].rx; 275 } 276 printf("\nAggregate statistics ===============================" 277 "\nTotal packets sent: %18"PRIu64 278 "\nTotal packets received: %14"PRIu64 279 "\nTotal packets dropped: %15"PRIu64, 280 total_packets_tx, 281 total_packets_rx, 282 total_packets_dropped); 283 printf("\n====================================================\n"); 284 } 285 286 static void 287 l2fwd_event_print_stats(struct l2fwd_resources *rsrc) 288 { 289 uint64_t prev_tsc = 0, diff_tsc, cur_tsc, timer_tsc = 0; 290 const uint64_t timer_period = rsrc->timer_period; 291 292 while (!rsrc->force_quit) { 293 /* if timer is enabled */ 294 if (timer_period > 0) { 295 cur_tsc = rte_rdtsc(); 296 diff_tsc = cur_tsc - prev_tsc; 297 298 /* advance the timer */ 299 timer_tsc += diff_tsc; 300 301 /* if timer has reached its timeout */ 302 if (unlikely(timer_tsc >= timer_period)) { 303 print_stats(rsrc); 304 /* reset the timer */ 305 timer_tsc = 0; 306 } 307 prev_tsc = cur_tsc; 308 } 309 } 310 } 311 312 313 static void 314 signal_handler(int signum) 315 { 316 struct l2fwd_resources *rsrc = l2fwd_get_rsrc(); 317 if (signum == SIGINT || signum == SIGTERM) { 318 printf("\n\nSignal %d received, preparing to exit...\n", 319 signum); 320 rsrc->force_quit = true; 321 } 322 } 323 324 int 325 main(int argc, char **argv) 326 { 327 struct l2fwd_resources *rsrc; 328 uint16_t nb_ports_available = 0; 329 uint32_t nb_ports_in_mask = 0; 330 uint16_t port_id, last_port; 331 uint32_t nb_mbufs; 332 uint16_t nb_ports; 333 int ret; 334 335 /* init EAL */ 336 ret = rte_eal_init(argc, argv); 337 if (ret < 0) 338 rte_panic("Invalid EAL arguments\n"); 339 argc -= ret; 340 argv += ret; 341 342 rsrc = l2fwd_get_rsrc(); 343 344 signal(SIGINT, signal_handler); 345 signal(SIGTERM, signal_handler); 346 347 /* parse application arguments (after the EAL ones) */ 348 ret = l2fwd_event_parse_args(argc, argv, rsrc); 349 if (ret < 0) 350 rte_panic("Invalid L2FWD arguments\n"); 351 352 printf("MAC updating %s\n", rsrc->mac_updating ? "enabled" : 353 "disabled"); 354 355 nb_ports = rte_eth_dev_count_avail(); 356 if (nb_ports == 0) 357 rte_panic("No Ethernet ports - bye\n"); 358 359 /* check port mask to possible port mask */ 360 if (rsrc->enabled_port_mask & ~((1 << nb_ports) - 1)) 361 rte_panic("Invalid portmask; possible (0x%x)\n", 362 (1 << nb_ports) - 1); 363 364 /* reset l2fwd_dst_ports */ 365 for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) 366 rsrc->dst_ports[port_id] = 0; 367 last_port = 0; 368 369 /* 370 * Each logical core is assigned a dedicated TX queue on each port. 371 */ 372 RTE_ETH_FOREACH_DEV(port_id) { 373 /* skip ports that are not enabled */ 374 if ((rsrc->enabled_port_mask & (1 << port_id)) == 0) 375 continue; 376 377 if (nb_ports_in_mask % 2) { 378 rsrc->dst_ports[port_id] = last_port; 379 rsrc->dst_ports[last_port] = port_id; 380 } else { 381 last_port = port_id; 382 } 383 384 nb_ports_in_mask++; 385 } 386 if (nb_ports_in_mask % 2) { 387 printf("Notice: odd number of ports in portmask.\n"); 388 rsrc->dst_ports[last_port] = last_port; 389 } 390 391 nb_mbufs = RTE_MAX(nb_ports * (RTE_TEST_RX_DESC_DEFAULT + 392 RTE_TEST_TX_DESC_DEFAULT + 393 MAX_PKT_BURST + rte_lcore_count() * 394 MEMPOOL_CACHE_SIZE), 8192U); 395 396 /* create the mbuf pool */ 397 rsrc->pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool", 398 nb_mbufs, MEMPOOL_CACHE_SIZE, 0, 399 RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); 400 if (rsrc->pktmbuf_pool == NULL) 401 rte_panic("Cannot init mbuf pool\n"); 402 403 nb_ports_available = l2fwd_event_init_ports(rsrc); 404 if (!nb_ports_available) 405 rte_panic("All available ports are disabled. Please set portmask.\n"); 406 407 l2fwd_poll_resource_setup(rsrc); 408 409 /* initialize port stats */ 410 memset(&rsrc->port_stats, 0, 411 sizeof(struct l2fwd_port_statistics)); 412 413 /* All settings are done. Now enable eth devices */ 414 RTE_ETH_FOREACH_DEV(port_id) { 415 /* skip ports that are not enabled */ 416 if ((rsrc->enabled_port_mask & 417 (1 << port_id)) == 0) 418 continue; 419 420 ret = rte_eth_dev_start(port_id); 421 if (ret < 0) 422 rte_panic("rte_eth_dev_start:err=%d, port=%u\n", ret, 423 port_id); 424 } 425 426 check_all_ports_link_status(rsrc, rsrc->enabled_port_mask); 427 428 /* launch per-lcore init on every lcore */ 429 rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, rsrc, 430 SKIP_MASTER); 431 l2fwd_event_print_stats(rsrc); 432 rte_eal_mp_wait_lcore(); 433 434 RTE_ETH_FOREACH_DEV(port_id) { 435 if ((rsrc->enabled_port_mask & 436 (1 << port_id)) == 0) 437 continue; 438 printf("Closing port %d...", port_id); 439 rte_eth_dev_stop(port_id); 440 rte_eth_dev_close(port_id); 441 printf(" Done\n"); 442 } 443 printf("Bye...\n"); 444 445 return 0; 446 } 447