1 /* SPDX-License-Identifier: BSD-3-Clause
2 * Copyright(C) 2019 Marvell International Ltd.
5 #include <rte_string_fns.h>
7 #include "l2fwd_event.h"
8 #include "l2fwd_poll.h"
12 l2fwd_event_usage(const char *prgname)
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"
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"
27 " Valid only if --mode=eventdev\n"
28 " --config: Configure forwarding port pair mapping\n"
29 " Default: alternate port pairs\n\n",
34 l2fwd_event_parse_portmask(const char *portmask)
39 /* parse hexadecimal string */
40 pm = strtoul(portmask, &end, 16);
41 if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
51 l2fwd_event_parse_nqueue(const char *q_arg)
56 /* parse hexadecimal string */
57 n = strtoul(q_arg, &end, 10);
58 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
62 if (n >= MAX_RX_QUEUE_PER_LCORE)
69 l2fwd_event_parse_timer_period(const char *q_arg)
74 /* parse number string */
75 n = strtol(q_arg, &end, 10);
76 if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
78 if (n >= MAX_TIMER_PERIOD)
85 l2fwd_event_parse_mode(const char *optarg,
86 struct l2fwd_resources *rsrc)
88 if (!strncmp(optarg, "poll", 4))
89 rsrc->event_mode = false;
90 else if (!strncmp(optarg, "eventdev", 8))
91 rsrc->event_mode = true;
95 l2fwd_event_parse_eventq_sched(const char *optarg,
96 struct l2fwd_resources *rsrc)
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;
107 l2fwd_parse_port_pair_config(const char *q_arg, struct l2fwd_resources *rsrc)
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;
123 while ((p = strchr(p0, '(')) != NULL) {
130 if (size >= sizeof(s))
134 if (rte_strsplit(s, sizeof(s), str_fld,
135 _NUM_FLD, ',') != _NUM_FLD)
138 for (i = 0; i < _NUM_FLD; i++) {
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)
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);
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]);
159 rsrc->dst_ports[int_fld[FLD_PORT1]] = int_fld[FLD_PORT2];
160 rsrc->dst_ports[int_fld[FLD_PORT2]] = int_fld[FLD_PORT1];
165 rsrc->port_pairs = true;
170 static const char short_options[] =
172 "q:" /* number of queues */
173 "T:" /* timer period */
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"
183 /* long options mapped to a short option */
185 /* first long only option value must be >= 256, so that we won't
186 * conflict with short options
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,
194 /* Parse the argument given in the command line of the application */
196 l2fwd_event_parse_args(int argc, char **argv, struct l2fwd_resources *rsrc)
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},
210 int opt, ret, timer_secs;
211 char *prgname = argv[0];
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;
221 while ((opt = getopt_long(argc, argvopt, short_options,
222 lgopts, &option_index)) != EOF) {
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);
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);
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);
255 rsrc->timer_period = timer_secs;
256 /* convert to number of cycles */
257 rsrc->timer_period *= rte_get_timer_hz();
260 case CMD_LINE_OPT_MODE_NUM:
261 l2fwd_event_parse_mode(optarg, rsrc);
264 case CMD_LINE_OPT_EVENTQ_SCHED_NUM:
265 l2fwd_event_parse_eventq_sched(optarg, rsrc);
268 case CMD_LINE_OPT_PORT_PAIR_CONF_NUM:
269 ret = l2fwd_parse_port_pair_config(optarg, rsrc);
271 printf("Invalid port pair config\n");
272 l2fwd_event_usage(prgname);
282 l2fwd_event_usage(prgname);
287 rsrc->mac_updating = mac_updating;
290 argv[optind-1] = prgname;
293 optind = 1; /* reset getopt lib */
298 * Check port pair config with enabled port mask,
299 * and for valid port pair combinations.
302 check_port_pair_config(struct l2fwd_resources *rsrc)
304 uint32_t port_pair_mask = 0;
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)))
313 portid = rsrc->dst_ports[index];
314 if (portid == UINT32_MAX) {
315 printf("port %u is enabled in but no valid port pair\n",
320 if (!rte_eth_dev_is_valid_port(index)) {
321 printf("port %u is not valid\n", index);
325 if (!rte_eth_dev_is_valid_port(portid)) {
326 printf("port %u is not valid\n", portid);
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);
336 port_pair_mask |= (1 << portid);
337 port_pair_mask |= (1 << index);
344 l2fwd_launch_one_lcore(void *args)
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;
350 if (rsrc->event_mode)
351 evt_rsrc->ops.l2fwd_event_loop(rsrc);
353 poll_rsrc->poll_main_loop(rsrc);
358 /* Check the link status of all ports in up to 9s, and print them finally */
360 check_all_ports_link_status(struct l2fwd_resources *rsrc,
363 #define CHECK_INTERVAL 100 /* 100ms */
364 #define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
366 uint8_t count, all_ports_up, print_flag = 0;
367 struct rte_eth_link link;
370 printf("\nChecking link status...");
372 for (count = 0; count <= MAX_CHECK_TIME; count++) {
373 if (rsrc->force_quit)
376 RTE_ETH_FOREACH_DEV(port_id) {
377 if (rsrc->force_quit)
379 if ((port_mask & (1 << port_id)) == 0)
381 memset(&link, 0, sizeof(link));
382 ret = rte_eth_link_get_nowait(port_id, &link);
386 printf("Port %u link get failed: %s\n",
387 port_id, rte_strerror(-ret));
390 /* print link status if flag set */
391 if (print_flag == 1) {
392 if (link.link_status)
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"));
399 printf("Port %d Link Down\n", port_id);
402 /* clear all_ports_up flag if any link down */
403 if (link.link_status == ETH_LINK_DOWN) {
408 /* after finally printing all link status, get out */
412 if (all_ports_up == 0) {
415 rte_delay_ms(CHECK_INTERVAL);
418 /* set the print_flag if all ports up or timeout */
419 if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
426 /* Print out statistics on packets dropped */
428 print_stats(struct l2fwd_resources *rsrc)
430 uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
433 total_packets_dropped = 0;
434 total_packets_tx = 0;
435 total_packets_rx = 0;
437 const char clr[] = {27, '[', '2', 'J', '\0' };
438 const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0' };
440 /* Clear screen and move to top left */
441 printf("%s%s", clr, topLeft);
443 printf("\nPort statistics ====================================");
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)
449 printf("\nStatistics for port %u ------------------------------"
450 "\nPackets sent: %29"PRIu64
451 "\nPackets received: %25"PRIu64
452 "\nPackets dropped: %26"PRIu64,
454 rsrc->port_stats[port_id].tx,
455 rsrc->port_stats[port_id].rx,
456 rsrc->port_stats[port_id].dropped);
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;
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;
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],
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);
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],
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);
511 printf("\nAggregate lcore statistics ========================="
512 "\nTotal packets sent: %23"PRIu64
513 "\nTotal packets received: %19"PRIu64
514 "\nTotal packets dropped: %20"PRIu64,
517 total_packets_dropped);
518 printf("\n====================================================\n");
522 l2fwd_event_print_stats(struct l2fwd_resources *rsrc)
524 uint64_t prev_tsc = 0, diff_tsc, cur_tsc, timer_tsc = 0;
525 const uint64_t timer_period = rsrc->timer_period;
527 while (!rsrc->force_quit) {
528 /* if timer is enabled */
529 if (timer_period > 0) {
530 cur_tsc = rte_rdtsc();
531 diff_tsc = cur_tsc - prev_tsc;
533 /* advance the timer */
534 timer_tsc += diff_tsc;
536 /* if timer has reached its timeout */
537 if (unlikely(timer_tsc >= timer_period)) {
539 /* reset the timer */
549 signal_handler(int signum)
551 struct l2fwd_resources *rsrc = l2fwd_get_rsrc();
552 if (signum == SIGINT || signum == SIGTERM) {
553 printf("\n\nSignal %d received, preparing to exit...\n",
555 rsrc->force_quit = true;
560 main(int argc, char **argv)
562 struct l2fwd_resources *rsrc;
563 uint16_t nb_ports_available = 0;
564 uint32_t nb_ports_in_mask = 0;
565 uint16_t port_id, last_port;
571 ret = rte_eal_init(argc, argv);
573 rte_panic("Invalid EAL arguments\n");
577 rsrc = l2fwd_get_rsrc();
579 signal(SIGINT, signal_handler);
580 signal(SIGTERM, signal_handler);
582 /* parse application arguments (after the EAL ones) */
583 ret = l2fwd_event_parse_args(argc, argv, rsrc);
585 rte_panic("Invalid L2FWD arguments\n");
587 printf("MAC updating %s\n", rsrc->mac_updating ? "enabled" :
590 nb_ports = rte_eth_dev_count_avail();
592 rte_panic("No Ethernet ports - bye\n");
594 /* check port mask to possible port mask */
595 if (rsrc->enabled_port_mask & ~((1 << nb_ports) - 1))
596 rte_panic("Invalid portmask; possible (0x%x)\n",
597 (1 << nb_ports) - 1);
599 if (!rsrc->port_pairs) {
602 * Each logical core is assigned a dedicated TX queue on each
605 RTE_ETH_FOREACH_DEV(port_id) {
606 /* skip ports that are not enabled */
607 if ((rsrc->enabled_port_mask & (1 << port_id)) == 0)
610 if (nb_ports_in_mask % 2) {
611 rsrc->dst_ports[port_id] = last_port;
612 rsrc->dst_ports[last_port] = port_id;
619 if (nb_ports_in_mask % 2) {
620 printf("Notice: odd number of ports in portmask.\n");
621 rsrc->dst_ports[last_port] = last_port;
624 if (check_port_pair_config(rsrc) < 0)
625 rte_panic("Invalid port pair config\n");
628 nb_mbufs = RTE_MAX(nb_ports * (RTE_TEST_RX_DESC_DEFAULT +
629 RTE_TEST_TX_DESC_DEFAULT +
630 MAX_PKT_BURST + rte_lcore_count() *
631 MEMPOOL_CACHE_SIZE), 8192U);
633 /* create the mbuf pool */
634 rsrc->pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool",
635 nb_mbufs, MEMPOOL_CACHE_SIZE, 0,
636 RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
637 if (rsrc->pktmbuf_pool == NULL)
638 rte_panic("Cannot init mbuf pool\n");
640 nb_ports_available = l2fwd_event_init_ports(rsrc);
641 if (!nb_ports_available)
642 rte_panic("All available ports are disabled. Please set portmask.\n");
644 /* Configure eventdev parameters if required */
645 if (rsrc->event_mode)
646 l2fwd_event_resource_setup(rsrc);
648 l2fwd_poll_resource_setup(rsrc);
650 /* initialize port stats */
651 memset(&rsrc->port_stats, 0,
652 sizeof(struct l2fwd_port_statistics));
654 /* All settings are done. Now enable eth devices */
655 RTE_ETH_FOREACH_DEV(port_id) {
656 /* skip ports that are not enabled */
657 if ((rsrc->enabled_port_mask &
658 (1 << port_id)) == 0)
661 ret = rte_eth_dev_start(port_id);
663 rte_panic("rte_eth_dev_start:err=%d, port=%u\n", ret,
667 if (rsrc->event_mode)
668 l2fwd_event_service_setup(rsrc);
670 check_all_ports_link_status(rsrc, rsrc->enabled_port_mask);
672 /* launch per-lcore init on every lcore */
673 rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, rsrc,
675 l2fwd_event_print_stats(rsrc);
676 if (rsrc->event_mode) {
677 struct l2fwd_event_resources *evt_rsrc =
679 for (i = 0; i < evt_rsrc->rx_adptr.nb_rx_adptr; i++)
680 rte_event_eth_rx_adapter_stop(
681 evt_rsrc->rx_adptr.rx_adptr[i]);
682 for (i = 0; i < evt_rsrc->tx_adptr.nb_tx_adptr; i++)
683 rte_event_eth_tx_adapter_stop(
684 evt_rsrc->tx_adptr.tx_adptr[i]);
686 RTE_ETH_FOREACH_DEV(port_id) {
687 if ((rsrc->enabled_port_mask &
688 (1 << port_id)) == 0)
690 rte_eth_dev_stop(port_id);
693 rte_eal_mp_wait_lcore();
694 RTE_ETH_FOREACH_DEV(port_id) {
695 if ((rsrc->enabled_port_mask &
696 (1 << port_id)) == 0)
698 rte_eth_dev_close(port_id);
701 rte_event_dev_stop(evt_rsrc->event_d_id);
702 rte_event_dev_close(evt_rsrc->event_d_id);
705 rte_eal_mp_wait_lcore();
707 RTE_ETH_FOREACH_DEV(port_id) {
708 if ((rsrc->enabled_port_mask &
709 (1 << port_id)) == 0)
711 printf("Closing port %d...", port_id);
712 rte_eth_dev_stop(port_id);
713 rte_eth_dev_close(port_id);