[11/13] net/mlx5: add default flows for hairpin
Checks
Commit Message
When using hairpin all traffic from TX hairpin queues should jump
to dedecated table where matching can be done using regesters.
Signed-off-by: Ori Kam <orika@mellanox.com>
---
drivers/net/mlx5/mlx5.h | 2 ++
drivers/net/mlx5/mlx5_flow.c | 60 +++++++++++++++++++++++++++++++++++++++
drivers/net/mlx5/mlx5_flow.h | 9 ++++++
drivers/net/mlx5/mlx5_flow_dv.c | 63 +++++++++++++++++++++++++++++++++++++++--
drivers/net/mlx5/mlx5_trigger.c | 18 ++++++++++++
5 files changed, 150 insertions(+), 2 deletions(-)
Comments
> -----Original Message-----
> From: Ori Kam <orika@mellanox.com>
> Sent: Thursday, September 26, 2019 9:29
> To: Matan Azrad <matan@mellanox.com>; Shahaf Shuler
> <shahafs@mellanox.com>; Slava Ovsiienko <viacheslavo@mellanox.com>
> Cc: dev@dpdk.org; Ori Kam <orika@mellanox.com>; jingjing.wu@intel.com;
> stephen@networkplumber.org
> Subject: [PATCH 11/13] net/mlx5: add default flows for hairpin
>
> When using hairpin all traffic from TX hairpin queues should jump to
> dedecated table where matching can be done using regesters.
>
> Signed-off-by: Ori Kam <orika@mellanox.com>
Acked-by: Viacheslav Ovsiienko <viacheslavo@mellanox.com>
> ---
> drivers/net/mlx5/mlx5.h | 2 ++
> drivers/net/mlx5/mlx5_flow.c | 60
> +++++++++++++++++++++++++++++++++++++++
> drivers/net/mlx5/mlx5_flow.h | 9 ++++++
> drivers/net/mlx5/mlx5_flow_dv.c | 63
> +++++++++++++++++++++++++++++++++++++++--
> drivers/net/mlx5/mlx5_trigger.c | 18 ++++++++++++
> 5 files changed, 150 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h index
> 41eb35a..5f1a25d 100644
> --- a/drivers/net/mlx5/mlx5.h
> +++ b/drivers/net/mlx5/mlx5.h
> @@ -556,6 +556,7 @@ struct mlx5_flow_tbl_resource { };
>
> #define MLX5_MAX_TABLES UINT16_MAX
> +#define MLX5_HAIRPIN_TX_TABLE (UINT16_MAX - 1)
> #define MLX5_MAX_TABLES_FDB UINT16_MAX
>
> #define MLX5_DBR_PAGE_SIZE 4096 /* Must be >= 512. */ @@ -872,6
> +873,7 @@ int mlx5_dev_filter_ctrl(struct rte_eth_dev *dev, int
> mlx5_flow_start(struct rte_eth_dev *dev, struct mlx5_flows *list); void
> mlx5_flow_stop(struct rte_eth_dev *dev, struct mlx5_flows *list); int
> mlx5_flow_verify(struct rte_eth_dev *dev);
> +int mlx5_ctrl_flow_source_queue(struct rte_eth_dev *dev, uint32_t
> +queue);
> int mlx5_ctrl_flow_vlan(struct rte_eth_dev *dev,
> struct rte_flow_item_eth *eth_spec,
> struct rte_flow_item_eth *eth_mask,
> diff --git a/drivers/net/mlx5/mlx5_flow.c b/drivers/net/mlx5/mlx5_flow.c
> index 00afc18..33ed204 100644
> --- a/drivers/net/mlx5/mlx5_flow.c
> +++ b/drivers/net/mlx5/mlx5_flow.c
> @@ -2712,6 +2712,66 @@ struct rte_flow * }
>
> /**
> + * Enable default hairpin egress flow.
> + *
> + * @param dev
> + * Pointer to Ethernet device.
> + * @param queue
> + * The queue index.
> + *
> + * @return
> + * 0 on success, a negative errno value otherwise and rte_errno is set.
> + */
> +int
> +mlx5_ctrl_flow_source_queue(struct rte_eth_dev *dev,
> + uint32_t queue)
> +{
> + struct mlx5_priv *priv = dev->data->dev_private;
> + const struct rte_flow_attr attr = {
> + .egress = 1,
> + .priority = 0,
> + };
> + struct mlx5_rte_flow_item_tx_queue queue_spec = {
> + .queue = queue,
> + };
> + struct mlx5_rte_flow_item_tx_queue queue_mask = {
> + .queue = UINT32_MAX,
> + };
> + struct rte_flow_item items[] = {
> + {
> + .type = MLX5_RTE_FLOW_ITEM_TYPE_TX_QUEUE,
> + .spec = &queue_spec,
> + .last = NULL,
> + .mask = &queue_mask,
> + },
> + {
> + .type = RTE_FLOW_ITEM_TYPE_END,
> + },
> + };
> + struct rte_flow_action_jump jump = {
> + .group = MLX5_HAIRPIN_TX_TABLE,
> + };
> + struct rte_flow_action actions[2];
> + struct rte_flow *flow;
> + struct rte_flow_error error;
> +
> + actions[0].type = RTE_FLOW_ACTION_TYPE_JUMP;
> + actions[0].conf = &jump;
> + actions[1].type = RTE_FLOW_ACTION_TYPE_END;
> + flow = flow_list_create(dev, &priv->ctrl_flows,
> + &attr, items, actions, false, &error);
> + if (!flow) {
> + DRV_LOG(DEBUG,
> + "Failed to create ctrl flow: rte_errno(%d),"
> + " type(%d), message(%s)\n",
> + rte_errno, error.type,
> + error.message ? error.message : " (no stated
> reason)");
> + return -rte_errno;
> + }
> + return 0;
> +}
> +
> +/**
> * Enable a control flow configured from the control plane.
> *
> * @param dev
> diff --git a/drivers/net/mlx5/mlx5_flow.h b/drivers/net/mlx5/mlx5_flow.h
> index 1b14fb7..bb67380 100644
> --- a/drivers/net/mlx5/mlx5_flow.h
> +++ b/drivers/net/mlx5/mlx5_flow.h
> @@ -44,6 +44,7 @@ enum modify_reg {
> enum mlx5_rte_flow_item_type {
> MLX5_RTE_FLOW_ITEM_TYPE_END = INT_MIN,
> MLX5_RTE_FLOW_ITEM_TYPE_TAG,
> + MLX5_RTE_FLOW_ITEM_TYPE_TX_QUEUE,
> };
>
> /* Private rte flow actions. */
> @@ -64,6 +65,11 @@ struct mlx5_rte_flow_action_set_tag {
> rte_be32_t data;
> };
>
> +/* Matches on source queue. */
> +struct mlx5_rte_flow_item_tx_queue {
> + uint32_t queue;
> +};
> +
> /* Pattern outer Layer bits. */
> #define MLX5_FLOW_LAYER_OUTER_L2 (1u << 0) #define
> MLX5_FLOW_LAYER_OUTER_L3_IPV4 (1u << 1) @@ -102,6 +108,9 @@
> struct mlx5_rte_flow_action_set_tag { #define
> MLX5_FLOW_LAYER_IPV6_ENCAP (1u << 23) #define
> MLX5_FLOW_LAYER_NVGRE (1u << 24)
>
> +/* Queue items. */
> +#define MLX5_FLOW_ITEM_TX_QUEUE (1u << 25)
> +
> /* Outer Masks. */
> #define MLX5_FLOW_LAYER_OUTER_L3 \
> (MLX5_FLOW_LAYER_OUTER_L3_IPV4 |
> MLX5_FLOW_LAYER_OUTER_L3_IPV6) diff --git
> a/drivers/net/mlx5/mlx5_flow_dv.c b/drivers/net/mlx5/mlx5_flow_dv.c
> index dde0831..2b48680 100644
> --- a/drivers/net/mlx5/mlx5_flow_dv.c
> +++ b/drivers/net/mlx5/mlx5_flow_dv.c
> @@ -3357,7 +3357,9 @@ struct field_modify_info modify_tcp[] = {
> return ret;
> for (; items->type != RTE_FLOW_ITEM_TYPE_END; items++) {
> int tunnel = !!(item_flags & MLX5_FLOW_LAYER_TUNNEL);
> - switch (items->type) {
> + int type = items->type;
> +
> + switch (type) {
> case RTE_FLOW_ITEM_TYPE_VOID:
> break;
> case RTE_FLOW_ITEM_TYPE_PORT_ID:
> @@ -3518,6 +3520,9 @@ struct field_modify_info modify_tcp[] = {
> return ret;
> last_item = MLX5_FLOW_LAYER_ICMP6;
> break;
> + case MLX5_RTE_FLOW_ITEM_TYPE_TAG:
> + case MLX5_RTE_FLOW_ITEM_TYPE_TX_QUEUE:
> + break;
> default:
> return rte_flow_error_set(error, ENOTSUP,
>
> RTE_FLOW_ERROR_TYPE_ITEM,
> @@ -3526,11 +3531,12 @@ struct field_modify_info modify_tcp[] = {
> item_flags |= last_item;
> }
> for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
> + int type = actions->type;
> if (actions_n == MLX5_DV_MAX_NUMBER_OF_ACTIONS)
> return rte_flow_error_set(error, ENOTSUP,
>
> RTE_FLOW_ERROR_TYPE_ACTION,
> actions, "too many
> actions");
> - switch (actions->type) {
> + switch (type) {
> case RTE_FLOW_ACTION_TYPE_VOID:
> break;
> case RTE_FLOW_ACTION_TYPE_PORT_ID:
> @@ -3796,6 +3802,8 @@ struct field_modify_info modify_tcp[] = {
>
> MLX5_FLOW_ACTION_INC_TCP_ACK :
>
> MLX5_FLOW_ACTION_DEC_TCP_ACK;
> break;
> + case MLX5_RTE_FLOW_ACTION_TYPE_TAG:
> + break;
> default:
> return rte_flow_error_set(error, ENOTSUP,
>
> RTE_FLOW_ERROR_TYPE_ACTION,
> @@ -5291,6 +5299,51 @@ struct field_modify_info modify_tcp[] = { }
>
> /**
> + * Add Tx queue matcher
> + *
> + * @param[in] dev
> + * Pointer to the dev struct.
> + * @param[in, out] matcher
> + * Flow matcher.
> + * @param[in, out] key
> + * Flow matcher value.
> + * @param[in] item
> + * Flow pattern to translate.
> + * @param[in] inner
> + * Item is inner pattern.
> + */
> +static void
> +flow_dv_translate_item_tx_queue(struct rte_eth_dev *dev,
> + void *matcher, void *key,
> + const struct rte_flow_item *item)
> +{
> + const struct mlx5_rte_flow_item_tx_queue *queue_m;
> + const struct mlx5_rte_flow_item_tx_queue *queue_v;
> + void *misc_m =
> + MLX5_ADDR_OF(fte_match_param, matcher,
> misc_parameters);
> + void *misc_v =
> + MLX5_ADDR_OF(fte_match_param, key, misc_parameters);
> + struct mlx5_txq_ctrl *txq;
> + uint32_t queue;
> +
> +
> + queue_m = (const void *)item->mask;
> + if (!queue_m)
> + return;
> + queue_v = (const void *)item->spec;
> + if (!queue_v)
> + return;
> + txq = mlx5_txq_get(dev, queue_v->queue);
> + if (!txq)
> + return;
> + queue = txq->obj->sq->id;
> + MLX5_SET(fte_match_set_misc, misc_m, source_sqn, queue_m-
> >queue);
> + MLX5_SET(fte_match_set_misc, misc_v, source_sqn,
> + queue & queue_m->queue);
> + mlx5_txq_release(dev, queue_v->queue); }
> +
> +/**
> * Fill the flow with DV spec.
> *
> * @param[in] dev
> @@ -5866,6 +5919,12 @@ struct field_modify_info modify_tcp[] = {
> items);
> last_item = MLX5_FLOW_ITEM_TAG;
> break;
> + case MLX5_RTE_FLOW_ITEM_TYPE_TX_QUEUE:
> + flow_dv_translate_item_tx_queue(dev,
> match_mask,
> + match_value,
> + items);
> + last_item = MLX5_FLOW_ITEM_TX_QUEUE;
> + break;
> default:
> break;
> }
> diff --git a/drivers/net/mlx5/mlx5_trigger.c
> b/drivers/net/mlx5/mlx5_trigger.c index a4fcdb3..a476cd5 100644
> --- a/drivers/net/mlx5/mlx5_trigger.c
> +++ b/drivers/net/mlx5/mlx5_trigger.c
> @@ -396,6 +396,24 @@
> unsigned int j;
> int ret;
>
> + /*
> + * Hairpin txq default flow should be created no matter if it is
> + * isolation mode. Or else all the packets to be sent will be sent
> + * out directly without the TX flow actions, e.g. encapsulation.
> + */
> + for (i = 0; i != priv->txqs_n; ++i) {
> + struct mlx5_txq_ctrl *txq_ctrl = mlx5_txq_get(dev, i);
> + if (!txq_ctrl)
> + continue;
> + if (txq_ctrl->type == MLX5_TXQ_TYPE_HAIRPIN) {
> + ret = mlx5_ctrl_flow_source_queue(dev, i);
> + if (ret) {
> + mlx5_txq_release(dev, i);
> + goto error;
> + }
> + }
> + mlx5_txq_release(dev, i);
> + }
> if (priv->config.dv_esw_en && !priv->config.vf)
> if (!mlx5_flow_create_esw_table_zero_flow(dev))
> goto error;
> --
> 1.8.3.1
@@ -556,6 +556,7 @@ struct mlx5_flow_tbl_resource {
};
#define MLX5_MAX_TABLES UINT16_MAX
+#define MLX5_HAIRPIN_TX_TABLE (UINT16_MAX - 1)
#define MLX5_MAX_TABLES_FDB UINT16_MAX
#define MLX5_DBR_PAGE_SIZE 4096 /* Must be >= 512. */
@@ -872,6 +873,7 @@ int mlx5_dev_filter_ctrl(struct rte_eth_dev *dev,
int mlx5_flow_start(struct rte_eth_dev *dev, struct mlx5_flows *list);
void mlx5_flow_stop(struct rte_eth_dev *dev, struct mlx5_flows *list);
int mlx5_flow_verify(struct rte_eth_dev *dev);
+int mlx5_ctrl_flow_source_queue(struct rte_eth_dev *dev, uint32_t queue);
int mlx5_ctrl_flow_vlan(struct rte_eth_dev *dev,
struct rte_flow_item_eth *eth_spec,
struct rte_flow_item_eth *eth_mask,
@@ -2712,6 +2712,66 @@ struct rte_flow *
}
/**
+ * Enable default hairpin egress flow.
+ *
+ * @param dev
+ * Pointer to Ethernet device.
+ * @param queue
+ * The queue index.
+ *
+ * @return
+ * 0 on success, a negative errno value otherwise and rte_errno is set.
+ */
+int
+mlx5_ctrl_flow_source_queue(struct rte_eth_dev *dev,
+ uint32_t queue)
+{
+ struct mlx5_priv *priv = dev->data->dev_private;
+ const struct rte_flow_attr attr = {
+ .egress = 1,
+ .priority = 0,
+ };
+ struct mlx5_rte_flow_item_tx_queue queue_spec = {
+ .queue = queue,
+ };
+ struct mlx5_rte_flow_item_tx_queue queue_mask = {
+ .queue = UINT32_MAX,
+ };
+ struct rte_flow_item items[] = {
+ {
+ .type = MLX5_RTE_FLOW_ITEM_TYPE_TX_QUEUE,
+ .spec = &queue_spec,
+ .last = NULL,
+ .mask = &queue_mask,
+ },
+ {
+ .type = RTE_FLOW_ITEM_TYPE_END,
+ },
+ };
+ struct rte_flow_action_jump jump = {
+ .group = MLX5_HAIRPIN_TX_TABLE,
+ };
+ struct rte_flow_action actions[2];
+ struct rte_flow *flow;
+ struct rte_flow_error error;
+
+ actions[0].type = RTE_FLOW_ACTION_TYPE_JUMP;
+ actions[0].conf = &jump;
+ actions[1].type = RTE_FLOW_ACTION_TYPE_END;
+ flow = flow_list_create(dev, &priv->ctrl_flows,
+ &attr, items, actions, false, &error);
+ if (!flow) {
+ DRV_LOG(DEBUG,
+ "Failed to create ctrl flow: rte_errno(%d),"
+ " type(%d), message(%s)\n",
+ rte_errno, error.type,
+ error.message ? error.message : " (no stated reason)");
+ return -rte_errno;
+ }
+ return 0;
+}
+
+/**
* Enable a control flow configured from the control plane.
*
* @param dev
@@ -44,6 +44,7 @@ enum modify_reg {
enum mlx5_rte_flow_item_type {
MLX5_RTE_FLOW_ITEM_TYPE_END = INT_MIN,
MLX5_RTE_FLOW_ITEM_TYPE_TAG,
+ MLX5_RTE_FLOW_ITEM_TYPE_TX_QUEUE,
};
/* Private rte flow actions. */
@@ -64,6 +65,11 @@ struct mlx5_rte_flow_action_set_tag {
rte_be32_t data;
};
+/* Matches on source queue. */
+struct mlx5_rte_flow_item_tx_queue {
+ uint32_t queue;
+};
+
/* Pattern outer Layer bits. */
#define MLX5_FLOW_LAYER_OUTER_L2 (1u << 0)
#define MLX5_FLOW_LAYER_OUTER_L3_IPV4 (1u << 1)
@@ -102,6 +108,9 @@ struct mlx5_rte_flow_action_set_tag {
#define MLX5_FLOW_LAYER_IPV6_ENCAP (1u << 23)
#define MLX5_FLOW_LAYER_NVGRE (1u << 24)
+/* Queue items. */
+#define MLX5_FLOW_ITEM_TX_QUEUE (1u << 25)
+
/* Outer Masks. */
#define MLX5_FLOW_LAYER_OUTER_L3 \
(MLX5_FLOW_LAYER_OUTER_L3_IPV4 | MLX5_FLOW_LAYER_OUTER_L3_IPV6)
@@ -3357,7 +3357,9 @@ struct field_modify_info modify_tcp[] = {
return ret;
for (; items->type != RTE_FLOW_ITEM_TYPE_END; items++) {
int tunnel = !!(item_flags & MLX5_FLOW_LAYER_TUNNEL);
- switch (items->type) {
+ int type = items->type;
+
+ switch (type) {
case RTE_FLOW_ITEM_TYPE_VOID:
break;
case RTE_FLOW_ITEM_TYPE_PORT_ID:
@@ -3518,6 +3520,9 @@ struct field_modify_info modify_tcp[] = {
return ret;
last_item = MLX5_FLOW_LAYER_ICMP6;
break;
+ case MLX5_RTE_FLOW_ITEM_TYPE_TAG:
+ case MLX5_RTE_FLOW_ITEM_TYPE_TX_QUEUE:
+ break;
default:
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ITEM,
@@ -3526,11 +3531,12 @@ struct field_modify_info modify_tcp[] = {
item_flags |= last_item;
}
for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
+ int type = actions->type;
if (actions_n == MLX5_DV_MAX_NUMBER_OF_ACTIONS)
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION,
actions, "too many actions");
- switch (actions->type) {
+ switch (type) {
case RTE_FLOW_ACTION_TYPE_VOID:
break;
case RTE_FLOW_ACTION_TYPE_PORT_ID:
@@ -3796,6 +3802,8 @@ struct field_modify_info modify_tcp[] = {
MLX5_FLOW_ACTION_INC_TCP_ACK :
MLX5_FLOW_ACTION_DEC_TCP_ACK;
break;
+ case MLX5_RTE_FLOW_ACTION_TYPE_TAG:
+ break;
default:
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION,
@@ -5291,6 +5299,51 @@ struct field_modify_info modify_tcp[] = {
}
/**
+ * Add Tx queue matcher
+ *
+ * @param[in] dev
+ * Pointer to the dev struct.
+ * @param[in, out] matcher
+ * Flow matcher.
+ * @param[in, out] key
+ * Flow matcher value.
+ * @param[in] item
+ * Flow pattern to translate.
+ * @param[in] inner
+ * Item is inner pattern.
+ */
+static void
+flow_dv_translate_item_tx_queue(struct rte_eth_dev *dev,
+ void *matcher, void *key,
+ const struct rte_flow_item *item)
+{
+ const struct mlx5_rte_flow_item_tx_queue *queue_m;
+ const struct mlx5_rte_flow_item_tx_queue *queue_v;
+ void *misc_m =
+ MLX5_ADDR_OF(fte_match_param, matcher, misc_parameters);
+ void *misc_v =
+ MLX5_ADDR_OF(fte_match_param, key, misc_parameters);
+ struct mlx5_txq_ctrl *txq;
+ uint32_t queue;
+
+
+ queue_m = (const void *)item->mask;
+ if (!queue_m)
+ return;
+ queue_v = (const void *)item->spec;
+ if (!queue_v)
+ return;
+ txq = mlx5_txq_get(dev, queue_v->queue);
+ if (!txq)
+ return;
+ queue = txq->obj->sq->id;
+ MLX5_SET(fte_match_set_misc, misc_m, source_sqn, queue_m->queue);
+ MLX5_SET(fte_match_set_misc, misc_v, source_sqn,
+ queue & queue_m->queue);
+ mlx5_txq_release(dev, queue_v->queue);
+}
+
+/**
* Fill the flow with DV spec.
*
* @param[in] dev
@@ -5866,6 +5919,12 @@ struct field_modify_info modify_tcp[] = {
items);
last_item = MLX5_FLOW_ITEM_TAG;
break;
+ case MLX5_RTE_FLOW_ITEM_TYPE_TX_QUEUE:
+ flow_dv_translate_item_tx_queue(dev, match_mask,
+ match_value,
+ items);
+ last_item = MLX5_FLOW_ITEM_TX_QUEUE;
+ break;
default:
break;
}
@@ -396,6 +396,24 @@
unsigned int j;
int ret;
+ /*
+ * Hairpin txq default flow should be created no matter if it is
+ * isolation mode. Or else all the packets to be sent will be sent
+ * out directly without the TX flow actions, e.g. encapsulation.
+ */
+ for (i = 0; i != priv->txqs_n; ++i) {
+ struct mlx5_txq_ctrl *txq_ctrl = mlx5_txq_get(dev, i);
+ if (!txq_ctrl)
+ continue;
+ if (txq_ctrl->type == MLX5_TXQ_TYPE_HAIRPIN) {
+ ret = mlx5_ctrl_flow_source_queue(dev, i);
+ if (ret) {
+ mlx5_txq_release(dev, i);
+ goto error;
+ }
+ }
+ mlx5_txq_release(dev, i);
+ }
if (priv->config.dv_esw_en && !priv->config.vf)
if (!mlx5_flow_create_esw_table_zero_flow(dev))
goto error;