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