diff options
Diffstat (limited to 'target/linux/generic/backport-5.10/725-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch')
-rw-r--r-- | target/linux/generic/backport-5.10/725-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch | 264 |
1 files changed, 0 insertions, 264 deletions
diff --git a/target/linux/generic/backport-5.10/725-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch b/target/linux/generic/backport-5.10/725-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch deleted file mode 100644 index e7b9af1951..0000000000 --- a/target/linux/generic/backport-5.10/725-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch +++ /dev/null @@ -1,264 +0,0 @@ -From d5f19486cee79d04c054427577ac96ed123706db Mon Sep 17 00:00:00 2001 -From: Vladimir Oltean <vladimir.oltean@nxp.com> -Date: Wed, 6 Jan 2021 11:51:35 +0200 -Subject: [PATCH] net: dsa: listen for SWITCHDEV_{FDB,DEL}_ADD_TO_DEVICE on - foreign bridge neighbors - -Some DSA switches (and not only) cannot learn source MAC addresses from -packets injected from the CPU. They only perform hardware address -learning from inbound traffic. - -This can be problematic when we have a bridge spanning some DSA switch -ports and some non-DSA ports (which we'll call "foreign interfaces" from -DSA's perspective). - -There are 2 classes of problems created by the lack of learning on -CPU-injected traffic: -- excessive flooding, due to the fact that DSA treats those addresses as - unknown -- the risk of stale routes, which can lead to temporary packet loss - -To illustrate the second class, consider the following situation, which -is common in production equipment (wireless access points, where there -is a WLAN interface and an Ethernet switch, and these form a single -bridging domain). - - AP 1: - +------------------------------------------------------------------------+ - | br0 | - +------------------------------------------------------------------------+ - +------------+ +------------+ +------------+ +------------+ +------------+ - | swp0 | | swp1 | | swp2 | | swp3 | | wlan0 | - +------------+ +------------+ +------------+ +------------+ +------------+ - | ^ ^ - | | | - | | | - | Client A Client B - | - | - | - +------------+ +------------+ +------------+ +------------+ +------------+ - | swp0 | | swp1 | | swp2 | | swp3 | | wlan0 | - +------------+ +------------+ +------------+ +------------+ +------------+ - +------------------------------------------------------------------------+ - | br0 | - +------------------------------------------------------------------------+ - AP 2 - -- br0 of AP 1 will know that Clients A and B are reachable via wlan0 -- the hardware fdb of a DSA switch driver today is not kept in sync with - the software entries on other bridge ports, so it will not know that - clients A and B are reachable via the CPU port UNLESS the hardware - switch itself performs SA learning from traffic injected from the CPU. - Nonetheless, a substantial number of switches don't. -- the hardware fdb of the DSA switch on AP 2 may autonomously learn that - Client A and B are reachable through swp0. Therefore, the software br0 - of AP 2 also may or may not learn this. In the example we're - illustrating, some Ethernet traffic has been going on, and br0 from AP - 2 has indeed learnt that it can reach Client B through swp0. - -One of the wireless clients, say Client B, disconnects from AP 1 and -roams to AP 2. The topology now looks like this: - - AP 1: - +------------------------------------------------------------------------+ - | br0 | - +------------------------------------------------------------------------+ - +------------+ +------------+ +------------+ +------------+ +------------+ - | swp0 | | swp1 | | swp2 | | swp3 | | wlan0 | - +------------+ +------------+ +------------+ +------------+ +------------+ - | ^ - | | - | Client A - | - | - | Client B - | | - | v - +------------+ +------------+ +------------+ +------------+ +------------+ - | swp0 | | swp1 | | swp2 | | swp3 | | wlan0 | - +------------+ +------------+ +------------+ +------------+ +------------+ - +------------------------------------------------------------------------+ - | br0 | - +------------------------------------------------------------------------+ - AP 2 - -- br0 of AP 1 still knows that Client A is reachable via wlan0 (no change) -- br0 of AP 1 will (possibly) know that Client B has left wlan0. There - are cases where it might never find out though. Either way, DSA today - does not process that notification in any way. -- the hardware FDB of the DSA switch on AP 1 may learn autonomously that - Client B can be reached via swp0, if it receives any packet with - Client 1's source MAC address over Ethernet. -- the hardware FDB of the DSA switch on AP 2 still thinks that Client B - can be reached via swp0. It does not know that it has roamed to wlan0, - because it doesn't perform SA learning from the CPU port. - -Now Client A contacts Client B. -AP 1 routes the packet fine towards swp0 and delivers it on the Ethernet -segment. -AP 2 sees a frame on swp0 and its fdb says that the destination is swp0. -Hairpinning is disabled => drop. - -This problem comes from the fact that these switches have a 'blind spot' -for addresses coming from software bridging. The generic solution is not -to assume that hardware learning can be enabled somehow, but to listen -to more bridge learning events. It turns out that the bridge driver does -learn in software from all inbound frames, in __br_handle_local_finish. -A proper SWITCHDEV_FDB_ADD_TO_DEVICE notification is emitted for the -addresses serviced by the bridge on 'foreign' interfaces. The software -bridge also does the right thing on migration, by notifying that the old -entry is deleted, so that does not need to be special-cased in DSA. When -it is deleted, we just need to delete our static FDB entry towards the -CPU too, and wait. - -The problem is that DSA currently only cares about SWITCHDEV_FDB_ADD_TO_DEVICE -events received on its own interfaces, such as static FDB entries. - -Luckily we can change that, and DSA can listen to all switchdev FDB -add/del events in the system and figure out if those events were emitted -by a bridge that spans at least one of DSA's own ports. In case that is -true, DSA will also offload that address towards its own CPU port, in -the eventuality that there might be bridge clients attached to the DSA -switch who want to talk to the station connected to the foreign -interface. - -In terms of implementation, we need to keep the fdb_info->added_by_user -check for the case where the switchdev event was targeted directly at a -DSA switch port. But we don't need to look at that flag for snooped -events. So the check is currently too late, we need to move it earlier. -This also simplifies the code a bit, since we avoid uselessly allocating -and freeing switchdev_work. - -We could probably do some improvements in the future. For example, -multi-bridge support is rudimentary at the moment. If there are two -bridges spanning a DSA switch's ports, and both of them need to service -the same MAC address, then what will happen is that the migration of one -of those stations will trigger the deletion of the FDB entry from the -CPU port while it is still used by other bridge. That could be improved -with reference counting but is left for another time. - -This behavior needs to be enabled at driver level by setting -ds->assisted_learning_on_cpu_port = true. This is because we don't want -to inflict a potential performance penalty (accesses through -MDIO/I2C/SPI are expensive) to hardware that really doesn't need it -because address learning on the CPU port works there. - -Reported-by: DENG Qingfang <dqfext@gmail.com> -Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> -Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> -Reviewed-by: Andrew Lunn <andrew@lunn.ch> -Signed-off-by: Jakub Kicinski <kuba@kernel.org> ---- - include/net/dsa.h | 5 +++++ - net/dsa/slave.c | 66 +++++++++++++++++++++++++++++++++++++++++++++---------- - 2 files changed, 60 insertions(+), 11 deletions(-) - ---- a/include/net/dsa.h -+++ b/include/net/dsa.h -@@ -317,6 +317,11 @@ struct dsa_switch { - */ - bool untag_bridge_pvid; - -+ /* Let DSA manage the FDB entries towards the CPU, based on the -+ * software bridge database. -+ */ -+ bool assisted_learning_on_cpu_port; -+ - /* In case vlan_filtering_is_global is set, the VLAN awareness state - * should be retrieved from here and not from the per-port settings. - */ ---- a/net/dsa/slave.c -+++ b/net/dsa/slave.c -@@ -2149,6 +2149,28 @@ static void dsa_slave_switchdev_event_wo - dev_put(dp->slave); - } - -+static int dsa_lower_dev_walk(struct net_device *lower_dev, -+ struct netdev_nested_priv *priv) -+{ -+ if (dsa_slave_dev_check(lower_dev)) { -+ priv->data = (void *)netdev_priv(lower_dev); -+ return 1; -+ } -+ -+ return 0; -+} -+ -+static struct dsa_slave_priv *dsa_slave_dev_lower_find(struct net_device *dev) -+{ -+ struct netdev_nested_priv priv = { -+ .data = NULL, -+ }; -+ -+ netdev_walk_all_lower_dev_rcu(dev, dsa_lower_dev_walk, &priv); -+ -+ return (struct dsa_slave_priv *)priv.data; -+} -+ - /* Called under rcu_read_lock() */ - static int dsa_slave_switchdev_event(struct notifier_block *unused, - unsigned long event, void *ptr) -@@ -2167,10 +2189,37 @@ static int dsa_slave_switchdev_event(str - return notifier_from_errno(err); - case SWITCHDEV_FDB_ADD_TO_DEVICE: - case SWITCHDEV_FDB_DEL_TO_DEVICE: -- if (!dsa_slave_dev_check(dev)) -- return NOTIFY_DONE; -+ fdb_info = ptr; -+ -+ if (dsa_slave_dev_check(dev)) { -+ if (!fdb_info->added_by_user) -+ return NOTIFY_OK; -+ -+ dp = dsa_slave_to_port(dev); -+ } else { -+ /* Snoop addresses learnt on foreign interfaces -+ * bridged with us, for switches that don't -+ * automatically learn SA from CPU-injected traffic -+ */ -+ struct net_device *br_dev; -+ struct dsa_slave_priv *p; -+ -+ br_dev = netdev_master_upper_dev_get_rcu(dev); -+ if (!br_dev) -+ return NOTIFY_DONE; -+ -+ if (!netif_is_bridge_master(br_dev)) -+ return NOTIFY_DONE; -+ -+ p = dsa_slave_dev_lower_find(br_dev); -+ if (!p) -+ return NOTIFY_DONE; - -- dp = dsa_slave_to_port(dev); -+ dp = p->dp->cpu_dp; -+ -+ if (!dp->ds->assisted_learning_on_cpu_port) -+ return NOTIFY_DONE; -+ } - - if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del) - return NOTIFY_DONE; -@@ -2185,18 +2234,13 @@ static int dsa_slave_switchdev_event(str - switchdev_work->port = dp->index; - switchdev_work->event = event; - -- fdb_info = ptr; -- -- if (!fdb_info->added_by_user) { -- kfree(switchdev_work); -- return NOTIFY_OK; -- } -- - ether_addr_copy(switchdev_work->addr, - fdb_info->addr); - switchdev_work->vid = fdb_info->vid; - -- dev_hold(dev); -+ /* Hold a reference on the slave for dsa_fdb_offload_notify */ -+ if (dsa_is_user_port(dp->ds, dp->index)) -+ dev_hold(dev); - dsa_schedule_work(&switchdev_work->work); - break; - default: |