xref: /dpdk/examples/l2fwd-event/main.c (revision 4ff457986f760a9d96a2bc82e3f24f2817cd33aa)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2019 Marvell International Ltd.
3  */
4 
5 #include "l2fwd_poll.h"
6 
7 /* display usage */
8 static void
9 l2fwd_event_usage(const char *prgname)
10 {
11 	printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
12 	       "  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
13 	       "  -q NQ: number of queue (=ports) per lcore (default is 1)\n"
14 	       "  -T PERIOD: statistics will be refreshed each PERIOD seconds "
15 	       "		(0 to disable, 10 default, 86400 maximum)\n"
16 	       "  --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n"
17 	       "      When enabled:\n"
18 	       "       - The source MAC address is replaced by the TX port MAC address\n"
19 	       "       - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n",
20 	       prgname);
21 }
22 
23 static int
24 l2fwd_event_parse_portmask(const char *portmask)
25 {
26 	char *end = NULL;
27 	unsigned long pm;
28 
29 	/* parse hexadecimal string */
30 	pm = strtoul(portmask, &end, 16);
31 	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
32 		return -1;
33 
34 	if (pm == 0)
35 		return -1;
36 
37 	return pm;
38 }
39 
40 static unsigned int
41 l2fwd_event_parse_nqueue(const char *q_arg)
42 {
43 	char *end = NULL;
44 	unsigned long n;
45 
46 	/* parse hexadecimal string */
47 	n = strtoul(q_arg, &end, 10);
48 	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
49 		return 0;
50 	if (n == 0)
51 		return 0;
52 	if (n >= MAX_RX_QUEUE_PER_LCORE)
53 		return 0;
54 
55 	return n;
56 }
57 
58 static int
59 l2fwd_event_parse_timer_period(const char *q_arg)
60 {
61 	char *end = NULL;
62 	int n;
63 
64 	/* parse number string */
65 	n = strtol(q_arg, &end, 10);
66 	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
67 		return -1;
68 	if (n >= MAX_TIMER_PERIOD)
69 		return -1;
70 
71 	return n;
72 }
73 
74 static const char short_options[] =
75 	"p:"  /* portmask */
76 	"q:"  /* number of queues */
77 	"T:"  /* timer period */
78 	;
79 
80 #define CMD_LINE_OPT_MAC_UPDATING "mac-updating"
81 #define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating"
82 
83 enum {
84 	/* long options mapped to a short option */
85 
86 	/* first long only option value must be >= 256, so that we won't
87 	 * conflict with short options
88 	 */
89 	CMD_LINE_OPT_MIN_NUM = 256,
90 };
91 
92 /* Parse the argument given in the command line of the application */
93 static int
94 l2fwd_event_parse_args(int argc, char **argv,
95 		struct l2fwd_resources *rsrc)
96 {
97 	int mac_updating = 1;
98 	struct option lgopts[] = {
99 		{ CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1},
100 		{ CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0},
101 		{NULL, 0, 0, 0}
102 	};
103 	int opt, ret, timer_secs;
104 	char *prgname = argv[0];
105 	char **argvopt;
106 	int option_index;
107 
108 	argvopt = argv;
109 	while ((opt = getopt_long(argc, argvopt, short_options,
110 				  lgopts, &option_index)) != EOF) {
111 
112 		switch (opt) {
113 		/* portmask */
114 		case 'p':
115 			rsrc->enabled_port_mask =
116 					l2fwd_event_parse_portmask(optarg);
117 			if (rsrc->enabled_port_mask == 0) {
118 				printf("invalid portmask\n");
119 				l2fwd_event_usage(prgname);
120 				return -1;
121 			}
122 			break;
123 
124 		/* nqueue */
125 		case 'q':
126 			rsrc->rx_queue_per_lcore =
127 					l2fwd_event_parse_nqueue(optarg);
128 			if (rsrc->rx_queue_per_lcore == 0) {
129 				printf("invalid queue number\n");
130 				l2fwd_event_usage(prgname);
131 				return -1;
132 			}
133 			break;
134 
135 		/* timer period */
136 		case 'T':
137 			timer_secs = l2fwd_event_parse_timer_period(optarg);
138 			if (timer_secs < 0) {
139 				printf("invalid timer period\n");
140 				l2fwd_event_usage(prgname);
141 				return -1;
142 			}
143 			rsrc->timer_period = timer_secs;
144 			/* convert to number of cycles */
145 			rsrc->timer_period *= rte_get_timer_hz();
146 			break;
147 
148 		/* long options */
149 		case 0:
150 			break;
151 
152 		default:
153 			l2fwd_event_usage(prgname);
154 			return -1;
155 		}
156 	}
157 
158 	rsrc->mac_updating = mac_updating;
159 
160 	if (optind >= 0)
161 		argv[optind-1] = prgname;
162 
163 	ret = optind-1;
164 	optind = 1; /* reset getopt lib */
165 	return ret;
166 }
167 
168 static int
169 l2fwd_launch_one_lcore(void *args)
170 {
171 	struct l2fwd_resources *rsrc = args;
172 	struct l2fwd_poll_resources *poll_rsrc = rsrc->poll_rsrc;
173 
174 	poll_rsrc->poll_main_loop(rsrc);
175 
176 	return 0;
177 }
178 
179 /* Check the link status of all ports in up to 9s, and print them finally */
180 static void
181 check_all_ports_link_status(struct l2fwd_resources *rsrc,
182 			    uint32_t port_mask)
183 {
184 #define CHECK_INTERVAL 100 /* 100ms */
185 #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
186 	uint16_t port_id;
187 	uint8_t count, all_ports_up, print_flag = 0;
188 	struct rte_eth_link link;
189 
190 	printf("\nChecking link status...");
191 	fflush(stdout);
192 	for (count = 0; count <= MAX_CHECK_TIME; count++) {
193 		if (rsrc->force_quit)
194 			return;
195 		all_ports_up = 1;
196 		RTE_ETH_FOREACH_DEV(port_id) {
197 			if (rsrc->force_quit)
198 				return;
199 			if ((port_mask & (1 << port_id)) == 0)
200 				continue;
201 			memset(&link, 0, sizeof(link));
202 			rte_eth_link_get_nowait(port_id, &link);
203 			/* print link status if flag set */
204 			if (print_flag == 1) {
205 				if (link.link_status)
206 					printf(
207 					"Port%d Link Up. Speed %u Mbps - %s\n",
208 						port_id, link.link_speed,
209 				(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
210 					("full-duplex") : ("half-duplex\n"));
211 				else
212 					printf("Port %d Link Down\n", port_id);
213 				continue;
214 			}
215 			/* clear all_ports_up flag if any link down */
216 			if (link.link_status == ETH_LINK_DOWN) {
217 				all_ports_up = 0;
218 				break;
219 			}
220 		}
221 		/* after finally printing all link status, get out */
222 		if (print_flag == 1)
223 			break;
224 
225 		if (all_ports_up == 0) {
226 			printf(".");
227 			fflush(stdout);
228 			rte_delay_ms(CHECK_INTERVAL);
229 		}
230 
231 		/* set the print_flag if all ports up or timeout */
232 		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
233 			print_flag = 1;
234 			printf("done\n");
235 		}
236 	}
237 }
238 
239 /* Print out statistics on packets dropped */
240 static void
241 print_stats(struct l2fwd_resources *rsrc)
242 {
243 	uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
244 	uint32_t port_id;
245 
246 	total_packets_dropped = 0;
247 	total_packets_tx = 0;
248 	total_packets_rx = 0;
249 
250 	const char clr[] = {27, '[', '2', 'J', '\0' };
251 	const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0' };
252 
253 		/* Clear screen and move to top left */
254 	printf("%s%s", clr, topLeft);
255 
256 	printf("\nPort statistics ====================================");
257 
258 	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) {
259 		/* skip disabled ports */
260 		if ((rsrc->enabled_port_mask & (1 << port_id)) == 0)
261 			continue;
262 		printf("\nStatistics for port %u ------------------------------"
263 			   "\nPackets sent: %24"PRIu64
264 			   "\nPackets received: %20"PRIu64
265 			   "\nPackets dropped: %21"PRIu64,
266 			   port_id,
267 			   rsrc->port_stats[port_id].tx,
268 			   rsrc->port_stats[port_id].rx,
269 			   rsrc->port_stats[port_id].dropped);
270 
271 		total_packets_dropped +=
272 					rsrc->port_stats[port_id].dropped;
273 		total_packets_tx += rsrc->port_stats[port_id].tx;
274 		total_packets_rx += rsrc->port_stats[port_id].rx;
275 	}
276 	printf("\nAggregate statistics ==============================="
277 		   "\nTotal packets sent: %18"PRIu64
278 		   "\nTotal packets received: %14"PRIu64
279 		   "\nTotal packets dropped: %15"PRIu64,
280 		   total_packets_tx,
281 		   total_packets_rx,
282 		   total_packets_dropped);
283 	printf("\n====================================================\n");
284 }
285 
286 static void
287 l2fwd_event_print_stats(struct l2fwd_resources *rsrc)
288 {
289 	uint64_t prev_tsc = 0, diff_tsc, cur_tsc, timer_tsc = 0;
290 	const uint64_t timer_period = rsrc->timer_period;
291 
292 	while (!rsrc->force_quit) {
293 		/* if timer is enabled */
294 		if (timer_period > 0) {
295 			cur_tsc = rte_rdtsc();
296 			diff_tsc = cur_tsc - prev_tsc;
297 
298 			/* advance the timer */
299 			timer_tsc += diff_tsc;
300 
301 			/* if timer has reached its timeout */
302 			if (unlikely(timer_tsc >= timer_period)) {
303 				print_stats(rsrc);
304 				/* reset the timer */
305 				timer_tsc = 0;
306 			}
307 			prev_tsc = cur_tsc;
308 		}
309 	}
310 }
311 
312 
313 static void
314 signal_handler(int signum)
315 {
316 	struct l2fwd_resources *rsrc = l2fwd_get_rsrc();
317 	if (signum == SIGINT || signum == SIGTERM) {
318 		printf("\n\nSignal %d received, preparing to exit...\n",
319 				signum);
320 		rsrc->force_quit = true;
321 	}
322 }
323 
324 int
325 main(int argc, char **argv)
326 {
327 	struct l2fwd_resources *rsrc;
328 	uint16_t nb_ports_available = 0;
329 	uint32_t nb_ports_in_mask = 0;
330 	uint16_t port_id, last_port;
331 	uint32_t nb_mbufs;
332 	uint16_t nb_ports;
333 	int ret;
334 
335 	/* init EAL */
336 	ret = rte_eal_init(argc, argv);
337 	if (ret < 0)
338 		rte_panic("Invalid EAL arguments\n");
339 	argc -= ret;
340 	argv += ret;
341 
342 	rsrc = l2fwd_get_rsrc();
343 
344 	signal(SIGINT, signal_handler);
345 	signal(SIGTERM, signal_handler);
346 
347 	/* parse application arguments (after the EAL ones) */
348 	ret = l2fwd_event_parse_args(argc, argv, rsrc);
349 	if (ret < 0)
350 		rte_panic("Invalid L2FWD arguments\n");
351 
352 	printf("MAC updating %s\n", rsrc->mac_updating ? "enabled" :
353 			"disabled");
354 
355 	nb_ports = rte_eth_dev_count_avail();
356 	if (nb_ports == 0)
357 		rte_panic("No Ethernet ports - bye\n");
358 
359 	/* check port mask to possible port mask */
360 	if (rsrc->enabled_port_mask & ~((1 << nb_ports) - 1))
361 		rte_panic("Invalid portmask; possible (0x%x)\n",
362 			(1 << nb_ports) - 1);
363 
364 	/* reset l2fwd_dst_ports */
365 	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
366 		rsrc->dst_ports[port_id] = 0;
367 	last_port = 0;
368 
369 	/*
370 	 * Each logical core is assigned a dedicated TX queue on each port.
371 	 */
372 	RTE_ETH_FOREACH_DEV(port_id) {
373 		/* skip ports that are not enabled */
374 		if ((rsrc->enabled_port_mask & (1 << port_id)) == 0)
375 			continue;
376 
377 		if (nb_ports_in_mask % 2) {
378 			rsrc->dst_ports[port_id] = last_port;
379 			rsrc->dst_ports[last_port] = port_id;
380 		} else {
381 			last_port = port_id;
382 		}
383 
384 		nb_ports_in_mask++;
385 	}
386 	if (nb_ports_in_mask % 2) {
387 		printf("Notice: odd number of ports in portmask.\n");
388 		rsrc->dst_ports[last_port] = last_port;
389 	}
390 
391 	nb_mbufs = RTE_MAX(nb_ports * (RTE_TEST_RX_DESC_DEFAULT +
392 				       RTE_TEST_TX_DESC_DEFAULT +
393 				       MAX_PKT_BURST + rte_lcore_count() *
394 				       MEMPOOL_CACHE_SIZE), 8192U);
395 
396 	/* create the mbuf pool */
397 	rsrc->pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool",
398 			nb_mbufs, MEMPOOL_CACHE_SIZE, 0,
399 			RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
400 	if (rsrc->pktmbuf_pool == NULL)
401 		rte_panic("Cannot init mbuf pool\n");
402 
403 	nb_ports_available = l2fwd_event_init_ports(rsrc);
404 	if (!nb_ports_available)
405 		rte_panic("All available ports are disabled. Please set portmask.\n");
406 
407 	l2fwd_poll_resource_setup(rsrc);
408 
409 	/* initialize port stats */
410 	memset(&rsrc->port_stats, 0,
411 					sizeof(struct l2fwd_port_statistics));
412 
413 	/* All settings are done. Now enable eth devices */
414 	RTE_ETH_FOREACH_DEV(port_id) {
415 		/* skip ports that are not enabled */
416 		if ((rsrc->enabled_port_mask &
417 					(1 << port_id)) == 0)
418 			continue;
419 
420 		ret = rte_eth_dev_start(port_id);
421 		if (ret < 0)
422 			rte_panic("rte_eth_dev_start:err=%d, port=%u\n", ret,
423 				  port_id);
424 	}
425 
426 	check_all_ports_link_status(rsrc, rsrc->enabled_port_mask);
427 
428 	/* launch per-lcore init on every lcore */
429 	rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, rsrc,
430 				 SKIP_MASTER);
431 	l2fwd_event_print_stats(rsrc);
432 	rte_eal_mp_wait_lcore();
433 
434 	RTE_ETH_FOREACH_DEV(port_id) {
435 		if ((rsrc->enabled_port_mask &
436 						(1 << port_id)) == 0)
437 			continue;
438 		printf("Closing port %d...", port_id);
439 		rte_eth_dev_stop(port_id);
440 		rte_eth_dev_close(port_id);
441 		printf(" Done\n");
442 	}
443 	printf("Bye...\n");
444 
445 	return 0;
446 }
447