eswitch.c 69.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
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
int mlx5_eswitch_modify_esw_vport_context(struct mlx5_eswitch *esw, u16 vport,
					  void *in, int inlen)
{
	return modify_esw_vport_context_cmd(esw->dev, vport, in, inlen);
}

static int query_esw_vport_context_cmd(struct mlx5_core_dev *dev, u16 vport,
				       void *out, int outlen)
{
	u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)] = {};

	MLX5_SET(query_esw_vport_context_in, in, opcode,
		 MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT);
	MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport);
	MLX5_SET(modify_esw_vport_context_in, in, other_vport, 1);
	return mlx5_cmd_exec(dev, in, sizeof(in), out, outlen);
}

int mlx5_eswitch_query_esw_vport_context(struct mlx5_eswitch *esw, u16 vport,
					 void *out, int outlen)
{
	return query_esw_vport_context_cmd(esw->dev, vport, out, outlen);
}

161
static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u16 vport,
162
				  u16 vlan, u8 qos, u8 set_flags)
163
{
164
	u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {0};
165
166
167

	if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
	    !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
168
		return -EOPNOTSUPP;
169

170
171
172
173
	esw_debug(dev, "Set Vport[%d] VLAN %d qos %d set=%x\n",
		  vport, vlan, qos, set_flags);

	if (set_flags & SET_VLAN_STRIP)
174
175
		MLX5_SET(modify_esw_vport_context_in, in,
			 esw_vport_context.vport_cvlan_strip, 1);
176
177

	if (set_flags & SET_VLAN_INSERT) {
178
179
180
		/* insert only if no vlan in packet */
		MLX5_SET(modify_esw_vport_context_in, in,
			 esw_vport_context.vport_cvlan_insert, 1);
181

182
183
184
185
186
187
188
189
190
191
192
193
194
195
		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));
}

196
/* E-Switch FDB */
Mark Bloch's avatar
Mark Bloch committed
197
static struct mlx5_flow_handle *
198
__esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u16 vport, bool rx_rule,
199
			 u8 mac_c[ETH_ALEN], u8 mac_v[ETH_ALEN])
200
{
201
202
	int match_header = (is_zero_ether_addr(mac_c) ? 0 :
			    MLX5_MATCH_OUTER_HEADERS);
Mark Bloch's avatar
Mark Bloch committed
203
	struct mlx5_flow_handle *flow_rule = NULL;
204
	struct mlx5_flow_act flow_act = {0};
205
	struct mlx5_flow_destination dest = {};
206
	struct mlx5_flow_spec *spec;
207
208
	void *mv_misc = NULL;
	void *mc_misc = NULL;
209
210
	u8 *dmac_v = NULL;
	u8 *dmac_c = NULL;
211

212
213
	if (rx_rule)
		match_header |= MLX5_MATCH_MISC_PARAMETERS;
214

215
216
	spec = kvzalloc(sizeof(*spec), GFP_KERNEL);
	if (!spec)
217
		return NULL;
218

219
	dmac_v = MLX5_ADDR_OF(fte_match_param, spec->match_value,
220
			      outer_headers.dmac_47_16);
221
	dmac_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria,
222
223
			      outer_headers.dmac_47_16);

224
	if (match_header & MLX5_MATCH_OUTER_HEADERS) {
225
226
227
		ether_addr_copy(dmac_v, mac_v);
		ether_addr_copy(dmac_c, mac_c);
	}
228

229
	if (match_header & MLX5_MATCH_MISC_PARAMETERS) {
230
231
232
233
		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);
234
		MLX5_SET(fte_match_set_misc, mv_misc, source_port, MLX5_VPORT_UPLINK);
235
236
237
		MLX5_SET_TO_ONES(fte_match_set_misc, mc_misc, source_port);
	}

238
	dest.type = MLX5_FLOW_DESTINATION_TYPE_VPORT;
239
	dest.vport.num = vport;
240
241
242
243

	esw_debug(esw->dev,
		  "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n",
		  dmac_v, dmac_c, vport);
244
	spec->match_criteria_enable = match_header;
245
	flow_act.action =  MLX5_FLOW_CONTEXT_ACTION_FWD_DEST;
246
	flow_rule =
247
		mlx5_add_flow_rules(esw->fdb_table.legacy.fdb, spec,
248
				    &flow_act, &dest, 1);
249
	if (IS_ERR(flow_rule)) {
250
251
		esw_warn(esw->dev,
			 "FDB: Failed to add flow rule: dmac_v(%pM) dmac_c(%pM) -> vport(%d), err(%ld)\n",
252
253
254
			 dmac_v, dmac_c, vport, PTR_ERR(flow_rule));
		flow_rule = NULL;
	}
255
256

	kvfree(spec);
257
258
259
	return flow_rule;
}

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

	eth_broadcast_addr(mac_c);
266
267
268
	return __esw_fdb_set_vport_rule(esw, vport, false, mac_c, mac);
}

Mark Bloch's avatar
Mark Bloch committed
269
static struct mlx5_flow_handle *
270
esw_fdb_set_vport_allmulti_rule(struct mlx5_eswitch *esw, u16 vport)
271
272
273
274
275
276
277
278
279
280
281
{
	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
282
static struct mlx5_flow_handle *
283
esw_fdb_set_vport_promisc_rule(struct mlx5_eswitch *esw, u16 vport)
284
285
286
287
288
289
290
{
	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);
291
292
}

293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
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;
}

324
static int esw_create_legacy_fdb_table(struct mlx5_eswitch *esw)
325
{
326
	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
327
	struct mlx5_flow_table_attr ft_attr = {};
328
	struct mlx5_core_dev *dev = esw->dev;
329
	struct mlx5_flow_namespace *root_ns;
330
	struct mlx5_flow_table *fdb;
331
332
333
334
	struct mlx5_flow_group *g;
	void *match_criteria;
	int table_size;
	u32 *flow_group_in;
335
	u8 *dmac;
336
	int err = 0;
337
338
339
340

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

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

347
	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
348
349
350
351
	if (!flow_group_in)
		return -ENOMEM;

	table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
352
	ft_attr.max_fte = table_size;
353
	ft_attr.prio = LEGACY_FDB_PRIO;
354
	fdb = mlx5_create_flow_table(root_ns, &ft_attr);
355
	if (IS_ERR(fdb)) {
356
357
358
359
		err = PTR_ERR(fdb);
		esw_warn(dev, "Failed to create FDB Table err %d\n", err);
		goto out;
	}
360
	esw->fdb_table.legacy.fdb = fdb;
361

362
	/* Addresses group : Full match unicast/multicast addresses */
363
364
365
366
367
	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);
368
369
	/* Preserve 2 entries for allmulti and promisc rules*/
	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 3);
370
371
	eth_broadcast_addr(dmac);
	g = mlx5_create_flow_group(fdb, flow_group_in);
372
	if (IS_ERR(g)) {
373
374
375
376
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create flow group err(%d)\n", err);
		goto out;
	}
377
	esw->fdb_table.legacy.addr_grp = g;
378
379
380
381
382
383
384
385
386

	/* 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);
387
	if (IS_ERR(g)) {
388
389
390
391
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create allmulti flow group err(%d)\n", err);
		goto out;
	}
392
	esw->fdb_table.legacy.allmulti_grp = g;
393
394
395
396
397
398
399
400
401
402
403

	/* 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);
404
	if (IS_ERR(g)) {
405
406
407
408
		err = PTR_ERR(g);
		esw_warn(dev, "Failed to create promisc flow group err(%d)\n", err);
		goto out;
	}
409
	esw->fdb_table.legacy.promisc_grp = g;
410

411
out:
412
413
	if (err)
		esw_destroy_legacy_fdb_table(esw);
414

415
	kvfree(flow_group_in);
416
	return err;
417
418
}

419
420
421
422
423
424
425
426
427
428
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;
}

429
static void esw_destroy_legacy_fdb_table(struct mlx5_eswitch *esw)
430
{
431
	esw_debug(esw->dev, "Destroy FDB Table\n");
432
	if (!esw->fdb_table.legacy.fdb)
433
434
		return;

435
436
437
438
439
440
	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);
441
	mlx5_destroy_flow_table(esw->fdb_table.legacy.fdb);
442

443
	esw->fdb_table.legacy.fdb = NULL;
444
445
446
	esw->fdb_table.legacy.addr_grp = NULL;
	esw->fdb_table.legacy.allmulti_grp = NULL;
	esw->fdb_table.legacy.promisc_grp = NULL;
447
448
}

449
450
451
452
static int esw_create_legacy_table(struct mlx5_eswitch *esw)
{
	int err;

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

455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
	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);
}

473
474
475
476
477
478
479
/* 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;
480
	u16 vport = vaddr->vport;
481
482
	int err;

483
484
	/* Skip mlx5_mpfs_add_mac for eswitch_managers,
	 * it is already done by its netdev in mlx5e_execute_l2_action
485
	 */
486
	if (esw->manager_vport == vport)
487
488
489
490
		goto fdb_add;

	err = mlx5_mpfs_add_mac(esw->dev, mac);
	if (err) {
491
		esw_warn(esw->dev,
492
			 "Failed to add L2 table mac(%pM) for vport(0x%x), err(%d)\n",
493
494
			 mac, vport, err);
		return err;
495
	}
496
	vaddr->mpfs = true;
497

498
fdb_add:
499
	/* SRIOV is enabled: Forward UC MAC to vport */
500
	if (esw->fdb_table.legacy.fdb && esw->mode == SRIOV_LEGACY)
501
502
		vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);

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

506
	return 0;
507
508
}

509
static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
510
{
511
	u8 *mac = vaddr->node.addr;
512
	u16 vport = vaddr->vport;
513
	int err = 0;
514

515
516
	/* Skip mlx5_mpfs_del_mac for eswitch managerss,
	 * it is already done by its netdev in mlx5e_execute_l2_action
517
	 */
518
	if (!vaddr->mpfs || esw->manager_vport == vport)
519
		goto fdb_del;
520

521
522
523
524
525
526
	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;
527

528
fdb_del:
529
	if (vaddr->flow_rule)
Mark Bloch's avatar
Mark Bloch committed
530
		mlx5_del_flow_rules(vaddr->flow_rule);
531
532
533
534
535
	vaddr->flow_rule = NULL;

	return 0;
}

536
537
538
539
540
static void update_allmulti_vports(struct mlx5_eswitch *esw,
				   struct vport_addr *vaddr,
				   struct esw_mc_addr *esw_mc)
{
	u8 *mac = vaddr->node.addr;
541
542
	struct mlx5_vport *vport;
	u16 i, vport_num;
543

544
	mlx5_esw_for_all_vports(esw, i, vport) {
545
546
547
548
549
		struct hlist_head *vport_hash = vport->mc_list;
		struct vport_addr *iter_vaddr =
					l2addr_hash_find(vport_hash,
							 mac,
							 struct vport_addr);
550
		vport_num = vport->vport;
551
		if (IS_ERR_OR_NULL(vport->allmulti_rule) ||
552
		    vaddr->vport == vport_num)
553
554
555
556
557
558
559
560
561
562
563
			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",
564
					 mac, vport_num);
565
566
				continue;
			}
567
			iter_vaddr->vport = vport_num;
568
569
570
			iter_vaddr->flow_rule =
					esw_fdb_set_vport_rule(esw,
							       mac,
571
							       vport_num);
572
			iter_vaddr->mc_promisc = true;
573
574
575
576
			break;
		case MLX5_ACTION_DEL:
			if (!iter_vaddr)
				continue;
Mark Bloch's avatar
Mark Bloch committed
577
			mlx5_del_flow_rules(iter_vaddr->flow_rule);
578
579
580
581
582
583
			l2addr_hash_del(iter_vaddr);
			break;
		}
	}
}

584
585
586
587
588
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;
589
	u16 vport = vaddr->vport;
590

591
	if (!esw->fdb_table.legacy.fdb)
592
593
594
595
596
597
598
599
600
601
602
		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 */
603
		esw_fdb_set_vport_rule(esw, mac, MLX5_VPORT_UPLINK);
604
605
606
607

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

608
add:
609
610
611
612
613
614
	/* 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++;

615
616
617
618
619
620
621
622
623
624
625
626
627
628
	/* 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;
629
	u16 vport = vaddr->vport;
630

631
	if (!esw->fdb_table.legacy.fdb)
632
633
634
635
636
637
		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)",
638
639
640
			 mac, vport);
		return -EINVAL;
	}
641
642
643
644
645
646
	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
647
		mlx5_del_flow_rules(vaddr->flow_rule);
648
649
	vaddr->flow_rule = NULL;

650
651
652
653
	/* 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))
654
		return 0;
655

656
657
658
	/* Remove this multicast mac from all the mc promiscuous vports */
	update_allmulti_vports(esw, vaddr, esw_mc);

659
	if (esw_mc->uplink_rule)
Mark Bloch's avatar
Mark Bloch committed
660
		mlx5_del_flow_rules(esw_mc->uplink_rule);
661
662

	l2addr_hash_del(esw_mc);
663
664
665
	return 0;
}

666
667
/* Apply vport UC/MC list to HW l2 table and FDB table */
static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw,
668
				      struct mlx5_vport *vport, int list_type)
669
{
670
671
672
673
	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;
674
675
676
677
678
	struct l2addr_node *node;
	struct hlist_head *hash;
	struct hlist_node *tmp;
	int hi;

679
680
681
682
683
684
	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;
685
	for_each_l2hash_node(node, tmp, hash, hi) {
686
		addr = container_of(node, struct vport_addr, node);
687
688
		switch (addr->action) {
		case MLX5_ACTION_ADD:
689
			vport_addr_add(esw, addr);
690
691
692
			addr->action = MLX5_ACTION_NONE;
			break;
		case MLX5_ACTION_DEL:
693
			vport_addr_del(esw, addr);
694
695
696
697
698
699
			l2addr_hash_del(addr);
			break;
		}
	}
}

700
701
/* Sync vport UC/MC list from vport context */
static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
702
				       struct mlx5_vport *vport, int list_type)
703
{
704
	bool is_uc = list_type == MLX5_NVPRT_LIST_TYPE_UC;
705
	u8 (*mac_list)[ETH_ALEN];
706
707
	struct l2addr_node *node;
	struct vport_addr *addr;
708
709
710
711
712
713
714
	struct hlist_head *hash;
	struct hlist_node *tmp;
	int size;
	int err;
	int hi;
	int i;

715
716
	size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) :
		       MLX5_MAX_MC_PER_VPORT(esw->dev);
717
718
719
720
721

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

722
	hash = is_uc ? vport->uc_list : vport->mc_list;
723
724

	for_each_l2hash_node(node, tmp, hash, hi) {
725
		addr = container_of(node, struct vport_addr, node);
726
727
728
		addr->action = MLX5_ACTION_DEL;
	}

729
730
731
	if (!vport->enabled)
		goto out;

732
	err = mlx5_query_nic_vport_mac_list(esw->dev, vport->vport, list_type,
733
734
					    mac_list, &size);
	if (err)
735
		goto out;
736
	esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n",
737
		  vport->vport, is_uc ? "UC" : "MC", size);
738
739

	for (i = 0; i < size; i++) {
740
		if (is_uc && !is_valid_ether_addr(mac_list[i]))
741
742
			continue;

743
744
745
746
		if (!is_uc && !is_multicast_ether_addr(mac_list[i]))
			continue;

		addr = l2addr_hash_find(hash, mac_list[i], struct vport_addr);
747
748
		if (addr) {
			addr->action = MLX5_ACTION_NONE;
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
			/* 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;
			}
767
768
769
			continue;
		}

770
		addr = l2addr_hash_add(hash, mac_list[i], struct vport_addr,
771
772
773
774
				       GFP_KERNEL);
		if (!addr) {
			esw_warn(esw->dev,
				 "Failed to add MAC(%pM) to vport[%d] DB\n",
775
				 mac_list[i], vport->vport);
776
777
			continue;
		}
778
		addr->vport = vport->vport;
779
780
		addr->action = MLX5_ACTION_ADD;
	}
781
out:
782
783
784
	kfree(mac_list);
}

785
786
787
/* Sync vport UC/MC list from vport context
 * Must be called after esw_update_vport_addr_list
 */
788
789
static void esw_update_vport_mc_promisc(struct mlx5_eswitch *esw,
					struct mlx5_vport *vport)
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
{
	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",
813
				 mac, vport->vport);
814
815
			continue;
		}
816
		addr->vport = vport->vport;
817
818
819
820
821
822
		addr->action = MLX5_ACTION_ADD;
		addr->mc_promisc = true;
	}
}

/* Apply vport rx mode to HW FDB table */
823
824
static void esw_apply_vport_rx_mode(struct mlx5_eswitch *esw,
				    struct mlx5_vport *vport,
825
826
				    bool promisc, bool mc_promisc)
{
827
	struct esw_mc_addr *allmulti_addr = &esw->mc_promisc;
828
829
830
831
832
833

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

	if (mc_promisc) {
		vport->allmulti_rule =
834
			esw_fdb_set_vport_allmulti_rule(esw, vport->vport);
835
836
837
		if (!allmulti_addr->uplink_rule)
			allmulti_addr->uplink_rule =
				esw_fdb_set_vport_allmulti_rule(esw,
838
								MLX5_VPORT_UPLINK);
839
840
		allmulti_addr->refcnt++;
	} else if (vport->allmulti_rule) {
Mark Bloch's avatar
Mark Bloch committed
841
		mlx5_del_flow_rules(vport->allmulti_rule);
842
843
844
845
846
847
		vport->allmulti_rule = NULL;

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

		if (allmulti_addr->uplink_rule)
Mark Bloch's avatar
Mark Bloch committed
848
			mlx5_del_flow_rules(allmulti_addr->uplink_rule);
849
850
851
852
853
854
855
856
		allmulti_addr->uplink_rule = NULL;
	}

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

	if (promisc) {
857
858
		vport->promisc_rule =
			esw_fdb_set_vport_promisc_rule(esw, vport->vport);
859
	} else if (vport->promisc_rule) {
Mark Bloch's avatar
Mark Bloch committed
860
		mlx5_del_flow_rules(vport->promisc_rule);
861
862
863
864
865
		vport->promisc_rule = NULL;
	}
}

/* Sync vport rx mode from vport context */
866
867
static void esw_update_vport_rx_mode(struct mlx5_eswitch *esw,
				     struct mlx5_vport *vport)
868
869
870
871
872
873
874
{
	int promisc_all = 0;
	int promisc_uc = 0;
	int promisc_mc = 0;
	int err;

	err = mlx5_query_nic_vport_promisc(esw->dev,
875
					   vport->vport,
876
877
878
879
880
881
					   &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",
882
		  vport->vport, promisc_all, promisc_mc);
883

884
	if (!vport->info.trusted || !vport->enabled) {
885
886
887
888
889
		promisc_uc = 0;
		promisc_mc = 0;
		promisc_all = 0;
	}

890
	esw_apply_vport_rx_mode(esw, vport, promisc_all,
891
892
893
				(promisc_all || promisc_mc));
}

894
static void esw_vport_change_handle_locked(struct mlx5_vport *vport)
895
896
{
	struct mlx5_core_dev *dev = vport->dev;
897
	struct mlx5_eswitch *esw = dev->priv.eswitch;
898
899
900
	u8 mac[ETH_ALEN];

	mlx5_query_nic_vport_mac_address(dev, vport->vport, mac);
901
902
903
904
	esw_debug(dev, "vport[%d] Context Changed: perm mac: %pM\n",
		  vport->vport, mac);

	if (vport->enabled_events & UC_ADDR_CHANGE) {
905
906
		esw_update_vport_addr_list(esw, vport, MLX5_NVPRT_LIST_TYPE_UC);
		esw_apply_vport_addr_list(esw, vport, MLX5_NVPRT_LIST_TYPE_UC);
907
	}
908

909
910
	if (vport->enabled_events & MC_ADDR_CHANGE)
		esw_update_vport_addr_list(esw, vport, MLX5_NVPRT_LIST_TYPE_MC);
911
912

	if (vport->enabled_events & PROMISC_CHANGE) {
913
		esw_update_vport_rx_mode(esw, vport);
914
		if (!IS_ERR_OR_NULL(vport->allmulti_rule))
915
			esw_update_vport_mc_promisc(esw, vport);
916
917
	}

918
919
	if (vport->enabled_events & (PROMISC_CHANGE | MC_ADDR_CHANGE))
		esw_apply_vport_addr_list(esw, vport, MLX5_NVPRT_LIST_TYPE_MC);
920

921
	esw_debug(esw->dev, "vport[%d] Context Changed: Done\n", vport->vport);
922
923
	if (vport->enabled)
		arm_vport_context_events_cmd(dev, vport->vport,
924
					     vport->enabled_events);
925
926
}

927
928
929
930
931
932
933
934
935
936
937
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);
}

938
939
int esw_vport_enable_egress_acl(struct mlx5_eswitch *esw,
				struct mlx5_vport *vport)
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
{
	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;

956
957
958
959
960
	if (!MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support))
		return -EOPNOTSUPP;

	if (!IS_ERR_OR_NULL(vport->egress.acl))
		return 0;
961
962
963
964

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

965
	root_ns = mlx5_get_flow_vport_acl_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_EGRESS,
966
			mlx5_eswitch_vport_num_to_index(esw, vport->vport));
967
	if (!root_ns) {
968
		esw_warn(dev, "Failed to get E-Switch egress flow namespace for vport (%d)\n", vport->vport);
969
		return -EOPNOTSUPP;
970
971
	}

972
	flow_group_in = kvzalloc(inlen, GFP_KERNEL);
973
	if (!flow_group_in)
974
		return -ENOMEM;
975
976

	acl = mlx5_create_vport_flow_table(root_ns, 0, table_size, 0, vport->vport);
977
	if (IS_ERR(acl)) {
978
979
980
981
982
983
984
985
		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);
986
	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.cvlan_tag);
987
988
989
990
991
	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);
992
	if (IS_ERR(vlan_grp)) {
993
994
995
996
997
998
999
1000
1001
1002
		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);
1003
	if (IS_ERR(drop_grp)) {
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
		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:
1014
	kvfree(flow_group_in);
1015
1016
1017
1018
	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);
1019
	return err;
1020
1021
}

1022
1023
void esw_vport_cleanup_egress_rules(struct mlx5_eswitch *esw,
				    struct mlx5_vport *vport)
1024
1025
{
	if (!IS_ERR_OR_NULL(vport->egress.allowed_vlan))
Mark Bloch's avatar
Mark Bloch committed
1026
		mlx5_del_flow_rules(vport->egress.allowed_vlan);
1027
1028

	if (!IS_ERR_OR_NULL(vport->egress.drop_rule))
Mark Bloch's avatar
Mark Bloch committed
1029
		mlx5_del_flow_rules(vport->egress.drop_rule);
1030
1031
1032
1033
1034

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

1035
1036
void esw_vport_disable_egress_acl(struct mlx5_eswitch *esw,
				  struct mlx5_vport *vport)
1037
1038
1039
1040
1041
1042
{
	if (IS_ERR_OR_NULL(vport->egress.acl))
		return;

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

1043
	esw_vport_cleanup_egress_rules(esw, vport);
1044
1045
1046
1047
1048
1049
1050
1051
	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;
}