1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(C) 2020 Marvell International Ltd. 3 */ 4 5 #include <arpa/inet.h> 6 #include <errno.h> 7 #include <getopt.h> 8 #include <inttypes.h> 9 #include <signal.h> 10 #include <stdarg.h> 11 #include <stdbool.h> 12 #include <stdint.h> 13 #include <stdio.h> 14 #include <stdlib.h> 15 #include <string.h> 16 #include <sys/socket.h> 17 #include <sys/types.h> 18 #include <sys/queue.h> 19 #include <unistd.h> 20 21 #include <rte_branch_prediction.h> 22 #include <rte_common.h> 23 #include <rte_eal.h> 24 #include <rte_ethdev.h> 25 #include <rte_log.h> 26 #include <rte_mempool.h> 27 #include <rte_per_lcore.h> 28 #include <rte_string_fns.h> 29 #include <rte_vect.h> 30 31 #include <cmdline_parse.h> 32 #include <cmdline_parse_etheraddr.h> 33 34 /* Log type */ 35 #define RTE_LOGTYPE_L3FWD_GRAPH RTE_LOGTYPE_USER1 36 37 /* 38 * Configurable number of RX/TX ring descriptors 39 */ 40 #define RTE_TEST_RX_DESC_DEFAULT 1024 41 #define RTE_TEST_TX_DESC_DEFAULT 1024 42 43 #define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS 44 #define MAX_RX_QUEUE_PER_PORT 128 45 46 #define MAX_RX_QUEUE_PER_LCORE 16 47 48 #define MAX_LCORE_PARAMS 1024 49 50 #define NB_SOCKETS 8 51 52 /**< Ports set in promiscuous mode off by default. */ 53 static int promiscuous_on; 54 55 static int numa_on = 1; /**< NUMA is enabled by default. */ 56 static int per_port_pool; /**< Use separate buffer pools per port; disabled */ 57 /**< by default */ 58 59 static volatile bool force_quit; 60 61 /* Ethernet addresses of ports */ 62 static uint64_t dest_eth_addr[RTE_MAX_ETHPORTS]; 63 xmm_t val_eth[RTE_MAX_ETHPORTS]; 64 65 /* Mask of enabled ports */ 66 static uint32_t enabled_port_mask; 67 68 struct lcore_rx_queue { 69 uint16_t port_id; 70 uint8_t queue_id; 71 }; 72 73 /* Lcore conf */ 74 struct lcore_conf { 75 uint16_t n_rx_queue; 76 struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE]; 77 } __rte_cache_aligned; 78 79 static struct lcore_conf lcore_conf[RTE_MAX_LCORE]; 80 81 struct lcore_params { 82 uint16_t port_id; 83 uint8_t queue_id; 84 uint8_t lcore_id; 85 } __rte_cache_aligned; 86 87 static struct lcore_params lcore_params_array[MAX_LCORE_PARAMS]; 88 static struct lcore_params lcore_params_array_default[] = { 89 {0, 0, 2}, {0, 1, 2}, {0, 2, 2}, {1, 0, 2}, {1, 1, 2}, 90 {1, 2, 2}, {2, 0, 2}, {3, 0, 3}, {3, 1, 3}, 91 }; 92 93 static struct lcore_params *lcore_params = lcore_params_array_default; 94 static uint16_t nb_lcore_params = RTE_DIM(lcore_params_array_default); 95 96 static struct rte_eth_conf port_conf = { 97 .rxmode = { 98 .mq_mode = ETH_MQ_RX_RSS, 99 .max_rx_pkt_len = RTE_ETHER_MAX_LEN, 100 .split_hdr_size = 0, 101 }, 102 .rx_adv_conf = { 103 .rss_conf = { 104 .rss_key = NULL, 105 .rss_hf = ETH_RSS_IP, 106 }, 107 }, 108 .txmode = { 109 .mq_mode = ETH_MQ_TX_NONE, 110 }, 111 }; 112 113 static int 114 check_lcore_params(void) 115 { 116 uint8_t queue, lcore; 117 int socketid; 118 uint16_t i; 119 120 for (i = 0; i < nb_lcore_params; ++i) { 121 queue = lcore_params[i].queue_id; 122 if (queue >= MAX_RX_QUEUE_PER_PORT) { 123 printf("Invalid queue number: %hhu\n", queue); 124 return -1; 125 } 126 lcore = lcore_params[i].lcore_id; 127 if (!rte_lcore_is_enabled(lcore)) { 128 printf("Error: lcore %hhu is not enabled in lcore mask\n", 129 lcore); 130 return -1; 131 } 132 133 if (lcore == rte_get_master_lcore()) { 134 printf("Error: lcore %u is master lcore\n", lcore); 135 return -1; 136 } 137 socketid = rte_lcore_to_socket_id(lcore); 138 if ((socketid != 0) && (numa_on == 0)) { 139 printf("Warning: lcore %hhu is on socket %d with numa off\n", 140 lcore, socketid); 141 } 142 } 143 144 return 0; 145 } 146 147 static int 148 check_port_config(void) 149 { 150 uint16_t portid; 151 uint16_t i; 152 153 for (i = 0; i < nb_lcore_params; ++i) { 154 portid = lcore_params[i].port_id; 155 if ((enabled_port_mask & (1 << portid)) == 0) { 156 printf("Port %u is not enabled in port mask\n", portid); 157 return -1; 158 } 159 if (!rte_eth_dev_is_valid_port(portid)) { 160 printf("Port %u is not present on the board\n", portid); 161 return -1; 162 } 163 } 164 165 return 0; 166 } 167 168 static int 169 init_lcore_rx_queues(void) 170 { 171 uint16_t i, nb_rx_queue; 172 uint8_t lcore; 173 174 for (i = 0; i < nb_lcore_params; ++i) { 175 lcore = lcore_params[i].lcore_id; 176 nb_rx_queue = lcore_conf[lcore].n_rx_queue; 177 if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) { 178 printf("Error: too many queues (%u) for lcore: %u\n", 179 (unsigned int)nb_rx_queue + 1, 180 (unsigned int)lcore); 181 return -1; 182 } 183 184 lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id = 185 lcore_params[i].port_id; 186 lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id = 187 lcore_params[i].queue_id; 188 lcore_conf[lcore].n_rx_queue++; 189 } 190 191 return 0; 192 } 193 194 /* Display usage */ 195 static void 196 print_usage(const char *prgname) 197 { 198 fprintf(stderr, 199 "%s [EAL options] --" 200 " -p PORTMASK" 201 " [-P]" 202 " --config (port,queue,lcore)[,(port,queue,lcore)]" 203 " [--eth-dest=X,MM:MM:MM:MM:MM:MM]" 204 " [--enable-jumbo [--max-pkt-len PKTLEN]]" 205 " [--no-numa]" 206 " [--per-port-pool]\n\n" 207 208 " -p PORTMASK: Hexadecimal bitmask of ports to configure\n" 209 " -P : Enable promiscuous mode\n" 210 " --config (port,queue,lcore): Rx queue configuration\n" 211 " --eth-dest=X,MM:MM:MM:MM:MM:MM: Ethernet destination for " 212 "port X\n" 213 " --enable-jumbo: Enable jumbo frames\n" 214 " --max-pkt-len: Under the premise of enabling jumbo,\n" 215 " maximum packet length in decimal (64-9600)\n" 216 " --no-numa: Disable numa awareness\n" 217 " --per-port-pool: Use separate buffer pool per port\n\n", 218 prgname); 219 } 220 221 static int 222 parse_max_pkt_len(const char *pktlen) 223 { 224 unsigned long len; 225 char *end = NULL; 226 227 /* Parse decimal string */ 228 len = strtoul(pktlen, &end, 10); 229 if ((pktlen[0] == '\0') || (end == NULL) || (*end != '\0')) 230 return -1; 231 232 if (len == 0) 233 return -1; 234 235 return len; 236 } 237 238 static int 239 parse_portmask(const char *portmask) 240 { 241 char *end = NULL; 242 unsigned long pm; 243 244 /* Parse hexadecimal string */ 245 pm = strtoul(portmask, &end, 16); 246 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) 247 return -1; 248 249 if (pm == 0) 250 return -1; 251 252 return pm; 253 } 254 255 static int 256 parse_config(const char *q_arg) 257 { 258 enum fieldnames { FLD_PORT = 0, FLD_QUEUE, FLD_LCORE, _NUM_FLD }; 259 unsigned long int_fld[_NUM_FLD]; 260 const char *p, *p0 = q_arg; 261 char *str_fld[_NUM_FLD]; 262 uint32_t size; 263 char s[256]; 264 char *end; 265 int i; 266 267 nb_lcore_params = 0; 268 269 while ((p = strchr(p0, '(')) != NULL) { 270 ++p; 271 p0 = strchr(p, ')'); 272 if (p0 == NULL) 273 return -1; 274 275 size = p0 - p; 276 if (size >= sizeof(s)) 277 return -1; 278 279 memcpy(s, p, size); 280 s[size] = '\0'; 281 if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') != 282 _NUM_FLD) 283 return -1; 284 for (i = 0; i < _NUM_FLD; i++) { 285 errno = 0; 286 int_fld[i] = strtoul(str_fld[i], &end, 0); 287 if (errno != 0 || end == str_fld[i]) 288 return -1; 289 } 290 291 if (nb_lcore_params >= MAX_LCORE_PARAMS) { 292 printf("Exceeded max number of lcore params: %hu\n", 293 nb_lcore_params); 294 return -1; 295 } 296 297 if (int_fld[FLD_PORT] >= RTE_MAX_ETHPORTS || 298 int_fld[FLD_LCORE] >= RTE_MAX_LCORE) { 299 printf("Invalid port/lcore id\n"); 300 return -1; 301 } 302 303 lcore_params_array[nb_lcore_params].port_id = 304 (uint8_t)int_fld[FLD_PORT]; 305 lcore_params_array[nb_lcore_params].queue_id = 306 (uint8_t)int_fld[FLD_QUEUE]; 307 lcore_params_array[nb_lcore_params].lcore_id = 308 (uint8_t)int_fld[FLD_LCORE]; 309 ++nb_lcore_params; 310 } 311 lcore_params = lcore_params_array; 312 313 return 0; 314 } 315 316 static void 317 parse_eth_dest(const char *optarg) 318 { 319 uint8_t c, *dest, peer_addr[6]; 320 uint16_t portid; 321 char *port_end; 322 323 errno = 0; 324 portid = strtoul(optarg, &port_end, 10); 325 if (errno != 0 || port_end == optarg || *port_end++ != ',') 326 rte_exit(EXIT_FAILURE, "Invalid eth-dest: %s", optarg); 327 if (portid >= RTE_MAX_ETHPORTS) 328 rte_exit(EXIT_FAILURE, 329 "eth-dest: port %d >= RTE_MAX_ETHPORTS(%d)\n", portid, 330 RTE_MAX_ETHPORTS); 331 332 if (cmdline_parse_etheraddr(NULL, port_end, &peer_addr, 333 sizeof(peer_addr)) < 0) 334 rte_exit(EXIT_FAILURE, "Invalid ethernet address: %s\n", 335 port_end); 336 dest = (uint8_t *)&dest_eth_addr[portid]; 337 for (c = 0; c < 6; c++) 338 dest[c] = peer_addr[c]; 339 *(uint64_t *)(val_eth + portid) = dest_eth_addr[portid]; 340 } 341 342 #define MAX_JUMBO_PKT_LEN 9600 343 #define MEMPOOL_CACHE_SIZE 256 344 345 static const char short_options[] = "p:" /* portmask */ 346 "P" /* promiscuous */ 347 ; 348 349 #define CMD_LINE_OPT_CONFIG "config" 350 #define CMD_LINE_OPT_ETH_DEST "eth-dest" 351 #define CMD_LINE_OPT_NO_NUMA "no-numa" 352 #define CMD_LINE_OPT_ENABLE_JUMBO "enable-jumbo" 353 #define CMD_LINE_OPT_PER_PORT_POOL "per-port-pool" 354 enum { 355 /* Long options mapped to a short option */ 356 357 /* First long only option value must be >= 256, so that we won't 358 * conflict with short options 359 */ 360 CMD_LINE_OPT_MIN_NUM = 256, 361 CMD_LINE_OPT_CONFIG_NUM, 362 CMD_LINE_OPT_ETH_DEST_NUM, 363 CMD_LINE_OPT_NO_NUMA_NUM, 364 CMD_LINE_OPT_ENABLE_JUMBO_NUM, 365 CMD_LINE_OPT_PARSE_PER_PORT_POOL, 366 }; 367 368 static const struct option lgopts[] = { 369 {CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM}, 370 {CMD_LINE_OPT_ETH_DEST, 1, 0, CMD_LINE_OPT_ETH_DEST_NUM}, 371 {CMD_LINE_OPT_NO_NUMA, 0, 0, CMD_LINE_OPT_NO_NUMA_NUM}, 372 {CMD_LINE_OPT_ENABLE_JUMBO, 0, 0, CMD_LINE_OPT_ENABLE_JUMBO_NUM}, 373 {CMD_LINE_OPT_PER_PORT_POOL, 0, 0, CMD_LINE_OPT_PARSE_PER_PORT_POOL}, 374 {NULL, 0, 0, 0}, 375 }; 376 377 /* 378 * This expression is used to calculate the number of mbufs needed 379 * depending on user input, taking into account memory for rx and 380 * tx hardware rings, cache per lcore and mtable per port per lcore. 381 * RTE_MAX is used to ensure that NB_MBUF never goes below a minimum 382 * value of 8192 383 */ 384 #define NB_MBUF(nports) \ 385 RTE_MAX((nports * nb_rx_queue * nb_rxd + \ 386 nports * nb_lcores * RTE_GRAPH_BURST_SIZE + \ 387 nports * n_tx_queue * nb_txd + \ 388 nb_lcores * MEMPOOL_CACHE_SIZE), 8192u) 389 390 /* Parse the argument given in the command line of the application */ 391 static int 392 parse_args(int argc, char **argv) 393 { 394 char *prgname = argv[0]; 395 int option_index; 396 char **argvopt; 397 int opt, ret; 398 399 argvopt = argv; 400 401 /* Error or normal output strings. */ 402 while ((opt = getopt_long(argc, argvopt, short_options, lgopts, 403 &option_index)) != EOF) { 404 405 switch (opt) { 406 /* Portmask */ 407 case 'p': 408 enabled_port_mask = parse_portmask(optarg); 409 if (enabled_port_mask == 0) { 410 fprintf(stderr, "Invalid portmask\n"); 411 print_usage(prgname); 412 return -1; 413 } 414 break; 415 416 case 'P': 417 promiscuous_on = 1; 418 break; 419 420 /* Long options */ 421 case CMD_LINE_OPT_CONFIG_NUM: 422 ret = parse_config(optarg); 423 if (ret) { 424 fprintf(stderr, "Invalid config\n"); 425 print_usage(prgname); 426 return -1; 427 } 428 break; 429 430 case CMD_LINE_OPT_ETH_DEST_NUM: 431 parse_eth_dest(optarg); 432 break; 433 434 case CMD_LINE_OPT_NO_NUMA_NUM: 435 numa_on = 0; 436 break; 437 438 case CMD_LINE_OPT_ENABLE_JUMBO_NUM: { 439 const struct option lenopts = {"max-pkt-len", 440 required_argument, 0, 0}; 441 442 port_conf.rxmode.offloads |= DEV_RX_OFFLOAD_JUMBO_FRAME; 443 port_conf.txmode.offloads |= DEV_TX_OFFLOAD_MULTI_SEGS; 444 445 /* 446 * if no max-pkt-len set, use the default 447 * value RTE_ETHER_MAX_LEN. 448 */ 449 if (getopt_long(argc, argvopt, "", &lenopts, 450 &option_index) == 0) { 451 ret = parse_max_pkt_len(optarg); 452 if (ret < 64 || ret > MAX_JUMBO_PKT_LEN) { 453 fprintf(stderr, "Invalid maximum " 454 "packet length\n"); 455 print_usage(prgname); 456 return -1; 457 } 458 port_conf.rxmode.max_rx_pkt_len = ret; 459 } 460 break; 461 } 462 463 case CMD_LINE_OPT_PARSE_PER_PORT_POOL: 464 printf("Per port buffer pool is enabled\n"); 465 per_port_pool = 1; 466 break; 467 468 default: 469 print_usage(prgname); 470 return -1; 471 } 472 } 473 474 if (optind >= 0) 475 argv[optind - 1] = prgname; 476 ret = optind - 1; 477 optind = 1; /* Reset getopt lib */ 478 479 return ret; 480 } 481 482 static void 483 signal_handler(int signum) 484 { 485 if (signum == SIGINT || signum == SIGTERM) { 486 printf("\n\nSignal %d received, preparing to exit...\n", 487 signum); 488 force_quit = true; 489 } 490 } 491 492 int 493 main(int argc, char **argv) 494 { 495 uint16_t portid; 496 int ret; 497 498 /* Init EAL */ 499 ret = rte_eal_init(argc, argv); 500 if (ret < 0) 501 rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n"); 502 argc -= ret; 503 argv += ret; 504 505 force_quit = false; 506 signal(SIGINT, signal_handler); 507 signal(SIGTERM, signal_handler); 508 509 /* Pre-init dst MACs for all ports to 02:00:00:00:00:xx */ 510 for (portid = 0; portid < RTE_MAX_ETHPORTS; portid++) { 511 dest_eth_addr[portid] = 512 RTE_ETHER_LOCAL_ADMIN_ADDR + ((uint64_t)portid << 40); 513 *(uint64_t *)(val_eth + portid) = dest_eth_addr[portid]; 514 } 515 516 /* Parse application arguments (after the EAL ones) */ 517 ret = parse_args(argc, argv); 518 if (ret < 0) 519 rte_exit(EXIT_FAILURE, "Invalid L3FWD_GRAPH parameters\n"); 520 521 if (check_lcore_params() < 0) 522 rte_exit(EXIT_FAILURE, "check_lcore_params() failed\n"); 523 524 ret = init_lcore_rx_queues(); 525 if (ret < 0) 526 rte_exit(EXIT_FAILURE, "init_lcore_rx_queues() failed\n"); 527 528 if (check_port_config() < 0) 529 rte_exit(EXIT_FAILURE, "check_port_config() failed\n"); 530 531 printf("Bye...\n"); 532 533 return ret; 534 } 535