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