File Index
Files and directories
Sign in
FILE

fs_chains.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/lib/fs_chains.c C
  1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
  2// Copyright (c) 2020 Mellanox Technologies.
  3
  4#include <linux/mlx5/driver.h>
  5#include <linux/mlx5/mlx5_ifc.h>
  6#include <linux/mlx5/fs.h>
  7
  8#include "lib/fs_chains.h"
  9#include "fs_ft_pool.h"
 10#include "en/mapping.h"
 11#include "fs_core.h"
 12#include "en_tc.h"
 13
 14#define chains_lock(chains) ((chains)->lock)
 15#define chains_ht(chains) ((chains)->chains_ht)
 16#define prios_ht(chains) ((chains)->prios_ht)
 17#define chains_default_ft(chains) ((chains)->chains_default_ft)
 18#define chains_end_ft(chains) ((chains)->chains_end_ft)
 19#define FT_TBL_SZ (64 * 1024)
 20
 21struct mlx5_fs_chains {
 22	struct mlx5_core_dev *dev;
 23
 24	struct rhashtable chains_ht;
 25	struct rhashtable prios_ht;
 26	/* Protects above chains_ht and prios_ht */
 27	struct mutex lock;
 28
 29	struct mlx5_flow_table *chains_default_ft;
 30	struct mlx5_flow_table *chains_end_ft;
 31	struct mapping_ctx *chains_mapping;
 32
 33	enum mlx5_flow_namespace_type ns;
 34	u32 group_num;
 35	u32 flags;
 36	int fs_base_prio;
 37	int fs_base_level;
 38};
 39
 40struct fs_chain {
 41	struct rhash_head node;
 42
 43	u32 chain;
 44
 45	int ref;
 46	int id;
 47
 48	struct mlx5_fs_chains *chains;
 49	struct list_head prios_list;
 50	struct mlx5_flow_handle *restore_rule;
 51	struct mlx5_modify_hdr *miss_modify_hdr;
 52};
 53
 54struct prio_key {
 55	u32 chain;
 56	u32 prio;
 57	u32 level;
 58};
 59
 60struct prio {
 61	struct rhash_head node;
 62	struct list_head list;
 63
 64	struct prio_key key;
 65
 66	int ref;
 67
 68	struct fs_chain *chain;
 69	struct mlx5_flow_table *ft;
 70	struct mlx5_flow_table *next_ft;
 71	struct mlx5_flow_group *miss_group;
 72	struct mlx5_flow_handle *miss_rule;
 73};
 74
 75static const struct rhashtable_params chain_params = {
 76	.head_offset = offsetof(struct fs_chain, node),
 77	.key_offset = offsetof(struct fs_chain, chain),
 78	.key_len = sizeof_field(struct fs_chain, chain),
 79	.automatic_shrinking = true,
 80};
 81
 82static const struct rhashtable_params prio_params = {
 83	.head_offset = offsetof(struct prio, node),
 84	.key_offset = offsetof(struct prio, key),
 85	.key_len = sizeof_field(struct prio, key),
 86	.automatic_shrinking = true,
 87};
 88
 89bool mlx5_chains_prios_supported(struct mlx5_fs_chains *chains)
 90{
 91	return chains->flags & MLX5_CHAINS_AND_PRIOS_SUPPORTED;
 92}
 93
 94bool mlx5_chains_ignore_flow_level_supported(struct mlx5_fs_chains *chains)
 95{
 96	return chains->flags & MLX5_CHAINS_IGNORE_FLOW_LEVEL_SUPPORTED;
 97}
 98
 99bool mlx5_chains_backwards_supported(struct mlx5_fs_chains *chains)
100{
101	return mlx5_chains_prios_supported(chains) &&
102	       mlx5_chains_ignore_flow_level_supported(chains);
103}
104
105u32 mlx5_chains_get_chain_range(struct mlx5_fs_chains *chains)
106{
107	if (!mlx5_chains_prios_supported(chains))
108		return 1;
109
110	if (mlx5_chains_ignore_flow_level_supported(chains))
111		return UINT_MAX - 1;
112
113	/* We should get here only for eswitch case */
114	return FDB_TC_MAX_CHAIN;
115}
116
117u32 mlx5_chains_get_nf_ft_chain(struct mlx5_fs_chains *chains)
118{
119	return mlx5_chains_get_chain_range(chains) + 1;
120}
121
122u32 mlx5_chains_get_prio_range(struct mlx5_fs_chains *chains)
123{
124	if (mlx5_chains_ignore_flow_level_supported(chains))
125		return UINT_MAX;
126
127	if (!chains->dev->priv.eswitch ||
128	    chains->dev->priv.eswitch->mode != MLX5_ESWITCH_OFFLOADS)
129		return 1;
130
131	/* We should get here only for eswitch case */
132	return FDB_TC_MAX_PRIO;
133}
134
135static unsigned int mlx5_chains_get_level_range(struct mlx5_fs_chains *chains)
136{
137	if (mlx5_chains_ignore_flow_level_supported(chains))
138		return UINT_MAX;
139
140	/* Same value for FDB and NIC RX tables */
141	return FDB_TC_LEVELS_PER_PRIO;
142}
143
144void
145mlx5_chains_set_end_ft(struct mlx5_fs_chains *chains,
146		       struct mlx5_flow_table *ft)
147{
148	chains_end_ft(chains) = ft;
149}
150
151static struct mlx5_flow_table *
152mlx5_chains_create_table(struct mlx5_fs_chains *chains,
153			 u32 chain, u32 prio, u32 level)
154{
155	struct mlx5_flow_table_attr ft_attr = {};
156	struct mlx5_flow_namespace *ns;
157	struct mlx5_flow_table *ft;
158	int sz;
159
160	if (chains->flags & MLX5_CHAINS_FT_TUNNEL_SUPPORTED)
161		ft_attr.flags |= (MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT |
162				  MLX5_FLOW_TABLE_TUNNEL_EN_DECAP);
163
164	sz = (chain == mlx5_chains_get_nf_ft_chain(chains)) ?
165		FT_TBL_SZ : MLX5_FS_MAX_POOL_SIZE;
166	ft_attr.max_fte = sz;
167
168	/* We use chains_default_ft(chains) as the table's next_ft till
169	 * ignore_flow_level is allowed on FT creation and not just for FTEs.
170	 * Instead caller should add an explicit miss rule if needed.
171	 */
172	ft_attr.next_ft = chains_default_ft(chains);
173
174	/* The root table(chain 0, prio 1, level 0) is required to be
175	 * connected to the previous fs_core managed prio.
176	 * We always create it, as a managed table, in order to align with
177	 * fs_core logic.
178	 */
179	if (!mlx5_chains_ignore_flow_level_supported(chains) ||
180	    (chain == 0 && prio == 1 && level == 0)) {
181		ft_attr.level = chains->fs_base_level;
182		ft_attr.prio = chains->fs_base_prio + prio - 1;
183		ns = (chains->ns == MLX5_FLOW_NAMESPACE_FDB) ?
184			mlx5_get_fdb_sub_ns(chains->dev, chain) :
185			mlx5_get_flow_namespace(chains->dev, chains->ns);
186	} else {
187		ft_attr.flags |= MLX5_FLOW_TABLE_UNMANAGED;
188		ft_attr.prio = chains->fs_base_prio;
189		/* Firmware doesn't allow us to create another level 0 table,
190		 * so we create all unmanaged tables as level 1 (base + 1).
191		 *
192		 * To connect them, we use explicit miss rules with
193		 * ignore_flow_level. Caller is responsible to create
194		 * these rules (if needed).
195		 */
196		ft_attr.level = chains->fs_base_level + 1;
197		ns = mlx5_get_flow_namespace(chains->dev, chains->ns);
198	}
199
200	if (!ns) {
201		mlx5_core_warn(chains->dev, "Failed to get flow namespace\n");
202		return ERR_PTR(-EOPNOTSUPP);
203	}
204
205	ft_attr.autogroup.num_reserved_entries = 2;
206	ft_attr.autogroup.max_num_groups = chains->group_num;
207	ft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr);
208	if (IS_ERR(ft)) {
209		mlx5_core_warn(chains->dev, "Failed to create chains table err %d (chain: %d, prio: %d, level: %d, size: %d)\n",
210			       (int)PTR_ERR(ft), chain, prio, level, sz);
211		return ft;
212	}
213
214	return ft;
215}
216
217static int
218create_chain_restore(struct fs_chain *chain)
219{
220	struct mlx5_eswitch *esw = chain->chains->dev->priv.eswitch;
221	u8 modact[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {};
222	struct mlx5_fs_chains *chains = chain->chains;
223	enum mlx5e_tc_attr_to_reg mapped_obj_to_reg;
224	struct mlx5_modify_hdr *mod_hdr;
225	u32 index;
226	int err;
227
228	if (chain->chain == mlx5_chains_get_nf_ft_chain(chains) ||
229	    !mlx5_chains_prios_supported(chains) ||
230	    !chains->chains_mapping)
231		return 0;
232
233	err = mlx5_chains_get_chain_mapping(chains, chain->chain, &index);
234	if (err)
235		return err;
236	if (index == MLX5_FS_DEFAULT_FLOW_TAG) {
237		/* we got the special default flow tag id, so we won't know
238		 * if we actually marked the packet with the restore rule
239		 * we create.
240		 *
241		 * This case isn't possible with MLX5_FS_DEFAULT_FLOW_TAG = 0.
242		 */
243		err = mlx5_chains_get_chain_mapping(chains, chain->chain, &index);
244		mapping_remove(chains->chains_mapping, MLX5_FS_DEFAULT_FLOW_TAG);
245		if (err)
246			return err;
247	}
248
249	chain->id = index;
250
251	if (chains->ns == MLX5_FLOW_NAMESPACE_FDB) {
252		mapped_obj_to_reg = MAPPED_OBJ_TO_REG;
253		chain->restore_rule = esw_add_restore_rule(esw, chain->id);
254		if (IS_ERR(chain->restore_rule)) {
255			err = PTR_ERR(chain->restore_rule);
256			goto err_rule;
257		}
258	} else if (chains->ns == MLX5_FLOW_NAMESPACE_KERNEL) {
259		/* For NIC RX we don't need a restore rule
260		 * since we write the metadata to reg_b
261		 * that is passed to SW directly.
262		 */
263		mapped_obj_to_reg = NIC_MAPPED_OBJ_TO_REG;
264	} else {
265		err = -EINVAL;
266		goto err_rule;
267	}
268
269	MLX5_SET(set_action_in, modact, action_type, MLX5_ACTION_TYPE_SET);
270	MLX5_SET(set_action_in, modact, field,
271		 mlx5e_tc_attr_to_reg_mappings[mapped_obj_to_reg].mfield);
272	MLX5_SET(set_action_in, modact, offset,
273		 mlx5e_tc_attr_to_reg_mappings[mapped_obj_to_reg].moffset);
274	MLX5_SET(set_action_in, modact, length,
275		 mlx5e_tc_attr_to_reg_mappings[mapped_obj_to_reg].mlen == 32 ?
276		 0 : mlx5e_tc_attr_to_reg_mappings[mapped_obj_to_reg].mlen);
277	MLX5_SET(set_action_in, modact, data, chain->id);
278	mod_hdr = mlx5_modify_header_alloc(chains->dev, chains->ns,
279					   1, modact);
280	if (IS_ERR(mod_hdr)) {
281		err = PTR_ERR(mod_hdr);
282		goto err_mod_hdr;
283	}
284	chain->miss_modify_hdr = mod_hdr;
285
286	return 0;
287
288err_mod_hdr:
289	if (!IS_ERR_OR_NULL(chain->restore_rule))
290		mlx5_del_flow_rules(chain->restore_rule);
291err_rule:
292	/* Datapath can't find this mapping, so we can safely remove it */
293	mapping_remove(chains->chains_mapping, chain->id);
294	return err;
295}
296
297static void destroy_chain_restore(struct fs_chain *chain)
298{
299	struct mlx5_fs_chains *chains = chain->chains;
300
301	if (!chain->miss_modify_hdr)
302		return;
303
304	if (chain->restore_rule)
305		mlx5_del_flow_rules(chain->restore_rule);
306
307	mlx5_modify_header_dealloc(chains->dev, chain->miss_modify_hdr);
308	mapping_remove(chains->chains_mapping, chain->id);
309}
310
311static struct fs_chain *
312mlx5_chains_create_chain(struct mlx5_fs_chains *chains, u32 chain)
313{
314	struct fs_chain *chain_s = NULL;
315	int err;
316
317	chain_s = kvzalloc(sizeof(*chain_s), GFP_KERNEL);
318	if (!chain_s)
319		return ERR_PTR(-ENOMEM);
320
321	chain_s->chains = chains;
322	chain_s->chain = chain;
323	INIT_LIST_HEAD(&chain_s->prios_list);
324
325	err = create_chain_restore(chain_s);
326	if (err)
327		goto err_restore;
328
329	err = rhashtable_insert_fast(&chains_ht(chains), &chain_s->node,
330				     chain_params);
331	if (err)
332		goto err_insert;
333
334	return chain_s;
335
336err_insert:
337	destroy_chain_restore(chain_s);
338err_restore:
339	kvfree(chain_s);
340	return ERR_PTR(err);
341}
342
343static void
344mlx5_chains_destroy_chain(struct fs_chain *chain)
345{
346	struct mlx5_fs_chains *chains = chain->chains;
347
348	rhashtable_remove_fast(&chains_ht(chains), &chain->node,
349			       chain_params);
350
351	destroy_chain_restore(chain);
352	kvfree(chain);
353}
354
355static struct fs_chain *
356mlx5_chains_get_chain(struct mlx5_fs_chains *chains, u32 chain)
357{
358	struct fs_chain *chain_s;
359
360	chain_s = rhashtable_lookup_fast(&chains_ht(chains), &chain,
361					 chain_params);
362	if (!chain_s) {
363		chain_s = mlx5_chains_create_chain(chains, chain);
364		if (IS_ERR(chain_s))
365			return chain_s;
366	}
367
368	chain_s->ref++;
369
370	return chain_s;
371}
372
373static struct mlx5_flow_handle *
374mlx5_chains_add_miss_rule(struct fs_chain *chain,
375			  struct mlx5_flow_table *ft,
376			  struct mlx5_flow_table *next_ft)
377{
378	struct mlx5_fs_chains *chains = chain->chains;
379	struct mlx5_flow_destination dest = {};
380	struct mlx5_flow_act act = {};
381
382	act.flags  = FLOW_ACT_NO_APPEND;
383	if (mlx5_chains_ignore_flow_level_supported(chain->chains))
384		act.flags |= FLOW_ACT_IGNORE_FLOW_LEVEL;
385
386	act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
387	dest.type  = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
388	dest.ft = next_ft;
389
390	if (chains->chains_mapping && next_ft == chains_end_ft(chains) &&
391	    chain->chain != mlx5_chains_get_nf_ft_chain(chains) &&
392	    mlx5_chains_prios_supported(chains)) {
393		act.modify_hdr = chain->miss_modify_hdr;
394		act.action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
395	}
396
397	return mlx5_add_flow_rules(ft, NULL, &act, &dest, 1);
398}
399
400static int
401mlx5_chains_update_prio_prevs(struct prio *prio,
402			      struct mlx5_flow_table *next_ft)
403{
404	struct mlx5_flow_handle *miss_rules[FDB_TC_LEVELS_PER_PRIO + 1] = {};
405	struct fs_chain *chain = prio->chain;
406	struct prio *pos;
407	int n = 0, err;
408
409	if (prio->key.level)
410		return 0;
411
412	/* Iterate in reverse order until reaching the level 0 rule of
413	 * the previous priority, adding all the miss rules first, so we can
414	 * revert them if any of them fails.
415	 */
416	pos = prio;
417	list_for_each_entry_continue_reverse(pos,
418					     &chain->prios_list,
419					     list) {
420		miss_rules[n] = mlx5_chains_add_miss_rule(chain,
421							  pos->ft,
422							  next_ft);
423		if (IS_ERR(miss_rules[n])) {
424			err = PTR_ERR(miss_rules[n]);
425			goto err_prev_rule;
426		}
427
428		n++;
429		if (!pos->key.level)
430			break;
431	}
432
433	/* Success, delete old miss rules, and update the pointers. */
434	n = 0;
435	pos = prio;
436	list_for_each_entry_continue_reverse(pos,
437					     &chain->prios_list,
438					     list) {
439		mlx5_del_flow_rules(pos->miss_rule);
440
441		pos->miss_rule = miss_rules[n];
442		pos->next_ft = next_ft;
443
444		n++;
445		if (!pos->key.level)
446			break;
447	}
448
449	return 0;
450
451err_prev_rule:
452	while (--n >= 0)
453		mlx5_del_flow_rules(miss_rules[n]);
454
455	return err;
456}
457
458static void
459mlx5_chains_put_chain(struct fs_chain *chain)
460{
461	if (--chain->ref == 0)
462		mlx5_chains_destroy_chain(chain);
463}
464
465static struct prio *
466mlx5_chains_create_prio(struct mlx5_fs_chains *chains,
467			u32 chain, u32 prio, u32 level)
468{
469	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
470	struct mlx5_flow_handle *miss_rule;
471	struct mlx5_flow_group *miss_group;
472	struct mlx5_flow_table *next_ft;
473	struct mlx5_flow_table *ft;
474	struct fs_chain *chain_s;
475	struct list_head *pos;
476	struct prio *prio_s;
477	u32 *flow_group_in;
478	int err;
479
480	chain_s = mlx5_chains_get_chain(chains, chain);
481	if (IS_ERR(chain_s))
482		return ERR_CAST(chain_s);
483
484	prio_s = kvzalloc(sizeof(*prio_s), GFP_KERNEL);
485	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
486	if (!prio_s || !flow_group_in) {
487		err = -ENOMEM;
488		goto err_alloc;
489	}
490
491	/* Chain's prio list is sorted by prio and level.
492	 * And all levels of some prio point to the next prio's level 0.
493	 * Example list (prio, level):
494	 * (3,0)->(3,1)->(5,0)->(5,1)->(6,1)->(7,0)
495	 * In hardware, we will we have the following pointers:
496	 * (3,0) -> (5,0) -> (7,0) -> Slow path
497	 * (3,1) -> (5,0)
498	 * (5,1) -> (7,0)
499	 * (6,1) -> (7,0)
500	 */
501
502	/* Default miss for each chain: */
503	next_ft = (chain == mlx5_chains_get_nf_ft_chain(chains)) ?
504		  chains_default_ft(chains) :
505		  chains_end_ft(chains);
506	list_for_each(pos, &chain_s->prios_list) {
507		struct prio *p = list_entry(pos, struct prio, list);
508
509		/* exit on first pos that is larger */
510		if (prio < p->key.prio || (prio == p->key.prio &&
511					   level < p->key.level)) {
512			/* Get next level 0 table */
513			next_ft = p->key.level == 0 ? p->ft : p->next_ft;
514			break;
515		}
516	}
517
518	ft = mlx5_chains_create_table(chains, chain, prio, level);
519	if (IS_ERR(ft)) {
520		err = PTR_ERR(ft);
521		goto err_create;
522	}
523
524	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index,
525		 ft->max_fte - 2);
526	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index,
527		 ft->max_fte - 1);
528	miss_group = mlx5_create_flow_group(ft, flow_group_in);
529	if (IS_ERR(miss_group)) {
530		err = PTR_ERR(miss_group);
531		goto err_group;
532	}
533
534	/* Add miss rule to next_ft */
535	miss_rule = mlx5_chains_add_miss_rule(chain_s, ft, next_ft);
536	if (IS_ERR(miss_rule)) {
537		err = PTR_ERR(miss_rule);
538		goto err_miss_rule;
539	}
540
541	prio_s->miss_group = miss_group;
542	prio_s->miss_rule = miss_rule;
543	prio_s->next_ft = next_ft;
544	prio_s->chain = chain_s;
545	prio_s->key.chain = chain;
546	prio_s->key.prio = prio;
547	prio_s->key.level = level;
548	prio_s->ft = ft;
549
550	err = rhashtable_insert_fast(&prios_ht(chains), &prio_s->node,
551				     prio_params);
552	if (err)
553		goto err_insert;
554
555	list_add(&prio_s->list, pos->prev);
556
557	/* Table is ready, connect it */
558	err = mlx5_chains_update_prio_prevs(prio_s, ft);
559	if (err)
560		goto err_update;
561
562	kvfree(flow_group_in);
563	return prio_s;
564
565err_update:
566	list_del(&prio_s->list);
567	rhashtable_remove_fast(&prios_ht(chains), &prio_s->node,
568			       prio_params);
569err_insert:
570	mlx5_del_flow_rules(miss_rule);
571err_miss_rule:
572	mlx5_destroy_flow_group(miss_group);
573err_group:
574	mlx5_destroy_flow_table(ft);
575err_create:
576err_alloc:
577	kvfree(prio_s);
578	kvfree(flow_group_in);
579	mlx5_chains_put_chain(chain_s);
580	return ERR_PTR(err);
581}
582
583static void
584mlx5_chains_destroy_prio(struct mlx5_fs_chains *chains,
585			 struct prio *prio)
586{
587	struct fs_chain *chain = prio->chain;
588
589	WARN_ON(mlx5_chains_update_prio_prevs(prio,
590					      prio->next_ft));
591
592	list_del(&prio->list);
593	rhashtable_remove_fast(&prios_ht(chains), &prio->node,
594			       prio_params);
595	mlx5_del_flow_rules(prio->miss_rule);
596	mlx5_destroy_flow_group(prio->miss_group);
597	mlx5_destroy_flow_table(prio->ft);
598	mlx5_chains_put_chain(chain);
599	kvfree(prio);
600}
601
602struct mlx5_flow_table *
603mlx5_chains_get_table(struct mlx5_fs_chains *chains, u32 chain, u32 prio,
604		      u32 level)
605{
606	struct mlx5_flow_table *prev_fts;
607	struct prio *prio_s;
608	struct prio_key key;
609	int l = 0;
610
611	if ((chain > mlx5_chains_get_chain_range(chains) &&
612	     chain != mlx5_chains_get_nf_ft_chain(chains)) ||
613	    prio > mlx5_chains_get_prio_range(chains) ||
614	    level > mlx5_chains_get_level_range(chains))
615		return ERR_PTR(-EOPNOTSUPP);
616
617	/* create earlier levels for correct fs_core lookup when
618	 * connecting tables.
619	 */
620	for (l = 0; l < level; l++) {
621		prev_fts = mlx5_chains_get_table(chains, chain, prio, l);
622		if (IS_ERR(prev_fts)) {
623			prio_s = ERR_CAST(prev_fts);
624			goto err_get_prevs;
625		}
626	}
627
628	key.chain = chain;
629	key.prio = prio;
630	key.level = level;
631
632	mutex_lock(&chains_lock(chains));
633	prio_s = rhashtable_lookup_fast(&prios_ht(chains), &key,
634					prio_params);
635	if (!prio_s) {
636		prio_s = mlx5_chains_create_prio(chains, chain,
637						 prio, level);
638		if (IS_ERR(prio_s))
639			goto err_create_prio;
640	}
641
642	++prio_s->ref;
643	mutex_unlock(&chains_lock(chains));
644
645	return prio_s->ft;
646
647err_create_prio:
648	mutex_unlock(&chains_lock(chains));
649err_get_prevs:
650	while (--l >= 0)
651		mlx5_chains_put_table(chains, chain, prio, l);
652	return ERR_CAST(prio_s);
653}
654
655void
656mlx5_chains_put_table(struct mlx5_fs_chains *chains, u32 chain, u32 prio,
657		      u32 level)
658{
659	struct prio *prio_s;
660	struct prio_key key;
661
662	key.chain = chain;
663	key.prio = prio;
664	key.level = level;
665
666	mutex_lock(&chains_lock(chains));
667	prio_s = rhashtable_lookup_fast(&prios_ht(chains), &key,
668					prio_params);
669	if (!prio_s)
670		goto err_get_prio;
671
672	if (--prio_s->ref == 0)
673		mlx5_chains_destroy_prio(chains, prio_s);
674	mutex_unlock(&chains_lock(chains));
675
676	while (level-- > 0)
677		mlx5_chains_put_table(chains, chain, prio, level);
678
679	return;
680
681err_get_prio:
682	mutex_unlock(&chains_lock(chains));
683	WARN_ONCE(1,
684		  "Couldn't find table: (chain: %d prio: %d level: %d)",
685		  chain, prio, level);
686}
687
688struct mlx5_flow_table *
689mlx5_chains_get_tc_end_ft(struct mlx5_fs_chains *chains)
690{
691	return chains_end_ft(chains);
692}
693
694struct mlx5_flow_table *
695mlx5_chains_create_global_table(struct mlx5_fs_chains *chains)
696{
697	u32 chain, prio, level;
698	int err;
699
700	if (!mlx5_chains_ignore_flow_level_supported(chains)) {
701		err = -EOPNOTSUPP;
702
703		mlx5_core_warn(chains->dev,
704			       "Couldn't create global flow table, ignore_flow_level not supported.");
705		goto err_ignore;
706	}
707
708	chain = mlx5_chains_get_chain_range(chains);
709	prio = mlx5_chains_get_prio_range(chains);
710	level = mlx5_chains_get_level_range(chains);
711
712	return mlx5_chains_create_table(chains, chain, prio, level);
713
714err_ignore:
715	return ERR_PTR(err);
716}
717
718void
719mlx5_chains_destroy_global_table(struct mlx5_fs_chains *chains,
720				 struct mlx5_flow_table *ft)
721{
722	mlx5_destroy_flow_table(ft);
723}
724
725static struct mlx5_fs_chains *
726mlx5_chains_init(struct mlx5_core_dev *dev, struct mlx5_chains_attr *attr)
727{
728	struct mlx5_fs_chains *chains;
729	int err;
730
731	chains = kzalloc(sizeof(*chains), GFP_KERNEL);
732	if (!chains)
733		return ERR_PTR(-ENOMEM);
734
735	chains->dev = dev;
736	chains->flags = attr->flags;
737	chains->ns = attr->ns;
738	chains->group_num = attr->max_grp_num;
739	chains->chains_mapping = attr->mapping;
740	chains->fs_base_prio = attr->fs_base_prio;
741	chains->fs_base_level = attr->fs_base_level;
742	chains_default_ft(chains) = chains_end_ft(chains) = attr->default_ft;
743
744	err = rhashtable_init(&chains_ht(chains), &chain_params);
745	if (err)
746		goto init_chains_ht_err;
747
748	err = rhashtable_init(&prios_ht(chains), &prio_params);
749	if (err)
750		goto init_prios_ht_err;
751
752	mutex_init(&chains_lock(chains));
753
754	return chains;
755
756init_prios_ht_err:
757	rhashtable_destroy(&chains_ht(chains));
758init_chains_ht_err:
759	kfree(chains);
760	return ERR_PTR(err);
761}
762
763static void
764mlx5_chains_cleanup(struct mlx5_fs_chains *chains)
765{
766	mutex_destroy(&chains_lock(chains));
767	rhashtable_destroy(&prios_ht(chains));
768	rhashtable_destroy(&chains_ht(chains));
769
770	kfree(chains);
771}
772
773struct mlx5_fs_chains *
774mlx5_chains_create(struct mlx5_core_dev *dev, struct mlx5_chains_attr *attr)
775{
776	struct mlx5_fs_chains *chains;
777
778	chains = mlx5_chains_init(dev, attr);
779
780	return chains;
781}
782
783void
784mlx5_chains_destroy(struct mlx5_fs_chains *chains)
785{
786	mlx5_chains_cleanup(chains);
787}
788
789int
790mlx5_chains_get_chain_mapping(struct mlx5_fs_chains *chains, u32 chain,
791			      u32 *chain_mapping)
792{
793	struct mapping_ctx *ctx = chains->chains_mapping;
794	struct mlx5_mapped_obj mapped_obj = {};
795
796	mapped_obj.type = MLX5_MAPPED_OBJ_CHAIN;
797	mapped_obj.chain = chain;
798	return mapping_add(ctx, &mapped_obj, chain_mapping);
799}
800
801int
802mlx5_chains_put_chain_mapping(struct mlx5_fs_chains *chains, u32 chain_mapping)
803{
804	struct mapping_ctx *ctx = chains->chains_mapping;
805
806	return mapping_remove(ctx, chain_mapping);
807}
808
809void
810mlx5_chains_print_info(struct mlx5_fs_chains *chains)
811{
812	mlx5_core_dbg(chains->dev, "Flow table chains groups(%d)\n", chains->group_num);
813}
~/ Read-only file preview.