* [PATCH 01/10] app/dma-perf: fix use-after-free
2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
@ 2025-08-12 2:06 ` Chengwen Feng
2025-08-12 2:07 ` [PATCH 02/10] app/dma-perf: add global section for config file Chengwen Feng
` (8 subsequent siblings)
9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-12 2:06 UTC (permalink / raw)
To: thomas, honest.jiang; +Cc: dev, liuyonglong
The test_case->eal_args was pointer the entry of cfgfile, it will be
used later, but the cfgfile was closed in load_configs(). This commit
fix it by using strdup.
Fixes: 623dc9364dc6 ("app/dma-perf: introduce DMA performance test")
Cc: stable@dpdk.org
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
app/test-dma-perf/main.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/test-dma-perf/main.c b/app/test-dma-perf/main.c
index 0586b3e1d0..25a79d1d6c 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -480,6 +480,8 @@ load_configs(const char *path)
section_name, "test_seconds"));
test_case->eal_args = rte_cfgfile_get_entry(cfgfile, section_name, "eal_args");
+ if (test_case->eal_args != NULL)
+ test_case->eal_args = strdup(test_case->eal_args);
test_case->is_valid = true;
}
--
2.17.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 02/10] app/dma-perf: add global section for config file
2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
2025-08-12 2:06 ` [PATCH 01/10] app/dma-perf: fix use-after-free Chengwen Feng
@ 2025-08-12 2:07 ` Chengwen Feng
2025-08-12 2:07 ` [PATCH 03/10] app/dma-perf: use argparse lib to parse argument Chengwen Feng
` (7 subsequent siblings)
9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-12 2:07 UTC (permalink / raw)
To: thomas, honest.jiang; +Cc: dev, liuyonglong
This commit add global section which contains 'eal_args' entry
which specifies the EAL arguments for all testcases currently.
With this commit, users no longer need to enter EAL arguments on the
command line.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
app/test-dma-perf/config.ini | 17 ++++---
app/test-dma-perf/main.c | 89 +++++++++++++++++-------------------
app/test-dma-perf/main.h | 8 +++-
doc/guides/tools/dmaperf.rst | 28 +++++++-----
4 files changed, 77 insertions(+), 65 deletions(-)
diff --git a/app/test-dma-perf/config.ini b/app/test-dma-perf/config.ini
index 61e49dbae5..63a30dc56b 100644
--- a/app/test-dma-perf/config.ini
+++ b/app/test-dma-perf/config.ini
@@ -4,7 +4,13 @@
; Supported test types are DMA_MEM_COPY and CPU_MEM_COPY.
-; Parameters:
+; There are two types of configuration sections: global configuration section and testcase
+; configuration sections.
+
+; The global configuration section contains the "eal_args" entry which specifies the EAL arguments
+; for all testcases.
+
+; The testcase configuration sections contain the following parameters:
; "mem_size" denotes the size of the memory footprint in megabytes (MB) for source and destination.
; "buf_size" denotes the memory size of a single operation in bytes (B).
; "dma_ring_size" denotes the dma ring buffer size. It should be must be a power of two, and between
@@ -59,6 +65,9 @@
; If you do not specify a result file, one will be generated with the same name as the configuration
; file, with the addition of "_result.csv" at the end.
+[GLOBAL]
+eal_args=--in-memory --file-prefix=test -l 9-12
+
[case1]
type=DMA_MEM_COPY
mem_size=10
@@ -71,7 +80,6 @@ cache_flush=0
test_seconds=2
lcore_dma0=lcore=10,dev=0000:00:04.1,dir=mem2mem
lcore_dma1=lcore=11,dev=0000:00:04.2,dir=mem2mem
-eal_args=--in-memory --file-prefix=test
[case2]
type=DMA_MEM_COPY
@@ -87,7 +95,6 @@ cache_flush=0
test_seconds=2
lcore_dma0=lcore=10,dev=0000:00:04.1,dir=mem2mem
lcore_dma1=lcore=11,dev=0000:00:04.2,dir=mem2mem
-eal_args=--in-memory --file-prefix=test
[case3]
skip=1
@@ -103,7 +110,6 @@ test_seconds=2
lcore_dma0=lcore=10,dev=0000:00:04.1,dir=mem2mem
lcore_dma1=lcore=11,dev=0000:00:04.2,dir=dev2mem,raddr=0x200000000,coreid=1,pfid=2,vfid=3
lcore_dma2=lcore=12,dev=0000:00:04.3,dir=mem2dev,raddr=0x300000000,coreid=3,pfid=2,vfid=1
-eal_args=--in-memory --file-prefix=test
[case4]
type=CPU_MEM_COPY
@@ -113,5 +119,4 @@ src_numa_node=0
dst_numa_node=1
cache_flush=0
test_seconds=2
-lcore = 3, 4
-eal_args=--in-memory --no-pci
+lcore = 10, 11
diff --git a/app/test-dma-perf/main.c b/app/test-dma-perf/main.c
index 25a79d1d6c..54a5e573fc 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -23,9 +23,6 @@
#define CSV_HDR_FMT "Case %u : %s,lcore,DMA,DMA ring size,kick batch size,buffer size(B),number of buffers,memory(MB),average cycle,bandwidth(Gbps),MOps\n"
-#define MAX_EAL_PARAM_NB 100
-#define MAX_EAL_PARAM_LEN 1024
-
#define DMA_MEM_COPY "DMA_MEM_COPY"
#define CPU_MEM_COPY "CPU_MEM_COPY"
@@ -45,6 +42,9 @@ enum {
#define MAX_TEST_CASES 16
static struct test_configure test_cases[MAX_TEST_CASES];
+#define GLOBAL_SECTION_NAME "GLOBAL"
+static struct global_configure global_cfg;
+
char output_str[MAX_WORKER_NB + 1][MAX_OUTPUT_STR_LEN];
static FILE *fd;
@@ -288,6 +288,40 @@ static int populate_dma_dev_config(const char *key, const char *value, void *tes
return ret;
}
+static int
+parse_global_config(struct rte_cfgfile *cfgfile)
+{
+ char *tokens[MAX_EAL_ARGV_NB];
+ const char *entry;
+ int token_nb;
+ char *args;
+ int ret;
+ int i;
+
+ ret = rte_cfgfile_num_sections(cfgfile, GLOBAL_SECTION_NAME, strlen(GLOBAL_SECTION_NAME));
+ if (ret != 1) {
+ printf("Error: GLOBAL section not exist or has multiple!\n");
+ return -1;
+ }
+
+ entry = rte_cfgfile_get_entry(cfgfile, GLOBAL_SECTION_NAME, "eal_args");
+ if (entry == NULL) {
+ printf("Error: GLOBAL section must have 'eal_args' entry!\n");
+ return -1;
+ }
+ args = strdup(entry);
+ if (args == NULL) {
+ printf("Error: dup GLOBAL 'eal_args' failed!\n");
+ return -1;
+ }
+ token_nb = rte_strsplit(args, strlen(args), tokens, MAX_EAL_ARGV_NB, ' ');
+ for (i = 0; i < token_nb; i++)
+ global_cfg.eal_argv[i] = tokens[i];
+ global_cfg.eal_argc = i;
+
+ return 0;
+}
+
static uint16_t
load_configs(const char *path)
{
@@ -311,7 +345,10 @@ load_configs(const char *path)
exit(1);
}
- nb_sections = rte_cfgfile_num_sections(cfgfile, NULL, 0);
+ if (parse_global_config(cfgfile) != 0)
+ exit(1);
+
+ nb_sections = rte_cfgfile_num_sections(cfgfile, NULL, 0) - 1;
if (nb_sections > MAX_TEST_CASES) {
printf("Error: The maximum number of cases is %d.\n", MAX_TEST_CASES);
exit(1);
@@ -479,9 +516,6 @@ load_configs(const char *path)
test_case->test_secs = (uint16_t)atoi(rte_cfgfile_get_entry(cfgfile,
section_name, "test_seconds"));
- test_case->eal_args = rte_cfgfile_get_entry(cfgfile, section_name, "eal_args");
- if (test_case->eal_args != NULL)
- test_case->eal_args = strdup(test_case->eal_args);
test_case->is_valid = true;
}
@@ -490,36 +524,6 @@ load_configs(const char *path)
return i;
}
-/* Parse the argument given in the command line of the application */
-static int
-append_eal_args(int argc, char **argv, const char *eal_args, char **new_argv)
-{
- int i;
- char *tokens[MAX_EAL_PARAM_NB];
- char args[MAX_EAL_PARAM_LEN] = {0};
- int token_nb, new_argc = 0;
-
- for (i = 0; i < argc; i++) {
- if ((strcmp(argv[i], CMDLINE_CONFIG_ARG) == 0) ||
- (strcmp(argv[i], CMDLINE_RESULT_ARG) == 0)) {
- i++;
- continue;
- }
- strlcpy(new_argv[new_argc], argv[i], MAX_EAL_PARAM_LEN);
- new_argc++;
- }
-
- if (eal_args) {
- strlcpy(args, eal_args, MAX_EAL_PARAM_LEN);
- token_nb = rte_strsplit(args, strlen(args),
- tokens, MAX_EAL_PARAM_NB, ' ');
- for (i = 0; i < token_nb; i++)
- strlcpy(new_argv[new_argc++], tokens[i], MAX_EAL_PARAM_LEN);
- }
-
- return new_argc;
-}
-
int
main(int argc, char *argv[])
{
@@ -528,17 +532,9 @@ main(int argc, char *argv[])
uint32_t i, nb_lcores;
pid_t cpid, wpid;
int wstatus;
- char args[MAX_EAL_PARAM_NB][MAX_EAL_PARAM_LEN];
- char *pargs[MAX_EAL_PARAM_NB];
char *cfg_path_ptr = NULL;
char *rst_path_ptr = NULL;
char rst_path[PATH_MAX];
- int new_argc;
-
- memset(args, 0, sizeof(args));
-
- for (i = 0; i < RTE_DIM(pargs); i++)
- pargs[i] = args[i];
for (i = 0; i < (uint32_t)argc; i++) {
if (strncmp(argv[i], CMDLINE_CONFIG_ARG, MAX_LONG_OPT_SZ) == 0)
@@ -595,8 +591,7 @@ main(int argc, char *argv[])
} else if (cpid == 0) {
printf("\nRunning case %u\n\n", i + 1);
- new_argc = append_eal_args(argc, argv, test_cases[i].eal_args, pargs);
- ret = rte_eal_init(new_argc, pargs);
+ ret = rte_eal_init(global_cfg.eal_argc, global_cfg.eal_argv);
if (ret < 0)
rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n");
diff --git a/app/test-dma-perf/main.h b/app/test-dma-perf/main.h
index 59eb648b3d..fa3a8ebf2e 100644
--- a/app/test-dma-perf/main.h
+++ b/app/test-dma-perf/main.h
@@ -69,10 +69,16 @@ struct test_configure {
uint8_t cache_flush;
uint32_t nr_buf;
uint16_t test_secs;
- const char *eal_args;
uint8_t scenario_id;
};
+#define MAX_EAL_ARGV_NB 100
+
+struct global_configure {
+ char *eal_argv[MAX_EAL_ARGV_NB];
+ int eal_argc;
+};
+
int mem_copy_benchmark(struct test_configure *cfg);
#endif /* MAIN_H */
diff --git a/doc/guides/tools/dmaperf.rst b/doc/guides/tools/dmaperf.rst
index b7ff41065f..fc37eb6b98 100644
--- a/doc/guides/tools/dmaperf.rst
+++ b/doc/guides/tools/dmaperf.rst
@@ -27,6 +27,9 @@ along with the application to demonstrate all the parameters.
.. code-block:: ini
+ [GLOBAL]
+ eal_args=--in-memory --file-prefix=test -l 9-12
+
[case1]
type=DMA_MEM_COPY
mem_size=10
@@ -39,7 +42,6 @@ along with the application to demonstrate all the parameters.
test_seconds=2
lcore_dma0=lcore=10,dev=0000:00:04.2,dir=mem2mem
lcore_dma0=lcore=11,dev=0000:00:04.3,dir=mem2mem
- eal_args=--in-memory --file-prefix=test
[case2]
type=CPU_MEM_COPY
@@ -49,8 +51,7 @@ along with the application to demonstrate all the parameters.
dst_numa_node=1
cache_flush=0
test_seconds=2
- lcore = 3, 4
- eal_args=--in-memory --no-pci
+ lcore = 10, 11
[case3]
skip=1
@@ -68,11 +69,12 @@ along with the application to demonstrate all the parameters.
lcore_dma0=lcore=10,dev=0000:00:04.1,dir=mem2mem
lcore_dma1=lcore=11,dev=0000:00:04.2,dir=dev2mem,raddr=0x200000000,coreid=1,pfid=2,vfid=3
lcore_dma2=lcore=12,dev=0000:00:04.3,dir=mem2dev,raddr=0x200000000,coreid=1,pfid=2,vfid=3
- eal_args=--in-memory --file-prefix=test
-The configuration file is divided into multiple sections, each section represents a test case.
+The configuration file is divided into two type sections, the first is global configuration
+section; the second is testcase configuration sections which contain multiple sections, each
+section represents a testcase.
The four mandatory variables ``mem_size``, ``buf_size``, ``dma_ring_size``, and ``kick_batch``
-can vary in each test case.
+can vary in each testcase.
The format for this is ``variable=first,last,increment,ADD|MUL``.
This means that the first value of the variable is ``first``,
the last value is ``last``, ``increment`` is the step size,
@@ -88,8 +90,15 @@ Each case can only have one variable change,
and each change will generate a scenario, so each case can have multiple scenarios.
-Configuration Parameters
-~~~~~~~~~~~~~~~~~~~~~~~~
+Global Configuration Parameters
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``eal_args``
+ Specifies the EAL arguments for all testcases.
+
+
+Testcase Configuration Parameters
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
``skip``
To skip a test-case, must be configured as ``1``
@@ -167,9 +176,6 @@ Configuration Parameters
``lcore``
Specifies the lcore for CPU testing.
-``eal_args``
- Specifies the EAL arguments.
-
Running the Application
-----------------------
--
2.17.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 03/10] app/dma-perf: use argparse lib to parse argument
2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
2025-08-12 2:06 ` [PATCH 01/10] app/dma-perf: fix use-after-free Chengwen Feng
2025-08-12 2:07 ` [PATCH 02/10] app/dma-perf: add global section for config file Chengwen Feng
@ 2025-08-12 2:07 ` Chengwen Feng
2025-08-12 2:07 ` [PATCH 04/10] app/dma-perf: refactor output csv Chengwen Feng
` (6 subsequent siblings)
9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-12 2:07 UTC (permalink / raw)
To: thomas, honest.jiang; +Cc: dev, liuyonglong
This commit uses argparse API to parse arguments.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
app/test-dma-perf/main.c | 96 ++++++++++++++++++++++-------------
app/test-dma-perf/meson.build | 2 +-
2 files changed, 63 insertions(+), 35 deletions(-)
diff --git a/app/test-dma-perf/main.c b/app/test-dma-perf/main.c
index 54a5e573fc..fd54f46da2 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -12,6 +12,7 @@
#include <inttypes.h>
#include <libgen.h>
+#include <rte_argparse.h>
#include <rte_eal.h>
#include <rte_cfgfile.h>
#include <rte_string_fns.h>
@@ -26,13 +27,8 @@
#define DMA_MEM_COPY "DMA_MEM_COPY"
#define CPU_MEM_COPY "CPU_MEM_COPY"
-#define CMDLINE_CONFIG_ARG "--config"
-#define CMDLINE_RESULT_ARG "--result"
-
#define MAX_PARAMS_PER_ENTRY 4
-#define MAX_LONG_OPT_SZ 64
-
enum {
TEST_TYPE_NONE = 0,
TEST_TYPE_DMA_MEM_COPY,
@@ -45,6 +41,9 @@ static struct test_configure test_cases[MAX_TEST_CASES];
#define GLOBAL_SECTION_NAME "GLOBAL"
static struct global_configure global_cfg;
+static char *config_path;
+static char *result_path;
+
char output_str[MAX_WORKER_NB + 1][MAX_OUTPUT_STR_LEN];
static FILE *fd;
@@ -524,54 +523,83 @@ load_configs(const char *path)
return i;
}
-int
-main(int argc, char *argv[])
+static int
+parse_args(int argc, char **argv)
{
+ static struct rte_argparse obj = {
+ .prog_name = "test-dma-perf",
+ .usage = "[optional parameters]",
+ .descriptor = NULL,
+ .epilog = NULL,
+ .exit_on_error = true,
+ .args = {
+ { "--config", NULL, "Specify a configuration file",
+ (void *)&config_path, NULL,
+ RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_STR,
+ },
+ { "--result", NULL, "Optional, specify a result file name",
+ (void *)&result_path, NULL,
+ RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_STR,
+ },
+ ARGPARSE_ARG_END(),
+ },
+ };
+ char rst_path[PATH_MAX + 16] = {0};
int ret;
- uint16_t case_nb;
- uint32_t i, nb_lcores;
- pid_t cpid, wpid;
- int wstatus;
- char *cfg_path_ptr = NULL;
- char *rst_path_ptr = NULL;
- char rst_path[PATH_MAX];
-
- for (i = 0; i < (uint32_t)argc; i++) {
- if (strncmp(argv[i], CMDLINE_CONFIG_ARG, MAX_LONG_OPT_SZ) == 0)
- cfg_path_ptr = argv[i + 1];
- if (strncmp(argv[i], CMDLINE_RESULT_ARG, MAX_LONG_OPT_SZ) == 0)
- rst_path_ptr = argv[i + 1];
- }
- if (cfg_path_ptr == NULL) {
+
+ ret = rte_argparse_parse(&obj, argc, argv);
+ if (ret < 0)
+ exit(1);
+
+ if (config_path == NULL) {
printf("Config file not assigned.\n");
- return -1;
+ exit(1);
}
- if (rst_path_ptr == NULL) {
- strlcpy(rst_path, cfg_path_ptr, PATH_MAX);
+
+ if (result_path == NULL) {
+ strlcpy(rst_path, config_path, PATH_MAX);
char *token = strtok(basename(rst_path), ".");
if (token == NULL) {
printf("Config file error.\n");
- return -1;
+ exit(1);
}
strcat(token, "_result.csv");
- rst_path_ptr = rst_path;
+ result_path = strdup(rst_path);
+ if (result_path == NULL) {
+ printf("Generate result file path error.\n");
+ exit(1);
+ }
}
-
- case_nb = load_configs(cfg_path_ptr);
- fd = fopen(rst_path_ptr, "w");
+ fd = fopen(result_path, "w");
if (fd == NULL) {
printf("Open output CSV file error.\n");
- return -1;
+ exit(1);
}
fclose(fd);
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ uint32_t i, nb_lcores;
+ pid_t cpid, wpid;
+ uint16_t case_nb;
+ int wstatus;
+ int ret;
+
+ parse_args(argc, argv);
+
+ case_nb = load_configs(config_path);
+
printf("Running cases...\n");
for (i = 0; i < case_nb; i++) {
if (test_cases[i].is_skip) {
printf("Test case %d configured to be skipped.\n\n", i + 1);
snprintf(output_str[0], MAX_OUTPUT_STR_LEN, "Skip the test-case %d\n",
i + 1);
- if (open_output_csv(rst_path_ptr))
+ if (open_output_csv(result_path))
return 0;
continue;
}
@@ -579,7 +607,7 @@ main(int argc, char *argv[])
if (!test_cases[i].is_valid) {
printf("Invalid test case %d.\n\n", i + 1);
snprintf(output_str[0], MAX_OUTPUT_STR_LEN, "Invalid case %d\n", i + 1);
- if (open_output_csv(rst_path_ptr))
+ if (open_output_csv(result_path))
return 0;
continue;
}
@@ -601,7 +629,7 @@ main(int argc, char *argv[])
rte_exit(EXIT_FAILURE,
"There should be at least 2 worker lcores.\n");
- fd = fopen(rst_path_ptr, "a");
+ fd = fopen(result_path, "a");
if (!fd) {
printf("Open output CSV file error.\n");
return 0;
diff --git a/app/test-dma-perf/meson.build b/app/test-dma-perf/meson.build
index 40d6b7f8e4..02af3128d8 100644
--- a/app/test-dma-perf/meson.build
+++ b/app/test-dma-perf/meson.build
@@ -7,7 +7,7 @@ if is_windows
subdir_done()
endif
-deps += ['dmadev', 'mbuf', 'cfgfile']
+deps += ['dmadev', 'mbuf', 'cfgfile', 'argparse']
sources = files(
'main.c',
--
2.17.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 04/10] app/dma-perf: refactor output csv
2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
` (2 preceding siblings ...)
2025-08-12 2:07 ` [PATCH 03/10] app/dma-perf: use argparse lib to parse argument Chengwen Feng
@ 2025-08-12 2:07 ` Chengwen Feng
2025-08-12 2:07 ` [PATCH 05/10] app/dma-perf: support list DMA devices Chengwen Feng
` (5 subsequent siblings)
9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-12 2:07 UTC (permalink / raw)
To: thomas, honest.jiang; +Cc: dev, liuyonglong
The original implement involved many functions and global variables,
This commit refactor it.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
app/test-dma-perf/benchmark.c | 7 ++-
app/test-dma-perf/main.c | 92 +++++++++++++----------------------
app/test-dma-perf/main.h | 4 +-
3 files changed, 38 insertions(+), 65 deletions(-)
diff --git a/app/test-dma-perf/benchmark.c b/app/test-dma-perf/benchmark.c
index 6d617ea200..b798199dc1 100644
--- a/app/test-dma-perf/benchmark.c
+++ b/app/test-dma-perf/benchmark.c
@@ -119,11 +119,11 @@ output_result(struct test_configure *cfg, struct lcore_params *para,
printf("Average Bandwidth: %.3lf Gbps, MOps: %.3lf\n", bandwidth, mops);
if (cfg->is_dma)
- snprintf(output_str[lcore_id], MAX_OUTPUT_STR_LEN, CSV_LINE_DMA_FMT,
+ output_csv(CSV_LINE_DMA_FMT,
scenario_id, lcore_id, dma_name, ring_size, kick_batch, buf_size,
nr_buf, memory, ave_cycle, bandwidth, mops);
else
- snprintf(output_str[lcore_id], MAX_OUTPUT_STR_LEN, CSV_LINE_CPU_FMT,
+ output_csv(CSV_LINE_CPU_FMT,
scenario_id, lcore_id, buf_size,
nr_buf, memory, ave_cycle, bandwidth, mops);
}
@@ -863,8 +863,7 @@ mem_copy_benchmark(struct test_configure *cfg)
}
printf("\nAverage Cycles/op per worker: %.1lf, Total Bandwidth: %.3lf Gbps, Total MOps: %.3lf\n",
(avg_cycles_total * (float) 1.0) / nb_workers, bandwidth_total, mops_total);
- snprintf(output_str[MAX_WORKER_NB], MAX_OUTPUT_STR_LEN, CSV_TOTAL_LINE_FMT,
- cfg->scenario_id, nr_buf, memory * nb_workers,
+ output_csv(CSV_TOTAL_LINE_FMT, cfg->scenario_id, nr_buf, memory * nb_workers,
(avg_cycles_total * (float) 1.0) / nb_workers, bandwidth_total, mops_total);
out:
diff --git a/app/test-dma-perf/main.c b/app/test-dma-perf/main.c
index fd54f46da2..7c121208dd 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -44,60 +44,52 @@ static struct global_configure global_cfg;
static char *config_path;
static char *result_path;
-char output_str[MAX_WORKER_NB + 1][MAX_OUTPUT_STR_LEN];
-
-static FILE *fd;
-
-static void
-output_csv(bool need_blankline)
+__rte_format_printf(1, 2)
+void
+output_csv(const char *fmt, ...)
{
- uint32_t i;
+#define MAX_OUTPUT_STR_LEN 512
+ char str[MAX_OUTPUT_STR_LEN] = {0};
+ va_list ap;
+ FILE *fd;
- if (need_blankline) {
- fprintf(fd, ",,,,,,,,\n");
- fprintf(fd, ",,,,,,,,\n");
+ fd = fopen(result_path, "a");
+ if (fd == NULL) {
+ printf("Open output CSV file error.\n");
+ return;
}
- for (i = 0; i < RTE_DIM(output_str); i++) {
- if (output_str[i][0]) {
- fprintf(fd, "%s", output_str[i]);
- output_str[i][0] = '\0';
- }
- }
+ va_start(ap, fmt);
+ vsnprintf(str, MAX_OUTPUT_STR_LEN, fmt, ap);
+ va_end(ap);
+
+ fprintf(fd, "%s", str);
fflush(fd);
+ fclose(fd);
}
static void
-output_env_info(void)
+output_blanklines(int lines)
{
- snprintf(output_str[0], MAX_OUTPUT_STR_LEN, "Test Environment:\n");
- snprintf(output_str[1], MAX_OUTPUT_STR_LEN, "CPU frequency,%.3lf Ghz",
- rte_get_timer_hz() / 1000000000.0);
-
- output_csv(true);
+ int i;
+ for (i = 0; i < lines; i++)
+ output_csv("%s\n", ",,,,,,,,");
}
static void
-output_header(uint32_t case_id, struct test_configure *case_cfg)
+output_env_info(void)
{
- snprintf(output_str[0], MAX_OUTPUT_STR_LEN,
- CSV_HDR_FMT, case_id, case_cfg->test_type_str);
-
- output_csv(true);
+ output_blanklines(2);
+ output_csv("Test Environment:\n"
+ "CPU frequency,%.3lf Ghz\n", rte_get_timer_hz() / 1000000000.0);
+ output_blanklines(1);
}
-static int
-open_output_csv(const char *rst_path_ptr)
+static void
+output_header(uint32_t case_id, struct test_configure *case_cfg)
{
- fd = fopen(rst_path_ptr, "a");
- if (!fd) {
- printf("Open output CSV file error.\n");
- return 1;
- }
- output_csv(true);
- fclose(fd);
- return 0;
+ output_csv(CSV_HDR_FMT, case_id, case_cfg->test_type_str);
}
static int
@@ -121,7 +113,6 @@ run_test_case(struct test_configure *case_cfg)
static void
run_test(uint32_t case_id, struct test_configure *case_cfg)
{
- uint32_t i;
uint32_t nb_lcores = rte_lcore_count();
struct test_configure_entry *mem_size = &case_cfg->mem_size;
struct test_configure_entry *buf_size = &case_cfg->buf_size;
@@ -130,9 +121,6 @@ run_test(uint32_t case_id, struct test_configure *case_cfg)
struct test_configure_entry dummy = { 0 };
struct test_configure_entry *var_entry = &dummy;
- for (i = 0; i < RTE_DIM(output_str); i++)
- memset(output_str[i], 0, MAX_OUTPUT_STR_LEN);
-
if (nb_lcores <= case_cfg->num_worker) {
printf("Case %u: Not enough lcores.\n", case_id);
return;
@@ -162,8 +150,6 @@ run_test(uint32_t case_id, struct test_configure *case_cfg)
if (run_test_case(case_cfg) < 0)
printf("\nTest fails! skipping this scenario.\n");
- else
- output_csv(false);
if (var_entry->op == OP_ADD)
var_entry->cur += var_entry->incr;
@@ -545,6 +531,7 @@ parse_args(int argc, char **argv)
},
};
char rst_path[PATH_MAX + 16] = {0};
+ FILE *fd;
int ret;
ret = rte_argparse_parse(&obj, argc, argv);
@@ -597,18 +584,15 @@ main(int argc, char *argv[])
for (i = 0; i < case_nb; i++) {
if (test_cases[i].is_skip) {
printf("Test case %d configured to be skipped.\n\n", i + 1);
- snprintf(output_str[0], MAX_OUTPUT_STR_LEN, "Skip the test-case %d\n",
- i + 1);
- if (open_output_csv(result_path))
- return 0;
+ output_blanklines(2);
+ output_csv("Skip the test-case %d\n", i + 1);
continue;
}
if (!test_cases[i].is_valid) {
printf("Invalid test case %d.\n\n", i + 1);
- snprintf(output_str[0], MAX_OUTPUT_STR_LEN, "Invalid case %d\n", i + 1);
- if (open_output_csv(result_path))
- return 0;
+ output_blanklines(2);
+ output_csv("Invalid case %d\n", i + 1);
continue;
}
@@ -629,12 +613,6 @@ main(int argc, char *argv[])
rte_exit(EXIT_FAILURE,
"There should be at least 2 worker lcores.\n");
- fd = fopen(result_path, "a");
- if (!fd) {
- printf("Open output CSV file error.\n");
- return 0;
- }
-
output_env_info();
run_test(i + 1, &test_cases[i]);
@@ -642,8 +620,6 @@ main(int argc, char *argv[])
/* clean up the EAL */
rte_eal_cleanup();
- fclose(fd);
-
printf("\nCase %u completed.\n\n", i + 1);
exit(EXIT_SUCCESS);
diff --git a/app/test-dma-perf/main.h b/app/test-dma-perf/main.h
index fa3a8ebf2e..54dc1c4c2a 100644
--- a/app/test-dma-perf/main.h
+++ b/app/test-dma-perf/main.h
@@ -11,12 +11,9 @@
#include <rte_dev.h>
#define MAX_WORKER_NB 128
-#define MAX_OUTPUT_STR_LEN 512
#define MAX_DMA_NB 128
-extern char output_str[MAX_WORKER_NB + 1][MAX_OUTPUT_STR_LEN];
-
typedef enum {
OP_NONE = 0,
OP_ADD,
@@ -79,6 +76,7 @@ struct global_configure {
int eal_argc;
};
+void output_csv(const char *fmt, ...);
int mem_copy_benchmark(struct test_configure *cfg);
#endif /* MAIN_H */
--
2.17.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 05/10] app/dma-perf: support list DMA devices
2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
` (3 preceding siblings ...)
2025-08-12 2:07 ` [PATCH 04/10] app/dma-perf: refactor output csv Chengwen Feng
@ 2025-08-12 2:07 ` Chengwen Feng
2025-08-12 2:07 ` [PATCH 06/10] app/dma-perf: add more global config Chengwen Feng
` (4 subsequent siblings)
9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-12 2:07 UTC (permalink / raw)
To: thomas, honest.jiang; +Cc: dev, liuyonglong
Because the names of dmadevs are different, this commit support list
DMA devices, so that user could quickly know how to set the lcore_dma
parameters.
The command: dpdk-test-dma-perf --config ./config.ini --list-dma
The output like:
DMA device: 0000:7b:00.0-ch0
transfer-capa: mem2mem
max-segs: 0 numa-node: 0
DMA device: 0000:7b:00.0-ch1
transfer-capa: mem2mem
max-segs: 0 numa-node: 0
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
app/test-dma-perf/main.c | 49 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/app/test-dma-perf/main.c b/app/test-dma-perf/main.c
index 7c121208dd..da9ae0a1bb 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -43,6 +43,7 @@ static struct global_configure global_cfg;
static char *config_path;
static char *result_path;
+static bool list_dma;
__rte_format_printf(1, 2)
void
@@ -527,6 +528,10 @@ parse_args(int argc, char **argv)
(void *)&result_path, NULL,
RTE_ARGPARSE_VALUE_REQUIRED, RTE_ARGPARSE_VALUE_TYPE_STR,
},
+ { "--list-dma", NULL, "Optional, list dma devices and exit",
+ (void *)&list_dma, (void *)true,
+ RTE_ARGPARSE_VALUE_NONE, RTE_ARGPARSE_VALUE_TYPE_BOOL,
+ },
ARGPARSE_ARG_END(),
},
};
@@ -567,6 +572,45 @@ parse_args(int argc, char **argv)
return 0;
}
+static void
+list_dma_dev(void)
+{
+ static const struct {
+ uint64_t capability;
+ const char *name;
+ } capa_names[] = {
+ { RTE_DMA_CAPA_MEM_TO_MEM, "mem2mem" },
+ { RTE_DMA_CAPA_MEM_TO_DEV, "mem2dev" },
+ { RTE_DMA_CAPA_DEV_TO_MEM, "dev2mem" },
+ { RTE_DMA_CAPA_DEV_TO_DEV, "dev2dev" },
+ };
+ struct rte_dma_info info;
+ int idx = 0;
+ uint32_t i;
+ int ret;
+
+ ret = rte_eal_init(global_cfg.eal_argc, global_cfg.eal_argv);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE, "Invalid EAL arguments\n");
+
+ RTE_DMA_FOREACH_DEV(idx) {
+ ret = rte_dma_info_get(idx, &info);
+ if (ret != 0)
+ continue;
+
+ printf("\nDMA device name: %s\n", info.dev_name);
+ printf(" transfer-capa:");
+ for (i = 0; i < RTE_DIM(capa_names); i++) {
+ if (capa_names[i].capability & info.dev_capa)
+ printf(" %s", capa_names[i].name);
+ }
+ printf("\n");
+ printf(" max-segs: %u numa-node: %d\n", info.max_sges, info.numa_node);
+ }
+
+ rte_eal_cleanup();
+}
+
int
main(int argc, char *argv[])
{
@@ -580,6 +624,11 @@ main(int argc, char *argv[])
case_nb = load_configs(config_path);
+ if (list_dma) {
+ list_dma_dev();
+ return 0;
+ }
+
printf("Running cases...\n");
for (i = 0; i < case_nb; i++) {
if (test_cases[i].is_skip) {
--
2.17.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 06/10] app/dma-perf: add more global config
2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
` (4 preceding siblings ...)
2025-08-12 2:07 ` [PATCH 05/10] app/dma-perf: support list DMA devices Chengwen Feng
@ 2025-08-12 2:07 ` Chengwen Feng
2025-08-12 2:07 ` [PATCH 07/10] app/dma-perf: remove invalid or redundant field Chengwen Feng
` (3 subsequent siblings)
9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-12 2:07 UTC (permalink / raw)
To: thomas, honest.jiang; +Cc: dev, liuyonglong
This commit add 'cache_flush' and 'test_seconds' to the global
configuration.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
app/test-dma-perf/benchmark.c | 4 ++--
app/test-dma-perf/config.ini | 22 +++++++---------------
app/test-dma-perf/main.c | 21 +++++++++++++++------
app/test-dma-perf/main.h | 6 ++++--
doc/guides/tools/dmaperf.rst | 22 +++++++++-------------
5 files changed, 37 insertions(+), 38 deletions(-)
diff --git a/app/test-dma-perf/benchmark.c b/app/test-dma-perf/benchmark.c
index b798199dc1..d7c8d4c1b0 100644
--- a/app/test-dma-perf/benchmark.c
+++ b/app/test-dma-perf/benchmark.c
@@ -683,7 +683,7 @@ mem_copy_benchmark(struct test_configure *cfg)
unsigned int buf_size = cfg->buf_size.cur;
uint16_t kick_batch = cfg->kick_batch.cur;
uint16_t nb_workers = cfg->num_worker;
- uint16_t test_secs = cfg->test_secs;
+ uint16_t test_secs = global_cfg.test_secs;
float memory = 0;
uint32_t avg_cycles = 0;
uint32_t avg_cycles_total;
@@ -703,7 +703,7 @@ mem_copy_benchmark(struct test_configure *cfg)
if (config_dmadevs(cfg) < 0)
goto out;
- if (cfg->cache_flush == 1) {
+ if (global_cfg.cache_flush > 0) {
cache_flush_buf(srcs, buf_size, nr_buf);
cache_flush_buf(dsts, buf_size, nr_buf);
rte_mb();
diff --git a/app/test-dma-perf/config.ini b/app/test-dma-perf/config.ini
index 63a30dc56b..f74d9b6617 100644
--- a/app/test-dma-perf/config.ini
+++ b/app/test-dma-perf/config.ini
@@ -7,8 +7,11 @@
; There are two types of configuration sections: global configuration section and testcase
; configuration sections.
-; The global configuration section contains the "eal_args" entry which specifies the EAL arguments
-; for all testcases.
+; The global configuration section contains the following parameters:
+; "eal_args" entry which specifies the EAL arguments for all testcases.
+; "cache_flush" is used to determine whether or not the cache should be flushed, with 1 indicating
+; to flush and 0 indicating to not flush.
+; "test_seconds" controls the test time of each case.
; The testcase configuration sections contain the following parameters:
; "mem_size" denotes the size of the memory footprint in megabytes (MB) for source and destination.
@@ -22,11 +25,6 @@
; src_numa_node is used to control the numa node where the source memory is allocated.
; dst_numa_node is used to control the numa node where the destination memory is allocated.
-; cache_flush is used to determine whether or not the cache should be flushed, with 1 indicating to
-; flush and 0 indicating to not flush.
-
-; test_seconds controls the test time of the whole case.
-
; To use DMA for a test, please specify the "lcore_dma" parameter.
; If you have already set the "-l" and "-a" parameters using EAL,
; make sure that the value of "lcore_dma" falls within their range of the values.
@@ -67,6 +65,8 @@
[GLOBAL]
eal_args=--in-memory --file-prefix=test -l 9-12
+cache_flush=0
+test_seconds=2
[case1]
type=DMA_MEM_COPY
@@ -76,8 +76,6 @@ dma_ring_size=1024
kick_batch=32
src_numa_node=0
dst_numa_node=0
-cache_flush=0
-test_seconds=2
lcore_dma0=lcore=10,dev=0000:00:04.1,dir=mem2mem
lcore_dma1=lcore=11,dev=0000:00:04.2,dir=mem2mem
@@ -91,8 +89,6 @@ dma_dst_sge=1
kick_batch=32
src_numa_node=0
dst_numa_node=0
-cache_flush=0
-test_seconds=2
lcore_dma0=lcore=10,dev=0000:00:04.1,dir=mem2mem
lcore_dma1=lcore=11,dev=0000:00:04.2,dir=mem2mem
@@ -105,8 +101,6 @@ dma_ring_size=1024
kick_batch=32
src_numa_node=0
dst_numa_node=0
-cache_flush=0
-test_seconds=2
lcore_dma0=lcore=10,dev=0000:00:04.1,dir=mem2mem
lcore_dma1=lcore=11,dev=0000:00:04.2,dir=dev2mem,raddr=0x200000000,coreid=1,pfid=2,vfid=3
lcore_dma2=lcore=12,dev=0000:00:04.3,dir=mem2dev,raddr=0x300000000,coreid=3,pfid=2,vfid=1
@@ -117,6 +111,4 @@ mem_size=10
buf_size=64,8192,2,MUL
src_numa_node=0
dst_numa_node=1
-cache_flush=0
-test_seconds=2
lcore = 10, 11
diff --git a/app/test-dma-perf/main.c b/app/test-dma-perf/main.c
index da9ae0a1bb..097f36b21c 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -39,7 +39,7 @@ enum {
static struct test_configure test_cases[MAX_TEST_CASES];
#define GLOBAL_SECTION_NAME "GLOBAL"
-static struct global_configure global_cfg;
+struct global_configure global_cfg;
static char *config_path;
static char *result_path;
@@ -305,6 +305,20 @@ parse_global_config(struct rte_cfgfile *cfgfile)
global_cfg.eal_argv[i] = tokens[i];
global_cfg.eal_argc = i;
+ entry = rte_cfgfile_get_entry(cfgfile, GLOBAL_SECTION_NAME, "cache_flush");
+ if (entry == NULL) {
+ printf("Error: GLOBAL section don't has 'cache_flush' entry!\n");
+ return -1;
+ }
+ global_cfg.cache_flush = (uint8_t)atoi(entry);
+
+ entry = rte_cfgfile_get_entry(cfgfile, GLOBAL_SECTION_NAME, "test_seconds");
+ if (entry == NULL) {
+ printf("Error: GLOBAL section don't has 'test_seconds' entry!\n");
+ return -1;
+ }
+ global_cfg.test_secs = (uint16_t)atoi(entry);
+
return 0;
}
@@ -497,11 +511,6 @@ load_configs(const char *path)
continue;
}
- test_case->cache_flush =
- (uint8_t)atoi(rte_cfgfile_get_entry(cfgfile, section_name, "cache_flush"));
- test_case->test_secs = (uint16_t)atoi(rte_cfgfile_get_entry(cfgfile,
- section_name, "test_seconds"));
-
test_case->is_valid = true;
}
diff --git a/app/test-dma-perf/main.h b/app/test-dma-perf/main.h
index 54dc1c4c2a..97c81eb2eb 100644
--- a/app/test-dma-perf/main.h
+++ b/app/test-dma-perf/main.h
@@ -63,9 +63,7 @@ struct test_configure {
uint16_t num_worker;
uint8_t nb_src_sges;
uint8_t nb_dst_sges;
- uint8_t cache_flush;
uint32_t nr_buf;
- uint16_t test_secs;
uint8_t scenario_id;
};
@@ -74,8 +72,12 @@ struct test_configure {
struct global_configure {
char *eal_argv[MAX_EAL_ARGV_NB];
int eal_argc;
+ uint8_t cache_flush;
+ uint16_t test_secs;
};
+extern struct global_configure global_cfg;
+
void output_csv(const char *fmt, ...);
int mem_copy_benchmark(struct test_configure *cfg);
diff --git a/doc/guides/tools/dmaperf.rst b/doc/guides/tools/dmaperf.rst
index fc37eb6b98..7c6906a494 100644
--- a/doc/guides/tools/dmaperf.rst
+++ b/doc/guides/tools/dmaperf.rst
@@ -29,6 +29,8 @@ along with the application to demonstrate all the parameters.
[GLOBAL]
eal_args=--in-memory --file-prefix=test -l 9-12
+ cache_flush=0
+ test_seconds=2
[case1]
type=DMA_MEM_COPY
@@ -38,8 +40,6 @@ along with the application to demonstrate all the parameters.
kick_batch=32
src_numa_node=0
dst_numa_node=0
- cache_flush=0
- test_seconds=2
lcore_dma0=lcore=10,dev=0000:00:04.2,dir=mem2mem
lcore_dma0=lcore=11,dev=0000:00:04.3,dir=mem2mem
@@ -49,8 +49,6 @@ along with the application to demonstrate all the parameters.
buf_size=64,8192,2,MUL
src_numa_node=0
dst_numa_node=1
- cache_flush=0
- test_seconds=2
lcore = 10, 11
[case3]
@@ -64,8 +62,6 @@ along with the application to demonstrate all the parameters.
kick_batch=32
src_numa_node=0
dst_numa_node=0
- cache_flush=0
- test_seconds=2
lcore_dma0=lcore=10,dev=0000:00:04.1,dir=mem2mem
lcore_dma1=lcore=11,dev=0000:00:04.2,dir=dev2mem,raddr=0x200000000,coreid=1,pfid=2,vfid=3
lcore_dma2=lcore=12,dev=0000:00:04.3,dir=mem2dev,raddr=0x200000000,coreid=1,pfid=2,vfid=3
@@ -96,6 +92,13 @@ Global Configuration Parameters
``eal_args``
Specifies the EAL arguments for all testcases.
+``cache_flush``
+ Determines whether the cache should be flushed.
+ ``1`` indicates to flush and ``0`` to not flush.
+
+``test_seconds``
+ Controls the test time for each scenario.
+
Testcase Configuration Parameters
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -131,13 +134,6 @@ Testcase Configuration Parameters
``dst_numa_node``
Controls the NUMA node where the destination memory is allocated.
-``cache_flush``
- Determines whether the cache should be flushed.
- ``1`` indicates to flush and ``0`` to not flush.
-
-``test_seconds``
- Controls the test time for each scenario.
-
``lcore_dma``
Specifies the lcore/DMA mapping and per device specific config.
--
2.17.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 07/10] app/dma-perf: remove invalid or redundant field
2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
` (5 preceding siblings ...)
2025-08-12 2:07 ` [PATCH 06/10] app/dma-perf: add more global config Chengwen Feng
@ 2025-08-12 2:07 ` Chengwen Feng
2025-08-12 2:07 ` [PATCH 08/10] app/dma-perf: refactor load config function Chengwen Feng
` (2 subsequent siblings)
9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-12 2:07 UTC (permalink / raw)
To: thomas, honest.jiang; +Cc: dev, liuyonglong
Remove invalid or redundant fields to make code more clean.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
app/test-dma-perf/benchmark.c | 20 +++++++++-----------
app/test-dma-perf/main.c | 19 ++++---------------
app/test-dma-perf/main.h | 12 ++++++------
3 files changed, 19 insertions(+), 32 deletions(-)
diff --git a/app/test-dma-perf/benchmark.c b/app/test-dma-perf/benchmark.c
index d7c8d4c1b0..663f81ef5c 100644
--- a/app/test-dma-perf/benchmark.c
+++ b/app/test-dma-perf/benchmark.c
@@ -103,7 +103,7 @@ output_result(struct test_configure *cfg, struct lcore_params *para,
uint32_t lcore_id = para->lcore_id;
char *dma_name = para->dma_name;
- if (cfg->is_dma) {
+ if (cfg->test_type == TEST_TYPE_DMA_MEM_COPY) {
printf("lcore %u, DMA %s, DMA Ring Size: %u, Kick Batch Size: %u", lcore_id,
dma_name, ring_size, kick_batch);
if (cfg->is_sg)
@@ -118,7 +118,7 @@ output_result(struct test_configure *cfg, struct lcore_params *para,
ave_cycle, buf_size, nr_buf, memory, rte_get_timer_hz()/1000000000.0);
printf("Average Bandwidth: %.3lf Gbps, MOps: %.3lf\n", bandwidth, mops);
- if (cfg->is_dma)
+ if (cfg->test_type == TEST_TYPE_DMA_MEM_COPY)
output_csv(CSV_LINE_DMA_FMT,
scenario_id, lcore_id, dma_name, ring_size, kick_batch, buf_size,
nr_buf, memory, ave_cycle, bandwidth, mops);
@@ -436,16 +436,15 @@ dummy_free_ext_buf(void *addr, void *opaque)
}
static int
-setup_memory_env(struct test_configure *cfg,
+setup_memory_env(struct test_configure *cfg, uint32_t nr_buf,
struct rte_mbuf ***srcs, struct rte_mbuf ***dsts,
struct rte_dma_sge **src_sges, struct rte_dma_sge **dst_sges)
{
unsigned int cur_buf_size = cfg->buf_size.cur;
unsigned int buf_size = cur_buf_size + RTE_PKTMBUF_HEADROOM;
+ bool is_src_numa_incorrect, is_dst_numa_incorrect;
unsigned int nr_sockets;
- uint32_t nr_buf = cfg->nr_buf;
uint32_t i;
- bool is_src_numa_incorrect, is_dst_numa_incorrect;
nr_sockets = rte_socket_count();
is_src_numa_incorrect = (cfg->src_numa_node >= nr_sockets);
@@ -579,7 +578,7 @@ get_work_function(struct test_configure *cfg)
{
lcore_function_t *fn;
- if (cfg->is_dma) {
+ if (cfg->test_type == TEST_TYPE_DMA_MEM_COPY) {
if (!cfg->is_sg)
fn = do_dma_plain_mem_copy;
else
@@ -694,12 +693,11 @@ mem_copy_benchmark(struct test_configure *cfg)
int ret = 0;
nr_buf = align_buffer_count(cfg, &nr_sgsrc, &nr_sgdst);
- cfg->nr_buf = nr_buf;
- if (setup_memory_env(cfg, &srcs, &dsts, &src_sges, &dst_sges) < 0)
+ if (setup_memory_env(cfg, nr_buf, &srcs, &dsts, &src_sges, &dst_sges) < 0)
goto out;
- if (cfg->is_dma)
+ if (cfg->test_type == TEST_TYPE_DMA_MEM_COPY)
if (config_dmadevs(cfg) < 0)
goto out;
@@ -722,7 +720,7 @@ mem_copy_benchmark(struct test_configure *cfg)
printf("lcore parameters malloc failure for lcore %d\n", lcore_id);
break;
}
- if (cfg->is_dma) {
+ if (cfg->test_type == TEST_TYPE_DMA_MEM_COPY) {
lcores[i]->dma_name = lcore_dma_map->dma_names;
lcores[i]->dev_id = lcore_dma_map->dma_id;
lcores[i]->kick_batch = kick_batch;
@@ -921,7 +919,7 @@ mem_copy_benchmark(struct test_configure *cfg)
lcores[i] = NULL;
}
- if (cfg->is_dma) {
+ if (cfg->test_type == TEST_TYPE_DMA_MEM_COPY) {
for (i = 0; i < nb_workers; i++) {
lcore_dma_map = &cfg->dma_config[i].lcore_dma_map;
printf("Stopping dmadev %d\n", lcore_dma_map->dma_id);
diff --git a/app/test-dma-perf/main.c b/app/test-dma-perf/main.c
index 097f36b21c..e630d2980a 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -29,12 +29,6 @@
#define MAX_PARAMS_PER_ENTRY 4
-enum {
- TEST_TYPE_NONE = 0,
- TEST_TYPE_DMA_MEM_COPY,
- TEST_TYPE_CPU_MEM_COPY
-};
-
#define MAX_TEST_CASES 16
static struct test_configure test_cases[MAX_TEST_CASES];
@@ -90,7 +84,8 @@ output_env_info(void)
static void
output_header(uint32_t case_id, struct test_configure *case_cfg)
{
- output_csv(CSV_HDR_FMT, case_id, case_cfg->test_type_str);
+ static const char * const type_str[] = { "NONE", DMA_MEM_COPY, CPU_MEM_COPY };
+ output_csv(CSV_HDR_FMT, case_id, type_str[case_cfg->test_type]);
}
static int
@@ -104,7 +99,7 @@ run_test_case(struct test_configure *case_cfg)
ret = mem_copy_benchmark(case_cfg);
break;
default:
- printf("Unknown test type. %s\n", case_cfg->test_type_str);
+ printf("Unknown test type\n");
break;
}
@@ -336,7 +331,6 @@ load_configs(const char *path)
const char *skip;
struct rte_kvargs *kvlist;
int args_nr, nb_vp;
- bool is_dma;
printf("config file parsing...\n");
cfgfile = rte_cfgfile_load(path, 0);
@@ -374,19 +368,14 @@ load_configs(const char *path)
if (strcmp(case_type, DMA_MEM_COPY) == 0) {
test_case->test_type = TEST_TYPE_DMA_MEM_COPY;
- test_case->test_type_str = DMA_MEM_COPY;
- is_dma = true;
} else if (strcmp(case_type, CPU_MEM_COPY) == 0) {
test_case->test_type = TEST_TYPE_CPU_MEM_COPY;
- test_case->test_type_str = CPU_MEM_COPY;
- is_dma = false;
} else {
printf("Error: Wrong test case type %s in case%d.\n", case_type, i + 1);
test_case->is_valid = false;
continue;
}
- test_case->is_dma = is_dma;
test_case->src_numa_node = (int)atoi(rte_cfgfile_get_entry(cfgfile,
section_name, "src_numa_node"));
test_case->dst_numa_node = (int)atoi(rte_cfgfile_get_entry(cfgfile,
@@ -410,7 +399,7 @@ load_configs(const char *path)
} else if (args_nr == 4)
nb_vp++;
- if (is_dma) {
+ if (test_case->test_type == TEST_TYPE_DMA_MEM_COPY) {
ring_size_str = rte_cfgfile_get_entry(cfgfile, section_name,
"dma_ring_size");
args_nr = parse_entry(ring_size_str, &test_case->ring_size);
diff --git a/app/test-dma-perf/main.h b/app/test-dma-perf/main.h
index 97c81eb2eb..c565dad9a7 100644
--- a/app/test-dma-perf/main.h
+++ b/app/test-dma-perf/main.h
@@ -12,7 +12,11 @@
#define MAX_WORKER_NB 128
-#define MAX_DMA_NB 128
+enum {
+ TEST_TYPE_NONE = 0,
+ TEST_TYPE_DMA_MEM_COPY,
+ TEST_TYPE_CPU_MEM_COPY
+};
typedef enum {
OP_NONE = 0,
@@ -49,12 +53,8 @@ struct test_configure {
bool is_valid;
bool is_skip;
uint8_t test_type;
- const char *test_type_str;
uint16_t src_numa_node;
uint16_t dst_numa_node;
- uint16_t opcode;
- bool is_dma;
- bool is_sg;
struct lcore_dma_config dma_config[MAX_WORKER_NB];
struct test_configure_entry mem_size;
struct test_configure_entry buf_size;
@@ -63,7 +63,7 @@ struct test_configure {
uint16_t num_worker;
uint8_t nb_src_sges;
uint8_t nb_dst_sges;
- uint32_t nr_buf;
+ bool is_sg;
uint8_t scenario_id;
};
--
2.17.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 08/10] app/dma-perf: refactor load config function
2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
` (6 preceding siblings ...)
2025-08-12 2:07 ` [PATCH 07/10] app/dma-perf: remove invalid or redundant field Chengwen Feng
@ 2025-08-12 2:07 ` Chengwen Feng
2025-08-12 2:07 ` [PATCH 09/10] app/dma-perf: refactor benchmark function Chengwen Feng
2025-08-12 2:07 ` [PATCH 10/10] app/dma-perf: support specific error info Chengwen Feng
9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-12 2:07 UTC (permalink / raw)
To: thomas, honest.jiang; +Cc: dev, liuyonglong
The load_configs() function has 198 lines, and hard to understand.
This commit split it to three functions.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
app/test-dma-perf/main.c | 212 ++++++++++++++++++++-------------------
1 file changed, 109 insertions(+), 103 deletions(-)
diff --git a/app/test-dma-perf/main.c b/app/test-dma-perf/main.c
index e630d2980a..d8d1e3d3f8 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -191,6 +191,20 @@ parse_lcore(struct test_configure *test_case, const char *value)
return 0;
}
+static void
+parse_cpu_config(struct test_configure *test_case, int case_id,
+ struct rte_cfgfile *cfgfile, char *section_name)
+{
+ const char *lcore;
+
+ lcore = rte_cfgfile_get_entry(cfgfile, section_name, "lcore");
+ int lcore_ret = parse_lcore(test_case, lcore);
+ if (lcore_ret < 0) {
+ printf("parse lcore error in case %d.\n", case_id);
+ test_case->is_valid = false;
+ }
+}
+
static int
parse_entry(const char *value, struct test_configure_entry *entry)
{
@@ -269,6 +283,91 @@ static int populate_dma_dev_config(const char *key, const char *value, void *tes
return ret;
}
+static void
+parse_dma_config(struct test_configure *test_case, int case_id,
+ struct rte_cfgfile *cfgfile, char *section_name, int *nb_vp)
+{
+ const char *ring_size_str, *kick_batch_str, *src_sges_str, *dst_sges_str;
+ char lc_dma[RTE_DEV_NAME_MAX_LEN];
+ struct rte_kvargs *kvlist;
+ const char *lcore_dma;
+ int args_nr;
+ int i;
+
+ ring_size_str = rte_cfgfile_get_entry(cfgfile, section_name, "dma_ring_size");
+ args_nr = parse_entry(ring_size_str, &test_case->ring_size);
+ if (args_nr < 0) {
+ printf("parse error in case %d.\n", case_id);
+ test_case->is_valid = false;
+ return;
+ } else if (args_nr == 4)
+ *nb_vp += 1;
+
+ src_sges_str = rte_cfgfile_get_entry(cfgfile, section_name, "dma_src_sge");
+ if (src_sges_str != NULL)
+ test_case->nb_src_sges = (int)atoi(rte_cfgfile_get_entry(cfgfile,
+ section_name, "dma_src_sge"));
+
+ dst_sges_str = rte_cfgfile_get_entry(cfgfile, section_name, "dma_dst_sge");
+ if (dst_sges_str != NULL)
+ test_case->nb_dst_sges = (int)atoi(rte_cfgfile_get_entry(cfgfile,
+ section_name, "dma_dst_sge"));
+
+ if ((src_sges_str != NULL && dst_sges_str == NULL) ||
+ (src_sges_str == NULL && dst_sges_str != NULL)) {
+ printf("parse dma_src_sge, dma_dst_sge error in case %d.\n", case_id);
+ test_case->is_valid = false;
+ return;
+ } else if (src_sges_str != NULL && dst_sges_str != NULL) {
+ if (test_case->nb_src_sges == 0 || test_case->nb_dst_sges == 0) {
+ printf("dma_src_sge and dma_dst_sge can not be 0 in case %d.\n", case_id);
+ test_case->is_valid = false;
+ return;
+ }
+ test_case->is_sg = true;
+ }
+
+ kick_batch_str = rte_cfgfile_get_entry(cfgfile, section_name, "kick_batch");
+ args_nr = parse_entry(kick_batch_str, &test_case->kick_batch);
+ if (args_nr < 0) {
+ printf("parse error in case %d.\n", case_id);
+ test_case->is_valid = false;
+ return;
+ } else if (args_nr == 4)
+ *nb_vp += 1;
+
+ i = 0;
+ while (1) {
+ snprintf(lc_dma, RTE_DEV_NAME_MAX_LEN, "lcore_dma%d", i);
+ lcore_dma = rte_cfgfile_get_entry(cfgfile, section_name, lc_dma);
+ if (lcore_dma == NULL)
+ break;
+
+ kvlist = rte_kvargs_parse(lcore_dma, NULL);
+ if (kvlist == NULL) {
+ printf("rte_kvargs_parse() error");
+ test_case->is_valid = false;
+ break;
+ }
+
+ if (rte_kvargs_process(kvlist, NULL, populate_dma_dev_config,
+ (void *)&test_case->dma_config[i]) < 0) {
+ printf("rte_kvargs_process() error\n");
+ rte_kvargs_free(kvlist);
+ test_case->is_valid = false;
+ break;
+ }
+ i++;
+ test_case->num_worker++;
+ rte_kvargs_free(kvlist);
+ }
+
+ if (test_case->num_worker == 0) {
+ printf("Error: Parsing %s Failed\n", lc_dma);
+ test_case->is_valid = false;
+ }
+}
+
static int
parse_global_config(struct rte_cfgfile *cfgfile)
{
@@ -320,17 +419,14 @@ parse_global_config(struct rte_cfgfile *cfgfile)
static uint16_t
load_configs(const char *path)
{
- struct rte_cfgfile *cfgfile;
- int nb_sections, i;
+ const char *mem_size_str, *buf_size_str;
struct test_configure *test_case;
char section_name[CFG_NAME_LEN];
+ struct rte_cfgfile *cfgfile;
const char *case_type;
- const char *lcore_dma;
- const char *mem_size_str, *buf_size_str, *ring_size_str, *kick_batch_str,
- *src_sges_str, *dst_sges_str;
- const char *skip;
- struct rte_kvargs *kvlist;
+ int nb_sections, i;
int args_nr, nb_vp;
+ const char *skip;
printf("config file parsing...\n");
cfgfile = rte_cfgfile_load(path, 0);
@@ -358,6 +454,7 @@ load_configs(const char *path)
continue;
}
+ test_case->is_valid = true;
case_type = rte_cfgfile_get_entry(cfgfile, section_name, "type");
if (case_type == NULL) {
printf("Error: No case type in case %d, the test will be finished here.\n",
@@ -399,108 +496,17 @@ load_configs(const char *path)
} else if (args_nr == 4)
nb_vp++;
- if (test_case->test_type == TEST_TYPE_DMA_MEM_COPY) {
- ring_size_str = rte_cfgfile_get_entry(cfgfile, section_name,
- "dma_ring_size");
- args_nr = parse_entry(ring_size_str, &test_case->ring_size);
- if (args_nr < 0) {
- printf("parse error in case %d.\n", i + 1);
- test_case->is_valid = false;
- continue;
- } else if (args_nr == 4)
- nb_vp++;
-
- src_sges_str = rte_cfgfile_get_entry(cfgfile, section_name,
- "dma_src_sge");
- if (src_sges_str != NULL) {
- test_case->nb_src_sges = (int)atoi(rte_cfgfile_get_entry(cfgfile,
- section_name, "dma_src_sge"));
- }
-
- dst_sges_str = rte_cfgfile_get_entry(cfgfile, section_name,
- "dma_dst_sge");
- if (dst_sges_str != NULL) {
- test_case->nb_dst_sges = (int)atoi(rte_cfgfile_get_entry(cfgfile,
- section_name, "dma_dst_sge"));
- }
-
- if ((src_sges_str != NULL && dst_sges_str == NULL) ||
- (src_sges_str == NULL && dst_sges_str != NULL)) {
- printf("parse dma_src_sge, dma_dst_sge error in case %d.\n",
- i + 1);
- test_case->is_valid = false;
- continue;
- } else if (src_sges_str != NULL && dst_sges_str != NULL) {
- test_case->is_sg = true;
-
- if (test_case->nb_src_sges == 0 || test_case->nb_dst_sges == 0) {
- printf("dma_src_sge and dma_dst_sge can not be 0 in case %d.\n",
- i + 1);
- test_case->is_valid = false;
- continue;
- }
- } else {
- test_case->is_sg = false;
- }
-
- kick_batch_str = rte_cfgfile_get_entry(cfgfile, section_name, "kick_batch");
- args_nr = parse_entry(kick_batch_str, &test_case->kick_batch);
- if (args_nr < 0) {
- printf("parse error in case %d.\n", i + 1);
- test_case->is_valid = false;
- continue;
- } else if (args_nr == 4)
- nb_vp++;
-
- char lc_dma[RTE_DEV_NAME_MAX_LEN];
- int i = 0;
- while (1) {
- snprintf(lc_dma, RTE_DEV_NAME_MAX_LEN, "lcore_dma%d", i);
- lcore_dma = rte_cfgfile_get_entry(cfgfile, section_name, lc_dma);
- if (lcore_dma == NULL)
- break;
-
- kvlist = rte_kvargs_parse(lcore_dma, NULL);
- if (kvlist == NULL) {
- printf("rte_kvargs_parse() error");
- test_case->is_valid = false;
- break;
- }
-
- if (rte_kvargs_process(kvlist, NULL, populate_dma_dev_config,
- (void *)&test_case->dma_config[i]) < 0) {
- printf("rte_kvargs_process() error\n");
- rte_kvargs_free(kvlist);
- test_case->is_valid = false;
- break;
- }
- i++;
- test_case->num_worker++;
- rte_kvargs_free(kvlist);
- }
-
- if (test_case->num_worker == 0) {
- printf("Error: Parsing %s Failed\n", lc_dma);
- continue;
- }
- } else {
- lcore_dma = rte_cfgfile_get_entry(cfgfile, section_name, "lcore");
- int lcore_ret = parse_lcore(test_case, lcore_dma);
- if (lcore_ret < 0) {
- printf("parse lcore error in case %d.\n", i + 1);
- test_case->is_valid = false;
- continue;
- }
- }
+ if (test_case->test_type == TEST_TYPE_DMA_MEM_COPY)
+ parse_dma_config(test_case, i + 1, cfgfile, section_name, &nb_vp);
+ else
+ parse_cpu_config(test_case, i + 1, cfgfile, section_name);
- if (nb_vp > 1) {
+ if (test_case->is_valid && nb_vp > 1) {
printf("Case %d error, each section can only have a single variable parameter.\n",
i + 1);
test_case->is_valid = false;
continue;
}
-
- test_case->is_valid = true;
}
rte_cfgfile_close(cfgfile);
--
2.17.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 09/10] app/dma-perf: refactor benchmark function
2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
` (7 preceding siblings ...)
2025-08-12 2:07 ` [PATCH 08/10] app/dma-perf: refactor load config function Chengwen Feng
@ 2025-08-12 2:07 ` Chengwen Feng
2025-08-12 2:07 ` [PATCH 10/10] app/dma-perf: support specific error info Chengwen Feng
9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-12 2:07 UTC (permalink / raw)
To: thomas, honest.jiang; +Cc: dev, liuyonglong
This commit refactor mem_copy_benchmark() function by splitting the
data verification part into an independent function.
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
app/test-dma-perf/benchmark.c | 153 +++++++++++++++++++---------------
1 file changed, 84 insertions(+), 69 deletions(-)
diff --git a/app/test-dma-perf/benchmark.c b/app/test-dma-perf/benchmark.c
index 663f81ef5c..1145493152 100644
--- a/app/test-dma-perf/benchmark.c
+++ b/app/test-dma-perf/benchmark.c
@@ -669,27 +669,100 @@ attach_ext_buffer(struct vchan_dev_config *vchan_dev, struct lcore_params *lcore
return 0;
}
+static int
+verify_data(struct test_configure *cfg, struct rte_mbuf **srcs, struct rte_mbuf **dsts,
+ uint32_t nr_buf)
+{
+ struct rte_mbuf **src_buf = NULL, **dst_buf = NULL;
+ uint32_t nr_buf_pt = nr_buf / cfg->num_worker;
+ struct vchan_dev_config *vchan_dev = NULL;
+ unsigned int buf_size = cfg->buf_size.cur;
+ uint32_t offset, work_idx, i, j;
+
+ for (work_idx = 0; work_idx < cfg->num_worker; work_idx++) {
+ vchan_dev = &cfg->dma_config[work_idx].vchan_dev;
+ offset = nr_buf / cfg->num_worker * work_idx;
+ src_buf = srcs + offset;
+ dst_buf = dsts + offset;
+
+ if (vchan_dev->tdir == RTE_DMA_DIR_MEM_TO_MEM && !cfg->is_sg) {
+ for (i = 0; i < nr_buf_pt; i++) {
+ if (memcmp(rte_pktmbuf_mtod(src_buf[i], void *),
+ rte_pktmbuf_mtod(dst_buf[i], void *),
+ cfg->buf_size.cur) != 0) {
+ printf("Copy validation fails for buffer number %d\n", i);
+ return -1;
+ }
+ }
+ continue;
+ }
+
+ if (vchan_dev->tdir == RTE_DMA_DIR_MEM_TO_MEM && cfg->is_sg) {
+ size_t src_remsz = buf_size % cfg->nb_src_sges;
+ size_t dst_remsz = buf_size % cfg->nb_dst_sges;
+ size_t src_sz = buf_size / cfg->nb_src_sges;
+ size_t dst_sz = buf_size / cfg->nb_dst_sges;
+ uint8_t src[buf_size], dst[buf_size];
+ uint8_t *sbuf, *dbuf, *ptr;
+
+ for (i = 0; i < (nr_buf_pt / RTE_MAX(cfg->nb_src_sges, cfg->nb_dst_sges));
+ i++) {
+ sbuf = src;
+ dbuf = dst;
+ ptr = NULL;
+
+ for (j = 0; j < cfg->nb_src_sges; j++) {
+ ptr = rte_pktmbuf_mtod(src_buf[i * cfg->nb_src_sges + j],
+ uint8_t *);
+ memcpy(sbuf, ptr, src_sz);
+ sbuf += src_sz;
+ }
+ if (src_remsz)
+ memcpy(sbuf, ptr + src_sz, src_remsz);
+
+ for (j = 0; j < cfg->nb_dst_sges; j++) {
+ ptr = rte_pktmbuf_mtod(dst_buf[i * cfg->nb_dst_sges + j],
+ uint8_t *);
+ memcpy(dbuf, ptr, dst_sz);
+ dbuf += dst_sz;
+ }
+ if (dst_remsz)
+ memcpy(dbuf, ptr + dst_sz, dst_remsz);
+
+ if (memcmp(src, dst, buf_size) != 0) {
+ printf("SG Copy validation fails for buffer number %d\n",
+ i * cfg->nb_src_sges);
+ return -1;
+ }
+ }
+ continue;
+ }
+ }
+
+ return 0;
+}
+
int
mem_copy_benchmark(struct test_configure *cfg)
{
- uint32_t i, j, k;
- uint32_t offset;
- unsigned int lcore_id = 0;
struct rte_mbuf **srcs = NULL, **dsts = NULL, **m = NULL;
struct rte_dma_sge *src_sges = NULL, *dst_sges = NULL;
struct vchan_dev_config *vchan_dev = NULL;
struct lcore_dma_map_t *lcore_dma_map = NULL;
unsigned int buf_size = cfg->buf_size.cur;
uint16_t kick_batch = cfg->kick_batch.cur;
- uint16_t nb_workers = cfg->num_worker;
uint16_t test_secs = global_cfg.test_secs;
- float memory = 0;
- uint32_t avg_cycles = 0;
+ uint16_t nb_workers = cfg->num_worker;
+ uint32_t nr_sgsrc = 0, nr_sgdst = 0;
+ float bandwidth, bandwidth_total;
+ unsigned int lcore_id = 0;
uint32_t avg_cycles_total;
+ uint32_t avg_cycles = 0;
float mops, mops_total;
- float bandwidth, bandwidth_total;
- uint32_t nr_sgsrc = 0, nr_sgdst = 0;
+ float memory = 0;
uint32_t nr_buf;
+ uint32_t offset;
+ uint32_t i, k;
int ret = 0;
nr_buf = align_buffer_count(cfg, &nr_sgsrc, &nr_sgdst);
@@ -781,67 +854,9 @@ mem_copy_benchmark(struct test_configure *cfg)
rte_eal_mp_wait_lcore();
- for (k = 0; k < nb_workers; k++) {
- struct rte_mbuf **src_buf = NULL, **dst_buf = NULL;
- uint32_t nr_buf_pt = nr_buf / nb_workers;
- vchan_dev = &cfg->dma_config[k].vchan_dev;
- offset = nr_buf / nb_workers * k;
- src_buf = srcs + offset;
- dst_buf = dsts + offset;
-
- if (vchan_dev->tdir == RTE_DMA_DIR_MEM_TO_MEM && !cfg->is_sg) {
- for (i = 0; i < nr_buf_pt; i++) {
- if (memcmp(rte_pktmbuf_mtod(src_buf[i], void *),
- rte_pktmbuf_mtod(dst_buf[i], void *),
- cfg->buf_size.cur) != 0) {
- printf("Copy validation fails for buffer number %d\n", i);
- ret = -1;
- goto out;
- }
- }
- } else if (vchan_dev->tdir == RTE_DMA_DIR_MEM_TO_MEM && cfg->is_sg) {
- size_t src_remsz = buf_size % cfg->nb_src_sges;
- size_t dst_remsz = buf_size % cfg->nb_dst_sges;
- size_t src_sz = buf_size / cfg->nb_src_sges;
- size_t dst_sz = buf_size / cfg->nb_dst_sges;
- uint8_t src[buf_size], dst[buf_size];
- uint8_t *sbuf, *dbuf, *ptr;
-
- for (i = 0; i < (nr_buf_pt / RTE_MAX(cfg->nb_src_sges, cfg->nb_dst_sges));
- i++) {
- sbuf = src;
- dbuf = dst;
- ptr = NULL;
-
- for (j = 0; j < cfg->nb_src_sges; j++) {
- ptr = rte_pktmbuf_mtod(src_buf[i * cfg->nb_src_sges + j],
- uint8_t *);
- memcpy(sbuf, ptr, src_sz);
- sbuf += src_sz;
- }
-
- if (src_remsz)
- memcpy(sbuf, ptr + src_sz, src_remsz);
-
- for (j = 0; j < cfg->nb_dst_sges; j++) {
- ptr = rte_pktmbuf_mtod(dst_buf[i * cfg->nb_dst_sges + j],
- uint8_t *);
- memcpy(dbuf, ptr, dst_sz);
- dbuf += dst_sz;
- }
-
- if (dst_remsz)
- memcpy(dbuf, ptr + dst_sz, dst_remsz);
-
- if (memcmp(src, dst, buf_size) != 0) {
- printf("SG Copy validation fails for buffer number %d\n",
- i * cfg->nb_src_sges);
- ret = -1;
- goto out;
- }
- }
- }
- }
+ ret = verify_data(cfg, srcs, dsts, nr_buf);
+ if (ret != 0)
+ goto out;
mops_total = 0;
bandwidth_total = 0;
--
2.17.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 10/10] app/dma-perf: support specific error info
2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
` (8 preceding siblings ...)
2025-08-12 2:07 ` [PATCH 09/10] app/dma-perf: refactor benchmark function Chengwen Feng
@ 2025-08-12 2:07 ` Chengwen Feng
9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-12 2:07 UTC (permalink / raw)
To: thomas, honest.jiang; +Cc: dev, liuyonglong
If some entry was not provided in section, it will be segment fault
in original impl. For example:
dpdk-test-dma-perf --config ./config.ini
config file parsing...
Segmentation fault (core dumped)
This commit support specific error information, the new output (e.g.):
dpdk-test-dma-perf --config ./config.ini
config file parsing...
Error: can't get entry 'dst_numa_node' in section 'case1'
Signed-off-by: Chengwen Feng <fengchengwen@huawei.com>
---
app/test-dma-perf/main.c | 67 +++++++++++++++-------------------------
1 file changed, 25 insertions(+), 42 deletions(-)
diff --git a/app/test-dma-perf/main.c b/app/test-dma-perf/main.c
index d8d1e3d3f8..9d17212d4c 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -158,6 +158,18 @@ run_test(uint32_t case_id, struct test_configure *case_cfg)
}
}
+/* Exit process if the entry couldn't find in the section. */
+static const char *
+get_cfgfile_entry(struct rte_cfgfile *cfgfile, const char *section_name, const char *entry_name)
+{
+ const char *entry = rte_cfgfile_get_entry(cfgfile, section_name, entry_name);
+ if (entry == NULL) {
+ printf("Error: can't get entry '%s' in section '%s'\n", entry_name, section_name);
+ exit(1);
+ }
+ return entry;
+}
+
static int
parse_lcore(struct test_configure *test_case, const char *value)
{
@@ -165,9 +177,6 @@ parse_lcore(struct test_configure *test_case, const char *value)
char *input;
struct lcore_dma_map_t *lcore_dma_map;
- if (test_case == NULL || value == NULL)
- return -1;
-
len = strlen(value);
input = (char *)malloc((len + 1) * sizeof(char));
strlcpy(input, value, len + 1);
@@ -195,9 +204,7 @@ static void
parse_cpu_config(struct test_configure *test_case, int case_id,
struct rte_cfgfile *cfgfile, char *section_name)
{
- const char *lcore;
-
- lcore = rte_cfgfile_get_entry(cfgfile, section_name, "lcore");
+ const char *lcore = get_cfgfile_entry(cfgfile, section_name, "lcore");
int lcore_ret = parse_lcore(test_case, lcore);
if (lcore_ret < 0) {
printf("parse lcore error in case %d.\n", case_id);
@@ -213,9 +220,6 @@ parse_entry(const char *value, struct test_configure_entry *entry)
int args_nr = -1;
int ret;
- if (value == NULL || entry == NULL)
- goto out;
-
strncpy(input, value, 254);
if (*input == '\0')
goto out;
@@ -294,7 +298,7 @@ parse_dma_config(struct test_configure *test_case, int case_id,
int args_nr;
int i;
- ring_size_str = rte_cfgfile_get_entry(cfgfile, section_name, "dma_ring_size");
+ ring_size_str = get_cfgfile_entry(cfgfile, section_name, "dma_ring_size");
args_nr = parse_entry(ring_size_str, &test_case->ring_size);
if (args_nr < 0) {
printf("parse error in case %d.\n", case_id);
@@ -305,13 +309,11 @@ parse_dma_config(struct test_configure *test_case, int case_id,
src_sges_str = rte_cfgfile_get_entry(cfgfile, section_name, "dma_src_sge");
if (src_sges_str != NULL)
- test_case->nb_src_sges = (int)atoi(rte_cfgfile_get_entry(cfgfile,
- section_name, "dma_src_sge"));
+ test_case->nb_src_sges = (int)atoi(src_sges_str);
dst_sges_str = rte_cfgfile_get_entry(cfgfile, section_name, "dma_dst_sge");
if (dst_sges_str != NULL)
- test_case->nb_dst_sges = (int)atoi(rte_cfgfile_get_entry(cfgfile,
- section_name, "dma_dst_sge"));
+ test_case->nb_dst_sges = (int)atoi(dst_sges_str);
if ((src_sges_str != NULL && dst_sges_str == NULL) ||
(src_sges_str == NULL && dst_sges_str != NULL)) {
@@ -327,7 +329,7 @@ parse_dma_config(struct test_configure *test_case, int case_id,
test_case->is_sg = true;
}
- kick_batch_str = rte_cfgfile_get_entry(cfgfile, section_name, "kick_batch");
+ kick_batch_str = get_cfgfile_entry(cfgfile, section_name, "kick_batch");
args_nr = parse_entry(kick_batch_str, &test_case->kick_batch);
if (args_nr < 0) {
printf("parse error in case %d.\n", case_id);
@@ -384,11 +386,7 @@ parse_global_config(struct rte_cfgfile *cfgfile)
return -1;
}
- entry = rte_cfgfile_get_entry(cfgfile, GLOBAL_SECTION_NAME, "eal_args");
- if (entry == NULL) {
- printf("Error: GLOBAL section must have 'eal_args' entry!\n");
- return -1;
- }
+ entry = get_cfgfile_entry(cfgfile, GLOBAL_SECTION_NAME, "eal_args");
args = strdup(entry);
if (args == NULL) {
printf("Error: dup GLOBAL 'eal_args' failed!\n");
@@ -399,18 +397,10 @@ parse_global_config(struct rte_cfgfile *cfgfile)
global_cfg.eal_argv[i] = tokens[i];
global_cfg.eal_argc = i;
- entry = rte_cfgfile_get_entry(cfgfile, GLOBAL_SECTION_NAME, "cache_flush");
- if (entry == NULL) {
- printf("Error: GLOBAL section don't has 'cache_flush' entry!\n");
- return -1;
- }
+ entry = get_cfgfile_entry(cfgfile, GLOBAL_SECTION_NAME, "cache_flush");
global_cfg.cache_flush = (uint8_t)atoi(entry);
- entry = rte_cfgfile_get_entry(cfgfile, GLOBAL_SECTION_NAME, "test_seconds");
- if (entry == NULL) {
- printf("Error: GLOBAL section don't has 'test_seconds' entry!\n");
- return -1;
- }
+ entry = get_cfgfile_entry(cfgfile, GLOBAL_SECTION_NAME, "test_seconds");
global_cfg.test_secs = (uint16_t)atoi(entry);
return 0;
@@ -455,14 +445,7 @@ load_configs(const char *path)
}
test_case->is_valid = true;
- case_type = rte_cfgfile_get_entry(cfgfile, section_name, "type");
- if (case_type == NULL) {
- printf("Error: No case type in case %d, the test will be finished here.\n",
- i + 1);
- test_case->is_valid = false;
- continue;
- }
-
+ case_type = get_cfgfile_entry(cfgfile, section_name, "type");
if (strcmp(case_type, DMA_MEM_COPY) == 0) {
test_case->test_type = TEST_TYPE_DMA_MEM_COPY;
} else if (strcmp(case_type, CPU_MEM_COPY) == 0) {
@@ -473,12 +456,12 @@ load_configs(const char *path)
continue;
}
- test_case->src_numa_node = (int)atoi(rte_cfgfile_get_entry(cfgfile,
+ test_case->src_numa_node = (int)atoi(get_cfgfile_entry(cfgfile,
section_name, "src_numa_node"));
- test_case->dst_numa_node = (int)atoi(rte_cfgfile_get_entry(cfgfile,
+ test_case->dst_numa_node = (int)atoi(get_cfgfile_entry(cfgfile,
section_name, "dst_numa_node"));
nb_vp = 0;
- mem_size_str = rte_cfgfile_get_entry(cfgfile, section_name, "mem_size");
+ mem_size_str = get_cfgfile_entry(cfgfile, section_name, "mem_size");
args_nr = parse_entry(mem_size_str, &test_case->mem_size);
if (args_nr < 0) {
printf("parse error in case %d.\n", i + 1);
@@ -487,7 +470,7 @@ load_configs(const char *path)
} else if (args_nr == 4)
nb_vp++;
- buf_size_str = rte_cfgfile_get_entry(cfgfile, section_name, "buf_size");
+ buf_size_str = get_cfgfile_entry(cfgfile, section_name, "buf_size");
args_nr = parse_entry(buf_size_str, &test_case->buf_size);
if (args_nr < 0) {
printf("parse error in case %d.\n", i + 1);
--
2.17.1
^ permalink raw reply [flat|nested] 21+ messages in thread