]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/mlx5/mlx5_core/mlx5_eswitch.c
MFV 364467:
[FreeBSD/FreeBSD.git] / sys / dev / mlx5 / mlx5_core / mlx5_eswitch.c
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
50 enum {
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 */
57 struct 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 */
64 struct 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 */
71 struct vport_addr {
72         struct l2addr_node     node;
73         u8                     action;
74         u32                    vport;
75         struct mlx5_flow_rule *flow_rule; /* SRIOV only */
76 };
77
78 enum {
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
87 static 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 */
116 static 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
131 static 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);
165 out:
166         return err;
167 }
168
169 static 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
184 static 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 */
217 static struct mlx5_flow_rule *
218 esw_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         }
260 out:
261         kfree(match_v);
262         kfree(match_c);
263         return flow_rule;
264 }
265
266 static 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;
320 out:
321         kfree(flow_group_in);
322         if (err && !IS_ERR_OR_NULL(fdb))
323                 mlx5_destroy_flow_table(fdb);
324         return err;
325 }
326
327 static 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 */
340 typedef int (*vport_addr_action)(struct mlx5_eswitch *esw,
341                                  struct vport_addr *vaddr);
342
343 static 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;
374 abort:
375         l2addr_hash_del(esw_uc);
376         return err;
377 }
378
379 static 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
406 static 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);
426 add:
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
437 static 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 */
474 static 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 */
509 static 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
572 static 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
604 static 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;
673 out:
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
681 static 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
694 static 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
711 static 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;
766 out:
767         kfree(flow_group_in);
768         if (err && !IS_ERR_OR_NULL(acl))
769                 mlx5_destroy_flow_table(acl);
770 }
771
772 static 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
780 static 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
795 static 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         }
845 out:
846         kfree(match_v);
847         kfree(match_c);
848         return err;
849 }
850
851 static 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         }
921 out:
922         kfree(match_v);
923         kfree(match_c);
924         return err;
925 }
926
927 static 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
965 static 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
986 static 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 */
1023 int 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
1059 abort:
1060         esw_enable_vport(esw, 0, UC_ADDR_CHANGE);
1061         return err;
1062 }
1063
1064 void 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
1084 int 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;
1146 abort:
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
1155 void 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
1171 void 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
1194 static 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
1206 int 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
1237 int 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
1250 int 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
1276 int 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