0f52bdd7635906c50295f4c403dacb7dce671f49
[dpdk.git] / examples / vmdq / main.c
1 /*-
2  *   BSD LICENSE
3  * 
4  *   Copyright(c) 2010-2014 Intel Corporation. All rights reserved.
5  *   All rights reserved.
6  * 
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  * 
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  * 
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include <stdint.h>
35 #include <sys/queue.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <stdio.h>
39 #include <assert.h>
40 #include <errno.h>
41 #include <signal.h>
42 #include <stdarg.h>
43 #include <inttypes.h>
44 #include <getopt.h>
45
46 #include <rte_common.h>
47 #include <rte_log.h>
48 #include <rte_memory.h>
49 #include <rte_memcpy.h>
50 #include <rte_memzone.h>
51 #include <rte_tailq.h>
52 #include <rte_eal.h>
53 #include <rte_per_lcore.h>
54 #include <rte_launch.h>
55 #include <rte_atomic.h>
56 #include <rte_cycles.h>
57 #include <rte_prefetch.h>
58 #include <rte_lcore.h>
59 #include <rte_per_lcore.h>
60 #include <rte_branch_prediction.h>
61 #include <rte_interrupts.h>
62 #include <rte_pci.h>
63 #include <rte_random.h>
64 #include <rte_debug.h>
65 #include <rte_ether.h>
66 #include <rte_ethdev.h>
67 #include <rte_ring.h>
68 #include <rte_log.h>
69 #include <rte_mempool.h>
70 #include <rte_mbuf.h>
71 #include <rte_memcpy.h>
72
73 #include "main.h"
74
75 #define MAX_QUEUES 128
76 /*
77  * For 10 GbE, 128 queues require roughly 
78  * 128*512 (RX/TX_queue_nb * RX/TX_ring_descriptors_nb) per port. 
79  */
80 #define NUM_MBUFS_PER_PORT (128*512)
81 #define MBUF_CACHE_SIZE 64
82 #define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
83
84 /*
85  * RX and TX Prefetch, Host, and Write-back threshold values should be
86  * carefully set for optimal performance. Consult the network
87  * controller's datasheet and supporting DPDK documentation for guidance
88  * on how these parameters should be set.
89  */
90 #define RX_PTHRESH 8 /**< Default values of RX prefetch threshold reg. */
91 #define RX_HTHRESH 8 /**< Default values of RX host threshold reg. */
92 #define RX_WTHRESH 4 /**< Default values of RX write-back threshold reg. */
93  
94 /*
95  * These default values are optimized for use with the Intel(R) 82599 10 GbE
96  * Controller and the DPDK ixgbe PMD. Consider using other values for other
97  * network controllers and/or network drivers.
98  */
99 #define TX_PTHRESH 36 /**< Default values of TX prefetch threshold reg. */
100 #define TX_HTHRESH 0  /**< Default values of TX host threshold reg. */
101 #define TX_WTHRESH 0  /**< Default values of TX write-back threshold reg. */
102  
103 #define MAX_PKT_BURST 32
104
105 /*
106  * Configurable number of RX/TX ring descriptors
107  */
108 #define RTE_TEST_RX_DESC_DEFAULT 128
109 #define RTE_TEST_TX_DESC_DEFAULT 512
110
111 #define INVALID_PORT_ID 0xFF
112
113 /* mask of enabled ports */
114 static uint32_t enabled_port_mask = 0;
115
116 /* number of pools (if user does not specify any, 8 by default */
117 static uint32_t num_queues = 8;
118 static uint32_t num_pools = 8;
119
120 /*
121  * RX and TX Prefetch, Host, and Write-back threshold values should be
122  * carefully set for optimal performance. Consult the network
123  * controller's datasheet and supporting DPDK documentation for guidance
124  * on how these parameters should be set.
125  */
126 /* Default configuration for rx and tx thresholds etc. */
127 static const struct rte_eth_rxconf rx_conf_default = {
128         .rx_thresh = {
129                 .pthresh = RX_PTHRESH,
130                 .hthresh = RX_HTHRESH,
131                 .wthresh = RX_WTHRESH,
132         },
133         .rx_drop_en = 1,
134 };
135
136 /*
137  * These default values are optimized for use with the Intel(R) 82599 10 GbE
138  * Controller and the DPDK ixgbe/igb PMD. Consider using other values for other
139  * network controllers and/or network drivers.
140  */
141 static const struct rte_eth_txconf tx_conf_default = {
142         .tx_thresh = {
143                 .pthresh = TX_PTHRESH,
144                 .hthresh = TX_HTHRESH,
145                 .wthresh = TX_WTHRESH,
146         },
147         .tx_free_thresh = 0, /* Use PMD default values */
148         .tx_rs_thresh = 0, /* Use PMD default values */
149 };
150
151 /* empty vmdq configuration structure. Filled in programatically */
152 static const struct rte_eth_conf vmdq_conf_default = {
153         .rxmode = {
154                 .mq_mode        = ETH_MQ_RX_VMDQ_ONLY,
155                 .split_hdr_size = 0,
156                 .header_split   = 0, /**< Header Split disabled */
157                 .hw_ip_checksum = 0, /**< IP checksum offload disabled */
158                 .hw_vlan_filter = 0, /**< VLAN filtering disabled */
159                 .jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
160         },
161
162         .txmode = {
163                 .mq_mode = ETH_MQ_TX_NONE,
164         },
165         .rx_adv_conf = {
166                 /*
167                  * should be overridden separately in code with
168                  * appropriate values
169                  */
170                 .vmdq_rx_conf = {
171                         .nb_queue_pools = ETH_8_POOLS,
172                         .enable_default_pool = 0,
173                         .default_pool = 0,
174                         .nb_pool_maps = 0,
175                         .pool_map = {{0, 0},},
176                 },
177         },
178 };
179
180 static unsigned lcore_ids[RTE_MAX_LCORE];
181 static uint8_t ports[RTE_MAX_ETHPORTS];
182 static unsigned num_ports = 0; /**< The number of ports specified in command line */
183
184 /* array used for printing out statistics */
185 volatile unsigned long rxPackets[ MAX_QUEUES ] = {0};
186
187 const uint16_t vlan_tags[] = {
188         0,  1,  2,  3,  4,  5,  6,  7,
189         8,  9, 10, 11,  12, 13, 14, 15,
190         16, 17, 18, 19, 20, 21, 22, 23,
191         24, 25, 26, 27, 28, 29, 30, 31,
192         32, 33, 34, 35, 36, 37, 38, 39,
193         40, 41, 42, 43, 44, 45, 46, 47,
194         48, 49, 50, 51, 52, 53, 54, 55,
195         56, 57, 58, 59, 60, 61, 62, 63,
196 };
197
198 /* ethernet addresses of ports */
199 static struct ether_addr vmdq_ports_eth_addr[RTE_MAX_ETHPORTS];
200
201 #define MAX_QUEUE_NUM_10G 128
202 #define MAX_QUEUE_NUM_1G 8
203 #define MAX_POOL_MAP_NUM_10G 64
204 #define MAX_POOL_MAP_NUM_1G 32
205 #define MAX_POOL_NUM_10G 64
206 #define MAX_POOL_NUM_1G 8
207 /* Builds up the correct configuration for vmdq based on the vlan tags array
208  * given above, and determine the queue number and pool map number according to valid pool number */
209 static inline int
210 get_eth_conf(struct rte_eth_conf *eth_conf, uint32_t num_pools)
211 {
212         struct rte_eth_vmdq_rx_conf conf;
213         unsigned i;
214
215         conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
216         conf.enable_default_pool = 0;
217         conf.default_pool = 0; /* set explicit value, even if not used */
218         switch (num_pools) {
219         /* For 10G NIC like 82599, 128 is valid for queue number */
220         case MAX_POOL_NUM_10G:
221                 num_queues = MAX_QUEUE_NUM_10G;
222                 conf.nb_pool_maps = MAX_POOL_MAP_NUM_10G;
223                 break;
224         /* For 1G NIC like i350, 82580 and 82576, 8 is valid for queue number */
225         case MAX_POOL_NUM_1G:
226                 num_queues = MAX_QUEUE_NUM_1G;
227                 conf.nb_pool_maps = MAX_POOL_MAP_NUM_1G;
228                 break;
229         default:
230                 return -1;
231         }
232
233         for (i = 0; i < conf.nb_pool_maps; i++){
234                 conf.pool_map[i].vlan_id = vlan_tags[ i ];
235                 conf.pool_map[i].pools = (1UL << (i % num_pools));
236         }
237
238         (void)(rte_memcpy(eth_conf, &vmdq_conf_default, sizeof(*eth_conf)));
239         (void)(rte_memcpy(&eth_conf->rx_adv_conf.vmdq_rx_conf, &conf,
240                    sizeof(eth_conf->rx_adv_conf.vmdq_rx_conf)));
241         return 0;
242 }
243
244 /*
245  * Validate the pool number accrording to the max pool number gotten form dev_info
246  * If the pool number is invalid, give the error message and return -1 
247  */
248 static inline int
249 validate_num_pools(uint32_t max_nb_pools)
250 {
251         if (num_pools > max_nb_pools) {
252                 printf("invalid number of pools\n");
253                 return -1;
254         }
255
256         switch (max_nb_pools) {
257         /* For 10G NIC like 82599, 64 is valid for pool number */
258         case MAX_POOL_NUM_10G:
259                 if (num_pools != MAX_POOL_NUM_10G) {
260                         printf("invalid number of pools\n");
261                         return -1;
262                 }
263                 break;
264         /* For 1G NIC like i350, 82580 and 82576, 8 is valid for pool number */
265         case MAX_POOL_NUM_1G:
266                 if (num_pools != MAX_POOL_NUM_1G) {
267                         printf("invalid number of pools\n");
268                         return -1;
269                 }
270                 break;
271         default:
272                 return -1;
273         }
274
275         return 0;
276 }
277
278 /*
279  * Initialises a given port using global settings and with the rx buffers
280  * coming from the mbuf_pool passed as parameter
281  */
282 static inline int
283 port_init(uint8_t port, struct rte_mempool *mbuf_pool)
284 {
285         struct rte_eth_dev_info dev_info;
286         struct rte_eth_conf port_conf;
287         uint16_t rxRings, txRings = (uint16_t)rte_lcore_count();
288         const uint16_t rxRingSize = RTE_TEST_RX_DESC_DEFAULT, txRingSize = RTE_TEST_TX_DESC_DEFAULT;
289         int retval;
290         uint16_t q;
291         uint32_t max_nb_pools;
292
293         /* The max pool number from dev_info will be used to validate the pool number specified in cmd line */
294         rte_eth_dev_info_get (port, &dev_info);
295         max_nb_pools = (uint32_t)dev_info.max_vmdq_pools;
296         retval = validate_num_pools(max_nb_pools);
297         if (retval < 0)
298                 return retval;
299
300         retval = get_eth_conf(&port_conf, num_pools);
301         if (retval < 0)
302                 return retval;
303
304         if (port >= rte_eth_dev_count()) return -1;
305
306         rxRings = (uint16_t)num_queues,
307         retval = rte_eth_dev_configure(port, rxRings, txRings, &port_conf);
308         if (retval != 0)
309                 return retval;
310
311         for (q = 0; q < rxRings; q ++) {
312                 retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
313                                                 rte_eth_dev_socket_id(port), &rx_conf_default,
314                                                 mbuf_pool);
315                 if (retval < 0)
316                         return retval;
317         }
318
319         for (q = 0; q < txRings; q ++) {
320                 retval = rte_eth_tx_queue_setup(port, q, txRingSize,
321                                                 rte_eth_dev_socket_id(port), &tx_conf_default);
322                 if (retval < 0)
323                         return retval;
324         }
325
326         retval  = rte_eth_dev_start(port);
327         if (retval < 0)
328                 return retval;
329
330         rte_eth_macaddr_get(port, &vmdq_ports_eth_addr[port]);
331         printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
332                         " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
333                         (unsigned)port,
334                         vmdq_ports_eth_addr[port].addr_bytes[0], 
335                         vmdq_ports_eth_addr[port].addr_bytes[1], 
336                         vmdq_ports_eth_addr[port].addr_bytes[2],
337                         vmdq_ports_eth_addr[port].addr_bytes[3], 
338                         vmdq_ports_eth_addr[port].addr_bytes[4], 
339                         vmdq_ports_eth_addr[port].addr_bytes[5]);
340
341         return 0;
342 }
343
344 /* Check num_pools parameter and set it if OK*/
345 static int
346 vmdq_parse_num_pools(const char *q_arg)
347 {
348         char *end = NULL;
349         int n;
350  
351         /* parse number string */
352         n = strtol(q_arg, &end, 10);
353         if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
354                 return -1;
355
356         num_pools = n;
357
358         return 0;
359 }
360
361
362 static int
363 parse_portmask(const char *portmask)
364 {
365         char *end = NULL;
366         unsigned long pm;
367
368         /* parse hexadecimal string */
369         pm = strtoul(portmask, &end, 16);
370         if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
371                 return -1;
372
373         if (pm == 0)
374                 return -1;
375
376         return pm;
377 }
378
379 /* Display usage */
380 static void
381 vmdq_usage(const char *prgname)
382 {
383         printf("%s [EAL options] -- -p PORTMASK]\n"
384         "  --nb-pools NP: number of pools\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                 {NULL, 0, 0, 0}
399         };
400
401         /* Parse command line */
402         while ((opt = getopt_long(argc, argv, "p:",long_option,&option_index)) != EOF) {
403                 switch (opt) {
404                 /* portmask */
405                 case 'p':
406                         enabled_port_mask = parse_portmask(optarg);
407                         if (enabled_port_mask == 0) {
408                                 printf("invalid portmask\n");
409                                 vmdq_usage(prgname);
410                                 return -1;
411                         }
412                         break;
413                 case 0:
414                         if (vmdq_parse_num_pools(optarg) == -1){
415                                 printf("invalid number of pools\n");
416                                 vmdq_usage(prgname);
417                                 return -1;
418                         }
419                         break;
420
421                 default:
422                         vmdq_usage(prgname);
423                         return -1;
424                 }
425         }
426
427         for(i = 0; i < RTE_MAX_ETHPORTS; i++) {
428                 if (enabled_port_mask & (1 << i))
429                         ports[num_ports++] = (uint8_t)i;
430         }
431
432         if (num_ports < 2 || num_ports % 2) {
433                 printf("Current enabled port number is %u,"
434                         "but it should be even and at least 2\n",num_ports);
435                 return -1;
436         }
437
438         return 0;
439 }
440
441 static void
442 update_mac_address(struct rte_mbuf *m, unsigned dst_port)
443 {
444         struct ether_hdr *eth;
445         void *tmp;
446  
447         eth = rte_pktmbuf_mtod(m, struct ether_hdr *);
448  
449         /* 02:00:00:00:00:xx */
450         tmp = &eth->d_addr.addr_bytes[0];
451         *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
452  
453         /* src addr */
454         ether_addr_copy(&vmdq_ports_eth_addr[dst_port], &eth->s_addr);
455 }
456
457 #ifndef RTE_EXEC_ENV_BAREMETAL
458 /* When we receive a HUP signal, print out our stats */
459 static void
460 sighup_handler(int signum)
461 {
462         unsigned q;
463         for (q = 0; q < num_queues; q ++) {
464                 if (q % (num_queues/num_pools) == 0)
465                         printf("\nPool %u: ", q/(num_queues/num_pools));
466                 printf("%lu ", rxPackets[ q ]);
467         }
468         printf("\nFinished handling signal %d\n", signum);
469 }
470 #endif
471
472 /*
473  * Main thread that does the work, reading from INPUT_PORT
474  * and writing to OUTPUT_PORT
475  */
476 static int
477 lcore_main(__attribute__((__unused__)) void* dummy)
478 {
479         const uint16_t lcore_id = (uint16_t)rte_lcore_id();
480         const uint16_t num_cores = (uint16_t)rte_lcore_count();
481         uint16_t core_id = 0;
482         uint16_t startQueue, endQueue;
483         uint16_t q, i, p;
484         const uint16_t remainder = (uint16_t)(num_queues % num_cores);
485
486         for (i = 0; i < num_cores; i ++)
487                 if (lcore_ids[i] == lcore_id) {
488                         core_id = i;
489                         break;
490                 }
491
492         if (remainder != 0) {
493                 if (core_id < remainder) {
494                         startQueue = (uint16_t)(core_id * (num_queues/num_cores + 1));
495                         endQueue = (uint16_t)(startQueue + (num_queues/num_cores) + 1);
496                 } else {
497                         startQueue = (uint16_t)(core_id * (num_queues/num_cores) + remainder);
498                         endQueue = (uint16_t)(startQueue + (num_queues/num_cores));
499                 }
500         } else {
501                 startQueue = (uint16_t)(core_id * (num_queues/num_cores));
502                 endQueue = (uint16_t)(startQueue + (num_queues/num_cores));
503         }
504
505         printf("core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_id, 
506                 (unsigned)lcore_id, startQueue, endQueue - 1);
507
508         if (startQueue == endQueue) {
509                 printf("lcore %u has nothing to do\n", lcore_id);
510                 return (0);
511         }
512
513         for (;;) {
514                 struct rte_mbuf *buf[MAX_PKT_BURST];
515                 const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]);
516
517                 for (p = 0; p < num_ports; p++) {
518                         const uint8_t sport = ports[p];
519                         const uint8_t dport = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
520
521                         if ((sport == INVALID_PORT_ID) || (dport == INVALID_PORT_ID)) 
522                                 continue;
523
524                         for (q = startQueue; q < endQueue; q++) {
525                                 const uint16_t rxCount = rte_eth_rx_burst(sport,
526                                         q, buf, buf_size);
527
528                                 if (unlikely(rxCount == 0))
529                                         continue;
530
531                                 rxPackets[q] += rxCount;
532
533                                 for (i = 0; i < rxCount; i++)
534                                         update_mac_address(buf[i], dport);
535
536                                 const uint16_t txCount = rte_eth_tx_burst(dport,
537                                         lcore_id, buf, rxCount);
538
539                                 if (txCount != rxCount) {
540                                         for (i = txCount; i < rxCount; i++)
541                                                 rte_pktmbuf_free(buf[i]);
542                                 }
543                         }
544                 }
545         }
546 }
547
548 /* 
549  * Update the global var NUM_PORTS and array PORTS according to system ports number
550  * and return valid ports number
551  */     
552 static unsigned check_ports_num(unsigned nb_ports)
553 {
554         unsigned valid_num_ports = num_ports;
555         unsigned portid;
556
557         if (num_ports > nb_ports) {
558                 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
559                         num_ports, nb_ports);
560                 num_ports = nb_ports;
561         }       
562
563         for (portid = 0; portid < num_ports; portid ++) {
564                 if (ports[portid] >= nb_ports) {
565                         printf("\nSpecified port ID(%u) exceeds max system port ID(%u)\n",
566                                 ports[portid], (nb_ports - 1));
567                         ports[portid] = INVALID_PORT_ID;
568                         valid_num_ports --;
569                 }
570         }
571         return valid_num_ports;
572 }
573
574 /* Main function, does initialisation and calls the per-lcore functions */
575 int
576 MAIN(int argc, char *argv[])
577 {
578         struct rte_mempool *mbuf_pool;
579         unsigned lcore_id, core_id = 0;
580         int ret;
581         unsigned nb_ports, valid_num_ports;
582         uint8_t portid;
583
584 #ifndef RTE_EXEC_ENV_BAREMETAL
585         signal(SIGHUP, sighup_handler);
586 #endif
587
588         /* init EAL */
589         ret = rte_eal_init(argc, argv);
590         if (ret < 0)
591                 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
592         argc -= ret;
593         argv += ret;
594
595         /* parse app arguments */
596         ret = vmdq_parse_args(argc, argv);
597         if (ret < 0)
598                 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
599
600         if (rte_eal_pci_probe() != 0)
601                 rte_exit(EXIT_FAILURE, "Error with NIC driver initialization\n");
602         
603         for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id ++) 
604                 if (rte_lcore_is_enabled(lcore_id)) 
605                         lcore_ids[core_id ++] = lcore_id;
606         
607         if (rte_lcore_count() > RTE_MAX_LCORE) 
608                 rte_exit(EXIT_FAILURE,"Not enough cores\n");
609         
610         nb_ports = rte_eth_dev_count();
611         if (nb_ports > RTE_MAX_ETHPORTS)
612                 nb_ports = RTE_MAX_ETHPORTS;
613
614         /* 
615          * Update the global var NUM_PORTS and global array PORTS 
616          * and get value of var VALID_NUM_PORTS according to system ports number 
617          */     
618         valid_num_ports = check_ports_num(nb_ports);    
619
620         if (valid_num_ports < 2 || valid_num_ports % 2) {
621                 printf("Current valid ports number is %u\n", valid_num_ports);
622                 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
623         }
624
625         mbuf_pool = rte_mempool_create("MBUF_POOL", NUM_MBUFS_PER_PORT * nb_ports,
626                                        MBUF_SIZE, MBUF_CACHE_SIZE,
627                                        sizeof(struct rte_pktmbuf_pool_private),
628                                        rte_pktmbuf_pool_init, NULL,
629                                        rte_pktmbuf_init, NULL,
630                                        rte_socket_id(), 0);
631         if (mbuf_pool == NULL)
632                 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
633
634         /* initialize all ports */
635         for (portid = 0; portid < nb_ports; portid++) {
636                 /* skip ports that are not enabled */
637                 if ((enabled_port_mask & (1 << portid)) == 0) {
638                         printf("\nSkipping disabled port %d\n", portid);
639                         continue;
640                 }
641                 if (port_init(portid, mbuf_pool) != 0) 
642                         rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
643         }
644
645         /* call lcore_main() on every lcore */
646         rte_eal_mp_remote_launch(lcore_main, NULL, CALL_MASTER);
647         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
648                 if (rte_eal_wait_lcore(lcore_id) < 0)
649                         return -1;
650         }
651
652         return 0;
653 }