eswitch.c 68.3 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
#include "ecpf.h"
43
44
45
46
47
48
49

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

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

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

67
68
69
static void esw_destroy_legacy_fdb_table(struct mlx5_eswitch *esw);
static void esw_cleanup_vepa_rules(struct mlx5_eswitch *esw);

70
71
/* Vport context events */
#define SRIOV_VPORT_EVENTS (UC_ADDR_CHANGE | \
72
73
			    MC_ADDR_CHANGE | \
			    PROMISC_CHANGE)
74

75
76
77
static struct mlx5_vport *mlx5_eswitch_get_vport(struct mlx5_eswitch *esw,
						 u16 vport_num)
{
78
79
	u16 idx = mlx5_eswitch_vport_num_to_index(esw, vport_num);

80
	WARN_ON(idx > esw->total_vports - 1);
81
	return &esw->vports[idx];
82
83
}

84
static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport,
85
86
					u32 events_mask)
{
87
88
	int in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)]   = {0};
	int out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)] = {0};
89
90
91
92
93
94
	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);
95
	MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1);
96
97
98
99
100
101
102
103
104
105
106
	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);
107
108
109
	if (events_mask & PROMISC_CHANGE)
		MLX5_SET(nic_vport_context, nic_vport_ctx,
			 event_on_promisc_change, 1);
110

111
	return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
112
113
}

114
115
116
117
/* E-Switch vport context HW commands */
static int modify_esw_vport_context_cmd(struct mlx5_core_dev *dev, u16 vport,
					void *in, int inlen)
{
118
	u32 out[MLX5_ST_SZ_DW(modify_esw_vport_context_out)] = {0};
119

120
121
	MLX5_SET(modify_esw_vport_context_in, in, opcode,
		 MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT);
122
	MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport);
123
	MLX5_SET(modify_esw_vport_context_in, in, other_vport, 1);
124
	return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
125
126
}

127
static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u16 vport,
128
				  u16 vlan, u8 qos, u8 set_flags)
129
{
130
	u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {0};
131
132
133

	if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
	    !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
134
		return -EOPNOTSUPP;
135

136
137
138
139
	esw_debug(dev, "Set Vport[%d] VLAN %d qos %d set=%x\n",
		  vport, vlan, qos, set_flags);

	if (set_flags & SET_VLAN_STRIP)
140
141
		MLX5_SET(modify_esw_vport_context_in, in,
			 esw_vport_context.vport_cvlan_strip, 1);
142
143

	if (set_flags & SET_VLAN_INSERT) {
144
145
146
		/* insert only if no vlan in packet */
		MLX5_SET(modify_esw_vport_context_in, in,
			 esw_vport_context.vport_cvlan_insert, 1);
147

148
149
150
151
152
153
154
155
156
157
158
159
160
161
		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));
}

162
/* E-Switch FDB */
Mark Bloch's avatar
Mark Bloch committed
163
static struct mlx5_flow_handle *
164
__esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u16 vport, bool rx_rule,
165
			 u8 mac_c[ETH_ALEN], u8 mac_v[ETH_ALEN])
166
{
167
168
	int match_header = (is_zero_ether_addr(mac_c) ? 0 :
			    MLX5_MATCH_OUTER_HEADERS);
Mark Bloch's avatar
Mark Bloch committed
169
	struct mlx5_flow_handle *flow_rule = NULL;
170
	struct mlx5_flow_act flow_act = {0};
171
	struct mlx5_flow_destination dest = {};
172
	struct mlx5_flow_spec *spec;
173
174
	void *mv_misc = NULL;
	void *mc_misc = NULL;
175
176
	u8 *dmac_v = NULL;
	u8 *dmac_c = NULL;
177

178
179
	if (rx_rule)
		match_header |= MLX5_MATCH_MISC_PARAMETERS;
180

181
182
	spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
	if (!spec)
183
		return NULL;
184

185
	dmac_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
186
			      outer_headers.dmac_47_16);
187
	dmac_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
188
189
			      outer_headers.dmac_47_16);

190
	if (match_header & MLX5_MATCH_OUTER_HEADERS) {
191
192
193
		ether_addr_copy(dmac_v, mac_v);
		ether_addr_copy(dmac_c, mac_c);
	}
194

195
	if (match_header & MLX5_MATCH_MISC_PARAMETERS) {
196
197
198
199
		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);
200
		MLX5_SET(fte_match_set_misc, mv_misc, source_port, MLX5_VPORT_UPLINK);
201
202
203
		MLX5_SET_TO_ONES(fte_match_set_misc, mc_misc, source_port);
	}

204
	dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
205
	dest.vport.num = vport;
206
207
208
209

	esw_debug(esw->dev,
		  "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n",
		  dmac_v, dmac_c, vport);
210
	spec->match_criteria_enable = match_header;
211
	flow_act.action =  MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
212
	flow_rule =
213
		mlx5_add_flow_rules(esw->fdb_table.legacy.fdb, spec,
214
				    &flow_act, &dest, 1);
215
	if (IS_ERR(flow_rule)) {
216
217
		esw_warn(esw->dev,
			 "FDB: Failed to add flow rule: dmac_v(%pM) dmac_c(%pM) -> vport(%d), err(%ld)\n",
218
219
220
			 dmac_v, dmac_c, vport, PTR_ERR(flow_rule));
		flow_rule = NULL;
	}
221
222

	kvfree(spec);
223
224
225
	return flow_rule;
}

Mark Bloch's avatar
Mark Bloch committed
226
static struct mlx5_flow_handle *
227
esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u16 vport)
228
229
230
231
{
	u8 mac_c[ETH_ALEN];

	eth_broadcast_addr(mac_c);
232
233
234
	return __esw_fdb_set_vport_rule(esw, vport, false, mac_c, mac);
}

Mark Bloch's avatar
Mark Bloch committed
235
static struct mlx5_flow_handle *
236
esw_fdb_set_vport_allmulti_rule(struct mlx5_eswitch *esw, u16 vport)
237
238
239
240
241
242
243
244
245
246
247
{
	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
248
static struct mlx5_flow_handle *
249
esw_fdb_set_vport_promisc_rule(struct mlx5_eswitch *esw, u16 vport)
250
251
252
253
254
255
256
{
	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);
257
258
}

259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
enum {
	LEGACY_VEPA_PRIO = 0,
	LEGACY_FDB_PRIO,
};

static int esw_create_legacy_vepa_table(struct mlx5_eswitch *esw)
{
	struct mlx5_core_dev *dev = esw->dev;
	struct mlx5_flow_namespace *root_ns;
	struct mlx5_flow_table *fdb;
	int err;

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

	/* num FTE 2, num FG 2 */
	fdb = mlx5_create_auto_grouped_flow_table(root_ns, LEGACY_VEPA_PRIO,
						  2, 2, 0, 0);
	if (IS_ERR(fdb)) {
		err = PTR_ERR(fdb);
		esw_warn(dev, "Failed to create VEPA FDB err %d\n", err);
		return err;
	}
	esw->fdb_table.legacy.vepa_fdb = fdb;

	return 0;
}

290
static int esw_create_legacy_fdb_table(struct mlx5_eswitch *esw)
291
{
292
	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
293
	struct mlx5_flow_table_attr ft_attr = {};
294
	struct mlx5_core_dev *dev = esw->dev;
295
	struct mlx5_flow_namespace *root_ns;
296
	struct mlx5_flow_table *fdb;
297
298
299
300
	struct mlx5_flow_group *g;
	void *match_criteria;
	int table_size;
	u32 *flow_group_in;
301
	u8 *dmac;
302
	int err = 0;
303
304
305
306

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

307
	root_ns = mlx5_get_fdb_sub_ns(dev, 0);
308
309
	if (!root_ns) {
		esw_warn(dev, "Failed to get FDB flow namespace\n");
310
		return -EOPNOTSUPP;
311
	}
312

313
	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
314
315
316
317
	if (!flow_group_in)
		return -ENOMEM;

	table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
318
	ft_attr.max_fte = table_size;
319
	ft_attr.prio = LEGACY_FDB_PRIO;
320
	fdb = mlx5_create_flow_table(root_ns, &ft_attr);
321
	if (IS_ERR(fdb)) {
322
323
324
325
		err = PTR_ERR(fdb);
		esw_warn(dev, "Failed to create FDB Table err %d\n", err);
		goto out;
	}
326
	esw->fdb_table.legacy.fdb = fdb;
327

328
	/* Addresses group : Full match unicast/multicast addresses */
329
330
331
332
333
	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);
334
335
	/* Preserve 2 entries for allmulti and promisc rules*/
	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 3);
336
337
	eth_broadcast_addr(dmac);
	g = mlx5_create_flow_group(fdb, flow_group_in);
338
	if (IS_ERR(g)) {
339
340
341
342
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create flow group err(%d)\n", err);
		goto out;
	}
343
	esw->fdb_table.legacy.addr_grp = g;
344
345
346
347
348
349
350
351
352

	/* 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);
353
	if (IS_ERR(g)) {
354
355
356
357
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create allmulti flow group err(%d)\n", err);
		goto out;
	}
358
	esw->fdb_table.legacy.allmulti_grp = g;
359
360
361
362
363
364
365
366
367
368
369

	/* 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);
370
	if (IS_ERR(g)) {
371
372
373
374
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create promisc flow group err(%d)\n", err);
		goto out;
	}
375
	esw->fdb_table.legacy.promisc_grp = g;
376

377
out:
378
379
	if (err)
		esw_destroy_legacy_fdb_table(esw);
380

381
	kvfree(flow_group_in);
382
	return err;
383
384
}

385
386
387
388
389
390
391
392
393
394
static void esw_destroy_legacy_vepa_table(struct mlx5_eswitch *esw)
{
	esw_debug(esw->dev, "Destroy VEPA Table\n");
	if (!esw->fdb_table.legacy.vepa_fdb)
		return;

	mlx5_destroy_flow_table(esw->fdb_table.legacy.vepa_fdb);
	esw->fdb_table.legacy.vepa_fdb = NULL;
}

395
static void esw_destroy_legacy_fdb_table(struct mlx5_eswitch *esw)
396
{
397
	esw_debug(esw->dev, "Destroy FDB Table\n");
398
	if (!esw->fdb_table.legacy.fdb)
399
400
		return;

401
402
403
404
405
406
	if (esw->fdb_table.legacy.promisc_grp)
		mlx5_destroy_flow_group(esw->fdb_table.legacy.promisc_grp);
	if (esw->fdb_table.legacy.allmulti_grp)
		mlx5_destroy_flow_group(esw->fdb_table.legacy.allmulti_grp);
	if (esw->fdb_table.legacy.addr_grp)
		mlx5_destroy_flow_group(esw->fdb_table.legacy.addr_grp);
407
	mlx5_destroy_flow_table(esw->fdb_table.legacy.fdb);
408

409
	esw->fdb_table.legacy.fdb = NULL;
410
411
412
	esw->fdb_table.legacy.addr_grp = NULL;
	esw->fdb_table.legacy.allmulti_grp = NULL;
	esw->fdb_table.legacy.promisc_grp = NULL;
413
414
}

415
416
417
418
static int esw_create_legacy_table(struct mlx5_eswitch *esw)
{
	int err;

419
420
	memset(&esw->fdb_table.legacy, 0, sizeof(struct legacy_fdb));

421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
	err = esw_create_legacy_vepa_table(esw);
	if (err)
		return err;

	err = esw_create_legacy_fdb_table(esw);
	if (err)
		esw_destroy_legacy_vepa_table(esw);

	return err;
}

static void esw_destroy_legacy_table(struct mlx5_eswitch *esw)
{
	esw_cleanup_vepa_rules(esw);
	esw_destroy_legacy_fdb_table(esw);
	esw_destroy_legacy_vepa_table(esw);
}

439
440
441
442
443
444
445
/* 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;
446
	u16 vport = vaddr->vport;
447
448
	int err;

449
450
	/* Skip mlx5_mpfs_add_mac for eswitch_managers,
	 * it is already done by its netdev in mlx5e_execute_l2_action
451
	 */
452
	if (esw->manager_vport == vport)
453
454
455
456
		goto fdb_add;

	err = mlx5_mpfs_add_mac(esw->dev, mac);
	if (err) {
457
		esw_warn(esw->dev,
458
			 "Failed to add L2 table mac(%pM) for vport(0x%x), err(%d)\n",
459
460
			 mac, vport, err);
		return err;
461
	}
462
	vaddr->mpfs = true;
463

464
fdb_add:
465
	/* SRIOV is enabled: Forward UC MAC to vport */
466
	if (esw->fdb_table.legacy.fdb && esw->mode == SRIOV_LEGACY)
467
468
		vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);

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

472
	return 0;
473
474
}

475
static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
476
{
477
	u8 *mac = vaddr->node.addr;
478
	u16 vport = vaddr->vport;
479
	int err = 0;
480

481
482
	/* Skip mlx5_mpfs_del_mac for eswitch managerss,
	 * it is already done by its netdev in mlx5e_execute_l2_action
483
	 */
484
	if (!vaddr->mpfs || esw->manager_vport == vport)
485
		goto fdb_del;
486

487
488
489
490
491
492
	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;
493

494
fdb_del:
495
	if (vaddr->flow_rule)
Mark Bloch's avatar
Mark Bloch committed
496
		mlx5_del_flow_rules(vaddr->flow_rule);
497
498
499
500
501
	vaddr->flow_rule = NULL;

	return 0;
}

502
503
504
505
506
static void update_allmulti_vports(struct mlx5_eswitch *esw,
				   struct vport_addr *vaddr,
				   struct esw_mc_addr *esw_mc)
{
	u8 *mac = vaddr->node.addr;
507
508
	struct mlx5_vport *vport;
	u16 i, vport_num;
509

510
	mlx5_esw_for_all_vports(esw, i, vport) {
511
512
513
514
515
		struct hlist_head *vport_hash = vport->mc_list;
		struct vport_addr *iter_vaddr =
					l2addr_hash_find(vport_hash,
							 mac,
							 struct vport_addr);
516
		vport_num = vport->vport;
517
		if (IS_ERR_OR_NULL(vport->allmulti_rule) ||
518
		    vaddr->vport == vport_num)
519
520
521
522
523
524
525
526
527
528
529
			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",
530
					 mac, vport_num);
531
532
				continue;
			}
533
			iter_vaddr->vport = vport_num;
534
535
536
			iter_vaddr->flow_rule =
					esw_fdb_set_vport_rule(esw,
							       mac,
537
							       vport_num);
538
			iter_vaddr->mc_promisc = true;
539
540
541
542
			break;
		case MLX5_ACTION_DEL:
			if (!iter_vaddr)
				continue;
Mark Bloch's avatar
Mark Bloch committed
543
			mlx5_del_flow_rules(iter_vaddr->flow_rule);
544
545
546
547
548
549
			l2addr_hash_del(iter_vaddr);
			break;
		}
	}
}

550
551
552
553
554
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;
555
	u16 vport = vaddr->vport;
556

557
	if (!esw->fdb_table.legacy.fdb)
558
559
560
561
562
563
564
565
566
567
568
		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 */
569
		esw_fdb_set_vport_rule(esw, mac, MLX5_VPORT_UPLINK);
570
571
572
573

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

574
add:
575
576
577
578
579
580
	/* 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++;

581
582
583
584
585
586
587
588
589
590
591
592
593
594
	/* 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;
595
	u16 vport = vaddr->vport;
596

597
	if (!esw->fdb_table.legacy.fdb)
598
599
600
601
602
603
		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)",
604
605
606
			 mac, vport);
		return -EINVAL;
	}
607
608
609
610
611
612
	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
613
		mlx5_del_flow_rules(vaddr->flow_rule);
614
615
	vaddr->flow_rule = NULL;

616
617
618
619
	/* 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))
620
		return 0;
621

622
623
624
	/* Remove this multicast mac from all the mc promiscuous vports */
	update_allmulti_vports(esw, vaddr, esw_mc);

625
	if (esw_mc->uplink_rule)
Mark Bloch's avatar
Mark Bloch committed
626
		mlx5_del_flow_rules(esw_mc->uplink_rule);
627
628

	l2addr_hash_del(esw_mc);
629
630
631
	return 0;
}

632
633
/* Apply vport UC/MC list to HW l2 table and FDB table */
static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw,
634
				      u16 vport_num, int list_type)
635
{
636
	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
637
638
639
640
	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;
641
642
643
644
645
	struct l2addr_node *node;
	struct hlist_head *hash;
	struct hlist_node *tmp;
	int hi;

646
647
648
649
650
651
	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;
652
	for_each_l2hash_node(node, tmp, hash, hi) {
653
		addr = container_of(node, struct vport_addr, node);
654
655
		switch (addr->action) {
		case MLX5_ACTION_ADD:
656
			vport_addr_add(esw, addr);
657
658
659
			addr->action = MLX5_ACTION_NONE;
			break;
		case MLX5_ACTION_DEL:
660
			vport_addr_del(esw, addr);
661
662
663
664
665
666
			l2addr_hash_del(addr);
			break;
		}
	}
}

667
668
/* Sync vport UC/MC list from vport context */
static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
669
				       u16 vport_num, int list_type)
670
{
671
	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
672
	bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
673
	u8 (*mac_list)[ETH_ALEN];
674
675
	struct l2addr_node *node;
	struct vport_addr *addr;
676
677
678
679
680
681
682
	struct hlist_head *hash;
	struct hlist_node *tmp;
	int size;
	int err;
	int hi;
	int i;

683
684
	size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) :
		       MLX5_MAX_MC_PER_VPORT(esw->dev);
685
686
687
688
689

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

690
	hash = is_uc ? vport->uc_list : vport->mc_list;
691
692

	for_each_l2hash_node(node, tmp, hash, hi) {
693
		addr = container_of(node, struct vport_addr, node);
694
695
696
		addr->action = MLX5_ACTION_DEL;
	}

697
698
699
	if (!vport->enabled)
		goto out;

700
	err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, list_type,
701
702
					    mac_list, &size);
	if (err)
703
		goto out;
704
705
	esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n",
		  vport_num, is_uc ? "UC" : "MC", size);
706
707

	for (i = 0; i < size; i++) {
708
		if (is_uc && !is_valid_ether_addr(mac_list[i]))
709
710
			continue;

711
712
713
714
		if (!is_uc && !is_multicast_ether_addr(mac_list[i]))
			continue;

		addr = l2addr_hash_find(hash, mac_list[i], struct vport_addr);
715
716
		if (addr) {
			addr->action = MLX5_ACTION_NONE;
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
			/* 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;
			}
735
736
737
			continue;
		}

738
		addr = l2addr_hash_add(hash, mac_list[i], struct vport_addr,
739
740
741
742
743
744
745
				       GFP_KERNEL);
		if (!addr) {
			esw_warn(esw->dev,
				 "Failed to add MAC(%pM) to vport[%d] DB\n",
				 mac_list[i], vport_num);
			continue;
		}
746
		addr->vport = vport_num;
747
748
		addr->action = MLX5_ACTION_ADD;
	}
749
out:
750
751
752
	kfree(mac_list);
}

753
754
755
/* Sync vport UC/MC list from vport context
 * Must be called after esw_update_vport_addr_list
 */
756
static void esw_update_vport_mc_promisc(struct mlx5_eswitch *esw, u16 vport_num)
757
{
758
	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
	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 */
791
static void esw_apply_vport_rx_mode(struct mlx5_eswitch *esw, u16 vport_num,
792
793
				    bool promisc, bool mc_promisc)
{
794
	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
795
	struct esw_mc_addr *allmulti_addr = &esw->mc_promisc;
796
797
798
799
800
801
802
803
804
805

	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,
806
								MLX5_VPORT_UPLINK);
807
808
		allmulti_addr->refcnt++;
	} else if (vport->allmulti_rule) {
Mark Bloch's avatar
Mark Bloch committed
809
		mlx5_del_flow_rules(vport->allmulti_rule);
810
811
812
813
814
815
		vport->allmulti_rule = NULL;

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

		if (allmulti_addr->uplink_rule)
Mark Bloch's avatar
Mark Bloch committed
816
			mlx5_del_flow_rules(allmulti_addr->uplink_rule);
817
818
819
820
821
822
823
824
825
826
827
		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
828
		mlx5_del_flow_rules(vport->promisc_rule);
829
830
831
832
833
		vport->promisc_rule = NULL;
	}
}

/* Sync vport rx mode from vport context */
834
static void esw_update_vport_rx_mode(struct mlx5_eswitch *esw, u16 vport_num)
835
{
836
	struct mlx5_vport *vport = mlx5_eswitch_get_vport(esw, vport_num);
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
	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);

852
	if (!vport->info.trusted || !vport->enabled) {
853
854
855
856
857
858
859
860
861
		promisc_uc = 0;
		promisc_mc = 0;
		promisc_all = 0;
	}

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

862
static void esw_vport_change_handle_locked(struct mlx5_vport *vport)
863
864
{
	struct mlx5_core_dev *dev = vport->dev;
865
	struct mlx5_eswitch *esw = dev->priv.eswitch;
866
867
868
	u8 mac[ETH_ALEN];

	mlx5_query_nic_vport_mac_address(dev, vport->vport, mac);
869
870
871
872
873
874
875
876
877
	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);
	}
878

879
880
881
	if (vport->enabled_events & MC_ADDR_CHANGE) {
		esw_update_vport_addr_list(esw, vport->vport,
					   MLX5_NVPRT_LIST_TYPE_MC);
882
883
884
885
886
887
888
889
890
	}

	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)) {
891
892
893
		esw_apply_vport_addr_list(esw, vport->vport,
					  MLX5_NVPRT_LIST_TYPE_MC);
	}
894

895
	esw_debug(esw->dev, "vport[%d] Context Changed: Done\n", vport->vport);
896
897
	if (vport->enabled)
		arm_vport_context_events_cmd(dev, vport->vport,
898
					     vport->enabled_events);
899
900
}

901
902
903
904
905
906
907
908
909
910
911
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);
}

912
913
int esw_vport_enable_egress_acl(struct mlx5_eswitch *esw,
				struct mlx5_vport *vport)
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
{
	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;

930
931
932
933
934
	if (!MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support))
		return -EOPNOTSUPP;

	if (!IS_ERR_OR_NULL(vport->egress.acl))
		return 0;
935
936
937
938

	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));

939
940
	root_ns = mlx5_get_flow_vport_acl_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_EGRESS,
						    vport->vport);
941
	if (!root_ns) {
942
		esw_warn(dev, "Failed to get E-Switch egress flow namespace for vport (%d)\n", vport->vport);
943
		return -EOPNOTSUPP;
944
945
	}

946
	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
947
	if (!flow_group_in)
948
		return -ENOMEM;
949
950

	acl = mlx5_create_vport_flow_table(root_ns, 0, table_size, 0, vport->vport);
951
	if (IS_ERR(acl)) {
952
953
954
955
956
957
958
959
		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);
960
	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.cvlan_tag);
961
962
963
964
965
	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);
966
	if (IS_ERR(vlan_grp)) {
967
968
969
970
971
972
973
974
975
976
		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);
977
	if (IS_ERR(drop_grp)) {
978
979
980
981
982
983
984
985
986
987
		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:
988
	kvfree(flow_group_in);
989
990
991
992
	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);
993
	return err;
994
995
}

996
997
void esw_vport_cleanup_egress_rules(struct mlx5_eswitch *esw,
				    struct mlx5_vport *vport)
998
999
{
	if (!IS_ERR_OR_NULL(vport->egress.allowed_vlan))
Mark Bloch's avatar
Mark Bloch committed
1000
		mlx5_del_flow_rules(vport->egress.allowed_vlan);
1001
1002

	if (!IS_ERR_OR_NULL(vport->egress.drop_rule))
Mark Bloch's avatar
Mark Bloch committed
1003
		mlx5_del_flow_rules(vport->egress.drop_rule);
1004
1005
1006
1007
1008

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

1009
1010
void esw_vport_disable_egress_acl(struct mlx5_eswitch *esw,
				  struct mlx5_vport *vport)
1011
1012
1013
1014
1015
1016
{
	if (IS_ERR_OR_NULL(vport->egress.acl))
		return;

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

1017
	esw_vport_cleanup_egress_rules(esw, vport);
1018
1019
1020
1021
1022
1023
1024
1025
	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;
}

1026
1027
int esw_vport_enable_ingress_acl(struct mlx5_eswitch *esw,
				 struct mlx5_vport *vport)
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
{
	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;

1048
1049
1050
1051
1052
	if (!MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support))
		return -EOPNOTSUPP;

	if (!IS_ERR_OR_NULL(vport->ingress.acl))
		return 0;
1053
1054
1055
1056

	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));

1057
1058
	root_ns = mlx5_get_flow_vport_acl_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_INGRESS,
						    vport->vport);
1059
	if (!root_ns) {
1060
		esw_warn(dev, "Failed to get E-Switch ingress flow namespace for vport (%d)\n", vport->vport);
1061
		return -EOPNOTSUPP;
1062
1063
	}

1064
	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
1065
	if (!flow_group_in)
1066
		return -ENOMEM;
1067
1068

	acl = mlx5_create_vport_flow_table(root_ns, 0, table_size, 0, vport->vport);
1069
	if (IS_ERR(acl)) {