[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