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