Merge tag 'fsnotify_for_v5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
[linux-2.6-microblaze.git] / drivers / net / ethernet / mellanox / mlx5 / core / esw / sample.c
1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2021 Mellanox Technologies. */
3
4 #include <linux/skbuff.h>
5 #include <net/psample.h>
6 #include "en/mapping.h"
7 #include "esw/sample.h"
8 #include "eswitch.h"
9 #include "en_tc.h"
10 #include "fs_core.h"
11
12 #define MLX5_ESW_VPORT_TBL_SIZE_SAMPLE (64 * 1024)
13
14 static const struct esw_vport_tbl_namespace mlx5_esw_vport_tbl_sample_ns = {
15         .max_fte = MLX5_ESW_VPORT_TBL_SIZE_SAMPLE,
16         .max_num_groups = 0,    /* default num of groups */
17         .flags = MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT | MLX5_FLOW_TABLE_TUNNEL_EN_DECAP,
18 };
19
20 struct mlx5_esw_psample {
21         struct mlx5e_priv *priv;
22         struct mlx5_flow_table *termtbl;
23         struct mlx5_flow_handle *termtbl_rule;
24         DECLARE_HASHTABLE(hashtbl, 8);
25         struct mutex ht_lock; /* protect hashtbl */
26         DECLARE_HASHTABLE(restore_hashtbl, 8);
27         struct mutex restore_lock; /* protect restore_hashtbl */
28 };
29
30 struct mlx5_sampler {
31         struct hlist_node hlist;
32         u32 sampler_id;
33         u32 sample_ratio;
34         u32 sample_table_id;
35         u32 default_table_id;
36         int count;
37 };
38
39 struct mlx5_sample_flow {
40         struct mlx5_sampler *sampler;
41         struct mlx5_sample_restore *restore;
42         struct mlx5_flow_attr *pre_attr;
43         struct mlx5_flow_handle *pre_rule;
44         struct mlx5_flow_handle *rule;
45 };
46
47 struct mlx5_sample_restore {
48         struct hlist_node hlist;
49         struct mlx5_modify_hdr *modify_hdr;
50         struct mlx5_flow_handle *rule;
51         u32 obj_id;
52         int count;
53 };
54
55 static int
56 sampler_termtbl_create(struct mlx5_esw_psample *esw_psample)
57 {
58         struct mlx5_core_dev *dev = esw_psample->priv->mdev;
59         struct mlx5_eswitch *esw = dev->priv.eswitch;
60         struct mlx5_flow_table_attr ft_attr = {};
61         struct mlx5_flow_destination dest = {};
62         struct mlx5_flow_namespace *root_ns;
63         struct mlx5_flow_act act = {};
64         int err;
65
66         if (!MLX5_CAP_ESW_FLOWTABLE_FDB(dev, termination_table))  {
67                 mlx5_core_warn(dev, "termination table is not supported\n");
68                 return -EOPNOTSUPP;
69         }
70
71         root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
72         if (!root_ns) {
73                 mlx5_core_warn(dev, "failed to get FDB flow namespace\n");
74                 return -EOPNOTSUPP;
75         }
76
77         ft_attr.flags = MLX5_FLOW_TABLE_TERMINATION | MLX5_FLOW_TABLE_UNMANAGED;
78         ft_attr.autogroup.max_num_groups = 1;
79         ft_attr.prio = FDB_SLOW_PATH;
80         ft_attr.max_fte = 1;
81         ft_attr.level = 1;
82         esw_psample->termtbl = mlx5_create_auto_grouped_flow_table(root_ns, &ft_attr);
83         if (IS_ERR(esw_psample->termtbl)) {
84                 err = PTR_ERR(esw_psample->termtbl);
85                 mlx5_core_warn(dev, "failed to create termtbl, err: %d\n", err);
86                 return err;
87         }
88
89         act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
90         dest.vport.num = esw->manager_vport;
91         esw_psample->termtbl_rule = mlx5_add_flow_rules(esw_psample->termtbl, NULL, &act, &dest, 1);
92         if (IS_ERR(esw_psample->termtbl_rule)) {
93                 err = PTR_ERR(esw_psample->termtbl_rule);
94                 mlx5_core_warn(dev, "failed to create termtbl rule, err: %d\n", err);
95                 mlx5_destroy_flow_table(esw_psample->termtbl);
96                 return err;
97         }
98
99         return 0;
100 }
101
102 static void
103 sampler_termtbl_destroy(struct mlx5_esw_psample *esw_psample)
104 {
105         mlx5_del_flow_rules(esw_psample->termtbl_rule);
106         mlx5_destroy_flow_table(esw_psample->termtbl);
107 }
108
109 static int
110 sampler_obj_create(struct mlx5_core_dev *mdev, struct mlx5_sampler *sampler)
111 {
112         u32 in[MLX5_ST_SZ_DW(create_sampler_obj_in)] = {};
113         u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
114         u64 general_obj_types;
115         void *obj;
116         int err;
117
118         general_obj_types = MLX5_CAP_GEN_64(mdev, general_obj_types);
119         if (!(general_obj_types & MLX5_HCA_CAP_GENERAL_OBJECT_TYPES_SAMPLER))
120                 return -EOPNOTSUPP;
121         if (!MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, ignore_flow_level))
122                 return -EOPNOTSUPP;
123
124         obj = MLX5_ADDR_OF(create_sampler_obj_in, in, sampler_object);
125         MLX5_SET(sampler_obj, obj, table_type, FS_FT_FDB);
126         MLX5_SET(sampler_obj, obj, ignore_flow_level, 1);
127         MLX5_SET(sampler_obj, obj, level, 1);
128         MLX5_SET(sampler_obj, obj, sample_ratio, sampler->sample_ratio);
129         MLX5_SET(sampler_obj, obj, sample_table_id, sampler->sample_table_id);
130         MLX5_SET(sampler_obj, obj, default_table_id, sampler->default_table_id);
131         MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
132         MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_GENERAL_OBJECT_TYPES_SAMPLER);
133
134         err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
135         if (!err)
136                 sampler->sampler_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
137
138         return err;
139 }
140
141 static void
142 sampler_obj_destroy(struct mlx5_core_dev *mdev, u32 sampler_id)
143 {
144         u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {};
145         u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
146
147         MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
148         MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_GENERAL_OBJECT_TYPES_SAMPLER);
149         MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, sampler_id);
150
151         mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
152 }
153
154 static u32
155 sampler_hash(u32 sample_ratio, u32 default_table_id)
156 {
157         return jhash_2words(sample_ratio, default_table_id, 0);
158 }
159
160 static int
161 sampler_cmp(u32 sample_ratio1, u32 default_table_id1, u32 sample_ratio2, u32 default_table_id2)
162 {
163         return sample_ratio1 != sample_ratio2 || default_table_id1 != default_table_id2;
164 }
165
166 static struct mlx5_sampler *
167 sampler_get(struct mlx5_esw_psample *esw_psample, u32 sample_ratio, u32 default_table_id)
168 {
169         struct mlx5_sampler *sampler;
170         u32 hash_key;
171         int err;
172
173         mutex_lock(&esw_psample->ht_lock);
174         hash_key = sampler_hash(sample_ratio, default_table_id);
175         hash_for_each_possible(esw_psample->hashtbl, sampler, hlist, hash_key)
176                 if (!sampler_cmp(sampler->sample_ratio, sampler->default_table_id,
177                                  sample_ratio, default_table_id))
178                         goto add_ref;
179
180         sampler = kzalloc(sizeof(*sampler), GFP_KERNEL);
181         if (!sampler) {
182                 err = -ENOMEM;
183                 goto err_alloc;
184         }
185
186         sampler->sample_table_id = esw_psample->termtbl->id;
187         sampler->default_table_id = default_table_id;
188         sampler->sample_ratio = sample_ratio;
189
190         err = sampler_obj_create(esw_psample->priv->mdev, sampler);
191         if (err)
192                 goto err_create;
193
194         hash_add(esw_psample->hashtbl, &sampler->hlist, hash_key);
195
196 add_ref:
197         sampler->count++;
198         mutex_unlock(&esw_psample->ht_lock);
199         return sampler;
200
201 err_create:
202         kfree(sampler);
203 err_alloc:
204         mutex_unlock(&esw_psample->ht_lock);
205         return ERR_PTR(err);
206 }
207
208 static void
209 sampler_put(struct mlx5_esw_psample *esw_psample, struct mlx5_sampler *sampler)
210 {
211         mutex_lock(&esw_psample->ht_lock);
212         if (--sampler->count == 0) {
213                 hash_del(&sampler->hlist);
214                 sampler_obj_destroy(esw_psample->priv->mdev, sampler->sampler_id);
215                 kfree(sampler);
216         }
217         mutex_unlock(&esw_psample->ht_lock);
218 }
219
220 static struct mlx5_modify_hdr *
221 sample_metadata_rule_get(struct mlx5_core_dev *mdev, u32 obj_id)
222 {
223         struct mlx5e_tc_mod_hdr_acts mod_acts = {};
224         struct mlx5_modify_hdr *modify_hdr;
225         int err;
226
227         err = mlx5e_tc_match_to_reg_set(mdev, &mod_acts, MLX5_FLOW_NAMESPACE_FDB,
228                                         CHAIN_TO_REG, obj_id);
229         if (err)
230                 goto err_set_regc0;
231
232         modify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_FDB,
233                                               mod_acts.num_actions,
234                                               mod_acts.actions);
235         if (IS_ERR(modify_hdr)) {
236                 err = PTR_ERR(modify_hdr);
237                 goto err_modify_hdr;
238         }
239
240         dealloc_mod_hdr_actions(&mod_acts);
241         return modify_hdr;
242
243 err_modify_hdr:
244         dealloc_mod_hdr_actions(&mod_acts);
245 err_set_regc0:
246         return ERR_PTR(err);
247 }
248
249 static struct mlx5_sample_restore *
250 sample_restore_get(struct mlx5_esw_psample *esw_psample, u32 obj_id)
251 {
252         struct mlx5_core_dev *mdev = esw_psample->priv->mdev;
253         struct mlx5_eswitch *esw = mdev->priv.eswitch;
254         struct mlx5_sample_restore *restore;
255         struct mlx5_modify_hdr *modify_hdr;
256         int err;
257
258         mutex_lock(&esw_psample->restore_lock);
259         hash_for_each_possible(esw_psample->restore_hashtbl, restore, hlist, obj_id)
260                 if (restore->obj_id == obj_id)
261                         goto add_ref;
262
263         restore = kzalloc(sizeof(*restore), GFP_KERNEL);
264         if (!restore) {
265                 err = -ENOMEM;
266                 goto err_alloc;
267         }
268         restore->obj_id = obj_id;
269
270         modify_hdr = sample_metadata_rule_get(mdev, obj_id);
271         if (IS_ERR(modify_hdr)) {
272                 err = PTR_ERR(modify_hdr);
273                 goto err_modify_hdr;
274         }
275         restore->modify_hdr = modify_hdr;
276
277         restore->rule = esw_add_restore_rule(esw, obj_id);
278         if (IS_ERR(restore->rule)) {
279                 err = PTR_ERR(restore->rule);
280                 goto err_restore;
281         }
282
283         hash_add(esw_psample->restore_hashtbl, &restore->hlist, obj_id);
284 add_ref:
285         restore->count++;
286         mutex_unlock(&esw_psample->restore_lock);
287         return restore;
288
289 err_restore:
290         mlx5_modify_header_dealloc(mdev, restore->modify_hdr);
291 err_modify_hdr:
292         kfree(restore);
293 err_alloc:
294         mutex_unlock(&esw_psample->restore_lock);
295         return ERR_PTR(err);
296 }
297
298 static void
299 sample_restore_put(struct mlx5_esw_psample *esw_psample, struct mlx5_sample_restore *restore)
300 {
301         mutex_lock(&esw_psample->restore_lock);
302         if (--restore->count == 0)
303                 hash_del(&restore->hlist);
304         mutex_unlock(&esw_psample->restore_lock);
305
306         if (!restore->count) {
307                 mlx5_del_flow_rules(restore->rule);
308                 mlx5_modify_header_dealloc(esw_psample->priv->mdev, restore->modify_hdr);
309                 kfree(restore);
310         }
311 }
312
313 void mlx5_esw_sample_skb(struct sk_buff *skb, struct mlx5_mapped_obj *mapped_obj)
314 {
315         u32 trunc_size = mapped_obj->sample.trunc_size;
316         struct psample_group psample_group = {};
317         struct psample_metadata md = {};
318
319         md.trunc_size = trunc_size ? min(trunc_size, skb->len) : skb->len;
320         md.in_ifindex = skb->dev->ifindex;
321         psample_group.group_num = mapped_obj->sample.group_id;
322         psample_group.net = &init_net;
323         skb_push(skb, skb->mac_len);
324
325         psample_sample_packet(&psample_group, skb, mapped_obj->sample.rate, &md);
326 }
327
328 /* For the following typical flow table:
329  *
330  * +-------------------------------+
331  * +       original flow table     +
332  * +-------------------------------+
333  * +         original match        +
334  * +-------------------------------+
335  * + sample action + other actions +
336  * +-------------------------------+
337  *
338  * We translate the tc filter with sample action to the following HW model:
339  *
340  *         +---------------------+
341  *         + original flow table +
342  *         +---------------------+
343  *         +   original match    +
344  *         +---------------------+
345  *                    |
346  *                    v
347  * +------------------------------------------------+
348  * +                Flow Sampler Object             +
349  * +------------------------------------------------+
350  * +                    sample ratio                +
351  * +------------------------------------------------+
352  * +    sample table id    |    default table id    +
353  * +------------------------------------------------+
354  *            |                            |
355  *            v                            v
356  * +-----------------------------+  +----------------------------------------+
357  * +        sample table         +  + default table per <vport, chain, prio> +
358  * +-----------------------------+  +----------------------------------------+
359  * + forward to management vport +  +            original match              +
360  * +-----------------------------+  +----------------------------------------+
361  *                                  +            other actions               +
362  *                                  +----------------------------------------+
363  */
364 struct mlx5_flow_handle *
365 mlx5_esw_sample_offload(struct mlx5_esw_psample *esw_psample,
366                         struct mlx5_flow_spec *spec,
367                         struct mlx5_flow_attr *attr)
368 {
369         struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
370         struct mlx5_vport_tbl_attr per_vport_tbl_attr;
371         struct mlx5_esw_flow_attr *pre_esw_attr;
372         struct mlx5_mapped_obj restore_obj = {};
373         struct mlx5_sample_flow *sample_flow;
374         struct mlx5_sample_attr *sample_attr;
375         struct mlx5_flow_table *default_tbl;
376         struct mlx5_flow_attr *pre_attr;
377         struct mlx5_eswitch *esw;
378         u32 obj_id;
379         int err;
380
381         if (IS_ERR_OR_NULL(esw_psample))
382                 return ERR_PTR(-EOPNOTSUPP);
383
384         /* If slow path flag is set, eg. when the neigh is invalid for encap,
385          * don't offload sample action.
386          */
387         esw = esw_psample->priv->mdev->priv.eswitch;
388         if (attr->flags & MLX5_ESW_ATTR_FLAG_SLOW_PATH)
389                 return mlx5_eswitch_add_offloaded_rule(esw, spec, attr);
390
391         sample_flow = kzalloc(sizeof(*sample_flow), GFP_KERNEL);
392         if (!sample_flow)
393                 return ERR_PTR(-ENOMEM);
394         esw_attr->sample->sample_flow = sample_flow;
395
396         /* Allocate default table per vport, chain and prio. Otherwise, there is
397          * only one default table for the same sampler object. Rules with different
398          * prio and chain may overlap. For CT sample action, per vport default
399          * table is needed to resotre the metadata.
400          */
401         per_vport_tbl_attr.chain = attr->chain;
402         per_vport_tbl_attr.prio = attr->prio;
403         per_vport_tbl_attr.vport = esw_attr->in_rep->vport;
404         per_vport_tbl_attr.vport_ns = &mlx5_esw_vport_tbl_sample_ns;
405         default_tbl = mlx5_esw_vporttbl_get(esw, &per_vport_tbl_attr);
406         if (IS_ERR(default_tbl)) {
407                 err = PTR_ERR(default_tbl);
408                 goto err_default_tbl;
409         }
410
411         /* Perform the original matches on the default table.
412          * Offload all actions except the sample action.
413          */
414         esw_attr->sample->sample_default_tbl = default_tbl;
415         /* When offloading sample and encap action, if there is no valid
416          * neigh data struct, a slow path rule is offloaded first. Source
417          * port metadata match is set at that time. A per vport table is
418          * already allocated. No need to match it again. So clear the source
419          * port metadata match.
420          */
421         mlx5_eswitch_clear_rule_source_port(esw, spec);
422         sample_flow->rule = mlx5_eswitch_add_offloaded_rule(esw, spec, attr);
423         if (IS_ERR(sample_flow->rule)) {
424                 err = PTR_ERR(sample_flow->rule);
425                 goto err_offload_rule;
426         }
427
428         /* Create sampler object. */
429         sample_flow->sampler = sampler_get(esw_psample, esw_attr->sample->rate, default_tbl->id);
430         if (IS_ERR(sample_flow->sampler)) {
431                 err = PTR_ERR(sample_flow->sampler);
432                 goto err_sampler;
433         }
434
435         /* Create an id mapping reg_c0 value to sample object. */
436         restore_obj.type = MLX5_MAPPED_OBJ_SAMPLE;
437         restore_obj.sample.group_id = esw_attr->sample->group_num;
438         restore_obj.sample.rate = esw_attr->sample->rate;
439         restore_obj.sample.trunc_size = esw_attr->sample->trunc_size;
440         err = mapping_add(esw->offloads.reg_c0_obj_pool, &restore_obj, &obj_id);
441         if (err)
442                 goto err_obj_id;
443         esw_attr->sample->restore_obj_id = obj_id;
444
445         /* Create sample restore context. */
446         sample_flow->restore = sample_restore_get(esw_psample, obj_id);
447         if (IS_ERR(sample_flow->restore)) {
448                 err = PTR_ERR(sample_flow->restore);
449                 goto err_sample_restore;
450         }
451
452         /* Perform the original matches on the original table. Offload the
453          * sample action. The destination is the sampler object.
454          */
455         pre_attr = mlx5_alloc_flow_attr(MLX5_FLOW_NAMESPACE_FDB);
456         if (!pre_attr) {
457                 err = -ENOMEM;
458                 goto err_alloc_flow_attr;
459         }
460         sample_attr = kzalloc(sizeof(*sample_attr), GFP_KERNEL);
461         if (!sample_attr) {
462                 err = -ENOMEM;
463                 goto err_alloc_sample_attr;
464         }
465         pre_esw_attr = pre_attr->esw_attr;
466         pre_attr->action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
467         pre_attr->modify_hdr = sample_flow->restore->modify_hdr;
468         pre_attr->flags = MLX5_ESW_ATTR_FLAG_SAMPLE;
469         pre_attr->chain = attr->chain;
470         pre_attr->prio = attr->prio;
471         pre_esw_attr->sample = sample_attr;
472         pre_esw_attr->sample->sampler_id = sample_flow->sampler->sampler_id;
473         pre_esw_attr->in_mdev = esw_attr->in_mdev;
474         pre_esw_attr->in_rep = esw_attr->in_rep;
475         sample_flow->pre_rule = mlx5_eswitch_add_offloaded_rule(esw, spec, pre_attr);
476         if (IS_ERR(sample_flow->pre_rule)) {
477                 err = PTR_ERR(sample_flow->pre_rule);
478                 goto err_pre_offload_rule;
479         }
480         sample_flow->pre_attr = pre_attr;
481
482         return sample_flow->rule;
483
484 err_pre_offload_rule:
485         kfree(sample_attr);
486 err_alloc_sample_attr:
487         kfree(pre_attr);
488 err_alloc_flow_attr:
489         sample_restore_put(esw_psample, sample_flow->restore);
490 err_sample_restore:
491         mapping_remove(esw->offloads.reg_c0_obj_pool, obj_id);
492 err_obj_id:
493         sampler_put(esw_psample, sample_flow->sampler);
494 err_sampler:
495         /* For sample offload, rule is added in default_tbl. No need to call
496          * mlx5_esw_chains_put_table()
497          */
498         attr->prio = 0;
499         attr->chain = 0;
500         mlx5_eswitch_del_offloaded_rule(esw, sample_flow->rule, attr);
501 err_offload_rule:
502         mlx5_esw_vporttbl_put(esw, &per_vport_tbl_attr);
503 err_default_tbl:
504         kfree(sample_flow);
505         return ERR_PTR(err);
506 }
507
508 void
509 mlx5_esw_sample_unoffload(struct mlx5_esw_psample *esw_psample,
510                           struct mlx5_flow_handle *rule,
511                           struct mlx5_flow_attr *attr)
512 {
513         struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
514         struct mlx5_sample_flow *sample_flow;
515         struct mlx5_vport_tbl_attr tbl_attr;
516         struct mlx5_flow_attr *pre_attr;
517         struct mlx5_eswitch *esw;
518
519         if (IS_ERR_OR_NULL(esw_psample))
520                 return;
521
522         /* If slow path flag is set, sample action is not offloaded.
523          * No need to delete sample rule.
524          */
525         esw = esw_psample->priv->mdev->priv.eswitch;
526         if (attr->flags & MLX5_ESW_ATTR_FLAG_SLOW_PATH) {
527                 mlx5_eswitch_del_offloaded_rule(esw, rule, attr);
528                 return;
529         }
530
531         sample_flow = esw_attr->sample->sample_flow;
532         pre_attr = sample_flow->pre_attr;
533         memset(pre_attr, 0, sizeof(*pre_attr));
534         esw = esw_psample->priv->mdev->priv.eswitch;
535         mlx5_eswitch_del_offloaded_rule(esw, sample_flow->pre_rule, pre_attr);
536         mlx5_eswitch_del_offloaded_rule(esw, sample_flow->rule, attr);
537
538         sample_restore_put(esw_psample, sample_flow->restore);
539         mapping_remove(esw->offloads.reg_c0_obj_pool, esw_attr->sample->restore_obj_id);
540         sampler_put(esw_psample, sample_flow->sampler);
541         tbl_attr.chain = attr->chain;
542         tbl_attr.prio = attr->prio;
543         tbl_attr.vport = esw_attr->in_rep->vport;
544         tbl_attr.vport_ns = &mlx5_esw_vport_tbl_sample_ns;
545         mlx5_esw_vporttbl_put(esw, &tbl_attr);
546
547         kfree(pre_attr->esw_attr->sample);
548         kfree(pre_attr);
549         kfree(sample_flow);
550 }
551
552 struct mlx5_esw_psample *
553 mlx5_esw_sample_init(struct mlx5e_priv *priv)
554 {
555         struct mlx5_esw_psample *esw_psample;
556         int err;
557
558         esw_psample = kzalloc(sizeof(*esw_psample), GFP_KERNEL);
559         if (!esw_psample)
560                 return ERR_PTR(-ENOMEM);
561         esw_psample->priv = priv;
562         err = sampler_termtbl_create(esw_psample);
563         if (err)
564                 goto err_termtbl;
565
566         mutex_init(&esw_psample->ht_lock);
567         mutex_init(&esw_psample->restore_lock);
568
569         return esw_psample;
570
571 err_termtbl:
572         kfree(esw_psample);
573         return ERR_PTR(err);
574 }
575
576 void
577 mlx5_esw_sample_cleanup(struct mlx5_esw_psample *esw_psample)
578 {
579         if (IS_ERR_OR_NULL(esw_psample))
580                 return;
581
582         mutex_destroy(&esw_psample->restore_lock);
583         mutex_destroy(&esw_psample->ht_lock);
584         sampler_termtbl_destroy(esw_psample);
585         kfree(esw_psample);
586 }