[PATCH v6 4/4] app/graph: add custom feature nodes for ip4 output arc

Nitin Saxena nsaxena at marvell.com
Fri Jan 3 07:06:07 CET 2025


- Added cmdline argument "--enable-graph-feature-arc" to call
  rte_graph_feature_arc_init() before rte_graph_create() which creates
  in-built arcs and feature nodes
- Added custom feature nodes in app/graph which are added to ip4 output
  arc.
- Custom features can be enabled/disabled at runtime on any ethdev via
  CLI.

graph> help feature
graph> feature enable <arc name> <feature name> <port-id>
graph> feature disable <arc name> <feature name> <port-id>
graph> graph stats show

Signed-off-by: Nitin Saxena <nsaxena at marvell.com>
---
 app/graph/commands.list     |   6 ++
 app/graph/feature.c         | 141 ++++++++++++++++++++++++++++++
 app/graph/feature.h         |  13 +++
 app/graph/graph.c           |   4 +
 app/graph/ip4_output_hook.c | 169 ++++++++++++++++++++++++++++++++++++
 app/graph/main.c            |  15 +++-
 app/graph/meson.build       |   2 +
 app/graph/module_api.h      |   2 +
 8 files changed, 351 insertions(+), 1 deletion(-)
 create mode 100644 app/graph/feature.c
 create mode 100644 app/graph/feature.h
 create mode 100644 app/graph/ip4_output_hook.c

diff --git a/app/graph/commands.list b/app/graph/commands.list
index c027f73b0e..49d81f50ae 100644
--- a/app/graph/commands.list
+++ b/app/graph/commands.list
@@ -31,3 +31,9 @@ help ipv6_lookup                                         # Print help on ipv6_lo
 neigh add ipv4 <IPv4>ip <STRING>mac                      # Add static neighbour for IPv4
 neigh add ipv6 <IPv6>ip <STRING>mac                      # Add static neighbour for IPv6
 help neigh                                               # Print help on neigh commands
+
+feature arcs                                             # show all feature arcs
+feature <STRING>name show                                # Show feature arc details
+feature enable <STRING>arc_name <STRING>feature_name <UINT16>interface   # Enable feature on interface
+feature disable <STRING>arc_name <STRING>feature_name <UINT16>interface  # Disable feature on interface
+help feature                                             # Print help on feature command
diff --git a/app/graph/feature.c b/app/graph/feature.c
new file mode 100644
index 0000000000..2cf21b11ce
--- /dev/null
+++ b/app/graph/feature.c
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <cmdline_parse.h>
+#include <cmdline_parse_num.h>
+#include <cmdline_parse_string.h>
+#include <cmdline_socket.h>
+#include <rte_ethdev.h>
+
+#include "module_api.h"
+
+static const char
+cmd_feature_arcs_help[] = "feature arcs    # Display all feature arcs";
+
+static const char
+cmd_feature_show_help[] = "feature <arc_name> show   # Display features within an arc";
+
+static const char
+cmd_feature_enable_help[] = "feature enable <arc name> <feature name> <port-id>";
+
+static const char
+cmd_feature_disable_help[] = "feature disable <arc name> <feature name> <port-id>";
+
+static void
+feature_show(const char *arc_name)
+{
+	rte_graph_feature_arc_t _arc;
+	uint32_t length, count, i;
+
+	length = strlen(conn->msg_out);
+	conn->msg_out += length;
+
+	if (rte_graph_feature_arc_lookup_by_name(arc_name, &_arc) < 0)
+		return;
+
+	count = rte_graph_feature_arc_num_features(_arc);
+
+	if (count) {
+		snprintf(conn->msg_out, conn->msg_out_len_max, "\n%s%s%s\n",
+			 "----------------------------- feature arc: ",
+			 rte_graph_feature_arc_get(_arc)->feature_arc_name,
+			 " -----------------------------");
+		for (i = 0; i < count; i++)
+			snprintf(conn->msg_out + strlen(conn->msg_out),
+				 conn->msg_out_len_max, "%s\n",
+				 rte_graph_feature_arc_feature_to_name(_arc, i));
+	}
+	length = strlen(conn->msg_out);
+	conn->msg_out_len_max -= length;
+}
+
+static void
+feature_arcs_show(void)
+{
+	uint32_t length, count, i;
+	char **names;
+
+	length = strlen(conn->msg_out);
+	conn->msg_out += length;
+
+	count = rte_graph_feature_arc_names_get(NULL);
+
+	if (count) {
+		names = malloc(count);
+		if (!names) {
+			snprintf(conn->msg_out, conn->msg_out_len_max, "Failed to allocate memory\n");
+			return;
+		}
+		count = rte_graph_feature_arc_names_get(names);
+		snprintf(conn->msg_out, conn->msg_out_len_max, "\n%s\n",
+			 "----------------------------- feature arcs -----------------------------");
+		for (i = 0; i < count; i++)
+			feature_show(names[i]);
+		free(names);
+	}
+	length = strlen(conn->msg_out);
+	conn->msg_out_len_max -= length;
+}
+
+void
+cmd_feature_parsed(void *parsed_result, __rte_unused struct cmdline *cl,
+		   __rte_unused void *data)
+{
+	struct cmd_feature_result *res = parsed_result;
+
+	feature_show(res->name);
+}
+
+
+void
+cmd_feature_arcs_parsed(__rte_unused void *parsed_result, __rte_unused struct cmdline *cl,
+			__rte_unused void *data)
+{
+		feature_arcs_show();
+}
+
+void
+cmd_feature_enable_parsed(void *parsed_result, __rte_unused struct cmdline *cl,
+			  __rte_unused void *data)
+{
+	struct cmd_feature_enable_result *res = parsed_result;
+	rte_graph_feature_arc_t arc;
+
+	if (!rte_graph_feature_arc_lookup_by_name(res->arc_name, &arc))
+		rte_graph_feature_enable(arc, res->interface, res->feature_name,
+					 res->interface, NULL);
+}
+
+void
+cmd_feature_disable_parsed(void *parsed_result, __rte_unused struct cmdline *cl,
+			   __rte_unused void *data)
+{
+	struct cmd_feature_disable_result *res = parsed_result;
+	rte_graph_feature_arc_t arc;
+
+	if (!rte_graph_feature_arc_lookup_by_name(res->arc_name, &arc))
+		rte_graph_feature_disable(arc, res->interface, res->feature_name, NULL);
+
+}
+
+void
+cmd_help_feature_parsed(__rte_unused void *parsed_result, __rte_unused struct cmdline *cl,
+			__rte_unused void *data)
+{
+	size_t len;
+
+	len = strlen(conn->msg_out);
+	conn->msg_out += len;
+	snprintf(conn->msg_out, conn->msg_out_len_max, "\n%s\n%s\n%s\n%s\n%s\n",
+		 "----------------------------- feature command help -----------------------------",
+		 cmd_feature_arcs_help, cmd_feature_show_help, cmd_feature_enable_help,
+		 cmd_feature_disable_help);
+
+	len = strlen(conn->msg_out);
+	conn->msg_out_len_max -= len;
+}
diff --git a/app/graph/feature.h b/app/graph/feature.h
new file mode 100644
index 0000000000..76393dabc6
--- /dev/null
+++ b/app/graph/feature.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#ifndef APP_GRAPH_FEATURE_H
+#define APP_GRAPH_FEATURE_H
+
+#include <cmdline_parse.h>
+#include <rte_graph_feature_arc_worker.h>
+
+int feature_enable(const char *arc_name, const char *feature_name, uint16_t portid);
+int feature_disable(const char *arc_name, const char *feature_name, uint16_t portid);
+#endif
diff --git a/app/graph/graph.c b/app/graph/graph.c
index 3af031cbaf..3ccd702ed9 100644
--- a/app/graph/graph.c
+++ b/app/graph/graph.c
@@ -12,6 +12,7 @@
 #include <cmdline_socket.h>
 #include <rte_ethdev.h>
 #include <rte_graph_worker.h>
+#include <rte_graph_feature_arc_worker.h>
 #include <rte_log.h>
 
 #include "graph_priv.h"
@@ -265,6 +266,9 @@ cmd_graph_start_parsed(__rte_unused void *parsed_result, __rte_unused struct cmd
 	uint32_t nb_graphs = 0, nb_conf, i;
 	int rc = -EINVAL;
 
+	if (app_graph_feature_arc_enabled())
+		rte_graph_feature_arc_init();
+
 	conf = graph_rxtx_node_config_get(&nb_conf, &nb_graphs);
 	for (i = 0; i < MAX_GRAPH_USECASES; i++) {
 		if (!strcmp(graph_config.usecases[i].name, "l3fwd")) {
diff --git a/app/graph/ip4_output_hook.c b/app/graph/ip4_output_hook.c
new file mode 100644
index 0000000000..1a389f1113
--- /dev/null
+++ b/app/graph/ip4_output_hook.c
@@ -0,0 +1,169 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#include <rte_graph.h>
+#include <rte_graph_worker.h>
+#include <rte_graph_feature_arc_worker.h>
+
+#include "rte_node_ip4_api.h"
+
+#define IP4_OUTPUT_HOOK_FEATURE1_NAME "app_graph_ip4_output_hook_f1"
+#define IP4_OUTPUT_HOOK_FEATURE2_NAME "app_graph_ip4_output_hook_f2"
+
+struct output_hook_node_ctx {
+	rte_graph_feature_arc_t out_arc;
+	uint16_t last_index;
+};
+
+#define OUTPUT_HOOK_FEATURE_ARC(ctx) \
+	(((struct output_hook_node_ctx *)ctx)->out_arc)
+
+#define OUTPUT_HOOK_LAST_NEXT_INDEX(ctx) \
+	(((struct output_hook_node_ctx *)ctx)->last_index)
+
+static int
+__app_graph_ip4_output_hook_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	rte_graph_feature_arc_t feature;
+
+	RTE_SET_USED(graph);
+
+	rte_graph_feature_arc_lookup_by_name(RTE_IP4_OUTPUT_FEATURE_ARC_NAME, &feature);
+
+	OUTPUT_HOOK_FEATURE_ARC(node->ctx) = feature;
+	/* pkt_drop */
+	OUTPUT_HOOK_LAST_NEXT_INDEX(node->ctx) = 0;
+
+	return 0;
+}
+
+static __rte_always_inline uint16_t
+__app_graph_ip4_output_hook_node_process(struct rte_graph *graph, struct rte_node *node,
+					 void **objs, uint16_t nb_objs)
+{
+	struct rte_graph_feature_arc *arc =
+		rte_graph_feature_arc_get(OUTPUT_HOOK_FEATURE_ARC(node->ctx));
+	int feat_dyn_off = rte_graph_feature_arc_mbuf_dynfield_offset_get();
+	struct rte_graph_feature_arc_mbuf_dynfields *mbfields = NULL;
+	rte_graph_feature_data_t fdata;
+	void **to_next, **from;
+	uint16_t last_spec = 0;
+	rte_edge_t next_index;
+	struct rte_mbuf *mbuf;
+	uint16_t held = 0;
+	uint16_t next;
+	int i;
+
+	/* Speculative next */
+	next_index = OUTPUT_HOOK_LAST_NEXT_INDEX(node->ctx);
+
+	from = objs;
+	to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+	for (i = 0; i < nb_objs; i++) {
+
+		mbuf = (struct rte_mbuf *)objs[i];
+
+		/* Send mbuf to next enabled feature */
+		mbfields = rte_graph_feature_arc_mbuf_dynfields_get(mbuf, feat_dyn_off);
+		fdata = rte_graph_feature_data_next_feature_get(arc, mbfields->feature_data);
+		next = rte_graph_feature_data_edge_get(arc, fdata);
+
+		if (unlikely(next_index != next)) {
+			/* Copy things successfully speculated till now */
+			rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+			from += last_spec;
+			to_next += last_spec;
+			held += last_spec;
+			last_spec = 0;
+
+			rte_node_enqueue_x1(graph, node, next, from[0]);
+			from += 1;
+		} else {
+			last_spec += 1;
+		}
+	}
+	/* !!! Home run !!! */
+	if (likely(last_spec == nb_objs)) {
+		rte_node_next_stream_move(graph, node, next_index);
+		return nb_objs;
+	}
+	held += last_spec;
+	rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+	rte_node_next_stream_put(graph, node, next_index, held);
+	OUTPUT_HOOK_LAST_NEXT_INDEX(node->ctx) = next;
+
+	return nb_objs;
+}
+
+static int
+app_graph_ip4_output_hook_node1_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	return __app_graph_ip4_output_hook_node_init(graph, node);
+}
+
+static __rte_always_inline uint16_t
+app_graph_ip4_output_hook_node1_process(struct rte_graph *graph, struct rte_node *node,
+					void **objs, uint16_t nb_objs)
+{
+	return __app_graph_ip4_output_hook_node_process(graph, node, objs, nb_objs);
+}
+
+static struct rte_node_register app_graph_ip4_output_hook_node1 = {
+	.process = app_graph_ip4_output_hook_node1_process,
+	.init = app_graph_ip4_output_hook_node1_init,
+	.name = "app_graph_ip4_output_hook_node1",
+	.nb_edges = 1,
+	.next_nodes = {
+		[0] = "pkt_drop",
+	},
+};
+
+RTE_NODE_REGISTER(app_graph_ip4_output_hook_node1);
+
+static int
+app_graph_ip4_output_hook_node2_init(const struct rte_graph *graph, struct rte_node *node)
+{
+	return __app_graph_ip4_output_hook_node_init(graph, node);
+}
+
+static __rte_always_inline uint16_t
+app_graph_ip4_output_hook_node2_process(struct rte_graph *graph, struct rte_node *node,
+					void **objs, uint16_t nb_objs)
+{
+	return __app_graph_ip4_output_hook_node_process(graph, node, objs, nb_objs);
+}
+
+static struct rte_node_register app_graph_ip4_output_hook_node2 = {
+	.process = app_graph_ip4_output_hook_node2_process,
+	.init = app_graph_ip4_output_hook_node2_init,
+	.name = "app_graph_ip4_output_hook_node2",
+	.nb_edges = 1,
+	.next_nodes = {
+		[0] = "pkt_drop",
+	},
+};
+
+RTE_NODE_REGISTER(app_graph_ip4_output_hook_node2);
+
+/* if feature1 */
+struct rte_graph_feature_register app_graph_ip4_output_hook_feature1 = {
+	.feature_name = IP4_OUTPUT_HOOK_FEATURE1_NAME,
+	.arc_name = RTE_IP4_OUTPUT_FEATURE_ARC_NAME,
+	/* Same as regular function */
+	.feature_process_fn = app_graph_ip4_output_hook_node1_process,
+	.feature_node = &app_graph_ip4_output_hook_node1,
+	.runs_before =  IP4_OUTPUT_HOOK_FEATURE2_NAME,
+};
+
+/* if feature2 (same as f1) */
+struct rte_graph_feature_register app_graph_ip4_output_hook_feature2 = {
+	.feature_name = IP4_OUTPUT_HOOK_FEATURE2_NAME,
+	.arc_name = RTE_IP4_OUTPUT_FEATURE_ARC_NAME,
+	/* Same as regular function */
+	.feature_node = &app_graph_ip4_output_hook_node2,
+	.feature_process_fn = app_graph_ip4_output_hook_node2_process,
+};
+
+RTE_GRAPH_FEATURE_REGISTER(app_graph_ip4_output_hook_feature1);
+RTE_GRAPH_FEATURE_REGISTER(app_graph_ip4_output_hook_feature2);
diff --git a/app/graph/main.c b/app/graph/main.c
index 465376425c..56294f2693 100644
--- a/app/graph/main.c
+++ b/app/graph/main.c
@@ -28,6 +28,7 @@ static struct app_params {
 	struct conn_params conn;
 	char *script_name;
 	bool enable_graph_stats;
+	bool enable_feature_arc;
 } app = {
 	.conn = {
 		.welcome = "\nWelcome!\n\n",
@@ -42,6 +43,7 @@ static struct app_params {
 	},
 	.script_name = NULL,
 	.enable_graph_stats = false,
+	.enable_feature_arc = false,
 };
 
 static void
@@ -59,6 +61,7 @@ app_args_parse(int argc, char **argv)
 	struct option lgopts[] = {
 		{"help", 0, 0, 'H'},
 		{"enable-graph-stats", 0, 0, 'g'},
+		{"enable-graph-feature-arc", 0, 0, 'f'},
 	};
 	int h_present, p_present, s_present, n_args, i;
 	char *app_name = argv[0];
@@ -81,7 +84,7 @@ app_args_parse(int argc, char **argv)
 	p_present = 0;
 	s_present = 0;
 
-	while ((opt = getopt_long(argc, argv, "h:p:s:", lgopts, &option_index)) != EOF) {
+	while ((opt = getopt_long(argc, argv, "h:p:s:f", lgopts, &option_index)) != EOF) {
 		switch (opt) {
 		case 'h':
 			if (h_present) {
@@ -142,6 +145,10 @@ app_args_parse(int argc, char **argv)
 			       "--enable-graph-stats");
 			break;
 
+		case 'f':
+			app.enable_feature_arc = true;
+			break;
+
 		case 'H':
 		default:
 			printf(usage, app_name);
@@ -159,6 +166,12 @@ app_graph_stats_enabled(void)
 	return app.enable_graph_stats;
 }
 
+bool
+app_graph_feature_arc_enabled(void)
+{
+	return app.enable_feature_arc;
+}
+
 bool
 app_graph_exit(void)
 {
diff --git a/app/graph/meson.build b/app/graph/meson.build
index 344e4a418f..d7e8c431ea 100644
--- a/app/graph/meson.build
+++ b/app/graph/meson.build
@@ -24,6 +24,8 @@ sources = files(
         'mempool.c',
         'neigh.c',
         'utils.c',
+        'feature.c',
+        'ip4_output_hook.c'
 )
 
 cmd_h = custom_target('commands_hdr',
diff --git a/app/graph/module_api.h b/app/graph/module_api.h
index b872872dc1..25f88e28de 100644
--- a/app/graph/module_api.h
+++ b/app/graph/module_api.h
@@ -20,6 +20,7 @@
 #include "neigh.h"
 #include "route.h"
 #include "utils.h"
+#include "feature.h"
 
 /*
  * Externs
@@ -28,6 +29,7 @@ extern volatile bool force_quit;
 extern struct conn *conn;
 
 bool app_graph_stats_enabled(void);
+bool app_graph_feature_arc_enabled(void);
 bool app_graph_exit(void);
 
 #endif
-- 
2.43.0



More information about the dev mailing list