xref: /dpdk/examples/l2fwd-event/main.c (revision 9e991f217fc8719e38a812dc280dba5f84db9f59)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(C) 2019 Marvell International Ltd.
3  */
4 
5 #include "l2fwd_event.h"
6 #include "l2fwd_poll.h"
7 
8 /* display usage */
9 static void
10 l2fwd_event_usage(const char *prgname)
11 {
12 	printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
13 	       "  -p PORTMASK: hexadecimal bitmask of ports to configure\n"
14 	       "  -q NQ: number of queue (=ports) per lcore (default is 1)\n"
15 	       "  -T PERIOD: statistics will be refreshed each PERIOD seconds "
16 	       "		(0 to disable, 10 default, 86400 maximum)\n"
17 	       "  --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n"
18 	       "      When enabled:\n"
19 	       "       - The source MAC address is replaced by the TX port MAC address\n"
20 	       "       - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n"
21 	       "  --mode: Packet transfer mode for I/O, poll or eventdev\n"
22 	       "          Default mode = eventdev\n"
23 	       "  --eventq-sched: Event queue schedule type, ordered, atomic or parallel.\n"
24 	       "                  Default: atomic\n"
25 	       "                  Valid only if --mode=eventdev\n\n",
26 	       prgname);
27 }
28 
29 static int
30 l2fwd_event_parse_portmask(const char *portmask)
31 {
32 	char *end = NULL;
33 	unsigned long pm;
34 
35 	/* parse hexadecimal string */
36 	pm = strtoul(portmask, &end, 16);
37 	if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
38 		return -1;
39 
40 	if (pm == 0)
41 		return -1;
42 
43 	return pm;
44 }
45 
46 static unsigned int
47 l2fwd_event_parse_nqueue(const char *q_arg)
48 {
49 	char *end = NULL;
50 	unsigned long n;
51 
52 	/* parse hexadecimal string */
53 	n = strtoul(q_arg, &end, 10);
54 	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
55 		return 0;
56 	if (n == 0)
57 		return 0;
58 	if (n >= MAX_RX_QUEUE_PER_LCORE)
59 		return 0;
60 
61 	return n;
62 }
63 
64 static int
65 l2fwd_event_parse_timer_period(const char *q_arg)
66 {
67 	char *end = NULL;
68 	int n;
69 
70 	/* parse number string */
71 	n = strtol(q_arg, &end, 10);
72 	if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
73 		return -1;
74 	if (n >= MAX_TIMER_PERIOD)
75 		return -1;
76 
77 	return n;
78 }
79 
80 static void
81 l2fwd_event_parse_mode(const char *optarg,
82 		       struct l2fwd_resources *rsrc)
83 {
84 	if (!strncmp(optarg, "poll", 4))
85 		rsrc->event_mode = false;
86 	else if (!strncmp(optarg, "eventdev", 8))
87 		rsrc->event_mode = true;
88 }
89 
90 static void
91 l2fwd_event_parse_eventq_sched(const char *optarg,
92 			       struct l2fwd_resources *rsrc)
93 {
94 	if (!strncmp(optarg, "ordered", 7))
95 		rsrc->sched_type = RTE_SCHED_TYPE_ORDERED;
96 	else if (!strncmp(optarg, "atomic", 6))
97 		rsrc->sched_type = RTE_SCHED_TYPE_ATOMIC;
98 	else if (!strncmp(optarg, "parallel", 8))
99 		rsrc->sched_type = RTE_SCHED_TYPE_PARALLEL;
100 }
101 
102 static const char short_options[] =
103 	"p:"  /* portmask */
104 	"q:"  /* number of queues */
105 	"T:"  /* timer period */
106 	;
107 
108 #define CMD_LINE_OPT_MAC_UPDATING "mac-updating"
109 #define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating"
110 #define CMD_LINE_OPT_MODE "mode"
111 #define CMD_LINE_OPT_EVENTQ_SCHED "eventq-sched"
112 
113 enum {
114 	/* long options mapped to a short option */
115 
116 	/* first long only option value must be >= 256, so that we won't
117 	 * conflict with short options
118 	 */
119 	CMD_LINE_OPT_MIN_NUM = 256,
120 	CMD_LINE_OPT_MODE_NUM,
121 	CMD_LINE_OPT_EVENTQ_SCHED_NUM,
122 };
123 
124 /* Parse the argument given in the command line of the application */
125 static int
126 l2fwd_event_parse_args(int argc, char **argv,
127 		struct l2fwd_resources *rsrc)
128 {
129 	int mac_updating = 1;
130 	struct option lgopts[] = {
131 		{ CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1},
132 		{ CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0},
133 		{ CMD_LINE_OPT_MODE, required_argument, NULL,
134 							CMD_LINE_OPT_MODE_NUM},
135 		{ CMD_LINE_OPT_EVENTQ_SCHED, required_argument, NULL,
136 						CMD_LINE_OPT_EVENTQ_SCHED_NUM},
137 		{NULL, 0, 0, 0}
138 	};
139 	int opt, ret, timer_secs;
140 	char *prgname = argv[0];
141 	char **argvopt;
142 	int option_index;
143 
144 	argvopt = argv;
145 	while ((opt = getopt_long(argc, argvopt, short_options,
146 				  lgopts, &option_index)) != EOF) {
147 
148 		switch (opt) {
149 		/* portmask */
150 		case 'p':
151 			rsrc->enabled_port_mask =
152 					l2fwd_event_parse_portmask(optarg);
153 			if (rsrc->enabled_port_mask == 0) {
154 				printf("invalid portmask\n");
155 				l2fwd_event_usage(prgname);
156 				return -1;
157 			}
158 			break;
159 
160 		/* nqueue */
161 		case 'q':
162 			rsrc->rx_queue_per_lcore =
163 					l2fwd_event_parse_nqueue(optarg);
164 			if (rsrc->rx_queue_per_lcore == 0) {
165 				printf("invalid queue number\n");
166 				l2fwd_event_usage(prgname);
167 				return -1;
168 			}
169 			break;
170 
171 		/* timer period */
172 		case 'T':
173 			timer_secs = l2fwd_event_parse_timer_period(optarg);
174 			if (timer_secs < 0) {
175 				printf("invalid timer period\n");
176 				l2fwd_event_usage(prgname);
177 				return -1;
178 			}
179 			rsrc->timer_period = timer_secs;
180 			/* convert to number of cycles */
181 			rsrc->timer_period *= rte_get_timer_hz();
182 			break;
183 
184 		case CMD_LINE_OPT_MODE_NUM:
185 			l2fwd_event_parse_mode(optarg, rsrc);
186 			break;
187 
188 		case CMD_LINE_OPT_EVENTQ_SCHED_NUM:
189 			l2fwd_event_parse_eventq_sched(optarg, rsrc);
190 			break;
191 
192 		/* long options */
193 		case 0:
194 			break;
195 
196 		default:
197 			l2fwd_event_usage(prgname);
198 			return -1;
199 		}
200 	}
201 
202 	rsrc->mac_updating = mac_updating;
203 
204 	if (optind >= 0)
205 		argv[optind-1] = prgname;
206 
207 	ret = optind-1;
208 	optind = 1; /* reset getopt lib */
209 	return ret;
210 }
211 
212 static int
213 l2fwd_launch_one_lcore(void *args)
214 {
215 	struct l2fwd_resources *rsrc = args;
216 	struct l2fwd_poll_resources *poll_rsrc = rsrc->poll_rsrc;
217 	struct l2fwd_event_resources *evt_rsrc = rsrc->evt_rsrc;
218 
219 	if (rsrc->event_mode)
220 		evt_rsrc->ops.l2fwd_event_loop(rsrc);
221 	else
222 		poll_rsrc->poll_main_loop(rsrc);
223 
224 	return 0;
225 }
226 
227 /* Check the link status of all ports in up to 9s, and print them finally */
228 static void
229 check_all_ports_link_status(struct l2fwd_resources *rsrc,
230 			    uint32_t port_mask)
231 {
232 #define CHECK_INTERVAL 100 /* 100ms */
233 #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
234 	uint16_t port_id;
235 	uint8_t count, all_ports_up, print_flag = 0;
236 	struct rte_eth_link link;
237 	int ret;
238 
239 	printf("\nChecking link status...");
240 	fflush(stdout);
241 	for (count = 0; count <= MAX_CHECK_TIME; count++) {
242 		if (rsrc->force_quit)
243 			return;
244 		all_ports_up = 1;
245 		RTE_ETH_FOREACH_DEV(port_id) {
246 			if (rsrc->force_quit)
247 				return;
248 			if ((port_mask & (1 << port_id)) == 0)
249 				continue;
250 			memset(&link, 0, sizeof(link));
251 			ret = rte_eth_link_get_nowait(port_id, &link);
252 			if (ret < 0) {
253 				all_ports_up = 0;
254 				if (print_flag == 1)
255 					printf("Port %u link get failed: %s\n",
256 						port_id, rte_strerror(-ret));
257 				continue;
258 			}
259 			/* print link status if flag set */
260 			if (print_flag == 1) {
261 				if (link.link_status)
262 					printf(
263 					"Port%d Link Up. Speed %u Mbps - %s\n",
264 						port_id, link.link_speed,
265 				(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
266 					("full-duplex") : ("half-duplex\n"));
267 				else
268 					printf("Port %d Link Down\n", port_id);
269 				continue;
270 			}
271 			/* clear all_ports_up flag if any link down */
272 			if (link.link_status == ETH_LINK_DOWN) {
273 				all_ports_up = 0;
274 				break;
275 			}
276 		}
277 		/* after finally printing all link status, get out */
278 		if (print_flag == 1)
279 			break;
280 
281 		if (all_ports_up == 0) {
282 			printf(".");
283 			fflush(stdout);
284 			rte_delay_ms(CHECK_INTERVAL);
285 		}
286 
287 		/* set the print_flag if all ports up or timeout */
288 		if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
289 			print_flag = 1;
290 			printf("done\n");
291 		}
292 	}
293 }
294 
295 /* Print out statistics on packets dropped */
296 static void
297 print_stats(struct l2fwd_resources *rsrc)
298 {
299 	uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
300 	uint32_t port_id;
301 
302 	total_packets_dropped = 0;
303 	total_packets_tx = 0;
304 	total_packets_rx = 0;
305 
306 	const char clr[] = {27, '[', '2', 'J', '\0' };
307 	const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0' };
308 
309 		/* Clear screen and move to top left */
310 	printf("%s%s", clr, topLeft);
311 
312 	printf("\nPort statistics ====================================");
313 
314 	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) {
315 		/* skip disabled ports */
316 		if ((rsrc->enabled_port_mask & (1 << port_id)) == 0)
317 			continue;
318 		printf("\nStatistics for port %u ------------------------------"
319 			   "\nPackets sent: %29"PRIu64
320 			   "\nPackets received: %25"PRIu64
321 			   "\nPackets dropped: %26"PRIu64,
322 			   port_id,
323 			   rsrc->port_stats[port_id].tx,
324 			   rsrc->port_stats[port_id].rx,
325 			   rsrc->port_stats[port_id].dropped);
326 
327 		total_packets_dropped +=
328 					rsrc->port_stats[port_id].dropped;
329 		total_packets_tx += rsrc->port_stats[port_id].tx;
330 		total_packets_rx += rsrc->port_stats[port_id].rx;
331 	}
332 
333 	if (rsrc->event_mode) {
334 		struct l2fwd_event_resources *evt_rsrc = rsrc->evt_rsrc;
335 		struct rte_event_eth_rx_adapter_stats rx_adptr_stats;
336 		struct rte_event_eth_tx_adapter_stats tx_adptr_stats;
337 		int ret, i;
338 
339 		for (i = 0; i < evt_rsrc->rx_adptr.nb_rx_adptr; i++) {
340 			ret = rte_event_eth_rx_adapter_stats_get(
341 					evt_rsrc->rx_adptr.rx_adptr[i],
342 					&rx_adptr_stats);
343 			if (ret < 0)
344 				continue;
345 			printf("\nRx adapter[%d] statistics===================="
346 				   "\nReceive queue poll count: %17"PRIu64
347 				   "\nReceived packet count: %20"PRIu64
348 				   "\nEventdev enqueue count: %19"PRIu64
349 				   "\nEventdev enqueue retry count: %13"PRIu64
350 				   "\nReceived packet dropped count: %12"PRIu64
351 				   "\nRx enqueue start timestamp: %15"PRIu64
352 				   "\nRx enqueue block cycles: %18"PRIu64
353 				   "\nRx enqueue unblock timestamp: %13"PRIu64,
354 				   evt_rsrc->rx_adptr.rx_adptr[i],
355 				   rx_adptr_stats.rx_poll_count,
356 				   rx_adptr_stats.rx_packets,
357 				   rx_adptr_stats.rx_enq_count,
358 				   rx_adptr_stats.rx_enq_retry,
359 				   rx_adptr_stats.rx_dropped,
360 				   rx_adptr_stats.rx_enq_start_ts,
361 				   rx_adptr_stats.rx_enq_block_cycles,
362 				   rx_adptr_stats.rx_enq_end_ts);
363 		}
364 		for (i = 0; i <  evt_rsrc->tx_adptr.nb_tx_adptr; i++) {
365 			ret = rte_event_eth_tx_adapter_stats_get(
366 					evt_rsrc->tx_adptr.tx_adptr[i],
367 					&tx_adptr_stats);
368 			if (ret < 0)
369 				continue;
370 			printf("\nTx adapter[%d] statistics===================="
371 				   "\nNumber of transmit retries: %15"PRIu64
372 				   "\nNumber of packets transmitted: %12"PRIu64
373 				   "\nNumber of packets dropped: %16"PRIu64,
374 				   evt_rsrc->tx_adptr.tx_adptr[i],
375 				   tx_adptr_stats.tx_retry,
376 				   tx_adptr_stats.tx_packets,
377 				   tx_adptr_stats.tx_dropped);
378 		}
379 	}
380 	printf("\nAggregate lcore statistics ========================="
381 		   "\nTotal packets sent: %23"PRIu64
382 		   "\nTotal packets received: %19"PRIu64
383 		   "\nTotal packets dropped: %20"PRIu64,
384 		   total_packets_tx,
385 		   total_packets_rx,
386 		   total_packets_dropped);
387 	printf("\n====================================================\n");
388 }
389 
390 static void
391 l2fwd_event_print_stats(struct l2fwd_resources *rsrc)
392 {
393 	uint64_t prev_tsc = 0, diff_tsc, cur_tsc, timer_tsc = 0;
394 	const uint64_t timer_period = rsrc->timer_period;
395 
396 	while (!rsrc->force_quit) {
397 		/* if timer is enabled */
398 		if (timer_period > 0) {
399 			cur_tsc = rte_rdtsc();
400 			diff_tsc = cur_tsc - prev_tsc;
401 
402 			/* advance the timer */
403 			timer_tsc += diff_tsc;
404 
405 			/* if timer has reached its timeout */
406 			if (unlikely(timer_tsc >= timer_period)) {
407 				print_stats(rsrc);
408 				/* reset the timer */
409 				timer_tsc = 0;
410 			}
411 			prev_tsc = cur_tsc;
412 		}
413 	}
414 }
415 
416 
417 static void
418 signal_handler(int signum)
419 {
420 	struct l2fwd_resources *rsrc = l2fwd_get_rsrc();
421 	if (signum == SIGINT || signum == SIGTERM) {
422 		printf("\n\nSignal %d received, preparing to exit...\n",
423 				signum);
424 		rsrc->force_quit = true;
425 	}
426 }
427 
428 int
429 main(int argc, char **argv)
430 {
431 	struct l2fwd_resources *rsrc;
432 	uint16_t nb_ports_available = 0;
433 	uint32_t nb_ports_in_mask = 0;
434 	uint16_t port_id, last_port;
435 	uint32_t nb_mbufs;
436 	uint16_t nb_ports;
437 	int i, ret;
438 
439 	/* init EAL */
440 	ret = rte_eal_init(argc, argv);
441 	if (ret < 0)
442 		rte_panic("Invalid EAL arguments\n");
443 	argc -= ret;
444 	argv += ret;
445 
446 	rsrc = l2fwd_get_rsrc();
447 
448 	signal(SIGINT, signal_handler);
449 	signal(SIGTERM, signal_handler);
450 
451 	/* parse application arguments (after the EAL ones) */
452 	ret = l2fwd_event_parse_args(argc, argv, rsrc);
453 	if (ret < 0)
454 		rte_panic("Invalid L2FWD arguments\n");
455 
456 	printf("MAC updating %s\n", rsrc->mac_updating ? "enabled" :
457 			"disabled");
458 
459 	nb_ports = rte_eth_dev_count_avail();
460 	if (nb_ports == 0)
461 		rte_panic("No Ethernet ports - bye\n");
462 
463 	/* check port mask to possible port mask */
464 	if (rsrc->enabled_port_mask & ~((1 << nb_ports) - 1))
465 		rte_panic("Invalid portmask; possible (0x%x)\n",
466 			(1 << nb_ports) - 1);
467 
468 	/* reset l2fwd_dst_ports */
469 	for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
470 		rsrc->dst_ports[port_id] = 0;
471 	last_port = 0;
472 
473 	/*
474 	 * Each logical core is assigned a dedicated TX queue on each port.
475 	 */
476 	RTE_ETH_FOREACH_DEV(port_id) {
477 		/* skip ports that are not enabled */
478 		if ((rsrc->enabled_port_mask & (1 << port_id)) == 0)
479 			continue;
480 
481 		if (nb_ports_in_mask % 2) {
482 			rsrc->dst_ports[port_id] = last_port;
483 			rsrc->dst_ports[last_port] = port_id;
484 		} else {
485 			last_port = port_id;
486 		}
487 
488 		nb_ports_in_mask++;
489 	}
490 	if (nb_ports_in_mask % 2) {
491 		printf("Notice: odd number of ports in portmask.\n");
492 		rsrc->dst_ports[last_port] = last_port;
493 	}
494 
495 	nb_mbufs = RTE_MAX(nb_ports * (RTE_TEST_RX_DESC_DEFAULT +
496 				       RTE_TEST_TX_DESC_DEFAULT +
497 				       MAX_PKT_BURST + rte_lcore_count() *
498 				       MEMPOOL_CACHE_SIZE), 8192U);
499 
500 	/* create the mbuf pool */
501 	rsrc->pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool",
502 			nb_mbufs, MEMPOOL_CACHE_SIZE, 0,
503 			RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
504 	if (rsrc->pktmbuf_pool == NULL)
505 		rte_panic("Cannot init mbuf pool\n");
506 
507 	nb_ports_available = l2fwd_event_init_ports(rsrc);
508 	if (!nb_ports_available)
509 		rte_panic("All available ports are disabled. Please set portmask.\n");
510 
511 	/* Configure eventdev parameters if required */
512 	if (rsrc->event_mode)
513 		l2fwd_event_resource_setup(rsrc);
514 	else
515 		l2fwd_poll_resource_setup(rsrc);
516 
517 	/* initialize port stats */
518 	memset(&rsrc->port_stats, 0,
519 					sizeof(struct l2fwd_port_statistics));
520 
521 	/* All settings are done. Now enable eth devices */
522 	RTE_ETH_FOREACH_DEV(port_id) {
523 		/* skip ports that are not enabled */
524 		if ((rsrc->enabled_port_mask &
525 					(1 << port_id)) == 0)
526 			continue;
527 
528 		ret = rte_eth_dev_start(port_id);
529 		if (ret < 0)
530 			rte_panic("rte_eth_dev_start:err=%d, port=%u\n", ret,
531 				  port_id);
532 	}
533 
534 	if (rsrc->event_mode)
535 		l2fwd_event_service_setup(rsrc);
536 
537 	check_all_ports_link_status(rsrc, rsrc->enabled_port_mask);
538 
539 	/* launch per-lcore init on every lcore */
540 	rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, rsrc,
541 				 SKIP_MASTER);
542 	l2fwd_event_print_stats(rsrc);
543 	if (rsrc->event_mode) {
544 		struct l2fwd_event_resources *evt_rsrc =
545 							rsrc->evt_rsrc;
546 		for (i = 0; i < evt_rsrc->rx_adptr.nb_rx_adptr; i++)
547 			rte_event_eth_rx_adapter_stop(
548 				evt_rsrc->rx_adptr.rx_adptr[i]);
549 		for (i = 0; i < evt_rsrc->tx_adptr.nb_tx_adptr; i++)
550 			rte_event_eth_tx_adapter_stop(
551 				evt_rsrc->tx_adptr.tx_adptr[i]);
552 
553 		RTE_ETH_FOREACH_DEV(port_id) {
554 			if ((rsrc->enabled_port_mask &
555 							(1 << port_id)) == 0)
556 				continue;
557 			rte_eth_dev_stop(port_id);
558 		}
559 
560 		rte_eal_mp_wait_lcore();
561 		RTE_ETH_FOREACH_DEV(port_id) {
562 			if ((rsrc->enabled_port_mask &
563 							(1 << port_id)) == 0)
564 				continue;
565 			rte_eth_dev_close(port_id);
566 		}
567 
568 		rte_event_dev_stop(evt_rsrc->event_d_id);
569 		rte_event_dev_close(evt_rsrc->event_d_id);
570 
571 	} else {
572 		rte_eal_mp_wait_lcore();
573 
574 		RTE_ETH_FOREACH_DEV(port_id) {
575 			if ((rsrc->enabled_port_mask &
576 							(1 << port_id)) == 0)
577 				continue;
578 			printf("Closing port %d...", port_id);
579 			rte_eth_dev_stop(port_id);
580 			rte_eth_dev_close(port_id);
581 			printf(" Done\n");
582 		}
583 	}
584 	printf("Bye...\n");
585 
586 	return 0;
587 }
588