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