update Intel copyright years to 2014
[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 };
134
135 /*
136  * These default values are optimized for use with the Intel(R) 82599 10 GbE
137  * Controller and the DPDK ixgbe/igb PMD. Consider using other values for other
138  * network controllers and/or network drivers.
139  */
140 static const struct rte_eth_txconf tx_conf_default = {
141         .tx_thresh = {
142                 .pthresh = TX_PTHRESH,
143                 .hthresh = TX_HTHRESH,
144                 .wthresh = TX_WTHRESH,
145         },
146         .tx_free_thresh = 0, /* Use PMD default values */
147         .tx_rs_thresh = 0, /* Use PMD default values */
148 };
149
150 /* empty vmdq configuration structure. Filled in programatically */
151 static const struct rte_eth_conf vmdq_conf_default = {
152         .rxmode = {
153                 .mq_mode        = ETH_MQ_RX_VMDQ_ONLY,
154                 .split_hdr_size = 0,
155                 .header_split   = 0, /**< Header Split disabled */
156                 .hw_ip_checksum = 0, /**< IP checksum offload disabled */
157                 .hw_vlan_filter = 0, /**< VLAN filtering disabled */
158                 .jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
159         },
160
161         .txmode = {
162                 .mq_mode = ETH_MQ_TX_NONE,
163         },
164         .rx_adv_conf = {
165                 /*
166                  * should be overridden separately in code with
167                  * appropriate values
168                  */
169                 .vmdq_rx_conf = {
170                         .nb_queue_pools = ETH_8_POOLS,
171                         .enable_default_pool = 0,
172                         .default_pool = 0,
173                         .nb_pool_maps = 0,
174                         .pool_map = {{0, 0},},
175                 },
176         },
177 };
178
179 static unsigned lcore_ids[RTE_MAX_LCORE];
180 static uint8_t ports[RTE_MAX_ETHPORTS];
181 static unsigned num_ports = 0; /**< The number of ports specified in command line */
182
183 /* array used for printing out statistics */
184 volatile unsigned long rxPackets[ MAX_QUEUES ] = {0};
185
186 const uint16_t vlan_tags[] = {
187         0,  1,  2,  3,  4,  5,  6,  7,
188         8,  9, 10, 11,  12, 13, 14, 15,
189         16, 17, 18, 19, 20, 21, 22, 23,
190         24, 25, 26, 27, 28, 29, 30, 31,
191         32, 33, 34, 35, 36, 37, 38, 39,
192         40, 41, 42, 43, 44, 45, 46, 47,
193         48, 49, 50, 51, 52, 53, 54, 55,
194         56, 57, 58, 59, 60, 61, 62, 63,
195 };
196
197 /* ethernet addresses of ports */
198 static struct ether_addr vmdq_ports_eth_addr[RTE_MAX_ETHPORTS];
199
200 #define MAX_QUEUE_NUM_10G 128
201 #define MAX_QUEUE_NUM_1G 8
202 #define MAX_POOL_MAP_NUM_10G 64
203 #define MAX_POOL_MAP_NUM_1G 32
204 #define MAX_POOL_NUM_10G 64
205 #define MAX_POOL_NUM_1G 8
206 /* Builds up the correct configuration for vmdq based on the vlan tags array
207  * given above, and determine the queue number and pool map number according to valid pool number */
208 static inline int
209 get_eth_conf(struct rte_eth_conf *eth_conf, uint32_t num_pools)
210 {
211         struct rte_eth_vmdq_rx_conf conf;
212         unsigned i;
213
214         conf.nb_queue_pools = (enum rte_eth_nb_pools)num_pools;
215         conf.enable_default_pool = 0;
216         conf.default_pool = 0; /* set explicit value, even if not used */
217         switch (num_pools) {
218         /* For 10G NIC like 82599, 128 is valid for queue number */
219         case MAX_POOL_NUM_10G:
220                 num_queues = MAX_QUEUE_NUM_10G;
221                 conf.nb_pool_maps = MAX_POOL_MAP_NUM_10G;
222                 break;
223         /* For 1G NIC like i350, 82580 and 82576, 8 is valid for queue number */
224         case MAX_POOL_NUM_1G:
225                 num_queues = MAX_QUEUE_NUM_1G;
226                 conf.nb_pool_maps = MAX_POOL_MAP_NUM_1G;
227                 break;
228         default:
229                 return -1;
230         }
231
232         for (i = 0; i < conf.nb_pool_maps; i++){
233                 conf.pool_map[i].vlan_id = vlan_tags[ i ];
234                 conf.pool_map[i].pools = (1UL << (i % num_pools));
235         }
236
237         (void)(rte_memcpy(eth_conf, &vmdq_conf_default, sizeof(*eth_conf)));
238         (void)(rte_memcpy(&eth_conf->rx_adv_conf.vmdq_rx_conf, &conf,
239                    sizeof(eth_conf->rx_adv_conf.vmdq_rx_conf)));
240         return 0;
241 }
242
243 /*
244  * Validate the pool number accrording to the max pool number gotten form dev_info
245  * If the pool number is invalid, give the error message and return -1 
246  */
247 static inline int
248 validate_num_pools(uint32_t max_nb_pools)
249 {
250         if (num_pools > max_nb_pools) {
251                 printf("invalid number of pools\n");
252                 return -1;
253         }
254
255         switch (max_nb_pools) {
256         /* For 10G NIC like 82599, 64 is valid for pool number */
257         case MAX_POOL_NUM_10G:
258                 if (num_pools != MAX_POOL_NUM_10G) {
259                         printf("invalid number of pools\n");
260                         return -1;
261                 }
262                 break;
263         /* For 1G NIC like i350, 82580 and 82576, 8 is valid for pool number */
264         case MAX_POOL_NUM_1G:
265                 if (num_pools != MAX_POOL_NUM_1G) {
266                         printf("invalid number of pools\n");
267                         return -1;
268                 }
269                 break;
270         default:
271                 return -1;
272         }
273
274         return 0;
275 }
276
277 /*
278  * Initialises a given port using global settings and with the rx buffers
279  * coming from the mbuf_pool passed as parameter
280  */
281 static inline int
282 port_init(uint8_t port, struct rte_mempool *mbuf_pool)
283 {
284         struct rte_eth_dev_info dev_info;
285         struct rte_eth_conf port_conf;
286         uint16_t rxRings, txRings = (uint16_t)rte_lcore_count();
287         const uint16_t rxRingSize = RTE_TEST_RX_DESC_DEFAULT, txRingSize = RTE_TEST_TX_DESC_DEFAULT;
288         int retval;
289         uint16_t q;
290         uint32_t max_nb_pools;
291
292         /* The max pool number from dev_info will be used to validate the pool number specified in cmd line */
293         rte_eth_dev_info_get (port, &dev_info);
294         max_nb_pools = (uint32_t)dev_info.max_vmdq_pools;
295         retval = validate_num_pools(max_nb_pools);
296         if (retval < 0)
297                 return retval;
298
299         retval = get_eth_conf(&port_conf, num_pools);
300         if (retval < 0)
301                 return retval;
302
303         if (port >= rte_eth_dev_count()) return -1;
304
305         rxRings = (uint16_t)num_queues,
306         retval = rte_eth_dev_configure(port, rxRings, txRings, &port_conf);
307         if (retval != 0)
308                 return retval;
309
310         for (q = 0; q < rxRings; q ++) {
311                 retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
312                                                 rte_eth_dev_socket_id(port), &rx_conf_default,
313                                                 mbuf_pool);
314                 if (retval < 0)
315                         return retval;
316         }
317
318         for (q = 0; q < txRings; q ++) {
319                 retval = rte_eth_tx_queue_setup(port, q, txRingSize,
320                                                 rte_eth_dev_socket_id(port), &tx_conf_default);
321                 if (retval < 0)
322                         return retval;
323         }
324
325         retval  = rte_eth_dev_start(port);
326         if (retval < 0)
327                 return retval;
328
329         rte_eth_macaddr_get(port, &vmdq_ports_eth_addr[port]);
330         printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
331                         " %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
332                         (unsigned)port,
333                         vmdq_ports_eth_addr[port].addr_bytes[0], 
334                         vmdq_ports_eth_addr[port].addr_bytes[1], 
335                         vmdq_ports_eth_addr[port].addr_bytes[2],
336                         vmdq_ports_eth_addr[port].addr_bytes[3], 
337                         vmdq_ports_eth_addr[port].addr_bytes[4], 
338                         vmdq_ports_eth_addr[port].addr_bytes[5]);
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
355         num_pools = n;
356
357         return 0;
358 }
359
360
361 static int
362 parse_portmask(const char *portmask)
363 {
364         char *end = NULL;
365         unsigned long pm;
366
367         /* parse hexadecimal string */
368         pm = strtoul(portmask, &end, 16);
369         if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
370                 return -1;
371
372         if (pm == 0)
373                 return -1;
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                prgname);
385 }
386
387 /*  Parse the argument (num_pools) given in the command line of the application */
388 static int
389 vmdq_parse_args(int argc, char **argv)
390 {
391         int opt;
392         int option_index;
393         unsigned i;
394         const char *prgname = argv[0];
395         static struct option long_option[] = {
396                 {"nb-pools", required_argument, NULL, 0},
397                 {NULL, 0, 0, 0}
398         };
399
400         /* Parse command line */
401         while ((opt = getopt_long(argc, argv, "p:",long_option,&option_index)) != EOF) {
402                 switch (opt) {
403                 /* portmask */
404                 case 'p':
405                         enabled_port_mask = parse_portmask(optarg);
406                         if (enabled_port_mask == 0) {
407                                 printf("invalid portmask\n");
408                                 vmdq_usage(prgname);
409                                 return -1;
410                         }
411                         break;
412                 case 0:
413                         if (vmdq_parse_num_pools(optarg) == -1){
414                                 printf("invalid number of pools\n");
415                                 vmdq_usage(prgname);
416                                 return -1;
417                         }
418                         break;
419
420                 default:
421                         vmdq_usage(prgname);
422                         return -1;
423                 }
424         }
425
426         for(i = 0; i < RTE_MAX_ETHPORTS; i++) {
427                 if (enabled_port_mask & (1 << i))
428                         ports[num_ports++] = (uint8_t)i;
429         }
430
431         if (num_ports < 2 || num_ports % 2) {
432                 printf("Current enabled port number is %u,"
433                         "but it should be even and at least 2\n",num_ports);
434                 return -1;
435         }
436
437         return 0;
438 }
439
440 static void
441 update_mac_address(struct rte_mbuf *m, unsigned dst_port)
442 {
443         struct ether_hdr *eth;
444         void *tmp;
445  
446         eth = rte_pktmbuf_mtod(m, struct ether_hdr *);
447  
448         /* 02:00:00:00:00:xx */
449         tmp = &eth->d_addr.addr_bytes[0];
450         *((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
451  
452         /* src addr */
453         ether_addr_copy(&vmdq_ports_eth_addr[dst_port], &eth->s_addr);
454 }
455
456 #ifndef RTE_EXEC_ENV_BAREMETAL
457 /* When we receive a HUP signal, print out our stats */
458 static void
459 sighup_handler(int signum)
460 {
461         unsigned q;
462         for (q = 0; q < num_queues; q ++) {
463                 if (q % (num_queues/num_pools) == 0)
464                         printf("\nPool %u: ", q/(num_queues/num_pools));
465                 printf("%lu ", rxPackets[ q ]);
466         }
467         printf("\nFinished handling signal %d\n", signum);
468 }
469 #endif
470
471 /*
472  * Main thread that does the work, reading from INPUT_PORT
473  * and writing to OUTPUT_PORT
474  */
475 static  __attribute__((noreturn)) int
476 lcore_main(__attribute__((__unused__)) void* dummy)
477 {
478         const uint16_t lcore_id = (uint16_t)rte_lcore_id();
479         const uint16_t num_cores = (uint16_t)rte_lcore_count();
480         uint16_t core_id = 0;
481         uint16_t startQueue, endQueue;
482         uint16_t q, i, p;
483         const uint16_t remainder = (uint16_t)(num_queues % num_cores);
484
485         for (i = 0; i < num_cores; i ++)
486                 if (lcore_ids[i] == lcore_id) {
487                         core_id = i;
488                         break;
489                 }
490
491         if (remainder != 0) {
492                 if (core_id < remainder) {
493                         startQueue = (uint16_t)(core_id * (num_queues/num_cores + 1));
494                         endQueue = (uint16_t)(startQueue + (num_queues/num_cores) + 1);
495                 } else {
496                         startQueue = (uint16_t)(core_id * (num_queues/num_cores) + remainder);
497                         endQueue = (uint16_t)(startQueue + (num_queues/num_cores));
498                 }
499         } else {
500                 startQueue = (uint16_t)(core_id * (num_queues/num_cores));
501                 endQueue = (uint16_t)(startQueue + (num_queues/num_cores));
502         }
503
504         printf("core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_id, 
505                 (unsigned)lcore_id, startQueue, endQueue - 1);
506
507         for (;;) {
508                 struct rte_mbuf *buf[MAX_PKT_BURST];
509                 const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]);
510
511                 for (p = 0; p < num_ports; p++) {
512                         const uint8_t sport = ports[p];
513                         const uint8_t dport = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
514
515                         if ((sport == INVALID_PORT_ID) || (dport == INVALID_PORT_ID)) 
516                                 continue;
517
518                         for (q = startQueue; q < endQueue; q++) {
519                                 const uint16_t rxCount = rte_eth_rx_burst(sport,
520                                         q, buf, buf_size);
521
522                                 if (unlikely(rxCount == 0))
523                                         continue;
524
525                                 rxPackets[q] += rxCount;
526
527                                 for (i = 0; i < rxCount; i++)
528                                         update_mac_address(buf[i], dport);
529
530                                 const uint16_t txCount = rte_eth_tx_burst(dport,
531                                         lcore_id, buf, rxCount);
532
533                                 if (txCount != rxCount) {
534                                         for (i = txCount; i < rxCount; i++)
535                                                 rte_pktmbuf_free(buf[i]);
536                                 }
537                         }
538                 }
539         }
540 }
541
542 /* 
543  * Update the global var NUM_PORTS and array PORTS according to system ports number
544  * and return valid ports number
545  */     
546 static unsigned check_ports_num(unsigned nb_ports)
547 {
548         unsigned valid_num_ports = num_ports;
549         unsigned portid;
550
551         if (num_ports > nb_ports) {
552                 printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
553                         num_ports, nb_ports);
554                 num_ports = nb_ports;
555         }       
556
557         for (portid = 0; portid < num_ports; portid ++) {
558                 if (ports[portid] >= nb_ports) {
559                         printf("\nSpecified port ID(%u) exceeds max system port ID(%u)\n",
560                                 ports[portid], (nb_ports - 1));
561                         ports[portid] = INVALID_PORT_ID;
562                         valid_num_ports --;
563                 }
564         }
565         return valid_num_ports;
566 }
567
568 /* Main function, does initialisation and calls the per-lcore functions */
569 int
570 MAIN(int argc, char *argv[])
571 {
572         struct rte_mempool *mbuf_pool;
573         unsigned lcore_id, core_id = 0;
574         int ret;
575         unsigned nb_ports, valid_num_ports;
576         uint8_t portid;
577
578 #ifndef RTE_EXEC_ENV_BAREMETAL
579         signal(SIGHUP, sighup_handler);
580 #endif
581
582         /* init EAL */
583         ret = rte_eal_init(argc, argv);
584         if (ret < 0)
585                 rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
586         argc -= ret;
587         argv += ret;
588
589         /* parse app arguments */
590         ret = vmdq_parse_args(argc, argv);
591         if (ret < 0)
592                 rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
593
594         if (rte_pmd_init_all() != 0 || rte_eal_pci_probe() != 0)
595                 rte_exit(EXIT_FAILURE, "Error with NIC driver initialization\n");
596         
597         for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id ++) 
598                 if (rte_lcore_is_enabled(lcore_id)) 
599                         lcore_ids[core_id ++] = lcore_id;
600         
601         if (rte_lcore_count() > RTE_MAX_LCORE) 
602                 rte_exit(EXIT_FAILURE,"Not enough cores\n");
603         
604         nb_ports = rte_eth_dev_count();
605         if (nb_ports > RTE_MAX_ETHPORTS)
606                 nb_ports = RTE_MAX_ETHPORTS;
607
608         /* 
609          * Update the global var NUM_PORTS and global array PORTS 
610          * and get value of var VALID_NUM_PORTS according to system ports number 
611          */     
612         valid_num_ports = check_ports_num(nb_ports);    
613
614         if (valid_num_ports < 2 || valid_num_ports % 2) {
615                 printf("Current valid ports number is %u\n", valid_num_ports);
616                 rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
617         }
618
619         mbuf_pool = rte_mempool_create("MBUF_POOL", NUM_MBUFS_PER_PORT * nb_ports,
620                                        MBUF_SIZE, MBUF_CACHE_SIZE,
621                                        sizeof(struct rte_pktmbuf_pool_private),
622                                        rte_pktmbuf_pool_init, NULL,
623                                        rte_pktmbuf_init, NULL,
624                                        rte_socket_id(), 0);
625         if (mbuf_pool == NULL)
626                 rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
627
628         /* initialize all ports */
629         for (portid = 0; portid < nb_ports; portid++) {
630                 /* skip ports that are not enabled */
631                 if ((enabled_port_mask & (1 << portid)) == 0) {
632                         printf("\nSkipping disabled port %d\n", portid);
633                         continue;
634                 }
635                 if (port_init(portid, mbuf_pool) != 0) 
636                         rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
637         }
638
639         /* call lcore_main() on every lcore */
640         rte_eal_mp_remote_launch(lcore_main, NULL, CALL_MASTER);
641         RTE_LCORE_FOREACH_SLAVE(lcore_id) {
642                 if (rte_eal_wait_lcore(lcore_id) < 0)
643                         return -1;
644         }
645
646         return 0;
647 }