On 9/16/2026 1:18 PM, Anatoly Burakov wrote:
Use the new flow graph API and the common parsing framework to implement
flow parser for flow director.
The FDIR flow tracking is moved inside the new engine, and the FDIR code is
refactored to not mix software tracking with HW writes.
Signed-off-by: Anatoly Burakov<[email protected]>
---
<snip>
+ hash_handle = rte_hash_create(&hash_params);
- /* drop queue is always fixed */
- IXGBE_DEV_FDIR_CONF(eth_dev)->drop_queue = IXGBE_FDIR_DROP_QUEUE;
+ if (hash_handle == NULL) {
+ PMD_INIT_LOG(ERR, "Failed to create fdir hash table!");
+ rte_hash_free(hash_handle);
no need to free NULL
+ ci_refcount_release(&state->ref);
+ return NULL;
+ }
+
+ state->hash_handle = hash_handle;
+ state->mask_conf.mode = RTE_FDIR_MODE_NONE;
+
+ /* drop queue is always fixed */
+ IXGBE_DEV_PRIVATE_TO_FDIR_CONF(adapter)->drop_queue =
IXGBE_FDIR_DROP_QUEUE;
+ }
- return 0;
+ return state;
}
<snip>
+/* hardware loses its flow director setup across a stop/start cycle
*/ +void +ixgbe_fdir_hw_invalidate(struct rte_eth_dev *dev) { - int
ret; + struct ixgbe_adapter *adapter = IXGBE_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
+ struct ixgbe_fdir_state *state = &adapter->fdir_state;
- ret = rte_hash_lookup(fdir_info->hash_handle, (const void *)key);
- if (ret < 0)
- return NULL;
-
- return fdir_info->hash_map[ret];
+ if (state != NULL) {
nit: can the state be NULL at all?
+ state->mask_conf.hw_configured = false;
+ state->mask_conf.mask_programmed = false;
+ }
}
<snip>
+
+static int
+ixgbe_flow_fdir_flow_unregister(struct ci_flow *flow, struct rte_flow_error
*error)
+{
+ struct ixgbe_fdir_flow *fdir_flow = (struct ixgbe_fdir_flow *)flow;
+ struct ixgbe_fdir_priv *priv = flow->engine_priv;
+ struct ixgbe_fdir_state *state = priv->state;
+ int ret;
+
+ ret = ixgbe_fdir_table_del(state->hash_handle, fdir_flow);
+ if (ret == -ENOENT) {
+ return rte_flow_error_set(error, ENOENT,
+ RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+ "Flow director filter is missing from the filter
table");
+ }
+
+ ixgbe_fdir_mask_release(&state->mask_conf);
Do we also need to check if there are any other rules and, if there are
none, reset hw_configured as well as global_fdir_conf->mode, and restore
all HW state, such as the adjusted packet buffer (pballoc from
ixgbe_fdir_configure())?
I mean, should we do a similar cleanup to ixgbe_flow_fdir_flow_uninstall()?
We can end up in a situation where ixgbe_fdir_filter_program() fails
while installing the first flow, after the HW and masks have already
been configured. Based on the ci_flow_create() logic, if the
installation fails, only ci_flow_unregister() is called. This leaves us
in an inconsistent state where we have no flows installed, but FDIR
remains configured.
+
+ return 0;
+}
+
+static int
+ixgbe_flow_fdir_configure_hw(struct ixgbe_adapter *adapter,
+ struct ixgbe_fdir_mask_state *mask_state,
+ struct rte_flow_error *error)
+{
+ struct rte_eth_fdir_conf *global_fdir_conf =
IXGBE_DEV_PRIVATE_TO_FDIR_CONF(adapter);
+ struct rte_eth_fdir_conf local_fdir_conf = *global_fdir_conf;
+ int ret;
+
+ local_fdir_conf.mode = mask_state->mode;
+
+ ret = ixgbe_fdir_configure(adapter, &local_fdir_conf,
&mask_state->mask);
There is a problem with calling ixgbe_fdir_configure() multiple times.
This call changes the HW state:
/* ixgbe_fdir.c:ixgbe_fdir_configure() */
IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(0),
(IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(0)) - pbsize));
With every invocation, IXGBE_RXPBSIZE(0) is decreased by pbsize.
Could we configure FDIR only once during device start, without
configuring it with the first rule? Instead, for the first rule, we
could just call ixgbe_fdir_set_input_mask() / ixgbe_set_fdir_flex_conf().
+ if (ret != 0) {
+ return rte_flow_error_set(error, -ret,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "Failed to configure flow director");
+ }
+
+ global_fdir_conf->mode = mask_state->mode;
+ mask_state->hw_configured = true;
+
+ return 0;
+}
+
+static int
+ixgbe_flow_fdir_program_mask(struct ixgbe_adapter *adapter,
+ struct ixgbe_fdir_mask_state *mask_state,
+ struct rte_flow_error *error)
+{
+ struct ixgbe_hw_fdir_info *global_fdir_info =
IXGBE_DEV_PRIVATE_TO_FDIR_INFO(adapter);
+ int ret;
+
+ if (mask_state->mask.flex_bytes_mask != 0) {
+ ret = ixgbe_fdir_set_flexbytes_offset(adapter,
mask_state->flex_bytes_offset);
+ if (ret != 0) {
+ return rte_flow_error_set(error, -ret,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "Failed to set flex bytes offset");
+ }
+ }
+
+ ret = ixgbe_fdir_set_input_mask(adapter, &mask_state->mask,
mask_state->mode);
+ if (ret != 0) {
+ return rte_flow_error_set(error, -ret,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "Failed to set input mask");
+ }
+
+ /* record what is now in hardware for ixgbe_fdir_info_get() */
+ global_fdir_info->mask = mask_state->mask;
+ global_fdir_info->flex_bytes_offset = mask_state->flex_bytes_offset;
+ mask_state->mask_programmed = true;
+
+ return 0;
+}
+
+static int
+ixgbe_flow_fdir_flow_install(struct ci_flow *flow,
+ struct rte_flow_error *error)
+{
+ struct ixgbe_adapter *adapter =
IXGBE_DEV_PRIVATE_TO_ADAPTER(flow->dev_data->dev_private);
+ struct ixgbe_fdir_flow *fdir_flow = (struct ixgbe_fdir_flow *)flow;
+ struct ixgbe_fdir_priv *priv = flow->engine_priv;
+ struct ixgbe_fdir_mask_state *mask_conf = &priv->state->mask_conf;
+ int ret;
+
+ if (!mask_conf->hw_configured) {
+ ret = ixgbe_flow_fdir_configure_hw(adapter, mask_conf, error);
+ if (ret != 0)
+ return ret;
return rte_flow_error_set() instead?
+ }
+
+ if (!mask_conf->mask_programmed) {
the previous invocation of the ixgbe_flow_fdir_configure_hw() have
already programmed mask
(->ixgbe_fdir_configure->ixgbe_fdir_set_input_mask). Do we really need
to do this one more time?
+ ret = ixgbe_flow_fdir_program_mask(adapter, mask_conf, error);
+ if (ret != 0)
do we need to call ixgbe_fdir_hw_invalidate() on failure?
+ return ret;
+ }
+
+ ret = ixgbe_fdir_filter_program(adapter, &fdir_flow->rule,
fdir_flow->queue,
+ fdir_flow->fdircmd_flags, fdir_flow->fdirhash);
+ if (ret != 0) {
+ return rte_flow_error_set(error, -ret,
+ RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
+ "Failed to program flow director filter");
+ }
+
+ return 0;
+}
+
+static int
+ixgbe_flow_fdir_flow_uninstall(struct ci_flow *flow,
+ struct rte_flow_error *error)
+{
+ struct ixgbe_adapter *adapter =
IXGBE_DEV_PRIVATE_TO_ADAPTER(flow->dev_data->dev_private);
+ struct rte_eth_fdir_conf *global_fdir_conf =
IXGBE_DEV_PRIVATE_TO_FDIR_CONF(adapter);
+ struct ixgbe_fdir_flow *fdir_flow = (struct ixgbe_fdir_flow *)flow;
+ struct ixgbe_fdir_priv *priv = flow->engine_priv;
+ struct ixgbe_fdir_mask_state *mask_conf = &priv->state->mask_conf;
+ int ret;
+
+ ret = ixgbe_fdir_filter_clear(adapter, fdir_flow->fdirhash);
+ if (ret != 0) {
+ return rte_flow_error_set(error, -ret,
+ RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+ "Failed to remove flow director filter");
+ }
+
+ /* unregister has not run yet, so this filter is still counted */
+ if (mask_conf->ref.count > 1)
+ return 0;
+
+ mask_conf->hw_configured = false;
+ mask_conf->mask_programmed = false;
+ global_fdir_conf->mode = RTE_FDIR_MODE_NONE;
+
+ ret = ixgbe_fdir_reset_tables(adapter);
+ if (ret != 0) {
+ return rte_flow_error_set(error, -ret,
+ RTE_FLOW_ERROR_TYPE_HANDLE, NULL,
+ "Failed to reset flow director tables");
Would it be worth doing this in a separate function, such as
ixgbe_flow_fdir_configure_hw(), and also resetting the masks to their
defaults?
+ }
+
+ return 0;
+}
<snip>
--
Regards,
Vladimir