xref: /linux/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/mlx5hws_table.c (revision 9410645520e9b820069761f3450ef6661418e279)
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
3 
4 #include "mlx5hws_internal.h"
5 
mlx5hws_table_get_id(struct mlx5hws_table * tbl)6 u32 mlx5hws_table_get_id(struct mlx5hws_table *tbl)
7 {
8 	return tbl->ft_id;
9 }
10 
hws_table_init_next_ft_attr(struct mlx5hws_table * tbl,struct mlx5hws_cmd_ft_create_attr * ft_attr)11 static void hws_table_init_next_ft_attr(struct mlx5hws_table *tbl,
12 					struct mlx5hws_cmd_ft_create_attr *ft_attr)
13 {
14 	ft_attr->type = tbl->fw_ft_type;
15 	if (tbl->type == MLX5HWS_TABLE_TYPE_FDB)
16 		ft_attr->level = tbl->ctx->caps->fdb_ft.max_level - 1;
17 	else
18 		ft_attr->level = tbl->ctx->caps->nic_ft.max_level - 1;
19 	ft_attr->rtc_valid = true;
20 }
21 
hws_table_set_cap_attr(struct mlx5hws_table * tbl,struct mlx5hws_cmd_ft_create_attr * ft_attr)22 static void hws_table_set_cap_attr(struct mlx5hws_table *tbl,
23 				   struct mlx5hws_cmd_ft_create_attr *ft_attr)
24 {
25 	/* Enabling reformat_en or decap_en for the first flow table
26 	 * must be done when all VFs are down.
27 	 * However, HWS doesn't know when it is required to create the first FT.
28 	 * On the other hand, HWS doesn't use all these FT capabilities at all
29 	 * (the API doesn't even provide a way to specify these flags), so we'll
30 	 * just set these caps on all the flow tables.
31 	 * If HCA_CAP.fdb_dynamic_tunnel is set, this constraint is N/A.
32 	 */
33 	if (!MLX5_CAP_ESW_FLOWTABLE(tbl->ctx->mdev, fdb_dynamic_tunnel)) {
34 		ft_attr->reformat_en = true;
35 		ft_attr->decap_en = true;
36 	}
37 }
38 
hws_table_up_default_fdb_miss_tbl(struct mlx5hws_table * tbl)39 static int hws_table_up_default_fdb_miss_tbl(struct mlx5hws_table *tbl)
40 {
41 	struct mlx5hws_cmd_ft_create_attr ft_attr = {0};
42 	struct mlx5hws_cmd_set_fte_attr fte_attr = {0};
43 	struct mlx5hws_cmd_forward_tbl *default_miss;
44 	struct mlx5hws_cmd_set_fte_dest dest = {0};
45 	struct mlx5hws_context *ctx = tbl->ctx;
46 	u8 tbl_type = tbl->type;
47 
48 	if (tbl->type != MLX5HWS_TABLE_TYPE_FDB)
49 		return 0;
50 
51 	if (ctx->common_res[tbl_type].default_miss) {
52 		ctx->common_res[tbl_type].default_miss->refcount++;
53 		return 0;
54 	}
55 
56 	ft_attr.type = tbl->fw_ft_type;
57 	ft_attr.level = tbl->ctx->caps->fdb_ft.max_level; /* The last level */
58 	ft_attr.rtc_valid = false;
59 
60 	dest.destination_type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
61 	dest.destination_id = ctx->caps->eswitch_manager_vport_number;
62 
63 	fte_attr.action_flags = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
64 	fte_attr.dests_num = 1;
65 	fte_attr.dests = &dest;
66 
67 	default_miss = mlx5hws_cmd_forward_tbl_create(ctx->mdev, &ft_attr, &fte_attr);
68 	if (!default_miss) {
69 		mlx5hws_err(ctx, "Failed to default miss table type: 0x%x\n", tbl_type);
70 		return -EINVAL;
71 	}
72 
73 	/* ctx->ctrl_lock must be held here */
74 	ctx->common_res[tbl_type].default_miss = default_miss;
75 	ctx->common_res[tbl_type].default_miss->refcount++;
76 
77 	return 0;
78 }
79 
80 /* Called under ctx->ctrl_lock */
hws_table_down_default_fdb_miss_tbl(struct mlx5hws_table * tbl)81 static void hws_table_down_default_fdb_miss_tbl(struct mlx5hws_table *tbl)
82 {
83 	struct mlx5hws_cmd_forward_tbl *default_miss;
84 	struct mlx5hws_context *ctx = tbl->ctx;
85 	u8 tbl_type = tbl->type;
86 
87 	if (tbl->type != MLX5HWS_TABLE_TYPE_FDB)
88 		return;
89 
90 	default_miss = ctx->common_res[tbl_type].default_miss;
91 	if (--default_miss->refcount)
92 		return;
93 
94 	mlx5hws_cmd_forward_tbl_destroy(ctx->mdev, default_miss);
95 	ctx->common_res[tbl_type].default_miss = NULL;
96 }
97 
hws_table_connect_to_default_miss_tbl(struct mlx5hws_table * tbl,u32 ft_id)98 static int hws_table_connect_to_default_miss_tbl(struct mlx5hws_table *tbl, u32 ft_id)
99 {
100 	struct mlx5hws_cmd_ft_modify_attr ft_attr = {0};
101 	int ret;
102 
103 	if (unlikely(tbl->type != MLX5HWS_TABLE_TYPE_FDB))
104 		pr_warn("HWS: invalid table type %d\n", tbl->type);
105 
106 	mlx5hws_cmd_set_attr_connect_miss_tbl(tbl->ctx,
107 					      tbl->fw_ft_type,
108 					      tbl->type,
109 					      &ft_attr);
110 
111 	ret = mlx5hws_cmd_flow_table_modify(tbl->ctx->mdev, &ft_attr, ft_id);
112 	if (ret) {
113 		mlx5hws_err(tbl->ctx, "Failed to connect FT to default FDB FT\n");
114 		return ret;
115 	}
116 
117 	return 0;
118 }
119 
mlx5hws_table_create_default_ft(struct mlx5_core_dev * mdev,struct mlx5hws_table * tbl,u32 * ft_id)120 int mlx5hws_table_create_default_ft(struct mlx5_core_dev *mdev,
121 				    struct mlx5hws_table *tbl,
122 				    u32 *ft_id)
123 {
124 	struct mlx5hws_cmd_ft_create_attr ft_attr = {0};
125 	int ret;
126 
127 	hws_table_init_next_ft_attr(tbl, &ft_attr);
128 	hws_table_set_cap_attr(tbl, &ft_attr);
129 
130 	ret = mlx5hws_cmd_flow_table_create(mdev, &ft_attr, ft_id);
131 	if (ret) {
132 		mlx5hws_err(tbl->ctx, "Failed creating default ft\n");
133 		return ret;
134 	}
135 
136 	if (tbl->type == MLX5HWS_TABLE_TYPE_FDB) {
137 		/* Take/create ref over the default miss */
138 		ret = hws_table_up_default_fdb_miss_tbl(tbl);
139 		if (ret) {
140 			mlx5hws_err(tbl->ctx, "Failed to get default fdb miss\n");
141 			goto free_ft_obj;
142 		}
143 		ret = hws_table_connect_to_default_miss_tbl(tbl, *ft_id);
144 		if (ret) {
145 			mlx5hws_err(tbl->ctx, "Failed connecting to default miss tbl\n");
146 			goto down_miss_tbl;
147 		}
148 	}
149 
150 	return 0;
151 
152 down_miss_tbl:
153 	hws_table_down_default_fdb_miss_tbl(tbl);
154 free_ft_obj:
155 	mlx5hws_cmd_flow_table_destroy(mdev, ft_attr.type, *ft_id);
156 	return ret;
157 }
158 
mlx5hws_table_destroy_default_ft(struct mlx5hws_table * tbl,u32 ft_id)159 void mlx5hws_table_destroy_default_ft(struct mlx5hws_table *tbl,
160 				      u32 ft_id)
161 {
162 	mlx5hws_cmd_flow_table_destroy(tbl->ctx->mdev, tbl->fw_ft_type, ft_id);
163 	hws_table_down_default_fdb_miss_tbl(tbl);
164 }
165 
hws_table_init_check_hws_support(struct mlx5hws_context * ctx,struct mlx5hws_table * tbl)166 static int hws_table_init_check_hws_support(struct mlx5hws_context *ctx,
167 					    struct mlx5hws_table *tbl)
168 {
169 	if (!(ctx->flags & MLX5HWS_CONTEXT_FLAG_HWS_SUPPORT)) {
170 		mlx5hws_err(ctx, "HWS not supported, cannot create mlx5hws_table\n");
171 		return -EOPNOTSUPP;
172 	}
173 
174 	return 0;
175 }
176 
hws_table_init(struct mlx5hws_table * tbl)177 static int hws_table_init(struct mlx5hws_table *tbl)
178 {
179 	struct mlx5hws_context *ctx = tbl->ctx;
180 	int ret;
181 
182 	ret = hws_table_init_check_hws_support(ctx, tbl);
183 	if (ret)
184 		return ret;
185 
186 	if (mlx5hws_table_get_fw_ft_type(tbl->type, (u8 *)&tbl->fw_ft_type)) {
187 		pr_warn("HWS: invalid table type %d\n", tbl->type);
188 		return -EOPNOTSUPP;
189 	}
190 
191 	mutex_lock(&ctx->ctrl_lock);
192 	ret = mlx5hws_table_create_default_ft(tbl->ctx->mdev, tbl, &tbl->ft_id);
193 	if (ret) {
194 		mlx5hws_err(tbl->ctx, "Failed to create flow table object\n");
195 		mutex_unlock(&ctx->ctrl_lock);
196 		return ret;
197 	}
198 
199 	ret = mlx5hws_action_get_default_stc(ctx, tbl->type);
200 	if (ret)
201 		goto tbl_destroy;
202 
203 	INIT_LIST_HEAD(&tbl->matchers_list);
204 	INIT_LIST_HEAD(&tbl->default_miss.head);
205 
206 	mutex_unlock(&ctx->ctrl_lock);
207 
208 	return 0;
209 
210 tbl_destroy:
211 	mlx5hws_table_destroy_default_ft(tbl, tbl->ft_id);
212 	mutex_unlock(&ctx->ctrl_lock);
213 	return ret;
214 }
215 
hws_table_uninit(struct mlx5hws_table * tbl)216 static void hws_table_uninit(struct mlx5hws_table *tbl)
217 {
218 	mutex_lock(&tbl->ctx->ctrl_lock);
219 	mlx5hws_action_put_default_stc(tbl->ctx, tbl->type);
220 	mlx5hws_table_destroy_default_ft(tbl, tbl->ft_id);
221 	mutex_unlock(&tbl->ctx->ctrl_lock);
222 }
223 
mlx5hws_table_create(struct mlx5hws_context * ctx,struct mlx5hws_table_attr * attr)224 struct mlx5hws_table *mlx5hws_table_create(struct mlx5hws_context *ctx,
225 					   struct mlx5hws_table_attr *attr)
226 {
227 	struct mlx5hws_table *tbl;
228 	int ret;
229 
230 	if (attr->type > MLX5HWS_TABLE_TYPE_FDB) {
231 		mlx5hws_err(ctx, "Invalid table type %d\n", attr->type);
232 		return NULL;
233 	}
234 
235 	tbl = kzalloc(sizeof(*tbl), GFP_KERNEL);
236 	if (!tbl)
237 		return NULL;
238 
239 	tbl->ctx = ctx;
240 	tbl->type = attr->type;
241 	tbl->level = attr->level;
242 
243 	ret = hws_table_init(tbl);
244 	if (ret) {
245 		mlx5hws_err(ctx, "Failed to initialise table\n");
246 		goto free_tbl;
247 	}
248 
249 	mutex_lock(&ctx->ctrl_lock);
250 	list_add(&tbl->tbl_list_node, &ctx->tbl_list);
251 	mutex_unlock(&ctx->ctrl_lock);
252 
253 	return tbl;
254 
255 free_tbl:
256 	kfree(tbl);
257 	return NULL;
258 }
259 
mlx5hws_table_destroy(struct mlx5hws_table * tbl)260 int mlx5hws_table_destroy(struct mlx5hws_table *tbl)
261 {
262 	struct mlx5hws_context *ctx = tbl->ctx;
263 	int ret;
264 
265 	mutex_lock(&ctx->ctrl_lock);
266 	if (!list_empty(&tbl->matchers_list)) {
267 		mlx5hws_err(tbl->ctx, "Cannot destroy table containing matchers\n");
268 		ret = -EBUSY;
269 		goto unlock_err;
270 	}
271 
272 	if (!list_empty(&tbl->default_miss.head)) {
273 		mlx5hws_err(tbl->ctx, "Cannot destroy table pointed by default miss\n");
274 		ret = -EBUSY;
275 		goto unlock_err;
276 	}
277 
278 	list_del_init(&tbl->tbl_list_node);
279 	mutex_unlock(&ctx->ctrl_lock);
280 
281 	hws_table_uninit(tbl);
282 	kfree(tbl);
283 
284 	return 0;
285 
286 unlock_err:
287 	mutex_unlock(&ctx->ctrl_lock);
288 	return ret;
289 }
290 
hws_table_get_last_ft(struct mlx5hws_table * tbl)291 static u32 hws_table_get_last_ft(struct mlx5hws_table *tbl)
292 {
293 	struct mlx5hws_matcher *matcher;
294 
295 	if (list_empty(&tbl->matchers_list))
296 		return tbl->ft_id;
297 
298 	matcher = list_last_entry(&tbl->matchers_list, struct mlx5hws_matcher, list_node);
299 	return matcher->end_ft_id;
300 }
301 
mlx5hws_table_ft_set_default_next_ft(struct mlx5hws_table * tbl,u32 ft_id)302 int mlx5hws_table_ft_set_default_next_ft(struct mlx5hws_table *tbl, u32 ft_id)
303 {
304 	struct mlx5hws_cmd_ft_modify_attr ft_attr = {0};
305 	int ret;
306 
307 	/* Due to FW limitation, resetting the flow table to default action will
308 	 * disconnect RTC when ignore_flow_level_rtc_valid is not supported.
309 	 */
310 	if (!tbl->ctx->caps->nic_ft.ignore_flow_level_rtc_valid)
311 		return 0;
312 
313 	if (tbl->type == MLX5HWS_TABLE_TYPE_FDB)
314 		return hws_table_connect_to_default_miss_tbl(tbl, ft_id);
315 
316 	ft_attr.type = tbl->fw_ft_type;
317 	ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION;
318 	ft_attr.table_miss_action = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION_DEFAULT;
319 
320 	ret = mlx5hws_cmd_flow_table_modify(tbl->ctx->mdev, &ft_attr, ft_id);
321 	if (ret) {
322 		mlx5hws_err(tbl->ctx, "Failed to set FT default miss action\n");
323 		return ret;
324 	}
325 
326 	return 0;
327 }
328 
mlx5hws_table_ft_set_next_rtc(struct mlx5hws_context * ctx,u32 ft_id,u32 fw_ft_type,u32 rtc_0_id,u32 rtc_1_id)329 int mlx5hws_table_ft_set_next_rtc(struct mlx5hws_context *ctx,
330 				  u32 ft_id,
331 				  u32 fw_ft_type,
332 				  u32 rtc_0_id,
333 				  u32 rtc_1_id)
334 {
335 	struct mlx5hws_cmd_ft_modify_attr ft_attr = {0};
336 
337 	ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_RTC_ID;
338 	ft_attr.type = fw_ft_type;
339 	ft_attr.rtc_id_0 = rtc_0_id;
340 	ft_attr.rtc_id_1 = rtc_1_id;
341 
342 	return mlx5hws_cmd_flow_table_modify(ctx->mdev, &ft_attr, ft_id);
343 }
344 
hws_table_ft_set_next_ft(struct mlx5hws_context * ctx,u32 ft_id,u32 fw_ft_type,u32 next_ft_id)345 static int hws_table_ft_set_next_ft(struct mlx5hws_context *ctx,
346 				    u32 ft_id,
347 				    u32 fw_ft_type,
348 				    u32 next_ft_id)
349 {
350 	struct mlx5hws_cmd_ft_modify_attr ft_attr = {0};
351 
352 	ft_attr.modify_fs = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION;
353 	ft_attr.table_miss_action = MLX5_IFC_MODIFY_FLOW_TABLE_MISS_ACTION_GOTO_TBL;
354 	ft_attr.type = fw_ft_type;
355 	ft_attr.table_miss_id = next_ft_id;
356 
357 	return mlx5hws_cmd_flow_table_modify(ctx->mdev, &ft_attr, ft_id);
358 }
359 
mlx5hws_table_update_connected_miss_tables(struct mlx5hws_table * dst_tbl)360 int mlx5hws_table_update_connected_miss_tables(struct mlx5hws_table *dst_tbl)
361 {
362 	struct mlx5hws_table *src_tbl;
363 	int ret;
364 
365 	if (list_empty(&dst_tbl->default_miss.head))
366 		return 0;
367 
368 	list_for_each_entry(src_tbl, &dst_tbl->default_miss.head, default_miss.next) {
369 		ret = mlx5hws_table_connect_to_miss_table(src_tbl, dst_tbl);
370 		if (ret) {
371 			mlx5hws_err(dst_tbl->ctx,
372 				    "Failed to update source miss table, unexpected behavior\n");
373 			return ret;
374 		}
375 	}
376 
377 	return 0;
378 }
379 
mlx5hws_table_connect_to_miss_table(struct mlx5hws_table * src_tbl,struct mlx5hws_table * dst_tbl)380 int mlx5hws_table_connect_to_miss_table(struct mlx5hws_table *src_tbl,
381 					struct mlx5hws_table *dst_tbl)
382 {
383 	struct mlx5hws_matcher *matcher;
384 	u32 last_ft_id;
385 	int ret;
386 
387 	last_ft_id = hws_table_get_last_ft(src_tbl);
388 
389 	if (dst_tbl) {
390 		if (list_empty(&dst_tbl->matchers_list)) {
391 			/* Connect src_tbl last_ft to dst_tbl start anchor */
392 			ret = hws_table_ft_set_next_ft(src_tbl->ctx,
393 						       last_ft_id,
394 						       src_tbl->fw_ft_type,
395 						       dst_tbl->ft_id);
396 			if (ret)
397 				return ret;
398 
399 			/* Reset last_ft RTC to default RTC */
400 			ret = mlx5hws_table_ft_set_next_rtc(src_tbl->ctx,
401 							    last_ft_id,
402 							    src_tbl->fw_ft_type,
403 							    0, 0);
404 			if (ret)
405 				return ret;
406 		} else {
407 			/* Connect src_tbl last_ft to first matcher RTC */
408 			matcher = list_first_entry(&dst_tbl->matchers_list,
409 						   struct mlx5hws_matcher,
410 						   list_node);
411 			ret = mlx5hws_table_ft_set_next_rtc(src_tbl->ctx,
412 							    last_ft_id,
413 							    src_tbl->fw_ft_type,
414 							    matcher->match_ste.rtc_0_id,
415 							    matcher->match_ste.rtc_1_id);
416 			if (ret)
417 				return ret;
418 
419 			/* Reset next miss FT to default */
420 			ret = mlx5hws_table_ft_set_default_next_ft(src_tbl, last_ft_id);
421 			if (ret)
422 				return ret;
423 		}
424 	} else {
425 		/* Reset next miss FT to default */
426 		ret = mlx5hws_table_ft_set_default_next_ft(src_tbl, last_ft_id);
427 		if (ret)
428 			return ret;
429 
430 		/* Reset last_ft RTC to default RTC */
431 		ret = mlx5hws_table_ft_set_next_rtc(src_tbl->ctx,
432 						    last_ft_id,
433 						    src_tbl->fw_ft_type,
434 						    0, 0);
435 		if (ret)
436 			return ret;
437 	}
438 
439 	src_tbl->default_miss.miss_tbl = dst_tbl;
440 
441 	return 0;
442 }
443 
hws_table_set_default_miss_not_valid(struct mlx5hws_table * tbl,struct mlx5hws_table * miss_tbl)444 static int hws_table_set_default_miss_not_valid(struct mlx5hws_table *tbl,
445 						struct mlx5hws_table *miss_tbl)
446 {
447 	if (!tbl->ctx->caps->nic_ft.ignore_flow_level_rtc_valid) {
448 		mlx5hws_err(tbl->ctx, "Default miss table is not supported\n");
449 		return -EOPNOTSUPP;
450 	}
451 
452 	if ((miss_tbl && miss_tbl->type != tbl->type)) {
453 		mlx5hws_err(tbl->ctx, "Invalid arguments\n");
454 		return -EINVAL;
455 	}
456 
457 	return 0;
458 }
459 
mlx5hws_table_set_default_miss(struct mlx5hws_table * tbl,struct mlx5hws_table * miss_tbl)460 int mlx5hws_table_set_default_miss(struct mlx5hws_table *tbl,
461 				   struct mlx5hws_table *miss_tbl)
462 {
463 	struct mlx5hws_context *ctx = tbl->ctx;
464 	struct mlx5hws_table *old_miss_tbl;
465 	int ret;
466 
467 	ret = hws_table_set_default_miss_not_valid(tbl, miss_tbl);
468 	if (ret)
469 		return ret;
470 
471 	mutex_lock(&ctx->ctrl_lock);
472 
473 	old_miss_tbl = tbl->default_miss.miss_tbl;
474 	ret = mlx5hws_table_connect_to_miss_table(tbl, miss_tbl);
475 	if (ret)
476 		goto out;
477 
478 	if (old_miss_tbl)
479 		list_del_init(&tbl->default_miss.next);
480 
481 	old_miss_tbl = tbl->default_miss.miss_tbl;
482 	if (old_miss_tbl)
483 		list_del_init(&old_miss_tbl->default_miss.head);
484 
485 	if (miss_tbl)
486 		list_add(&tbl->default_miss.next, &miss_tbl->default_miss.head);
487 
488 	mutex_unlock(&ctx->ctrl_lock);
489 	return 0;
490 out:
491 	mutex_unlock(&ctx->ctrl_lock);
492 	return ret;
493 }
494