1/*-
2 * Copyright (c) 2013-2017, Mellanox Technologies, Ltd.  All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS `AS IS' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 * $FreeBSD$
26 */
27
28#include <linux/etherdevice.h>
29#include <dev/mlx5/driver.h>
30#include <dev/mlx5/mlx5_ifc.h>
31#include <dev/mlx5/vport.h>
32#include <dev/mlx5/fs.h>
33#include <dev/mlx5/mpfs.h>
34#include "mlx5_core.h"
35#include "eswitch.h"
36
37#define UPLINK_VPORT 0xFFFF
38
39#define MLX5_DEBUG_ESWITCH_MASK BIT(3)
40
41#define esw_info(dev, format, ...)				\
42	printf("mlx5_core: INFO: ""(%s): E-Switch: " format, (dev)->priv.name, ##__VA_ARGS__)
43
44#define esw_warn(dev, format, ...)				\
45	printf("mlx5_core: WARN: ""(%s): E-Switch: " format, (dev)->priv.name, ##__VA_ARGS__)
46
47#define esw_debug(dev, format, ...)				\
48	mlx5_core_dbg_mask(dev, MLX5_DEBUG_ESWITCH_MASK, format, ##__VA_ARGS__)
49
50enum {
51	MLX5_ACTION_NONE = 0,
52	MLX5_ACTION_ADD  = 1,
53	MLX5_ACTION_DEL  = 2,
54};
55
56/* E-Switch UC L2 table hash node */
57struct esw_uc_addr {
58	struct l2addr_node node;
59	u32                table_index;
60	u32                vport;
61};
62
63/* E-Switch MC FDB table hash node */
64struct esw_mc_addr { /* SRIOV only */
65	struct l2addr_node     node;
66	struct mlx5_flow_rule *uplink_rule; /* Forward to uplink rule */
67	u32                    refcnt;
68};
69
70/* Vport UC/MC hash node */
71struct vport_addr {
72	struct l2addr_node     node;
73	u8                     action;
74	u32                    vport;
75	struct mlx5_flow_rule *flow_rule; /* SRIOV only */
76};
77
78enum {
79	UC_ADDR_CHANGE = BIT(0),
80	MC_ADDR_CHANGE = BIT(1),
81};
82
83/* Vport context events */
84#define SRIOV_VPORT_EVENTS (UC_ADDR_CHANGE | \
85			    MC_ADDR_CHANGE)
86
87static int arm_vport_context_events_cmd(struct mlx5_core_dev *dev, u16 vport,
88					u32 events_mask)
89{
90	int in[MLX5_ST_SZ_DW(modify_nic_vport_context_in)] = {0};
91	int out[MLX5_ST_SZ_DW(modify_nic_vport_context_out)] = {0};
92	void *nic_vport_ctx;
93
94	MLX5_SET(modify_nic_vport_context_in, in,
95		 opcode, MLX5_CMD_OP_MODIFY_NIC_VPORT_CONTEXT);
96	MLX5_SET(modify_nic_vport_context_in, in, field_select.change_event, 1);
97	MLX5_SET(modify_nic_vport_context_in, in, vport_number, vport);
98	if (vport)
99		MLX5_SET(modify_nic_vport_context_in, in, other_vport, 1);
100	nic_vport_ctx = MLX5_ADDR_OF(modify_nic_vport_context_in,
101				     in, nic_vport_context);
102
103	MLX5_SET(nic_vport_context, nic_vport_ctx, arm_change_event, 1);
104
105	if (events_mask & UC_ADDR_CHANGE)
106		MLX5_SET(nic_vport_context, nic_vport_ctx,
107			 event_on_uc_address_change, 1);
108	if (events_mask & MC_ADDR_CHANGE)
109		MLX5_SET(nic_vport_context, nic_vport_ctx,
110			 event_on_mc_address_change, 1);
111
112	return mlx5_cmd_exec(dev, in, sizeof(in), out, sizeof(out));
113}
114
115/* E-Switch vport context HW commands */
116static int query_esw_vport_context_cmd(struct mlx5_core_dev *mdev, u32 vport,
117				       u32 *out, int outlen)
118{
119	u32 in[MLX5_ST_SZ_DW(query_esw_vport_context_in)] = {0};
120
121	MLX5_SET(query_nic_vport_context_in, in, opcode,
122		 MLX5_CMD_OP_QUERY_ESW_VPORT_CONTEXT);
123
124	MLX5_SET(query_esw_vport_context_in, in, vport_number, vport);
125	if (vport)
126		MLX5_SET(query_esw_vport_context_in, in, other_vport, 1);
127
128	return mlx5_cmd_exec(mdev, in, sizeof(in), out, outlen);
129}
130
131static int query_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
132				 u16 *vlan, u8 *qos)
133{
134	u32 out[MLX5_ST_SZ_DW(query_esw_vport_context_out)] = {0};
135	int err;
136	bool cvlan_strip;
137	bool cvlan_insert;
138
139	*vlan = 0;
140	*qos = 0;
141
142	if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
143	    !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
144		return -ENOTSUPP;
145
146	err = query_esw_vport_context_cmd(dev, vport, out, sizeof(out));
147	if (err)
148		goto out;
149
150	cvlan_strip = MLX5_GET(query_esw_vport_context_out, out,
151			       esw_vport_context.vport_cvlan_strip);
152
153	cvlan_insert = MLX5_GET(query_esw_vport_context_out, out,
154				esw_vport_context.vport_cvlan_insert);
155
156	if (cvlan_strip || cvlan_insert) {
157		*vlan = MLX5_GET(query_esw_vport_context_out, out,
158				 esw_vport_context.cvlan_id);
159		*qos = MLX5_GET(query_esw_vport_context_out, out,
160				esw_vport_context.cvlan_pcp);
161	}
162
163	esw_debug(dev, "Query Vport[%d] cvlan: VLAN %d qos=%d\n",
164		  vport, *vlan, *qos);
165out:
166	return err;
167}
168
169static int modify_esw_vport_context_cmd(struct mlx5_core_dev *dev, u16 vport,
170					void *in, int inlen)
171{
172	u32 out[MLX5_ST_SZ_DW(modify_esw_vport_context_out)] = {0};
173
174	MLX5_SET(modify_esw_vport_context_in, in, vport_number, vport);
175	if (vport)
176		MLX5_SET(modify_esw_vport_context_in, in, other_vport, 1);
177
178	MLX5_SET(modify_esw_vport_context_in, in, opcode,
179		 MLX5_CMD_OP_MODIFY_ESW_VPORT_CONTEXT);
180
181	return mlx5_cmd_exec(dev, in, inlen, out, sizeof(out));
182}
183
184static int modify_esw_vport_cvlan(struct mlx5_core_dev *dev, u32 vport,
185				  u16 vlan, u8 qos, bool set)
186{
187	u32 in[MLX5_ST_SZ_DW(modify_esw_vport_context_in)] = {0};
188
189	if (!MLX5_CAP_ESW(dev, vport_cvlan_strip) ||
190	    !MLX5_CAP_ESW(dev, vport_cvlan_insert_if_not_exist))
191		return -ENOTSUPP;
192
193	esw_debug(dev, "Set Vport[%d] VLAN %d qos %d set=%d\n",
194		  vport, vlan, qos, set);
195
196	if (set) {
197		MLX5_SET(modify_esw_vport_context_in, in,
198			 esw_vport_context.vport_cvlan_strip, 1);
199		/* insert only if no vlan in packet */
200		MLX5_SET(modify_esw_vport_context_in, in,
201			 esw_vport_context.vport_cvlan_insert, 1);
202		MLX5_SET(modify_esw_vport_context_in, in,
203			 esw_vport_context.cvlan_pcp, qos);
204		MLX5_SET(modify_esw_vport_context_in, in,
205			 esw_vport_context.cvlan_id, vlan);
206	}
207
208	MLX5_SET(modify_esw_vport_context_in, in,
209		 field_select.vport_cvlan_strip, 1);
210	MLX5_SET(modify_esw_vport_context_in, in,
211		 field_select.vport_cvlan_insert, 1);
212
213	return modify_esw_vport_context_cmd(dev, vport, in, sizeof(in));
214}
215
216/* E-Switch FDB */
217static struct mlx5_flow_rule *
218esw_fdb_set_vport_rule(struct mlx5_eswitch *esw, u8 mac[ETH_ALEN], u32 vport)
219{
220	int match_header = MLX5_MATCH_OUTER_HEADERS;
221	struct mlx5_flow_destination dest;
222	struct mlx5_flow_rule *flow_rule = NULL;
223	u32 *match_v;
224	u32 *match_c;
225	u8 *dmac_v;
226	u8 *dmac_c;
227
228	match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
229	match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
230	if (!match_v || !match_c) {
231		printf("mlx5_core: WARN: ""FDB: Failed to alloc match parameters\n");
232		goto out;
233	}
234	dmac_v = MLX5_ADDR_OF(fte_match_param, match_v,
235			      outer_headers.dmac_47_16);
236	dmac_c = MLX5_ADDR_OF(fte_match_param, match_c,
237			      outer_headers.dmac_47_16);
238
239	ether_addr_copy(dmac_v, mac);
240	/* Match criteria mask */
241	memset(dmac_c, 0xff, 6);
242
243	dest.type = MLX5_FLOW_CONTEXT_DEST_TYPE_VPORT;
244	dest.vport_num = vport;
245
246	esw_debug(esw->dev,
247		  "\tFDB add rule dmac_v(%pM) dmac_c(%pM) -> vport(%d)\n",
248		  dmac_v, dmac_c, vport);
249	flow_rule =
250		mlx5_add_flow_rule(esw->fdb_table.fdb,
251				   match_header,
252				   match_c,
253				   match_v,
254				   MLX5_FLOW_CONTEXT_ACTION_FWD_DEST,
255				   0, &dest);
256	if (IS_ERR_OR_NULL(flow_rule)) {
257		printf("mlx5_core: WARN: ""FDB: Failed to add flow rule: dmac_v(%pM) dmac_c(%pM) -> vport(%d), err(%ld)\n", dmac_v, dmac_c, vport, PTR_ERR(flow_rule));
258		flow_rule = NULL;
259	}
260out:
261	kfree(match_v);
262	kfree(match_c);
263	return flow_rule;
264}
265
266static int esw_create_fdb_table(struct mlx5_eswitch *esw)
267{
268	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
269	struct mlx5_core_dev *dev = esw->dev;
270	struct mlx5_flow_namespace *root_ns;
271	struct mlx5_flow_table *fdb;
272	struct mlx5_flow_group *g;
273	void *match_criteria;
274	int table_size;
275	u32 *flow_group_in;
276	u8 *dmac;
277	int err = 0;
278
279	esw_debug(dev, "Create FDB log_max_size(%d)\n",
280		  MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size));
281
282	root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_FDB);
283	if (!root_ns) {
284		esw_warn(dev, "Failed to get FDB flow namespace\n");
285		return -ENOMEM;
286	}
287
288	flow_group_in = mlx5_vzalloc(inlen);
289	if (!flow_group_in)
290		return -ENOMEM;
291	memset(flow_group_in, 0, inlen);
292
293	/* (-2) Since MaorG said so .. */
294	table_size = BIT(MLX5_CAP_ESW_FLOWTABLE_FDB(dev, log_max_ft_size)) - 2;
295
296	fdb = mlx5_create_flow_table(root_ns, 0, "FDB", table_size);
297	if (IS_ERR_OR_NULL(fdb)) {
298		err = PTR_ERR(fdb);
299		esw_warn(dev, "Failed to create FDB Table err %d\n", err);
300		goto out;
301	}
302
303	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable,
304		 MLX5_MATCH_OUTER_HEADERS);
305	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
306	dmac = MLX5_ADDR_OF(fte_match_param, match_criteria, outer_headers.dmac_47_16);
307	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
308	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, table_size - 1);
309	eth_broadcast_addr(dmac);
310
311	g = mlx5_create_flow_group(fdb, flow_group_in);
312	if (IS_ERR_OR_NULL(g)) {
313		err = PTR_ERR(g);
314		esw_warn(dev, "Failed to create flow group err(%d)\n", err);
315		goto out;
316	}
317
318	esw->fdb_table.addr_grp = g;
319	esw->fdb_table.fdb = fdb;
320out:
321	kfree(flow_group_in);
322	if (err && !IS_ERR_OR_NULL(fdb))
323		mlx5_destroy_flow_table(fdb);
324	return err;
325}
326
327static void esw_destroy_fdb_table(struct mlx5_eswitch *esw)
328{
329	if (!esw->fdb_table.fdb)
330		return;
331
332	esw_debug(esw->dev, "Destroy FDB Table\n");
333	mlx5_destroy_flow_group(esw->fdb_table.addr_grp);
334	mlx5_destroy_flow_table(esw->fdb_table.fdb);
335	esw->fdb_table.fdb = NULL;
336	esw->fdb_table.addr_grp = NULL;
337}
338
339/* E-Switch vport UC/MC lists management */
340typedef int (*vport_addr_action)(struct mlx5_eswitch *esw,
341				 struct vport_addr *vaddr);
342
343static int esw_add_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
344{
345	struct hlist_head *hash = esw->l2_table.l2_hash;
346	struct esw_uc_addr *esw_uc;
347	u8 *mac = vaddr->node.addr;
348	u32 vport = vaddr->vport;
349	int err;
350
351	esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr);
352	if (esw_uc) {
353		esw_warn(esw->dev,
354			 "Failed to set L2 mac(%pM) for vport(%d), mac is already in use by vport(%d)\n",
355			 mac, vport, esw_uc->vport);
356		return -EEXIST;
357	}
358
359	esw_uc = l2addr_hash_add(hash, mac, struct esw_uc_addr, GFP_KERNEL);
360	if (!esw_uc)
361		return -ENOMEM;
362	esw_uc->vport = vport;
363
364	err = mlx5_mpfs_add_mac(esw->dev, &esw_uc->table_index, mac, 0, 0);
365	if (err)
366		goto abort;
367
368	if (esw->fdb_table.fdb) /* SRIOV is enabled: Forward UC MAC to vport */
369		vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);
370
371	esw_debug(esw->dev, "\tADDED UC MAC: vport[%d] %pM index:%d fr(%p)\n",
372		  vport, mac, esw_uc->table_index, vaddr->flow_rule);
373	return err;
374abort:
375	l2addr_hash_del(esw_uc);
376	return err;
377}
378
379static int esw_del_uc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
380{
381	struct hlist_head *hash = esw->l2_table.l2_hash;
382	struct esw_uc_addr *esw_uc;
383	u8 *mac = vaddr->node.addr;
384	u32 vport = vaddr->vport;
385
386	esw_uc = l2addr_hash_find(hash, mac, struct esw_uc_addr);
387	if (!esw_uc || esw_uc->vport != vport) {
388		esw_debug(esw->dev,
389			  "MAC(%pM) doesn't belong to vport (%d)\n",
390			  mac, vport);
391		return -EINVAL;
392	}
393	esw_debug(esw->dev, "\tDELETE UC MAC: vport[%d] %pM index:%d fr(%p)\n",
394		  vport, mac, esw_uc->table_index, vaddr->flow_rule);
395
396	mlx5_mpfs_del_mac(esw->dev, esw_uc->table_index);
397
398	if (vaddr->flow_rule)
399		mlx5_del_flow_rule(vaddr->flow_rule);
400	vaddr->flow_rule = NULL;
401
402	l2addr_hash_del(esw_uc);
403	return 0;
404}
405
406static int esw_add_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
407{
408	struct hlist_head *hash = esw->mc_table;
409	struct esw_mc_addr *esw_mc;
410	u8 *mac = vaddr->node.addr;
411	u32 vport = vaddr->vport;
412
413	if (!esw->fdb_table.fdb)
414		return 0;
415
416	esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
417	if (esw_mc)
418		goto add;
419
420	esw_mc = l2addr_hash_add(hash, mac, struct esw_mc_addr, GFP_KERNEL);
421	if (!esw_mc)
422		return -ENOMEM;
423
424	esw_mc->uplink_rule = /* Forward MC MAC to Uplink */
425		esw_fdb_set_vport_rule(esw, mac, UPLINK_VPORT);
426add:
427	esw_mc->refcnt++;
428	/* Forward MC MAC to vport */
429	vaddr->flow_rule = esw_fdb_set_vport_rule(esw, mac, vport);
430	esw_debug(esw->dev,
431		  "\tADDED MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n",
432		  vport, mac, vaddr->flow_rule,
433		  esw_mc->refcnt, esw_mc->uplink_rule);
434	return 0;
435}
436
437static int esw_del_mc_addr(struct mlx5_eswitch *esw, struct vport_addr *vaddr)
438{
439	struct hlist_head *hash = esw->mc_table;
440	struct esw_mc_addr *esw_mc;
441	u8 *mac = vaddr->node.addr;
442	u32 vport = vaddr->vport;
443
444	if (!esw->fdb_table.fdb)
445		return 0;
446
447	esw_mc = l2addr_hash_find(hash, mac, struct esw_mc_addr);
448	if (!esw_mc) {
449		esw_warn(esw->dev,
450			 "Failed to find eswitch MC addr for MAC(%pM) vport(%d)",
451			 mac, vport);
452		return -EINVAL;
453	}
454	esw_debug(esw->dev,
455		  "\tDELETE MC MAC: vport[%d] %pM fr(%p) refcnt(%d) uplinkfr(%p)\n",
456		  vport, mac, vaddr->flow_rule, esw_mc->refcnt,
457		  esw_mc->uplink_rule);
458
459	if (vaddr->flow_rule)
460		mlx5_del_flow_rule(vaddr->flow_rule);
461	vaddr->flow_rule = NULL;
462
463	if (--esw_mc->refcnt)
464		return 0;
465
466	if (esw_mc->uplink_rule)
467		mlx5_del_flow_rule(esw_mc->uplink_rule);
468
469	l2addr_hash_del(esw_mc);
470	return 0;
471}
472
473/* Apply vport UC/MC list to HW l2 table and FDB table */
474static void esw_apply_vport_addr_list(struct mlx5_eswitch *esw,
475				      u32 vport_num, int list_type)
476{
477	struct mlx5_vport *vport = &esw->vports[vport_num];
478	bool is_uc = list_type == MLX5_NIC_VPORT_LIST_TYPE_UC;
479	vport_addr_action vport_addr_add;
480	vport_addr_action vport_addr_del;
481	struct vport_addr *addr;
482	struct l2addr_node *node;
483	struct hlist_head *hash;
484	struct hlist_node *tmp;
485	int hi;
486
487	vport_addr_add = is_uc ? esw_add_uc_addr :
488				 esw_add_mc_addr;
489	vport_addr_del = is_uc ? esw_del_uc_addr :
490				 esw_del_mc_addr;
491
492	hash = is_uc ? vport->uc_list : vport->mc_list;
493	for_each_l2hash_node(node, tmp, hash, hi) {
494		addr = container_of(node, struct vport_addr, node);
495		switch (addr->action) {
496		case MLX5_ACTION_ADD:
497			vport_addr_add(esw, addr);
498			addr->action = MLX5_ACTION_NONE;
499			break;
500		case MLX5_ACTION_DEL:
501			vport_addr_del(esw, addr);
502			l2addr_hash_del(addr);
503			break;
504		}
505	}
506}
507
508/* Sync vport UC/MC list from vport context */
509static void esw_update_vport_addr_list(struct mlx5_eswitch *esw,
510				       u32 vport_num, int list_type)
511{
512	struct mlx5_vport *vport = &esw->vports[vport_num];
513	bool is_uc = list_type == MLX5_NIC_VPORT_LIST_TYPE_UC;
514	u8 (*mac_list)[ETH_ALEN];
515	struct l2addr_node *node;
516	struct vport_addr *addr;
517	struct hlist_head *hash;
518	struct hlist_node *tmp;
519	int size;
520	int err;
521	int hi;
522	int i;
523
524	size = is_uc ? MLX5_MAX_UC_PER_VPORT(esw->dev) :
525		       MLX5_MAX_MC_PER_VPORT(esw->dev);
526
527	mac_list = kcalloc(size, ETH_ALEN, GFP_KERNEL);
528	if (!mac_list)
529		return;
530
531	hash = is_uc ? vport->uc_list : vport->mc_list;
532
533	for_each_l2hash_node(node, tmp, hash, hi) {
534		addr = container_of(node, struct vport_addr, node);
535		addr->action = MLX5_ACTION_DEL;
536	}
537
538	err = mlx5_query_nic_vport_mac_list(esw->dev, vport_num, list_type,
539					    mac_list, &size);
540	if (err)
541		return;
542	esw_debug(esw->dev, "vport[%d] context update %s list size (%d)\n",
543		  vport_num, is_uc ? "UC" : "MC", size);
544
545	for (i = 0; i < size; i++) {
546		if (is_uc && !is_valid_ether_addr(mac_list[i]))
547			continue;
548
549		if (!is_uc && !is_multicast_ether_addr(mac_list[i]))
550			continue;
551
552		addr = l2addr_hash_find(hash, mac_list[i], struct vport_addr);
553		if (addr) {
554			addr->action = MLX5_ACTION_NONE;
555			continue;
556		}
557
558		addr = l2addr_hash_add(hash, mac_list[i], struct vport_addr,
559				       GFP_KERNEL);
560		if (!addr) {
561			esw_warn(esw->dev,
562				 "Failed to add MAC(%pM) to vport[%d] DB\n",
563				 mac_list[i], vport_num);
564			continue;
565		}
566		addr->vport = vport_num;
567		addr->action = MLX5_ACTION_ADD;
568	}
569	kfree(mac_list);
570}
571
572static void esw_vport_change_handler(struct work_struct *work)
573{
574	struct mlx5_vport *vport =
575		container_of(work, struct mlx5_vport, vport_change_handler);
576	struct mlx5_core_dev *dev = vport->dev;
577	struct mlx5_eswitch *esw = dev->priv.eswitch;
578	u8 mac[ETH_ALEN];
579
580	mlx5_query_nic_vport_mac_address(dev, vport->vport, mac);
581	esw_debug(dev, "vport[%d] Context Changed: perm mac: %pM\n",
582		  vport->vport, mac);
583
584	if (vport->enabled_events & UC_ADDR_CHANGE) {
585		esw_update_vport_addr_list(esw, vport->vport,
586					   MLX5_NIC_VPORT_LIST_TYPE_UC);
587		esw_apply_vport_addr_list(esw, vport->vport,
588					  MLX5_NIC_VPORT_LIST_TYPE_UC);
589	}
590
591	if (vport->enabled_events & MC_ADDR_CHANGE) {
592		esw_update_vport_addr_list(esw, vport->vport,
593					   MLX5_NIC_VPORT_LIST_TYPE_MC);
594		esw_apply_vport_addr_list(esw, vport->vport,
595					  MLX5_NIC_VPORT_LIST_TYPE_MC);
596	}
597
598	esw_debug(esw->dev, "vport[%d] Context Changed: Done\n", vport->vport);
599	if (vport->enabled)
600		arm_vport_context_events_cmd(dev, vport->vport,
601					     vport->enabled_events);
602}
603
604static void esw_vport_enable_egress_acl(struct mlx5_eswitch *esw,
605					struct mlx5_vport *vport)
606{
607	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
608	struct mlx5_flow_group *vlan_grp = NULL;
609	struct mlx5_flow_group *drop_grp = NULL;
610	struct mlx5_core_dev *dev = esw->dev;
611	struct mlx5_flow_namespace *root_ns;
612	struct mlx5_flow_table *acl;
613	void *match_criteria;
614	char table_name[32];
615	u32 *flow_group_in;
616	int table_size = 2;
617	int err = 0;
618
619	if (!MLX5_CAP_ESW_EGRESS_ACL(dev, ft_support))
620		return;
621
622	esw_debug(dev, "Create vport[%d] egress ACL log_max_size(%d)\n",
623		  vport->vport, MLX5_CAP_ESW_EGRESS_ACL(dev, log_max_ft_size));
624
625	root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_EGRESS);
626	if (!root_ns) {
627		esw_warn(dev, "Failed to get E-Switch egress flow namespace\n");
628		return;
629	}
630
631	flow_group_in = mlx5_vzalloc(inlen);
632	if (!flow_group_in)
633		return;
634
635	snprintf(table_name, 32, "egress_%d", vport->vport);
636	acl = mlx5_create_vport_flow_table(root_ns, vport->vport, 0, table_name, table_size);
637	if (IS_ERR_OR_NULL(acl)) {
638		err = PTR_ERR(acl);
639		esw_warn(dev, "Failed to create E-Switch vport[%d] egress flow Table, err(%d)\n",
640			 vport->vport, err);
641		goto out;
642	}
643
644	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
645	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
646	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.cvlan_tag);
647	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.first_vid);
648	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
649	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 0);
650
651	vlan_grp = mlx5_create_flow_group(acl, flow_group_in);
652	if (IS_ERR_OR_NULL(vlan_grp)) {
653		err = PTR_ERR(vlan_grp);
654		esw_warn(dev, "Failed to create E-Switch vport[%d] egress allowed vlans flow group, err(%d)\n",
655			 vport->vport, err);
656		goto out;
657	}
658
659	memset(flow_group_in, 0, inlen);
660	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 1);
661	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 1);
662	drop_grp = mlx5_create_flow_group(acl, flow_group_in);
663	if (IS_ERR_OR_NULL(drop_grp)) {
664		err = PTR_ERR(drop_grp);
665		esw_warn(dev, "Failed to create E-Switch vport[%d] egress drop flow group, err(%d)\n",
666			 vport->vport, err);
667		goto out;
668	}
669
670	vport->egress.acl = acl;
671	vport->egress.drop_grp = drop_grp;
672	vport->egress.allowed_vlans_grp = vlan_grp;
673out:
674	kfree(flow_group_in);
675	if (err && !IS_ERR_OR_NULL(vlan_grp))
676		mlx5_destroy_flow_group(vlan_grp);
677	if (err && !IS_ERR_OR_NULL(acl))
678		mlx5_destroy_flow_table(acl);
679}
680
681static void esw_vport_cleanup_egress_rules(struct mlx5_eswitch *esw,
682					   struct mlx5_vport *vport)
683{
684	if (!IS_ERR_OR_NULL(vport->egress.allowed_vlan))
685		mlx5_del_flow_rule(vport->egress.allowed_vlan);
686
687	if (!IS_ERR_OR_NULL(vport->egress.drop_rule))
688		mlx5_del_flow_rule(vport->egress.drop_rule);
689
690	vport->egress.allowed_vlan = NULL;
691	vport->egress.drop_rule = NULL;
692}
693
694static void esw_vport_disable_egress_acl(struct mlx5_eswitch *esw,
695					 struct mlx5_vport *vport)
696{
697	if (IS_ERR_OR_NULL(vport->egress.acl))
698		return;
699
700	esw_debug(esw->dev, "Destroy vport[%d] E-Switch egress ACL\n", vport->vport);
701
702	esw_vport_cleanup_egress_rules(esw, vport);
703	mlx5_destroy_flow_group(vport->egress.allowed_vlans_grp);
704	mlx5_destroy_flow_group(vport->egress.drop_grp);
705	mlx5_destroy_flow_table(vport->egress.acl);
706	vport->egress.allowed_vlans_grp = NULL;
707	vport->egress.drop_grp = NULL;
708	vport->egress.acl = NULL;
709}
710
711static void esw_vport_enable_ingress_acl(struct mlx5_eswitch *esw,
712					 struct mlx5_vport *vport)
713{
714	int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in);
715	struct mlx5_core_dev *dev = esw->dev;
716	struct mlx5_flow_namespace *root_ns;
717	struct mlx5_flow_table *acl;
718	struct mlx5_flow_group *g;
719	void *match_criteria;
720	char table_name[32];
721	u32 *flow_group_in;
722	int table_size = 1;
723	int err = 0;
724
725	if (!MLX5_CAP_ESW_INGRESS_ACL(dev, ft_support))
726		return;
727
728	esw_debug(dev, "Create vport[%d] ingress ACL log_max_size(%d)\n",
729		  vport->vport, MLX5_CAP_ESW_INGRESS_ACL(dev, log_max_ft_size));
730
731	root_ns = mlx5_get_flow_namespace(dev, MLX5_FLOW_NAMESPACE_ESW_INGRESS);
732	if (!root_ns) {
733		esw_warn(dev, "Failed to get E-Switch ingress flow namespace\n");
734		return;
735	}
736
737	flow_group_in = mlx5_vzalloc(inlen);
738	if (!flow_group_in)
739		return;
740
741	snprintf(table_name, 32, "ingress_%d", vport->vport);
742	acl = mlx5_create_vport_flow_table(root_ns, vport->vport, 0, table_name, table_size);
743	if (IS_ERR_OR_NULL(acl)) {
744		err = PTR_ERR(acl);
745		esw_warn(dev, "Failed to create E-Switch vport[%d] ingress flow Table, err(%d)\n",
746			 vport->vport, err);
747		goto out;
748	}
749
750	MLX5_SET(create_flow_group_in, flow_group_in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS);
751	match_criteria = MLX5_ADDR_OF(create_flow_group_in, flow_group_in, match_criteria);
752	MLX5_SET_TO_ONES(fte_match_param, match_criteria, outer_headers.cvlan_tag);
753	MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, 0);
754	MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, 0);
755
756	g = mlx5_create_flow_group(acl, flow_group_in);
757	if (IS_ERR_OR_NULL(g)) {
758		err = PTR_ERR(g);
759		esw_warn(dev, "Failed to create E-Switch vport[%d] ingress flow group, err(%d)\n",
760			 vport->vport, err);
761		goto out;
762	}
763
764	vport->ingress.acl = acl;
765	vport->ingress.drop_grp = g;
766out:
767	kfree(flow_group_in);
768	if (err && !IS_ERR_OR_NULL(acl))
769		mlx5_destroy_flow_table(acl);
770}
771
772static void esw_vport_cleanup_ingress_rules(struct mlx5_eswitch *esw,
773					    struct mlx5_vport *vport)
774{
775	if (!IS_ERR_OR_NULL(vport->ingress.drop_rule))
776		mlx5_del_flow_rule(vport->ingress.drop_rule);
777	vport->ingress.drop_rule = NULL;
778}
779
780static void esw_vport_disable_ingress_acl(struct mlx5_eswitch *esw,
781					  struct mlx5_vport *vport)
782{
783	if (IS_ERR_OR_NULL(vport->ingress.acl))
784		return;
785
786	esw_debug(esw->dev, "Destroy vport[%d] E-Switch ingress ACL\n", vport->vport);
787
788	esw_vport_cleanup_ingress_rules(esw, vport);
789	mlx5_destroy_flow_group(vport->ingress.drop_grp);
790	mlx5_destroy_flow_table(vport->ingress.acl);
791	vport->ingress.acl = NULL;
792	vport->ingress.drop_grp = NULL;
793}
794
795static int esw_vport_ingress_config(struct mlx5_eswitch *esw,
796				    struct mlx5_vport *vport)
797{
798	struct mlx5_flow_destination dest;
799	u32 *match_v;
800	u32 *match_c;
801	int err = 0;
802
803	if (IS_ERR_OR_NULL(vport->ingress.acl)) {
804		esw_warn(esw->dev,
805			 "vport[%d] configure ingress rules failed, ingress acl is not initialized!\n",
806			 vport->vport);
807		return -EPERM;
808	}
809
810	esw_vport_cleanup_ingress_rules(esw, vport);
811
812	if (!vport->vlan && !vport->qos)
813		return 0;
814
815	esw_debug(esw->dev,
816		  "vport[%d] configure ingress rules, vlan(%d) qos(%d)\n",
817		  vport->vport, vport->vlan, vport->qos);
818
819	match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
820	match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
821	if (!match_v || !match_c) {
822		err = -ENOMEM;
823		esw_warn(esw->dev, "vport[%d] configure ingress rules failed, err(%d)\n",
824			 vport->vport, err);
825		goto out;
826	}
827	MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.cvlan_tag);
828	MLX5_SET_TO_ONES(fte_match_param, match_v, outer_headers.cvlan_tag);
829
830	dest.type = MLX5_FLOW_CONTEXT_DEST_TYPE_VPORT;
831	dest.vport_num = vport->vport;
832
833	vport->ingress.drop_rule =
834		mlx5_add_flow_rule(vport->ingress.acl,
835				   MLX5_MATCH_OUTER_HEADERS,
836				   match_c,
837				   match_v,
838				   MLX5_FLOW_CONTEXT_ACTION_DROP,
839				   0, &dest);
840	if (IS_ERR_OR_NULL(vport->ingress.drop_rule)) {
841		err = PTR_ERR(vport->ingress.drop_rule);
842		printf("mlx5_core: WARN: ""vport[%d] configure ingress rules, err(%d)\n", vport->vport, err);
843		vport->ingress.drop_rule = NULL;
844	}
845out:
846	kfree(match_v);
847	kfree(match_c);
848	return err;
849}
850
851static int esw_vport_egress_config(struct mlx5_eswitch *esw,
852				   struct mlx5_vport *vport)
853{
854	struct mlx5_flow_destination dest;
855	u32 *match_v;
856	u32 *match_c;
857	int err = 0;
858
859	if (IS_ERR_OR_NULL(vport->egress.acl)) {
860		esw_warn(esw->dev, "vport[%d] configure rgress rules failed, egress acl is not initialized!\n",
861			 vport->vport);
862		return -EPERM;
863	}
864
865	esw_vport_cleanup_egress_rules(esw, vport);
866
867	if (!vport->vlan && !vport->qos)
868		return 0;
869
870	esw_debug(esw->dev,
871		  "vport[%d] configure egress rules, vlan(%d) qos(%d)\n",
872		  vport->vport, vport->vlan, vport->qos);
873
874	match_v = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
875	match_c = kzalloc(MLX5_ST_SZ_BYTES(fte_match_param), GFP_KERNEL);
876	if (!match_v || !match_c) {
877		err = -ENOMEM;
878		esw_warn(esw->dev, "vport[%d] configure egress rules failed, err(%d)\n",
879			 vport->vport, err);
880		goto out;
881	}
882
883	/* Allowed vlan rule */
884	MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.cvlan_tag);
885	MLX5_SET_TO_ONES(fte_match_param, match_v, outer_headers.cvlan_tag);
886	MLX5_SET_TO_ONES(fte_match_param, match_c, outer_headers.first_vid);
887	MLX5_SET(fte_match_param, match_v, outer_headers.first_vid, vport->vlan);
888
889	dest.type = MLX5_FLOW_CONTEXT_DEST_TYPE_VPORT;
890	dest.vport_num = vport->vport;
891
892	vport->egress.allowed_vlan =
893		mlx5_add_flow_rule(vport->egress.acl,
894				   MLX5_MATCH_OUTER_HEADERS,
895				   match_c,
896				   match_v,
897				   MLX5_FLOW_CONTEXT_ACTION_ALLOW,
898				   0, &dest);
899	if (IS_ERR_OR_NULL(vport->egress.allowed_vlan)) {
900		err = PTR_ERR(vport->egress.allowed_vlan);
901		printf("mlx5_core: WARN: ""vport[%d] configure egress allowed vlan rule failed, err(%d)\n", vport->vport, err);
902		vport->egress.allowed_vlan = NULL;
903		goto out;
904	}
905
906	/* Drop others rule (star rule) */
907	memset(match_c, 0, MLX5_ST_SZ_BYTES(fte_match_param));
908	memset(match_v, 0, MLX5_ST_SZ_BYTES(fte_match_param));
909	vport->egress.drop_rule =
910		mlx5_add_flow_rule(vport->egress.acl,
911				   0,
912				   match_c,
913				   match_v,
914				   MLX5_FLOW_CONTEXT_ACTION_DROP,
915				   0, &dest);
916	if (IS_ERR_OR_NULL(vport->egress.drop_rule)) {
917		err = PTR_ERR(vport->egress.drop_rule);
918		printf("mlx5_core: WARN: ""vport[%d] configure egress drop rule failed, err(%d)\n", vport->vport, err);
919		vport->egress.drop_rule = NULL;
920	}
921out:
922	kfree(match_v);
923	kfree(match_c);
924	return err;
925}
926
927static void esw_enable_vport(struct mlx5_eswitch *esw, int vport_num,
928			     int enable_events)
929{
930	struct mlx5_vport *vport = &esw->vports[vport_num];
931	unsigned long flags;
932
933	mutex_lock(&vport->state_lock);
934	WARN_ON(vport->enabled);
935
936	esw_debug(esw->dev, "Enabling VPORT(%d)\n", vport_num);
937
938	if (vport_num) { /* Only VFs need ACLs for VST and spoofchk filtering */
939		esw_vport_enable_ingress_acl(esw, vport);
940		esw_vport_enable_egress_acl(esw, vport);
941		esw_vport_ingress_config(esw, vport);
942		esw_vport_egress_config(esw, vport);
943	}
944
945	mlx5_modify_vport_admin_state(esw->dev,
946				      MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
947				      vport_num,
948				      MLX5_ESW_VPORT_ADMIN_STATE_AUTO);
949
950	/* Sync with current vport context */
951	vport->enabled_events = enable_events;
952	esw_vport_change_handler(&vport->vport_change_handler);
953
954	spin_lock_irqsave(&vport->lock, flags);
955	vport->enabled = true;
956	spin_unlock_irqrestore(&vport->lock, flags);
957
958	arm_vport_context_events_cmd(esw->dev, vport_num, enable_events);
959
960	esw->enabled_vports++;
961	esw_debug(esw->dev, "Enabled VPORT(%d)\n", vport_num);
962	mutex_unlock(&vport->state_lock);
963}
964
965static void esw_cleanup_vport(struct mlx5_eswitch *esw, u16 vport_num)
966{
967	struct mlx5_vport *vport = &esw->vports[vport_num];
968	struct l2addr_node *node;
969	struct vport_addr *addr;
970	struct hlist_node *tmp;
971	int hi;
972
973	for_each_l2hash_node(node, tmp, vport->uc_list, hi) {
974		addr = container_of(node, struct vport_addr, node);
975		addr->action = MLX5_ACTION_DEL;
976	}
977	esw_apply_vport_addr_list(esw, vport_num, MLX5_NIC_VPORT_LIST_TYPE_UC);
978
979	for_each_l2hash_node(node, tmp, vport->mc_list, hi) {
980		addr = container_of(node, struct vport_addr, node);
981		addr->action = MLX5_ACTION_DEL;
982	}
983	esw_apply_vport_addr_list(esw, vport_num, MLX5_NIC_VPORT_LIST_TYPE_MC);
984}
985
986static void esw_disable_vport(struct mlx5_eswitch *esw, int vport_num)
987{
988	struct mlx5_vport *vport = &esw->vports[vport_num];
989	unsigned long flags;
990
991	mutex_lock(&vport->state_lock);
992	if (!vport->enabled) {
993		mutex_unlock(&vport->state_lock);
994		return;
995	}
996
997	esw_debug(esw->dev, "Disabling vport(%d)\n", vport_num);
998	/* Mark this vport as disabled to discard new events */
999	spin_lock_irqsave(&vport->lock, flags);
1000	vport->enabled = false;
1001	vport->enabled_events = 0;
1002	spin_unlock_irqrestore(&vport->lock, flags);
1003
1004	mlx5_modify_vport_admin_state(esw->dev,
1005				      MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
1006				      vport_num,
1007				      MLX5_ESW_VPORT_ADMIN_STATE_DOWN);
1008	/* Wait for current already scheduled events to complete */
1009	flush_workqueue(esw->work_queue);
1010	/* Disable events from this vport */
1011	arm_vport_context_events_cmd(esw->dev, vport->vport, 0);
1012	/* We don't assume VFs will cleanup after themselves */
1013	esw_cleanup_vport(esw, vport_num);
1014	if (vport_num) {
1015		esw_vport_disable_egress_acl(esw, vport);
1016		esw_vport_disable_ingress_acl(esw, vport);
1017	}
1018	esw->enabled_vports--;
1019	mutex_unlock(&vport->state_lock);
1020}
1021
1022/* Public E-Switch API */
1023int mlx5_eswitch_enable_sriov(struct mlx5_eswitch *esw, int nvfs)
1024{
1025	int err;
1026	int i;
1027
1028	if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
1029	    MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
1030		return 0;
1031
1032	if (!MLX5_CAP_GEN(esw->dev, eswitch_flow_table) ||
1033	    !MLX5_CAP_ESW_FLOWTABLE_FDB(esw->dev, ft_support)) {
1034		esw_warn(esw->dev, "E-Switch FDB is not supported, aborting ...\n");
1035		return -ENOTSUPP;
1036	}
1037
1038	if (!MLX5_CAP_ESW_INGRESS_ACL(esw->dev, ft_support))
1039		esw_warn(esw->dev, "E-Switch ingress ACL is not supported by FW\n");
1040
1041	if (!MLX5_CAP_ESW_EGRESS_ACL(esw->dev, ft_support))
1042		esw_warn(esw->dev, "E-Switch engress ACL is not supported by FW\n");
1043
1044	esw_info(esw->dev, "E-Switch enable SRIOV: nvfs(%d)\n", nvfs);
1045
1046	esw_disable_vport(esw, 0);
1047
1048	err = esw_create_fdb_table(esw);
1049	if (err)
1050		goto abort;
1051
1052	for (i = 0; i <= nvfs; i++)
1053		esw_enable_vport(esw, i, SRIOV_VPORT_EVENTS);
1054
1055	esw_info(esw->dev, "SRIOV enabled: active vports(%d)\n",
1056		 esw->enabled_vports);
1057	return 0;
1058
1059abort:
1060	esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
1061	return err;
1062}
1063
1064void mlx5_eswitch_disable_sriov(struct mlx5_eswitch *esw)
1065{
1066	int i;
1067
1068	if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
1069	    MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
1070		return;
1071
1072	esw_info(esw->dev, "disable SRIOV: active vports(%d)\n",
1073		 esw->enabled_vports);
1074
1075	for (i = 0; i < esw->total_vports; i++)
1076		esw_disable_vport(esw, i);
1077
1078	esw_destroy_fdb_table(esw);
1079
1080	/* VPORT 0 (PF) must be enabled back with non-sriov configuration */
1081	esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
1082}
1083
1084int mlx5_eswitch_init(struct mlx5_core_dev *dev, int total_vports)
1085{
1086	int l2_table_size = 1 << MLX5_CAP_GEN(dev, log_max_l2_table);
1087	struct mlx5_eswitch *esw;
1088	int vport_num;
1089	int err;
1090
1091	if (!MLX5_CAP_GEN(dev, vport_group_manager) ||
1092	    MLX5_CAP_GEN(dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
1093		return 0;
1094
1095	esw_info(dev,
1096		 "Total vports %d, l2 table size(%d), per vport: max uc(%d) max mc(%d)\n",
1097		 total_vports, l2_table_size,
1098		 MLX5_MAX_UC_PER_VPORT(dev),
1099		 MLX5_MAX_MC_PER_VPORT(dev));
1100
1101	esw = kzalloc(sizeof(*esw), GFP_KERNEL);
1102	if (!esw)
1103		return -ENOMEM;
1104
1105	esw->dev = dev;
1106
1107	esw->l2_table.bitmap = kcalloc(BITS_TO_LONGS(l2_table_size),
1108				   sizeof(uintptr_t), GFP_KERNEL);
1109	if (!esw->l2_table.bitmap) {
1110		err = -ENOMEM;
1111		goto abort;
1112	}
1113	esw->l2_table.size = l2_table_size;
1114
1115	esw->work_queue = create_singlethread_workqueue("mlx5_esw_wq");
1116	if (!esw->work_queue) {
1117		err = -ENOMEM;
1118		goto abort;
1119	}
1120
1121	esw->vports = kcalloc(total_vports, sizeof(struct mlx5_vport),
1122			      GFP_KERNEL);
1123	if (!esw->vports) {
1124		err = -ENOMEM;
1125		goto abort;
1126	}
1127
1128	for (vport_num = 0; vport_num < total_vports; vport_num++) {
1129		struct mlx5_vport *vport = &esw->vports[vport_num];
1130
1131		vport->vport = vport_num;
1132		vport->dev = dev;
1133		INIT_WORK(&vport->vport_change_handler,
1134			  esw_vport_change_handler);
1135		spin_lock_init(&vport->lock);
1136		mutex_init(&vport->state_lock);
1137	}
1138
1139	esw->total_vports = total_vports;
1140	esw->enabled_vports = 0;
1141
1142	dev->priv.eswitch = esw;
1143	esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
1144	/* VF Vports will be enabled when SRIOV is enabled */
1145	return 0;
1146abort:
1147	if (esw->work_queue)
1148		destroy_workqueue(esw->work_queue);
1149	kfree(esw->l2_table.bitmap);
1150	kfree(esw->vports);
1151	kfree(esw);
1152	return err;
1153}
1154
1155void mlx5_eswitch_cleanup(struct mlx5_eswitch *esw)
1156{
1157	if (!esw || !MLX5_CAP_GEN(esw->dev, vport_group_manager) ||
1158	    MLX5_CAP_GEN(esw->dev, port_type) != MLX5_CAP_PORT_TYPE_ETH)
1159		return;
1160
1161	esw_info(esw->dev, "cleanup\n");
1162	esw_disable_vport(esw, 0);
1163
1164	esw->dev->priv.eswitch = NULL;
1165	destroy_workqueue(esw->work_queue);
1166	kfree(esw->l2_table.bitmap);
1167	kfree(esw->vports);
1168	kfree(esw);
1169}
1170
1171void mlx5_eswitch_vport_event(struct mlx5_eswitch *esw, struct mlx5_eqe *eqe)
1172{
1173	struct mlx5_eqe_vport_change *vc_eqe = &eqe->data.vport_change;
1174	u16 vport_num = be16_to_cpu(vc_eqe->vport_num);
1175	struct mlx5_vport *vport;
1176
1177	if (!esw) {
1178		printf("mlx5_core: WARN: ""MLX5 E-Switch: vport %d got an event while eswitch is not initialized\n", vport_num);
1179		return;
1180	}
1181
1182	vport = &esw->vports[vport_num];
1183	spin_lock(&vport->lock);
1184	if (vport->enabled)
1185		queue_work(esw->work_queue, &vport->vport_change_handler);
1186	spin_unlock(&vport->lock);
1187}
1188
1189/* Vport Administration */
1190#define ESW_ALLOWED(esw) \
1191	(esw && MLX5_CAP_GEN(esw->dev, vport_group_manager) && mlx5_core_is_pf(esw->dev))
1192#define LEGAL_VPORT(esw, vport) (vport >= 0 && vport < esw->total_vports)
1193
1194static void node_guid_gen_from_mac(u64 *node_guid, u8 mac[ETH_ALEN])
1195{
1196	((u8 *)node_guid)[7] = mac[0];
1197	((u8 *)node_guid)[6] = mac[1];
1198	((u8 *)node_guid)[5] = mac[2];
1199	((u8 *)node_guid)[4] = 0xff;
1200	((u8 *)node_guid)[3] = 0xfe;
1201	((u8 *)node_guid)[2] = mac[3];
1202	((u8 *)node_guid)[1] = mac[4];
1203	((u8 *)node_guid)[0] = mac[5];
1204}
1205
1206int mlx5_eswitch_set_vport_mac(struct mlx5_eswitch *esw,
1207			       int vport, u8 mac[ETH_ALEN])
1208{
1209	int err = 0;
1210	u64 node_guid;
1211
1212	if (!ESW_ALLOWED(esw))
1213		return -EPERM;
1214	if (!LEGAL_VPORT(esw, vport))
1215		return -EINVAL;
1216
1217	err = mlx5_modify_nic_vport_mac_address(esw->dev, vport, mac);
1218	if (err) {
1219		mlx5_core_warn(esw->dev,
1220			       "Failed to mlx5_modify_nic_vport_mac vport(%d) err=(%d)\n",
1221			       vport, err);
1222		return err;
1223	}
1224
1225	node_guid_gen_from_mac(&node_guid, mac);
1226	err = mlx5_modify_nic_vport_node_guid(esw->dev, vport, node_guid);
1227	if (err) {
1228		mlx5_core_warn(esw->dev,
1229			       "Failed to mlx5_modify_nic_vport_node_guid vport(%d) err=(%d)\n",
1230			       vport, err);
1231		return err;
1232	}
1233
1234	return err;
1235}
1236
1237int mlx5_eswitch_set_vport_state(struct mlx5_eswitch *esw,
1238				 int vport, int link_state)
1239{
1240	if (!ESW_ALLOWED(esw))
1241		return -EPERM;
1242	if (!LEGAL_VPORT(esw, vport))
1243		return -EINVAL;
1244
1245	return mlx5_modify_vport_admin_state(esw->dev,
1246					     MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
1247					     vport, link_state);
1248}
1249
1250int mlx5_eswitch_get_vport_config(struct mlx5_eswitch *esw,
1251				  int vport, struct mlx5_esw_vport_info *ivi)
1252{
1253	u16 vlan;
1254	u8 qos;
1255
1256	if (!ESW_ALLOWED(esw))
1257		return -EPERM;
1258	if (!LEGAL_VPORT(esw, vport))
1259		return -EINVAL;
1260
1261	memset(ivi, 0, sizeof(*ivi));
1262	ivi->vf = vport - 1;
1263
1264	mlx5_query_nic_vport_mac_address(esw->dev, vport, ivi->mac);
1265	ivi->linkstate = mlx5_query_vport_admin_state(esw->dev,
1266						      MLX5_QUERY_VPORT_STATE_IN_OP_MOD_ESW_VPORT,
1267						      vport);
1268	query_esw_vport_cvlan(esw->dev, vport, &vlan, &qos);
1269	ivi->vlan = vlan;
1270	ivi->qos = qos;
1271	ivi->spoofchk = 0;
1272
1273	return 0;
1274}
1275
1276int mlx5_eswitch_set_vport_vlan(struct mlx5_eswitch *esw,
1277				int vport, u16 vlan, u8 qos)
1278{
1279	struct mlx5_vport *evport;
1280	int err = 0;
1281	int set = 0;
1282
1283	if (!ESW_ALLOWED(esw))
1284		return -EPERM;
1285	if (!LEGAL_VPORT(esw, vport) || (vlan > 4095) || (qos > 7))
1286		return -EINVAL;
1287
1288	if (vlan || qos)
1289		set = 1;
1290
1291	evport = &esw->vports[vport];
1292
1293	err = modify_esw_vport_cvlan(esw->dev, vport, vlan, qos, set);
1294	if (err)
1295		return err;
1296
1297	mutex_lock(&evport->state_lock);
1298	evport->vlan = vlan;
1299	evport->qos = qos;
1300	if (evport->enabled) {
1301		esw_vport_ingress_config(esw, evport);
1302		esw_vport_egress_config(esw, evport);
1303	}
1304	mutex_unlock(&evport->state_lock);
1305	return err;
1306}
1307
1308