xref: /dpdk/drivers/net/mlx5/hws/mlx5dr_table.c (revision 486f9aac0cbe2598a76c853890c1d557747f71cf)
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_set_fte_attr fte_attr = {0};
24 	struct mlx5dr_cmd_forward_tbl *default_miss;
25 	struct mlx5dr_cmd_set_fte_dest dest = {0};
26 	struct mlx5dr_context *ctx = tbl->ctx;
27 	uint8_t tbl_type = tbl->type;
28 
29 	if (tbl->type != MLX5DR_TABLE_TYPE_FDB)
30 		return 0;
31 
32 	if (ctx->common_res[tbl_type].default_miss) {
33 		ctx->common_res[tbl_type].default_miss->refcount++;
34 		return 0;
35 	}
36 
37 	ft_attr.type = tbl->fw_ft_type;
38 	ft_attr.level = tbl->ctx->caps->fdb_ft.max_level; /* The last level */
39 	ft_attr.rtc_valid = false;
40 
41 	dest.destination_type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
42 	dest.destination_id = ctx->caps->eswitch_manager_vport_number;
43 	fte_attr.action_flags = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
44 	fte_attr.dests_num = 1;
45 	fte_attr.dests = &dest;
46 
47 	default_miss = mlx5dr_cmd_forward_tbl_create(mlx5dr_context_get_local_ibv(ctx),
48 						     &ft_attr, &fte_attr);
49 	if (!default_miss) {
50 		DR_LOG(ERR, "Failed to default miss table type: 0x%x", tbl_type);
51 		return rte_errno;
52 	}
53 
54 	ctx->common_res[tbl_type].default_miss = default_miss;
55 	ctx->common_res[tbl_type].default_miss->refcount++;
56 	return 0;
57 }
58 
59 /* Called under pthread_spin_lock(&ctx->ctrl_lock) */
60 static void mlx5dr_table_down_default_fdb_miss_tbl(struct mlx5dr_table *tbl)
61 {
62 	struct mlx5dr_cmd_forward_tbl *default_miss;
63 	struct mlx5dr_context *ctx = tbl->ctx;
64 	uint8_t tbl_type = tbl->type;
65 
66 	if (tbl->type != MLX5DR_TABLE_TYPE_FDB)
67 		return;
68 
69 	default_miss = ctx->common_res[tbl_type].default_miss;
70 	if (--default_miss->refcount)
71 		return;
72 
73 	mlx5dr_cmd_forward_tbl_destroy(default_miss);
74 	ctx->common_res[tbl_type].default_miss = NULL;
75 }
76 
77 static int
78 mlx5dr_table_connect_to_default_miss_tbl(struct mlx5dr_table *tbl,
79 					 struct mlx5dr_devx_obj *ft)
80 {
81 	struct mlx5dr_cmd_ft_modify_attr ft_attr = {0};
82 	int ret;
83 
84 	assert(tbl->type == MLX5DR_TABLE_TYPE_FDB);
85 
86 	mlx5dr_cmd_set_attr_connect_miss_tbl(tbl->ctx,
87 					     tbl->fw_ft_type,
88 					     tbl->type,
89 					     &ft_attr);
90 
91 	/* Connect to next */
92 	ret = mlx5dr_cmd_flow_table_modify(ft, &ft_attr);
93 	if (ret) {
94 		DR_LOG(ERR, "Failed to connect FT to default FDB FT");
95 		return ret;
96 	}
97 
98 	return 0;
99 }
100 
101 struct mlx5dr_devx_obj *
102 mlx5dr_table_create_default_ft(struct ibv_context *ibv,
103 			       struct mlx5dr_table *tbl)
104 {
105 	struct mlx5dr_cmd_ft_create_attr ft_attr = {0};
106 	struct mlx5dr_devx_obj *ft_obj;
107 	int ret;
108 
109 	mlx5dr_table_init_next_ft_attr(tbl, &ft_attr);
110 
111 	ft_obj = mlx5dr_cmd_flow_table_create(ibv, &ft_attr);
112 	if (ft_obj && tbl->type == MLX5DR_TABLE_TYPE_FDB) {
113 		/* Take/create ref over the default miss */
114 		ret = mlx5dr_table_up_default_fdb_miss_tbl(tbl);
115 		if (ret) {
116 			DR_LOG(ERR, "Failed to get default fdb miss");
117 			goto free_ft_obj;
118 		}
119 		ret = mlx5dr_table_connect_to_default_miss_tbl(tbl, ft_obj);
120 		if (ret) {
121 			DR_LOG(ERR, "Failed connecting to default miss tbl");
122 			goto down_miss_tbl;
123 		}
124 	}
125 
126 	return ft_obj;
127 
128 down_miss_tbl:
129 	mlx5dr_table_down_default_fdb_miss_tbl(tbl);
130 free_ft_obj:
131 	mlx5dr_cmd_destroy_obj(ft_obj);
132 	return NULL;
133 }
134 
135 static int
136 mlx5dr_table_init_check_hws_support(struct mlx5dr_context *ctx,
137 				    struct mlx5dr_table *tbl)
138 {
139 	if (!(ctx->flags & MLX5DR_CONTEXT_FLAG_HWS_SUPPORT)) {
140 		DR_LOG(ERR, "HWS not supported, cannot create mlx5dr_table");
141 		rte_errno = EOPNOTSUPP;
142 		return rte_errno;
143 	}
144 
145 	if (mlx5dr_context_shared_gvmi_used(ctx) && tbl->type == MLX5DR_TABLE_TYPE_FDB) {
146 		DR_LOG(ERR, "FDB with shared port resources is not supported");
147 		rte_errno = EOPNOTSUPP;
148 		return rte_errno;
149 	}
150 
151 	return 0;
152 }
153 
154 static int
155 mlx5dr_table_shared_gvmi_resource_create(struct mlx5dr_context *ctx,
156 					 enum mlx5dr_table_type type,
157 					 struct mlx5dr_context_shared_gvmi_res *gvmi_res)
158 {
159 	struct mlx5dr_cmd_ft_create_attr ft_attr = {0};
160 	uint32_t calculated_ft_id;
161 	int ret;
162 
163 	if (!mlx5dr_context_shared_gvmi_used(ctx))
164 		return 0;
165 
166 	ft_attr.type = mlx5dr_table_get_res_fw_ft_type(type, false);
167 	ft_attr.level = ctx->caps->nic_ft.max_level - 1;
168 	ft_attr.rtc_valid = true;
169 
170 	gvmi_res->end_ft =
171 		mlx5dr_cmd_flow_table_create(mlx5dr_context_get_local_ibv(ctx),
172 					     &ft_attr);
173 	if (!gvmi_res->end_ft) {
174 		DR_LOG(ERR, "Failed to create end-ft");
175 		return rte_errno;
176 	}
177 
178 	calculated_ft_id =
179 		mlx5dr_table_get_res_fw_ft_type(type, false) << FT_ID_FT_TYPE_OFFSET;
180 	calculated_ft_id |= gvmi_res->end_ft->id;
181 
182 	/* create alias to that FT */
183 	ret = mlx5dr_matcher_create_aliased_obj(ctx,
184 						ctx->local_ibv_ctx,
185 						ctx->ibv_ctx,
186 						ctx->caps->vhca_id,
187 						calculated_ft_id,
188 						MLX5_GENERAL_OBJ_TYPE_FT_ALIAS,
189 						&gvmi_res->aliased_end_ft);
190 	if (ret) {
191 		DR_LOG(ERR, "Failed to create alias end-ft");
192 		goto free_end_ft;
193 	}
194 
195 	return 0;
196 
197 free_end_ft:
198 	mlx5dr_cmd_destroy_obj(gvmi_res->end_ft);
199 
200 	return rte_errno;
201 }
202 
203 static void
204 mlx5dr_table_shared_gvmi_resourse_destroy(struct mlx5dr_context *ctx,
205 					  struct mlx5dr_context_shared_gvmi_res *gvmi_res)
206 {
207 	if (!mlx5dr_context_shared_gvmi_used(ctx))
208 		return;
209 
210 	if (gvmi_res->aliased_end_ft) {
211 		mlx5dr_cmd_destroy_obj(gvmi_res->aliased_end_ft);
212 		gvmi_res->aliased_end_ft = NULL;
213 	}
214 	if (gvmi_res->end_ft) {
215 		mlx5dr_cmd_destroy_obj(gvmi_res->end_ft);
216 		gvmi_res->end_ft = NULL;
217 	}
218 }
219 
220 /* called under spinlock ctx->ctrl_lock */
221 static struct mlx5dr_context_shared_gvmi_res *
222 mlx5dr_table_get_shared_gvmi_res(struct mlx5dr_context *ctx, enum mlx5dr_table_type type)
223 {
224 	int ret;
225 
226 	if (!mlx5dr_context_shared_gvmi_used(ctx))
227 		return NULL;
228 
229 	if (ctx->gvmi_res[type].aliased_end_ft) {
230 		ctx->gvmi_res[type].refcount++;
231 		return &ctx->gvmi_res[type];
232 	}
233 
234 	ret = mlx5dr_table_shared_gvmi_resource_create(ctx, type, &ctx->gvmi_res[type]);
235 	if (ret) {
236 		DR_LOG(ERR, "Failed to create shared gvmi res for type: %d", type);
237 		goto out;
238 	}
239 
240 	ctx->gvmi_res[type].refcount = 1;
241 
242 	return &ctx->gvmi_res[type];
243 
244 out:
245 	return NULL;
246 }
247 
248 /* called under spinlock ctx->ctrl_lock */
249 static void mlx5dr_table_put_shared_gvmi_res(struct mlx5dr_table *tbl)
250 {
251 	struct mlx5dr_context *ctx = tbl->ctx;
252 
253 	if (!mlx5dr_context_shared_gvmi_used(ctx))
254 		return;
255 
256 	if (--ctx->gvmi_res[tbl->type].refcount)
257 		return;
258 
259 	mlx5dr_table_shared_gvmi_resourse_destroy(ctx, &ctx->gvmi_res[tbl->type]);
260 }
261 
262 static void mlx5dr_table_uninit_shared_ctx_res(struct mlx5dr_table *tbl)
263 {
264 	struct mlx5dr_context *ctx = tbl->ctx;
265 
266 	if (!mlx5dr_context_shared_gvmi_used(ctx))
267 		return;
268 
269 	mlx5dr_cmd_destroy_obj(tbl->local_ft);
270 
271 	mlx5dr_table_put_shared_gvmi_res(tbl);
272 }
273 
274 /* called under spin_lock ctx->ctrl_lock */
275 static int mlx5dr_table_init_shared_ctx_res(struct mlx5dr_context *ctx, struct mlx5dr_table *tbl)
276 {
277 	struct mlx5dr_cmd_ft_modify_attr ft_attr = {0};
278 	int ret;
279 
280 	if (!mlx5dr_context_shared_gvmi_used(ctx))
281 		return 0;
282 
283 	/* create local-ft for root access */
284 	tbl->local_ft =
285 		mlx5dr_table_create_default_ft(mlx5dr_context_get_local_ibv(ctx), tbl);
286 	if (!tbl->local_ft) {
287 		DR_LOG(ERR, "Failed to create local-ft");
288 		return rte_errno;
289 	}
290 
291 	if (!mlx5dr_table_get_shared_gvmi_res(tbl->ctx, tbl->type)) {
292 		DR_LOG(ERR, "Failed to shared gvmi resources");
293 		goto clean_local_ft;
294 	}
295 
296 	/* On shared gvmi the default behavior is jump to alias end ft */
297 	mlx5dr_cmd_set_attr_connect_miss_tbl(tbl->ctx,
298 					     tbl->fw_ft_type,
299 					     tbl->type,
300 					     &ft_attr);
301 
302 	ret = mlx5dr_cmd_flow_table_modify(tbl->ft, &ft_attr);
303 	if (ret) {
304 		DR_LOG(ERR, "Failed to point table to its default miss");
305 		goto clean_shared_res;
306 	}
307 
308 	return 0;
309 
310 clean_shared_res:
311 	mlx5dr_table_put_shared_gvmi_res(tbl);
312 clean_local_ft:
313 	mlx5dr_table_destroy_default_ft(tbl, tbl->local_ft);
314 	return rte_errno;
315 }
316 
317 void mlx5dr_table_destroy_default_ft(struct mlx5dr_table *tbl,
318 				     struct mlx5dr_devx_obj *ft_obj)
319 {
320 	mlx5dr_cmd_destroy_obj(ft_obj);
321 	mlx5dr_table_down_default_fdb_miss_tbl(tbl);
322 }
323 
324 static int mlx5dr_table_init(struct mlx5dr_table *tbl)
325 {
326 	struct mlx5dr_context *ctx = tbl->ctx;
327 	int ret;
328 
329 	if (mlx5dr_table_is_root(tbl))
330 		return 0;
331 
332 	ret = mlx5dr_table_init_check_hws_support(ctx, tbl);
333 	if (ret)
334 		return ret;
335 
336 	switch (tbl->type) {
337 	case MLX5DR_TABLE_TYPE_NIC_RX:
338 		tbl->fw_ft_type = FS_FT_NIC_RX;
339 		break;
340 	case MLX5DR_TABLE_TYPE_NIC_TX:
341 		tbl->fw_ft_type = FS_FT_NIC_TX;
342 		break;
343 	case MLX5DR_TABLE_TYPE_FDB:
344 		tbl->fw_ft_type = FS_FT_FDB;
345 		break;
346 	default:
347 		assert(0);
348 		break;
349 	}
350 
351 	pthread_spin_lock(&ctx->ctrl_lock);
352 	tbl->ft = mlx5dr_table_create_default_ft(tbl->ctx->ibv_ctx, tbl);
353 	if (!tbl->ft) {
354 		DR_LOG(ERR, "Failed to create flow table devx object");
355 		pthread_spin_unlock(&ctx->ctrl_lock);
356 		return rte_errno;
357 	}
358 
359 	ret = mlx5dr_table_init_shared_ctx_res(ctx, tbl);
360 	if (ret)
361 		goto tbl_destroy;
362 
363 	ret = mlx5dr_action_get_default_stc(ctx, tbl->type);
364 	if (ret)
365 		goto free_shared_ctx;
366 
367 	pthread_spin_unlock(&ctx->ctrl_lock);
368 
369 	return 0;
370 
371 free_shared_ctx:
372 	mlx5dr_table_uninit_shared_ctx_res(tbl);
373 tbl_destroy:
374 	mlx5dr_table_destroy_default_ft(tbl, tbl->ft);
375 	pthread_spin_unlock(&ctx->ctrl_lock);
376 	return rte_errno;
377 }
378 
379 static void mlx5dr_table_uninit(struct mlx5dr_table *tbl)
380 {
381 	if (mlx5dr_table_is_root(tbl))
382 		return;
383 	pthread_spin_lock(&tbl->ctx->ctrl_lock);
384 	mlx5dr_action_put_default_stc(tbl->ctx, tbl->type);
385 	mlx5dr_table_uninit_shared_ctx_res(tbl);
386 	mlx5dr_table_destroy_default_ft(tbl, tbl->ft);
387 	pthread_spin_unlock(&tbl->ctx->ctrl_lock);
388 }
389 
390 struct mlx5dr_table *mlx5dr_table_create(struct mlx5dr_context *ctx,
391 					 struct mlx5dr_table_attr *attr)
392 {
393 	struct mlx5dr_table *tbl;
394 	int ret;
395 
396 	if (attr->type > MLX5DR_TABLE_TYPE_FDB) {
397 		DR_LOG(ERR, "Invalid table type %d", attr->type);
398 		return NULL;
399 	}
400 
401 	tbl = simple_calloc(1, sizeof(*tbl));
402 	if (!tbl) {
403 		rte_errno = ENOMEM;
404 		return NULL;
405 	}
406 
407 	tbl->ctx = ctx;
408 	tbl->type = attr->type;
409 	tbl->level = attr->level;
410 
411 	ret = mlx5dr_table_init(tbl);
412 	if (ret) {
413 		DR_LOG(ERR, "Failed to initialise table");
414 		goto free_tbl;
415 	}
416 
417 	pthread_spin_lock(&ctx->ctrl_lock);
418 	LIST_INSERT_HEAD(&ctx->head, tbl, next);
419 	pthread_spin_unlock(&ctx->ctrl_lock);
420 
421 	return tbl;
422 
423 free_tbl:
424 	simple_free(tbl);
425 	return NULL;
426 }
427 
428 int mlx5dr_table_destroy(struct mlx5dr_table *tbl)
429 {
430 	struct mlx5dr_context *ctx = tbl->ctx;
431 	pthread_spin_lock(&ctx->ctrl_lock);
432 	if (!LIST_EMPTY(&tbl->head) || !LIST_EMPTY(&tbl->isolated_matchers)) {
433 		DR_LOG(ERR, "Cannot destroy table containing matchers");
434 		rte_errno = EBUSY;
435 		goto unlock_err;
436 	}
437 
438 	if (!LIST_EMPTY(&tbl->default_miss.head)) {
439 		DR_LOG(ERR, "Cannot destroy table pointed by default miss");
440 		rte_errno = EBUSY;
441 		goto unlock_err;
442 	}
443 
444 	LIST_REMOVE(tbl, next);
445 	pthread_spin_unlock(&ctx->ctrl_lock);
446 	mlx5dr_table_uninit(tbl);
447 	simple_free(tbl);
448 
449 	return 0;
450 
451 unlock_err:
452 	pthread_spin_unlock(&ctx->ctrl_lock);
453 	return -rte_errno;
454 }
455 
456 static struct mlx5dr_devx_obj *
457 mlx5dr_table_get_last_ft(struct mlx5dr_table *tbl)
458 {
459 	struct mlx5dr_devx_obj *last_ft = tbl->ft;
460 	struct mlx5dr_matcher *matcher;
461 
462 	LIST_FOREACH(matcher, &tbl->head, next)
463 		last_ft = matcher->end_ft;
464 
465 	return last_ft;
466 }
467 
468 int mlx5dr_table_ft_set_default_next_ft(struct mlx5dr_table *tbl,
469 					struct mlx5dr_devx_obj *ft_obj)
470 {
471 	struct mlx5dr_cmd_ft_modify_attr ft_attr = {0};
472 	int ret;
473 
474 	/* Due to FW limitation, resetting the flow table to default action will
475 	 * disconnect RTC when ignore_flow_level_rtc_valid is not supported.
476 	 */
477 	if (!tbl->ctx->caps->nic_ft.ignore_flow_level_rtc_valid)
478 		return 0;
479 
480 	if (tbl->type == MLX5DR_TABLE_TYPE_FDB)
481 		return mlx5dr_table_connect_to_default_miss_tbl(tbl, ft_obj);
482 
483 	ft_attr.type = tbl->fw_ft_type;
484 	ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION;
485 	ft_attr.table_miss_action = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION_DEFAULT;
486 
487 	ret = mlx5dr_cmd_flow_table_modify(ft_obj, &ft_attr);
488 	if (ret) {
489 		DR_LOG(ERR, "Failed to set FT default miss action");
490 		return ret;
491 	}
492 
493 	return 0;
494 }
495 
496 int mlx5dr_table_ft_set_next_rtc(struct mlx5dr_devx_obj *ft,
497 				 uint32_t fw_ft_type,
498 				 struct mlx5dr_devx_obj *rtc_0,
499 				 struct mlx5dr_devx_obj *rtc_1)
500 {
501 	struct mlx5dr_cmd_ft_modify_attr ft_attr = {0};
502 
503 	ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_RTC_ID;
504 	ft_attr.type = fw_ft_type;
505 	ft_attr.rtc_id_0 = rtc_0 ? rtc_0->id : 0;
506 	ft_attr.rtc_id_1 = rtc_1 ? rtc_1->id : 0;
507 
508 	return mlx5dr_cmd_flow_table_modify(ft, &ft_attr);
509 }
510 
511 static int mlx5dr_table_ft_set_next_ft(struct mlx5dr_devx_obj *ft,
512 				       uint32_t fw_ft_type,
513 				       uint32_t next_ft_id)
514 {
515 	struct mlx5dr_cmd_ft_modify_attr ft_attr = {0};
516 
517 	ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION;
518 	ft_attr.table_miss_action = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION_GOTO_TBL;
519 	ft_attr.type = fw_ft_type;
520 	ft_attr.table_miss_id = next_ft_id;
521 
522 	return mlx5dr_cmd_flow_table_modify(ft, &ft_attr);
523 }
524 
525 int mlx5dr_table_update_connected_miss_tables(struct mlx5dr_table *dst_tbl)
526 {
527 	struct mlx5dr_table *src_tbl;
528 	int ret;
529 
530 	if (LIST_EMPTY(&dst_tbl->default_miss.head))
531 		return 0;
532 
533 	LIST_FOREACH(src_tbl, &dst_tbl->default_miss.head, default_miss.next) {
534 		ret = mlx5dr_table_connect_to_miss_table(src_tbl, dst_tbl, false);
535 		if (ret) {
536 			DR_LOG(ERR, "Failed to update source miss table, unexpected behavior");
537 			return ret;
538 		}
539 	}
540 
541 	return 0;
542 }
543 
544 int mlx5dr_table_connect_src_ft_to_miss_table(struct mlx5dr_table *src_tbl,
545 					      struct mlx5dr_devx_obj *ft,
546 					      struct mlx5dr_table *dst_tbl)
547 {
548 	struct mlx5dr_matcher *matcher;
549 	int ret;
550 
551 	if (dst_tbl) {
552 		if (LIST_EMPTY(&dst_tbl->head)) {
553 			/* Connect src_tbl ft to dst_tbl start anchor */
554 			ret = mlx5dr_table_ft_set_next_ft(ft,
555 							  src_tbl->fw_ft_type,
556 							  dst_tbl->ft->id);
557 			if (ret)
558 				return ret;
559 
560 			/* Reset ft RTC to default RTC */
561 			ret = mlx5dr_table_ft_set_next_rtc(ft,
562 							   src_tbl->fw_ft_type,
563 							   NULL, NULL);
564 			if (ret)
565 				return ret;
566 		} else {
567 			/* Connect src_tbl ft to first matcher RTC */
568 			matcher = LIST_FIRST(&dst_tbl->head);
569 			ret = mlx5dr_table_ft_set_next_rtc(ft,
570 							   src_tbl->fw_ft_type,
571 							   matcher->match_ste.rtc_0,
572 							   matcher->match_ste.rtc_1);
573 			if (ret)
574 				return ret;
575 
576 			/* Reset next miss FT to default */
577 			ret = mlx5dr_table_ft_set_default_next_ft(src_tbl, ft);
578 			if (ret)
579 				return ret;
580 		}
581 	} else {
582 		/* Reset next miss FT to default */
583 		ret = mlx5dr_table_ft_set_default_next_ft(src_tbl, ft);
584 		if (ret)
585 			return ret;
586 
587 		/* Reset ft RTC to default RTC */
588 		ret = mlx5dr_table_ft_set_next_rtc(ft,
589 						   src_tbl->fw_ft_type,
590 						   NULL, NULL);
591 		if (ret)
592 			return ret;
593 	}
594 
595 	return 0;
596 }
597 
598 int mlx5dr_table_connect_to_miss_table(struct mlx5dr_table *src_tbl,
599 				       struct mlx5dr_table *dst_tbl,
600 				       bool only_update_last_ft)
601 {
602 	struct mlx5dr_matcher *matcher;
603 	struct mlx5dr_devx_obj *ft;
604 	int ret;
605 
606 	/* Connect last FT in the src_tbl matchers chain */
607 	ft = mlx5dr_table_get_last_ft(src_tbl);
608 	ret = mlx5dr_table_connect_src_ft_to_miss_table(src_tbl, ft, dst_tbl);
609 	if (ret)
610 		return ret;
611 
612 	if (!only_update_last_ft) {
613 		/* Connect isolated matchers FT */
614 		LIST_FOREACH(matcher, &src_tbl->isolated_matchers, next) {
615 			ft = matcher->end_ft;
616 			ret = mlx5dr_table_connect_src_ft_to_miss_table(src_tbl, ft, dst_tbl);
617 			if (ret)
618 				return ret;
619 		}
620 	}
621 
622 	src_tbl->default_miss.miss_tbl = dst_tbl;
623 
624 	return 0;
625 }
626 
627 static int mlx5dr_table_set_default_miss_not_valid(struct mlx5dr_table *tbl,
628 						   struct mlx5dr_table *miss_tbl)
629 {
630 	if (!tbl->ctx->caps->nic_ft.ignore_flow_level_rtc_valid ||
631 	    mlx5dr_context_shared_gvmi_used(tbl->ctx)) {
632 		DR_LOG(ERR, "Default miss table is not supported");
633 		rte_errno = EOPNOTSUPP;
634 		return -rte_errno;
635 	}
636 
637 	if (mlx5dr_table_is_root(tbl) ||
638 	    (miss_tbl && mlx5dr_table_is_root(miss_tbl)) ||
639 	    (miss_tbl && miss_tbl->type != tbl->type)) {
640 		DR_LOG(ERR, "Invalid arguments");
641 		rte_errno = EINVAL;
642 		return -rte_errno;
643 	}
644 
645 	return 0;
646 }
647 
648 int mlx5dr_table_set_default_miss(struct mlx5dr_table *tbl,
649 				  struct mlx5dr_table *miss_tbl)
650 {
651 	struct mlx5dr_context *ctx = tbl->ctx;
652 	struct mlx5dr_table *old_miss_tbl;
653 	int ret;
654 
655 	ret = mlx5dr_table_set_default_miss_not_valid(tbl, miss_tbl);
656 	if (ret)
657 		return ret;
658 
659 	pthread_spin_lock(&ctx->ctrl_lock);
660 	old_miss_tbl = tbl->default_miss.miss_tbl;
661 	ret = mlx5dr_table_connect_to_miss_table(tbl, miss_tbl, false);
662 	if (ret)
663 		goto out;
664 
665 	if (old_miss_tbl)
666 		LIST_REMOVE(tbl, default_miss.next);
667 
668 	if (miss_tbl)
669 		LIST_INSERT_HEAD(&miss_tbl->default_miss.head, tbl, default_miss.next);
670 
671 	pthread_spin_unlock(&ctx->ctrl_lock);
672 	return 0;
673 out:
674 	pthread_spin_unlock(&ctx->ctrl_lock);
675 	return -ret;
676 }
677