* [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 ` (10 more replies) 0 siblings, 11 replies; 33+ 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] 33+ 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 ` (9 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (8 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (7 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (6 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (5 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (4 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (3 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (2 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (10 more replies) 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng 10 siblings, 11 replies; 33+ 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] 33+ 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 ` (9 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (8 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (7 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (6 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (5 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (4 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (3 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 ` (2 subsequent siblings) 10 siblings, 0 replies; 33+ 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] 33+ 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 2025-09-11 14:53 ` [EXTERNAL] [PATCH 00/10] bugfix and refactor of dma-perf Vamsi Krishna Attunuru 10 siblings, 0 replies; 33+ 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] 33+ 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 2025-09-11 14:53 ` [EXTERNAL] [PATCH 00/10] bugfix and refactor of dma-perf Vamsi Krishna Attunuru 10 siblings, 0 replies; 33+ 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] 33+ messages in thread
* RE: [EXTERNAL] [PATCH 00/10] bugfix and refactor of dma-perf 2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng ` (9 preceding siblings ...) 2025-08-12 2:07 ` [PATCH 10/10] app/dma-perf: support specific error info Chengwen Feng @ 2025-09-11 14:53 ` Vamsi Krishna Attunuru 10 siblings, 0 replies; 33+ messages in thread From: Vamsi Krishna Attunuru @ 2025-09-11 14:53 UTC (permalink / raw) To: Chengwen Feng, thomas, honest.jiang; +Cc: dev, liuyonglong >-----Original Message----- >From: Chengwen Feng <fengchengwen@huawei.com> >Sent: Tuesday, August 12, 2025 7:37 AM >To: thomas@monjalon.net; honest.jiang@foxmail.com >Cc: dev@dpdk.org; liuyonglong@huawei.com >Subject: [EXTERNAL] [PATCH 00/10] bugfix and refactor of dma-perf > >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 ZjQcmQRYFpfptBannerStart Prioritize security for external emails: >Confirm sender and content safety before clicking links or opening >attachments <https://us-phishalarm- >ewt.proofpoint.com/EWT/v1/CRVmXkqW!ti3Z1f8UAnTa1E-8- >d26bzh7tQFqbKBdLU9oLN5L- >Aaqbzf0spmN1mMd9tGvrkwR2vLtT9OuVOzwc_JrGGPezhvZxK2lONqTfg$> >Report Suspicious > >ZjQcmQRYFpfptBannerEnd >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 Thank you, Feng, for the refactoring. Acked-by: Vamsi Attunuru <vattunuru@marvell.com> ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v2 00/10] bugfix and refactor of dma-perf 2025-08-11 10:54 [PATCH 0/9] bugfix and refactor of dma-perf Chengwen Feng ` (9 preceding siblings ...) 2025-08-12 2:06 ` [PATCH 00/10] bugfix and refactor of dma-perf Chengwen Feng @ 2025-09-17 3:33 ` Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 01/10] app/dma-perf: fix use-after-free Chengwen Feng ` (9 more replies) 10 siblings, 10 replies; 33+ messages in thread From: Chengwen Feng @ 2025-09-17 3:33 UTC (permalink / raw) To: thomas, honest.jiang; +Cc: dev, liuyonglong, vattunuru This patchset contain one bugfix and refactor commits 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 global argv[0] not prog name. friendly remide if there are no dmadev when list-dma. add ack from Vamsi Attunuru. app/test-dma-perf/benchmark.c | 182 ++++++----- app/test-dma-perf/config.ini | 35 +- app/test-dma-perf/main.c | 581 ++++++++++++++++++---------------- app/test-dma-perf/main.h | 28 +- app/test-dma-perf/meson.build | 2 +- doc/guides/tools/dmaperf.rst | 50 +-- 6 files changed, 470 insertions(+), 408 deletions(-) -- 2.17.1 ^ permalink raw reply [flat|nested] 33+ messages in thread
* [PATCH v2 01/10] app/dma-perf: fix use-after-free 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng @ 2025-09-17 3:33 ` Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 02/10] app/dma-perf: add global section for config file Chengwen Feng ` (8 subsequent siblings) 9 siblings, 0 replies; 33+ messages in thread From: Chengwen Feng @ 2025-09-17 3:33 UTC (permalink / raw) To: thomas, honest.jiang; +Cc: dev, liuyonglong, vattunuru 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> Acked-by: Vamsi Attunuru <vattunuru@marvell.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] 33+ messages in thread
* [PATCH v2 02/10] app/dma-perf: add global section for config file 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 01/10] app/dma-perf: fix use-after-free Chengwen Feng @ 2025-09-17 3:33 ` Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 03/10] app/dma-perf: use argparse lib to parse argument Chengwen Feng ` (7 subsequent siblings) 9 siblings, 0 replies; 33+ messages in thread From: Chengwen Feng @ 2025-09-17 3:33 UTC (permalink / raw) To: thomas, honest.jiang; +Cc: dev, liuyonglong, vattunuru 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> Acked-by: Vamsi Attunuru <vattunuru@marvell.com> --- app/test-dma-perf/config.ini | 17 ++++--- app/test-dma-perf/main.c | 91 +++++++++++++++++------------------- app/test-dma-perf/main.h | 8 +++- doc/guides/tools/dmaperf.rst | 28 ++++++----- 4 files changed, 79 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..7c2bf951ae 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,42 @@ 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) +{ + static char prog_name[] = "test-dma-perf"; + 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, ' '); + global_cfg.eal_argv[0] = prog_name; + for (i = 0; i < token_nb; i++) + global_cfg.eal_argv[i + 1] = tokens[i]; + global_cfg.eal_argc = i + 1; + + return 0; +} + static uint16_t load_configs(const char *path) { @@ -311,7 +347,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 +518,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 +526,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 +534,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 +593,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..6fd5ea24c1 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 + 1]; + 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] 33+ messages in thread
* [PATCH v2 03/10] app/dma-perf: use argparse lib to parse argument 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 01/10] app/dma-perf: fix use-after-free Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 02/10] app/dma-perf: add global section for config file Chengwen Feng @ 2025-09-17 3:33 ` Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 04/10] app/dma-perf: refactor output csv Chengwen Feng ` (6 subsequent siblings) 9 siblings, 0 replies; 33+ messages in thread From: Chengwen Feng @ 2025-09-17 3:33 UTC (permalink / raw) To: thomas, honest.jiang; +Cc: dev, liuyonglong, vattunuru This commit uses argparse API to parse arguments. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> Acked-by: Vamsi Attunuru <vattunuru@marvell.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 7c2bf951ae..99b5b753ec 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; @@ -526,54 +525,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; } @@ -581,7 +609,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; } @@ -603,7 +631,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] 33+ messages in thread
* [PATCH v2 04/10] app/dma-perf: refactor output csv 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng ` (2 preceding siblings ...) 2025-09-17 3:33 ` [PATCH v2 03/10] app/dma-perf: use argparse lib to parse argument Chengwen Feng @ 2025-09-17 3:33 ` Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 05/10] app/dma-perf: support list DMA devices Chengwen Feng ` (5 subsequent siblings) 9 siblings, 0 replies; 33+ messages in thread From: Chengwen Feng @ 2025-09-17 3:33 UTC (permalink / raw) To: thomas, honest.jiang; +Cc: dev, liuyonglong, vattunuru The original implement involved many functions and global variables, This commit refactor it. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> Acked-by: Vamsi Attunuru <vattunuru@marvell.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 99b5b753ec..1d0850e7a6 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; @@ -547,6 +533,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); @@ -599,18 +586,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; } @@ -631,12 +615,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]); @@ -644,8 +622,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 6fd5ea24c1..f0f68c0150 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] 33+ messages in thread
* [PATCH v2 05/10] app/dma-perf: support list DMA devices 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng ` (3 preceding siblings ...) 2025-09-17 3:33 ` [PATCH v2 04/10] app/dma-perf: refactor output csv Chengwen Feng @ 2025-09-17 3:33 ` Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 06/10] app/dma-perf: add more global config Chengwen Feng ` (4 subsequent siblings) 9 siblings, 0 replies; 33+ messages in thread From: Chengwen Feng @ 2025-09-17 3:33 UTC (permalink / raw) To: thomas, honest.jiang; +Cc: dev, liuyonglong, vattunuru 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> Acked-by: Vamsi Attunuru <vattunuru@marvell.com> --- app/test-dma-perf/main.c | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/app/test-dma-perf/main.c b/app/test-dma-perf/main.c index 1d0850e7a6..601e2a73a0 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 @@ -529,6 +530,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(), }, }; @@ -569,6 +574,51 @@ 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; + uint32_t count = 0; + 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); + count++; + } + + printf("\n"); + if (count == 0) + printf("There are no dmadev devices!\n\n"); + + rte_eal_cleanup(); +} + int main(int argc, char *argv[]) { @@ -582,6 +632,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] 33+ messages in thread
* [PATCH v2 06/10] app/dma-perf: add more global config 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng ` (4 preceding siblings ...) 2025-09-17 3:33 ` [PATCH v2 05/10] app/dma-perf: support list DMA devices Chengwen Feng @ 2025-09-17 3:33 ` Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 07/10] app/dma-perf: remove invalid or redundant field Chengwen Feng ` (3 subsequent siblings) 9 siblings, 0 replies; 33+ messages in thread From: Chengwen Feng @ 2025-09-17 3:33 UTC (permalink / raw) To: thomas, honest.jiang; +Cc: dev, liuyonglong, vattunuru This commit add 'cache_flush' and 'test_seconds' to the global configuration. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> Acked-by: Vamsi Attunuru <vattunuru@marvell.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 601e2a73a0..153a844e7e 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; @@ -307,6 +307,20 @@ parse_global_config(struct rte_cfgfile *cfgfile) global_cfg.eal_argv[i + 1] = tokens[i]; global_cfg.eal_argc = i + 1; + 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; } @@ -499,11 +513,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 f0f68c0150..5d9077c62d 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 + 1]; 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] 33+ messages in thread
* [PATCH v2 07/10] app/dma-perf: remove invalid or redundant field 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng ` (5 preceding siblings ...) 2025-09-17 3:33 ` [PATCH v2 06/10] app/dma-perf: add more global config Chengwen Feng @ 2025-09-17 3:33 ` Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 08/10] app/dma-perf: refactor load config function Chengwen Feng ` (2 subsequent siblings) 9 siblings, 0 replies; 33+ messages in thread From: Chengwen Feng @ 2025-09-17 3:33 UTC (permalink / raw) To: thomas, honest.jiang; +Cc: dev, liuyonglong, vattunuru Remove invalid or redundant fields to make code more clean. Signed-off-by: Chengwen Feng <fengchengwen@huawei.com> Acked-by: Vamsi Attunuru <vattunuru@marvell.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 153a844e7e..08a7848dec 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; } @@ -338,7 +333,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); @@ -376,19 +370,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, @@ -412,7 +401,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 5d9077c62d..63e31951fa 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] 33+ messages in thread
* [PATCH v2 08/10] app/dma-perf: refactor load config function 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng ` (6 preceding siblings ...) 2025-09-17 3:33 ` [PATCH v2 07/10] app/dma-perf: remove invalid or redundant field Chengwen Feng @ 2025-09-17 3:33 ` Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 09/10] app/dma-perf: refactor benchmark function Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 10/10] app/dma-perf: support specific error info Chengwen Feng 9 siblings, 0 replies; 33+ messages in thread From: Chengwen Feng @ 2025-09-17 3:33 UTC (permalink / raw) To: thomas, honest.jiang; +Cc: dev, liuyonglong, vattunuru 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> Acked-by: Vamsi Attunuru <vattunuru@marvell.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 08a7848dec..97718ad9ee 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) { @@ -322,17 +421,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); @@ -360,6 +456,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", @@ -401,108 +498,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] 33+ messages in thread
* [PATCH v2 09/10] app/dma-perf: refactor benchmark function 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng ` (7 preceding siblings ...) 2025-09-17 3:33 ` [PATCH v2 08/10] app/dma-perf: refactor load config function Chengwen Feng @ 2025-09-17 3:33 ` Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 10/10] app/dma-perf: support specific error info Chengwen Feng 9 siblings, 0 replies; 33+ messages in thread From: Chengwen Feng @ 2025-09-17 3:33 UTC (permalink / raw) To: thomas, honest.jiang; +Cc: dev, liuyonglong, vattunuru 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> Acked-by: Vamsi Attunuru <vattunuru@marvell.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] 33+ messages in thread
* [PATCH v2 10/10] app/dma-perf: support specific error info 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng ` (8 preceding siblings ...) 2025-09-17 3:33 ` [PATCH v2 09/10] app/dma-perf: refactor benchmark function Chengwen Feng @ 2025-09-17 3:33 ` Chengwen Feng 9 siblings, 0 replies; 33+ messages in thread From: Chengwen Feng @ 2025-09-17 3:33 UTC (permalink / raw) To: thomas, honest.jiang; +Cc: dev, liuyonglong, vattunuru 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> Acked-by: Vamsi Attunuru <vattunuru@marvell.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 97718ad9ee..bc4f0449ec 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); @@ -385,11 +387,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"); @@ -401,18 +399,10 @@ parse_global_config(struct rte_cfgfile *cfgfile) global_cfg.eal_argv[i + 1] = tokens[i]; global_cfg.eal_argc = i + 1; - 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; @@ -457,14 +447,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) { @@ -475,12 +458,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); @@ -489,7 +472,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] 33+ messages in thread
end of thread, other threads:[~2025-09-17 3:34 UTC | newest] Thread overview: 33+ 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 2025-09-11 14:53 ` [EXTERNAL] [PATCH 00/10] bugfix and refactor of dma-perf Vamsi Krishna Attunuru 2025-09-17 3:33 ` [PATCH v2 " Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 01/10] app/dma-perf: fix use-after-free Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 02/10] app/dma-perf: add global section for config file Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 03/10] app/dma-perf: use argparse lib to parse argument Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 04/10] app/dma-perf: refactor output csv Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 05/10] app/dma-perf: support list DMA devices Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 06/10] app/dma-perf: add more global config Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 07/10] app/dma-perf: remove invalid or redundant field Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 08/10] app/dma-perf: refactor load config function Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 09/10] app/dma-perf: refactor benchmark function Chengwen Feng 2025-09-17 3:33 ` [PATCH v2 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).