xref: /dpdk/examples/vmdq_dcb/main.c (revision e9d48c0072d36eb6423b45fba4ec49d0def6c36f)
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 /* basic constants used in application */
76 #define NUM_QUEUES 128
77 
78 #define NUM_MBUFS 64*1024
79 #define MBUF_CACHE_SIZE 64
80 #define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
81 
82 #define INVALID_PORT_ID 0xFF
83 
84 /* mask of enabled ports */
85 static uint32_t enabled_port_mask = 0;
86 
87 /* number of pools (if user does not specify any, 16 by default */
88 static enum rte_eth_nb_pools num_pools = ETH_16_POOLS;
89 
90 /*
91  * RX and TX Prefetch, Host, and Write-back threshold values should be
92  * carefully set for optimal performance. Consult the network
93  * controller's datasheet and supporting DPDK documentation for guidance
94  * on how these parameters should be set.
95  */
96 /* Default configuration for rx and tx thresholds etc. */
97 static const struct rte_eth_rxconf rx_conf_default = {
98 	.rx_thresh = {
99 		.pthresh = 8,
100 		.hthresh = 8,
101 		.wthresh = 4,
102 	},
103 };
104 
105 /*
106  * These default values are optimized for use with the Intel(R) 82599 10 GbE
107  * Controller and the DPDK ixgbe PMD. Consider using other values for other
108  * network controllers and/or network drivers.
109  */
110 static const struct rte_eth_txconf tx_conf_default = {
111 	.tx_thresh = {
112 		.pthresh = 36,
113 		.hthresh = 0,
114 		.wthresh = 0,
115 	},
116 	.tx_free_thresh = 0, /* Use PMD default values */
117 	.tx_rs_thresh = 0, /* Use PMD default values */
118 };
119 
120 /* empty vmdq+dcb configuration structure. Filled in programatically */
121 static const struct rte_eth_conf vmdq_dcb_conf_default = {
122 	.rxmode = {
123 		.mq_mode        = ETH_MQ_RX_VMDQ_DCB,
124 		.split_hdr_size = 0,
125 		.header_split   = 0, /**< Header Split disabled */
126 		.hw_ip_checksum = 0, /**< IP checksum offload disabled */
127 		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
128 		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
129 	},
130 	.txmode = {
131 		.mq_mode = ETH_MQ_TX_NONE,
132 	},
133 	.rx_adv_conf = {
134 		/*
135 		 * should be overridden separately in code with
136 		 * appropriate values
137 		 */
138 		.vmdq_dcb_conf = {
139 			.nb_queue_pools = ETH_16_POOLS,
140 			.enable_default_pool = 0,
141 			.default_pool = 0,
142 			.nb_pool_maps = 0,
143 			.pool_map = {{0, 0},},
144 			.dcb_queue = {0},
145 		},
146 	},
147 };
148 
149 static uint8_t ports[RTE_MAX_ETHPORTS];
150 static unsigned num_ports = 0;
151 
152 /* array used for printing out statistics */
153 volatile unsigned long rxPackets[ NUM_QUEUES ] = {0};
154 
155 const uint16_t vlan_tags[] = {
156 	0,  1,  2,  3,  4,  5,  6,  7,
157 	8,  9, 10, 11,	12, 13, 14, 15,
158 	16, 17, 18, 19, 20, 21, 22, 23,
159 	24, 25, 26, 27, 28, 29, 30, 31
160 };
161 
162 /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array
163  * given above, and the number of traffic classes available for use. */
164 static inline int
165 get_eth_conf(struct rte_eth_conf *eth_conf, enum rte_eth_nb_pools num_pools)
166 {
167 	struct rte_eth_vmdq_dcb_conf conf;
168 	unsigned i;
169 
170 	if (num_pools != ETH_16_POOLS && num_pools != ETH_32_POOLS ) return -1;
171 
172 	conf.nb_queue_pools = num_pools;
173 	conf.enable_default_pool = 0;
174 	conf.default_pool = 0; /* set explicit value, even if not used */
175 	conf.nb_pool_maps = sizeof( vlan_tags )/sizeof( vlan_tags[ 0 ]);
176 	for (i = 0; i < conf.nb_pool_maps; i++){
177 		conf.pool_map[i].vlan_id = vlan_tags[ i ];
178 		conf.pool_map[i].pools = 1 << (i % num_pools);
179 	}
180 	for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){
181 		conf.dcb_queue[i] = (uint8_t)(i % (NUM_QUEUES/num_pools));
182 	}
183 	(void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf)));
184 	(void)(rte_memcpy(&eth_conf->rx_adv_conf.vmdq_dcb_conf, &conf,
185 		   sizeof(eth_conf->rx_adv_conf.vmdq_dcb_conf)));
186 	return 0;
187 }
188 
189 /*
190  * Initialises a given port using global settings and with the rx buffers
191  * coming from the mbuf_pool passed as parameter
192  */
193 static inline int
194 port_init(uint8_t port, struct rte_mempool *mbuf_pool)
195 {
196 	struct rte_eth_conf port_conf;
197 	const uint16_t rxRings = ETH_VMDQ_DCB_NUM_QUEUES,
198 		txRings = (uint16_t)rte_lcore_count();
199 	const uint16_t rxRingSize = 128, txRingSize = 512;
200 	int retval;
201 	uint16_t q;
202 
203 	retval = get_eth_conf(&port_conf, num_pools);
204 	if (retval < 0)
205 		return retval;
206 
207 	if (port >= rte_eth_dev_count()) return -1;
208 
209 	retval = rte_eth_dev_configure(port, rxRings, txRings, &port_conf);
210 	if (retval != 0)
211 		return retval;
212 
213 	for (q = 0; q < rxRings; q ++) {
214 		retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
215 						rte_eth_dev_socket_id(port), &rx_conf_default,
216 						mbuf_pool);
217 		if (retval < 0)
218 			return retval;
219 	}
220 
221 	for (q = 0; q < txRings; q ++) {
222 		retval = rte_eth_tx_queue_setup(port, q, txRingSize,
223 						rte_eth_dev_socket_id(port), &tx_conf_default);
224 		if (retval < 0)
225 			return retval;
226 	}
227 
228 	retval  = rte_eth_dev_start(port);
229 	if (retval < 0)
230 		return retval;
231 
232 	struct ether_addr addr;
233 	rte_eth_macaddr_get(port, &addr);
234 	printf("Port %u MAC: %02"PRIx8" %02"PRIx8" %02"PRIx8
235 			" %02"PRIx8" %02"PRIx8" %02"PRIx8"\n",
236 			(unsigned)port,
237 			addr.addr_bytes[0], addr.addr_bytes[1], addr.addr_bytes[2],
238 			addr.addr_bytes[3], addr.addr_bytes[4], addr.addr_bytes[5]);
239 
240 	return 0;
241 }
242 
243 /* Check num_pools parameter and set it if OK*/
244 static int
245 vmdq_parse_num_pools(const char *q_arg)
246 {
247 	char *end = NULL;
248 	int n;
249 
250 	/* parse number string */
251 	n = strtol(q_arg, &end, 10);
252 	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
253 		return -1;
254 	if (n != 16 && n != 32)
255 		return -1;
256 	if (n == 16)
257 		num_pools = ETH_16_POOLS;
258 	else
259 		num_pools = ETH_32_POOLS;
260 
261 	return 0;
262 }
263 
264 static int
265 parse_portmask(const char *portmask)
266 {
267 	char *end = NULL;
268 	unsigned long pm;
269 
270 	/* parse hexadecimal string */
271 	pm = strtoul(portmask, &end, 16);
272 	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
273 		return -1;
274 
275 	if (pm == 0)
276 		return -1;
277 
278 	return pm;
279 }
280 
281 /* Display usage */
282 static void
283 vmdq_usage(const char *prgname)
284 {
285 	printf("%s [EAL options] -- -p PORTMASK]\n"
286 	       "  --nb-pools NP: number of pools (16 default, 32)\n",
287 	       prgname);
288 }
289 
290 /*  Parse the argument (num_pools) given in the command line of the application */
291 static int
292 vmdq_parse_args(int argc, char **argv)
293 {
294 	int opt;
295 	int option_index;
296 	unsigned i;
297 	const char *prgname = argv[0];
298 	static struct option long_option[] = {
299 		{"nb-pools", required_argument, NULL, 0},
300 		{NULL, 0, 0, 0}
301 	};
302 
303 	/* Parse command line */
304 	while ((opt = getopt_long(argc, argv, "p:",long_option,&option_index)) != EOF) {
305 		switch (opt) {
306 		/* portmask */
307 		case 'p':
308 			enabled_port_mask = parse_portmask(optarg);
309 			if (enabled_port_mask == 0) {
310 				printf("invalid portmask\n");
311 				vmdq_usage(prgname);
312 				return -1;
313 			}
314 			break;
315 		case 0:
316 			if (vmdq_parse_num_pools(optarg) == -1){
317 				printf("invalid number of pools\n");
318 				vmdq_usage(prgname);
319 				return -1;
320 			}
321 			break;
322 		default:
323 			vmdq_usage(prgname);
324 			return -1;
325 		}
326 	}
327 
328 	for(i = 0; i < RTE_MAX_ETHPORTS; i++)
329 	{
330 		if (enabled_port_mask & (1 << i))
331 			ports[num_ports++] = (uint8_t)i;
332 	}
333 
334 	if (num_ports < 2 || num_ports % 2) {
335 		printf("Current enabled port number is %u,"
336 			"but it should be even and at least 2\n",num_ports);
337 		return -1;
338 	}
339 
340 	return 0;
341 }
342 
343 
344 #ifndef RTE_EXEC_ENV_BAREMETAL
345 /* When we receive a HUP signal, print out our stats */
346 static void
347 sighup_handler(int signum)
348 {
349 	unsigned q;
350 	for (q = 0; q < NUM_QUEUES; q ++) {
351 		if (q % (NUM_QUEUES/num_pools) == 0)
352 			printf("\nPool %u: ", q/(NUM_QUEUES/num_pools));
353 		printf("%lu ", rxPackets[ q ]);
354 	}
355 	printf("\nFinished handling signal %d\n", signum);
356 }
357 #endif
358 
359 /*
360  * Main thread that does the work, reading from INPUT_PORT
361  * and writing to OUTPUT_PORT
362  */
363 static  __attribute__((noreturn)) int
364 lcore_main(void *arg)
365 {
366 	const uintptr_t core_num = (uintptr_t)arg;
367 	const unsigned num_cores = rte_lcore_count();
368 	uint16_t startQueue = (uint16_t)(core_num * (NUM_QUEUES/num_cores));
369 	uint16_t endQueue = (uint16_t)(startQueue + (NUM_QUEUES/num_cores));
370 	uint16_t q, i, p;
371 
372 	printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num,
373 	       rte_lcore_id(), startQueue, endQueue - 1);
374 
375 	for (;;) {
376 		struct rte_mbuf *buf[32];
377 		const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]);
378 		for (p = 0; p < num_ports; p++) {
379 			const uint8_t src = ports[p];
380 			const uint8_t dst = ports[p ^ 1]; /* 0 <-> 1, 2 <-> 3 etc */
381 
382 			if ((src == INVALID_PORT_ID) || (dst == INVALID_PORT_ID))
383 				continue;
384 
385 			for (q = startQueue; q < endQueue; q++) {
386 				const uint16_t rxCount = rte_eth_rx_burst(src,
387 					q, buf, buf_size);
388 				if (rxCount == 0)
389 					continue;
390 				rxPackets[q] += rxCount;
391 
392 				const uint16_t txCount = rte_eth_tx_burst(dst,
393 					(uint16_t)core_num, buf, rxCount);
394 				if (txCount != rxCount) {
395 					for (i = txCount; i < rxCount; i++)
396 						rte_pktmbuf_free(buf[i]);
397 				}
398 			}
399 		}
400 	}
401 }
402 
403 /*
404  * Update the global var NUM_PORTS and array PORTS according to system ports number
405  * and return valid ports number
406  */
407 static unsigned check_ports_num(unsigned nb_ports)
408 {
409 	unsigned valid_num_ports = num_ports;
410 	unsigned portid;
411 
412 	if (num_ports > nb_ports) {
413 		printf("\nSpecified port number(%u) exceeds total system port number(%u)\n",
414 			num_ports, nb_ports);
415 		num_ports = nb_ports;
416 	}
417 
418 	for (portid = 0; portid < num_ports; portid ++) {
419 		if (ports[portid] >= nb_ports) {
420 			printf("\nSpecified port ID(%u) exceeds max system port ID(%u)\n",
421 				ports[portid], (nb_ports - 1));
422 			ports[portid] = INVALID_PORT_ID;
423 			valid_num_ports --;
424 		}
425 	}
426 	return valid_num_ports;
427 }
428 
429 
430 /* Main function, does initialisation and calls the per-lcore functions */
431 int
432 MAIN(int argc, char *argv[])
433 {
434 	unsigned cores;
435 	struct rte_mempool *mbuf_pool;
436 	unsigned lcore_id;
437 	uintptr_t i;
438 	int ret;
439 	unsigned nb_ports, valid_num_ports;
440 	uint8_t portid;
441 
442 #ifndef RTE_EXEC_ENV_BAREMETAL
443 	signal(SIGHUP, sighup_handler);
444 #endif
445 
446 	/* init EAL */
447 	ret = rte_eal_init(argc, argv);
448 	if (ret < 0)
449 		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
450 	argc -= ret;
451 	argv += ret;
452 
453 	/* parse app arguments */
454 	ret = vmdq_parse_args(argc, argv);
455 	if (ret < 0)
456 		rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
457 
458 	if (rte_ixgbe_pmd_init() != 0 ||
459 			rte_eal_pci_probe() != 0)
460 		rte_exit(EXIT_FAILURE, "Error with NIC driver initialization\n");
461 
462 	cores = rte_lcore_count();
463 	if ((cores & (cores - 1)) != 0 || cores > 128) {
464 		rte_exit(EXIT_FAILURE,"This program can only run on an even"
465 				"number of cores(1-128)\n\n");
466 	}
467 
468 	nb_ports = rte_eth_dev_count();
469 	if (nb_ports > RTE_MAX_ETHPORTS)
470 		nb_ports = RTE_MAX_ETHPORTS;
471 
472         /*
473 	 * Update the global var NUM_PORTS and global array PORTS
474 	 * and get value of var VALID_NUM_PORTS according to system ports number
475 	 */
476 	valid_num_ports = check_ports_num(nb_ports);
477 
478 	if (valid_num_ports < 2 || valid_num_ports % 2) {
479 		printf("Current valid ports number is %u\n", valid_num_ports);
480 		rte_exit(EXIT_FAILURE, "Error with valid ports number is not even or less than 2\n");
481 	}
482 
483 	mbuf_pool = rte_mempool_create("MBUF_POOL", NUM_MBUFS * nb_ports,
484 				       MBUF_SIZE, MBUF_CACHE_SIZE,
485 				       sizeof(struct rte_pktmbuf_pool_private),
486 				       rte_pktmbuf_pool_init, NULL,
487 				       rte_pktmbuf_init, NULL,
488 				       rte_socket_id(), 0);
489 	if (mbuf_pool == NULL)
490 		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
491 
492 	/* initialize all ports */
493 	for (portid = 0; portid < nb_ports; portid++) {
494 		/* skip ports that are not enabled */
495 		if ((enabled_port_mask & (1 << portid)) == 0) {
496 			printf("\nSkipping disabled port %d\n", portid);
497 			continue;
498 		}
499 		if (port_init(portid, mbuf_pool) != 0)
500 			rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
501 	}
502 
503 	/* call lcore_main() on every slave lcore */
504 	i = 0;
505 	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
506 		rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id);
507 	}
508 	/* call on master too */
509 	(void) lcore_main((void*)i);
510 
511 	return 0;
512 }
513