Skip to content

Commit

Permalink
net: dsa: Unset vlan_filtering when ports leave the bridge
Browse files Browse the repository at this point in the history
When ports are standalone (after they left the bridge), they should have
no VLAN filtering semantics (they should pass all traffic to the CPU).
Currently this is not true for switchdev drivers, because the bridge
"forgets" to unset that.

Normally one would think that doing this at the bridge layer would be a
better idea, i.e. call br_vlan_filter_toggle() from br_del_if(), similar
to how nbp_vlan_init() is called from br_add_if().

However what complicates that approach, and makes this one preferable,
is the fact that for the bridge core, vlan_filtering is a per-bridge
setting, whereas for switchdev/DSA it is per-port. Also there are
switches where the setting is per the entire device, and unsetting
vlan_filtering one by one, for each leaving port, would not be possible
from the bridge core without a certain level of awareness. So do this in
DSA and let drivers be unaware of it.

Signed-off-by: Vladimir Oltean <olteanv@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Vladimir Oltean authored and David S. Miller committed May 1, 2019
1 parent 7228b23 commit d371b7c
Showing 1 changed file with 29 additions and 0 deletions.
29 changes: 29 additions & 0 deletions net/dsa/switch.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* (at your option) any later version.
*/

#include <linux/if_bridge.h>
#include <linux/netdevice.h>
#include <linux/notifier.h>
#include <linux/if_vlan.h>
Expand Down Expand Up @@ -71,13 +72,41 @@ static int dsa_switch_bridge_join(struct dsa_switch *ds,
static int dsa_switch_bridge_leave(struct dsa_switch *ds,
struct dsa_notifier_bridge_info *info)
{
bool unset_vlan_filtering = br_vlan_enabled(info->br);
int err, i;

if (ds->index == info->sw_index && ds->ops->port_bridge_leave)
ds->ops->port_bridge_leave(ds, info->port, info->br);

if (ds->index != info->sw_index && ds->ops->crosschip_bridge_leave)
ds->ops->crosschip_bridge_leave(ds, info->sw_index, info->port,
info->br);

/* If the bridge was vlan_filtering, the bridge core doesn't trigger an
* event for changing vlan_filtering setting upon slave ports leaving
* it. That is a good thing, because that lets us handle it and also
* handle the case where the switch's vlan_filtering setting is global
* (not per port). When that happens, the correct moment to trigger the
* vlan_filtering callback is only when the last port left this bridge.
*/
if (unset_vlan_filtering && ds->vlan_filtering_is_global) {
for (i = 0; i < ds->num_ports; i++) {
if (i == info->port)
continue;
if (dsa_to_port(ds, i)->bridge_dev == info->br) {
unset_vlan_filtering = false;
break;
}
}
}
if (unset_vlan_filtering) {
struct switchdev_trans trans = {0};

err = dsa_port_vlan_filtering(&ds->ports[info->port],
false, &trans);
if (err && err != EOPNOTSUPP)
return err;
}
return 0;
}

Expand Down

0 comments on commit d371b7c

Please sign in to comment.