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 <[email protected]>
---
 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

Reply via email to