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