aboutsummaryrefslogtreecommitdiffstats
path: root/net/dsa
diff options
context:
space:
mode:
authorVivien Didelot <vivien.didelot@savoirfairelinux.com>2015-08-13 12:52:17 -0400
committerDavid S. Miller <davem@davemloft.net>2015-08-14 00:31:12 -0400
commit111495361598205967f1be4e07d4726b0f762d60 (patch)
tree55f2a584cbd0811e64ccce8e904e67c33902db58 /net/dsa
parent35103d11173b8fea874183f8aa508ae71234d299 (diff)
net: dsa: add support for switchdev VLAN objects
Add new functions in DSA drivers to access hardware VLAN entries through SWITCHDEV_OBJ_PORT_VLAN objects: - port_pvid_get() and vlan_getnext() to dump a VLAN - port_vlan_del() to exclude a port from a VLAN - port_pvid_set() and port_vlan_add() to join a port to a VLAN The DSA infrastructure will ensure that each VLAN of the given range does not already belong to another bridge. If it does, it will fallback to software VLAN and won't program the hardware. Signed-off-by: Vivien Didelot <vivien.didelot@savoirfairelinux.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dsa')
-rw-r--r--net/dsa/slave.c158
1 files changed, 158 insertions, 0 deletions
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index aa0266f7d0ce..373ff315030d 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -200,6 +200,152 @@ out:
200 return 0; 200 return 0;
201} 201}
202 202
203static int dsa_bridge_check_vlan_range(struct dsa_switch *ds,
204 const struct net_device *bridge,
205 u16 vid_begin, u16 vid_end)
206{
207 struct dsa_slave_priv *p;
208 struct net_device *dev, *vlan_br;
209 DECLARE_BITMAP(members, DSA_MAX_PORTS);
210 DECLARE_BITMAP(untagged, DSA_MAX_PORTS);
211 u16 vid;
212 int member, err;
213
214 if (!ds->drv->vlan_getnext || !vid_begin)
215 return -EOPNOTSUPP;
216
217 vid = vid_begin - 1;
218
219 do {
220 err = ds->drv->vlan_getnext(ds, &vid, members, untagged);
221 if (err)
222 break;
223
224 if (vid > vid_end)
225 break;
226
227 member = find_first_bit(members, DSA_MAX_PORTS);
228 if (member == DSA_MAX_PORTS)
229 continue;
230
231 dev = ds->ports[member];
232 p = netdev_priv(dev);
233 vlan_br = p->bridge_dev;
234 if (vlan_br == bridge)
235 continue;
236
237 netdev_dbg(vlan_br, "hardware VLAN %d already in use\n", vid);
238 return -EOPNOTSUPP;
239 } while (vid < vid_end);
240
241 return err == -ENOENT ? 0 : err;
242}
243
244static int dsa_slave_port_vlan_add(struct net_device *dev,
245 struct switchdev_obj *obj)
246{
247 struct switchdev_obj_vlan *vlan = &obj->u.vlan;
248 struct dsa_slave_priv *p = netdev_priv(dev);
249 struct dsa_switch *ds = p->parent;
250 u16 vid;
251 int err;
252
253 switch (obj->trans) {
254 case SWITCHDEV_TRANS_PREPARE:
255 if (!ds->drv->port_vlan_add || !ds->drv->port_pvid_set)
256 return -EOPNOTSUPP;
257
258 /* If the requested port doesn't belong to the same bridge as
259 * the VLAN members, fallback to software VLAN (hopefully).
260 */
261 err = dsa_bridge_check_vlan_range(ds, p->bridge_dev,
262 vlan->vid_begin,
263 vlan->vid_end);
264 if (err)
265 return err;
266 break;
267 case SWITCHDEV_TRANS_COMMIT:
268 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
269 err = ds->drv->port_vlan_add(ds, p->port, vid,
270 vlan->flags &
271 BRIDGE_VLAN_INFO_UNTAGGED);
272 if (!err && vlan->flags & BRIDGE_VLAN_INFO_PVID)
273 err = ds->drv->port_pvid_set(ds, p->port, vid);
274 if (err)
275 return err;
276 }
277 break;
278 default:
279 return -EOPNOTSUPP;
280 }
281
282 return 0;
283}
284
285static int dsa_slave_port_vlan_del(struct net_device *dev,
286 struct switchdev_obj *obj)
287{
288 struct switchdev_obj_vlan *vlan = &obj->u.vlan;
289 struct dsa_slave_priv *p = netdev_priv(dev);
290 struct dsa_switch *ds = p->parent;
291 u16 vid;
292 int err;
293
294 if (!ds->drv->port_vlan_del)
295 return -EOPNOTSUPP;
296
297 for (vid = vlan->vid_begin; vid <= vlan->vid_end; ++vid) {
298 err = ds->drv->port_vlan_del(ds, p->port, vid);
299 if (err)
300 return err;
301 }
302
303 return 0;
304}
305
306static int dsa_slave_port_vlan_dump(struct net_device *dev,
307 struct switchdev_obj *obj)
308{
309 struct switchdev_obj_vlan *vlan = &obj->u.vlan;
310 struct dsa_slave_priv *p = netdev_priv(dev);
311 struct dsa_switch *ds = p->parent;
312 DECLARE_BITMAP(members, DSA_MAX_PORTS);
313 DECLARE_BITMAP(untagged, DSA_MAX_PORTS);
314 u16 pvid, vid = 0;
315 int err;
316
317 if (!ds->drv->vlan_getnext || !ds->drv->port_pvid_get)
318 return -EOPNOTSUPP;
319
320 err = ds->drv->port_pvid_get(ds, p->port, &pvid);
321 if (err)
322 return err;
323
324 for (;;) {
325 err = ds->drv->vlan_getnext(ds, &vid, members, untagged);
326 if (err)
327 break;
328
329 if (!test_bit(p->port, members))
330 continue;
331
332 memset(vlan, 0, sizeof(*vlan));
333 vlan->vid_begin = vlan->vid_end = vid;
334
335 if (vid == pvid)
336 vlan->flags |= BRIDGE_VLAN_INFO_PVID;
337
338 if (test_bit(p->port, untagged))
339 vlan->flags |= BRIDGE_VLAN_INFO_UNTAGGED;
340
341 err = obj->cb(dev, obj);
342 if (err)
343 break;
344 }
345
346 return err == -ENOENT ? 0 : err;
347}
348
203static int dsa_slave_port_fdb_add(struct net_device *dev, 349static int dsa_slave_port_fdb_add(struct net_device *dev,
204 struct switchdev_obj *obj) 350 struct switchdev_obj *obj)
205{ 351{
@@ -341,6 +487,9 @@ static int dsa_slave_port_obj_add(struct net_device *dev,
341 case SWITCHDEV_OBJ_PORT_FDB: 487 case SWITCHDEV_OBJ_PORT_FDB:
342 err = dsa_slave_port_fdb_add(dev, obj); 488 err = dsa_slave_port_fdb_add(dev, obj);
343 break; 489 break;
490 case SWITCHDEV_OBJ_PORT_VLAN:
491 err = dsa_slave_port_vlan_add(dev, obj);
492 break;
344 default: 493 default:
345 err = -EOPNOTSUPP; 494 err = -EOPNOTSUPP;
346 break; 495 break;
@@ -358,6 +507,9 @@ static int dsa_slave_port_obj_del(struct net_device *dev,
358 case SWITCHDEV_OBJ_PORT_FDB: 507 case SWITCHDEV_OBJ_PORT_FDB:
359 err = dsa_slave_port_fdb_del(dev, obj); 508 err = dsa_slave_port_fdb_del(dev, obj);
360 break; 509 break;
510 case SWITCHDEV_OBJ_PORT_VLAN:
511 err = dsa_slave_port_vlan_del(dev, obj);
512 break;
361 default: 513 default:
362 err = -EOPNOTSUPP; 514 err = -EOPNOTSUPP;
363 break; 515 break;
@@ -375,6 +527,9 @@ static int dsa_slave_port_obj_dump(struct net_device *dev,
375 case SWITCHDEV_OBJ_PORT_FDB: 527 case SWITCHDEV_OBJ_PORT_FDB:
376 err = dsa_slave_port_fdb_dump(dev, obj); 528 err = dsa_slave_port_fdb_dump(dev, obj);
377 break; 529 break;
530 case SWITCHDEV_OBJ_PORT_VLAN:
531 err = dsa_slave_port_vlan_dump(dev, obj);
532 break;
378 default: 533 default:
379 err = -EOPNOTSUPP; 534 err = -EOPNOTSUPP;
380 break; 535 break;
@@ -794,6 +949,9 @@ static const struct net_device_ops dsa_slave_netdev_ops = {
794 .ndo_netpoll_cleanup = dsa_slave_netpoll_cleanup, 949 .ndo_netpoll_cleanup = dsa_slave_netpoll_cleanup,
795 .ndo_poll_controller = dsa_slave_poll_controller, 950 .ndo_poll_controller = dsa_slave_poll_controller,
796#endif 951#endif
952 .ndo_bridge_getlink = switchdev_port_bridge_getlink,
953 .ndo_bridge_setlink = switchdev_port_bridge_setlink,
954 .ndo_bridge_dellink = switchdev_port_bridge_dellink,
797}; 955};
798 956
799static const struct switchdev_ops dsa_slave_switchdev_ops = { 957static const struct switchdev_ops dsa_slave_switchdev_ops = {