[PATCH] net/mlx5: redirect LACP traffic for legacy E-Switch

Bing Zhao bingz at nvidia.com
Tue May 26 07:57:04 CEST 2026


Hi,

> -----Original Message-----
> From: Dariusz Sosnowski <dsosnowski at nvidia.com>
> Sent: Friday, May 15, 2026 8:37 PM
> To: Slava Ovsiienko <viacheslavo at nvidia.com>; Bing Zhao
> <bingz at nvidia.com>; Ori Kam <orika at nvidia.com>; Suanming Mou
> <suanmingm at nvidia.com>; Matan Azrad <matan at nvidia.com>
> Cc: dev at dpdk.org; stable at dpdk.org
> Subject: [PATCH] net/mlx5: redirect LACP traffic for legacy E-Switch
> 
> Offending patch fixed the LACP miss rule logic for NICs where switchdev is
> enabled. In this case, LACP miss rules should be inserted if and only if
> started port is a main port on the embedded switch.
> Side effect of that change was that LACP miss rules are not inserted when
> switchdev is disabled and legacy SR-IOV switch mode is used.
> 
> This patch addresses that:
> 
> - Fix the LACP rule insertion condition.
> - Move HWS table for LACP rule creation out of FDB rules,
>   so they can be created separately.
> 
> Fixes: 87e4384d2662 ("net/mlx5: fix condition of LACP miss flow")
> Cc: stable at dpdk.org
> 
> Signed-off-by: Dariusz Sosnowski <dsosnowski at nvidia.com>
> ---
>  drivers/net/mlx5/mlx5.h         |   1 +
>  drivers/net/mlx5/mlx5_flow.h    |  40 ++++++++-
>  drivers/net/mlx5/mlx5_flow_hw.c | 140 ++++++++++++++++++++++++--------
>  drivers/net/mlx5/mlx5_trigger.c |   7 +-
>  4 files changed, 145 insertions(+), 43 deletions(-)
> 
> diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h index
> 49a0c03544..ab5c76bfc4 100644
> --- a/drivers/net/mlx5/mlx5.h
> +++ b/drivers/net/mlx5/mlx5.h
> @@ -2040,6 +2040,7 @@ struct mlx5_priv {
>  	rte_spinlock_t hw_ctrl_lock;
>  	LIST_HEAD(hw_ctrl_flow, mlx5_ctrl_flow_entry) hw_ctrl_flows;
>  	LIST_HEAD(hw_ext_ctrl_flow, mlx5_ctrl_flow_entry) hw_ext_ctrl_flows;
> +	struct mlx5_flow_hw_lacp_miss *hw_lacp_miss; /* HWS LACP miss flow
> +tables */
>  	struct mlx5_flow_hw_ctrl_fdb *hw_ctrl_fdb; /* FDB control flow
> context */
>  	struct mlx5_flow_hw_ctrl_nic *hw_ctrl_nic; /* NIC control flow
> context */
>  	struct rte_flow_pattern_template *hw_tx_repr_tagging_pt; diff --git
> a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h index
> c9e72a33d6..3f5ba55bf9 100644
> --- a/drivers/net/mlx5/mlx5_flow.h
> +++ b/drivers/net/mlx5/mlx5_flow.h
> @@ -3031,6 +3031,13 @@ struct mlx5_flow_hw_ctrl_rx {
> 
> 	[MLX5_FLOW_HW_CTRL_RX_EXPANDED_RSS_MAX];
>  };
> 
> +/* Contains all templates and table required for redirecting LACP
> +traffic with HWS. */ struct mlx5_flow_hw_lacp_miss {
> +	struct rte_flow_pattern_template *lacp_rx_items_tmpl;
> +	struct rte_flow_actions_template *lacp_rx_actions_tmpl;
> +	struct rte_flow_template_table *hw_lacp_rx_tbl; };
> +
>  /* Contains all templates required for control flow rules in FDB with
> HWS. */  struct mlx5_flow_hw_ctrl_fdb {
>  	struct rte_flow_pattern_template *esw_mgr_items_tmpl; @@ -3042,9
> +3049,6 @@ struct mlx5_flow_hw_ctrl_fdb {
>  	struct rte_flow_pattern_template *port_items_tmpl;
>  	struct rte_flow_actions_template *jump_one_actions_tmpl;
>  	struct rte_flow_template_table *hw_esw_zero_tbl;
> -	struct rte_flow_pattern_template *lacp_rx_items_tmpl;
> -	struct rte_flow_actions_template *lacp_rx_actions_tmpl;
> -	struct rte_flow_template_table *hw_lacp_rx_tbl;
>  };
> 
>  struct mlx5_flow_hw_ctrl_nic {
> @@ -3735,6 +3739,36 @@ mlx5_indirect_list_handles_release(struct
> rte_eth_dev *dev);
> 
>  bool mlx5_flow_is_steering_disabled(void);
> 
> +/**
> + * Returns true if Rx control rule for LACP traffic is needed.
> + *
> + * mlx5 PMD needs to create a rule matching LACP traffic and forwarding
> it back to kernel if:
> + *
> + * - Underlying device is a bond interface.
> + * - User did not request to handle LACP traffic in user space.
> + *
> + * Creation of this rule is also controlled by the E-Switch mode:
> + *
> + * - It must be created in legacy mode.
> + * - It must be created only on proxy port in switchdev mode.
> + *
> + * @param[in] priv
> + *   Pointer to Ethernet device structure.
> + *
> + * @return
> + *   True if LACP rules must be created.
> + *   False otherwise.
> + */
> +static inline bool
> +mlx5_flow_lacp_miss_needed(struct rte_eth_dev *dev) {
> +	struct mlx5_priv *priv = dev->data->dev_private;
> +
> +	return !priv->sh->config.lacp_by_user &&
> +	    priv->pf_bond >= 0 &&
> +	    (!priv->sh->esw_mode || (priv->sh->esw_mode && priv->master)); }
> +
>  #ifdef HAVE_MLX5_HWS_SUPPORT
> 
>  #define MLX5_REPR_STC_MEMORY_LOG 11
> diff --git a/drivers/net/mlx5/mlx5_flow_hw.c
> b/drivers/net/mlx5/mlx5_flow_hw.c index b6bb9f12a6..c133230cb7 100644
> --- a/drivers/net/mlx5/mlx5_flow_hw.c
> +++ b/drivers/net/mlx5/mlx5_flow_hw.c
> @@ -10820,15 +10820,6 @@ flow_hw_cleanup_ctrl_fdb_tables(struct
> rte_eth_dev *dev)
>  	if (!priv->hw_ctrl_fdb)
>  		return;
>  	hw_ctrl_fdb = priv->hw_ctrl_fdb;
> -	/* Clean up templates used for LACP default miss table. */
> -	if (hw_ctrl_fdb->hw_lacp_rx_tbl)
> -		claim_zero(flow_hw_table_destroy(dev, hw_ctrl_fdb-
> >hw_lacp_rx_tbl, NULL));
> -	if (hw_ctrl_fdb->lacp_rx_actions_tmpl)
> -		claim_zero(flow_hw_actions_template_destroy(dev, hw_ctrl_fdb-
> >lacp_rx_actions_tmpl,
> -			   NULL));
> -	if (hw_ctrl_fdb->lacp_rx_items_tmpl)
> -		claim_zero(flow_hw_pattern_template_destroy(dev, hw_ctrl_fdb-
> >lacp_rx_items_tmpl,
> -			   NULL));
>  	/* Clean up templates used for default FDB jump rule. */
>  	if (hw_ctrl_fdb->hw_esw_zero_tbl)
>  		claim_zero(flow_hw_table_destroy(dev, hw_ctrl_fdb-
> >hw_esw_zero_tbl, NULL)); @@ -10898,6 +10889,99 @@
> flow_hw_create_lacp_rx_table(struct rte_eth_dev *dev,
>  	return flow_hw_table_create(dev, &cfg, &it, 1, &at, 1, error);  }
> 
> +/*
> + * Clean up templates and table used for redirecting LACP traffic to
> kernel.
> + *
> + * @param dev
> + *   Pointer to Ethernet device.
> + */
> +static void
> +flow_hw_cleanup_lacp_miss_tables(struct rte_eth_dev *dev) {
> +	struct mlx5_priv *priv = dev->data->dev_private;
> +	struct mlx5_flow_hw_lacp_miss *hw_lacp_miss;
> +
> +	if (priv->hw_lacp_miss == NULL)
> +		return;
> +
> +	hw_lacp_miss = priv->hw_lacp_miss;
> +
> +	if (hw_lacp_miss->hw_lacp_rx_tbl)
> +		claim_zero(flow_hw_table_destroy(dev, hw_lacp_miss-
> >hw_lacp_rx_tbl, NULL));
> +	if (hw_lacp_miss->lacp_rx_actions_tmpl)
> +		claim_zero(flow_hw_actions_template_destroy(dev,
> +							    hw_lacp_miss-
> >lacp_rx_actions_tmpl,
> +							    NULL));
> +	if (hw_lacp_miss->lacp_rx_items_tmpl)
> +		claim_zero(flow_hw_pattern_template_destroy(dev,
> +							    hw_lacp_miss-
> >lacp_rx_items_tmpl,
> +							    NULL));
> +
> +	mlx5_free(hw_lacp_miss);
> +	priv->hw_lacp_miss = NULL;
> +}
> +
> +/*
> + * Create templates and table for redirecting LACP traffic to kernel.
> + *
> + * LACP traffic redirection is needed whenever LACP bond is managed by
> the kernel.
> + * Required rule has a following structure:
> + *
> + * - ingress rule on root table
> + * - match EtherType 0x8809
> + * - action DEFAULT_MISS
> + *
> + * @param dev
> + *   Pointer to Ethernet device.
> + *
> + * @return
> + *   0 on success. Negative errno otherwise.
> + */
> +static int
> +flow_hw_create_lacp_miss_tables(struct rte_eth_dev *dev) {
> +	struct mlx5_priv *priv = dev->data->dev_private;
> +	struct mlx5_flow_hw_lacp_miss *hw_lacp_miss;
> +
> +	if (mlx5_flow_is_steering_disabled())
> +		return 0;
> +
> +	hw_lacp_miss = mlx5_malloc(MLX5_MEM_ZERO, sizeof(*hw_lacp_miss), 0,
> SOCKET_ID_ANY);
> +	if (!hw_lacp_miss) {
> +		DRV_LOG(ERR, "port %u Failed to allocate memory for LACP miss
> tables",
> +			dev->data->port_id);
> +		return -ENOMEM;
> +	}
> +	priv->hw_lacp_miss = hw_lacp_miss;
> +
> +	hw_lacp_miss->lacp_rx_items_tmpl =
> flow_hw_create_lacp_rx_pattern_template(dev, NULL);
> +	if (!hw_lacp_miss->lacp_rx_items_tmpl) {
> +		DRV_LOG(ERR, "port %u Failed to create pattern template for
> LACP Rx traffic",
> +			dev->data->port_id);
> +		goto error;
> +	}
> +	hw_lacp_miss->lacp_rx_actions_tmpl =
> flow_hw_create_lacp_rx_actions_template(dev, NULL);
> +	if (!hw_lacp_miss->lacp_rx_actions_tmpl) {
> +		DRV_LOG(ERR, "port %u Failed to create actions template for
> LACP Rx traffic",
> +			dev->data->port_id);
> +		goto error;
> +	}
> +	hw_lacp_miss->hw_lacp_rx_tbl =
> +		flow_hw_create_lacp_rx_table(dev, hw_lacp_miss-
> >lacp_rx_items_tmpl,
> +					     hw_lacp_miss->lacp_rx_actions_tmpl,
> NULL);
> +	if (!hw_lacp_miss->hw_lacp_rx_tbl) {
> +		DRV_LOG(ERR, "port %u Failed to create template table for LACP
> Rx traffic",
> +			dev->data->port_id);
> +		goto error;
> +	}
> +
> +	return 0;
> +
> +error:
> +	flow_hw_cleanup_lacp_miss_tables(dev);
> +	return -EINVAL;
> +}
> +
>  /**
>   * Creates a set of flow tables used to create control flows used
>   * when E-Switch is engaged.
> @@ -11000,31 +11084,6 @@ flow_hw_create_fdb_ctrl_tables(struct rte_eth_dev
> *dev, struct rte_flow_error *e
>  			goto err;
>  		}
>  	}
> -	/* Create LACP default miss table. */
> -	if (!priv->sh->config.lacp_by_user && priv->pf_bond >= 0 && priv-
> >master) {
> -		hw_ctrl_fdb->lacp_rx_items_tmpl =
> -				flow_hw_create_lacp_rx_pattern_template(dev,
> error);
> -		if (!hw_ctrl_fdb->lacp_rx_items_tmpl) {
> -			DRV_LOG(ERR, "port %u failed to create pattern template"
> -				" for LACP Rx traffic", dev->data->port_id);
> -			goto err;
> -		}
> -		hw_ctrl_fdb->lacp_rx_actions_tmpl =
> -				flow_hw_create_lacp_rx_actions_template(dev,
> error);
> -		if (!hw_ctrl_fdb->lacp_rx_actions_tmpl) {
> -			DRV_LOG(ERR, "port %u failed to create actions template"
> -				" for LACP Rx traffic", dev->data->port_id);
> -			goto err;
> -		}
> -		hw_ctrl_fdb->hw_lacp_rx_tbl = flow_hw_create_lacp_rx_table
> -				(dev, hw_ctrl_fdb->lacp_rx_items_tmpl,
> -				 hw_ctrl_fdb->lacp_rx_actions_tmpl, error);
> -		if (!hw_ctrl_fdb->hw_lacp_rx_tbl) {
> -			DRV_LOG(ERR, "port %u failed to create template table
> for"
> -				" for LACP Rx traffic", dev->data->port_id);
> -			goto err;
> -		}
> -	}
>  	return 0;
> 
>  err:
> @@ -11754,6 +11813,7 @@ __mlx5_flow_hw_resource_release(struct rte_eth_dev
> *dev, bool ctx_close)
> 
>  	mlx5_flow_hw_rxq_flag_set(dev, false);
>  	flow_hw_flush_all_ctrl_flows(dev);
> +	flow_hw_cleanup_lacp_miss_tables(dev);
>  	flow_hw_cleanup_ctrl_fdb_tables(dev);
>  	flow_hw_cleanup_ctrl_nic_tables(dev);
>  	flow_hw_cleanup_tx_repr_tagging(dev);
> @@ -12160,6 +12220,14 @@ __flow_hw_configure(struct rte_eth_dev *dev,
>  		if (ret)
>  			goto err;
>  	}
> +	if (mlx5_flow_lacp_miss_needed(dev)) {
> +		ret = flow_hw_create_lacp_miss_tables(dev);
> +		if (ret) {
> +			rte_flow_error_set(error, -ret,
> RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
> +					   "Unable to create LACP miss flow
> tables");
> +			goto err;
> +		}
> +	}
>  	if (is_proxy) {
>  		ret = flow_hw_create_vport_actions(priv);
>  		if (ret) {
> @@ -16234,10 +16302,10 @@ mlx5_flow_hw_lacp_rx_flow(struct rte_eth_dev
> *dev)
>  		.type = MLX5_CTRL_FLOW_TYPE_LACP_RX,
>  	};
> 
> -	if (!priv->dr_ctx || !priv->hw_ctrl_fdb || !priv->hw_ctrl_fdb-
> >hw_lacp_rx_tbl)
> +	if (!priv->dr_ctx || !priv->hw_lacp_miss ||
> +!priv->hw_lacp_miss->hw_lacp_rx_tbl)
>  		return 0;
>  	return flow_hw_create_ctrl_flow(dev, dev,
> -					priv->hw_ctrl_fdb->hw_lacp_rx_tbl,
> +					priv->hw_lacp_miss->hw_lacp_rx_tbl,
>  					eth_lacp, 0, miss_action, 0, &flow_info,
> false);  }
> 
> diff --git a/drivers/net/mlx5/mlx5_trigger.c
> b/drivers/net/mlx5/mlx5_trigger.c index a070aaecfd..32cd18717d 100644
> --- a/drivers/net/mlx5/mlx5_trigger.c
> +++ b/drivers/net/mlx5/mlx5_trigger.c
> @@ -1672,9 +1672,8 @@ mlx5_traffic_enable_hws(struct rte_eth_dev *dev)
>  	} else {
>  		DRV_LOG(INFO, "port %u FDB default rule is disabled", dev-
> >data->port_id);
>  	}
> -	if (!priv->sh->config.lacp_by_user && priv->pf_bond >= 0 && priv-
> >master)
> -		if (mlx5_flow_hw_lacp_rx_flow(dev))
> -			goto error;
> +	if (mlx5_flow_lacp_miss_needed(dev) &&
> mlx5_flow_hw_lacp_rx_flow(dev) != 0)
> +		goto error;
>  	if (priv->isolated)
>  		return 0;
>  	ret = mlx5_flow_hw_create_ctrl_rx_tables(dev);
> @@ -1796,7 +1795,7 @@ mlx5_traffic_enable(struct rte_eth_dev *dev)
>  		DRV_LOG(INFO, "port %u FDB default rule is disabled",
>  			dev->data->port_id);
>  	}
> -	if (!priv->sh->config.lacp_by_user && priv->pf_bond >= 0 && priv-
> >master) {
> +	if (mlx5_flow_lacp_miss_needed(dev)) {
>  		ret = mlx5_flow_lacp_miss(dev);
>  		if (ret)
>  			DRV_LOG(INFO, "port %u LACP rule cannot be created - "
> --
> 2.47.3

Acked-by: Bing Zhao <bingz at nvidia.com>


More information about the stable mailing list