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