blob: 7d8cd9bc0ecc8bbf31a246884c386729de1471a0 [file] [log] [blame]
Vivien Didelotf515f192017-02-03 13:20:20 -05001/*
2 * Handling of a single switch chip, part of a switch fabric
3 *
Vivien Didelot4333d612017-03-28 15:10:36 -04004 * Copyright (c) 2017 Savoir-faire Linux Inc.
5 * Vivien Didelot <vivien.didelot@savoirfairelinux.com>
Vivien Didelotf515f192017-02-03 13:20:20 -05006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
Vladimir Olteand371b7c2019-04-28 21:45:46 +030013#include <linux/if_bridge.h>
Vivien Didelotf515f192017-02-03 13:20:20 -050014#include <linux/netdevice.h>
15#include <linux/notifier.h>
Florian Fainelli061f6a52019-02-20 14:35:39 -080016#include <linux/if_vlan.h>
Vivien Didelot1faabf72017-05-19 17:00:52 -040017#include <net/switchdev.h>
Vivien Didelotea5dd342017-05-17 15:46:03 -040018
19#include "dsa_priv.h"
Vivien Didelotf515f192017-02-03 13:20:20 -050020
Vivien Didelot1faabf72017-05-19 17:00:52 -040021static unsigned int dsa_switch_fastest_ageing_time(struct dsa_switch *ds,
22 unsigned int ageing_time)
23{
24 int i;
25
26 for (i = 0; i < ds->num_ports; ++i) {
27 struct dsa_port *dp = &ds->ports[i];
28
29 if (dp->ageing_time && dp->ageing_time < ageing_time)
30 ageing_time = dp->ageing_time;
31 }
32
33 return ageing_time;
34}
35
36static int dsa_switch_ageing_time(struct dsa_switch *ds,
37 struct dsa_notifier_ageing_time_info *info)
38{
39 unsigned int ageing_time = info->ageing_time;
40 struct switchdev_trans *trans = info->trans;
41
Vivien Didelot1faabf72017-05-19 17:00:52 -040042 if (switchdev_trans_ph_prepare(trans)) {
43 if (ds->ageing_time_min && ageing_time < ds->ageing_time_min)
44 return -ERANGE;
45 if (ds->ageing_time_max && ageing_time > ds->ageing_time_max)
46 return -ERANGE;
47 return 0;
48 }
49
50 /* Program the fastest ageing time in case of multiple bridges */
51 ageing_time = dsa_switch_fastest_ageing_time(ds, ageing_time);
52
53 if (ds->ops->set_ageing_time)
54 return ds->ops->set_ageing_time(ds, ageing_time);
55
56 return 0;
57}
58
Vivien Didelot04d3a4c2017-02-03 13:20:21 -050059static int dsa_switch_bridge_join(struct dsa_switch *ds,
60 struct dsa_notifier_bridge_info *info)
61{
62 if (ds->index == info->sw_index && ds->ops->port_bridge_join)
63 return ds->ops->port_bridge_join(ds, info->port, info->br);
64
Vivien Didelot40ef2c92017-03-30 17:37:14 -040065 if (ds->index != info->sw_index && ds->ops->crosschip_bridge_join)
66 return ds->ops->crosschip_bridge_join(ds, info->sw_index,
67 info->port, info->br);
Vivien Didelot04d3a4c2017-02-03 13:20:21 -050068
69 return 0;
70}
71
72static int dsa_switch_bridge_leave(struct dsa_switch *ds,
73 struct dsa_notifier_bridge_info *info)
74{
Vladimir Olteand371b7c2019-04-28 21:45:46 +030075 bool unset_vlan_filtering = br_vlan_enabled(info->br);
76 int err, i;
77
Vivien Didelot04d3a4c2017-02-03 13:20:21 -050078 if (ds->index == info->sw_index && ds->ops->port_bridge_leave)
79 ds->ops->port_bridge_leave(ds, info->port, info->br);
80
Vivien Didelot40ef2c92017-03-30 17:37:14 -040081 if (ds->index != info->sw_index && ds->ops->crosschip_bridge_leave)
82 ds->ops->crosschip_bridge_leave(ds, info->sw_index, info->port,
83 info->br);
Vivien Didelot04d3a4c2017-02-03 13:20:21 -050084
Vladimir Olteand371b7c2019-04-28 21:45:46 +030085 /* If the bridge was vlan_filtering, the bridge core doesn't trigger an
86 * event for changing vlan_filtering setting upon slave ports leaving
87 * it. That is a good thing, because that lets us handle it and also
88 * handle the case where the switch's vlan_filtering setting is global
89 * (not per port). When that happens, the correct moment to trigger the
90 * vlan_filtering callback is only when the last port left this bridge.
91 */
92 if (unset_vlan_filtering && ds->vlan_filtering_is_global) {
93 for (i = 0; i < ds->num_ports; i++) {
94 if (i == info->port)
95 continue;
96 if (dsa_to_port(ds, i)->bridge_dev == info->br) {
97 unset_vlan_filtering = false;
98 break;
99 }
100 }
101 }
102 if (unset_vlan_filtering) {
103 struct switchdev_trans trans = {0};
104
105 err = dsa_port_vlan_filtering(&ds->ports[info->port],
106 false, &trans);
107 if (err && err != EOPNOTSUPP)
108 return err;
109 }
Vivien Didelot04d3a4c2017-02-03 13:20:21 -0500110 return 0;
111}
112
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400113static int dsa_switch_fdb_add(struct dsa_switch *ds,
114 struct dsa_notifier_fdb_info *info)
115{
Vivien Didelot31692412017-11-30 12:56:43 -0500116 int port = dsa_towards_port(ds, info->sw_index, info->port);
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400117
Arkadi Sharshevsky1b6dd552017-08-06 16:15:40 +0300118 if (!ds->ops->port_fdb_add)
119 return -EOPNOTSUPP;
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400120
Vivien Didelot31692412017-11-30 12:56:43 -0500121 return ds->ops->port_fdb_add(ds, port, info->addr, info->vid);
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400122}
123
124static int dsa_switch_fdb_del(struct dsa_switch *ds,
125 struct dsa_notifier_fdb_info *info)
126{
Vivien Didelot31692412017-11-30 12:56:43 -0500127 int port = dsa_towards_port(ds, info->sw_index, info->port);
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400128
129 if (!ds->ops->port_fdb_del)
130 return -EOPNOTSUPP;
131
Vivien Didelot31692412017-11-30 12:56:43 -0500132 return ds->ops->port_fdb_del(ds, port, info->addr, info->vid);
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400133}
134
Vivien Didelote6db98d2017-11-30 11:24:00 -0500135static int
136dsa_switch_mdb_prepare_bitmap(struct dsa_switch *ds,
137 const struct switchdev_obj_port_mdb *mdb,
138 const unsigned long *bitmap)
139{
140 int port, err;
141
142 if (!ds->ops->port_mdb_prepare || !ds->ops->port_mdb_add)
143 return -EOPNOTSUPP;
144
145 for_each_set_bit(port, bitmap, ds->num_ports) {
146 err = ds->ops->port_mdb_prepare(ds, port, mdb);
147 if (err)
148 return err;
149 }
150
151 return 0;
152}
153
154static void dsa_switch_mdb_add_bitmap(struct dsa_switch *ds,
155 const struct switchdev_obj_port_mdb *mdb,
156 const unsigned long *bitmap)
157{
158 int port;
159
160 for_each_set_bit(port, bitmap, ds->num_ports)
161 ds->ops->port_mdb_add(ds, port, mdb);
162}
163
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400164static int dsa_switch_mdb_add(struct dsa_switch *ds,
165 struct dsa_notifier_mdb_info *info)
166{
167 const struct switchdev_obj_port_mdb *mdb = info->mdb;
168 struct switchdev_trans *trans = info->trans;
Vivien Didelote6db98d2017-11-30 11:24:00 -0500169 int port;
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400170
Vivien Didelota1a6b7e2017-06-15 16:14:48 -0400171 /* Build a mask of Multicast group members */
Salvatore Mesoraca0015b802018-07-16 21:10:34 -0700172 bitmap_zero(ds->bitmap, ds->num_ports);
Vivien Didelota1a6b7e2017-06-15 16:14:48 -0400173 if (ds->index == info->sw_index)
Salvatore Mesoraca0015b802018-07-16 21:10:34 -0700174 set_bit(info->port, ds->bitmap);
Vivien Didelota1a6b7e2017-06-15 16:14:48 -0400175 for (port = 0; port < ds->num_ports; port++)
Andrew Lunnae451022017-11-09 23:11:02 +0100176 if (dsa_is_dsa_port(ds, port))
Salvatore Mesoraca0015b802018-07-16 21:10:34 -0700177 set_bit(port, ds->bitmap);
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400178
Vivien Didelote6db98d2017-11-30 11:24:00 -0500179 if (switchdev_trans_ph_prepare(trans))
Salvatore Mesoraca0015b802018-07-16 21:10:34 -0700180 return dsa_switch_mdb_prepare_bitmap(ds, mdb, ds->bitmap);
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400181
Salvatore Mesoraca0015b802018-07-16 21:10:34 -0700182 dsa_switch_mdb_add_bitmap(ds, mdb, ds->bitmap);
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400183
184 return 0;
185}
186
187static int dsa_switch_mdb_del(struct dsa_switch *ds,
188 struct dsa_notifier_mdb_info *info)
189{
190 const struct switchdev_obj_port_mdb *mdb = info->mdb;
191
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400192 if (!ds->ops->port_mdb_del)
193 return -EOPNOTSUPP;
194
Vivien Didelota1a6b7e2017-06-15 16:14:48 -0400195 if (ds->index == info->sw_index)
196 return ds->ops->port_mdb_del(ds, info->port, mdb);
197
198 return 0;
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400199}
200
Florian Fainelli061f6a52019-02-20 14:35:39 -0800201static int dsa_port_vlan_device_check(struct net_device *vlan_dev,
202 int vlan_dev_vid,
203 void *arg)
204{
205 struct switchdev_obj_port_vlan *vlan = arg;
206 u16 vid;
207
208 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
209 if (vid == vlan_dev_vid)
210 return -EBUSY;
211 }
212
213 return 0;
214}
215
216static int dsa_port_vlan_check(struct dsa_switch *ds, int port,
217 const struct switchdev_obj_port_vlan *vlan)
218{
219 const struct dsa_port *dp = dsa_to_port(ds, port);
220 int err = 0;
221
222 /* Device is not bridged, let it proceed with the VLAN device
223 * creation.
224 */
225 if (!dp->bridge_dev)
226 return err;
227
Vladimir Oltean85478d72019-04-28 21:45:42 +0300228 /* dsa_slave_vlan_rx_{add,kill}_vid() cannot use the prepare phase and
Florian Fainelli061f6a52019-02-20 14:35:39 -0800229 * already checks whether there is an overlapping bridge VLAN entry
230 * with the same VID, so here we only need to check that if we are
231 * adding a bridge VLAN entry there is not an overlapping VLAN device
232 * claiming that VID.
233 */
234 return vlan_for_each(dp->slave, dsa_port_vlan_device_check,
235 (void *)vlan);
236}
237
Vivien Didelot9c428c52017-11-30 11:23:59 -0500238static int
239dsa_switch_vlan_prepare_bitmap(struct dsa_switch *ds,
240 const struct switchdev_obj_port_vlan *vlan,
241 const unsigned long *bitmap)
242{
243 int port, err;
244
245 if (!ds->ops->port_vlan_prepare || !ds->ops->port_vlan_add)
246 return -EOPNOTSUPP;
247
248 for_each_set_bit(port, bitmap, ds->num_ports) {
Florian Fainelli061f6a52019-02-20 14:35:39 -0800249 err = dsa_port_vlan_check(ds, port, vlan);
250 if (err)
251 return err;
252
Vivien Didelot9c428c52017-11-30 11:23:59 -0500253 err = ds->ops->port_vlan_prepare(ds, port, vlan);
254 if (err)
255 return err;
256 }
257
258 return 0;
259}
260
261static void
262dsa_switch_vlan_add_bitmap(struct dsa_switch *ds,
263 const struct switchdev_obj_port_vlan *vlan,
264 const unsigned long *bitmap)
265{
266 int port;
267
268 for_each_set_bit(port, bitmap, ds->num_ports)
269 ds->ops->port_vlan_add(ds, port, vlan);
270}
271
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400272static int dsa_switch_vlan_add(struct dsa_switch *ds,
273 struct dsa_notifier_vlan_info *info)
274{
275 const struct switchdev_obj_port_vlan *vlan = info->vlan;
276 struct switchdev_trans *trans = info->trans;
Vivien Didelot9c428c52017-11-30 11:23:59 -0500277 int port;
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400278
Vivien Didelot1ca4aa92017-06-07 18:12:14 -0400279 /* Build a mask of VLAN members */
Salvatore Mesoraca0015b802018-07-16 21:10:34 -0700280 bitmap_zero(ds->bitmap, ds->num_ports);
Vivien Didelot1ca4aa92017-06-07 18:12:14 -0400281 if (ds->index == info->sw_index)
Salvatore Mesoraca0015b802018-07-16 21:10:34 -0700282 set_bit(info->port, ds->bitmap);
Vivien Didelotb2f81d32017-06-07 18:12:15 -0400283 for (port = 0; port < ds->num_ports; port++)
284 if (dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))
Salvatore Mesoraca0015b802018-07-16 21:10:34 -0700285 set_bit(port, ds->bitmap);
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400286
Vivien Didelot9c428c52017-11-30 11:23:59 -0500287 if (switchdev_trans_ph_prepare(trans))
Salvatore Mesoraca0015b802018-07-16 21:10:34 -0700288 return dsa_switch_vlan_prepare_bitmap(ds, vlan, ds->bitmap);
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400289
Salvatore Mesoraca0015b802018-07-16 21:10:34 -0700290 dsa_switch_vlan_add_bitmap(ds, vlan, ds->bitmap);
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400291
292 return 0;
293}
294
295static int dsa_switch_vlan_del(struct dsa_switch *ds,
296 struct dsa_notifier_vlan_info *info)
297{
298 const struct switchdev_obj_port_vlan *vlan = info->vlan;
299
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400300 if (!ds->ops->port_vlan_del)
301 return -EOPNOTSUPP;
302
Vivien Didelot1ca4aa92017-06-07 18:12:14 -0400303 if (ds->index == info->sw_index)
304 return ds->ops->port_vlan_del(ds, info->port, vlan);
305
306 return 0;
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400307}
308
Vivien Didelotf515f192017-02-03 13:20:20 -0500309static int dsa_switch_event(struct notifier_block *nb,
310 unsigned long event, void *info)
311{
312 struct dsa_switch *ds = container_of(nb, struct dsa_switch, nb);
313 int err;
314
315 switch (event) {
Vivien Didelot1faabf72017-05-19 17:00:52 -0400316 case DSA_NOTIFIER_AGEING_TIME:
317 err = dsa_switch_ageing_time(ds, info);
318 break;
Vivien Didelot04d3a4c2017-02-03 13:20:21 -0500319 case DSA_NOTIFIER_BRIDGE_JOIN:
320 err = dsa_switch_bridge_join(ds, info);
321 break;
322 case DSA_NOTIFIER_BRIDGE_LEAVE:
323 err = dsa_switch_bridge_leave(ds, info);
324 break;
Vivien Didelot685fb6a2017-05-19 17:00:53 -0400325 case DSA_NOTIFIER_FDB_ADD:
326 err = dsa_switch_fdb_add(ds, info);
327 break;
328 case DSA_NOTIFIER_FDB_DEL:
329 err = dsa_switch_fdb_del(ds, info);
330 break;
Vivien Didelot8ae5bcd2017-05-19 17:00:54 -0400331 case DSA_NOTIFIER_MDB_ADD:
332 err = dsa_switch_mdb_add(ds, info);
333 break;
334 case DSA_NOTIFIER_MDB_DEL:
335 err = dsa_switch_mdb_del(ds, info);
336 break;
Vivien Didelotd0c627b2017-05-19 17:00:55 -0400337 case DSA_NOTIFIER_VLAN_ADD:
338 err = dsa_switch_vlan_add(ds, info);
339 break;
340 case DSA_NOTIFIER_VLAN_DEL:
341 err = dsa_switch_vlan_del(ds, info);
342 break;
Vivien Didelotf515f192017-02-03 13:20:20 -0500343 default:
344 err = -EOPNOTSUPP;
345 break;
346 }
347
348 /* Non-switchdev operations cannot be rolled back. If a DSA driver
349 * returns an error during the chained call, switch chips may be in an
350 * inconsistent state.
351 */
352 if (err)
353 dev_dbg(ds->dev, "breaking chain for DSA event %lu (%d)\n",
354 event, err);
355
356 return notifier_from_errno(err);
357}
358
359int dsa_switch_register_notifier(struct dsa_switch *ds)
360{
361 ds->nb.notifier_call = dsa_switch_event;
362
363 return raw_notifier_chain_register(&ds->dst->nh, &ds->nb);
364}
365
366void dsa_switch_unregister_notifier(struct dsa_switch *ds)
367{
368 int err;
369
370 err = raw_notifier_chain_unregister(&ds->dst->nh, &ds->nb);
371 if (err)
372 dev_err(ds->dev, "failed to unregister notifier (%d)\n", err);
373}