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