vdpa/mlx5: add basic steering configurations
[dpdk.git] / drivers / vdpa / mlx5 / mlx5_vdpa_steer.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright 2019 Mellanox Technologies, Ltd
3  */
4 #include <netinet/in.h>
5
6 #include <rte_malloc.h>
7 #include <rte_errno.h>
8 #include <rte_common.h>
9
10 #include <mlx5_common.h>
11
12 #include "mlx5_vdpa_utils.h"
13 #include "mlx5_vdpa.h"
14
15 int
16 mlx5_vdpa_steer_unset(struct mlx5_vdpa_priv *priv)
17 {
18         int ret __rte_unused;
19         unsigned i;
20
21         for (i = 0; i < RTE_DIM(priv->steer.rss); ++i) {
22                 if (priv->steer.rss[i].flow) {
23                         claim_zero(mlx5_glue->dv_destroy_flow
24                                                      (priv->steer.rss[i].flow));
25                         priv->steer.rss[i].flow = NULL;
26                 }
27                 if (priv->steer.rss[i].tir_action) {
28                         claim_zero(mlx5_glue->destroy_flow_action
29                                                (priv->steer.rss[i].tir_action));
30                         priv->steer.rss[i].tir_action = NULL;
31                 }
32                 if (priv->steer.rss[i].tir) {
33                         claim_zero(mlx5_devx_cmd_destroy
34                                                       (priv->steer.rss[i].tir));
35                         priv->steer.rss[i].tir = NULL;
36                 }
37                 if (priv->steer.rss[i].matcher) {
38                         claim_zero(mlx5_glue->dv_destroy_flow_matcher
39                                                   (priv->steer.rss[i].matcher));
40                         priv->steer.rss[i].matcher = NULL;
41                 }
42         }
43         if (priv->steer.tbl) {
44                 claim_zero(mlx5_glue->dr_destroy_flow_tbl(priv->steer.tbl));
45                 priv->steer.tbl = NULL;
46         }
47         if (priv->steer.domain) {
48                 claim_zero(mlx5_glue->dr_destroy_domain(priv->steer.domain));
49                 priv->steer.domain = NULL;
50         }
51         if (priv->steer.rqt) {
52                 claim_zero(mlx5_devx_cmd_destroy(priv->steer.rqt));
53                 priv->steer.rqt = NULL;
54         }
55         return 0;
56 }
57
58 /*
59  * According to VIRTIO_NET Spec the virtqueues index identity its type by:
60  * 0 receiveq1
61  * 1 transmitq1
62  * ...
63  * 2(N-1) receiveqN
64  * 2(N-1)+1 transmitqN
65  * 2N controlq
66  */
67 static uint8_t
68 is_virtq_recvq(int virtq_index, int nr_vring)
69 {
70         if (virtq_index % 2 == 0 && virtq_index != nr_vring - 1)
71                 return 1;
72         return 0;
73 }
74
75 #define MLX5_VDPA_DEFAULT_RQT_SIZE 512
76 static int __rte_unused
77 mlx5_vdpa_rqt_prepare(struct mlx5_vdpa_priv *priv)
78 {
79         struct mlx5_vdpa_virtq *virtq;
80         uint32_t rqt_n = RTE_MIN(MLX5_VDPA_DEFAULT_RQT_SIZE,
81                                  1 << priv->log_max_rqt_size);
82         struct mlx5_devx_rqt_attr *attr = rte_zmalloc(__func__, sizeof(*attr)
83                                                       + rqt_n *
84                                                       sizeof(uint32_t), 0);
85         uint32_t i = 0, j;
86         int ret = 0;
87
88         if (!attr) {
89                 DRV_LOG(ERR, "Failed to allocate RQT attributes memory.");
90                 rte_errno = ENOMEM;
91                 return -ENOMEM;
92         }
93         SLIST_FOREACH(virtq, &priv->virtq_list, next) {
94                 if (is_virtq_recvq(virtq->index, priv->nr_virtqs)) {
95                         attr->rq_list[i] = virtq->virtq->id;
96                         i++;
97                 }
98         }
99         for (j = 0; i != rqt_n; ++i, ++j)
100                 attr->rq_list[i] = attr->rq_list[j];
101         attr->rq_type = MLX5_INLINE_Q_TYPE_VIRTQ;
102         attr->rqt_max_size = rqt_n;
103         attr->rqt_actual_size = rqt_n;
104         if (!priv->steer.rqt) {
105                 priv->steer.rqt = mlx5_devx_cmd_create_rqt(priv->ctx, attr);
106                 if (!priv->steer.rqt) {
107                         DRV_LOG(ERR, "Failed to create RQT.");
108                         ret = -rte_errno;
109                 }
110         } else {
111                 ret = mlx5_devx_cmd_modify_rqt(priv->steer.rqt, attr);
112                 if (ret)
113                         DRV_LOG(ERR, "Failed to modify RQT.");
114         }
115         rte_free(attr);
116         return ret;
117 }
118
119 static int __rte_unused
120 mlx5_vdpa_rss_flows_create(struct mlx5_vdpa_priv *priv)
121 {
122 #ifdef HAVE_MLX5DV_DR
123         struct mlx5_devx_tir_attr tir_att = {
124                 .disp_type = MLX5_TIRC_DISP_TYPE_INDIRECT,
125                 .rx_hash_fn = MLX5_RX_HASH_FN_TOEPLITZ,
126                 .transport_domain = priv->td->id,
127                 .indirect_table = priv->steer.rqt->id,
128                 .rx_hash_symmetric = 1,
129                 .rx_hash_toeplitz_key = { 0x2cc681d1, 0x5bdbf4f7, 0xfca28319,
130                                           0xdb1a3e94, 0x6b9e38d9, 0x2c9c03d1,
131                                           0xad9944a7, 0xd9563d59, 0x063c25f3,
132                                           0xfc1fdc2a },
133         };
134         struct {
135                 size_t size;
136                 /**< Size of match value. Do NOT split size and key! */
137                 uint32_t buf[MLX5_ST_SZ_DW(fte_match_param)];
138                 /**< Matcher value. This value is used as the mask or a key. */
139         } matcher_mask = {
140                                 .size = sizeof(matcher_mask.buf),
141                         },
142           matcher_value = {
143                                 .size = sizeof(matcher_value.buf),
144                         };
145         struct mlx5dv_flow_matcher_attr dv_attr = {
146                 .type = IBV_FLOW_ATTR_NORMAL,
147                 .match_mask = (void *)&matcher_mask,
148         };
149         void *match_m = matcher_mask.buf;
150         void *match_v = matcher_value.buf;
151         void *headers_m = MLX5_ADDR_OF(fte_match_param, match_m, outer_headers);
152         void *headers_v = MLX5_ADDR_OF(fte_match_param, match_v, outer_headers);
153         void *actions[1];
154         const uint8_t l3_hash =
155                 (1 << MLX5_RX_HASH_FIELD_SELECT_SELECTED_FIELDS_SRC_IP) |
156                 (1 << MLX5_RX_HASH_FIELD_SELECT_SELECTED_FIELDS_DST_IP);
157         const uint8_t l4_hash =
158                 (1 << MLX5_RX_HASH_FIELD_SELECT_SELECTED_FIELDS_L4_SPORT) |
159                 (1 << MLX5_RX_HASH_FIELD_SELECT_SELECTED_FIELDS_L4_DPORT);
160         enum { PRIO, CRITERIA, IP_VER_M, IP_VER_V, IP_PROT_M, IP_PROT_V, L3_BIT,
161                L4_BIT, HASH, END};
162         const uint8_t vars[RTE_DIM(priv->steer.rss)][END] = {
163                 { 7, 0, 0, 0, 0, 0, 0, 0, 0 },
164                 { 6, 1 << MLX5_MATCH_CRITERIA_ENABLE_OUTER_BIT, 0xf, 4, 0, 0,
165                  MLX5_L3_PROT_TYPE_IPV4, 0, l3_hash },
166                 { 6, 1 << MLX5_MATCH_CRITERIA_ENABLE_OUTER_BIT, 0xf, 6, 0, 0,
167                  MLX5_L3_PROT_TYPE_IPV6, 0, l3_hash },
168                 { 5, 1 << MLX5_MATCH_CRITERIA_ENABLE_OUTER_BIT, 0xf, 4, 0xff,
169                  IPPROTO_UDP, MLX5_L3_PROT_TYPE_IPV4, MLX5_L4_PROT_TYPE_UDP,
170                  l3_hash | l4_hash },
171                 { 5, 1 << MLX5_MATCH_CRITERIA_ENABLE_OUTER_BIT, 0xf, 4, 0xff,
172                  IPPROTO_TCP, MLX5_L3_PROT_TYPE_IPV4, MLX5_L4_PROT_TYPE_TCP,
173                  l3_hash | l4_hash },
174                 { 5, 1 << MLX5_MATCH_CRITERIA_ENABLE_OUTER_BIT, 0xf, 6, 0xff,
175                  IPPROTO_UDP, MLX5_L3_PROT_TYPE_IPV6, MLX5_L4_PROT_TYPE_UDP,
176                  l3_hash | l4_hash },
177                 { 5, 1 << MLX5_MATCH_CRITERIA_ENABLE_OUTER_BIT, 0xf, 6, 0xff,
178                  IPPROTO_TCP, MLX5_L3_PROT_TYPE_IPV6, MLX5_L4_PROT_TYPE_TCP,
179                  l3_hash | l4_hash },
180         };
181         unsigned i;
182
183         for (i = 0; i < RTE_DIM(priv->steer.rss); ++i) {
184                 dv_attr.priority = vars[i][PRIO];
185                 dv_attr.match_criteria_enable = vars[i][CRITERIA];
186                 MLX5_SET(fte_match_set_lyr_2_4, headers_m, ip_version,
187                          vars[i][IP_VER_M]);
188                 MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_version,
189                          vars[i][IP_VER_V]);
190                 MLX5_SET(fte_match_set_lyr_2_4, headers_m, ip_protocol,
191                          vars[i][IP_PROT_M]);
192                 MLX5_SET(fte_match_set_lyr_2_4, headers_v, ip_protocol,
193                          vars[i][IP_PROT_V]);
194                 tir_att.rx_hash_field_selector_outer.l3_prot_type =
195                                                                 vars[i][L3_BIT];
196                 tir_att.rx_hash_field_selector_outer.l4_prot_type =
197                                                                 vars[i][L4_BIT];
198                 tir_att.rx_hash_field_selector_outer.selected_fields =
199                                                                   vars[i][HASH];
200                 priv->steer.rss[i].matcher = mlx5_glue->dv_create_flow_matcher
201                                          (priv->ctx, &dv_attr, priv->steer.tbl);
202                 if (!priv->steer.rss[i].matcher) {
203                         DRV_LOG(ERR, "Failed to create matcher %d.", i);
204                         goto error;
205                 }
206                 priv->steer.rss[i].tir = mlx5_devx_cmd_create_tir(priv->ctx,
207                                                                   &tir_att);
208                 if (!priv->steer.rss[i].tir) {
209                         DRV_LOG(ERR, "Failed to create TIR %d.", i);
210                         goto error;
211                 }
212                 priv->steer.rss[i].tir_action =
213                                 mlx5_glue->dv_create_flow_action_dest_devx_tir
214                                                   (priv->steer.rss[i].tir->obj);
215                 if (!priv->steer.rss[i].tir_action) {
216                         DRV_LOG(ERR, "Failed to create TIR action %d.", i);
217                         goto error;
218                 }
219                 actions[0] = priv->steer.rss[i].tir_action;
220                 priv->steer.rss[i].flow = mlx5_glue->dv_create_flow
221                                         (priv->steer.rss[i].matcher,
222                                          (void *)&matcher_value, 1, actions);
223                 if (!priv->steer.rss[i].flow) {
224                         DRV_LOG(ERR, "Failed to create flow %d.", i);
225                         goto error;
226                 }
227         }
228         return 0;
229 error:
230         /* Resources will be freed by the caller. */
231         return -1;
232 #else
233         (void)priv;
234         return -ENOTSUP;
235 #endif /* HAVE_MLX5DV_DR */
236 }
237
238 int
239 mlx5_vdpa_steer_setup(struct mlx5_vdpa_priv *priv)
240 {
241 #ifdef HAVE_MLX5DV_DR
242         if (mlx5_vdpa_rqt_prepare(priv))
243                 return -1;
244         priv->steer.domain = mlx5_glue->dr_create_domain(priv->ctx,
245                                                   MLX5DV_DR_DOMAIN_TYPE_NIC_RX);
246         if (!priv->steer.domain) {
247                 DRV_LOG(ERR, "Failed to create Rx domain.");
248                 goto error;
249         }
250         priv->steer.tbl = mlx5_glue->dr_create_flow_tbl(priv->steer.domain, 0);
251         if (!priv->steer.tbl) {
252                 DRV_LOG(ERR, "Failed to create table 0 with Rx domain.");
253                 goto error;
254         }
255         if (mlx5_vdpa_rss_flows_create(priv))
256                 goto error;
257         return 0;
258 error:
259         mlx5_vdpa_steer_unset(priv);
260         return -1;
261 #else
262         (void)priv;
263         return -ENOTSUP;
264 #endif /* HAVE_MLX5DV_DR */
265 }