dpdk-fm10k/examples/l2fwd-event/main.c
Sarosh Arif ce6b8c3154 examples: fix port mask parsing failure handling
Giving invalid or zero portmask as command line option to
these applications will have an unexpected response.
The reason behind this is that the return value of function
that parses portmask is stored in a variable whose datatype is
unsigned int, hence returning -1 in case of zero or
invalid portmask causes an unexpected behaviour.
If we return 0 instead of -1 this issue can be resolved.
The program already contains the functionality to print
"invalid portmask" and program usage if zero is returned.

Signed-off-by: Sarosh Arif <sarosh.arif@emumba.com>
Acked-by: Bruce Richardson <bruce.richardson@intel.com>
2020-07-30 23:24:40 +02:00

720 lines
18 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(C) 2019 Marvell International Ltd.
*/
#include <rte_string_fns.h>
#include "l2fwd_event.h"
#include "l2fwd_poll.h"
/* display usage */
static void
l2fwd_event_usage(const char *prgname)
{
printf("%s [EAL options] -- -p PORTMASK [-q NQ]\n"
" -p PORTMASK: hexadecimal bitmask of ports to configure\n"
" -q NQ: number of queue (=ports) per lcore (default is 1)\n"
" -T PERIOD: statistics will be refreshed each PERIOD seconds "
" (0 to disable, 10 default, 86400 maximum)\n"
" --[no-]mac-updating: Enable or disable MAC addresses updating (enabled by default)\n"
" When enabled:\n"
" - The source MAC address is replaced by the TX port MAC address\n"
" - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID\n"
" --mode: Packet transfer mode for I/O, poll or eventdev\n"
" Default mode = eventdev\n"
" --eventq-sched: Event queue schedule type, ordered, atomic or parallel.\n"
" Default: atomic\n"
" Valid only if --mode=eventdev\n"
" --config: Configure forwarding port pair mapping\n"
" Default: alternate port pairs\n\n",
prgname);
}
static int
l2fwd_event_parse_portmask(const char *portmask)
{
char *end = NULL;
unsigned long pm;
/* parse hexadecimal string */
pm = strtoul(portmask, &end, 16);
if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0'))
return 0;
return pm;
}
static unsigned int
l2fwd_event_parse_nqueue(const char *q_arg)
{
char *end = NULL;
unsigned long n;
/* parse hexadecimal string */
n = strtoul(q_arg, &end, 10);
if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
return 0;
if (n == 0)
return 0;
if (n >= MAX_RX_QUEUE_PER_LCORE)
return 0;
return n;
}
static int
l2fwd_event_parse_timer_period(const char *q_arg)
{
char *end = NULL;
int n;
/* parse number string */
n = strtol(q_arg, &end, 10);
if ((q_arg[0] == '\0') || (end == NULL) || (*end != '\0'))
return -1;
if (n >= MAX_TIMER_PERIOD)
return -1;
return n;
}
static void
l2fwd_event_parse_mode(const char *optarg,
struct l2fwd_resources *rsrc)
{
if (!strncmp(optarg, "poll", 4))
rsrc->event_mode = false;
else if (!strncmp(optarg, "eventdev", 8))
rsrc->event_mode = true;
}
static void
l2fwd_event_parse_eventq_sched(const char *optarg,
struct l2fwd_resources *rsrc)
{
if (!strncmp(optarg, "ordered", 7))
rsrc->sched_type = RTE_SCHED_TYPE_ORDERED;
else if (!strncmp(optarg, "atomic", 6))
rsrc->sched_type = RTE_SCHED_TYPE_ATOMIC;
else if (!strncmp(optarg, "parallel", 8))
rsrc->sched_type = RTE_SCHED_TYPE_PARALLEL;
}
static int
l2fwd_parse_port_pair_config(const char *q_arg, struct l2fwd_resources *rsrc)
{
enum fieldnames {
FLD_PORT1 = 0,
FLD_PORT2,
_NUM_FLD
};
const char *p, *p0 = q_arg;
uint16_t int_fld[_NUM_FLD];
char *str_fld[_NUM_FLD];
uint16_t port_pair = 0;
unsigned int size;
char s[256];
char *end;
int i;
while ((p = strchr(p0, '(')) != NULL) {
++p;
p0 = strchr(p, ')');
if (p0 == NULL)
return -1;
size = p0 - p;
if (size >= sizeof(s))
return -1;
memcpy(s, p, size);
if (rte_strsplit(s, sizeof(s), str_fld,
_NUM_FLD, ',') != _NUM_FLD)
return -1;
for (i = 0; i < _NUM_FLD; i++) {
errno = 0;
int_fld[i] = strtoul(str_fld[i], &end, 0);
if (errno != 0 || end == str_fld[i] ||
int_fld[i] >= RTE_MAX_ETHPORTS)
return -1;
}
if (port_pair >= RTE_MAX_ETHPORTS / 2) {
printf("exceeded max number of port pair params: Current %d Max = %d\n",
port_pair, RTE_MAX_ETHPORTS / 2);
return -1;
}
if ((rsrc->dst_ports[int_fld[FLD_PORT1]] != UINT32_MAX) ||
(rsrc->dst_ports[int_fld[FLD_PORT2]] != UINT32_MAX)) {
printf("Duplicate port pair (%d,%d) config\n",
int_fld[FLD_PORT1], int_fld[FLD_PORT2]);
return -1;
}
rsrc->dst_ports[int_fld[FLD_PORT1]] = int_fld[FLD_PORT2];
rsrc->dst_ports[int_fld[FLD_PORT2]] = int_fld[FLD_PORT1];
port_pair++;
}
rsrc->port_pairs = true;
return 0;
}
static const char short_options[] =
"p:" /* portmask */
"q:" /* number of queues */
"T:" /* timer period */
;
#define CMD_LINE_OPT_MAC_UPDATING "mac-updating"
#define CMD_LINE_OPT_NO_MAC_UPDATING "no-mac-updating"
#define CMD_LINE_OPT_MODE "mode"
#define CMD_LINE_OPT_EVENTQ_SCHED "eventq-sched"
#define CMD_LINE_OPT_PORT_PAIR_CONF "config"
enum {
/* long options mapped to a short option */
/* first long only option value must be >= 256, so that we won't
* conflict with short options
*/
CMD_LINE_OPT_MIN_NUM = 256,
CMD_LINE_OPT_MODE_NUM,
CMD_LINE_OPT_EVENTQ_SCHED_NUM,
CMD_LINE_OPT_PORT_PAIR_CONF_NUM,
};
/* Parse the argument given in the command line of the application */
static int
l2fwd_event_parse_args(int argc, char **argv, struct l2fwd_resources *rsrc)
{
int mac_updating = 1;
struct option lgopts[] = {
{ CMD_LINE_OPT_MAC_UPDATING, no_argument, &mac_updating, 1},
{ CMD_LINE_OPT_NO_MAC_UPDATING, no_argument, &mac_updating, 0},
{ CMD_LINE_OPT_MODE, required_argument, NULL,
CMD_LINE_OPT_MODE_NUM},
{ CMD_LINE_OPT_EVENTQ_SCHED, required_argument, NULL,
CMD_LINE_OPT_EVENTQ_SCHED_NUM},
{ CMD_LINE_OPT_PORT_PAIR_CONF, required_argument, NULL,
CMD_LINE_OPT_PORT_PAIR_CONF_NUM},
{NULL, 0, 0, 0}
};
int opt, ret, timer_secs;
char *prgname = argv[0];
uint16_t port_id;
int option_index;
char **argvopt;
/* reset l2fwd_dst_ports */
for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++)
rsrc->dst_ports[port_id] = UINT32_MAX;
argvopt = argv;
while ((opt = getopt_long(argc, argvopt, short_options,
lgopts, &option_index)) != EOF) {
switch (opt) {
/* portmask */
case 'p':
rsrc->enabled_port_mask =
l2fwd_event_parse_portmask(optarg);
if (rsrc->enabled_port_mask == 0) {
printf("invalid portmask\n");
l2fwd_event_usage(prgname);
return -1;
}
break;
/* nqueue */
case 'q':
rsrc->rx_queue_per_lcore =
l2fwd_event_parse_nqueue(optarg);
if (rsrc->rx_queue_per_lcore == 0) {
printf("invalid queue number\n");
l2fwd_event_usage(prgname);
return -1;
}
break;
/* timer period */
case 'T':
timer_secs = l2fwd_event_parse_timer_period(optarg);
if (timer_secs < 0) {
printf("invalid timer period\n");
l2fwd_event_usage(prgname);
return -1;
}
rsrc->timer_period = timer_secs;
/* convert to number of cycles */
rsrc->timer_period *= rte_get_timer_hz();
break;
case CMD_LINE_OPT_MODE_NUM:
l2fwd_event_parse_mode(optarg, rsrc);
break;
case CMD_LINE_OPT_EVENTQ_SCHED_NUM:
l2fwd_event_parse_eventq_sched(optarg, rsrc);
break;
case CMD_LINE_OPT_PORT_PAIR_CONF_NUM:
ret = l2fwd_parse_port_pair_config(optarg, rsrc);
if (ret) {
printf("Invalid port pair config\n");
l2fwd_event_usage(prgname);
return -1;
}
break;
/* long options */
case 0:
break;
default:
l2fwd_event_usage(prgname);
return -1;
}
}
rsrc->mac_updating = mac_updating;
if (optind >= 0)
argv[optind-1] = prgname;
ret = optind-1;
optind = 1; /* reset getopt lib */
return ret;
}
/*
* Check port pair config with enabled port mask,
* and for valid port pair combinations.
*/
static int
check_port_pair_config(struct l2fwd_resources *rsrc)
{
uint32_t port_pair_mask = 0;
uint32_t portid;
uint16_t index;
for (index = 0; index < rte_eth_dev_count_avail(); index++) {
if ((rsrc->enabled_port_mask & (1 << index)) == 0 ||
(port_pair_mask & (1 << index)))
continue;
portid = rsrc->dst_ports[index];
if (portid == UINT32_MAX) {
printf("port %u is enabled in but no valid port pair\n",
index);
return -1;
}
if (!rte_eth_dev_is_valid_port(index)) {
printf("port %u is not valid\n", index);
return -1;
}
if (!rte_eth_dev_is_valid_port(portid)) {
printf("port %u is not valid\n", portid);
return -1;
}
if (port_pair_mask & (1 << portid) &&
rsrc->dst_ports[portid] != index) {
printf("port %u is used in other port pairs\n", portid);
return -1;
}
port_pair_mask |= (1 << portid);
port_pair_mask |= (1 << index);
}
return 0;
}
static int
l2fwd_launch_one_lcore(void *args)
{
struct l2fwd_resources *rsrc = args;
struct l2fwd_poll_resources *poll_rsrc = rsrc->poll_rsrc;
struct l2fwd_event_resources *evt_rsrc = rsrc->evt_rsrc;
if (rsrc->event_mode)
evt_rsrc->ops.l2fwd_event_loop(rsrc);
else
poll_rsrc->poll_main_loop(rsrc);
return 0;
}
/* Check the link status of all ports in up to 9s, and print them finally */
static void
check_all_ports_link_status(struct l2fwd_resources *rsrc,
uint32_t port_mask)
{
#define CHECK_INTERVAL 100 /* 100ms */
#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */
uint16_t port_id;
uint8_t count, all_ports_up, print_flag = 0;
struct rte_eth_link link;
int ret;
printf("\nChecking link status...");
fflush(stdout);
for (count = 0; count <= MAX_CHECK_TIME; count++) {
if (rsrc->force_quit)
return;
all_ports_up = 1;
RTE_ETH_FOREACH_DEV(port_id) {
if (rsrc->force_quit)
return;
if ((port_mask & (1 << port_id)) == 0)
continue;
memset(&link, 0, sizeof(link));
ret = rte_eth_link_get_nowait(port_id, &link);
if (ret < 0) {
all_ports_up = 0;
if (print_flag == 1)
printf("Port %u link get failed: %s\n",
port_id, rte_strerror(-ret));
continue;
}
/* print link status if flag set */
if (print_flag == 1) {
if (link.link_status)
printf(
"Port%d Link Up. Speed %u Mbps - %s\n",
port_id, link.link_speed,
(link.link_duplex == ETH_LINK_FULL_DUPLEX) ?
("full-duplex") : ("half-duplex"));
else
printf("Port %d Link Down\n", port_id);
continue;
}
/* clear all_ports_up flag if any link down */
if (link.link_status == ETH_LINK_DOWN) {
all_ports_up = 0;
break;
}
}
/* after finally printing all link status, get out */
if (print_flag == 1)
break;
if (all_ports_up == 0) {
printf(".");
fflush(stdout);
rte_delay_ms(CHECK_INTERVAL);
}
/* set the print_flag if all ports up or timeout */
if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) {
print_flag = 1;
printf("done\n");
}
}
}
/* Print out statistics on packets dropped */
static void
print_stats(struct l2fwd_resources *rsrc)
{
uint64_t total_packets_dropped, total_packets_tx, total_packets_rx;
uint32_t port_id;
total_packets_dropped = 0;
total_packets_tx = 0;
total_packets_rx = 0;
const char clr[] = {27, '[', '2', 'J', '\0' };
const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0' };
/* Clear screen and move to top left */
printf("%s%s", clr, topLeft);
printf("\nPort statistics ====================================");
for (port_id = 0; port_id < RTE_MAX_ETHPORTS; port_id++) {
/* skip disabled ports */
if ((rsrc->enabled_port_mask & (1 << port_id)) == 0)
continue;
printf("\nStatistics for port %u ------------------------------"
"\nPackets sent: %29"PRIu64
"\nPackets received: %25"PRIu64
"\nPackets dropped: %26"PRIu64,
port_id,
rsrc->port_stats[port_id].tx,
rsrc->port_stats[port_id].rx,
rsrc->port_stats[port_id].dropped);
total_packets_dropped +=
rsrc->port_stats[port_id].dropped;
total_packets_tx += rsrc->port_stats[port_id].tx;
total_packets_rx += rsrc->port_stats[port_id].rx;
}
if (rsrc->event_mode) {
struct l2fwd_event_resources *evt_rsrc = rsrc->evt_rsrc;
struct rte_event_eth_rx_adapter_stats rx_adptr_stats;
struct rte_event_eth_tx_adapter_stats tx_adptr_stats;
int ret, i;
for (i = 0; i < evt_rsrc->rx_adptr.nb_rx_adptr; i++) {
ret = rte_event_eth_rx_adapter_stats_get(
evt_rsrc->rx_adptr.rx_adptr[i],
&rx_adptr_stats);
if (ret < 0)
continue;
printf("\nRx adapter[%d] statistics===================="
"\nReceive queue poll count: %17"PRIu64
"\nReceived packet count: %20"PRIu64
"\nEventdev enqueue count: %19"PRIu64
"\nEventdev enqueue retry count: %13"PRIu64
"\nReceived packet dropped count: %12"PRIu64
"\nRx enqueue start timestamp: %15"PRIu64
"\nRx enqueue block cycles: %18"PRIu64
"\nRx enqueue unblock timestamp: %13"PRIu64,
evt_rsrc->rx_adptr.rx_adptr[i],
rx_adptr_stats.rx_poll_count,
rx_adptr_stats.rx_packets,
rx_adptr_stats.rx_enq_count,
rx_adptr_stats.rx_enq_retry,
rx_adptr_stats.rx_dropped,
rx_adptr_stats.rx_enq_start_ts,
rx_adptr_stats.rx_enq_block_cycles,
rx_adptr_stats.rx_enq_end_ts);
}
for (i = 0; i < evt_rsrc->tx_adptr.nb_tx_adptr; i++) {
ret = rte_event_eth_tx_adapter_stats_get(
evt_rsrc->tx_adptr.tx_adptr[i],
&tx_adptr_stats);
if (ret < 0)
continue;
printf("\nTx adapter[%d] statistics===================="
"\nNumber of transmit retries: %15"PRIu64
"\nNumber of packets transmitted: %12"PRIu64
"\nNumber of packets dropped: %16"PRIu64,
evt_rsrc->tx_adptr.tx_adptr[i],
tx_adptr_stats.tx_retry,
tx_adptr_stats.tx_packets,
tx_adptr_stats.tx_dropped);
}
}
printf("\nAggregate lcore statistics ========================="
"\nTotal packets sent: %23"PRIu64
"\nTotal packets received: %19"PRIu64
"\nTotal packets dropped: %20"PRIu64,
total_packets_tx,
total_packets_rx,
total_packets_dropped);
printf("\n====================================================\n");
fflush(stdout);
}
static void
l2fwd_event_print_stats(struct l2fwd_resources *rsrc)
{
uint64_t prev_tsc = 0, diff_tsc, cur_tsc, timer_tsc = 0;
const uint64_t timer_period = rsrc->timer_period;
while (!rsrc->force_quit) {
/* if timer is enabled */
if (timer_period > 0) {
cur_tsc = rte_rdtsc();
diff_tsc = cur_tsc - prev_tsc;
/* advance the timer */
timer_tsc += diff_tsc;
/* if timer has reached its timeout */
if (unlikely(timer_tsc >= timer_period)) {
print_stats(rsrc);
/* reset the timer */
timer_tsc = 0;
}
prev_tsc = cur_tsc;
}
}
}
static void
signal_handler(int signum)
{
struct l2fwd_resources *rsrc = l2fwd_get_rsrc();
if (signum == SIGINT || signum == SIGTERM) {
printf("\n\nSignal %d received, preparing to exit...\n",
signum);
rsrc->force_quit = true;
}
}
int
main(int argc, char **argv)
{
struct l2fwd_resources *rsrc;
uint16_t nb_ports_available = 0;
uint32_t nb_ports_in_mask = 0;
uint16_t port_id, last_port;
uint32_t nb_mbufs;
uint16_t nb_ports;
int i, ret;
/* init EAL */
ret = rte_eal_init(argc, argv);
if (ret < 0)
rte_panic("Invalid EAL arguments\n");
argc -= ret;
argv += ret;
rsrc = l2fwd_get_rsrc();
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
/* parse application arguments (after the EAL ones) */
ret = l2fwd_event_parse_args(argc, argv, rsrc);
if (ret < 0)
rte_panic("Invalid L2FWD arguments\n");
printf("MAC updating %s\n", rsrc->mac_updating ? "enabled" :
"disabled");
nb_ports = rte_eth_dev_count_avail();
if (nb_ports == 0)
rte_panic("No Ethernet ports - bye\n");
/* check port mask to possible port mask */
if (rsrc->enabled_port_mask & ~((1 << nb_ports) - 1))
rte_panic("Invalid portmask; possible (0x%x)\n",
(1 << nb_ports) - 1);
if (!rsrc->port_pairs) {
last_port = 0;
/*
* Each logical core is assigned a dedicated TX queue on each
* port.
*/
RTE_ETH_FOREACH_DEV(port_id) {
/* skip ports that are not enabled */
if ((rsrc->enabled_port_mask & (1 << port_id)) == 0)
continue;
if (nb_ports_in_mask % 2) {
rsrc->dst_ports[port_id] = last_port;
rsrc->dst_ports[last_port] = port_id;
} else {
last_port = port_id;
}
nb_ports_in_mask++;
}
if (nb_ports_in_mask % 2) {
printf("Notice: odd number of ports in portmask.\n");
rsrc->dst_ports[last_port] = last_port;
}
} else {
if (check_port_pair_config(rsrc) < 0)
rte_panic("Invalid port pair config\n");
}
nb_mbufs = RTE_MAX(nb_ports * (RTE_TEST_RX_DESC_DEFAULT +
RTE_TEST_TX_DESC_DEFAULT +
MAX_PKT_BURST + rte_lcore_count() *
MEMPOOL_CACHE_SIZE), 8192U);
/* create the mbuf pool */
rsrc->pktmbuf_pool = rte_pktmbuf_pool_create("mbuf_pool",
nb_mbufs, MEMPOOL_CACHE_SIZE, 0,
RTE_MBUF_DEFAULT_BUF_SIZE, rte_socket_id());
if (rsrc->pktmbuf_pool == NULL)
rte_panic("Cannot init mbuf pool\n");
nb_ports_available = l2fwd_event_init_ports(rsrc);
if (!nb_ports_available)
rte_panic("All available ports are disabled. Please set portmask.\n");
/* Configure eventdev parameters if required */
if (rsrc->event_mode)
l2fwd_event_resource_setup(rsrc);
else
l2fwd_poll_resource_setup(rsrc);
/* initialize port stats */
memset(&rsrc->port_stats, 0,
sizeof(struct l2fwd_port_statistics));
/* All settings are done. Now enable eth devices */
RTE_ETH_FOREACH_DEV(port_id) {
/* skip ports that are not enabled */
if ((rsrc->enabled_port_mask &
(1 << port_id)) == 0)
continue;
ret = rte_eth_dev_start(port_id);
if (ret < 0)
rte_panic("rte_eth_dev_start:err=%d, port=%u\n", ret,
port_id);
}
if (rsrc->event_mode)
l2fwd_event_service_setup(rsrc);
check_all_ports_link_status(rsrc, rsrc->enabled_port_mask);
/* launch per-lcore init on every lcore */
rte_eal_mp_remote_launch(l2fwd_launch_one_lcore, rsrc,
SKIP_MASTER);
l2fwd_event_print_stats(rsrc);
if (rsrc->event_mode) {
struct l2fwd_event_resources *evt_rsrc =
rsrc->evt_rsrc;
for (i = 0; i < evt_rsrc->rx_adptr.nb_rx_adptr; i++)
rte_event_eth_rx_adapter_stop(
evt_rsrc->rx_adptr.rx_adptr[i]);
for (i = 0; i < evt_rsrc->tx_adptr.nb_tx_adptr; i++)
rte_event_eth_tx_adapter_stop(
evt_rsrc->tx_adptr.tx_adptr[i]);
RTE_ETH_FOREACH_DEV(port_id) {
if ((rsrc->enabled_port_mask &
(1 << port_id)) == 0)
continue;
rte_eth_dev_stop(port_id);
}
rte_eal_mp_wait_lcore();
RTE_ETH_FOREACH_DEV(port_id) {
if ((rsrc->enabled_port_mask &
(1 << port_id)) == 0)
continue;
rte_eth_dev_close(port_id);
}
rte_event_dev_stop(evt_rsrc->event_d_id);
rte_event_dev_close(evt_rsrc->event_d_id);
} else {
rte_eal_mp_wait_lcore();
RTE_ETH_FOREACH_DEV(port_id) {
if ((rsrc->enabled_port_mask &
(1 << port_id)) == 0)
continue;
printf("Closing port %d...", port_id);
rte_eth_dev_stop(port_id);
rte_eth_dev_close(port_id);
printf(" Done\n");
}
}
printf("Bye...\n");
return 0;
}