eswitch.c 64.1 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
 * Copyright (c) 2015, Mellanox Technologies. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <linux/etherdevice.h>
#include <linux/mlx5/driver.h>
#include <linux/mlx5/mlx5_ifc.h>
#include <linux/mlx5/vport.h>
37
#include <linux/mlx5/fs.h>
38
#include "mlx5_core.h"
39
#include "lib/eq.h"
40
#include "eswitch.h"
41
#include "fs_core.h"
42
43
44
45
46
47
48

enum {
	MLX5_ACTION_NONE = 0,
	MLX5_ACTION_ADD  = 1,
	MLX5_ACTION_DEL  = 2,
};

49
50
51
52
/* Vport UC/MC hash node */
struct vport_addr {
	struct l2addr_node     node;
	u8                     action;
53
	u16                    vport;
54
55
	struct mlx5_flow_handle *flow_rule;
	bool mpfs; /* UC MAC was added to MPFs */
56
57
	/* A flag indicating that mac was added due to mc promiscuous vport */
	bool mc_promisc;
58
59
60
61
62
};

enum {
	UC_ADDR_CHANGE = BIT(0),
	MC_ADDR_CHANGE = BIT(1),
63
	PROMISC_CHANGE = BIT(3),
64
65
};

66
67
/* Vport context events */
#define SRIOV_VPORT_EVENTS (UC_ADDR_CHANGE | \
68
69
			    MC_ADDR_CHANGE | \
			    PROMISC_CHANGE)
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
/* The vport getter/iterator are only valid after esw->total_vports
 * and vport->vport are initialized in mlx5_eswitch_init.
 */
#define mlx5_esw_for_all_vports(esw, i, vport)			\
	for ((i) = MLX5_VPORT_PF;				\
	     (vport) = &(esw)->vports[i],			\
	     (i) < (esw)->total_vports; (i)++)

#define mlx5_esw_for_each_vf_vport(esw, i, vport, nvfs)	\
	for ((i) = MLX5_VPORT_FIRST_VF;				\
	     (vport) = &(esw)->vports[i],			\
	     (i) <= (nvfs); (i)++)

static struct mlx5_vport *mlx5_eswitch_get_vport(struct mlx5_eswitch *esw,
						 u16 vport_num)
{
87
88
	u16 idx = mlx5_eswitch_vport_num_to_index(esw, vport_num);

89
	WARN_ON(vport_num > esw->total_vports - 1);
90
	return &esw->vports[idx];
91
92
}

93
static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport,
94
95
					u32 events_mask)
{
96
97
	int in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)]   = {0};
	int out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)] = {0};
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
	void *nic_vport_ctx;

	MLX5_SET(modify_nic_vport_context_in, in,
		 opcode, MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
	MLX5_SET(modify_nic_vport_context_in, in, field_select.change_event, 1);
	MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport);
	if (vport)
		MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1);
	nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in,
				     in, nic_vport_context);

	MLX5_SET(nic_vport_context, nic_vport_ctx, arm_change_event, 1);

	if (events_mask & UC_ADDR_CHANGE)
		MLX5_SET(nic_vport_context, nic_vport_ctx,
			 event_on_uc_address_change, 1);
	if (events_mask & MC_ADDR_CHANGE)
		MLX5_SET(nic_vport_context, nic_vport_ctx,
			 event_on_mc_address_change, 1);
117
118
119
	if (events_mask & PROMISC_CHANGE)
		MLX5_SET(nic_vport_context, nic_vport_ctx,
			 event_on_promisc_change, 1);
120

121
	return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
122
123
}

124
125
126
127
/* E-Switch vport context HW commands */
static int modify_esw_vport_context_cmd(struct mlx5_core_dev *dev, u16 vport,
					void *in, int inlen)
{
128
	u32 out[MLX5_ST_SZ_DW(modify_esw_vport_context_out)] = {0};
129

130
131
	MLX5_SET(modify_esw_vport_context_in, in, opcode,
		 MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT);
132
133
134
	MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport);
	if (vport)
		MLX5_SET(modify_esw_vport_context_in, in, other_vport, 1);
135
	return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
136
137
}

138
static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u16 vport,
139
				  u16 vlan, u8 qos, u8 set_flags)
140
{
141
	u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {0};
142
143
144

	if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
	    !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
145
		return -EOPNOTSUPP;
146

147
148
149
150
	esw_debug(dev, "Set Vport[%d] VLAN %d qos %d set=%x\n",
		  vport, vlan, qos, set_flags);

	if (set_flags & SET_VLAN_STRIP)
151
152
		MLX5_SET(modify_esw_vport_context_in, in,
			 esw_vport_context.vport_cvlan_strip, 1);
153
154

	if (set_flags & SET_VLAN_INSERT) {
155
156
157
		/* insert only if no vlan in packet */
		MLX5_SET(modify_esw_vport_context_in, in,
			 esw_vport_context.vport_cvlan_insert, 1);
158

159
160
161
162
163
164
165
166
167
168
169
170
171
172
		MLX5_SET(modify_esw_vport_context_in, in,
			 esw_vport_context.cvlan_pcp, qos);
		MLX5_SET(modify_esw_vport_context_in, in,
			 esw_vport_context.cvlan_id, vlan);
	}

	MLX5_SET(modify_esw_vport_context_in, in,
		 field_select.vport_cvlan_strip, 1);
	MLX5_SET(modify_esw_vport_context_in, in,
		 field_select.vport_cvlan_insert, 1);

	return modify_esw_vport_context_cmd(dev, vport, in, sizeof(in));
}

173
/* E-Switch FDB */
Mark Bloch's avatar
Mark Bloch committed
174
static struct mlx5_flow_handle *
175
__esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u16 vport, bool rx_rule,
176
			 u8 mac_c[ETH_ALEN], u8 mac_v[ETH_ALEN])
177
{
178
179
	int match_header = (is_zero_ether_addr(mac_c) ? 0 :
			    MLX5_MATCH_OUTER_HEADERS);
Mark Bloch's avatar
Mark Bloch committed
180
	struct mlx5_flow_handle *flow_rule = NULL;
181
	struct mlx5_flow_act flow_act = {0};
182
	struct mlx5_flow_destination dest = {};
183
	struct mlx5_flow_spec *spec;
184
185
	void *mv_misc = NULL;
	void *mc_misc = NULL;
186
187
	u8 *dmac_v = NULL;
	u8 *dmac_c = NULL;
188

189
190
	if (rx_rule)
		match_header |= MLX5_MATCH_MISC_PARAMETERS;
191

192
193
	spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
	if (!spec)
194
		return NULL;
195

196
	dmac_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
197
			      outer_headers.dmac_47_16);
198
	dmac_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
199
200
			      outer_headers.dmac_47_16);

201
	if (match_header & MLX5_MATCH_OUTER_HEADERS) {
202
203
204
		ether_addr_copy(dmac_v, mac_v);
		ether_addr_copy(dmac_c, mac_c);
	}
205

206
	if (match_header & MLX5_MATCH_MISC_PARAMETERS) {
207
208
209
210
		mv_misc  = MLX5_ADDR_OF(fte_match_param, spec->match_value,
					misc_parameters);
		mc_misc  = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
					misc_parameters);
211
		MLX5_SET(fte_match_set_misc, mv_misc, source_port, MLX5_VPORT_UPLINK);
212
213
214
		MLX5_SET_TO_ONES(fte_match_set_misc, mc_misc, source_port);
	}

215
	dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
216
	dest.vport.num = vport;
217
218
219
220

	esw_debug(esw->dev,
		  "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n",
		  dmac_v, dmac_c, vport);
221
	spec->match_criteria_enable = match_header;
222
	flow_act.action =  MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
223
	flow_rule =
224
		mlx5_add_flow_rules(esw->fdb_table.legacy.fdb, spec,
225
				    &flow_act, &dest, 1);
226
	if (IS_ERR(flow_rule)) {
227
228
		esw_warn(esw->dev,
			 "FDB: Failed to add flow rule: dmac_v(%pM) dmac_c(%pM) -> vport(%d), err(%ld)\n",
229
230
231
			 dmac_v, dmac_c, vport, PTR_ERR(flow_rule));
		flow_rule = NULL;
	}
232
233

	kvfree(spec);
234
235
236
	return flow_rule;
}

Mark Bloch's avatar
Mark Bloch committed
237
static struct mlx5_flow_handle *
238
esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u16 vport)
239
240
241
242
{
	u8 mac_c[ETH_ALEN];

	eth_broadcast_addr(mac_c);
243
244
245
	return __esw_fdb_set_vport_rule(esw, vport, false, mac_c, mac);
}

Mark Bloch's avatar
Mark Bloch committed
246
static struct mlx5_flow_handle *
247
esw_fdb_set_vport_allmulti_rule(struct mlx5_eswitch *esw, u16 vport)
248
249
250
251
252
253
254
255
256
257
258
{
	u8 mac_c[ETH_ALEN];
	u8 mac_v[ETH_ALEN];

	eth_zero_addr(mac_c);
	eth_zero_addr(mac_v);
	mac_c[0] = 0x01;
	mac_v[0] = 0x01;
	return __esw_fdb_set_vport_rule(esw, vport, false, mac_c, mac_v);
}

Mark Bloch's avatar
Mark Bloch committed
259
static struct mlx5_flow_handle *
260
esw_fdb_set_vport_promisc_rule(struct mlx5_eswitch *esw, u16 vport)
261
262
263
264
265
266
267
{
	u8 mac_c[ETH_ALEN];
	u8 mac_v[ETH_ALEN];

	eth_zero_addr(mac_c);
	eth_zero_addr(mac_v);
	return __esw_fdb_set_vport_rule(esw, vport, true, mac_c, mac_v);
268
269
}

270
static int esw_create_legacy_fdb_table(struct mlx5_eswitch *esw)
271
{
272
	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
273
	struct mlx5_flow_table_attr ft_attr = {};
274
	struct mlx5_core_dev *dev = esw->dev;
275
	struct mlx5_flow_namespace *root_ns;
276
	struct mlx5_flow_table *fdb;
277
278
279
280
	struct mlx5_flow_group *g;
	void *match_criteria;
	int table_size;
	u32 *flow_group_in;
281
	u8 *dmac;
282
	int err = 0;
283
284
285
286

	esw_debug(dev, "Create FDB log_max_size(%d)\n",
		  MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));

287
	root_ns = mlx5_get_fdb_sub_ns(dev, 0);
288
289
	if (!root_ns) {
		esw_warn(dev, "Failed to get FDB flow namespace\n");
290
		return -EOPNOTSUPP;
291
	}
292

293
	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
294
295
296
297
	if (!flow_group_in)
		return -ENOMEM;

	table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
298
299
300

	ft_attr.max_fte = table_size;
	fdb = mlx5_create_flow_table(root_ns, &ft_attr);
301
	if (IS_ERR(fdb)) {
302
303
304
305
		err = PTR_ERR(fdb);
		esw_warn(dev, "Failed to create FDB Table err %d\n", err);
		goto out;
	}
306
	esw->fdb_table.legacy.fdb = fdb;
307

308
	/* Addresses group : Full match unicast/multicast addresses */
309
310
311
312
313
	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
		 MLX5_MATCH_OUTER_HEADERS);
	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
	dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, outer_headers.dmac_47_16);
	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
314
315
	/* Preserve 2 entries for allmulti and promisc rules*/
	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 3);
316
317
	eth_broadcast_addr(dmac);
	g = mlx5_create_flow_group(fdb, flow_group_in);
318
	if (IS_ERR(g)) {
319
320
321
322
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create flow group err(%d)\n", err);
		goto out;
	}
323
	esw->fdb_table.legacy.addr_grp = g;
324
325
326
327
328
329
330
331
332

	/* Allmulti group : One rule that forwards any mcast traffic */
	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
		 MLX5_MATCH_OUTER_HEADERS);
	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, table_size - 2);
	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 2);
	eth_zero_addr(dmac);
	dmac[0] = 0x01;
	g = mlx5_create_flow_group(fdb, flow_group_in);
333
	if (IS_ERR(g)) {
334
335
336
337
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create allmulti flow group err(%d)\n", err);
		goto out;
	}
338
	esw->fdb_table.legacy.allmulti_grp = g;
339
340
341
342
343
344
345
346
347
348
349

	/* Promiscuous group :
	 * One rule that forward all unmatched traffic from previous groups
	 */
	eth_zero_addr(dmac);
	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
		 MLX5_MATCH_MISC_PARAMETERS);
	MLX5_SET_TO_ONES(fte_match_param, match_criteria, misc_parameters.source_port);
	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, table_size - 1);
	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 1);
	g = mlx5_create_flow_group(fdb, flow_group_in);
350
	if (IS_ERR(g)) {
351
352
353
354
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create promisc flow group err(%d)\n", err);
		goto out;
	}
355
	esw->fdb_table.legacy.promisc_grp = g;
356

357
out:
358
	if (err) {
359
360
361
		if (!IS_ERR_OR_NULL(esw->fdb_table.legacy.allmulti_grp)) {
			mlx5_destroy_flow_group(esw->fdb_table.legacy.allmulti_grp);
			esw->fdb_table.legacy.allmulti_grp = NULL;
362
		}
363
364
365
		if (!IS_ERR_OR_NULL(esw->fdb_table.legacy.addr_grp)) {
			mlx5_destroy_flow_group(esw->fdb_table.legacy.addr_grp);
			esw->fdb_table.legacy.addr_grp = NULL;
366
		}
367
368
369
		if (!IS_ERR_OR_NULL(esw->fdb_table.legacy.fdb)) {
			mlx5_destroy_flow_table(esw->fdb_table.legacy.fdb);
			esw->fdb_table.legacy.fdb = NULL;
370
371
372
		}
	}

373
	kvfree(flow_group_in);
374
	return err;
375
376
}

377
static void esw_destroy_legacy_fdb_table(struct mlx5_eswitch *esw)
378
{
379
	if (!esw->fdb_table.legacy.fdb)
380
381
		return;

382
	esw_debug(esw->dev, "Destroy FDB Table\n");
383
384
385
	mlx5_destroy_flow_group(esw->fdb_table.legacy.promisc_grp);
	mlx5_destroy_flow_group(esw->fdb_table.legacy.allmulti_grp);
	mlx5_destroy_flow_group(esw->fdb_table.legacy.addr_grp);
386
387
	mlx5_destroy_flow_table(esw->fdb_table.legacy.fdb);
	esw->fdb_table.legacy.fdb = NULL;
388
389
390
	esw->fdb_table.legacy.addr_grp = NULL;
	esw->fdb_table.legacy.allmulti_grp = NULL;
	esw->fdb_table.legacy.promisc_grp = NULL;
391
392
393
394
395
396
397
398
399
}

/* E-Switch vport UC/MC lists management */
typedef int (*vport_addr_action)(struct mlx5_eswitch *esw,
				 struct vport_addr *vaddr);

static int esw_add_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
{
	u8 *mac = vaddr->node.addr;
400
	u16 vport = vaddr->vport;
401
402
	int err;

403
404
	/* Skip mlx5_mpfs_add_mac for eswitch_managers,
	 * it is already done by its netdev in mlx5e_execute_l2_action
405
	 */
406
	if (esw->manager_vport == vport)
407
408
409
410
		goto fdb_add;

	err = mlx5_mpfs_add_mac(esw->dev, mac);
	if (err) {
411
		esw_warn(esw->dev,
412
			 "Failed to add L2 table mac(%pM) for vport(0x%x), err(%d)\n",
413
414
			 mac, vport, err);
		return err;
415
	}
416
	vaddr->mpfs = true;
417

418
fdb_add:
419
	/* SRIOV is enabled: Forward UC MAC to vport */
420
	if (esw->fdb_table.legacy.fdb && esw->mode == SRIOV_LEGACY)
421
422
		vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);

423
424
425
	esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM fr(%p)\n",
		  vport, mac, vaddr->flow_rule);

426
	return 0;
427
428
}

429
static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
430
{
431
	u8 *mac = vaddr->node.addr;
432
	u16 vport = vaddr->vport;
433
	int err = 0;
434

435
436
	/* Skip mlx5_mpfs_del_mac for eswitch managerss,
	 * it is already done by its netdev in mlx5e_execute_l2_action
437
	 */
438
	if (!vaddr->mpfs || esw->manager_vport == vport)
439
		goto fdb_del;
440

441
442
443
444
445
446
	err = mlx5_mpfs_del_mac(esw->dev, mac);
	if (err)
		esw_warn(esw->dev,
			 "Failed to del L2 table mac(%pM) for vport(%d), err(%d)\n",
			 mac, vport, err);
	vaddr->mpfs = false;
447

448
fdb_del:
449
	if (vaddr->flow_rule)
Mark Bloch's avatar
Mark Bloch committed
450
		mlx5_del_flow_rules(vaddr->flow_rule);
451
452
453
454
455
	vaddr->flow_rule = NULL;

	return 0;
}

456
457
458
459
460
static void update_allmulti_vports(struct mlx5_eswitch *esw,
				   struct vport_addr *vaddr,
				   struct esw_mc_addr *esw_mc)
{
	u8 *mac = vaddr->node.addr;
461
462
	struct mlx5_vport *vport;
	u16 i, vport_num;
463

464
	mlx5_esw_for_all_vports(esw, i, vport) {
465
466
467
468
469
		struct hlist_head *vport_hash = vport->mc_list;
		struct vport_addr *iter_vaddr =
					l2addr_hash_find(vport_hash,
							 mac,
							 struct vport_addr);
470
		vport_num = vport->vport;
471
		if (IS_ERR_OR_NULL(vport->allmulti_rule) ||
472
		    vaddr->vport == vport_num)
473
474
475
476
477
478
479
480
481
482
483
			continue;
		switch (vaddr->action) {
		case MLX5_ACTION_ADD:
			if (iter_vaddr)
				continue;
			iter_vaddr = l2addr_hash_add(vport_hash, mac,
						     struct vport_addr,
						     GFP_KERNEL);
			if (!iter_vaddr) {
				esw_warn(esw->dev,
					 "ALL-MULTI: Failed to add MAC(%pM) to vport[%d] DB\n",
484
					 mac, vport_num);
485
486
				continue;
			}
487
			iter_vaddr->vport = vport_num;
488
489
490
			iter_vaddr->flow_rule =
					esw_fdb_set_vport_rule(esw,
							       mac,
491
							       vport_num);
492
			iter_vaddr->mc_promisc = true;
493
494
495
496
			break;
		case MLX5_ACTION_DEL:
			if (!iter_vaddr)
				continue;
Mark Bloch's avatar
Mark Bloch committed
497
			mlx5_del_flow_rules(iter_vaddr->flow_rule);
498
499
500
501
502
503
			l2addr_hash_del(iter_vaddr);
			break;
		}
	}
}

504
505
506
507
508
static int esw_add_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
{
	struct hlist_head *hash = esw->mc_table;
	struct esw_mc_addr *esw_mc;
	u8 *mac = vaddr->node.addr;
509
	u16 vport = vaddr->vport;
510

511
	if (!esw->fdb_table.legacy.fdb)
512
513
514
515
516
517
518
519
520
521
522
		return 0;

	esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
	if (esw_mc)
		goto add;

	esw_mc = l2addr_hash_add(hash, mac, struct esw_mc_addr, GFP_KERNEL);
	if (!esw_mc)
		return -ENOMEM;

	esw_mc->uplink_rule = /* Forward MC MAC to Uplink */
523
		esw_fdb_set_vport_rule(esw, mac, MLX5_VPORT_UPLINK);
524
525
526
527

	/* Add this multicast mac to all the mc promiscuous vports */
	update_allmulti_vports(esw, vaddr, esw_mc);

528
add:
529
530
531
532
533
534
	/* If the multicast mac is added as a result of mc promiscuous vport,
	 * don't increment the multicast ref count
	 */
	if (!vaddr->mc_promisc)
		esw_mc->refcnt++;

535
536
537
538
539
540
541
542
543
544
545
546
547
548
	/* Forward MC MAC to vport */
	vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);
	esw_debug(esw->dev,
		  "\tADDED MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n",
		  vport, mac, vaddr->flow_rule,
		  esw_mc->refcnt, esw_mc->uplink_rule);
	return 0;
}

static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
{
	struct hlist_head *hash = esw->mc_table;
	struct esw_mc_addr *esw_mc;
	u8 *mac = vaddr->node.addr;
549
	u16 vport = vaddr->vport;
550

551
	if (!esw->fdb_table.legacy.fdb)
552
553
554
555
556
557
		return 0;

	esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
	if (!esw_mc) {
		esw_warn(esw->dev,
			 "Failed to find eswitch MC addr for MAC(%pM) vport(%d)",
558
559
560
			 mac, vport);
		return -EINVAL;
	}
561
562
563
564
565
566
	esw_debug(esw->dev,
		  "\tDELETE MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n",
		  vport, mac, vaddr->flow_rule, esw_mc->refcnt,
		  esw_mc->uplink_rule);

	if (vaddr->flow_rule)
Mark Bloch's avatar
Mark Bloch committed
567
		mlx5_del_flow_rules(vaddr->flow_rule);
568
569
	vaddr->flow_rule = NULL;

570
571
572
573
	/* If the multicast mac is added as a result of mc promiscuous vport,
	 * don't decrement the multicast ref count.
	 */
	if (vaddr->mc_promisc || (--esw_mc->refcnt > 0))
574
		return 0;
575

576
577
578
	/* Remove this multicast mac from all the mc promiscuous vports */
	update_allmulti_vports(esw, vaddr, esw_mc);

579
	if (esw_mc->uplink_rule)
Mark Bloch's avatar
Mark Bloch committed
580
		mlx5_del_flow_rules(esw_mc->uplink_rule);
581
582

	l2addr_hash_del(esw_mc);
583
584
585
	return 0;
}

586
587
/* Apply vport UC/MC list to HW l2 table and FDB table */
static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw,
588
				      u16 vport_num, int list_type)
589
{
590
	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
591
592
593
594
	bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
	vport_addr_action vport_addr_add;
	vport_addr_action vport_addr_del;
	struct vport_addr *addr;
595
596
597
598
599
	struct l2addr_node *node;
	struct hlist_head *hash;
	struct hlist_node *tmp;
	int hi;

600
601
602
603
604
605
	vport_addr_add = is_uc ? esw_add_uc_addr :
				 esw_add_mc_addr;
	vport_addr_del = is_uc ? esw_del_uc_addr :
				 esw_del_mc_addr;

	hash = is_uc ? vport->uc_list : vport->mc_list;
606
	for_each_l2hash_node(node, tmp, hash, hi) {
607
		addr = container_of(node, struct vport_addr, node);
608
609
		switch (addr->action) {
		case MLX5_ACTION_ADD:
610
			vport_addr_add(esw, addr);
611
612
613
			addr->action = MLX5_ACTION_NONE;
			break;
		case MLX5_ACTION_DEL:
614
			vport_addr_del(esw, addr);
615
616
617
618
619
620
			l2addr_hash_del(addr);
			break;
		}
	}
}

621
622
/* Sync vport UC/MC list from vport context */
static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
623
				       u16 vport_num, int list_type)
624
{
625
	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
626
	bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
627
	u8 (*mac_list)[ETH_ALEN];
628
629
	struct l2addr_node *node;
	struct vport_addr *addr;
630
631
632
633
634
635
636
	struct hlist_head *hash;
	struct hlist_node *tmp;
	int size;
	int err;
	int hi;
	int i;

637
638
	size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) :
		       MLX5_MAX_MC_PER_VPORT(esw->dev);
639
640
641
642
643

	mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL);
	if (!mac_list)
		return;

644
	hash = is_uc ? vport->uc_list : vport->mc_list;
645
646

	for_each_l2hash_node(node, tmp, hash, hi) {
647
		addr = container_of(node, struct vport_addr, node);
648
649
650
		addr->action = MLX5_ACTION_DEL;
	}

651
652
653
	if (!vport->enabled)
		goto out;

654
	err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, list_type,
655
656
					    mac_list, &size);
	if (err)
657
		goto out;
658
659
	esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n",
		  vport_num, is_uc ? "UC" : "MC", size);
660
661

	for (i = 0; i < size; i++) {
662
		if (is_uc && !is_valid_ether_addr(mac_list[i]))
663
664
			continue;

665
666
667
668
		if (!is_uc && !is_multicast_ether_addr(mac_list[i]))
			continue;

		addr = l2addr_hash_find(hash, mac_list[i], struct vport_addr);
669
670
		if (addr) {
			addr->action = MLX5_ACTION_NONE;
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
			/* If this mac was previously added because of allmulti
			 * promiscuous rx mode, its now converted to be original
			 * vport mac.
			 */
			if (addr->mc_promisc) {
				struct esw_mc_addr *esw_mc =
					l2addr_hash_find(esw->mc_table,
							 mac_list[i],
							 struct esw_mc_addr);
				if (!esw_mc) {
					esw_warn(esw->dev,
						 "Failed to MAC(%pM) in mcast DB\n",
						 mac_list[i]);
					continue;
				}
				esw_mc->refcnt++;
				addr->mc_promisc = false;
			}
689
690
691
			continue;
		}

692
		addr = l2addr_hash_add(hash, mac_list[i], struct vport_addr,
693
694
695
696
697
698
699
				       GFP_KERNEL);
		if (!addr) {
			esw_warn(esw->dev,
				 "Failed to add MAC(%pM) to vport[%d] DB\n",
				 mac_list[i], vport_num);
			continue;
		}
700
		addr->vport = vport_num;
701
702
		addr->action = MLX5_ACTION_ADD;
	}
703
out:
704
705
706
	kfree(mac_list);
}

707
708
709
/* Sync vport UC/MC list from vport context
 * Must be called after esw_update_vport_addr_list
 */
710
static void esw_update_vport_mc_promisc(struct mlx5_eswitch *esw, u16 vport_num)
711
{
712
	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
	struct l2addr_node *node;
	struct vport_addr *addr;
	struct hlist_head *hash;
	struct hlist_node *tmp;
	int hi;

	hash = vport->mc_list;

	for_each_l2hash_node(node, tmp, esw->mc_table, hi) {
		u8 *mac = node->addr;

		addr = l2addr_hash_find(hash, mac, struct vport_addr);
		if (addr) {
			if (addr->action == MLX5_ACTION_DEL)
				addr->action = MLX5_ACTION_NONE;
			continue;
		}
		addr = l2addr_hash_add(hash, mac, struct vport_addr,
				       GFP_KERNEL);
		if (!addr) {
			esw_warn(esw->dev,
				 "Failed to add allmulti MAC(%pM) to vport[%d] DB\n",
				 mac, vport_num);
			continue;
		}
		addr->vport = vport_num;
		addr->action = MLX5_ACTION_ADD;
		addr->mc_promisc = true;
	}
}

/* Apply vport rx mode to HW FDB table */
745
static void esw_apply_vport_rx_mode(struct mlx5_eswitch *esw, u16 vport_num,
746
747
				    bool promisc, bool mc_promisc)
{
748
	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
749
	struct esw_mc_addr *allmulti_addr = &esw->mc_promisc;
750
751
752
753
754
755
756
757
758
759

	if (IS_ERR_OR_NULL(vport->allmulti_rule) != mc_promisc)
		goto promisc;

	if (mc_promisc) {
		vport->allmulti_rule =
				esw_fdb_set_vport_allmulti_rule(esw, vport_num);
		if (!allmulti_addr->uplink_rule)
			allmulti_addr->uplink_rule =
				esw_fdb_set_vport_allmulti_rule(esw,
760
								MLX5_VPORT_UPLINK);
761
762
		allmulti_addr->refcnt++;
	} else if (vport->allmulti_rule) {
Mark Bloch's avatar
Mark Bloch committed
763
		mlx5_del_flow_rules(vport->allmulti_rule);
764
765
766
767
768
769
		vport->allmulti_rule = NULL;

		if (--allmulti_addr->refcnt > 0)
			goto promisc;

		if (allmulti_addr->uplink_rule)
Mark Bloch's avatar
Mark Bloch committed
770
			mlx5_del_flow_rules(allmulti_addr->uplink_rule);
771
772
773
774
775
776
777
778
779
780
781
		allmulti_addr->uplink_rule = NULL;
	}

promisc:
	if (IS_ERR_OR_NULL(vport->promisc_rule) != promisc)
		return;

	if (promisc) {
		vport->promisc_rule = esw_fdb_set_vport_promisc_rule(esw,
								     vport_num);
	} else if (vport->promisc_rule) {
Mark Bloch's avatar
Mark Bloch committed
782
		mlx5_del_flow_rules(vport->promisc_rule);
783
784
785
786
787
		vport->promisc_rule = NULL;
	}
}

/* Sync vport rx mode from vport context */
788
static void esw_update_vport_rx_mode(struct mlx5_eswitch *esw, u16 vport_num)
789
{
790
	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
	int promisc_all = 0;
	int promisc_uc = 0;
	int promisc_mc = 0;
	int err;

	err = mlx5_query_nic_vport_promisc(esw->dev,
					   vport_num,
					   &promisc_uc,
					   &promisc_mc,
					   &promisc_all);
	if (err)
		return;
	esw_debug(esw->dev, "vport[%d] context update rx mode promisc_all=%d, all_multi=%d\n",
		  vport_num, promisc_all, promisc_mc);

806
	if (!vport->info.trusted || !vport->enabled) {
807
808
809
810
811
812
813
814
815
		promisc_uc = 0;
		promisc_mc = 0;
		promisc_all = 0;
	}

	esw_apply_vport_rx_mode(esw, vport_num, promisc_all,
				(promisc_all || promisc_mc));
}

816
static void esw_vport_change_handle_locked(struct mlx5_vport *vport)
817
818
{
	struct mlx5_core_dev *dev = vport->dev;
819
	struct mlx5_eswitch *esw = dev->priv.eswitch;
820
821
822
	u8 mac[ETH_ALEN];

	mlx5_query_nic_vport_mac_address(dev, vport->vport, mac);
823
824
825
826
827
828
829
830
831
	esw_debug(dev, "vport[%d] Context Changed: perm mac: %pM\n",
		  vport->vport, mac);

	if (vport->enabled_events & UC_ADDR_CHANGE) {
		esw_update_vport_addr_list(esw, vport->vport,
					   MLX5_NVPRT_LIST_TYPE_UC);
		esw_apply_vport_addr_list(esw, vport->vport,
					  MLX5_NVPRT_LIST_TYPE_UC);
	}
832

833
834
835
	if (vport->enabled_events & MC_ADDR_CHANGE) {
		esw_update_vport_addr_list(esw, vport->vport,
					   MLX5_NVPRT_LIST_TYPE_MC);
836
837
838
839
840
841
842
843
844
	}

	if (vport->enabled_events & PROMISC_CHANGE) {
		esw_update_vport_rx_mode(esw, vport->vport);
		if (!IS_ERR_OR_NULL(vport->allmulti_rule))
			esw_update_vport_mc_promisc(esw, vport->vport);
	}

	if (vport->enabled_events & (PROMISC_CHANGE | MC_ADDR_CHANGE)) {
845
846
847
		esw_apply_vport_addr_list(esw, vport->vport,
					  MLX5_NVPRT_LIST_TYPE_MC);
	}
848

849
	esw_debug(esw->dev, "vport[%d] Context Changed: Done\n", vport->vport);
850
851
	if (vport->enabled)
		arm_vport_context_events_cmd(dev, vport->vport,
852
					     vport->enabled_events);
853
854
}

855
856
857
858
859
860
861
862
863
864
865
static void esw_vport_change_handler(struct work_struct *work)
{
	struct mlx5_vport *vport =
		container_of(work, struct mlx5_vport, vport_change_handler);
	struct mlx5_eswitch *esw = vport->dev->priv.eswitch;

	mutex_lock(&esw->state_lock);
	esw_vport_change_handle_locked(vport);
	mutex_unlock(&esw->state_lock);
}

866
867
static int esw_vport_enable_egress_acl(struct mlx5_eswitch *esw,
				       struct mlx5_vport *vport)
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
{
	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
	struct mlx5_flow_group *vlan_grp = NULL;
	struct mlx5_flow_group *drop_grp = NULL;
	struct mlx5_core_dev *dev = esw->dev;
	struct mlx5_flow_namespace *root_ns;
	struct mlx5_flow_table *acl;
	void *match_criteria;
	u32 *flow_group_in;
	/* The egress acl table contains 2 rules:
	 * 1)Allow traffic with vlan_tag=vst_vlan_id
	 * 2)Drop all other traffic.
	 */
	int table_size = 2;
	int err = 0;

884
885
886
887
888
	if (!MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support))
		return -EOPNOTSUPP;

	if (!IS_ERR_OR_NULL(vport->egress.acl))
		return 0;
889
890
891
892

	esw_debug(dev, "Create vport[%d] egress ACL log_max_size(%d)\n",
		  vport->vport, MLX5_CAP_ESW_EGRESS_ACL(dev, log_max_ft_size));

893
894
	root_ns = mlx5_get_flow_vport_acl_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_EGRESS,
						    vport->vport);
895
	if (!root_ns) {
896
		esw_warn(dev, "Failed to get E-Switch egress flow namespace for vport (%d)\n", vport->vport);
897
		return -EOPNOTSUPP;
898
899
	}

900
	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
901
	if (!flow_group_in)
902
		return -ENOMEM;
903
904

	acl = mlx5_create_vport_flow_table(root_ns, 0, table_size, 0, vport->vport);
905
	if (IS_ERR(acl)) {
906
907
908
909
910
911
912
913
		err = PTR_ERR(acl);
		esw_warn(dev, "Failed to create E-Switch vport[%d] egress flow Table, err(%d)\n",
			 vport->vport, err);
		goto out;
	}

	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
914
	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.cvlan_tag);
915
916
917
918
919
	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.first_vid);
	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 0);

	vlan_grp = mlx5_create_flow_group(acl, flow_group_in);
920
	if (IS_ERR(vlan_grp)) {
921
922
923
924
925
926
927
928
929
930
		err = PTR_ERR(vlan_grp);
		esw_warn(dev, "Failed to create E-Switch vport[%d] egress allowed vlans flow group, err(%d)\n",
			 vport->vport, err);
		goto out;
	}

	memset(flow_group_in, 0, inlen);
	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 1);
	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1);
	drop_grp = mlx5_create_flow_group(acl, flow_group_in);
931
	if (IS_ERR(drop_grp)) {
932
933
934
935
936
937
938
939
940
941
		err = PTR_ERR(drop_grp);
		esw_warn(dev, "Failed to create E-Switch vport[%d] egress drop flow group, err(%d)\n",
			 vport->vport, err);
		goto out;
	}

	vport->egress.acl = acl;
	vport->egress.drop_grp = drop_grp;
	vport->egress.allowed_vlans_grp = vlan_grp;
out:
942
	kvfree(flow_group_in);
943
944
945
946
	if (err && !IS_ERR_OR_NULL(vlan_grp))
		mlx5_destroy_flow_group(vlan_grp);
	if (err && !IS_ERR_OR_NULL(acl))
		mlx5_destroy_flow_table(acl);
947
	return err;
948
949
}

950
951
952
953
static void esw_vport_cleanup_egress_rules(struct mlx5_eswitch *esw,
					   struct mlx5_vport *vport)
{
	if (!IS_ERR_OR_NULL(vport->egress.allowed_vlan))
Mark Bloch's avatar
Mark Bloch committed
954
		mlx5_del_flow_rules(vport->egress.allowed_vlan);
955
956

	if (!IS_ERR_OR_NULL(vport->egress.drop_rule))
Mark Bloch's avatar
Mark Bloch committed
957
		mlx5_del_flow_rules(vport->egress.drop_rule);
958
959
960
961
962

	vport->egress.allowed_vlan = NULL;
	vport->egress.drop_rule = NULL;
}

963
964
965
966
967
968
969
970
static void esw_vport_disable_egress_acl(struct mlx5_eswitch *esw,
					 struct mlx5_vport *vport)
{
	if (IS_ERR_OR_NULL(vport->egress.acl))
		return;

	esw_debug(esw->dev, "Destroy vport[%d] E-Switch egress ACL\n", vport->vport);

971
	esw_vport_cleanup_egress_rules(esw, vport);
972
973
974
975
976
977
978
979
	mlx5_destroy_flow_group(vport->egress.allowed_vlans_grp);
	mlx5_destroy_flow_group(vport->egress.drop_grp);
	mlx5_destroy_flow_table(vport->egress.acl);
	vport->egress.allowed_vlans_grp = NULL;
	vport->egress.drop_grp = NULL;
	vport->egress.acl = NULL;
}

980
981
static int esw_vport_enable_ingress_acl(struct mlx5_eswitch *esw,
					struct mlx5_vport *vport)
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
{
	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
	struct mlx5_core_dev *dev = esw->dev;
	struct mlx5_flow_namespace *root_ns;
	struct mlx5_flow_table *acl;
	struct mlx5_flow_group *g;
	void *match_criteria;
	u32 *flow_group_in;
	/* The ingress acl table contains 4 groups
	 * (2 active rules at the same time -
	 *      1 allow rule from one of the first 3 groups.
	 *      1 drop rule from the last group):
	 * 1)Allow untagged traffic with smac=original mac.
	 * 2)Allow untagged traffic.
	 * 3)Allow traffic with smac=original mac.
	 * 4)Drop all other traffic.
	 */
	int table_size = 4;
	int err = 0;

1002
1003
1004
1005
1006
	if (!MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support))
		return -EOPNOTSUPP;

	if (!IS_ERR_OR_NULL(vport->ingress.acl))
		return 0;
1007
1008
1009
1010

	esw_debug(dev, "Create vport[%d] ingress ACL log_max_size(%d)\n",
		  vport->vport, MLX5_CAP_ESW_INGRESS_ACL(dev, log_max_ft_size));

1011
1012
	root_ns = mlx5_get_flow_vport_acl_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_INGRESS,
						    vport->vport);
1013
	if (!root_ns) {
1014
		esw_warn(dev, "Failed to get E-Switch ingress flow namespace for vport (%d)\n", vport->vport);
1015
		return -EOPNOTSUPP;
1016
1017
	}

1018
	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
1019
	if (!flow_group_in)
1020
		return -ENOMEM;
1021
1022

	acl = mlx5_create_vport_flow_table(root_ns, 0, table_size, 0, vport->vport);
1023
	if (IS_ERR(acl)) {
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
		err = PTR_ERR(acl);
		esw_warn(dev, "Failed to create E-Switch vport[%d] ingress flow Table, err(%d)\n",
			 vport->vport, err);
		goto out;
	}
	vport->ingress.acl = acl;

	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);

	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
1034
	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.cvlan_tag);
1035
1036
1037
1038
1039
1040
	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_47_16);
	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_15_0);
	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 0);

	g = mlx5_create_flow_group(acl, flow_group_in);
1041
	if (IS_ERR(g)) {
1042
1043
1044
1045
1046
1047
1048
1049
1050
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create E-Switch vport[%d] ingress untagged spoofchk flow group, err(%d)\n",
			 vport->vport, err);
		goto out;
	}
	vport->ingress.allow_untagged_spoofchk_grp = g;

	memset(flow_group_in, 0, inlen);
	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
1051
	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.cvlan_tag);
1052
1053
1054
1055
	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 1);
	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1);

	g = mlx5_create_flow_group(acl, flow_group_in);
1056
	if (IS_ERR(g)) {
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create E-Switch vport[%d] ingress untagged flow group, err(%d)\n",
			 vport->vport, err);
		goto out;
	}
	vport->ingress.allow_untagged_only_grp = g;

	memset(flow_group_in, 0, inlen);
	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_47_16);
	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.smac_15_0);
	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 2);
	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 2);

	g = mlx5_create_flow_group(acl, flow_group_in);