diff options
-rw-r--r-- | target/linux/realtek/patches-5.10/713-v5.12-net-dsa-configure-better-brport-flags-when-ports-lea.patch | 148 |
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 |