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