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