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 "en/tc/post_act.h"
8 #include "sample.h"
9 #include "eswitch.h"
10 #include "en_tc.h"
11 #include "fs_core.h"
12 
13 #define MLX5_ESW_VPORT_TBL_SIZE_SAMPLE (64 * 1024)
14 
15 static const struct esw_vport_tbl_namespace mlx5_esw_vport_tbl_sample_ns = {
16 	.max_fte = MLX5_ESW_VPORT_TBL_SIZE_SAMPLE,
17 	.max_num_groups = 0,    /* default num of groups */
18 	.flags = MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT | MLX5_FLOW_TABLE_TUNNEL_EN_DECAP,
19 };
20 
21 struct mlx5e_tc_psample {
22 	struct mlx5_eswitch *esw;
23 	struct mlx5_flow_table *termtbl;
24 	struct mlx5_flow_handle *termtbl_rule;
25 	DECLARE_HASHTABLE(hashtbl, 8);
26 	struct mutex ht_lock; /* protect hashtbl */
27 	DECLARE_HASHTABLE(restore_hashtbl, 8);
28 	struct mutex restore_lock; /* protect restore_hashtbl */
29 	struct mlx5e_post_act *post_act;
30 };
31 
32 struct mlx5e_sampler {
33 	struct hlist_node hlist;
34 	u32 sampler_id;
35 	u32 sample_ratio;
36 	u32 sample_table_id;
37 	u32 default_table_id;
38 	int count;
39 };
40 
41 struct mlx5e_sample_flow {
42 	struct mlx5e_sampler *sampler;
43 	struct mlx5e_sample_restore *restore;
44 	struct mlx5_flow_attr *pre_attr;
45 	struct mlx5_flow_handle *pre_rule;
46 	struct mlx5_flow_attr *post_attr;
47 	struct mlx5_flow_handle *post_rule;
48 	struct mlx5e_post_act_handle *post_act_handle;
49 };
50 
51 struct mlx5e_sample_restore {
52 	struct hlist_node hlist;
53 	struct mlx5_modify_hdr *modify_hdr;
54 	struct mlx5_flow_handle *rule;
55 	struct mlx5e_post_act_handle *post_act_handle;
56 	u32 obj_id;
57 	int count;
58 };
59 
60 static int
61 sampler_termtbl_create(struct mlx5e_tc_psample *tc_psample)
62 {
63 	struct mlx5_eswitch *esw = tc_psample->esw;
64 	struct mlx5_flow_table_attr ft_attr = {};
65 	struct mlx5_flow_destination dest = {};
66 	struct mlx5_core_dev *dev = esw->dev;
67 	struct mlx5_flow_namespace *root_ns;
68 	struct mlx5_flow_act act = {};
69 	int err;
70 
71 	if (!MLX5_CAP_ESW_FLOWTABLE_FDB(dev, termination_table))  {
72 		mlx5_core_warn(dev, "termination table is not supported\n");
73 		return -EOPNOTSUPP;
74 	}
75 
76 	root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
77 	if (!root_ns) {
78 		mlx5_core_warn(dev, "failed to get FDB flow namespace\n");
79 		return -EOPNOTSUPP;
80 	}
81 
82 	ft_attr.flags = MLX5_FLOW_TABLE_TERMINATION | MLX5_FLOW_TABLE_UNMANAGED;
83 	ft_attr.autogroup.max_num_groups = 1;
84 	ft_attr.prio = FDB_SLOW_PATH;
85 	ft_attr.max_fte = 1;
86 	ft_attr.level = 1;
87 	tc_psample->termtbl = mlx5_create_auto_grouped_flow_table(root_ns, &ft_attr);
88 	if (IS_ERR(tc_psample->termtbl)) {
89 		err = PTR_ERR(tc_psample->termtbl);
90 		mlx5_core_warn(dev, "failed to create termtbl, err: %d\n", err);
91 		return err;
92 	}
93 
94 	act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
95 	dest.vport.num = esw->manager_vport;
96 	tc_psample->termtbl_rule = mlx5_add_flow_rules(tc_psample->termtbl, NULL, &act, &dest, 1);
97 	if (IS_ERR(tc_psample->termtbl_rule)) {
98 		err = PTR_ERR(tc_psample->termtbl_rule);
99 		mlx5_core_warn(dev, "failed to create termtbl rule, err: %d\n", err);
100 		mlx5_destroy_flow_table(tc_psample->termtbl);
101 		return err;
102 	}
103 
104 	return 0;
105 }
106 
107 static void
108 sampler_termtbl_destroy(struct mlx5e_tc_psample *tc_psample)
109 {
110 	mlx5_del_flow_rules(tc_psample->termtbl_rule);
111 	mlx5_destroy_flow_table(tc_psample->termtbl);
112 }
113 
114 static int
115 sampler_obj_create(struct mlx5_core_dev *mdev, struct mlx5e_sampler *sampler)
116 {
117 	u32 in[MLX5_ST_SZ_DW(create_sampler_obj_in)] = {};
118 	u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
119 	u64 general_obj_types;
120 	void *obj;
121 	int err;
122 
123 	general_obj_types = MLX5_CAP_GEN_64(mdev, general_obj_types);
124 	if (!(general_obj_types & MLX5_HCA_CAP_GENERAL_OBJECT_TYPES_SAMPLER))
125 		return -EOPNOTSUPP;
126 	if (!MLX5_CAP_ESW_FLOWTABLE_FDB(mdev, ignore_flow_level))
127 		return -EOPNOTSUPP;
128 
129 	obj = MLX5_ADDR_OF(create_sampler_obj_in, in, sampler_object);
130 	MLX5_SET(sampler_obj, obj, table_type, FS_FT_FDB);
131 	MLX5_SET(sampler_obj, obj, ignore_flow_level, 1);
132 	MLX5_SET(sampler_obj, obj, level, 1);
133 	MLX5_SET(sampler_obj, obj, sample_ratio, sampler->sample_ratio);
134 	MLX5_SET(sampler_obj, obj, sample_table_id, sampler->sample_table_id);
135 	MLX5_SET(sampler_obj, obj, default_table_id, sampler->default_table_id);
136 	MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_CREATE_GENERAL_OBJECT);
137 	MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_GENERAL_OBJECT_TYPES_SAMPLER);
138 
139 	err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
140 	if (!err)
141 		sampler->sampler_id = MLX5_GET(general_obj_out_cmd_hdr, out, obj_id);
142 
143 	return err;
144 }
145 
146 static void
147 sampler_obj_destroy(struct mlx5_core_dev *mdev, u32 sampler_id)
148 {
149 	u32 in[MLX5_ST_SZ_DW(general_obj_in_cmd_hdr)] = {};
150 	u32 out[MLX5_ST_SZ_DW(general_obj_out_cmd_hdr)];
151 
152 	MLX5_SET(general_obj_in_cmd_hdr, in, opcode, MLX5_CMD_OP_DESTROY_GENERAL_OBJECT);
153 	MLX5_SET(general_obj_in_cmd_hdr, in, obj_type, MLX5_GENERAL_OBJECT_TYPES_SAMPLER);
154 	MLX5_SET(general_obj_in_cmd_hdr, in, obj_id, sampler_id);
155 
156 	mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out));
157 }
158 
159 static u32
160 sampler_hash(u32 sample_ratio, u32 default_table_id)
161 {
162 	return jhash_2words(sample_ratio, default_table_id, 0);
163 }
164 
165 static int
166 sampler_cmp(u32 sample_ratio1, u32 default_table_id1, u32 sample_ratio2, u32 default_table_id2)
167 {
168 	return sample_ratio1 != sample_ratio2 || default_table_id1 != default_table_id2;
169 }
170 
171 static struct mlx5e_sampler *
172 sampler_get(struct mlx5e_tc_psample *tc_psample, u32 sample_ratio, u32 default_table_id)
173 {
174 	struct mlx5e_sampler *sampler;
175 	u32 hash_key;
176 	int err;
177 
178 	mutex_lock(&tc_psample->ht_lock);
179 	hash_key = sampler_hash(sample_ratio, default_table_id);
180 	hash_for_each_possible(tc_psample->hashtbl, sampler, hlist, hash_key)
181 		if (!sampler_cmp(sampler->sample_ratio, sampler->default_table_id,
182 				 sample_ratio, default_table_id))
183 			goto add_ref;
184 
185 	sampler = kzalloc(sizeof(*sampler), GFP_KERNEL);
186 	if (!sampler) {
187 		err = -ENOMEM;
188 		goto err_alloc;
189 	}
190 
191 	sampler->sample_table_id = tc_psample->termtbl->id;
192 	sampler->default_table_id = default_table_id;
193 	sampler->sample_ratio = sample_ratio;
194 
195 	err = sampler_obj_create(tc_psample->esw->dev, sampler);
196 	if (err)
197 		goto err_create;
198 
199 	hash_add(tc_psample->hashtbl, &sampler->hlist, hash_key);
200 
201 add_ref:
202 	sampler->count++;
203 	mutex_unlock(&tc_psample->ht_lock);
204 	return sampler;
205 
206 err_create:
207 	kfree(sampler);
208 err_alloc:
209 	mutex_unlock(&tc_psample->ht_lock);
210 	return ERR_PTR(err);
211 }
212 
213 static void
214 sampler_put(struct mlx5e_tc_psample *tc_psample, struct mlx5e_sampler *sampler)
215 {
216 	mutex_lock(&tc_psample->ht_lock);
217 	if (--sampler->count == 0) {
218 		hash_del(&sampler->hlist);
219 		sampler_obj_destroy(tc_psample->esw->dev, sampler->sampler_id);
220 		kfree(sampler);
221 	}
222 	mutex_unlock(&tc_psample->ht_lock);
223 }
224 
225 /* obj_id is used to restore the sample parameters.
226  * Set fte_id in original flow table, then match it in the default table.
227  * Only set it for NICs can preserve reg_c or decap action. For other cases,
228  * use the same match in the default table.
229  * Use one header rewrite for both obj_id and fte_id.
230  */
231 static struct mlx5_modify_hdr *
232 sample_modify_hdr_get(struct mlx5_core_dev *mdev, u32 obj_id,
233 		      struct mlx5e_post_act_handle *handle)
234 {
235 	struct mlx5e_tc_mod_hdr_acts mod_acts = {};
236 	struct mlx5_modify_hdr *modify_hdr;
237 	int err;
238 
239 	err = mlx5e_tc_match_to_reg_set(mdev, &mod_acts, MLX5_FLOW_NAMESPACE_FDB,
240 					CHAIN_TO_REG, obj_id);
241 	if (err)
242 		goto err_set_regc0;
243 
244 	if (handle) {
245 		err = mlx5e_tc_post_act_set_handle(mdev, handle, &mod_acts);
246 		if (err)
247 			goto err_post_act;
248 	}
249 
250 	modify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_FDB,
251 					      mod_acts.num_actions,
252 					      mod_acts.actions);
253 	if (IS_ERR(modify_hdr)) {
254 		err = PTR_ERR(modify_hdr);
255 		goto err_modify_hdr;
256 	}
257 
258 	dealloc_mod_hdr_actions(&mod_acts);
259 	return modify_hdr;
260 
261 err_modify_hdr:
262 err_post_act:
263 	dealloc_mod_hdr_actions(&mod_acts);
264 err_set_regc0:
265 	return ERR_PTR(err);
266 }
267 
268 static u32
269 restore_hash(u32 obj_id, struct mlx5e_post_act_handle *post_act_handle)
270 {
271 	return jhash_2words(obj_id, hash32_ptr(post_act_handle), 0);
272 }
273 
274 static bool
275 restore_equal(struct mlx5e_sample_restore *restore, u32 obj_id,
276 	      struct mlx5e_post_act_handle *post_act_handle)
277 {
278 	return restore->obj_id == obj_id && restore->post_act_handle == post_act_handle;
279 }
280 
281 static struct mlx5e_sample_restore *
282 sample_restore_get(struct mlx5e_tc_psample *tc_psample, u32 obj_id,
283 		   struct mlx5e_post_act_handle *post_act_handle)
284 {
285 	struct mlx5_eswitch *esw = tc_psample->esw;
286 	struct mlx5_core_dev *mdev = esw->dev;
287 	struct mlx5e_sample_restore *restore;
288 	struct mlx5_modify_hdr *modify_hdr;
289 	u32 hash_key;
290 	int err;
291 
292 	mutex_lock(&tc_psample->restore_lock);
293 	hash_key = restore_hash(obj_id, post_act_handle);
294 	hash_for_each_possible(tc_psample->restore_hashtbl, restore, hlist, hash_key)
295 		if (restore_equal(restore, obj_id, post_act_handle))
296 			goto add_ref;
297 
298 	restore = kzalloc(sizeof(*restore), GFP_KERNEL);
299 	if (!restore) {
300 		err = -ENOMEM;
301 		goto err_alloc;
302 	}
303 	restore->obj_id = obj_id;
304 	restore->post_act_handle = post_act_handle;
305 
306 	modify_hdr = sample_modify_hdr_get(mdev, obj_id, post_act_handle);
307 	if (IS_ERR(modify_hdr)) {
308 		err = PTR_ERR(modify_hdr);
309 		goto err_modify_hdr;
310 	}
311 	restore->modify_hdr = modify_hdr;
312 
313 	restore->rule = esw_add_restore_rule(esw, obj_id);
314 	if (IS_ERR(restore->rule)) {
315 		err = PTR_ERR(restore->rule);
316 		goto err_restore;
317 	}
318 
319 	hash_add(tc_psample->restore_hashtbl, &restore->hlist, hash_key);
320 add_ref:
321 	restore->count++;
322 	mutex_unlock(&tc_psample->restore_lock);
323 	return restore;
324 
325 err_restore:
326 	mlx5_modify_header_dealloc(mdev, restore->modify_hdr);
327 err_modify_hdr:
328 	kfree(restore);
329 err_alloc:
330 	mutex_unlock(&tc_psample->restore_lock);
331 	return ERR_PTR(err);
332 }
333 
334 static void
335 sample_restore_put(struct mlx5e_tc_psample *tc_psample, struct mlx5e_sample_restore *restore)
336 {
337 	mutex_lock(&tc_psample->restore_lock);
338 	if (--restore->count == 0)
339 		hash_del(&restore->hlist);
340 	mutex_unlock(&tc_psample->restore_lock);
341 
342 	if (!restore->count) {
343 		mlx5_del_flow_rules(restore->rule);
344 		mlx5_modify_header_dealloc(tc_psample->esw->dev, restore->modify_hdr);
345 		kfree(restore);
346 	}
347 }
348 
349 void mlx5e_tc_sample_skb(struct sk_buff *skb, struct mlx5_mapped_obj *mapped_obj)
350 {
351 	u32 trunc_size = mapped_obj->sample.trunc_size;
352 	struct psample_group psample_group = {};
353 	struct psample_metadata md = {};
354 
355 	md.trunc_size = trunc_size ? min(trunc_size, skb->len) : skb->len;
356 	md.in_ifindex = skb->dev->ifindex;
357 	psample_group.group_num = mapped_obj->sample.group_id;
358 	psample_group.net = &init_net;
359 	skb_push(skb, skb->mac_len);
360 
361 	psample_sample_packet(&psample_group, skb, mapped_obj->sample.rate, &md);
362 }
363 
364 static int
365 add_post_rule(struct mlx5_eswitch *esw, struct mlx5e_sample_flow *sample_flow,
366 	      struct mlx5_flow_spec *spec, struct mlx5_flow_attr *attr,
367 	      u32 *default_tbl_id)
368 {
369 	struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
370 	u32 attr_sz = ns_to_attr_sz(MLX5_FLOW_NAMESPACE_FDB);
371 	struct mlx5_vport_tbl_attr per_vport_tbl_attr;
372 	struct mlx5_flow_table *default_tbl;
373 	struct mlx5_flow_attr *post_attr;
374 	int err;
375 
376 	/* Allocate default table per vport, chain and prio. Otherwise, there is
377 	 * only one default table for the same sampler object. Rules with different
378 	 * prio and chain may overlap. For CT sample action, per vport default
379 	 * table is needed to resotre the metadata.
380 	 */
381 	per_vport_tbl_attr.chain = attr->chain;
382 	per_vport_tbl_attr.prio = attr->prio;
383 	per_vport_tbl_attr.vport = esw_attr->in_rep->vport;
384 	per_vport_tbl_attr.vport_ns = &mlx5_esw_vport_tbl_sample_ns;
385 	default_tbl = mlx5_esw_vporttbl_get(esw, &per_vport_tbl_attr);
386 	if (IS_ERR(default_tbl)) {
387 		err = PTR_ERR(default_tbl);
388 		goto err_default_tbl;
389 	}
390 	*default_tbl_id = default_tbl->id;
391 
392 	post_attr = mlx5_alloc_flow_attr(MLX5_FLOW_NAMESPACE_FDB);
393 	if (!post_attr) {
394 		err = -ENOMEM;
395 		goto err_attr;
396 	}
397 	sample_flow->post_attr = post_attr;
398 	memcpy(post_attr, attr, attr_sz);
399 	/* Perform the original matches on the default table.
400 	 * Offload all actions except the sample action.
401 	 */
402 	post_attr->chain = 0;
403 	post_attr->prio = 0;
404 	post_attr->ft = default_tbl;
405 	post_attr->flags = MLX5_ESW_ATTR_FLAG_NO_IN_PORT;
406 
407 	/* When offloading sample and encap action, if there is no valid
408 	 * neigh data struct, a slow path rule is offloaded first. Source
409 	 * port metadata match is set at that time. A per vport table is
410 	 * already allocated. No need to match it again. So clear the source
411 	 * port metadata match.
412 	 */
413 	mlx5_eswitch_clear_rule_source_port(esw, spec);
414 	sample_flow->post_rule = mlx5_eswitch_add_offloaded_rule(esw, spec, post_attr);
415 	if (IS_ERR(sample_flow->post_rule)) {
416 		err = PTR_ERR(sample_flow->post_rule);
417 		goto err_rule;
418 	}
419 	return 0;
420 
421 err_rule:
422 	kfree(post_attr);
423 err_attr:
424 	mlx5_esw_vporttbl_put(esw, &per_vport_tbl_attr);
425 err_default_tbl:
426 	return err;
427 }
428 
429 static void
430 del_post_rule(struct mlx5_eswitch *esw, struct mlx5e_sample_flow *sample_flow,
431 	      struct mlx5_flow_attr *attr)
432 {
433 	struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
434 	struct mlx5_vport_tbl_attr tbl_attr;
435 
436 	mlx5_eswitch_del_offloaded_rule(esw, sample_flow->post_rule, sample_flow->post_attr);
437 	kfree(sample_flow->post_attr);
438 	tbl_attr.chain = attr->chain;
439 	tbl_attr.prio = attr->prio;
440 	tbl_attr.vport = esw_attr->in_rep->vport;
441 	tbl_attr.vport_ns = &mlx5_esw_vport_tbl_sample_ns;
442 	mlx5_esw_vporttbl_put(esw, &tbl_attr);
443 }
444 
445 /* For the following typical flow table:
446  *
447  * +-------------------------------+
448  * +       original flow table     +
449  * +-------------------------------+
450  * +         original match        +
451  * +-------------------------------+
452  * + sample action + other actions +
453  * +-------------------------------+
454  *
455  * We translate the tc filter with sample action to the following HW model:
456  *
457  *         +---------------------+
458  *         + original flow table +
459  *         +---------------------+
460  *         +   original match    +
461  *         +---------------------+
462  *               | set fte_id (if reg_c preserve cap)
463  *               | do decap (if required)
464  *               v
465  * +------------------------------------------------+
466  * +                Flow Sampler Object             +
467  * +------------------------------------------------+
468  * +                    sample ratio                +
469  * +------------------------------------------------+
470  * +    sample table id    |    default table id    +
471  * +------------------------------------------------+
472  *            |                            |
473  *            v                            v
474  * +-----------------------------+  +-------------------+
475  * +        sample table         +  +   default table   +
476  * +-----------------------------+  +-------------------+
477  * + forward to management vport +             |
478  * +-----------------------------+             |
479  *                                     +-------+------+
480  *                                     |              |reg_c preserve cap
481  *                                     |              |or decap action
482  *                                     v              v
483  *                        +-----------------+   +-------------+
484  *                        + per vport table +   + post action +
485  *                        +-----------------+   +-------------+
486  *                        + original match  +
487  *                        +-----------------+
488  *                        + other actions   +
489  *                        +-----------------+
490  */
491 struct mlx5_flow_handle *
492 mlx5e_tc_sample_offload(struct mlx5e_tc_psample *tc_psample,
493 			struct mlx5_flow_spec *spec,
494 			struct mlx5_flow_attr *attr,
495 			u32 tunnel_id)
496 {
497 	struct mlx5e_post_act_handle *post_act_handle = NULL;
498 	struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
499 	struct mlx5_esw_flow_attr *pre_esw_attr;
500 	struct mlx5_mapped_obj restore_obj = {};
501 	struct mlx5e_sample_flow *sample_flow;
502 	struct mlx5e_sample_attr *sample_attr;
503 	struct mlx5_flow_attr *pre_attr;
504 	struct mlx5_eswitch *esw;
505 	u32 default_tbl_id;
506 	u32 obj_id;
507 	int err;
508 
509 	if (IS_ERR_OR_NULL(tc_psample))
510 		return ERR_PTR(-EOPNOTSUPP);
511 
512 	/* If slow path flag is set, eg. when the neigh is invalid for encap,
513 	 * don't offload sample action.
514 	 */
515 	esw = tc_psample->esw;
516 	if (attr->flags & MLX5_ESW_ATTR_FLAG_SLOW_PATH)
517 		return mlx5_eswitch_add_offloaded_rule(esw, spec, attr);
518 
519 	sample_flow = kzalloc(sizeof(*sample_flow), GFP_KERNEL);
520 	if (!sample_flow)
521 		return ERR_PTR(-ENOMEM);
522 	sample_attr = attr->sample_attr;
523 	sample_attr->sample_flow = sample_flow;
524 
525 	/* For NICs with reg_c_preserve support or decap action, use
526 	 * post action instead of the per vport, chain and prio table.
527 	 * Only match the fte id instead of the same match in the
528 	 * original flow table.
529 	 */
530 	if (MLX5_CAP_GEN(esw->dev, reg_c_preserve) ||
531 	    attr->action & MLX5_FLOW_CONTEXT_ACTION_DECAP) {
532 		struct mlx5_flow_table *ft;
533 
534 		ft = mlx5e_tc_post_act_get_ft(tc_psample->post_act);
535 		default_tbl_id = ft->id;
536 		post_act_handle = mlx5e_tc_post_act_add(tc_psample->post_act, attr);
537 		if (IS_ERR(post_act_handle)) {
538 			err = PTR_ERR(post_act_handle);
539 			goto err_post_act;
540 		}
541 		sample_flow->post_act_handle = post_act_handle;
542 	} else {
543 		err = add_post_rule(esw, sample_flow, spec, attr, &default_tbl_id);
544 		if (err)
545 			goto err_post_rule;
546 	}
547 
548 	/* Create sampler object. */
549 	sample_flow->sampler = sampler_get(tc_psample, sample_attr->rate, default_tbl_id);
550 	if (IS_ERR(sample_flow->sampler)) {
551 		err = PTR_ERR(sample_flow->sampler);
552 		goto err_sampler;
553 	}
554 
555 	/* Create an id mapping reg_c0 value to sample object. */
556 	restore_obj.type = MLX5_MAPPED_OBJ_SAMPLE;
557 	restore_obj.sample.group_id = sample_attr->group_num;
558 	restore_obj.sample.rate = sample_attr->rate;
559 	restore_obj.sample.trunc_size = sample_attr->trunc_size;
560 	restore_obj.sample.tunnel_id = tunnel_id;
561 	err = mapping_add(esw->offloads.reg_c0_obj_pool, &restore_obj, &obj_id);
562 	if (err)
563 		goto err_obj_id;
564 	sample_attr->restore_obj_id = obj_id;
565 
566 	/* Create sample restore context. */
567 	sample_flow->restore = sample_restore_get(tc_psample, obj_id, post_act_handle);
568 	if (IS_ERR(sample_flow->restore)) {
569 		err = PTR_ERR(sample_flow->restore);
570 		goto err_sample_restore;
571 	}
572 
573 	/* Perform the original matches on the original table. Offload the
574 	 * sample action. The destination is the sampler object.
575 	 */
576 	pre_attr = mlx5_alloc_flow_attr(MLX5_FLOW_NAMESPACE_FDB);
577 	if (!pre_attr) {
578 		err = -ENOMEM;
579 		goto err_alloc_pre_flow_attr;
580 	}
581 	pre_attr->action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | MLX5_FLOW_CONTEXT_ACTION_MOD_HDR;
582 	/* For decap action, do decap in the original flow table instead of the
583 	 * default flow table.
584 	 */
585 	if (tunnel_id)
586 		pre_attr->action |= MLX5_FLOW_CONTEXT_ACTION_DECAP;
587 	pre_attr->modify_hdr = sample_flow->restore->modify_hdr;
588 	pre_attr->flags = MLX5_ESW_ATTR_FLAG_SAMPLE;
589 	pre_attr->inner_match_level = attr->inner_match_level;
590 	pre_attr->outer_match_level = attr->outer_match_level;
591 	pre_attr->chain = attr->chain;
592 	pre_attr->prio = attr->prio;
593 	pre_attr->sample_attr = attr->sample_attr;
594 	sample_attr->sampler_id = sample_flow->sampler->sampler_id;
595 	pre_esw_attr = pre_attr->esw_attr;
596 	pre_esw_attr->in_mdev = esw_attr->in_mdev;
597 	pre_esw_attr->in_rep = esw_attr->in_rep;
598 	sample_flow->pre_rule = mlx5_eswitch_add_offloaded_rule(esw, spec, pre_attr);
599 	if (IS_ERR(sample_flow->pre_rule)) {
600 		err = PTR_ERR(sample_flow->pre_rule);
601 		goto err_pre_offload_rule;
602 	}
603 	sample_flow->pre_attr = pre_attr;
604 
605 	return sample_flow->post_rule;
606 
607 err_pre_offload_rule:
608 	kfree(pre_attr);
609 err_alloc_pre_flow_attr:
610 	sample_restore_put(tc_psample, sample_flow->restore);
611 err_sample_restore:
612 	mapping_remove(esw->offloads.reg_c0_obj_pool, obj_id);
613 err_obj_id:
614 	sampler_put(tc_psample, sample_flow->sampler);
615 err_sampler:
616 	if (!post_act_handle)
617 		del_post_rule(esw, sample_flow, attr);
618 err_post_rule:
619 	if (post_act_handle)
620 		mlx5e_tc_post_act_del(tc_psample->post_act, post_act_handle);
621 err_post_act:
622 	kfree(sample_flow);
623 	return ERR_PTR(err);
624 }
625 
626 void
627 mlx5e_tc_sample_unoffload(struct mlx5e_tc_psample *tc_psample,
628 			  struct mlx5_flow_handle *rule,
629 			  struct mlx5_flow_attr *attr)
630 {
631 	struct mlx5_esw_flow_attr *esw_attr = attr->esw_attr;
632 	struct mlx5e_sample_flow *sample_flow;
633 	struct mlx5_vport_tbl_attr tbl_attr;
634 	struct mlx5_eswitch *esw;
635 
636 	if (IS_ERR_OR_NULL(tc_psample))
637 		return;
638 
639 	/* If slow path flag is set, sample action is not offloaded.
640 	 * No need to delete sample rule.
641 	 */
642 	esw = tc_psample->esw;
643 	if (attr->flags & MLX5_ESW_ATTR_FLAG_SLOW_PATH) {
644 		mlx5_eswitch_del_offloaded_rule(esw, rule, attr);
645 		return;
646 	}
647 
648 	/* The following delete order can't be changed, otherwise,
649 	 * will hit fw syndromes.
650 	 */
651 	sample_flow = attr->sample_attr->sample_flow;
652 	mlx5_eswitch_del_offloaded_rule(esw, sample_flow->pre_rule, sample_flow->pre_attr);
653 	if (!sample_flow->post_act_handle)
654 		mlx5_eswitch_del_offloaded_rule(esw, sample_flow->post_rule,
655 						sample_flow->post_attr);
656 
657 	sample_restore_put(tc_psample, sample_flow->restore);
658 	mapping_remove(esw->offloads.reg_c0_obj_pool, attr->sample_attr->restore_obj_id);
659 	sampler_put(tc_psample, sample_flow->sampler);
660 	if (sample_flow->post_act_handle) {
661 		mlx5e_tc_post_act_del(tc_psample->post_act, sample_flow->post_act_handle);
662 	} else {
663 		tbl_attr.chain = attr->chain;
664 		tbl_attr.prio = attr->prio;
665 		tbl_attr.vport = esw_attr->in_rep->vport;
666 		tbl_attr.vport_ns = &mlx5_esw_vport_tbl_sample_ns;
667 		mlx5_esw_vporttbl_put(esw, &tbl_attr);
668 		kfree(sample_flow->post_attr);
669 	}
670 
671 	kfree(sample_flow->pre_attr);
672 	kfree(sample_flow);
673 }
674 
675 struct mlx5e_tc_psample *
676 mlx5e_tc_sample_init(struct mlx5_eswitch *esw, struct mlx5e_post_act *post_act)
677 {
678 	struct mlx5e_tc_psample *tc_psample;
679 	int err;
680 
681 	tc_psample = kzalloc(sizeof(*tc_psample), GFP_KERNEL);
682 	if (!tc_psample)
683 		return ERR_PTR(-ENOMEM);
684 	if (IS_ERR_OR_NULL(post_act)) {
685 		err = PTR_ERR(post_act);
686 		goto err_post_act;
687 	}
688 	tc_psample->post_act = post_act;
689 	tc_psample->esw = esw;
690 	err = sampler_termtbl_create(tc_psample);
691 	if (err)
692 		goto err_post_act;
693 
694 	mutex_init(&tc_psample->ht_lock);
695 	mutex_init(&tc_psample->restore_lock);
696 
697 	return tc_psample;
698 
699 err_post_act:
700 	kfree(tc_psample);
701 	return ERR_PTR(err);
702 }
703 
704 void
705 mlx5e_tc_sample_cleanup(struct mlx5e_tc_psample *tc_psample)
706 {
707 	if (IS_ERR_OR_NULL(tc_psample))
708 		return;
709 
710 	mutex_destroy(&tc_psample->restore_lock);
711 	mutex_destroy(&tc_psample->ht_lock);
712 	sampler_termtbl_destroy(tc_psample);
713 	kfree(tc_psample);
714 }
715