1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2010-2014 Intel Corporation 3 */ 4 5 #include <stdint.h> 6 #include <sys/queue.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <stdio.h> 10 #include <assert.h> 11 #include <errno.h> 12 #include <signal.h> 13 #include <stdarg.h> 14 #include <inttypes.h> 15 #include <getopt.h> 16 17 #include <rte_common.h> 18 #include <rte_log.h> 19 #include <rte_memory.h> 20 #include <rte_memcpy.h> 21 #include <rte_eal.h> 22 #include <rte_launch.h> 23 #include <rte_atomic.h> 24 #include <rte_cycles.h> 25 #include <rte_prefetch.h> 26 #include <rte_lcore.h> 27 #include <rte_per_lcore.h> 28 #include <rte_branch_prediction.h> 29 #include <rte_interrupts.h> 30 #include <rte_random.h> 31 #include <rte_debug.h> 32 #include <rte_ether.h> 33 #include <rte_ethdev.h> 34 #include <rte_mempool.h> 35 #include <rte_mbuf.h> 36 37 /* basic constants used in application */ 38 #define MAX_QUEUES 1024 39 /* 40 * 1024 queues require to meet the needs of a large number of vmdq_pools. 41 * (RX/TX_queue_nb * RX/TX_ring_descriptors_nb) per port. 42 */ 43 #define NUM_MBUFS_PER_PORT (MAX_QUEUES * RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, \ 44 RTE_TEST_TX_DESC_DEFAULT)) 45 #define MBUF_CACHE_SIZE 64 46 47 #define MAX_PKT_BURST 32 48 49 /* 50 * Configurable number of RX/TX ring descriptors 51 */ 52 #define RTE_TEST_RX_DESC_DEFAULT 1024 53 #define RTE_TEST_TX_DESC_DEFAULT 1024 54 55 #define INVALID_PORT_ID 0xFF 56 57 /* mask of enabled ports */ 58 static uint32_t enabled_port_mask; 59 static uint16_t ports[RTE_MAX_ETHPORTS]; 60 static unsigned num_ports; 61 62 /* number of pools (if user does not specify any, 32 by default */ 63 static enum rte_eth_nb_pools num_pools = ETH_32_POOLS; 64 static enum rte_eth_nb_tcs num_tcs = ETH_4_TCS; 65 static uint16_t num_queues, num_vmdq_queues; 66 static uint16_t vmdq_pool_base, vmdq_queue_base; 67 static uint8_t rss_enable; 68 69 /* Empty vmdq+dcb configuration structure. Filled in programmatically. 8< */ 70 static const struct rte_eth_conf vmdq_dcb_conf_default = { 71 .rxmode = { 72 .mq_mode = ETH_MQ_RX_VMDQ_DCB, 73 .split_hdr_size = 0, 74 }, 75 .txmode = { 76 .mq_mode = ETH_MQ_TX_VMDQ_DCB, 77 }, 78 /* 79 * should be overridden separately in code with 80 * appropriate values 81 */ 82 .rx_adv_conf = { 83 .vmdq_dcb_conf = { 84 .nb_queue_pools = ETH_32_POOLS, 85 .enable_default_pool = 0, 86 .default_pool = 0, 87 .nb_pool_maps = 0, 88 .pool_map = {{0, 0},}, 89 .dcb_tc = {0}, 90 }, 91 .dcb_rx_conf = { 92 .nb_tcs = ETH_4_TCS, 93 /** Traffic class each UP mapped to. */ 94 .dcb_tc = {0}, 95 }, 96 .vmdq_rx_conf = { 97 .nb_queue_pools = ETH_32_POOLS, 98 .enable_default_pool = 0, 99 .default_pool = 0, 100 .nb_pool_maps = 0, 101 .pool_map = {{0, 0},}, 102 }, 103 }, 104 .tx_adv_conf = { 105 .vmdq_dcb_tx_conf = { 106 .nb_queue_pools = ETH_32_POOLS, 107 .dcb_tc = {0}, 108 }, 109 }, 110 }; 111 /* >8 End of empty vmdq+dcb configuration structure. */ 112 113 /* array used for printing out statistics */ 114 volatile unsigned long rxPackets[MAX_QUEUES] = {0}; 115 116 /* Dividing up the possible user priority values. 8< */ 117 const uint16_t vlan_tags[] = { 118 0, 1, 2, 3, 4, 5, 6, 7, 119 8, 9, 10, 11, 12, 13, 14, 15, 120 16, 17, 18, 19, 20, 21, 22, 23, 121 24, 25, 26, 27, 28, 29, 30, 31 122 }; 123 124 const uint16_t num_vlans = RTE_DIM(vlan_tags); 125 /* pool mac addr template, pool mac addr is like: 52 54 00 12 port# pool# */ 126 static struct rte_ether_addr pool_addr_template = { 127 .addr_bytes = {0x52, 0x54, 0x00, 0x12, 0x00, 0x00} 128 }; 129 130 /* ethernet addresses of ports */ 131 static struct rte_ether_addr vmdq_ports_eth_addr[RTE_MAX_ETHPORTS]; 132 133 /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array 134 * given above, and the number of traffic classes available for use. */ 135 static inline int 136 get_eth_conf(struct rte_eth_conf *eth_conf) 137 { 138 struct rte_eth_vmdq_dcb_conf conf; 139 struct rte_eth_vmdq_rx_conf vmdq_conf; 140 struct rte_eth_dcb_rx_conf dcb_conf; 141 struct rte_eth_vmdq_dcb_tx_conf tx_conf; 142 uint8_t i; 143 144 conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools; 145 vmdq_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools; 146 tx_conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools; 147 conf.nb_pool_maps = num_pools; 148 vmdq_conf.nb_pool_maps = num_pools; 149 conf.enable_default_pool = 0; 150 vmdq_conf.enable_default_pool = 0; 151 conf.default_pool = 0; /* set explicit value, even if not used */ 152 vmdq_conf.default_pool = 0; 153 154 for (i = 0; i < conf.nb_pool_maps; i++) { 155 conf.pool_map[i].vlan_id = vlan_tags[i]; 156 vmdq_conf.pool_map[i].vlan_id = vlan_tags[i]; 157 conf.pool_map[i].pools = 1UL << i; 158 vmdq_conf.pool_map[i].pools = 1UL << i; 159 } 160 for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){ 161 conf.dcb_tc[i] = i % num_tcs; 162 dcb_conf.dcb_tc[i] = i % num_tcs; 163 tx_conf.dcb_tc[i] = i % num_tcs; 164 } 165 dcb_conf.nb_tcs = (enum rte_eth_nb_tcs)num_tcs; 166 (void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf))); 167 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_dcb_conf, &conf, 168 sizeof(conf))); 169 (void)(rte_memcpy(ð_conf->rx_adv_conf.dcb_rx_conf, &dcb_conf, 170 sizeof(dcb_conf))); 171 (void)(rte_memcpy(ð_conf->rx_adv_conf.vmdq_rx_conf, &vmdq_conf, 172 sizeof(vmdq_conf))); 173 (void)(rte_memcpy(ð_conf->tx_adv_conf.vmdq_dcb_tx_conf, &tx_conf, 174 sizeof(tx_conf))); 175 if (rss_enable) { 176 eth_conf->rxmode.mq_mode = ETH_MQ_RX_VMDQ_DCB_RSS; 177 eth_conf->rx_adv_conf.rss_conf.rss_hf = ETH_RSS_IP | 178 ETH_RSS_UDP | 179 ETH_RSS_TCP | 180 ETH_RSS_SCTP; 181 } 182 return 0; 183 } 184 /* >8 End of dividing up the possible user priority values. */ 185 186 /* 187 * Initialises a given port using global settings and with the rx buffers 188 * coming from the mbuf_pool passed as parameter 189 */ 190 static inline int 191 port_init(uint16_t port, struct rte_mempool *mbuf_pool) 192 { 193 struct rte_eth_dev_info dev_info; 194 struct rte_eth_conf port_conf = {0}; 195 uint16_t rxRingSize = RTE_TEST_RX_DESC_DEFAULT; 196 uint16_t txRingSize = RTE_TEST_TX_DESC_DEFAULT; 197 int retval; 198 uint16_t q; 199 uint16_t queues_per_pool; 200 uint32_t max_nb_pools; 201 struct rte_eth_txconf txq_conf; 202 uint64_t rss_hf_tmp; 203 204 /* 205 * The max pool number from dev_info will be used to validate the pool 206 * number specified in cmd line 207 */ 208 retval = rte_eth_dev_info_get(port, &dev_info); 209 if (retval != 0) { 210 printf("Error during getting device (port %u) info: %s\n", 211 port, strerror(-retval)); 212 213 return retval; 214 } 215 216 max_nb_pools = (uint32_t)dev_info.max_vmdq_pools; 217 /* 218 * We allow to process part of VMDQ pools specified by num_pools in 219 * command line. 220 */ 221 if (num_pools > max_nb_pools) { 222 printf("num_pools %d >max_nb_pools %d\n", 223 num_pools, max_nb_pools); 224 return -1; 225 } 226 227 /* 228 * NIC queues are divided into pf queues and vmdq queues. 229 * There is assumption here all ports have the same configuration! 230 */ 231 vmdq_queue_base = dev_info.vmdq_queue_base; 232 vmdq_pool_base = dev_info.vmdq_pool_base; 233 printf("vmdq queue base: %d pool base %d\n", 234 vmdq_queue_base, vmdq_pool_base); 235 if (vmdq_pool_base == 0) { 236 num_vmdq_queues = dev_info.max_rx_queues; 237 num_queues = dev_info.max_rx_queues; 238 if (num_tcs != num_vmdq_queues / num_pools) { 239 printf("nb_tcs %d is invalid considering with" 240 " nb_pools %d, nb_tcs * nb_pools should = %d\n", 241 num_tcs, num_pools, num_vmdq_queues); 242 return -1; 243 } 244 } else { 245 queues_per_pool = dev_info.vmdq_queue_num / 246 dev_info.max_vmdq_pools; 247 if (num_tcs > queues_per_pool) { 248 printf("num_tcs %d > num of queues per pool %d\n", 249 num_tcs, queues_per_pool); 250 return -1; 251 } 252 num_vmdq_queues = num_pools * queues_per_pool; 253 num_queues = vmdq_queue_base + num_vmdq_queues; 254 printf("Configured vmdq pool num: %u," 255 " each vmdq pool has %u queues\n", 256 num_pools, queues_per_pool); 257 } 258 259 if (!rte_eth_dev_is_valid_port(port)) 260 return -1; 261 262 retval = get_eth_conf(&port_conf); 263 if (retval < 0) 264 return retval; 265 266 retval = rte_eth_dev_info_get(port, &dev_info); 267 if (retval != 0) { 268 printf("Error during getting device (port %u) info: %s\n", 269 port, strerror(-retval)); 270 271 return retval; 272 } 273 274 if (dev_info.tx_offload_capa & DEV_TX_OFFLOAD_MBUF_FAST_FREE) 275 port_conf.txmode.offloads |= 276 DEV_TX_OFFLOAD_MBUF_FAST_FREE; 277 278 rss_hf_tmp = port_conf.rx_adv_conf.rss_conf.rss_hf; 279 port_conf.rx_adv_conf.rss_conf.rss_hf &= 280 dev_info.flow_type_rss_offloads; 281 if (port_conf.rx_adv_conf.rss_conf.rss_hf != rss_hf_tmp) { 282 printf("Port %u modified RSS hash function based on hardware support," 283 "requested:%#"PRIx64" configured:%#"PRIx64"\n", 284 port, 285 rss_hf_tmp, 286 port_conf.rx_adv_conf.rss_conf.rss_hf); 287 } 288 289 /* 290 * Though in this example, all queues including pf queues are setup. 291 * This is because VMDQ queues doesn't always start from zero, and the 292 * PMD layer doesn't support selectively initialising part of rx/tx 293 * queues. 294 */ 295 retval = rte_eth_dev_configure(port, num_queues, num_queues, &port_conf); 296 if (retval != 0) 297 return retval; 298 299 retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &rxRingSize, 300 &txRingSize); 301 if (retval != 0) 302 return retval; 303 if (RTE_MAX(rxRingSize, txRingSize) > 304 RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, RTE_TEST_TX_DESC_DEFAULT)) { 305 printf("Mbuf pool has an insufficient size for port %u.\n", 306 port); 307 return -1; 308 } 309 310 for (q = 0; q < num_queues; q++) { 311 retval = rte_eth_rx_queue_setup(port, q, rxRingSize, 312 rte_eth_dev_socket_id(port), 313 NULL, 314 mbuf_pool); 315 if (retval < 0) { 316 printf("initialize rx queue %d failed\n", q); 317 return retval; 318 } 319 } 320 321 txq_conf = dev_info.default_txconf; 322 txq_conf.offloads = port_conf.txmode.offloads; 323 for (q = 0; q < num_queues; q++) { 324 retval = rte_eth_tx_queue_setup(port, q, txRingSize, 325 rte_eth_dev_socket_id(port), 326 &txq_conf); 327 if (retval < 0) { 328 printf("initialize tx queue %d failed\n", q); 329 return retval; 330 } 331 } 332 333 retval = rte_eth_dev_start(port); 334 if (retval < 0) { 335 printf("port %d start failed\n", port); 336 return retval; 337 } 338 339 retval = rte_eth_macaddr_get(port, &vmdq_ports_eth_addr[port]); 340 if (retval < 0) { 341 printf("port %d MAC address get failed: %s\n", port, 342 rte_strerror(-retval)); 343 return retval; 344 } 345 printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8 346 " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n", 347 (unsigned)port, 348 RTE_ETHER_ADDR_BYTES(&vmdq_ports_eth_addr[port])); 349 350 /* Set mac for each pool. 8< */ 351 for (q = 0; q < num_pools; q++) { 352 struct rte_ether_addr mac; 353 354 mac = pool_addr_template; 355 mac.addr_bytes[4] = port; 356 mac.addr_bytes[5] = q; 357 printf("Port %u vmdq pool %u set mac " RTE_ETHER_ADDR_PRT_FMT "\n", 358 port, q, RTE_ETHER_ADDR_BYTES(&mac)); 359 retval = rte_eth_dev_mac_addr_add(port, &mac, 360 q + vmdq_pool_base); 361 if (retval) { 362 printf("mac addr add failed at pool %d\n", q); 363 return retval; 364 } 365 } 366 /* >8 End of set mac for each pool. */ 367 368 return 0; 369 } 370 371 /* Check num_pools parameter and set it if OK*/ 372 static int 373 vmdq_parse_num_pools(const char *q_arg) 374 { 375 char *end = NULL; 376 int n; 377 378 /* parse number string */ 379 n = strtol(q_arg, &end, 10); 380 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) 381 return -1; 382 if (n != 16 && n != 32) 383 return -1; 384 if (n == 16) 385 num_pools = ETH_16_POOLS; 386 else 387 num_pools = ETH_32_POOLS; 388 389 return 0; 390 } 391 392 /* Check num_tcs parameter and set it if OK*/ 393 static int 394 vmdq_parse_num_tcs(const char *q_arg) 395 { 396 char *end = NULL; 397 int n; 398 399 /* parse number string */ 400 n = strtol(q_arg, &end, 10); 401 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0')) 402 return -1; 403 404 if (n != 4 && n != 8) 405 return -1; 406 if (n == 4) 407 num_tcs = ETH_4_TCS; 408 else 409 num_tcs = ETH_8_TCS; 410 411 return 0; 412 } 413 414 static int 415 parse_portmask(const char *portmask) 416 { 417 char *end = NULL; 418 unsigned long pm; 419 420 /* parse hexadecimal string */ 421 pm = strtoul(portmask, &end, 16); 422 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) 423 return 0; 424 425 return pm; 426 } 427 428 /* Display usage */ 429 static void 430 vmdq_usage(const char *prgname) 431 { 432 printf("%s [EAL options] -- -p PORTMASK]\n" 433 " --nb-pools NP: number of pools (32 default, 16)\n" 434 " --nb-tcs NP: number of TCs (4 default, 8)\n" 435 " --enable-rss: enable RSS (disabled by default)\n", 436 prgname); 437 } 438 439 /* Parse the argument (num_pools) given in the command line of the application */ 440 static int 441 vmdq_parse_args(int argc, char **argv) 442 { 443 int opt; 444 int option_index; 445 unsigned i; 446 const char *prgname = argv[0]; 447 static struct option long_option[] = { 448 {"nb-pools", required_argument, NULL, 0}, 449 {"nb-tcs", required_argument, NULL, 0}, 450 {"enable-rss", 0, NULL, 0}, 451 {NULL, 0, 0, 0} 452 }; 453 454 /* Parse command line */ 455 while ((opt = getopt_long(argc, argv, "p:", long_option, 456 &option_index)) != EOF) { 457 switch (opt) { 458 /* portmask */ 459 case 'p': 460 enabled_port_mask = parse_portmask(optarg); 461 if (enabled_port_mask == 0) { 462 printf("invalid portmask\n"); 463 vmdq_usage(prgname); 464 return -1; 465 } 466 break; 467 case 0: 468 if (!strcmp(long_option[option_index].name, "nb-pools")) { 469 if (vmdq_parse_num_pools(optarg) == -1) { 470 printf("invalid number of pools\n"); 471 return -1; 472 } 473 } 474 475 if (!strcmp(long_option[option_index].name, "nb-tcs")) { 476 if (vmdq_parse_num_tcs(optarg) == -1) { 477 printf("invalid number of tcs\n"); 478 return -1; 479 } 480 } 481 482 if (!strcmp(long_option[option_index].name, "enable-rss")) 483 rss_enable = 1; 484 break; 485 486 default: 487 vmdq_usage(prgname); 488 return -1; 489 } 490 } 491 492 for (i = 0; i < RTE_MAX_ETHPORTS; i++) { 493 if (enabled_port_mask & (1 << i)) 494 ports[num_ports++] = (uint8_t)i; 495 } 496 497 if (num_ports < 2 || num_ports % 2) { 498 printf("Current enabled port number is %u," 499 " but it should be even and at least 2\n", num_ports); 500 return -1; 501 } 502 503 return 0; 504 } 505 506 static void 507 update_mac_address(struct rte_mbuf *m, unsigned dst_port) 508 { 509 struct rte_ether_hdr *eth; 510 void *tmp; 511 512 eth = rte_pktmbuf_mtod(m, struct rte_ether_hdr *); 513 514 /* 02:00:00:00:00:xx */ 515 tmp = ð->d_addr.addr_bytes[0]; 516 *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40); 517 518 /* src addr */ 519 rte_ether_addr_copy(&vmdq_ports_eth_addr[dst_port], ð->s_addr); 520 } 521 522 /* When we receive a HUP signal, print out our stats */ 523 static void 524 sighup_handler(int signum) 525 { 526 unsigned q = vmdq_queue_base; 527 528 for (; q < num_queues; q++) { 529 if (q % (num_vmdq_queues / num_pools) == 0) 530 printf("\nPool %u: ", (q - vmdq_queue_base) / 531 (num_vmdq_queues / num_pools)); 532 printf("%lu ", rxPackets[q]); 533 } 534 printf("\nFinished handling signal %d\n", signum); 535 } 536 537 /* 538 * Main thread that does the work, reading from INPUT_PORT 539 * and writing to OUTPUT_PORT 540 */ 541 static int 542 lcore_main(void *arg) 543 { 544 const uintptr_t core_num = (uintptr_t)arg; 545 const unsigned num_cores = rte_lcore_count(); 546 uint16_t startQueue, endQueue; 547 uint16_t q, i, p; 548 const uint16_t quot = (uint16_t)(num_vmdq_queues / num_cores); 549 const uint16_t remainder = (uint16_t)(num_vmdq_queues % num_cores); 550 551 552 if (remainder) { 553 if (core_num < remainder) { 554 startQueue = (uint16_t)(core_num * (quot + 1)); 555 endQueue = (uint16_t)(startQueue + quot + 1); 556 } else { 557 startQueue = (uint16_t)(core_num * quot + remainder); 558 endQueue = (uint16_t)(startQueue + quot); 559 } 560 } else { 561 startQueue = (uint16_t)(core_num * quot); 562 endQueue = (uint16_t)(startQueue + quot); 563 } 564 565 /* vmdq queue idx doesn't always start from zero.*/ 566 startQueue += vmdq_queue_base; 567 endQueue += vmdq_queue_base; 568 printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num, 569 rte_lcore_id(), startQueue, endQueue - 1); 570 571 if (startQueue == endQueue) { 572 printf("lcore %u has nothing to do\n", (unsigned)core_num); 573 return 0; 574 } 575 576 for (;;) { 577 struct rte_mbuf *buf[MAX_PKT_BURST]; 578 const uint16_t buf_size = RTE_DIM(buf); 579 for (p = 0; p < num_ports; p++) { 580 const uint8_t src = ports[p]; 581 const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */ 582 583 if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID)) 584 continue; 585 586 for (q = startQueue; q < endQueue; q++) { 587 const uint16_t rxCount = rte_eth_rx_burst(src, 588 q, buf, buf_size); 589 590 if (unlikely(rxCount == 0)) 591 continue; 592 593 rxPackets[q] += rxCount; 594 595 for (i = 0; i < rxCount; i++) 596 update_mac_address(buf[i], dst); 597 598 const uint16_t txCount = rte_eth_tx_burst(dst, 599 q, buf, rxCount); 600 if (txCount != rxCount) { 601 for (i = txCount; i < rxCount; i++) 602 rte_pktmbuf_free(buf[i]); 603 } 604 } 605 } 606 } 607 } 608 609 /* 610 * Update the global var NUM_PORTS and array PORTS according to system ports number 611 * and return valid ports number 612 */ 613 static unsigned check_ports_num(unsigned nb_ports) 614 { 615 unsigned valid_num_ports = num_ports; 616 unsigned portid; 617 618 if (num_ports > nb_ports) { 619 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n", 620 num_ports, nb_ports); 621 num_ports = nb_ports; 622 } 623 624 for (portid = 0; portid < num_ports; portid++) { 625 if (!rte_eth_dev_is_valid_port(ports[portid])) { 626 printf("\nSpecified port ID(%u) is not valid\n", 627 ports[portid]); 628 ports[portid] = INVALID_PORT_ID; 629 valid_num_ports--; 630 } 631 } 632 return valid_num_ports; 633 } 634 635 636 /* Main function, does initialisation and calls the per-lcore functions */ 637 int 638 main(int argc, char *argv[]) 639 { 640 unsigned cores; 641 struct rte_mempool *mbuf_pool; 642 unsigned lcore_id; 643 uintptr_t i; 644 int ret; 645 unsigned nb_ports, valid_num_ports; 646 uint16_t portid; 647 648 signal(SIGHUP, sighup_handler); 649 650 /* init EAL */ 651 ret = rte_eal_init(argc, argv); 652 if (ret < 0) 653 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n"); 654 argc -= ret; 655 argv += ret; 656 657 /* parse app arguments */ 658 ret = vmdq_parse_args(argc, argv); 659 if (ret < 0) 660 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n"); 661 662 cores = rte_lcore_count(); 663 if ((cores & (cores - 1)) != 0 || cores > RTE_MAX_LCORE) { 664 rte_exit(EXIT_FAILURE,"This program can only run on an even" 665 " number of cores(1-%d)\n\n", RTE_MAX_LCORE); 666 } 667 668 nb_ports = rte_eth_dev_count_avail(); 669 670 /* 671 * Update the global var NUM_PORTS and global array PORTS 672 * and get value of var VALID_NUM_PORTS according to system ports number 673 */ 674 valid_num_ports = check_ports_num(nb_ports); 675 676 if (valid_num_ports < 2 || valid_num_ports % 2) { 677 printf("Current valid ports number is %u\n", valid_num_ports); 678 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n"); 679 } 680 681 mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL", 682 NUM_MBUFS_PER_PORT * nb_ports, MBUF_CACHE_SIZE, 683 0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id()); 684 if (mbuf_pool == NULL) 685 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n"); 686 687 /* initialize all ports */ 688 RTE_ETH_FOREACH_DEV(portid) { 689 /* skip ports that are not enabled */ 690 if ((enabled_port_mask & (1 << portid)) == 0) { 691 printf("\nSkipping disabled port %d\n", portid); 692 continue; 693 } 694 if (port_init(portid, mbuf_pool) != 0) 695 rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n"); 696 } 697 698 /* call lcore_main() on every worker lcore */ 699 i = 0; 700 RTE_LCORE_FOREACH_WORKER(lcore_id) { 701 rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id); 702 } 703 /* call on main too */ 704 (void) lcore_main((void*)i); 705 706 /* clean up the EAL */ 707 rte_eal_cleanup(); 708 709 return 0; 710 } 711