blob: ea0f4a5787c38e5639b2fcd362051ecda8a1d906 [file] [log] [blame]
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001/*
2 * drivers/net/ethernet/mellanox/mlxsw/spectrum_switchdev.c
3 * Copyright (c) 2015 Mellanox Technologies. All rights reserved.
4 * Copyright (c) 2015 Jiri Pirko <jiri@mellanox.com>
5 * Copyright (c) 2015 Ido Schimmel <idosch@mellanox.com>
6 * Copyright (c) 2015 Elad Raz <eladr@mellanox.com>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the names of the copyright holders nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * Alternatively, this software may be distributed under the terms of the
21 * GNU General Public License ("GPL") version 2 as published by the Free
22 * Software Foundation.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 * POSSIBILITY OF SUCH DAMAGE.
35 */
36
37#include <linux/kernel.h>
38#include <linux/types.h>
39#include <linux/netdevice.h>
40#include <linux/etherdevice.h>
41#include <linux/slab.h>
42#include <linux/device.h>
43#include <linux/skbuff.h>
44#include <linux/if_vlan.h>
45#include <linux/if_bridge.h>
46#include <linux/workqueue.h>
47#include <linux/jiffies.h>
Ido Schimmel4f2c6ae2016-01-27 15:16:43 +010048#include <linux/rtnetlink.h>
Jiri Pirko56ade8f2015-10-16 14:01:37 +020049#include <net/switchdev.h>
50
51#include "spectrum.h"
52#include "core.h"
53#include "reg.h"
54
Ido Schimmel5f6935c2017-05-16 19:38:26 +020055struct mlxsw_sp_bridge {
56 struct mlxsw_sp *mlxsw_sp;
57 struct {
58 struct delayed_work dw;
59#define MLXSW_SP_DEFAULT_LEARNING_INTERVAL 100
60 unsigned int interval; /* ms */
61 } fdb_notify;
62#define MLXSW_SP_MIN_AGEING_TIME 10
63#define MLXSW_SP_MAX_AGEING_TIME 1000000
64#define MLXSW_SP_DEFAULT_AGEING_TIME 300
65 u32 ageing_time;
66 struct mlxsw_sp_upper master_bridge;
67 struct list_head mids_list;
68 DECLARE_BITMAP(mids_bitmap, MLXSW_SP_MID_MAX);
69};
70
71struct mlxsw_sp_upper *mlxsw_sp_master_bridge(const struct mlxsw_sp *mlxsw_sp)
72{
73 return &mlxsw_sp->bridge->master_bridge;
74}
75
Elad Raze4b6f692016-01-10 21:06:27 +010076static u16 mlxsw_sp_port_vid_to_fid_get(struct mlxsw_sp_port *mlxsw_sp_port,
77 u16 vid)
78{
Ido Schimmel56918b62016-06-20 23:04:18 +020079 struct mlxsw_sp_fid *f = mlxsw_sp_vport_fid_get(mlxsw_sp_port);
Elad Raze4b6f692016-01-10 21:06:27 +010080 u16 fid = vid;
81
Ido Schimmel56918b62016-06-20 23:04:18 +020082 fid = f ? f->fid : fid;
Elad Raze4b6f692016-01-10 21:06:27 +010083
84 if (!fid)
85 fid = mlxsw_sp_port->pvid;
86
87 return fid;
88}
89
Ido Schimmel54a73202015-12-15 16:03:41 +010090static struct mlxsw_sp_port *
91mlxsw_sp_port_orig_get(struct net_device *dev,
92 struct mlxsw_sp_port *mlxsw_sp_port)
93{
94 struct mlxsw_sp_port *mlxsw_sp_vport;
Nogah Frankel1e5d9432017-02-09 14:54:48 +010095 struct mlxsw_sp_fid *fid;
Ido Schimmel54a73202015-12-15 16:03:41 +010096 u16 vid;
97
Nogah Frankel1e5d9432017-02-09 14:54:48 +010098 if (netif_is_bridge_master(dev)) {
99 fid = mlxsw_sp_vfid_find(mlxsw_sp_port->mlxsw_sp,
100 dev);
101 if (fid) {
102 mlxsw_sp_vport =
103 mlxsw_sp_port_vport_find_by_fid(mlxsw_sp_port,
104 fid->fid);
105 WARN_ON(!mlxsw_sp_vport);
106 return mlxsw_sp_vport;
107 }
108 }
109
Ido Schimmel54a73202015-12-15 16:03:41 +0100110 if (!is_vlan_dev(dev))
111 return mlxsw_sp_port;
112
113 vid = vlan_dev_vlan_id(dev);
114 mlxsw_sp_vport = mlxsw_sp_port_vport_find(mlxsw_sp_port, vid);
115 WARN_ON(!mlxsw_sp_vport);
116
117 return mlxsw_sp_vport;
118}
119
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200120static int mlxsw_sp_port_attr_get(struct net_device *dev,
121 struct switchdev_attr *attr)
122{
123 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
124 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
125
Ido Schimmel54a73202015-12-15 16:03:41 +0100126 mlxsw_sp_port = mlxsw_sp_port_orig_get(attr->orig_dev, mlxsw_sp_port);
127 if (!mlxsw_sp_port)
128 return -EINVAL;
129
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200130 switch (attr->id) {
131 case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
132 attr->u.ppid.id_len = sizeof(mlxsw_sp->base_mac);
133 memcpy(&attr->u.ppid.id, &mlxsw_sp->base_mac,
134 attr->u.ppid.id_len);
135 break;
136 case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
137 attr->u.brport_flags =
138 (mlxsw_sp_port->learning ? BR_LEARNING : 0) |
Ido Schimmel02930382015-10-28 10:16:58 +0100139 (mlxsw_sp_port->learning_sync ? BR_LEARNING_SYNC : 0) |
140 (mlxsw_sp_port->uc_flood ? BR_FLOOD : 0);
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200141 break;
142 default:
143 return -EOPNOTSUPP;
144 }
145
146 return 0;
147}
148
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200149static int mlxsw_sp_port_attr_stp_state_set(struct mlxsw_sp_port *mlxsw_sp_port,
150 struct switchdev_trans *trans,
151 u8 state)
152{
Ido Schimmel45bfe6b2017-05-16 19:38:32 +0200153 u16 vid;
154 int err;
155
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200156 if (switchdev_trans_ph_prepare(trans))
157 return 0;
158
Ido Schimmel45bfe6b2017-05-16 19:38:32 +0200159 if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
160 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
161 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, state);
162 if (err)
163 return err;
164 mlxsw_sp_port->stp_state = state;
165 return 0;
166 }
167
168 for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
169 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, state);
170 if (err)
171 return err;
172 }
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200173 mlxsw_sp_port->stp_state = state;
Ido Schimmel45bfe6b2017-05-16 19:38:32 +0200174
175 return 0;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200176}
177
Nogah Frankeleaa7df32017-02-09 14:54:43 +0100178static int __mlxsw_sp_port_flood_table_set(struct mlxsw_sp_port *mlxsw_sp_port,
179 u16 idx_begin, u16 idx_end,
180 enum mlxsw_sp_flood_table table,
181 bool set)
Ido Schimmel02930382015-10-28 10:16:58 +0100182{
183 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel7f71eb42015-12-15 16:03:37 +0100184 u16 local_port = mlxsw_sp_port->local_port;
185 enum mlxsw_flood_table_type table_type;
Ido Schimmelc06a94e2015-12-15 16:03:38 +0100186 u16 range = idx_end - idx_begin + 1;
Ido Schimmel02930382015-10-28 10:16:58 +0100187 char *sftr_pl;
188 int err;
189
Ido Schimmel99724c12016-07-04 08:23:14 +0200190 if (mlxsw_sp_port_is_vport(mlxsw_sp_port))
Ido Schimmel7f71eb42015-12-15 16:03:37 +0100191 table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID;
Ido Schimmel99724c12016-07-04 08:23:14 +0200192 else
Ido Schimmel7f71eb42015-12-15 16:03:37 +0100193 table_type = MLXSW_REG_SFGC_TABLE_TYPE_FID_OFFEST;
Ido Schimmel7f71eb42015-12-15 16:03:37 +0100194
Ido Schimmel02930382015-10-28 10:16:58 +0100195 sftr_pl = kmalloc(MLXSW_REG_SFTR_LEN, GFP_KERNEL);
196 if (!sftr_pl)
197 return -ENOMEM;
198
Nogah Frankeleaa7df32017-02-09 14:54:43 +0100199 mlxsw_reg_sftr_pack(sftr_pl, table, idx_begin,
200 table_type, range, local_port, set);
Ido Schimmel02930382015-10-28 10:16:58 +0100201 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sftr), sftr_pl);
Ido Schimmel02930382015-10-28 10:16:58 +0100202
Nogah Frankeleaa7df32017-02-09 14:54:43 +0100203 kfree(sftr_pl);
204 return err;
205}
206
207static int __mlxsw_sp_port_flood_set(struct mlxsw_sp_port *mlxsw_sp_port,
208 u16 idx_begin, u16 idx_end, bool uc_set,
Nogah Frankel71c365b2017-02-09 14:54:46 +0100209 bool bc_set, bool mc_set)
Nogah Frankeleaa7df32017-02-09 14:54:43 +0100210{
211 int err;
212
213 err = __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, idx_begin, idx_end,
214 MLXSW_SP_FLOOD_TABLE_UC, uc_set);
215 if (err)
216 return err;
217
218 err = __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, idx_begin, idx_end,
Nogah Frankel71c365b2017-02-09 14:54:46 +0100219 MLXSW_SP_FLOOD_TABLE_BC, bc_set);
Ido Schimmel28892862016-05-06 22:18:40 +0200220 if (err)
221 goto err_flood_bm_set;
Ido Schimmelaad8b6b2016-09-01 10:37:45 +0200222
Nogah Frankel71c365b2017-02-09 14:54:46 +0100223 err = __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, idx_begin, idx_end,
224 MLXSW_SP_FLOOD_TABLE_MC, mc_set);
225 if (err)
226 goto err_flood_mc_set;
Nogah Frankeleaa7df32017-02-09 14:54:43 +0100227 return 0;
Ido Schimmel02930382015-10-28 10:16:58 +0100228
Nogah Frankel71c365b2017-02-09 14:54:46 +0100229err_flood_mc_set:
230 __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, idx_begin, idx_end,
231 MLXSW_SP_FLOOD_TABLE_BC, !bc_set);
Ido Schimmel28892862016-05-06 22:18:40 +0200232err_flood_bm_set:
Nogah Frankeleaa7df32017-02-09 14:54:43 +0100233 __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, idx_begin, idx_end,
234 MLXSW_SP_FLOOD_TABLE_UC, !uc_set);
Ido Schimmel02930382015-10-28 10:16:58 +0100235 return err;
236}
237
Nogah Frankel69be01f2017-02-09 14:54:44 +0100238static int mlxsw_sp_port_flood_table_set(struct mlxsw_sp_port *mlxsw_sp_port,
239 enum mlxsw_sp_flood_table table,
240 bool set)
Ido Schimmel02930382015-10-28 10:16:58 +0100241{
242 struct net_device *dev = mlxsw_sp_port->dev;
243 u16 vid, last_visited_vid;
244 int err;
245
Ido Schimmel54a73202015-12-15 16:03:41 +0100246 if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
Ido Schimmel41b996c2016-06-20 23:04:17 +0200247 u16 fid = mlxsw_sp_vport_fid_get(mlxsw_sp_port)->fid;
248 u16 vfid = mlxsw_sp_fid_to_vfid(fid);
Ido Schimmel54a73202015-12-15 16:03:41 +0100249
Nogah Frankeleaa7df32017-02-09 14:54:43 +0100250 return __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, vfid,
Nogah Frankel69be01f2017-02-09 14:54:44 +0100251 vfid, table, set);
Ido Schimmel54a73202015-12-15 16:03:41 +0100252 }
253
Ido Schimmel02930382015-10-28 10:16:58 +0100254 for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
Nogah Frankeleaa7df32017-02-09 14:54:43 +0100255 err = __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, vid, vid,
Nogah Frankel69be01f2017-02-09 14:54:44 +0100256 table, set);
Ido Schimmel02930382015-10-28 10:16:58 +0100257 if (err) {
258 last_visited_vid = vid;
259 goto err_port_flood_set;
260 }
261 }
262
263 return 0;
264
265err_port_flood_set:
266 for_each_set_bit(vid, mlxsw_sp_port->active_vlans, last_visited_vid)
Nogah Frankel69be01f2017-02-09 14:54:44 +0100267 __mlxsw_sp_port_flood_table_set(mlxsw_sp_port, vid, vid, table,
268 !set);
Ido Schimmel02930382015-10-28 10:16:58 +0100269 netdev_err(dev, "Failed to configure unicast flooding\n");
270 return err;
271}
272
Nogah Frankel90e0f0c2017-02-09 14:54:49 +0100273static int mlxsw_sp_port_mc_disabled_set(struct mlxsw_sp_port *mlxsw_sp_port,
274 struct switchdev_trans *trans,
275 bool mc_disabled)
276{
277 int set;
278 int err = 0;
279
280 if (switchdev_trans_ph_prepare(trans))
281 return 0;
282
283 if (mlxsw_sp_port->mc_router != mlxsw_sp_port->mc_flood) {
284 set = mc_disabled ?
285 mlxsw_sp_port->mc_flood : mlxsw_sp_port->mc_router;
286 err = mlxsw_sp_port_flood_table_set(mlxsw_sp_port,
287 MLXSW_SP_FLOOD_TABLE_MC,
288 set);
289 }
290
291 if (!err)
292 mlxsw_sp_port->mc_disabled = mc_disabled;
293
294 return err;
295}
296
Ido Schimmele6060022016-06-20 23:04:11 +0200297int mlxsw_sp_vport_flood_set(struct mlxsw_sp_port *mlxsw_sp_vport, u16 fid,
Ido Schimmel47a0a9e2016-06-20 23:04:08 +0200298 bool set)
Ido Schimmel7f71eb42015-12-15 16:03:37 +0100299{
Nogah Frankel8ecd4592017-02-09 14:54:47 +0100300 bool mc_set = set;
Ido Schimmele6060022016-06-20 23:04:11 +0200301 u16 vfid;
302
Ido Schimmel7f71eb42015-12-15 16:03:37 +0100303 /* In case of vFIDs, index into the flooding table is relative to
304 * the start of the vFIDs range.
305 */
Ido Schimmele6060022016-06-20 23:04:11 +0200306 vfid = mlxsw_sp_fid_to_vfid(fid);
Nogah Frankel8ecd4592017-02-09 14:54:47 +0100307
308 if (set)
309 mc_set = mlxsw_sp_vport->mc_disabled ?
310 mlxsw_sp_vport->mc_flood : mlxsw_sp_vport->mc_router;
311
Nogah Frankel71c365b2017-02-09 14:54:46 +0100312 return __mlxsw_sp_port_flood_set(mlxsw_sp_vport, vfid, vfid, set, set,
Nogah Frankel8ecd4592017-02-09 14:54:47 +0100313 mc_set);
Ido Schimmel7f71eb42015-12-15 16:03:37 +0100314}
315
Ido Schimmel89b548f2016-08-24 12:00:27 +0200316static int mlxsw_sp_port_learning_set(struct mlxsw_sp_port *mlxsw_sp_port,
317 bool set)
318{
319 u16 vid;
320 int err;
321
322 if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
323 vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
324
Ido Schimmel7cbc4272017-05-16 19:38:33 +0200325 return mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, set);
Ido Schimmel89b548f2016-08-24 12:00:27 +0200326 }
327
328 for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
Ido Schimmel7cbc4272017-05-16 19:38:33 +0200329 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, set);
Ido Schimmel89b548f2016-08-24 12:00:27 +0200330 if (err)
331 goto err_port_vid_learning_set;
332 }
333
334 return 0;
335
336err_port_vid_learning_set:
337 for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID)
Ido Schimmel7cbc4272017-05-16 19:38:33 +0200338 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, !set);
Ido Schimmel89b548f2016-08-24 12:00:27 +0200339 return err;
340}
341
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200342static int mlxsw_sp_port_attr_br_flags_set(struct mlxsw_sp_port *mlxsw_sp_port,
343 struct switchdev_trans *trans,
344 unsigned long brport_flags)
345{
Ido Schimmel89b548f2016-08-24 12:00:27 +0200346 unsigned long learning = mlxsw_sp_port->learning ? BR_LEARNING : 0;
Ido Schimmel02930382015-10-28 10:16:58 +0100347 unsigned long uc_flood = mlxsw_sp_port->uc_flood ? BR_FLOOD : 0;
Ido Schimmel02930382015-10-28 10:16:58 +0100348 int err;
349
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200350 if (switchdev_trans_ph_prepare(trans))
351 return 0;
352
Ido Schimmel02930382015-10-28 10:16:58 +0100353 if ((uc_flood ^ brport_flags) & BR_FLOOD) {
Nogah Frankel69be01f2017-02-09 14:54:44 +0100354 err = mlxsw_sp_port_flood_table_set(mlxsw_sp_port,
355 MLXSW_SP_FLOOD_TABLE_UC,
356 !mlxsw_sp_port->uc_flood);
Ido Schimmel02930382015-10-28 10:16:58 +0100357 if (err)
358 return err;
359 }
360
Ido Schimmel89b548f2016-08-24 12:00:27 +0200361 if ((learning ^ brport_flags) & BR_LEARNING) {
362 err = mlxsw_sp_port_learning_set(mlxsw_sp_port,
363 !mlxsw_sp_port->learning);
364 if (err)
365 goto err_port_learning_set;
366 }
367
Ido Schimmel02930382015-10-28 10:16:58 +0100368 mlxsw_sp_port->uc_flood = brport_flags & BR_FLOOD ? 1 : 0;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200369 mlxsw_sp_port->learning = brport_flags & BR_LEARNING ? 1 : 0;
370 mlxsw_sp_port->learning_sync = brport_flags & BR_LEARNING_SYNC ? 1 : 0;
Ido Schimmel02930382015-10-28 10:16:58 +0100371
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200372 return 0;
Ido Schimmel89b548f2016-08-24 12:00:27 +0200373
374err_port_learning_set:
375 if ((uc_flood ^ brport_flags) & BR_FLOOD)
Nogah Frankel69be01f2017-02-09 14:54:44 +0100376 mlxsw_sp_port_flood_table_set(mlxsw_sp_port,
377 MLXSW_SP_FLOOD_TABLE_UC,
378 mlxsw_sp_port->uc_flood);
Ido Schimmel89b548f2016-08-24 12:00:27 +0200379 return err;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200380}
381
382static int mlxsw_sp_ageing_set(struct mlxsw_sp *mlxsw_sp, u32 ageing_time)
383{
384 char sfdat_pl[MLXSW_REG_SFDAT_LEN];
385 int err;
386
387 mlxsw_reg_sfdat_pack(sfdat_pl, ageing_time);
388 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfdat), sfdat_pl);
389 if (err)
390 return err;
Ido Schimmel5f6935c2017-05-16 19:38:26 +0200391 mlxsw_sp->bridge->ageing_time = ageing_time;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200392 return 0;
393}
394
395static int mlxsw_sp_port_attr_br_ageing_set(struct mlxsw_sp_port *mlxsw_sp_port,
396 struct switchdev_trans *trans,
Jiri Pirko135f9ec2015-10-28 10:17:02 +0100397 unsigned long ageing_clock_t)
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200398{
399 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Jiri Pirko135f9ec2015-10-28 10:17:02 +0100400 unsigned long ageing_jiffies = clock_t_to_jiffies(ageing_clock_t);
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200401 u32 ageing_time = jiffies_to_msecs(ageing_jiffies) / 1000;
402
Ido Schimmel869f63a2016-03-08 12:59:33 -0800403 if (switchdev_trans_ph_prepare(trans)) {
404 if (ageing_time < MLXSW_SP_MIN_AGEING_TIME ||
405 ageing_time > MLXSW_SP_MAX_AGEING_TIME)
406 return -ERANGE;
407 else
408 return 0;
409 }
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200410
411 return mlxsw_sp_ageing_set(mlxsw_sp, ageing_time);
412}
413
Elad Raz26a4ea02016-01-06 13:01:10 +0100414static int mlxsw_sp_port_attr_br_vlan_set(struct mlxsw_sp_port *mlxsw_sp_port,
415 struct switchdev_trans *trans,
416 struct net_device *orig_dev,
417 bool vlan_enabled)
418{
419 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
420
421 /* SWITCHDEV_TRANS_PREPARE phase */
Ido Schimmel5f6935c2017-05-16 19:38:26 +0200422 if ((!vlan_enabled) &&
423 (mlxsw_sp->bridge->master_bridge.dev == orig_dev)) {
Elad Raz26a4ea02016-01-06 13:01:10 +0100424 netdev_err(mlxsw_sp_port->dev, "Bridge must be vlan-aware\n");
425 return -EINVAL;
426 }
427
428 return 0;
429}
430
Nogah Frankel8ecd4592017-02-09 14:54:47 +0100431static int mlxsw_sp_port_attr_mc_router_set(struct mlxsw_sp_port *mlxsw_sp_port,
432 struct switchdev_trans *trans,
433 bool is_port_mc_router)
434{
435 if (switchdev_trans_ph_prepare(trans))
436 return 0;
437
438 mlxsw_sp_port->mc_router = is_port_mc_router;
439 if (!mlxsw_sp_port->mc_disabled)
440 return mlxsw_sp_port_flood_table_set(mlxsw_sp_port,
441 MLXSW_SP_FLOOD_TABLE_MC,
442 is_port_mc_router);
443
444 return 0;
445}
446
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200447static int mlxsw_sp_port_attr_set(struct net_device *dev,
448 const struct switchdev_attr *attr,
449 struct switchdev_trans *trans)
450{
451 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
452 int err = 0;
453
Ido Schimmel54a73202015-12-15 16:03:41 +0100454 mlxsw_sp_port = mlxsw_sp_port_orig_get(attr->orig_dev, mlxsw_sp_port);
455 if (!mlxsw_sp_port)
456 return -EINVAL;
457
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200458 switch (attr->id) {
459 case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
460 err = mlxsw_sp_port_attr_stp_state_set(mlxsw_sp_port, trans,
461 attr->u.stp_state);
462 break;
463 case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
464 err = mlxsw_sp_port_attr_br_flags_set(mlxsw_sp_port, trans,
465 attr->u.brport_flags);
466 break;
467 case SWITCHDEV_ATTR_ID_BRIDGE_AGEING_TIME:
468 err = mlxsw_sp_port_attr_br_ageing_set(mlxsw_sp_port, trans,
469 attr->u.ageing_time);
470 break;
Elad Raz26a4ea02016-01-06 13:01:10 +0100471 case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
472 err = mlxsw_sp_port_attr_br_vlan_set(mlxsw_sp_port, trans,
473 attr->orig_dev,
474 attr->u.vlan_filtering);
475 break;
Nogah Frankel8ecd4592017-02-09 14:54:47 +0100476 case SWITCHDEV_ATTR_ID_PORT_MROUTER:
477 err = mlxsw_sp_port_attr_mc_router_set(mlxsw_sp_port, trans,
478 attr->u.mrouter);
479 break;
Nogah Frankel90e0f0c2017-02-09 14:54:49 +0100480 case SWITCHDEV_ATTR_ID_BRIDGE_MC_DISABLED:
481 err = mlxsw_sp_port_mc_disabled_set(mlxsw_sp_port, trans,
482 attr->u.mc_disabled);
483 break;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200484 default:
485 err = -EOPNOTSUPP;
486 break;
487 }
488
489 return err;
490}
491
Ido Schimmel14d39462016-06-20 23:04:15 +0200492static int mlxsw_sp_fid_op(struct mlxsw_sp *mlxsw_sp, u16 fid, bool create)
493{
494 char sfmr_pl[MLXSW_REG_SFMR_LEN];
495
496 mlxsw_reg_sfmr_pack(sfmr_pl, !create, fid, fid);
497 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfmr), sfmr_pl);
498}
499
500static int mlxsw_sp_fid_map(struct mlxsw_sp *mlxsw_sp, u16 fid, bool valid)
501{
502 enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_VID_TO_FID;
503 char svfa_pl[MLXSW_REG_SVFA_LEN];
504
505 mlxsw_reg_svfa_pack(svfa_pl, 0, mt, valid, fid, fid);
506 return mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(svfa), svfa_pl);
507}
508
509static struct mlxsw_sp_fid *mlxsw_sp_fid_alloc(u16 fid)
510{
511 struct mlxsw_sp_fid *f;
512
513 f = kzalloc(sizeof(*f), GFP_KERNEL);
514 if (!f)
515 return NULL;
516
517 f->fid = fid;
518
519 return f;
520}
521
Ido Schimmel701b1862016-07-04 08:23:16 +0200522struct mlxsw_sp_fid *mlxsw_sp_fid_create(struct mlxsw_sp *mlxsw_sp, u16 fid)
Ido Schimmel14d39462016-06-20 23:04:15 +0200523{
524 struct mlxsw_sp_fid *f;
525 int err;
526
527 err = mlxsw_sp_fid_op(mlxsw_sp, fid, true);
528 if (err)
529 return ERR_PTR(err);
530
531 /* Although all the ports member in the FID might be using a
532 * {Port, VID} to FID mapping, we create a global VID-to-FID
533 * mapping. This allows a port to transition to VLAN mode,
534 * knowing the global mapping exists.
535 */
536 err = mlxsw_sp_fid_map(mlxsw_sp, fid, true);
537 if (err)
538 goto err_fid_map;
539
540 f = mlxsw_sp_fid_alloc(fid);
541 if (!f) {
542 err = -ENOMEM;
543 goto err_allocate_fid;
544 }
545
546 list_add(&f->list, &mlxsw_sp->fids);
547
548 return f;
549
550err_allocate_fid:
551 mlxsw_sp_fid_map(mlxsw_sp, fid, false);
552err_fid_map:
553 mlxsw_sp_fid_op(mlxsw_sp, fid, false);
554 return ERR_PTR(err);
555}
556
Ido Schimmel701b1862016-07-04 08:23:16 +0200557void mlxsw_sp_fid_destroy(struct mlxsw_sp *mlxsw_sp, struct mlxsw_sp_fid *f)
Ido Schimmel14d39462016-06-20 23:04:15 +0200558{
559 u16 fid = f->fid;
560
561 list_del(&f->list);
562
Arkadi Sharshevskybf952332017-03-17 09:38:00 +0100563 if (f->rif)
564 mlxsw_sp_rif_bridge_destroy(mlxsw_sp, f->rif);
Ido Schimmel99f44bb2016-07-04 08:23:17 +0200565
Ido Schimmel14d39462016-06-20 23:04:15 +0200566 kfree(f);
567
Ido Schimmel81682872016-08-17 16:39:36 +0200568 mlxsw_sp_fid_map(mlxsw_sp, fid, false);
569
Ido Schimmel14d39462016-06-20 23:04:15 +0200570 mlxsw_sp_fid_op(mlxsw_sp, fid, false);
571}
572
573static int __mlxsw_sp_port_fid_join(struct mlxsw_sp_port *mlxsw_sp_port,
574 u16 fid)
575{
576 struct mlxsw_sp_fid *f;
577
Ido Schimmelf1de7a22016-09-01 10:37:44 +0200578 if (test_bit(fid, mlxsw_sp_port->active_vlans))
579 return 0;
580
Ido Schimmel14d39462016-06-20 23:04:15 +0200581 f = mlxsw_sp_fid_find(mlxsw_sp_port->mlxsw_sp, fid);
582 if (!f) {
583 f = mlxsw_sp_fid_create(mlxsw_sp_port->mlxsw_sp, fid);
584 if (IS_ERR(f))
585 return PTR_ERR(f);
586 }
587
588 f->ref_count++;
589
Ido Schimmel22305372016-06-20 23:04:21 +0200590 netdev_dbg(mlxsw_sp_port->dev, "Joined FID=%d\n", fid);
591
Ido Schimmel14d39462016-06-20 23:04:15 +0200592 return 0;
593}
594
595static void __mlxsw_sp_port_fid_leave(struct mlxsw_sp_port *mlxsw_sp_port,
596 u16 fid)
597{
598 struct mlxsw_sp_fid *f;
599
600 f = mlxsw_sp_fid_find(mlxsw_sp_port->mlxsw_sp, fid);
601 if (WARN_ON(!f))
602 return;
603
Ido Schimmel22305372016-06-20 23:04:21 +0200604 netdev_dbg(mlxsw_sp_port->dev, "Left FID=%d\n", fid);
605
Ido Schimmelfe3f6d12016-06-20 23:04:19 +0200606 mlxsw_sp_port_fdb_flush(mlxsw_sp_port, fid);
607
Ido Schimmel14d39462016-06-20 23:04:15 +0200608 if (--f->ref_count == 0)
609 mlxsw_sp_fid_destroy(mlxsw_sp_port->mlxsw_sp, f);
610}
611
612static int mlxsw_sp_port_fid_map(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid,
613 bool valid)
614{
615 enum mlxsw_reg_svfa_mt mt = MLXSW_REG_SVFA_MT_PORT_VID_TO_FID;
616
617 /* If port doesn't have vPorts, then it can use the global
618 * VID-to-FID mapping.
619 */
620 if (list_empty(&mlxsw_sp_port->vports_list))
621 return 0;
622
623 return mlxsw_sp_port_vid_to_fid_set(mlxsw_sp_port, mt, valid, fid, fid);
624}
625
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200626static int mlxsw_sp_port_fid_join(struct mlxsw_sp_port *mlxsw_sp_port, u16 fid)
Ido Schimmel14d39462016-06-20 23:04:15 +0200627{
Nogah Frankel8ecd4592017-02-09 14:54:47 +0100628 bool mc_flood;
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200629 int err;
Ido Schimmel14d39462016-06-20 23:04:15 +0200630
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200631 err = __mlxsw_sp_port_fid_join(mlxsw_sp_port, fid);
632 if (err)
633 return err;
Ido Schimmel14d39462016-06-20 23:04:15 +0200634
Nogah Frankel8ecd4592017-02-09 14:54:47 +0100635 mc_flood = mlxsw_sp_port->mc_disabled ?
636 mlxsw_sp_port->mc_flood : mlxsw_sp_port->mc_router;
637
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200638 err = __mlxsw_sp_port_flood_set(mlxsw_sp_port, fid, fid,
Nogah Frankel71c365b2017-02-09 14:54:46 +0100639 mlxsw_sp_port->uc_flood, true,
Nogah Frankel8ecd4592017-02-09 14:54:47 +0100640 mc_flood);
Ido Schimmel14d39462016-06-20 23:04:15 +0200641 if (err)
642 goto err_port_flood_set;
643
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200644 err = mlxsw_sp_port_fid_map(mlxsw_sp_port, fid, true);
645 if (err)
646 goto err_port_fid_map;
Ido Schimmel14d39462016-06-20 23:04:15 +0200647
648 return 0;
649
650err_port_fid_map:
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200651 __mlxsw_sp_port_flood_set(mlxsw_sp_port, fid, fid, false, false, false);
Ido Schimmel14d39462016-06-20 23:04:15 +0200652err_port_flood_set:
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200653 __mlxsw_sp_port_fid_leave(mlxsw_sp_port, fid);
Ido Schimmel14d39462016-06-20 23:04:15 +0200654 return err;
655}
656
657static void mlxsw_sp_port_fid_leave(struct mlxsw_sp_port *mlxsw_sp_port,
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200658 u16 fid)
Ido Schimmel14d39462016-06-20 23:04:15 +0200659{
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200660 mlxsw_sp_port_fid_map(mlxsw_sp_port, fid, false);
661 __mlxsw_sp_port_flood_set(mlxsw_sp_port, fid, fid, false,
Nogah Frankel71c365b2017-02-09 14:54:46 +0100662 false, false);
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200663 __mlxsw_sp_port_fid_leave(mlxsw_sp_port, fid);
Ido Schimmel14d39462016-06-20 23:04:15 +0200664}
665
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200666static u16
667mlxsw_sp_port_pvid_determine(const struct mlxsw_sp_port *mlxsw_sp_port,
668 u16 vid, bool is_pvid)
Ido Schimmel584d73d2016-08-24 12:00:26 +0200669{
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200670 if (is_pvid)
671 return vid;
672 else if (mlxsw_sp_port->pvid == vid)
673 return 0; /* Dis-allow untagged packets */
674 else
675 return mlxsw_sp_port->pvid;
Ido Schimmel584d73d2016-08-24 12:00:26 +0200676}
677
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200678static int mlxsw_sp_port_vlan_add(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid,
679 bool is_untagged, bool is_pvid)
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200680{
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200681 u16 pvid = mlxsw_sp_port_pvid_determine(mlxsw_sp_port, vid, is_pvid);
682 u16 old_pvid = mlxsw_sp_port->pvid;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200683 int err;
684
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200685 err = mlxsw_sp_port_fid_join(mlxsw_sp_port, vid);
686 if (err)
Ido Schimmel14d39462016-06-20 23:04:15 +0200687 return err;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200688
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200689 err = mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, true,
690 is_untagged);
691 if (err)
692 goto err_port_vlan_set;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200693
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200694 err = mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid);
695 if (err)
696 goto err_port_pvid_set;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200697
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200698 err = mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid,
Ido Schimmel584d73d2016-08-24 12:00:26 +0200699 mlxsw_sp_port->learning);
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200700 if (err)
Ido Schimmel584d73d2016-08-24 12:00:26 +0200701 goto err_port_vid_learning_set;
Ido Schimmel584d73d2016-08-24 12:00:26 +0200702
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200703 err = mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid,
704 mlxsw_sp_port->stp_state);
705 if (err)
706 goto err_port_vid_stp_set;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200707
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200708 if (is_untagged)
709 __set_bit(vid, mlxsw_sp_port->untagged_vlans);
710 else
711 __clear_bit(vid, mlxsw_sp_port->untagged_vlans);
712 __set_bit(vid, mlxsw_sp_port->active_vlans);
Ido Schimmelb07a9662015-11-19 12:27:40 +0100713
714 return 0;
715
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200716err_port_vid_stp_set:
717 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
Ido Schimmel584d73d2016-08-24 12:00:26 +0200718err_port_vid_learning_set:
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200719 mlxsw_sp_port_pvid_set(mlxsw_sp_port, old_pvid);
Ido Schimmelb07a9662015-11-19 12:27:40 +0100720err_port_pvid_set:
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200721 mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
722err_port_vlan_set:
723 mlxsw_sp_port_fid_leave(mlxsw_sp_port, vid);
Ido Schimmelb07a9662015-11-19 12:27:40 +0100724 return err;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200725}
726
727static int mlxsw_sp_port_vlans_add(struct mlxsw_sp_port *mlxsw_sp_port,
728 const struct switchdev_obj_port_vlan *vlan,
729 struct switchdev_trans *trans)
730{
Elad Raze4a13052016-01-06 13:01:09 +0100731 bool flag_untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
732 bool flag_pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200733 u16 vid;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200734
735 if (switchdev_trans_ph_prepare(trans))
736 return 0;
737
Ido Schimmelfe9ccc72017-05-16 19:38:31 +0200738 for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
739 int err;
740
741 err = mlxsw_sp_port_vlan_add(mlxsw_sp_port, vid, flag_untagged,
742 flag_pvid);
743 if (err)
744 return err;
745 }
746
747 return 0;
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200748}
749
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +0100750static enum mlxsw_reg_sfd_rec_policy mlxsw_sp_sfd_rec_policy(bool dynamic)
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200751{
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +0100752 return dynamic ? MLXSW_REG_SFD_REC_POLICY_DYNAMIC_ENTRY_INGRESS :
753 MLXSW_REG_SFD_REC_POLICY_STATIC_ENTRY;
754}
755
756static enum mlxsw_reg_sfd_op mlxsw_sp_sfd_op(bool adding)
757{
758 return adding ? MLXSW_REG_SFD_OP_WRITE_EDIT :
759 MLXSW_REG_SFD_OP_WRITE_REMOVE;
760}
761
Ido Schimmel6e095fd2016-07-04 08:23:13 +0200762static int __mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port,
763 const char *mac, u16 fid, bool adding,
764 enum mlxsw_reg_sfd_rec_action action,
765 bool dynamic)
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +0100766{
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200767 char *sfd_pl;
768 int err;
769
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200770 sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
771 if (!sfd_pl)
772 return -ENOMEM;
773
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +0100774 mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
775 mlxsw_reg_sfd_uc_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
Ido Schimmel6e095fd2016-07-04 08:23:13 +0200776 mac, fid, action, local_port);
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +0100777 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
778 kfree(sfd_pl);
779
780 return err;
781}
782
Ido Schimmel6e095fd2016-07-04 08:23:13 +0200783static int mlxsw_sp_port_fdb_uc_op(struct mlxsw_sp *mlxsw_sp, u8 local_port,
784 const char *mac, u16 fid, bool adding,
785 bool dynamic)
786{
787 return __mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid, adding,
788 MLXSW_REG_SFD_REC_ACTION_NOP, dynamic);
789}
790
791int mlxsw_sp_rif_fdb_op(struct mlxsw_sp *mlxsw_sp, const char *mac, u16 fid,
792 bool adding)
793{
794 return __mlxsw_sp_port_fdb_uc_op(mlxsw_sp, 0, mac, fid, adding,
795 MLXSW_REG_SFD_REC_ACTION_FORWARD_IP_ROUTER,
796 false);
797}
798
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +0100799static int mlxsw_sp_port_fdb_uc_lag_op(struct mlxsw_sp *mlxsw_sp, u16 lag_id,
Ido Schimmel64771e32015-12-15 16:03:46 +0100800 const char *mac, u16 fid, u16 lag_vid,
801 bool adding, bool dynamic)
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +0100802{
803 char *sfd_pl;
804 int err;
805
806 sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
807 if (!sfd_pl)
808 return -ENOMEM;
809
810 mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
811 mlxsw_reg_sfd_uc_lag_pack(sfd_pl, 0, mlxsw_sp_sfd_rec_policy(dynamic),
Ido Schimmel64771e32015-12-15 16:03:46 +0100812 mac, fid, MLXSW_REG_SFD_REC_ACTION_NOP,
813 lag_vid, lag_id);
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +0100814 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200815 kfree(sfd_pl);
816
817 return err;
818}
819
820static int
821mlxsw_sp_port_fdb_static_add(struct mlxsw_sp_port *mlxsw_sp_port,
822 const struct switchdev_obj_port_fdb *fdb,
823 struct switchdev_trans *trans)
824{
Elad Raze4b6f692016-01-10 21:06:27 +0100825 u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, fdb->vid);
Ido Schimmel64771e32015-12-15 16:03:46 +0100826 u16 lag_vid = 0;
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +0100827
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200828 if (switchdev_trans_ph_prepare(trans))
829 return 0;
830
Ido Schimmel54a73202015-12-15 16:03:41 +0100831 if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
Ido Schimmel64771e32015-12-15 16:03:46 +0100832 lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
Ido Schimmel54a73202015-12-15 16:03:41 +0100833 }
834
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +0100835 if (!mlxsw_sp_port->lagged)
Jiri Pirko2fa9d452016-01-07 11:50:29 +0100836 return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port->mlxsw_sp,
837 mlxsw_sp_port->local_port,
Ido Schimmel9de6a802015-12-15 16:03:40 +0100838 fdb->addr, fid, true, false);
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +0100839 else
840 return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp,
841 mlxsw_sp_port->lag_id,
Ido Schimmel64771e32015-12-15 16:03:46 +0100842 fdb->addr, fid, lag_vid,
843 true, false);
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200844}
845
Elad Raz3a49b4f2016-01-10 21:06:28 +0100846static int mlxsw_sp_port_mdb_op(struct mlxsw_sp *mlxsw_sp, const char *addr,
847 u16 fid, u16 mid, bool adding)
848{
849 char *sfd_pl;
850 int err;
851
852 sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
853 if (!sfd_pl)
854 return -ENOMEM;
855
856 mlxsw_reg_sfd_pack(sfd_pl, mlxsw_sp_sfd_op(adding), 0);
857 mlxsw_reg_sfd_mc_pack(sfd_pl, 0, addr, fid,
858 MLXSW_REG_SFD_REC_ACTION_NOP, mid);
859 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
860 kfree(sfd_pl);
861 return err;
862}
863
864static int mlxsw_sp_port_smid_set(struct mlxsw_sp_port *mlxsw_sp_port, u16 mid,
865 bool add, bool clear_all_ports)
866{
867 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
868 char *smid_pl;
869 int err, i;
870
871 smid_pl = kmalloc(MLXSW_REG_SMID_LEN, GFP_KERNEL);
872 if (!smid_pl)
873 return -ENOMEM;
874
875 mlxsw_reg_smid_pack(smid_pl, mid, mlxsw_sp_port->local_port, add);
876 if (clear_all_ports) {
Ido Schimmel5ec2ee72017-03-24 08:02:48 +0100877 for (i = 1; i < mlxsw_core_max_ports(mlxsw_sp->core); i++)
Elad Raz3a49b4f2016-01-10 21:06:28 +0100878 if (mlxsw_sp->ports[i])
879 mlxsw_reg_smid_port_mask_set(smid_pl, i, 1);
880 }
881 err = mlxsw_reg_write(mlxsw_sp->core, MLXSW_REG(smid), smid_pl);
882 kfree(smid_pl);
883 return err;
884}
885
886static struct mlxsw_sp_mid *__mlxsw_sp_mc_get(struct mlxsw_sp *mlxsw_sp,
887 const unsigned char *addr,
Ido Schimmel46d08472016-10-30 10:09:22 +0100888 u16 fid)
Elad Raz3a49b4f2016-01-10 21:06:28 +0100889{
890 struct mlxsw_sp_mid *mid;
891
Ido Schimmel5f6935c2017-05-16 19:38:26 +0200892 list_for_each_entry(mid, &mlxsw_sp->bridge->mids_list, list) {
Ido Schimmel46d08472016-10-30 10:09:22 +0100893 if (ether_addr_equal(mid->addr, addr) && mid->fid == fid)
Elad Raz3a49b4f2016-01-10 21:06:28 +0100894 return mid;
895 }
896 return NULL;
897}
898
899static struct mlxsw_sp_mid *__mlxsw_sp_mc_alloc(struct mlxsw_sp *mlxsw_sp,
900 const unsigned char *addr,
Ido Schimmel46d08472016-10-30 10:09:22 +0100901 u16 fid)
Elad Raz3a49b4f2016-01-10 21:06:28 +0100902{
903 struct mlxsw_sp_mid *mid;
904 u16 mid_idx;
905
Ido Schimmel5f6935c2017-05-16 19:38:26 +0200906 mid_idx = find_first_zero_bit(mlxsw_sp->bridge->mids_bitmap,
Elad Raz3a49b4f2016-01-10 21:06:28 +0100907 MLXSW_SP_MID_MAX);
908 if (mid_idx == MLXSW_SP_MID_MAX)
909 return NULL;
910
911 mid = kzalloc(sizeof(*mid), GFP_KERNEL);
912 if (!mid)
913 return NULL;
914
Ido Schimmel5f6935c2017-05-16 19:38:26 +0200915 set_bit(mid_idx, mlxsw_sp->bridge->mids_bitmap);
Elad Raz3a49b4f2016-01-10 21:06:28 +0100916 ether_addr_copy(mid->addr, addr);
Ido Schimmel46d08472016-10-30 10:09:22 +0100917 mid->fid = fid;
Elad Raz3a49b4f2016-01-10 21:06:28 +0100918 mid->mid = mid_idx;
919 mid->ref_count = 0;
Ido Schimmel5f6935c2017-05-16 19:38:26 +0200920 list_add_tail(&mid->list, &mlxsw_sp->bridge->mids_list);
Elad Raz3a49b4f2016-01-10 21:06:28 +0100921
922 return mid;
923}
924
925static int __mlxsw_sp_mc_dec_ref(struct mlxsw_sp *mlxsw_sp,
926 struct mlxsw_sp_mid *mid)
927{
928 if (--mid->ref_count == 0) {
929 list_del(&mid->list);
Ido Schimmel5f6935c2017-05-16 19:38:26 +0200930 clear_bit(mid->mid, mlxsw_sp->bridge->mids_bitmap);
Elad Raz3a49b4f2016-01-10 21:06:28 +0100931 kfree(mid);
932 return 1;
933 }
934 return 0;
935}
936
937static int mlxsw_sp_port_mdb_add(struct mlxsw_sp_port *mlxsw_sp_port,
938 const struct switchdev_obj_port_mdb *mdb,
939 struct switchdev_trans *trans)
940{
941 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
942 struct net_device *dev = mlxsw_sp_port->dev;
943 struct mlxsw_sp_mid *mid;
944 u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, mdb->vid);
945 int err = 0;
946
947 if (switchdev_trans_ph_prepare(trans))
948 return 0;
949
Ido Schimmel46d08472016-10-30 10:09:22 +0100950 mid = __mlxsw_sp_mc_get(mlxsw_sp, mdb->addr, fid);
Elad Raz3a49b4f2016-01-10 21:06:28 +0100951 if (!mid) {
Ido Schimmel46d08472016-10-30 10:09:22 +0100952 mid = __mlxsw_sp_mc_alloc(mlxsw_sp, mdb->addr, fid);
Elad Raz3a49b4f2016-01-10 21:06:28 +0100953 if (!mid) {
954 netdev_err(dev, "Unable to allocate MC group\n");
955 return -ENOMEM;
956 }
957 }
958 mid->ref_count++;
959
960 err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, true,
961 mid->ref_count == 1);
962 if (err) {
963 netdev_err(dev, "Unable to set SMID\n");
964 goto err_out;
965 }
966
967 if (mid->ref_count == 1) {
968 err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb->addr, fid, mid->mid,
969 true);
970 if (err) {
971 netdev_err(dev, "Unable to set MC SFD\n");
972 goto err_out;
973 }
974 }
975
976 return 0;
977
978err_out:
979 __mlxsw_sp_mc_dec_ref(mlxsw_sp, mid);
980 return err;
981}
982
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200983static int mlxsw_sp_port_obj_add(struct net_device *dev,
984 const struct switchdev_obj *obj,
985 struct switchdev_trans *trans)
986{
987 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
988 int err = 0;
989
Ido Schimmel54a73202015-12-15 16:03:41 +0100990 mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
991 if (!mlxsw_sp_port)
992 return -EINVAL;
993
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200994 switch (obj->id) {
995 case SWITCHDEV_OBJ_ID_PORT_VLAN:
Ido Schimmel54a73202015-12-15 16:03:41 +0100996 if (mlxsw_sp_port_is_vport(mlxsw_sp_port))
997 return 0;
998
Jiri Pirko56ade8f2015-10-16 14:01:37 +0200999 err = mlxsw_sp_port_vlans_add(mlxsw_sp_port,
1000 SWITCHDEV_OBJ_PORT_VLAN(obj),
1001 trans);
1002 break;
1003 case SWITCHDEV_OBJ_ID_PORT_FDB:
1004 err = mlxsw_sp_port_fdb_static_add(mlxsw_sp_port,
1005 SWITCHDEV_OBJ_PORT_FDB(obj),
1006 trans);
1007 break;
Elad Raz3a49b4f2016-01-10 21:06:28 +01001008 case SWITCHDEV_OBJ_ID_PORT_MDB:
1009 err = mlxsw_sp_port_mdb_add(mlxsw_sp_port,
1010 SWITCHDEV_OBJ_PORT_MDB(obj),
1011 trans);
1012 break;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001013 default:
1014 err = -EOPNOTSUPP;
1015 break;
1016 }
1017
1018 return err;
1019}
1020
Ido Schimmelfe9ccc72017-05-16 19:38:31 +02001021static void mlxsw_sp_port_vlan_del(struct mlxsw_sp_port *mlxsw_sp_port, u16 vid)
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001022{
Ido Schimmelfe9ccc72017-05-16 19:38:31 +02001023 u16 pvid = mlxsw_sp_port->pvid == vid ? 0 : vid;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001024
Ido Schimmelfe9ccc72017-05-16 19:38:31 +02001025 __clear_bit(vid, mlxsw_sp_port->active_vlans);
1026 mlxsw_sp_port_vid_stp_set(mlxsw_sp_port, vid, BR_STATE_DISABLED);
1027 mlxsw_sp_port_vid_learning_set(mlxsw_sp_port, vid, false);
1028 mlxsw_sp_port_pvid_set(mlxsw_sp_port, pvid);
1029 mlxsw_sp_port_vlan_set(mlxsw_sp_port, vid, vid, false, false);
1030 mlxsw_sp_port_fid_leave(mlxsw_sp_port, vid);
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001031}
1032
1033static int mlxsw_sp_port_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port,
1034 const struct switchdev_obj_port_vlan *vlan)
1035{
Ido Schimmelfe9ccc72017-05-16 19:38:31 +02001036 u16 vid;
1037
1038 for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
1039 mlxsw_sp_port_vlan_del(mlxsw_sp_port, vid);
1040
1041 return 0;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001042}
1043
Ido Schimmel4dc236c2016-01-27 15:20:16 +01001044void mlxsw_sp_port_active_vlans_del(struct mlxsw_sp_port *mlxsw_sp_port)
1045{
1046 u16 vid;
1047
1048 for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID)
Ido Schimmelfe9ccc72017-05-16 19:38:31 +02001049 mlxsw_sp_port_vlan_del(mlxsw_sp_port, vid);
Ido Schimmel4dc236c2016-01-27 15:20:16 +01001050}
1051
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001052static int
1053mlxsw_sp_port_fdb_static_del(struct mlxsw_sp_port *mlxsw_sp_port,
1054 const struct switchdev_obj_port_fdb *fdb)
1055{
Elad Raze4b6f692016-01-10 21:06:27 +01001056 u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, fdb->vid);
Ido Schimmel64771e32015-12-15 16:03:46 +01001057 u16 lag_vid = 0;
Ido Schimmel9de6a802015-12-15 16:03:40 +01001058
Ido Schimmel54a73202015-12-15 16:03:41 +01001059 if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
Ido Schimmel64771e32015-12-15 16:03:46 +01001060 lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
Ido Schimmel54a73202015-12-15 16:03:41 +01001061 }
1062
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001063 if (!mlxsw_sp_port->lagged)
Jiri Pirko2fa9d452016-01-07 11:50:29 +01001064 return mlxsw_sp_port_fdb_uc_op(mlxsw_sp_port->mlxsw_sp,
1065 mlxsw_sp_port->local_port,
Ido Schimmel9de6a802015-12-15 16:03:40 +01001066 fdb->addr, fid,
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001067 false, false);
1068 else
1069 return mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp_port->mlxsw_sp,
1070 mlxsw_sp_port->lag_id,
Ido Schimmel64771e32015-12-15 16:03:46 +01001071 fdb->addr, fid, lag_vid,
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001072 false, false);
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001073}
1074
Elad Raz3a49b4f2016-01-10 21:06:28 +01001075static int mlxsw_sp_port_mdb_del(struct mlxsw_sp_port *mlxsw_sp_port,
1076 const struct switchdev_obj_port_mdb *mdb)
1077{
1078 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
1079 struct net_device *dev = mlxsw_sp_port->dev;
1080 struct mlxsw_sp_mid *mid;
1081 u16 fid = mlxsw_sp_port_vid_to_fid_get(mlxsw_sp_port, mdb->vid);
1082 u16 mid_idx;
1083 int err = 0;
1084
Ido Schimmel46d08472016-10-30 10:09:22 +01001085 mid = __mlxsw_sp_mc_get(mlxsw_sp, mdb->addr, fid);
Elad Raz3a49b4f2016-01-10 21:06:28 +01001086 if (!mid) {
1087 netdev_err(dev, "Unable to remove port from MC DB\n");
1088 return -EINVAL;
1089 }
1090
1091 err = mlxsw_sp_port_smid_set(mlxsw_sp_port, mid->mid, false, false);
1092 if (err)
1093 netdev_err(dev, "Unable to remove port from SMID\n");
1094
1095 mid_idx = mid->mid;
1096 if (__mlxsw_sp_mc_dec_ref(mlxsw_sp, mid)) {
1097 err = mlxsw_sp_port_mdb_op(mlxsw_sp, mdb->addr, fid, mid_idx,
1098 false);
1099 if (err)
1100 netdev_err(dev, "Unable to remove MC SFD\n");
1101 }
1102
1103 return err;
1104}
1105
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001106static int mlxsw_sp_port_obj_del(struct net_device *dev,
1107 const struct switchdev_obj *obj)
1108{
1109 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
1110 int err = 0;
1111
Ido Schimmel54a73202015-12-15 16:03:41 +01001112 mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
1113 if (!mlxsw_sp_port)
1114 return -EINVAL;
1115
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001116 switch (obj->id) {
1117 case SWITCHDEV_OBJ_ID_PORT_VLAN:
Ido Schimmel54a73202015-12-15 16:03:41 +01001118 if (mlxsw_sp_port_is_vport(mlxsw_sp_port))
1119 return 0;
1120
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001121 err = mlxsw_sp_port_vlans_del(mlxsw_sp_port,
1122 SWITCHDEV_OBJ_PORT_VLAN(obj));
1123 break;
1124 case SWITCHDEV_OBJ_ID_PORT_FDB:
1125 err = mlxsw_sp_port_fdb_static_del(mlxsw_sp_port,
1126 SWITCHDEV_OBJ_PORT_FDB(obj));
1127 break;
Elad Raz3a49b4f2016-01-10 21:06:28 +01001128 case SWITCHDEV_OBJ_ID_PORT_MDB:
1129 err = mlxsw_sp_port_mdb_del(mlxsw_sp_port,
1130 SWITCHDEV_OBJ_PORT_MDB(obj));
Dan Carpenter00ae40e2016-01-13 15:28:23 +03001131 break;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001132 default:
1133 err = -EOPNOTSUPP;
1134 break;
1135 }
1136
1137 return err;
1138}
1139
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001140static struct mlxsw_sp_port *mlxsw_sp_lag_rep_port(struct mlxsw_sp *mlxsw_sp,
1141 u16 lag_id)
1142{
1143 struct mlxsw_sp_port *mlxsw_sp_port;
Jiri Pirkoc1a38312016-10-21 16:07:23 +02001144 u64 max_lag_members;
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001145 int i;
1146
Jiri Pirkoc1a38312016-10-21 16:07:23 +02001147 max_lag_members = MLXSW_CORE_RES_GET(mlxsw_sp->core,
1148 MAX_LAG_MEMBERS);
1149 for (i = 0; i < max_lag_members; i++) {
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001150 mlxsw_sp_port = mlxsw_sp_port_lagged_get(mlxsw_sp, lag_id, i);
1151 if (mlxsw_sp_port)
1152 return mlxsw_sp_port;
1153 }
1154 return NULL;
1155}
1156
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001157static int mlxsw_sp_port_fdb_dump(struct mlxsw_sp_port *mlxsw_sp_port,
1158 struct switchdev_obj_port_fdb *fdb,
Ido Schimmel304f5152016-01-27 15:20:24 +01001159 switchdev_obj_dump_cb_t *cb,
1160 struct net_device *orig_dev)
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001161{
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001162 struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp;
Ido Schimmel3f47f862016-01-27 15:20:25 +01001163 struct mlxsw_sp_port *tmp;
Ido Schimmel56918b62016-06-20 23:04:18 +02001164 struct mlxsw_sp_fid *f;
1165 u16 vport_fid;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001166 char *sfd_pl;
1167 char mac[ETH_ALEN];
Ido Schimmel9de6a802015-12-15 16:03:40 +01001168 u16 fid;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001169 u8 local_port;
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001170 u16 lag_id;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001171 u8 num_rec;
1172 int stored_err = 0;
1173 int i;
1174 int err;
1175
1176 sfd_pl = kmalloc(MLXSW_REG_SFD_LEN, GFP_KERNEL);
1177 if (!sfd_pl)
1178 return -ENOMEM;
1179
Ido Schimmel56918b62016-06-20 23:04:18 +02001180 f = mlxsw_sp_vport_fid_get(mlxsw_sp_port);
1181 vport_fid = f ? f->fid : 0;
Ido Schimmel54a73202015-12-15 16:03:41 +01001182
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001183 mlxsw_reg_sfd_pack(sfd_pl, MLXSW_REG_SFD_OP_QUERY_DUMP, 0);
1184 do {
1185 mlxsw_reg_sfd_num_rec_set(sfd_pl, MLXSW_REG_SFD_REC_MAX_COUNT);
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001186 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfd), sfd_pl);
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001187 if (err)
1188 goto out;
1189
1190 num_rec = mlxsw_reg_sfd_num_rec_get(sfd_pl);
1191
1192 /* Even in case of error, we have to run the dump to the end
1193 * so the session in firmware is finished.
1194 */
1195 if (stored_err)
1196 continue;
1197
1198 for (i = 0; i < num_rec; i++) {
1199 switch (mlxsw_reg_sfd_rec_type_get(sfd_pl, i)) {
1200 case MLXSW_REG_SFD_REC_TYPE_UNICAST:
Ido Schimmel9de6a802015-12-15 16:03:40 +01001201 mlxsw_reg_sfd_uc_unpack(sfd_pl, i, mac, &fid,
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001202 &local_port);
1203 if (local_port == mlxsw_sp_port->local_port) {
Ido Schimmel004f85e2016-01-27 15:20:22 +01001204 if (vport_fid && vport_fid == fid)
1205 fdb->vid = 0;
1206 else if (!vport_fid &&
1207 !mlxsw_sp_fid_is_vfid(fid))
Ido Schimmel54a73202015-12-15 16:03:41 +01001208 fdb->vid = fid;
Ido Schimmel004f85e2016-01-27 15:20:22 +01001209 else
1210 continue;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001211 ether_addr_copy(fdb->addr, mac);
1212 fdb->ndm_state = NUD_REACHABLE;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001213 err = cb(&fdb->obj);
1214 if (err)
1215 stored_err = err;
1216 }
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001217 break;
1218 case MLXSW_REG_SFD_REC_TYPE_UNICAST_LAG:
1219 mlxsw_reg_sfd_uc_lag_unpack(sfd_pl, i,
Ido Schimmel9de6a802015-12-15 16:03:40 +01001220 mac, &fid, &lag_id);
Ido Schimmel3f47f862016-01-27 15:20:25 +01001221 tmp = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id);
1222 if (tmp && tmp->local_port ==
1223 mlxsw_sp_port->local_port) {
Ido Schimmel304f5152016-01-27 15:20:24 +01001224 /* LAG records can only point to LAG
1225 * devices or VLAN devices on top.
1226 */
1227 if (!netif_is_lag_master(orig_dev) &&
1228 !is_vlan_dev(orig_dev))
1229 continue;
Ido Schimmel004f85e2016-01-27 15:20:22 +01001230 if (vport_fid && vport_fid == fid)
1231 fdb->vid = 0;
1232 else if (!vport_fid &&
1233 !mlxsw_sp_fid_is_vfid(fid))
Ido Schimmel54a73202015-12-15 16:03:41 +01001234 fdb->vid = fid;
Ido Schimmel004f85e2016-01-27 15:20:22 +01001235 else
1236 continue;
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001237 ether_addr_copy(fdb->addr, mac);
1238 fdb->ndm_state = NUD_REACHABLE;
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001239 err = cb(&fdb->obj);
1240 if (err)
1241 stored_err = err;
1242 }
1243 break;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001244 }
1245 }
1246 } while (num_rec == MLXSW_REG_SFD_REC_MAX_COUNT);
1247
1248out:
1249 kfree(sfd_pl);
1250 return stored_err ? stored_err : err;
1251}
1252
1253static int mlxsw_sp_port_vlan_dump(struct mlxsw_sp_port *mlxsw_sp_port,
1254 struct switchdev_obj_port_vlan *vlan,
1255 switchdev_obj_dump_cb_t *cb)
1256{
1257 u16 vid;
1258 int err = 0;
1259
Ido Schimmel54a73202015-12-15 16:03:41 +01001260 if (mlxsw_sp_port_is_vport(mlxsw_sp_port)) {
1261 vlan->flags = 0;
1262 vlan->vid_begin = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
1263 vlan->vid_end = mlxsw_sp_vport_vid_get(mlxsw_sp_port);
1264 return cb(&vlan->obj);
1265 }
1266
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001267 for_each_set_bit(vid, mlxsw_sp_port->active_vlans, VLAN_N_VID) {
1268 vlan->flags = 0;
1269 if (vid == mlxsw_sp_port->pvid)
1270 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
Elad Razfc1273a2016-01-06 13:01:11 +01001271 if (test_bit(vid, mlxsw_sp_port->untagged_vlans))
1272 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001273 vlan->vid_begin = vid;
1274 vlan->vid_end = vid;
1275 err = cb(&vlan->obj);
1276 if (err)
1277 break;
1278 }
1279 return err;
1280}
1281
1282static int mlxsw_sp_port_obj_dump(struct net_device *dev,
1283 struct switchdev_obj *obj,
1284 switchdev_obj_dump_cb_t *cb)
1285{
1286 struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev);
1287 int err = 0;
1288
Ido Schimmel54a73202015-12-15 16:03:41 +01001289 mlxsw_sp_port = mlxsw_sp_port_orig_get(obj->orig_dev, mlxsw_sp_port);
1290 if (!mlxsw_sp_port)
1291 return -EINVAL;
1292
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001293 switch (obj->id) {
1294 case SWITCHDEV_OBJ_ID_PORT_VLAN:
1295 err = mlxsw_sp_port_vlan_dump(mlxsw_sp_port,
1296 SWITCHDEV_OBJ_PORT_VLAN(obj), cb);
1297 break;
1298 case SWITCHDEV_OBJ_ID_PORT_FDB:
1299 err = mlxsw_sp_port_fdb_dump(mlxsw_sp_port,
Ido Schimmel304f5152016-01-27 15:20:24 +01001300 SWITCHDEV_OBJ_PORT_FDB(obj), cb,
1301 obj->orig_dev);
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001302 break;
1303 default:
1304 err = -EOPNOTSUPP;
1305 break;
1306 }
1307
1308 return err;
1309}
1310
Jiri Pirkoc7070fc2015-10-28 10:17:05 +01001311static const struct switchdev_ops mlxsw_sp_port_switchdev_ops = {
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001312 .switchdev_port_attr_get = mlxsw_sp_port_attr_get,
1313 .switchdev_port_attr_set = mlxsw_sp_port_attr_set,
1314 .switchdev_port_obj_add = mlxsw_sp_port_obj_add,
1315 .switchdev_port_obj_del = mlxsw_sp_port_obj_del,
1316 .switchdev_port_obj_dump = mlxsw_sp_port_obj_dump,
1317};
1318
Ido Schimmel45827d72016-01-27 15:20:21 +01001319static void mlxsw_sp_fdb_call_notifiers(bool learning_sync, bool adding,
1320 char *mac, u16 vid,
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001321 struct net_device *dev)
1322{
1323 struct switchdev_notifier_fdb_info info;
1324 unsigned long notifier_type;
1325
Ido Schimmel45827d72016-01-27 15:20:21 +01001326 if (learning_sync) {
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001327 info.addr = mac;
1328 info.vid = vid;
1329 notifier_type = adding ? SWITCHDEV_FDB_ADD : SWITCHDEV_FDB_DEL;
1330 call_switchdev_notifiers(notifier_type, dev, &info.info);
1331 }
1332}
1333
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001334static void mlxsw_sp_fdb_notify_mac_process(struct mlxsw_sp *mlxsw_sp,
1335 char *sfn_pl, int rec_index,
1336 bool adding)
1337{
1338 struct mlxsw_sp_port *mlxsw_sp_port;
1339 char mac[ETH_ALEN];
1340 u8 local_port;
Ido Schimmel9de6a802015-12-15 16:03:40 +01001341 u16 vid, fid;
Jiri Pirko12f15012016-01-07 11:50:30 +01001342 bool do_notification = true;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001343 int err;
1344
Ido Schimmel9de6a802015-12-15 16:03:40 +01001345 mlxsw_reg_sfn_mac_unpack(sfn_pl, rec_index, mac, &fid, &local_port);
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001346 mlxsw_sp_port = mlxsw_sp->ports[local_port];
1347 if (!mlxsw_sp_port) {
1348 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Incorrect local port in FDB notification\n");
Jiri Pirko12f15012016-01-07 11:50:30 +01001349 goto just_remove;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001350 }
1351
Ido Schimmelaac78a42015-12-15 16:03:42 +01001352 if (mlxsw_sp_fid_is_vfid(fid)) {
Ido Schimmelaac78a42015-12-15 16:03:42 +01001353 struct mlxsw_sp_port *mlxsw_sp_vport;
1354
Ido Schimmeld0ec8752016-06-20 23:04:12 +02001355 mlxsw_sp_vport = mlxsw_sp_port_vport_find_by_fid(mlxsw_sp_port,
1356 fid);
Ido Schimmelaac78a42015-12-15 16:03:42 +01001357 if (!mlxsw_sp_vport) {
1358 netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n");
Jiri Pirko12f15012016-01-07 11:50:30 +01001359 goto just_remove;
Ido Schimmelaac78a42015-12-15 16:03:42 +01001360 }
Ido Schimmel004f85e2016-01-27 15:20:22 +01001361 vid = 0;
Ido Schimmelaac78a42015-12-15 16:03:42 +01001362 /* Override the physical port with the vPort. */
1363 mlxsw_sp_port = mlxsw_sp_vport;
1364 } else {
1365 vid = fid;
1366 }
1367
Jiri Pirko12f15012016-01-07 11:50:30 +01001368do_fdb_op:
Jiri Pirko2fa9d452016-01-07 11:50:29 +01001369 err = mlxsw_sp_port_fdb_uc_op(mlxsw_sp, local_port, mac, fid,
Jiri Pirko12f15012016-01-07 11:50:30 +01001370 adding, true);
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001371 if (err) {
Ido Schimmelc0e01ea2017-05-18 13:03:52 +02001372 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n");
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001373 return;
1374 }
1375
Jiri Pirko12f15012016-01-07 11:50:30 +01001376 if (!do_notification)
1377 return;
Ido Schimmel45827d72016-01-27 15:20:21 +01001378 mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning_sync,
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001379 adding, mac, vid, mlxsw_sp_port->dev);
Jiri Pirko12f15012016-01-07 11:50:30 +01001380 return;
1381
1382just_remove:
1383 adding = false;
1384 do_notification = false;
1385 goto do_fdb_op;
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001386}
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001387
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001388static void mlxsw_sp_fdb_notify_mac_lag_process(struct mlxsw_sp *mlxsw_sp,
1389 char *sfn_pl, int rec_index,
1390 bool adding)
1391{
1392 struct mlxsw_sp_port *mlxsw_sp_port;
Ido Schimmele43aca22016-01-27 15:20:23 +01001393 struct net_device *dev;
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001394 char mac[ETH_ALEN];
Ido Schimmel64771e32015-12-15 16:03:46 +01001395 u16 lag_vid = 0;
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001396 u16 lag_id;
Ido Schimmel9de6a802015-12-15 16:03:40 +01001397 u16 vid, fid;
Jiri Pirko12f15012016-01-07 11:50:30 +01001398 bool do_notification = true;
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001399 int err;
1400
Ido Schimmel9de6a802015-12-15 16:03:40 +01001401 mlxsw_reg_sfn_mac_lag_unpack(sfn_pl, rec_index, mac, &fid, &lag_id);
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001402 mlxsw_sp_port = mlxsw_sp_lag_rep_port(mlxsw_sp, lag_id);
1403 if (!mlxsw_sp_port) {
1404 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Cannot find port representor for LAG\n");
Jiri Pirko12f15012016-01-07 11:50:30 +01001405 goto just_remove;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001406 }
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001407
Ido Schimmelaac78a42015-12-15 16:03:42 +01001408 if (mlxsw_sp_fid_is_vfid(fid)) {
Ido Schimmelaac78a42015-12-15 16:03:42 +01001409 struct mlxsw_sp_port *mlxsw_sp_vport;
1410
Ido Schimmeld0ec8752016-06-20 23:04:12 +02001411 mlxsw_sp_vport = mlxsw_sp_port_vport_find_by_fid(mlxsw_sp_port,
1412 fid);
Ido Schimmelaac78a42015-12-15 16:03:42 +01001413 if (!mlxsw_sp_vport) {
1414 netdev_err(mlxsw_sp_port->dev, "Failed to find a matching vPort following FDB notification\n");
Jiri Pirko12f15012016-01-07 11:50:30 +01001415 goto just_remove;
Ido Schimmelaac78a42015-12-15 16:03:42 +01001416 }
1417
Ido Schimmel004f85e2016-01-27 15:20:22 +01001418 lag_vid = mlxsw_sp_vport_vid_get(mlxsw_sp_vport);
Ido Schimmele43aca22016-01-27 15:20:23 +01001419 dev = mlxsw_sp_vport->dev;
Ido Schimmel004f85e2016-01-27 15:20:22 +01001420 vid = 0;
Ido Schimmelaac78a42015-12-15 16:03:42 +01001421 /* Override the physical port with the vPort. */
1422 mlxsw_sp_port = mlxsw_sp_vport;
1423 } else {
Ido Schimmele43aca22016-01-27 15:20:23 +01001424 dev = mlxsw_sp_lag_get(mlxsw_sp, lag_id)->dev;
Ido Schimmelaac78a42015-12-15 16:03:42 +01001425 vid = fid;
1426 }
1427
Jiri Pirko12f15012016-01-07 11:50:30 +01001428do_fdb_op:
Ido Schimmel64771e32015-12-15 16:03:46 +01001429 err = mlxsw_sp_port_fdb_uc_lag_op(mlxsw_sp, lag_id, mac, fid, lag_vid,
Jiri Pirko12f15012016-01-07 11:50:30 +01001430 adding, true);
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001431 if (err) {
Ido Schimmelc0e01ea2017-05-18 13:03:52 +02001432 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to set FDB entry\n");
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001433 return;
1434 }
1435
Jiri Pirko12f15012016-01-07 11:50:30 +01001436 if (!do_notification)
1437 return;
Ido Schimmel45827d72016-01-27 15:20:21 +01001438 mlxsw_sp_fdb_call_notifiers(mlxsw_sp_port->learning_sync, adding, mac,
Ido Schimmele43aca22016-01-27 15:20:23 +01001439 vid, dev);
Jiri Pirko12f15012016-01-07 11:50:30 +01001440 return;
1441
1442just_remove:
1443 adding = false;
1444 do_notification = false;
1445 goto do_fdb_op;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001446}
1447
1448static void mlxsw_sp_fdb_notify_rec_process(struct mlxsw_sp *mlxsw_sp,
1449 char *sfn_pl, int rec_index)
1450{
1451 switch (mlxsw_reg_sfn_rec_type_get(sfn_pl, rec_index)) {
1452 case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC:
1453 mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
1454 rec_index, true);
1455 break;
1456 case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC:
1457 mlxsw_sp_fdb_notify_mac_process(mlxsw_sp, sfn_pl,
1458 rec_index, false);
1459 break;
Jiri Pirko8a1ab5d2015-12-03 12:12:29 +01001460 case MLXSW_REG_SFN_REC_TYPE_LEARNED_MAC_LAG:
1461 mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
1462 rec_index, true);
1463 break;
1464 case MLXSW_REG_SFN_REC_TYPE_AGED_OUT_MAC_LAG:
1465 mlxsw_sp_fdb_notify_mac_lag_process(mlxsw_sp, sfn_pl,
1466 rec_index, false);
1467 break;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001468 }
1469}
1470
1471static void mlxsw_sp_fdb_notify_work_schedule(struct mlxsw_sp *mlxsw_sp)
1472{
Ido Schimmel5f6935c2017-05-16 19:38:26 +02001473 struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge;
1474
1475 mlxsw_core_schedule_dw(&bridge->fdb_notify.dw,
1476 msecs_to_jiffies(bridge->fdb_notify.interval));
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001477}
1478
1479static void mlxsw_sp_fdb_notify_work(struct work_struct *work)
1480{
Ido Schimmel5f6935c2017-05-16 19:38:26 +02001481 struct mlxsw_sp_bridge *bridge;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001482 struct mlxsw_sp *mlxsw_sp;
1483 char *sfn_pl;
1484 u8 num_rec;
1485 int i;
1486 int err;
1487
1488 sfn_pl = kmalloc(MLXSW_REG_SFN_LEN, GFP_KERNEL);
1489 if (!sfn_pl)
1490 return;
1491
Ido Schimmel5f6935c2017-05-16 19:38:26 +02001492 bridge = container_of(work, struct mlxsw_sp_bridge, fdb_notify.dw.work);
1493 mlxsw_sp = bridge->mlxsw_sp;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001494
Ido Schimmel4f2c6ae2016-01-27 15:16:43 +01001495 rtnl_lock();
Ido Schimmel1803e0f2016-08-24 12:00:23 +02001496 mlxsw_reg_sfn_pack(sfn_pl);
1497 err = mlxsw_reg_query(mlxsw_sp->core, MLXSW_REG(sfn), sfn_pl);
1498 if (err) {
1499 dev_err_ratelimited(mlxsw_sp->bus_info->dev, "Failed to get FDB notifications\n");
1500 goto out;
1501 }
1502 num_rec = mlxsw_reg_sfn_num_rec_get(sfn_pl);
1503 for (i = 0; i < num_rec; i++)
1504 mlxsw_sp_fdb_notify_rec_process(mlxsw_sp, sfn_pl, i);
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001505
Ido Schimmel1803e0f2016-08-24 12:00:23 +02001506out:
Ido Schimmel4f2c6ae2016-01-27 15:16:43 +01001507 rtnl_unlock();
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001508 kfree(sfn_pl);
1509 mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
1510}
1511
1512static int mlxsw_sp_fdb_init(struct mlxsw_sp *mlxsw_sp)
1513{
Ido Schimmel5f6935c2017-05-16 19:38:26 +02001514 struct mlxsw_sp_bridge *bridge = mlxsw_sp->bridge;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001515 int err;
1516
1517 err = mlxsw_sp_ageing_set(mlxsw_sp, MLXSW_SP_DEFAULT_AGEING_TIME);
1518 if (err) {
1519 dev_err(mlxsw_sp->bus_info->dev, "Failed to set default ageing time\n");
1520 return err;
1521 }
Ido Schimmel5f6935c2017-05-16 19:38:26 +02001522 INIT_DELAYED_WORK(&bridge->fdb_notify.dw, mlxsw_sp_fdb_notify_work);
1523 bridge->fdb_notify.interval = MLXSW_SP_DEFAULT_LEARNING_INTERVAL;
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001524 mlxsw_sp_fdb_notify_work_schedule(mlxsw_sp);
1525 return 0;
1526}
1527
1528static void mlxsw_sp_fdb_fini(struct mlxsw_sp *mlxsw_sp)
1529{
Ido Schimmel5f6935c2017-05-16 19:38:26 +02001530 cancel_delayed_work_sync(&mlxsw_sp->bridge->fdb_notify.dw);
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001531}
1532
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001533int mlxsw_sp_switchdev_init(struct mlxsw_sp *mlxsw_sp)
1534{
Ido Schimmel5f6935c2017-05-16 19:38:26 +02001535 struct mlxsw_sp_bridge *bridge;
1536
1537 bridge = kzalloc(sizeof(*mlxsw_sp->bridge), GFP_KERNEL);
1538 if (!bridge)
1539 return -ENOMEM;
1540 mlxsw_sp->bridge = bridge;
1541 bridge->mlxsw_sp = mlxsw_sp;
1542
1543 INIT_LIST_HEAD(&mlxsw_sp->bridge->mids_list);
1544
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001545 return mlxsw_sp_fdb_init(mlxsw_sp);
1546}
1547
1548void mlxsw_sp_switchdev_fini(struct mlxsw_sp *mlxsw_sp)
1549{
1550 mlxsw_sp_fdb_fini(mlxsw_sp);
Ido Schimmel5f6935c2017-05-16 19:38:26 +02001551 WARN_ON(!list_empty(&mlxsw_sp->bridge->mids_list));
1552 kfree(mlxsw_sp->bridge);
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001553}
1554
Jiri Pirko56ade8f2015-10-16 14:01:37 +02001555void mlxsw_sp_port_switchdev_init(struct mlxsw_sp_port *mlxsw_sp_port)
1556{
1557 mlxsw_sp_port->dev->switchdev_ops = &mlxsw_sp_port_switchdev_ops;
1558}
1559
1560void mlxsw_sp_port_switchdev_fini(struct mlxsw_sp_port *mlxsw_sp_port)
1561{
1562}