DPDK patches and discussions
 help / color / mirror / Atom feed
* [PATCH 0/9] bugfix and refactor of dma-perf
@ 2025-08-11 10:54 Chengwen Feng
  2025-08-11 10:54 ` [PATCH 1/9] app/dma-perf: fix use-after-free Chengwen Feng
                   ` (9 more replies)
  0 siblings, 10 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-11 10:54 UTC (permalink / raw)
  To: thomas; +Cc: dev, liuyonglong

This patchset contain one bugfix and eight refactor commit for
test-dma-perf application.

Chengwen Feng (9):
  app/dma-perf: fix use-after-free
  app/dma-perf: add global section for config file
  app/dma-perf: use argparse lib to parse argument
  app/dma-perf: refactor output csv
  app/dma-perf: support list DMA devices
  app/dma-perf: add more global config
  app/dma-perf: remove invalid or redundant field
  app/dma-perf: refactor load config function
  app/dma-perf: refactor benchmark function

 app/test-dma-perf/benchmark.c | 182 +++++------
 app/test-dma-perf/config.ini  |  33 +-
 app/test-dma-perf/main.c      | 549 +++++++++++++++++++---------------
 app/test-dma-perf/main.h      |  28 +-
 app/test-dma-perf/meson.build |   2 +-
 doc/guides/tools/dmaperf.rst  |  47 +--
 6 files changed, 454 insertions(+), 387 deletions(-)

-- 
2.17.1


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

* [PATCH 1/9] app/dma-perf: fix use-after-free
  2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng
@ 2025-08-11 10:54 ` Chengwen Feng
  2025-08-11 10:54 ` [PATCH 2/9] 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-11 10:54 UTC (permalink / raw)
  To: thomas; +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 2/9] app/dma-perf: add global section for config file
  2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng
  2025-08-11 10:54 ` [PATCH 1/9] app/dma-perf: fix use-after-free Chengwen Feng
@ 2025-08-11 10:54 ` Chengwen Feng
  2025-08-11 10:54 ` [PATCH 3/9] 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-11 10:54 UTC (permalink / raw)
  To: thomas; +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 | 15 +++---
 app/test-dma-perf/main.c     | 89 +++++++++++++++++-------------------
 app/test-dma-perf/main.h     |  8 +++-
 doc/guides/tools/dmaperf.rst | 26 +++++++----
 4 files changed, 74 insertions(+), 64 deletions(-)

diff --git a/app/test-dma-perf/config.ini b/app/test-dma-perf/config.ini
index 61e49dbae5..80cbcbb762 100644
--- a/app/test-dma-perf/config.ini
+++ b/app/test-dma-perf/config.ini
@@ -4,7 +4,11 @@
 
 ; Supported test types are DMA_MEM_COPY and CPU_MEM_COPY.
 
-; Parameters:
+; There are two types of configuration sections: global configuration and testcase configuration.
+; The global section contains the "eal_args" entry which specifies the EAL arguments for all
+; testcases.
+
+; The testcase configuration contains 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 +63,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 +78,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 +93,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 +108,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 +117,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..51a9cc8df8 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,9 +69,10 @@ 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 sections which contains multiple sections, each section
+represents a test case.
 The four mandatory variables ``mem_size``, ``buf_size``, ``dma_ring_size``, and ``kick_batch``
 can vary in each test case.
 The format for this is ``variable=first,last,increment,ADD|MUL``.
@@ -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 3/9] app/dma-perf: use argparse lib to parse argument
  2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng
  2025-08-11 10:54 ` [PATCH 1/9] app/dma-perf: fix use-after-free Chengwen Feng
  2025-08-11 10:54 ` [PATCH 2/9] app/dma-perf: add global section for config file Chengwen Feng
@ 2025-08-11 10:54 ` Chengwen Feng
  2025-08-11 10:54 ` [PATCH 4/9] app/dma-perf: refactor output csv Chengwen Feng
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-11 10:54 UTC (permalink / raw)
  To: thomas; +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      | 91 ++++++++++++++++++++++++-----------
 app/test-dma-perf/meson.build |  2 +-
 2 files changed, 63 insertions(+), 30 deletions(-)

diff --git a/app/test-dma-perf/main.c b/app/test-dma-perf/main.c
index 54a5e573fc..ed582b609f 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>
@@ -45,6 +46,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 +528,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 +612,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 +634,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 4/9] app/dma-perf: refactor output csv
  2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng
                   ` (2 preceding siblings ...)
  2025-08-11 10:54 ` [PATCH 3/9] app/dma-perf: use argparse lib to parse argument Chengwen Feng
@ 2025-08-11 10:54 ` Chengwen Feng
  2025-08-11 10:54 ` [PATCH 5/9] 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-11 10:54 UTC (permalink / raw)
  To: thomas; +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      | 87 +++++++++++++----------------------
 app/test-dma-perf/main.h      |  4 +-
 3 files changed, 35 insertions(+), 63 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 ed582b609f..9b0b3a4103 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -49,60 +49,49 @@ 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)
+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 - 1, fmt, 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
@@ -126,7 +115,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;
@@ -135,9 +123,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;
@@ -550,6 +535,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);
@@ -602,18 +588,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;
 		}
 
@@ -634,12 +617,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]);
@@ -647,8 +624,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 5/9] app/dma-perf: support list DMA devices
  2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng
                   ` (3 preceding siblings ...)
  2025-08-11 10:54 ` [PATCH 4/9] app/dma-perf: refactor output csv Chengwen Feng
@ 2025-08-11 10:54 ` Chengwen Feng
  2025-08-11 10:54 ` [PATCH 6/9] 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-11 10:54 UTC (permalink / raw)
  To: thomas; +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 9b0b3a4103..7525ab3622 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -48,6 +48,7 @@ static struct global_configure global_cfg;
 
 static char *config_path;
 static char *result_path;
+static bool  list_dma;
 
 void
 output_csv(const char *fmt, ...)
@@ -531,6 +532,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(),
 		},
 	};
@@ -571,6 +576,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[])
 {
@@ -584,6 +628,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 6/9] app/dma-perf: add more global config
  2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng
                   ` (4 preceding siblings ...)
  2025-08-11 10:54 ` [PATCH 5/9] app/dma-perf: support list DMA devices Chengwen Feng
@ 2025-08-11 10:54 ` Chengwen Feng
  2025-08-11 10:54 ` [PATCH 7/9] 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-11 10:54 UTC (permalink / raw)
  To: thomas; +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  | 21 ++++++++-------------
 5 files changed, 36 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 80cbcbb762..f1debcdc11 100644
--- a/app/test-dma-perf/config.ini
+++ b/app/test-dma-perf/config.ini
@@ -5,8 +5,11 @@
 ; Supported test types are DMA_MEM_COPY and CPU_MEM_COPY.
 
 ; There are two types of configuration sections: global configuration and testcase configuration.
-; The global section contains the "eal_args" entry which specifies the EAL arguments for all
-; testcases.
+; The global section contains 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 contains following parameters:
 ; "mem_size" denotes the size of the memory footprint in megabytes (MB) for source and destination.
@@ -20,11 +23,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.
@@ -65,6 +63,8 @@
 
 [GLOBAL]
 eal_args=--in-memory --file-prefix=test -l 9-12
+cache_flush=0
+test_seconds=2
 
 [case1]
 type=DMA_MEM_COPY
@@ -74,8 +74,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
 
@@ -89,8 +87,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
 
@@ -103,8 +99,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
@@ -115,6 +109,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 7525ab3622..fc92f6191a 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -44,7 +44,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;
@@ -309,6 +309,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;
 }
 
@@ -501,11 +515,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 51a9cc8df8..265d719913 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,12 @@ 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 +133,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 7/9] app/dma-perf: remove invalid or redundant field
  2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng
                   ` (5 preceding siblings ...)
  2025-08-11 10:54 ` [PATCH 6/9] app/dma-perf: add more global config Chengwen Feng
@ 2025-08-11 10:54 ` Chengwen Feng
  2025-08-11 10:54 ` [PATCH 8/9] 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-11 10:54 UTC (permalink / raw)
  To: thomas; +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      | 24 ++++--------------------
 app/test-dma-perf/main.h      | 12 ++++++------
 3 files changed, 19 insertions(+), 37 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 fc92f6191a..27d2af46f8 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -27,19 +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,
-	TEST_TYPE_CPU_MEM_COPY
-};
-
 #define MAX_TEST_CASES 16
 static struct test_configure test_cases[MAX_TEST_CASES];
 
@@ -92,7 +81,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
@@ -106,7 +96,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;
 	}
 
@@ -340,7 +330,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);
@@ -378,19 +367,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,
@@ -414,7 +398,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 8/9] app/dma-perf: refactor load config function
  2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng
                   ` (6 preceding siblings ...)
  2025-08-11 10:54 ` [PATCH 7/9] app/dma-perf: remove invalid or redundant field Chengwen Feng
@ 2025-08-11 10:54 ` Chengwen Feng
  2025-08-11 10:54 ` [PATCH 9/9] app/dma-perf: refactor benchmark function Chengwen Feng
  2025-08-12  2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
  9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-11 10:54 UTC (permalink / raw)
  To: thomas; +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 27d2af46f8..a4f9aeb859 100644
--- a/app/test-dma-perf/main.c
+++ b/app/test-dma-perf/main.c
@@ -190,6 +190,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)
 {
@@ -268,6 +282,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)
 {
@@ -319,17 +418,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);
@@ -357,6 +453,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",
@@ -398,108 +495,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 9/9] app/dma-perf: refactor benchmark function
  2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng
                   ` (7 preceding siblings ...)
  2025-08-11 10:54 ` [PATCH 8/9] app/dma-perf: refactor load config function Chengwen Feng
@ 2025-08-11 10:54 ` Chengwen Feng
  2025-08-12  2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng
  9 siblings, 0 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-11 10:54 UTC (permalink / raw)
  To: thomas; +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 00/10] bugfix and refactor of dma-perf
  2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng
                   ` (8 preceding siblings ...)
  2025-08-11 10:54 ` [PATCH 9/9] app/dma-perf: refactor benchmark function Chengwen Feng
@ 2025-08-12  2:06 ` Chengwen Feng
  2025-08-12  2:06   ` [PATCH 01/10] app/dma-perf: fix use-after-free Chengwen Feng
                     ` (9 more replies)
  9 siblings, 10 replies; 21+ messages in thread
From: Chengwen Feng @ 2025-08-12  2:06 UTC (permalink / raw)
  To: thomas, honest.jiang; +Cc: dev, liuyonglong

This patchset contain one bugfix and eight refactor commit for
test-dma-perf application.

Chengwen Feng (10):
  app/dma-perf: fix use-after-free
  app/dma-perf: add global section for config file
  app/dma-perf: use argparse lib to parse argument
  app/dma-perf: refactor output csv
  app/dma-perf: support list DMA devices
  app/dma-perf: add more global config
  app/dma-perf: remove invalid or redundant field
  app/dma-perf: refactor load config function
  app/dma-perf: refactor benchmark function
  app/dma-perf: support specific error info

---
v2: fix CI error: format string is not a string literal
    add one extra commit: support specific error info

 app/test-dma-perf/benchmark.c | 182 ++++++-----
 app/test-dma-perf/config.ini  |  35 +--
 app/test-dma-perf/main.c      | 573 ++++++++++++++++++----------------
 app/test-dma-perf/main.h      |  28 +-
 app/test-dma-perf/meson.build |   2 +-
 doc/guides/tools/dmaperf.rst  |  50 +--
 6 files changed, 462 insertions(+), 408 deletions(-)

-- 
2.17.1


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

* [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

end of thread, other threads:[~2025-08-12  2:08 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng
2025-08-11 10:54 ` [PATCH 1/9] app/dma-perf: fix use-after-free Chengwen Feng
2025-08-11 10:54 ` [PATCH 2/9] app/dma-perf: add global section for config file Chengwen Feng
2025-08-11 10:54 ` [PATCH 3/9] app/dma-perf: use argparse lib to parse argument Chengwen Feng
2025-08-11 10:54 ` [PATCH 4/9] app/dma-perf: refactor output csv Chengwen Feng
2025-08-11 10:54 ` [PATCH 5/9] app/dma-perf: support list DMA devices Chengwen Feng
2025-08-11 10:54 ` [PATCH 6/9] app/dma-perf: add more global config Chengwen Feng
2025-08-11 10:54 ` [PATCH 7/9] app/dma-perf: remove invalid or redundant field Chengwen Feng
2025-08-11 10:54 ` [PATCH 8/9] app/dma-perf: refactor load config function Chengwen Feng
2025-08-11 10:54 ` [PATCH 9/9] app/dma-perf: refactor benchmark function Chengwen Feng
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   ` [PATCH 03/10] app/dma-perf: use argparse lib to parse argument Chengwen Feng
2025-08-12  2:07   ` [PATCH 04/10] app/dma-perf: refactor output csv Chengwen Feng
2025-08-12  2:07   ` [PATCH 05/10] app/dma-perf: support list DMA devices Chengwen Feng
2025-08-12  2:07   ` [PATCH 06/10] app/dma-perf: add more global config Chengwen Feng
2025-08-12  2:07   ` [PATCH 07/10] app/dma-perf: remove invalid or redundant field Chengwen Feng
2025-08-12  2:07   ` [PATCH 08/10] app/dma-perf: refactor load config function 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).