1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 // Copyright (c) 2020 Mellanox Technologies.
3
4 #include <dev/mlx5/driver.h>
5 #include <dev/mlx5/mlx5_ifc.h>
6 #include <dev/mlx5/fs.h>
7
8 #include "mlx5_core.h"
9 #include "fs_chains.h"
10 #include "fs_ft_pool.h"
11 #include "fs_core.h"
12
13 #define chains_lock(chains) ((chains)->lock)
14 #define chains_xa(chains) ((chains)->chains_xa)
15 #define prios_xa(chains) ((chains)->prios_xa)
16 #define chains_default_ft(chains) ((chains)->chains_default_ft)
17 #define chains_end_ft(chains) ((chains)->chains_end_ft)
18 #define FT_TBL_SZ (64 * 1024)
19
20 struct mlx5_fs_chains {
21 struct mlx5_core_dev *dev;
22
23 struct xarray chains_xa;
24 struct xarray prios_xa;
25 /* Protects above chains_ht and prios_ht */
26 struct mutex lock;
27
28 struct mlx5_flow_table *chains_default_ft;
29 struct mlx5_flow_table *chains_end_ft;
30
31 enum mlx5_flow_namespace_type ns;
32 u32 group_num;
33 u32 flags;
34 int fs_base_prio;
35 int fs_base_level;
36 };
37
38 struct fs_chain {
39 u32 chain;
40
41 int ref;
42 int id;
43 uint32_t xa_idx;
44
45 struct mlx5_fs_chains *chains;
46 struct list_head prios_list;
47 struct mlx5_flow_handle *restore_rule;
48 struct mlx5_modify_hdr *miss_modify_hdr;
49 };
50
51 struct prio_key {
52 u32 chain;
53 u32 prio;
54 u32 level;
55 };
56
57 struct prio {
58 struct list_head list;
59
60 struct prio_key key;
61 uint32_t xa_idx;
62
63 int ref;
64
65 struct fs_chain *chain;
66 struct mlx5_flow_table *ft;
67 struct mlx5_flow_table *next_ft;
68 struct mlx5_flow_group *miss_group;
69 struct mlx5_flow_handle *miss_rule;
70 };
71
72 /*
73 static const struct rhashtable_params chain_params = {
74 .head_offset = offsetof(struct fs_chain, node),
75 .key_offset = offsetof(struct fs_chain, chain),
76 .key_len = sizeof_field(struct fs_chain, chain),
77 .automatic_shrinking = true,
78 };
79
80 static const struct rhashtable_params prio_params = {
81 .head_offset = offsetof(struct prio, node),
82 .key_offset = offsetof(struct prio, key),
83 .key_len = sizeof_field(struct prio, key),
84 .automatic_shrinking = true,
85 };
86 */
87
mlx5_chains_prios_supported(struct mlx5_fs_chains * chains)88 bool mlx5_chains_prios_supported(struct mlx5_fs_chains *chains)
89 {
90 return chains->flags & MLX5_CHAINS_AND_PRIOS_SUPPORTED;
91 }
92
mlx5_chains_ignore_flow_level_supported(struct mlx5_fs_chains * chains)93 bool mlx5_chains_ignore_flow_level_supported(struct mlx5_fs_chains *chains)
94 {
95 return chains->flags & MLX5_CHAINS_IGNORE_FLOW_LEVEL_SUPPORTED;
96 }
97
mlx5_chains_backwards_supported(struct mlx5_fs_chains * chains)98 bool mlx5_chains_backwards_supported(struct mlx5_fs_chains *chains)
99 {
100 return mlx5_chains_prios_supported(chains) &&
101 mlx5_chains_ignore_flow_level_supported(chains);
102 }
103
mlx5_chains_get_chain_range(struct mlx5_fs_chains * chains)104 u32 mlx5_chains_get_chain_range(struct mlx5_fs_chains *chains)
105 {
106 if (!mlx5_chains_prios_supported(chains))
107 return 1;
108
109 if (mlx5_chains_ignore_flow_level_supported(chains))
110 return UINT_MAX - 1;
111
112 /* We should get here only for eswitch case */
113 return FDB_TC_MAX_CHAIN;
114 }
115
mlx5_chains_get_nf_ft_chain(struct mlx5_fs_chains * chains)116 u32 mlx5_chains_get_nf_ft_chain(struct mlx5_fs_chains *chains)
117 {
118 return mlx5_chains_get_chain_range(chains) + 1;
119 }
120
mlx5_chains_get_prio_range(struct mlx5_fs_chains * chains)121 u32 mlx5_chains_get_prio_range(struct mlx5_fs_chains *chains)
122 {
123 if (mlx5_chains_ignore_flow_level_supported(chains))
124 return UINT_MAX;
125
126 if (!chains->dev->priv.eswitch)
127 return 1;
128
129 /* We should get here only for eswitch case */
130 return FDB_TC_MAX_PRIO;
131 }
132
mlx5_chains_get_level_range(struct mlx5_fs_chains * chains)133 static unsigned int mlx5_chains_get_level_range(struct mlx5_fs_chains *chains)
134 {
135 if (mlx5_chains_ignore_flow_level_supported(chains))
136 return UINT_MAX;
137
138 /* Same value for FDB and NIC RX tables */
139 return FDB_TC_LEVELS_PER_PRIO;
140 }
141
142 void
mlx5_chains_set_end_ft(struct mlx5_fs_chains * chains,struct mlx5_flow_table * ft)143 mlx5_chains_set_end_ft(struct mlx5_fs_chains *chains,
144 struct mlx5_flow_table *ft)
145 {
146 chains_end_ft(chains) = ft;
147 }
148
149 static struct mlx5_flow_table *
mlx5_chains_create_table(struct mlx5_fs_chains * chains,u32 chain,u32 prio,u32 level)150 mlx5_chains_create_table(struct mlx5_fs_chains *chains,
151 u32 chain, u32 prio, u32 level)
152 {
153 struct mlx5_flow_table_attr ft_attr = {};
154 struct mlx5_flow_namespace *ns;
155 struct mlx5_flow_table *ft;
156 int sz;
157
158 if (chains->flags & MLX5_CHAINS_FT_TUNNEL_SUPPORTED)
159 ft_attr.flags |= (MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT |
160 MLX5_FLOW_TABLE_TUNNEL_EN_DECAP);
161
162 sz = (chain == mlx5_chains_get_nf_ft_chain(chains)) ? FT_TBL_SZ : POOL_NEXT_SIZE;
163 ft_attr.max_fte = sz;
164
165 /* We use chains_default_ft(chains) as the table's next_ft till
166 * ignore_flow_level is allowed on FT creation and not just for FTEs.
167 * Instead caller should add an explicit miss rule if needed.
168 */
169 ft_attr.next_ft = chains_default_ft(chains);
170
171 /* The root table(chain 0, prio 1, level 0) is required to be
172 * connected to the previous fs_core managed prio.
173 * We always create it, as a managed table, in order to align with
174 * fs_core logic.
175 */
176 if (!mlx5_chains_ignore_flow_level_supported(chains) ||
177 (chain == 0 && prio == 1 && level == 0)) {
178 ft_attr.level = chains->fs_base_level;
179 ft_attr.prio = chains->fs_base_prio;
180 ns = (chains->ns == MLX5_FLOW_NAMESPACE_FDB) ?
181 mlx5_get_fdb_sub_ns(chains->dev, chain) :
182 mlx5_get_flow_namespace(chains->dev, chains->ns);
183 } else {
184 ft_attr.flags |= MLX5_FLOW_TABLE_UNMANAGED;
185 ft_attr.prio = chains->fs_base_prio;
186 /* Firmware doesn't allow us to create another level 0 table,
187 * so we create all unmanaged tables as level 1 (base + 1).
188 *
189 * To connect them, we use explicit miss rules with
190 * ignore_flow_level. Caller is responsible to create
191 * these rules (if needed).
192 */
193 ft_attr.level = chains->fs_base_level + 1;
194 ns = mlx5_get_flow_namespace(chains->dev, chains->ns);
195 }
196
197 ft_attr.autogroup.num_reserved_entries = 2;
198 ft_attr.autogroup.max_num_groups = chains->group_num;
199 ft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr);
200 if (IS_ERR(ft)) {
201 mlx5_core_warn(chains->dev, "Failed to create chains table err %d (chain: %d, prio: %d, level: %d, size: %d)\n",
202 (int)PTR_ERR(ft), chain, prio, level, sz);
203 return ft;
204 }
205
206 return ft;
207 }
208
209 static struct fs_chain *
mlx5_chains_create_chain(struct mlx5_fs_chains * chains,u32 chain)210 mlx5_chains_create_chain(struct mlx5_fs_chains *chains, u32 chain)
211 {
212 struct fs_chain *chain_s = NULL;
213 int err;
214
215 chain_s = kvzalloc(sizeof(*chain_s), GFP_KERNEL);
216 if (!chain_s)
217 return ERR_PTR(-ENOMEM);
218
219 chain_s->chains = chains;
220 chain_s->chain = chain;
221 INIT_LIST_HEAD(&chain_s->prios_list);
222
223 err = xa_alloc(&chains_xa(chains), &chain_s->xa_idx, chain_s,
224 xa_limit_32b, GFP_KERNEL);
225 if (err)
226 goto err_insert;
227
228 return chain_s;
229
230 err_insert:
231 kvfree(chain_s);
232 return ERR_PTR(err);
233 }
234
235 static void
mlx5_chains_destroy_chain(struct fs_chain * chain)236 mlx5_chains_destroy_chain(struct fs_chain *chain)
237 {
238 struct mlx5_fs_chains *chains = chain->chains;
239
240 xa_erase(&chains_xa(chains), chain->xa_idx);
241 kvfree(chain);
242 }
243
244 static struct fs_chain *
mlx5_chains_get_chain(struct mlx5_fs_chains * chains,u32 chain)245 mlx5_chains_get_chain(struct mlx5_fs_chains *chains, u32 chain)
246 {
247 struct fs_chain *chain_s = NULL;
248 unsigned long idx;
249
250 xa_for_each(&chains_xa(chains), idx, chain_s) {
251 if (chain_s->chain == chain)
252 break;
253 }
254
255 if (!chain_s) {
256 chain_s = mlx5_chains_create_chain(chains, chain);
257 if (IS_ERR(chain_s))
258 return chain_s;
259 }
260
261 chain_s->ref++;
262
263 return chain_s;
264 }
265
266 static struct mlx5_flow_handle *
mlx5_chains_add_miss_rule(struct fs_chain * chain,struct mlx5_flow_table * ft,struct mlx5_flow_table * next_ft)267 mlx5_chains_add_miss_rule(struct fs_chain *chain,
268 struct mlx5_flow_table *ft,
269 struct mlx5_flow_table *next_ft)
270 {
271 struct mlx5_flow_destination dest = {};
272 struct mlx5_flow_act act = {};
273
274 act.flags = FLOW_ACT_NO_APPEND;
275 if (mlx5_chains_ignore_flow_level_supported(chain->chains))
276 act.flags |= FLOW_ACT_IGNORE_FLOW_LEVEL;
277
278 act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
279 dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE;
280 dest.ft = next_ft;
281
282 return mlx5_add_flow_rules(ft, NULL, &act, &dest, 1);
283 }
284
285 static int
mlx5_chains_update_prio_prevs(struct prio * prio,struct mlx5_flow_table * next_ft)286 mlx5_chains_update_prio_prevs(struct prio *prio,
287 struct mlx5_flow_table *next_ft)
288 {
289 struct mlx5_flow_handle *miss_rules[FDB_TC_LEVELS_PER_PRIO + 1] = {};
290 struct fs_chain *chain = prio->chain;
291 struct prio *pos;
292 int n = 0, err;
293
294 if (prio->key.level)
295 return 0;
296
297 /* Iterate in reverse order until reaching the level 0 rule of
298 * the previous priority, adding all the miss rules first, so we can
299 * revert them if any of them fails.
300 */
301 pos = prio;
302 list_for_each_entry_continue_reverse(pos,
303 &chain->prios_list,
304 list) {
305 miss_rules[n] = mlx5_chains_add_miss_rule(chain,
306 pos->ft,
307 next_ft);
308 if (IS_ERR(miss_rules[n])) {
309 err = PTR_ERR(miss_rules[n]);
310 goto err_prev_rule;
311 }
312
313 n++;
314 if (!pos->key.level)
315 break;
316 }
317
318 /* Success, delete old miss rules, and update the pointers. */
319 n = 0;
320 pos = prio;
321 list_for_each_entry_continue_reverse(pos,
322 &chain->prios_list,
323 list) {
324 mlx5_del_flow_rules(&pos->miss_rule);
325
326 pos->miss_rule = miss_rules[n];
327 pos->next_ft = next_ft;
328
329 n++;
330 if (!pos->key.level)
331 break;
332 }
333
334 return 0;
335
336 err_prev_rule:
337 while (--n >= 0)
338 mlx5_del_flow_rules(&miss_rules[n]);
339
340 return err;
341 }
342
343 static void
mlx5_chains_put_chain(struct fs_chain * chain)344 mlx5_chains_put_chain(struct fs_chain *chain)
345 {
346 if (--chain->ref == 0)
347 mlx5_chains_destroy_chain(chain);
348 }
349
350 static struct prio *
mlx5_chains_create_prio(struct mlx5_fs_chains * chains,u32 chain,u32 prio,u32 level)351 mlx5_chains_create_prio(struct mlx5_fs_chains *chains,
352 u32 chain, u32 prio, u32 level)
353 {
354 int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
355 struct mlx5_flow_handle *miss_rule;
356 struct mlx5_flow_group *miss_group;
357 struct mlx5_flow_table *next_ft;
358 struct mlx5_flow_table *ft;
359 struct fs_chain *chain_s;
360 struct list_head *pos;
361 struct prio *prio_s;
362 u32 *flow_group_in;
363 int err;
364
365 chain_s = mlx5_chains_get_chain(chains, chain);
366 if (IS_ERR(chain_s))
367 return ERR_CAST(chain_s);
368
369 prio_s = kvzalloc(sizeof(*prio_s), GFP_KERNEL);
370 flow_group_in = kvzalloc(inlen, GFP_KERNEL);
371 if (!prio_s || !flow_group_in) {
372 err = -ENOMEM;
373 goto err_alloc;
374 }
375
376 /* Chain's prio list is sorted by prio and level.
377 * And all levels of some prio point to the next prio's level 0.
378 * Example list (prio, level):
379 * (3,0)->(3,1)->(5,0)->(5,1)->(6,1)->(7,0)
380 * In hardware, we will we have the following pointers:
381 * (3,0) -> (5,0) -> (7,0) -> Slow path
382 * (3,1) -> (5,0)
383 * (5,1) -> (7,0)
384 * (6,1) -> (7,0)
385 */
386
387 /* Default miss for each chain: */
388 next_ft = (chain == mlx5_chains_get_nf_ft_chain(chains)) ?
389 chains_default_ft(chains) :
390 chains_end_ft(chains);
391 list_for_each(pos, &chain_s->prios_list) {
392 struct prio *p = list_entry(pos, struct prio, list);
393
394 /* exit on first pos that is larger */
395 if (prio < p->key.prio || (prio == p->key.prio &&
396 level < p->key.level)) {
397 /* Get next level 0 table */
398 next_ft = p->key.level == 0 ? p->ft : p->next_ft;
399 break;
400 }
401 }
402
403 ft = mlx5_chains_create_table(chains, chain, prio, level);
404 if (IS_ERR(ft)) {
405 err = PTR_ERR(ft);
406 goto err_create;
407 }
408
409 MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index,
410 ft->max_fte - 2);
411 MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index,
412 ft->max_fte - 1);
413 miss_group = mlx5_create_flow_group(ft, flow_group_in);
414 if (IS_ERR(miss_group)) {
415 err = PTR_ERR(miss_group);
416 goto err_group;
417 }
418
419 /* Add miss rule to next_ft */
420 miss_rule = mlx5_chains_add_miss_rule(chain_s, ft, next_ft);
421 if (IS_ERR(miss_rule)) {
422 err = PTR_ERR(miss_rule);
423 goto err_miss_rule;
424 }
425
426 prio_s->miss_group = miss_group;
427 prio_s->miss_rule = miss_rule;
428 prio_s->next_ft = next_ft;
429 prio_s->chain = chain_s;
430 prio_s->key.chain = chain;
431 prio_s->key.prio = prio;
432 prio_s->key.level = level;
433 prio_s->ft = ft;
434
435 err = xa_alloc(&prios_xa(chains), &prio_s->xa_idx, prio_s,
436 xa_limit_32b, GFP_KERNEL);
437 if (err)
438 goto err_insert;
439
440 list_add(&prio_s->list, pos->prev);
441
442 /* Table is ready, connect it */
443 err = mlx5_chains_update_prio_prevs(prio_s, ft);
444 if (err)
445 goto err_update;
446
447 kvfree(flow_group_in);
448 return prio_s;
449
450 err_update:
451 list_del(&prio_s->list);
452 xa_erase(&prios_xa(chains), prio_s->xa_idx);
453 err_insert:
454 mlx5_del_flow_rules(&miss_rule);
455 err_miss_rule:
456 mlx5_destroy_flow_group(miss_group);
457 err_group:
458 mlx5_destroy_flow_table(ft);
459 err_create:
460 err_alloc:
461 kvfree(prio_s);
462 kvfree(flow_group_in);
463 mlx5_chains_put_chain(chain_s);
464 return ERR_PTR(err);
465 }
466
467 static void
mlx5_chains_destroy_prio(struct mlx5_fs_chains * chains,struct prio * prio)468 mlx5_chains_destroy_prio(struct mlx5_fs_chains *chains,
469 struct prio *prio)
470 {
471 struct fs_chain *chain = prio->chain;
472
473 WARN_ON(mlx5_chains_update_prio_prevs(prio,
474 prio->next_ft));
475
476 list_del(&prio->list);
477 xa_erase(&prios_xa(chains), prio->xa_idx);
478 mlx5_del_flow_rules(&prio->miss_rule);
479 mlx5_destroy_flow_group(prio->miss_group);
480 mlx5_destroy_flow_table(prio->ft);
481 mlx5_chains_put_chain(chain);
482 kvfree(prio);
483 }
484
485 struct mlx5_flow_table *
mlx5_chains_get_table(struct mlx5_fs_chains * chains,u32 chain,u32 prio,u32 level)486 mlx5_chains_get_table(struct mlx5_fs_chains *chains, u32 chain, u32 prio,
487 u32 level)
488 {
489 struct mlx5_flow_table *prev_fts;
490 struct prio *prio_s;
491 unsigned long idx;
492 int l = 0;
493
494 if ((chain > mlx5_chains_get_chain_range(chains) &&
495 chain != mlx5_chains_get_nf_ft_chain(chains)) ||
496 prio > mlx5_chains_get_prio_range(chains) ||
497 level > mlx5_chains_get_level_range(chains))
498 return ERR_PTR(-EOPNOTSUPP);
499
500 /* create earlier levels for correct fs_core lookup when
501 * connecting tables.
502 */
503 for (l = 0; l < level; l++) {
504 prev_fts = mlx5_chains_get_table(chains, chain, prio, l);
505 if (IS_ERR(prev_fts)) {
506 prio_s = ERR_CAST(prev_fts);
507 goto err_get_prevs;
508 }
509 }
510
511 mutex_lock(&chains_lock(chains));
512 xa_for_each(&prios_xa(chains), idx, prio_s) {
513 if (chain == prio_s->key.chain &&
514 prio == prio_s->key.prio &&
515 level == prio_s->key.level)
516 break;
517 }
518 if (!prio_s) {
519 prio_s = mlx5_chains_create_prio(chains, chain,
520 prio, level);
521 if (IS_ERR(prio_s))
522 goto err_create_prio;
523 }
524
525 ++prio_s->ref;
526 mutex_unlock(&chains_lock(chains));
527
528 return prio_s->ft;
529
530 err_create_prio:
531 mutex_unlock(&chains_lock(chains));
532 err_get_prevs:
533 while (--l >= 0)
534 mlx5_chains_put_table(chains, chain, prio, l);
535 return ERR_CAST(prio_s);
536 }
537
538 void
mlx5_chains_put_table(struct mlx5_fs_chains * chains,u32 chain,u32 prio,u32 level)539 mlx5_chains_put_table(struct mlx5_fs_chains *chains, u32 chain, u32 prio,
540 u32 level)
541 {
542 struct prio *prio_s;
543 unsigned long idx;
544
545 mutex_lock(&chains_lock(chains));
546
547 xa_for_each(&prios_xa(chains), idx, prio_s) {
548 if (chain == prio_s->key.chain &&
549 prio == prio_s->key.prio &&
550 level == prio_s->key.level)
551 break;
552 }
553 if (!prio_s)
554 goto err_get_prio;
555
556 if (--prio_s->ref == 0)
557 mlx5_chains_destroy_prio(chains, prio_s);
558 mutex_unlock(&chains_lock(chains));
559
560 while (level-- > 0)
561 mlx5_chains_put_table(chains, chain, prio, level);
562
563 return;
564
565 err_get_prio:
566 mutex_unlock(&chains_lock(chains));
567 WARN_ONCE(1,
568 "Couldn't find table: (chain: %d prio: %d level: %d)",
569 chain, prio, level);
570 }
571
572 struct mlx5_flow_table *
mlx5_chains_get_tc_end_ft(struct mlx5_fs_chains * chains)573 mlx5_chains_get_tc_end_ft(struct mlx5_fs_chains *chains)
574 {
575 return chains_end_ft(chains);
576 }
577
578 struct mlx5_flow_table *
mlx5_chains_create_global_table(struct mlx5_fs_chains * chains)579 mlx5_chains_create_global_table(struct mlx5_fs_chains *chains)
580 {
581 u32 chain, prio, level;
582 int err;
583
584 if (!mlx5_chains_ignore_flow_level_supported(chains)) {
585 err = -EOPNOTSUPP;
586
587 mlx5_core_warn(chains->dev,
588 "Couldn't create global flow table, ignore_flow_level not supported.");
589 goto err_ignore;
590 }
591
592 chain = mlx5_chains_get_chain_range(chains),
593 prio = mlx5_chains_get_prio_range(chains);
594 level = mlx5_chains_get_level_range(chains);
595
596 return mlx5_chains_create_table(chains, chain, prio, level);
597
598 err_ignore:
599 return ERR_PTR(err);
600 }
601
602 void
mlx5_chains_destroy_global_table(struct mlx5_fs_chains * chains,struct mlx5_flow_table * ft)603 mlx5_chains_destroy_global_table(struct mlx5_fs_chains *chains,
604 struct mlx5_flow_table *ft)
605 {
606 mlx5_destroy_flow_table(ft);
607 }
608
609 static struct mlx5_fs_chains *
mlx5_chains_init(struct mlx5_core_dev * dev,struct mlx5_chains_attr * attr)610 mlx5_chains_init(struct mlx5_core_dev *dev, struct mlx5_chains_attr *attr)
611 {
612 struct mlx5_fs_chains *chains;
613
614 chains = kzalloc(sizeof(*chains), GFP_KERNEL);
615 if (!chains)
616 return ERR_PTR(-ENOMEM);
617
618 chains->dev = dev;
619 chains->flags = attr->flags;
620 chains->ns = attr->ns;
621 chains->group_num = attr->max_grp_num;
622 chains->fs_base_prio = attr->fs_base_prio;
623 chains->fs_base_level = attr->fs_base_level;
624 chains_default_ft(chains) = chains_end_ft(chains) = attr->default_ft;
625
626 xa_init(&chains_xa(chains));
627 xa_init(&prios_xa(chains));
628
629 mutex_init(&chains_lock(chains));
630
631 return chains;
632 }
633
634 static void
mlx5_chains_cleanup(struct mlx5_fs_chains * chains)635 mlx5_chains_cleanup(struct mlx5_fs_chains *chains)
636 {
637 mutex_destroy(&chains_lock(chains));
638 xa_destroy(&prios_xa(chains));
639 xa_destroy(&chains_xa(chains));
640
641 kfree(chains);
642 }
643
644 struct mlx5_fs_chains *
mlx5_chains_create(struct mlx5_core_dev * dev,struct mlx5_chains_attr * attr)645 mlx5_chains_create(struct mlx5_core_dev *dev, struct mlx5_chains_attr *attr)
646 {
647 struct mlx5_fs_chains *chains;
648
649 chains = mlx5_chains_init(dev, attr);
650
651 return chains;
652 }
653
654 void
mlx5_chains_destroy(struct mlx5_fs_chains * chains)655 mlx5_chains_destroy(struct mlx5_fs_chains *chains)
656 {
657 mlx5_chains_cleanup(chains);
658 }
659
660 void
mlx5_chains_print_info(struct mlx5_fs_chains * chains)661 mlx5_chains_print_info(struct mlx5_fs_chains *chains)
662 {
663 mlx5_core_dbg(chains->dev, "Flow table chains groups(%d)\n", chains->group_num);
664 }
665