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