[RFC PATCH 2/2] graph: add ip4 output feature arc

2024-04-26 Thread Nitin Saxena
Signed-off-by: Nitin Saxena 
Change-Id: I80021403c343354c7e494c6bc79b83b0d0fe6b7c
---
 lib/node/ip4_rewrite.c  | 278 
 lib/node/ip4_rewrite_priv.h |  10 +-
 lib/node/node_private.h |  10 +-
 lib/node/rte_node_ip4_api.h |   3 +
 4 files changed, 233 insertions(+), 68 deletions(-)

diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index 34a920df5e..60efd6b171 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -20,6 +20,7 @@ struct ip4_rewrite_node_ctx {
int mbuf_priv1_off;
/* Cached next index */
uint16_t next_index;
+   rte_graph_feature_arc_t output_feature_arc;
 };
 
 static struct ip4_rewrite_node_main *ip4_rewrite_nm;
@@ -30,21 +31,34 @@ static struct ip4_rewrite_node_main *ip4_rewrite_nm;
 #define IP4_REWRITE_NODE_PRIV1_OFF(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->mbuf_priv1_off)
 
+#define IP4_REWRITE_NODE_OUTPUT_FEATURE_ARC(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->output_feature_arc)
+
 static uint16_t
 ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
 void **objs, uint16_t nb_objs)
 {
+   rte_graph_feature_arc_t out_feature_arc = 
IP4_REWRITE_NODE_OUTPUT_FEATURE_ARC(node->ctx);
+   uint16_t next0 = 0, next1 = 0, next2 = 0, next3 = 0, next_index;
struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
struct ip4_rewrite_nh_header *nh = ip4_rewrite_nm->nh;
const int dyn = IP4_REWRITE_NODE_PRIV1_OFF(node->ctx);
-   uint16_t next0, next1, next2, next3, next_index;
-   struct rte_ipv4_hdr *ip0, *ip1, *ip2, *ip3;
uint16_t n_left_from, held = 0, last_spec = 0;
+   struct rte_ipv4_hdr *ip0, *ip1, *ip2, *ip3;
+   int b0_feat, b1_feat, b2_feat, b3_feat;
+   rte_graph_feature_t f0, f1, f2, f3;
+   uint16_t tx0, tx1, tx2, tx3;
+   int64_t fd0, fd1, fd2, fd3;
void *d0, *d1, *d2, *d3;
void **to_next, **from;
rte_xmm_t priv01;
rte_xmm_t priv23;
-   int i;
+   int i, has_feat;
+
+   RTE_SET_USED(fd0);
+   RTE_SET_USED(fd1);
+   RTE_SET_USED(fd2);
+   RTE_SET_USED(fd3);
 
/* Speculative next as last next */
next_index = IP4_REWRITE_NODE_LAST_NEXT(node->ctx);
@@ -83,54 +97,167 @@ ip4_rewrite_node_process(struct rte_graph *graph, struct 
rte_node *node,
priv23.u64[0] = node_mbuf_priv1(mbuf2, dyn)->u;
priv23.u64[1] = node_mbuf_priv1(mbuf3, dyn)->u;
 
-   /* Increment checksum by one. */
-   priv01.u32[1] += rte_cpu_to_be_16(0x0100);
-   priv01.u32[3] += rte_cpu_to_be_16(0x0100);
-   priv23.u32[1] += rte_cpu_to_be_16(0x0100);
-   priv23.u32[3] += rte_cpu_to_be_16(0x0100);
-
-   /* Update ttl,cksum rewrite ethernet hdr on mbuf0 */
-   d0 = rte_pktmbuf_mtod(mbuf0, void *);
-   rte_memcpy(d0, nh[priv01.u16[0]].rewrite_data,
-  nh[priv01.u16[0]].rewrite_len);
-
-   next0 = nh[priv01.u16[0]].tx_node;
-   ip0 = (struct rte_ipv4_hdr *)((uint8_t *)d0 +
- sizeof(struct rte_ether_hdr));
-   ip0->time_to_live = priv01.u16[1] - 1;
-   ip0->hdr_checksum = priv01.u16[2] + priv01.u16[3];
-
-   /* Update ttl,cksum rewrite ethernet hdr on mbuf1 */
-   d1 = rte_pktmbuf_mtod(mbuf1, void *);
-   rte_memcpy(d1, nh[priv01.u16[4]].rewrite_data,
-  nh[priv01.u16[4]].rewrite_len);
-
-   next1 = nh[priv01.u16[4]].tx_node;
-   ip1 = (struct rte_ipv4_hdr *)((uint8_t *)d1 +
- sizeof(struct rte_ether_hdr));
-   ip1->time_to_live = priv01.u16[5] - 1;
-   ip1->hdr_checksum = priv01.u16[6] + priv01.u16[7];
-
-   /* Update ttl,cksum rewrite ethernet hdr on mbuf2 */
-   d2 = rte_pktmbuf_mtod(mbuf2, void *);
-   rte_memcpy(d2, nh[priv23.u16[0]].rewrite_data,
-  nh[priv23.u16[0]].rewrite_len);
-   next2 = nh[priv23.u16[0]].tx_node;
-   ip2 = (struct rte_ipv4_hdr *)((uint8_t *)d2 +
- sizeof(struct rte_ether_hdr));
-   ip2->time_to_live = priv23.u16[1] - 1;
-   ip2->hdr_checksum = priv23.u16[2] + priv23.u16[3];
-
-   /* Update ttl,cksum rewrite ethernet hdr on mbuf3 */
-   d3 = rte_pktmbuf_mtod(mbuf3, void *);
-   rte_memcpy(d3, nh[priv23.u16[4]].rewrite_data,
-  nh[priv23.u16[4]].rewrite_len);
-
-   next3 = nh[priv23.u16[4]].tx_node;
-   ip3 = (struct rte_ipv4_hdr *)((uint8_t *)d3 +
- sizeof(struct rte_ether_hdr));
-  

[RFC PATCH 1/2] graph: add feature arc support

2024-04-26 Thread Nitin Saxena
Signed-off-by: Nitin Saxena 
Change-Id: I8ca9d6285aaeedabc75131af8d28f8b2f63f614b
---
 lib/graph/graph_feature_arc.c| 834 +++
 lib/graph/meson.build|   2 +
 lib/graph/rte_graph_feature_arc.h| 310 +
 lib/graph/rte_graph_feature_arc_worker.h | 483 +
 lib/graph/version.map|  16 +
 5 files changed, 1645 insertions(+)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 00..fc3662727d
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,834 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include 
+#include 
+
+#define __RTE_GRAPH_FEATURE_ARC_MAX 32
+
+#define rte_graph_uint_cast(x) ((unsigned int)x)
+
+rte_graph_feature_arc_main_t *__feature_arc_main;
+
+static int
+feature_lookup(struct rte_graph_feature_arc *dfl, const char *feat_name,
+  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   const char *name;
+
+   if (!feat_name)
+   return -1;
+
+   if (slot)
+   *slot = 0;
+
+   STAILQ_FOREACH(finfo, &dfl->all_features, next_feature) {
+   RTE_VERIFY(finfo->feature_arc == dfl);
+   name = rte_node_id_to_name(finfo->feature_node->id);
+   if (!strncmp(name, feat_name, RTE_GRAPH_NAMESIZE)) {
+   if (ffinfo)
+   *ffinfo = finfo;
+   return 0;
+   }
+   if (slot)
+   (*slot)++;
+   }
+   return -1;
+}
+
+static int
+feature_arc_lookup(rte_graph_feature_arc_t _dfl)
+{
+   struct rte_graph_feature_arc *dfl = rte_graph_feature_arc_get(_dfl);
+   rte_graph_feature_arc_main_t *dm = __feature_arc_main;
+   uint32_t iter;
+
+   if (!__feature_arc_main)
+   return -1;
+
+   for (iter = 0; iter < dm->max_feature_arcs; iter++) {
+   if (dm->feature_arcs[iter] == RTE_GRAPH_FEATURE_ARC_INITIALIZER)
+   continue;
+
+   if (dfl == (rte_graph_feature_arc_get(dm->feature_arcs[iter])))
+   return 0;
+   }
+   return -1;
+}
+
+static int
+get_existing_edge(const char *arc_name, struct rte_node_register *parent_node,
+ struct rte_node_register *child_node, rte_edge_t *_edge)
+{
+   char **next_edges = NULL;
+   uint32_t count, i;
+
+   RTE_SET_USED(arc_name);
+
+   count = rte_node_edge_get(parent_node->id, NULL);
+   next_edges = malloc(count);
+
+   if (!next_edges)
+   return -1;
+
+   count = rte_node_edge_get(parent_node->id, next_edges);
+   for (i = 0; i < count; i++) {
+   if (strstr(child_node->name, next_edges[i])) {
+   graph_dbg("%s: Edge exists [%s[%u]: \"%s\"]", arc_name,
+ parent_node->name, i, child_node->name);
+   if (_edge)
+   *_edge = (rte_edge_t)i;
+
+   free(next_edges);
+   return 0;
+   }
+   }
+   free(next_edges);
+
+   return -1;
+}
+
+static int
+connect_graph_nodes(struct rte_node_register *parent_node, struct 
rte_node_register *child_node,
+   rte_edge_t *_edge, char *arc_name)
+{
+   const char *next_node = NULL;
+   rte_edge_t edge;
+
+   if (!get_existing_edge(arc_name, parent_node, child_node, &edge)) {
+   graph_dbg("%s: add_feature: Edge reused [%s[%u]: \"%s\"]", 
arc_name,
+   parent_node->name, edge, child_node->name);
+
+   if (_edge)
+   *_edge = edge;
+
+   return 0;
+   }
+
+   /* Node to be added */
+   next_node = child_node->name;
+
+   edge = rte_node_edge_update(parent_node->id, RTE_EDGE_ID_INVALID, 
&next_node, 1);
+
+   if (edge == RTE_EDGE_ID_INVALID) {
+   graph_err("edge invalid");
+   return -1;
+   }
+   edge = rte_node_edge_count(parent_node->id) - 1;
+
+   graph_dbg("%s: add_feature: edge added [%s[%u]: \"%s\"]", arc_name, 
parent_node->name, edge,
+   child_node->name);
+
+   if (_edge)
+   *_edge = edge;
+
+   return 0;
+}
+
+static int
+feature_arc_init(rte_graph_feature_arc_main_t **pfl, uint32_t max_feature_arcs)
+{
+   rte_graph_feature_arc_main_t *pm = NULL;
+   uint32_t i;
+   size_t sz;
+
+  

[RFC PATCH 0/2] add feature arc in rte_graph

2024-04-26 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to connect various
rte_graph nodes, as feature nodes, and allow packets steering across
these nodes in a generic manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
policy lookup. On the other hand, packets routed to another interface
(eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
is disabled on eth1. Feature-arc allows rte_graph applications to manage
such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Seamlessly steer packets across feature nodes based on wheter feature
is enabled or disabled on an interface. Features enabled on one
interface may not be enabled on another interface with in a same feature
arc.

2. Allow enabling/disabling of features on an interface at runtime,
so that if a feature is disabled, packets associated with that interface
won't be steered to corresponding feature node.

3. Provides mechanism to hook custom/user-defined nodes to a feature
node and allow packet steering from feature node to custom node without
changing former's fast path function

4. Allow expressing features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Nitin Saxena (2):
  graph: add feature arc support
  graph: add ip4 output feature arc

 lib/graph/graph_feature_arc.c| 834 +++
 lib/graph/meson.build|   2 +
 lib/graph/rte_graph_feature_arc.h| 310 +
 lib/graph/rte_graph_feature_arc_worker.h | 483 +
 lib/graph/version.map|  16 +
 lib/node/ip4_rewrite.c   | 278 ++--
 lib/node/ip4_rewrite_priv.h  |  10 +-
 lib/node/node_private.h  |  10 +-
 lib/node/rte_node_ip4_api.h  |   3 +
 9 files changed, 1878 insertions(+), 68 deletions(-)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

-- 
2.25.1



[RFC PATCH 0/3] add feature arc in rte_graph

2024-09-07 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to connect various
rte_graph nodes, as feature nodes, and allow packets steering across
these nodes in a generic manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
policy lookup. On the other hand, packets routed to another interface
(eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
is disabled on eth1. Feature-arc allows rte_graph applications to manage
such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Seamlessly steer packets across feature nodes based on wheter feature
is enabled or disabled on an interface. Features enabled on one
interface may not be enabled on another interface with in a same feature
arc.

2. Allow enabling/disabling of features on an interface at runtime,
so that if a feature is disabled, packets associated with that interface
won't be steered to corresponding feature node.

3. Provides mechanism to hook custom/user-defined nodes to a feature
node and allow packet steering from feature node to custom node without
changing former's fast path function

4. Allow expressing features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Nitin Saxena (3):
  graph: add feature arc support
  graph: add feature arc option in graph create
  graph: add IPv4 output feature arc

 lib/graph/graph.c|   1 +
 lib/graph/graph_feature_arc.c| 959 +++
 lib/graph/graph_populate.c   |   7 +-
 lib/graph/graph_private.h|   3 +
 lib/graph/meson.build|   2 +
 lib/graph/node.c |   2 +
 lib/graph/rte_graph.h|   3 +
 lib/graph/rte_graph_feature_arc.h| 373 +
 lib/graph/rte_graph_feature_arc_worker.h | 548 +
 lib/graph/version.map|  17 +
 lib/node/ip4_rewrite.c   | 476 ---
 lib/node/ip4_rewrite_priv.h  |   9 +-
 lib/node/node_private.h  |  19 +-
 lib/node/rte_node_ip4_api.h  |   3 +
 14 files changed, 2325 insertions(+), 97 deletions(-)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

-- 
2.43.0



[RFC PATCH 1/3] graph: add feature arc support

2024-09-07 Thread Nitin Saxena
add feature arc to allow dynamic steering of packets across graph nodes
based on protocol features enabled on incoming or outgoing interface

Signed-off-by: Nitin Saxena 
---
 lib/graph/graph_feature_arc.c| 959 +++
 lib/graph/meson.build|   2 +
 lib/graph/rte_graph_feature_arc.h| 373 +
 lib/graph/rte_graph_feature_arc_worker.h | 548 +
 lib/graph/version.map|  17 +
 5 files changed, 1899 insertions(+)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 00..3b05bac137
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,959 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include 
+#include 
+
+#define __RTE_GRAPH_FEATURE_ARC_MAX 32
+
+#define ARC_PASSIVE_LIST(arc) (arc->active_feature_list ^ 0x1)
+
+#define rte_graph_uint_cast(x) ((unsigned int)x)
+#define feat_dbg graph_err
+
+rte_graph_feature_arc_main_t *__feature_arc_main;
+
+/* Make sure fast path cache line is compact */
+_Static_assert((offsetof(struct rte_graph_feature_arc, slow_path_variables)
+   - offsetof(struct rte_graph_feature_arc, fast_path_variables))
+  <= RTE_CACHE_LINE_SIZE);
+
+
+static int
+feature_lookup(struct rte_graph_feature_arc *arc, const char *feat_name,
+  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   const char *name;
+
+   if (!feat_name)
+   return -1;
+
+   if (slot)
+   *slot = 0;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   RTE_VERIFY(finfo->feature_arc == arc);
+   name = rte_node_id_to_name(finfo->feature_node->id);
+   if (!strncmp(name, feat_name, RTE_GRAPH_NAMESIZE)) {
+   if (ffinfo)
+   *ffinfo = finfo;
+   return 0;
+   }
+   if (slot)
+   (*slot)++;
+   }
+   return -1;
+}
+
+static int
+feature_arc_node_info_lookup(struct rte_graph_feature_arc *arc, uint32_t 
feature_index,
+struct rte_graph_feature_node_list **ppfinfo)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   uint32_t index = 0;
+
+   if (!ppfinfo)
+   return -1;
+
+   *ppfinfo = NULL;
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   if (index == feature_index) {
+   if (finfo->node_index == feature_index)
+   return -1;
+   *ppfinfo = finfo;
+   }
+   index++;
+   }
+   if (feature_index && (index >= feature_index))
+   return -1;
+
+   return 0;
+}
+
+static void
+prepare_feature_arc(struct rte_graph_feature_arc *arc)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   uint32_t index = 0;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   finfo->node_index = index;
+   index++;
+   }
+}
+
+static int
+feature_arc_lookup(rte_graph_feature_arc_t _arc)
+{
+   struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
+   rte_graph_feature_arc_main_t *dm = __feature_arc_main;
+   uint32_t iter;
+
+   if (!__feature_arc_main)
+   return -1;
+
+   for (iter = 0; iter < dm->max_feature_arcs; iter++) {
+   if (dm->feature_arcs[iter] == RTE_GRAPH_FEATURE_ARC_INITIALIZER)
+   continue;
+
+   if (arc == (rte_graph_feature_arc_get(dm->feature_arcs[iter])))
+   return 0;
+   }
+   return -1;
+}
+
+static int
+get_existing_edge(const char *arc_name, struct rte_node_register *parent_node,
+ struct rte_node_register *child_node, rte_edge_t *_edge)
+{
+   char **next_edges = NULL;
+   uint32_t count, i;
+
+   RTE_SET_USED(arc_name);
+
+   count = rte_node_edge_get(parent_node->id, NULL);
+   next_edges = malloc(count);
+
+   if (!next_edges)
+   return -1;
+
+   count = rte_node_edge_get(parent_node->id, next_edges);
+   for (i = 0; i < count; i++) {
+   if (strstr(child_node->name, next_edges[i])) {
+   feat_dbg("%s: Edge exists [%s[%u]: \"%s\"]", arc_name,
+ parent_node->name, i, child_node->name);
+   if (_edge)
+   *_edge = (rte_edge_t)i;
+
+ 

[RFC PATCH 2/3] graph: add feature arc option in graph create

2024-09-07 Thread Nitin Saxena
Added option in graph create to call feature-specific process node
functions. This removes extra overhead for checking feature arc status
in nodes where application is not using feature arc processing

Signed-off-by: Pavan Nikhilesh 
---
 lib/graph/graph.c  | 1 +
 lib/graph/graph_populate.c | 7 ++-
 lib/graph/graph_private.h  | 3 +++
 lib/graph/node.c   | 2 ++
 lib/graph/rte_graph.h  | 3 +++
 5 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/lib/graph/graph.c b/lib/graph/graph.c
index d5b8c9f918..b0ad3a83ae 100644
--- a/lib/graph/graph.c
+++ b/lib/graph/graph.c
@@ -455,6 +455,7 @@ rte_graph_create(const char *name, struct rte_graph_param 
*prm)
graph->parent_id = RTE_GRAPH_ID_INVALID;
graph->lcore_id = RTE_MAX_LCORE;
graph->num_pkt_to_capture = prm->num_pkt_to_capture;
+   graph->feature_arc_enabled = prm->feature_arc_enable;
if (prm->pcap_filename)
rte_strscpy(graph->pcap_filename, prm->pcap_filename, 
RTE_GRAPH_PCAP_FILE_SZ);
 
diff --git a/lib/graph/graph_populate.c b/lib/graph/graph_populate.c
index ed596a7711..2892f3cce2 100644
--- a/lib/graph/graph_populate.c
+++ b/lib/graph/graph_populate.c
@@ -79,8 +79,13 @@ graph_nodes_populate(struct graph *_graph)
if (graph_pcap_is_enable()) {
node->process = graph_pcap_dispatch;
node->original_process = graph_node->node->process;
-   } else
+   if (_graph->feature_arc_enabled)
+   node->original_process = 
graph_node->node->feat_arc_proc;
+   } else {
node->process = graph_node->node->process;
+   if (_graph->feature_arc_enabled)
+   node->process = graph_node->node->feat_arc_proc;
+   }
memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
pid = graph_node->node->parent_id;
if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
index d557d55f2d..58ba0abeff 100644
--- a/lib/graph/graph_private.h
+++ b/lib/graph/graph_private.h
@@ -56,6 +56,7 @@ struct node {
unsigned int lcore_id;
/**< Node runs on the Lcore ID used for mcore dispatch model. */
rte_node_process_t process;   /**< Node process function. */
+   rte_node_process_t feat_arc_proc; /**< Node feature-arch process 
function. */
rte_node_init_t init; /**< Node init function. */
rte_node_fini_t fini; /**< Node fini function. */
rte_node_t id;/**< Allocated identifier for the node. */
@@ -126,6 +127,8 @@ struct graph {
/**< Number of packets to be captured per core. */
char pcap_filename[RTE_GRAPH_PCAP_FILE_SZ];
/**< pcap file name/path. */
+   uint8_t feature_arc_enabled;
+   /**< Graph feature arc. */
STAILQ_HEAD(gnode_list, graph_node) node_list;
/**< Nodes in a graph. */
 };
diff --git a/lib/graph/node.c b/lib/graph/node.c
index 99a9622779..d8fd273543 100644
--- a/lib/graph/node.c
+++ b/lib/graph/node.c
@@ -90,6 +90,7 @@ __rte_node_register(const struct rte_node_register *reg)
goto free;
node->flags = reg->flags;
node->process = reg->process;
+   node->feat_arc_proc = reg->feat_arc_proc;
node->init = reg->init;
node->fini = reg->fini;
node->nb_edges = reg->nb_edges;
@@ -137,6 +138,7 @@ node_clone(struct node *node, const char *name)
/* Clone the source node */
reg->flags = node->flags;
reg->process = node->process;
+   reg->feat_arc_proc = node->feat_arc_proc;
reg->init = node->init;
reg->fini = node->fini;
reg->nb_edges = node->nb_edges;
diff --git a/lib/graph/rte_graph.h b/lib/graph/rte_graph.h
index ecfec2068a..ebbdbbea48 100644
--- a/lib/graph/rte_graph.h
+++ b/lib/graph/rte_graph.h
@@ -163,6 +163,8 @@ struct rte_graph_param {
uint64_t num_pkt_to_capture; /**< Number of packets to capture. */
char *pcap_filename; /**< Filename in which packets to be captured.*/
 
+   bool feature_arc_enable; /**< Enable Graph feature arc. */
+
union {
struct {
uint64_t rsvd; /**< Reserved for rtc model. */
@@ -470,6 +472,7 @@ struct rte_node_register {
uint64_t flags;   /**< Node configuration flag. */
 #define RTE_NODE_SOURCE_F (1ULL << 0) /**< Node type is source. */
rte_node_process_t process; /**< Node process function. */
+   rte_node_process_t feat_arc_proc; /**< Node feature-arch process 
function. */
rte_node_init_t init;   /**< Node init function. */
rte_node_fini_t fini;   /**< Node fini function. */
rte_node_t id;  /**< Node Identifier. */
-- 
2.43.0



[RFC PATCH 3/3] graph: add IPv4 output feature arc

2024-09-07 Thread Nitin Saxena
add ipv4-output feature arc in ipv4-rewrite node to allow
custom/standard nodes(like outbound IPsec policy node) in outgoing
forwarding path

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_rewrite.c  | 476 +---
 lib/node/ip4_rewrite_priv.h |   9 +-
 lib/node/node_private.h |  19 +-
 lib/node/rte_node_ip4_api.h |   3 +
 4 files changed, 411 insertions(+), 96 deletions(-)

diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index 34a920df5e..916f180a3d 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -15,39 +15,156 @@
 #include "ip4_rewrite_priv.h"
 #include "node_private.h"
 
+#define ALL_PKT_MASK 0xf
+
 struct ip4_rewrite_node_ctx {
+   rte_graph_feature_arc_t output_feature_arc;
/* Dynamic offset to mbuf priv1 */
int mbuf_priv1_off;
/* Cached next index */
uint16_t next_index;
+   uint16_t last_tx;
 };
 
+typedef struct rewrite_priv_vars {
+   union {
+   struct {
+   rte_xmm_t xmm1;
+   };
+   struct __rte_packed {
+   uint16_t next0;
+   uint16_t next1;
+   uint16_t next2;
+   uint16_t next3;
+   uint16_t last_tx_interface;
+   uint16_t last_if_feature;
+   uint16_t actual_feat_mask;
+   uint16_t speculative_feat_mask;
+   };
+   };
+} rewrite_priv_vars_t;
+
 static struct ip4_rewrite_node_main *ip4_rewrite_nm;
 
 #define IP4_REWRITE_NODE_LAST_NEXT(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->next_index)
 
+#define IP4_REWRITE_NODE_LAST_TX(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->last_tx)
+
 #define IP4_REWRITE_NODE_PRIV1_OFF(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->mbuf_priv1_off)
 
-static uint16_t
-ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
-void **objs, uint16_t nb_objs)
+#define IP4_REWRITE_NODE_OUTPUT_FEATURE_ARC(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->output_feature_arc)
+
+static __rte_always_inline void
+prefetch_mbuf_and_dynfield(struct rte_mbuf *mbuf)
 {
+   /* prefetch first cache line required for accessing buf_addr */
+   rte_prefetch0((void *)mbuf);
+}
+
+static __rte_always_inline void
+check_output_feature_x4(struct rte_graph_feature_arc *arc,
+   const rte_graph_feature_rt_list_t flist,
+   rewrite_priv_vars_t *pvar, struct node_mbuf_priv1 
*priv0,
+   struct node_mbuf_priv1 *priv1, struct node_mbuf_priv1 
*priv2,
+   struct node_mbuf_priv1 *priv3)
+{
+   uint32_t mask = 0;
+   uint16_t xor = 0;
+
+   /*
+* interface edge's start from 1 and not from 0 as "pkt_drop"
+* is next node at 0th index
+*/
+   priv0->if_index = pvar->next0 - 1;
+   priv1->if_index = pvar->next1 - 1;
+   priv2->if_index = pvar->next2 - 1;
+   priv3->if_index = pvar->next3 - 1;
+
+   /* Find out if all packets are sent to last_tx_interface */
+   xor = pvar->last_tx_interface ^ priv0->if_index;
+   xor += priv0->if_index ^ priv1->if_index;
+   xor += priv1->if_index ^ priv2->if_index;
+   xor += priv2->if_index ^ priv3->if_index;
+
+   if (likely(!xor)) {
+   /* copy last interface feature and feature mask */
+   priv0->current_feature = priv1->current_feature =
+   priv2->current_feature = priv3->current_feature =
+   pvar->last_if_feature;
+   pvar->actual_feat_mask = pvar->speculative_feat_mask;
+   } else {
+   /* create a mask for index which does not have feature
+* Also override next edge and if feature enabled, get feature
+*/
+   mask = rte_graph_feature_arc_feature_set(arc, flist, 
priv0->if_index,
+
&priv0->current_feature,
+&pvar->next0);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv1->if_index,
+
&priv1->current_feature,
+&pvar->next1)) << 
1);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv2->if_index,
+
&priv2->current_feature,
+&pvar->next2)) << 
2);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv3->if_index,
+   

[RFC PATCH 1/3] graph: add feature arc support

2024-09-07 Thread Nitin Saxena
add feature arc to allow dynamic steering of packets across graph nodes
based on protocol features enabled on incoming or outgoing interface

Signed-off-by: Nitin Saxena 
---
 lib/graph/graph_feature_arc.c| 959 +++
 lib/graph/meson.build|   2 +
 lib/graph/rte_graph_feature_arc.h| 373 +
 lib/graph/rte_graph_feature_arc_worker.h | 548 +
 lib/graph/version.map|  17 +
 5 files changed, 1899 insertions(+)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 00..3b05bac137
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,959 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include 
+#include 
+
+#define __RTE_GRAPH_FEATURE_ARC_MAX 32
+
+#define ARC_PASSIVE_LIST(arc) (arc->active_feature_list ^ 0x1)
+
+#define rte_graph_uint_cast(x) ((unsigned int)x)
+#define feat_dbg graph_err
+
+rte_graph_feature_arc_main_t *__feature_arc_main;
+
+/* Make sure fast path cache line is compact */
+_Static_assert((offsetof(struct rte_graph_feature_arc, slow_path_variables)
+   - offsetof(struct rte_graph_feature_arc, fast_path_variables))
+  <= RTE_CACHE_LINE_SIZE);
+
+
+static int
+feature_lookup(struct rte_graph_feature_arc *arc, const char *feat_name,
+  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   const char *name;
+
+   if (!feat_name)
+   return -1;
+
+   if (slot)
+   *slot = 0;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   RTE_VERIFY(finfo->feature_arc == arc);
+   name = rte_node_id_to_name(finfo->feature_node->id);
+   if (!strncmp(name, feat_name, RTE_GRAPH_NAMESIZE)) {
+   if (ffinfo)
+   *ffinfo = finfo;
+   return 0;
+   }
+   if (slot)
+   (*slot)++;
+   }
+   return -1;
+}
+
+static int
+feature_arc_node_info_lookup(struct rte_graph_feature_arc *arc, uint32_t 
feature_index,
+struct rte_graph_feature_node_list **ppfinfo)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   uint32_t index = 0;
+
+   if (!ppfinfo)
+   return -1;
+
+   *ppfinfo = NULL;
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   if (index == feature_index) {
+   if (finfo->node_index == feature_index)
+   return -1;
+   *ppfinfo = finfo;
+   }
+   index++;
+   }
+   if (feature_index && (index >= feature_index))
+   return -1;
+
+   return 0;
+}
+
+static void
+prepare_feature_arc(struct rte_graph_feature_arc *arc)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   uint32_t index = 0;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   finfo->node_index = index;
+   index++;
+   }
+}
+
+static int
+feature_arc_lookup(rte_graph_feature_arc_t _arc)
+{
+   struct rte_graph_feature_arc *arc = rte_graph_feature_arc_get(_arc);
+   rte_graph_feature_arc_main_t *dm = __feature_arc_main;
+   uint32_t iter;
+
+   if (!__feature_arc_main)
+   return -1;
+
+   for (iter = 0; iter < dm->max_feature_arcs; iter++) {
+   if (dm->feature_arcs[iter] == RTE_GRAPH_FEATURE_ARC_INITIALIZER)
+   continue;
+
+   if (arc == (rte_graph_feature_arc_get(dm->feature_arcs[iter])))
+   return 0;
+   }
+   return -1;
+}
+
+static int
+get_existing_edge(const char *arc_name, struct rte_node_register *parent_node,
+ struct rte_node_register *child_node, rte_edge_t *_edge)
+{
+   char **next_edges = NULL;
+   uint32_t count, i;
+
+   RTE_SET_USED(arc_name);
+
+   count = rte_node_edge_get(parent_node->id, NULL);
+   next_edges = malloc(count);
+
+   if (!next_edges)
+   return -1;
+
+   count = rte_node_edge_get(parent_node->id, next_edges);
+   for (i = 0; i < count; i++) {
+   if (strstr(child_node->name, next_edges[i])) {
+   feat_dbg("%s: Edge exists [%s[%u]: \"%s\"]", arc_name,
+ parent_node->name, i, child_node->name);
+   if (_edge)
+   *_edge = (rte_edge_t)i;
+
+ 

[RFC PATCH 3/3] graph: add IPv4 output feature arc

2024-09-07 Thread Nitin Saxena
add ipv4-output feature arc in ipv4-rewrite node to allow
custom/standard nodes(like outbound IPsec policy node) in outgoing
forwarding path

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_rewrite.c  | 476 +---
 lib/node/ip4_rewrite_priv.h |   9 +-
 lib/node/node_private.h |  19 +-
 lib/node/rte_node_ip4_api.h |   3 +
 4 files changed, 411 insertions(+), 96 deletions(-)

diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index 34a920df5e..916f180a3d 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -15,39 +15,156 @@
 #include "ip4_rewrite_priv.h"
 #include "node_private.h"
 
+#define ALL_PKT_MASK 0xf
+
 struct ip4_rewrite_node_ctx {
+   rte_graph_feature_arc_t output_feature_arc;
/* Dynamic offset to mbuf priv1 */
int mbuf_priv1_off;
/* Cached next index */
uint16_t next_index;
+   uint16_t last_tx;
 };
 
+typedef struct rewrite_priv_vars {
+   union {
+   struct {
+   rte_xmm_t xmm1;
+   };
+   struct __rte_packed {
+   uint16_t next0;
+   uint16_t next1;
+   uint16_t next2;
+   uint16_t next3;
+   uint16_t last_tx_interface;
+   uint16_t last_if_feature;
+   uint16_t actual_feat_mask;
+   uint16_t speculative_feat_mask;
+   };
+   };
+} rewrite_priv_vars_t;
+
 static struct ip4_rewrite_node_main *ip4_rewrite_nm;
 
 #define IP4_REWRITE_NODE_LAST_NEXT(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->next_index)
 
+#define IP4_REWRITE_NODE_LAST_TX(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->last_tx)
+
 #define IP4_REWRITE_NODE_PRIV1_OFF(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->mbuf_priv1_off)
 
-static uint16_t
-ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
-void **objs, uint16_t nb_objs)
+#define IP4_REWRITE_NODE_OUTPUT_FEATURE_ARC(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->output_feature_arc)
+
+static __rte_always_inline void
+prefetch_mbuf_and_dynfield(struct rte_mbuf *mbuf)
 {
+   /* prefetch first cache line required for accessing buf_addr */
+   rte_prefetch0((void *)mbuf);
+}
+
+static __rte_always_inline void
+check_output_feature_x4(struct rte_graph_feature_arc *arc,
+   const rte_graph_feature_rt_list_t flist,
+   rewrite_priv_vars_t *pvar, struct node_mbuf_priv1 
*priv0,
+   struct node_mbuf_priv1 *priv1, struct node_mbuf_priv1 
*priv2,
+   struct node_mbuf_priv1 *priv3)
+{
+   uint32_t mask = 0;
+   uint16_t xor = 0;
+
+   /*
+* interface edge's start from 1 and not from 0 as "pkt_drop"
+* is next node at 0th index
+*/
+   priv0->if_index = pvar->next0 - 1;
+   priv1->if_index = pvar->next1 - 1;
+   priv2->if_index = pvar->next2 - 1;
+   priv3->if_index = pvar->next3 - 1;
+
+   /* Find out if all packets are sent to last_tx_interface */
+   xor = pvar->last_tx_interface ^ priv0->if_index;
+   xor += priv0->if_index ^ priv1->if_index;
+   xor += priv1->if_index ^ priv2->if_index;
+   xor += priv2->if_index ^ priv3->if_index;
+
+   if (likely(!xor)) {
+   /* copy last interface feature and feature mask */
+   priv0->current_feature = priv1->current_feature =
+   priv2->current_feature = priv3->current_feature =
+   pvar->last_if_feature;
+   pvar->actual_feat_mask = pvar->speculative_feat_mask;
+   } else {
+   /* create a mask for index which does not have feature
+* Also override next edge and if feature enabled, get feature
+*/
+   mask = rte_graph_feature_arc_feature_set(arc, flist, 
priv0->if_index,
+
&priv0->current_feature,
+&pvar->next0);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv1->if_index,
+
&priv1->current_feature,
+&pvar->next1)) << 
1);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv2->if_index,
+
&priv2->current_feature,
+&pvar->next2)) << 
2);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv3->if_index,
+   

[RFC PATCH 0/3] add featur arc in rte_graph

2024-09-07 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to connect various
rte_graph nodes, as feature nodes, and allow packets steering across
these nodes in a generic manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
policy lookup. On the other hand, packets routed to another interface
(eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
is disabled on eth1. Feature-arc allows rte_graph applications to manage
such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Seamlessly steer packets across feature nodes based on wheter feature
is enabled or disabled on an interface. Features enabled on one
interface may not be enabled on another interface with in a same feature
arc.

2. Allow enabling/disabling of features on an interface at runtime,
so that if a feature is disabled, packets associated with that interface
won't be steered to corresponding feature node.

3. Provides mechanism to hook custom/user-defined nodes to a feature
node and allow packet steering from feature node to custom node without
changing former's fast path function

4. Allow expressing features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Nitin Saxena (3):
  graph: add feature arc support
  graph: add feature arc option in graph create
  graph: add IPv4 output feature arc

 lib/graph/graph.c|   1 +
 lib/graph/graph_feature_arc.c| 959 +++
 lib/graph/graph_populate.c   |   7 +-
 lib/graph/graph_private.h|   3 +
 lib/graph/meson.build|   2 +
 lib/graph/node.c |   2 +
 lib/graph/rte_graph.h|   3 +
 lib/graph/rte_graph_feature_arc.h| 373 +
 lib/graph/rte_graph_feature_arc_worker.h | 548 +
 lib/graph/version.map|  17 +
 lib/node/ip4_rewrite.c   | 476 ---
 lib/node/ip4_rewrite_priv.h  |   9 +-
 lib/node/node_private.h  |  19 +-
 lib/node/rte_node_ip4_api.h  |   3 +
 14 files changed, 2325 insertions(+), 97 deletions(-)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

-- 
2.43.0



[RFC PATCH 2/3] graph: add feature arc option in graph create

2024-09-07 Thread Nitin Saxena
Added option in graph create to call feature-specific process node
functions. This removes extra overhead for checking feature arc status
in nodes where application is not using feature arc processing

Signed-off-by: Pavan Nikhilesh 
---
 lib/graph/graph.c  | 1 +
 lib/graph/graph_populate.c | 7 ++-
 lib/graph/graph_private.h  | 3 +++
 lib/graph/node.c   | 2 ++
 lib/graph/rte_graph.h  | 3 +++
 5 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/lib/graph/graph.c b/lib/graph/graph.c
index d5b8c9f918..b0ad3a83ae 100644
--- a/lib/graph/graph.c
+++ b/lib/graph/graph.c
@@ -455,6 +455,7 @@ rte_graph_create(const char *name, struct rte_graph_param 
*prm)
graph->parent_id = RTE_GRAPH_ID_INVALID;
graph->lcore_id = RTE_MAX_LCORE;
graph->num_pkt_to_capture = prm->num_pkt_to_capture;
+   graph->feature_arc_enabled = prm->feature_arc_enable;
if (prm->pcap_filename)
rte_strscpy(graph->pcap_filename, prm->pcap_filename, 
RTE_GRAPH_PCAP_FILE_SZ);
 
diff --git a/lib/graph/graph_populate.c b/lib/graph/graph_populate.c
index ed596a7711..2892f3cce2 100644
--- a/lib/graph/graph_populate.c
+++ b/lib/graph/graph_populate.c
@@ -79,8 +79,13 @@ graph_nodes_populate(struct graph *_graph)
if (graph_pcap_is_enable()) {
node->process = graph_pcap_dispatch;
node->original_process = graph_node->node->process;
-   } else
+   if (_graph->feature_arc_enabled)
+   node->original_process = 
graph_node->node->feat_arc_proc;
+   } else {
node->process = graph_node->node->process;
+   if (_graph->feature_arc_enabled)
+   node->process = graph_node->node->feat_arc_proc;
+   }
memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
pid = graph_node->node->parent_id;
if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
index d557d55f2d..58ba0abeff 100644
--- a/lib/graph/graph_private.h
+++ b/lib/graph/graph_private.h
@@ -56,6 +56,7 @@ struct node {
unsigned int lcore_id;
/**< Node runs on the Lcore ID used for mcore dispatch model. */
rte_node_process_t process;   /**< Node process function. */
+   rte_node_process_t feat_arc_proc; /**< Node feature-arch process 
function. */
rte_node_init_t init; /**< Node init function. */
rte_node_fini_t fini; /**< Node fini function. */
rte_node_t id;/**< Allocated identifier for the node. */
@@ -126,6 +127,8 @@ struct graph {
/**< Number of packets to be captured per core. */
char pcap_filename[RTE_GRAPH_PCAP_FILE_SZ];
/**< pcap file name/path. */
+   uint8_t feature_arc_enabled;
+   /**< Graph feature arc. */
STAILQ_HEAD(gnode_list, graph_node) node_list;
/**< Nodes in a graph. */
 };
diff --git a/lib/graph/node.c b/lib/graph/node.c
index 99a9622779..d8fd273543 100644
--- a/lib/graph/node.c
+++ b/lib/graph/node.c
@@ -90,6 +90,7 @@ __rte_node_register(const struct rte_node_register *reg)
goto free;
node->flags = reg->flags;
node->process = reg->process;
+   node->feat_arc_proc = reg->feat_arc_proc;
node->init = reg->init;
node->fini = reg->fini;
node->nb_edges = reg->nb_edges;
@@ -137,6 +138,7 @@ node_clone(struct node *node, const char *name)
/* Clone the source node */
reg->flags = node->flags;
reg->process = node->process;
+   reg->feat_arc_proc = node->feat_arc_proc;
reg->init = node->init;
reg->fini = node->fini;
reg->nb_edges = node->nb_edges;
diff --git a/lib/graph/rte_graph.h b/lib/graph/rte_graph.h
index ecfec2068a..ebbdbbea48 100644
--- a/lib/graph/rte_graph.h
+++ b/lib/graph/rte_graph.h
@@ -163,6 +163,8 @@ struct rte_graph_param {
uint64_t num_pkt_to_capture; /**< Number of packets to capture. */
char *pcap_filename; /**< Filename in which packets to be captured.*/
 
+   bool feature_arc_enable; /**< Enable Graph feature arc. */
+
union {
struct {
uint64_t rsvd; /**< Reserved for rtc model. */
@@ -470,6 +472,7 @@ struct rte_node_register {
uint64_t flags;   /**< Node configuration flag. */
 #define RTE_NODE_SOURCE_F (1ULL << 0) /**< Node type is source. */
rte_node_process_t process; /**< Node process function. */
+   rte_node_process_t feat_arc_proc; /**< Node feature-arch process 
function. */
rte_node_init_t init;   /**< Node init function. */
rte_node_fini_t fini;   /**< Node fini function. */
rte_node_t id;  /**< Node Identifier. */
-- 
2.43.0



[dpdk-dev] [PATCH] net/thunderx: fix MTU configuration for jumbo pkts

2018-04-16 Thread Nitin Saxena
thunderx pmd driver passes dev_info.max_rx_pktlen as
9200 (via rte_eth_dev_info_get()) to application.
But, when application tries to set MTU as
(9200 - sizeof(ethernet_header_t)) the operation fails
because of missing CRC and VLAN additions.

This patch fixes the following for thunderx pmd driver:
 - Sets NIC_HW_MAX_FRS to 9216 (instead of 9200)
 - Sets NIC_HW_MAX_MTU to 9190 (NIC_HW_MAX_FRS - ETH_HLEN
   - ETHER_CRC_LEN - 2*VLAN_HLEN)
 - Sets dev_info->max_rx_pkt_len to NIC_HW_MAX_MTU +
   ETH_HLEN (instead of 9200)
 - Allows rte_eth_dev_set_mtu() to pass if application
   (like VPP) calls rte_eth_dev_set_mtu() before
   rte_eth_dev_start() by putting appropriate check for
   dev->data->dev_started

Fixes: 65d9804edc05 ("net/thunderx: support MTU configuration")

Signed-off-by: Nitin Saxena 
---
 drivers/net/thunderx/base/nicvf_hw_defs.h |  5 -
 drivers/net/thunderx/nicvf_ethdev.c   | 15 +++
 2 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/drivers/net/thunderx/base/nicvf_hw_defs.h 
b/drivers/net/thunderx/base/nicvf_hw_defs.h
index b13c21f..b12c8ec 100644
--- a/drivers/net/thunderx/base/nicvf_hw_defs.h
+++ b/drivers/net/thunderx/base/nicvf_hw_defs.h
@@ -171,7 +171,10 @@
 
 /* Min/Max packet size */
 #define NIC_HW_MIN_FRS  (64)
-#define NIC_HW_MAX_FRS  (9200) /* 9216 max pkt including FCS */
+/* ETH_HLEN+ETH_FCS_LEN+2*VLAN_HLEN */
+#define NIC_HW_L2_OVERHEAD  (26)
+#define NIC_HW_MAX_MTU  (9190)
+#define NIC_HW_MAX_FRS  (NIC_HW_MAX_MTU + NIC_HW_L2_OVERHEAD)
 #define NIC_HW_MAX_SEGS (12)
 
 /* Descriptor alignments */
diff --git a/drivers/net/thunderx/nicvf_ethdev.c 
b/drivers/net/thunderx/nicvf_ethdev.c
index 75e9d16..a7931af 100644
--- a/drivers/net/thunderx/nicvf_ethdev.c
+++ b/drivers/net/thunderx/nicvf_ethdev.c
@@ -162,7 +162,7 @@ static int
 nicvf_dev_set_mtu(struct rte_eth_dev *dev, uint16_t mtu)
 {
struct nicvf *nic = nicvf_pmd_priv(dev);
-   uint32_t buffsz, frame_size = mtu + ETHER_HDR_LEN + ETHER_CRC_LEN;
+   uint32_t buffsz, frame_size = mtu + NIC_HW_L2_OVERHEAD;
size_t i;
struct rte_eth_rxmode *rxmode = &dev->data->dev_conf.rxmode;
 
@@ -180,7 +180,7 @@ nicvf_dev_set_mtu(struct rte_eth_dev *dev, uint16_t mtu)
 * Refuse mtu that requires the support of scattered packets
 * when this feature has not been enabled before.
 */
-   if (!dev->data->scattered_rx &&
+   if (dev->data->dev_started && !dev->data->scattered_rx &&
(frame_size + 2 * VLAN_TAG_SIZE > buffsz))
return -EINVAL;
 
@@ -194,11 +194,11 @@ nicvf_dev_set_mtu(struct rte_eth_dev *dev, uint16_t mtu)
else
rxmode->offloads &= ~DEV_RX_OFFLOAD_JUMBO_FRAME;
 
-   if (nicvf_mbox_update_hw_max_frs(nic, frame_size))
+   if (nicvf_mbox_update_hw_max_frs(nic, mtu))
return -EINVAL;
 
-   /* Update max frame size */
-   rxmode->max_rx_pkt_len = (uint32_t)frame_size;
+   /* Update max_rx_pkt_len */
+   rxmode->max_rx_pkt_len = mtu + ETHER_HDR_LEN;
nic->mtu = mtu;
 
for (i = 0; i < nic->sqs_count; i++)
@@ -1408,7 +1408,7 @@ nicvf_dev_info_get(struct rte_eth_dev *dev, struct 
rte_eth_dev_info *dev_info)
dev_info->speed_capa |= ETH_LINK_SPEED_40G;
 
dev_info->min_rx_bufsize = ETHER_MIN_MTU;
-   dev_info->max_rx_pktlen = NIC_HW_MAX_FRS;
+   dev_info->max_rx_pktlen = NIC_HW_MAX_MTU + ETHER_HDR_LEN;
dev_info->max_rx_queues =
(uint16_t)MAX_RCV_QUEUES_PER_QS * (MAX_SQS_PER_VF + 1);
dev_info->max_tx_queues =
@@ -1741,8 +1741,7 @@ nicvf_dev_start(struct rte_eth_dev *dev)
/* Setup MTU based on max_rx_pkt_len or default */
mtu = dev->data->dev_conf.rxmode.offloads & DEV_RX_OFFLOAD_JUMBO_FRAME ?
dev->data->dev_conf.rxmode.max_rx_pkt_len
-   -  ETHER_HDR_LEN - ETHER_CRC_LEN
-   : ETHER_MTU;
+   -  ETHER_HDR_LEN : ETHER_MTU;
 
if (nicvf_dev_set_mtu(dev, mtu)) {
PMD_INIT_LOG(ERR, "Failed to set default mtu size");
-- 
2.7.4



RE: [EXTERNAL] Re: [RFC PATCH 0/3] add feature arc in rte_graph

2024-10-14 Thread Nitin Saxena
Hi David and all,

>> I see no non-RFC series following this original submission.
>> It will slip to next release unless there is an objection.

I had pushed non RFC patch series before -rc1 date (11th oct). 
We have an ABI change in this patch series 
https://patches.dpdk.org/project/dpdk/patch/20241010133111.2764712-3-nsax...@marvell.com/
Could you help merge this patch series in rc2 otherwise it has to wait for next 
LTS

Thanks,
Nitin

> -Original Message-
> From: David Marchand 
> Sent: Tuesday, October 8, 2024 1:34 PM
> To: Nitin Saxena 
> Cc: Jerin Jacob ; Kiran Kumar Kokkilagadda
> ; Nithin Kumar Dabilpuram
> ; Zhirun Yan ;
> dev@dpdk.org; Nitin Saxena ; Robin Jarry
> ; Christophe Fontaine 
> Subject: [EXTERNAL] Re: [RFC PATCH 0/3] add feature arc in rte_graph
> 
> Hi graph guys, On Sat, Sep 7, 2024 at 9: 31 AM Nitin Saxena
>  wrote: > > Feature arc represents an ordered list of
> features/protocols at a given > networking layer. It is a high level 
> abstraction
> to connect 
> Hi graph guys,
> 
> On Sat, Sep 7, 2024 at 9:31 AM Nitin Saxena  wrote:
> >
> > Feature arc represents an ordered list of features/protocols at a
> > given networking layer. It is a high level abstraction to connect
> > various rte_graph nodes, as feature nodes, and allow packets steering
> > across these nodes in a generic manner.
> >
> > Features (or feature nodes) are nodes which handles partial or
> > complete handling of a protocol in fast path. Like ipv4-rewrite node,
> > which adds rewrite data to an outgoing IPv4 packet.
> >
> > However in above example, outgoing interface(say "eth0") may have
> > outbound IPsec policy enabled, hence packets must be steered from
> > ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
> > policy lookup. On the other hand, packets routed to another interface
> > (eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
> > is disabled on eth1. Feature-arc allows rte_graph applications to
> > manage such constraints easily
> >
> > Feature arc abstraction allows rte_graph based application to
> >
> > 1. Seamlessly steer packets across feature nodes based on wheter
> > feature is enabled or disabled on an interface. Features enabled on
> > one interface may not be enabled on another interface with in a same
> > feature arc.
> >
> > 2. Allow enabling/disabling of features on an interface at runtime, so
> > that if a feature is disabled, packets associated with that interface
> > won't be steered to corresponding feature node.
> >
> > 3. Provides mechanism to hook custom/user-defined nodes to a feature
> > node and allow packet steering from feature node to custom node
> > without changing former's fast path function
> >
> > 4. Allow expressing features in a particular sequential order so that
> > packets are steered in an ordered way across nodes in fast path. For
> > eg: if IPsec and IPv4 features are enabled on an ingress interface,
> > packets must be sent to IPsec inbound policy node first and then to
> > ipv4 lookup node.
> >
> > This patch series adds feature arc library in rte_graph and also adds
> > "ipv4-output" feature arc handling in "ipv4-rewrite" node.
> >
> > Nitin Saxena (3):
> >   graph: add feature arc support
> >   graph: add feature arc option in graph create
> >   graph: add IPv4 output feature arc
> >
> >  lib/graph/graph.c|   1 +
> >  lib/graph/graph_feature_arc.c| 959 +++
> >  lib/graph/graph_populate.c   |   7 +-
> >  lib/graph/graph_private.h|   3 +
> >  lib/graph/meson.build|   2 +
> >  lib/graph/node.c |   2 +
> >  lib/graph/rte_graph.h|   3 +
> >  lib/graph/rte_graph_feature_arc.h| 373 +
> >  lib/graph/rte_graph_feature_arc_worker.h | 548 +
> >  lib/graph/version.map|  17 +
> >  lib/node/ip4_rewrite.c   | 476 ---
> >  lib/node/ip4_rewrite_priv.h  |   9 +-
> >  lib/node/node_private.h  |  19 +-
> >  lib/node/rte_node_ip4_api.h  |   3 +
> >  14 files changed, 2325 insertions(+), 97 deletions(-)  create mode
> > 100644 lib/graph/graph_feature_arc.c  create mode 100644
> > lib/graph/rte_graph_feature_arc.h  create mode 100644
> > lib/graph/rte_graph_feature_arc_worker.h
> 
> I see no non-RFC series following this original submission.
> It will slip to next release unless there is an objection.
> 
> Btw, I suggest copying Robin (and Christophe) for graph related changes.
> 
> 
> --
> David Marchand



RE: [EXTERNAL] Re: [RFC PATCH 0/3] add feature arc in rte_graph

2024-10-08 Thread Nitin Saxena
Hi David,

I just sent v2 version for this patch series.

Will add Robin and Christophe from next version onwards

Thanks,
Nitin

> -Original Message-
> From: David Marchand 
> Sent: Tuesday, October 8, 2024 1:34 PM
> To: Nitin Saxena 
> Cc: Jerin Jacob ; Kiran Kumar Kokkilagadda
> ; Nithin Kumar Dabilpuram
> ; Zhirun Yan ;
> dev@dpdk.org; Nitin Saxena ; Robin Jarry
> ; Christophe Fontaine 
> Subject: [EXTERNAL] Re: [RFC PATCH 0/3] add feature arc in rte_graph
> 
> Hi graph guys, On Sat, Sep 7, 2024 at 9: 31 AM Nitin Saxena
>  wrote: > > Feature arc represents an ordered list of
> features/protocols at a given > networking layer. It is a high level 
> abstraction
> to connect 
> Hi graph guys,
> 
> On Sat, Sep 7, 2024 at 9:31 AM Nitin Saxena  wrote:
> >
> > Feature arc represents an ordered list of features/protocols at a
> > given networking layer. It is a high level abstraction to connect
> > various rte_graph nodes, as feature nodes, and allow packets steering
> > across these nodes in a generic manner.
> >
> > Features (or feature nodes) are nodes which handles partial or
> > complete handling of a protocol in fast path. Like ipv4-rewrite node,
> > which adds rewrite data to an outgoing IPv4 packet.
> >
> > However in above example, outgoing interface(say "eth0") may have
> > outbound IPsec policy enabled, hence packets must be steered from
> > ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
> > policy lookup. On the other hand, packets routed to another interface
> > (eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
> > is disabled on eth1. Feature-arc allows rte_graph applications to
> > manage such constraints easily
> >
> > Feature arc abstraction allows rte_graph based application to
> >
> > 1. Seamlessly steer packets across feature nodes based on wheter
> > feature is enabled or disabled on an interface. Features enabled on
> > one interface may not be enabled on another interface with in a same
> > feature arc.
> >
> > 2. Allow enabling/disabling of features on an interface at runtime, so
> > that if a feature is disabled, packets associated with that interface
> > won't be steered to corresponding feature node.
> >
> > 3. Provides mechanism to hook custom/user-defined nodes to a feature
> > node and allow packet steering from feature node to custom node
> > without changing former's fast path function
> >
> > 4. Allow expressing features in a particular sequential order so that
> > packets are steered in an ordered way across nodes in fast path. For
> > eg: if IPsec and IPv4 features are enabled on an ingress interface,
> > packets must be sent to IPsec inbound policy node first and then to
> > ipv4 lookup node.
> >
> > This patch series adds feature arc library in rte_graph and also adds
> > "ipv4-output" feature arc handling in "ipv4-rewrite" node.
> >
> > Nitin Saxena (3):
> >   graph: add feature arc support
> >   graph: add feature arc option in graph create
> >   graph: add IPv4 output feature arc
> >
> >  lib/graph/graph.c|   1 +
> >  lib/graph/graph_feature_arc.c| 959 +++
> >  lib/graph/graph_populate.c   |   7 +-
> >  lib/graph/graph_private.h|   3 +
> >  lib/graph/meson.build|   2 +
> >  lib/graph/node.c |   2 +
> >  lib/graph/rte_graph.h|   3 +
> >  lib/graph/rte_graph_feature_arc.h| 373 +
> >  lib/graph/rte_graph_feature_arc_worker.h | 548 +
> >  lib/graph/version.map|  17 +
> >  lib/node/ip4_rewrite.c   | 476 ---
> >  lib/node/ip4_rewrite_priv.h  |   9 +-
> >  lib/node/node_private.h  |  19 +-
> >  lib/node/rte_node_ip4_api.h  |   3 +
> >  14 files changed, 2325 insertions(+), 97 deletions(-)  create mode
> > 100644 lib/graph/graph_feature_arc.c  create mode 100644
> > lib/graph/rte_graph_feature_arc.h  create mode 100644
> > lib/graph/rte_graph_feature_arc_worker.h
> 
> I see no non-RFC series following this original submission.
> It will slip to next release unless there is an objection.
> 
> Btw, I suggest copying Robin (and Christophe) for graph related changes.
> 
> 
> --
> David Marchand



[RFC PATCH v2 1/5] graph: add feature arc support

2024-10-08 Thread Nitin Saxena
add feature arc to allow dynamic steering of packets across graph nodes
based on protocol features enabled on incoming or outgoing interface

Signed-off-by: Nitin Saxena 
---
 doc/guides/rel_notes/release_24_11.rst   |   11 +-
 lib/graph/graph_feature_arc.c| 1223 ++
 lib/graph/meson.build|2 +
 lib/graph/rte_graph_feature_arc.h|  429 
 lib/graph/rte_graph_feature_arc_worker.h |  672 
 lib/graph/version.map|   20 +
 6 files changed, 2356 insertions(+), 1 deletion(-)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

diff --git a/doc/guides/rel_notes/release_24_11.rst 
b/doc/guides/rel_notes/release_24_11.rst
index 0ff70d9057..24852aa8e0 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -23,7 +23,6 @@ DPDK Release 24.11
 
 New Features
 
-
 .. This section should contain new features added in this release.
Sample format:
 
@@ -55,6 +54,16 @@ New Features
  Also, make sure to start the actual text at the margin.
  ===
 
+* **Added feature arc abstraction in graph library.**
+
+  Feature arc abstraction helps ``rte_graph`` based applications to steer
+  packets across different node path(s) based on the features (or protocols)
+  enabled on interfaces. Different feature node paths can be enabled/disabled
+  at runtime on some or on all interfaces. This abstraction also help
+  applications to hook their ``custom nodes`` in standard DPDK node paths
+  without any code changes in the later.
+
+  * Added ``ip4-output`` feature arc processing in ``ip4_rewrite`` node.
 
 Removed Items
 -
diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 00..ff99f7b26a
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,1223 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include 
+#include 
+
+#define ARC_PASSIVE_LIST(arc) (arc->active_feature_list ^ 0x1)
+
+#define rte_graph_uint_cast(x) ((unsigned int)x)
+#define feat_dbg graph_dbg
+
+static rte_graph_feature_arc_main_t *__rte_graph_feature_arc_main;
+
+/* Make sure fast path cache line is compact */
+_Static_assert((offsetof(struct rte_graph_feature_arc, slow_path_variables)
+   - offsetof(struct rte_graph_feature_arc, fast_path_variables))
+  <= RTE_CACHE_LINE_SIZE,
+  "Fast path feature arc variables exceed cache line size");
+
+#define connect_graph_nodes(node1, node2, edge, arc_name) \
+   __connect_graph_nodes(node1, node2, edge, arc_name, __LINE__)
+
+#define FEAT_COND_ERR(cond, fmt, ...)  \
+   do {   \
+   if (cond)  \
+   graph_err(fmt, ##__VA_ARGS__); \
+   } while (0)
+
+/*
+ * lookup feature name and get control path node_list as well as feature index
+ * at which it is inserted
+ */
+static int
+feature_lookup(struct rte_graph_feature_arc *arc, const char *feat_name,
+  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   const char *name;
+   uint32_t fi = 0;
+
+   if (!feat_name)
+   return -1;
+
+   if (slot)
+   *slot = UINT32_MAX;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   RTE_VERIFY(finfo->feature_arc == arc);
+   name = rte_node_id_to_name(finfo->feature_node->id);
+   if (!strncmp(name, feat_name, strlen(name))) {
+   if (ffinfo)
+   *ffinfo = finfo;
+   if (slot)
+   *slot = fi;
+   return 0;
+   }
+   fi++;
+   }
+   return -1;
+}
+
+/* Lookup used only during rte_graph_feature_add() */
+static int
+feature_add_lookup(struct rte_graph_feature_arc *arc, const char *feat_name,
+  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   const char *name;
+   uint32_t fi = 0;
+
+   if (!feat_name)
+   return -1;
+
+   if (slot)
+   *slot = 0;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   RTE_VERIFY(finfo->feature_arc == arc);
+   name = rte_node_id_to_name(finfo->feature_node->id);
+   if (!strncmp(name, feat_name, strlen(name))) {
+

[RFC PATCH v2 3/5] graph: add IPv4 output feature arc

2024-10-08 Thread Nitin Saxena
add ipv4-output feature arc in ipv4-rewrite node to allow
custom/standard nodes(like outbound IPsec policy node) in outgoing
forwarding path

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_rewrite.c  | 476 +---
 lib/node/ip4_rewrite_priv.h |  15 +-
 lib/node/node_private.h |  20 +-
 lib/node/rte_node_ip4_api.h |   3 +
 4 files changed, 417 insertions(+), 97 deletions(-)

diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index 34a920df5e..5a160335f2 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -15,39 +15,156 @@
 #include "ip4_rewrite_priv.h"
 #include "node_private.h"
 
+#define ALL_PKT_MASK 0xf
+
 struct ip4_rewrite_node_ctx {
+   rte_graph_feature_arc_t output_feature_arc;
/* Dynamic offset to mbuf priv1 */
int mbuf_priv1_off;
/* Cached next index */
uint16_t next_index;
+   uint16_t last_tx;
 };
 
+typedef struct rewrite_priv_vars {
+   union {
+   struct {
+   rte_xmm_t xmm1;
+   };
+   struct __rte_packed {
+   uint16_t next0;
+   uint16_t next1;
+   uint16_t next2;
+   uint16_t next3;
+   uint16_t last_tx_interface;
+   uint16_t last_if_feature;
+   uint16_t actual_feat_mask;
+   uint16_t speculative_feat_mask;
+   };
+   };
+} rewrite_priv_vars_t;
+
 static struct ip4_rewrite_node_main *ip4_rewrite_nm;
 
 #define IP4_REWRITE_NODE_LAST_NEXT(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->next_index)
 
+#define IP4_REWRITE_NODE_LAST_TX(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->last_tx)
+
 #define IP4_REWRITE_NODE_PRIV1_OFF(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->mbuf_priv1_off)
 
-static uint16_t
-ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
-void **objs, uint16_t nb_objs)
+#define IP4_REWRITE_NODE_OUTPUT_FEATURE_ARC(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->output_feature_arc)
+
+static __rte_always_inline void
+prefetch_mbuf_and_dynfield(struct rte_mbuf *mbuf)
 {
+   /* prefetch first cache line required for accessing buf_addr */
+   rte_prefetch0((void *)mbuf);
+}
+
+static __rte_always_inline void
+check_output_feature_x4(struct rte_graph_feature_arc *arc,
+   const rte_graph_feature_rt_list_t flist,
+   rewrite_priv_vars_t *pvar, struct node_mbuf_priv1 
*priv0,
+   struct node_mbuf_priv1 *priv1, struct node_mbuf_priv1 
*priv2,
+   struct node_mbuf_priv1 *priv3)
+{
+   uint32_t mask = 0;
+   uint16_t xor = 0;
+
+   /*
+* interface edge's start from 1 and not from 0 as "pkt_drop"
+* is next node at 0th index
+*/
+   priv0->if_index = pvar->next0 - 1;
+   priv1->if_index = pvar->next1 - 1;
+   priv2->if_index = pvar->next2 - 1;
+   priv3->if_index = pvar->next3 - 1;
+
+   /* Find out if all packets are sent to last_tx_interface */
+   xor = pvar->last_tx_interface ^ priv0->if_index;
+   xor += priv0->if_index ^ priv1->if_index;
+   xor += priv1->if_index ^ priv2->if_index;
+   xor += priv2->if_index ^ priv3->if_index;
+
+   if (likely(!xor)) {
+   /* copy last interface feature and feature mask */
+   priv0->current_feature = priv1->current_feature =
+   priv2->current_feature = priv3->current_feature =
+   pvar->last_if_feature;
+   pvar->actual_feat_mask = pvar->speculative_feat_mask;
+   } else {
+   /* create a mask for index which does not have feature
+* Also override next edge and if feature enabled, get feature
+*/
+   mask = rte_graph_feature_arc_feature_set(arc, flist, 
priv0->if_index,
+
&priv0->current_feature,
+&pvar->next0);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv1->if_index,
+
&priv1->current_feature,
+&pvar->next1)) << 
1);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv2->if_index,
+
&priv2->current_feature,
+&pvar->next2)) << 
2);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv3->if_index,
+   

[RFC PATCH v2 2/5] graph: add feature arc option in graph create

2024-10-08 Thread Nitin Saxena
Added option in graph create to call feature-specific process node
functions. This removes extra overhead for checking feature arc status
in nodes where application is not using feature arc processing

Signed-off-by: Pavan Nikhilesh 
Signed-off-by: Nitin Saxena 
---
 lib/graph/graph.c  | 1 +
 lib/graph/graph_populate.c | 7 ++-
 lib/graph/graph_private.h  | 3 +++
 lib/graph/node.c   | 2 ++
 lib/graph/rte_graph.h  | 3 +++
 5 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/lib/graph/graph.c b/lib/graph/graph.c
index d5b8c9f918..b0ad3a83ae 100644
--- a/lib/graph/graph.c
+++ b/lib/graph/graph.c
@@ -455,6 +455,7 @@ rte_graph_create(const char *name, struct rte_graph_param 
*prm)
graph->parent_id = RTE_GRAPH_ID_INVALID;
graph->lcore_id = RTE_MAX_LCORE;
graph->num_pkt_to_capture = prm->num_pkt_to_capture;
+   graph->feature_arc_enabled = prm->feature_arc_enable;
if (prm->pcap_filename)
rte_strscpy(graph->pcap_filename, prm->pcap_filename, 
RTE_GRAPH_PCAP_FILE_SZ);
 
diff --git a/lib/graph/graph_populate.c b/lib/graph/graph_populate.c
index ed596a7711..5d8aa7b903 100644
--- a/lib/graph/graph_populate.c
+++ b/lib/graph/graph_populate.c
@@ -79,8 +79,13 @@ graph_nodes_populate(struct graph *_graph)
if (graph_pcap_is_enable()) {
node->process = graph_pcap_dispatch;
node->original_process = graph_node->node->process;
-   } else
+   if (_graph->feature_arc_enabled && 
graph_node->node->feat_arc_proc)
+   node->original_process = 
graph_node->node->feat_arc_proc;
+   } else {
node->process = graph_node->node->process;
+   if (_graph->feature_arc_enabled && 
graph_node->node->feat_arc_proc)
+   node->process = graph_node->node->feat_arc_proc;
+   }
memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
pid = graph_node->node->parent_id;
if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
index d557d55f2d..58ba0abeff 100644
--- a/lib/graph/graph_private.h
+++ b/lib/graph/graph_private.h
@@ -56,6 +56,7 @@ struct node {
unsigned int lcore_id;
/**< Node runs on the Lcore ID used for mcore dispatch model. */
rte_node_process_t process;   /**< Node process function. */
+   rte_node_process_t feat_arc_proc; /**< Node feature-arch process 
function. */
rte_node_init_t init; /**< Node init function. */
rte_node_fini_t fini; /**< Node fini function. */
rte_node_t id;/**< Allocated identifier for the node. */
@@ -126,6 +127,8 @@ struct graph {
/**< Number of packets to be captured per core. */
char pcap_filename[RTE_GRAPH_PCAP_FILE_SZ];
/**< pcap file name/path. */
+   uint8_t feature_arc_enabled;
+   /**< Graph feature arc. */
STAILQ_HEAD(gnode_list, graph_node) node_list;
/**< Nodes in a graph. */
 };
diff --git a/lib/graph/node.c b/lib/graph/node.c
index 99a9622779..d8fd273543 100644
--- a/lib/graph/node.c
+++ b/lib/graph/node.c
@@ -90,6 +90,7 @@ __rte_node_register(const struct rte_node_register *reg)
goto free;
node->flags = reg->flags;
node->process = reg->process;
+   node->feat_arc_proc = reg->feat_arc_proc;
node->init = reg->init;
node->fini = reg->fini;
node->nb_edges = reg->nb_edges;
@@ -137,6 +138,7 @@ node_clone(struct node *node, const char *name)
/* Clone the source node */
reg->flags = node->flags;
reg->process = node->process;
+   reg->feat_arc_proc = node->feat_arc_proc;
reg->init = node->init;
reg->fini = node->fini;
reg->nb_edges = node->nb_edges;
diff --git a/lib/graph/rte_graph.h b/lib/graph/rte_graph.h
index ecfec2068a..ebbdbbea48 100644
--- a/lib/graph/rte_graph.h
+++ b/lib/graph/rte_graph.h
@@ -163,6 +163,8 @@ struct rte_graph_param {
uint64_t num_pkt_to_capture; /**< Number of packets to capture. */
char *pcap_filename; /**< Filename in which packets to be captured.*/
 
+   bool feature_arc_enable; /**< Enable Graph feature arc. */
+
union {
struct {
uint64_t rsvd; /**< Reserved for rtc model. */
@@ -470,6 +472,7 @@ struct rte_node_register {
uint64_t flags;   /**< Node configuration flag. */
 #define RTE_NODE_SOURCE_F (1ULL << 0) /**< Node type is source. */
rte_node_process_t process; /**< Node process funct

[RFC PATCH v2 0/5] add feature arc in rte_graph

2024-10-08 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to connect various
rte_graph nodes, as feature nodes, and allow packets steering across
these nodes in a generic manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
policy lookup. On the other hand, packets routed to another interface
(eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
is disabled on eth1. Feature-arc allows rte_graph applications to manage
such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Seamlessly steer packets across feature nodes based on whether
feature is enabled or disabled on an interface. Features enabled on one
interface may not be enabled on another interface with in a same feature
arc.

2. Allow enabling/disabling of features on an interface at runtime,
so that if a feature is disabled, packets associated with that interface
won't be steered to corresponding feature node.

3. Provides mechanism to hook custom/user-defined nodes to a feature
node and allow packet steering from feature node to custom node without
changing former's fast path function

4. Allow expressing features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Changes in v2:
- Added unit tests for feature arc
- Fixed issues found in testing
- Added new public APIs rte_graph_feature_arc_feature_to_node(),
  rte_graph_feature_arc_feature_to_name(),
  rte_graph_feature_arc_num_features()
- Added programming guide for feature arc
- Added release notes for feature arc

Nitin Saxena (5):
  graph: add feature arc support
  graph: add feature arc option in graph create
  graph: add IPv4 output feature arc
  test/graph_feature_arc: add functional tests
  docs: add programming guide for feature arc

 app/test/meson.build|1 +
 app/test/test_graph_feature_arc.c   | 1415 +++
 doc/guides/prog_guide/graph_lib.rst |  289 
 doc/guides/prog_guide/img/feature_arc-1.jpg |  Bin 0 -> 48984 bytes
 doc/guides/prog_guide/img/feature_arc-2.jpg |  Bin 0 -> 113287 bytes
 doc/guides/prog_guide/img/feature_arc-3.jpg |  Bin 0 -> 93408 bytes
 doc/guides/rel_notes/release_24_11.rst  |   11 +-
 lib/graph/graph.c   |1 +
 lib/graph/graph_feature_arc.c   | 1223 
 lib/graph/graph_populate.c  |7 +-
 lib/graph/graph_private.h   |3 +
 lib/graph/meson.build   |2 +
 lib/graph/node.c|2 +
 lib/graph/rte_graph.h   |3 +
 lib/graph/rte_graph_feature_arc.h   |  429 ++
 lib/graph/rte_graph_feature_arc_worker.h|  672 +
 lib/graph/version.map   |   20 +
 lib/node/ip4_rewrite.c  |  476 +--
 lib/node/ip4_rewrite_priv.h |   15 +-
 lib/node/node_private.h |   20 +-
 lib/node/rte_node_ip4_api.h |3 +
 21 files changed, 4493 insertions(+), 99 deletions(-)
 create mode 100644 app/test/test_graph_feature_arc.c
 create mode 100644 doc/guides/prog_guide/img/feature_arc-1.jpg
 create mode 100644 doc/guides/prog_guide/img/feature_arc-2.jpg
 create mode 100644 doc/guides/prog_guide/img/feature_arc-3.jpg
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

-- 
2.43.0



[RFC PATCH v2 4/5] test/graph_feature_arc: add functional tests

2024-10-08 Thread Nitin Saxena
Added functional unit test case for verifying feature arc control plane
and fast path APIs

How to run:
$ echo "graph_feature_arc_autotest" | ./bin/dpdk-test

Signed-off-by: Nitin Saxena 
---
 app/test/meson.build  |1 +
 app/test/test_graph_feature_arc.c | 1415 +
 2 files changed, 1416 insertions(+)
 create mode 100644 app/test/test_graph_feature_arc.c

diff --git a/app/test/meson.build b/app/test/meson.build
index e29258e6ec..740fa1bfb4 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -90,6 +90,7 @@ source_file_deps = {
 'test_func_reentrancy.c': ['hash', 'lpm'],
 'test_graph.c': ['graph'],
 'test_graph_perf.c': ['graph'],
+'test_graph_feature_arc.c': ['graph'],
 'test_hash.c': ['net', 'hash'],
 'test_hash_functions.c': ['hash'],
 'test_hash_multiwriter.c': ['hash'],
diff --git a/app/test/test_graph_feature_arc.c 
b/app/test/test_graph_feature_arc.c
new file mode 100644
index 00..e185fcd393
--- /dev/null
+++ b/app/test/test_graph_feature_arc.c
@@ -0,0 +1,1415 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "test.h"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#ifndef RTE_EXEC_ENV_WINDOWS
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define MBUFF_SIZE 512
+#define TEST_ARC1_NAME "arc1"
+#define TEST_ARC2_NAME "arc2"
+#define MAX_INDEXES 10
+#define MAX_FEATURES 5
+
+#define SOURCE1 "test_node_arc_source1"
+#define INPUT_STATIC "test_node_arc_input_static"
+#define OUTPUT_STATIC "test_node_arc_output_static"
+#define PKT_FREE_STATIC "test_node_arc_pkt_free_static"
+#define ARC1_FEATURE1 "test_node_arc1_feature1"
+#define ARC1_FEATURE2 "test_node_arc1_feature2"
+#define ARC2_FEATURE1 "test_node_arc2_feature1"
+#define ARC2_FEATURE2 "test_node_arc2_feature2"
+#define ARC2_FEATURE3 "test_node_arc2_feature3"
+#define DUMMY1_STATIC "test_node_arc_dummy1_static"
+#define DUMMY2_STATIC "test_node_arc_dummy2_static"
+
+/* (Node index, Node Name, feature user data base */
+#define FOREACH_TEST_NODE_ARC {\
+   R(0, SOURCE1, 64)   \
+   R(1, INPUT_STATIC, 128) \
+   R(2, OUTPUT_STATIC, 256)\
+   R(3, PKT_FREE_STATIC, 512)  \
+   R(4, ARC1_FEATURE1, 1024)   \
+   R(5, ARC1_FEATURE2, 2048)   \
+   R(6, ARC2_FEATURE1, 4096)   \
+   R(7, ARC2_FEATURE2, 8192)   \
+   R(8, ARC2_FEATURE3, 16384)  \
+   R(9, DUMMY1_STATIC, 32768)  \
+   R(10, DUMMY2_STATIC, 65536) \
+   }
+
+/**
+ * ARC1: Feature arc on ingress interface
+ * ARC2: Feature arc on egress interface
+ * XX_static: Static nodes
+ * XX_featureX: Feature X on arc
+ *
+ *-> ARC1_FEATURE1
+ *   || |
+ *   || v
+ *   ||   ARC1_FEATURE2
+ *   || |
+ *   |v v
+ *  SOURCE1 ->-> INPUT_STATIC --> OUTPUT_STATIC -> PKT_FREE_STATIC
+ * |   |  | ^  ^ ^
+ * |   |  | |  | |
+ * |   |   --> ARC2_FEATURE1   | |
+ * |   |  ^ ^  | |
+ * |   |  | |  | |
+ * |--c-> ARC2_FEATURE2  |
+ * |  | ^|
+ * |  | ||
+ *  --> ARC2_FEATURE3 ---
+ */
+const char *node_names_feature_arc[] = {
+   SOURCE1, INPUT_STATIC, OUTPUT_STATIC, PKT_FREE_STATIC,
+   ARC1_FEATURE1, ARC1_FEATURE2, ARC2_FEATURE1, ARC2_FEATURE2, 
ARC2_FEATURE3,
+   DUMMY1_STATIC, DUMMY2_STATIC
+};
+
+#define MAX_NODES  RTE_DIM(node_names_feature_arc)
+
+/* Function declaraions */
+static uint16_t
+source1_fn(struct rte_graph *graph, struct rte_node *node,
+  void **objs, uint16_t nb_objs);
+static uint16_t
+input_fn(struct rte_graph *graph, struct rte_node *node,
+void **objs, uint16_t nb_objs);
+static uint16_t
+input_fa_fn(struct rte_graph *graph, struct rte_node *node,
+   void **objs, uint16_t nb_objs);
+static uint16_t
+output_fn(struct rte_graph *graph, struct rte_node *node,
+ void **o

[PATCH v3 1/5] graph: add feature arc support

2024-10-09 Thread Nitin Saxena
add feature arc to allow dynamic steering of packets across graph nodes
based on protocol features enabled on incoming or outgoing interface

Signed-off-by: Nitin Saxena 
---
 doc/guides/rel_notes/release_24_11.rst   |   10 +
 lib/graph/graph_feature_arc.c| 1223 ++
 lib/graph/meson.build|2 +
 lib/graph/rte_graph_feature_arc.h|  431 
 lib/graph/rte_graph_feature_arc_worker.h |  674 
 lib/graph/version.map|   20 +
 6 files changed, 2360 insertions(+)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

diff --git a/doc/guides/rel_notes/release_24_11.rst 
b/doc/guides/rel_notes/release_24_11.rst
index e0a9aa55a1..d6d64518e0 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -67,6 +67,16 @@ New Features
 
   The new statistics are useful for debugging and profiling.
 
+* **Added feature arc abstraction in graph library.**
+
+  Feature arc abstraction helps ``rte_graph`` based applications to steer
+  packets across different node path(s) based on the features (or protocols)
+  enabled on interfaces. Different feature node paths can be enabled/disabled
+  at runtime on some or on all interfaces. This abstraction also help
+  applications to hook their ``custom nodes`` in standard DPDK node paths
+  without any code changes in the later.
+
+  * Added ``ip4-output`` feature arc processing in ``ip4_rewrite`` node.
 
 Removed Items
 -
diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 00..ff99f7b26a
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,1223 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include 
+#include 
+
+#define ARC_PASSIVE_LIST(arc) (arc->active_feature_list ^ 0x1)
+
+#define rte_graph_uint_cast(x) ((unsigned int)x)
+#define feat_dbg graph_dbg
+
+static rte_graph_feature_arc_main_t *__rte_graph_feature_arc_main;
+
+/* Make sure fast path cache line is compact */
+_Static_assert((offsetof(struct rte_graph_feature_arc, slow_path_variables)
+   - offsetof(struct rte_graph_feature_arc, fast_path_variables))
+  <= RTE_CACHE_LINE_SIZE,
+  "Fast path feature arc variables exceed cache line size");
+
+#define connect_graph_nodes(node1, node2, edge, arc_name) \
+   __connect_graph_nodes(node1, node2, edge, arc_name, __LINE__)
+
+#define FEAT_COND_ERR(cond, fmt, ...)  \
+   do {   \
+   if (cond)  \
+   graph_err(fmt, ##__VA_ARGS__); \
+   } while (0)
+
+/*
+ * lookup feature name and get control path node_list as well as feature index
+ * at which it is inserted
+ */
+static int
+feature_lookup(struct rte_graph_feature_arc *arc, const char *feat_name,
+  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   const char *name;
+   uint32_t fi = 0;
+
+   if (!feat_name)
+   return -1;
+
+   if (slot)
+   *slot = UINT32_MAX;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   RTE_VERIFY(finfo->feature_arc == arc);
+   name = rte_node_id_to_name(finfo->feature_node->id);
+   if (!strncmp(name, feat_name, strlen(name))) {
+   if (ffinfo)
+   *ffinfo = finfo;
+   if (slot)
+   *slot = fi;
+   return 0;
+   }
+   fi++;
+   }
+   return -1;
+}
+
+/* Lookup used only during rte_graph_feature_add() */
+static int
+feature_add_lookup(struct rte_graph_feature_arc *arc, const char *feat_name,
+  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   const char *name;
+   uint32_t fi = 0;
+
+   if (!feat_name)
+   return -1;
+
+   if (slot)
+   *slot = 0;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   RTE_VERIFY(finfo->feature_arc == arc);
+   name = rte_node_id_to_name(finfo->feature_node->id);
+   if (!strncmp(name, feat_name, strlen(name))) {
+   if (ffinfo)
+   *ffinfo = finfo;
+   if (slot)
+   *slot = fi;
+   return 0;
+   }
+   /* Update slot 

[PATCH v3 2/5] graph: add feature arc option in graph create

2024-10-09 Thread Nitin Saxena
Added option in graph create to call feature-specific process node
functions. This removes extra overhead for checking feature arc status
in nodes where application is not using feature arc processing

Signed-off-by: Pavan Nikhilesh 
Signed-off-by: Nitin Saxena 
---
 doc/guides/rel_notes/release_24_11.rst | 3 +++
 lib/graph/graph.c  | 1 +
 lib/graph/graph_populate.c | 7 ++-
 lib/graph/graph_private.h  | 3 +++
 lib/graph/node.c   | 2 ++
 lib/graph/rte_graph.h  | 3 +++
 6 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/doc/guides/rel_notes/release_24_11.rst 
b/doc/guides/rel_notes/release_24_11.rst
index d6d64518e0..5195badf54 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -122,6 +122,9 @@ ABI Changes
Also, make sure to start the actual text at the margin.
===
 
+* graph: Added new `feature_arc_enable` parameter in `struct rte_graph_param` 
to
+  allow `rte_graph_walk()` call `feat_arc_proc` node callback function, if it
+  is provided in node registration
 
 Known Issues
 
diff --git a/lib/graph/graph.c b/lib/graph/graph.c
index d5b8c9f918..b0ad3a83ae 100644
--- a/lib/graph/graph.c
+++ b/lib/graph/graph.c
@@ -455,6 +455,7 @@ rte_graph_create(const char *name, struct rte_graph_param 
*prm)
graph->parent_id = RTE_GRAPH_ID_INVALID;
graph->lcore_id = RTE_MAX_LCORE;
graph->num_pkt_to_capture = prm->num_pkt_to_capture;
+   graph->feature_arc_enabled = prm->feature_arc_enable;
if (prm->pcap_filename)
rte_strscpy(graph->pcap_filename, prm->pcap_filename, 
RTE_GRAPH_PCAP_FILE_SZ);
 
diff --git a/lib/graph/graph_populate.c b/lib/graph/graph_populate.c
index ed596a7711..5d8aa7b903 100644
--- a/lib/graph/graph_populate.c
+++ b/lib/graph/graph_populate.c
@@ -79,8 +79,13 @@ graph_nodes_populate(struct graph *_graph)
if (graph_pcap_is_enable()) {
node->process = graph_pcap_dispatch;
node->original_process = graph_node->node->process;
-   } else
+   if (_graph->feature_arc_enabled && 
graph_node->node->feat_arc_proc)
+   node->original_process = 
graph_node->node->feat_arc_proc;
+   } else {
node->process = graph_node->node->process;
+   if (_graph->feature_arc_enabled && 
graph_node->node->feat_arc_proc)
+   node->process = graph_node->node->feat_arc_proc;
+   }
memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
pid = graph_node->node->parent_id;
if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
index d557d55f2d..58ba0abeff 100644
--- a/lib/graph/graph_private.h
+++ b/lib/graph/graph_private.h
@@ -56,6 +56,7 @@ struct node {
unsigned int lcore_id;
/**< Node runs on the Lcore ID used for mcore dispatch model. */
rte_node_process_t process;   /**< Node process function. */
+   rte_node_process_t feat_arc_proc; /**< Node feature-arch process 
function. */
rte_node_init_t init; /**< Node init function. */
rte_node_fini_t fini; /**< Node fini function. */
rte_node_t id;/**< Allocated identifier for the node. */
@@ -126,6 +127,8 @@ struct graph {
/**< Number of packets to be captured per core. */
char pcap_filename[RTE_GRAPH_PCAP_FILE_SZ];
/**< pcap file name/path. */
+   uint8_t feature_arc_enabled;
+   /**< Graph feature arc. */
STAILQ_HEAD(gnode_list, graph_node) node_list;
/**< Nodes in a graph. */
 };
diff --git a/lib/graph/node.c b/lib/graph/node.c
index 99a9622779..d8fd273543 100644
--- a/lib/graph/node.c
+++ b/lib/graph/node.c
@@ -90,6 +90,7 @@ __rte_node_register(const struct rte_node_register *reg)
goto free;
node->flags = reg->flags;
node->process = reg->process;
+   node->feat_arc_proc = reg->feat_arc_proc;
node->init = reg->init;
node->fini = reg->fini;
node->nb_edges = reg->nb_edges;
@@ -137,6 +138,7 @@ node_clone(struct node *node, const char *name)
/* Clone the source node */
reg->flags = node->flags;
reg->process = node->process;
+   reg->feat_arc_proc = node->feat_arc_proc;
reg->init = node->init;
reg->fini = node->fini;
reg->nb_edges = node->nb_edges;
diff --git a/lib/graph/rte_graph.h b/lib/graph/rte_graph.h
index ecfec206

[PATCH v3 0/5] add feature arc in rte_graph

2024-10-09 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to connect various
rte_graph nodes, as feature nodes, and allow packets steering across
these nodes in a generic manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
policy lookup. On the other hand, packets routed to another interface
(eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
is disabled on eth1. Feature-arc allows rte_graph applications to manage
such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Seamlessly steer packets across feature nodes based on whether
feature is enabled or disabled on an interface. Features enabled on one
interface may not be enabled on another interface with in a same feature
arc.

2. Allow enabling/disabling of features on an interface at runtime,
so that if a feature is disabled, packets associated with that interface
won't be steered to corresponding feature node.

3. Provides mechanism to hook custom/user-defined nodes to a feature
node and allow packet steering from feature node to custom node without
changing former's fast path function

4. Allow expressing features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Changes in v3:
- rte_graph_feature_arc_t typedef from uint64_t to uintptr_t to fix
  compilation on 32-bit machine
- Updated images in .png format
- Added ABI change section in release notes
- Fixed DPDK CI failures

Changes in v2:
- Added unit tests for feature arc
- Fixed issues found in testing
- Added new public APIs rte_graph_feature_arc_feature_to_node(),
  rte_graph_feature_arc_feature_to_name(),
  rte_graph_feature_arc_num_features()
- Added programming guide for feature arc
- Added release notes for feature arc

Nitin Saxena (5):
  graph: add feature arc support
  graph: add feature arc option in graph create
  graph: add IPv4 output feature arc
  test/graph_feature_arc: add functional tests
  docs: add programming guide for feature arc

 app/test/meson.build|1 +
 app/test/test_graph_feature_arc.c   | 1410 +++
 doc/guides/prog_guide/graph_lib.rst |  288 
 doc/guides/prog_guide/img/feature_arc-1.png |  Bin 0 -> 61532 bytes
 doc/guides/prog_guide/img/feature_arc-2.png |  Bin 0 -> 155806 bytes
 doc/guides/prog_guide/img/feature_arc-3.png |  Bin 0 -> 143697 bytes
 doc/guides/rel_notes/release_24_11.rst  |   13 +
 lib/graph/graph.c   |1 +
 lib/graph/graph_feature_arc.c   | 1223 
 lib/graph/graph_populate.c  |7 +-
 lib/graph/graph_private.h   |3 +
 lib/graph/meson.build   |2 +
 lib/graph/node.c|2 +
 lib/graph/rte_graph.h   |3 +
 lib/graph/rte_graph_feature_arc.h   |  431 ++
 lib/graph/rte_graph_feature_arc_worker.h|  674 +
 lib/graph/version.map   |   20 +
 lib/node/ip4_rewrite.c  |  476 +--
 lib/node/ip4_rewrite_priv.h |   15 +-
 lib/node/node_private.h |   20 +-
 lib/node/rte_node_ip4_api.h |3 +
 21 files changed, 4494 insertions(+), 98 deletions(-)
 create mode 100644 app/test/test_graph_feature_arc.c
 create mode 100644 doc/guides/prog_guide/img/feature_arc-1.png
 create mode 100644 doc/guides/prog_guide/img/feature_arc-2.png
 create mode 100644 doc/guides/prog_guide/img/feature_arc-3.png
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

-- 
2.43.0



[PATCH v3 4/5] test/graph_feature_arc: add functional tests

2024-10-09 Thread Nitin Saxena
Added functional unit test case for verifying feature arc control plane
and fast path APIs

How to run:
$ echo "graph_feature_arc_autotest" | ./bin/dpdk-test

Signed-off-by: Nitin Saxena 
---
 app/test/meson.build  |1 +
 app/test/test_graph_feature_arc.c | 1410 +
 2 files changed, 1411 insertions(+)
 create mode 100644 app/test/test_graph_feature_arc.c

diff --git a/app/test/meson.build b/app/test/meson.build
index e29258e6ec..740fa1bfb4 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -90,6 +90,7 @@ source_file_deps = {
 'test_func_reentrancy.c': ['hash', 'lpm'],
 'test_graph.c': ['graph'],
 'test_graph_perf.c': ['graph'],
+'test_graph_feature_arc.c': ['graph'],
 'test_hash.c': ['net', 'hash'],
 'test_hash_functions.c': ['hash'],
 'test_hash_multiwriter.c': ['hash'],
diff --git a/app/test/test_graph_feature_arc.c 
b/app/test/test_graph_feature_arc.c
new file mode 100644
index 00..8ea7a7d3eb
--- /dev/null
+++ b/app/test/test_graph_feature_arc.c
@@ -0,0 +1,1410 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "test.h"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#ifndef RTE_EXEC_ENV_WINDOWS
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define MBUFF_SIZE 512
+#define TEST_ARC1_NAME "arc1"
+#define TEST_ARC2_NAME "arc2"
+#define MAX_INDEXES 10
+#define MAX_FEATURES 5
+
+#define SOURCE1 "test_node_arc_source1"
+#define INPUT_STATIC "test_node_arc_input_static"
+#define OUTPUT_STATIC "test_node_arc_output_static"
+#define PKT_FREE_STATIC "test_node_arc_pkt_free_static"
+#define ARC1_FEATURE1 "test_node_arc1_feature1"
+#define ARC1_FEATURE2 "test_node_arc1_feature2"
+#define ARC2_FEATURE1 "test_node_arc2_feature1"
+#define ARC2_FEATURE2 "test_node_arc2_feature2"
+#define ARC2_FEATURE3 "test_node_arc2_feature3"
+#define DUMMY1_STATIC "test_node_arc_dummy1_static"
+#define DUMMY2_STATIC "test_node_arc_dummy2_static"
+
+/* (Node index, Node Name, feature user data base */
+#define FOREACH_TEST_NODE_ARC {\
+   R(0, SOURCE1, 64)   \
+   R(1, INPUT_STATIC, 128) \
+   R(2, OUTPUT_STATIC, 256)\
+   R(3, PKT_FREE_STATIC, 512)  \
+   R(4, ARC1_FEATURE1, 1024)   \
+   R(5, ARC1_FEATURE2, 2048)   \
+   R(6, ARC2_FEATURE1, 4096)   \
+   R(7, ARC2_FEATURE2, 8192)   \
+   R(8, ARC2_FEATURE3, 16384)  \
+   R(9, DUMMY1_STATIC, 32768)  \
+   R(10, DUMMY2_STATIC, 65536) \
+   }
+
+/**
+ * ARC1: Feature arc on ingress interface
+ * ARC2: Feature arc on egress interface
+ * XX_static: Static nodes
+ * XX_featureX: Feature X on arc
+ *
+ *-> ARC1_FEATURE1
+ *   || |
+ *   || v
+ *   ||   ARC1_FEATURE2
+ *   || |
+ *   |v v
+ *  SOURCE1 ->-> INPUT_STATIC --> OUTPUT_STATIC -> PKT_FREE_STATIC
+ * |   |  | ^  ^ ^
+ * |   |  | |  | |
+ * |   |   --> ARC2_FEATURE1   | |
+ * |   |  ^ ^  | |
+ * |   |  | |  | |
+ * |--c-> ARC2_FEATURE2  |
+ * |  | ^|
+ * |  | ||
+ *  --> ARC2_FEATURE3 ---
+ */
+const char *node_names_feature_arc[] = {
+   SOURCE1, INPUT_STATIC, OUTPUT_STATIC, PKT_FREE_STATIC,
+   ARC1_FEATURE1, ARC1_FEATURE2, ARC2_FEATURE1, ARC2_FEATURE2, 
ARC2_FEATURE3,
+   DUMMY1_STATIC, DUMMY2_STATIC
+};
+
+#define MAX_NODES  RTE_DIM(node_names_feature_arc)
+
+/* Function declarations */
+static uint16_t
+source1_fn(struct rte_graph *graph, struct rte_node *node,
+  void **objs, uint16_t nb_objs);
+static uint16_t
+input_fn(struct rte_graph *graph, struct rte_node *node,
+void **objs, uint16_t nb_objs);
+static uint16_t
+input_fa_fn(struct rte_graph *graph, struct rte_node *node,
+   void **objs, uint16_t nb_objs);
+static uint16_t
+output_fn(struct rte_graph *graph, struct rte_node *node,
+ void **o

[PATCH v3 3/5] graph: add IPv4 output feature arc

2024-10-09 Thread Nitin Saxena
add ipv4-output feature arc in ipv4-rewrite node to allow
custom/standard nodes(like outbound IPsec policy node) in outgoing
forwarding path

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_rewrite.c  | 476 +---
 lib/node/ip4_rewrite_priv.h |  15 +-
 lib/node/node_private.h |  20 +-
 lib/node/rte_node_ip4_api.h |   3 +
 4 files changed, 417 insertions(+), 97 deletions(-)

diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index 34a920df5e..5a160335f2 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -15,39 +15,156 @@
 #include "ip4_rewrite_priv.h"
 #include "node_private.h"
 
+#define ALL_PKT_MASK 0xf
+
 struct ip4_rewrite_node_ctx {
+   rte_graph_feature_arc_t output_feature_arc;
/* Dynamic offset to mbuf priv1 */
int mbuf_priv1_off;
/* Cached next index */
uint16_t next_index;
+   uint16_t last_tx;
 };
 
+typedef struct rewrite_priv_vars {
+   union {
+   struct {
+   rte_xmm_t xmm1;
+   };
+   struct __rte_packed {
+   uint16_t next0;
+   uint16_t next1;
+   uint16_t next2;
+   uint16_t next3;
+   uint16_t last_tx_interface;
+   uint16_t last_if_feature;
+   uint16_t actual_feat_mask;
+   uint16_t speculative_feat_mask;
+   };
+   };
+} rewrite_priv_vars_t;
+
 static struct ip4_rewrite_node_main *ip4_rewrite_nm;
 
 #define IP4_REWRITE_NODE_LAST_NEXT(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->next_index)
 
+#define IP4_REWRITE_NODE_LAST_TX(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->last_tx)
+
 #define IP4_REWRITE_NODE_PRIV1_OFF(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->mbuf_priv1_off)
 
-static uint16_t
-ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
-void **objs, uint16_t nb_objs)
+#define IP4_REWRITE_NODE_OUTPUT_FEATURE_ARC(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->output_feature_arc)
+
+static __rte_always_inline void
+prefetch_mbuf_and_dynfield(struct rte_mbuf *mbuf)
 {
+   /* prefetch first cache line required for accessing buf_addr */
+   rte_prefetch0((void *)mbuf);
+}
+
+static __rte_always_inline void
+check_output_feature_x4(struct rte_graph_feature_arc *arc,
+   const rte_graph_feature_rt_list_t flist,
+   rewrite_priv_vars_t *pvar, struct node_mbuf_priv1 
*priv0,
+   struct node_mbuf_priv1 *priv1, struct node_mbuf_priv1 
*priv2,
+   struct node_mbuf_priv1 *priv3)
+{
+   uint32_t mask = 0;
+   uint16_t xor = 0;
+
+   /*
+* interface edge's start from 1 and not from 0 as "pkt_drop"
+* is next node at 0th index
+*/
+   priv0->if_index = pvar->next0 - 1;
+   priv1->if_index = pvar->next1 - 1;
+   priv2->if_index = pvar->next2 - 1;
+   priv3->if_index = pvar->next3 - 1;
+
+   /* Find out if all packets are sent to last_tx_interface */
+   xor = pvar->last_tx_interface ^ priv0->if_index;
+   xor += priv0->if_index ^ priv1->if_index;
+   xor += priv1->if_index ^ priv2->if_index;
+   xor += priv2->if_index ^ priv3->if_index;
+
+   if (likely(!xor)) {
+   /* copy last interface feature and feature mask */
+   priv0->current_feature = priv1->current_feature =
+   priv2->current_feature = priv3->current_feature =
+   pvar->last_if_feature;
+   pvar->actual_feat_mask = pvar->speculative_feat_mask;
+   } else {
+   /* create a mask for index which does not have feature
+* Also override next edge and if feature enabled, get feature
+*/
+   mask = rte_graph_feature_arc_feature_set(arc, flist, 
priv0->if_index,
+
&priv0->current_feature,
+&pvar->next0);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv1->if_index,
+
&priv1->current_feature,
+&pvar->next1)) << 
1);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv2->if_index,
+
&priv2->current_feature,
+&pvar->next2)) << 
2);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv3->if_index,
+   

[PATCH v4 0/5] add feature arc in rte_graph

2024-10-10 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to connect various
rte_graph nodes, as feature nodes, and allow packets steering across
these nodes in a generic manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
policy lookup. On the other hand, packets routed to another interface
(eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
is disabled on eth1. Feature-arc allows rte_graph applications to manage
such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Seamlessly steer packets across feature nodes based on whether
feature is enabled or disabled on an interface. Features enabled on one
interface may not be enabled on another interface with in a same feature
arc.

2. Allow enabling/disabling of features on an interface at runtime,
so that if a feature is disabled, packets associated with that interface
won't be steered to corresponding feature node.

3. Provides mechanism to hook custom/user-defined nodes to a feature
node and allow packet steering from feature node to custom node without
changing former's fast path function

4. Allow expressing features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Changes in v4:
- Fixed clang build compilations
- Captured `feat_arc_proc` function in ABI change section of release notes

Changes in v3:
- rte_graph_feature_arc_t typedef from uint64_t to uintptr_t to fix
  compilation on 32-bit machine
- Updated images in .png format
- Added ABI change section in release notes
- Fixed DPDK CI failures

Changes in v2:
- Added unit tests for feature arc
- Fixed issues found in testing
- Added new public APIs rte_graph_feature_arc_feature_to_node(),
  rte_graph_feature_arc_feature_to_name(),
  rte_graph_feature_arc_num_features()
- Added programming guide for feature arc
- Added release notes for feature arc

Nitin Saxena (5):
  graph: add feature arc support
  graph: add feature arc option in graph create
  graph: add IPv4 output feature arc
  test/graph_feature_arc: add functional tests
  docs: add programming guide for feature arc

 app/test/meson.build|1 +
 app/test/test_graph_feature_arc.c   | 1410 +++
 doc/guides/prog_guide/graph_lib.rst |  288 
 doc/guides/prog_guide/img/feature_arc-1.png |  Bin 0 -> 61532 bytes
 doc/guides/prog_guide/img/feature_arc-2.png |  Bin 0 -> 155806 bytes
 doc/guides/prog_guide/img/feature_arc-3.png |  Bin 0 -> 143697 bytes
 doc/guides/rel_notes/release_24_11.rst  |   17 +
 lib/graph/graph.c   |1 +
 lib/graph/graph_feature_arc.c   | 1236 
 lib/graph/graph_populate.c  |7 +-
 lib/graph/graph_private.h   |3 +
 lib/graph/meson.build   |2 +
 lib/graph/node.c|2 +
 lib/graph/rte_graph.h   |3 +
 lib/graph/rte_graph_feature_arc.h   |  431 ++
 lib/graph/rte_graph_feature_arc_worker.h|  679 +
 lib/graph/version.map   |   20 +
 lib/node/ip4_rewrite.c  |  476 +--
 lib/node/ip4_rewrite_priv.h |   15 +-
 lib/node/node_private.h |   20 +-
 lib/node/rte_node_ip4_api.h |3 +
 21 files changed, 4516 insertions(+), 98 deletions(-)
 create mode 100644 app/test/test_graph_feature_arc.c
 create mode 100644 doc/guides/prog_guide/img/feature_arc-1.png
 create mode 100644 doc/guides/prog_guide/img/feature_arc-2.png
 create mode 100644 doc/guides/prog_guide/img/feature_arc-3.png
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

-- 
2.43.0



[PATCH v4 1/5] graph: add feature arc support

2024-10-10 Thread Nitin Saxena
add feature arc to allow dynamic steering of packets across graph nodes
based on protocol features enabled on incoming or outgoing interface

Signed-off-by: Nitin Saxena 
---
 doc/guides/rel_notes/release_24_11.rst   |   10 +
 lib/graph/graph_feature_arc.c| 1236 ++
 lib/graph/meson.build|2 +
 lib/graph/rte_graph_feature_arc.h|  431 
 lib/graph/rte_graph_feature_arc_worker.h |  679 
 lib/graph/version.map|   20 +
 6 files changed, 2378 insertions(+)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

diff --git a/doc/guides/rel_notes/release_24_11.rst 
b/doc/guides/rel_notes/release_24_11.rst
index 2f78f2d125..bd5589b01c 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -82,6 +82,16 @@ New Features
 
   The new statistics are useful for debugging and profiling.
 
+* **Added feature arc abstraction in graph library.**
+
+  Feature arc abstraction helps ``rte_graph`` based applications to steer
+  packets across different node path(s) based on the features (or protocols)
+  enabled on interfaces. Different feature node paths can be enabled/disabled
+  at runtime on some or on all interfaces. This abstraction also help
+  applications to hook their ``custom nodes`` in standard DPDK node paths
+  without any code changes in the later.
+
+  * Added ``ip4-output`` feature arc processing in ``ip4_rewrite`` node.
 
 Removed Items
 -
diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 00..0f8633c317
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,1236 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include 
+#include 
+
+#define ARC_PASSIVE_LIST(list) (list ^ 0x1)
+
+#define rte_graph_uint_cast(x) ((unsigned int)x)
+#define feat_dbg graph_dbg
+
+static rte_graph_feature_arc_main_t *__rte_graph_feature_arc_main;
+
+/* Make sure fast path cache line is compact */
+_Static_assert((offsetof(struct rte_graph_feature_arc, slow_path_variables)
+   - offsetof(struct rte_graph_feature_arc, fast_path_variables))
+  <= RTE_CACHE_LINE_SIZE,
+  "Fast path feature arc variables exceed cache line size");
+
+#define connect_graph_nodes(node1, node2, edge, arc_name) \
+   __connect_graph_nodes(node1, node2, edge, arc_name, __LINE__)
+
+#define FEAT_COND_ERR(cond, fmt, ...)  \
+   do {   \
+   if (cond)  \
+   graph_err(fmt, ##__VA_ARGS__); \
+   } while (0)
+
+/*
+ * lookup feature name and get control path node_list as well as feature index
+ * at which it is inserted
+ */
+static int
+feature_lookup(struct rte_graph_feature_arc *arc, const char *feat_name,
+  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   const char *name;
+   uint32_t fi = 0;
+
+   if (!feat_name)
+   return -1;
+
+   if (slot)
+   *slot = UINT32_MAX;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   RTE_VERIFY(finfo->feature_arc == arc);
+   name = rte_node_id_to_name(finfo->feature_node->id);
+   if (!strncmp(name, feat_name, strlen(name))) {
+   if (ffinfo)
+   *ffinfo = finfo;
+   if (slot)
+   *slot = fi;
+   return 0;
+   }
+   fi++;
+   }
+   return -1;
+}
+
+/* Lookup used only during rte_graph_feature_add() */
+static int
+feature_add_lookup(struct rte_graph_feature_arc *arc, const char *feat_name,
+  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   const char *name;
+   uint32_t fi = 0;
+
+   if (!feat_name)
+   return -1;
+
+   if (slot)
+   *slot = 0;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   RTE_VERIFY(finfo->feature_arc == arc);
+   name = rte_node_id_to_name(finfo->feature_node->id);
+   if (!strncmp(name, feat_name, strlen(name))) {
+   if (ffinfo)
+   *ffinfo = finfo;
+   if (slot)
+   *slot = fi;
+   return 0;
+   }
+   /* Update slot whe

[PATCH v4 2/5] graph: add feature arc option in graph create

2024-10-10 Thread Nitin Saxena
Added option in graph create to call feature-specific process node
functions. This removes extra overhead for checking feature arc status
in nodes where application is not using feature arc processing

Signed-off-by: Pavan Nikhilesh 
Signed-off-by: Nitin Saxena 
---
 doc/guides/rel_notes/release_24_11.rst | 7 +++
 lib/graph/graph.c  | 1 +
 lib/graph/graph_populate.c | 7 ++-
 lib/graph/graph_private.h  | 3 +++
 lib/graph/node.c   | 2 ++
 lib/graph/rte_graph.h  | 3 +++
 6 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/doc/guides/rel_notes/release_24_11.rst 
b/doc/guides/rel_notes/release_24_11.rst
index bd5589b01c..237c057647 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -137,6 +137,13 @@ ABI Changes
Also, make sure to start the actual text at the margin.
===
 
+* graph: Added feature arc specific `feat_arc_proc` node callback function in
+  `struct rte_node_register`. If this function is not NULL and
+  `feature_arc_enable` is set to `true` in `struct rte_graph_param`,
+  rte_graph_walk() calls `feat_arc_proc` callback function instead of `process`
+
+* graph: Added `feature_arc_enable` parameter in `struct rte_graph_param` for
+  calling non-NULL `feat_arc_proc` callback function by `rte_graph_walk()`
 
 Known Issues
 
diff --git a/lib/graph/graph.c b/lib/graph/graph.c
index d5b8c9f918..b0ad3a83ae 100644
--- a/lib/graph/graph.c
+++ b/lib/graph/graph.c
@@ -455,6 +455,7 @@ rte_graph_create(const char *name, struct rte_graph_param 
*prm)
graph->parent_id = RTE_GRAPH_ID_INVALID;
graph->lcore_id = RTE_MAX_LCORE;
graph->num_pkt_to_capture = prm->num_pkt_to_capture;
+   graph->feature_arc_enabled = prm->feature_arc_enable;
if (prm->pcap_filename)
rte_strscpy(graph->pcap_filename, prm->pcap_filename, 
RTE_GRAPH_PCAP_FILE_SZ);
 
diff --git a/lib/graph/graph_populate.c b/lib/graph/graph_populate.c
index ed596a7711..5d8aa7b903 100644
--- a/lib/graph/graph_populate.c
+++ b/lib/graph/graph_populate.c
@@ -79,8 +79,13 @@ graph_nodes_populate(struct graph *_graph)
if (graph_pcap_is_enable()) {
node->process = graph_pcap_dispatch;
node->original_process = graph_node->node->process;
-   } else
+   if (_graph->feature_arc_enabled && 
graph_node->node->feat_arc_proc)
+   node->original_process = 
graph_node->node->feat_arc_proc;
+   } else {
node->process = graph_node->node->process;
+   if (_graph->feature_arc_enabled && 
graph_node->node->feat_arc_proc)
+   node->process = graph_node->node->feat_arc_proc;
+   }
memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
pid = graph_node->node->parent_id;
if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
index d557d55f2d..58ba0abeff 100644
--- a/lib/graph/graph_private.h
+++ b/lib/graph/graph_private.h
@@ -56,6 +56,7 @@ struct node {
unsigned int lcore_id;
/**< Node runs on the Lcore ID used for mcore dispatch model. */
rte_node_process_t process;   /**< Node process function. */
+   rte_node_process_t feat_arc_proc; /**< Node feature-arch process 
function. */
rte_node_init_t init; /**< Node init function. */
rte_node_fini_t fini; /**< Node fini function. */
rte_node_t id;/**< Allocated identifier for the node. */
@@ -126,6 +127,8 @@ struct graph {
/**< Number of packets to be captured per core. */
char pcap_filename[RTE_GRAPH_PCAP_FILE_SZ];
/**< pcap file name/path. */
+   uint8_t feature_arc_enabled;
+   /**< Graph feature arc. */
STAILQ_HEAD(gnode_list, graph_node) node_list;
/**< Nodes in a graph. */
 };
diff --git a/lib/graph/node.c b/lib/graph/node.c
index 99a9622779..d8fd273543 100644
--- a/lib/graph/node.c
+++ b/lib/graph/node.c
@@ -90,6 +90,7 @@ __rte_node_register(const struct rte_node_register *reg)
goto free;
node->flags = reg->flags;
node->process = reg->process;
+   node->feat_arc_proc = reg->feat_arc_proc;
node->init = reg->init;
node->fini = reg->fini;
node->nb_edges = reg->nb_edges;
@@ -137,6 +138,7 @@ node_clone(struct node *node, const char *name)
/* Clone the source node */
reg->flags = node->flags;
reg->process = node->process;
+  

[PATCH v4 4/5] test/graph_feature_arc: add functional tests

2024-10-10 Thread Nitin Saxena
Added functional unit test case for verifying feature arc control plane
and fast path APIs

How to run:
$ echo "graph_feature_arc_autotest" | ./bin/dpdk-test

Signed-off-by: Nitin Saxena 
---
 app/test/meson.build  |1 +
 app/test/test_graph_feature_arc.c | 1410 +
 2 files changed, 1411 insertions(+)
 create mode 100644 app/test/test_graph_feature_arc.c

diff --git a/app/test/meson.build b/app/test/meson.build
index e29258e6ec..740fa1bfb4 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -90,6 +90,7 @@ source_file_deps = {
 'test_func_reentrancy.c': ['hash', 'lpm'],
 'test_graph.c': ['graph'],
 'test_graph_perf.c': ['graph'],
+'test_graph_feature_arc.c': ['graph'],
 'test_hash.c': ['net', 'hash'],
 'test_hash_functions.c': ['hash'],
 'test_hash_multiwriter.c': ['hash'],
diff --git a/app/test/test_graph_feature_arc.c 
b/app/test/test_graph_feature_arc.c
new file mode 100644
index 00..58b676e215
--- /dev/null
+++ b/app/test/test_graph_feature_arc.c
@@ -0,0 +1,1410 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "test.h"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#ifndef RTE_EXEC_ENV_WINDOWS
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define MBUFF_SIZE 512
+#define TEST_ARC1_NAME "arc1"
+#define TEST_ARC2_NAME "arc2"
+#define MAX_INDEXES 10
+#define MAX_FEATURES 5
+
+#define SOURCE1 "test_node_arc_source1"
+#define INPUT_STATIC "test_node_arc_input_static"
+#define OUTPUT_STATIC "test_node_arc_output_static"
+#define PKT_FREE_STATIC "test_node_arc_pkt_free_static"
+#define ARC1_FEATURE1 "test_node_arc1_feature1"
+#define ARC1_FEATURE2 "test_node_arc1_feature2"
+#define ARC2_FEATURE1 "test_node_arc2_feature1"
+#define ARC2_FEATURE2 "test_node_arc2_feature2"
+#define ARC2_FEATURE3 "test_node_arc2_feature3"
+#define DUMMY1_STATIC "test_node_arc_dummy1_static"
+#define DUMMY2_STATIC "test_node_arc_dummy2_static"
+
+/* (Node index, Node Name, feature user data base */
+#define FOREACH_TEST_NODE_ARC {\
+   R(0, SOURCE1, 64)   \
+   R(1, INPUT_STATIC, 128) \
+   R(2, OUTPUT_STATIC, 256)\
+   R(3, PKT_FREE_STATIC, 512)  \
+   R(4, ARC1_FEATURE1, 1024)   \
+   R(5, ARC1_FEATURE2, 2048)   \
+   R(6, ARC2_FEATURE1, 4096)   \
+   R(7, ARC2_FEATURE2, 8192)   \
+   R(8, ARC2_FEATURE3, 16384)  \
+   R(9, DUMMY1_STATIC, 32768)  \
+   R(10, DUMMY2_STATIC, 65536) \
+   }
+
+/**
+ * ARC1: Feature arc on ingress interface
+ * ARC2: Feature arc on egress interface
+ * XX_static: Static nodes
+ * XX_featureX: Feature X on arc
+ *
+ *-> ARC1_FEATURE1
+ *   || |
+ *   || v
+ *   ||   ARC1_FEATURE2
+ *   || |
+ *   |v v
+ *  SOURCE1 ->-> INPUT_STATIC --> OUTPUT_STATIC -> PKT_FREE_STATIC
+ * |   |  | ^  ^ ^
+ * |   |  | |  | |
+ * |   |   --> ARC2_FEATURE1   | |
+ * |   |  ^ ^  | |
+ * |   |  | |  | |
+ * |--c-> ARC2_FEATURE2  |
+ * |  | ^|
+ * |  | ||
+ *  --> ARC2_FEATURE3 ---
+ */
+const char *node_names_feature_arc[] = {
+   SOURCE1, INPUT_STATIC, OUTPUT_STATIC, PKT_FREE_STATIC,
+   ARC1_FEATURE1, ARC1_FEATURE2, ARC2_FEATURE1, ARC2_FEATURE2, 
ARC2_FEATURE3,
+   DUMMY1_STATIC, DUMMY2_STATIC
+};
+
+#define MAX_NODES  RTE_DIM(node_names_feature_arc)
+
+/* Function declarations */
+static uint16_t
+source1_fn(struct rte_graph *graph, struct rte_node *node,
+  void **objs, uint16_t nb_objs);
+static uint16_t
+input_fn(struct rte_graph *graph, struct rte_node *node,
+void **objs, uint16_t nb_objs);
+static uint16_t
+input_fa_fn(struct rte_graph *graph, struct rte_node *node,
+   void **objs, uint16_t nb_objs);
+static uint16_t
+output_fn(struct rte_graph *graph, struct rte_node *node,
+ void **o

[PATCH v4 3/5] graph: add IPv4 output feature arc

2024-10-10 Thread Nitin Saxena
add ipv4-output feature arc in ipv4-rewrite node to allow
custom/standard nodes(like outbound IPsec policy node) in outgoing
forwarding path

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_rewrite.c  | 476 +---
 lib/node/ip4_rewrite_priv.h |  15 +-
 lib/node/node_private.h |  20 +-
 lib/node/rte_node_ip4_api.h |   3 +
 4 files changed, 417 insertions(+), 97 deletions(-)

diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index 34a920df5e..824ef9a4cd 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -15,39 +15,156 @@
 #include "ip4_rewrite_priv.h"
 #include "node_private.h"
 
+#define ALL_PKT_MASK 0xf
+
 struct ip4_rewrite_node_ctx {
+   rte_graph_feature_arc_t output_feature_arc;
/* Dynamic offset to mbuf priv1 */
int mbuf_priv1_off;
/* Cached next index */
uint16_t next_index;
+   uint16_t last_tx;
 };
 
+typedef struct rewrite_priv_vars {
+   union {
+   struct {
+   rte_xmm_t xmm1;
+   };
+   struct __rte_packed {
+   uint16_t next0;
+   uint16_t next1;
+   uint16_t next2;
+   uint16_t next3;
+   uint16_t last_tx_interface;
+   uint16_t last_if_feature;
+   uint16_t actual_feat_mask;
+   uint16_t speculative_feat_mask;
+   };
+   };
+} rewrite_priv_vars_t;
+
 static struct ip4_rewrite_node_main *ip4_rewrite_nm;
 
 #define IP4_REWRITE_NODE_LAST_NEXT(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->next_index)
 
+#define IP4_REWRITE_NODE_LAST_TX(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->last_tx)
+
 #define IP4_REWRITE_NODE_PRIV1_OFF(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->mbuf_priv1_off)
 
-static uint16_t
-ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
-void **objs, uint16_t nb_objs)
+#define IP4_REWRITE_NODE_OUTPUT_FEATURE_ARC(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->output_feature_arc)
+
+static __rte_always_inline void
+prefetch_mbuf_and_dynfield(struct rte_mbuf *mbuf)
 {
+   /* prefetch first cache line required for accessing buf_addr */
+   rte_prefetch0((void *)mbuf);
+}
+
+static __rte_always_inline void
+check_output_feature_x4(struct rte_graph_feature_arc *arc,
+   const rte_graph_feature_rt_list_t flist,
+   rewrite_priv_vars_t *pvar, struct node_mbuf_priv1 
*priv0,
+   struct node_mbuf_priv1 *priv1, struct node_mbuf_priv1 
*priv2,
+   struct node_mbuf_priv1 *priv3)
+{
+   uint32_t mask = 0;
+   uint16_t xor = 0;
+
+   /*
+* interface edge's start from 1 and not from 0 as "pkt_drop"
+* is next node at 0th index
+*/
+   priv0->if_index = pvar->next0 - 1;
+   priv1->if_index = pvar->next1 - 1;
+   priv2->if_index = pvar->next2 - 1;
+   priv3->if_index = pvar->next3 - 1;
+
+   /* Find out if all packets are sent to last_tx_interface */
+   xor = pvar->last_tx_interface ^ priv0->if_index;
+   xor += priv0->if_index ^ priv1->if_index;
+   xor += priv1->if_index ^ priv2->if_index;
+   xor += priv2->if_index ^ priv3->if_index;
+
+   if (likely(!xor)) {
+   /* copy last interface feature and feature mask */
+   priv0->current_feature = priv1->current_feature =
+   priv2->current_feature = priv3->current_feature =
+   pvar->last_if_feature;
+   pvar->actual_feat_mask = pvar->speculative_feat_mask;
+   } else {
+   /* create a mask for index which does not have feature
+* Also override next edge and if feature enabled, get feature
+*/
+   mask = rte_graph_feature_arc_feature_set(arc, flist, 
priv0->if_index,
+
&priv0->current_feature,
+&pvar->next0);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv1->if_index,
+
&priv1->current_feature,
+&pvar->next1)) << 
1);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv2->if_index,
+
&priv2->current_feature,
+&pvar->next2)) << 
2);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv3->if_index,
+   

RE: [EXTERNAL] graph: make the in-built nodes better reusable

2024-10-21 Thread Nitin Saxena
Hi Robin,

Thanks for initiating this thread. I agree with majority of your points that 
you highlighted
We(Marvell) also started integrating *rte_graph* in various use-cases and found 
need of following items:

1. Hot-plugging of ports at runtime. (Example link status change from DOWN -> 
UP and vice-versa OR addition of new port at runtime)
Instead of creating source node(ethdev-rx--} for each port/queue 
per worker, we can have single "ethdev-rx" node for every worker. Each instance 
of "ethdev-rx" would poll list of all (ports, queue) mapped to it by control 
plane at runtime. With RCU primitives, control plane can runtime update list of 
all {ports, queue} for each "ethdev-rx' instance. This would enable 
hot-plugging feature and should avoid following issues that you highlighted:
- Recreation of graph instance due to port hot-plugging feature
- Statistics issue where obscure version of "ethdev-rx-X" shows up due to port 
delete in control plane
- Due to single instance of node, any addition cloning per (port, queue) per 
worker can be avoided (that exists today)
- Any naming concerns regarding "ethdev-rx--", since each worker 
would have single "ethdev-rx" instance

2. Node mbuf dynamic field (node_mbuf_priv1) refactor
It is better to have following fields in node_mbuf_priv1() structure
- uint16_t rx_interface for any incoming decision per interface. This 
corresponds to virtual interfaces as well like IPIP interface
- uint16_t tx_interface for any outgoing decision per interface. Applies to 
virtual interfaces as well
- Fields related to feature_arc, if it gets accepted

3. Runtime enable/disable of sub-graphs per interface
- Feature arc patch series[0] solves this issue.

4. Reusability of in-built nodes by out-of-tree applications
- Feature arc patch series[0] solves this issue

5. Hard codes static next_nodes
- Feature arc patch series solves this by adding static edges (but not 
hard-coded). Feature arc manages next_nodes() across various sub-graphs

6. IP route lookup
a) Changing ip lookup implementation from current LPM to RCU based rte_fib for 
performance and functional reasons
b) Any control plane route update should also update fast path fib
- When user updates MAC address of local port, rewrite-data must change that 
resides in next_hop structure
- rewrite data should also be changed when connected next-hop has changed its 
MAC
- When any port link status goes down, fib entry should be removed from fast 
path
c) Polling for partial routes in control plane. Partial routes (or incomplete 
routes) are those whose ARP has not been yet resolved. Only complete routes 
must be added to fib
d) Notion of sw_tx_port instead of ethdev_tx_port in ipX-lookup to enable 
tunnel protocols like IP-IP.
e) Adding punt feature to send packets to LINUX control plane
f) VRF and Multi-path routing (Nice to have features)

7. Optimized UDP integration with L7 protocols

8. Nice to have feature: Minimalistic control plane in app/graph with single 
epoll_fd() interface for handling external asynchronous events
- CLI socket
- libevent socket like in grout
- Any other socket polling like Netlink or application socket

9. LINUX control plane integration with standard daemons like Strongswan, FRR 
etc

8. VLAN nodes (Not immediate item for us but in roadmap)

Also see inline comments below.

[0]: 
https://patches.dpdk.org/project/dpdk/cover/20241014143401.3135897-1-nsax...@marvell.com/

> -Original Message-
> From: Robin Jarry 
> Sent: Monday, October 21, 2024 6:23 PM
> To: dev@dpdk.org; gr...@dpdk.org
> Cc: Jerin Jacob ; Nitin Saxena
> ; David Marchand
> ; Christophe Fontaine
> 
> Subject: [EXTERNAL] graph: make the in-built nodes better reusable
>
> Hi all,
>
> I am starting this discussion to see what can be done in order to make the in-
> built nodes (i.e. https://urldefense.proofpoint.com/v2/url?u=https-
> 3A__git.dpdk.org_dpdk_tree_lib_node&d=DwIFaQ&c=nKjWec2b6R0mOyPaz7
> xtfQ&r=cy0AkAKzy6-iEbDgLPJ8thryQyEKLLeVyCjs970CZak&m=flNiUH9F-
> aH7KzskEvR75ZZ2lHIwkRJ9j_PhBa3gcQOfmzHJqwEnHLKN-OOAQzy-
> &s=9V_iwJ-I7YNbJNpc6UEZhkV7fE7xXIOvIXPd9Ozqkys&e=) easier to reuse in
> external applications.
>
> So far here are the limitations I have found when working on grout. Some of
> these limitations are trivial, some others are more tricky. I hope we can get
> clean solutions.
>
> ethdev_rx and ethdev_tx require cloning
> ---
>
> These nodes have been written to receive from or transmit to a single queue.
> When changing the number of ports and/or rx/tx queues. The graph needs to
> be recreated.
>
> * Node names must all be unique (hence, node clones need to have
>   different names than their original).
>
>   => There is a routine that automatically adds a "unique" suffix to t

Re: [EXTERNAL] Re: [RFC PATCH 0/3] add feature arc in rte_graph

2024-10-17 Thread Nitin Saxena
Hi Robin,

See inline comments

Thanks,
Nitin

On Thu, Oct 17, 2024 at 1:20 PM Robin Jarry  wrote:
>
> Hi Nitin, all,
>
> Nitin Saxena, Oct 17, 2024 at 09:03:
> > Hi Robin/David and all,
> >
> > We realized the feature arc patch series is difficult to understand as
> > a new concept. Our objectives are following with feature arc changes
> >
> > 1. Allow reusability of standard DPDK nodes (defined in lib/nodes/*)
> >with out-of-tree applications (like grout). Currently out-of-tree
> >graph applications are duplicating standard nodes but not reusing
> >the standard ones which are available. In the long term, we would
> >like to mature standard DPDK nodes with flexibility of hooking them
> >to out-of-tree application nodes.
>
> It would be ideal if the in-built nodes could be reused. When we started
> working on grout, I tried multiple approaches where I could reuse these
> nodes, but all failed. The nodes public API seems tailored for app/graph
> but does not fit well with other control plane implementations.
>
> One of the main issues I had is that the ethdev_rx and ethdev_tx nodes
> are cloned per rxq / txq associated with a graph worker. The rte_node
> API requires that every clone has a unique name. This in turn makes hot
> plugging of DPDK ports very complex, if not impossible.

Agreed. I guess hot plugging of DPDK ports was not the objective when
initial changes went in. But we can add hot-plugging functionality
without affecting performance

>
> For example, with the in-built nodes, it is not possible to change the
> number of ports or their number of RX queues without destroying the
> whole graph and creating a new one from scratch.

Coincidentally, I have also encountered these technical issues while
writing an out-of-tree application [1]. I had internal discussions
with @Jerin Jacob  and other graph maintainers to fix these
shortcomings. If you want, we can collaborate on fixing these issues

For [port, rq] pair mapping to worker core, I have an alternate design
[2] which currently stops worker cores. It can be enhanced by RCU
based scheme for an ideal DPDK implementation

[1]: 
https://marvellembeddedprocessors.github.io/dao/guides/applications/secgw-graph.html
[2]: 
https://github.com/MarvellEmbeddedProcessors/dao/blob/dao-devel/app/secgw-graph/nodes/rxtx/ethdev-rx.c#L27

>
> Also, the current implementation of "ip{4,6}-rewrite" handles writing
> ethernet header data. This would prevent it from using this node for an
> IP-in-IP tunnel interface as we did in grout.

For IP-in-IP, a separate rewrite node would be required which computes
checksum etc. but not add rewrite data.

>
> Do you think we could change the in-built nodes to enforce OSI layer
> separation of concerns? It would make them much more flexible.

Yes. We are also in agreement to make RFC compliant optimized in-built
nodes with such flexibility in place.

> It may
> cause a slight drop of performance because you'd be splitting processing
> in two different nodes. But I think flexibility is more important.
> Otherwise, the in-built nodes can only be used for very specific
> use-cases.
>
> Finally, I would like to improve the rte_node API to allow defining and
> enforcing per-packet metadata that every node expects as input. The
> current in-built nodes rely on mbuf dynamic fields for this but this
> means you only have 9x32 bits available. And using all of these may
> break some drivers (ixgbe) that rely on dynfields to work. Have you
> considered using mbuf private data for this?

IMO,  "node_mbuf_priv_t" would be ideal for most of the use-cases as
it fits in second 64B cache line. With mbuf private data, fast path
have to access another cache line per packet which may not be
efficient from performance PoV. But we can discuss in more detail
about it. Although, I thought of adding "sw_if_index" (which is not
same as port_id) to accommodate IP-in-IP like software interfaces

>
> >
> > 2. Flexibility to enable/disable sub-graphs per interface based on the
> >runtime configuration updates. Protocol sub-graphs can be
> >selectively enabled for few (or all interfaces) at runtime
> >
> > 3. More than one sub-graphs/features can be enabled on an interface.
> >So a packet has to follow a sequential ordering node path on worker
> >cores. Packets may need to move from one sub-graph to another
> >sub-graph per interface
> >
> > 4. Last but not least, an optimized implementation which does not (or
> >minimally) stop worker cores for any control plane runtime updates.
> >Any performance regression should also be avoided
> >
> > I am planning to create a draft presentation on feature arc which
> > I can share, when ready, to discuss. If needed, I can also plan to
> > present that in one of the DPDK community meetings. Their we can also
> > discuss if there are any alternatives of achieving above objectives
>
> Looking forward to this.

Sure. Will share ppt asap

>
> Thanks!
>


Re: [EXTERNAL] Re: [RFC PATCH 0/3] add feature arc in rte_graph

2024-10-17 Thread Nitin Saxena
Hi Robin/David and all,

We realized the feature arc patch series is difficult to understand as
a new concept. Our objectives are following with feature arc changes

1. Allow reusability of standard DPDK nodes (defined in lib/nodes/*)
with out-of-tree applications (like grout). Currently out-of-tree
graph applications are duplicating standard nodes but not reusing the
standard ones
which are available. In the long term, we would like to mature
standard DPDK nodes with flexibility of hooking them to out-of-tree
application nodes.

2. Flexibility to enable/disable sub-graphs per interface based on the
runtime configuration updates. Protocol sub-graphs can be selectively
enabled for few (or all interfaces) at runtime

3. More than one sub-graphs/features can be enabled on an interface.
So a packet has to follow a sequential ordering node path on worker
cores.
Packets may need to move from one sub-graph to another sub-graph per interface

4. Last but not least, an optimized implementation which does not (or
minimally) stop worker cores for any control plane runtime updates.
Any performance regression should also be avoided

I am planning to create a draft presentation on feature arc which I
can share, when ready, to discuss. If needed, I can also plan to
present that in one of the DPDK community meetings.
Their we can also discuss if there are any alternatives of achieving
above objectives

Thanks,
Nitin
.
On Wed, Oct 16, 2024 at 7:20 PM Nitin Saxena  wrote:
>
> Hi Robin,
>
> Thanks for the review
> Please see my replies inline
>
> Thanks,
> Nitin
>
> On Wed, Oct 16, 2024 at 3:08 PM Robin Jarry  wrote:
> >
> > Hi folks,
> >
> > David Marchand, Oct 16, 2024 at 11:24:
> > > On Mon, Oct 14, 2024 at 1:12 PM Nitin Saxena  wrote:
> > >> I had pushed non RFC patch series before -rc1 date (11th oct).
> > >> We have an ABI change in this patch series 
> > >> https://patches.dpdk.org/project/dpdk/patch/20241010133111.2764712-3-nsax...@marvell.com/
> > >> Could you help merge this patch series in rc2 otherwise it has to wait 
> > >> for next LTS
> > >
> > > Just read through the series, I am not confident with this addition.
> > > It requires a lot of changes in the node code for supporting it, where
> > > it should be something handled in/facilitated by the graph library
> > > itself.
> >
> > As far as I can tell, it will be very complicated (if not impossible) to
> > determine in a generic manner whether a packet must be steered towards
> > a sub tree or not. The decision *must* come from the originating node in
> > some way or another.
>
> Nitin> I am not sure if it *must* always be from the originating node?
> What about a control plane which wants to enable "IP4 feature" on
> interface  'X'  by assigning IP address?
> A originating node (say: ip4-input) *must not* activate IP4 lookup
> sub-graph for interface "X " until control plane assigns any IP
> address to it.
>
> Regarding the complexity of adopting feature arc changes in fast path,
> - a sub-optimal change for feature-arc would be simple and trivial but
> at the cost of performance.
> - Complexity increases when feature arc changes are optimally
> integrated (like "ip4_rewrite" changes in the patch) with no
> performance regression
>
> >
> > > I did not read much from Robin or Christophe who have been writing
> > > more node code than me.
> > > I would prefer their opinion before going forward.
> >
> > This series is indeed very dense. I like the concept of having
> > extensible sub trees in the graph but it feels like the implementation
> > is more complex than it should be.
> >
> > Lacking of another solution, we went for a naive approach in grout.
> > Basically, some nodes have undefined next nodes which are extended using
> > a dedicated API.
>
> Nitin> With an initial glance, it looks like "grout" is trying to
> solve a use-case where a child is being added to the parent's
> undefined next node. This is trying to create a runtime  parent-child
> relationship
>
> On the other hand, feature arc not just create parent-child
> relationships but also sibling-sibling relationships as well. Also
> enabling sub-graph per interface is critical functionality in feature
> arc that adds complexity
>
> Let's assume a use-case in ingress direction, at the IPv4 layer,
> where IPv4-input is the *originating node* and
>
> - On interface X, IPsec-policy, IP4-classify() and IPv4-lookup
> sub-graphs are enabled in a sequential order
> - On interface Y, IP4-classify() and IPv4-lookup sub-graphs are
> enabled. in a sequential order. i.e. I

Re: [EXTERNAL] Re: [RFC PATCH 0/3] add feature arc in rte_graph

2024-10-17 Thread Nitin Saxena
Hi Robin,

Thanks for the review
Please see my replies inline

Thanks,
Nitin

On Wed, Oct 16, 2024 at 3:08 PM Robin Jarry  wrote:
>
> Hi folks,
>
> David Marchand, Oct 16, 2024 at 11:24:
> > On Mon, Oct 14, 2024 at 1:12 PM Nitin Saxena  wrote:
> >> I had pushed non RFC patch series before -rc1 date (11th oct).
> >> We have an ABI change in this patch series 
> >> https://patches.dpdk.org/project/dpdk/patch/20241010133111.2764712-3-nsax...@marvell.com/
> >> Could you help merge this patch series in rc2 otherwise it has to wait for 
> >> next LTS
> >
> > Just read through the series, I am not confident with this addition.
> > It requires a lot of changes in the node code for supporting it, where
> > it should be something handled in/facilitated by the graph library
> > itself.
>
> As far as I can tell, it will be very complicated (if not impossible) to
> determine in a generic manner whether a packet must be steered towards
> a sub tree or not. The decision *must* come from the originating node in
> some way or another.

Nitin> I am not sure if it *must* always be from the originating node?
What about a control plane which wants to enable "IP4 feature" on
interface  'X'  by assigning IP address?
A originating node (say: ip4-input) *must not* activate IP4 lookup
sub-graph for interface "X " until control plane assigns any IP
address to it.

Regarding the complexity of adopting feature arc changes in fast path,
- a sub-optimal change for feature-arc would be simple and trivial but
at the cost of performance.
- Complexity increases when feature arc changes are optimally
integrated (like "ip4_rewrite" changes in the patch) with no
performance regression

>
> > I did not read much from Robin or Christophe who have been writing
> > more node code than me.
> > I would prefer their opinion before going forward.
>
> This series is indeed very dense. I like the concept of having
> extensible sub trees in the graph but it feels like the implementation
> is more complex than it should be.
>
> Lacking of another solution, we went for a naive approach in grout.
> Basically, some nodes have undefined next nodes which are extended using
> a dedicated API.

Nitin> With an initial glance, it looks like "grout" is trying to
solve a use-case where a child is being added to the parent's
undefined next node. This is trying to create a runtime  parent-child
relationship

On the other hand, feature arc not just create parent-child
relationships but also sibling-sibling relationships as well. Also
enabling sub-graph per interface is critical functionality in feature
arc that adds complexity

Let's assume a use-case in ingress direction, at the IPv4 layer,
where IPv4-input is the *originating node* and

- On interface X, IPsec-policy, IP4-classify() and IPv4-lookup
sub-graphs are enabled in a sequential order
- On interface Y, IP4-classify() and IPv4-lookup sub-graphs are
enabled. in a sequential order. i.e. IPsec-policy is *disabled* on
interface Y

In fast path, following processing should happen for "mbuf0" which is
received on interface "X"
- "ipv4-input" sends mbuf0 to the first enabled sub-graph node for
interface X, "IPsec-policy"
- In "IPsec-policy" node processing, if policy action results in
"bypass" action for mbuf0, it must then be sent to next enabled
sub-graph  i.e. "IPv4-classify" (from "IPsec-policy" node)
- In "IPv4-classify" node processing, if classify fails for mbuf0 then
it should finally be sent to "IPv4-lookup" node (from "IPv4-classify"
node)

whereas for "mbuf1" received on interface Y following fast path
processing must happen
- "Ipv4-input" sends mbuf1 to the first enabled sub-graph node for
interface Y, "IPv4-classify"
- If "IPv4-classify" fails for mbuf1, then it should finally be sent
to IPv4-lookup node

To behave differently for interface X and interface Y as above
- First of all, IPsec-policy/IPv4-classify/IPv4-lookup must be
connected to "ipv4-input" node (Parent-Child relationship)
- Also, IPsec-policy/IPv4-classify/IPv4-lookup must also be connected
with each other (Sibling-Sibling relationship)
- Fast path APIs provide *rte_edges_t* to send mbuf from one node to
another node
   1. Based on interface (either Interface X or Interface Y)
   2. Based on which node, fast path APIs are called. Next enabled
feature/sub-graph can only be determined from previous enabled
feature/sub-graph in fast path

Not sure if grout handles above use-cases in the same manner. AFAIR ,
for any control plane change grout re-creates "graph" objects which
may not be required with feature arc.

>
> https://github.com/DPDK/grout/blob/v0

Re: [EXTERNAL] [RFC PATCH 0/3] add feature arc in rte_graph

2024-10-17 Thread Nitin Saxena
Hi Christophe,

Please see inline comments

Thanks,
Nitin

On Thu, Oct 17, 2024 at 2:02 PM Christophe Fontaine  wrote:
>
> Hi all,
>
> What about the following steps:
> - update the nodes so they work on the current layer (example: for all L3 
> nodes, the current mbuf data offset *must* be pointing to the IP header)

Agreed. It would be better if nodes uses
rte_pktmbuf_[append()/shrink() etc..] APIs to manipulate layer data
offset

> - define a public data structure that would be shared across nodes through 
> priv data, and not dynfields ?

Eventually public data structures should be defined to serve *a
purpose*. Do you refer to creating a generic public structure? If yes,
IMO, it may not be tuned for performance
IMO, we need to create public structures for each specific purpose.
Feature arc is also a public data structure which optimally saves
following variables in 8 byte compact structure
(rte_graph_feature_daa_t) for every interface
- rte_edge_t (uint16_t)
- next enabled feature (uint8_t) per index (from current node)
-  Per interface feature specific user_data (uint32_t)

Due to its compact nature, 8 such objects per interface can be saved
in one 64B cache line. So IMO, it is better to create public
structures for a given purpose and optimally define them fields and
APIs.
Also feature arc specific 3B data is saved in mbuf dynfield. Hard to
say if priv data would provide a better solution.

> This structure would be the "internal api" (so, that has to be tracked across 
> dpdk releases) between nodes.
> We’d need common data shared for all the nodes as well as specific data 
> between 2 nodes.
> As we get to this point, this (hopefully) will help with the node reusability.

Feature arc also maintains data between 2 nodes per interface and also
for all nodes which are added as features.

>
> - Update the feature arcs to leverage this well known structure, and refine 
> the api
> - Define which part of the stack needs to be defined as a feature arc, with 
> the benefit of the generic API to enable/disable that feature, and which part 
> needs to be dynamically pluggable.
> For instance, for a router, it may not make sense to define IPv4 support as a 
> feature arc.
> So, we’d statically connect eth_input to ip_input.

Agreed

> Yet, lldp support is a good candidate for a feature arc: we need to configure 
> it per interface, and this is independent of the main graph.
>

There would be more protocols which need to be enabled per interface

> WDYT?
> Christophe
>
> > On 17 Oct 2024, at 09:50, Robin Jarry  wrote:
> >
> > Hi Nitin, all,
> >
> > Nitin Saxena, Oct 17, 2024 at 09:03:
> >> Hi Robin/David and all,
> >>
> >> We realized the feature arc patch series is difficult to understand as a 
> >> new concept. Our objectives are following with feature arc changes
> >>
> >> 1. Allow reusability of standard DPDK nodes (defined in lib/nodes/*)
> >> with out-of-tree applications (like grout). Currently out-of-treegraph 
> >> applications are duplicating standard nodes but not reusingthe 
> >> standard ones which are available. In the long term, we wouldlike to 
> >> mature standard DPDK nodes with flexibility of hooking themto 
> >> out-of-tree application nodes.
> >
> > It would be ideal if the in-built nodes could be reused. When we started 
> > working on grout, I tried multiple approaches where I could reuse these 
> > nodes, but all failed. The nodes public API seems tailored for app/graph 
> > but does not fit well with other control plane implementations.
> >
> > One of the main issues I had is that the ethdev_rx and ethdev_tx nodes are 
> > cloned per rxq / txq associated with a graph worker. The rte_node API 
> > requires that every clone has a unique name. This in turn makes hot 
> > plugging of DPDK ports very complex, if not impossible.
> >
> > For example, with the in-built nodes, it is not possible to change the 
> > number of ports or their number of RX queues without destroying the whole 
> > graph and creating a new one from scratch.
> >
> > Also, the current implementation of "ip{4,6}-rewrite" handles writing 
> > ethernet header data. This would prevent it from using this node for an 
> > IP-in-IP tunnel interface as we did in grout.
> >
> > Do you think we could change the in-built nodes to enforce OSI layer 
> > separation of concerns? It would make them much more flexible. It may cause 
> > a slight drop of performance because you'd be splitting processing in two 
> > different nodes. But I think flexibility is more important. Otherwise, the 
> > in-built nodes can only be used for very specif

RE: [EXTERNAL] Re: [PATCH v3 0/5] add feature arc in rte_graph

2024-10-09 Thread Nitin Saxena
Hi Christophe,

Thanks for the review. See my comments inline

Thanks,
Nitin

> -Original Message-
> From: Christophe Fontaine 
> Sent: Wednesday, October 9, 2024 7:51 PM
> To: Nitin Saxena 
> Cc: Jerin Jacob ; Kiran Kumar Kokkilagadda
> ; Nithin Kumar Dabilpuram
> ; Zhirun Yan ; Robin
> Jarry ; dev@dpdk.org; Nitin Saxena
> 
> Subject: [EXTERNAL] Re: [PATCH v3 0/5] add feature arc in rte_graph
> 
> > On 9 Oct 2024, at 15:29, Nitin Saxena  wrote:
> >
> > Feature arc represents an ordered list of features/protocols at a
> > given networking layer. It is a high level abstraction to connect
> > various rte_graph nodes, as feature nodes, and allow packets steering
> > across these nodes in a generic manner.
> >
> > Features (or feature nodes) are nodes which handles partial or
> > complete handling of a protocol in fast path. Like ipv4-rewrite node,
> > which adds rewrite data to an outgoing IPv4 packet.
> >
> > However in above example, outgoing interface(say "eth0") may have
> > outbound IPsec policy enabled, hence packets must be steered from
> > ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
> > policy lookup. On the other hand, packets routed to another interface
> > (eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
> > is disabled on eth1. Feature-arc allows rte_graph applications to
> > manage such constraints easily
> >
> > Feature arc abstraction allows rte_graph based application to
> >
> > 1. Seamlessly steer packets across feature nodes based on whether
> > feature is enabled or disabled on an interface. Features enabled on
> > one interface may not be enabled on another interface with in a same
> > feature arc.
> >
> > 2. Allow enabling/disabling of features on an interface at runtime, so
> > that if a feature is disabled, packets associated with that interface
> > won't be steered to corresponding feature node.
> >
> > 3. Provides mechanism to hook custom/user-defined nodes to a feature
> > node and allow packet steering from feature node to custom node
> > without changing former's fast path function
> 
> Hi,
> 
> As a general comment, I would have expected a modification on
> rte_graph_walk so the nodes would *not* need to be aware of the enabled
> feature arcs.

If your point is that nodes has to adopt rte_feature_xxx() APIs in its 
"process_func()" to take advantage of feature arc, then your assessment is true.
So it is true that each feature node knows on which *arc* it sits on. However 
*unaware* part comes from the fact that once a node is integrated with 
feature_arc APIs, then the node does not need to know what is the next node to 
which current mbuf needs to be sent. So unaware functionality comes after 
integrating feature arc APIs. 

Regarding your rte_graph_walk() comment: IMO, decision of determining next_edge 
( or next node) lies with each *node* and not with rte_graph_walk(). Feature 
arc is extending the functionality of determining next_edge to control plane as 
well (as control plane enables/disable features). This way without changing 
feature node code, control plane is able to steer packets to different nodes. 
This is the major functionality feature arc is trying to simplify

> Yet, in the series, the nodes need to be aware if a feature arc is enabled or
> not.
> Isn’t this a contradiction or did I miss something ?
> 
> Christophe
> 
> >
> > 4. Allow expressing features in a particular sequential order so that
> > packets are steered in an ordered way across nodes in fast path. For
> > eg: if IPsec and IPv4 features are enabled on an ingress interface,
> > packets must be sent to IPsec inbound policy node first and then to
> > ipv4 lookup node.
> >
> > This patch series adds feature arc library in rte_graph and also adds
> > "ipv4-output" feature arc handling in "ipv4-rewrite" node.
> >
> > Changes in v3:
> > - rte_graph_feature_arc_t typedef from uint64_t to uintptr_t to fix
> > compilation on 32-bit machine
> > - Updated images in .png format
> > - Added ABI change section in release notes
> > - Fixed DPDK CI failures
> >
> > Changes in v2:
> > - Added unit tests for feature arc
> > - Fixed issues found in testing
> > - Added new public APIs rte_graph_feature_arc_feature_to_node(),
> >  rte_graph_feature_arc_feature_to_name(),
> >  rte_graph_feature_arc_num_features()
> > - Added programming guide for feature arc
> > - Added release notes for feature arc
> >
> > Nitin Saxena (5):
> >  graph: add feature arc support
> >  graph: add feature

RE: [EXTERNAL] Re: [PATCH v3 0/5] add feature arc in rte_graph

2024-10-09 Thread Nitin Saxena
Thanks Stephen. Will fix compilation in next version.

Thanks,
Nitin

> -Original Message-
> From: Stephen Hemminger 
> Sent: Wednesday, October 9, 2024 11:08 PM
> To: Nitin Saxena 
> Cc: Jerin Jacob ; Kiran Kumar Kokkilagadda
> ; Nithin Kumar Dabilpuram
> ; Zhirun Yan ; Robin
> Jarry ; Christophe Fontaine ;
> dev@dpdk.org; Nitin Saxena 
> Subject: [EXTERNAL] Re: [PATCH v3 0/5] add feature arc in rte_graph
> 
> On Wed, 9 Oct 2024 18: 59: 57 +0530 Nitin Saxena 
> wrote: > Feature arc represents an ordered list of features/protocols at a 
> given
> > networking layer. It is a high level abstraction to connect various > 
> > rte_graph
> 
> On Wed, 9 Oct 2024 18:59:57 +0530
> Nitin Saxena  wrote:
> 
> > Feature arc represents an ordered list of features/protocols at a
> > given networking layer. It is a high level abstraction to connect
> > various rte_graph nodes, as feature nodes, and allow packets steering
> > across these nodes in a generic manner.
> >
> > Features (or feature nodes) are nodes which handles partial or
> > complete handling of a protocol in fast path. Like ipv4-rewrite node,
> > which adds rewrite data to an outgoing IPv4 packet.
> >
> > However in above example, outgoing interface(say "eth0") may have
> > outbound IPsec policy enabled, hence packets must be steered from
> > ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
> > policy lookup. On the other hand, packets routed to another interface
> > (eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
> > is disabled on eth1. Feature-arc allows rte_graph applications to
> > manage such constraints easily
> >
> > Feature arc abstraction allows rte_graph based application to
> >
> > 1. Seamlessly steer packets across feature nodes based on whether
> > feature is enabled or disabled on an interface. Features enabled on
> > one interface may not be enabled on another interface with in a same
> > feature arc.
> >
> > 2. Allow enabling/disabling of features on an interface at runtime, so
> > that if a feature is disabled, packets associated with that interface
> > won't be steered to corresponding feature node.
> >
> > 3. Provides mechanism to hook custom/user-defined nodes to a feature
> > node and allow packet steering from feature node to custom node
> > without changing former's fast path function
> >
> > 4. Allow expressing features in a particular sequential order so that
> > packets are steered in an ordered way across nodes in fast path. For
> > eg: if IPsec and IPv4 features are enabled on an ingress interface,
> > packets must be sent to IPsec inbound policy node first and then to
> > ipv4 lookup node.
> >
> > This patch series adds feature arc library in rte_graph and also adds
> > "ipv4-output" feature arc handling in "ipv4-rewrite" node.
> >
> > Changes in v3:
> > - rte_graph_feature_arc_t typedef from uint64_t to uintptr_t to fix
> >   compilation on 32-bit machine
> > - Updated images in .png format
> > - Added ABI change section in release notes
> > - Fixed DPDK CI failures
> >
> > Changes in v2:
> > - Added unit tests for feature arc
> > - Fixed issues found in testing
> > - Added new public APIs rte_graph_feature_arc_feature_to_node(),
> >   rte_graph_feature_arc_feature_to_name(),
> >   rte_graph_feature_arc_num_features()
> > - Added programming guide for feature arc
> > - Added release notes for feature arc
> >
> > Nitin Saxena (5):
> >   graph: add feature arc support
> >   graph: add feature arc option in graph create
> >   graph: add IPv4 output feature arc
> >   test/graph_feature_arc: add functional tests
> >   docs: add programming guide for feature arc
> >
> >  app/test/meson.build|1 +
> >  app/test/test_graph_feature_arc.c   | 1410 +++
> >  doc/guides/prog_guide/graph_lib.rst |  288 
> >  doc/guides/prog_guide/img/feature_arc-1.png |  Bin 0 -> 61532 bytes
> > doc/guides/prog_guide/img/feature_arc-2.png |  Bin 0 -> 155806 bytes
> > doc/guides/prog_guide/img/feature_arc-3.png |  Bin 0 -> 143697 bytes
> >  doc/guides/rel_notes/release_24_11.rst  |   13 +
> >  lib/graph/graph.c   |1 +
> >  lib/graph/graph_feature_arc.c   | 1223 
> >  lib/graph/graph_populate.c  |7 +-
> >  lib/graph/graph_private.h   |3 +
> >  lib/graph/meson.build   

RE: [RFC PATCH 1/3] graph: add feature arc support

2024-10-09 Thread Nitin Saxena
Hi Kiran,

See my inline comments. Somehow  I forgot to respond earlier

Thanks,
Nitin

> -Original Message-
> From: Kiran Kumar Kokkilagadda 
> Sent: Wednesday, September 11, 2024 10:11 AM
> To: Nitin Saxena ; Jerin Jacob ;
> Nithin Kumar Dabilpuram ; Zhirun Yan
> 
> Cc: dev@dpdk.org; Nitin Saxena 
> Subject: RE: [RFC PATCH 1/3] graph: add feature arc support
> 
> 
> 
> > -----Original Message-
> > From: Nitin Saxena 
> > Sent: Saturday, September 7, 2024 1:01 PM
> > To: Jerin Jacob ; Kiran Kumar Kokkilagadda
> > ; Nithin Kumar Dabilpuram
> > ; Zhirun Yan 
> > Cc: dev@dpdk.org; Nitin Saxena 
> > Subject: [RFC PATCH 1/3] graph: add feature arc support
> >
> > add feature arc to allow dynamic steering of packets across graph nodes
> > based on protocol features enabled on incoming or outgoing interface
> >
> > Signed-off-by: Nitin Saxena 
> > ---
> >  lib/graph/graph_feature_arc.c| 959 +++
> >  lib/graph/meson.build|   2 +
> >  lib/graph/rte_graph_feature_arc.h| 373 +
> >  lib/graph/rte_graph_feature_arc_worker.h | 548 +
> >  lib/graph/version.map|  17 +
> >  5 files changed, 1899 insertions(+)
> >  create mode 100644 lib/graph/graph_feature_arc.c
> >  create mode 100644 lib/graph/rte_graph_feature_arc.h
> >  create mode 100644 lib/graph/rte_graph_feature_arc_worker.h
> >
> > diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
> > new file mode 100644
> > index 00..3b05bac137
> > --- /dev/null
> > +++ b/lib/graph/graph_feature_arc.c
> > @@ -0,0 +1,959 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(C) 2024 Marvell International Ltd.
> > + */
> > +
> > +#include "graph_private.h"
> > +#include 
> > +#include 
> > +
> > +#define __RTE_GRAPH_FEATURE_ARC_MAX 32
> > +
> > +#define ARC_PASSIVE_LIST(arc) (arc->active_feature_list ^ 0x1)
> > +
> > +#define rte_graph_uint_cast(x) ((unsigned int)x)
> > +#define feat_dbg graph_err
> > +
> > +rte_graph_feature_arc_main_t *__feature_arc_main;
> > +
> > +/* Make sure fast path cache line is compact */
> > +_Static_assert((offsetof(struct rte_graph_feature_arc,
> slow_path_variables)
> > +   - offsetof(struct rte_graph_feature_arc, fast_path_variables))
> > +  <= RTE_CACHE_LINE_SIZE);
> > +
> > +
> > +static int
> > +feature_lookup(struct rte_graph_feature_arc *arc, const char *feat_name,
> > +  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
> > +{
> > +   struct rte_graph_feature_node_list *finfo = NULL;
> > +   const char *name;
> > +
> > +   if (!feat_name)
> > +   return -1;
> > +
> > +   if (slot)
> > +   *slot = 0;
> > +
> > +   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
> > +   RTE_VERIFY(finfo->feature_arc == arc);
> > +   name = rte_node_id_to_name(finfo->feature_node->id);
> > +   if (!strncmp(name, feat_name, RTE_GRAPH_NAMESIZE)) {
> > +   if (ffinfo)
> > +   *ffinfo = finfo;
> > +   return 0;
> > +   }
> > +   if (slot)
> > +   (*slot)++;
> > +   }
> > +   return -1;
> > +}
> > +
> > +static int
> > +feature_arc_node_info_lookup(struct rte_graph_feature_arc *arc, uint32_t
> > feature_index,
> > +struct rte_graph_feature_node_list **ppfinfo)
> > +{
> > +   struct rte_graph_feature_node_list *finfo = NULL;
> > +   uint32_t index = 0;
> > +
> > +   if (!ppfinfo)
> > +   return -1;
> > +
> > +   *ppfinfo = NULL;
> > +   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
> > +   if (index == feature_index) {
> > +   if (finfo->node_index == feature_index)
> > +   return -1;
> > +   *ppfinfo = finfo;
> > +   }
> > +   index++;
> > +   }
> > +   if (feature_index && (index >= feature_index))
> > +   return -1;
> > +
> > +   return 0;
> > +}
> > +
> > +static void
> > +prepare_feature_arc(struct rte_graph_feature_arc *arc)
> > +{
> > +   struct rte_graph_feature_node_list *finfo = NULL;
> > +   uint32_t index = 0;
> > +
> > +   STAILQ_FOREACH(finfo, &a

[PATCH v5 1/5] graph: add feature arc support

2024-10-14 Thread Nitin Saxena
add feature arc to allow dynamic steering of packets across graph nodes
based on protocol features enabled on incoming or outgoing interface

Signed-off-by: Nitin Saxena 
---
 doc/guides/rel_notes/release_24_11.rst   |   10 +
 lib/graph/graph_feature_arc.c| 1236 ++
 lib/graph/meson.build|2 +
 lib/graph/rte_graph_feature_arc.h|  431 
 lib/graph/rte_graph_feature_arc_worker.h |  679 
 lib/graph/version.map|   20 +
 6 files changed, 2378 insertions(+)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

diff --git a/doc/guides/rel_notes/release_24_11.rst 
b/doc/guides/rel_notes/release_24_11.rst
index a563812d02..1299de886a 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -162,6 +162,16 @@ New Features
 
   * Added independent enqueue feature.
 
+* **Added feature arc abstraction in graph library.**
+
+  Feature arc abstraction helps ``rte_graph`` based applications to steer
+  packets across different node path(s) based on the features (or protocols)
+  enabled on interfaces. Different feature node paths can be enabled/disabled
+  at runtime on some or on all interfaces. This abstraction also help
+  applications to hook their ``custom nodes`` in standard DPDK node paths
+  without any code changes in the later.
+
+  * Added ``ip4-output`` feature arc processing in ``ip4_rewrite`` node.
 
 Removed Items
 -
diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 00..0f8633c317
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,1236 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include 
+#include 
+
+#define ARC_PASSIVE_LIST(list) (list ^ 0x1)
+
+#define rte_graph_uint_cast(x) ((unsigned int)x)
+#define feat_dbg graph_dbg
+
+static rte_graph_feature_arc_main_t *__rte_graph_feature_arc_main;
+
+/* Make sure fast path cache line is compact */
+_Static_assert((offsetof(struct rte_graph_feature_arc, slow_path_variables)
+   - offsetof(struct rte_graph_feature_arc, fast_path_variables))
+  <= RTE_CACHE_LINE_SIZE,
+  "Fast path feature arc variables exceed cache line size");
+
+#define connect_graph_nodes(node1, node2, edge, arc_name) \
+   __connect_graph_nodes(node1, node2, edge, arc_name, __LINE__)
+
+#define FEAT_COND_ERR(cond, fmt, ...)  \
+   do {   \
+   if (cond)  \
+   graph_err(fmt, ##__VA_ARGS__); \
+   } while (0)
+
+/*
+ * lookup feature name and get control path node_list as well as feature index
+ * at which it is inserted
+ */
+static int
+feature_lookup(struct rte_graph_feature_arc *arc, const char *feat_name,
+  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   const char *name;
+   uint32_t fi = 0;
+
+   if (!feat_name)
+   return -1;
+
+   if (slot)
+   *slot = UINT32_MAX;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   RTE_VERIFY(finfo->feature_arc == arc);
+   name = rte_node_id_to_name(finfo->feature_node->id);
+   if (!strncmp(name, feat_name, strlen(name))) {
+   if (ffinfo)
+   *ffinfo = finfo;
+   if (slot)
+   *slot = fi;
+   return 0;
+   }
+   fi++;
+   }
+   return -1;
+}
+
+/* Lookup used only during rte_graph_feature_add() */
+static int
+feature_add_lookup(struct rte_graph_feature_arc *arc, const char *feat_name,
+  struct rte_graph_feature_node_list **ffinfo, uint32_t *slot)
+{
+   struct rte_graph_feature_node_list *finfo = NULL;
+   const char *name;
+   uint32_t fi = 0;
+
+   if (!feat_name)
+   return -1;
+
+   if (slot)
+   *slot = 0;
+
+   STAILQ_FOREACH(finfo, &arc->all_features, next_feature) {
+   RTE_VERIFY(finfo->feature_arc == arc);
+   name = rte_node_id_to_name(finfo->feature_node->id);
+   if (!strncmp(name, feat_name, strlen(name))) {
+   if (ffinfo)
+   *ffinfo = finfo;
+   if (slot)
+   *slot = fi;
+   return 0;
+   }
+   /* Update slot where new feature can be added */
+

[PATCH v5 4/5] test/graph_feature_arc: add functional tests

2024-10-14 Thread Nitin Saxena
Added functional unit test case for verifying feature arc control plane
and fast path APIs

How to run:
$ echo "graph_feature_arc_autotest" | ./bin/dpdk-test

Signed-off-by: Nitin Saxena 
---
 app/test/meson.build  |1 +
 app/test/test_graph_feature_arc.c | 1410 +
 2 files changed, 1411 insertions(+)
 create mode 100644 app/test/test_graph_feature_arc.c

diff --git a/app/test/meson.build b/app/test/meson.build
index e29258e6ec..740fa1bfb4 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -90,6 +90,7 @@ source_file_deps = {
 'test_func_reentrancy.c': ['hash', 'lpm'],
 'test_graph.c': ['graph'],
 'test_graph_perf.c': ['graph'],
+'test_graph_feature_arc.c': ['graph'],
 'test_hash.c': ['net', 'hash'],
 'test_hash_functions.c': ['hash'],
 'test_hash_multiwriter.c': ['hash'],
diff --git a/app/test/test_graph_feature_arc.c 
b/app/test/test_graph_feature_arc.c
new file mode 100644
index 00..58b676e215
--- /dev/null
+++ b/app/test/test_graph_feature_arc.c
@@ -0,0 +1,1410 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "test.h"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#ifndef RTE_EXEC_ENV_WINDOWS
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define MBUFF_SIZE 512
+#define TEST_ARC1_NAME "arc1"
+#define TEST_ARC2_NAME "arc2"
+#define MAX_INDEXES 10
+#define MAX_FEATURES 5
+
+#define SOURCE1 "test_node_arc_source1"
+#define INPUT_STATIC "test_node_arc_input_static"
+#define OUTPUT_STATIC "test_node_arc_output_static"
+#define PKT_FREE_STATIC "test_node_arc_pkt_free_static"
+#define ARC1_FEATURE1 "test_node_arc1_feature1"
+#define ARC1_FEATURE2 "test_node_arc1_feature2"
+#define ARC2_FEATURE1 "test_node_arc2_feature1"
+#define ARC2_FEATURE2 "test_node_arc2_feature2"
+#define ARC2_FEATURE3 "test_node_arc2_feature3"
+#define DUMMY1_STATIC "test_node_arc_dummy1_static"
+#define DUMMY2_STATIC "test_node_arc_dummy2_static"
+
+/* (Node index, Node Name, feature user data base */
+#define FOREACH_TEST_NODE_ARC {\
+   R(0, SOURCE1, 64)   \
+   R(1, INPUT_STATIC, 128) \
+   R(2, OUTPUT_STATIC, 256)\
+   R(3, PKT_FREE_STATIC, 512)  \
+   R(4, ARC1_FEATURE1, 1024)   \
+   R(5, ARC1_FEATURE2, 2048)   \
+   R(6, ARC2_FEATURE1, 4096)   \
+   R(7, ARC2_FEATURE2, 8192)   \
+   R(8, ARC2_FEATURE3, 16384)  \
+   R(9, DUMMY1_STATIC, 32768)  \
+   R(10, DUMMY2_STATIC, 65536) \
+   }
+
+/**
+ * ARC1: Feature arc on ingress interface
+ * ARC2: Feature arc on egress interface
+ * XX_static: Static nodes
+ * XX_featureX: Feature X on arc
+ *
+ *-> ARC1_FEATURE1
+ *   || |
+ *   || v
+ *   ||   ARC1_FEATURE2
+ *   || |
+ *   |v v
+ *  SOURCE1 ->-> INPUT_STATIC --> OUTPUT_STATIC -> PKT_FREE_STATIC
+ * |   |  | ^  ^ ^
+ * |   |  | |  | |
+ * |   |   --> ARC2_FEATURE1   | |
+ * |   |  ^ ^  | |
+ * |   |  | |  | |
+ * |--c-> ARC2_FEATURE2  |
+ * |  | ^|
+ * |  | ||
+ *  --> ARC2_FEATURE3 ---
+ */
+const char *node_names_feature_arc[] = {
+   SOURCE1, INPUT_STATIC, OUTPUT_STATIC, PKT_FREE_STATIC,
+   ARC1_FEATURE1, ARC1_FEATURE2, ARC2_FEATURE1, ARC2_FEATURE2, 
ARC2_FEATURE3,
+   DUMMY1_STATIC, DUMMY2_STATIC
+};
+
+#define MAX_NODES  RTE_DIM(node_names_feature_arc)
+
+/* Function declarations */
+static uint16_t
+source1_fn(struct rte_graph *graph, struct rte_node *node,
+  void **objs, uint16_t nb_objs);
+static uint16_t
+input_fn(struct rte_graph *graph, struct rte_node *node,
+void **objs, uint16_t nb_objs);
+static uint16_t
+input_fa_fn(struct rte_graph *graph, struct rte_node *node,
+   void **objs, uint16_t nb_objs);
+static uint16_t
+output_fn(struct rte_graph *graph, struct rte_node *node,
+ void **o

[PATCH v5 3/5] graph: add IPv4 output feature arc

2024-10-14 Thread Nitin Saxena
add ipv4-output feature arc in ipv4-rewrite node to allow
custom/standard nodes(like outbound IPsec policy node) in outgoing
forwarding path

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_rewrite.c  | 476 +---
 lib/node/ip4_rewrite_priv.h |  15 +-
 lib/node/node_private.h |  20 +-
 lib/node/rte_node_ip4_api.h |   3 +
 4 files changed, 417 insertions(+), 97 deletions(-)

diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index 34a920df5e..824ef9a4cd 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -15,39 +15,156 @@
 #include "ip4_rewrite_priv.h"
 #include "node_private.h"
 
+#define ALL_PKT_MASK 0xf
+
 struct ip4_rewrite_node_ctx {
+   rte_graph_feature_arc_t output_feature_arc;
/* Dynamic offset to mbuf priv1 */
int mbuf_priv1_off;
/* Cached next index */
uint16_t next_index;
+   uint16_t last_tx;
 };
 
+typedef struct rewrite_priv_vars {
+   union {
+   struct {
+   rte_xmm_t xmm1;
+   };
+   struct __rte_packed {
+   uint16_t next0;
+   uint16_t next1;
+   uint16_t next2;
+   uint16_t next3;
+   uint16_t last_tx_interface;
+   uint16_t last_if_feature;
+   uint16_t actual_feat_mask;
+   uint16_t speculative_feat_mask;
+   };
+   };
+} rewrite_priv_vars_t;
+
 static struct ip4_rewrite_node_main *ip4_rewrite_nm;
 
 #define IP4_REWRITE_NODE_LAST_NEXT(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->next_index)
 
+#define IP4_REWRITE_NODE_LAST_TX(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->last_tx)
+
 #define IP4_REWRITE_NODE_PRIV1_OFF(ctx) \
(((struct ip4_rewrite_node_ctx *)ctx)->mbuf_priv1_off)
 
-static uint16_t
-ip4_rewrite_node_process(struct rte_graph *graph, struct rte_node *node,
-void **objs, uint16_t nb_objs)
+#define IP4_REWRITE_NODE_OUTPUT_FEATURE_ARC(ctx) \
+   (((struct ip4_rewrite_node_ctx *)ctx)->output_feature_arc)
+
+static __rte_always_inline void
+prefetch_mbuf_and_dynfield(struct rte_mbuf *mbuf)
 {
+   /* prefetch first cache line required for accessing buf_addr */
+   rte_prefetch0((void *)mbuf);
+}
+
+static __rte_always_inline void
+check_output_feature_x4(struct rte_graph_feature_arc *arc,
+   const rte_graph_feature_rt_list_t flist,
+   rewrite_priv_vars_t *pvar, struct node_mbuf_priv1 
*priv0,
+   struct node_mbuf_priv1 *priv1, struct node_mbuf_priv1 
*priv2,
+   struct node_mbuf_priv1 *priv3)
+{
+   uint32_t mask = 0;
+   uint16_t xor = 0;
+
+   /*
+* interface edge's start from 1 and not from 0 as "pkt_drop"
+* is next node at 0th index
+*/
+   priv0->if_index = pvar->next0 - 1;
+   priv1->if_index = pvar->next1 - 1;
+   priv2->if_index = pvar->next2 - 1;
+   priv3->if_index = pvar->next3 - 1;
+
+   /* Find out if all packets are sent to last_tx_interface */
+   xor = pvar->last_tx_interface ^ priv0->if_index;
+   xor += priv0->if_index ^ priv1->if_index;
+   xor += priv1->if_index ^ priv2->if_index;
+   xor += priv2->if_index ^ priv3->if_index;
+
+   if (likely(!xor)) {
+   /* copy last interface feature and feature mask */
+   priv0->current_feature = priv1->current_feature =
+   priv2->current_feature = priv3->current_feature =
+   pvar->last_if_feature;
+   pvar->actual_feat_mask = pvar->speculative_feat_mask;
+   } else {
+   /* create a mask for index which does not have feature
+* Also override next edge and if feature enabled, get feature
+*/
+   mask = rte_graph_feature_arc_feature_set(arc, flist, 
priv0->if_index,
+
&priv0->current_feature,
+&pvar->next0);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv1->if_index,
+
&priv1->current_feature,
+&pvar->next1)) << 
1);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv2->if_index,
+
&priv2->current_feature,
+&pvar->next2)) << 
2);
+
+   mask |= ((rte_graph_feature_arc_feature_set(arc, flist, 
priv3->if_index,
+   

[PATCH v5 5/5] docs: add programming guide for feature arc

2024-10-14 Thread Nitin Saxena
Updated graph library guide with feature arc

Signed-off-by: Nitin Saxena 
---
 doc/guides/prog_guide/graph_lib.rst | 288 +++
 doc/guides/prog_guide/img/feature_arc-1.svg | 277 +++
 doc/guides/prog_guide/img/feature_arc-2.svg | 511 
 doc/guides/prog_guide/img/feature_arc-3.svg | 318 
 4 files changed, 1394 insertions(+)
 create mode 100644 doc/guides/prog_guide/img/feature_arc-1.svg
 create mode 100644 doc/guides/prog_guide/img/feature_arc-2.svg
 create mode 100644 doc/guides/prog_guide/img/feature_arc-3.svg

diff --git a/doc/guides/prog_guide/graph_lib.rst 
b/doc/guides/prog_guide/graph_lib.rst
index ad09bdfe26..d98b48b0e5 100644
--- a/doc/guides/prog_guide/graph_lib.rst
+++ b/doc/guides/prog_guide/graph_lib.rst
@@ -547,3 +547,291 @@ on success packet is enqueued to ``udp4_input`` node.
 
 Hash lookup is performed in ``udp4_input`` node with registered destination 
port
 and destination port in UDP packet , on success packet is handed to 
``udp_user_node``.
+
+Feature Arc
+---
+`Feature arc` represents an ordered list of `protocols/features` at a given
+networking layer. It is a high level abstraction to connect various `feature`
+nodes in `rte_graph` instance and allows seamless packets steering based on the
+sequence of enabled features at runtime on each interface.
+
+`Features` (or feature nodes) are nodes which handle partial or complete
+protocol processing in a given direction. For instance, `ipv4-rewrite` and
+`IPv4 IPsec encryption` are outbound features while `ipv4-lookup` and `IPv4
+IPsec decryption` are inbound features.  Further, `ipv4-rewrite` and `IPv4
+IPsec encryption` can collectively represent a `feature arc` towards egress
+direction with ordering constraints that `IPv4 IPsec encryption` must be
+performed before `ipv4-rewrite`.  Similarly, `IPv4 IPsec decryption` and
+`ipv4-lookup` can represent a `feature arc` in an ingress direction. Both of
+these `feature arc` can co-exist at an IPv4 layer in egress and ingress
+direction respectively.
+
+A `feature` can be represented by a single node or collection of multiple nodes
+performing feature processing collectively.
+
+.. figure:: img/feature_arc-1.*
+   :alt: feature-arc-1
+   :width: 350px
+   :align: center
+
+   Feature Arc overview
+
+Each `feature arc` is associated with a `Start` node from which all features in
+a feature arc are connected.  A `start` node itself is not a `feature` node but
+it is where `first enabled feature` is checked in fast path. In above figure,
+`Node-A` represents a `start node`.  There may be a `Sink` node as well which
+is child node for every feature in an arc.  'Sink` node is responsible of
+consuming those packets which are not consumed by intermediate enabled features
+between `start` and `sink` node. `Sink` node, if present, is the last enabled
+feature in a feature arc. A `feature` node statically connected to `start` node
+must also be added via feature arc API, `rte_graph_feature_add()``. Here 
`Node-B`
+acts as a `sink` node which is statically linked to `Node A`. `Feature` nodes
+are connected via `rte_graph_feature_add()` which takes care of connecting
+all `feature` nodes with each other and start node.
+
+.. code-block:: bash
+   :linenos:
+   :emphasize-lines: 8
+   :caption: Node-B statically linked to Node-A
+
+   static struct rte_node_register node_A_node = {
+.process = node_A_process_func,
+...
+...
+.name = "Node-A",
+.next_nodes =
+   {
+  [0] = "Node-B",
+   },
+.nb_edges = 1,
+   };
+
+When multiple features are enabled on an interface, it may be required to steer
+packets across `features` in a given order. For instance, if `Feature 1` and
+`Feature 2` both are enabled on an interface ``X``, it may be required to send
+packets to `Feature-1` before `Feature-2`. Such ordering constraints can be
+easily expressed with `feature arc`. In this case, `Feature 1` is called as
+``First Feature`` and `Feature 2` is called as ``Next Feature`` to `Feature 1`.
+
+.. figure:: img/feature_arc-2.*
+   :alt: feature-arc-2
+   :width: 600px
+   :align: center
+
+   First and Next features and their ordering
+
+In similar manner, even application specific ``custom features`` can be hooked
+to standard nodes. It is to be noted that this `custom feature` hooking to
+`feature arc` aware node does not require any code changes.
+
+It may be obvious by now that `features` enabled on one interface does not
+affect packets on other interfaces. In above example, if no feature is
+enabled on an interface ``X``, packets destined to interface ``X`` would be
+directly sent to `Node-B` from `Node-A`.
+
+.. figure:: img/feature_arc-3.*
+   :alt: feature-arc-3
+   :width: 550px
+   :align: center
+
+   Feature-2 consumed/non-consumed packet path
+
+When a `Feature-X` node receives packets via feature arc, it may decide whether
+to ``consume packet`` or send to `next

[PATCH v5 0/5] add feature arc in rte_graph

2024-10-14 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to connect various
rte_graph nodes, as feature nodes, and allow packets steering across
these nodes in a generic manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
policy lookup. On the other hand, packets routed to another interface
(eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
is disabled on eth1. Feature-arc allows rte_graph applications to manage
such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Seamlessly steer packets across feature nodes based on whether
feature is enabled or disabled on an interface. Features enabled on one
interface may not be enabled on another interface with in a same feature
arc.

2. Allow enabling/disabling of features on an interface at runtime,
so that if a feature is disabled, packets associated with that interface
won't be steered to corresponding feature node.

3. Provides mechanism to hook custom/user-defined nodes to a feature
node and allow packet steering from feature node to custom node without
changing former's fast path function

4. Allow expressing features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Changes in v2:
- Added unit tests for feature arc
- Fixed issues found in testing
- Added new public APIs rte_graph_feature_arc_feature_to_node(),
  rte_graph_feature_arc_feature_to_name(),
  rte_graph_feature_arc_num_features()
- Added programming guide for feature arc
- Added release notes for feature arc

Changes in v3:
- rte_graph_feature_arc_t typedef from uint64_t to uintptr_t to fix
  compilation on 32-bit machine
- Updated images in .png format
- Added ABI change section in release notes
- Fixed DPDK CI failures

Changes in v4:
- Fixed clang build compilations
- Captured `feat_arc_proc` function in ABI change section of release notes

Changes in v5:
- Updated images in .svg format

Nitin Saxena (5):
  graph: add feature arc support
  graph: add feature arc option in graph create
  graph: add IPv4 output feature arc
  test/graph_feature_arc: add functional tests
  docs: add programming guide for feature arc

 app/test/meson.build|1 +
 app/test/test_graph_feature_arc.c   | 1410 +++
 doc/guides/prog_guide/graph_lib.rst |  288 
 doc/guides/prog_guide/img/feature_arc-1.svg |  277 
 doc/guides/prog_guide/img/feature_arc-2.svg |  511 +++
 doc/guides/prog_guide/img/feature_arc-3.svg |  318 +
 doc/guides/rel_notes/release_24_11.rst  |   17 +
 lib/graph/graph.c   |1 +
 lib/graph/graph_feature_arc.c   | 1236 
 lib/graph/graph_populate.c  |7 +-
 lib/graph/graph_private.h   |3 +
 lib/graph/meson.build   |2 +
 lib/graph/node.c|2 +
 lib/graph/rte_graph.h   |3 +
 lib/graph/rte_graph_feature_arc.h   |  431 ++
 lib/graph/rte_graph_feature_arc_worker.h|  679 +
 lib/graph/version.map   |   20 +
 lib/node/ip4_rewrite.c  |  476 +--
 lib/node/ip4_rewrite_priv.h |   15 +-
 lib/node/node_private.h |   20 +-
 lib/node/rte_node_ip4_api.h |3 +
 21 files changed, 5622 insertions(+), 98 deletions(-)
 create mode 100644 app/test/test_graph_feature_arc.c
 create mode 100644 doc/guides/prog_guide/img/feature_arc-1.svg
 create mode 100644 doc/guides/prog_guide/img/feature_arc-2.svg
 create mode 100644 doc/guides/prog_guide/img/feature_arc-3.svg
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

-- 
2.43.0



[PATCH v5 2/5] graph: add feature arc option in graph create

2024-10-14 Thread Nitin Saxena
Added option in graph create to call feature-specific process node
functions. This removes extra overhead for checking feature arc status
in nodes where application is not using feature arc processing

Signed-off-by: Pavan Nikhilesh 
Signed-off-by: Nitin Saxena 
---
 doc/guides/rel_notes/release_24_11.rst | 7 +++
 lib/graph/graph.c  | 1 +
 lib/graph/graph_populate.c | 7 ++-
 lib/graph/graph_private.h  | 3 +++
 lib/graph/node.c   | 2 ++
 lib/graph/rte_graph.h  | 3 +++
 6 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/doc/guides/rel_notes/release_24_11.rst 
b/doc/guides/rel_notes/release_24_11.rst
index 1299de886a..451627a331 100644
--- a/doc/guides/rel_notes/release_24_11.rst
+++ b/doc/guides/rel_notes/release_24_11.rst
@@ -251,6 +251,13 @@ ABI Changes
 
 * eventdev: Added ``preschedule_type`` field to ``rte_event_dev_config`` 
structure.
 
+* graph: Added feature arc specific `feat_arc_proc` node callback function in
+  `struct rte_node_register`. If this function is not NULL and
+  `feature_arc_enable` is set to `true` in `struct rte_graph_param`,
+  rte_graph_walk() calls `feat_arc_proc` callback function instead of `process`
+
+* graph: Added `feature_arc_enable` parameter in `struct rte_graph_param` for
+  calling non-NULL `feat_arc_proc` callback function by `rte_graph_walk()`
 
 Known Issues
 
diff --git a/lib/graph/graph.c b/lib/graph/graph.c
index dff8e690a8..a764c5824e 100644
--- a/lib/graph/graph.c
+++ b/lib/graph/graph.c
@@ -455,6 +455,7 @@ rte_graph_create(const char *name, struct rte_graph_param 
*prm)
graph->parent_id = RTE_GRAPH_ID_INVALID;
graph->lcore_id = RTE_MAX_LCORE;
graph->num_pkt_to_capture = prm->num_pkt_to_capture;
+   graph->feature_arc_enabled = prm->feature_arc_enable;
if (prm->pcap_filename)
rte_strscpy(graph->pcap_filename, prm->pcap_filename, 
RTE_GRAPH_PCAP_FILE_SZ);
 
diff --git a/lib/graph/graph_populate.c b/lib/graph/graph_populate.c
index ed596a7711..5d8aa7b903 100644
--- a/lib/graph/graph_populate.c
+++ b/lib/graph/graph_populate.c
@@ -79,8 +79,13 @@ graph_nodes_populate(struct graph *_graph)
if (graph_pcap_is_enable()) {
node->process = graph_pcap_dispatch;
node->original_process = graph_node->node->process;
-   } else
+   if (_graph->feature_arc_enabled && 
graph_node->node->feat_arc_proc)
+   node->original_process = 
graph_node->node->feat_arc_proc;
+   } else {
node->process = graph_node->node->process;
+   if (_graph->feature_arc_enabled && 
graph_node->node->feat_arc_proc)
+   node->process = graph_node->node->feat_arc_proc;
+   }
memcpy(node->name, graph_node->node->name, RTE_GRAPH_NAMESIZE);
pid = graph_node->node->parent_id;
if (pid != RTE_NODE_ID_INVALID) { /* Cloned node */
diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
index d557d55f2d..58ba0abeff 100644
--- a/lib/graph/graph_private.h
+++ b/lib/graph/graph_private.h
@@ -56,6 +56,7 @@ struct node {
unsigned int lcore_id;
/**< Node runs on the Lcore ID used for mcore dispatch model. */
rte_node_process_t process;   /**< Node process function. */
+   rte_node_process_t feat_arc_proc; /**< Node feature-arch process 
function. */
rte_node_init_t init; /**< Node init function. */
rte_node_fini_t fini; /**< Node fini function. */
rte_node_t id;/**< Allocated identifier for the node. */
@@ -126,6 +127,8 @@ struct graph {
/**< Number of packets to be captured per core. */
char pcap_filename[RTE_GRAPH_PCAP_FILE_SZ];
/**< pcap file name/path. */
+   uint8_t feature_arc_enabled;
+   /**< Graph feature arc. */
STAILQ_HEAD(gnode_list, graph_node) node_list;
/**< Nodes in a graph. */
 };
diff --git a/lib/graph/node.c b/lib/graph/node.c
index 99a9622779..d8fd273543 100644
--- a/lib/graph/node.c
+++ b/lib/graph/node.c
@@ -90,6 +90,7 @@ __rte_node_register(const struct rte_node_register *reg)
goto free;
node->flags = reg->flags;
node->process = reg->process;
+   node->feat_arc_proc = reg->feat_arc_proc;
node->init = reg->init;
node->fini = reg->fini;
node->nb_edges = reg->nb_edges;
@@ -137,6 +138,7 @@ node_clone(struct node *node, const char *name)
/* Clone the source node */
reg->flags = node->flags;
reg->process = node->process;
+   reg->feat_arc_proc = node

[PATCH v6 0/4] add feature arc in rte_graph

2025-01-02 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to connect various
rte_graph nodes, as feature nodes, and allow packets steering across
these nodes in a generic manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to ipsec-outbound-policy node for outbound IPsec
policy lookup. On the other hand, packets routed to another interface
(eth1) will not be sent to ipsec-outbound-policy node as IPsec feature
is disabled on eth1. Feature-arc allows rte_graph applications to manage
such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Seamlessly steer packets across feature nodes based on whether
feature is enabled or disabled on an interface. Features enabled on one
interface may not be enabled on another interface with in a same feature
arc.

2. Allow enabling/disabling of features on an interface at runtime,
so that if a feature is disabled, packets associated with that interface
won't be steered to corresponding feature node.

3. Provides mechanism to hook custom/user-defined nodes to a feature
node and allow packet steering from feature node to custom node without
changing former's fast path function

4. Allow expressing features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Changes in v6:
- Rebased to latest main for DPDK-25.03
- Added constructor based feature arc/feature registration
- Changed design to handle fast path synchronization via RCU mechanism
  when any feature is enabled or disabled
- Added feature arc specific mbuf dynamic field to carry feature data
  across nodes
- Added feature arc example in app/graph
- Programming guide and functional test cases in future versions

Nitin Saxena (4):
  graph: add API to override node process function
  graph: add feature arc abstraction
  ip4: add ip4 output feature arc
  app/graph: add custom feature nodes for ip4 output arc

 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 +
 doc/api/doxy-api-index.md|2 +
 doc/guides/rel_notes/release_25_03.rst   |   10 +
 lib/graph/graph_feature_arc.c| 1780 ++
 lib/graph/graph_private.h|   15 +
 lib/graph/meson.build|4 +-
 lib/graph/node.c |   23 +
 lib/graph/rte_graph_feature_arc.h|  552 +++
 lib/graph/rte_graph_feature_arc_worker.h |  608 
 lib/graph/version.map|   20 +
 lib/node/ethdev_ctrl.c   |8 +
 lib/node/interface_tx_feature.c  |  133 ++
 lib/node/interface_tx_feature_priv.h |   33 +
 lib/node/ip4_rewrite.c   |  298 +++-
 lib/node/meson.build |1 +
 lib/node/node_private.h  |1 +
 lib/node/rte_node_ip4_api.h  |4 +
 24 files changed, 3838 insertions(+), 6 deletions(-)
 create mode 100644 app/graph/feature.c
 create mode 100644 app/graph/feature.h
 create mode 100644 app/graph/ip4_output_hook.c
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h
 create mode 100644 lib/node/interface_tx_feature.c
 create mode 100644 lib/node/interface_tx_feature_priv.h

-- 
2.43.0



[PATCH v6 1/4] graph: add API to override node process function

2025-01-02 Thread Nitin Saxena
New API used by feature arc library to override node's original
process() func.

Signed-off-by: Nitin Saxena 
---
 lib/graph/graph_private.h | 11 +++
 lib/graph/node.c  | 23 +++
 2 files changed, 34 insertions(+)

diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
index da48d73587..ceff0c8f50 100644
--- a/lib/graph/graph_private.h
+++ b/lib/graph/graph_private.h
@@ -198,6 +198,17 @@ struct node_head *node_list_head_get(void);
  */
 struct node *node_from_name(const char *name);
 
+/**
+ * @internal
+ *
+ * Override process func of a node.
+ *
+ * @return
+ *   - 0: Success.
+ *   - <0: Error
+ */
+int node_override_process_func(rte_node_t id, rte_node_process_t process);
+
 /* Graph list functions */
 STAILQ_HEAD(graph_head, graph);
 
diff --git a/lib/graph/node.c b/lib/graph/node.c
index 63db629da8..82834a6634 100644
--- a/lib/graph/node.c
+++ b/lib/graph/node.c
@@ -419,3 +419,26 @@ rte_node_max_count(void)
 {
return node_id;
 }
+
+int
+node_override_process_func(rte_node_t id, rte_node_process_t process)
+{
+   struct node *node;
+
+   NODE_ID_CHECK(id);
+   graph_spinlock_lock();
+
+   STAILQ_FOREACH(node, &node_list, next) {
+   if (node->id == id) {
+   node->process = process;
+   graph_spinlock_unlock();
+   return 0;
+   }
+   }
+
+   graph_spinlock_unlock();
+
+   return 0;
+fail:
+   return -1;
+}
-- 
2.43.0



[PATCH v6 2/4] graph: add feature arc abstraction

2025-01-02 Thread Nitin Saxena
Feature arc abstraction allows rte_graph based applications to
- Hook feature nodes between start_node and end_node of an arc
- Feature arc's are created via RTE_GRAPH_FEATURE_ARC_REGISTER()
- Feature nodes are added to an arc via RTE_GRAPH_FEATURE_REGISTER()
- If application explicitly calls rte_graph_feature_arc_init(), before
  rte_graph_create(), all features arcs  and associated feature nodes
  are automatically connected
- If rte_graph_feature_arc_init() is not called, feature arc module has
  no affect
- Packet path towards feature node(s) is enabled/disabled at
  runtime on per interface basis.
- More than one feature nodes can be added/enabled in an arc
- If any feature node is enabled on any interface, feature arc fast path
  APIs provide next edge for each mbuf

Once DPDK inbuilt nodes adopts feature arc abstraction, out-of-tree
nodes can be hooked in a generic manner

Signed-off-by: Nitin Saxena 
---
 doc/api/doxy-api-index.md|2 +
 doc/guides/rel_notes/release_25_03.rst   |   10 +
 lib/graph/graph_feature_arc.c| 1780 ++
 lib/graph/graph_private.h|4 +
 lib/graph/meson.build|4 +-
 lib/graph/rte_graph_feature_arc.h|  552 +++
 lib/graph/rte_graph_feature_arc_worker.h |  608 
 lib/graph/version.map|   20 +
 8 files changed, 2979 insertions(+), 1 deletion(-)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index f0193502bc..b6a5dedee5 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -213,6 +213,8 @@ The public API headers are grouped by topics:
 [table_wm](@ref rte_swx_table_wm.h)
   * [graph](@ref rte_graph.h):
 [graph_worker](@ref rte_graph_worker.h)
+[graph_feature_arc](@ref rte_graph_feature_arc.h)
+[graph_feature_arc_worker](@ref rte_graph_feature_arc_worker.h)
   * graph_nodes:
 [eth_node](@ref rte_node_eth_api.h),
 [ip4_node](@ref rte_node_ip4_api.h),
diff --git a/doc/guides/rel_notes/release_25_03.rst 
b/doc/guides/rel_notes/release_25_03.rst
index 426dfcd982..205215b5de 100644
--- a/doc/guides/rel_notes/release_25_03.rst
+++ b/doc/guides/rel_notes/release_25_03.rst
@@ -55,6 +55,16 @@ New Features
  Also, make sure to start the actual text at the margin.
  ===
 
+* **Added feature arc abstraction in graph library.**
+
+  Feature arc abstraction helps ``rte_graph`` based applications to steer
+  packets across different node path(s) based on the features (or protocols)
+  enabled on interfaces. Different feature node paths can be enabled/disabled
+  at runtime on some or on all interfaces. This abstraction also help
+  applications to hook ``out-of-tree nodes`` in in-built DPDK node paths
+  in a generic manner.
+
+  * Added ``ip4_output`` feature arc processing in ``ip4_rewrite`` node.
 
 Removed Items
 -
diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 00..895ec68f86
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,1780 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include 
+#include 
+#include 
+
+#define GRAPH_FEATURE_ARC_INITIALIZER  UINT64_MAX
+#define GRAPH_FEATURE_MAX_NUM_PER_ARC  (64)
+
+#define connect_graph_nodes(node1, node2, edge, arc_name) \
+   __connect_graph_nodes(node1, node2, edge, arc_name, __LINE__)
+
+#define FEATURE_ARC_MEMZONE_NAME "__rte_feature_arc_main_mz"
+
+#define graph_uint_cast(f) ((unsigned int)f)
+
+#define fdata_from_feat(arc, feat, index)  \
+   RTE_GRAPH_FEATURE_TO_FEATURE_DATA(arc, feat, index)
+
+#define feat_dbg graph_dbg
+
+#define FEAT_COND_ERR(cond, ...)   \
+   do {   \
+   if (cond)  \
+   graph_err(__VA_ARGS__);\
+   } while (0)
+
+#define FEAT_ERR(fn, ln, ...)  \
+   GRAPH_LOG2(ERR, fn, ln, __VA_ARGS__)
+
+#define FEAT_ERR_JMP(_err, fn, ln, ...)\
+   do {   \
+   FEAT_ERR(fn, ln, __VA_ARGS__); \
+   rte_errno = _err;  \
+   } while (0)
+
+static struct rte_mbuf_dynfield rte_graph_feature_arc_mbuf_desc = {
+   .name = RTE_GRAPH_FEATURE_ARC_DYNFIELD_NAME,
+   .size = sizeof(struct rte_graph_feature_arc_mbuf_dynfields),
+ 

[PATCH v6 3/4] ip4: add ip4 output feature arc

2025-01-02 Thread Nitin Saxena
Added ip4 output arc to allow applications to hook feature nodes in ip4
egress direction

Signed-off-by: Nitin Saxena 
---
 lib/node/ethdev_ctrl.c   |   8 +
 lib/node/interface_tx_feature.c  | 133 
 lib/node/interface_tx_feature_priv.h |  33 +++
 lib/node/ip4_rewrite.c   | 298 ++-
 lib/node/meson.build |   1 +
 lib/node/node_private.h  |   1 +
 lib/node/rte_node_ip4_api.h  |   4 +
 7 files changed, 474 insertions(+), 4 deletions(-)
 create mode 100644 lib/node/interface_tx_feature.c
 create mode 100644 lib/node/interface_tx_feature_priv.h

diff --git a/lib/node/ethdev_ctrl.c b/lib/node/ethdev_ctrl.c
index cd52e8be08..93ef7fbb95 100644
--- a/lib/node/ethdev_ctrl.c
+++ b/lib/node/ethdev_ctrl.c
@@ -14,6 +14,7 @@
 #include "ethdev_tx_priv.h"
 #include "ip4_rewrite_priv.h"
 #include "ip6_rewrite_priv.h"
+#include "interface_tx_feature_priv.h"
 #include "node_private.h"
 
 static struct ethdev_ctrl {
@@ -24,6 +25,7 @@ int
 rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
uint16_t nb_graphs)
 {
+   struct rte_node_register *if_tx_feature_node;
struct rte_node_register *ip4_rewrite_node;
struct rte_node_register *ip6_rewrite_node;
struct ethdev_tx_node_main *tx_node_data;
@@ -35,6 +37,7 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, 
uint16_t nb_confs,
int i, j, rc;
uint32_t id;
 
+   if_tx_feature_node = if_tx_feature_node_get();
ip4_rewrite_node = ip4_rewrite_node_get();
ip6_rewrite_node = ip6_rewrite_node_get();
tx_node_data = ethdev_tx_node_data_get();
@@ -125,6 +128,11 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, 
uint16_t nb_confs,
if (rc < 0)
return rc;
 
+   /* Add this tx port node to if_tx_feature_node */
+   rte_node_edge_update(if_tx_feature_node->id, 
RTE_EDGE_ID_INVALID,
+&next_nodes, 1);
+   rc = if_tx_feature_node_set_next(port_id,
+
rte_node_edge_count(if_tx_feature_node->id) - 1);
}
 
ctrl.nb_graphs = nb_graphs;
diff --git a/lib/node/interface_tx_feature.c b/lib/node/interface_tx_feature.c
new file mode 100644
index 00..35ac00f21e
--- /dev/null
+++ b/lib/node/interface_tx_feature.c
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include "rte_node_ip4_api.h"
+#include "node_private.h"
+#include "interface_tx_feature_priv.h"
+
+#define IF_TX_FEATURE_LAST_NEXT_INDEX(ctx) \
+   (((struct if_tx_feature_node_ctx *)ctx)->last_index)
+/*
+ * @internal array for mapping port to next node index
+ */
+struct if_tx_feature_node_main  {
+   uint16_t next_index[RTE_MAX_ETHPORTS];
+};
+
+struct if_tx_feature_node_ctx {
+   uint16_t last_index;
+};
+
+static struct if_tx_feature_node_main *if_tx_feature_nm;
+
+int
+if_tx_feature_node_set_next(uint16_t port_id, uint16_t next_index)
+{
+   if (if_tx_feature_nm == NULL) {
+   if_tx_feature_nm = rte_zmalloc(
+   "if_tx_feature_nm", sizeof(struct 
if_tx_feature_node_main),
+   RTE_CACHE_LINE_SIZE);
+   if (if_tx_feature_nm == NULL)
+   return -ENOMEM;
+   }
+   if_tx_feature_nm->next_index[port_id] = next_index;
+
+   return 0;
+}
+
+static int
+if_tx_feature_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+   RTE_SET_USED(graph);
+
+   /* pkt_drop */
+   IF_TX_FEATURE_LAST_NEXT_INDEX(node->ctx) = 0;
+
+   return 0;
+}
+
+static uint16_t
+if_tx_feature_node_process(struct rte_graph *graph, struct rte_node *node,
+  void **objs, uint16_t nb_objs)
+{
+   uint16_t held = 0, next;
+   void **to_next, **from;
+   uint16_t last_spec = 0;
+   rte_edge_t next_index;
+   struct rte_mbuf *mbuf;
+   int i;
+
+   /* Speculative next */
+   next_index = IF_TX_FEATURE_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];
+
+   /* port-tx node starts from next edge 1*/
+   next = if_tx_feature_nm->next_index[mbuf->port];
+
+   if (unlikely(next_index != next)) {
+   /* Copy things successfully speculated till now */
+   rte_memcpy(to_next, from, last_spec * sizeof(from[0]));
+   from += last_spec;
+ 

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

2025-01-02 Thread Nitin Saxena
- 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   
graph> feature disable   
graph> graph stats show

Signed-off-by: Nitin Saxena 
---
 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 ip mac  # Add static 
neighbour for IPv4
 neigh add ipv6 ip mac  # Add static 
neighbour for IPv6
 help neigh   # Print help on neigh 
commands
+
+feature arcs # show all feature 
arcs
+feature name show# Show feature arc 
details
+feature enable arc_name feature_name interface   # 
Enable feature on interface
+feature disable arc_name feature_name 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 00..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 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#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  show   # Display features within 
an arc";
+
+static const char
+cmd_feature_enable_help[] = "feature enable   
";
+
+static const char
+cmd_feature_disable_help[] = "feature disable   
";
+
+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 *

Re: Feature arc slides

2025-01-07 Thread Nitin Saxena
Hi Stephen,


On Mon, Jan 6, 2025 at 5:45 AM Stephen Hemminger
 wrote:
>
> On Fri, 3 Jan 2025 20:29:15 +0530
> Nitin Saxena  wrote:
>
> > Sending to DPDK community again
> >
> > Thanks,
> > Nitin
>
> Why not convert the slides into useful long term documentation in
> the doc/guides directory.

I will also add a programming guide to this patch. PPT purpose was to
describe feature arc concept and, if needed, present to the community

Thanks,
Nitin


Re: [EXTERNAL] [PATCH v6 0/4] add feature arc in rte_graph

2025-01-14 Thread Nitin Saxena
Hi,

As discussed on slack, please find details of meeting  for feature arc
discussion

==
Date and time: 16th Jan 2025 (Thursday) 12:00 PM (UTC + 1)

Join Zoom Meeting
Password:
267382

Meeting URL:
https://marvell.zoom.us/j/97204271800?pwd=qR8Tc5WawaYdw8CTP2DK6bA4EEXR2J.1&from=addon

Join by Telephone
Dial: +1 669 900 6833 (US Toll) or +1 689 278 1000 (US Toll)

Meeting ID: 972 0427 1800

Password: 267382

International and Toll Free Numbers
Join from a Video Conference Room
Marvell:
>From Touchpad, tap Join Zoom button. When prompted, enter Meeting ID
and Password

China sites: Dial * then 972 0427 1800
External: Dial 97204271...@zoomcrc.com
OR:
Dial 144.195.19.161 (US West), enter 972 0427 1800 and # when
prompted. International H323 IP addresses
==

Thanks,
Nitin

On Fri, Jan 10, 2025 at 7:29 PM Robin Jarry  wrote:
>
> Nitin Saxena, Jan 03, 2025 at 15:41:
> > Hi,
> >
> > Please find attached slides explaining feature arc concept and its
> > usage in graph library
> >
> > I can also present these slides to community to facilitate this patch
> > series review process
>
> Hi Nitin,
>
> thanks for taking the time to explain your design better.
>
> I would really like to have a video meeting to really understand what
> problem you are trying to solve. And how it could help the grout code
> base.
>
> Let me know how what you think.
>
> Cheers
>


[PATCH v2 0/2] node: add mbuf dynamic field for nodes

2025-04-05 Thread Nitin Saxena
Currently each rte_node registers separate mbuf dynamic fields for their
own purpose. This leads to wastage of mbuf space as once mbuf get passed
a particular node, the registered dynamic field(by that node) is no
longer used.

This patch series adds a global/common mbuf dynamic field which is
reusable by all the nodes(including out-of-tree nodes). This helps to
repurpose same mbuf dynamic field for other nodes. It contains two types
of fields: (a) persistent (b) overloadable.

While persistent fields are those which does not often changes during a
graph walk such as rx/tx interface, buffer flags etc. Currently there
are no persistent fields added but they can be added later

Overloadable fields are those which can be overloaded by two adjacent
nodes. Overloadable fields can be repurposed by other two adjacent nodes.

This patch series also updates ip4/ip6 lookup/rewrite nodes to use
overlaodable mbuf dynamic fields.

Changes in v2:
- removed usage of memzone for saving mbuf dynfield [Stephen]
- fixed checkpatch issues
- redefine RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE to 8 byte which are
  currently in use. Size can increase later based on the usage

Nitin Saxena (2):
  node: add global node mbuf dynfield
  node: use node mbuf dynfield in ip4 nodes

 doc/api/doxy-api-index.md  |   3 +-
 doc/guides/rel_notes/release_25_07.rst |   6 ++
 lib/node/ip4_lookup.c  |  14 +--
 lib/node/ip4_rewrite.c |  15 ++-
 lib/node/ip6_lookup.c  |  15 ++-
 lib/node/ip6_rewrite.c |  14 +--
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  46 
 lib/node/node_private.h|  40 +--
 lib/node/rte_node_mbuf_dynfield.h  | 139 +
 lib/node/version.map   |   3 +
 11 files changed, 223 insertions(+), 74 deletions(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

--
2.43.0



Re: [EXTERNAL] Re: [PATCH 1/2] node: add global node mbuf dynfield

2025-04-06 Thread Nitin Saxena
Hi Stephen,

Thanks,
Nitin

On Fri, Apr 4, 2025 at 8:51 PM Stephen Hemminger
 wrote:
>
> On Fri, 4 Apr 2025 08:11:07 +
> Pavan Nikhilesh Bhagavatula  wrote:
>
> > > Hi Stephen,
> > >
> > > Thanks for commenting. See response inline.
> > >
> > > Regards,
> > > Nitin
> > >
> > > On Tue, Apr 1, 2025 at 7:45 PM Stephen Hemminger
> > >  wrote:
> > > >
> > > > On Tue, 1 Apr 2025 09:50:46 +0530
> > > > Nitin Saxena  wrote:
> > > >
> > > > > +int rte_node_mbuf_dynfield_register(void)
> > > > > +{
> > > > > + struct node_mbuf_dynfield_mz *f = NULL;
> > > > > + const struct rte_memzone *mz = NULL;
> > > > > + int dyn_offset;
> > > > > +
> > > > > + RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_dynfield_t) <
> > > RTE_NODE_MBUF_DYNFIELD_SIZE);
> > > > > + RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_overload_fields_t) <
> > > > > +  RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE);
> > > > > +
> > > > > + mz =
> > > rte_memzone_lookup(NODE_MBUF_DYNFIELD_MEMZONE_NAME);
> > > >
> > > > Seems wasteful to have a whole memzone for this, the data is small.
> > > > Is there a reason it could not just be a global variable like timestamp.
> > > >
> > > Replaced usage of memzone with global variable in v2
> >
> > We need to use memzone to share the offset between primary and secondary
> > processes I don’t see any other way.
>
>
> Normally secondary just uses dynamic field lookup to find the offset.

rte_node_mbuf_dynfield_register() is doing both: register_offset() +
lookup_offset()
Very first call to rte_node_mbuf_dynfield_register() actually
registers dynamic offset for the buffer. All subsequent calls returns
offset based on what was registered earlier

Since secondary process support is required, should I bring back
memzone based implementation? since global variable would not work


Re: [PATCH v4 0/2] node: add mbuf dynamic field for nodes

2025-04-08 Thread Nitin Saxena
Hi David,

Thanks,
Nitin

On Tue, Apr 8, 2025 at 1:05 PM David Marchand  wrote:
>
> Hello Nitin,
>
> On Mon, Apr 7, 2025 at 9:48 AM Nitin Saxena  wrote:
> >
> > Currently each rte_node registers separate mbuf dynamic fields for their
> > own purpose. This leads to wastage of mbuf space as once mbuf get passed
> > a particular node, the registered dynamic field(by that node) is no
> > longer used.
> >
> > This patch series adds a global/common mbuf dynamic field which is
> > reusable by all the nodes(including out-of-tree nodes). This helps to
> > repurpose same mbuf dynamic field for other nodes. It contains two types
> > of fields: (a) persistent (b) overloadable.
> >
> > While persistent fields are those which does not often changes during a
> > graph walk such as rx/tx interface, buffer flags etc. Currently there
> > are no persistent fields added but they can be added later
> >
> > Overloadable fields are those which can be overloaded by two adjacent
> > nodes. Overloadable fields can be repurposed by other two adjacent nodes.
> >
> > This patch series also updates ip4/ip6 lookup/rewrite nodes to use
> > overlaodable mbuf dynamic fields.
> >
> > Changes in v4
> > - Fix github CI
> >
> > Changes in v3:
> > - Fix CI build error
> >
> > Changes in v2:
> > - removed usage of memzone for saving mbuf dynfield [Stephen]
> > - fixed checkpatch issues
> > - redefine RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE to 8 byte which are
> >   currently in use. Size can increase later based on the usage
> >
> >
> > Nitin Saxena (2):
> >   node: add global node mbuf dynfield
> >   node: use node mbuf dynfield in ip4 nodes
> >
> >  doc/api/doxy-api-index.md  |   3 +-
> >  doc/guides/rel_notes/release_25_07.rst |   6 ++
> >  lib/node/ip4_lookup.c  |  14 +--
> >  lib/node/ip4_rewrite.c |  15 ++-
> >  lib/node/ip6_lookup.c  |  15 ++-
> >  lib/node/ip6_rewrite.c |  14 +--
> >  lib/node/meson.build   |   2 +
> >  lib/node/node_mbuf_dynfield.c  |  44 
> >  lib/node/node_private.h|  40 +--
> >  lib/node/rte_node_mbuf_dynfield.h  | 141 +
> >  lib/node/version.map   |   3 +
>
> I did not review this series but I noticed some version.map update.
>
> When pulling main, you'll notice that exporting symbols must be done
> via RTE_EXPORT_*SYMBOL macros now.
> No version.map touching anymore, those files are generated at build
> time, so developers don't need to care about them.
>
> In lib/node/node_mbuf_dynfield.c, you need to include eal_export.h and add:
>
> +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_node_mbuf_dynfield_register, 25.07)
> +int rte_node_mbuf_dynfield_register(void)
> +{
> ...

Will do these changes in the next patchset. Thanks for letting me know

>
>
> --
> David Marchand
>


[PATCH v5 2/2] node: use node mbuf dynfield in ip4 nodes

2025-04-09 Thread Nitin Saxena
- Used global node mbuf in ip[4|6]_lookup/rewrite nodes
- Redefine node_mbuf_priv1() to rte_node_mbuf_overload_fields_get()

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_lookup.c | 14 ---
 lib/node/ip4_rewrite.c| 15 +---
 lib/node/ip6_lookup.c | 15 +---
 lib/node/ip6_rewrite.c| 14 ---
 lib/node/node_private.h   | 40 +++
 lib/node/rte_node_mbuf_dynfield.h |  9 +++
 6 files changed, 34 insertions(+), 73 deletions(-)

diff --git a/lib/node/ip4_lookup.c b/lib/node/ip4_lookup.c
index 604fcb267a..f6db3219f0 100644
--- a/lib/node/ip4_lookup.c
+++ b/lib/node/ip4_lookup.c
@@ -32,8 +32,6 @@ struct ip4_lookup_node_ctx {
int mbuf_priv1_off;
 };

-int node_mbuf_priv1_dynfield_offset = -1;
-
 static struct ip4_lookup_node_main ip4_lookup_nm;

 #define IP4_LOOKUP_NODE_LPM(ctx) \
@@ -182,17 +180,15 @@ ip4_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_lookup_node_ctx) > RTE_NODE_CTX_SZ);

+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -210,7 +206,7 @@ ip4_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP4_LOOKUP_NODE_LPM(node->ctx) = ip4_lookup_nm.lpm_tbl[graph->socket];
-   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

 #if defined(__ARM_NEON) || defined(RTE_ARCH_X86)
if (rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128)
diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index a9ab5eaa57..dfa4382350 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -259,19 +259,16 @@ ip4_rewrite_node_process(struct rte_graph *graph, struct 
rte_node *node,
 static int
 ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
-   static bool init_once;
+   int dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_rewrite_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-   init_once = true;
-   }
-   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
+
+   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip4_rewrite", "Initialized ip4_rewrite node initialized");

diff --git a/lib/node/ip6_lookup.c b/lib/node/ip6_lookup.c
index 827fc2f379..83c0500c76 100644
--- a/lib/node/ip6_lookup.c
+++ b/lib/node/ip6_lookup.c
@@ -318,18 +318,16 @@ ip6_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip6_lookup_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset =
-   rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;

+   if (!init_once) {
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -347,8 +345,7 @@ ip6_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP6_LOOKUP_NODE_LPM(node->ctx) = ip6_lookup_nm.lpm_tbl[graph->socket];
-   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) =
-   node_mbuf_priv1_dynfield_offset;
+   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip6_lookup", "Initialized ip6_lookup node");

diff --git a/lib/n

[PATCH v5 1/2] node: add global node mbuf dynfield

2025-04-09 Thread Nitin Saxena
This patch defines rte_node specific dynamic field structure
(rte_node_mbuf_dynfield_t)

rte_node_mbuf_dynfield_t structure holds two types of fields
- Persistent data fields which are preserved across graph walk.
  Currently size of persistent data fields is zero.
- Overloadable data fields which are used by any two adjacent nodes.
  Same fields can be repurposed by any other adjacent nodes

This dynfield can be also be used by out-of-tree nodes.

Signed-off-by: Nitin Saxena 
---
 doc/api/doxy-api-index.md  |   3 +-
 doc/guides/rel_notes/release_25_07.rst |   6 ++
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  57 +++
 lib/node/rte_node_mbuf_dynfield.h  | 132 +
 5 files changed, 199 insertions(+), 1 deletion(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5c425a2cb9..763cfb3f3c 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -219,7 +219,8 @@ The public API headers are grouped by topics:
 [ip4_node](@ref rte_node_ip4_api.h),
 [ip6_node](@ref rte_node_ip6_api.h),
 [udp4_input_node](@ref rte_node_udp4_input_api.h)
-
+  * graph_nodes_mbuf:
+[node_mbuf_dynfield](@ref rte_node_mbuf_dynfield.h)
 - **basic**:
   [bitops](@ref rte_bitops.h),
   [approx fraction](@ref rte_approx.h),
diff --git a/doc/guides/rel_notes/release_25_07.rst 
b/doc/guides/rel_notes/release_25_07.rst
index 093b85d206..2dc888e65d 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,12 @@ New Features
  Also, make sure to start the actual text at the margin.
  ===

+* **Added rte_node specific global mbuf dynamic field.**
+
+  Instead each node registering mbuf dynamic field for its own purpose, a
+  global structure is added which can be used/overloaded by all nodes
+  (including out-of-tree nodes). This minimizes footprint of node specific mbuf
+  dynamic field.

 Removed Items
 -
diff --git a/lib/node/meson.build b/lib/node/meson.build
index 0bed97a96c..152fe41129 100644
--- a/lib/node/meson.build
+++ b/lib/node/meson.build
@@ -8,6 +8,7 @@ if is_windows
 endif

 sources = files(
+'node_mbuf_dynfield.c',
 'ethdev_ctrl.c',
 'ethdev_rx.c',
 'ethdev_tx.c',
@@ -30,6 +31,7 @@ headers = files(
 'rte_node_ip4_api.h',
 'rte_node_ip6_api.h',
 'rte_node_udp4_input_api.h',
+'rte_node_mbuf_dynfield.h',
 )

 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
diff --git a/lib/node/node_mbuf_dynfield.c b/lib/node/node_mbuf_dynfield.c
new file mode 100644
index 00..6005e72ed6
--- /dev/null
+++ b/lib/node/node_mbuf_dynfield.c
@@ -0,0 +1,57 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+#include 
+#include 
+#include 
+#include 
+
+#define NODE_MBUF_DYNFIELD_MEMZONE_NAME "__rte_node_mbuf_dynfield"
+
+struct node_mbuf_dynfield_mz {
+   int dynfield_offset;
+};
+
+static const struct rte_mbuf_dynfield node_mbuf_dynfield_desc = {
+   .name = "rte_node_mbuf_dynfield",
+   .size = sizeof(rte_node_mbuf_dynfield_t),
+   .align = alignof(rte_node_mbuf_dynfield_t),
+};
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_node_mbuf_dynfield_register, 25.07);
+int rte_node_mbuf_dynfield_register(void)
+{
+   struct node_mbuf_dynfield_mz *f = NULL;
+   const struct rte_memzone *mz = NULL;
+   int dyn_offset;
+
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_dynfield_t) < 
RTE_NODE_MBUF_DYNFIELD_SIZE);
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_overload_fields_t) <
+RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE);
+
+   mz = rte_memzone_lookup(NODE_MBUF_DYNFIELD_MEMZONE_NAME);
+
+   if (!mz) {
+   mz = 
rte_memzone_reserve_aligned(NODE_MBUF_DYNFIELD_MEMZONE_NAME,
+sizeof(struct 
node_mbuf_dynfield_mz),
+SOCKET_ID_ANY, 0,
+RTE_CACHE_LINE_SIZE);
+   if (!mz) {
+   node_err("node_mbuf_dyn", "rte_memzone_reserve_aligned 
failed");
+   return -1;
+   }
+   dyn_offset = 
rte_mbuf_dynfield_register(&node_mbuf_dynfield_desc);
+   if (dyn_offset < 0) {
+   node_err("node_mbuf_dyn", "rte_mbuf_dynfield_register 
failed");
+   return -1;
+   }
+   f = (struct node_mbuf_dynfield_mz *)mz->addr;
+   f->dynfield_offset = dyn_offset;
+
+ 

[PATCH 1/2] node: add global node mbuf dynfield

2025-04-01 Thread Nitin Saxena
This patch defines rte_node specific dynamic field structure
(rte_node_mbuf_dynfield_t)

rte_node_mbuf_dynfield_t structure holds two types of fields
- Persistent data fields which are preserved across graph walk.
  Currently size of persistent data fields is zero.
- Overloadable data fields which are used by any two adjacent nodes.
  Same fields can be repurposed by any other adjacent nodes

This dynfield can be also be used by out-of-tree nodes.

Signed-off-by: Nitin Saxena 
---
 doc/api/doxy-api-index.md  |   3 +-
 doc/guides/rel_notes/release_25_07.rst |   6 ++
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  56 +++
 lib/node/rte_node_mbuf_dynfield.h  | 130 +
 lib/node/version.map   |   3 +
 6 files changed, 199 insertions(+), 1 deletion(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index b2fc24b3e4..6b93b3cd97 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -219,7 +219,8 @@ The public API headers are grouped by topics:
 [ip4_node](@ref rte_node_ip4_api.h),
 [ip6_node](@ref rte_node_ip6_api.h),
 [udp4_input_node](@ref rte_node_udp4_input_api.h)
-
+  * graph_nodes_mbuf:
+[node_mbuf_dynfield](@ref rte_node_mbuf_dynfield.h)
 - **basic**:
   [bitops](@ref rte_bitops.h),
   [approx fraction](@ref rte_approx.h),
diff --git a/doc/guides/rel_notes/release_25_07.rst 
b/doc/guides/rel_notes/release_25_07.rst
index cd1025aac0..72d5d88ff3 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,12 @@ New Features
  Also, make sure to start the actual text at the margin.
  ===

+* **Added rte_node specific global mbuf dynamic field.**
+
+  Instead each node registering mbuf dynamic field for its own purpose, a
+  global structure is added which can be used/overloaded by all nodes
+  (including out-of-tree nodes). This minimizes footprint of node specific mbuf
+  dynamic field.

 Removed Items
 -
diff --git a/lib/node/meson.build b/lib/node/meson.build
index 0bed97a96c..4330d0450b 100644
--- a/lib/node/meson.build
+++ b/lib/node/meson.build
@@ -8,6 +8,7 @@ if is_windows
 endif

 sources = files(
+'node_mbuf_dynfield.c',
 'ethdev_ctrl.c',
 'ethdev_rx.c',
 'ethdev_tx.c',
@@ -30,6 +31,7 @@ headers = files(
 'rte_node_ip4_api.h',
 'rte_node_ip6_api.h',
 'rte_node_udp4_input_api.h',
+'rte_node_mbuf_dynfield.h'
 )

 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
diff --git a/lib/node/node_mbuf_dynfield.c b/lib/node/node_mbuf_dynfield.c
new file mode 100644
index 00..e42631f432
--- /dev/null
+++ b/lib/node/node_mbuf_dynfield.c
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define NODE_MBUF_DYNFIELD_MEMZONE_NAME "__rte_node_mbuf_dynfield"
+
+struct node_mbuf_dynfield_mz {
+   int dynfield_offset;
+};
+
+static const struct rte_mbuf_dynfield node_mbuf_dynfield_desc = {
+   .name = "rte_node_mbuf_dynfield",
+   .size = sizeof(rte_node_mbuf_dynfield_t),
+   .align = alignof(rte_node_mbuf_dynfield_t),
+};
+
+int rte_node_mbuf_dynfield_register(void)
+{
+   struct node_mbuf_dynfield_mz *f = NULL;
+   const struct rte_memzone *mz = NULL;
+   int dyn_offset;
+
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_dynfield_t) < 
RTE_NODE_MBUF_DYNFIELD_SIZE);
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_overload_fields_t) <
+RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE);
+
+   mz = rte_memzone_lookup(NODE_MBUF_DYNFIELD_MEMZONE_NAME);
+
+   if (!mz) {
+   mz = rte_memzone_reserve(NODE_MBUF_DYNFIELD_MEMZONE_NAME,
+sizeof(struct node_mbuf_dynfield_mz),
+SOCKET_ID_ANY, 0);
+   if (!mz) {
+   node_err("node_mbuf_dyn", "memzone reserve failed for 
node mbuf field");
+   return -1;
+   }
+   dyn_offset = 
rte_mbuf_dynfield_register(&node_mbuf_dynfield_desc);
+   if (dyn_offset < 0) {
+   node_err("node_mbuf_dyn", "rte_mbuf_dynfield_register 
failed");
+   return -1;
+   }
+   f = (struct node_mbuf_dynfield_mz *)mz->addr;
+   f->dynfield_offset = dyn_offset;
+
+   node_dbg("node_mbuf_dyn", "memzone: %s of size 0x%zx at offset: 
%d",
+   

[PATCH 0/2] node: add mbuf dynamic field for nodes

2025-04-01 Thread Nitin Saxena
Currently each rte_node registers separate mbuf dynamic fields for their
own purpose. This leads to wastage of mbuf space as once mbuf get passed
a particular node, the registered dynamic field(by that node) is no
longer used.

This patch series adds a global/common mbuf dynamic field which is
reusable by all the nodes(including out-of-tree nodes). This helps to
repurpose same mbuf dynamic field for other nodes. It contains two types
of fields: (a) persistent (b) overloadable.

While persistent fields are those which does not often changes during a
graph walk such as rx/tx interface, buffer flags etc. Currently there
are no persistent fields added but they can be added later

Overloadable fields are those which can be overloaded by two adjacent
nodes. Overloadable fields can be repurposed by other two adjacent nodes.

This patch series also updates ip4/ip6 lookup/rewrite nodes to use
overlaodable mbuf dynamic fields.

Nitin Saxena (2):
  node: add global node mbuf dynfield
  node: use node mbuf dynfield in ip4 nodes

 doc/api/doxy-api-index.md  |   3 +-
 doc/guides/rel_notes/release_25_07.rst |   6 ++
 lib/node/ip4_lookup.c  |  14 +--
 lib/node/ip4_rewrite.c |  15 ++-
 lib/node/ip6_lookup.c  |  15 ++-
 lib/node/ip6_rewrite.c |  14 +--
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  56 ++
 lib/node/node_private.h|  40 +--
 lib/node/rte_node_mbuf_dynfield.h  | 139 +
 lib/node/version.map   |   3 +
 11 files changed, 233 insertions(+), 74 deletions(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

--
2.43.0



[PATCH 2/2] node: use node mbuf dynfield in ip4 nodes

2025-03-31 Thread Nitin Saxena
- Used global node mbuf in ip[4|6]_lookup/rewrite nodes
- Redefine node_mbuf_priv1() to rte_node_mbuf_overload_fields_get()

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_lookup.c | 14 ---
 lib/node/ip4_rewrite.c| 15 +---
 lib/node/ip6_lookup.c | 15 +---
 lib/node/ip6_rewrite.c| 14 ---
 lib/node/node_private.h   | 40 +++
 lib/node/rte_node_mbuf_dynfield.h |  9 +++
 6 files changed, 34 insertions(+), 73 deletions(-)

diff --git a/lib/node/ip4_lookup.c b/lib/node/ip4_lookup.c
index 0b474cd2bc..7e9d21a9c9 100644
--- a/lib/node/ip4_lookup.c
+++ b/lib/node/ip4_lookup.c
@@ -31,8 +31,6 @@ struct ip4_lookup_node_ctx {
int mbuf_priv1_off;
 };

-int node_mbuf_priv1_dynfield_offset = -1;
-
 static struct ip4_lookup_node_main ip4_lookup_nm;

 #define IP4_LOOKUP_NODE_LPM(ctx) \
@@ -180,17 +178,15 @@ ip4_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_lookup_node_ctx) > RTE_NODE_CTX_SZ);

+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -208,7 +204,7 @@ ip4_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP4_LOOKUP_NODE_LPM(node->ctx) = ip4_lookup_nm.lpm_tbl[graph->socket];
-   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

 #if defined(__ARM_NEON) || defined(RTE_ARCH_X86)
if (rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128)
diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index 34a920df5e..b05decc1a3 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -258,19 +258,16 @@ ip4_rewrite_node_process(struct rte_graph *graph, struct 
rte_node *node,
 static int
 ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
-   static bool init_once;
+   int dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_rewrite_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-   init_once = true;
-   }
-   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
+
+   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip4_rewrite", "Initialized ip4_rewrite node initialized");

diff --git a/lib/node/ip6_lookup.c b/lib/node/ip6_lookup.c
index f378d2d064..c140aa9cf3 100644
--- a/lib/node/ip6_lookup.c
+++ b/lib/node/ip6_lookup.c
@@ -316,18 +316,16 @@ ip6_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip6_lookup_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset =
-   rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;

+   if (!init_once) {
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -345,8 +343,7 @@ ip6_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP6_LOOKUP_NODE_LPM(node->ctx) = ip6_lookup_nm.lpm_tbl[graph->socket];
-   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) =
-   node_mbuf_priv1_dynfield_offset;
+   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip6_lookup", "Initialized ip6_lookup node");

diff --git a/lib/n

[PATCH v4 1/2] node: add global node mbuf dynfield

2025-04-07 Thread Nitin Saxena
This patch defines rte_node specific dynamic field structure
(rte_node_mbuf_dynfield_t)

rte_node_mbuf_dynfield_t structure holds two types of fields
- Persistent data fields which are preserved across graph walk.
  Currently size of persistent data fields is zero.
- Overloadable data fields which are used by any two adjacent nodes.
  Same fields can be repurposed by any other adjacent nodes

This dynfield can be also be used by out-of-tree nodes.

Signed-off-by: Nitin Saxena 
---
 doc/api/doxy-api-index.md  |   3 +-
 doc/guides/rel_notes/release_25_07.rst |   6 ++
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  44 +
 lib/node/rte_node_mbuf_dynfield.h  | 132 +
 lib/node/version.map   |   3 +
 6 files changed, 189 insertions(+), 1 deletion(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index b2fc24b3e4..6b93b3cd97 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -219,7 +219,8 @@ The public API headers are grouped by topics:
 [ip4_node](@ref rte_node_ip4_api.h),
 [ip6_node](@ref rte_node_ip6_api.h),
 [udp4_input_node](@ref rte_node_udp4_input_api.h)
-
+  * graph_nodes_mbuf:
+[node_mbuf_dynfield](@ref rte_node_mbuf_dynfield.h)
 - **basic**:
   [bitops](@ref rte_bitops.h),
   [approx fraction](@ref rte_approx.h),
diff --git a/doc/guides/rel_notes/release_25_07.rst 
b/doc/guides/rel_notes/release_25_07.rst
index cd1025aac0..72d5d88ff3 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,12 @@ New Features
  Also, make sure to start the actual text at the margin.
  ===

+* **Added rte_node specific global mbuf dynamic field.**
+
+  Instead each node registering mbuf dynamic field for its own purpose, a
+  global structure is added which can be used/overloaded by all nodes
+  (including out-of-tree nodes). This minimizes footprint of node specific mbuf
+  dynamic field.

 Removed Items
 -
diff --git a/lib/node/meson.build b/lib/node/meson.build
index 0bed97a96c..152fe41129 100644
--- a/lib/node/meson.build
+++ b/lib/node/meson.build
@@ -8,6 +8,7 @@ if is_windows
 endif

 sources = files(
+'node_mbuf_dynfield.c',
 'ethdev_ctrl.c',
 'ethdev_rx.c',
 'ethdev_tx.c',
@@ -30,6 +31,7 @@ headers = files(
 'rte_node_ip4_api.h',
 'rte_node_ip6_api.h',
 'rte_node_udp4_input_api.h',
+'rte_node_mbuf_dynfield.h',
 )

 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
diff --git a/lib/node/node_mbuf_dynfield.c b/lib/node/node_mbuf_dynfield.c
new file mode 100644
index 00..082918ae96
--- /dev/null
+++ b/lib/node/node_mbuf_dynfield.c
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+#include 
+#include 
+#include 
+
+#define NODE_MBUF_DYNFIELD_MEMZONE_NAME "__rte_node_mbuf_dynfield"
+
+struct node_mbuf_dynfield_mz {
+   int dynfield_offset;
+};
+
+static const struct rte_mbuf_dynfield node_mbuf_dynfield_desc = {
+   .name = "rte_node_mbuf_dynfield",
+   .size = sizeof(rte_node_mbuf_dynfield_t),
+   .align = alignof(rte_node_mbuf_dynfield_t),
+};
+
+int node_mbuf_dynfield_offset = -1;
+
+int rte_node_mbuf_dynfield_register(void)
+{
+   int dyn_offset;
+
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_dynfield_t) < 
RTE_NODE_MBUF_DYNFIELD_SIZE);
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_overload_fields_t) <
+RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE);
+
+   if (node_mbuf_dynfield_offset == -1) {
+   dyn_offset = 
rte_mbuf_dynfield_register(&node_mbuf_dynfield_desc);
+   if (dyn_offset < 0) {
+   node_err("node_mbuf_dyn", "rte_mbuf_dynfield_register 
failed");
+   return -1;
+   }
+   node_mbuf_dynfield_offset = dyn_offset;
+
+   node_dbg("node_mbuf_dyn", "node mbuf dynfield size %zu at 
offset: %d",
+ sizeof(rte_node_mbuf_dynfield_t), 
node_mbuf_dynfield_offset);
+   } else {
+   dyn_offset = node_mbuf_dynfield_offset;
+   }
+   return dyn_offset;
+}
diff --git a/lib/node/rte_node_mbuf_dynfield.h 
b/lib/node/rte_node_mbuf_dynfield.h
new file mode 100644
index 00..4293b3823d
--- /dev/null
+++ b/lib/node/rte_node_mbuf_dynfield.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_MBUF_DYNFIELD_H_
+#define _RTE_GRAPH_MBUF_DYNFIELD_H_
+
+#include

[PATCH v4 2/2] node: use node mbuf dynfield in ip4 nodes

2025-04-07 Thread Nitin Saxena
- Used global node mbuf in ip[4|6]_lookup/rewrite nodes
- Redefine node_mbuf_priv1() to rte_node_mbuf_overload_fields_get()

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_lookup.c | 14 ---
 lib/node/ip4_rewrite.c| 15 +---
 lib/node/ip6_lookup.c | 15 +---
 lib/node/ip6_rewrite.c| 14 ---
 lib/node/node_private.h   | 40 +++
 lib/node/rte_node_mbuf_dynfield.h |  9 +++
 6 files changed, 34 insertions(+), 73 deletions(-)

diff --git a/lib/node/ip4_lookup.c b/lib/node/ip4_lookup.c
index 0b474cd2bc..7e9d21a9c9 100644
--- a/lib/node/ip4_lookup.c
+++ b/lib/node/ip4_lookup.c
@@ -31,8 +31,6 @@ struct ip4_lookup_node_ctx {
int mbuf_priv1_off;
 };

-int node_mbuf_priv1_dynfield_offset = -1;
-
 static struct ip4_lookup_node_main ip4_lookup_nm;

 #define IP4_LOOKUP_NODE_LPM(ctx) \
@@ -180,17 +178,15 @@ ip4_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_lookup_node_ctx) > RTE_NODE_CTX_SZ);

+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -208,7 +204,7 @@ ip4_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP4_LOOKUP_NODE_LPM(node->ctx) = ip4_lookup_nm.lpm_tbl[graph->socket];
-   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

 #if defined(__ARM_NEON) || defined(RTE_ARCH_X86)
if (rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128)
diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index 34a920df5e..b05decc1a3 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -258,19 +258,16 @@ ip4_rewrite_node_process(struct rte_graph *graph, struct 
rte_node *node,
 static int
 ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
-   static bool init_once;
+   int dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_rewrite_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-   init_once = true;
-   }
-   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
+
+   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip4_rewrite", "Initialized ip4_rewrite node initialized");

diff --git a/lib/node/ip6_lookup.c b/lib/node/ip6_lookup.c
index f378d2d064..c140aa9cf3 100644
--- a/lib/node/ip6_lookup.c
+++ b/lib/node/ip6_lookup.c
@@ -316,18 +316,16 @@ ip6_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip6_lookup_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset =
-   rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;

+   if (!init_once) {
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -345,8 +343,7 @@ ip6_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP6_LOOKUP_NODE_LPM(node->ctx) = ip6_lookup_nm.lpm_tbl[graph->socket];
-   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) =
-   node_mbuf_priv1_dynfield_offset;
+   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip6_lookup", "Initialized ip6_lookup node");

diff --git a/lib/n

[PATCH v4 0/2] node: add mbuf dynamic field for nodes

2025-04-07 Thread Nitin Saxena
Currently each rte_node registers separate mbuf dynamic fields for their
own purpose. This leads to wastage of mbuf space as once mbuf get passed
a particular node, the registered dynamic field(by that node) is no
longer used.

This patch series adds a global/common mbuf dynamic field which is
reusable by all the nodes(including out-of-tree nodes). This helps to
repurpose same mbuf dynamic field for other nodes. It contains two types
of fields: (a) persistent (b) overloadable.

While persistent fields are those which does not often changes during a
graph walk such as rx/tx interface, buffer flags etc. Currently there
are no persistent fields added but they can be added later

Overloadable fields are those which can be overloaded by two adjacent
nodes. Overloadable fields can be repurposed by other two adjacent nodes.

This patch series also updates ip4/ip6 lookup/rewrite nodes to use
overlaodable mbuf dynamic fields.

Changes in v4
- Fix github CI

Changes in v3:
- Fix CI build error

Changes in v2:
- removed usage of memzone for saving mbuf dynfield [Stephen]
- fixed checkpatch issues
- redefine RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE to 8 byte which are
  currently in use. Size can increase later based on the usage


Nitin Saxena (2):
  node: add global node mbuf dynfield
  node: use node mbuf dynfield in ip4 nodes

 doc/api/doxy-api-index.md  |   3 +-
 doc/guides/rel_notes/release_25_07.rst |   6 ++
 lib/node/ip4_lookup.c  |  14 +--
 lib/node/ip4_rewrite.c |  15 ++-
 lib/node/ip6_lookup.c  |  15 ++-
 lib/node/ip6_rewrite.c |  14 +--
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  44 
 lib/node/node_private.h|  40 +--
 lib/node/rte_node_mbuf_dynfield.h  | 141 +
 lib/node/version.map   |   3 +
 11 files changed, 223 insertions(+), 74 deletions(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

--
2.43.0



[PATCH v7 2/5] graph: add feature arc abstraction

2025-04-19 Thread Nitin Saxena
Feature arc abstraction allows rte_graph based applications to
- Allow control plane to runtime enable/disable feature nodes.
  Fast path APIs helps to steer packets across enabled feature nodes
- Feature enable/disable based on indexes. Index can be interface index,
  route index, etc
- More than one feature nodes can be added to an arc and also provide
  mechanism to control features sequencing order in fast path.
- Does not require stopping of workers for control plane updates. RCU
  mechanism also provided
- Once DPDK inbuilt nodes adopts feature arc abstraction, out-of-tree
  nodes can also be hooked (with no custom changes in in-built nodes)

Signed-off-by: Nitin Saxena 
---
 doc/api/doxy-api-index.md|2 +
 doc/guides/rel_notes/release_25_07.rst   |   10 +
 lib/graph/graph_feature_arc.c| 2050 ++
 lib/graph/graph_private.h|4 +
 lib/graph/meson.build|4 +-
 lib/graph/rte_graph_feature_arc.h|  628 +++
 lib/graph/rte_graph_feature_arc_worker.h |  607 +++
 7 files changed, 3304 insertions(+), 1 deletion(-)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5c425a2cb9..6d8b531344 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -214,6 +214,8 @@ The public API headers are grouped by topics:
 [table_wm](@ref rte_swx_table_wm.h)
   * [graph](@ref rte_graph.h):
 [graph_worker](@ref rte_graph_worker.h)
+[graph_feature_arc](@ref rte_graph_feature_arc.h)
+[graph_feature_arc_worker](@ref rte_graph_feature_arc_worker.h)
   * graph_nodes:
 [eth_node](@ref rte_node_eth_api.h),
 [ip4_node](@ref rte_node_ip4_api.h),
diff --git a/doc/guides/rel_notes/release_25_07.rst 
b/doc/guides/rel_notes/release_25_07.rst
index 093b85d206..7f11c91b7a 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,16 @@ New Features
  Also, make sure to start the actual text at the margin.
  ===

+* **Added feature arc abstraction in graph library.**
+
+  Feature arc abstraction helps ``rte_graph`` based applications to steer
+  packets across different node path(s) based on the features (or protocols)
+  enabled on interfaces. Different feature node paths can be enabled/disabled
+  at runtime on some or on all interfaces. This abstraction also help
+  applications to hook ``out-of-tree nodes`` in DPDK in-built node paths in a
+  generic manner.
+
+  * Added ``ip4_output`` feature arc processing in ``ip4_rewrite`` node.

 Removed Items
 -
diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 00..350967b732
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,2050 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include 
+#include 
+#include 
+#include 
+
+#define GRAPH_FEATURE_MAX_NUM_PER_ARC  (64)
+
+#define connect_graph_nodes(node1, node2, edge, arc_name) \
+   __connect_graph_nodes(node1, node2, edge, arc_name, __LINE__)
+
+#define FEATURE_ARC_MEMZONE_NAME "__rte_feature_arc_main_mz"
+
+#define NUM_EXTRA_FEATURE_DATA   (2)
+
+#define graph_uint_cast(f) ((unsigned int)f)
+
+#define fdata_fix_get(arc, feat, index)\
+   RTE_GRAPH_FEATURE_TO_FEATURE_DATA(arc, feat, index)
+
+#define feat_dbg graph_dbg
+
+#define FEAT_COND_ERR(cond, ...)   \
+   do {   \
+   if (cond)  \
+   graph_err(__VA_ARGS__);\
+   } while (0)
+
+#define FEAT_ERR(fn, ln, ...)  \
+   GRAPH_LOG2(ERR, fn, ln, __VA_ARGS__)
+
+#define FEAT_ERR_JMP(_err, fn, ln, ...)\
+   do {   \
+   FEAT_ERR(fn, ln, __VA_ARGS__); \
+   rte_errno = _err;  \
+   } while (0)
+
+#define COND_ERR_JMP(_err, cond, fn, ln, ...)  \
+   do {   \
+   if (cond)  \
+   FEAT_ERR(fn, ln, __VA_ARGS__); \
+   rte_errno = _err;  \
+   } while (0)
+
+
+static struct rte_mbuf_dynfield rte_graph_feature_arc_mbuf_de

[PATCH v7 5/5] test/graph_feature_arc: add functional tests

2025-04-19 Thread Nitin Saxena
Added functional unit test case for verifying feature arc control plane
and fast path APIs

How to run:
$ echo "graph_feature_arc_autotest" | ./bin/dpdk-test

Signed-off-by: Nitin Saxena 
---
 app/test/meson.build  |1 +
 app/test/test_graph_feature_arc.c | 1374 +
 2 files changed, 1375 insertions(+)
 create mode 100644 app/test/test_graph_feature_arc.c

diff --git a/app/test/meson.build b/app/test/meson.build
index b6285a6b45..acb0d36c6b 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -90,6 +90,7 @@ source_file_deps = {
 'test_func_reentrancy.c': ['hash', 'lpm'],
 'test_graph.c': ['graph'],
 'test_graph_perf.c': ['graph'],
+'test_graph_feature_arc.c': ['graph'],
 'test_hash.c': ['net', 'hash'],
 'test_hash_functions.c': ['hash'],
 'test_hash_multiwriter.c': ['hash'],
diff --git a/app/test/test_graph_feature_arc.c 
b/app/test/test_graph_feature_arc.c
new file mode 100644
index 00..b46ab4457e
--- /dev/null
+++ b/app/test/test_graph_feature_arc.c
@@ -0,0 +1,1374 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "test.h"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#ifndef RTE_EXEC_ENV_WINDOWS
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define MBUF_NUM 256
+#define TEST_ARC1_NAME "arc1"
+#define TEST_ARC2_NAME "arc2"
+#define TEST_FAILED_ARC "failed_arc"
+#define MAX_INDEXES 10
+#define OVERRIDE_MAX_INDEX MBUF_NUM
+#define MAX_FEATURES 5
+
+#define dbg(...)
+
+#define SOURCE1 "arc_source1_node"
+#define INPUT_STATIC "arc_input_static_node"
+#define OUTPUT_STATIC "arc_output_static_node"
+#define PKT_FREE_STATIC "arc_pkt_free_static_node"
+#define ARC1_FEATURE1 "arc1_feature1_node"
+#define ARC1_FEATURE2 "arc1_feature2_node"
+#define ARC2_FEATURE1 "arc2_feature1_node"
+#define ARC2_FEATURE2 "arc2_feature2_node"
+#define ARC2_FEATURE3 "arc2_feature3_node"
+#define DUMMY1_STATIC "arc_dummy1_static_node"
+#define DUMMY2_STATIC "arc_dummy2_static_node"
+
+#define feature_cast(x) ((rte_graph_feature_t)(x))
+
+/* (Node index, Node Name, feature app_cookie base */
+#define FOREACH_TEST_NODE_ARC {\
+   R(0, SOURCE1, 64)   \
+   R(1, INPUT_STATIC, 128) \
+   R(2, OUTPUT_STATIC, 256)\
+   R(3, PKT_FREE_STATIC, 512)  \
+   R(4, ARC1_FEATURE1, 1024)   \
+   R(5, ARC1_FEATURE2, 2048)   \
+   R(6, ARC2_FEATURE1, 4096)   \
+   R(7, ARC2_FEATURE2, 8192)   \
+   R(8, ARC2_FEATURE3, 16384)  \
+   R(9, DUMMY1_STATIC, 32768)  \
+   R(10, DUMMY2_STATIC, 65536) \
+   }
+
+/**
+ * ARC1: Feature arc on ingress interface
+ * ARC2: Feature arc on egress interface
+ * XX_static: Static nodes
+ * XX_featureX: Feature X on arc
+ *
+ *-> ARC1_FEATURE1
+ *   || |
+ *   || v
+ *   ||   ARC1_FEATURE2
+ *   || |
+ *   |v v
+ *  SOURCE1 ->-> INPUT_STATIC --> OUTPUT_STATIC -> PKT_FREE_STATIC
+ * |   |  | ^  ^ ^
+ * |   |  | |  | |
+ * |   |   --> ARC2_FEATURE1   | |
+ * |   |  ^ ^  | |
+ * |   |  | |  | |
+ * |--c-> ARC2_FEATURE2  |
+ * |  | ^|
+ * |  | ||
+ *  --> ARC2_FEATURE3 ---
+ */
+const char *node_names_feature_arc[] = {
+   SOURCE1, INPUT_STATIC, OUTPUT_STATIC, PKT_FREE_STATIC,
+   ARC1_FEATURE1, ARC1_FEATURE2, ARC2_FEATURE1, ARC2_FEATURE2, 
ARC2_FEATURE3,
+   DUMMY1_STATIC, DUMMY2_STATIC
+};
+
+const char *arc_names[] = {
+   TEST_ARC1_NAME, TEST_ARC2_NAME,
+};
+
+const char *arc1_feature_seq[] = {
+   ARC1_FEATURE1, ARC1_FEATURE2, INPUT_STATIC,
+};
+
+const char *arc2_feature_seq[] = {
+   ARC2_FEATURE1, ARC2_FEATURE2, ARC2_FEATURE3, PKT_FREE_STATIC,
+};
+
+#define MAX_NODES  RTE_DIM(node_names_feature_arc)
+
+typedef enum {
+   START_NODE,
+   INTERMEDIATE_FEATURE,
+   END_FEATURE,
+} fp_n

[PATCH v7 3/5] ip4: add ip4 output feature arc

2025-04-19 Thread Nitin Saxena
- Added ip4 output arc to allow applications to hook feature nodes in ip4
egress direction
- Added interface_tx node as end feature to ip4 output arc

Signed-off-by: Nitin Saxena 
---
 lib/node/ethdev_ctrl.c   |   8 +
 lib/node/interface_tx_feature.c  | 213 
 lib/node/interface_tx_feature_priv.h |  33 
 lib/node/ip4_rewrite.c   | 286 ++-
 lib/node/meson.build |   1 +
 lib/node/node_private.h  |   1 +
 lib/node/rte_node_ip4_api.h  |   4 +
 7 files changed, 539 insertions(+), 7 deletions(-)
 create mode 100644 lib/node/interface_tx_feature.c
 create mode 100644 lib/node/interface_tx_feature_priv.h

diff --git a/lib/node/ethdev_ctrl.c b/lib/node/ethdev_ctrl.c
index dca7e817f9..f717903731 100644
--- a/lib/node/ethdev_ctrl.c
+++ b/lib/node/ethdev_ctrl.c
@@ -15,6 +15,7 @@
 #include "ethdev_tx_priv.h"
 #include "ip4_rewrite_priv.h"
 #include "ip6_rewrite_priv.h"
+#include "interface_tx_feature_priv.h"
 #include "node_private.h"

 static struct ethdev_ctrl {
@@ -26,6 +27,7 @@ int
 rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
uint16_t nb_graphs)
 {
+   struct rte_node_register *if_tx_feature_node;
struct rte_node_register *ip4_rewrite_node;
struct rte_node_register *ip6_rewrite_node;
struct ethdev_tx_node_main *tx_node_data;
@@ -37,6 +39,7 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, 
uint16_t nb_confs,
int i, j, rc;
uint32_t id;

+   if_tx_feature_node = if_tx_feature_node_get();
ip4_rewrite_node = ip4_rewrite_node_get();
ip6_rewrite_node = ip6_rewrite_node_get();
tx_node_data = ethdev_tx_node_data_get();
@@ -127,6 +130,11 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, 
uint16_t nb_confs,
if (rc < 0)
return rc;

+   /* Add this tx port node to if_tx_feature_node */
+   rte_node_edge_update(if_tx_feature_node->id, 
RTE_EDGE_ID_INVALID,
+&next_nodes, 1);
+   rc = if_tx_feature_node_set_next(port_id,
+
rte_node_edge_count(if_tx_feature_node->id) - 1);
}

ctrl.nb_graphs = nb_graphs;
diff --git a/lib/node/interface_tx_feature.c b/lib/node/interface_tx_feature.c
new file mode 100644
index 00..c8809d5f91
--- /dev/null
+++ b/lib/node/interface_tx_feature.c
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include "rte_node_ip4_api.h"
+#include "node_private.h"
+#include "interface_tx_feature_priv.h"
+
+#define IF_TX_FEATURE_LAST_NEXT_INDEX(ctx) \
+   (((struct if_tx_feature_node_ctx *)ctx)->last_index)
+/*
+ * @internal array for mapping port to next node index
+ */
+struct if_tx_feature_node_main  {
+   uint16_t next_index[RTE_MAX_ETHPORTS];
+};
+
+struct if_tx_feature_node_ctx {
+   uint16_t last_index;
+};
+
+static struct if_tx_feature_node_main *if_tx_feature_nm;
+
+int
+if_tx_feature_node_set_next(uint16_t port_id, uint16_t next_index)
+{
+   if (if_tx_feature_nm == NULL) {
+   if_tx_feature_nm = rte_zmalloc(
+   "if_tx_feature_nm", sizeof(struct 
if_tx_feature_node_main),
+   RTE_CACHE_LINE_SIZE);
+   if (if_tx_feature_nm == NULL)
+   return -ENOMEM;
+   }
+   if_tx_feature_nm->next_index[port_id] = next_index;
+
+   return 0;
+}
+
+static int
+if_tx_feature_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+   RTE_SET_USED(graph);
+
+   /* pkt_drop */
+   IF_TX_FEATURE_LAST_NEXT_INDEX(node->ctx) = 0;
+
+   return 0;
+}
+
+static uint16_t
+if_tx_feature_node_process(struct rte_graph *graph, struct rte_node *node,
+  void **objs, uint16_t nb_objs)
+{
+   uint16_t held = 0, next0 = 0, next1 = 0, next2 = 0, next3 = 0;
+   struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+   uint16_t last_spec = 0, fix_spec = 0;
+   void **to_next, **from;
+   rte_edge_t next_index;
+   uint16_t n_left_from;
+
+   /* Speculative next */
+   next_index = IF_TX_FEATURE_LAST_NEXT_INDEX(node->ctx);
+
+   from = objs;
+   n_left_from = nb_objs;
+   pkts = (struct rte_mbuf **)objs;
+
+   to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+   while (n_left_from > 4) {
+   if (likely(n_left_from > 7)) {
+   /* Prefetch next mbuf */
+   rte_prefetch0(objs[4]);
+   rte_prefetch0(objs[5]);
+   rte_prefetch0(objs[6]);

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

2025-04-19 Thread Nitin Saxena
- 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   
graph> feature disable   
graph> graph stats show

Signed-off-by: Nitin Saxena 
---
 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 | 174 
 app/graph/main.c|  15 +++-
 app/graph/meson.build   |   2 +
 app/graph/module_api.h  |   2 +
 8 files changed, 356 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 ip mac  # Add static 
neighbour for IPv4
 neigh add ipv6 ip mac  # Add static 
neighbour for IPv6
 help neigh   # Print help on neigh 
commands
+
+feature arcs # show all feature 
arcs
+feature name show# Show feature arc 
details
+feature enable arc_name feature_name interface   # 
Enable feature on interface
+feature disable arc_name feature_name 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 00..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 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#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  show   # Display features within 
an arc";
+
+static const char
+cmd_feature_enable_help[] = "feature enable   
";
+
+static const char
+cmd_feature_disable_help[] = "feature disable   
";
+
+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 *

[PATCH v7 1/5] graph: add API to override node process function

2025-04-19 Thread Nitin Saxena
New API used by feature arc library to override node's original
process() func.

Signed-off-by: Nitin Saxena 
---
 lib/graph/graph_private.h | 11 +++
 lib/graph/node.c  | 23 +++
 2 files changed, 34 insertions(+)

diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
index 813dd78b9d..579546e658 100644
--- a/lib/graph/graph_private.h
+++ b/lib/graph/graph_private.h
@@ -198,6 +198,17 @@ struct node_head *node_list_head_get(void);
  */
 struct node *node_from_name(const char *name);

+/**
+ * @internal
+ *
+ * Override process func of a node.
+ *
+ * @return
+ *   - 0: Success.
+ *   - <0: Error
+ */
+int node_override_process_func(rte_node_t id, rte_node_process_t process);
+
 /* Graph list functions */
 STAILQ_HEAD(graph_head, graph);

diff --git a/lib/graph/node.c b/lib/graph/node.c
index 101981ec24..c8a1cd5586 100644
--- a/lib/graph/node.c
+++ b/lib/graph/node.c
@@ -431,3 +431,26 @@ rte_node_max_count(void)
 {
return node_id;
 }
+
+int
+node_override_process_func(rte_node_t id, rte_node_process_t process)
+{
+   struct node *node;
+
+   NODE_ID_CHECK(id);
+   graph_spinlock_lock();
+
+   STAILQ_FOREACH(node, &node_list, next) {
+   if (node->id == id) {
+   node->process = process;
+   graph_spinlock_unlock();
+   return 0;
+   }
+   }
+
+   graph_spinlock_unlock();
+
+   return 0;
+fail:
+   return -1;
+}
--
2.43.0



[PATCH v7 0/5] add feature arc in rte_graph

2025-04-19 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to express relationship
between rte_graph nodes, as feature nodes, and allow packets steering
across these nodes in a simplified manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to outbound-IPsec-policy node. On the other hand,
packets routed to another interface (say eth1) must ot be sent to
IPsec node, as IPsec feature is disabled on eth1. Feature-arc allows
rte_graph applications to manage such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Runtime enable/disable features on an index (like interface), so that
if a feature is enabled at runtime, only then packets associated with
that interface will steer to corresponding feature node. Control plane
enables/disables features. Features enabled on one interface may not be
enabled on another interface with in a same feature arc.

2. Single feature arc represents a hook point at a network layer in an
given direction. Multiple feature arcs can be added in desginated nodes
to add various hook points in stack. Likt IPv4-output, IPv4-input,
IPv6-output, IPv6-input, ethernet-input, mpls-input, mpls-output etc.

3. Hook custom/out-of-tree nodes to DPDK in-built nodes and allow packet
steering from in-built node to custom node without requiring changes to
in-built fast path function

4. Express features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Changes in v7:
- Rebased to latest main for DPDK-25.07
- Added override_index_cb() in RTE_GRAPH_FEATURE_REGISTER() to allow
  features to take control of "max_index" (passed to
  rte_graph_feature_arc_create())
- Streamlined fast path APIs to make them more simpler
- Added quad_loop in "if_tx_feature" node
- Added functional test cases and fixed bugs
- Based on feedback from Redhat on v6 patches, it was tried to move
  feature arc APIs inside rte_graph library (to reduce node burden) but
  it was adding 4% performance regression for L3fwd usecase. For
  interested users: https://github.com/nsaxena16/dpdk/pull/2

Changes in v6:
- Rebased to latest main for DPDK-25.03
- Added constructor based feature arc/feature registration
- Changed design to handle fast path synchronization via RCU mechanism
  when any feature is enabled or disabled
- Added feature arc specific mbuf dynamic field to carry feature data
  across nodes
- Added feature arc example in app/graph
- Programming guide and functional test cases in future versions

Nitin Saxena (5):
  graph: add API to override node process function
  graph: add feature arc abstraction
  ip4: add ip4 output feature arc
  app/graph: add custom feature nodes for ip4 output arc
  test/graph_feature_arc: add functional tests

 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  |  174 ++
 app/graph/main.c |   15 +-
 app/graph/meson.build|2 +
 app/graph/module_api.h   |2 +
 app/test/meson.build |1 +
 app/test/test_graph_feature_arc.c| 1374 +++
 doc/api/doxy-api-index.md|2 +
 doc/guides/rel_notes/release_25_07.rst   |   10 +
 lib/graph/graph_feature_arc.c| 2050 ++
 lib/graph/graph_private.h|   15 +
 lib/graph/meson.build|4 +-
 lib/graph/node.c |   23 +
 lib/graph/rte_graph_feature_arc.h|  628 +++
 lib/graph/rte_graph_feature_arc_worker.h |  607 +++
 lib/node/ethdev_ctrl.c   |8 +
 lib/node/interface_tx_feature.c  |  213 +++
 lib/node/interface_tx_feature_priv.h |   33 +
 lib/node/ip4_rewrite.c   |  286 ++-
 lib/node/meson.build |1 +
 lib/node/node_private.h  |1 +
 lib/node/rte_node_ip4_api.h  |4 +
 25 files changed, 5608 insertions(+), 9 deletions(-)
 create mode 100644 app/graph/feature.c
 create mode 100644 app/graph/feature.h
 create mode 100644 app/graph/ip4_output_hook.c
 create mode 100644 app/test/test

Re: [PATCH v1 04/12] node: add process callback for IP4 FIB

2025-04-16 Thread Nitin Saxena
Hi Ankur,

Same comments apply to IPv6 nodes as well. See for ip4 lookup comments

Thanks,
Nitin

On Tue, Apr 15, 2025 at 6:20 PM Ankur Dwivedi  wrote:
>
> Adds the process callback function for ip4_lookup_fib node.
>
> Signed-off-by: Ankur Dwivedi 
> ---
>  lib/node/ip4_lookup_fib.c | 164 ++
>  1 file changed, 164 insertions(+)
>
> diff --git a/lib/node/ip4_lookup_fib.c b/lib/node/ip4_lookup_fib.c
> index e87864e672..c535b191f8 100644
> --- a/lib/node/ip4_lookup_fib.c
> +++ b/lib/node/ip4_lookup_fib.c
> @@ -40,6 +40,169 @@ static struct ip4_lookup_fib_node_main ip4_lookup_fib_nm;
>  #define IP4_LOOKUP_NODE_PRIV1_OFF(ctx) \
> (((struct ip4_lookup_fib_node_ctx *)ctx)->mbuf_priv1_off)
>
> +static uint16_t
> +ip4_lookup_fib_node_process(struct rte_graph *graph, struct rte_node *node, 
> void **objs,
> +   uint16_t nb_objs)
> +{
> +   struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
> +   struct rte_fib *fib = IP4_LOOKUP_NODE_FIB(node->ctx);
> +   const int dyn = IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx);
> +   struct rte_ipv4_hdr *ipv4_hdr;
> +   uint64_t next_hop[nb_objs];
> +   uint16_t lookup_err = 0;
> +   void **to_next, **from;
> +   uint16_t last_spec = 0;
> +   rte_edge_t next_index;
> +   uint16_t n_left_from;
> +   uint32_t ip[nb_objs];
> +   uint16_t held = 0;
> +   uint32_t drop_nh;
> +   uint16_t next;
> +   int i, rc;
> +
> +   /* Speculative next */
> +   next_index = RTE_NODE_IP4_LOOKUP_NEXT_REWRITE;

Is it possible if we add next_edge in node->ctx? Save next_index in
node->init() function and at the end of process() function for
better speculative performance

Also in general, this function is assuming packets are being forwarded
to rewrite node but there be can other paths as well like
- LOCAL
- PUNT etc.

So let next_hop returned from rte_fib_lookup_bulk() determine the
next_edge (even pkt_drop node). Control plane feeds next_edge in 8B
next_hop which I defined in other patch and also below
(rte_ip4_lookup_fib_next_hop_t)


> +   /* Drop node */
> +   drop_nh = ((uint32_t)RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP) << 16;

Let drop be determined from next_hop returned from rte_ip4_lookup_fib_next_hop_t
Control plane feeds default next_hop as part of setup_fib() as follows
struct {
   struct {
 uint32_t next_hop_od;
uint16_t next_edge;
uint16_t reserved;
   };
   uint64_t u4;
} rte_ip4_lookpu_fib_next_hop_t;

default_nh = {,next = RTE_NODE_IP4_LOOKUP_NEXT_PKT_DROP}; which is
programmed in setup_fib
> +
> +   pkts = (struct rte_mbuf **)objs;
> +   from = objs;
> +   n_left_from = nb_objs;
> +
> +   /* Get stream for the speculated next node */
> +   to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
> +
> +   for (i = OBJS_PER_CLINE; i < RTE_GRAPH_BURST_SIZE; i += 
> OBJS_PER_CLINE)
> +   rte_prefetch0(&objs[i]);
> +
> +#if RTE_GRAPH_BURST_SIZE > 64
> +   for (i = 0; i < 4 && i < n_left_from; i++) {
> +   rte_prefetch0(pkts[i]);
> +   rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[i], void *,
> +   sizeof(struct rte_ether_hdr)));
> +   }
> +#endif
> +
> +   i = 0;
> +   while (n_left_from >= 4) {
> +#if RTE_GRAPH_BURST_SIZE > 64
> +   if (likely(n_left_from > 7)) {
> +   rte_prefetch0(pkts[4]);
> +   rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[4], void *,
> +   sizeof(struct rte_ether_hdr)));
> +   rte_prefetch0(pkts[5]);
> +   rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[5], void *,
> +   sizeof(struct rte_ether_hdr)));
> +   rte_prefetch0(pkts[6]);
> +   rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[6], void *,
> +   sizeof(struct rte_ether_hdr)));
> +   rte_prefetch0(pkts[7]);
> +   rte_prefetch0(rte_pktmbuf_mtod_offset(pkts[7], void *,
> +   sizeof(struct rte_ether_hdr)));
> +   }
> +#endif
> +
> +   mbuf0 = pkts[0];
> +   mbuf1 = pkts[1];
> +   mbuf2 = pkts[2];
> +   mbuf3 = pkts[3];
> +   pkts += 4;
> +   n_left_from -= 4;
> +   /* Extract DIP of mbuf0 */
> +   ipv4_hdr = rte_pktmbuf_mtod_offset(mbuf0, struct rte_ipv4_hdr 
> *,
> +   sizeof(struct rte_ether_hdr));
> +   /* Extract cksum, ttl as ipv4 hdr is in cache */
> +   node_mbuf_priv1(mbuf0, dyn)->cksum = ipv4_hdr->hdr_checksum;
> +   node_mbuf_priv1(mbuf0, dyn)->ttl = ipv4_hdr->time_to_live;
> +
> +   ip[i++] = rte_be_to_cpu_32(ipv4_hdr->dst_addr);
> +
> +   /* Ex

Re: [PATCH v1 02/12] node: add IP4 lookup FIB node

2025-04-16 Thread Nitin Saxena
Hi Ankur,

Please see my comments inline below

Thanks,
Nitin

On Tue, Apr 15, 2025 at 5:41 PM Ankur Dwivedi  wrote:
>
> Adds a lookup FIB node for IP4.
>
> Signed-off-by: Ankur Dwivedi 
> ---
>  lib/node/ip4_lookup_fib.c | 127 ++
>  lib/node/meson.build  |   3 +-
>  2 files changed, 129 insertions(+), 1 deletion(-)
>  create mode 100644 lib/node/ip4_lookup_fib.c
>
> diff --git a/lib/node/ip4_lookup_fib.c b/lib/node/ip4_lookup_fib.c
> new file mode 100644
> index 00..9c71610718
> --- /dev/null
> +++ b/lib/node/ip4_lookup_fib.c
> @@ -0,0 +1,127 @@
> +/* SPDX-License-Identifier: BSD-3-Clause
> + * Copyright(C) 2025 Marvell.
> + */
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include "rte_node_ip4_api.h"
> +
> +#include "node_private.h"
> +
> +/* IP4 Lookup global data struct */
> +struct ip4_lookup_fib_node_main {
> +   struct rte_fib *fib[RTE_MAX_NUMA_NODES];
> +};
> +
> +struct ip4_lookup_fib_node_ctx {
> +   /* Socket's FIB */
> +   struct rte_fib *fib;
> +   /* Dynamic offset to mbuf priv1 */
> +   int mbuf_priv1_off;
> +};
> +
> +static struct ip4_lookup_fib_node_main ip4_lookup_fib_nm;
> +
> +#define FIB_MAX_ROUTES (1 << 16)
> +#define FIB_NUM_TBL8   (1 << 15)
> +#define FIB_DEFAULT_NH 999

These macros may not be required if we expose public setup_api() with
arguments. See below

> +
> +#define IP4_LOOKUP_NODE_FIB(ctx) \
> +   (((struct ip4_lookup_fib_node_ctx *)ctx)->fib)
> +
> +#define IP4_LOOKUP_NODE_PRIV1_OFF(ctx) \
> +   (((struct ip4_lookup_fib_node_ctx *)ctx)->mbuf_priv1_off)
> +
> +static int
> +setup_fib(unsigned int socket)

Should we add public API to allow applications to control MAX_ROUTES?
In a typical stack multiple fibs can be set up for each VRF (~~ port_id).

A public API:

int rte_ip4_lookup_fib_setup(int fib_index, int port_id, uint32_t
max_routes) where

For now we can assume fib_index == 0, is global fib table (can be
extended to VRF later). Socket_id can be determined from port_Id

> +{
> +   struct ip4_lookup_fib_node_main *nm = &ip4_lookup_fib_nm;
> +   struct rte_fib_conf conf;
> +   char s[RTE_FIB_NAMESIZE];
> +
> +   /* One fib per socket */
> +   if (nm->fib[socket])
> +   return 0;
> +
> +   conf.type = RTE_FIB_DIR24_8;
> +   conf.default_nh = FIB_DEFAULT_NH;

FIB_DEFAULT_NH can be defined in such a way, fast path can decode
next_edge from return of rte_fib_lookup_bulk()
Like
union {
   struct u64;
   struct {
   uint32_t next_hop_id;
   uint16_t  next_edge;
   uint16_t. reserved;
   };
} rte_ip4_lookup_fib_nexthop_t;

FIB_DEFAULT_NH should be set as

rte_ip4_lookup_fib_nexthop_t default_next_hop = {.next_edge =
IP4_FIB_LOOKUP_NEXT_DROP}

This way in fast path a return from fib_bulk() can be directly used to
send packet to pkt_drop node

> +   conf.max_routes = FIB_MAX_ROUTES;
> +   conf.rib_ext_sz = 0;
> +   conf.dir24_8.nh_sz = RTE_FIB_DIR24_8_4B;
> +   conf.dir24_8.num_tbl8 = FIB_NUM_TBL8;
> +   conf.flags = 0;
> +   snprintf(s, sizeof(s), "IPV4_LOOKUP_FIB_%d", socket);
> +   nm->fib[socket] = rte_fib_create(s, socket, &conf);
> +   if (nm->fib[socket] == NULL)
> +   return -rte_errno;
> +
> +   return 0;
> +}
> +
> +static int
> +ip4_lookup_fib_node_init(const struct rte_graph *graph, struct rte_node 
> *node)
> +{
> +   static uint8_t init_once;
> +   unsigned int socket;
> +   uint16_t lcore_id;
> +   int rc;
> +
> +   RTE_BUILD_BUG_ON(sizeof(struct ip4_lookup_fib_node_ctx) > 
> RTE_NODE_CTX_SZ);
> +
> +   if (!init_once) {
> +   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
> +   &node_mbuf_priv1_dynfield_desc);
> +   if (node_mbuf_priv1_dynfield_offset < 0)
> +   return -rte_errno;

You may need to rebase this patch on top of
https://patches.dpdk.org/project/dpdk/patch/20250409135554.2180390-2-nsax...@marvell.com/
for using global mbuf field

> +
> +   /* Setup FIB for all sockets */
> +   RTE_LCORE_FOREACH(lcore_id)

Instead can be use rte_socket_count() which allow to loop for socket
instead of port? Ideally we may need to add fib for virtual interfaces
or VRF (later)

> +   {
> +   socket = rte_lcore_to_socket_id(lcore_id);
> +   rc = setup_fib(socket);
> +   if (rc) {
> +   node_err("ip4_lookup_fib",
> +"Failed to setup fib for sock %u, 
> rc=%d",
> +socket, rc);
> +   return rc;
> +   }
> +   }
> +   init_once = 1;
> +   }
> +
> +   /* Update socket's FIB and mbuf dyn priv1 offset in node ctx */
> +   IP4_LOOKUP_NODE_FIB(node->ctx) = ip4_lookup_fib_n

[PATCH v5 0/2] node: add mbuf dynamic field for nodes

2025-04-10 Thread Nitin Saxena
Currently each rte_node registers separate mbuf dynamic fields for their
own purpose. This leads to wastage of mbuf space as once mbuf get passed
a particular node, the registered dynamic field(by that node) is no
longer used.

This patch series adds a global/common mbuf dynamic field which is
reusable by all the nodes(including out-of-tree nodes). This helps to
repurpose same mbuf dynamic field for other nodes. It contains two types
of fields: (a) persistent (b) overloadable.

While persistent fields are those which does not often changes during a
graph walk such as rx/tx interface, buffer flags etc. Currently there
are no persistent fields added but they can be added later

Overloadable fields are those which can be overloaded by two adjacent
nodes. Overloadable fields can be repurposed by other two adjacent nodes.

This patch series also updates ip4/ip6 lookup/rewrite nodes to use
overlaodable mbuf dynamic fields.

Changes in v5
- Rebase on latest main with version.map changes
- Bring back memzone based node mbuf dynfield for secondary
  process [Pavan]

Changes in v4
- Fix github CI

Changes in v3:
- Fix CI build error

Changes in v2:
- removed usage of memzone for saving mbuf dynfield [Stephen]
- fixed checkpatch issues
- redefine RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE to 8 byte which are
  currently in use. Size can increase later based on the usage

Nitin Saxena (2):
  node: add global node mbuf dynfield
  node: use node mbuf dynfield in ip4 nodes

 doc/api/doxy-api-index.md  |   3 +-
 doc/guides/rel_notes/release_25_07.rst |   6 ++
 lib/node/ip4_lookup.c  |  14 +--
 lib/node/ip4_rewrite.c |  15 ++-
 lib/node/ip6_lookup.c  |  15 ++-
 lib/node/ip6_rewrite.c |  14 +--
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  57 ++
 lib/node/node_private.h|  40 +--
 lib/node/rte_node_mbuf_dynfield.h  | 141 +
 10 files changed, 233 insertions(+), 74 deletions(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

--
2.43.0



[PATCH v6 0/2] node: add mbuf dynamic field for nodes

2025-04-28 Thread Nitin Saxena
Currently each rte_node registers separate mbuf dynamic fields for their
own purpose. This leads to wastage of mbuf space as once mbuf get passed
a particular node, the registered dynamic field(by that node) is no
longer used.

This patch series adds a global/common mbuf dynamic field which is
reusable by all the nodes(including out-of-tree nodes). This helps to
repurpose same mbuf dynamic field for other nodes. It contains two types
of fields: (a) persistent (b) overloadable.

While persistent fields are those which does not often changes during a
graph walk such as rx/tx interface, buffer flags etc. Currently there
are no persistent fields added but they can be added later

Overloadable fields are those which can be used by two adjacent nodes.
Same overloadable fields can be repurposed by other two adjacent nodes.

This patch series also updates ip4/ip6 lookup/rewrite nodes to use
overlaodable mbuf dynamic fields.

Changes in v6:
- Incorporate comments from Pavan

Changes in v5
- Rebase on latest main with version.map changes
- Bring back memzone based node mbuf dynfield for secondary
  process [Pavan]

Changes in v4
- Fix github CI

Changes in v3:
- Fix CI build error

Changes in v2:
- removed usage of memzone for saving mbuf dynfield [Stephen]
- fixed checkpatch issues
- redefine RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE to 8 byte which are
  currently in use. Size can increase later based on the usage


Nitin Saxena (2):
  node: add global node mbuf dynfield
  node: use node mbuf dynfield in ip4 nodes

 doc/api/doxy-api-index.md  |   2 +
 doc/guides/rel_notes/release_25_07.rst |   6 +
 lib/node/ip4_lookup.c  |  14 +--
 lib/node/ip4_rewrite.c |  15 +--
 lib/node/ip6_lookup.c  |  15 +--
 lib/node/ip6_rewrite.c |  14 +--
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  59 ++
 lib/node/node_private.h|  40 +--
 lib/node/rte_node_mbuf_dynfield.h  | 149 +
 10 files changed, 243 insertions(+), 73 deletions(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

--
2.43.0



[PATCH v6 1/2] node: add global node mbuf dynfield

2025-04-28 Thread Nitin Saxena
This patch defines rte_node specific dynamic field structure
(rte_node_mbuf_dynfield_t)

rte_node_mbuf_dynfield_t structure holds two types of fields
- Persistent data fields which are preserved across graph walk.
  Currently size of persistent data fields is zero.
- Overloadable data fields which are used by any two adjacent nodes.
  Same fields can be repurposed by any other adjacent nodes

This dynfield can be also be used by out-of-tree nodes.

Signed-off-by: Nitin Saxena 
---
 doc/api/doxy-api-index.md  |   2 +
 doc/guides/rel_notes/release_25_07.rst |   6 ++
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  59 +++
 lib/node/rte_node_mbuf_dynfield.h  | 140 +
 5 files changed, 209 insertions(+)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5c425a2cb9..e1c85bcfbd 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -219,6 +219,8 @@ The public API headers are grouped by topics:
 [ip4_node](@ref rte_node_ip4_api.h),
 [ip6_node](@ref rte_node_ip6_api.h),
 [udp4_input_node](@ref rte_node_udp4_input_api.h)
+  * graph_nodes_mbuf:
+[node_mbuf_dynfield](@ref rte_node_mbuf_dynfield.h)

 - **basic**:
   [bitops](@ref rte_bitops.h),
diff --git a/doc/guides/rel_notes/release_25_07.rst 
b/doc/guides/rel_notes/release_25_07.rst
index 093b85d206..2dc888e65d 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,12 @@ New Features
  Also, make sure to start the actual text at the margin.
  ===

+* **Added rte_node specific global mbuf dynamic field.**
+
+  Instead each node registering mbuf dynamic field for its own purpose, a
+  global structure is added which can be used/overloaded by all nodes
+  (including out-of-tree nodes). This minimizes footprint of node specific mbuf
+  dynamic field.

 Removed Items
 -
diff --git a/lib/node/meson.build b/lib/node/meson.build
index 0bed97a96c..152fe41129 100644
--- a/lib/node/meson.build
+++ b/lib/node/meson.build
@@ -8,6 +8,7 @@ if is_windows
 endif

 sources = files(
+'node_mbuf_dynfield.c',
 'ethdev_ctrl.c',
 'ethdev_rx.c',
 'ethdev_tx.c',
@@ -30,6 +31,7 @@ headers = files(
 'rte_node_ip4_api.h',
 'rte_node_ip6_api.h',
 'rte_node_udp4_input_api.h',
+'rte_node_mbuf_dynfield.h',
 )

 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
diff --git a/lib/node/node_mbuf_dynfield.c b/lib/node/node_mbuf_dynfield.c
new file mode 100644
index 00..6fbbf9604c
--- /dev/null
+++ b/lib/node/node_mbuf_dynfield.c
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define NODE_MBUF_DYNFIELD_MEMZONE_NAME "__rte_node_mbuf_dynfield"
+
+struct node_mbuf_dynfield_mz {
+   int dynfield_offset;
+};
+
+static const struct rte_mbuf_dynfield node_mbuf_dynfield_desc = {
+   .name = "rte_node_mbuf_dynfield",
+   .size = sizeof(rte_node_mbuf_dynfield_t),
+   .align = alignof(rte_node_mbuf_dynfield_t),
+};
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_node_mbuf_dynfield_register, 25.07);
+int rte_node_mbuf_dynfield_register(void)
+{
+   struct node_mbuf_dynfield_mz *f = NULL;
+   const struct rte_memzone *mz = NULL;
+   int dyn_offset;
+
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_dynfield_t) < 
RTE_NODE_MBUF_DYNFIELD_SIZE);
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_overload_fields_t) <
+RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE);
+
+   mz = rte_memzone_lookup(NODE_MBUF_DYNFIELD_MEMZONE_NAME);
+
+   if (!mz) {
+   mz = 
rte_memzone_reserve_aligned(NODE_MBUF_DYNFIELD_MEMZONE_NAME,
+sizeof(struct 
node_mbuf_dynfield_mz),
+SOCKET_ID_ANY, 0,
+RTE_CACHE_LINE_SIZE);
+   if (!mz) {
+   node_err("node_mbuf_dyn", "rte_memzone_reserve_aligned 
failed");
+   rte_errno = ENOMEM;
+   return -1;
+   }
+   dyn_offset = 
rte_mbuf_dynfield_register(&node_mbuf_dynfield_desc);
+   if (dyn_offset < 0) {
+   node_err("node_mbuf_dyn", "rte_mbuf_dynfield_register 
failed");
+   return dyn_offset;
+   }
+   f = (struct node_mbuf_dynfield_mz *)mz->addr;
+   f->dynfield_offset = dyn_offset;
+
+ 

[PATCH v6 2/2] node: use node mbuf dynfield in ip4 nodes

2025-04-28 Thread Nitin Saxena
- Used global node mbuf in ip[4|6]_lookup/rewrite nodes
- Redefine node_mbuf_priv1() to rte_node_mbuf_overload_fields_get()

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_lookup.c | 14 ---
 lib/node/ip4_rewrite.c| 15 +---
 lib/node/ip6_lookup.c | 15 +---
 lib/node/ip6_rewrite.c| 14 ---
 lib/node/node_private.h   | 40 +++
 lib/node/rte_node_mbuf_dynfield.h |  9 +++
 6 files changed, 34 insertions(+), 73 deletions(-)

diff --git a/lib/node/ip4_lookup.c b/lib/node/ip4_lookup.c
index 604fcb267a..f6db3219f0 100644
--- a/lib/node/ip4_lookup.c
+++ b/lib/node/ip4_lookup.c
@@ -32,8 +32,6 @@ struct ip4_lookup_node_ctx {
int mbuf_priv1_off;
 };

-int node_mbuf_priv1_dynfield_offset = -1;
-
 static struct ip4_lookup_node_main ip4_lookup_nm;

 #define IP4_LOOKUP_NODE_LPM(ctx) \
@@ -182,17 +180,15 @@ ip4_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_lookup_node_ctx) > RTE_NODE_CTX_SZ);

+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -210,7 +206,7 @@ ip4_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP4_LOOKUP_NODE_LPM(node->ctx) = ip4_lookup_nm.lpm_tbl[graph->socket];
-   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

 #if defined(__ARM_NEON) || defined(RTE_ARCH_X86)
if (rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128)
diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index a9ab5eaa57..dfa4382350 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -259,19 +259,16 @@ ip4_rewrite_node_process(struct rte_graph *graph, struct 
rte_node *node,
 static int
 ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
-   static bool init_once;
+   int dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_rewrite_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-   init_once = true;
-   }
-   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
+
+   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip4_rewrite", "Initialized ip4_rewrite node initialized");

diff --git a/lib/node/ip6_lookup.c b/lib/node/ip6_lookup.c
index 827fc2f379..83c0500c76 100644
--- a/lib/node/ip6_lookup.c
+++ b/lib/node/ip6_lookup.c
@@ -318,18 +318,16 @@ ip6_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip6_lookup_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset =
-   rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;

+   if (!init_once) {
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -347,8 +345,7 @@ ip6_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP6_LOOKUP_NODE_LPM(node->ctx) = ip6_lookup_nm.lpm_tbl[graph->socket];
-   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) =
-   node_mbuf_priv1_dynfield_offset;
+   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip6_lookup", "Initialized ip6_lookup node");

diff --git a/lib/n

Re: [PATCH v5 1/2] node: add global node mbuf dynfield

2025-04-28 Thread Nitin Saxena
Hi Pavan,

I have incorporated your comments in patch 6
Thanks for reviewing

Nitin

On Sat, Apr 19, 2025 at 12:33 AM Pavan Nikhilesh Bhagavatula
 wrote:
>
>
>
> > -Original Message-
> > From: Nitin Saxena 
> > Sent: Wednesday, April 9, 2025 7:26 PM
> > To: Nithin Kumar Dabilpuram ; Pavan Nikhilesh
> > Bhagavatula ; Robin Jarry
> > ; Christophe Fontaine 
> > Cc: dev@dpdk.org; Jerin Jacob ; Nitin Saxena
> > 
> > Subject: [PATCH v5 1/2] node: add global node mbuf dynfield
> >
> > This patch defines rte_node specific dynamic field structure
> > (rte_node_mbuf_dynfield_t)
> >
> > rte_node_mbuf_dynfield_t structure holds two types of fields
> > - Persistent data fields which are preserved across graph walk.
> >   Currently size of persistent data fields is zero.
> > - Overloadable data fields which are used by any two adjacent nodes.
> >   Same fields can be repurposed by any other adjacent nodes
> >
> > This dynfield can be also be used by out-of-tree nodes.
> >
> > Signed-off-by: Nitin Saxena 
> > ---
> >  doc/api/doxy-api-index.md  |   3 +-
> >  doc/guides/rel_notes/release_25_07.rst |   6 ++
> >  lib/node/meson.build   |   2 +
> >  lib/node/node_mbuf_dynfield.c  |  57 +++
> >  lib/node/rte_node_mbuf_dynfield.h  | 132
> > +
> >  5 files changed, 199 insertions(+), 1 deletion(-)
> >  create mode 100644 lib/node/node_mbuf_dynfield.c
> >  create mode 100644 lib/node/rte_node_mbuf_dynfield.h
> >
> > diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
> > index 5c425a2cb9..763cfb3f3c 100644
> > --- a/doc/api/doxy-api-index.md
> > +++ b/doc/api/doxy-api-index.md
> > @@ -219,7 +219,8 @@ The public API headers are grouped by topics:
> >  [ip4_node](@ref rte_node_ip4_api.h),
> >  [ip6_node](@ref rte_node_ip6_api.h),
> >  [udp4_input_node](@ref rte_node_udp4_input_api.h)
> > -
> > +  * graph_nodes_mbuf:
> > +[node_mbuf_dynfield](@ref rte_node_mbuf_dynfield.h)
>
> Missing blank line here.
Done

>
> >  - **basic**:
> >[bitops](@ref rte_bitops.h),
> >[approx fraction](@ref rte_approx.h),
> > diff --git a/doc/guides/rel_notes/release_25_07.rst
> > b/doc/guides/rel_notes/release_25_07.rst
> > index 093b85d206..2dc888e65d 100644
> > --- a/doc/guides/rel_notes/release_25_07.rst
> > +++ b/doc/guides/rel_notes/release_25_07.rst
> > @@ -55,6 +55,12 @@ New Features
> >   Also, make sure to start the actual text at the margin.
> >   ===
> >
> > +* **Added rte_node specific global mbuf dynamic field.**
> > +
> > +  Instead each node registering mbuf dynamic field for its own purpose, a
> > +  global structure is added which can be used/overloaded by all nodes
> > +  (including out-of-tree nodes). This minimizes footprint of node specific
> > mbuf
> > +  dynamic field.
> >
> >  Removed Items
> >  -
> > diff --git a/lib/node/meson.build b/lib/node/meson.build
> > index 0bed97a96c..152fe41129 100644
> > --- a/lib/node/meson.build
> > +++ b/lib/node/meson.build
> > @@ -8,6 +8,7 @@ if is_windows
> >  endif
> >
> >  sources = files(
> > +'node_mbuf_dynfield.c',
> >  'ethdev_ctrl.c',
> >  'ethdev_rx.c',
> >  'ethdev_tx.c',
> > @@ -30,6 +31,7 @@ headers = files(
> >  'rte_node_ip4_api.h',
> >  'rte_node_ip6_api.h',
> >  'rte_node_udp4_input_api.h',
> > +'rte_node_mbuf_dynfield.h',
> >  )
> >
> >  # Strict-aliasing rules are violated by uint8_t[] to context size casts.
> > diff --git a/lib/node/node_mbuf_dynfield.c b/lib/node/node_mbuf_dynfield.c
> > new file mode 100644
> > index 00..6005e72ed6
> > --- /dev/null
> > +++ b/lib/node/node_mbuf_dynfield.c
> > @@ -0,0 +1,57 @@
> > +/* SPDX-License-Identifier: BSD-3-Clause
> > + * Copyright(C) 2025 Marvell International Ltd.
> > + */
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +
> > +#define NODE_MBUF_DYNFIELD_MEMZONE_NAME
> > "__rte_node_mbuf_dynfield"
> > +
> > +struct node_mbuf_dynfield_mz {
> > + int dynfield_offset;
> > +};
> > +
> > +static const struct rte_mbuf_dynfield node_mbuf_dynfield_desc = {
> > + .name = "rte_node_mbuf_dynfield",
>

[PATCH v8 1/5] graph: add API to override node process function

2025-04-19 Thread Nitin Saxena
New API used by feature arc library to override node's original
process() func.

Signed-off-by: Nitin Saxena 
---
 lib/graph/graph_private.h | 11 +++
 lib/graph/node.c  | 23 +++
 2 files changed, 34 insertions(+)

diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
index 813dd78b9d..579546e658 100644
--- a/lib/graph/graph_private.h
+++ b/lib/graph/graph_private.h
@@ -198,6 +198,17 @@ struct node_head *node_list_head_get(void);
  */
 struct node *node_from_name(const char *name);

+/**
+ * @internal
+ *
+ * Override process func of a node.
+ *
+ * @return
+ *   - 0: Success.
+ *   - <0: Error
+ */
+int node_override_process_func(rte_node_t id, rte_node_process_t process);
+
 /* Graph list functions */
 STAILQ_HEAD(graph_head, graph);

diff --git a/lib/graph/node.c b/lib/graph/node.c
index 101981ec24..c8a1cd5586 100644
--- a/lib/graph/node.c
+++ b/lib/graph/node.c
@@ -431,3 +431,26 @@ rte_node_max_count(void)
 {
return node_id;
 }
+
+int
+node_override_process_func(rte_node_t id, rte_node_process_t process)
+{
+   struct node *node;
+
+   NODE_ID_CHECK(id);
+   graph_spinlock_lock();
+
+   STAILQ_FOREACH(node, &node_list, next) {
+   if (node->id == id) {
+   node->process = process;
+   graph_spinlock_unlock();
+   return 0;
+   }
+   }
+
+   graph_spinlock_unlock();
+
+   return 0;
+fail:
+   return -1;
+}
--
2.43.0



[PATCH v8 2/5] graph: add feature arc abstraction

2025-04-19 Thread Nitin Saxena
Feature arc abstraction allows rte_graph based applications to
- Allow control plane to runtime enable/disable feature nodes.
  Fast path APIs helps to steer packets across enabled feature nodes
- Feature enable/disable based on indexes. Index can be interface index,
  route index, etc
- More than one feature nodes can be added to an arc and also provide
  mechanism to control features sequencing order in fast path.
- Does not require stopping of workers for control plane updates. RCU
  mechanism also provided
- Once DPDK inbuilt nodes adopts feature arc abstraction, out-of-tree
  nodes can also be hooked (with no custom changes in DPDK in-built
  nodes)

Signed-off-by: Nitin Saxena 
---
 doc/api/doxy-api-index.md|2 +
 doc/guides/rel_notes/release_25_07.rst   |   10 +
 lib/graph/graph_feature_arc.c| 2050 ++
 lib/graph/graph_private.h|4 +
 lib/graph/meson.build|4 +-
 lib/graph/rte_graph_feature_arc.h|  628 +++
 lib/graph/rte_graph_feature_arc_worker.h |  607 +++
 7 files changed, 3304 insertions(+), 1 deletion(-)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5c425a2cb9..6d8b531344 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -214,6 +214,8 @@ The public API headers are grouped by topics:
 [table_wm](@ref rte_swx_table_wm.h)
   * [graph](@ref rte_graph.h):
 [graph_worker](@ref rte_graph_worker.h)
+[graph_feature_arc](@ref rte_graph_feature_arc.h)
+[graph_feature_arc_worker](@ref rte_graph_feature_arc_worker.h)
   * graph_nodes:
 [eth_node](@ref rte_node_eth_api.h),
 [ip4_node](@ref rte_node_ip4_api.h),
diff --git a/doc/guides/rel_notes/release_25_07.rst 
b/doc/guides/rel_notes/release_25_07.rst
index 093b85d206..7f11c91b7a 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,16 @@ New Features
  Also, make sure to start the actual text at the margin.
  ===

+* **Added feature arc abstraction in graph library.**
+
+  Feature arc abstraction helps ``rte_graph`` based applications to steer
+  packets across different node path(s) based on the features (or protocols)
+  enabled on interfaces. Different feature node paths can be enabled/disabled
+  at runtime on some or on all interfaces. This abstraction also help
+  applications to hook ``out-of-tree nodes`` in DPDK in-built node paths in a
+  generic manner.
+
+  * Added ``ip4_output`` feature arc processing in ``ip4_rewrite`` node.

 Removed Items
 -
diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 00..350967b732
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,2050 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include 
+#include 
+#include 
+#include 
+
+#define GRAPH_FEATURE_MAX_NUM_PER_ARC  (64)
+
+#define connect_graph_nodes(node1, node2, edge, arc_name) \
+   __connect_graph_nodes(node1, node2, edge, arc_name, __LINE__)
+
+#define FEATURE_ARC_MEMZONE_NAME "__rte_feature_arc_main_mz"
+
+#define NUM_EXTRA_FEATURE_DATA   (2)
+
+#define graph_uint_cast(f) ((unsigned int)f)
+
+#define fdata_fix_get(arc, feat, index)\
+   RTE_GRAPH_FEATURE_TO_FEATURE_DATA(arc, feat, index)
+
+#define feat_dbg graph_dbg
+
+#define FEAT_COND_ERR(cond, ...)   \
+   do {   \
+   if (cond)  \
+   graph_err(__VA_ARGS__);\
+   } while (0)
+
+#define FEAT_ERR(fn, ln, ...)  \
+   GRAPH_LOG2(ERR, fn, ln, __VA_ARGS__)
+
+#define FEAT_ERR_JMP(_err, fn, ln, ...)\
+   do {   \
+   FEAT_ERR(fn, ln, __VA_ARGS__); \
+   rte_errno = _err;  \
+   } while (0)
+
+#define COND_ERR_JMP(_err, cond, fn, ln, ...)  \
+   do {   \
+   if (cond)  \
+   FEAT_ERR(fn, ln, __VA_ARGS__); \
+   rte_errno = _err;  \
+   } while (0)
+
+
+static struct rte_mbuf_dynfield rte_graph_feature_arc_mbuf_de

[PATCH v8 0/5] add feature arc in rte_graph

2025-04-19 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to express relationship
between rte_graph nodes, as feature nodes, and allow packets steering
across these nodes in a simplified manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to outbound-IPsec-policy node. On the other hand,
packets routed to another interface (say eth1) must ot be sent to
IPsec node, as IPsec feature is disabled on eth1. Feature-arc allows
rte_graph applications to manage such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Runtime enable/disable features on an index (like interface), so that
if a feature is enabled at runtime, only then packets associated with
that interface will steer to corresponding feature node. Control plane
enables/disables features. Features enabled on one interface may not be
enabled on another interface with in a same feature arc.

2. Single feature arc represents a hook point at a network layer in an
given direction. Multiple feature arcs can be added in desginated nodes
to add various hook points in stack. Likt IPv4-output, IPv4-input,
IPv6-output, IPv6-input, ethernet-input, mpls-input, mpls-output etc.

3. Hook custom/out-of-tree nodes to DPDK in-built nodes and allow packet
steering from in-built node to custom node without requiring changes to
in-built fast path function

4. Express features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Changes in v8:
- Fix CI iol testing

Changes in v7:
- Rebased to latest main for DPDK-25.07
- Added override_index_cb() in RTE_GRAPH_FEATURE_REGISTER() to allow
  features to take control of "max_index" (passed to
  rte_graph_feature_arc_create())
- Streamlined fast path APIs to make them more simpler
- Added quad_loop in "if_tx_feature" node
- Added functional test cases and fixed bugs
- Based on feedback from Redhat on v6 patches, it was tried to move
  feature arc APIs inside rte_graph library (to reduce node burden) but
  it was adding 4% performance regression for L3fwd usecase. For
  interested users: https://github.com/nsaxena16/dpdk/pull/2

Changes in v6:
- Rebased to latest main for DPDK-25.03
- Added constructor based feature arc/feature registration
- Changed design to handle fast path synchronization via RCU mechanism
  when any feature is enabled or disabled
- Added feature arc specific mbuf dynamic field to carry feature data
  across nodes
- Added feature arc example in app/graph
- Programming guide and functional test cases in future versions

Nitin Saxena (5):
  graph: add API to override node process function
  graph: add feature arc abstraction
  ip4: add ip4 output feature arc
  app/graph: add custom feature nodes for ip4 output arc
  test/graph_feature_arc: add functional tests

 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  |  174 ++
 app/graph/main.c |   15 +-
 app/graph/meson.build|2 +
 app/graph/module_api.h   |2 +
 app/test/meson.build |1 +
 app/test/test_graph_feature_arc.c| 1374 +++
 doc/api/doxy-api-index.md|2 +
 doc/guides/rel_notes/release_25_07.rst   |   10 +
 lib/graph/graph_feature_arc.c| 2050 ++
 lib/graph/graph_private.h|   15 +
 lib/graph/meson.build|4 +-
 lib/graph/node.c |   23 +
 lib/graph/rte_graph_feature_arc.h|  628 +++
 lib/graph/rte_graph_feature_arc_worker.h |  607 +++
 lib/node/ethdev_ctrl.c   |8 +
 lib/node/interface_tx_feature.c  |  213 +++
 lib/node/interface_tx_feature_priv.h |   33 +
 lib/node/ip4_rewrite.c   |  286 ++-
 lib/node/meson.build |1 +
 lib/node/node_private.h  |1 +
 lib/node/rte_node_ip4_api.h  |4 +
 25 files changed, 5608 insertions(+), 9 deletions(-)
 create mode 100644 app/graph/feature.c
 create mode 100644 app/graph/feature.h
 create mode 100644 app/gr

[PATCH v8 3/5] ip4: add ip4 output feature arc

2025-04-19 Thread Nitin Saxena
- Added ip4 output arc to allow applications to hook feature nodes in ip4
egress direction
- Added interface_tx node as end feature to ip4 output arc

Signed-off-by: Nitin Saxena 
---
 lib/node/ethdev_ctrl.c   |   8 +
 lib/node/interface_tx_feature.c  | 213 
 lib/node/interface_tx_feature_priv.h |  33 
 lib/node/ip4_rewrite.c   | 286 ++-
 lib/node/meson.build |   1 +
 lib/node/node_private.h  |   1 +
 lib/node/rte_node_ip4_api.h  |   4 +
 7 files changed, 539 insertions(+), 7 deletions(-)
 create mode 100644 lib/node/interface_tx_feature.c
 create mode 100644 lib/node/interface_tx_feature_priv.h

diff --git a/lib/node/ethdev_ctrl.c b/lib/node/ethdev_ctrl.c
index dca7e817f9..f717903731 100644
--- a/lib/node/ethdev_ctrl.c
+++ b/lib/node/ethdev_ctrl.c
@@ -15,6 +15,7 @@
 #include "ethdev_tx_priv.h"
 #include "ip4_rewrite_priv.h"
 #include "ip6_rewrite_priv.h"
+#include "interface_tx_feature_priv.h"
 #include "node_private.h"

 static struct ethdev_ctrl {
@@ -26,6 +27,7 @@ int
 rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
uint16_t nb_graphs)
 {
+   struct rte_node_register *if_tx_feature_node;
struct rte_node_register *ip4_rewrite_node;
struct rte_node_register *ip6_rewrite_node;
struct ethdev_tx_node_main *tx_node_data;
@@ -37,6 +39,7 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, 
uint16_t nb_confs,
int i, j, rc;
uint32_t id;

+   if_tx_feature_node = if_tx_feature_node_get();
ip4_rewrite_node = ip4_rewrite_node_get();
ip6_rewrite_node = ip6_rewrite_node_get();
tx_node_data = ethdev_tx_node_data_get();
@@ -127,6 +130,11 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, 
uint16_t nb_confs,
if (rc < 0)
return rc;

+   /* Add this tx port node to if_tx_feature_node */
+   rte_node_edge_update(if_tx_feature_node->id, 
RTE_EDGE_ID_INVALID,
+&next_nodes, 1);
+   rc = if_tx_feature_node_set_next(port_id,
+
rte_node_edge_count(if_tx_feature_node->id) - 1);
}

ctrl.nb_graphs = nb_graphs;
diff --git a/lib/node/interface_tx_feature.c b/lib/node/interface_tx_feature.c
new file mode 100644
index 00..c8809d5f91
--- /dev/null
+++ b/lib/node/interface_tx_feature.c
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include "rte_node_ip4_api.h"
+#include "node_private.h"
+#include "interface_tx_feature_priv.h"
+
+#define IF_TX_FEATURE_LAST_NEXT_INDEX(ctx) \
+   (((struct if_tx_feature_node_ctx *)ctx)->last_index)
+/*
+ * @internal array for mapping port to next node index
+ */
+struct if_tx_feature_node_main  {
+   uint16_t next_index[RTE_MAX_ETHPORTS];
+};
+
+struct if_tx_feature_node_ctx {
+   uint16_t last_index;
+};
+
+static struct if_tx_feature_node_main *if_tx_feature_nm;
+
+int
+if_tx_feature_node_set_next(uint16_t port_id, uint16_t next_index)
+{
+   if (if_tx_feature_nm == NULL) {
+   if_tx_feature_nm = rte_zmalloc(
+   "if_tx_feature_nm", sizeof(struct 
if_tx_feature_node_main),
+   RTE_CACHE_LINE_SIZE);
+   if (if_tx_feature_nm == NULL)
+   return -ENOMEM;
+   }
+   if_tx_feature_nm->next_index[port_id] = next_index;
+
+   return 0;
+}
+
+static int
+if_tx_feature_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+   RTE_SET_USED(graph);
+
+   /* pkt_drop */
+   IF_TX_FEATURE_LAST_NEXT_INDEX(node->ctx) = 0;
+
+   return 0;
+}
+
+static uint16_t
+if_tx_feature_node_process(struct rte_graph *graph, struct rte_node *node,
+  void **objs, uint16_t nb_objs)
+{
+   uint16_t held = 0, next0 = 0, next1 = 0, next2 = 0, next3 = 0;
+   struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+   uint16_t last_spec = 0, fix_spec = 0;
+   void **to_next, **from;
+   rte_edge_t next_index;
+   uint16_t n_left_from;
+
+   /* Speculative next */
+   next_index = IF_TX_FEATURE_LAST_NEXT_INDEX(node->ctx);
+
+   from = objs;
+   n_left_from = nb_objs;
+   pkts = (struct rte_mbuf **)objs;
+
+   to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+   while (n_left_from > 4) {
+   if (likely(n_left_from > 7)) {
+   /* Prefetch next mbuf */
+   rte_prefetch0(objs[4]);
+   rte_prefetch0(objs[5]);
+   rte_prefetch0(objs[6]);

[PATCH v8 5/5] test/graph_feature_arc: add functional tests

2025-04-19 Thread Nitin Saxena
Added functional unit test case for verifying feature arc control plane
and fast path APIs

How to run:
$ echo "graph_feature_arc_autotest" | ./bin/dpdk-test

Signed-off-by: Nitin Saxena 
---
 app/test/meson.build  |1 +
 app/test/test_graph_feature_arc.c | 1374 +
 2 files changed, 1375 insertions(+)
 create mode 100644 app/test/test_graph_feature_arc.c

diff --git a/app/test/meson.build b/app/test/meson.build
index b6285a6b45..acb0d36c6b 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -90,6 +90,7 @@ source_file_deps = {
 'test_func_reentrancy.c': ['hash', 'lpm'],
 'test_graph.c': ['graph'],
 'test_graph_perf.c': ['graph'],
+'test_graph_feature_arc.c': ['graph'],
 'test_hash.c': ['net', 'hash'],
 'test_hash_functions.c': ['hash'],
 'test_hash_multiwriter.c': ['hash'],
diff --git a/app/test/test_graph_feature_arc.c 
b/app/test/test_graph_feature_arc.c
new file mode 100644
index 00..b46ab4457e
--- /dev/null
+++ b/app/test/test_graph_feature_arc.c
@@ -0,0 +1,1374 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "test.h"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#ifndef RTE_EXEC_ENV_WINDOWS
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define MBUF_NUM 256
+#define TEST_ARC1_NAME "arc1"
+#define TEST_ARC2_NAME "arc2"
+#define TEST_FAILED_ARC "failed_arc"
+#define MAX_INDEXES 10
+#define OVERRIDE_MAX_INDEX MBUF_NUM
+#define MAX_FEATURES 5
+
+#define dbg(...)
+
+#define SOURCE1 "arc_source1_node"
+#define INPUT_STATIC "arc_input_static_node"
+#define OUTPUT_STATIC "arc_output_static_node"
+#define PKT_FREE_STATIC "arc_pkt_free_static_node"
+#define ARC1_FEATURE1 "arc1_feature1_node"
+#define ARC1_FEATURE2 "arc1_feature2_node"
+#define ARC2_FEATURE1 "arc2_feature1_node"
+#define ARC2_FEATURE2 "arc2_feature2_node"
+#define ARC2_FEATURE3 "arc2_feature3_node"
+#define DUMMY1_STATIC "arc_dummy1_static_node"
+#define DUMMY2_STATIC "arc_dummy2_static_node"
+
+#define feature_cast(x) ((rte_graph_feature_t)(x))
+
+/* (Node index, Node Name, feature app_cookie base */
+#define FOREACH_TEST_NODE_ARC {\
+   R(0, SOURCE1, 64)   \
+   R(1, INPUT_STATIC, 128) \
+   R(2, OUTPUT_STATIC, 256)\
+   R(3, PKT_FREE_STATIC, 512)  \
+   R(4, ARC1_FEATURE1, 1024)   \
+   R(5, ARC1_FEATURE2, 2048)   \
+   R(6, ARC2_FEATURE1, 4096)   \
+   R(7, ARC2_FEATURE2, 8192)   \
+   R(8, ARC2_FEATURE3, 16384)  \
+   R(9, DUMMY1_STATIC, 32768)  \
+   R(10, DUMMY2_STATIC, 65536) \
+   }
+
+/**
+ * ARC1: Feature arc on ingress interface
+ * ARC2: Feature arc on egress interface
+ * XX_static: Static nodes
+ * XX_featureX: Feature X on arc
+ *
+ *-> ARC1_FEATURE1
+ *   || |
+ *   || v
+ *   ||   ARC1_FEATURE2
+ *   || |
+ *   |v v
+ *  SOURCE1 ->-> INPUT_STATIC --> OUTPUT_STATIC -> PKT_FREE_STATIC
+ * |   |  | ^  ^ ^
+ * |   |  | |  | |
+ * |   |   --> ARC2_FEATURE1   | |
+ * |   |  ^ ^  | |
+ * |   |  | |  | |
+ * |--c-> ARC2_FEATURE2  |
+ * |  | ^|
+ * |  | ||
+ *  --> ARC2_FEATURE3 ---
+ */
+const char *node_names_feature_arc[] = {
+   SOURCE1, INPUT_STATIC, OUTPUT_STATIC, PKT_FREE_STATIC,
+   ARC1_FEATURE1, ARC1_FEATURE2, ARC2_FEATURE1, ARC2_FEATURE2, 
ARC2_FEATURE3,
+   DUMMY1_STATIC, DUMMY2_STATIC
+};
+
+const char *arc_names[] = {
+   TEST_ARC1_NAME, TEST_ARC2_NAME,
+};
+
+const char *arc1_feature_seq[] = {
+   ARC1_FEATURE1, ARC1_FEATURE2, INPUT_STATIC,
+};
+
+const char *arc2_feature_seq[] = {
+   ARC2_FEATURE1, ARC2_FEATURE2, ARC2_FEATURE3, PKT_FREE_STATIC,
+};
+
+#define MAX_NODES  RTE_DIM(node_names_feature_arc)
+
+typedef enum {
+   START_NODE,
+   INTERMEDIATE_FEATURE,
+   END_FEATURE,
+} fp_n

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

2025-04-19 Thread Nitin Saxena
- 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   
graph> feature disable   
graph> graph stats show

Signed-off-by: Nitin Saxena 
---
 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 | 174 
 app/graph/main.c|  15 +++-
 app/graph/meson.build   |   2 +
 app/graph/module_api.h  |   2 +
 8 files changed, 356 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 ip mac  # Add static 
neighbour for IPv4
 neigh add ipv6 ip mac  # Add static 
neighbour for IPv6
 help neigh   # Print help on neigh 
commands
+
+feature arcs # show all feature 
arcs
+feature name show# Show feature arc 
details
+feature enable arc_name feature_name interface   # 
Enable feature on interface
+feature disable arc_name feature_name 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 00..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 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#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  show   # Display features within 
an arc";
+
+static const char
+cmd_feature_enable_help[] = "feature enable   
";
+
+static const char
+cmd_feature_disable_help[] = "feature disable   
";
+
+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 *

[PATCH v9 2/5] graph: add feature arc abstraction

2025-04-21 Thread Nitin Saxena
Feature arc abstraction allows rte_graph based applications to
- Allow control plane to runtime enable/disable feature nodes.
  Fast path APIs helps to steer packets across enabled feature nodes
- Feature enable/disable based on indexes. Index can be interface index,
  route index, etc
- More than one feature nodes can be added to an arc and also provide
  mechanism to control features sequencing order in fast path.
- Does not require stopping of workers for control plane updates. RCU
  mechanism also provided
- Once DPDK inbuilt nodes adopts feature arc abstraction, out-of-tree
  nodes can also be hooked (with no custom changes in DPDK in-built
  nodes)

Signed-off-by: Nitin Saxena 
---
 doc/api/doxy-api-index.md|2 +
 doc/guides/rel_notes/release_25_07.rst   |   10 +
 lib/graph/graph_feature_arc.c| 2050 ++
 lib/graph/graph_private.h|4 +
 lib/graph/meson.build|4 +-
 lib/graph/rte_graph_feature_arc.h|  634 +++
 lib/graph/rte_graph_feature_arc_worker.h |  607 +++
 7 files changed, 3310 insertions(+), 1 deletion(-)
 create mode 100644 lib/graph/graph_feature_arc.c
 create mode 100644 lib/graph/rte_graph_feature_arc.h
 create mode 100644 lib/graph/rte_graph_feature_arc_worker.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 5c425a2cb9..6d8b531344 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -214,6 +214,8 @@ The public API headers are grouped by topics:
 [table_wm](@ref rte_swx_table_wm.h)
   * [graph](@ref rte_graph.h):
 [graph_worker](@ref rte_graph_worker.h)
+[graph_feature_arc](@ref rte_graph_feature_arc.h)
+[graph_feature_arc_worker](@ref rte_graph_feature_arc_worker.h)
   * graph_nodes:
 [eth_node](@ref rte_node_eth_api.h),
 [ip4_node](@ref rte_node_ip4_api.h),
diff --git a/doc/guides/rel_notes/release_25_07.rst 
b/doc/guides/rel_notes/release_25_07.rst
index 093b85d206..7f11c91b7a 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,16 @@ New Features
  Also, make sure to start the actual text at the margin.
  ===

+* **Added feature arc abstraction in graph library.**
+
+  Feature arc abstraction helps ``rte_graph`` based applications to steer
+  packets across different node path(s) based on the features (or protocols)
+  enabled on interfaces. Different feature node paths can be enabled/disabled
+  at runtime on some or on all interfaces. This abstraction also help
+  applications to hook ``out-of-tree nodes`` in DPDK in-built node paths in a
+  generic manner.
+
+  * Added ``ip4_output`` feature arc processing in ``ip4_rewrite`` node.

 Removed Items
 -
diff --git a/lib/graph/graph_feature_arc.c b/lib/graph/graph_feature_arc.c
new file mode 100644
index 00..1c94246f4a
--- /dev/null
+++ b/lib/graph/graph_feature_arc.c
@@ -0,0 +1,2050 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#include "graph_private.h"
+#include 
+#include 
+#include 
+#include 
+
+#define GRAPH_FEATURE_MAX_NUM_PER_ARC  (64)
+
+#define connect_graph_nodes(node1, node2, edge, arc_name) \
+   __connect_graph_nodes(node1, node2, edge, arc_name, __LINE__)
+
+#define FEATURE_ARC_MEMZONE_NAME "__rte_feature_arc_main_mz"
+
+#define NUM_EXTRA_FEATURE_DATA   (2)
+
+#define graph_uint_cast(f) ((unsigned int)f)
+
+#define fdata_fix_get(arc, feat, index)\
+   RTE_GRAPH_FEATURE_TO_FEATURE_DATA(arc, feat, index)
+
+#define feat_dbg graph_dbg
+
+#define FEAT_COND_ERR(cond, ...)   \
+   do {   \
+   if (cond)  \
+   graph_err(__VA_ARGS__);\
+   } while (0)
+
+#define FEAT_ERR(fn, ln, ...)  \
+   GRAPH_LOG2(ERR, fn, ln, __VA_ARGS__)
+
+#define FEAT_ERR_JMP(_err, fn, ln, ...)\
+   do {   \
+   FEAT_ERR(fn, ln, __VA_ARGS__); \
+   rte_errno = _err;  \
+   } while (0)
+
+#define COND_ERR_JMP(_err, cond, fn, ln, ...)  \
+   do {   \
+   if (cond)  \
+   FEAT_ERR(fn, ln, __VA_ARGS__); \
+   rte_errno = _err;  \
+   } while (0)
+
+
+static struct rte_mbuf_dynfield rte_graph_feature_arc_mbuf_de

[PATCH v9 3/5] ip4: add ip4 output feature arc

2025-04-21 Thread Nitin Saxena
- Added ip4 output arc to allow applications to hook feature nodes in ip4
egress direction
- Added interface_tx node as end feature to ip4 output arc

Signed-off-by: Nitin Saxena 
---
 lib/node/ethdev_ctrl.c   |   8 +
 lib/node/interface_tx_feature.c  | 213 
 lib/node/interface_tx_feature_priv.h |  33 
 lib/node/ip4_rewrite.c   | 286 ++-
 lib/node/meson.build |   1 +
 lib/node/node_private.h  |   1 +
 lib/node/rte_node_ip4_api.h  |   4 +
 7 files changed, 539 insertions(+), 7 deletions(-)
 create mode 100644 lib/node/interface_tx_feature.c
 create mode 100644 lib/node/interface_tx_feature_priv.h

diff --git a/lib/node/ethdev_ctrl.c b/lib/node/ethdev_ctrl.c
index dca7e817f9..f717903731 100644
--- a/lib/node/ethdev_ctrl.c
+++ b/lib/node/ethdev_ctrl.c
@@ -15,6 +15,7 @@
 #include "ethdev_tx_priv.h"
 #include "ip4_rewrite_priv.h"
 #include "ip6_rewrite_priv.h"
+#include "interface_tx_feature_priv.h"
 #include "node_private.h"

 static struct ethdev_ctrl {
@@ -26,6 +27,7 @@ int
 rte_node_eth_config(struct rte_node_ethdev_config *conf, uint16_t nb_confs,
uint16_t nb_graphs)
 {
+   struct rte_node_register *if_tx_feature_node;
struct rte_node_register *ip4_rewrite_node;
struct rte_node_register *ip6_rewrite_node;
struct ethdev_tx_node_main *tx_node_data;
@@ -37,6 +39,7 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, 
uint16_t nb_confs,
int i, j, rc;
uint32_t id;

+   if_tx_feature_node = if_tx_feature_node_get();
ip4_rewrite_node = ip4_rewrite_node_get();
ip6_rewrite_node = ip6_rewrite_node_get();
tx_node_data = ethdev_tx_node_data_get();
@@ -127,6 +130,11 @@ rte_node_eth_config(struct rte_node_ethdev_config *conf, 
uint16_t nb_confs,
if (rc < 0)
return rc;

+   /* Add this tx port node to if_tx_feature_node */
+   rte_node_edge_update(if_tx_feature_node->id, 
RTE_EDGE_ID_INVALID,
+&next_nodes, 1);
+   rc = if_tx_feature_node_set_next(port_id,
+
rte_node_edge_count(if_tx_feature_node->id) - 1);
}

ctrl.nb_graphs = nb_graphs;
diff --git a/lib/node/interface_tx_feature.c b/lib/node/interface_tx_feature.c
new file mode 100644
index 00..c8809d5f91
--- /dev/null
+++ b/lib/node/interface_tx_feature.c
@@ -0,0 +1,213 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+
+#include "rte_node_ip4_api.h"
+#include "node_private.h"
+#include "interface_tx_feature_priv.h"
+
+#define IF_TX_FEATURE_LAST_NEXT_INDEX(ctx) \
+   (((struct if_tx_feature_node_ctx *)ctx)->last_index)
+/*
+ * @internal array for mapping port to next node index
+ */
+struct if_tx_feature_node_main  {
+   uint16_t next_index[RTE_MAX_ETHPORTS];
+};
+
+struct if_tx_feature_node_ctx {
+   uint16_t last_index;
+};
+
+static struct if_tx_feature_node_main *if_tx_feature_nm;
+
+int
+if_tx_feature_node_set_next(uint16_t port_id, uint16_t next_index)
+{
+   if (if_tx_feature_nm == NULL) {
+   if_tx_feature_nm = rte_zmalloc(
+   "if_tx_feature_nm", sizeof(struct 
if_tx_feature_node_main),
+   RTE_CACHE_LINE_SIZE);
+   if (if_tx_feature_nm == NULL)
+   return -ENOMEM;
+   }
+   if_tx_feature_nm->next_index[port_id] = next_index;
+
+   return 0;
+}
+
+static int
+if_tx_feature_node_init(const struct rte_graph *graph, struct rte_node *node)
+{
+   RTE_SET_USED(graph);
+
+   /* pkt_drop */
+   IF_TX_FEATURE_LAST_NEXT_INDEX(node->ctx) = 0;
+
+   return 0;
+}
+
+static uint16_t
+if_tx_feature_node_process(struct rte_graph *graph, struct rte_node *node,
+  void **objs, uint16_t nb_objs)
+{
+   uint16_t held = 0, next0 = 0, next1 = 0, next2 = 0, next3 = 0;
+   struct rte_mbuf *mbuf0, *mbuf1, *mbuf2, *mbuf3, **pkts;
+   uint16_t last_spec = 0, fix_spec = 0;
+   void **to_next, **from;
+   rte_edge_t next_index;
+   uint16_t n_left_from;
+
+   /* Speculative next */
+   next_index = IF_TX_FEATURE_LAST_NEXT_INDEX(node->ctx);
+
+   from = objs;
+   n_left_from = nb_objs;
+   pkts = (struct rte_mbuf **)objs;
+
+   to_next = rte_node_next_stream_get(graph, node, next_index, nb_objs);
+   while (n_left_from > 4) {
+   if (likely(n_left_from > 7)) {
+   /* Prefetch next mbuf */
+   rte_prefetch0(objs[4]);
+   rte_prefetch0(objs[5]);
+   rte_prefetch0(objs[6]);

[PATCH v9 5/5] test/graph_feature_arc: add functional tests

2025-04-21 Thread Nitin Saxena
Added functional unit test case for verifying feature arc control plane
and fast path APIs

How to run:
$ echo "graph_feature_arc_autotest" | ./bin/dpdk-test

Signed-off-by: Nitin Saxena 
---
 app/test/meson.build  |1 +
 app/test/test_graph_feature_arc.c | 1374 +
 2 files changed, 1375 insertions(+)
 create mode 100644 app/test/test_graph_feature_arc.c

diff --git a/app/test/meson.build b/app/test/meson.build
index b6285a6b45..acb0d36c6b 100644
--- a/app/test/meson.build
+++ b/app/test/meson.build
@@ -90,6 +90,7 @@ source_file_deps = {
 'test_func_reentrancy.c': ['hash', 'lpm'],
 'test_graph.c': ['graph'],
 'test_graph_perf.c': ['graph'],
+'test_graph_feature_arc.c': ['graph'],
 'test_hash.c': ['net', 'hash'],
 'test_hash_functions.c': ['hash'],
 'test_hash_multiwriter.c': ['hash'],
diff --git a/app/test/test_graph_feature_arc.c 
b/app/test/test_graph_feature_arc.c
new file mode 100644
index 00..b46ab4457e
--- /dev/null
+++ b/app/test/test_graph_feature_arc.c
@@ -0,0 +1,1374 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2024 Marvell International Ltd.
+ */
+
+#include "test.h"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#ifndef RTE_EXEC_ENV_WINDOWS
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define MBUF_NUM 256
+#define TEST_ARC1_NAME "arc1"
+#define TEST_ARC2_NAME "arc2"
+#define TEST_FAILED_ARC "failed_arc"
+#define MAX_INDEXES 10
+#define OVERRIDE_MAX_INDEX MBUF_NUM
+#define MAX_FEATURES 5
+
+#define dbg(...)
+
+#define SOURCE1 "arc_source1_node"
+#define INPUT_STATIC "arc_input_static_node"
+#define OUTPUT_STATIC "arc_output_static_node"
+#define PKT_FREE_STATIC "arc_pkt_free_static_node"
+#define ARC1_FEATURE1 "arc1_feature1_node"
+#define ARC1_FEATURE2 "arc1_feature2_node"
+#define ARC2_FEATURE1 "arc2_feature1_node"
+#define ARC2_FEATURE2 "arc2_feature2_node"
+#define ARC2_FEATURE3 "arc2_feature3_node"
+#define DUMMY1_STATIC "arc_dummy1_static_node"
+#define DUMMY2_STATIC "arc_dummy2_static_node"
+
+#define feature_cast(x) ((rte_graph_feature_t)(x))
+
+/* (Node index, Node Name, feature app_cookie base */
+#define FOREACH_TEST_NODE_ARC {\
+   R(0, SOURCE1, 64)   \
+   R(1, INPUT_STATIC, 128) \
+   R(2, OUTPUT_STATIC, 256)\
+   R(3, PKT_FREE_STATIC, 512)  \
+   R(4, ARC1_FEATURE1, 1024)   \
+   R(5, ARC1_FEATURE2, 2048)   \
+   R(6, ARC2_FEATURE1, 4096)   \
+   R(7, ARC2_FEATURE2, 8192)   \
+   R(8, ARC2_FEATURE3, 16384)  \
+   R(9, DUMMY1_STATIC, 32768)  \
+   R(10, DUMMY2_STATIC, 65536) \
+   }
+
+/**
+ * ARC1: Feature arc on ingress interface
+ * ARC2: Feature arc on egress interface
+ * XX_static: Static nodes
+ * XX_featureX: Feature X on arc
+ *
+ *-> ARC1_FEATURE1
+ *   || |
+ *   || v
+ *   ||   ARC1_FEATURE2
+ *   || |
+ *   |v v
+ *  SOURCE1 ->-> INPUT_STATIC --> OUTPUT_STATIC -> PKT_FREE_STATIC
+ * |   |  | ^  ^ ^
+ * |   |  | |  | |
+ * |   |   --> ARC2_FEATURE1   | |
+ * |   |  ^ ^  | |
+ * |   |  | |  | |
+ * |--c-> ARC2_FEATURE2  |
+ * |  | ^|
+ * |  | ||
+ *  --> ARC2_FEATURE3 ---
+ */
+const char *node_names_feature_arc[] = {
+   SOURCE1, INPUT_STATIC, OUTPUT_STATIC, PKT_FREE_STATIC,
+   ARC1_FEATURE1, ARC1_FEATURE2, ARC2_FEATURE1, ARC2_FEATURE2, 
ARC2_FEATURE3,
+   DUMMY1_STATIC, DUMMY2_STATIC
+};
+
+const char *arc_names[] = {
+   TEST_ARC1_NAME, TEST_ARC2_NAME,
+};
+
+const char *arc1_feature_seq[] = {
+   ARC1_FEATURE1, ARC1_FEATURE2, INPUT_STATIC,
+};
+
+const char *arc2_feature_seq[] = {
+   ARC2_FEATURE1, ARC2_FEATURE2, ARC2_FEATURE3, PKT_FREE_STATIC,
+};
+
+#define MAX_NODES  RTE_DIM(node_names_feature_arc)
+
+typedef enum {
+   START_NODE,
+   INTERMEDIATE_FEATURE,
+   END_FEATURE,
+} fp_n

[PATCH v9 0/5] add feature arc in rte_graph

2025-04-21 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to express relationship
between rte_graph nodes, as feature nodes, and allow packets steering
across these nodes in a simplified manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to outbound-IPsec-policy node. On the other hand,
packets routed to another interface (say eth1) must ot be sent to
IPsec node, as IPsec feature is disabled on eth1. Feature-arc allows
rte_graph applications to manage such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Runtime enable/disable features on an index (like interface), so that
if a feature is enabled at runtime, only then packets associated with
that interface will steer to corresponding feature node. Control plane
enables/disables features. Features enabled on one interface may not be
enabled on another interface with in a same feature arc.

2. Single feature arc represents a hook point at a network layer in an
given direction. Multiple feature arcs can be added in desginated nodes
to add various hook points in stack. Likt IPv4-output, IPv4-input,
IPv6-output, IPv6-input, ethernet-input, mpls-input, mpls-output etc.

3. Hook custom/out-of-tree nodes to DPDK in-built nodes and allow packet
steering from in-built node to custom node without requiring changes to
in-built fast path function

4. Express features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Changes in v9:
- Fix build failures

Changes in v8:
- Fix CI iol testing

Changes in v7:
- Rebased to latest main for DPDK-25.07
- Added override_index_cb() in RTE_GRAPH_FEATURE_REGISTER() to allow
  features to take control of "max_index" (passed to
  rte_graph_feature_arc_create())
- Streamlined fast path APIs to make them more simpler
- Added quad_loop in "if_tx_feature" node
- Added functional test cases and fixed bugs
- Based on feedback from Redhat on v6 patches, it was tried to move
  feature arc APIs inside rte_graph library (to reduce node burden) but
  it was adding 4% performance regression for L3fwd usecase. For
  interested users: https://github.com/nsaxena16/dpdk/pull/2

Changes in v6:
- Rebased to latest main for DPDK-25.03
- Added constructor based feature arc/feature registration
- Changed design to handle fast path synchronization via RCU mechanism
  when any feature is enabled or disabled
- Added feature arc specific mbuf dynamic field to carry feature data
  across nodes
- Added feature arc example in app/graph
- Programming guide and functional test cases in future versions


Nitin Saxena (5):
  graph: add API to override node process function
  graph: add feature arc abstraction
  ip4: add ip4 output feature arc
  app/graph: add custom feature nodes for ip4 output arc
  test/graph_feature_arc: add functional tests

 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  |  180 ++
 app/graph/main.c |   15 +-
 app/graph/meson.build|2 +
 app/graph/module_api.h   |2 +
 app/test/meson.build |1 +
 app/test/test_graph_feature_arc.c| 1374 +++
 doc/api/doxy-api-index.md|2 +
 doc/guides/rel_notes/release_25_07.rst   |   10 +
 lib/graph/graph_feature_arc.c| 2050 ++
 lib/graph/graph_private.h|   15 +
 lib/graph/meson.build|4 +-
 lib/graph/node.c |   23 +
 lib/graph/rte_graph_feature_arc.h|  634 +++
 lib/graph/rte_graph_feature_arc_worker.h |  607 +++
 lib/node/ethdev_ctrl.c   |8 +
 lib/node/interface_tx_feature.c  |  213 +++
 lib/node/interface_tx_feature_priv.h |   33 +
 lib/node/ip4_rewrite.c   |  286 ++-
 lib/node/meson.build |1 +
 lib/node/node_private.h  |1 +
 lib/node/rte_node_ip4_api.h  |4 +
 25 files changed, 5620 insertions(+), 9 deletions(-)
 create mode 100644 app/graph/feature.c
 create mode 100644 app/graph/feature.h
 create mo

[PATCH v9 1/5] graph: add API to override node process function

2025-04-21 Thread Nitin Saxena
New API used by feature arc library to override node's original
process() func.

Signed-off-by: Nitin Saxena 
---
 lib/graph/graph_private.h | 11 +++
 lib/graph/node.c  | 23 +++
 2 files changed, 34 insertions(+)

diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
index 813dd78b9d..579546e658 100644
--- a/lib/graph/graph_private.h
+++ b/lib/graph/graph_private.h
@@ -198,6 +198,17 @@ struct node_head *node_list_head_get(void);
  */
 struct node *node_from_name(const char *name);

+/**
+ * @internal
+ *
+ * Override process func of a node.
+ *
+ * @return
+ *   - 0: Success.
+ *   - <0: Error
+ */
+int node_override_process_func(rte_node_t id, rte_node_process_t process);
+
 /* Graph list functions */
 STAILQ_HEAD(graph_head, graph);

diff --git a/lib/graph/node.c b/lib/graph/node.c
index 101981ec24..c8a1cd5586 100644
--- a/lib/graph/node.c
+++ b/lib/graph/node.c
@@ -431,3 +431,26 @@ rte_node_max_count(void)
 {
return node_id;
 }
+
+int
+node_override_process_func(rte_node_t id, rte_node_process_t process)
+{
+   struct node *node;
+
+   NODE_ID_CHECK(id);
+   graph_spinlock_lock();
+
+   STAILQ_FOREACH(node, &node_list, next) {
+   if (node->id == id) {
+   node->process = process;
+   graph_spinlock_unlock();
+   return 0;
+   }
+   }
+
+   graph_spinlock_unlock();
+
+   return 0;
+fail:
+   return -1;
+}
--
2.43.0



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

2025-04-21 Thread Nitin Saxena
- 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   
graph> feature disable   
graph> graph stats show

Signed-off-by: Nitin Saxena 
---
 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 | 180 
 app/graph/main.c|  15 ++-
 app/graph/meson.build   |   2 +
 app/graph/module_api.h  |   2 +
 8 files changed, 362 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 ip mac  # Add static 
neighbour for IPv4
 neigh add ipv6 ip mac  # Add static 
neighbour for IPv6
 help neigh   # Print help on neigh 
commands
+
+feature arcs # show all feature 
arcs
+feature name show# Show feature arc 
details
+feature enable arc_name feature_name interface   # 
Enable feature on interface
+feature disable arc_name feature_name 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 00..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 
+#include 
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#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  show   # Display features within 
an arc";
+
+static const char
+cmd_feature_enable_help[] = "feature enable   
";
+
+static const char
+cmd_feature_disable_help[] = "feature disable   
";
+
+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 *

[PATCH v3 2/2] node: use node mbuf dynfield in ip4 nodes

2025-04-04 Thread Nitin Saxena
- Used global node mbuf in ip[4|6]_lookup/rewrite nodes
- Redefine node_mbuf_priv1() to rte_node_mbuf_overload_fields_get()

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_lookup.c | 14 ---
 lib/node/ip4_rewrite.c| 15 +---
 lib/node/ip6_lookup.c | 15 +---
 lib/node/ip6_rewrite.c| 14 ---
 lib/node/node_private.h   | 40 +++
 lib/node/rte_node_mbuf_dynfield.h |  9 +++
 6 files changed, 34 insertions(+), 73 deletions(-)

diff --git a/lib/node/ip4_lookup.c b/lib/node/ip4_lookup.c
index 0b474cd2bc..7e9d21a9c9 100644
--- a/lib/node/ip4_lookup.c
+++ b/lib/node/ip4_lookup.c
@@ -31,8 +31,6 @@ struct ip4_lookup_node_ctx {
int mbuf_priv1_off;
 };

-int node_mbuf_priv1_dynfield_offset = -1;
-
 static struct ip4_lookup_node_main ip4_lookup_nm;

 #define IP4_LOOKUP_NODE_LPM(ctx) \
@@ -180,17 +178,15 @@ ip4_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_lookup_node_ctx) > RTE_NODE_CTX_SZ);

+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -208,7 +204,7 @@ ip4_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP4_LOOKUP_NODE_LPM(node->ctx) = ip4_lookup_nm.lpm_tbl[graph->socket];
-   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

 #if defined(__ARM_NEON) || defined(RTE_ARCH_X86)
if (rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128)
diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index 34a920df5e..b05decc1a3 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -258,19 +258,16 @@ ip4_rewrite_node_process(struct rte_graph *graph, struct 
rte_node *node,
 static int
 ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
-   static bool init_once;
+   int dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_rewrite_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-   init_once = true;
-   }
-   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
+
+   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip4_rewrite", "Initialized ip4_rewrite node initialized");

diff --git a/lib/node/ip6_lookup.c b/lib/node/ip6_lookup.c
index f378d2d064..c140aa9cf3 100644
--- a/lib/node/ip6_lookup.c
+++ b/lib/node/ip6_lookup.c
@@ -316,18 +316,16 @@ ip6_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip6_lookup_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset =
-   rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;

+   if (!init_once) {
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -345,8 +343,7 @@ ip6_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP6_LOOKUP_NODE_LPM(node->ctx) = ip6_lookup_nm.lpm_tbl[graph->socket];
-   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) =
-   node_mbuf_priv1_dynfield_offset;
+   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip6_lookup", "Initialized ip6_lookup node");

diff --git a/lib/n

[PATCH v3 1/2] node: add global node mbuf dynfield

2025-04-04 Thread Nitin Saxena
This patch defines rte_node specific dynamic field structure
(rte_node_mbuf_dynfield_t)

rte_node_mbuf_dynfield_t structure holds two types of fields
- Persistent data fields which are preserved across graph walk.
  Currently size of persistent data fields is zero.
- Overloadable data fields which are used by any two adjacent nodes.
  Same fields can be repurposed by any other adjacent nodes

This dynfield can be also be used by out-of-tree nodes.

Signed-off-by: Nitin Saxena 
---
 doc/api/doxy-api-index.md  |   3 +-
 doc/guides/rel_notes/release_25_07.rst |   6 ++
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  44 +
 lib/node/rte_node_mbuf_dynfield.h  | 132 +
 lib/node/version.map   |   3 +
 6 files changed, 189 insertions(+), 1 deletion(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index b2fc24b3e4..6b93b3cd97 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -219,7 +219,8 @@ The public API headers are grouped by topics:
 [ip4_node](@ref rte_node_ip4_api.h),
 [ip6_node](@ref rte_node_ip6_api.h),
 [udp4_input_node](@ref rte_node_udp4_input_api.h)
-
+  * graph_nodes_mbuf:
+[node_mbuf_dynfield](@ref rte_node_mbuf_dynfield.h)
 - **basic**:
   [bitops](@ref rte_bitops.h),
   [approx fraction](@ref rte_approx.h),
diff --git a/doc/guides/rel_notes/release_25_07.rst 
b/doc/guides/rel_notes/release_25_07.rst
index cd1025aac0..72d5d88ff3 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,12 @@ New Features
  Also, make sure to start the actual text at the margin.
  ===

+* **Added rte_node specific global mbuf dynamic field.**
+
+  Instead each node registering mbuf dynamic field for its own purpose, a
+  global structure is added which can be used/overloaded by all nodes
+  (including out-of-tree nodes). This minimizes footprint of node specific mbuf
+  dynamic field.

 Removed Items
 -
diff --git a/lib/node/meson.build b/lib/node/meson.build
index 0bed97a96c..4330d0450b 100644
--- a/lib/node/meson.build
+++ b/lib/node/meson.build
@@ -8,6 +8,7 @@ if is_windows
 endif

 sources = files(
+'node_mbuf_dynfield.c',
 'ethdev_ctrl.c',
 'ethdev_rx.c',
 'ethdev_tx.c',
@@ -30,6 +31,7 @@ headers = files(
 'rte_node_ip4_api.h',
 'rte_node_ip6_api.h',
 'rte_node_udp4_input_api.h',
+'rte_node_mbuf_dynfield.h'
 )

 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
diff --git a/lib/node/node_mbuf_dynfield.c b/lib/node/node_mbuf_dynfield.c
new file mode 100644
index 00..082918ae96
--- /dev/null
+++ b/lib/node/node_mbuf_dynfield.c
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+#include 
+#include 
+#include 
+
+#define NODE_MBUF_DYNFIELD_MEMZONE_NAME "__rte_node_mbuf_dynfield"
+
+struct node_mbuf_dynfield_mz {
+   int dynfield_offset;
+};
+
+static const struct rte_mbuf_dynfield node_mbuf_dynfield_desc = {
+   .name = "rte_node_mbuf_dynfield",
+   .size = sizeof(rte_node_mbuf_dynfield_t),
+   .align = alignof(rte_node_mbuf_dynfield_t),
+};
+
+int node_mbuf_dynfield_offset = -1;
+
+int rte_node_mbuf_dynfield_register(void)
+{
+   int dyn_offset;
+
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_dynfield_t) < 
RTE_NODE_MBUF_DYNFIELD_SIZE);
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_overload_fields_t) <
+RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE);
+
+   if (node_mbuf_dynfield_offset == -1) {
+   dyn_offset = 
rte_mbuf_dynfield_register(&node_mbuf_dynfield_desc);
+   if (dyn_offset < 0) {
+   node_err("node_mbuf_dyn", "rte_mbuf_dynfield_register 
failed");
+   return -1;
+   }
+   node_mbuf_dynfield_offset = dyn_offset;
+
+   node_dbg("node_mbuf_dyn", "node mbuf dynfield size %zu at 
offset: %d",
+ sizeof(rte_node_mbuf_dynfield_t), 
node_mbuf_dynfield_offset);
+   } else {
+   dyn_offset = node_mbuf_dynfield_offset;
+   }
+   return dyn_offset;
+}
diff --git a/lib/node/rte_node_mbuf_dynfield.h 
b/lib/node/rte_node_mbuf_dynfield.h
new file mode 100644
index 00..4293b3823d
--- /dev/null
+++ b/lib/node/rte_node_mbuf_dynfield.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_MBUF_DYNFIELD_H_
+#define _RTE_GRAPH_MBUF_DYNFIELD_H_
+
+#include

[PATCH v3 0/2] node: add mbuf dynamic field for nodes

2025-04-04 Thread Nitin Saxena
Currently each rte_node registers separate mbuf dynamic fields for their
own purpose. This leads to wastage of mbuf space as once mbuf get passed
a particular node, the registered dynamic field(by that node) is no
longer used.

This patch series adds a global/common mbuf dynamic field which is
reusable by all the nodes(including out-of-tree nodes). This helps to
repurpose same mbuf dynamic field for other nodes. It contains two types
of fields: (a) persistent (b) overloadable.

While persistent fields are those which does not often changes during a
graph walk such as rx/tx interface, buffer flags etc. Currently there
are no persistent fields added but they can be added later

Overloadable fields are those which can be overloaded by two adjacent
nodes. Overloadable fields can be repurposed by other two adjacent nodes.

This patch series also updates ip4/ip6 lookup/rewrite nodes to use
overlaodable mbuf dynamic fields.

Changes in v3:
- Fix CI build error

Changes in v2:
- removed usage of memzone for saving mbuf dynfield [Stephen]
- fixed checkpatch issues
- redefine RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE to 8 byte which are
  currently in use. Size can increase later based on the usage


Nitin Saxena (2):
  node: add global node mbuf dynfield
  node: use node mbuf dynfield in ip4 nodes

 doc/api/doxy-api-index.md  |   3 +-
 doc/guides/rel_notes/release_25_07.rst |   6 ++
 lib/node/ip4_lookup.c  |  14 +--
 lib/node/ip4_rewrite.c |  15 ++-
 lib/node/ip6_lookup.c  |  15 ++-
 lib/node/ip6_rewrite.c |  14 +--
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  44 
 lib/node/node_private.h|  40 +--
 lib/node/rte_node_mbuf_dynfield.h  | 141 +
 lib/node/version.map   |   3 +
 11 files changed, 223 insertions(+), 74 deletions(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

--
2.43.0



Re: [PATCH 1/2] node: add global node mbuf dynfield

2025-04-04 Thread Nitin Saxena
Hi Stephen,

Thanks for commenting. See response inline.

Regards,
Nitin

On Tue, Apr 1, 2025 at 7:45 PM Stephen Hemminger
 wrote:
>
> On Tue, 1 Apr 2025 09:50:46 +0530
> Nitin Saxena  wrote:
>
> > +int rte_node_mbuf_dynfield_register(void)
> > +{
> > + struct node_mbuf_dynfield_mz *f = NULL;
> > + const struct rte_memzone *mz = NULL;
> > + int dyn_offset;
> > +
> > + RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_dynfield_t) < 
> > RTE_NODE_MBUF_DYNFIELD_SIZE);
> > + RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_overload_fields_t) <
> > +  RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE);
> > +
> > + mz = rte_memzone_lookup(NODE_MBUF_DYNFIELD_MEMZONE_NAME);
>
> Seems wasteful to have a whole memzone for this, the data is small.
> Is there a reason it could not just be a global variable like timestamp.
>
Replaced usage of memzone with global variable in v2

> I would prefer this was a clone of timestamp code, and put in 
> rte_mbuf_dynfield.c
rte_node_mbuf_dynfield_register() is local to graph based rte_nodes
and it is not targeted to be used by non-graph based applications

Do you still think we should move this API definition to rte_mbuf_dyn.c?


[PATCH v2 1/2] node: add global node mbuf dynfield

2025-04-03 Thread Nitin Saxena
This patch defines rte_node specific dynamic field structure
(rte_node_mbuf_dynfield_t)

rte_node_mbuf_dynfield_t structure holds two types of fields
- Persistent data fields which are preserved across graph walk.
  Currently size of persistent data fields is zero.
- Overloadable data fields which are used by any two adjacent nodes.
  Same fields can be repurposed by any other adjacent nodes

This dynfield can be also be used by out-of-tree nodes.

Signed-off-by: Nitin Saxena 
---
 doc/api/doxy-api-index.md  |   3 +-
 doc/guides/rel_notes/release_25_07.rst |   6 ++
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  46 +
 lib/node/rte_node_mbuf_dynfield.h  | 130 +
 lib/node/version.map   |   3 +
 6 files changed, 189 insertions(+), 1 deletion(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index b2fc24b3e4..6b93b3cd97 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -219,7 +219,8 @@ The public API headers are grouped by topics:
 [ip4_node](@ref rte_node_ip4_api.h),
 [ip6_node](@ref rte_node_ip6_api.h),
 [udp4_input_node](@ref rte_node_udp4_input_api.h)
-
+  * graph_nodes_mbuf:
+[node_mbuf_dynfield](@ref rte_node_mbuf_dynfield.h)
 - **basic**:
   [bitops](@ref rte_bitops.h),
   [approx fraction](@ref rte_approx.h),
diff --git a/doc/guides/rel_notes/release_25_07.rst 
b/doc/guides/rel_notes/release_25_07.rst
index cd1025aac0..72d5d88ff3 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -55,6 +55,12 @@ New Features
  Also, make sure to start the actual text at the margin.
  ===

+* **Added rte_node specific global mbuf dynamic field.**
+
+  Instead each node registering mbuf dynamic field for its own purpose, a
+  global structure is added which can be used/overloaded by all nodes
+  (including out-of-tree nodes). This minimizes footprint of node specific mbuf
+  dynamic field.

 Removed Items
 -
diff --git a/lib/node/meson.build b/lib/node/meson.build
index 0bed97a96c..4330d0450b 100644
--- a/lib/node/meson.build
+++ b/lib/node/meson.build
@@ -8,6 +8,7 @@ if is_windows
 endif

 sources = files(
+'node_mbuf_dynfield.c',
 'ethdev_ctrl.c',
 'ethdev_rx.c',
 'ethdev_tx.c',
@@ -30,6 +31,7 @@ headers = files(
 'rte_node_ip4_api.h',
 'rte_node_ip6_api.h',
 'rte_node_udp4_input_api.h',
+'rte_node_mbuf_dynfield.h'
 )

 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
diff --git a/lib/node/node_mbuf_dynfield.c b/lib/node/node_mbuf_dynfield.c
new file mode 100644
index 00..1163c1e991
--- /dev/null
+++ b/lib/node/node_mbuf_dynfield.c
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define NODE_MBUF_DYNFIELD_MEMZONE_NAME "__rte_node_mbuf_dynfield"
+
+struct node_mbuf_dynfield_mz {
+   int dynfield_offset;
+};
+
+static const struct rte_mbuf_dynfield node_mbuf_dynfield_desc = {
+   .name = "rte_node_mbuf_dynfield",
+   .size = sizeof(rte_node_mbuf_dynfield_t),
+   .align = alignof(rte_node_mbuf_dynfield_t),
+};
+
+int node_mbuf_dynfield_offset = -1;
+
+int rte_node_mbuf_dynfield_register(void)
+{
+   int dyn_offset;
+
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_dynfield_t) < 
RTE_NODE_MBUF_DYNFIELD_SIZE);
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_overload_fields_t) <
+RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE);
+
+   if (node_mbuf_dynfield_offset == -1) {
+   dyn_offset = 
rte_mbuf_dynfield_register(&node_mbuf_dynfield_desc);
+   if (dyn_offset < 0) {
+   node_err("node_mbuf_dyn", "rte_mbuf_dynfield_register 
failed");
+   return -1;
+   }
+   node_mbuf_dynfield_offset = dyn_offset;
+
+   node_dbg("node_mbuf_dyn", "node mbuf dynfield size %zu at 
offset: %d",
+ sizeof(rte_node_mbuf_dynfield_t), 
node_mbuf_dynfield_offset);
+   } else {
+   dyn_offset = node_mbuf_dynfield_offset;
+   }
+   return dyn_offset;
+}
diff --git a/lib/node/rte_node_mbuf_dynfield.h 
b/lib/node/rte_node_mbuf_dynfield.h
new file mode 100644
index 00..794251b1f1
--- /dev/null
+++ b/lib/node/rte_node_mbuf_dynfield.h
@@ -0,0 +1,130 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+
+#ifndef _RTE_GRAPH_MBUF_DYNFIELD_H_
+#define _RTE_GRAPH_MBUF_D

[PATCH v2 2/2] node: use node mbuf dynfield in ip4 nodes

2025-04-03 Thread Nitin Saxena
- Used global node mbuf in ip[4|6]_lookup/rewrite nodes
- Redefine node_mbuf_priv1() to rte_node_mbuf_overload_fields_get()

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_lookup.c | 14 ---
 lib/node/ip4_rewrite.c| 15 +---
 lib/node/ip6_lookup.c | 15 +---
 lib/node/ip6_rewrite.c| 14 ---
 lib/node/node_private.h   | 40 +++
 lib/node/rte_node_mbuf_dynfield.h |  9 +++
 6 files changed, 34 insertions(+), 73 deletions(-)

diff --git a/lib/node/ip4_lookup.c b/lib/node/ip4_lookup.c
index 0b474cd2bc..7e9d21a9c9 100644
--- a/lib/node/ip4_lookup.c
+++ b/lib/node/ip4_lookup.c
@@ -31,8 +31,6 @@ struct ip4_lookup_node_ctx {
int mbuf_priv1_off;
 };

-int node_mbuf_priv1_dynfield_offset = -1;
-
 static struct ip4_lookup_node_main ip4_lookup_nm;

 #define IP4_LOOKUP_NODE_LPM(ctx) \
@@ -180,17 +178,15 @@ ip4_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_lookup_node_ctx) > RTE_NODE_CTX_SZ);

+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -208,7 +204,7 @@ ip4_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP4_LOOKUP_NODE_LPM(node->ctx) = ip4_lookup_nm.lpm_tbl[graph->socket];
-   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

 #if defined(__ARM_NEON) || defined(RTE_ARCH_X86)
if (rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128)
diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index 34a920df5e..b05decc1a3 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -258,19 +258,16 @@ ip4_rewrite_node_process(struct rte_graph *graph, struct 
rte_node *node,
 static int
 ip4_rewrite_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
-   static bool init_once;
+   int dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_rewrite_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-   init_once = true;
-   }
-   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
+
+   IP4_REWRITE_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip4_rewrite", "Initialized ip4_rewrite node initialized");

diff --git a/lib/node/ip6_lookup.c b/lib/node/ip6_lookup.c
index f378d2d064..c140aa9cf3 100644
--- a/lib/node/ip6_lookup.c
+++ b/lib/node/ip6_lookup.c
@@ -316,18 +316,16 @@ ip6_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip6_lookup_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset =
-   rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;

+   if (!init_once) {
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -345,8 +343,7 @@ ip6_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP6_LOOKUP_NODE_LPM(node->ctx) = ip6_lookup_nm.lpm_tbl[graph->socket];
-   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) =
-   node_mbuf_priv1_dynfield_offset;
+   IP6_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip6_lookup", "Initialized ip6_lookup node");

diff --git a/lib/n

Re: [PATCH v3 00/14] add lookup fib nodes in graph library

2025-06-02 Thread Nitin Saxena
Hi Morten,

Let me take a stab regarding the VRF question.
Please find answers inline

Thanks,
Nitin

On Mon, Jun 2, 2025 at 2:12 PM Morten Brørup  wrote:
>
> +TO: Robin Jarry, might have relevant feedback for such a node
>
> > From: Ankur Dwivedi [mailto:adwiv...@marvell.com]
> > Sent: Monday, 2 June 2025 08.36
> >
> > This patch series adds two inbuilt nodes ip4_lookup_fib and
> > ip6_lookup_fib in graph library. These nodes uses the existing
> > Forwarding Information Base (FIB) library to create FIB, to do
> > route addition and route lookup.
> >
> > Two new commands (for ipv4 and ipv6) for providing the lookup mode
> > is added in the dpdk-graph application. fib or lpm can be given as
> > lookup mode. If these new lookup mode commands are not given, the
> > dpdk-graph uses lpm (Longest Prefix Match) or lpm6 by default.
> > If fib is given as lookup mode then the ip4_lookup_fib or
> > ip6_lookup_fib
> > nodes are used by the application.
>
> @Ankur, @Vladimir, @Jerin,
>
> A couple of high level questions...
>
> 1. Thread safety:
> I'm not familiar with the thread safety models of the FIB and Graph libraries.
> Is the FIB library thread safe, so one thread can invoke the FIB modify 
> operation while other threads invoke the FIB lookup operation?
> If it isn't, how does the Graph library make its use of the FIB library 
> thread safe?
>

Just a nit-pick here, there are two libraries used by rte_graph applications
- Graph library (lib/graph/*)
- Node library (lib/node/*)

Questions here are pertaining to "Node library" and not to "Graph library"

> 2. VRF support:
> This looks like a global route table for the entire graph.
Correct.

> Shouldn't there be a route table per FIB node?
Yes when VRF support is added in library, one FIB would support on route table

> Or how are VRFs supported by the Graph library?
> Or is there no requirement to support VRFs in the Graph library?
>
Currently the node library(instead of Graph library) does not support
VRF. There is an intention to add it in lib/node/ip[4|6]-lookup nodes
IMO, @Vladimir can correct me, even FIB support for multiple VRF is
unoptimized because rte_fib_lookup_bulk() function takes single fib
pointer instead of multiple fib pointers
For adding VRF support in ip[4|6]-lookup nodes ideally we would like to have

rte_fib_lookup_bulk(struct rte_fib *fib, uint32_t *ips, uint64_t
next_hops, int n); (OR different new API)

taking an array of fib pointers (or fib index) corresponding to each
nth packet. It should also avoid fib->lookup() callback for every
packet.
We can also add this API as part of "Node library" but we need "static
inline" version of fib->lookup() in rte_fib header file

> -Morten
>


[PATCH v7 2/2] node: use node mbuf dynfield in ip4 nodes

2025-06-19 Thread Nitin Saxena
- Used global node mbuf in ip[4|6]_lookup/rewrite nodes
- Redefine node_mbuf_priv1() to rte_node_mbuf_overload_fields_get()

Signed-off-by: Nitin Saxena 
---
 lib/node/ip4_lookup.c | 14 ---
 lib/node/ip4_lookup_fib.c | 17 ++---
 lib/node/ip4_rewrite.c| 32 +
 lib/node/ip6_lookup.c | 15 +---
 lib/node/ip6_lookup_fib.c | 17 ++---
 lib/node/ip6_rewrite.c| 14 ---
 lib/node/node_private.h   | 40 +++
 lib/node/rte_node_mbuf_dynfield.h |  9 +++
 8 files changed, 59 insertions(+), 99 deletions(-)

diff --git a/lib/node/ip4_lookup.c b/lib/node/ip4_lookup.c
index 604fcb267a..f6db3219f0 100644
--- a/lib/node/ip4_lookup.c
+++ b/lib/node/ip4_lookup.c
@@ -32,8 +32,6 @@ struct ip4_lookup_node_ctx {
int mbuf_priv1_off;
 };

-int node_mbuf_priv1_dynfield_offset = -1;
-
 static struct ip4_lookup_node_main ip4_lookup_nm;

 #define IP4_LOOKUP_NODE_LPM(ctx) \
@@ -182,17 +180,15 @@ ip4_lookup_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
uint16_t socket, lcore_id;
static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_lookup_node_ctx) > RTE_NODE_CTX_SZ);

+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0)
+   return -rte_errno;
if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-
/* Setup LPM tables for all sockets */
RTE_LCORE_FOREACH(lcore_id)
{
@@ -210,7 +206,7 @@ ip4_lookup_node_init(const struct rte_graph *graph, struct 
rte_node *node)

/* Update socket's LPM and mbuf dyn priv1 offset in node ctx */
IP4_LOOKUP_NODE_LPM(node->ctx) = ip4_lookup_nm.lpm_tbl[graph->socket];
-   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = node_mbuf_priv1_dynfield_offset;
+   IP4_LOOKUP_NODE_PRIV1_OFF(node->ctx) = dyn;

 #if defined(__ARM_NEON) || defined(RTE_ARCH_X86)
if (rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128)
diff --git a/lib/node/ip4_lookup_fib.c b/lib/node/ip4_lookup_fib.c
index f9a5c5aa5a..0857d889fc 100644
--- a/lib/node/ip4_lookup_fib.c
+++ b/lib/node/ip4_lookup_fib.c
@@ -277,18 +277,15 @@ setup_fib(int socket)
 static int
 ip4_lookup_fib_node_init(const struct rte_graph *graph, struct rte_node *node)
 {
-   static uint8_t init_once;
-   int rc;
+   int rc, dyn;

RTE_BUILD_BUG_ON(sizeof(struct ip4_lookup_fib_node_ctx) > 
RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
-
-   init_once = 1;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0) {
+   node_err("ip4_lookup_fib", "Failed to register mbuf dynfield, 
rc=%d",
+-rte_errno);
+   return -rte_errno;
}

rc = setup_fib(graph->socket);
@@ -300,7 +297,7 @@ ip4_lookup_fib_node_init(const struct rte_graph *graph, 
struct rte_node *node)

/* Update socket's FIB and mbuf dyn priv1 offset in node ctx */
IP4_LOOKUP_NODE_FIB(node->ctx) = ip4_lookup_fib_nm.fib[graph->socket];
-   IP4_LOOKUP_FIB_NODE_PRIV1_OFF(node->ctx) = 
node_mbuf_priv1_dynfield_offset;
+   IP4_LOOKUP_FIB_NODE_PRIV1_OFF(node->ctx) = dyn;

node_dbg("ip4_lookup_fib", "Initialized ip4_lookup_fib node");

diff --git a/lib/node/ip4_rewrite.c b/lib/node/ip4_rewrite.c
index b1fa386d9f..37bc3a511f 100644
--- a/lib/node/ip4_rewrite.c
+++ b/lib/node/ip4_rewrite.c
@@ -486,16 +486,18 @@ ip4_rewrite_node_init(const struct rte_graph *graph, 
struct rte_node *node)
 {
rte_graph_feature_arc_t feature_arc = RTE_GRAPH_FEATURE_ARC_INITIALIZER;
static bool init_once;
+   int dyn;

RTE_SET_USED(graph);
RTE_BUILD_BUG_ON(sizeof(struct ip4_rewrite_node_ctx) > RTE_NODE_CTX_SZ);

-   if (!init_once) {
-   node_mbuf_priv1_dynfield_offset = rte_mbuf_dynfield_register(
-   &node_mbuf_priv1_dynfield_desc);
-   if (node_mbuf_priv1_dynfield_offset < 0)
-   return -rte_errno;
+   dyn = rte_node_mbuf_dynfield_register();
+   if (dyn < 0) {
+   node_err("ip4_rewrite", "Failed to register mbuf dynfield");
+   return -rte_errno;
+   }

+   if (!init_once) {

[PATCH v7 1/2] node: add global node mbuf dynfield

2025-06-19 Thread Nitin Saxena
This patch defines rte_node specific dynamic field structure
(rte_node_mbuf_dynfield_t)

rte_node_mbuf_dynfield_t structure holds two types of fields
- Persistent data fields which are preserved across graph walk.
  Currently size of persistent data fields is zero.
- Overloadable data fields which are used by any two adjacent nodes.
  Same fields can be repurposed by any other adjacent nodes

This dynfield can be also be used by out-of-tree nodes.

Signed-off-by: Nitin Saxena 
---
 doc/api/doxy-api-index.md  |   2 +
 doc/guides/rel_notes/release_25_07.rst |   6 +
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  59 ++
 lib/node/rte_node_mbuf_dynfield.h  | 146 +
 5 files changed, 215 insertions(+)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

diff --git a/doc/api/doxy-api-index.md b/doc/api/doxy-api-index.md
index 1254e32b25..a9d8cbf2dc 100644
--- a/doc/api/doxy-api-index.md
+++ b/doc/api/doxy-api-index.md
@@ -222,6 +222,8 @@ The public API headers are grouped by topics:
 [ip4_node](@ref rte_node_ip4_api.h),
 [ip6_node](@ref rte_node_ip6_api.h),
 [udp4_input_node](@ref rte_node_udp4_input_api.h)
+  * graph_nodes_mbuf:
+[node_mbuf_dynfield](@ref rte_node_mbuf_dynfield.h)

 - **basic**:
   [bitops](@ref rte_bitops.h),
diff --git a/doc/guides/rel_notes/release_25_07.rst 
b/doc/guides/rel_notes/release_25_07.rst
index 19be7740c4..4be2d88ec9 100644
--- a/doc/guides/rel_notes/release_25_07.rst
+++ b/doc/guides/rel_notes/release_25_07.rst
@@ -141,6 +141,12 @@ New Features
   String values are simply saved as-is,
   while the boolean support allows for values "true", "false", "1" or "0".

+* **Added rte_nodes specific shared mbuf dynamic field.**
+
+  Instead each rte_node registers its own mbuf dynamic field for its specific
+  purpose, a global/shared structure is added which can be used/overloaded by
+  any node (including out-of-tree nodes). This minimizes footprint of node
+  specific mbuf dynamic field.

 Removed Items
 -
diff --git a/lib/node/meson.build b/lib/node/meson.build
index 8355c111fe..de0f118871 100644
--- a/lib/node/meson.build
+++ b/lib/node/meson.build
@@ -8,6 +8,7 @@ if is_windows
 endif

 sources = files(
+'node_mbuf_dynfield.c',
 'ethdev_ctrl.c',
 'ethdev_rx.c',
 'ethdev_tx.c',
@@ -34,6 +35,7 @@ headers = files(
 'rte_node_ip6_api.h',
 'rte_node_pkt_cls_api.h',
 'rte_node_udp4_input_api.h',
+'rte_node_mbuf_dynfield.h',
 )

 # Strict-aliasing rules are violated by uint8_t[] to context size casts.
diff --git a/lib/node/node_mbuf_dynfield.c b/lib/node/node_mbuf_dynfield.c
new file mode 100644
index 00..6fbbf9604c
--- /dev/null
+++ b/lib/node/node_mbuf_dynfield.c
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(C) 2025 Marvell International Ltd.
+ */
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#define NODE_MBUF_DYNFIELD_MEMZONE_NAME "__rte_node_mbuf_dynfield"
+
+struct node_mbuf_dynfield_mz {
+   int dynfield_offset;
+};
+
+static const struct rte_mbuf_dynfield node_mbuf_dynfield_desc = {
+   .name = "rte_node_mbuf_dynfield",
+   .size = sizeof(rte_node_mbuf_dynfield_t),
+   .align = alignof(rte_node_mbuf_dynfield_t),
+};
+
+RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_node_mbuf_dynfield_register, 25.07);
+int rte_node_mbuf_dynfield_register(void)
+{
+   struct node_mbuf_dynfield_mz *f = NULL;
+   const struct rte_memzone *mz = NULL;
+   int dyn_offset;
+
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_dynfield_t) < 
RTE_NODE_MBUF_DYNFIELD_SIZE);
+   RTE_BUILD_BUG_ON(sizeof(rte_node_mbuf_overload_fields_t) <
+RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE);
+
+   mz = rte_memzone_lookup(NODE_MBUF_DYNFIELD_MEMZONE_NAME);
+
+   if (!mz) {
+   mz = 
rte_memzone_reserve_aligned(NODE_MBUF_DYNFIELD_MEMZONE_NAME,
+sizeof(struct 
node_mbuf_dynfield_mz),
+SOCKET_ID_ANY, 0,
+RTE_CACHE_LINE_SIZE);
+   if (!mz) {
+   node_err("node_mbuf_dyn", "rte_memzone_reserve_aligned 
failed");
+   rte_errno = ENOMEM;
+   return -1;
+   }
+   dyn_offset = 
rte_mbuf_dynfield_register(&node_mbuf_dynfield_desc);
+   if (dyn_offset < 0) {
+   node_err("node_mbuf_dyn", "rte_mbuf_dynfield_register 
failed");
+   return dyn_offset;
+   }
+   f = (struct node_mbuf_dynfield_mz *

[PATCH v7 0/2] node: add mbuf dynamic field for nodes

2025-06-19 Thread Nitin Saxena
Currently each rte_node registers separate mbuf dynamic fields for their
own purpose. This leads to wastage of mbuf space as once mbuf get passed
a particular node, the registered dynamic field(by that node) is no
longer used.

This patch series adds a global/common mbuf dynamic field which is
reusable by all the nodes(including out-of-tree nodes). This helps to
repurpose same mbuf dynamic field for other nodes. It contains two types
of fields: (a) persistent (b) overloadable.

While persistent fields are those which does not often changes during a
graph walk such as rx/tx interface, buffer flags etc. Currently there
are no persistent fields added but they can be added later

Overloadable fields are those which can be used by two adjacent nodes.
Same overloadable fields can be repurposed by other two adjacent nodes.

This patch series also updates ip4/ip6 lookup/rewrite nodes to use
overlaodable mbuf dynamic fields.

Changes in v7:
- Rebase on latest main with changes rte_ip4_lookup_fib nodes as well
- Verified ip4 output arc functionality with both "ip4_lookup" and
  "ip4_lookup_fib" nodes

Changes in v6:
- Incorporate comments from Pavan

Changes in v5
- Rebase on latest main with version.map changes
- Bring back memzone based node mbuf dynfield for secondary
  process [Pavan]

Changes in v4
- Fix github CI

Changes in v3:
- Fix CI build error

Changes in v2:
- removed usage of memzone for saving mbuf dynfield [Stephen]
- fixed checkpatch issues
- redefine RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE to 8 byte which are
  currently in use. Size can increase later based on the usage


Nitin Saxena (2):
  node: add global node mbuf dynfield
  node: use node mbuf dynfield in ip4 nodes

 doc/api/doxy-api-index.md  |   2 +
 doc/guides/rel_notes/release_25_07.rst |   6 +
 lib/node/ip4_lookup.c  |  14 +--
 lib/node/ip4_lookup_fib.c  |  17 ++-
 lib/node/ip4_rewrite.c |  32 ++---
 lib/node/ip6_lookup.c  |  15 +--
 lib/node/ip6_lookup_fib.c  |  17 ++-
 lib/node/ip6_rewrite.c |  14 +--
 lib/node/meson.build   |   2 +
 lib/node/node_mbuf_dynfield.c  |  59 ++
 lib/node/node_private.h|  40 +--
 lib/node/rte_node_mbuf_dynfield.h  | 155 +
 12 files changed, 274 insertions(+), 99 deletions(-)
 create mode 100644 lib/node/node_mbuf_dynfield.c
 create mode 100644 lib/node/rte_node_mbuf_dynfield.h

--
2.43.0



Re: [PATCH v6 0/2] node: add mbuf dynamic field for nodes

2025-06-19 Thread Nitin Saxena
Hi Thomas,

Thanks,
Nitin

On Wed, Jun 11, 2025 at 5:15 PM Thomas Monjalon  wrote:
>
> 28/04/2025 12:37, Nitin Saxena:
> > Currently each rte_node registers separate mbuf dynamic fields for their
> > own purpose. This leads to wastage of mbuf space as once mbuf get passed
> > a particular node, the registered dynamic field(by that node) is no
> > longer used.
> >
> > This patch series adds a global/common mbuf dynamic field which is
> > reusable by all the nodes(including out-of-tree nodes). This helps to
> > repurpose same mbuf dynamic field for other nodes. It contains two types
> > of fields: (a) persistent (b) overloadable.
> >
> > While persistent fields are those which does not often changes during a
> > graph walk such as rx/tx interface, buffer flags etc. Currently there
> > are no persistent fields added but they can be added later
> >
> > Overloadable fields are those which can be used by two adjacent nodes.
> > Same overloadable fields can be repurposed by other two adjacent nodes.
> >
> > This patch series also updates ip4/ip6 lookup/rewrite nodes to use
> > overlaodable mbuf dynamic fields.
> >
> > Changes in v6:
> > - Incorporate comments from Pavan
> >
> > Changes in v5
> > - Rebase on latest main with version.map changes
> > - Bring back memzone based node mbuf dynfield for secondary
> >   process [Pavan]
>
> Please it needs to be rebased again on top of feature arc that you added.
> Thanks

Rebased patch-set v7 on top of main. Sorry could not do earlier
>
>


Re: [PATCH v7 1/2] node: add global node mbuf dynfield

2025-06-26 Thread Nitin Saxena
Hi Stephen,


On Thu, Jun 26, 2025 at 7:29 PM Stephen Hemminger
 wrote:
>
> On Thu, 19 Jun 2025 21:14:49 +0530
> Nitin Saxena  wrote:
>
> > +#ifndef RTE_NODE_MBUF_PERSISTENT_FIELDS_SIZE
> > +/** Size of persistent mbuf fields */
> > +#define RTE_NODE_MBUF_PERSISTENT_FIELDS_SIZE  (0)
> > +#endif /* RTE_NODE_MBUF_PERSISTENT_FIELDS_SIZE */
> > +
> > +#ifndef RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE
> > +/** Size of overloadable mbuf fields */
> > +#define RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE(8)
> > +#endif /* RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE */
> > +
> > +/** Size of node mbuf dynamic field */
> > +#define RTE_NODE_MBUF_DYNFIELD_SIZE \
> > + (RTE_NODE_MBUF_PERSISTENT_FIELDS_SIZE + 
> > RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE)
> > +
> > +/**
> > + * Node mbuf overloadable data.
> > + *
> > + * Out-of-tree nodes can repurpose overloadable fields via
> > + * rte_node_mbuf_overload_fields_get(mbuf). Overloadable fields are not
> > + * preserved and typically can be used with-in two adjacent nodes in the 
> > graph.
> > + */
> > +typedef struct rte_node_mbuf_overload_fields {
> > + union {
> > + uint8_t data[RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE];
> > + };
> > +} rte_node_mbuf_overload_fields_t;
>
> Having a blank scratchpad rather than a real defined structure means you
> lose any type checking or potential overflow checking.

Patchset-1 creates a blank scratchpad but patchset-2 adds following
fields when added field is used in ip4/ip6 nodes
So idea is to add relevant structures as and when they are used

 diff --git a/lib/node/rte_node_mbuf_dynfield.h
b/lib/node/rte_node_mbuf_dynfield.h
 index 1069c07d44..045c13e352 100644
 --- a/lib/node/rte_node_mbuf_dynfield.h
 +++ b/lib/node/rte_node_mbuf_dynfield.h
 @@ -46,6 +46,15 @@ extern "C" {
   */
  typedef struct rte_node_mbuf_overload_fields {
  >--union {
+>-->---/* Following fields used by ip[4|6]-lookup ->
ip[4|6]-rewrite nodes */
+>-->---union {
+>-->--->---struct {
+>-->--->--->---uint16_t nh;
+>-->--->--->---uint16_t ttl;
+>-->--->--->---uint32_t cksum;
+>-->--->---};
+>-->--->---uint64_t u;
+>-->---};
>-->---uint8_t data[RTE_NODE_MBUF_OVERLOADABLE_FIELDS_SIZE];
>--};
} rte_node_mbuf_overload_fields_t;


[PATCH v10 0/7] add feature arc in rte_graph

2025-06-04 Thread Nitin Saxena
Feature arc represents an ordered list of features/protocols at a given
networking layer. It is a high level abstraction to express relationship
between rte_graph nodes, as feature nodes, and allow packets steering
across these nodes in a simplified manner.

Features (or feature nodes) are nodes which handles partial or complete
handling of a protocol in fast path. Like ipv4-rewrite node, which adds
rewrite data to an outgoing IPv4 packet.

However in above example, outgoing interface(say "eth0") may have
outbound IPsec policy enabled, hence packets must be steered from
ipv4-rewrite node to outbound-IPsec-policy node. On the other hand,
packets routed to another interface (say eth1) must ot be sent to
IPsec node, as IPsec feature is disabled on eth1. Feature-arc allows
rte_graph applications to manage such constraints easily

Feature arc abstraction allows rte_graph based application to

1. Runtime enable/disable features on an index (like interface), so that
if a feature is enabled at runtime, only then packets associated with
that interface will steer to corresponding feature node. Control plane
enables/disables features. Features enabled on one interface may not be
enabled on another interface with in a same feature arc.

2. Single feature arc represents a hook point at a network layer in an
given direction. Multiple feature arcs can be added in desginated nodes
to add various hook points in stack. Likt IPv4-output, IPv4-input,
IPv6-output, IPv6-input, ethernet-input, mpls-input, mpls-output etc.

3. Hook custom/out-of-tree nodes to DPDK in-built nodes and allow packet
steering from in-built node to custom node without requiring changes to
in-built fast path function

4. Express features in a particular sequential order so that
packets are steered in an ordered way across nodes in fast path. For
eg: if IPsec and IPv4 features are enabled on an ingress interface,
packets must be sent to IPsec inbound policy node first and then to ipv4
lookup node.

This patch series adds feature arc library in rte_graph and also adds
"ipv4-output" feature arc handling in "ipv4-rewrite" node.

Changes in v10
- Incorporated all comments from Jerin
- Splitted feature arc spec patch into 3 patches
- added programming guide in each patch
- Fixed documentation comments

Changes in v9:
- Fix build failures

Changes in v8:
- Fix CI iol testing

Changes in v7:
- Rebased to latest main for DPDK-25.07
- Added override_index_cb() in RTE_GRAPH_FEATURE_REGISTER() to allow
  features to take control of "max_index" (passed to
  rte_graph_feature_arc_create())
- Streamlined fast path APIs to make them more simpler
- Added quad_loop in "if_tx_feature" node
- Added functional test cases and fixed bugs
- Based on feedback from Redhat on v6 patches, it was tried to move
  feature arc APIs inside rte_graph library (to reduce node burden) but
  it was adding 4% performance regression for L3fwd usecase. For
  interested users: https://github.com/nsaxena16/dpdk/pull/2

Changes in v6:
- Rebased to latest main for DPDK-25.03
- Added constructor based feature arc/feature registration
- Changed design to handle fast path synchronization via RCU mechanism
  when any feature is enabled or disabled
- Added feature arc specific mbuf dynamic field to carry feature data
  across nodes
- Added feature arc example in app/graph
- Programming guide and functional test cases in future versions


Nitin Saxena (7):
  graph: add API to override node process function
  graph: add feature arc registrations
  graph: add feature arc init APIs
  graph: add feature enable/disable APIs
  ip4: add ip4 output feature arc
  app/graph: add custom feature nodes for ip4 output arc
  test/graph_feature_arc: add functional tests

 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 |  180 ++
 app/graph/main.c|   15 +-
 app/graph/meson.build   |2 +
 app/graph/module_api.h  |2 +
 app/test/meson.build|1 +
 app/test/test_graph_feature_arc.c   | 1374 +
 doc/api/doxy-api-index.md   |2 +
 doc/guides/prog_guide/graph_lib.rst |  491 +
 doc/guides/prog_guide/img/feature_arc-1.svg |  269 +++
 doc/guides/prog_guide/img/feature_arc-2.svg |  511 +
 doc/guides/rel_notes/release_25_07.rst  |2 +
 doc/guides/tools/graph.rst  |   15 +
 lib/graph/graph_feature_arc.c   | 2050 +++
 lib/graph/graph_private.h   |   15 +
 lib/graph/meson.build   |4 +-
 lib/graph/node.c|   23 +
 lib/graph/rte_graph_feature_arc.h   |  644 ++
 lib/graph/rte_graph_feature_arc

[PATCH v10 1/7] graph: add API to override node process function

2025-06-04 Thread Nitin Saxena
New internal API used by feature arc library to override node's original
process() func.

Signed-off-by: Nitin Saxena 
---
 lib/graph/graph_private.h | 11 +++
 lib/graph/node.c  | 23 +++
 2 files changed, 34 insertions(+)

diff --git a/lib/graph/graph_private.h b/lib/graph/graph_private.h
index 813dd78b9d..579546e658 100644
--- a/lib/graph/graph_private.h
+++ b/lib/graph/graph_private.h
@@ -198,6 +198,17 @@ struct node_head *node_list_head_get(void);
  */
 struct node *node_from_name(const char *name);

+/**
+ * @internal
+ *
+ * Override process func of a node.
+ *
+ * @return
+ *   - 0: Success.
+ *   - <0: Error
+ */
+int node_override_process_func(rte_node_t id, rte_node_process_t process);
+
 /* Graph list functions */
 STAILQ_HEAD(graph_head, graph);

diff --git a/lib/graph/node.c b/lib/graph/node.c
index 101981ec24..c8a1cd5586 100644
--- a/lib/graph/node.c
+++ b/lib/graph/node.c
@@ -431,3 +431,26 @@ rte_node_max_count(void)
 {
return node_id;
 }
+
+int
+node_override_process_func(rte_node_t id, rte_node_process_t process)
+{
+   struct node *node;
+
+   NODE_ID_CHECK(id);
+   graph_spinlock_lock();
+
+   STAILQ_FOREACH(node, &node_list, next) {
+   if (node->id == id) {
+   node->process = process;
+   graph_spinlock_unlock();
+   return 0;
+   }
+   }
+
+   graph_spinlock_unlock();
+
+   return 0;
+fail:
+   return -1;
+}
--
2.43.0



  1   2   >