@@ -474,6 +474,10 @@ mlx5_alloc_shared_dr(struct mlx5_priv *priv)
err = mlx5_alloc_table_hash_list(priv);
if (err)
goto error;
+ sh->default_miss_action =
+ mlx5_glue->dr_create_flow_action_default_miss();
+ if (!sh->default_miss_action)
+ DRV_LOG(WARNING, "Default miss action is not supported.");
/* The resources below are only valid with DV support. */
#ifdef HAVE_IBV_FLOW_DV_SUPPORT
/* Init shared flex parsers list, no need lcore_share */
@@ -600,10 +604,6 @@ mlx5_alloc_shared_dr(struct mlx5_priv *priv)
__mlx5_discovery_misc5_cap(priv);
#endif /* HAVE_MLX5DV_DR */
- sh->default_miss_action =
- mlx5_glue->dr_create_flow_action_default_miss();
- if (!sh->default_miss_action)
- DRV_LOG(WARNING, "Default miss action is not supported.");
LIST_INIT(&sh->shared_rxqs);
return 0;
error:
@@ -1860,6 +1860,7 @@ struct mlx5_priv {
struct rte_flow_template_table *hw_esw_sq_miss_tbl;
struct rte_flow_template_table *hw_esw_zero_tbl;
struct rte_flow_template_table *hw_tx_meta_cpy_tbl;
+ struct rte_flow_template_table *hw_lacp_rx_tbl;
struct rte_flow_pattern_template *hw_tx_repr_tagging_pt;
struct rte_flow_actions_template *hw_tx_repr_tagging_at;
struct rte_flow_template_table *hw_tx_repr_tagging_tbl;
@@ -2880,6 +2880,7 @@ int mlx5_flow_hw_esw_destroy_sq_miss_flow(struct rte_eth_dev *dev,
int mlx5_flow_hw_esw_create_default_jump_flow(struct rte_eth_dev *dev);
int mlx5_flow_hw_create_tx_default_mreg_copy_flow(struct rte_eth_dev *dev);
int mlx5_flow_hw_tx_repr_matching_flow(struct rte_eth_dev *dev, uint32_t sqn, bool external);
+int mlx5_flow_hw_lacp_rx_flow(struct rte_eth_dev *dev);
int mlx5_flow_actions_validate(struct rte_eth_dev *dev,
const struct rte_flow_actions_template_attr *attr,
const struct rte_flow_action actions[],
@@ -2454,6 +2454,15 @@ __flow_hw_actions_translate(struct rte_eth_dev *dev,
dr_pos))
goto err;
break;
+ case MLX5_RTE_FLOW_ACTION_TYPE_DEFAULT_MISS:
+ /* Internal, can be skipped. */
+ if (!!attr->group) {
+ DRV_LOG(ERR, "DEFAULT MISS action is only"
+ " supported in root table.");
+ goto err;
+ }
+ acts->rule_acts[dr_pos].action = priv->hw_def_miss;
+ break;
case RTE_FLOW_ACTION_TYPE_END:
actions_end = true;
break;
@@ -5531,6 +5540,34 @@ flow_hw_validate_action_push_vlan(struct rte_eth_dev *dev,
#undef X_FIELD
}
+static int
+flow_hw_validate_action_default_miss(struct rte_eth_dev *dev,
+ const struct rte_flow_actions_template_attr *attr,
+ uint64_t action_flags,
+ struct rte_flow_error *error)
+{
+ /*
+ * The private DEFAULT_MISS action is used internally for LACP in control
+ * flows. So this validation can be ignored. It can be kept right now since
+ * the validation will be done only once.
+ */
+ struct mlx5_priv *priv = dev->data->dev_private;
+
+ if (!attr->ingress || attr->egress || attr->transfer)
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+ "DEFAULT MISS is only supported in ingress.");
+ if (!priv->hw_def_miss)
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+ "DEFAULT MISS action does not exist.");
+ if (action_flags & MLX5_FLOW_FATE_ACTIONS)
+ return rte_flow_error_set(error, EINVAL,
+ RTE_FLOW_ERROR_TYPE_ACTION, NULL,
+ "DEFAULT MISS should be the only termination.");
+ return 0;
+}
+
static int
mlx5_flow_hw_actions_validate(struct rte_eth_dev *dev,
const struct rte_flow_actions_template_attr *attr,
@@ -5568,7 +5605,7 @@ mlx5_flow_hw_actions_validate(struct rte_eth_dev *dev,
RTE_FLOW_ERROR_TYPE_ACTION,
action,
"mask type does not match action type");
- switch (action->type) {
+ switch ((int)action->type) {
case RTE_FLOW_ACTION_TYPE_VOID:
break;
case RTE_FLOW_ACTION_TYPE_INDIRECT_LIST:
@@ -5735,6 +5772,13 @@ mlx5_flow_hw_actions_validate(struct rte_eth_dev *dev,
case RTE_FLOW_ACTION_TYPE_END:
actions_end = true;
break;
+ case MLX5_RTE_FLOW_ACTION_TYPE_DEFAULT_MISS:
+ ret = flow_hw_validate_action_default_miss(dev, attr,
+ action_flags, error);
+ if (ret < 0)
+ return ret;
+ action_flags |= MLX5_FLOW_ACTION_DEFAULT_MISS;
+ break;
default:
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION,
@@ -5754,8 +5798,7 @@ flow_hw_actions_validate(struct rte_eth_dev *dev,
const struct rte_flow_action masks[],
struct rte_flow_error *error)
{
- return mlx5_flow_hw_actions_validate(dev, attr, actions, masks, NULL,
- error);
+ return mlx5_flow_hw_actions_validate(dev, attr, actions, masks, NULL, error);
}
@@ -5907,7 +5950,7 @@ flow_hw_dr_actions_template_create(struct rte_eth_dev *dev,
if (curr_off >= MLX5_HW_MAX_ACTS)
goto err_actions_num;
- switch (at->actions[i].type) {
+ switch ((int)at->actions[i].type) {
case RTE_FLOW_ACTION_TYPE_VOID:
break;
case RTE_FLOW_ACTION_TYPE_INDIRECT_LIST:
@@ -5998,6 +6041,10 @@ flow_hw_dr_actions_template_create(struct rte_eth_dev *dev,
}
at->dr_off[i] = cnt_off;
break;
+ case MLX5_RTE_FLOW_ACTION_TYPE_DEFAULT_MISS:
+ at->dr_off[i] = curr_off;
+ action_types[curr_off++] = MLX5DR_ACTION_TYP_MISS;
+ break;
default:
type = mlx5_hw_dr_action_types[at->actions[i].type];
at->dr_off[i] = curr_off;
@@ -7773,6 +7820,42 @@ flow_hw_create_tx_default_mreg_copy_pattern_template(struct rte_eth_dev *dev,
return flow_hw_pattern_template_create(dev, &tx_pa_attr, eth_all, error);
}
+/*
+ * Creating a flow pattern template with all LACP packets matching, only for NIC
+ * ingress domain.
+ *
+ * @param dev
+ * Pointer to Ethernet device.
+ * @param error
+ * Pointer to error structure.
+ *
+ * @return
+ * Pointer to flow pattern template on success, NULL otherwise.
+ */
+static struct rte_flow_pattern_template *
+flow_hw_create_lacp_rx_pattern_template(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+ struct rte_flow_pattern_template_attr pa_attr = {
+ .relaxed_matching = 0,
+ .ingress = 1,
+ };
+ struct rte_flow_item_eth lacp_mask = {
+ .dst.addr_bytes = "\x00\x00\x00\x00\x00\x00",
+ .src.addr_bytes = "\x00\x00\x00\x00\x00\x00",
+ .type = 0xFFFF,
+ };
+ struct rte_flow_item eth_all[] = {
+ [0] = {
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ .mask = &lacp_mask,
+ },
+ [1] = {
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ };
+ return flow_hw_pattern_template_create(dev, &pa_attr, eth_all, error);
+}
+
/**
* Creates a flow actions template with modify field action and masked jump action.
* Modify field action sets the least significant bit of REG_C_0 (usable by user-space)
@@ -8042,6 +8125,38 @@ flow_hw_create_tx_default_mreg_copy_actions_template(struct rte_eth_dev *dev,
masks, error);
}
+/*
+ * Creating an actions template to use default miss to re-route packets to the
+ * kernel driver stack.
+ * On root table, only DEFAULT_MISS action can be used.
+ *
+ * @param dev
+ * Pointer to Ethernet device.
+ * @param error
+ * Pointer to error structure.
+ *
+ * @return
+ * Pointer to flow actions template on success, NULL otherwise.
+ */
+static struct rte_flow_actions_template *
+flow_hw_create_lacp_rx_actions_template(struct rte_eth_dev *dev, struct rte_flow_error *error)
+{
+ struct rte_flow_actions_template_attr act_attr = {
+ .ingress = 1,
+ };
+ const struct rte_flow_action actions[] = {
+ [0] = {
+ .type = (enum rte_flow_action_type)
+ MLX5_RTE_FLOW_ACTION_TYPE_DEFAULT_MISS,
+ },
+ [1] = {
+ .type = RTE_FLOW_ACTION_TYPE_END,
+ },
+ };
+
+ return flow_hw_actions_template_create(dev, &act_attr, actions, actions, error);
+}
+
/**
* Creates a control flow table used to transfer traffic from E-Switch Manager
* and TX queues from group 0 to group 1.
@@ -8200,6 +8315,43 @@ flow_hw_create_ctrl_jump_table(struct rte_eth_dev *dev,
return flow_hw_table_create(dev, &cfg, &it, 1, &at, 1, error);
}
+/*
+ * Create a table on the root group to for the LACP traffic redirecting.
+ *
+ * @param dev
+ * Pointer to Ethernet device.
+ * @param it
+ * Pointer to flow pattern template.
+ * @param at
+ * Pointer to flow actions template.
+ *
+ * @return
+ * Pointer to flow table on success, NULL otherwise.
+ */
+static struct rte_flow_template_table *
+flow_hw_create_lacp_rx_table(struct rte_eth_dev *dev,
+ struct rte_flow_pattern_template *it,
+ struct rte_flow_actions_template *at,
+ struct rte_flow_error *error)
+{
+ struct rte_flow_template_table_attr attr = {
+ .flow_attr = {
+ .group = 0,
+ .priority = 0,
+ .ingress = 1,
+ .egress = 0,
+ .transfer = 0,
+ },
+ .nb_flows = 1,
+ };
+ struct mlx5_flow_template_table_cfg cfg = {
+ .attr = attr,
+ .external = false,
+ };
+
+ return flow_hw_table_create(dev, &cfg, &it, 1, &at, 1, error);
+}
+
/**
* Creates a set of flow tables used to create control flows used
* when E-Switch is engaged.
@@ -8220,10 +8372,12 @@ flow_hw_create_ctrl_tables(struct rte_eth_dev *dev, struct rte_flow_error *error
struct rte_flow_pattern_template *regc_sq_items_tmpl = NULL;
struct rte_flow_pattern_template *port_items_tmpl = NULL;
struct rte_flow_pattern_template *tx_meta_items_tmpl = NULL;
+ struct rte_flow_pattern_template *lacp_rx_items_tmpl = NULL;
struct rte_flow_actions_template *regc_jump_actions_tmpl = NULL;
struct rte_flow_actions_template *port_actions_tmpl = NULL;
struct rte_flow_actions_template *jump_one_actions_tmpl = NULL;
struct rte_flow_actions_template *tx_meta_actions_tmpl = NULL;
+ struct rte_flow_actions_template *lacp_rx_actions_tmpl = NULL;
uint32_t xmeta = priv->sh->config.dv_xmeta_en;
uint32_t repr_matching = priv->sh->config.repr_matching;
int ret;
@@ -8319,6 +8473,28 @@ flow_hw_create_ctrl_tables(struct rte_eth_dev *dev, struct rte_flow_error *error
goto err;
}
}
+ /* Create LACP default miss table. */
+ if (!priv->sh->config.lacp_by_user && priv->pf_bond >= 0) {
+ lacp_rx_items_tmpl = flow_hw_create_lacp_rx_pattern_template(dev, error);
+ if (!lacp_rx_items_tmpl) {
+ DRV_LOG(ERR, "port %u failed to create pattern template"
+ " for LACP Rx traffic", dev->data->port_id);
+ goto err;
+ }
+ lacp_rx_actions_tmpl = flow_hw_create_lacp_rx_actions_template(dev, error);
+ if (!lacp_rx_actions_tmpl) {
+ DRV_LOG(ERR, "port %u failed to create actions template"
+ " for LACP Rx traffic", dev->data->port_id);
+ goto err;
+ }
+ priv->hw_lacp_rx_tbl = flow_hw_create_lacp_rx_table(dev, lacp_rx_items_tmpl,
+ lacp_rx_actions_tmpl, error);
+ if (!priv->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:
/* Do not overwrite the rte_errno. */
@@ -8327,6 +8503,10 @@ flow_hw_create_ctrl_tables(struct rte_eth_dev *dev, struct rte_flow_error *error
ret = rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL,
"Failed to create control tables.");
+ if (priv->hw_tx_meta_cpy_tbl) {
+ flow_hw_table_destroy(dev, priv->hw_tx_meta_cpy_tbl, NULL);
+ priv->hw_tx_meta_cpy_tbl = NULL;
+ }
if (priv->hw_esw_zero_tbl) {
flow_hw_table_destroy(dev, priv->hw_esw_zero_tbl, NULL);
priv->hw_esw_zero_tbl = NULL;
@@ -8339,6 +8519,8 @@ flow_hw_create_ctrl_tables(struct rte_eth_dev *dev, struct rte_flow_error *error
flow_hw_table_destroy(dev, priv->hw_esw_sq_miss_root_tbl, NULL);
priv->hw_esw_sq_miss_root_tbl = NULL;
}
+ if (lacp_rx_actions_tmpl)
+ flow_hw_actions_template_destroy(dev, lacp_rx_actions_tmpl, NULL);
if (tx_meta_actions_tmpl)
flow_hw_actions_template_destroy(dev, tx_meta_actions_tmpl, NULL);
if (jump_one_actions_tmpl)
@@ -8347,6 +8529,8 @@ flow_hw_create_ctrl_tables(struct rte_eth_dev *dev, struct rte_flow_error *error
flow_hw_actions_template_destroy(dev, port_actions_tmpl, NULL);
if (regc_jump_actions_tmpl)
flow_hw_actions_template_destroy(dev, regc_jump_actions_tmpl, NULL);
+ if (lacp_rx_items_tmpl)
+ flow_hw_pattern_template_destroy(dev, lacp_rx_items_tmpl, NULL);
if (tx_meta_items_tmpl)
flow_hw_pattern_template_destroy(dev, tx_meta_items_tmpl, NULL);
if (port_items_tmpl)
@@ -8998,6 +9182,7 @@ flow_hw_configure(struct rte_eth_dev *dev,
struct rte_flow_queue_attr ctrl_queue_attr = {0};
bool is_proxy = !!(priv->sh->config.dv_esw_en && priv->master);
int ret = 0;
+ uint32_t action_flags;
if (!port_attr || !nb_queue || !queue_attr) {
rte_errno = EINVAL;
@@ -9229,12 +9414,21 @@ flow_hw_configure(struct rte_eth_dev *dev,
if (ret)
goto err;
}
+ /*
+ * DEFAULT_MISS action have different behaviors in different domains.
+ * In FDB, it will steering the packets to the E-switch manager.
+ * In NIC Rx root, it will steering the packet to the kernel driver stack.
+ * An action with all bits set in the flag can be created and the HWS
+ * layer will translate it properly when being used in different rules.
+ */
+ action_flags = MLX5DR_ACTION_FLAG_ROOT_RX | MLX5DR_ACTION_FLAG_HWS_RX |
+ MLX5DR_ACTION_FLAG_ROOT_TX | MLX5DR_ACTION_FLAG_HWS_TX;
+ if (is_proxy)
+ action_flags |= (MLX5DR_ACTION_FLAG_ROOT_FDB | MLX5DR_ACTION_FLAG_HWS_FDB);
+ priv->hw_def_miss = mlx5dr_action_create_default_miss(priv->dr_ctx, action_flags);
+ if (!priv->hw_def_miss)
+ goto err;
if (is_proxy) {
- /* Only supported on proxy port. */
- priv->hw_def_miss = mlx5dr_action_create_default_miss
- (priv->dr_ctx, MLX5DR_ACTION_FLAG_HWS_FDB);
- if (!priv->hw_def_miss)
- goto err;
ret = flow_hw_create_vport_actions(priv);
if (ret) {
rte_flow_error_set(error, -ret, RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
@@ -11956,6 +12150,43 @@ mlx5_flow_hw_tx_repr_matching_flow(struct rte_eth_dev *dev, uint32_t sqn, bool e
items, 0, actions, 0, &flow_info, external);
}
+int
+mlx5_flow_hw_lacp_rx_flow(struct rte_eth_dev *dev)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ struct rte_flow_item_eth lacp_item = {
+ .type = RTE_BE16(RTE_ETHER_TYPE_SLOW),
+ };
+ struct rte_flow_item eth_lacp[] = {
+ [0] = {
+ .type = RTE_FLOW_ITEM_TYPE_ETH,
+ .spec = &lacp_item,
+ .mask = &lacp_item,
+ },
+ [1] = {
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ };
+ struct rte_flow_action miss_action[] = {
+ [0] = {
+ .type = (enum rte_flow_action_type)
+ MLX5_RTE_FLOW_ACTION_TYPE_DEFAULT_MISS,
+ },
+ [1] = {
+ .type = RTE_FLOW_ACTION_TYPE_END,
+ },
+ };
+ struct mlx5_hw_ctrl_flow_info flow_info = {
+ .type = MLX5_HW_CTRL_FLOW_TYPE_LACP_RX,
+ };
+
+ MLX5_ASSERT(priv->master);
+ if (!priv->dr_ctx || !priv->hw_lacp_rx_tbl)
+ return 0;
+ return flow_hw_create_ctrl_flow(dev, dev, priv->hw_lacp_rx_tbl, eth_lacp, 0,
+ miss_action, 0, &flow_info, false);
+}
+
static uint32_t
__calc_pattern_flags(const enum mlx5_flow_ctrl_rx_eth_pattern_type eth_pattern_type)
{
@@ -1524,6 +1524,9 @@ mlx5_traffic_enable_hws(struct rte_eth_dev *dev)
}
if (priv->isolated)
return 0;
+ if (!priv->sh->config.lacp_by_user && priv->pf_bond >= 0)
+ if (mlx5_flow_hw_lacp_rx_flow(dev))
+ goto error;
if (dev->data->promiscuous)
flags |= MLX5_CTRL_PROMISCUOUS;
if (dev->data->all_multicast)