xref: /dpdk/examples/vmdq_dcb/main.c (revision 60efb44f73c9a5f3f19cc78ba2d6eaf398ab51dc)
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 	uint16_t rxRingSize = RTE_TEST_RX_DESC_DEFAULT;
231 	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 	retval = rte_eth_dev_adjust_nb_rx_tx_desc(port, &rxRingSize,
303 				&txRingSize);
304 	if (retval != 0)
305 		return retval;
306 	if (RTE_MAX(rxRingSize, txRingSize) >
307 	    RTE_MAX(RTE_TEST_RX_DESC_DEFAULT, RTE_TEST_TX_DESC_DEFAULT)) {
308 		printf("Mbuf pool has an insufficient size for port %u.\n",
309 			port);
310 		return -1;
311 	}
312 
313 	for (q = 0; q < num_queues; q++) {
314 		retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
315 					rte_eth_dev_socket_id(port),
316 					NULL,
317 					mbuf_pool);
318 		if (retval < 0) {
319 			printf("initialize rx queue %d failed\n", q);
320 			return retval;
321 		}
322 	}
323 
324 	for (q = 0; q < num_queues; q++) {
325 		retval = rte_eth_tx_queue_setup(port, q, txRingSize,
326 					rte_eth_dev_socket_id(port),
327 					NULL);
328 		if (retval < 0) {
329 			printf("initialize tx queue %d failed\n", q);
330 			return retval;
331 		}
332 	}
333 
334 	retval  = rte_eth_dev_start(port);
335 	if (retval < 0) {
336 		printf("port %d start failed\n", port);
337 		return retval;
338 	}
339 
340 	rte_eth_macaddr_get(port, &vmdq_ports_eth_addr[port]);
341 	printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
342 			" %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
343 			(unsigned)port,
344 			vmdq_ports_eth_addr[port].addr_bytes[0],
345 			vmdq_ports_eth_addr[port].addr_bytes[1],
346 			vmdq_ports_eth_addr[port].addr_bytes[2],
347 			vmdq_ports_eth_addr[port].addr_bytes[3],
348 			vmdq_ports_eth_addr[port].addr_bytes[4],
349 			vmdq_ports_eth_addr[port].addr_bytes[5]);
350 
351 	/* Set mac for each pool.*/
352 	for (q = 0; q < num_pools; q++) {
353 		struct ether_addr mac;
354 
355 		mac = pool_addr_template;
356 		mac.addr_bytes[4] = port;
357 		mac.addr_bytes[5] = q;
358 		printf("Port %u vmdq pool %u set mac %02x:%02x:%02x:%02x:%02x:%02x\n",
359 			port, q,
360 			mac.addr_bytes[0], mac.addr_bytes[1],
361 			mac.addr_bytes[2], mac.addr_bytes[3],
362 			mac.addr_bytes[4], mac.addr_bytes[5]);
363 		retval = rte_eth_dev_mac_addr_add(port, &mac,
364 				q + vmdq_pool_base);
365 		if (retval) {
366 			printf("mac addr add failed at pool %d\n", q);
367 			return retval;
368 		}
369 	}
370 
371 	return 0;
372 }
373 
374 /* Check num_pools parameter and set it if OK*/
375 static int
376 vmdq_parse_num_pools(const char *q_arg)
377 {
378 	char *end = NULL;
379 	int n;
380 
381 	/* parse number string */
382 	n = strtol(q_arg, &end, 10);
383 	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
384 		return -1;
385 	if (n != 16 && n != 32)
386 		return -1;
387 	if (n == 16)
388 		num_pools = ETH_16_POOLS;
389 	else
390 		num_pools = ETH_32_POOLS;
391 
392 	return 0;
393 }
394 
395 /* Check num_tcs parameter and set it if OK*/
396 static int
397 vmdq_parse_num_tcs(const char *q_arg)
398 {
399 	char *end = NULL;
400 	int n;
401 
402 	/* parse number string */
403 	n = strtol(q_arg, &end, 10);
404 	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
405 		return -1;
406 
407 	if (n != 4 && n != 8)
408 		return -1;
409 	if (n == 4)
410 		num_tcs = ETH_4_TCS;
411 	else
412 		num_tcs = ETH_8_TCS;
413 
414 	return 0;
415 }
416 
417 static int
418 parse_portmask(const char *portmask)
419 {
420 	char *end = NULL;
421 	unsigned long pm;
422 
423 	/* parse hexadecimal string */
424 	pm = strtoul(portmask, &end, 16);
425 	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
426 		return -1;
427 
428 	if (pm == 0)
429 		return -1;
430 
431 	return pm;
432 }
433 
434 /* Display usage */
435 static void
436 vmdq_usage(const char *prgname)
437 {
438 	printf("%s [EAL options] -- -p PORTMASK]\n"
439 	"  --nb-pools NP: number of pools (32 default, 16)\n"
440 	"  --nb-tcs NP: number of TCs (4 default, 8)\n"
441 	"  --enable-rss: enable RSS (disabled by default)\n",
442 	       prgname);
443 }
444 
445 /*  Parse the argument (num_pools) given in the command line of the application */
446 static int
447 vmdq_parse_args(int argc, char **argv)
448 {
449 	int opt;
450 	int option_index;
451 	unsigned i;
452 	const char *prgname = argv[0];
453 	static struct option long_option[] = {
454 		{"nb-pools", required_argument, NULL, 0},
455 		{"nb-tcs", required_argument, NULL, 0},
456 		{"enable-rss", 0, NULL, 0},
457 		{NULL, 0, 0, 0}
458 	};
459 
460 	/* Parse command line */
461 	while ((opt = getopt_long(argc, argv, "p:", long_option,
462 		&option_index)) != EOF) {
463 		switch (opt) {
464 		/* portmask */
465 		case 'p':
466 			enabled_port_mask = parse_portmask(optarg);
467 			if (enabled_port_mask == 0) {
468 				printf("invalid portmask\n");
469 				vmdq_usage(prgname);
470 				return -1;
471 			}
472 			break;
473 		case 0:
474 			if (!strcmp(long_option[option_index].name, "nb-pools")) {
475 				if (vmdq_parse_num_pools(optarg) == -1) {
476 					printf("invalid number of pools\n");
477 					return -1;
478 				}
479 			}
480 
481 			if (!strcmp(long_option[option_index].name, "nb-tcs")) {
482 				if (vmdq_parse_num_tcs(optarg) == -1) {
483 					printf("invalid number of tcs\n");
484 					return -1;
485 				}
486 			}
487 
488 			if (!strcmp(long_option[option_index].name, "enable-rss"))
489 				rss_enable = 1;
490 			break;
491 
492 		default:
493 			vmdq_usage(prgname);
494 			return -1;
495 		}
496 	}
497 
498 	for (i = 0; i < RTE_MAX_ETHPORTS; i++) {
499 		if (enabled_port_mask & (1 << i))
500 			ports[num_ports++] = (uint8_t)i;
501 	}
502 
503 	if (num_ports < 2 || num_ports % 2) {
504 		printf("Current enabled port number is %u,"
505 			" but it should be even and at least 2\n", num_ports);
506 		return -1;
507 	}
508 
509 	return 0;
510 }
511 
512 static void
513 update_mac_address(struct rte_mbuf *m, unsigned dst_port)
514 {
515 	struct ether_hdr *eth;
516 	void *tmp;
517 
518 	eth = rte_pktmbuf_mtod(m, struct ether_hdr *);
519 
520 	/* 02:00:00:00:00:xx */
521 	tmp = &eth->d_addr.addr_bytes[0];
522 	*((uint64_t *)tmp) = 0x000000000002 + ((uint64_t)dst_port << 40);
523 
524 	/* src addr */
525 	ether_addr_copy(&vmdq_ports_eth_addr[dst_port], &eth->s_addr);
526 }
527 
528 /* When we receive a HUP signal, print out our stats */
529 static void
530 sighup_handler(int signum)
531 {
532 	unsigned q = vmdq_queue_base;
533 
534 	for (; q < num_queues; q++) {
535 		if (q % (num_vmdq_queues / num_pools) == 0)
536 			printf("\nPool %u: ", (q - vmdq_queue_base) /
537 					      (num_vmdq_queues / num_pools));
538 		printf("%lu ", rxPackets[q]);
539 	}
540 	printf("\nFinished handling signal %d\n", signum);
541 }
542 
543 /*
544  * Main thread that does the work, reading from INPUT_PORT
545  * and writing to OUTPUT_PORT
546  */
547 static int
548 lcore_main(void *arg)
549 {
550 	const uintptr_t core_num = (uintptr_t)arg;
551 	const unsigned num_cores = rte_lcore_count();
552 	uint16_t startQueue, endQueue;
553 	uint16_t q, i, p;
554 	const uint16_t quot = (uint16_t)(num_vmdq_queues / num_cores);
555 	const uint16_t remainder = (uint16_t)(num_vmdq_queues % num_cores);
556 
557 
558 	if (remainder) {
559 		if (core_num < remainder) {
560 			startQueue = (uint16_t)(core_num * (quot + 1));
561 			endQueue = (uint16_t)(startQueue + quot + 1);
562 		} else {
563 			startQueue = (uint16_t)(core_num * quot + remainder);
564 			endQueue = (uint16_t)(startQueue + quot);
565 		}
566 	} else {
567 		startQueue = (uint16_t)(core_num * quot);
568 		endQueue = (uint16_t)(startQueue + quot);
569 	}
570 
571 	/* vmdq queue idx doesn't always start from zero.*/
572 	startQueue += vmdq_queue_base;
573 	endQueue   += vmdq_queue_base;
574 	printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num,
575 	       rte_lcore_id(), startQueue, endQueue - 1);
576 
577 	if (startQueue == endQueue) {
578 		printf("lcore %u has nothing to do\n", (unsigned)core_num);
579 		return 0;
580 	}
581 
582 	for (;;) {
583 		struct rte_mbuf *buf[MAX_PKT_BURST];
584 		const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]);
585 		for (p = 0; p < num_ports; p++) {
586 			const uint8_t src = ports[p];
587 			const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
588 
589 			if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID))
590 				continue;
591 
592 			for (q = startQueue; q < endQueue; q++) {
593 				const uint16_t rxCount = rte_eth_rx_burst(src,
594 					q, buf, buf_size);
595 
596 				if (unlikely(rxCount == 0))
597 					continue;
598 
599 				rxPackets[q] += rxCount;
600 
601 				for (i = 0; i < rxCount; i++)
602 					update_mac_address(buf[i], dst);
603 
604 				const uint16_t txCount = rte_eth_tx_burst(dst,
605 					q, buf, rxCount);
606 				if (txCount != rxCount) {
607 					for (i = txCount; i < rxCount; i++)
608 						rte_pktmbuf_free(buf[i]);
609 				}
610 			}
611 		}
612 	}
613 }
614 
615 /*
616  * Update the global var NUM_PORTS and array PORTS according to system ports number
617  * and return valid ports number
618  */
619 static unsigned check_ports_num(unsigned nb_ports)
620 {
621 	unsigned valid_num_ports = num_ports;
622 	unsigned portid;
623 
624 	if (num_ports > nb_ports) {
625 		printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
626 			num_ports, nb_ports);
627 		num_ports = nb_ports;
628 	}
629 
630 	for (portid = 0; portid < num_ports; portid++) {
631 		if (ports[portid] >= nb_ports) {
632 			printf("\nSpecified port ID(%u) exceeds max system port ID(%u)\n",
633 				ports[portid], (nb_ports - 1));
634 			ports[portid] = INVALID_PORT_ID;
635 			valid_num_ports--;
636 		}
637 	}
638 	return valid_num_ports;
639 }
640 
641 
642 /* Main function, does initialisation and calls the per-lcore functions */
643 int
644 main(int argc, char *argv[])
645 {
646 	unsigned cores;
647 	struct rte_mempool *mbuf_pool;
648 	unsigned lcore_id;
649 	uintptr_t i;
650 	int ret;
651 	unsigned nb_ports, valid_num_ports;
652 	uint8_t portid;
653 
654 	signal(SIGHUP, sighup_handler);
655 
656 	/* init EAL */
657 	ret = rte_eal_init(argc, argv);
658 	if (ret < 0)
659 		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
660 	argc -= ret;
661 	argv += ret;
662 
663 	/* parse app arguments */
664 	ret = vmdq_parse_args(argc, argv);
665 	if (ret < 0)
666 		rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
667 
668 	cores = rte_lcore_count();
669 	if ((cores & (cores - 1)) != 0 || cores > RTE_MAX_LCORE) {
670 		rte_exit(EXIT_FAILURE,"This program can only run on an even"
671 				" number of cores(1-%d)\n\n", RTE_MAX_LCORE);
672 	}
673 
674 	nb_ports = rte_eth_dev_count();
675 
676 	/*
677 	 * Update the global var NUM_PORTS and global array PORTS
678 	 * and get value of var VALID_NUM_PORTS according to system ports number
679 	 */
680 	valid_num_ports = check_ports_num(nb_ports);
681 
682 	if (valid_num_ports < 2 || valid_num_ports % 2) {
683 		printf("Current valid ports number is %u\n", valid_num_ports);
684 		rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
685 	}
686 
687 	mbuf_pool = rte_pktmbuf_pool_create("MBUF_POOL",
688 		NUM_MBUFS_PER_PORT * nb_ports, MBUF_CACHE_SIZE,
689 		0, RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
690 	if (mbuf_pool == NULL)
691 		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
692 
693 	/* initialize all ports */
694 	for (portid = 0; portid < nb_ports; portid++) {
695 		/* skip ports that are not enabled */
696 		if ((enabled_port_mask & (1 << portid)) == 0) {
697 			printf("\nSkipping disabled port %d\n", portid);
698 			continue;
699 		}
700 		if (port_init(portid, mbuf_pool) != 0)
701 			rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
702 	}
703 
704 	/* call lcore_main() on every slave lcore */
705 	i = 0;
706 	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
707 		rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id);
708 	}
709 	/* call on master too */
710 	(void) lcore_main((void*)i);
711 
712 	return 0;
713 }
714