Thanks Yan for the review comments. > -----Original Message----- > From: Yan, Zhirun <zhirun....@intel.com> > Sent: Friday, May 12, 2023 4:38 PM > To: Vamsi Krishna Attunuru <vattun...@marvell.com>; dev@dpdk.org; > tho...@monjalon.net; Jerin Jacob Kollanukkaran <jer...@marvell.com> > Cc: Nithin Kumar Dabilpuram <ndabilpu...@marvell.com>; Liang, Cunming > <cunming.li...@intel.com>; Wang, Haiyue <haiyue.w...@intel.com>; Sunil > Kumar Kori <sk...@marvell.com> > Subject: [EXT] RE: [PATCH v2 4/4] app: add testgraph application > > External Email > > ---------------------------------------------------------------------- > > > > -----Original Message----- > > From: Vamsi Attunuru <vattun...@marvell.com> > > Sent: Tuesday, April 25, 2023 9:15 PM > > To: dev@dpdk.org; tho...@monjalon.net; jer...@marvell.com > > Cc: vattun...@marvell.com; ndabilpu...@marvell.com > > Subject: [PATCH v2 4/4] app: add testgraph application > > > > Patch adds test-graph application to validate graph > > and node libraries. > > > > Signed-off-by: Vamsi Attunuru <vattun...@marvell.com> > > --- > > app/meson.build | 1 + > > app/test-graph/cmdline.c | 211 +++++ > > app/test-graph/cmdline_graph.c | 294 +++++++ > > app/test-graph/cmdline_graph.h | 19 + > > app/test-graph/meson.build | 14 + > > app/test-graph/parameters.c | 157 ++++ > > app/test-graph/testgraph.c | 1426 > ++++++++++++++++++++++++++++++++ > > app/test-graph/testgraph.h | 91 ++ > > doc/guides/tools/index.rst | 1 + > > doc/guides/tools/testgraph.rst | 131 +++ > > 10 files changed, 2345 insertions(+) > > > > diff --git a/app/meson.build b/app/meson.build > > index 74d2420f67..6c7b24e604 100644 > > --- a/app/meson.build > > +++ b/app/meson.build > > @@ -22,6 +22,7 @@ apps = [ > > 'test-eventdev', > > 'test-fib', > > 'test-flow-perf', > > + 'test-graph', > > 'test-gpudev', > > 'test-mldev', > > 'test-pipeline', > > diff --git a/app/test-graph/cmdline.c b/app/test-graph/cmdline.c > > new file mode 100644 > > index 0000000000..d9474d827a > > --- /dev/null > > +++ b/app/test-graph/cmdline.c > > @@ -0,0 +1,211 @@ > > +/* SPDX-License-Identifier: BSD-3-Clause > > + * Copyright(C) 2023 Marvell International Ltd. > > + */ > > + > > +#include <stdlib.h> > > + > > +#include <cmdline.h> > > +#include <cmdline_parse.h> > > +#include <cmdline_parse_etheraddr.h> > > +#include <cmdline_parse_ipaddr.h> > > +#include <cmdline_parse_num.h> > > +#include <cmdline_parse_string.h> > > +#include <cmdline_rdline.h> > > +#include <cmdline_socket.h> > > + > > +#include "cmdline_graph.h" > > +#include "testgraph.h" > > + > > +static struct cmdline *testgraph_cl; > > +static cmdline_parse_ctx_t *main_ctx; > > + > > +/* *** Help command with introduction. *** */ > > +struct cmd_help_brief_result { > > + cmdline_fixed_string_t help; > > +}; > > + > > +static void > > +cmd_help_brief_parsed(__rte_unused void *parsed_result, struct > cmdline *cl, > > __rte_unused void *data) > > +{ > > + cmdline_printf(cl, > > + "\n" > > + "Help is available for the following sections:\n\n" > > + " help control : Start and stop > > graph walk.\n" > > + " help display : Displaying port, > > stats and config > > " > > + "information.\n" > > + " help config : Configuration > > information.\n" > > + " help all : All of the above > > sections.\n\n"); > > +} > > + > > +static cmdline_parse_token_string_t cmd_help_brief_help = > > + TOKEN_STRING_INITIALIZER(struct cmd_help_brief_result, help, > "help"); > > + > > +static cmdline_parse_inst_t cmd_help_brief = { > > + .f = cmd_help_brief_parsed, > > + .data = NULL, > > + .help_str = "help: Show help", > > + .tokens = { > > + (void *)&cmd_help_brief_help, > > + NULL, > > + }, > > +}; > > + > > +/* *** Help command with help sections. *** */ > > +struct cmd_help_long_result { > > + cmdline_fixed_string_t help; > > + cmdline_fixed_string_t section; > > +}; > > + > > +static void > > +cmd_help_long_parsed(void *parsed_result, struct cmdline *cl, > __rte_unused > > void *data) > > +{ > > + int show_all = 0; > > + struct cmd_help_long_result *res = parsed_result; > > + > > + if (!strcmp(res->section, "all")) > > + show_all = 1; > > + > > + if (show_all || !strcmp(res->section, "control")) { > > + > > + cmdline_printf(cl, "\n" > > + "Control forwarding:\n" > > + "-------------------\n\n" > > + > > + "start graph_walk\n" > > + " Start graph_walk on worker threads.\n\n" > > + > > + "stop graph_walk\n" > > + " Stop worker threads from running > > graph_walk.\n\n" > > + > > + "quit\n" > > + " Quit to prompt.\n\n"); > > + } > > + > > + if (show_all || !strcmp(res->section, "display")) { > > + > > + cmdline_printf(cl, > > + "\n" > > + "Display:\n" > > + "--------\n\n" > > + > > + "show node_list\n" > > + " Display the list of supported nodes.\n\n" > > + > > + "show graph_stats\n" > > + " Display the node statistics of graph > cluster.\n\n"); > > + } > > + > > + if (show_all || !strcmp(res->section, "config")) { > > + cmdline_printf(cl, "\n" > > + "Configuration:\n" > > + "--------------\n" > > + "set lcore_config > > (port_id0,rxq0,lcore_idX),..." > > + ".....,(port_idX,rxqX,lcoreidY)\n" > > + " Set lcore configuration.\n\n" > > + > > + "create_graph > > (node0_name,node1_name,...,nodeX_name)\n" > > + " Create graph instances using the provided > > node details.\n\n" > > + > > + "destroy_graph\n" > > + " Destroy the graph instances.\n\n"); > > + } > > +} > > + > > +static cmdline_parse_token_string_t cmd_help_long_help = > > + TOKEN_STRING_INITIALIZER(struct cmd_help_long_result, help, > "help"); > > + > > +static cmdline_parse_token_string_t cmd_help_long_section = > > TOKEN_STRING_INITIALIZER( > > + struct cmd_help_long_result, section, "all#control#display#config"); > > + > > +static cmdline_parse_inst_t cmd_help_long = { > > + .f = cmd_help_long_parsed, > > + .data = NULL, > > + .help_str = "help all|control|display|config: " > > + "Show help", > > + .tokens = { > > + (void *)&cmd_help_long_help, > > + (void *)&cmd_help_long_section, > > + NULL, > > + }, > > +}; > > + > > +/* *** QUIT *** */ > > +struct cmd_quit_result { > > + cmdline_fixed_string_t quit; > > +}; > > + > > +static void > > +cmd_quit_parsed(__rte_unused void *parsed_result, struct cmdline *cl, > > __rte_unused void *data) > > +{ > > + cmdline_quit(cl); > > +} > > + > > +static cmdline_parse_token_string_t cmd_quit_quit = > > + TOKEN_STRING_INITIALIZER(struct cmd_quit_result, quit, "quit"); > > + > > +static cmdline_parse_inst_t cmd_quit = { > > + .f = cmd_quit_parsed, > > + .data = NULL, > > + .help_str = "quit: Exit application", > > + .tokens = { > > + (void *)&cmd_quit_quit, > > + NULL, > > + }, > > +}; > > + > > +/* list of instructions */ > > +static cmdline_parse_ctx_t builtin_ctx[] = { > > + (cmdline_parse_inst_t *)&cmd_help_brief, > > + (cmdline_parse_inst_t *)&cmd_help_long, > > + (cmdline_parse_inst_t *)&cmd_quit, > > + (cmdline_parse_inst_t *)&cmd_show_node_list, > > + (cmdline_parse_inst_t *)&cmd_set_lcore_config, > > + (cmdline_parse_inst_t *)&cmd_create_graph, > > + (cmdline_parse_inst_t *)&cmd_destroy_graph, > > + (cmdline_parse_inst_t *)&cmd_start_graph_walk, > > + (cmdline_parse_inst_t *)&cmd_stop_graph_walk, > > + (cmdline_parse_inst_t *)&cmd_show_graph_stats, > > + NULL, > > +}; > > + > > +int > > +init_cmdline(void) > > +{ > > + unsigned int count; > > + unsigned int i; > > + > > + count = 0; > > + for (i = 0; builtin_ctx[i] != NULL; i++) > > + count++; > > + > > + /* cmdline expects a NULL terminated array */ > > + main_ctx = calloc(count + 1, sizeof(main_ctx[0])); > > + if (main_ctx == NULL) > > + return -1; > > + > > + count = 0; > > + for (i = 0; builtin_ctx[i] != NULL; i++, count++) > > + main_ctx[count] = builtin_ctx[i]; > > + > > + return 0; > > +} > > + > > +void > > +prompt_exit(void) > > +{ > > + cmdline_quit(testgraph_cl); > > +} > > + > > +/* prompt function, called from main on MAIN lcore */ > > +void > > +prompt(void) > > +{ > > + testgraph_cl = cmdline_stdin_new(main_ctx, "testgraph> "); > > + if (testgraph_cl == NULL) { > > + fprintf(stderr, "Failed to create stdin based cmdline > context\n"); > > + return; > > + } > > + > > + cmdline_interact(testgraph_cl); > > + cmdline_stdin_exit(testgraph_cl); > > +} > > diff --git a/app/test-graph/cmdline_graph.c b/app/test- > graph/cmdline_graph.c > > new file mode 100644 > > index 0000000000..d66149a224 > > --- /dev/null > > +++ b/app/test-graph/cmdline_graph.c > > @@ -0,0 +1,294 @@ > > +/* SPDX-License-Identifier: BSD-3-Clause > > + * Copyright(C) 2023 Marvell International Ltd. > > + */ > > + > > +#include <stdlib.h> > > +#include <string.h> > > + > > +#include <cmdline_parse.h> > > +#include <cmdline_parse_num.h> > > +#include <cmdline_parse_string.h> > > + > > +#include "cmdline_graph.h" > > +#include "testgraph.h" > > + > > +/* *** Show supported node details *** */ > > +struct cmd_show_node_list_result { > > + cmdline_fixed_string_t show; > > + cmdline_fixed_string_t node_list; > > +}; > > + > > +static cmdline_parse_token_string_t cmd_show_node_list_show = > > + TOKEN_STRING_INITIALIZER(struct cmd_show_node_list_result, > show, > > "show"); > > +static cmdline_parse_token_string_t cmd_show_node_list_node_list = > > + TOKEN_STRING_INITIALIZER(struct cmd_show_node_list_result, > > node_list, "node_list"); > > + > > +static void > > +cmd_show_node_list_parsed(__rte_unused void *parsed_result, > __rte_unused > > struct cmdline *cl, > > + __rte_unused void *data) > > +{ > > + rte_node_t node_cnt = rte_node_max_count(); > > + rte_node_t id; > > + > > + printf("\n**** Supported Graph Nodes ****\n"); > > + for (id = 0; id < node_cnt; id++) > > + printf("%s\n", rte_node_id_to_name(id)); > > + > > + printf("********************************\n"); > > +} > > + > > +cmdline_parse_inst_t cmd_show_node_list = { > > + .f = cmd_show_node_list_parsed, > > + .data = NULL, > > + .help_str = "show node_list", > > + .tokens = { > > + (void *)&cmd_show_node_list_show, > > + (void *)&cmd_show_node_list_node_list, > > + NULL, > > + }, > > +}; > > + > > +/* *** Set lcore config *** */ > > +struct cmd_set_lcore_config_result { > > + cmdline_fixed_string_t set; > > + cmdline_fixed_string_t lcore_config; > > + cmdline_multi_string_t token_string; > > +}; > > + > > +static cmdline_parse_token_string_t cmd_set_lcore_config_set = > > + TOKEN_STRING_INITIALIZER(struct cmd_set_lcore_config_result, > set, > > "set"); > > +static cmdline_parse_token_string_t cmd_set_lcore_config_lcore_config > = > > + TOKEN_STRING_INITIALIZER(struct cmd_set_lcore_config_result, > > lcore_config, "lcore_config"); > > +static cmdline_parse_token_string_t cmd_set_lcore_config_token_string > = > > TOKEN_STRING_INITIALIZER( > > + struct cmd_set_lcore_config_result, token_string, > > TOKEN_STRING_MULTI); > > + > > +static void > > +cmd_set_lcore_config_parsed(void *parsed_result, __rte_unused struct > > cmdline *cl, > > + __rte_unused void *data) > > +{ > > + struct cmd_set_lcore_config_result *res = parsed_result; > > + const char *t_str = res->token_string; > > + int ret; > > + > > + /* Parse string */ > > + ret = parse_config(t_str); > > + if (ret) { > > + printf(" lcore_config string parse error\n"); > > + return; > > + } > > + > > + validate_config(); > > +} > > + > > +cmdline_parse_inst_t cmd_set_lcore_config = { > > + .f = cmd_set_lcore_config_parsed, > > + .data = NULL, > > + .help_str = "set lcore_config " > > + "(port,queue,lcore),[(port,queue,lcore) ... > > (port,queue,lcore)]", > > + .tokens = { > > + (void *)&cmd_set_lcore_config_set, > > + (void *)&cmd_set_lcore_config_lcore_config, > > + (void *)&cmd_set_lcore_config_token_string, > > + NULL, > > + }, > > +}; > > + > > +/* *** Create graph *** */ > > +struct cmd_create_graph_result { > > + cmdline_fixed_string_t create_graph; > > + cmdline_multi_string_t token_string; > > +}; > > + > > +static cmdline_parse_token_string_t cmd_create_graph_create_graph = > > + TOKEN_STRING_INITIALIZER(struct cmd_create_graph_result, > > create_graph, "create_graph"); > > +static cmdline_parse_token_string_t cmd_create_graph_token_string = > > + TOKEN_STRING_INITIALIZER(struct cmd_create_graph_result, > > token_string, TOKEN_STRING_MULTI); > > + > > +static void > > +cmd_create_graph_parsed(void *parsed_result, __rte_unused struct > cmdline > > *cl, > > + __rte_unused void *data) > > +{ > > + struct cmd_create_graph_result *res = parsed_result; > > + const char *t_str = res->token_string; > > + uint64_t valid_nodes = 0; > > + int ret; > > + > > + ret = parse_node_patterns(t_str); > > + if (ret) { > > + printf("parse_node_patterns failed\n"); > > + cleanup_node_pattern(); > > + return; > > + } > > + > > + ret = validate_node_names(&valid_nodes); > > + if (ret) { > > + printf("validate_node_names() failed\n"); > > + cleanup_node_pattern(); > > + return; > > + } > > Hi Vamsi, > > The global node_pattern should be cleaned in destroy stage or make sure it > populated from cmd line completely. > In Interactive-mode, user may create and destroy a graph and then create a > new one. > For this case, the node name is changed by the previous creation, especially > for rx/tx node.(ethdev_rx -> ethdev_rx-0-0) > Sure, will fix it next version.
> > + > > + nb_conf = ethdev_ports_setup(); > > + > > + ethdev_rxq_configure(); > > + ethdev_txq_configure(); > > + > > + ret = configure_graph_nodes(valid_nodes); > > + if (ret) > > + rte_exit(EXIT_FAILURE, "configure_graph_nodes: > err=%d\n", > > ret); > > + > > + ret = create_graph(valid_nodes); > > + if (ret) > > + rte_exit(EXIT_FAILURE, "create_graph: err=%d\n", ret); > > + > > + stats = create_graph_cluster_stats(); > > + if (stats == NULL) > > + rte_exit(EXIT_FAILURE, "create_graph_cluster_stats() > failed\n"); > > + > > + check_all_ports_link_status(enabled_port_mask); > > + start_eth_ports(); > > +} > > Could this create cmd be break down for graph_config and graph_create? > In graph_config stage, we can set/verify graph topo, chose graph-walk > models and update node ctx, etc. > How about having a separate cmd to select the graph-walk model, that could be run before create_graph cmd. Yes it could be split if really required, let me check what all can be moved out of graph_create(). > > + > > +cmdline_parse_inst_t cmd_create_graph = { > > + .f = cmd_create_graph_parsed, > > + .data = NULL, > > + .help_str = "create_graph " > > + "[node_name0,node_name1,node_name2 ... > node_nameX]", > > + .tokens = { > > + (void *)&cmd_create_graph_create_graph, > > + (void *)&cmd_create_graph_token_string, > > + NULL, > > + }, > > +}; > > + > > +/**** Destroy graph ****/ > > +struct cmd_destroy_graph_result { > > + cmdline_fixed_string_t destroy_graph; > > +}; > > + > > +static cmdline_parse_token_string_t cmd_destroy_graph_destroy_graph > = > > + TOKEN_STRING_INITIALIZER(struct cmd_destroy_graph_result, > > destroy_graph, "destroy_graph"); > > + > > +static void > > +cmd_destroy_graph_parsed(__rte_unused void *parsed_result, > __rte_unused > > struct cmdline *cl, > > + __rte_unused void *data) > > +{ > > + uint32_t lcore_id; > > + > > + run_graph_walk = false; > > + graph_walk_quit = true; > > + > > + RTE_LCORE_FOREACH_WORKER(lcore_id) > > + rte_eal_wait_lcore(lcore_id); > > + > > + destroy_graph(); > > + stop_eth_ports(); > > +} > > + > > +cmdline_parse_inst_t cmd_destroy_graph = { > > + .f = cmd_destroy_graph_parsed, > > + .data = NULL, > > + .help_str = "destroy_graph", > > + .tokens = { > > + (void *)&cmd_destroy_graph_destroy_graph, > > + NULL, > > + }, > > +}; > > + > > +/**** Start graph_walk ****/ > > +struct cmd_start_graph_walk_result { > > + cmdline_fixed_string_t start; > > + cmdline_fixed_string_t graph_walk; > > +}; > > + > > +static cmdline_parse_token_string_t cmd_start_graph_walk_start = > > + TOKEN_STRING_INITIALIZER(struct cmd_start_graph_walk_result, > start, > > "start"); > > +static cmdline_parse_token_string_t cmd_start_graph_walk_graph_walk > = > > + TOKEN_STRING_INITIALIZER(struct cmd_start_graph_walk_result, > > graph_walk, "graph_walk"); > > + > > +static void > > +cmd_start_graph_walk_parsed(__rte_unused void *parsed_result, > > __rte_unused struct cmdline *cl, > > + __rte_unused void *data) > > +{ > > + static bool launch_graph_walk; > > + > > + if (!launch_graph_walk) { > > + rte_eal_mp_remote_launch(graph_main_loop, NULL, > > SKIP_MAIN); > > As run_graph_walk is used to control graph-walk, Why not put the launch in > create stage? Yes, will change in next version. > > > + launch_graph_walk = true; > > + } > > + > > + run_graph_walk = true; > > +} > > + > > +cmdline_parse_inst_t cmd_start_graph_walk = { > > + .f = cmd_start_graph_walk_parsed, > > + .data = NULL, > > + .help_str = "start graph_walk", > > + .tokens = { > > + (void *)&cmd_start_graph_walk_start, > > + (void *)&cmd_start_graph_walk_graph_walk, > > + NULL, > > + }, > > +}; > > + > > +/**** Stop graph_walk ****/ > > +struct cmd_stop_graph_walk_result { > > + cmdline_fixed_string_t stop; > > + cmdline_fixed_string_t graph_walk; > > +}; > > + > > +static cmdline_parse_token_string_t cmd_stop_graph_walk_stop = > > + TOKEN_STRING_INITIALIZER(struct cmd_stop_graph_walk_result, > stop, > > "stop"); > > +static cmdline_parse_token_string_t cmd_stop_graph_walk_graph_walk > = > > + TOKEN_STRING_INITIALIZER(struct cmd_stop_graph_walk_result, > > graph_walk, "graph_walk"); > > + > > +static void > > +cmd_stop_graph_walk_parsed(__rte_unused void *parsed_result, > > __rte_unused struct cmdline *cl, > > + __rte_unused void *data) > > +{ > > + run_graph_walk = false; > > +} > > + > > +cmdline_parse_inst_t cmd_stop_graph_walk = { > > + .f = cmd_stop_graph_walk_parsed, > > + .data = NULL, > > + .help_str = "stop graph_walk", > > + .tokens = { > > + (void *)&cmd_stop_graph_walk_stop, > > + (void *)&cmd_stop_graph_walk_graph_walk, > > + NULL, > > + }, > > +}; > > + > > +/**** Show graph_stats ****/ > > +struct cmd_show_graph_stats_result { > > + cmdline_fixed_string_t show; > > + cmdline_fixed_string_t graph_stats; > > +}; > > + > > +static cmdline_parse_token_string_t cmd_show_graph_stats_show = > > + TOKEN_STRING_INITIALIZER(struct cmd_show_graph_stats_result, > > show, "show"); > > +static cmdline_parse_token_string_t > cmd_show_graph_stats_graph_stats = > > + TOKEN_STRING_INITIALIZER(struct cmd_show_graph_stats_result, > > graph_stats, "graph_stats"); > > + > > +static void > > +cmd_show_graph_stats_parsed(__rte_unused void *parsed_result, > > __rte_unused struct cmdline *cl, > > + __rte_unused void *data) > > +{ > > + if (rte_graph_has_stats_feature()) { > > + if (stats) > > + rte_graph_cluster_stats_get(stats, 0); > > + } else { > > + printf(" graph stats feature not enabled in rte_config.\n"); > > + } > > +} > > + > > +cmdline_parse_inst_t cmd_show_graph_stats = { > > + .f = cmd_show_graph_stats_parsed, > > + .data = NULL, > > + .help_str = "show graph_stats", > > + .tokens = { > > + (void *)&cmd_show_graph_stats_show, > > + (void *)&cmd_show_graph_stats_graph_stats, > > + NULL, > > + }, > > +}; > > diff --git a/app/test-graph/cmdline_graph.h b/app/test- > graph/cmdline_graph.h > > new file mode 100644 > > index 0000000000..2846ff5425 > > --- /dev/null > > +++ b/app/test-graph/cmdline_graph.h > > @@ -0,0 +1,19 @@ > > +/* SPDX-License-Identifier: BSD-3-Clause > > + * Copyright(C) 2023 Marvell International Ltd. > > + */ > > + > > +#ifndef _CMDLINE_GRAPH_H_ > > +#define _CMDLINE_GRAPH_H_ > > + > > +extern cmdline_parse_inst_t cmd_show_node_list; > > +extern cmdline_parse_inst_t cmd_set_lcore_config; > > + > > +extern cmdline_parse_inst_t cmd_create_graph; > > +extern cmdline_parse_inst_t cmd_destroy_graph; > > + > > +extern cmdline_parse_inst_t cmd_start_graph_walk; > > +extern cmdline_parse_inst_t cmd_stop_graph_walk; > > + > > +extern cmdline_parse_inst_t cmd_show_graph_stats; > > + > > +#endif /* _CMDLINE_GRAPH_H_ */ > > diff --git a/app/test-graph/meson.build b/app/test-graph/meson.build > > new file mode 100644 > > index 0000000000..d93802a975 > > --- /dev/null > > +++ b/app/test-graph/meson.build > > @@ -0,0 +1,14 @@ > > +# SPDX-License-Identifier: BSD-3-Clause > > +# Copyright(C) 2023 Marvell International Ltd. > > + > > +# override default name to drop the hyphen > > +name = 'test-graph' > > +cflags += '-Wno-deprecated-declarations' > > +sources = files( > > + 'cmdline.c', > > + 'cmdline_graph.c', > > + 'parameters.c', > > + 'testgraph.c', > > +) > > + > > +deps += ['ethdev', 'cmdline', 'graph', 'node', 'eal'] > > diff --git a/app/test-graph/parameters.c b/app/test-graph/parameters.c > > new file mode 100644 > > index 0000000000..b990ca4a1c > > --- /dev/null > > +++ b/app/test-graph/parameters.c > > @@ -0,0 +1,157 @@ > > +/* SPDX-License-Identifier: BSD-3-Clause > > + * Copyright(C) 2023 Marvell International Ltd. > > + */ > > + > > +#include <getopt.h> > > +#include <stdlib.h> > > + > > +#include "testgraph.h" > > + > > +static const char short_options[] = "p:" /* portmask */ > > + "P" /* promiscuous */ > > + "i" /* interactive */ > > + ; > > + > > +#define CMD_LINE_OPT_CONFIG "config" > > +#define CMD_LINE_OPT_NODE_PATTERN "node-pattern" > > +#define CMD_LINE_OPT_INTERACTIVE "interactive" > > +#define CMD_LINE_OPT_NO_NUMA "no-numa" > > +#define CMD_LINE_OPT_PER_PORT_POOL "per-port-pool" > > +enum { > > + /* Long options mapped to a short option */ > > + > > + /* First long only option value must be >= 256, so that we won't > > + * conflict with short options > > + */ > > + CMD_LINE_OPT_MIN_NUM = 256, > > + CMD_LINE_OPT_CONFIG_NUM, > > + CMD_LINE_OPT_NODE_PATTERN_NUM, > > + CMD_LINE_OPT_INTERACTIVE_NUM, > > + CMD_LINE_OPT_NO_NUMA_NUM, > > + CMD_LINE_OPT_PARSE_PER_PORT_POOL, > > +}; > > + > > +static const struct option lgopts[] = { > > + {CMD_LINE_OPT_CONFIG, 1, 0, CMD_LINE_OPT_CONFIG_NUM}, > > + {CMD_LINE_OPT_NODE_PATTERN, 1, 0, > > CMD_LINE_OPT_NODE_PATTERN_NUM}, > > + {CMD_LINE_OPT_INTERACTIVE, 0, 0, > > CMD_LINE_OPT_INTERACTIVE_NUM}, > > + {CMD_LINE_OPT_NO_NUMA, 0, 0, > CMD_LINE_OPT_NO_NUMA_NUM}, > > + {CMD_LINE_OPT_PER_PORT_POOL, 0, 0, > > CMD_LINE_OPT_PARSE_PER_PORT_POOL}, > > + {NULL, 0, 0, 0}, > > +}; > > + > > +/* Display usage */ > > +static void > > +print_usage(const char *prgname) > > +{ > > + fprintf(stderr, > > + "%s [EAL options] --" > > + " -p PORTMASK" > > + " [-P]" > > + " [-i]" > > + " --config (port,queue,lcore)[,(port,queue,lcore)]" > > + " --node-pattern > (node_name0,node_name1[,node_nameX)]" > > + " [--no-numa]" > > + " [--per-port-pool]" > > + " [--interactive]" > > + > > + " -p PORTMASK: Hexadecimal bitmask of ports to > configure\n" > > + " -P : Enable promiscuous mode\n" > > + " -i : Enter interactive mode\n" > > + " --config (port,queue,lcore): Rx queue configuration\n" > > + " --node-pattern (node_names): node patterns to create > > graph\n" > > + " --no-numa: Disable numa awareness\n" > > + " --per-port-pool: Use separate buffer pool per port\n" > > + " --interactive: Enter interactive mode\n", > > + prgname); > > +} > > + > > +static int > > +parse_portmask(const char *portmask) > > +{ > > + char *end = NULL; > > + unsigned long pm; > > + > > + /* Parse hexadecimal string */ > > + pm = strtoul(portmask, &end, 16); > > + if ((portmask[0] == '\0') || (end == NULL) || (*end != '\0')) > > + return 0; > > + > > + return pm; > > +} > > + > > +/* Parse the argument given in the command line of the application */ > > +int > > +parse_cmdline_args(int argc, char **argv) > > +{ > > + char *prgname = argv[0]; > > + int option_index; > > + char **argvopt; > > + int opt, ret; > > + > > + argvopt = argv; > > + > > + /* Error or normal output strings. */ > > + while ((opt = getopt_long(argc, argvopt, short_options, lgopts, > > &option_index)) != EOF) { > > + > > + switch (opt) { > > + /* Portmask */ > > + case 'p': > > + enabled_port_mask = parse_portmask(optarg); > > + if (enabled_port_mask == 0) { > > + fprintf(stderr, "Invalid portmask\n"); > > + print_usage(prgname); > > + return -1; > > + } > > + break; > > + > > + case 'P': > > + promiscuous_on = 1; > > + break; > > + > > + /* Long options */ > > + case CMD_LINE_OPT_CONFIG_NUM: > > + ret = parse_config(optarg); > > + if (ret) { > > + fprintf(stderr, "Invalid config\n"); > > + print_usage(prgname); > > + return -1; > > + } > > + break; > > + case CMD_LINE_OPT_NODE_PATTERN_NUM: > > + ret = parse_node_patterns(optarg); > > + if (ret) { > > + fprintf(stderr, "Invalid node_patterns\n"); > > + print_usage(prgname); > > + return -1; > > + } > > + break; > > + > > + case CMD_LINE_OPT_INTERACTIVE_NUM: > > + case 'i': > > + printf("Interactive-mode selected\n"); > > + interactive = 1; > > + break; > > + > > + case CMD_LINE_OPT_NO_NUMA_NUM: > > + numa_on = 0; > > + break; > > + > > + case CMD_LINE_OPT_PARSE_PER_PORT_POOL: > > + printf("Per port buffer pool is enabled\n"); > > + per_port_pool = 1; > > + break; > > + > > + default: > > + print_usage(prgname); > > + return -1; > > + } > > + } > > + > > + if (optind >= 0) > > + argv[optind - 1] = prgname; > > + ret = optind - 1; > > + optind = 1; /* Reset getopt lib */ > > + > > + return ret; > > +} > > diff --git a/app/test-graph/testgraph.c b/app/test-graph/testgraph.c > > new file mode 100644 > > index 0000000000..aff921acf2 > > --- /dev/null > > +++ b/app/test-graph/testgraph.c > > @@ -0,0 +1,1426 @@ > > +/* SPDX-License-Identifier: BSD-3-Clause > > + * Copyright(C) 2023 Marvell International Ltd. > > + */ > > + > > +#include <fnmatch.h> > > +#include <signal.h> > > +#include <stdlib.h> > > + > > +#include <rte_bus.h> > > +#include <rte_byteorder.h> > > +#include <rte_common.h> > > +#include <rte_dev.h> > > +#include <rte_eal.h> > > +#include <rte_ethdev.h> > > +#include <rte_ether.h> > > +#include <rte_graph_worker.h> > > +#include <rte_launch.h> > > +#include <rte_lcore.h> > > +#include <rte_log.h> > > +#include <rte_malloc.h> > > +#include <rte_mempool.h> > > +#include <rte_per_lcore.h> > > +#include <ethdev_rx_priv.h> > > +#include <ethdev_tx_priv.h> > > +#include <punt_kernel_priv.h> > > +#include <kernel_recv_priv.h> > > + > > +#include "testgraph.h" > > + > > +/* Log type */ > > +#define RTE_LOGTYPE_TEST_GRAPH RTE_LOGTYPE_USER1 > > + > > +/* > > + * Configurable number of RX/TX ring descriptors > > + */ > > +#define RX_DESC_DEFAULT 1024 > > +#define TX_DESC_DEFAULT 1024 > > + > > +#define MAX_TX_QUEUE_PER_PORT RTE_MAX_ETHPORTS > > +#define MAX_RX_QUEUE_PER_PORT 128 > > + > > +#define MAX_RX_QUEUE_PER_LCORE 16 > > + > > +#define NB_SOCKETS 8 > > + > > +/* Static global variables used within this file. */ > > +uint16_t nb_rxd = RX_DESC_DEFAULT; > > +uint16_t nb_txd = TX_DESC_DEFAULT; > > + > > +static volatile bool force_quit; > > +volatile bool graph_walk_quit; > > +volatile bool run_graph_walk = true; > > + > > +char node_pattern[MAX_NODE_PATTERNS][RTE_NODE_NAMESIZE] = > {0}; > > +const char **node_patterns; > > +uint8_t num_patterns; > > + > > +uint8_t interactive; /**< interactive mode is off by default */ > > +int promiscuous_on; /**< Ports set in promiscuous mode off by default. > */ > > +int numa_on = 1; /**< NUMA is enabled by default. */ > > +int per_port_pool; /**< Use separate buffer pools per port, disabled by > default > > */ > > +int testgraph_logtype; /**< Log type for testgraph logs */ > > + > > +struct rte_graph_cluster_stats *stats; > > + > > +uint32_t enabled_port_mask; /**< Mask of enabled ports */ > > + > > +uint32_t nb_conf; > > + > > +struct lcore_rx_queue { > > + uint16_t port_id; > > + uint8_t queue_id; > > + char node_name[RTE_NODE_NAMESIZE]; > > +}; > > + > > +/* Lcore conf */ > > +struct lcore_conf { > > + uint16_t n_rx_queue; > > + struct lcore_rx_queue rx_queue_list[MAX_RX_QUEUE_PER_LCORE]; > > + char punt_kernel_node_name[RTE_NODE_NAMESIZE]; > > + char kernel_recv_node_name[RTE_NODE_NAMESIZE]; > > + > > + struct rte_graph *graph; > > + char name[RTE_GRAPH_NAMESIZE]; > > + rte_graph_t graph_id; > > +} __rte_cache_aligned; > > + > > +static struct lcore_conf lcore_conf[RTE_MAX_LCORE]; > > + > > +struct lcore_params lcore_params_array[MAX_LCORE_PARAMS]; > > +static struct lcore_params lcore_params_array_default[] = { > > + {0, 0, 2}, {1, 0, 2}, {2, 0, 2}, {0, 1, 3}, {1, 1, 3}, > > + {2, 1, 3}, {0, 2, 4}, {1, 2, 4}, {2, 2, 4}, > > +}; > > + > > +struct lcore_params *lcore_params = lcore_params_array_default; > > +uint16_t nb_lcore_params = RTE_DIM(lcore_params_array_default); > > + > > +static struct rte_eth_conf port_conf = { > > + .rxmode = { > > + .mq_mode = RTE_ETH_MQ_RX_RSS, > > + }, > > + .rx_adv_conf = { > > + .rss_conf = { > > + .rss_key = NULL, > > + .rss_hf = RTE_ETH_RSS_IP, > > + }, > > + }, > > + .txmode = { > > + .mq_mode = RTE_ETH_MQ_TX_NONE, > > + }, > > +}; > > + > > +static const struct node_list test_node_list[] = {{{"ethdev_rx", > "ethdev_tx"}, 0, > > 2}, > > + {{"ethdev_rx", > "punt_kernel"}, > > 0, 2}, > > + {{"kernel_recv", > "ethdev_tx"}, > > 0, 2} }; > > + > > Here we support 3 topo, could you support the original l3fwd nodes first? Sure, firstly started with simple nodes which are quite independent and does not require additional configurations like lpm, rewrite etc. > > > +static const struct node_list supported_nodes[] = {{{"ethdev_rx"}, > > TEST_GRAPH_ETHDEV_RX_NODE, 1}, > > + {{"ethdev_tx"}, > > TEST_GRAPH_ETHDEV_TX_NODE, 1}, > > + {{"punt_kernel"}, > > TEST_GRAPH_PUNT_KERNEL_NODE, 1}, > > + {{"kernel_recv"}, > > TEST_GRAPH_KERNEL_RECV_NODE, 1}, > > + {{"ip4_lookup"}, > > TEST_GRAPH_IP4_LOOKUP_NODE, 1}, > > + {{"ip4_rewrite"}, > > TEST_GRAPH_IP4_REWRITE_NODE, 1}, > > + {{"pkt_cls"}, > > TEST_GRAPH_PKT_CLS_NODE, 1}, > > + {{"pkt_drop"}, > > TEST_GRAPH_PKT_DROP_NODE, 1}, > > + {{"NULL"}, > > TEST_GRAPH_NULL_NODE, 1} }; > > + > > +static struct rte_mempool > > *pktmbuf_pool[RTE_MAX_ETHPORTS][NB_SOCKETS]; > > + > > +struct rte_node_ethdev_config ethdev_conf[RTE_MAX_ETHPORTS]; > > + > > +static int > > +check_lcore_params(void) > > +{ > > + uint8_t queue, lcore; > > + int socketid; > > + uint16_t i; > > + > > + for (i = 0; i < nb_lcore_params; ++i) { > > + queue = lcore_params[i].queue_id; > > + if (queue >= MAX_RX_QUEUE_PER_PORT) { > > + printf("Invalid queue number: %hhu\n", queue); > > + return -1; > > + } > > + lcore = lcore_params[i].lcore_id; > > + if (!rte_lcore_is_enabled(lcore)) { > > + printf("Error: lcore %hhu is not enabled in lcore > mask\n", > > lcore); > > + return -1; > > + } > > + > > + if (lcore == rte_get_main_lcore()) { > > + printf("Error: lcore %u is main lcore\n", lcore); > > + return -1; > > + } > > + socketid = rte_lcore_to_socket_id(lcore); > > + if ((socketid != 0) && (numa_on == 0)) { > > + printf("Warning: lcore %hhu is on socket %d with > numa > > off\n", lcore, > > + socketid); > > + } > > + } > > + > > + return 0; > > +} > > + > > +static int > > +check_port_config(void) > > +{ > > + uint16_t portid; > > + uint16_t i; > > + > > + for (i = 0; i < nb_lcore_params; ++i) { > > + portid = lcore_params[i].port_id; > > + if ((enabled_port_mask & (1 << portid)) == 0) { > > + printf("Port %u is not enabled in port mask\n", > portid); > > + return -1; > > + } > > + if (!rte_eth_dev_is_valid_port(portid)) { > > + printf("Port %u is not present on the board\n", > portid); > > + return -1; > > + } > > + } > > + > > + return 0; > > +} > > + > > +static uint8_t > > +get_port_n_rx_queues(const uint16_t port) > > +{ > > + int queue = -1; > > + uint16_t i; > > + > > + for (i = 0; i < nb_lcore_params; ++i) { > > + if (lcore_params[i].port_id == port) { > > + if (lcore_params[i].queue_id == queue + 1) > > + queue = lcore_params[i].queue_id; > > + else > > + rte_exit(EXIT_FAILURE, "Queue ids of the > > port %d must be" > > + " in sequence and must start with > 0\n", > > + lcore_params[i].port_id); > > + } > > + } > > + > > + return (uint8_t)(++queue); > > +} > > + > > +static int > > +init_lcore_rx_queues(void) > > +{ > > + uint16_t i, nb_rx_queue; > > + uint8_t lcore; > > + > > + for (i = 0; i < nb_lcore_params; ++i) { > > + lcore = lcore_params[i].lcore_id; > > + nb_rx_queue = lcore_conf[lcore].n_rx_queue; > > + if (nb_rx_queue >= MAX_RX_QUEUE_PER_LCORE) { > > + printf("Error: too many queues (%u) for lcore: %u\n", > > + (unsigned int)nb_rx_queue + 1, (unsigned > int)lcore); > > + return -1; > > + } > > + > > + lcore_conf[lcore].rx_queue_list[nb_rx_queue].port_id = > > lcore_params[i].port_id; > > + lcore_conf[lcore].rx_queue_list[nb_rx_queue].queue_id = > > lcore_params[i].queue_id; > > + lcore_conf[lcore].n_rx_queue++; > > + } > > + > > + return 0; > > +} > > + > > +int > > +validate_config(void) > > +{ > > + int rc = -1; > > + > > + if (check_lcore_params() < 0) { > > + printf("check_lcore_params() failed\n"); > > + goto exit; > > + } > > + > > + if (init_lcore_rx_queues() < 0) { > > + printf("init_lcore_rx_queues() failed\n"); > > + goto exit; > > + } > > + > > + if (check_port_config() < 0) { > > + printf("check_port_config() failed\n"); > > + goto exit; > > + } > > + > > + return 0; > > + > > +exit: > > + return rc; > > +} > > + > > +#define MEMPOOL_CACHE_SIZE 256 > > + > > +/* > > + * This expression is used to calculate the number of mbufs needed > > + * depending on user input, taking into account memory for rx and > > + * tx hardware rings, cache per lcore and mtable per port per lcore. > > + * RTE_MAX is used to ensure that NB_MBUF never goes below a > minimum > > + * value of 8192 > > + */ > > +#define NB_MBUF(nports) > > \ > > + RTE_MAX((nports * nb_rx_queue * nb_rxd + nports * nb_lcores * > > RTE_GRAPH_BURST_SIZE + \ > > + nports * nb_tx_queue * nb_txd + nb_lcores * > > MEMPOOL_CACHE_SIZE), \ > > + 8192u) > > + > > +static int > > +init_mem(uint16_t portid, uint32_t nb_mbuf) > > +{ > > + uint32_t lcore_id; > > + int socketid; > > + char s[64]; > > + > > + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { > > + if (rte_lcore_is_enabled(lcore_id) == 0) > > + continue; > > + > > + if (numa_on) > > + socketid = rte_lcore_to_socket_id(lcore_id); > > + else > > + socketid = 0; > > + > > + if (socketid >= NB_SOCKETS) { > > + rte_exit(EXIT_FAILURE, "Socket %d of lcore %u is out > of > > range %d\n", > > + socketid, lcore_id, NB_SOCKETS); > > + } > > + > > + if (pktmbuf_pool[portid][socketid] == NULL) { > > + snprintf(s, sizeof(s), "mbuf_pool_%d:%d", portid, > > socketid); > > + /* Create a pool with priv size of a cacheline */ > > + pktmbuf_pool[portid][socketid] = > > rte_pktmbuf_pool_create( > > + s, nb_mbuf, MEMPOOL_CACHE_SIZE, > > RTE_CACHE_LINE_SIZE, > > + RTE_MBUF_DEFAULT_BUF_SIZE, socketid); > > + if (pktmbuf_pool[portid][socketid] == NULL) > > + rte_exit(EXIT_FAILURE, "Cannot init mbuf > pool > > on socket %d\n", > > + socketid); > > + else > > + printf("Allocated mbuf pool on socket %d\n", > > socketid); > > + } > > + } > > + > > + return 0; > > +} > > + > > +void > > +check_all_ports_link_status(uint32_t port_mask) > > +{ > > +#define CHECK_INTERVAL 100 /* 100ms */ > > +#define MAX_CHECK_TIME 90 /* 9s (90 * 100ms) in total */ > > + uint8_t count, all_ports_up, print_flag = 0; > > + struct rte_eth_link link; > > + uint16_t portid; > > + int ret; > > + char link_status_text[RTE_ETH_LINK_MAX_STR_LEN]; > > + > > + printf("\nChecking link status"); > > + fflush(stdout); > > + for (count = 0; count <= MAX_CHECK_TIME; count++) { > > + if (force_quit) > > + return; > > + all_ports_up = 1; > > + RTE_ETH_FOREACH_DEV(portid) { > > + if (force_quit) > > + return; > > + if ((port_mask & (1 << portid)) == 0) > > + continue; > > + memset(&link, 0, sizeof(link)); > > + ret = rte_eth_link_get_nowait(portid, &link); > > + if (ret < 0) { > > + all_ports_up = 0; > > + if (print_flag == 1) > > + printf("Port %u link get failed: %s\n", > > portid, > > + rte_strerror(-ret)); > > + continue; > > + } > > + /* Print link status if flag set */ > > + if (print_flag == 1) { > > + rte_eth_link_to_str(link_status_text, > > sizeof(link_status_text), > > + &link); > > + printf("Port %d %s\n", portid, > link_status_text); > > + continue; > > + } > > + /* Clear all_ports_up flag if any link down */ > > + if (link.link_status == RTE_ETH_LINK_DOWN) { > > + all_ports_up = 0; > > + break; > > + } > > + } > > + /* After finally printing all link status, get out */ > > + if (print_flag == 1) > > + break; > > + > > + if (all_ports_up == 0) { > > + printf("."); > > + fflush(stdout); > > + rte_delay_ms(CHECK_INTERVAL); > > + } > > + > > + /* Set the print_flag if all ports up or timeout */ > > + if (all_ports_up == 1 || count == (MAX_CHECK_TIME - 1)) { > > + print_flag = 1; > > + printf("Done\n"); > > + } > > + } > > +} > > + > > +static void > > +signal_handler(int signum) > > +{ > > + if (signum == SIGINT || signum == SIGTERM) { > > + printf("\n\nSignal %d received, preparing to exit...\n", > signum); > > + force_quit = true; > > + } > > + prompt_exit(); > > +} > > + > > +int > > +graph_main_loop(void *conf) > > +{ > > + struct lcore_conf *qconf; > > + struct rte_graph *graph; > > + uint32_t lcore_id; > > + > > + RTE_SET_USED(conf); > > + > > + lcore_id = rte_lcore_id(); > > + qconf = &lcore_conf[lcore_id]; > > + graph = qconf->graph; > > + > > + if (!graph) { > > + RTE_LOG(INFO, TEST_GRAPH, "Lcore %u has nothing to > do\n", > > lcore_id); > > + return 0; > > + } > > + > > + RTE_LOG(INFO, TEST_GRAPH, "Entering main loop on lcore %u, > > graph %s(%p)\n", lcore_id, > > + qconf->name, graph); > > + > > + while (likely(!force_quit && !graph_walk_quit)) { > > + if (likely(run_graph_walk)) > > + rte_graph_walk(graph); > > + } > > graph_walk_quit could be changed to true in destroy stage, we should set it > false in create stage also. > Otherwise, it will walk only once for interactive-mode. > > I am agree to use different switch for different stage to start/stop, > create/destroy and termination. > force_quit + graph_walk_quit + run_graph_walk looks little overcomplicated. > Could it only use > force_quit to pause the walk in worker? Sure, will simplify the path. > > > + > > + return 0; > > +} > > + > > +struct rte_graph_cluster_stats * > > +create_graph_cluster_stats(void) > > +{ > > + struct rte_graph_cluster_stats_param s_param; > > + struct rte_graph_cluster_stats *stat; > > + const char *pattern = "worker_*"; > > + > > + /* Prepare stats object */ > > + memset(&s_param, 0, sizeof(s_param)); > > + s_param.f = stdout; > > + s_param.socket_id = SOCKET_ID_ANY; > > + s_param.graph_patterns = &pattern; > > + s_param.nb_graph_patterns = 1; > > + > > + stat = rte_graph_cluster_stats_create(&s_param); > > + if (stat == NULL) > > + rte_exit(EXIT_FAILURE, "Unable to create stats object\n"); > > + > > + return stat; > > +} > > + > > +static void > > +print_stats(void) > > +{ > > + const char topLeft[] = {27, '[', '1', ';', '1', 'H', '\0'}; > > + const char clr[] = {27, '[', '2', 'J', '\0'}; > > + > > + while (!force_quit) { > > + /* Clear screen and move to top left */ > > + printf("%s%s", clr, topLeft); > > + rte_graph_cluster_stats_get(stats, 0); > > + rte_delay_ms(1E3); > > + } > > + > > + rte_graph_cluster_stats_destroy(stats); > > +} > > + > > +int > > +parse_config(const char *q_arg) > > +{ > > + enum fieldnames { FLD_PORT = 0, FLD_QUEUE, FLD_LCORE, > > _NUM_FLD }; > > + unsigned long int_fld[_NUM_FLD]; > > + const char *p, *p0 = q_arg; > > + char *str_fld[_NUM_FLD]; > > + uint32_t size; > > + char s[256]; > > + char *end; > > + int i; > > + > > + nb_lcore_params = 0; > > + > > + while ((p = strchr(p0, '(')) != NULL) { > > + ++p; > > + p0 = strchr(p, ')'); > > + if (p0 == NULL) > > + goto exit; > > + > > + size = p0 - p; > > + if (size >= sizeof(s)) > > + goto exit; > > + > > + memcpy(s, p, size); > > + s[size] = '\0'; > > + if (rte_strsplit(s, sizeof(s), str_fld, _NUM_FLD, ',') != > _NUM_FLD) > > + goto exit; > > + for (i = 0; i < _NUM_FLD; i++) { > > + errno = 0; > > + int_fld[i] = strtoul(str_fld[i], &end, 0); > > + if (errno != 0 || end == str_fld[i]) > > + goto exit; > > + } > > + > > + if (nb_lcore_params >= MAX_LCORE_PARAMS) { > > + printf("Exceeded max number of lcore params: > %hu\n", > > nb_lcore_params); > > + goto exit; > > + } > > + > > + if (int_fld[FLD_PORT] >= RTE_MAX_ETHPORTS || > > int_fld[FLD_LCORE] >= RTE_MAX_LCORE) { > > + printf("Invalid port/lcore id\n"); > > + goto exit; > > + } > > + > > + lcore_params_array[nb_lcore_params].port_id = > > (uint8_t)int_fld[FLD_PORT]; > > + lcore_params_array[nb_lcore_params].queue_id = > > (uint8_t)int_fld[FLD_QUEUE]; > > + lcore_params_array[nb_lcore_params].lcore_id = > > (uint8_t)int_fld[FLD_LCORE]; > > + ++nb_lcore_params; > > + } > > + lcore_params = lcore_params_array; > > + > > + return 0; > > +exit: > > + /* Revert to default config */ > > + lcore_params = lcore_params_array_default; > > + nb_lcore_params = RTE_DIM(lcore_params_array_default); > > + > > + return -1; > > +} > > + > > +int > > +parse_node_patterns(const char *q_arg) > > +{ > > + const char *p, *p0 = q_arg; > > + int ret = -EINVAL; > > + uint32_t size; > > + > > + num_patterns = 0; > > + > > + p = strchr(p0, '('); > > + if (p != NULL) { > > + ++p; > > + while ((p0 = strchr(p, ',')) != NULL) { > > + size = p0 - p; > > + if (size >= RTE_NODE_NAMESIZE) > > + goto exit; > > + > > + if (num_patterns >= MAX_NODE_PATTERNS) { > > + printf("Too many nodes passed.\n"); > > + goto exit; > > + } > > + > > + memcpy(node_pattern[num_patterns++], p, size); > > Here, if not make node_pattern clean, node_pattern[0](size,MAX) will be > tainted by previous create. Ack. will fix it. > > > + p = p0 + 1; > > + } > > + > > + p0 = strchr(p, ')'); > > + if (p0 != NULL) { > > + size = p0 - p; > > + if (size >= RTE_NODE_NAMESIZE) > > + goto exit; > > + > > + if (num_patterns >= MAX_NODE_PATTERNS) { > > + printf("Too many nodes passed.\n"); > > + goto exit; > > + } > > + > > + memcpy(node_pattern[num_patterns++], p, size); > > + } else { > > + goto exit; > > + } > > + } else { > > + goto exit; > > + } > > + > > + return 0; > > +exit: > > + return ret; > > +} > > + > > +static void > > +set_default_node_pattern(void) > > +{ > > + uint16_t idx; > > + > > + for (idx = 0; idx < test_node_list[0].size; idx++) > > + strcpy(node_pattern[num_patterns++], > > test_node_list[0].nodes[idx]); > > +} > > + > > +int > > +validate_node_names(uint64_t *valid_nodes) > > Actually, this func limits the node name to follow test_node_list. > It's OK if there are few nodes/edges. > > But how to describe much nodes/edges like > graph_dot { > A->B; > A->C; > B->D; > C->D; > } > > If update edges implicitly, there is no need to specific the node sequence. > Both of (A,B,C,D) and (A,C,B,D) are correct. Correct, will rework on interpreting /parsing it as expected by the topology. > > > +{ > > + rte_node_t node_cnt = rte_node_max_count(); > > + bool pattern_matched = false; > > + rte_node_t id = 0; > > + int ret = -EINVAL; > > + uint16_t idx, i, j; > > + > > + for (idx = 0; idx < num_patterns; idx++) { > > + for (id = 0; id < node_cnt; id++) { > > + if (strncmp(node_pattern[idx], > > rte_node_id_to_name(id), > > + RTE_GRAPH_NAMESIZE) == 0) > > + break; > > + } > > + if (node_cnt == id) { > > + printf("Invalid node name passed\n"); > > + return ret; > > + } > > + } > > + > > + for (i = 0; i < RTE_DIM(test_node_list); i++) { > > + idx = 0; > > + if (test_node_list[i].size == num_patterns) { > > + for (j = 0; j < num_patterns; j++) { > > + if (strncmp(node_pattern[j], > > test_node_list[i].nodes[j], > > + RTE_GRAPH_NAMESIZE) == 0) > > + idx++; > > + } > > + if (idx == num_patterns) > > + pattern_matched = true; > > + } > > + } > > + > > + if (!pattern_matched) { > > + printf("Unsupported node pattern passed\n\n"); > > + printf("Test supported node patterns are:\n"); > > + for (i = 0; i < RTE_DIM(test_node_list); i++) { > > + printf("("); > > + for (j = 0; j < (test_node_list[i].size - 1); j++) > > + printf("%s,", test_node_list[i].nodes[j]); > > + printf("%s", test_node_list[i].nodes[j]); > > + printf(")\n"); > > + } > > + > > + return ret; > > + } > > + > > + for (i = 0; i < RTE_DIM(supported_nodes); i++) { > > + for (j = 0; j < num_patterns; j++) { > > + if (strncmp(node_pattern[j], > > supported_nodes[i].nodes[0], > > + RTE_GRAPH_NAMESIZE) == 0) { > > + *valid_nodes |= supported_nodes[i].test_id; > > + break; > > + } > > + } > > + } > > + > > + return 0; > > +} > > + > > +void > > +cleanup_node_pattern(void) > > +{ > > + while (num_patterns) { > > + memset(node_pattern[num_patterns - 1], 0, > > RTE_GRAPH_NAMESIZE); > > + num_patterns--; > > + } > > +} > > + > > +static int > > +ethdev_tx_node_configure(void) > > +{ > > + struct ethdev_tx_node_main *tx_node_data; > > + struct rte_node_register *tx_node; > > + char name[RTE_NODE_NAMESIZE]; > > + uint16_t port_id; > > + uint32_t id; > > + > > + tx_node_data = ethdev_tx_node_data_get(); > > + tx_node = ethdev_tx_node_get(); > > + > > + RTE_ETH_FOREACH_DEV(port_id) { > > + /* Skip ports that are not enabled */ > > + if ((enabled_port_mask & (1 << port_id)) == 0) { > > + printf("\nSkipping disabled port %d\n", port_id); > > + continue; > > + } > > + > > + if (!rte_eth_dev_is_valid_port(port_id)) > > + return -EINVAL; > > + > > + /* Create a per port tx node from base node */ > > + snprintf(name, sizeof(name), "%u", port_id); > > + id = rte_node_clone(tx_node->id, name); > > + tx_node_data->nodes[port_id] = id; > > + > > + printf("ethdev:: Tx node %s-%s: is at %u\n", tx_node- > >name, > > name, id); > > + } > > + > > + return 0; > > +} > > + > > +static int > > +punt_kernel_node_configure(void) > > +{ > > + struct rte_node_register *punt_node; > > + char name[RTE_NODE_NAMESIZE]; > > + struct lcore_conf *qconf; > > + uint32_t lcore_id; > > + uint32_t id; > > + > > + punt_node = punt_kernel_node_get(); > > + > > + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { > > + > > + if (rte_lcore_is_enabled(lcore_id) == 0) > > + continue; > > + > > + qconf = &lcore_conf[lcore_id]; > > + > > + /* Create a per lcore punt_kernel node from base node */ > > + snprintf(name, sizeof(name), "%u", lcore_id); > > + id = rte_node_clone(punt_node->id, name); > > + strcpy(qconf->punt_kernel_node_name, > > rte_node_id_to_name(id)); > > + > > + printf("punt_kernel node %s-%s: is at %u\n", punt_node- > >name, > > name, id); > > + } > > + > > + return 0; > > +} > > + > > +static int > > +kernel_recv_node_configure(void) > > +{ > > + struct rte_node_register *recv_node; > > + char name[RTE_NODE_NAMESIZE]; > > + struct lcore_conf *qconf; > > + uint32_t lcore_id; > > + uint32_t id; > > + > > + recv_node = kernel_recv_node_get(); > > + > > + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { > > + > > + if (rte_lcore_is_enabled(lcore_id) == 0) > > + continue; > > + > > + qconf = &lcore_conf[lcore_id]; > > + > > + /* Create a per lcore kernel_recv node from base node */ > > + snprintf(name, sizeof(name), "%u", lcore_id); > > + id = rte_node_clone(recv_node->id, name); > > + strcpy(qconf->kernel_recv_node_name, > > rte_node_id_to_name(id)); > > + > > + printf("kernel_recv node %s-%s: is at %u\n", recv_node- > >name, > > name, id); > > + } > > + > > + return 0; > > +} > > + > > +static int > > +ethdev_rx_node_configure(struct rte_node_ethdev_config *conf, > uint16_t > > nb_confs) > > +{ > > + char name[RTE_NODE_NAMESIZE]; > > + uint16_t i, j, port_id; > > + uint32_t id; > > + > > + for (i = 0; i < nb_confs; i++) { > > + port_id = conf[i].port_id; > > + > > + if (!rte_eth_dev_is_valid_port(port_id)) > > + return -EINVAL; > > + > > + /* Create node for each rx port queue pair */ > > + for (j = 0; j < conf[i].num_rx_queues; j++) { > > + struct ethdev_rx_node_main *rx_node_data; > > + struct rte_node_register *rx_node; > > + ethdev_rx_node_elem_t *elem; > > + > > + rx_node_data = ethdev_rx_get_node_data_get(); > > + rx_node = ethdev_rx_node_get(); > > + snprintf(name, sizeof(name), "%u-%u", port_id, j); > > + /* Clone a new rx node with same edges as parent */ > > + id = rte_node_clone(rx_node->id, name); > > + if (id == RTE_NODE_ID_INVALID) > > + return -EIO; > > + > > + /* Add it to list of ethdev rx nodes for lookup */ > > + elem = malloc(sizeof(ethdev_rx_node_elem_t)); > > + if (elem == NULL) > > + return -ENOMEM; > > + memset(elem, 0, sizeof(ethdev_rx_node_elem_t)); > > + elem->ctx.port_id = port_id; > > + elem->ctx.queue_id = j; > > + elem->nid = id; > > + elem->next = rx_node_data->head; > > + rx_node_data->head = elem; > > + > > + printf("ethdev:: Rx node %s-%s: is at %u\n", > rx_node- > > >name, name, id); > > + } > > + } > > + > > + return 0; > > +} > > + > > +static int > > +update_ethdev_rx_node_next(rte_node_t id, const char *edge_name) > > +{ > > + struct ethdev_rx_node_main *rx_node_data; > > + ethdev_rx_node_elem_t *elem; > > + char *next_nodes[16]; > > + rte_edge_t count; > > + uint16_t i; > > + > > + count = rte_node_edge_count(id); > > + rte_node_edge_get(id, next_nodes); > > + > > + for (i = 0; i < count; i++) { > > + if (fnmatch(edge_name, next_nodes[i], 0) == 0) { > > + rx_node_data = ethdev_rx_get_node_data_get(); > > + elem = rx_node_data->head; > > + while (elem->next != rx_node_data->head) { > > + if (elem->nid == id) > > + break; > > + elem = elem->next; > > + } > > + > > + if (elem->nid == id) > > + elem->ctx.cls_next = i; > > + break; > > + } > > + } > > + > > + return 0; > > +} > > + > > +static int > > +link_ethdev_rx_to_tx_node(uint32_t lcore_id) > > +{ > > + const char * const pattern[] = {"ethdev_tx-*"}; > > + uint16_t queue, queue_id, port_id; > > + char name[RTE_NODE_NAMESIZE]; > > + const char *next_node = name; > > + struct lcore_conf *qconf; > > + uint32_t rx_id; > > + > > + qconf = &lcore_conf[lcore_id]; > > + > > + for (queue = 0; queue < qconf->n_rx_queue; ++queue) { > > + port_id = qconf->rx_queue_list[queue].port_id; > > + queue_id = qconf->rx_queue_list[queue].queue_id; > > + > > + snprintf(name, sizeof(name), "ethdev_rx-%u-%u", port_id, > > queue_id); > > + rx_id = rte_node_from_name(name); > > + > > + /* Fill node pattern */ > > + strcpy(node_pattern[num_patterns++], name); > > + > > + /* Prepare the actual name of the cloned node */ > > + snprintf(name, sizeof(name), "ethdev_tx-%u", port_id); > > + > > + /* Update ethdev_rx node edges */ > > + rte_node_edge_update(rx_id, RTE_EDGE_ID_INVALID, > > &next_node, 1); > > + > > + /* Fill node pattern */ > > + strcpy(node_pattern[num_patterns++], name); > > + > > + /* Update node_next details */ > > + update_ethdev_rx_node_next(rx_id, pattern[0]); > > + } > > + > > + return 0; > > +} > > + > > +static int > > +link_ethdev_rx_to_punt_kernel_node(uint32_t lcore_id) > > +{ > > + const char * const pattern[] = {"punt_kernel-*"}; > > + uint16_t queueid, portid, queue; > > + char name[RTE_NODE_NAMESIZE]; > > + const char *next_node = name; > > + struct lcore_conf *qconf; > > + rte_node_t rx_id; > > + > > + qconf = &lcore_conf[lcore_id]; > > + > > + for (queue = 0; queue < qconf->n_rx_queue; ++queue) { > > + > > + portid = qconf->rx_queue_list[queue].port_id; > > + queueid = qconf->rx_queue_list[queue].queue_id; > > + > > + snprintf(name, sizeof(name), "ethdev_rx-%u-%u", portid, > > queueid); > > + rx_id = rte_node_from_name(name); > > + > > + /* Fill node pattern */ > > + strcpy(node_pattern[num_patterns++], name); > > + > > + next_node = qconf->punt_kernel_node_name; > > + > > + /* Fill node pattern */ > > + strcpy(node_pattern[num_patterns++], next_node); > > + > > + /* Update ethdev_rx node edges */ > > + rte_node_edge_update(rx_id, RTE_EDGE_ID_INVALID, > > &next_node, 1); > > + > > + /* Update node_next details */ > > + update_ethdev_rx_node_next(rx_id, pattern[0]); > > + } > > + > > + return 0; > > +} > > + > > +static int > > +update_kernel_recv_node_next(rte_node_t id, const char *edge_name) > > +{ > > + struct kernel_recv_node_main *rx_node_data; > > + kernel_recv_node_elem_t *elem; > > + char *next_nodes[16]; > > + rte_edge_t count; > > + uint16_t i; > > + > > + count = rte_node_edge_count(id); > > + rte_node_edge_get(id, next_nodes); > > + > > + for (i = 0; i < count; i++) { > > + if (fnmatch(edge_name, next_nodes[i], 0) == 0) { > > + rx_node_data = kernel_recv_node_data_get(); > > + elem = rx_node_data->head; > > + while (elem->next != rx_node_data->head) { > > + if (elem->nid == id) > > + break; > > + elem = elem->next; > > + } > > + > > + if (elem->nid == id) > > + elem->ctx.recv_info->cls_next = i; > > + break; > > + } > > + } > > + > > + return 0; > > +} > > + > > +static int > > +link_kernel_recv_to_ethdev_tx_node(uint32_t lcore_id) > > +{ > > + const char * const pattern[] = {"ethdev_tx-*"}; > > + char name[RTE_NODE_NAMESIZE]; > > + const char *next_node = name; > > + struct lcore_conf *qconf; > > + uint16_t port_id; > > + rte_node_t id; > > + > > + qconf = &lcore_conf[lcore_id]; > > + > > + id = rte_node_from_name(qconf->kernel_recv_node_name); > > + > > + /* Fill node pattern */ > > + strcpy(node_pattern[num_patterns++], qconf- > > >kernel_recv_node_name); > > + > > + port_id = lcore_id; > > + > > + if ((enabled_port_mask & (1 << port_id)) == 0) { > > + /* Use available port_id */ > > + RTE_ETH_FOREACH_DEV(port_id) { > > + if ((enabled_port_mask & (1 << port_id)) != 0) > > + break; > > + } > > + } > > + > > + if (!rte_eth_dev_is_valid_port(port_id)) { > > + printf("Port %u is not present on the board\n", port_id); > > + return -1; > > + } > > + > > + /* Prepare the actual name of the cloned node */ > > + snprintf(name, sizeof(name), "ethdev_tx-%u", port_id); > > + > > + /* Update ethdev_rx node edges */ > > + rte_node_edge_update(id, RTE_EDGE_ID_INVALID, &next_node, > 1); > > + > > + /* Fill node pattern */ > > + strcpy(node_pattern[num_patterns++], name); > > + > > + /* Update node_next details */ > > + update_kernel_recv_node_next(id, pattern[0]); > > + > > + return 0; > > +} > > These explicitly update node and link could be put in a independent stage, > like graph_config. > > Also, I wonder if update link could be more generic. Cause they have some > similar steps. > > BTY, In this patch, it could support rx->tx first. > Then add a new patch to support other topo(kernel_recv, punt_kernel and > l3fwd). > And we could enable some help function in another patch. > It could make the patch small. Yes, make sense, will rework on this. > > > + > > +uint32_t > > +ethdev_ports_setup(void) > > +{ > > + struct rte_eth_dev_info dev_info; > > + uint32_t nb_tx_queue, nb_lcores; > > + uint32_t nb_ports, nb_conf = 0; > > + uint8_t nb_rx_queue; > > + uint16_t portid; > > + int ret; > > + > > + nb_ports = rte_eth_dev_count_avail(); > > + nb_lcores = rte_lcore_count(); > > + > > + RTE_ETH_FOREACH_DEV(portid) { > > + struct rte_eth_conf local_port_conf = port_conf; > > + > > + /* Skip ports that are not enabled */ > > + if ((enabled_port_mask & (1 << portid)) == 0) { > > + printf("\nSkipping disabled port %d\n", portid); > > + continue; > > + } > > + > > + /* Init port */ > > + printf("Initializing port %d ... ", portid); > > + fflush(stdout); > > + > > + nb_rx_queue = get_port_n_rx_queues(portid); > > + nb_tx_queue = nb_lcores; > > + if (nb_tx_queue > MAX_TX_QUEUE_PER_PORT) > > + nb_tx_queue = MAX_TX_QUEUE_PER_PORT; > > + printf("Creating queues: nb_rxq=%d nb_txq=%u...\n", > > nb_rx_queue, nb_tx_queue); > > + > > + rte_eth_dev_info_get(portid, &dev_info); > > + > > + if (dev_info.tx_offload_capa & > > RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE) > > + local_port_conf.txmode.offloads |= > > RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE; > > + > > + local_port_conf.rx_adv_conf.rss_conf.rss_hf &= > > dev_info.flow_type_rss_offloads; > > + if (local_port_conf.rx_adv_conf.rss_conf.rss_hf != > > + port_conf.rx_adv_conf.rss_conf.rss_hf) { > > + printf("Port %u modified RSS hash function based on > > hardware support," > > + "requested:%#" PRIx64 " configured:%#" PRIx64 > > "\n", > > + portid, port_conf.rx_adv_conf.rss_conf.rss_hf, > > + local_port_conf.rx_adv_conf.rss_conf.rss_hf); > > + } > > + > > + ret = rte_eth_dev_configure(portid, nb_rx_queue, > nb_tx_queue, > > &local_port_conf); > > + if (ret < 0) > > + rte_exit(EXIT_FAILURE, "Cannot configure device: > > err=%d, port=%d\n", ret, > > + portid); > > + > > + ret = rte_eth_dev_adjust_nb_rx_tx_desc(portid, &nb_rxd, > > &nb_txd); > > + if (ret < 0) > > + rte_exit(EXIT_FAILURE, > > + "Cannot adjust number of descriptors: > > err=%d," > > + "port=%d\n", > > + ret, portid); > > + > > + /* Init memory */ > > + if (!per_port_pool) > > + ret = init_mem(0, NB_MBUF(nb_ports)); > > + else > > + ret = init_mem(portid, NB_MBUF(1)); > > + > > + if (ret < 0) > > + rte_exit(EXIT_FAILURE, "init_mem() failed\n"); > > + > > + /* Setup ethdev node config */ > > + ethdev_conf[nb_conf].port_id = portid; > > + ethdev_conf[nb_conf].num_rx_queues = nb_rx_queue; > > + ethdev_conf[nb_conf].num_tx_queues = nb_tx_queue; > > + if (!per_port_pool) > > + ethdev_conf[nb_conf].mp = pktmbuf_pool[0]; > > + > > + else > > + ethdev_conf[nb_conf].mp = pktmbuf_pool[portid]; > > + ethdev_conf[nb_conf].mp_count = NB_SOCKETS; > > + > > + nb_conf++; > > + } > > + > > + return nb_conf; > > +} > > + > > +void > > +ethdev_rxq_configure(void) > > +{ > > + struct rte_eth_dev_info dev_info; > > + uint16_t queueid, portid; > > + struct lcore_conf *qconf; > > + uint8_t queue, socketid; > > + uint32_t lcore_id; > > + int ret; > > + > > + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { > > + if (rte_lcore_is_enabled(lcore_id) == 0) > > + continue; > > + qconf = &lcore_conf[lcore_id]; > > + printf("\nInitializing rx queues on lcore %u ... ", lcore_id); > > + fflush(stdout); > > + > > + /* Init RX queues */ > > + for (queue = 0; queue < qconf->n_rx_queue; ++queue) { > > + struct rte_eth_rxconf rxq_conf; > > + > > + portid = qconf->rx_queue_list[queue].port_id; > > + queueid = qconf->rx_queue_list[queue].queue_id; > > + > > + if (numa_on) > > + socketid = > > (uint8_t)rte_lcore_to_socket_id(lcore_id); > > + else > > + socketid = 0; > > + > > + printf("rxq=%d,%d,%d ", portid, queueid, socketid); > > + fflush(stdout); > > + > > + rte_eth_dev_info_get(portid, &dev_info); > > + rxq_conf = dev_info.default_rxconf; > > + rxq_conf.offloads = port_conf.rxmode.offloads; > > + if (!per_port_pool) > > + ret = rte_eth_rx_queue_setup(portid, > queueid, > > nb_rxd, socketid, > > + &rxq_conf, > > pktmbuf_pool[0][socketid]); > > + else > > + ret = rte_eth_rx_queue_setup(portid, > queueid, > > nb_rxd, socketid, > > + &rxq_conf, > > + > > pktmbuf_pool[portid][socketid]); > > + if (ret < 0) > > + rte_exit(EXIT_FAILURE, > > "rte_eth_rx_queue_setup: err=%d, port=%d\n", > > + ret, portid); > > + > > + snprintf(qconf->rx_queue_list[queue].node_name, > > RTE_NODE_NAMESIZE, > > + "ethdev_rx-%u-%u", portid, queueid); > > + } > > + } > > + printf("\n"); > > +} > > + > > +void > > +ethdev_txq_configure(void) > > +{ > > + struct rte_eth_dev_info dev_info; > > + struct rte_eth_txconf *txconf; > > + uint16_t queueid, portid; > > + uint32_t lcore_id; > > + uint8_t socketid; > > + int ret; > > + > > + RTE_ETH_FOREACH_DEV(portid) { > > + struct rte_eth_conf local_port_conf = port_conf; > > + > > + /* Skip ports that are not enabled */ > > + if ((enabled_port_mask & (1 << portid)) == 0) { > > + printf("\nSkipping disabled port %d\n", portid); > > + continue; > > + } > > + > > + rte_eth_dev_info_get(portid, &dev_info); > > + > > + /* Init one TX queue per (lcore,port) pair */ > > + queueid = 0; > > + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { > > + if (rte_lcore_is_enabled(lcore_id) == 0) > > + continue; > > + > > + if (numa_on) > > + socketid = > > (uint8_t)rte_lcore_to_socket_id(lcore_id); > > + else > > + socketid = 0; > > + > > + printf("txq=%u,%d,%d ", lcore_id, queueid, > socketid); > > + fflush(stdout); > > + > > + txconf = &dev_info.default_txconf; > > + txconf->offloads = local_port_conf.txmode.offloads; > > + ret = rte_eth_tx_queue_setup(portid, queueid, > nb_txd, > > socketid, txconf); > > + if (ret < 0) > > + rte_exit(EXIT_FAILURE, > > "rte_eth_tx_queue_setup: err=%d, port=%d\n", > > + ret, portid); > > + queueid++; > > + } > > + } > > + printf("\n"); > > +} > > + > > +int > > +configure_graph_nodes(uint64_t valid_nodes) > > +{ > > + int ret = 0; > > + > > + if (valid_nodes & TEST_GRAPH_ETHDEV_RX_NODE) { > > + ret = ethdev_rx_node_configure(ethdev_conf, nb_conf); > > + if (ret) { > > + printf("ethdev_rx_node_configure: err=%d\n", ret); > > + goto exit; > > + } > > + } > > + > > + if (valid_nodes & TEST_GRAPH_ETHDEV_TX_NODE) { > > + ret = ethdev_tx_node_configure(); > > + if (ret) { > > + printf("ethdev_tx_node_configure: err=%d\n", ret); > > + goto exit; > > + } > > + } > > + > > + if (valid_nodes & TEST_GRAPH_PUNT_KERNEL_NODE) { > > + ret = punt_kernel_node_configure(); > > + if (ret) { > > + printf("punt_kernel_node_configure: err=%d\n", > ret); > > + goto exit; > > + } > > + } > > + > > + if (valid_nodes & TEST_GRAPH_KERNEL_RECV_NODE) { > > + ret = kernel_recv_node_configure(); > > + if (ret) { > > + printf("kernel_recv_node_configure: err=%d\n", > ret); > > + goto exit; > > + } > > + } > > + > > +exit: > > + return ret; > > +} > > + > > +static int > > +link_graph_nodes(uint64_t valid_nodes, uint32_t lcore_id) > > +{ > > + int ret = 0; > > + > > + num_patterns = 0; > > + > > + if (valid_nodes == (TEST_GRAPH_ETHDEV_TX_NODE | > > TEST_GRAPH_ETHDEV_RX_NODE)) { > > + ret = link_ethdev_rx_to_tx_node(lcore_id); > > + if (ret) { > > + printf("link_ethdev_rx_to_tx_node: err=%d\n", ret); > > + goto exit; > > + } > > + } else if (valid_nodes == (TEST_GRAPH_ETHDEV_RX_NODE | > > TEST_GRAPH_PUNT_KERNEL_NODE)) { > > + link_ethdev_rx_to_punt_kernel_node(lcore_id); > > + } else if (valid_nodes == (TEST_GRAPH_ETHDEV_TX_NODE | > > TEST_GRAPH_KERNEL_RECV_NODE)) { > > + link_kernel_recv_to_ethdev_tx_node(lcore_id); > > + } else { > > + printf("Invalid node map\n"); > > + ret = -EINVAL; > > + } > > + > > +exit: > > + return ret; > > +} > > + > > +void > > +start_eth_ports(void) > > +{ > > + uint16_t portid; > > + int ret; > > + > > + RTE_ETH_FOREACH_DEV(portid) { > > + if ((enabled_port_mask & (1 << portid)) == 0) > > + continue; > > + > > + ret = rte_eth_dev_start(portid); > > + if (ret < 0) > > + rte_exit(EXIT_FAILURE, "rte_eth_dev_start: err=%d, > > port=%d\n", ret, portid); > > + > > + if (promiscuous_on) > > + rte_eth_promiscuous_enable(portid); > > + } > > +} > > + > > +void > > +stop_eth_ports(void) > > +{ > > + uint16_t portid; > > + int ret; > > + > > + RTE_ETH_FOREACH_DEV(portid) { > > + if ((enabled_port_mask & (1 << portid)) == 0) > > + continue; > > + printf("Closing port %d...", portid); > > + ret = rte_eth_dev_stop(portid); > > + if (ret != 0) > > + printf("Failed to stop port %u: %s\n", portid, > > rte_strerror(-ret)); > > + rte_eth_dev_close(portid); > close should be in exit stage or when application is killed. ack > > > + } > > +} > > + > > +int > > +create_graph(uint64_t valid_nodes) > > +{ > > + struct rte_graph_param graph_conf; > > + struct lcore_conf *qconf; > > + uint32_t lcore_id; > > + int ret; > > + > > + node_patterns = malloc(MAX_NODE_PATTERNS * > > sizeof(*node_patterns)); > > + if (!node_patterns) > > + return -ENOMEM; > > + > > + memset(&graph_conf, 0, sizeof(graph_conf)); > > + graph_conf.node_patterns = node_patterns; > > + > > + for (lcore_id = 0; lcore_id < RTE_MAX_LCORE; lcore_id++) { > > + rte_graph_t graph_id; > > + rte_edge_t i; > > + > > + if (rte_lcore_is_enabled(lcore_id) == 0) > > + continue; > > + > > + qconf = &lcore_conf[lcore_id]; > > + > > + /* Skip graph creation if no source exists */ > > + if (!qconf->n_rx_queue) > > + continue; > > + > > + ret = link_graph_nodes(valid_nodes, lcore_id); > > + if (ret) > > + rte_exit(EXIT_FAILURE, "link_graph_nodes(): > failed\n"); > > + > > + for (i = 0; i < num_patterns; i++) { > > + graph_conf.node_patterns[i] = node_pattern[i]; > > + printf("%s,", graph_conf.node_patterns[i]); > > + } > > + printf("\n"); > > + > > + graph_conf.nb_node_patterns = num_patterns; > > + > > + graph_conf.socket_id = rte_lcore_to_socket_id(lcore_id); > > + > > + snprintf(qconf->name, sizeof(qconf->name), "worker_%u", > > lcore_id); > > + > > + graph_id = rte_graph_create(qconf->name, &graph_conf); > > + if (graph_id == RTE_GRAPH_ID_INVALID) > > + rte_exit(EXIT_FAILURE, "rte_graph_create(): > graph_id > > invalid for lcore%u\n", > > + lcore_id); > > + > > + qconf->graph_id = graph_id; > > + qconf->graph = rte_graph_lookup(qconf->name); > > + > > + if (!qconf->graph) > > + rte_exit(EXIT_FAILURE, "rte_graph_lookup(): graph > %s > > not found\n", > > + qconf->name); > > + } > > + > > + return 0; > > +} > > + > > +int > > +destroy_graph(void) > > +{ > > + uint32_t lcore_id; > > + rte_graph_t id; > > + int ret; > > + > > + RTE_LCORE_FOREACH_WORKER(lcore_id) > > + { > > + if (lcore_conf[lcore_id].graph) { > > + id = > rte_graph_from_name(lcore_conf[lcore_id].name); > > + if (rte_graph_destroy(id)) { > > + printf("graph_id %u destroy failed.\n", id); > > + ret = -1; > > + } > > + } > > + } > > + > > + if (node_patterns) > > + free(node_patterns); > > Cause this cmd could be called twice in interactive-mode, we should set it to > NULL after free. ack > > > + > > + return ret; > > +} > > + > > +int > > +main(int argc, char **argv) > > +{ > > + uint64_t valid_nodes; > > + uint32_t lcore_id; > > + int ret; > > + > > + graph_walk_quit = false; > > + force_quit = false; > > + interactive = 0; > > + > > + node_patterns = NULL; > > + > > + signal(SIGINT, signal_handler); > > + signal(SIGTERM, signal_handler); > > + > > + testgraph_logtype = rte_log_register("testgraph"); > > + if (testgraph_logtype < 0) > > + rte_exit(EXIT_FAILURE, "Cannot register log type"); > > + > > + set_default_node_pattern(); > > + > > + rte_log_set_level(testgraph_logtype, RTE_LOG_DEBUG); > > + > > + ret = rte_eal_init(argc, argv); > > + if (ret < 0) > > + rte_exit(EXIT_FAILURE, "Cannot init EAL: %s\n", > > rte_strerror(rte_errno)); > > + argc -= ret; > > + argv += ret; > > + > > + if (argc > 1) { > > + ret = parse_cmdline_args(argc, argv); > > + if (ret < 0) > > + rte_exit(EXIT_FAILURE, "Invalid command line > > parameters\n"); > > + } > > + > > +#ifdef RTE_LIB_CMDLINE > > + if (init_cmdline() != 0) > > + rte_exit(EXIT_FAILURE, "Could not initialise cmdline > > context.\n"); > > + > > + if (interactive == 1) { > > + prompt(); > > + } else > > +#endif > > + { > > + if (validate_config() < 0) > > + rte_exit(EXIT_FAILURE, "Config validation failed.\n"); > > + > > + ret = validate_node_names(&valid_nodes); > > + if (ret) > > + rte_exit(EXIT_FAILURE, "validate_node_names: > > err=%d\n", ret); > > + > > + nb_conf = ethdev_ports_setup(); > > + > > + ethdev_rxq_configure(); > > + > > + ethdev_txq_configure(); > > + > > + ret = configure_graph_nodes(valid_nodes); > > + if (ret) > > + rte_exit(EXIT_FAILURE, "configure_graph_nodes: > > err=%d\n", ret); > > + > > + ret = create_graph(valid_nodes); > > + if (ret) > > + rte_exit(EXIT_FAILURE, "create_graph: err=%d\n", > ret); > > + > > + stats = create_graph_cluster_stats(); > > + if (stats == NULL) > > + rte_exit(EXIT_FAILURE, > "create_graph_cluster_stats() > > failed\n"); > > + > > + check_all_ports_link_status(enabled_port_mask); > > + > > + start_eth_ports(); > > + > > + /* Launch per-lcore init on every worker lcore */ > > + rte_eal_mp_remote_launch(graph_main_loop, NULL, > > SKIP_MAIN); > > + > > + /* Accumulate and print stats on main until exit */ > > + if (rte_graph_has_stats_feature()) > > + print_stats(); > > + > > + /* Wait for worker cores to exit */ > > + RTE_LCORE_FOREACH_WORKER(lcore_id) { > > + ret = rte_eal_wait_lcore(lcore_id); > > + if (ret < 0) > > + break; > > + } > > + > > + ret = destroy_graph(); > > + > > + stop_eth_ports(); > > + } > > + > > + /* clean up the EAL */ > > + ret = rte_eal_cleanup(); > > + if (ret != 0) > > + rte_exit(EXIT_FAILURE, "EAL cleanup failed: %s\n", strerror(- > > ret)); > > + > > + return EXIT_SUCCESS; > > +} > > diff --git a/app/test-graph/testgraph.h b/app/test-graph/testgraph.h > > new file mode 100644 > > index 0000000000..ea1f8ef4fa > > --- /dev/null > > +++ b/app/test-graph/testgraph.h > > @@ -0,0 +1,91 @@ > > +/* SPDX-License-Identifier: BSD-3-Clause > > + * Copyright(C) 2023 Marvell International Ltd. > > + */ > > + > > +#ifndef _TESTGRAPH_H_ > > +#define _TESTGRAPH_H_ > > + > > +#include <stdbool.h> > > + > > +#include <rte_ethdev.h> > > +#include <rte_graph.h> > > +#include <rte_node_eth_api.h> > > + > > +#include <cmdline.h> > > +#include <cmdline_parse.h> > > + > > + > > +#define MAX_LCORE_PARAMS 1024 > > +#define MAX_NODE_PATTERNS 128 > > + > > +#ifndef BIT_ULL > > +#define BIT_ULL(nr) (1ULL << (nr)) > > +#endif > > + > > +#define TEST_GRAPH_ETHDEV_RX_NODE BIT_ULL(0) > > +#define TEST_GRAPH_ETHDEV_TX_NODE BIT_ULL(1) > > +#define TEST_GRAPH_PUNT_KERNEL_NODE BIT_ULL(2) > > +#define TEST_GRAPH_KERNEL_RECV_NODE BIT_ULL(3) > > +#define TEST_GRAPH_IP4_LOOKUP_NODE BIT_ULL(4) > > +#define TEST_GRAPH_IP4_REWRITE_NODE BIT_ULL(5) > > +#define TEST_GRAPH_PKT_CLS_NODE BIT_ULL(6) > > +#define TEST_GRAPH_PKT_DROP_NODE BIT_ULL(7) > > +#define TEST_GRAPH_NULL_NODE BIT_ULL(8) > > + > > +static volatile bool force_quit; > > + > > +extern struct rte_node_ethdev_config > ethdev_conf[RTE_MAX_ETHPORTS]; > > +extern uint32_t enabled_port_mask; > > +extern uint32_t nb_conf; > > + > > +extern int promiscuous_on; /**< Ports set in promiscuous mode off by > > default. */ > > +extern uint8_t interactive; /**< interactive mode is disabled by > default. > > */ > > +extern uint32_t enabled_port_mask; /**< Mask of enabled ports */ > > +extern int numa_on; /**< NUMA is enabled by default. > */ > > +extern int per_port_pool; > > + > > +extern volatile bool graph_walk_quit; > > +extern volatile bool run_graph_walk; > > +extern struct rte_graph_cluster_stats *stats; > > + > > +extern char > node_pattern[MAX_NODE_PATTERNS][RTE_NODE_NAMESIZE]; > > +extern uint8_t num_patterns; > > + > > +struct node_list { > > + const char *nodes[MAX_NODE_PATTERNS]; > > + uint64_t test_id; > > + uint8_t size; > > +}; > > + > > +struct lcore_params { > > + uint16_t port_id; > > + uint8_t queue_id; > > + uint8_t lcore_id; > > +} __rte_cache_aligned; > > + > > +void prompt(void); > > +void prompt_exit(void); > > +int init_cmdline(void); > > +int validate_config(void); > > +int parse_cmdline_args(int argc, char **argv); > > +uint32_t ethdev_ports_setup(void); > > +void ethdev_rxq_configure(void); > > +void ethdev_txq_configure(void); > > +void start_eth_ports(void); > > +void stop_eth_ports(void); > > +int create_graph(uint64_t valid_nodes); > > +int destroy_graph(void); > > +int graph_main_loop(void *conf); > > +struct rte_graph_cluster_stats *create_graph_cluster_stats(void); > > +void check_all_ports_link_status(uint32_t port_mask); > > +int configure_graph_nodes(uint64_t valid_nodes); > > + > > +int parse_config(const char *q_arg); > > +int parse_node_patterns(const char *q_arg); > > +int validate_node_names(uint64_t *valid_nodes); > > +void cleanup_node_pattern(void); > > + > > +#define TESTGRAPH_LOG(level, fmt, args...) > > \ > > + rte_log(RTE_LOG_##level, testgraph_logtype, "testgraph: " fmt, > ##args) > > + > > +#endif /* _TESTGRAPH_H_ */ > > diff --git a/doc/guides/tools/index.rst b/doc/guides/tools/index.rst > > index 6f84fc31ff..f18c508fa2 100644 > > --- a/doc/guides/tools/index.rst > > +++ b/doc/guides/tools/index.rst > > @@ -20,6 +20,7 @@ DPDK Tools User Guides > > cryptoperf > > comp_perf > > testeventdev > > + testgraph > > testregex > > testmldev > > dts > > diff --git a/doc/guides/tools/testgraph.rst > b/doc/guides/tools/testgraph.rst > > new file mode 100644 > > index 0000000000..3c1e058724 > > --- /dev/null > > +++ b/doc/guides/tools/testgraph.rst > > @@ -0,0 +1,131 @@ > > +.. SPDX-License-Identifier: BSD-3-Clause > > + Copyright(C) 2023 Marvell International Ltd. > > + > > +dpdk-test-graph Application > > +=========================== > > + > > +The ``dpdk-test-graph`` tool is a Data Plane Development Kit (DPDK) > application > > that allows > > +exercising various graph library features. This application has a generic > > framework to add > > +new test configurations and expand test coverage to verify the > functionality of > > graph nodes > > +and observe the graph cluster statistics. > > + > > +Running the Application > > +----------------------- > > + > > +The application has a number of command line options: > > + > > +.. code-block:: console > > + > > + dpdk-test-eventdev [EAL Options] -- [application options] > > + > > +EAL Options > > +~~~~~~~~~~~ > > + > > +The following are the EAL command-line options that can be used in > > conjunction > > +with the ``dpdk-test-graph`` application. > > +See the DPDK Getting Started Guides for more information on these > options. > > + > > +* ``-c <COREMASK>`` or ``-l <CORELIST>`` > > + > > + Set the hexadecimal bitmask of the cores to run on. The corelist > > is a > > + list of cores to use. > > + > > +Application Options > > +~~~~~~~~~~~~~~~~~~~ > > + > > +The following are the application command-line options: > > + > > +* ``-p <n>`` > > + > > + Set the ethdev port mask. > > + > > +* ``-P`` > > + > > + Set the ethdev ports in promiscuous mode. > > + > > +* ``--config <config>`` > > + > > + Set the Rxq configuration. > > + (i.e. ``--config > > (port_id,rxq,lcore_id)[,(port_id,rxq,lcore_id)]``). > > + > > +* ``--node-pattern <n>`` > > + > > + Set the node patterns to use in graph creation. > > + (i.e. ``--node-pattern (node_name0,node_name1[,node_nameX])``). > > + > > +* ``--per-port-pool`` > > + > > + Use separate buffer pool per port. > > + > > +* ``--no-numa`` > > + > > + Disable numa awareness. > > + > > +* ``--interactive`` > > + > > + Switch to interactive mode. > > + > > +Running the Tool > > +~~~~~~~~~~~~~~~~ > > + > > +Here is the sample command line to run simple iofwd test:: > > + > > + ./dpdk-test-graph -a 0002:03:00.0 -a 0002:04:00.0 -c 0xF -- -p 0x3 > > -P \ > > + --config "(0,0,2),(1,0,2)" --node-pattern "(ethdev_rx,ethdev_tx)" > > + > > +Below is a sample command line to punt rx packets to kernel:: > > + > > + ./dpdk-test-graph -a 0002:03:00.0 -a 0002:04:00.0 -c 0xF -- -p 0x3 > > -P \ > > + --config "(0,0,2),(1,0,2)" --node-pattern "(ethdev_rx,punt_kernel)" > > + > > +Interactive mode > > +~~~~~~~~~~~~~~~~ > > + > > +Tool uses ``--interactive`` command line option to enter interactive mode > and > > use cmdline options > > +to setup the required node configurations, create graph and than start > > graph_walk. > > + > > + > > +testgraph> help > > + > > +Help is available for the following sections: > > + > > + help control : Start and stop graph walk. > > + help display : Displaying port, stats and config > > information. > > + help config : Configuration information. > > + help all : All of the above sections. > > + > > +testgraph> help all > > + > > +Control forwarding: > > + > > +start graph_walk > > + Start graph_walk on worker threads. > > + > > +stop graph_walk > > + Stop worker threads from running graph_walk. > > + > > +quit > > + Quit to prompt. > > + > > + > > +Display: > > + > > +show node_list > > + Display the list of supported nodes. > > + > > +show graph_stats > > + Display the node statistics of graph cluster. > > + > > + > > +Configuration: > > + > > +set lcore_config > > (port_id0,rxq0,lcore_idX),........,(port_idX,rxqX,lcoreidY) > > + Set lcore configuration. > > + > > +create_graph (node0_name,node1_name,...,nodeX_name) > > + Create graph instances using the provided node details. > > + > > +destroy_graph > > + Destroy the graph instances. > > + > > +testgraph> > > -- > > 2.25.1