diff mbox series

[net,2/2] net: dsa: mv88e6xxx: Never apply VLANs on standalone ports to VTU

Message ID 20210308150405.3694678-3-tobias@waldekranz.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series net: dsa: Accept software VLANs for stacked interfaces | expand

Checks

Context Check Description
netdev/cover_letter success Link
netdev/fixes_present success Link
netdev/patch_count success Link
netdev/tree_selection success Clearly marked for net
netdev/subject_prefix success Link
netdev/cc_maintainers success CCed 7 of 7 maintainers
netdev/source_inline success Was 0 now: 0
netdev/verify_signedoff success Link
netdev/module_param success Was 0 now: 0
netdev/build_32bit success Errors and warnings before: 0 this patch: 0
netdev/kdoc success Errors and warnings before: 0 this patch: 0
netdev/verify_fixes success Link
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 64 lines checked
netdev/build_allmodconfig_warn success Errors and warnings before: 0 this patch: 0
netdev/header_inline success Link

Commit Message

Tobias Waldekranz March 8, 2021, 3:04 p.m. UTC
Standalone ports always have VLAN filtering (Port Control 2, 802.1Q
Mode) disabled. So adding VIDs for any VLAN uppers to the VTU does not
make one bit of difference on the ingress filtering, the CPU will
still receive traffic from all VLANs.

It does however needlessly consume a precious global resource, namely
a VID. Therefore, we refine the requirement for accepting a VLAN on a
port by mandating that the port must be offloading a bridge, in which
case the device will actually make use of the filtering.

Fixes: 061f6a505ac3 ("net: dsa: Add ndo_vlan_rx_{add, kill}_vid implementation")
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
---
 drivers/net/dsa/mv88e6xxx/chip.c | 40 ++++++++++++++++++++++++++++----
 1 file changed, 36 insertions(+), 4 deletions(-)
diff mbox series

Patch

diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c
index 903d619e08ed..0ba44bcac7da 100644
--- a/drivers/net/dsa/mv88e6xxx/chip.c
+++ b/drivers/net/dsa/mv88e6xxx/chip.c
@@ -1618,6 +1618,38 @@  static int mv88e6xxx_port_vlan_filtering(struct dsa_switch *ds, int port,
 	return err;
 }
 
+static bool mv88e6xxx_port_offloads_vlans(struct dsa_switch *ds, int port)
+{
+	struct dsa_port *dp = dsa_to_port(ds, port);
+	struct mv88e6xxx_chip *chip = ds->priv;
+
+	if (!mv88e6xxx_max_vid(chip))
+		return false;
+
+	/* There are roughly two scenarios in which VLANs may be added
+	 * to a non-bridged port:
+	 *  (a)        (b)     br0
+	 *                     /
+	 *      vlan1       bond0
+	 *       /          /   \
+	 *     swp0       swp0  swp1
+	 *
+	 * Either (a) a VLAN upper is added to a non-bridged port; or
+	 * (b) a port is an indirect lower to a bridge via some
+	 * stacked interface that is not offloaded, e.g. a bond in
+	 * broadcast mode.
+	 *
+	 * We still get a callback in these cases as there are other
+	 * DSA devices which cannot control VLAN filtering per
+	 * port. mv88e6xxx is not one of those, so we can safely
+	 * fallback to software VLANs.
+	 */
+	if (dsa_is_user_port(ds, port) && !dp->bridge_dev)
+		return false;
+
+	return true;
+}
+
 static int
 mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
 			    const struct switchdev_obj_port_vlan *vlan)
@@ -1625,9 +1657,6 @@  mv88e6xxx_port_vlan_prepare(struct dsa_switch *ds, int port,
 	struct mv88e6xxx_chip *chip = ds->priv;
 	int err;
 
-	if (!mv88e6xxx_max_vid(chip))
-		return -EOPNOTSUPP;
-
 	/* If the requested port doesn't belong to the same bridge as the VLAN
 	 * members, do not support it (yet) and fallback to software VLAN.
 	 */
@@ -1993,6 +2022,9 @@  static int mv88e6xxx_port_vlan_add(struct dsa_switch *ds, int port,
 	u8 member;
 	int err;
 
+	if (!mv88e6xxx_port_offloads_vlans(ds, port))
+		return -EOPNOTSUPP;
+
 	err = mv88e6xxx_port_vlan_prepare(ds, port, vlan);
 	if (err)
 		return err;
@@ -2081,7 +2113,7 @@  static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
 	int err = 0;
 	u16 pvid;
 
-	if (!mv88e6xxx_max_vid(chip))
+	if (!mv88e6xxx_port_offloads_vlans(ds, port))
 		return -EOPNOTSUPP;
 
 	mv88e6xxx_reg_lock(chip);