DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC 1/2] app/testpmd: add option to set flow control mode
@ 2026-10-17 19:48 Stephen Hemminger
  2026-10-17 19:48 ` [RFC 2/2] app/testpmd: add option to limit run time Stephen Hemminger
  0 siblings, 1 reply; 2+ messages in thread
From: Stephen Hemminger @ 2026-10-17 19:48 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Aman Singh

When doing benchmarking it is important to disable NIC
flow control. Testpmd can do this only from the interactive
command line, there was no option to do this from a script.

Add --flow-ctrl=none|rx|tx|full, applied each time a port
is started. Devices without flow control support are skipped.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 app/test-pmd/parameters.c             | 18 +++++++++++++++++
 app/test-pmd/testpmd.c                | 28 +++++++++++++++++++++++++++
 app/test-pmd/testpmd.h                |  1 +
 doc/guides/testpmd_app_ug/run_app.rst |  5 +++++
 4 files changed, 52 insertions(+)

diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index 833b14eda4..f031dd7445 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -207,6 +207,8 @@ enum {
 	TESTPMD_OPT_NO_LSC_INTERRUPT_NUM,
 #define TESTPMD_OPT_NO_RMV_INTERRUPT "no-rmv-interrupt"
 	TESTPMD_OPT_NO_RMV_INTERRUPT_NUM,
+#define TESTPMD_OPT_FLOW_CTRL "flow-ctrl"
+	TESTPMD_OPT_FLOW_CTRL_NUM,
 #define TESTPMD_OPT_PRINT_EVENT "print-event"
 	TESTPMD_OPT_PRINT_EVENT_NUM,
 #define TESTPMD_OPT_MASK_EVENT "mask-event"
@@ -357,6 +359,7 @@ static const struct option long_options[] = {
 	NO_ARG(TESTPMD_OPT_DISABLE_DEVICE_START),
 	NO_ARG(TESTPMD_OPT_NO_LSC_INTERRUPT),
 	NO_ARG(TESTPMD_OPT_NO_RMV_INTERRUPT),
+	REQUIRED_ARG(TESTPMD_OPT_FLOW_CTRL),
 	REQUIRED_ARG(TESTPMD_OPT_PRINT_EVENT),
 	REQUIRED_ARG(TESTPMD_OPT_MASK_EVENT),
 	REQUIRED_ARG(TESTPMD_OPT_TX_OFFLOADS),
@@ -515,6 +518,8 @@ usage(char* progname)
 	printf("  --disable-device-start: do not automatically start port\n");
 	printf("  --no-lsc-interrupt: disable link status change interrupt.\n");
 	printf("  --no-rmv-interrupt: disable device removal interrupt.\n");
+	printf("  --flow-ctrl=none|rx|tx|full: set Ethernet flow control"
+	       " mode when ports are started.\n");
 #ifdef RTE_LIB_BITRATESTATS
 	printf("  --bitrate-stats=N: set the logical core N to perform "
 		"bit-rate calculation.\n");
@@ -1635,6 +1640,19 @@ launch_args_parse(int argc, char** argv)
 		case TESTPMD_OPT_NO_RMV_INTERRUPT_NUM:
 			rmv_interrupt = 0;
 			break;
+		case TESTPMD_OPT_FLOW_CTRL_NUM:
+			if (!strcmp(optarg, "none"))
+				flow_ctrl_mode = RTE_ETH_FC_NONE;
+			else if (!strcmp(optarg, "rx"))
+				flow_ctrl_mode = RTE_ETH_FC_RX_PAUSE;
+			else if (!strcmp(optarg, "tx"))
+				flow_ctrl_mode = RTE_ETH_FC_TX_PAUSE;
+			else if (!strcmp(optarg, "full"))
+				flow_ctrl_mode = RTE_ETH_FC_FULL;
+			else
+				rte_exit(EXIT_FAILURE,
+					 "flow-ctrl must be none, rx, tx or full\n");
+			break;
 		case TESTPMD_OPT_FLOW_ISOLATE_ALL_NUM:
 			flow_isolate_all = 1;
 			break;
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index d6211a9a8a..e1766eec72 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -446,6 +446,12 @@ uint8_t lsc_interrupt = 1; /* enabled by default */
  */
 uint8_t rmv_interrupt = 1; /* enabled by default */
 
+/*
+ * Ethernet flow control mode applied on port start.
+ * Negative means leave the device default.
+ */
+int flow_ctrl_mode = -1;
+
 uint8_t hot_plug = 0; /**< hotplug disabled by default. */
 
 /* After attach, port setup is called on event or by iterator */
@@ -2973,6 +2979,26 @@ update_bonding_port_dev_conf(portid_t bond_pid)
 #endif
 }
 
+static void
+port_flow_ctrl_apply(portid_t pi)
+{
+	struct rte_eth_fc_conf fc_conf;
+	int ret;
+
+	if (flow_ctrl_mode < 0 || !is_proc_primary())
+		return;
+
+	memset(&fc_conf, 0, sizeof(fc_conf));
+	ret = rte_eth_dev_flow_ctrl_get(pi, &fc_conf);
+	if (ret == 0) {
+		fc_conf.mode = flow_ctrl_mode;
+		ret = rte_eth_dev_flow_ctrl_set(pi, &fc_conf);
+	}
+	if (ret != 0 && ret != -ENOTSUP)
+		fprintf(stderr, "Port %u: failed to set flow control: %s\n",
+			pi, rte_strerror(-ret));
+}
+
 int
 start_port(portid_t pid)
 {
@@ -3229,6 +3255,8 @@ start_port(portid_t pid)
 			fprintf(stderr, "Port %d can not be set into started\n",
 				pi);
 
+		port_flow_ctrl_apply(pi);
+
 		if (eth_macaddr_get_print_err(pi, &port->eth_addr) == 0)
 			printf("Port %d: " RTE_ETHER_ADDR_PRT_FMT "\n", pi,
 					RTE_ETHER_ADDR_BYTES(&port->eth_addr));
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 2ca4aaf086..6d4c3e8f84 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -541,6 +541,7 @@ extern uint8_t no_device_start; /**<set by "--disable-device-start" parameter */
 extern volatile int test_done; /* stop packet forwarding when set to 1. */
 extern uint8_t lsc_interrupt; /**< disabled by "--no-lsc-interrupt" parameter */
 extern uint8_t rmv_interrupt; /**< disabled by "--no-rmv-interrupt" parameter */
+extern int flow_ctrl_mode; /**< set by "--flow-ctrl" parameter */
 extern uint32_t event_print_mask;
 /**< set by "--print-event xxxx" and "--mask-event xxxx parameters */
 extern bool setup_on_probe_event; /**< disabled by port setup-on iterator */
diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index bcfbf33f92..e678058b97 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -224,6 +224,11 @@ The command line options are:
 
     Disable RSS (Receive Side Scaling).
 
+*   ``--flow-ctrl=mode``
+
+    Set the Ethernet flow control mode of all ports each time they are started.
+    The possible values are ``none``, ``rx``, ``tx`` and ``full``.
+
 *   ``--port-topology=mode``
 
     Set port topology, where mode is ``paired`` (the default), ``chained`` or ``loop``.
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [RFC 2/2] app/testpmd: add option to limit run time
  2026-10-17 19:48 [RFC 1/2] app/testpmd: add option to set flow control mode Stephen Hemminger
@ 2026-10-17 19:48 ` Stephen Hemminger
  0 siblings, 0 replies; 2+ messages in thread
From: Stephen Hemminger @ 2026-10-17 19:48 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Aman Singh

When benchmarking it is useful to be able to run testpmd for
a fixed amount of time.  In non-interactive mode testpmd runs
until a signal arrives or something is read from stdin.

Add --run-time=N to stop forwarding and exit after N seconds,
printing the final statistics as on a normal exit.
The stats period loop is folded into the same wait.

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 app/test-pmd/parameters.c             | 18 ++++++++
 app/test-pmd/testpmd.c                | 61 ++++++++++++++++++---------
 app/test-pmd/testpmd.h                |  1 +
 doc/guides/testpmd_app_ug/run_app.rst |  6 +++
 4 files changed, 66 insertions(+), 20 deletions(-)

diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index f031dd7445..c852075dfb 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -61,6 +61,8 @@ enum {
 	TESTPMD_OPT_TX_FIRST_NUM,
 #define TESTPMD_OPT_STATS_PERIOD "stats-period"
 	TESTPMD_OPT_STATS_PERIOD_NUM,
+#define TESTPMD_OPT_RUN_TIME "run-time"
+	TESTPMD_OPT_RUN_TIME_NUM,
 #define TESTPMD_OPT_DISPLAY_XSTATS "display-xstats"
 	TESTPMD_OPT_DISPLAY_XSTATS_NUM,
 #define TESTPMD_OPT_NB_CORES "nb-cores"
@@ -282,6 +284,7 @@ static const struct option long_options[] = {
 	REQUIRED_ARG(TESTPMD_OPT_ETH_PEER),
 	NO_ARG(TESTPMD_OPT_TX_FIRST),
 	REQUIRED_ARG(TESTPMD_OPT_STATS_PERIOD),
+	REQUIRED_ARG(TESTPMD_OPT_RUN_TIME),
 	REQUIRED_ARG(TESTPMD_OPT_DISPLAY_XSTATS),
 	REQUIRED_ARG(TESTPMD_OPT_NB_CORES),
 	REQUIRED_ARG(TESTPMD_OPT_NB_PORTS),
@@ -408,6 +411,8 @@ usage(char* progname)
 	       "(only if interactive is disabled).\n");
 	printf("  --stats-period=PERIOD: statistics will be shown "
 	       "every PERIOD seconds (only if interactive is disabled).\n");
+	printf("  --run-time=N: stop forwarding and exit after N seconds "
+	       "(only if interactive is disabled).\n");
 	printf("  --display-xstats xstat_name1[,...]: comma-separated list of "
 	       "extended statistics to show. Used with --stats-period "
 	       "specified or interactive commands that show Rx/Tx statistics "
@@ -997,6 +1002,19 @@ launch_args_parse(int argc, char** argv)
 				"packets first\n");
 			tx_first = 1;
 			break;
+		case TESTPMD_OPT_RUN_TIME_NUM: {
+			char *end = NULL;
+			unsigned long secs;
+
+			errno = 0;
+			secs = strtoul(optarg, &end, 10);
+			if (optarg[0] == '\0' || *end != '\0' || errno != 0 ||
+					secs == 0 || secs > INT32_MAX)
+				rte_exit(EXIT_FAILURE, "Invalid run-time value\n");
+
+			run_time = secs;
+			break;
+		}
 		case TESTPMD_OPT_STATS_PERIOD_NUM: {
 			char *end = NULL;
 
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index e1766eec72..aaab1a83a6 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -242,6 +242,7 @@ uint16_t mbuf_data_size[MAX_SEGS_BUFFER_SPLIT] = {
 uint32_t param_total_num_mbufs = 0;  /**< number of mbufs in all pools - if
                                       * specified on command-line. */
 uint16_t stats_period; /**< Period to show statistics (disabled by default) */
+uint32_t run_time; /**< Seconds to forward before exit (0 = no limit) */
 
 /** Extended statistics to show. */
 struct rte_eth_xstat_name *xstats_display;
@@ -4556,6 +4557,41 @@ disable_primary_monitor(void)
 }
 #endif
 
+/*
+ * Non-interactive wait: print stats every stats_period seconds
+ * and stop after run_time seconds, or on signal.
+ */
+static void
+forward_wait(void)
+{
+	uint64_t hz = rte_get_timer_hz();
+	uint64_t now = rte_get_timer_cycles();
+	uint64_t end = now + (uint64_t)run_time * hz;
+	uint64_t next_stats = now;
+
+	while (f_quit == 0) {
+		uint64_t wake;
+
+		now = rte_get_timer_cycles();
+		if (run_time != 0 && now >= end)
+			break;
+
+		if (stats_period != 0 && now >= next_stats) {
+			print_stats();
+			next_stats = now + (uint64_t)stats_period * hz;
+		}
+
+		/* wake at least once a second to check for signal */
+		wake = now + hz;
+		if (stats_period != 0 && next_stats < wake)
+			wake = next_stats;
+		if (run_time != 0 && end < wake)
+			wake = end;
+
+		rte_delay_us_sleep((wake - now) * US_PER_S / hz);
+	}
+}
+
 int
 main(int argc, char** argv)
 {
@@ -4650,6 +4686,9 @@ main(int argc, char** argv)
 		rte_exit(EXIT_FAILURE, "--tx-first cannot be used on "
 				"interactive mode.\n");
 
+	if (run_time && interactive)
+		rte_exit(EXIT_FAILURE, "--run-time cannot be used on interactive mode.\n");
+
 	if (tx_first && lsc_interrupt) {
 		fprintf(stderr,
 			"Warning: lsc_interrupt needs to be off when using tx_first. Disabling.\n");
@@ -4755,26 +4794,8 @@ main(int argc, char** argv)
 	} else {
 		printf("No commandline core given, start packet forwarding\n");
 		start_packet_forwarding(tx_first);
-		if (stats_period != 0) {
-			uint64_t prev_time = 0, cur_time, diff_time = 0;
-			uint64_t timer_period;
-
-			/* Convert to number of cycles */
-			timer_period = stats_period * rte_get_timer_hz();
-
-			while (f_quit == 0) {
-				cur_time = rte_get_timer_cycles();
-				diff_time += cur_time - prev_time;
-
-				if (diff_time >= timer_period) {
-					print_stats();
-					/* Reset the timer */
-					diff_time = 0;
-				}
-				/* Sleep to avoid unnecessary checks */
-				prev_time = cur_time;
-				rte_delay_us_sleep(US_PER_S);
-			}
+		if (stats_period != 0 || run_time != 0) {
+			forward_wait();
 		} else {
 			char c;
 
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 6d4c3e8f84..3c9c78a59c 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -625,6 +625,7 @@ extern uint16_t mbuf_data_size[MAX_SEGS_BUFFER_SPLIT];
 extern uint32_t param_total_num_mbufs;
 
 extern uint16_t stats_period;
+extern uint32_t run_time;
 
 extern struct rte_eth_xstat_name *xstats_display;
 extern unsigned int xstats_display_num;
diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index e678058b97..6c6da5319c 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -64,6 +64,12 @@ The command line options are:
     Display statistics every PERIOD seconds, if interactive mode is disabled.
     The default value is 0, which means that the statistics will not be displayed.
 
+*   ``--run-time N``
+
+    Stop forwarding and exit after N seconds, if interactive mode is disabled.
+    Final statistics are displayed as on a normal exit.
+    The default value is 0, which means run until interrupted.
+
 *   ``--display-xstats xstat_name1[,...]``
 
     Display comma-separated list of extended statistics every PERIOD seconds
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-10-17 19:49 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-17 19:48 [RFC 1/2] app/testpmd: add option to set flow control mode Stephen Hemminger
2026-10-17 19:48 ` [RFC 2/2] app/testpmd: add option to limit run time Stephen Hemminger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox