[PATCH v3 02/19] net/intel/common: add flow engines infrastructure
Medvedkin, Vladimir
vladimir.medvedkin at intel.com
Sat Sep 19 18:09:23 CEST 2026
On 9/16/2026 1:18 PM, Anatoly Burakov wrote:
> Current implementation of flow engines in various drivers have a few issues
> that need to be corrected.
>
> For one, some of the
seems like a missed word?
> are fundamentally incompatible with secondary
> processes, because the flow engine registration and creation will
> allocate structures in shared memory but use process-local pointers to
> point to flow engines and pattern tables.
>
> For another, a lot of them are needlessly complicated and rely on a
> separation between patterns and parsing that is hard to reason about and
> maintain: they do not define memory ownership model, they do not define the
> way in which we approach parameter and pattern parsing, and they
> occasionally do weird things like passing around pointers-to-void-pointers
> or even using pointers as integer values.
>
> Another common problem is extremely convoluted internal tracking, flow
> installation, flow replay, and cleanup code. This infrastructure is usually
> done in an ad-hoc manner that has a lot of boilerplate.
>
> These issues can be corrected, but because of how much code there is to the
> current infrastructure and how tightly coupled it is, it would be easier to
> just build new one from scratch, and gradually migrate all engines to use
> it. This patch is intended as a first step towards that goal, and defines
> both common data types to be used by all rte_flow parsers, as well as the
> interaction model that is to be followed by all drivers.
>
> We define a set of structures that will represent:
>
> - Defined rte_flow parsing interaction model and code flow (ops struct)
> - Defined memory allocation and ownership model for all engines
> - Scratch space format for all engines (variably allocated typed struct)
> - Flow rule format for all engines (variably allocated typed struct)
> - Engine definitions that are compatible with secondary process model
> - Implementations of common rte_flow operations
> - Various supporting infrastructure for parser customization, e.g. hooks
> - Support for using custom allocation (e.g. for mempool-based alloc)
> - Support for replaying all flows to restore HW state
> - Support for removing all flows without modifying HW state
>
> The design intent is heavily documented right inside the header and is to
> be considered authoritative design document for how to build rte_flow
> parsers for Intel Ethernet drivers going forward.
>
> Signed-off-by: Anatoly Burakov<anatoly.burakov at intel.com>
> ---
<snip>
> +/* enable all engines for a specific driver instance - caller must serialize initialization */
> +static inline int
> +ci_flow_engine_conf_init(struct ci_flow_engine_conf *engine_conf,
> + const struct ci_flow_engine_list *engine_list,
> + struct rte_eth_dev_data *dev_data)
> +{
> + struct ci_flow_engine_ref engine_ref;
> +
> + /* reject invalid configuration */
> + if (engine_conf == NULL || engine_list == NULL || dev_data == NULL)
> + return -1;
return -EINVAL?
> +
> + /* init the lock */
> + rte_rwlock_init(&engine_conf->config_lock);
> +
<snip>
> +/* parse a flow using a specific engine - caller must hold config lock */
> +static inline int
> +ci_flow_parse(const struct ci_flow_engine_conf *engine_conf,
> + const struct ci_flow_engine *engine,
> + const struct rte_flow_attr *attr,
> + const struct rte_flow_item pattern[],
> + const struct rte_flow_action actions[],
> + struct ci_flow *flow,
> + struct rte_flow_error *error)
> +{
> + enum ci_match_type match_type;
> + struct ci_flow_engine_ctx *ctx;
> + int ret = 0;
> +
> + /*
> + * Determine the type of matching we are going to perform based on the
> + * presence of pattern graph and pattern_parse callback. The logic is as
> + * follows:
> + *
> + * - if graph but no callback, match against graph
> + *
> + * Expected default case: pattern matching is graph based, no special
> + * handling for any pattern items.
> + *
> + * - if both graph and callback, match against callback + graph
> + *
> + * Preprocessor case, i.e. preprocess the pattern with the callback
> + * before handling the matching to the graph engine. The assumption is
> + * that the graph will be set up with a proper ignore list to skip over
> + * nodes that weren't meant for the graph processing.
> + *
> + * - if no graph but callback, match against callback
> + *
> + * Fully custom pattern parsing case.
> + *
> + * - if no graph and no callback, match against empty graph
> + *
> + * "Pattern is not meaningful" case, for engines that do not care about
> + * the pattern at all. A default matching behavior against empty
> + * patterns is provided (i.e. allow NULL pattern, and allow END or ANY
> + * -> END patterns). Note that this is not the same as ignoring pattern
> + * entirely: the engine will still reject patterns that are not empty.
> + */
> + match_type = engine->graph == NULL ?
> + (engine->ops->pattern_parse == NULL ? CI_MATCH_EMPTY : CI_MATCH_CALLBACK) :
> + (engine->ops->pattern_parse == NULL ? CI_MATCH_GRAPH : CI_MATCH_ALL);
> +
> + CI_DRV_LOG(DEBUG, "engine '%s': parsing flow", engine->name);
> +
> + /* allocate context */
> + ctx = (struct ci_flow_engine_ctx *)calloc(1,
> + RTE_MAX(engine->ctx_size, sizeof(struct ci_flow_engine_ctx)));
> + if (ctx == NULL) {
> + return rte_flow_error_set(error, ENOMEM,
> + RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
> + "Failed to allocate memory for rule engine context");
> + }
> + ctx->dev_data = engine_conf->dev_data;
> + ctx->attr = attr;
> + ctx->pattern = pattern;
> + ctx->actions = actions;
> + flow->dev_data = engine_conf->dev_data;
it was set in ci_flow_alloc()
> +
> + /* parse flow parameters */
> + ret = engine->ops->ctx_init(actions, attr, ctx, error);
<snip>
> +
> + /* no engine could handle this flow */
> + CI_DRV_LOG(DEBUG, "no engine accepted the flow");
> + flow = NULL;
> + rte_flow_error_set(error, ENOTSUP,
> + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
> + "No flow engine could handle the requested flow");
This unconditionally rewrites error string, don't we want to keep the
last reason why flow wasn't created? Same is applied for validate.
> +unlock:
> + rte_rwlock_write_unlock(&engine_conf->config_lock);
> +
> + return (struct rte_flow *)flow;
> +}
> +
<snip>
--
Regards,
Vladimir
More information about the dev
mailing list