File Index
Files and directories
Sign in
FILE

table.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/steering/hws/table.c C
  1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
  2/* Copyright (c) 2024 NVIDIA Corporation & Affiliates */
  3
  4#include "internal.h"
  5
  6u32 mlx5hws_table_get_id(struct mlx5hws_table *tbl)
  7{
  8	return tbl->ft_id;
  9}
 10
 11static 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
 25static 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
 42static 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 */
 84static 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
101static 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
123int 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
155down_miss_tbl:
156	hws_table_down_default_fdb_miss_tbl(tbl);
157free_ft_obj:
158	mlx5hws_cmd_flow_table_destroy(mdev, ft_attr.type, *ft_id);
159	return ret;
160}
161
162void 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
169static 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
180static 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
216tbl_destroy:
217	mlx5hws_table_destroy_default_ft(tbl, tbl->ft_id);
218	mutex_unlock(&ctx->ctrl_lock);
219	return ret;
220}
221
222static 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
230struct 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
262free_tbl:
263	kfree(tbl);
264	return NULL;
265}
266
267int 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
293unlock_err:
294	mutex_unlock(&ctx->ctrl_lock);
295	return ret;
296}
297
298static 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
309int 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
336int 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
352int 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
367int 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
387int 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
451static 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
467int 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
491out:
492	mutex_unlock(&ctx->ctrl_lock);
493	return ret;
494}
~/ Read-only file preview.