xref: /dpdk/drivers/net/mlx5/hws/mlx5dr_table.c (revision 665b49c51639a10c553433bc2bcd85c7331c631e)
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright (c) 2022 NVIDIA Corporation & Affiliates
3  */
4 
5 #include "mlx5dr_internal.h"
6 
7 static void mlx5dr_table_init_next_ft_attr(struct mlx5dr_table *tbl,
8 					   struct mlx5dr_cmd_ft_create_attr *ft_attr)
9 {
10 	ft_attr->type = tbl->fw_ft_type;
11 	if (tbl->type == MLX5DR_TABLE_TYPE_FDB)
12 		ft_attr->level = tbl->ctx->caps->fdb_ft.max_level - 1;
13 	else
14 		ft_attr->level = tbl->ctx->caps->nic_ft.max_level - 1;
15 	ft_attr->rtc_valid = true;
16 }
17 
18 /* Call this under ctx->ctrl_lock */
19 static int
20 mlx5dr_table_up_default_fdb_miss_tbl(struct mlx5dr_table *tbl)
21 {
22 	struct mlx5dr_cmd_ft_create_attr ft_attr = {0};
23 	struct mlx5dr_cmd_forward_tbl *default_miss;
24 	struct mlx5dr_context *ctx = tbl->ctx;
25 	uint8_t tbl_type = tbl->type;
26 	uint32_t vport;
27 
28 	if (tbl->type != MLX5DR_TABLE_TYPE_FDB)
29 		return 0;
30 
31 	if (ctx->common_res[tbl_type].default_miss) {
32 		ctx->common_res[tbl_type].default_miss->refcount++;
33 		return 0;
34 	}
35 
36 	ft_attr.type = tbl->fw_ft_type;
37 	ft_attr.level = tbl->ctx->caps->fdb_ft.max_level; /* The last level */
38 	ft_attr.rtc_valid = false;
39 
40 	assert(ctx->caps->eswitch_manager);
41 	vport = ctx->caps->eswitch_manager_vport_number;
42 
43 	default_miss = mlx5dr_cmd_miss_ft_create(mlx5dr_context_get_local_ibv(ctx),
44 						 &ft_attr, vport);
45 	if (!default_miss) {
46 		DR_LOG(ERR, "Failed to default miss table type: 0x%x", tbl_type);
47 		return rte_errno;
48 	}
49 
50 	ctx->common_res[tbl_type].default_miss = default_miss;
51 	ctx->common_res[tbl_type].default_miss->refcount++;
52 	return 0;
53 }
54 
55 /* Called under pthread_spin_lock(&ctx->ctrl_lock) */
56 static void mlx5dr_table_down_default_fdb_miss_tbl(struct mlx5dr_table *tbl)
57 {
58 	struct mlx5dr_cmd_forward_tbl *default_miss;
59 	struct mlx5dr_context *ctx = tbl->ctx;
60 	uint8_t tbl_type = tbl->type;
61 
62 	if (tbl->type != MLX5DR_TABLE_TYPE_FDB)
63 		return;
64 
65 	default_miss = ctx->common_res[tbl_type].default_miss;
66 	if (--default_miss->refcount)
67 		return;
68 
69 	mlx5dr_cmd_miss_ft_destroy(default_miss);
70 
71 	simple_free(default_miss);
72 	ctx->common_res[tbl_type].default_miss = NULL;
73 }
74 
75 static int
76 mlx5dr_table_connect_to_default_miss_tbl(struct mlx5dr_table *tbl,
77 					 struct mlx5dr_devx_obj *ft)
78 {
79 	struct mlx5dr_cmd_ft_modify_attr ft_attr = {0};
80 	int ret;
81 
82 	assert(tbl->type == MLX5DR_TABLE_TYPE_FDB);
83 
84 	mlx5dr_cmd_set_attr_connect_miss_tbl(tbl->ctx,
85 					     tbl->fw_ft_type,
86 					     tbl->type,
87 					     &ft_attr);
88 
89 	/* Connect to next */
90 	ret = mlx5dr_cmd_flow_table_modify(ft, &ft_attr);
91 	if (ret) {
92 		DR_LOG(ERR, "Failed to connect FT to default FDB FT");
93 		return errno;
94 	}
95 
96 	return 0;
97 }
98 
99 struct mlx5dr_devx_obj *
100 mlx5dr_table_create_default_ft(struct ibv_context *ibv,
101 			       struct mlx5dr_table *tbl)
102 {
103 	struct mlx5dr_cmd_ft_create_attr ft_attr = {0};
104 	struct mlx5dr_devx_obj *ft_obj;
105 	int ret;
106 
107 	mlx5dr_table_init_next_ft_attr(tbl, &ft_attr);
108 
109 	ft_obj = mlx5dr_cmd_flow_table_create(ibv, &ft_attr);
110 	if (ft_obj && tbl->type == MLX5DR_TABLE_TYPE_FDB) {
111 		/* Take/create ref over the default miss */
112 		ret = mlx5dr_table_up_default_fdb_miss_tbl(tbl);
113 		if (ret) {
114 			DR_LOG(ERR, "Failed to get default fdb miss");
115 			goto free_ft_obj;
116 		}
117 		ret = mlx5dr_table_connect_to_default_miss_tbl(tbl, ft_obj);
118 		if (ret) {
119 			DR_LOG(ERR, "Failed connecting to default miss tbl");
120 			goto down_miss_tbl;
121 		}
122 	}
123 
124 	return ft_obj;
125 
126 down_miss_tbl:
127 	mlx5dr_table_down_default_fdb_miss_tbl(tbl);
128 free_ft_obj:
129 	mlx5dr_cmd_destroy_obj(ft_obj);
130 	return NULL;
131 }
132 
133 static int
134 mlx5dr_table_init_check_hws_support(struct mlx5dr_context *ctx,
135 				    struct mlx5dr_table *tbl)
136 {
137 	if (!(ctx->flags & MLX5DR_CONTEXT_FLAG_HWS_SUPPORT)) {
138 		DR_LOG(ERR, "HWS not supported, cannot create mlx5dr_table");
139 		rte_errno = EOPNOTSUPP;
140 		return rte_errno;
141 	}
142 
143 	if (mlx5dr_context_shared_gvmi_used(ctx) && tbl->type == MLX5DR_TABLE_TYPE_FDB) {
144 		DR_LOG(ERR, "FDB with shared port resources is not supported");
145 		rte_errno = EOPNOTSUPP;
146 		return rte_errno;
147 	}
148 
149 	return 0;
150 }
151 
152 static int
153 mlx5dr_table_shared_gvmi_resource_create(struct mlx5dr_context *ctx,
154 					 enum mlx5dr_table_type type,
155 					 struct mlx5dr_context_shared_gvmi_res *gvmi_res)
156 {
157 	struct mlx5dr_cmd_ft_create_attr ft_attr = {0};
158 	uint32_t calculated_ft_id;
159 	int ret;
160 
161 	if (!mlx5dr_context_shared_gvmi_used(ctx))
162 		return 0;
163 
164 	ft_attr.type = mlx5dr_table_get_res_fw_ft_type(type, false);
165 	ft_attr.level = ctx->caps->nic_ft.max_level - 1;
166 	ft_attr.rtc_valid = true;
167 
168 	gvmi_res->end_ft =
169 		mlx5dr_cmd_flow_table_create(mlx5dr_context_get_local_ibv(ctx),
170 					     &ft_attr);
171 	if (!gvmi_res->end_ft) {
172 		DR_LOG(ERR, "Failed to create end-ft");
173 		return rte_errno;
174 	}
175 
176 	calculated_ft_id =
177 		mlx5dr_table_get_res_fw_ft_type(type, false) << FT_ID_FT_TYPE_OFFSET;
178 	calculated_ft_id |= gvmi_res->end_ft->id;
179 
180 	/* create alias to that FT */
181 	ret = mlx5dr_matcher_create_aliased_obj(ctx,
182 						ctx->local_ibv_ctx,
183 						ctx->ibv_ctx,
184 						ctx->caps->vhca_id,
185 						calculated_ft_id,
186 						MLX5_GENERAL_OBJ_TYPE_FT_ALIAS,
187 						&gvmi_res->aliased_end_ft);
188 	if (ret) {
189 		DR_LOG(ERR, "Failed to create alias end-ft");
190 		goto free_end_ft;
191 	}
192 
193 	return 0;
194 
195 free_end_ft:
196 	mlx5dr_cmd_destroy_obj(gvmi_res->end_ft);
197 
198 	return rte_errno;
199 }
200 
201 static void
202 mlx5dr_table_shared_gvmi_resourse_destroy(struct mlx5dr_context *ctx,
203 					  struct mlx5dr_context_shared_gvmi_res *gvmi_res)
204 {
205 	if (!mlx5dr_context_shared_gvmi_used(ctx))
206 		return;
207 
208 	if (gvmi_res->aliased_end_ft) {
209 		mlx5dr_cmd_destroy_obj(gvmi_res->aliased_end_ft);
210 		gvmi_res->aliased_end_ft = NULL;
211 	}
212 	if (gvmi_res->end_ft) {
213 		mlx5dr_cmd_destroy_obj(gvmi_res->end_ft);
214 		gvmi_res->end_ft = NULL;
215 	}
216 }
217 
218 /* called under spinlock ctx->ctrl_lock */
219 static struct mlx5dr_context_shared_gvmi_res *
220 mlx5dr_table_get_shared_gvmi_res(struct mlx5dr_context *ctx, enum mlx5dr_table_type type)
221 {
222 	int ret;
223 
224 	if (!mlx5dr_context_shared_gvmi_used(ctx))
225 		return NULL;
226 
227 	if (ctx->gvmi_res[type].aliased_end_ft) {
228 		ctx->gvmi_res[type].refcount++;
229 		return &ctx->gvmi_res[type];
230 	}
231 
232 	ret = mlx5dr_table_shared_gvmi_resource_create(ctx, type, &ctx->gvmi_res[type]);
233 	if (ret) {
234 		DR_LOG(ERR, "Failed to create shared gvmi res for type: %d", type);
235 		goto out;
236 	}
237 
238 	ctx->gvmi_res[type].refcount = 1;
239 
240 	return &ctx->gvmi_res[type];
241 
242 out:
243 	return NULL;
244 }
245 
246 /* called under spinlock ctx->ctrl_lock */
247 static void mlx5dr_table_put_shared_gvmi_res(struct mlx5dr_table *tbl)
248 {
249 	struct mlx5dr_context *ctx = tbl->ctx;
250 
251 	if (!mlx5dr_context_shared_gvmi_used(ctx))
252 		return;
253 
254 	if (--ctx->gvmi_res[tbl->type].refcount)
255 		return;
256 
257 	mlx5dr_table_shared_gvmi_resourse_destroy(ctx, &ctx->gvmi_res[tbl->type]);
258 }
259 
260 static void mlx5dr_table_uninit_shared_ctx_res(struct mlx5dr_table *tbl)
261 {
262 	struct mlx5dr_context *ctx = tbl->ctx;
263 
264 	if (!mlx5dr_context_shared_gvmi_used(ctx))
265 		return;
266 
267 	mlx5dr_cmd_destroy_obj(tbl->local_ft);
268 
269 	mlx5dr_table_put_shared_gvmi_res(tbl);
270 }
271 
272 /* called under spin_lock ctx->ctrl_lock */
273 static int mlx5dr_table_init_shared_ctx_res(struct mlx5dr_context *ctx, struct mlx5dr_table *tbl)
274 {
275 	if (!mlx5dr_context_shared_gvmi_used(ctx))
276 		return 0;
277 
278 	/* create local-ft for root access */
279 	tbl->local_ft =
280 		mlx5dr_table_create_default_ft(mlx5dr_context_get_local_ibv(ctx), tbl);
281 	if (!tbl->local_ft) {
282 		DR_LOG(ERR, "Failed to create local-ft");
283 		return rte_errno;
284 	}
285 
286 	if (!mlx5dr_table_get_shared_gvmi_res(tbl->ctx, tbl->type)) {
287 		DR_LOG(ERR, "Failed to shared gvmi resources");
288 		goto clean_local_ft;
289 	}
290 
291 	return 0;
292 
293 clean_local_ft:
294 	mlx5dr_table_destroy_default_ft(tbl, tbl->local_ft);
295 	return rte_errno;
296 }
297 
298 void mlx5dr_table_destroy_default_ft(struct mlx5dr_table *tbl,
299 				     struct mlx5dr_devx_obj *ft_obj)
300 {
301 	mlx5dr_cmd_destroy_obj(ft_obj);
302 	mlx5dr_table_down_default_fdb_miss_tbl(tbl);
303 }
304 
305 static int mlx5dr_table_init(struct mlx5dr_table *tbl)
306 {
307 	struct mlx5dr_context *ctx = tbl->ctx;
308 	int ret;
309 
310 	if (mlx5dr_table_is_root(tbl))
311 		return 0;
312 
313 	ret = mlx5dr_table_init_check_hws_support(ctx, tbl);
314 	if (ret)
315 		return ret;
316 
317 	switch (tbl->type) {
318 	case MLX5DR_TABLE_TYPE_NIC_RX:
319 		tbl->fw_ft_type = FS_FT_NIC_RX;
320 		break;
321 	case MLX5DR_TABLE_TYPE_NIC_TX:
322 		tbl->fw_ft_type = FS_FT_NIC_TX;
323 		break;
324 	case MLX5DR_TABLE_TYPE_FDB:
325 		tbl->fw_ft_type = FS_FT_FDB;
326 		break;
327 	default:
328 		assert(0);
329 		break;
330 	}
331 
332 	pthread_spin_lock(&ctx->ctrl_lock);
333 	tbl->ft = mlx5dr_table_create_default_ft(tbl->ctx->ibv_ctx, tbl);
334 	if (!tbl->ft) {
335 		DR_LOG(ERR, "Failed to create flow table devx object");
336 		pthread_spin_unlock(&ctx->ctrl_lock);
337 		return rte_errno;
338 	}
339 
340 	ret = mlx5dr_action_get_default_stc(ctx, tbl->type);
341 	if (ret)
342 		goto tbl_destroy;
343 
344 	ret = mlx5dr_table_init_shared_ctx_res(ctx, tbl);
345 	if (ret)
346 		goto put_stc;
347 
348 	pthread_spin_unlock(&ctx->ctrl_lock);
349 
350 	return 0;
351 
352 put_stc:
353 	mlx5dr_action_put_default_stc(ctx, tbl->type);
354 tbl_destroy:
355 	mlx5dr_table_destroy_default_ft(tbl, tbl->ft);
356 	pthread_spin_unlock(&ctx->ctrl_lock);
357 	return rte_errno;
358 }
359 
360 static void mlx5dr_table_uninit(struct mlx5dr_table *tbl)
361 {
362 	if (mlx5dr_table_is_root(tbl))
363 		return;
364 	pthread_spin_lock(&tbl->ctx->ctrl_lock);
365 	mlx5dr_action_put_default_stc(tbl->ctx, tbl->type);
366 	mlx5dr_table_destroy_default_ft(tbl, tbl->ft);
367 	mlx5dr_table_uninit_shared_ctx_res(tbl);
368 	pthread_spin_unlock(&tbl->ctx->ctrl_lock);
369 }
370 
371 struct mlx5dr_table *mlx5dr_table_create(struct mlx5dr_context *ctx,
372 					 struct mlx5dr_table_attr *attr)
373 {
374 	struct mlx5dr_table *tbl;
375 	int ret;
376 
377 	if (attr->type > MLX5DR_TABLE_TYPE_FDB) {
378 		DR_LOG(ERR, "Invalid table type %d", attr->type);
379 		return NULL;
380 	}
381 
382 	tbl = simple_malloc(sizeof(*tbl));
383 	if (!tbl) {
384 		rte_errno = ENOMEM;
385 		return NULL;
386 	}
387 
388 	tbl->ctx = ctx;
389 	tbl->type = attr->type;
390 	tbl->level = attr->level;
391 	LIST_INIT(&tbl->head);
392 
393 	ret = mlx5dr_table_init(tbl);
394 	if (ret) {
395 		DR_LOG(ERR, "Failed to initialise table");
396 		goto free_tbl;
397 	}
398 
399 	pthread_spin_lock(&ctx->ctrl_lock);
400 	LIST_INSERT_HEAD(&ctx->head, tbl, next);
401 	pthread_spin_unlock(&ctx->ctrl_lock);
402 
403 	return tbl;
404 
405 free_tbl:
406 	simple_free(tbl);
407 	return NULL;
408 }
409 
410 int mlx5dr_table_destroy(struct mlx5dr_table *tbl)
411 {
412 	struct mlx5dr_context *ctx = tbl->ctx;
413 
414 	pthread_spin_lock(&ctx->ctrl_lock);
415 	LIST_REMOVE(tbl, next);
416 	pthread_spin_unlock(&ctx->ctrl_lock);
417 	mlx5dr_table_uninit(tbl);
418 	simple_free(tbl);
419 
420 	return 0;
421 }
422