Documentation/networking/switchdev.rst says: - with VLAN filtering turned off, the bridge will process all ingress traffic for the port, except for the traffic tagged with a VLAN ID destined for a VLAN upper. But there is currently no way to configure this in dsa. The vlan upper will trigger a vlan add to the driver, but it is the same message as a newly configured bridge VLAN. Therefore traffic tagged with the VID will continue to be forwarded to other ports, and therefore we cannot support VLAN uppers on ports of a VLAN unaware bridges. Signed-off-by: Jonas Gorski --- net/dsa/port.c | 23 ++++------------------- net/dsa/user.c | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 20 deletions(-) diff --git a/net/dsa/port.c b/net/dsa/port.c index 082573ae6864..d7746885f7e0 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -728,35 +728,20 @@ static bool dsa_port_can_apply_vlan_filtering(struct dsa_port *dp, { struct dsa_switch *ds = dp->ds; struct dsa_port *other_dp; - int err; - /* VLAN awareness was off, so the question is "can we turn it on". + /* VLAN awareness was on, so the question is "can we turn it off". * We may have had 8021q uppers, those need to go. Make sure we don't * enter an inconsistent state: deny changing the VLAN awareness state * as long as we have 8021q uppers. */ - if (vlan_filtering && dsa_port_is_user(dp)) { - struct net_device *br = dsa_port_bridge_dev_get(dp); + if (!vlan_filtering && dsa_port_is_user(dp)) { struct net_device *upper_dev, *user = dp->user; struct list_head *iter; netdev_for_each_upper_dev_rcu(user, upper_dev, iter) { - struct bridge_vlan_info br_info; - u16 vid; - - if (!is_vlan_dev(upper_dev)) - continue; - - vid = vlan_dev_vlan_id(upper_dev); - - /* br_vlan_get_info() returns -EINVAL or -ENOENT if the - * device, respectively the VID is not found, returning - * 0 means success, which is a failure for us here. - */ - err = br_vlan_get_info(br, vid, &br_info); - if (err == 0) { + if (is_vlan_dev(upper_dev)) { NL_SET_ERR_MSG_MOD(extack, - "Must first remove VLAN uppers having VIDs also present in bridge"); + "Must first remove VLAN uppers from bridged ports"); return false; } } diff --git a/net/dsa/user.c b/net/dsa/user.c index e8c6452780b0..35265829aa90 100644 --- a/net/dsa/user.c +++ b/net/dsa/user.c @@ -3156,6 +3156,30 @@ dsa_prevent_bridging_8021q_upper(struct net_device *dev, return NOTIFY_DONE; } +/* Must be called under rcu_read_lock() */ +static int +dsa_user_vlan_check_for_any_8021q_uppers(struct dsa_port *dp) +{ + struct dsa_switch *ds = dp->ds; + struct dsa_port *other_dp; + + dsa_switch_for_each_user_port(other_dp, ds) { + struct net_device *user = other_dp->user; + struct net_device *upper_dev; + struct list_head *iter; + + if (!dsa_port_bridge_same(dp, other_dp)) + continue; + + netdev_for_each_upper_dev_rcu(user, upper_dev, iter) { + if (is_vlan_dev(upper_dev)) + return -EBUSY; + } + } + + return 0; +} + static int dsa_user_check_8021q_upper(struct net_device *dev, struct netdev_notifier_changeupper_info *info) @@ -3167,10 +3191,22 @@ dsa_user_check_8021q_upper(struct net_device *dev, int err = NOTIFY_DONE; u16 vid; - if (!br || !br_vlan_enabled(br)) + if (!br) return NOTIFY_DONE; extack = netdev_notifier_info_to_extack(&info->info); + + if (!br_vlan_enabled(br)) { + rcu_read_lock(); + err = dsa_user_vlan_check_for_any_8021q_uppers(dp); + rcu_read_unlock(); + if (err) { + NL_SET_ERR_MSG_MOD(extack, + "VLAN uppers not supported with non filtering bridges"); + return notifier_from_errno(err); + } + } + vid = vlan_dev_vlan_id(info->upper_dev); /* br_vlan_get_info() returns -EINVAL or -ENOENT if the -- 2.43.0