xref: /dpdk/examples/vmdq/main.c (revision e5ffdd1457c0fb4e8365f524ee2529ac726edcf3)
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 }
654