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