From: Vamsi Attunuru <vattunuru@marvell.com>
To: <dev@dpdk.org>, <thomas@monjalon.net>, <jerinj@marvell.com>
Cc: <vattunuru@marvell.com>, <ndabilpuram@marvell.com>
Subject: [PATCH v2 4/4] app: add testgraph application
Date: Tue, 25 Apr 2023 06:15:16 -0700 [thread overview]
Message-ID: <20230425131516.3308612-5-vattunuru@marvell.com> (raw)
In-Reply-To: <20230425131516.3308612-1-vattunuru@marvell.com>
Patch adds test-graph application to validate graph
and node libraries.
Signed-off-by: Vamsi Attunuru <vattunuru@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;
+ }
+
+ 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();
+}
+
+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);
+ 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} };
+
+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);
+ }
+
+ 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);
+ 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)
+{
+ 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;
+}
+
+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);
+ }
+}
+
+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);
+
+ 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
next prev parent reply other threads:[~2023-04-25 13:16 UTC|newest]
Thread overview: 182+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-04-21 6:02 [PATCH 0/4] app: introduce " Vamsi Attunuru
2023-04-21 6:02 ` [PATCH 1/4] node: add pkt punt to kernel node Vamsi Attunuru
2023-05-29 17:29 ` Jerin Jacob
2023-05-31 12:36 ` [EXT] " Vamsi Krishna Attunuru
2023-04-21 6:02 ` [PATCH 2/4] node: add a node to receive pkts from kernel Vamsi Attunuru
2023-05-29 17:39 ` Jerin Jacob
2023-06-01 2:40 ` [EXT] " Vamsi Krishna Attunuru
2023-04-21 6:02 ` [PATCH 3/4] node: remove hardcoded node next details Vamsi Attunuru
2023-04-21 6:02 ` [PATCH 4/4] app: add testgraph application Vamsi Attunuru
2023-05-09 6:09 ` Jerin Jacob
2023-04-25 13:15 ` [PATCH v2 0/4] app: introduce " Vamsi Attunuru
2023-04-25 13:15 ` [PATCH v2 1/4] node: add pkt punt to kernel node Vamsi Attunuru
2023-05-30 8:16 ` Nithin Dabilpuram
2023-05-31 12:35 ` [EXT] " Vamsi Krishna Attunuru
2023-04-25 13:15 ` [PATCH v2 2/4] node: add a node to receive pkts from kernel Vamsi Attunuru
2023-04-25 13:15 ` [PATCH v2 3/4] node: remove hardcoded node next details Vamsi Attunuru
2023-04-25 13:15 ` Vamsi Attunuru [this message]
2023-05-09 2:34 ` [EXT] [PATCH v2 4/4] app: add testgraph application Sunil Kumar Kori
2023-05-09 3:39 ` Vamsi Krishna Attunuru
2023-05-09 8:53 ` Sunil Kumar Kori
2023-05-09 12:24 ` Vamsi Krishna Attunuru
2023-05-12 11:08 ` Yan, Zhirun
2023-05-22 7:07 ` Vamsi Krishna Attunuru
2023-05-30 7:34 ` Jerin Jacob
2023-06-01 2:44 ` [EXT] " Vamsi Krishna Attunuru
2023-07-20 15:52 ` Rakesh Kudurumalla
2023-07-21 6:48 ` Jerin Jacob
2023-07-21 7:01 ` Sunil Kumar Kori
2023-07-21 7:39 ` Rakesh Kudurumalla
2023-09-08 11:00 ` Sunil Kumar Kori
2023-09-08 10:49 ` [PATCH v3 1/1] app/graph: add example for different usecases skori
2023-09-09 1:18 ` [EXT] " Nithin Kumar Dabilpuram
2023-09-19 16:04 ` [PATCH v4 01/14] app/graph: add application framework to read CLI skori
2023-09-19 16:04 ` [PATCH v4 02/14] app/graph: add telnet connectivity framework skori
2023-09-20 4:34 ` Jerin Jacob
2023-09-20 8:14 ` Bruce Richardson
2023-09-19 16:04 ` [PATCH v4 03/14] app/graph: add parser utility APIs skori
2023-09-19 16:04 ` [PATCH v4 04/14] app/graph: add mempool command line interfaces skori
2023-09-19 16:04 ` [PATCH v4 05/14] app/graph: add ethdev " skori
2023-09-19 16:04 ` [PATCH v4 06/14] app/graph: add ipv4_lookup " skori
2023-09-19 16:04 ` [PATCH v4 07/14] app/graph: add ipv6_lookup " skori
2023-09-19 16:04 ` [PATCH v4 08/14] app/graph: add neigh " skori
2023-09-19 16:04 ` [PATCH v4 09/14] app/graph: add ethdev_rx " skori
2023-09-19 16:04 ` [PATCH v4 10/14] app/graph: add graph " skori
2023-09-19 16:04 ` [PATCH v4 11/14] app/graph: add CLI option to enable graph stats skori
2023-09-19 16:04 ` [PATCH v4 12/14] app/graph: add l3fwd usecase skori
2023-09-19 16:04 ` [PATCH v4 13/14] doc: add graph application user guide skori
2023-09-20 4:28 ` Jerin Jacob
2023-09-19 16:04 ` [PATCH v4 14/14] maintainers: add maintainers for graph app skori
2023-09-20 4:40 ` [PATCH v4 01/14] app/graph: add application framework to read CLI Jerin Jacob
2023-09-21 10:08 ` [PATCH v5 00/12] add CLI based graph application skori
2023-09-21 10:08 ` [PATCH v5 01/12] app/graph: add application framework to read CLI skori
2023-09-21 10:08 ` [PATCH v5 02/12] app/graph: add telnet connectivity framework skori
2023-09-21 10:08 ` [PATCH v5 03/12] app/graph: add parser utility APIs skori
2023-09-21 10:08 ` [PATCH v5 04/12] app/graph: add mempool command line interfaces skori
2023-09-21 10:08 ` [PATCH v5 05/12] app/graph: add ethdev " skori
2023-09-21 10:08 ` [PATCH v5 06/12] app/graph: add ipv4_lookup " skori
2023-09-21 10:08 ` [PATCH v5 07/12] app/graph: add ipv6_lookup " skori
2023-09-21 10:08 ` [PATCH v5 08/12] app/graph: add neigh " skori
2023-09-21 10:08 ` [PATCH v5 09/12] app/graph: add ethdev_rx " skori
2023-09-21 10:08 ` [PATCH v5 10/12] app/graph: add graph " skori
2023-09-21 10:08 ` [PATCH v5 11/12] app/graph: add CLI option to enable graph stats skori
2023-09-21 10:08 ` [PATCH v5 12/12] app/graph: add l3fwd usecase skori
2023-09-26 10:57 ` [PATCH v6 00/12] add CLI based graph application skori
2023-09-26 10:57 ` [PATCH v6 01/12] app/graph: add application framework to read CLI skori
2023-09-26 10:57 ` [PATCH v6 02/12] app/graph: add telnet connectivity framework skori
2023-09-26 10:57 ` [PATCH v6 03/12] app/graph: add parser utility APIs skori
2023-09-26 10:57 ` [PATCH v6 04/12] app/graph: add mempool command line interfaces skori
2023-09-26 10:57 ` [PATCH v6 05/12] app/graph: add ethdev " skori
2023-09-26 10:57 ` [PATCH v6 06/12] app/graph: add ipv4_lookup " skori
2023-09-26 10:57 ` [PATCH v6 07/12] app/graph: add ipv6_lookup " skori
2023-09-26 10:57 ` [PATCH v6 08/12] app/graph: add neigh " skori
2023-09-26 10:57 ` [PATCH v6 09/12] app/graph: add ethdev_rx " skori
2023-09-26 10:57 ` [PATCH v6 10/12] app/graph: add graph " skori
2023-09-26 10:57 ` [PATCH v6 11/12] app/graph: add CLI option to enable graph stats skori
2023-09-26 10:57 ` [PATCH v6 12/12] app/graph: add l3fwd use case skori
2023-09-27 11:54 ` [PATCH v7 00/12] add CLI based graph application skori
2023-09-27 11:54 ` [PATCH v7 01/12] app/graph: add application framework to read CLI skori
2023-09-27 11:54 ` [PATCH v7 02/12] app/graph: add telnet connectivity framework skori
2023-09-27 11:54 ` [PATCH v7 03/12] app/graph: add parser utility APIs skori
2023-09-27 11:54 ` [PATCH v7 04/12] app/graph: add mempool command line interfaces skori
2023-09-27 11:54 ` [PATCH v7 05/12] app/graph: add ethdev " skori
2023-09-27 11:54 ` [PATCH v7 06/12] app/graph: add ipv4_lookup " skori
2023-09-27 11:54 ` [PATCH v7 07/12] app/graph: add ipv6_lookup " skori
2023-09-27 11:54 ` [PATCH v7 08/12] app/graph: add neigh " skori
2023-09-27 11:54 ` [PATCH v7 09/12] app/graph: add ethdev_rx " skori
2023-09-27 11:54 ` [PATCH v7 10/12] app/graph: add graph " skori
2023-09-27 11:54 ` [PATCH v7 11/12] app/graph: add CLI option to enable graph stats skori
2023-09-27 11:54 ` [PATCH v7 12/12] app/graph: add l3fwd use case skori
2023-09-29 9:58 ` [PATCH v8 00/12] add CLI based graph application skori
2023-09-29 9:58 ` [PATCH v8 01/12] app/graph: add application framework to read CLI skori
2023-10-16 9:00 ` Jerin Jacob
2023-10-17 6:19 ` [EXT] " Sunil Kumar Kori
2023-10-18 6:33 ` [PATCH v9 00/12] add CLI based graph application skori
2023-10-18 6:33 ` [PATCH v9 01/12] app/graph: support application CLI framework skori
2023-10-18 6:33 ` [PATCH v9 02/12] app/graph: support telnet connectivity framework skori
2023-10-18 6:33 ` [PATCH v9 03/12] app/graph: support parser utility APIs skori
2023-10-18 6:33 ` [PATCH v9 04/12] app/graph: support mempool command line interfaces skori
2023-10-18 6:33 ` [PATCH v9 05/12] app/graph: support ethdev " skori
2023-10-18 6:33 ` [PATCH v9 06/12] app/graph: support IPv4 lookup " skori
2023-10-18 6:33 ` [PATCH v9 07/12] app/graph: support IPv6 " skori
2023-10-18 6:33 ` [PATCH v9 08/12] app/graph: support neigh " skori
2023-10-18 6:33 ` [PATCH v9 09/12] app/graph: support ethdev Rx " skori
2023-10-18 6:33 ` [PATCH v9 10/12] app/graph: support graph " skori
2023-10-18 6:33 ` [PATCH v9 11/12] app/graph: support CLI option to enable graph stats skori
2023-10-18 6:33 ` [PATCH v9 12/12] app/graph: support l3fwd use case skori
2023-10-18 10:38 ` Jerin Jacob
2023-10-19 10:49 ` [PATCH v10 00/12] add CLI based graph application skori
2023-10-19 10:49 ` [PATCH v10 01/12] app/graph: support application CLI framework skori
2023-10-19 10:49 ` [PATCH v10 02/12] app/graph: support telnet connectivity framework skori
2023-10-19 10:49 ` [PATCH v10 03/12] app/graph: support parser utility APIs skori
2023-10-19 10:49 ` [PATCH v10 04/12] app/graph: support mempool command line interfaces skori
2023-10-19 10:49 ` [PATCH v10 05/12] app/graph: support ethdev " skori
2023-10-19 10:49 ` [PATCH v10 06/12] app/graph: support IPv4 lookup " skori
2023-10-19 10:49 ` [PATCH v10 07/12] app/graph: support IPv6 " skori
2023-10-19 10:49 ` [PATCH v10 08/12] app/graph: support neigh " skori
2023-10-19 10:49 ` [PATCH v10 09/12] app/graph: support ethdev Rx " skori
2023-10-19 10:49 ` [PATCH v10 10/12] app/graph: support graph " skori
2023-10-19 10:49 ` [PATCH v10 11/12] app/graph: support CLI option to enable graph stats skori
2023-10-19 10:50 ` [PATCH v10 12/12] app/graph: support l3fwd use case skori
2023-10-19 12:28 ` [EXT] " Jerin Jacob Kollanukkaran
2023-10-23 7:06 ` Nithin Dabilpuram
2023-10-19 17:29 ` [PATCH v11 00/12] add CLI based graph application skori
2023-10-19 17:30 ` [PATCH v11 01/12] app/graph: support application CLI framework skori
2023-10-23 7:03 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 02/12] app/graph: support telnet connectivity framework skori
2023-10-23 7:03 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 03/12] app/graph: support parser utility APIs skori
2023-10-23 7:03 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 04/12] app/graph: support mempool command line interfaces skori
2023-10-23 7:04 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 05/12] app/graph: support ethdev " skori
2023-10-23 7:04 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 06/12] app/graph: support IPv4 lookup " skori
2023-10-23 7:04 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 07/12] app/graph: support IPv6 " skori
2023-10-23 7:04 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 08/12] app/graph: support neigh " skori
2023-10-23 7:05 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 09/12] app/graph: support ethdev Rx " skori
2023-10-23 7:05 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 10/12] app/graph: support graph " skori
2023-10-23 7:06 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 11/12] app/graph: support CLI option to enable graph stats skori
2023-10-23 7:06 ` Nithin Dabilpuram
2023-10-19 17:30 ` [PATCH v11 12/12] app/graph: support l3fwd use case skori
2023-10-23 13:03 ` [PATCH v11 00/12] add CLI based graph application Sunil Kumar Kori
2023-11-03 16:44 ` Thomas Monjalon
2023-09-29 9:58 ` [PATCH v8 02/12] app/graph: add telnet connectivity framework skori
2023-10-16 9:04 ` Jerin Jacob
2023-10-17 6:21 ` [EXT] " Sunil Kumar Kori
2023-09-29 9:58 ` [PATCH v8 03/12] app/graph: add parser utility APIs skori
2023-10-16 9:05 ` Jerin Jacob
2023-09-29 9:58 ` [PATCH v8 04/12] app/graph: add mempool command line interfaces skori
2023-10-16 9:09 ` Jerin Jacob
2023-10-17 6:22 ` [EXT] " Sunil Kumar Kori
2023-09-29 9:58 ` [PATCH v8 05/12] app/graph: add ethdev " skori
2023-10-16 13:20 ` Jerin Jacob
2023-10-16 14:10 ` Bruce Richardson
2023-10-17 6:26 ` [EXT] " Sunil Kumar Kori
2023-09-29 9:58 ` [PATCH v8 06/12] app/graph: add ipv4_lookup " skori
2023-10-16 15:47 ` Jerin Jacob
2023-10-17 6:30 ` [EXT] " Sunil Kumar Kori
2023-09-29 9:58 ` [PATCH v8 07/12] app/graph: add ipv6_lookup " skori
2023-09-29 9:58 ` [PATCH v8 08/12] app/graph: add neigh " skori
2023-09-29 9:58 ` [PATCH v8 09/12] app/graph: add ethdev_rx " skori
2023-09-29 9:58 ` [PATCH v8 10/12] app/graph: add graph " skori
2023-09-29 9:58 ` [PATCH v8 11/12] app/graph: add CLI option to enable graph stats skori
2023-10-16 15:55 ` Jerin Jacob
2023-09-29 9:58 ` [PATCH v8 12/12] app/graph: add l3fwd use case skori
2023-10-16 16:01 ` [PATCH v8 00/12] add CLI based graph application Jerin Jacob
2023-10-16 16:27 ` Stephen Hemminger
2023-06-02 16:22 ` [PATCH v3 0/3] node: Introduce kernel_rx & kernel_tx nodes Vamsi Attunuru
2023-06-02 16:22 ` [PATCH v3 1/3] node/kernel_tx: support packet transmit to kernel Vamsi Attunuru
2023-06-05 12:47 ` Nithin Dabilpuram
2023-06-12 19:32 ` Thomas Monjalon
2023-06-02 16:22 ` [PATCH v3 2/3] node/kernel_rx: support receiving packets from kernel Vamsi Attunuru
2023-06-05 12:50 ` Nithin Dabilpuram
2023-06-02 16:22 ` [PATCH v3 3/3] node/ethdev_rx: remove hardcoded node next details Vamsi Attunuru
2023-06-05 12:51 ` Nithin Dabilpuram
2023-06-12 16:12 ` [PATCH v3 0/3] node: Introduce kernel_rx & kernel_tx nodes Vamsi Krishna Attunuru
2023-06-12 19:31 ` Thomas Monjalon
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230425131516.3308612-5-vattunuru@marvell.com \
--to=vattunuru@marvell.com \
--cc=dev@dpdk.org \
--cc=jerinj@marvell.com \
--cc=ndabilpuram@marvell.com \
--cc=thomas@monjalon.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).