Hi Anatoly,

On 9/16/2026 1:18 PM, Anatoly Burakov wrote:
This commit adds a flow graph parsing API. This is a helper API intended to
help ethdev drivers implement rte_flow parsers, as common usages map to
graph traversal problem very well.

Features provided by the API:
- Flow graph, edge, and node definitions
- Graph traversal logic
- Declarative validation against common flow item types
- Per-node validation and state processing callbacks

Signed-off-by: Anatoly Burakov<[email protected]>
---
  doc/guides/prog_guide/ethdev/flow_graph.rst | 749 ++++++++++++++++++++
  doc/guides/prog_guide/ethdev/index.rst      |   1 +
  doc/guides/rel_notes/release_26_11.rst      |   5 +
  lib/ethdev/flow_graph.h                     | 507 +++++++++++++
  lib/ethdev/meson.build                      |   1 +
  5 files changed, 1263 insertions(+)
  create mode 100644 doc/guides/prog_guide/ethdev/flow_graph.rst
  create mode 100644 lib/ethdev/flow_graph.h

<snip>
+/**
+ * Graph node definition.
+ *
+ * Node validity rules:
+ * - all nodes must define a name,
+ * - all non-END nodes must define an edge list,
+ * - start node must not define validation/processing callbacks.
+ */
+struct flow_graph_node {
+       const char *name;                    /**< Node name. */
+       enum rte_flow_item_type type;        /**< Flow item type to match. */
+       enum flow_graph_node_expect constraints; /**< Common validation 
constraints (ORed). */
since this field behaves as a bitmask, it's better to change the type to uint32_t
+       flow_graph_node_validate_fn validate;  /**< Validation callback (NULL 
if unsupported). */
+       flow_graph_node_process_fn process;    /**< Processing callback (NULL 
if no extraction needed). */
+};
+
<snip>
+static inline bool
+_flow_graph_node_is_valid(const struct flow_graph *graph,
+                          const struct flow_graph_node *node,
+                          struct rte_flow_error *error)
+{
+       size_t node_idx;
+
+       if (node == NULL) {
+               rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+                               "Flow graph node pointer is NULL");
+               return false;
+       }
+
+       if (node->name == NULL) {
+               rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_UNSPECIFIED, node,
+                               "Flow graph node name is not defined");
+               return false;
+       }
+
+       node_idx = _flow_graph_get_node_index(graph, node);
+
+       /* first node can't have callbacks because there's no item */
+       if (node_idx == FLOW_GRAPH_NODE_FIRST &&
+                       (node->validate != NULL || node->process != NULL)) {
+               rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_UNSPECIFIED, node,
+                               "Flow graph start node callbacks are not 
allowed");
+               return false;
+       }
+
+       /* all non-END nodes must have edges */
+       if (node->type != RTE_FLOW_ITEM_TYPE_END &&
this statement does not check edges for FLOW_GRAPH_NODE_FIRST node since it's type is 0 (thus RTE_FLOW_ITEM_TYPE_END)
+                       graph->edges[node_idx].next == NULL) {
+               rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_UNSPECIFIED, node,
+                               "Flow graph edge list is not defined for non-END 
node");
+               return false;
+       }
+
+       return true;
+}
+
<snip>
+       /* Traverse pattern items */
+       for (item = pattern; item->type != RTE_FLOW_ITEM_TYPE_END; item++) {
+
+               /* Skip items in the graph's ignore list */
+               if (_flow_graph_node_is_ignored(graph, item->type)) {
+                       FLOW_GRAPH_LOG(DEBUG, "ignored item %s",
+                                       
flow_graph_item_type_to_str(item->type));
+                       continue;
+               }
+
+               /* Find the next graph node for this item type */
+               cur_node = _flow_graph_find_next_node(graph, cur_node,
+                               item->type, error);
+               if (cur_node == NULL) {
+                       FLOW_GRAPH_LOG(DEBUG, "cannot traverse to item %s",
+                                       
flow_graph_item_type_to_str(item->type));
+                       return rte_flow_error_set(error, ENOTSUP,
+                                       RTE_FLOW_ERROR_TYPE_ITEM,
+                                       item, "Pattern item not supported");
if _flow_graph_node_is_valid() fails this rewrites the reason why it failed
+               }
+               FLOW_GRAPH_LOG(DEBUG, "processing %s", cur_node->name);
+               /* Validate and process the current item at this node */
+               ret = _flow_graph_visit_node(cur_node, ctx, item, error);
+               if (ret != 0) {
+                       /* error may be NULL */
+                       if (error != NULL)
+                               FLOW_GRAPH_LOG(DEBUG, "%s", error->message);
+                       return ret;
+               }
+       }
+
+       /* Pattern items have ended but we still need to process the end */
why not have this tail processing of the RTE_FLOW_ITEM_TYPE_END item inside the loop?
+       cur_node = _flow_graph_find_next_node(graph, cur_node, item->type, 
error);
+       if (cur_node == NULL) {
+               FLOW_GRAPH_LOG(DEBUG, "cannot traverse to item %s",
+                               flow_graph_item_type_to_str(item->type));
+               return rte_flow_error_set(error, ENOTSUP,
+                               RTE_FLOW_ERROR_TYPE_ITEM,
+                               item, "Pattern item not supported");
+       }
+       ret = _flow_graph_visit_node(cur_node, ctx, item, error);
+       if (ret != 0) {
+               /* error may be NULL */
+               if (error != NULL)
+                       FLOW_GRAPH_LOG(DEBUG, "%s", error->message);
+               return ret;
+       }
+
+       return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _FLOW_GRAPH_H_ */
diff --git a/lib/ethdev/meson.build b/lib/ethdev/meson.build
index 8ba6c708a24..99ff3c990c1 100644
--- a/lib/ethdev/meson.build
+++ b/lib/ethdev/meson.build
@@ -40,6 +40,7 @@ driver_sdk_headers += files(
          'ethdev_pci.h',
          'ethdev_vdev.h',
          'rte_flow_driver.h',
+        'flow_graph.h',
          'rte_mtr_driver.h',
          'rte_tm_driver.h',
  )

--
Regards,
Vladimir

Reply via email to