net/mlx5: support device control of representor matching

In some E-Switch use cases, applications want to receive all traffic
on a single port. Since currently, flow API does not provide a way to
match traffic forwarded to any port representor, this patch adds
support for controlling representor matching on ingress flow rules.

Representor matching is controlled through a new device argument
repr_matching_en.

- If representor matching is enabled (default setting),
  then each ingress pattern template has an implicit REPRESENTED_PORT
  item added. Flow rules based on this pattern template will match
  the vport associated with the port on which the rule is created.
- If representor matching is disabled, then there will be no implicit
  item added. As a result ingress flow rules will match traffic
  coming to any port, not only the port on which the flow rule is
  created.

Representor matching is enabled by default, to provide an expected
default behavior.

This patch enables egress flow rules on representors when E-Switch is
enabled in the following configurations:

- repr_matching_en=1 and dv_xmeta_en=4
- repr_matching_en=1 and dv_xmeta_en=0
- repr_matching_en=0 and dv_xmeta_en=0

When representor matching is enabled, the following logic is
implemented:

1. Creating an egress template table in group 0 for each port. These
   tables will hold default flow rules defined as follows:

      pattern SQ
      actions MODIFY_FIELD (set available bits in REG_C_0 to
                            vport_meta_tag)
              MODIFY_FIELD (copy REG_A to REG_C_1, only when
                            dv_xmeta_en == 4)
              JUMP (group 1)

2. Egress pattern templates created by an application have an implicit
   MLX5_RTE_FLOW_ITEM_TYPE_TAG item prepended to the pattern, which
   matches available bits of REG_C_0.

3. Egress flow rules created by an application have an implicit
   MLX5_RTE_FLOW_ITEM_TYPE_TAG item prepended to the pattern, which
   matches vport_meta_tag placed in available bits of REG_C_0.

4. Egress template tables created by an application, which are in
   group n, are placed in group n + 1.

5. Items and actions related to META are operating on REG_A when
   dv_xmeta_en == 0 or REG_C_1 when dv_xmeta_en == 4.

When representor matching is disabled and extended metadata is disabled,
no changes to the current logic are required.

Signed-off-by: Dariusz Sosnowski <dsosnowski@nvidia.com>
Acked-by: Viacheslav Ovsiienko <viacheslavo@nvidia.com>
This commit is contained in:
Dariusz Sosnowski 2022-10-20 18:41:51 +03:00 committed by Raslan Darawsheh
parent 26e1eaf2da
commit 483181f7b6
8 changed files with 794 additions and 166 deletions

View File

@ -1092,6 +1092,17 @@ for an additional list of options shared with other mlx5 drivers.
<Primary_PCI_BDF>,representor=pf[0,1]vf[0-2]
- ``repr_matching_en`` parameter [int]
- 0. If representor matching is disabled, then there will be no implicit
item added. As a result, ingress flow rules will match traffic
coming to any port, not only the port on which flow rule is created.
- 1. If representor matching is enabled (default setting),
then each ingress pattern template has an implicit REPRESENTED_PORT
item added. Flow rules based on this pattern template will match
the vport associated with port on which rule is created.
- ``max_dump_files_num`` parameter [int]
The maximum number of files per PMD entity that may be created for debug information.

View File

@ -1568,6 +1568,9 @@ mlx5_dev_spawn(struct rte_device *dpdk_dev,
if (priv->sh->config.dv_flow_en == 2) {
#ifdef HAVE_MLX5_HWS_SUPPORT
if (priv->sh->config.dv_esw_en) {
uint32_t usable_bits;
uint32_t required_bits;
if (priv->sh->dv_regc0_mask == UINT32_MAX) {
DRV_LOG(ERR, "E-Switch port metadata is required when using HWS "
"but it is disabled (configure it through devlink)");
@ -1580,6 +1583,14 @@ mlx5_dev_spawn(struct rte_device *dpdk_dev,
err = ENOTSUP;
goto error;
}
usable_bits = __builtin_popcount(priv->sh->dv_regc0_mask);
required_bits = __builtin_popcount(priv->vport_meta_mask);
if (usable_bits < required_bits) {
DRV_LOG(ERR, "Not enough bits available in reg_c[0] to provide "
"representor matching.");
err = ENOTSUP;
goto error;
}
}
if (priv->vport_meta_mask)
flow_hw_set_port_info(eth_dev);

View File

@ -181,6 +181,9 @@
/* HW steering counter's query interval. */
#define MLX5_HWS_CNT_CYCLE_TIME "svc_cycle_time"
/* Device parameter to control representor matching in ingress/egress flows with HWS. */
#define MLX5_REPR_MATCHING_EN "repr_matching_en"
/* Shared memory between primary and secondary processes. */
struct mlx5_shared_data *mlx5_shared_data;
@ -1283,6 +1286,8 @@ mlx5_dev_args_check_handler(const char *key, const char *val, void *opaque)
config->cnt_svc.service_core = tmp;
} else if (strcmp(MLX5_HWS_CNT_CYCLE_TIME, key) == 0) {
config->cnt_svc.cycle_time = tmp;
} else if (strcmp(MLX5_REPR_MATCHING_EN, key) == 0) {
config->repr_matching = !!tmp;
}
return 0;
}
@ -1321,6 +1326,7 @@ mlx5_shared_dev_ctx_args_config(struct mlx5_dev_ctx_shared *sh,
MLX5_FDB_DEFAULT_RULE_EN,
MLX5_HWS_CNT_SERVICE_CORE,
MLX5_HWS_CNT_CYCLE_TIME,
MLX5_REPR_MATCHING_EN,
NULL,
};
int ret = 0;
@ -1335,6 +1341,7 @@ mlx5_shared_dev_ctx_args_config(struct mlx5_dev_ctx_shared *sh,
config->fdb_def_rule = 1;
config->cnt_svc.cycle_time = MLX5_CNT_SVC_CYCLE_TIME_DEFAULT;
config->cnt_svc.service_core = rte_get_main_lcore();
config->repr_matching = 1;
if (mkvlist != NULL) {
/* Process parameters. */
ret = mlx5_kvargs_process(mkvlist, params,
@ -1368,6 +1375,11 @@ mlx5_shared_dev_ctx_args_config(struct mlx5_dev_ctx_shared *sh,
config->dv_xmeta_en);
config->dv_xmeta_en = MLX5_XMETA_MODE_LEGACY;
}
if (config->dv_flow_en != 2 && !config->repr_matching) {
DRV_LOG(DEBUG, "Disabling representor matching is valid only "
"when HW Steering is enabled.");
config->repr_matching = 1;
}
if (config->tx_pp && !sh->dev_cap.txpp_en) {
DRV_LOG(ERR, "Packet pacing is not supported.");
rte_errno = ENODEV;
@ -1411,6 +1423,7 @@ mlx5_shared_dev_ctx_args_config(struct mlx5_dev_ctx_shared *sh,
DRV_LOG(DEBUG, "\"allow_duplicate_pattern\" is %u.",
config->allow_duplicate_pattern);
DRV_LOG(DEBUG, "\"fdb_def_rule_en\" is %u.", config->fdb_def_rule);
DRV_LOG(DEBUG, "\"repr_matching_en\" is %u.", config->repr_matching);
return 0;
}

View File

@ -321,6 +321,7 @@ struct mlx5_sh_config {
} cnt_svc; /* configure for HW steering's counter's service. */
/* Allow/Prevent the duplicate rules pattern. */
uint32_t fdb_def_rule:1; /* Create FDB default jump rule */
uint32_t repr_matching:1; /* Enable implicit vport matching in HWS FDB. */
};
/* Structure for VF VLAN workaround. */
@ -371,6 +372,7 @@ struct mlx5_hw_q_job {
void *out_data;
} __rte_packed;
struct rte_flow_item_ethdev port_spec;
struct rte_flow_item_tag tag_spec;
} __rte_packed;
};
@ -1686,6 +1688,9 @@ 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_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;
struct mlx5_indexed_pool *flows[MLX5_FLOW_TYPE_MAXI];
/* RTE Flow rules. */
uint32_t ctrl_flows; /* Control flow rules. */

View File

@ -1123,7 +1123,11 @@ mlx5_flow_get_reg_id(struct rte_eth_dev *dev,
}
break;
case MLX5_METADATA_TX:
return REG_A;
if (config->dv_flow_en == 2 && config->dv_xmeta_en == MLX5_XMETA_MODE_META32_HWS) {
return REG_C_1;
} else {
return REG_A;
}
case MLX5_METADATA_FDB:
switch (config->dv_xmeta_en) {
case MLX5_XMETA_MODE_LEGACY:
@ -11351,7 +11355,7 @@ mlx5_flow_pick_transfer_proxy(struct rte_eth_dev *dev,
return 0;
}
}
return rte_flow_error_set(error, EINVAL,
return rte_flow_error_set(error, ENODEV,
RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
NULL, "unable to find a proxy port");
}

View File

@ -1210,12 +1210,18 @@ struct rte_flow_pattern_template {
struct rte_flow_pattern_template_attr attr;
struct mlx5dr_match_template *mt; /* mlx5 match template. */
uint64_t item_flags; /* Item layer flags. */
uint64_t orig_item_nb; /* Number of pattern items provided by the user (with END item). */
uint32_t refcnt; /* Reference counter. */
/*
* If true, then rule pattern should be prepended with
* represented_port pattern item.
*/
bool implicit_port;
/*
* If true, then rule pattern should be prepended with
* tag pattern item for representor matching.
*/
bool implicit_tag;
};
/* Flow action template struct. */
@ -2497,6 +2503,7 @@ int mlx5_flow_hw_esw_create_sq_miss_flow(struct rte_eth_dev *dev,
uint32_t sqn);
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);
int mlx5_flow_actions_validate(struct rte_eth_dev *dev,
const struct rte_flow_actions_template_attr *attr,
const struct rte_flow_action actions[],

File diff suppressed because it is too large Load Diff

View File

@ -1065,6 +1065,69 @@ mlx5_hairpin_get_peer_ports(struct rte_eth_dev *dev, uint16_t *peer_ports,
return ret;
}
#ifdef HAVE_MLX5_HWS_SUPPORT
/**
* Check if starting representor port is allowed.
*
* If transfer proxy port is configured for HWS, then starting representor port
* is allowed if and only if transfer proxy port is started as well.
*
* @param dev
* Pointer to Ethernet device structure.
*
* @return
* If stopping representor port is allowed, then 0 is returned.
* Otherwise rte_errno is set, and negative errno value is returned.
*/
static int
mlx5_hw_representor_port_allowed_start(struct rte_eth_dev *dev)
{
struct mlx5_priv *priv = dev->data->dev_private;
struct rte_eth_dev *proxy_dev;
struct mlx5_priv *proxy_priv;
uint16_t proxy_port_id = UINT16_MAX;
int ret;
MLX5_ASSERT(priv->sh->config.dv_flow_en == 2);
MLX5_ASSERT(priv->sh->config.dv_esw_en);
MLX5_ASSERT(priv->representor);
ret = rte_flow_pick_transfer_proxy(dev->data->port_id, &proxy_port_id, NULL);
if (ret) {
if (ret == -ENODEV)
DRV_LOG(ERR, "Starting representor port %u is not allowed. Transfer "
"proxy port is not available.", dev->data->port_id);
else
DRV_LOG(ERR, "Failed to pick transfer proxy for port %u (ret = %d)",
dev->data->port_id, ret);
return ret;
}
proxy_dev = &rte_eth_devices[proxy_port_id];
proxy_priv = proxy_dev->data->dev_private;
if (proxy_priv->dr_ctx == NULL) {
DRV_LOG(DEBUG, "Starting representor port %u is allowed, but default traffic flows"
" will not be created. Transfer proxy port must be configured"
" for HWS and started.",
dev->data->port_id);
return 0;
}
if (!proxy_dev->data->dev_started) {
DRV_LOG(ERR, "Failed to start port %u: transfer proxy (port %u) must be started",
dev->data->port_id, proxy_port_id);
rte_errno = EAGAIN;
return -rte_errno;
}
if (priv->sh->config.repr_matching && !priv->dr_ctx) {
DRV_LOG(ERR, "Failed to start port %u: with representor matching enabled, port "
"must be configured for HWS", dev->data->port_id);
rte_errno = EINVAL;
return -rte_errno;
}
return 0;
}
#endif
/**
* DPDK callback to start the device.
*
@ -1084,6 +1147,19 @@ mlx5_dev_start(struct rte_eth_dev *dev)
int fine_inline;
DRV_LOG(DEBUG, "port %u starting device", dev->data->port_id);
#ifdef HAVE_MLX5_HWS_SUPPORT
if (priv->sh->config.dv_flow_en == 2) {
/* If there is no E-Switch, then there are no start/stop order limitations. */
if (!priv->sh->config.dv_esw_en)
goto continue_dev_start;
/* If master is being started, then it is always allowed. */
if (priv->master)
goto continue_dev_start;
if (mlx5_hw_representor_port_allowed_start(dev))
return -rte_errno;
}
continue_dev_start:
#endif
fine_inline = rte_mbuf_dynflag_lookup
(RTE_PMD_MLX5_FINE_GRANULARITY_INLINE, NULL);
if (fine_inline >= 0)
@ -1248,6 +1324,53 @@ mlx5_dev_start(struct rte_eth_dev *dev)
return -rte_errno;
}
#ifdef HAVE_MLX5_HWS_SUPPORT
/**
* Check if stopping transfer proxy port is allowed.
*
* If transfer proxy port is configured for HWS, then it is allowed to stop it
* if and only if all other representor ports are stopped.
*
* @param dev
* Pointer to Ethernet device structure.
*
* @return
* If stopping transfer proxy port is allowed, then 0 is returned.
* Otherwise rte_errno is set, and negative errno value is returned.
*/
static int
mlx5_hw_proxy_port_allowed_stop(struct rte_eth_dev *dev)
{
struct mlx5_priv *priv = dev->data->dev_private;
bool representor_started = false;
uint16_t port_id;
MLX5_ASSERT(priv->sh->config.dv_flow_en == 2);
MLX5_ASSERT(priv->sh->config.dv_esw_en);
MLX5_ASSERT(priv->master);
/* If transfer proxy port was not configured for HWS, then stopping it is allowed. */
if (!priv->dr_ctx)
return 0;
MLX5_ETH_FOREACH_DEV(port_id, dev->device) {
const struct rte_eth_dev *port_dev = &rte_eth_devices[port_id];
const struct mlx5_priv *port_priv = port_dev->data->dev_private;
if (port_id != dev->data->port_id &&
port_priv->domain_id == priv->domain_id &&
port_dev->data->dev_started)
representor_started = true;
}
if (representor_started) {
DRV_LOG(INFO, "Failed to stop port %u: attached representor ports"
" must be stopped before stopping transfer proxy port",
dev->data->port_id);
rte_errno = EBUSY;
return -rte_errno;
}
return 0;
}
#endif
/**
* DPDK callback to stop the device.
*
@ -1261,6 +1384,21 @@ mlx5_dev_stop(struct rte_eth_dev *dev)
{
struct mlx5_priv *priv = dev->data->dev_private;
#ifdef HAVE_MLX5_HWS_SUPPORT
if (priv->sh->config.dv_flow_en == 2) {
/* If there is no E-Switch, then there are no start/stop order limitations. */
if (!priv->sh->config.dv_esw_en)
goto continue_dev_stop;
/* If representor is being stopped, then it is always allowed. */
if (priv->representor)
goto continue_dev_stop;
if (mlx5_hw_proxy_port_allowed_stop(dev)) {
dev->data->dev_started = 1;
return -rte_errno;
}
}
continue_dev_stop:
#endif
dev->data->dev_started = 0;
/* Prevent crashes when queues are still in use. */
dev->rx_pkt_burst = rte_eth_pkt_burst_dummy;
@ -1296,13 +1434,21 @@ static int
mlx5_traffic_enable_hws(struct rte_eth_dev *dev)
{
struct mlx5_priv *priv = dev->data->dev_private;
struct mlx5_sh_config *config = &priv->sh->config;
unsigned int i;
int ret;
if (priv->sh->config.dv_esw_en && priv->master) {
if (priv->sh->config.dv_xmeta_en == MLX5_XMETA_MODE_META32_HWS)
if (mlx5_flow_hw_create_tx_default_mreg_copy_flow(dev))
goto error;
/*
* With extended metadata enabled, the Tx metadata copy is handled by default
* Tx tagging flow rules, so default Tx flow rule is not needed. It is only
* required when representor matching is disabled.
*/
if (config->dv_esw_en &&
!config->repr_matching &&
config->dv_xmeta_en == MLX5_XMETA_MODE_META32_HWS &&
priv->master) {
if (mlx5_flow_hw_create_tx_default_mreg_copy_flow(dev))
goto error;
}
for (i = 0; i < priv->txqs_n; ++i) {
struct mlx5_txq_ctrl *txq = mlx5_txq_get(dev, i);
@ -1311,17 +1457,22 @@ mlx5_traffic_enable_hws(struct rte_eth_dev *dev)
if (!txq)
continue;
queue = mlx5_txq_get_sqn(txq);
if ((priv->representor || priv->master) &&
priv->sh->config.dv_esw_en) {
if ((priv->representor || priv->master) && config->dv_esw_en) {
if (mlx5_flow_hw_esw_create_sq_miss_flow(dev, queue)) {
mlx5_txq_release(dev, i);
goto error;
}
}
if (config->dv_esw_en && config->repr_matching) {
if (mlx5_flow_hw_tx_repr_matching_flow(dev, queue)) {
mlx5_txq_release(dev, i);
goto error;
}
}
mlx5_txq_release(dev, i);
}
if (priv->sh->config.fdb_def_rule) {
if ((priv->master || priv->representor) && priv->sh->config.dv_esw_en) {
if (config->fdb_def_rule) {
if ((priv->master || priv->representor) && config->dv_esw_en) {
if (!mlx5_flow_hw_esw_create_default_jump_flow(dev))
priv->fdb_def_rule = 1;
else