[PATCH v3 01/19] ethdev: add flow graph API
Medvedkin, Vladimir
vladimir.medvedkin at intel.com
Sat Sep 19 18:09:03 CEST 2026
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<anatoly.burakov at intel.com>
> ---
> 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
More information about the dev
mailing list