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