eswitch.c 68.5 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
struct mlx5_vport *__must_check
mlx5_eswitch_get_vport(struct mlx5_eswitch *esw, u16 vport_num)
77
{
78
79
80
81
82
83
84
85
86
87
88
89
	u16 idx;

	if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager))
		return ERR_PTR(-EPERM);

	idx = mlx5_eswitch_vport_num_to_index(esw, vport_num);

	if (idx > esw->total_vports - 1) {
		esw_debug(esw->dev, "vport out of range: num(0x%x), idx(0x%x)\n",
			  vport_num, idx);
		return ERR_PTR(-EINVAL);
	}
90
91

	return &esw->vports[idx];
92
93
}

94
static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport,
95
96
					u32 events_mask)
{
97
98
	int in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)]   = {0};
	int out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)] = {0};
99
100
101
102
103
104
	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);
105
	MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1);
106
107
108
109
110
111
112
113
114
115
116
	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
	MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport);
133
	MLX5_SET(modify_esw_vport_context_in, in, other_vport, 1);
134
	return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
135
136
}

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

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

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

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

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

158
159
160
161
162
163
164
165
166
167
168
169
170
171
		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));
}

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

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

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

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

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

205
	if (match_header & MLX5_MATCH_MISC_PARAMETERS) {
206
207
208
209
		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);
210
		MLX5_SET(fte_match_set_misc, mv_misc, source_port, MLX5_VPORT_UPLINK);
211
212
213
		MLX5_SET_TO_ONES(fte_match_set_misc, mc_misc, source_port);
	}

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

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

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

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

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

Mark Bloch's avatar
Mark Bloch committed
245
static struct mlx5_flow_handle *
246
esw_fdb_set_vport_allmulti_rule(struct mlx5_eswitch *esw, u16 vport)
247
248
249
250
251
252
253
254
255
256
257
{
	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
258
static struct mlx5_flow_handle *
259
esw_fdb_set_vport_promisc_rule(struct mlx5_eswitch *esw, u16 vport)
260
261
262
263
264
265
266
{
	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);
267
268
}

269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
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;
}

300
static int esw_create_legacy_fdb_table(struct mlx5_eswitch *esw)
301
{
302
	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
303
	struct mlx5_flow_table_attr ft_attr = {};
304
	struct mlx5_core_dev *dev = esw->dev;
305
	struct mlx5_flow_namespace *root_ns;
306
	struct mlx5_flow_table *fdb;
307
308
309
310
	struct mlx5_flow_group *g;
	void *match_criteria;
	int table_size;
	u32 *flow_group_in;
311
	u8 *dmac;
312
	int err = 0;
313
314
315
316

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

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

323
	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
324
325
326
327
	if (!flow_group_in)
		return -ENOMEM;

	table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
328
	ft_attr.max_fte = table_size;
329
	ft_attr.prio = LEGACY_FDB_PRIO;
330
	fdb = mlx5_create_flow_table(root_ns, &ft_attr);
331
	if (IS_ERR(fdb)) {
332
333
334
335
		err = PTR_ERR(fdb);
		esw_warn(dev, "Failed to create FDB Table err %d\n", err);
		goto out;
	}
336
	esw->fdb_table.legacy.fdb = fdb;
337

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

	/* 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);
363
	if (IS_ERR(g)) {
364
365
366
367
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create allmulti flow group err(%d)\n", err);
		goto out;
	}
368
	esw->fdb_table.legacy.allmulti_grp = g;
369
370
371
372
373
374
375
376
377
378
379

	/* 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);
380
	if (IS_ERR(g)) {
381
382
383
384
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create promisc flow group err(%d)\n", err);
		goto out;
	}
385
	esw->fdb_table.legacy.promisc_grp = g;
386

387
out:
388
389
	if (err)
		esw_destroy_legacy_fdb_table(esw);
390

391
	kvfree(flow_group_in);
392
	return err;
393
394
}

395
396
397
398
399
400
401
402
403
404
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;
}

405
static void esw_destroy_legacy_fdb_table(struct mlx5_eswitch *esw)
406
{
407
	esw_debug(esw->dev, "Destroy FDB Table\n");
408
	if (!esw->fdb_table.legacy.fdb)
409
410
		return;

411
412
413
414
415
416
	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);
417
	mlx5_destroy_flow_table(esw->fdb_table.legacy.fdb);
418

419
	esw->fdb_table.legacy.fdb = NULL;
420
421
422
	esw->fdb_table.legacy.addr_grp = NULL;
	esw->fdb_table.legacy.allmulti_grp = NULL;
	esw->fdb_table.legacy.promisc_grp = NULL;
423
424
}

425
426
427
428
static int esw_create_legacy_table(struct mlx5_eswitch *esw)
{
	int err;

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

431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
	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);
}

449
450
451
452
453
454
455
/* 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;
456
	u16 vport = vaddr->vport;
457
458
	int err;

459
460
	/* Skip mlx5_mpfs_add_mac for eswitch_managers,
	 * it is already done by its netdev in mlx5e_execute_l2_action
461
	 */
462
	if (esw->manager_vport == vport)
463
464
465
466
		goto fdb_add;

	err = mlx5_mpfs_add_mac(esw->dev, mac);
	if (err) {
467
		esw_warn(esw->dev,
468
			 "Failed to add L2 table mac(%pM) for vport(0x%x), err(%d)\n",
469
470
			 mac, vport, err);
		return err;
471
	}
472
	vaddr->mpfs = true;
473

474
fdb_add:
475
	/* SRIOV is enabled: Forward UC MAC to vport */
476
	if (esw->fdb_table.legacy.fdb && esw->mode == SRIOV_LEGACY)
477
478
		vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);

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

482
	return 0;
483
484
}

485
static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
486
{
487
	u8 *mac = vaddr->node.addr;
488
	u16 vport = vaddr->vport;
489
	int err = 0;
490

491
492
	/* Skip mlx5_mpfs_del_mac for eswitch managerss,
	 * it is already done by its netdev in mlx5e_execute_l2_action
493
	 */
494
	if (!vaddr->mpfs || esw->manager_vport == vport)
495
		goto fdb_del;
496

497
498
499
500
501
502
	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;
503

504
fdb_del:
505
	if (vaddr->flow_rule)
Mark Bloch's avatar
Mark Bloch committed
506
		mlx5_del_flow_rules(vaddr->flow_rule);
507
508
509
510
511
	vaddr->flow_rule = NULL;

	return 0;
}

512
513
514
515
516
static void update_allmulti_vports(struct mlx5_eswitch *esw,
				   struct vport_addr *vaddr,
				   struct esw_mc_addr *esw_mc)
{
	u8 *mac = vaddr->node.addr;
517
518
	struct mlx5_vport *vport;
	u16 i, vport_num;
519

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

560
561
562
563
564
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;
565
	u16 vport = vaddr->vport;
566

567
	if (!esw->fdb_table.legacy.fdb)
568
569
570
571
572
573
574
575
576
577
578
		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 */
579
		esw_fdb_set_vport_rule(esw, mac, MLX5_VPORT_UPLINK);
580
581
582
583

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

584
add:
585
586
587
588
589
590
	/* 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++;

591
592
593
594
595
596
597
598
599
600
601
602
603
604
	/* 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;
605
	u16 vport = vaddr->vport;
606

607
	if (!esw->fdb_table.legacy.fdb)
608
609
610
611
612
613
		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)",
614
615
616
			 mac, vport);
		return -EINVAL;
	}
617
618
619
620
621
622
	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
623
		mlx5_del_flow_rules(vaddr->flow_rule);
624
625
	vaddr->flow_rule = NULL;

626
627
628
629
	/* 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))
630
		return 0;
631

632
633
634
	/* Remove this multicast mac from all the mc promiscuous vports */
	update_allmulti_vports(esw, vaddr, esw_mc);

635
	if (esw_mc->uplink_rule)
Mark Bloch's avatar
Mark Bloch committed
636
		mlx5_del_flow_rules(esw_mc->uplink_rule);
637
638

	l2addr_hash_del(esw_mc);
639
640
641
	return 0;
}

642
643
/* Apply vport UC/MC list to HW l2 table and FDB table */
static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw,
644
				      struct mlx5_vport *vport, int list_type)
645
{
646
647
648
649
	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;
650
651
652
653
654
	struct l2addr_node *node;
	struct hlist_head *hash;
	struct hlist_node *tmp;
	int hi;

655
656
657
658
659
660
	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;
661
	for_each_l2hash_node(node, tmp, hash, hi) {
662
		addr = container_of(node, struct vport_addr, node);
663
664
		switch (addr->action) {
		case MLX5_ACTION_ADD:
665
			vport_addr_add(esw, addr);
666
667
668
			addr->action = MLX5_ACTION_NONE;
			break;
		case MLX5_ACTION_DEL:
669
			vport_addr_del(esw, addr);
670
671
672
673
674
675
			l2addr_hash_del(addr);
			break;
		}
	}
}

676
677
/* Sync vport UC/MC list from vport context */
static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
678
				       struct mlx5_vport *vport, int list_type)
679
{
680
	bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
681
	u8 (*mac_list)[ETH_ALEN];
682
683
	struct l2addr_node *node;
	struct vport_addr *addr;
684
685
686
687
688
689
690
	struct hlist_head *hash;
	struct hlist_node *tmp;
	int size;
	int err;
	int hi;
	int i;

691
692
	size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) :
		       MLX5_MAX_MC_PER_VPORT(esw->dev);
693
694
695
696
697

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

698
	hash = is_uc ? vport->uc_list : vport->mc_list;
699
700

	for_each_l2hash_node(node, tmp, hash, hi) {
701
		addr = container_of(node, struct vport_addr, node);
702
703
704
		addr->action = MLX5_ACTION_DEL;
	}

705
706
707
	if (!vport->enabled)
		goto out;

708
	err = mlx5_query_nic_vport_mac_list(esw->dev, vport->vport, list_type,
709
710
					    mac_list, &size);
	if (err)
711
		goto out;
712
	esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n",
713
		  vport->vport, is_uc ? "UC" : "MC", size);
714
715

	for (i = 0; i < size; i++) {
716
		if (is_uc && !is_valid_ether_addr(mac_list[i]))
717
718
			continue;

719
720
721
722
		if (!is_uc && !is_multicast_ether_addr(mac_list[i]))
			continue;

		addr = l2addr_hash_find(hash, mac_list[i], struct vport_addr);
723
724
		if (addr) {
			addr->action = MLX5_ACTION_NONE;
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
			/* 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;
			}
743
744
745
			continue;
		}

746
		addr = l2addr_hash_add(hash, mac_list[i], struct vport_addr,
747
748
749
750
				       GFP_KERNEL);
		if (!addr) {
			esw_warn(esw->dev,
				 "Failed to add MAC(%pM) to vport[%d] DB\n",
751
				 mac_list[i], vport->vport);
752
753
			continue;
		}
754
		addr->vport = vport->vport;
755
756
		addr->action = MLX5_ACTION_ADD;
	}
757
out:
758
759
760
	kfree(mac_list);
}

761
762
763
/* Sync vport UC/MC list from vport context
 * Must be called after esw_update_vport_addr_list
 */
764
765
static void esw_update_vport_mc_promisc(struct mlx5_eswitch *esw,
					struct mlx5_vport *vport)
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
{
	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",
789
				 mac, vport->vport);
790
791
			continue;
		}
792
		addr->vport = vport->vport;
793
794
795
796
797
798
		addr->action = MLX5_ACTION_ADD;
		addr->mc_promisc = true;
	}
}

/* Apply vport rx mode to HW FDB table */
799
800
static void esw_apply_vport_rx_mode(struct mlx5_eswitch *esw,
				    struct mlx5_vport *vport,
801
802
				    bool promisc, bool mc_promisc)
{
803
	struct esw_mc_addr *allmulti_addr = &esw->mc_promisc;
804
805
806
807
808
809

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

	if (mc_promisc) {
		vport->allmulti_rule =
810
			esw_fdb_set_vport_allmulti_rule(esw, vport->vport);
811
812
813
		if (!allmulti_addr->uplink_rule)
			allmulti_addr->uplink_rule =
				esw_fdb_set_vport_allmulti_rule(esw,
814
								MLX5_VPORT_UPLINK);
815
816
		allmulti_addr->refcnt++;
	} else if (vport->allmulti_rule) {
Mark Bloch's avatar
Mark Bloch committed
817
		mlx5_del_flow_rules(vport->allmulti_rule);
818
819
820
821
822
823
		vport->allmulti_rule = NULL;

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

		if (allmulti_addr->uplink_rule)
Mark Bloch's avatar
Mark Bloch committed
824
			mlx5_del_flow_rules(allmulti_addr->uplink_rule);
825
826
827
828
829
830
831
832
		allmulti_addr->uplink_rule = NULL;
	}

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

	if (promisc) {
833
834
		vport->promisc_rule =
			esw_fdb_set_vport_promisc_rule(esw, vport->vport);
835
	} else if (vport->promisc_rule) {
Mark Bloch's avatar
Mark Bloch committed
836
		mlx5_del_flow_rules(vport->promisc_rule);
837
838
839
840
841
		vport->promisc_rule = NULL;
	}
}

/* Sync vport rx mode from vport context */
842
843
static void esw_update_vport_rx_mode(struct mlx5_eswitch *esw,
				     struct mlx5_vport *vport)
844
845
846
847
848
849
850
{
	int promisc_all = 0;
	int promisc_uc = 0;
	int promisc_mc = 0;
	int err;

	err = mlx5_query_nic_vport_promisc(esw->dev,
851
					   vport->vport,
852
853
854
855
856
857
					   &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",
858
		  vport->vport, promisc_all, promisc_mc);
859

860
	if (!vport->info.trusted || !vport->enabled) {
861
862
863
864
865
		promisc_uc = 0;
		promisc_mc = 0;
		promisc_all = 0;
	}

866
	esw_apply_vport_rx_mode(esw, vport, promisc_all,
867
868
869
				(promisc_all || promisc_mc));
}

870
static void esw_vport_change_handle_locked(struct mlx5_vport *vport)
871
872
{
	struct mlx5_core_dev *dev = vport->dev;
873
	struct mlx5_eswitch *esw = dev->priv.eswitch;
874
875
876
	u8 mac[ETH_ALEN];

	mlx5_query_nic_vport_mac_address(dev, vport->vport, mac);
877
878
879
880
	esw_debug(dev, "vport[%d] Context Changed: perm mac: %pM\n",
		  vport->vport, mac);

	if (vport->enabled_events & UC_ADDR_CHANGE) {
881
882
		esw_update_vport_addr_list(esw, vport, MLX5_NVPRT_LIST_TYPE_UC);
		esw_apply_vport_addr_list(esw, vport, MLX5_NVPRT_LIST_TYPE_UC);
883
	}
884

885
886
	if (vport->enabled_events & MC_ADDR_CHANGE)
		esw_update_vport_addr_list(esw, vport, MLX5_NVPRT_LIST_TYPE_MC);
887
888

	if (vport->enabled_events & PROMISC_CHANGE) {
889
		esw_update_vport_rx_mode(esw, vport);
890
		if (!IS_ERR_OR_NULL(vport->allmulti_rule))
891
			esw_update_vport_mc_promisc(esw, vport);
892
893
	}

894
895
	if (vport->enabled_events & (PROMISC_CHANGE | MC_ADDR_CHANGE))
		esw_apply_vport_addr_list(esw, vport, MLX5_NVPRT_LIST_TYPE_MC);
896

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

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

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

932
933
934
935
936
	if (!MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support))
		return -EOPNOTSUPP;

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

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

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

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

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

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

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

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

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

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

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

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