aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--target/linux/realtek/patches-5.10/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch148
1 files changed, 148 insertions, 0 deletions
diff --git a/target/linux/realtek/patches-5.10/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch b/target/linux/realtek/patches-5.10/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch
new file mode 100644
index 0000000000..a3bfec59ab
--- /dev/null
+++ b/target/linux/realtek/patches-5.10/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch
@@ -0,0 +1,148 @@
+From: Vladimir Oltean <vladimir.oltean@nxp.com>
+Date: Fri, 12 Feb 2021 17:15:54 +0200
+Subject: [PATCH] net: dsa: configure better brport flags when ports leave the
+ bridge
+
+Bugfixed version of upstream commit 5e38c15856e9 ("net: dsa: configure
+better brport flags when ports leave the bridge")
+
+For a DSA switch port operating in standalone mode, address learning
+doesn't make much sense since that is a bridge function. In fact,
+address learning even breaks setups such as this one:
+
+ +---------------------------------------------+
+ | |
+ | +-------------------+ |
+ | | br0 | send receive |
+ | +--------+-+--------+ +--------+ +--------+ |
+ | | | | | | | | | |
+ | | swp0 | | swp1 | | swp2 | | swp3 | |
+ | | | | | | | | | |
+ +-+--------+-+--------+-+--------+-+--------+-+
+ | ^ | ^
+ | | | |
+ | +-----------+ |
+ | |
+ +--------------------------------+
+
+because if the switch has a single FDB (can offload a single bridge)
+then source address learning on swp3 can "steal" the source MAC address
+of swp2 from br0's FDB, because learning frames coming from swp2 will be
+done twice: first on the swp1 ingress port, second on the swp3 ingress
+port. So the hardware FDB will become out of sync with the software
+bridge, and when swp2 tries to send one more packet towards swp1, the
+ASIC will attempt to short-circuit the forwarding path and send it
+directly to swp3 (since that's the last port it learned that address on),
+which it obviously can't, because swp3 operates in standalone mode.
+
+So DSA drivers operating in standalone mode should still configure a
+list of bridge port flags even when they are standalone. Currently DSA
+attempts to call dsa_port_bridge_flags with 0, which disables egress
+flooding of unknown unicast and multicast, something which doesn't make
+much sense. For the switches that implement .port_egress_floods - b53
+and mv88e6xxx, it probably doesn't matter too much either, since they
+can possibly inject traffic from the CPU into a standalone port,
+regardless of MAC DA, even if egress flooding is turned off for that
+port, but certainly not all DSA switches can do that - sja1105, for
+example, can't. So it makes sense to use a better common default there,
+such as "flood everything".
+
+It should also be noted that what DSA calls "dsa_port_bridge_flags()"
+is a degenerate name for just calling .port_egress_floods(), since
+nothing else is implemented - not learning, in particular. But disabling
+address learning, something that this driver is also coding up for, will
+be supported by individual drivers once .port_egress_floods is replaced
+with a more generic .port_bridge_flags.
+
+Previous attempts to code up this logic have been in the common bridge
+layer, but as pointed out by Ido Schimmel, there are corner cases that
+are missed when doing that:
+https://patchwork.kernel.org/project/netdevbpf/patch/20210209151936.97382-5-olteanv@gmail.com/
+
+So, at least for now, let's leave DSA in charge of setting port flags
+before and after the bridge join and leave.
+
+Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
+Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+[ backport and bugfix: break dsa_port_bridge_flags() out of loop ]
+Signed-off-by: Bjørn Mork <bjorn@mork.no>
+---
+ net/dsa/port.c | 45 ++++++++++++++++++++++++++++++++++++++-------
+ 1 file changed, 38 insertions(+), 7 deletions(-)
+
+--- a/net/dsa/port.c
++++ b/net/dsa/port.c
+@@ -134,6 +134,27 @@ void dsa_port_disable(struct dsa_port *d
+ rtnl_unlock();
+ }
+
++static void dsa_port_change_brport_flags(struct dsa_port *dp,
++ bool bridge_offload)
++{
++ unsigned long mask, flags;
++ int flag, err;
++
++ mask = BR_LEARNING | BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD;
++ if (bridge_offload)
++ flags = mask;
++ else
++ flags = mask & ~BR_LEARNING;
++
++ for_each_set_bit(flag, &mask, 32) {
++ err = dsa_port_pre_bridge_flags(dp, BIT(flag), NULL, NULL);
++ if (err)
++ flags &= ~BIT(flag);
++ }
++
++ dsa_port_bridge_flags(dp, flags, NULL, NULL);
++}
++
+ int dsa_port_bridge_join(struct dsa_port *dp, struct net_device *br)
+ {
+ struct dsa_notifier_bridge_info info = {
+@@ -144,10 +165,10 @@ int dsa_port_bridge_join(struct dsa_port
+ };
+ int err;
+
+- /* Set the flooding mode before joining the port in the switch */
+- err = dsa_port_bridge_flags(dp, BR_FLOOD | BR_MCAST_FLOOD, NULL, NULL);
+- if (err)
+- return err;
++ /* Notify the port driver to set its configurable flags in a way that
++ * matches the initial settings of a bridge port.
++ */
++ dsa_port_change_brport_flags(dp, true);
+
+ /* Here the interface is already bridged. Reflect the current
+ * configuration so that drivers can program their chips accordingly.
+@@ -158,7 +179,7 @@ int dsa_port_bridge_join(struct dsa_port
+
+ /* The bridging is rolled back on error */
+ if (err) {
+- dsa_port_bridge_flags(dp, 0, NULL, NULL);
++ dsa_port_change_brport_flags(dp, false);
+ dp->bridge_dev = NULL;
+ }
+
+@@ -184,8 +205,18 @@ void dsa_port_bridge_leave(struct dsa_po
+ if (err)
+ pr_err("DSA: failed to notify DSA_NOTIFIER_BRIDGE_LEAVE\n");
+
+- /* Port is leaving the bridge, disable flooding */
+- dsa_port_bridge_flags(dp, 0, NULL, NULL);
++ /* Configure the port for standalone mode (no address learning,
++ * flood everything).
++ * The bridge only emits SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS events
++ * when the user requests it through netlink or sysfs, but not
++ * automatically at port join or leave, so we need to handle resetting
++ * the brport flags ourselves. But we even prefer it that way, because
++ * otherwise, some setups might never get the notification they need,
++ * for example, when a port leaves a LAG that offloads the bridge,
++ * it becomes standalone, but as far as the bridge is concerned, no
++ * port ever left.
++ */
++ dsa_port_change_brport_flags(dp, false);
+
+ /* Port left the bridge, put in BR_STATE_DISABLED by the bridge layer,
+ * so allow it to be in BR_STATE_FORWARDING to be kept functional