xref: /dpdk/examples/vmdq_dcb/main.c (revision e60f71ebd68f8b841bda43f210861e010ad5eb61)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright(c) 2010-2013 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 
35 #include <stdint.h>
36 #include <sys/queue.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <stdio.h>
40 #include <assert.h>
41 #include <errno.h>
42 #include <signal.h>
43 #include <stdarg.h>
44 #include <inttypes.h>
45 #include <getopt.h>
46 
47 #include <rte_common.h>
48 #include <rte_log.h>
49 #include <rte_memory.h>
50 #include <rte_memcpy.h>
51 #include <rte_memzone.h>
52 #include <rte_tailq.h>
53 #include <rte_eal.h>
54 #include <rte_per_lcore.h>
55 #include <rte_launch.h>
56 #include <rte_atomic.h>
57 #include <rte_cycles.h>
58 #include <rte_prefetch.h>
59 #include <rte_lcore.h>
60 #include <rte_per_lcore.h>
61 #include <rte_branch_prediction.h>
62 #include <rte_interrupts.h>
63 #include <rte_pci.h>
64 #include <rte_random.h>
65 #include <rte_debug.h>
66 #include <rte_ether.h>
67 #include <rte_ethdev.h>
68 #include <rte_ring.h>
69 #include <rte_log.h>
70 #include <rte_mempool.h>
71 #include <rte_mbuf.h>
72 #include <rte_memcpy.h>
73 
74 #include "main.h"
75 
76 /* basic constants used in application */
77 #define NUM_QUEUES 128
78 
79 #define NUM_MBUFS 64*1024
80 #define MBUF_CACHE_SIZE 64
81 #define MBUF_SIZE (2048 + sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM)
82 
83 #define RX_PORT 0
84 #define TX_PORT 1
85 
86 /* number of pools (if user does not specify any, 16 by default */
87 static enum rte_eth_nb_pools num_pools = ETH_16_POOLS;
88 
89 /*
90  * RX and TX Prefetch, Host, and Write-back threshold values should be
91  * carefully set for optimal performance. Consult the network
92  * controller's datasheet and supporting DPDK documentation for guidance
93  * on how these parameters should be set.
94  */
95 /* Default configuration for rx and tx thresholds etc. */
96 static const struct rte_eth_rxconf rx_conf_default = {
97 	.rx_thresh = {
98 		.pthresh = 8,
99 		.hthresh = 8,
100 		.wthresh = 4,
101 	},
102 };
103 
104 /*
105  * These default values are optimized for use with the Intel(R) 82599 10 GbE
106  * Controller and the DPDK ixgbe PMD. Consider using other values for other
107  * network controllers and/or network drivers.
108  */
109 static const struct rte_eth_txconf tx_conf_default = {
110 	.tx_thresh = {
111 		.pthresh = 36,
112 		.hthresh = 0,
113 		.wthresh = 0,
114 	},
115 	.tx_free_thresh = 0, /* Use PMD default values */
116 	.tx_rs_thresh = 0, /* Use PMD default values */
117 };
118 
119 /* empty vmdq+dcb configuration structure. Filled in programatically */
120 static const struct rte_eth_conf vmdq_dcb_conf_default = {
121 	.rxmode = {
122 		.mq_mode        = ETH_MQ_RX_VMDQ_DCB,
123 		.split_hdr_size = 0,
124 		.header_split   = 0, /**< Header Split disabled */
125 		.hw_ip_checksum = 0, /**< IP checksum offload disabled */
126 		.hw_vlan_filter = 0, /**< VLAN filtering disabled */
127 		.jumbo_frame    = 0, /**< Jumbo Frame Support disabled */
128 	},
129 	.txmode = {
130 		.mq_mode = ETH_MQ_TX_NONE,
131 	},
132 	.rx_adv_conf = {
133 		/*
134 		 * should be overridden separately in code with
135 		 * appropriate values
136 		 */
137 		.vmdq_dcb_conf = {
138 			.nb_queue_pools = ETH_16_POOLS,
139 			.enable_default_pool = 0,
140 			.default_pool = 0,
141 			.nb_pool_maps = 0,
142 			.pool_map = {{0, 0},},
143 			.dcb_queue = {0},
144 		},
145 	},
146 };
147 
148 /* array used for printing out statistics */
149 volatile unsigned long rxPackets[ NUM_QUEUES ] = {0};
150 
151 const uint16_t vlan_tags[] = {
152 	0,  1,  2,  3,  4,  5,  6,  7,
153 	8,  9, 10, 11,	12, 13, 14, 15,
154 	16, 17, 18, 19, 20, 21, 22, 23,
155 	24, 25, 26, 27, 28, 29, 30, 31
156 };
157 
158 /* Builds up the correct configuration for vmdq+dcb based on the vlan tags array
159  * given above, and the number of traffic classes available for use. */
160 static inline int
161 get_eth_conf(struct rte_eth_conf *eth_conf, enum rte_eth_nb_pools num_pools)
162 {
163 	struct rte_eth_vmdq_dcb_conf conf;
164 	unsigned i;
165 
166 	if (num_pools != ETH_16_POOLS && num_pools != ETH_32_POOLS ) return -1;
167 
168 	conf.nb_queue_pools = num_pools;
169 	conf.enable_default_pool = 0;
170 	conf.default_pool = 0; /* set explicit value, even if not used */
171 	conf.nb_pool_maps = sizeof( vlan_tags )/sizeof( vlan_tags[ 0 ]);
172 	for (i = 0; i < conf.nb_pool_maps; i++){
173 		conf.pool_map[i].vlan_id = vlan_tags[ i ];
174 		conf.pool_map[i].pools = 1 << (i % num_pools);
175 	}
176 	for (i = 0; i < ETH_DCB_NUM_USER_PRIORITIES; i++){
177 		conf.dcb_queue[i] = (uint8_t)(i % (NUM_QUEUES/num_pools));
178 	}
179 	(void)(rte_memcpy(eth_conf, &vmdq_dcb_conf_default, sizeof(*eth_conf)));
180 	(void)(rte_memcpy(&eth_conf->rx_adv_conf.vmdq_dcb_conf, &conf,
181 		   sizeof(eth_conf->rx_adv_conf.vmdq_dcb_conf)));
182 	return 0;
183 }
184 
185 /*
186  * Initialises a given port using global settings and with the rx buffers
187  * coming from the mbuf_pool passed as parameter
188  */
189 static inline int
190 port_init(uint8_t port, struct rte_mempool *mbuf_pool)
191 {
192 	struct rte_eth_conf port_conf;
193 	const uint16_t rxRings = ETH_VMDQ_DCB_NUM_QUEUES,
194 		txRings = (uint16_t)rte_lcore_count();
195 	const uint16_t rxRingSize = 128, txRingSize = 512;
196 	int retval;
197 	uint16_t q;
198 
199 	retval = get_eth_conf(&port_conf, num_pools);
200 	if (retval < 0)
201 		return retval;
202 
203 	if (port >= rte_eth_dev_count()) return -1;
204 
205 	retval = rte_eth_dev_configure(port, rxRings, txRings, &port_conf);
206 	if (retval != 0)
207 		return retval;
208 
209 	for (q = 0; q < rxRings; q ++) {
210 		retval = rte_eth_rx_queue_setup(port, q, rxRingSize,
211 						rte_eth_dev_socket_id(port), &rx_conf_default,
212 						mbuf_pool);
213 		if (retval < 0)
214 			return retval;
215 	}
216 
217 	for (q = 0; q < txRings; q ++) {
218 		retval = rte_eth_tx_queue_setup(port, q, txRingSize,
219 						rte_eth_dev_socket_id(port), &tx_conf_default);
220 		if (retval < 0)
221 			return retval;
222 	}
223 
224 	retval  = rte_eth_dev_start(port);
225 	if (retval < 0)
226 		return retval;
227 
228 	return 0;
229 }
230 
231 /* Check num_pools parameter and set it if OK*/
232 static int
233 vmdq_parse_num_pools(const char *q_arg)
234 {
235 	char *end = NULL;
236 	int n;
237 
238 	/* parse number string */
239 	n = strtol(q_arg, &end, 10);
240 	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
241 		return -1;
242 	if (n != 16 && n != 32)
243 		return -1;
244 	if (n == 16)
245 		num_pools = ETH_16_POOLS;
246 	else
247 		num_pools = ETH_32_POOLS;
248 
249 	return 0;
250 }
251 
252 /* Display usage */
253 static void
254 vmdq_usage(const char *prgname)
255 {
256 	printf("%s [EAL options] -- [-nb-pools NP]\n"
257 	       "  -nb-pools NP: number of pools (16 default, 32)\n",
258 	       prgname);
259 }
260 
261 /*  Parse the argument (num_pools) given in the command line of the application */
262 static int
263 vmdq_parse_args(int argc, char **argv)
264 {
265 	int opt;
266 	int option_index;
267 	const char *prgname = argv[0];
268 
269 	static struct option long_option[] = {
270 		{"nb-pools", required_argument, NULL, 0},
271 		{NULL, 0, 0, 0}
272 	};
273 
274 	/* Parse command line */
275 	while ((opt = getopt_long_only(argc, argv, "",long_option,&option_index)) != EOF) {
276 		switch (opt) {
277 		case 0:
278 			if (vmdq_parse_num_pools(optarg) == -1){
279 				printf("invalid number of pools\n");
280 				vmdq_usage(prgname);
281 				return -1;
282 			}
283 			break;
284 		default:
285 			vmdq_usage(prgname);
286 			return -1;
287 		}
288 	}
289 	return 0;
290 
291 
292 }
293 
294 
295 #ifndef RTE_EXEC_ENV_BAREMETAL
296 /* When we receive a HUP signal, print out our stats */
297 static void
298 sighup_handler(int signum)
299 {
300 	unsigned q;
301 	for (q = 0; q < NUM_QUEUES; q ++) {
302 		if (q % (NUM_QUEUES/num_pools) == 0)
303 			printf("\nPool %u: ", q/(NUM_QUEUES/num_pools));
304 		printf("%lu ", rxPackets[ q ]);
305 	}
306 	printf("\nFinished handling signal %d\n", signum);
307 }
308 #endif
309 
310 /*
311  * Main thread that does the work, reading from INPUT_PORT
312  * and writing to OUTPUT_PORT
313  */
314 static  __attribute__((noreturn)) int
315 lcore_main(void *arg)
316 {
317 	const uintptr_t core_num = (uintptr_t)arg;
318 	const unsigned num_cores = rte_lcore_count();
319 	uint16_t startQueue = (uint16_t)(core_num * (NUM_QUEUES/num_cores));
320 	uint16_t endQueue = (uint16_t)(startQueue + (NUM_QUEUES/num_cores));
321 	uint16_t q, i;
322 
323 	printf("Core %u(lcore %u) reading queues %i-%i\n", (unsigned)core_num,
324 	       rte_lcore_id(), startQueue, endQueue - 1);
325 
326 	for (;;) {
327 		struct rte_mbuf *buf[32];
328 		const uint16_t buf_size = sizeof(buf) / sizeof(buf[0]);
329 
330 		for (q = startQueue; q < endQueue; q++) {
331 			const uint16_t rxCount = rte_eth_rx_burst(RX_PORT,
332 					q, buf, buf_size);
333 			if (rxCount == 0)
334 				continue;
335 			rxPackets[q] += rxCount;
336 
337 			const uint16_t txCount = rte_eth_tx_burst(TX_PORT,
338 					(uint16_t)core_num, buf, rxCount);
339 			if (txCount != rxCount) {
340 				for (i = txCount; i < rxCount; i++)
341 					rte_pktmbuf_free(buf[i]);
342 			}
343 		}
344 	}
345 }
346 
347 /* Main function, does initialisation and calls the per-lcore functions */
348 int
349 MAIN(int argc, char *argv[])
350 {
351 	unsigned cores;
352 	struct rte_mempool *mbuf_pool;
353 	unsigned lcore_id;
354 	uintptr_t i;
355 	int ret;
356 
357 #ifndef RTE_EXEC_ENV_BAREMETAL
358 	signal(SIGHUP, sighup_handler);
359 #endif
360 
361 	/* init EAL */
362 	ret = rte_eal_init(argc, argv);
363 	if (ret < 0)
364 		rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
365 	argc -= ret;
366 	argv += ret;
367 
368 	/* parse app arguments */
369 	ret = vmdq_parse_args(argc, argv);
370 	if (ret < 0)
371 		rte_exit(EXIT_FAILURE, "Invalid VMDQ argument\n");
372 
373 	if (rte_ixgbe_pmd_init() != 0 ||
374 			rte_eal_pci_probe() != 0)
375 		rte_exit(EXIT_FAILURE, "Error with NIC driver initialization\n");
376 
377 	cores = rte_lcore_count();
378 	if ((cores & (cores - 1)) != 0 || cores > 16) {
379 		rte_exit(EXIT_FAILURE,
380 			 "This program can only run on 2,4,8 or 16 cores\n\n");
381 	}
382 
383 	mbuf_pool = rte_mempool_create("MBUF_POOL", NUM_MBUFS,
384 				       MBUF_SIZE, MBUF_CACHE_SIZE,
385 				       sizeof(struct rte_pktmbuf_pool_private),
386 				       rte_pktmbuf_pool_init, NULL,
387 				       rte_pktmbuf_init, NULL,
388 				       rte_socket_id(), 0);
389 	if (mbuf_pool == NULL)
390 		rte_exit(EXIT_FAILURE, "Cannot create mbuf pool\n");
391 
392 	if (port_init(RX_PORT, mbuf_pool) != 0 ||
393 	    port_init(TX_PORT, mbuf_pool) != 0)
394 		rte_exit(EXIT_FAILURE, "Cannot initialize network ports\n");
395 
396 	/* call lcore_main() on every slave lcore */
397 	i = 0;
398 	RTE_LCORE_FOREACH_SLAVE(lcore_id) {
399 		rte_eal_remote_launch(lcore_main, (void*)i++, lcore_id);
400 	}
401 	/* call on master too */
402 	(void) lcore_main((void*)i);
403 
404 	return 0;
405 }
406