diff options
30 files changed, 2365 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.10/770-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch b/target/linux/generic/backport-5.10/770-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch new file mode 100644 index 0000000000..c43cb4d1f2 --- /dev/null +++ b/target/linux/generic/backport-5.10/770-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch @@ -0,0 +1,126 @@ +From 90dc8fd36078a536671adae884d0b929cce6480a Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 6 Jan 2021 11:51:30 +0200 +Subject: [PATCH] net: bridge: notify switchdev of disappearance of old FDB + entry upon migration + +Currently the bridge emits atomic switchdev notifications for +dynamically learnt FDB entries. Monitoring these notifications works +wonders for switchdev drivers that want to keep their hardware FDB in +sync with the bridge's FDB. + +For example station A wants to talk to station B in the diagram below, +and we are concerned with the behavior of the bridge on the DUT device: + + DUT + +-------------------------------------+ + | br0 | + | +------+ +------+ +------+ +------+ | + | | | | | | | | | | + | | swp0 | | swp1 | | swp2 | | eth0 | | + +-------------------------------------+ + | | | + Station A | | + | | + +--+------+--+ +--+------+--+ + | | | | | | | | + | | swp0 | | | | swp0 | | + Another | +------+ | | +------+ | Another + switch | br0 | | br0 | switch + | +------+ | | +------+ | + | | | | | | | | + | | swp1 | | | | swp1 | | + +--+------+--+ +--+------+--+ + | + Station B + +Interfaces swp0, swp1, swp2 are handled by a switchdev driver that has +the following property: frames injected from its control interface bypass +the internal address analyzer logic, and therefore, this hardware does +not learn from the source address of packets transmitted by the network +stack through it. So, since bridging between eth0 (where Station B is +attached) and swp0 (where Station A is attached) is done in software, +the switchdev hardware will never learn the source address of Station B. +So the traffic towards that destination will be treated as unknown, i.e. +flooded. + +This is where the bridge notifications come in handy. When br0 on the +DUT sees frames with Station B's MAC address on eth0, the switchdev +driver gets these notifications and can install a rule to send frames +towards Station B's address that are incoming from swp0, swp1, swp2, +only towards the control interface. This is all switchdev driver private +business, which the notification makes possible. + +All is fine until someone unplugs Station B's cable and moves it to the +other switch: + + DUT + +-------------------------------------+ + | br0 | + | +------+ +------+ +------+ +------+ | + | | | | | | | | | | + | | swp0 | | swp1 | | swp2 | | eth0 | | + +-------------------------------------+ + | | | + Station A | | + | | + +--+------+--+ +--+------+--+ + | | | | | | | | + | | swp0 | | | | swp0 | | + Another | +------+ | | +------+ | Another + switch | br0 | | br0 | switch + | +------+ | | +------+ | + | | | | | | | | + | | swp1 | | | | swp1 | | + +--+------+--+ +--+------+--+ + | + Station B + +Luckily for the use cases we care about, Station B is noisy enough that +the DUT hears it (on swp1 this time). swp1 receives the frames and +delivers them to the bridge, who enters the unlikely path in br_fdb_update +of updating an existing entry. It moves the entry in the software bridge +to swp1 and emits an addition notification towards that. + +As far as the switchdev driver is concerned, all that it needs to ensure +is that traffic between Station A and Station B is not forever broken. +If it does nothing, then the stale rule to send frames for Station B +towards the control interface remains in place. But Station B is no +longer reachable via the control interface, but via a port that can +offload the bridge port learning attribute. It's just that the port is +prevented from learning this address, since the rule overrides FDB +updates. So the rule needs to go. The question is via what mechanism. + +It sure would be possible for this switchdev driver to keep track of all +addresses which are sent to the control interface, and then also listen +for bridge notifier events on its own ports, searching for the ones that +have a MAC address which was previously sent to the control interface. +But this is cumbersome and inefficient. Instead, with one small change, +the bridge could notify of the address deletion from the old port, in a +symmetrical manner with how it did for the insertion. Then the switchdev +driver would not be required to monitor learn/forget events for its own +ports. It could just delete the rule towards the control interface upon +bridge entry migration. This would make hardware address learning be +possible again. Then it would take a few more packets until the hardware +and software FDB would be in sync again. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> +Reviewed-by: Ido Schimmel <idosch@nvidia.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + net/bridge/br_fdb.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -602,6 +602,7 @@ void br_fdb_update(struct net_bridge *br + /* fastpath: update of existing entry */ + if (unlikely(source != fdb->dst && + !test_bit(BR_FDB_STICKY, &fdb->flags))) { ++ br_switchdev_fdb_notify(fdb, RTM_DELNEIGH); + fdb->dst = source; + fdb_modified = true; + /* Take over HW learned entry */ diff --git a/target/linux/generic/backport-5.10/771-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch b/target/linux/generic/backport-5.10/771-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch new file mode 100644 index 0000000000..e4fd9eb2e4 --- /dev/null +++ b/target/linux/generic/backport-5.10/771-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch @@ -0,0 +1,52 @@ +From 2fd186501b1cff155cc4a755c210793cfc0dffb5 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 6 Jan 2021 11:51:31 +0200 +Subject: [PATCH] net: dsa: be louder when a non-legacy FDB operation fails + +The dev_close() call was added in commit c9eb3e0f8701 ("net: dsa: Add +support for learning FDB through notification") "to indicate inconsistent +situation" when we could not delete an FDB entry from the port. + +bridge fdb del d8:58:d7:00:ca:6d dev swp0 self master + +It is a bit drastic and at the same time not helpful if the above fails +to only print with netdev_dbg log level, but on the other hand to bring +the interface down. + +So increase the verbosity of the error message, and drop dev_close(). + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + net/dsa/slave.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -2030,7 +2030,9 @@ static void dsa_slave_switchdev_event_wo + + err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid); + if (err) { +- netdev_dbg(dev, "fdb add failed err=%d\n", err); ++ netdev_err(dev, ++ "failed to add %pM vid %d to fdb: %d\n", ++ fdb_info->addr, fdb_info->vid, err); + break; + } + fdb_info->offloaded = true; +@@ -2045,9 +2047,11 @@ static void dsa_slave_switchdev_event_wo + + err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid); + if (err) { +- netdev_dbg(dev, "fdb del failed err=%d\n", err); +- dev_close(dev); ++ netdev_err(dev, ++ "failed to delete %pM vid %d from fdb: %d\n", ++ fdb_info->addr, fdb_info->vid, err); + } ++ + break; + } + rtnl_unlock(); diff --git a/target/linux/generic/backport-5.10/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch b/target/linux/generic/backport-5.10/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch new file mode 100644 index 0000000000..31502f5b23 --- /dev/null +++ b/target/linux/generic/backport-5.10/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch @@ -0,0 +1,226 @@ +From c4bb76a9a0ef87c4cc1f636defed5f12deb9f5a7 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 6 Jan 2021 11:51:32 +0200 +Subject: [PATCH] net: dsa: don't use switchdev_notifier_fdb_info in + dsa_switchdev_event_work + +Currently DSA doesn't add FDB entries on the CPU port, because it only +does so through switchdev, which is associated with a net_device, and +there are none of those for the CPU port. + +But actually FDB addresses on the CPU port have some use cases of their +own, if the switchdev operations are initiated from within the DSA +layer. There is just one problem with the existing code: it passes a +structure in dsa_switchdev_event_work which was retrieved directly from +switchdev, so it contains a net_device. We need to generalize the +contents to something that covers the CPU port as well: the "ds, port" +tuple is fine for that. + +Note that the new procedure for notifying the successful FDB offload is +inspired from the rocker model. + +Also, nothing was being done if added_by_user was false. Let's check for +that a lot earlier, and don't actually bother to schedule the worker +for nothing. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + net/dsa/dsa_priv.h | 12 +++++ + net/dsa/slave.c | 106 ++++++++++++++++++++++----------------------- + 2 files changed, 65 insertions(+), 53 deletions(-) + +--- a/net/dsa/dsa_priv.h ++++ b/net/dsa/dsa_priv.h +@@ -73,6 +73,18 @@ struct dsa_notifier_mtu_info { + int mtu; + }; + ++struct dsa_switchdev_event_work { ++ struct dsa_switch *ds; ++ int port; ++ struct work_struct work; ++ unsigned long event; ++ /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and ++ * SWITCHDEV_FDB_DEL_TO_DEVICE ++ */ ++ unsigned char addr[ETH_ALEN]; ++ u16 vid; ++}; ++ + struct dsa_slave_priv { + /* Copy of CPU port xmit for faster access in slave transmit hot path */ + struct sk_buff * (*xmit)(struct sk_buff *skb, +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -2005,76 +2005,66 @@ static int dsa_slave_netdevice_event(str + return NOTIFY_DONE; + } + +-struct dsa_switchdev_event_work { +- struct work_struct work; +- struct switchdev_notifier_fdb_info fdb_info; +- struct net_device *dev; +- unsigned long event; +-}; ++static void ++dsa_fdb_offload_notify(struct dsa_switchdev_event_work *switchdev_work) ++{ ++ struct dsa_switch *ds = switchdev_work->ds; ++ struct switchdev_notifier_fdb_info info; ++ struct dsa_port *dp; ++ ++ if (!dsa_is_user_port(ds, switchdev_work->port)) ++ return; ++ ++ info.addr = switchdev_work->addr; ++ info.vid = switchdev_work->vid; ++ info.offloaded = true; ++ dp = dsa_to_port(ds, switchdev_work->port); ++ call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, ++ dp->slave, &info.info, NULL); ++} + + static void dsa_slave_switchdev_event_work(struct work_struct *work) + { + struct dsa_switchdev_event_work *switchdev_work = + container_of(work, struct dsa_switchdev_event_work, work); +- struct net_device *dev = switchdev_work->dev; +- struct switchdev_notifier_fdb_info *fdb_info; +- struct dsa_port *dp = dsa_slave_to_port(dev); ++ struct dsa_switch *ds = switchdev_work->ds; ++ struct dsa_port *dp; + int err; + ++ dp = dsa_to_port(ds, switchdev_work->port); ++ + rtnl_lock(); + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: +- fdb_info = &switchdev_work->fdb_info; +- if (!fdb_info->added_by_user) +- break; +- +- err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid); ++ err = dsa_port_fdb_add(dp, switchdev_work->addr, ++ switchdev_work->vid); + if (err) { +- netdev_err(dev, +- "failed to add %pM vid %d to fdb: %d\n", +- fdb_info->addr, fdb_info->vid, err); ++ dev_err(ds->dev, ++ "port %d failed to add %pM vid %d to fdb: %d\n", ++ dp->index, switchdev_work->addr, ++ switchdev_work->vid, err); + break; + } +- fdb_info->offloaded = true; +- call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev, +- &fdb_info->info, NULL); ++ dsa_fdb_offload_notify(switchdev_work); + break; + + case SWITCHDEV_FDB_DEL_TO_DEVICE: +- fdb_info = &switchdev_work->fdb_info; +- if (!fdb_info->added_by_user) +- break; +- +- err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid); ++ err = dsa_port_fdb_del(dp, switchdev_work->addr, ++ switchdev_work->vid); + if (err) { +- netdev_err(dev, +- "failed to delete %pM vid %d from fdb: %d\n", +- fdb_info->addr, fdb_info->vid, err); ++ dev_err(ds->dev, ++ "port %d failed to delete %pM vid %d from fdb: %d\n", ++ dp->index, switchdev_work->addr, ++ switchdev_work->vid, err); + } + + break; + } + rtnl_unlock(); + +- kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); +- dev_put(dev); +-} +- +-static int +-dsa_slave_switchdev_fdb_work_init(struct dsa_switchdev_event_work * +- switchdev_work, +- const struct switchdev_notifier_fdb_info * +- fdb_info) +-{ +- memcpy(&switchdev_work->fdb_info, fdb_info, +- sizeof(switchdev_work->fdb_info)); +- switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); +- if (!switchdev_work->fdb_info.addr) +- return -ENOMEM; +- ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, +- fdb_info->addr); +- return 0; ++ if (dsa_is_user_port(ds, dp->index)) ++ dev_put(dp->slave); + } + + /* Called under rcu_read_lock() */ +@@ -2082,7 +2072,9 @@ static int dsa_slave_switchdev_event(str + unsigned long event, void *ptr) + { + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); ++ const struct switchdev_notifier_fdb_info *fdb_info; + struct dsa_switchdev_event_work *switchdev_work; ++ struct dsa_port *dp; + int err; + + if (event == SWITCHDEV_PORT_ATTR_SET) { +@@ -2095,20 +2087,32 @@ static int dsa_slave_switchdev_event(str + if (!dsa_slave_dev_check(dev)) + return NOTIFY_DONE; + ++ dp = dsa_slave_to_port(dev); ++ + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (!switchdev_work) + return NOTIFY_BAD; + + INIT_WORK(&switchdev_work->work, + dsa_slave_switchdev_event_work); +- switchdev_work->dev = dev; ++ switchdev_work->ds = dp->ds; ++ switchdev_work->port = dp->index; + switchdev_work->event = event; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + case SWITCHDEV_FDB_DEL_TO_DEVICE: +- if (dsa_slave_switchdev_fdb_work_init(switchdev_work, ptr)) +- goto err_fdb_work_init; ++ 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); + break; + default: +@@ -2118,10 +2122,6 @@ static int dsa_slave_switchdev_event(str + + dsa_schedule_work(&switchdev_work->work); + return NOTIFY_OK; +- +-err_fdb_work_init: +- kfree(switchdev_work); +- return NOTIFY_BAD; + } + + static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused, diff --git a/target/linux/generic/backport-5.10/773-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch b/target/linux/generic/backport-5.10/773-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch new file mode 100644 index 0000000000..49e095a587 --- /dev/null +++ b/target/linux/generic/backport-5.10/773-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch @@ -0,0 +1,85 @@ +From 447d290a58bd335d68f665713842365d3d6447df Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 6 Jan 2021 11:51:33 +0200 +Subject: [PATCH] net: dsa: move switchdev event implementation under the same + switch/case statement + +We'll need to start listening to SWITCHDEV_FDB_{ADD,DEL}_TO_DEVICE +events even for interfaces where dsa_slave_dev_check returns false, so +we need that check inside the switch-case statement for SWITCHDEV_FDB_*. + +This movement also avoids a useless allocation / free of switchdev_work +on the untreated "default event" case. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + net/dsa/slave.c | 35 ++++++++++++++++------------------- + 1 file changed, 16 insertions(+), 19 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -2077,31 +2077,29 @@ static int dsa_slave_switchdev_event(str + struct dsa_port *dp; + int err; + +- if (event == SWITCHDEV_PORT_ATTR_SET) { ++ switch (event) { ++ case SWITCHDEV_PORT_ATTR_SET: + err = switchdev_handle_port_attr_set(dev, ptr, + dsa_slave_dev_check, + dsa_slave_port_attr_set); + return notifier_from_errno(err); +- } +- +- if (!dsa_slave_dev_check(dev)) +- return NOTIFY_DONE; ++ case SWITCHDEV_FDB_ADD_TO_DEVICE: ++ case SWITCHDEV_FDB_DEL_TO_DEVICE: ++ if (!dsa_slave_dev_check(dev)) ++ return NOTIFY_DONE; + +- dp = dsa_slave_to_port(dev); ++ dp = dsa_slave_to_port(dev); + +- switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); +- if (!switchdev_work) +- return NOTIFY_BAD; +- +- INIT_WORK(&switchdev_work->work, +- dsa_slave_switchdev_event_work); +- switchdev_work->ds = dp->ds; +- switchdev_work->port = dp->index; +- switchdev_work->event = event; ++ switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); ++ if (!switchdev_work) ++ return NOTIFY_BAD; ++ ++ INIT_WORK(&switchdev_work->work, ++ dsa_slave_switchdev_event_work); ++ switchdev_work->ds = dp->ds; ++ switchdev_work->port = dp->index; ++ switchdev_work->event = event; + +- switch (event) { +- case SWITCHDEV_FDB_ADD_TO_DEVICE: +- case SWITCHDEV_FDB_DEL_TO_DEVICE: + fdb_info = ptr; + + if (!fdb_info->added_by_user) { +@@ -2114,13 +2112,12 @@ static int dsa_slave_switchdev_event(str + switchdev_work->vid = fdb_info->vid; + + dev_hold(dev); ++ dsa_schedule_work(&switchdev_work->work); + break; + default: +- kfree(switchdev_work); + return NOTIFY_DONE; + } + +- dsa_schedule_work(&switchdev_work->work); + return NOTIFY_OK; + } + diff --git a/target/linux/generic/backport-5.10/774-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch b/target/linux/generic/backport-5.10/774-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch new file mode 100644 index 0000000000..cbf2739469 --- /dev/null +++ b/target/linux/generic/backport-5.10/774-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch @@ -0,0 +1,42 @@ +From 5fb4a451a87d8ed3363d28b63a3295399373d6c4 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 6 Jan 2021 11:51:34 +0200 +Subject: [PATCH] net: dsa: exit early in dsa_slave_switchdev_event if we can't + program the FDB + +Right now, the following would happen for a switch driver that does not +implement .port_fdb_add or .port_fdb_del. + +dsa_slave_switchdev_event returns NOTIFY_OK and schedules: +-> dsa_slave_switchdev_event_work + -> dsa_port_fdb_add + -> dsa_port_notify(DSA_NOTIFIER_FDB_ADD) + -> dsa_switch_fdb_add + -> if (!ds->ops->port_fdb_add) return -EOPNOTSUPP; + -> an error is printed with dev_dbg, and + dsa_fdb_offload_notify(switchdev_work) is not called. + +We can avoid scheduling the worker for nothing and say NOTIFY_DONE. +Because we don't call dsa_fdb_offload_notify, the static FDB entry will +remain just in the software bridge. + +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> +--- + net/dsa/slave.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -2090,6 +2090,9 @@ static int dsa_slave_switchdev_event(str + + dp = dsa_slave_to_port(dev); + ++ if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del) ++ return NOTIFY_DONE; ++ + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (!switchdev_work) + return NOTIFY_BAD; diff --git a/target/linux/generic/backport-5.10/775-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch b/target/linux/generic/backport-5.10/775-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch new file mode 100644 index 0000000000..978ece93b3 --- /dev/null +++ b/target/linux/generic/backport-5.10/775-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch @@ -0,0 +1,264 @@ +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 +@@ -2067,6 +2067,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) +@@ -2085,10 +2107,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; +@@ -2103,18 +2152,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: diff --git a/target/linux/generic/backport-5.4/770-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch b/target/linux/generic/backport-5.4/770-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch new file mode 100644 index 0000000000..df4e74cd96 --- /dev/null +++ b/target/linux/generic/backport-5.4/770-v5.12-net-bridge-notify-switchdev-of-disappearance-of-old-.patch @@ -0,0 +1,126 @@ +From 90dc8fd36078a536671adae884d0b929cce6480a Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 6 Jan 2021 11:51:30 +0200 +Subject: [PATCH] net: bridge: notify switchdev of disappearance of old FDB + entry upon migration + +Currently the bridge emits atomic switchdev notifications for +dynamically learnt FDB entries. Monitoring these notifications works +wonders for switchdev drivers that want to keep their hardware FDB in +sync with the bridge's FDB. + +For example station A wants to talk to station B in the diagram below, +and we are concerned with the behavior of the bridge on the DUT device: + + DUT + +-------------------------------------+ + | br0 | + | +------+ +------+ +------+ +------+ | + | | | | | | | | | | + | | swp0 | | swp1 | | swp2 | | eth0 | | + +-------------------------------------+ + | | | + Station A | | + | | + +--+------+--+ +--+------+--+ + | | | | | | | | + | | swp0 | | | | swp0 | | + Another | +------+ | | +------+ | Another + switch | br0 | | br0 | switch + | +------+ | | +------+ | + | | | | | | | | + | | swp1 | | | | swp1 | | + +--+------+--+ +--+------+--+ + | + Station B + +Interfaces swp0, swp1, swp2 are handled by a switchdev driver that has +the following property: frames injected from its control interface bypass +the internal address analyzer logic, and therefore, this hardware does +not learn from the source address of packets transmitted by the network +stack through it. So, since bridging between eth0 (where Station B is +attached) and swp0 (where Station A is attached) is done in software, +the switchdev hardware will never learn the source address of Station B. +So the traffic towards that destination will be treated as unknown, i.e. +flooded. + +This is where the bridge notifications come in handy. When br0 on the +DUT sees frames with Station B's MAC address on eth0, the switchdev +driver gets these notifications and can install a rule to send frames +towards Station B's address that are incoming from swp0, swp1, swp2, +only towards the control interface. This is all switchdev driver private +business, which the notification makes possible. + +All is fine until someone unplugs Station B's cable and moves it to the +other switch: + + DUT + +-------------------------------------+ + | br0 | + | +------+ +------+ +------+ +------+ | + | | | | | | | | | | + | | swp0 | | swp1 | | swp2 | | eth0 | | + +-------------------------------------+ + | | | + Station A | | + | | + +--+------+--+ +--+------+--+ + | | | | | | | | + | | swp0 | | | | swp0 | | + Another | +------+ | | +------+ | Another + switch | br0 | | br0 | switch + | +------+ | | +------+ | + | | | | | | | | + | | swp1 | | | | swp1 | | + +--+------+--+ +--+------+--+ + | + Station B + +Luckily for the use cases we care about, Station B is noisy enough that +the DUT hears it (on swp1 this time). swp1 receives the frames and +delivers them to the bridge, who enters the unlikely path in br_fdb_update +of updating an existing entry. It moves the entry in the software bridge +to swp1 and emits an addition notification towards that. + +As far as the switchdev driver is concerned, all that it needs to ensure +is that traffic between Station A and Station B is not forever broken. +If it does nothing, then the stale rule to send frames for Station B +towards the control interface remains in place. But Station B is no +longer reachable via the control interface, but via a port that can +offload the bridge port learning attribute. It's just that the port is +prevented from learning this address, since the rule overrides FDB +updates. So the rule needs to go. The question is via what mechanism. + +It sure would be possible for this switchdev driver to keep track of all +addresses which are sent to the control interface, and then also listen +for bridge notifier events on its own ports, searching for the ones that +have a MAC address which was previously sent to the control interface. +But this is cumbersome and inefficient. Instead, with one small change, +the bridge could notify of the address deletion from the old port, in a +symmetrical manner with how it did for the insertion. Then the switchdev +driver would not be required to monitor learn/forget events for its own +ports. It could just delete the rule towards the control interface upon +bridge entry migration. This would make hardware address learning be +possible again. Then it would take a few more packets until the hardware +and software FDB would be in sync again. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Acked-by: Nikolay Aleksandrov <nikolay@nvidia.com> +Reviewed-by: Ido Schimmel <idosch@nvidia.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + net/bridge/br_fdb.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -581,6 +581,7 @@ void br_fdb_update(struct net_bridge *br + + /* fastpath: update of existing entry */ + if (unlikely(source != fdb->dst && !fdb->is_sticky)) { ++ br_switchdev_fdb_notify(fdb, RTM_DELNEIGH); + fdb->dst = source; + fdb_modified = true; + /* Take over HW learned entry */ diff --git a/target/linux/generic/backport-5.4/771-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch b/target/linux/generic/backport-5.4/771-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch new file mode 100644 index 0000000000..967502f022 --- /dev/null +++ b/target/linux/generic/backport-5.4/771-v5.12-net-dsa-be-louder-when-a-non-legacy-FDB-operation-fa.patch @@ -0,0 +1,52 @@ +From 2fd186501b1cff155cc4a755c210793cfc0dffb5 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 6 Jan 2021 11:51:31 +0200 +Subject: [PATCH] net: dsa: be louder when a non-legacy FDB operation fails + +The dev_close() call was added in commit c9eb3e0f8701 ("net: dsa: Add +support for learning FDB through notification") "to indicate inconsistent +situation" when we could not delete an FDB entry from the port. + +bridge fdb del d8:58:d7:00:ca:6d dev swp0 self master + +It is a bit drastic and at the same time not helpful if the above fails +to only print with netdev_dbg log level, but on the other hand to bring +the interface down. + +So increase the verbosity of the error message, and drop dev_close(). + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Reviewed-by: Andrew Lunn <andrew@lunn.ch> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + net/dsa/slave.c | 10 +++++++--- + 1 file changed, 7 insertions(+), 3 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1590,7 +1590,9 @@ static void dsa_slave_switchdev_event_wo + + err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid); + if (err) { +- netdev_dbg(dev, "fdb add failed err=%d\n", err); ++ netdev_err(dev, ++ "failed to add %pM vid %d to fdb: %d\n", ++ fdb_info->addr, fdb_info->vid, err); + break; + } + fdb_info->offloaded = true; +@@ -1605,9 +1607,11 @@ static void dsa_slave_switchdev_event_wo + + err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid); + if (err) { +- netdev_dbg(dev, "fdb del failed err=%d\n", err); +- dev_close(dev); ++ netdev_err(dev, ++ "failed to delete %pM vid %d from fdb: %d\n", ++ fdb_info->addr, fdb_info->vid, err); + } ++ + break; + } + rtnl_unlock(); diff --git a/target/linux/generic/backport-5.4/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch b/target/linux/generic/backport-5.4/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch new file mode 100644 index 0000000000..ad7c242280 --- /dev/null +++ b/target/linux/generic/backport-5.4/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch @@ -0,0 +1,226 @@ +From c4bb76a9a0ef87c4cc1f636defed5f12deb9f5a7 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 6 Jan 2021 11:51:32 +0200 +Subject: [PATCH] net: dsa: don't use switchdev_notifier_fdb_info in + dsa_switchdev_event_work + +Currently DSA doesn't add FDB entries on the CPU port, because it only +does so through switchdev, which is associated with a net_device, and +there are none of those for the CPU port. + +But actually FDB addresses on the CPU port have some use cases of their +own, if the switchdev operations are initiated from within the DSA +layer. There is just one problem with the existing code: it passes a +structure in dsa_switchdev_event_work which was retrieved directly from +switchdev, so it contains a net_device. We need to generalize the +contents to something that covers the CPU port as well: the "ds, port" +tuple is fine for that. + +Note that the new procedure for notifying the successful FDB offload is +inspired from the rocker model. + +Also, nothing was being done if added_by_user was false. Let's check for +that a lot earlier, and don't actually bother to schedule the worker +for nothing. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + net/dsa/dsa_priv.h | 12 +++++ + net/dsa/slave.c | 106 ++++++++++++++++++++++----------------------- + 2 files changed, 65 insertions(+), 53 deletions(-) + +--- a/net/dsa/dsa_priv.h ++++ b/net/dsa/dsa_priv.h +@@ -62,6 +62,18 @@ struct dsa_notifier_vlan_info { + int port; + }; + ++struct dsa_switchdev_event_work { ++ struct dsa_switch *ds; ++ int port; ++ struct work_struct work; ++ unsigned long event; ++ /* Specific for SWITCHDEV_FDB_ADD_TO_DEVICE and ++ * SWITCHDEV_FDB_DEL_TO_DEVICE ++ */ ++ unsigned char addr[ETH_ALEN]; ++ u16 vid; ++}; ++ + struct dsa_slave_priv { + /* Copy of CPU port xmit for faster access in slave transmit hot path */ + struct sk_buff * (*xmit)(struct sk_buff *skb, +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1565,76 +1565,66 @@ static int dsa_slave_netdevice_event(str + return NOTIFY_DONE; + } + +-struct dsa_switchdev_event_work { +- struct work_struct work; +- struct switchdev_notifier_fdb_info fdb_info; +- struct net_device *dev; +- unsigned long event; +-}; ++static void ++dsa_fdb_offload_notify(struct dsa_switchdev_event_work *switchdev_work) ++{ ++ struct dsa_switch *ds = switchdev_work->ds; ++ struct switchdev_notifier_fdb_info info; ++ struct dsa_port *dp; ++ ++ if (!dsa_is_user_port(ds, switchdev_work->port)) ++ return; ++ ++ info.addr = switchdev_work->addr; ++ info.vid = switchdev_work->vid; ++ info.offloaded = true; ++ dp = dsa_to_port(ds, switchdev_work->port); ++ call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, ++ dp->slave, &info.info, NULL); ++} + + static void dsa_slave_switchdev_event_work(struct work_struct *work) + { + struct dsa_switchdev_event_work *switchdev_work = + container_of(work, struct dsa_switchdev_event_work, work); +- struct net_device *dev = switchdev_work->dev; +- struct switchdev_notifier_fdb_info *fdb_info; +- struct dsa_port *dp = dsa_slave_to_port(dev); ++ struct dsa_switch *ds = switchdev_work->ds; ++ struct dsa_port *dp; + int err; + ++ dp = dsa_to_port(ds, switchdev_work->port); ++ + rtnl_lock(); + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: +- fdb_info = &switchdev_work->fdb_info; +- if (!fdb_info->added_by_user) +- break; +- +- err = dsa_port_fdb_add(dp, fdb_info->addr, fdb_info->vid); ++ err = dsa_port_fdb_add(dp, switchdev_work->addr, ++ switchdev_work->vid); + if (err) { +- netdev_err(dev, +- "failed to add %pM vid %d to fdb: %d\n", +- fdb_info->addr, fdb_info->vid, err); ++ dev_err(ds->dev, ++ "port %d failed to add %pM vid %d to fdb: %d\n", ++ dp->index, switchdev_work->addr, ++ switchdev_work->vid, err); + break; + } +- fdb_info->offloaded = true; +- call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev, +- &fdb_info->info, NULL); ++ dsa_fdb_offload_notify(switchdev_work); + break; + + case SWITCHDEV_FDB_DEL_TO_DEVICE: +- fdb_info = &switchdev_work->fdb_info; +- if (!fdb_info->added_by_user) +- break; +- +- err = dsa_port_fdb_del(dp, fdb_info->addr, fdb_info->vid); ++ err = dsa_port_fdb_del(dp, switchdev_work->addr, ++ switchdev_work->vid); + if (err) { +- netdev_err(dev, +- "failed to delete %pM vid %d from fdb: %d\n", +- fdb_info->addr, fdb_info->vid, err); ++ dev_err(ds->dev, ++ "port %d failed to delete %pM vid %d from fdb: %d\n", ++ dp->index, switchdev_work->addr, ++ switchdev_work->vid, err); + } + + break; + } + rtnl_unlock(); + +- kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); +- dev_put(dev); +-} +- +-static int +-dsa_slave_switchdev_fdb_work_init(struct dsa_switchdev_event_work * +- switchdev_work, +- const struct switchdev_notifier_fdb_info * +- fdb_info) +-{ +- memcpy(&switchdev_work->fdb_info, fdb_info, +- sizeof(switchdev_work->fdb_info)); +- switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); +- if (!switchdev_work->fdb_info.addr) +- return -ENOMEM; +- ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, +- fdb_info->addr); +- return 0; ++ if (dsa_is_user_port(ds, dp->index)) ++ dev_put(dp->slave); + } + + /* Called under rcu_read_lock() */ +@@ -1642,7 +1632,9 @@ static int dsa_slave_switchdev_event(str + unsigned long event, void *ptr) + { + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); ++ const struct switchdev_notifier_fdb_info *fdb_info; + struct dsa_switchdev_event_work *switchdev_work; ++ struct dsa_port *dp; + int err; + + if (event == SWITCHDEV_PORT_ATTR_SET) { +@@ -1655,20 +1647,32 @@ static int dsa_slave_switchdev_event(str + if (!dsa_slave_dev_check(dev)) + return NOTIFY_DONE; + ++ dp = dsa_slave_to_port(dev); ++ + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (!switchdev_work) + return NOTIFY_BAD; + + INIT_WORK(&switchdev_work->work, + dsa_slave_switchdev_event_work); +- switchdev_work->dev = dev; ++ switchdev_work->ds = dp->ds; ++ switchdev_work->port = dp->index; + switchdev_work->event = event; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */ + case SWITCHDEV_FDB_DEL_TO_DEVICE: +- if (dsa_slave_switchdev_fdb_work_init(switchdev_work, ptr)) +- goto err_fdb_work_init; ++ 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); + break; + default: +@@ -1678,10 +1682,6 @@ static int dsa_slave_switchdev_event(str + + dsa_schedule_work(&switchdev_work->work); + return NOTIFY_OK; +- +-err_fdb_work_init: +- kfree(switchdev_work); +- return NOTIFY_BAD; + } + + static int dsa_slave_switchdev_blocking_event(struct notifier_block *unused, diff --git a/target/linux/generic/backport-5.4/773-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch b/target/linux/generic/backport-5.4/773-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch new file mode 100644 index 0000000000..619c71c7bb --- /dev/null +++ b/target/linux/generic/backport-5.4/773-v5.12-net-dsa-move-switchdev-event-implementation-under-th.patch @@ -0,0 +1,85 @@ +From 447d290a58bd335d68f665713842365d3d6447df Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 6 Jan 2021 11:51:33 +0200 +Subject: [PATCH] net: dsa: move switchdev event implementation under the same + switch/case statement + +We'll need to start listening to SWITCHDEV_FDB_{ADD,DEL}_TO_DEVICE +events even for interfaces where dsa_slave_dev_check returns false, so +we need that check inside the switch-case statement for SWITCHDEV_FDB_*. + +This movement also avoids a useless allocation / free of switchdev_work +on the untreated "default event" case. + +Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com> +Reviewed-by: Florian Fainelli <f.fainelli@gmail.com> +Signed-off-by: Jakub Kicinski <kuba@kernel.org> +--- + net/dsa/slave.c | 35 ++++++++++++++++------------------- + 1 file changed, 16 insertions(+), 19 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1637,31 +1637,29 @@ static int dsa_slave_switchdev_event(str + struct dsa_port *dp; + int err; + +- if (event == SWITCHDEV_PORT_ATTR_SET) { ++ switch (event) { ++ case SWITCHDEV_PORT_ATTR_SET: + err = switchdev_handle_port_attr_set(dev, ptr, + dsa_slave_dev_check, + dsa_slave_port_attr_set); + return notifier_from_errno(err); +- } +- +- if (!dsa_slave_dev_check(dev)) +- return NOTIFY_DONE; ++ case SWITCHDEV_FDB_ADD_TO_DEVICE: ++ case SWITCHDEV_FDB_DEL_TO_DEVICE: ++ if (!dsa_slave_dev_check(dev)) ++ return NOTIFY_DONE; + +- dp = dsa_slave_to_port(dev); ++ dp = dsa_slave_to_port(dev); + +- switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); +- if (!switchdev_work) +- return NOTIFY_BAD; +- +- INIT_WORK(&switchdev_work->work, +- dsa_slave_switchdev_event_work); +- switchdev_work->ds = dp->ds; +- switchdev_work->port = dp->index; +- switchdev_work->event = event; ++ switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); ++ if (!switchdev_work) ++ return NOTIFY_BAD; ++ ++ INIT_WORK(&switchdev_work->work, ++ dsa_slave_switchdev_event_work); ++ switchdev_work->ds = dp->ds; ++ switchdev_work->port = dp->index; ++ switchdev_work->event = event; + +- switch (event) { +- case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */ +- case SWITCHDEV_FDB_DEL_TO_DEVICE: + fdb_info = ptr; + + if (!fdb_info->added_by_user) { +@@ -1674,13 +1672,12 @@ static int dsa_slave_switchdev_event(str + switchdev_work->vid = fdb_info->vid; + + dev_hold(dev); ++ dsa_schedule_work(&switchdev_work->work); + break; + default: +- kfree(switchdev_work); + return NOTIFY_DONE; + } + +- dsa_schedule_work(&switchdev_work->work); + return NOTIFY_OK; + } + diff --git a/target/linux/generic/backport-5.4/774-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch b/target/linux/generic/backport-5.4/774-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch new file mode 100644 index 0000000000..25e0d30e1a --- /dev/null +++ b/target/linux/generic/backport-5.4/774-v5.12-net-dsa-exit-early-in-dsa_slave_switchdev_event-if-w.patch @@ -0,0 +1,42 @@ +From 5fb4a451a87d8ed3363d28b63a3295399373d6c4 Mon Sep 17 00:00:00 2001 +From: Vladimir Oltean <vladimir.oltean@nxp.com> +Date: Wed, 6 Jan 2021 11:51:34 +0200 +Subject: [PATCH] net: dsa: exit early in dsa_slave_switchdev_event if we can't + program the FDB + +Right now, the following would happen for a switch driver that does not +implement .port_fdb_add or .port_fdb_del. + +dsa_slave_switchdev_event returns NOTIFY_OK and schedules: +-> dsa_slave_switchdev_event_work + -> dsa_port_fdb_add + -> dsa_port_notify(DSA_NOTIFIER_FDB_ADD) + -> dsa_switch_fdb_add + -> if (!ds->ops->port_fdb_add) return -EOPNOTSUPP; + -> an error is printed with dev_dbg, and + dsa_fdb_offload_notify(switchdev_work) is not called. + +We can avoid scheduling the worker for nothing and say NOTIFY_DONE. +Because we don't call dsa_fdb_offload_notify, the static FDB entry will +remain just in the software bridge. + +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> +--- + net/dsa/slave.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1650,6 +1650,9 @@ static int dsa_slave_switchdev_event(str + + dp = dsa_slave_to_port(dev); + ++ if (!dp->ds->ops->port_fdb_add || !dp->ds->ops->port_fdb_del) ++ return NOTIFY_DONE; ++ + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (!switchdev_work) + return NOTIFY_BAD; diff --git a/target/linux/generic/backport-5.4/775-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch b/target/linux/generic/backport-5.4/775-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch new file mode 100644 index 0000000000..ecc74ea050 --- /dev/null +++ b/target/linux/generic/backport-5.4/775-v5.12-net-dsa-listen-for-SWITCHDEV_-FDB-DEL-_ADD_TO_DEVICE.patch @@ -0,0 +1,263 @@ +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> +[Backported to linux-5.4.y] +Signed-off-by: DENG Qingfang <dqfext@gmail.com> +--- + include/net/dsa.h | 5 ++++ + net/dsa/slave.c | 63 ++++++++++++++++++++++++++++++++++++++--------- + 2 files changed, 57 insertions(+), 11 deletions(-) + +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -279,6 +279,11 @@ struct dsa_switch { + */ + bool configure_vlan_while_not_filtering; + ++ /* 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 +@@ -1627,6 +1627,25 @@ static void dsa_slave_switchdev_event_wo + dev_put(dp->slave); + } + ++static int dsa_lower_dev_walk(struct net_device *lower_dev, void *data) ++{ ++ if (dsa_slave_dev_check(lower_dev)) { ++ *((void **)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 dsa_slave_priv *data = NULL; ++ ++ netdev_walk_all_lower_dev_rcu(dev, dsa_lower_dev_walk, (void **) &data); ++ ++ return data; ++} ++ + /* Called under rcu_read_lock() */ + static int dsa_slave_switchdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +@@ -1645,10 +1664,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; +@@ -1663,18 +1709,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: diff --git a/target/linux/generic/hack-5.10/710-net-dsa-mv88e6xxx-default-VID-1.patch b/target/linux/generic/hack-5.10/710-net-dsa-mv88e6xxx-default-VID-1.patch new file mode 100644 index 0000000000..f301cc1e2d --- /dev/null +++ b/target/linux/generic/hack-5.10/710-net-dsa-mv88e6xxx-default-VID-1.patch @@ -0,0 +1,18 @@ +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -2088,6 +2088,7 @@ static int mv88e6xxx_port_fdb_add(struct + struct mv88e6xxx_chip *chip = ds->priv; + int err; + ++ vid = vid ? : 1; + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, + MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC); +@@ -2102,6 +2103,7 @@ static int mv88e6xxx_port_fdb_del(struct + struct mv88e6xxx_chip *chip = ds->priv; + int err; + ++ vid = vid ? : 1; + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, 0); + mv88e6xxx_reg_unlock(chip); diff --git a/target/linux/generic/hack-5.10/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch b/target/linux/generic/hack-5.10/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch new file mode 100644 index 0000000000..46a1ba1d96 --- /dev/null +++ b/target/linux/generic/hack-5.10/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch @@ -0,0 +1,12 @@ +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -2650,6 +2650,9 @@ static int mv88e6xxx_setup_port(struct m + if (dsa_is_cpu_port(ds, port)) + reg = 0; + ++ /* Disable ATU member violation interrupt */ ++ reg |= MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG; ++ + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, + reg); + if (err) diff --git a/target/linux/generic/hack-5.4/710-net-dsa-mv88e6xxx-default-VID-1.patch b/target/linux/generic/hack-5.4/710-net-dsa-mv88e6xxx-default-VID-1.patch new file mode 100644 index 0000000000..5dc5ac6825 --- /dev/null +++ b/target/linux/generic/hack-5.4/710-net-dsa-mv88e6xxx-default-VID-1.patch @@ -0,0 +1,18 @@ +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -1930,6 +1930,7 @@ static int mv88e6xxx_port_fdb_add(struct + struct mv88e6xxx_chip *chip = ds->priv; + int err; + ++ vid = vid ? : 1; + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, + MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC); +@@ -1944,6 +1945,7 @@ static int mv88e6xxx_port_fdb_del(struct + struct mv88e6xxx_chip *chip = ds->priv; + int err; + ++ vid = vid ? : 1; + mv88e6xxx_reg_lock(chip); + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, 0); + mv88e6xxx_reg_unlock(chip); diff --git a/target/linux/generic/hack-5.4/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch b/target/linux/generic/hack-5.4/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch new file mode 100644 index 0000000000..1da388c862 --- /dev/null +++ b/target/linux/generic/hack-5.4/711-net-dsa-mv88e6xxx-disable-ATU-violation.patch @@ -0,0 +1,12 @@ +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -2492,6 +2492,9 @@ static int mv88e6xxx_setup_port(struct m + if (dsa_is_cpu_port(ds, port)) + reg = 0; + ++ /* Disable ATU member violation interrupt */ ++ reg |= MV88E6XXX_PORT_ASSOC_VECTOR_IGNORE_WRONG; ++ + err = mv88e6xxx_port_write(chip, port, MV88E6XXX_PORT_ASSOC_VECTOR, + reg); + if (err) diff --git a/target/linux/generic/pending-5.10/762-net-bridge-switchdev-Refactor-br_switchdev_fdb_notif.patch b/target/linux/generic/pending-5.10/762-net-bridge-switchdev-Refactor-br_switchdev_fdb_notif.patch new file mode 100644 index 0000000000..081d002c7e --- /dev/null +++ b/target/linux/generic/pending-5.10/762-net-bridge-switchdev-Refactor-br_switchdev_fdb_notif.patch @@ -0,0 +1,75 @@ +From 46fe6cecb296d850c1ee2b333e57093ac4b733f3 Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:09 +0100 +Subject: [PATCH] net: bridge: switchdev: Refactor br_switchdev_fdb_notify + +Instead of having to add more and more arguments to +br_switchdev_fdb_call_notifiers, get rid of it and build the info +struct directly in br_switchdev_fdb_notify. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +--- + net/bridge/br_switchdev.c | 41 +++++++++++---------------------------- + 1 file changed, 11 insertions(+), 30 deletions(-) + +--- a/net/bridge/br_switchdev.c ++++ b/net/bridge/br_switchdev.c +@@ -102,46 +102,27 @@ int br_switchdev_set_port_flag(struct ne + return 0; + } + +-static void +-br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac, +- u16 vid, struct net_device *dev, +- bool added_by_user, bool offloaded) +-{ +- struct switchdev_notifier_fdb_info info; +- unsigned long notifier_type; +- +- info.addr = mac; +- info.vid = vid; +- info.added_by_user = added_by_user; +- info.offloaded = offloaded; +- notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE; +- call_switchdev_notifiers(notifier_type, dev, &info.info, NULL); +-} +- + void + br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) + { ++ struct switchdev_notifier_fdb_info info = { ++ .addr = fdb->key.addr.addr, ++ .vid = fdb->key.vlan_id, ++ .added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags), ++ .offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags), ++ }; ++ + if (!fdb->dst) + return; + + switch (type) { + case RTM_DELNEIGH: +- br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr, +- fdb->key.vlan_id, +- fdb->dst->dev, +- test_bit(BR_FDB_ADDED_BY_USER, +- &fdb->flags), +- test_bit(BR_FDB_OFFLOADED, +- &fdb->flags)); ++ call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE, ++ fdb->dst->dev, &info.info, NULL); + break; + case RTM_NEWNEIGH: +- br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr, +- fdb->key.vlan_id, +- fdb->dst->dev, +- test_bit(BR_FDB_ADDED_BY_USER, +- &fdb->flags), +- test_bit(BR_FDB_OFFLOADED, +- &fdb->flags)); ++ call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE, ++ fdb->dst->dev, &info.info, NULL); + break; + } + } diff --git a/target/linux/generic/pending-5.10/763-net-bridge-switchdev-Include-local-flag-in-FDB-notif.patch b/target/linux/generic/pending-5.10/763-net-bridge-switchdev-Include-local-flag-in-FDB-notif.patch new file mode 100644 index 0000000000..41374c88df --- /dev/null +++ b/target/linux/generic/pending-5.10/763-net-bridge-switchdev-Include-local-flag-in-FDB-notif.patch @@ -0,0 +1,42 @@ +From ec5be4f79026282925ae383caa431a8d41e3456a Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:10 +0100 +Subject: [PATCH] net: bridge: switchdev: Include local flag in FDB + notifications + +Some switchdev drivers, notably DSA, ignore all dynamically learned +address notifications (!added_by_user) as these are autonomously added +by the switch. Previously, such a notification was indistinguishable +from a local address notification. Include a local bit in the +notification so that the two classes can be discriminated. + +This allows DSA-like devices to add local addresses to the hardware +FDB (with the CPU as the destination), thereby avoiding flows towards +the CPU being flooded by the switch as unknown unicast. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + include/net/switchdev.h | 1 + + net/bridge/br_switchdev.c | 1 + + 2 files changed, 2 insertions(+) + +--- a/include/net/switchdev.h ++++ b/include/net/switchdev.h +@@ -224,6 +224,7 @@ struct switchdev_notifier_fdb_info { + const unsigned char *addr; + u16 vid; + u8 added_by_user:1, ++ local:1, + offloaded:1; + }; + +--- a/net/bridge/br_switchdev.c ++++ b/net/bridge/br_switchdev.c +@@ -109,6 +109,7 @@ br_switchdev_fdb_notify(const struct net + .addr = fdb->key.addr.addr, + .vid = fdb->key.vlan_id, + .added_by_user = test_bit(BR_FDB_ADDED_BY_USER, &fdb->flags), ++ .local = test_bit(BR_FDB_LOCAL, &fdb->flags), + .offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags), + }; + diff --git a/target/linux/generic/pending-5.10/764-net-bridge-switchdev-Send-FDB-notifications-for-host.patch b/target/linux/generic/pending-5.10/764-net-bridge-switchdev-Send-FDB-notifications-for-host.patch new file mode 100644 index 0000000000..2317989fa5 --- /dev/null +++ b/target/linux/generic/pending-5.10/764-net-bridge-switchdev-Send-FDB-notifications-for-host.patch @@ -0,0 +1,94 @@ +From 2e50fd9322047253c327550b4485cf8761035a8c Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:11 +0100 +Subject: [PATCH] net: bridge: switchdev: Send FDB notifications for host + addresses + +Treat addresses added to the bridge itself in the same way as regular +ports and send out a notification so that drivers may sync it down to +the hardware FDB. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + net/bridge/br_fdb.c | 4 ++-- + net/bridge/br_private.h | 7 ++++--- + net/bridge/br_switchdev.c | 11 +++++------ + 3 files changed, 11 insertions(+), 11 deletions(-) + +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -602,7 +602,7 @@ void br_fdb_update(struct net_bridge *br + /* fastpath: update of existing entry */ + if (unlikely(source != fdb->dst && + !test_bit(BR_FDB_STICKY, &fdb->flags))) { +- br_switchdev_fdb_notify(fdb, RTM_DELNEIGH); ++ br_switchdev_fdb_notify(br, fdb, RTM_DELNEIGH); + fdb->dst = source; + fdb_modified = true; + /* Take over HW learned entry */ +@@ -735,7 +735,7 @@ static void fdb_notify(struct net_bridge + int err = -ENOBUFS; + + if (swdev_notify) +- br_switchdev_fdb_notify(fdb, type); ++ br_switchdev_fdb_notify(br, fdb, type); + + skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); + if (skb == NULL) +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -1527,8 +1527,8 @@ bool nbp_switchdev_allowed_egress(const + int br_switchdev_set_port_flag(struct net_bridge_port *p, + unsigned long flags, + unsigned long mask); +-void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, +- int type); ++void br_switchdev_fdb_notify(struct net_bridge *br, ++ const struct net_bridge_fdb_entry *fdb, int type); + int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, + struct netlink_ext_ack *extack); + int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid); +@@ -1574,7 +1574,8 @@ static inline int br_switchdev_port_vlan + } + + static inline void +-br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) ++br_switchdev_fdb_notify(struct net_bridge *br, ++ const struct net_bridge_fdb_entry *fdb, int type) + { + } + +--- a/net/bridge/br_switchdev.c ++++ b/net/bridge/br_switchdev.c +@@ -103,7 +103,8 @@ int br_switchdev_set_port_flag(struct ne + } + + void +-br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) ++br_switchdev_fdb_notify(struct net_bridge *br, ++ const struct net_bridge_fdb_entry *fdb, int type) + { + struct switchdev_notifier_fdb_info info = { + .addr = fdb->key.addr.addr, +@@ -112,18 +113,16 @@ br_switchdev_fdb_notify(const struct net + .local = test_bit(BR_FDB_LOCAL, &fdb->flags), + .offloaded = test_bit(BR_FDB_OFFLOADED, &fdb->flags), + }; +- +- if (!fdb->dst) +- return; ++ struct net_device *dev = fdb->dst ? fdb->dst->dev : br->dev; + + switch (type) { + case RTM_DELNEIGH: + call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE, +- fdb->dst->dev, &info.info, NULL); ++ dev, &info.info, NULL); + break; + case RTM_NEWNEIGH: + call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE, +- fdb->dst->dev, &info.info, NULL); ++ dev, &info.info, NULL); + break; + } + } diff --git a/target/linux/generic/pending-5.10/765-net-dsa-Include-local-addresses-in-assisted-CPU-port.patch b/target/linux/generic/pending-5.10/765-net-dsa-Include-local-addresses-in-assisted-CPU-port.patch new file mode 100644 index 0000000000..9246d1fa40 --- /dev/null +++ b/target/linux/generic/pending-5.10/765-net-dsa-Include-local-addresses-in-assisted-CPU-port.patch @@ -0,0 +1,36 @@ +From dd082716b43a3684b2f473ae5d1e76d1c076d86d Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:12 +0100 +Subject: [PATCH] net: dsa: Include local addresses in assisted CPU port + learning + +Add local addresses (i.e. the ports' MAC addresses) to the hardware +FDB when assisted CPU port learning is enabled. + +NOTE: The bridge's own MAC address is also "local". If that address is +not shared with any port, the bridge's MAC is not be added by this +functionality - but the following commit takes care of that case. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + net/dsa/slave.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -2144,10 +2144,12 @@ static int dsa_slave_switchdev_event(str + fdb_info = ptr; + + if (dsa_slave_dev_check(dev)) { +- if (!fdb_info->added_by_user) +- return NOTIFY_OK; +- + dp = dsa_slave_to_port(dev); ++ ++ if (fdb_info->local && dp->ds->assisted_learning_on_cpu_port) ++ dp = dp->cpu_dp; ++ else if (!fdb_info->added_by_user) ++ return NOTIFY_OK; + } else { + /* Snoop addresses learnt on foreign interfaces + * bridged with us, for switches that don't diff --git a/target/linux/generic/pending-5.10/766-net-dsa-Include-bridge-addresses-in-assisted-CPU-por.patch b/target/linux/generic/pending-5.10/766-net-dsa-Include-bridge-addresses-in-assisted-CPU-por.patch new file mode 100644 index 0000000000..77f0dca252 --- /dev/null +++ b/target/linux/generic/pending-5.10/766-net-dsa-Include-bridge-addresses-in-assisted-CPU-por.patch @@ -0,0 +1,30 @@ +From 0663ebde114a6fb2c28c622ba5212b302d4d2581 Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:13 +0100 +Subject: [PATCH] net: dsa: Include bridge addresses in assisted CPU port + learning + +Now that notifications are sent out for addresses added to the bridge +itself, extend DSA to include those addresses in the hardware FDB when +assisted CPU port learning is enabled. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + net/dsa/slave.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -2158,7 +2158,11 @@ static int dsa_slave_switchdev_event(str + struct net_device *br_dev; + struct dsa_slave_priv *p; + +- br_dev = netdev_master_upper_dev_get_rcu(dev); ++ if (netif_is_bridge_master(dev)) ++ br_dev = dev; ++ else ++ br_dev = netdev_master_upper_dev_get_rcu(dev); ++ + if (!br_dev) + return NOTIFY_DONE; + diff --git a/target/linux/generic/pending-5.10/767-net-dsa-Sync-static-FDB-entries-on-foreign-interface.patch b/target/linux/generic/pending-5.10/767-net-dsa-Sync-static-FDB-entries-on-foreign-interface.patch new file mode 100644 index 0000000000..39271108ec --- /dev/null +++ b/target/linux/generic/pending-5.10/767-net-dsa-Sync-static-FDB-entries-on-foreign-interface.patch @@ -0,0 +1,56 @@ +From 81e39fd78db82fb51b05fff309b9c521f1a0bc5a Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:14 +0100 +Subject: [PATCH] net: dsa: Sync static FDB entries on foreign interfaces to + hardware + +Reuse the "assisted_learning_on_cpu_port" functionality to always add +entries for user-configured entries on foreign interfaces, even if +assisted_learning_on_cpu_port is not enabled. E.g. in this situation: + + br0 + / \ +swp0 dummy0 + +$ bridge fdb add 02:00:de:ad:00:01 dev dummy0 vlan 1 master + +Results in DSA adding an entry in the hardware FDB, pointing this +address towards the CPU port. + +The same is true for entries added to the bridge itself, e.g: + +$ bridge fdb add 02:00:de:ad:00:01 dev br0 vlan 1 self + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + net/dsa/slave.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -2151,9 +2151,12 @@ static int dsa_slave_switchdev_event(str + else if (!fdb_info->added_by_user) + return NOTIFY_OK; + } else { +- /* Snoop addresses learnt on foreign interfaces +- * bridged with us, for switches that don't +- * automatically learn SA from CPU-injected traffic ++ /* Snoop addresses added to foreign interfaces ++ * bridged with us, or the bridge ++ * itself. Dynamically learned addresses can ++ * also be added for switches that don't ++ * automatically learn SA from CPU-injected ++ * traffic. + */ + struct net_device *br_dev; + struct dsa_slave_priv *p; +@@ -2175,7 +2178,8 @@ static int dsa_slave_switchdev_event(str + + dp = p->dp->cpu_dp; + +- if (!dp->ds->assisted_learning_on_cpu_port) ++ if (!fdb_info->added_by_user && ++ !dp->ds->assisted_learning_on_cpu_port) + return NOTIFY_DONE; + } + diff --git a/target/linux/generic/pending-5.10/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-5.10/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch new file mode 100644 index 0000000000..5b8d058247 --- /dev/null +++ b/target/linux/generic/pending-5.10/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch @@ -0,0 +1,27 @@ +From: Tobias Waldekranz <tobias@waldekranz.com> +Subject: [RFC net-next 7/7] net: dsa: mv88e6xxx: Request assisted learning on CPU port +Date: Sat, 16 Jan 2021 02:25:15 +0100 +Archived-At: <https://lore.kernel.org/netdev/20210116012515.3152-8-tobias@waldekranz.com/> + +While the hardware is capable of performing learning on the CPU port, +it requires alot of additions to the bridge's forwarding path in order +to handle multi-destination traffic correctly. + +Until that is in place, opt for the next best thing and let DSA sync +the relevant addresses down to the hardware FDB. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + drivers/net/dsa/mv88e6xxx/chip.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -5405,6 +5405,7 @@ static int mv88e6xxx_register_switch(str + ds->ops = &mv88e6xxx_switch_ops; + ds->ageing_time_min = chip->info->age_time_coeff; + ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; ++ ds->assisted_learning_on_cpu_port = true; + + dev_set_drvdata(dev, ds); + diff --git a/target/linux/generic/pending-5.4/762-net-bridge-switchdev-Refactor-br_switchdev_fdb_notif.patch b/target/linux/generic/pending-5.4/762-net-bridge-switchdev-Refactor-br_switchdev_fdb_notif.patch new file mode 100644 index 0000000000..bfa2d375e1 --- /dev/null +++ b/target/linux/generic/pending-5.4/762-net-bridge-switchdev-Refactor-br_switchdev_fdb_notif.patch @@ -0,0 +1,71 @@ +From 46fe6cecb296d850c1ee2b333e57093ac4b733f3 Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:09 +0100 +Subject: [PATCH] net: bridge: switchdev: Refactor br_switchdev_fdb_notify + +Instead of having to add more and more arguments to +br_switchdev_fdb_call_notifiers, get rid of it and build the info +struct directly in br_switchdev_fdb_notify. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +Reviewed-by: Vladimir Oltean <olteanv@gmail.com> +--- + net/bridge/br_switchdev.c | 37 +++++++++++-------------------------- + 1 file changed, 11 insertions(+), 26 deletions(-) + +--- a/net/bridge/br_switchdev.c ++++ b/net/bridge/br_switchdev.c +@@ -102,42 +102,27 @@ int br_switchdev_set_port_flag(struct ne + return 0; + } + +-static void +-br_switchdev_fdb_call_notifiers(bool adding, const unsigned char *mac, +- u16 vid, struct net_device *dev, +- bool added_by_user, bool offloaded) +-{ +- struct switchdev_notifier_fdb_info info; +- unsigned long notifier_type; +- +- info.addr = mac; +- info.vid = vid; +- info.added_by_user = added_by_user; +- info.offloaded = offloaded; +- notifier_type = adding ? SWITCHDEV_FDB_ADD_TO_DEVICE : SWITCHDEV_FDB_DEL_TO_DEVICE; +- call_switchdev_notifiers(notifier_type, dev, &info.info, NULL); +-} +- + void + br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) + { ++ struct switchdev_notifier_fdb_info info = { ++ .addr = fdb->key.addr.addr, ++ .vid = fdb->key.vlan_id, ++ .added_by_user = fdb->added_by_user, ++ .offloaded = fdb->offloaded, ++ }; ++ + if (!fdb->dst) + return; + + switch (type) { + case RTM_DELNEIGH: +- br_switchdev_fdb_call_notifiers(false, fdb->key.addr.addr, +- fdb->key.vlan_id, +- fdb->dst->dev, +- fdb->added_by_user, +- fdb->offloaded); ++ call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE, ++ fdb->dst->dev, &info.info, NULL); + break; + case RTM_NEWNEIGH: +- br_switchdev_fdb_call_notifiers(true, fdb->key.addr.addr, +- fdb->key.vlan_id, +- fdb->dst->dev, +- fdb->added_by_user, +- fdb->offloaded); ++ call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE, ++ fdb->dst->dev, &info.info, NULL); + break; + } + } diff --git a/target/linux/generic/pending-5.4/763-net-bridge-switchdev-Include-local-flag-in-FDB-notif.patch b/target/linux/generic/pending-5.4/763-net-bridge-switchdev-Include-local-flag-in-FDB-notif.patch new file mode 100644 index 0000000000..49d6f079ba --- /dev/null +++ b/target/linux/generic/pending-5.4/763-net-bridge-switchdev-Include-local-flag-in-FDB-notif.patch @@ -0,0 +1,42 @@ +From ec5be4f79026282925ae383caa431a8d41e3456a Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:10 +0100 +Subject: [PATCH] net: bridge: switchdev: Include local flag in FDB + notifications + +Some switchdev drivers, notably DSA, ignore all dynamically learned +address notifications (!added_by_user) as these are autonomously added +by the switch. Previously, such a notification was indistinguishable +from a local address notification. Include a local bit in the +notification so that the two classes can be discriminated. + +This allows DSA-like devices to add local addresses to the hardware +FDB (with the CPU as the destination), thereby avoiding flows towards +the CPU being flooded by the switch as unknown unicast. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + include/net/switchdev.h | 1 + + net/bridge/br_switchdev.c | 1 + + 2 files changed, 2 insertions(+) + +--- a/include/net/switchdev.h ++++ b/include/net/switchdev.h +@@ -124,6 +124,7 @@ struct switchdev_notifier_fdb_info { + const unsigned char *addr; + u16 vid; + u8 added_by_user:1, ++ local:1, + offloaded:1; + }; + +--- a/net/bridge/br_switchdev.c ++++ b/net/bridge/br_switchdev.c +@@ -109,6 +109,7 @@ br_switchdev_fdb_notify(const struct net + .addr = fdb->key.addr.addr, + .vid = fdb->key.vlan_id, + .added_by_user = fdb->added_by_user, ++ .local = fdb->is_local, + .offloaded = fdb->offloaded, + }; + diff --git a/target/linux/generic/pending-5.4/764-net-bridge-switchdev-Send-FDB-notifications-for-host.patch b/target/linux/generic/pending-5.4/764-net-bridge-switchdev-Send-FDB-notifications-for-host.patch new file mode 100644 index 0000000000..8b869dd8f3 --- /dev/null +++ b/target/linux/generic/pending-5.4/764-net-bridge-switchdev-Send-FDB-notifications-for-host.patch @@ -0,0 +1,94 @@ +From 2e50fd9322047253c327550b4485cf8761035a8c Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:11 +0100 +Subject: [PATCH] net: bridge: switchdev: Send FDB notifications for host + addresses + +Treat addresses added to the bridge itself in the same way as regular +ports and send out a notification so that drivers may sync it down to +the hardware FDB. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + net/bridge/br_fdb.c | 4 ++-- + net/bridge/br_private.h | 7 ++++--- + net/bridge/br_switchdev.c | 11 +++++------ + 3 files changed, 11 insertions(+), 11 deletions(-) + +--- a/net/bridge/br_fdb.c ++++ b/net/bridge/br_fdb.c +@@ -581,7 +581,7 @@ void br_fdb_update(struct net_bridge *br + + /* fastpath: update of existing entry */ + if (unlikely(source != fdb->dst && !fdb->is_sticky)) { +- br_switchdev_fdb_notify(fdb, RTM_DELNEIGH); ++ br_switchdev_fdb_notify(br, fdb, RTM_DELNEIGH); + fdb->dst = source; + fdb_modified = true; + /* Take over HW learned entry */ +@@ -697,7 +697,7 @@ static void fdb_notify(struct net_bridge + int err = -ENOBUFS; + + if (swdev_notify) +- br_switchdev_fdb_notify(fdb, type); ++ br_switchdev_fdb_notify(br, fdb, type); + + skb = nlmsg_new(fdb_nlmsg_size(), GFP_ATOMIC); + if (skb == NULL) +--- a/net/bridge/br_private.h ++++ b/net/bridge/br_private.h +@@ -1203,8 +1203,8 @@ bool nbp_switchdev_allowed_egress(const + int br_switchdev_set_port_flag(struct net_bridge_port *p, + unsigned long flags, + unsigned long mask); +-void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, +- int type); ++void br_switchdev_fdb_notify(struct net_bridge *br, ++ const struct net_bridge_fdb_entry *fdb, int type); + int br_switchdev_port_vlan_add(struct net_device *dev, u16 vid, u16 flags, + struct netlink_ext_ack *extack); + int br_switchdev_port_vlan_del(struct net_device *dev, u16 vid); +@@ -1250,7 +1250,8 @@ static inline int br_switchdev_port_vlan + } + + static inline void +-br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) ++br_switchdev_fdb_notify(struct net_bridge *br, ++ const struct net_bridge_fdb_entry *fdb, int type) + { + } + +--- a/net/bridge/br_switchdev.c ++++ b/net/bridge/br_switchdev.c +@@ -103,7 +103,8 @@ int br_switchdev_set_port_flag(struct ne + } + + void +-br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) ++br_switchdev_fdb_notify(struct net_bridge *br, ++ const struct net_bridge_fdb_entry *fdb, int type) + { + struct switchdev_notifier_fdb_info info = { + .addr = fdb->key.addr.addr, +@@ -112,18 +113,16 @@ br_switchdev_fdb_notify(const struct net + .local = fdb->is_local, + .offloaded = fdb->offloaded, + }; +- +- if (!fdb->dst) +- return; ++ struct net_device *dev = fdb->dst ? fdb->dst->dev : br->dev; + + switch (type) { + case RTM_DELNEIGH: + call_switchdev_notifiers(SWITCHDEV_FDB_DEL_TO_DEVICE, +- fdb->dst->dev, &info.info, NULL); ++ dev, &info.info, NULL); + break; + case RTM_NEWNEIGH: + call_switchdev_notifiers(SWITCHDEV_FDB_ADD_TO_DEVICE, +- fdb->dst->dev, &info.info, NULL); ++ dev, &info.info, NULL); + break; + } + } diff --git a/target/linux/generic/pending-5.4/765-net-dsa-Include-local-addresses-in-assisted-CPU-port.patch b/target/linux/generic/pending-5.4/765-net-dsa-Include-local-addresses-in-assisted-CPU-port.patch new file mode 100644 index 0000000000..618ccf0982 --- /dev/null +++ b/target/linux/generic/pending-5.4/765-net-dsa-Include-local-addresses-in-assisted-CPU-port.patch @@ -0,0 +1,36 @@ +From dd082716b43a3684b2f473ae5d1e76d1c076d86d Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:12 +0100 +Subject: [PATCH] net: dsa: Include local addresses in assisted CPU port + learning + +Add local addresses (i.e. the ports' MAC addresses) to the hardware +FDB when assisted CPU port learning is enabled. + +NOTE: The bridge's own MAC address is also "local". If that address is +not shared with any port, the bridge's MAC is not be added by this +functionality - but the following commit takes care of that case. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + net/dsa/slave.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1695,10 +1695,12 @@ static int dsa_slave_switchdev_event(str + fdb_info = ptr; + + if (dsa_slave_dev_check(dev)) { +- if (!fdb_info->added_by_user) +- return NOTIFY_OK; +- + dp = dsa_slave_to_port(dev); ++ ++ if (fdb_info->local && dp->ds->assisted_learning_on_cpu_port) ++ dp = dp->cpu_dp; ++ else if (!fdb_info->added_by_user) ++ return NOTIFY_OK; + } else { + /* Snoop addresses learnt on foreign interfaces + * bridged with us, for switches that don't diff --git a/target/linux/generic/pending-5.4/766-net-dsa-Include-bridge-addresses-in-assisted-CPU-por.patch b/target/linux/generic/pending-5.4/766-net-dsa-Include-bridge-addresses-in-assisted-CPU-por.patch new file mode 100644 index 0000000000..ed34c7ad24 --- /dev/null +++ b/target/linux/generic/pending-5.4/766-net-dsa-Include-bridge-addresses-in-assisted-CPU-por.patch @@ -0,0 +1,30 @@ +From 0663ebde114a6fb2c28c622ba5212b302d4d2581 Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:13 +0100 +Subject: [PATCH] net: dsa: Include bridge addresses in assisted CPU port + learning + +Now that notifications are sent out for addresses added to the bridge +itself, extend DSA to include those addresses in the hardware FDB when +assisted CPU port learning is enabled. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + net/dsa/slave.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1709,7 +1709,11 @@ static int dsa_slave_switchdev_event(str + struct net_device *br_dev; + struct dsa_slave_priv *p; + +- br_dev = netdev_master_upper_dev_get_rcu(dev); ++ if (netif_is_bridge_master(dev)) ++ br_dev = dev; ++ else ++ br_dev = netdev_master_upper_dev_get_rcu(dev); ++ + if (!br_dev) + return NOTIFY_DONE; + diff --git a/target/linux/generic/pending-5.4/767-net-dsa-Sync-static-FDB-entries-on-foreign-interface.patch b/target/linux/generic/pending-5.4/767-net-dsa-Sync-static-FDB-entries-on-foreign-interface.patch new file mode 100644 index 0000000000..94f45c6be4 --- /dev/null +++ b/target/linux/generic/pending-5.4/767-net-dsa-Sync-static-FDB-entries-on-foreign-interface.patch @@ -0,0 +1,56 @@ +From 81e39fd78db82fb51b05fff309b9c521f1a0bc5a Mon Sep 17 00:00:00 2001 +From: Tobias Waldekranz <tobias@waldekranz.com> +Date: Sat, 16 Jan 2021 02:25:14 +0100 +Subject: [PATCH] net: dsa: Sync static FDB entries on foreign interfaces to + hardware + +Reuse the "assisted_learning_on_cpu_port" functionality to always add +entries for user-configured entries on foreign interfaces, even if +assisted_learning_on_cpu_port is not enabled. E.g. in this situation: + + br0 + / \ +swp0 dummy0 + +$ bridge fdb add 02:00:de:ad:00:01 dev dummy0 vlan 1 master + +Results in DSA adding an entry in the hardware FDB, pointing this +address towards the CPU port. + +The same is true for entries added to the bridge itself, e.g: + +$ bridge fdb add 02:00:de:ad:00:01 dev br0 vlan 1 self + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + net/dsa/slave.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +--- a/net/dsa/slave.c ++++ b/net/dsa/slave.c +@@ -1702,9 +1702,12 @@ static int dsa_slave_switchdev_event(str + else if (!fdb_info->added_by_user) + return NOTIFY_OK; + } else { +- /* Snoop addresses learnt on foreign interfaces +- * bridged with us, for switches that don't +- * automatically learn SA from CPU-injected traffic ++ /* Snoop addresses added to foreign interfaces ++ * bridged with us, or the bridge ++ * itself. Dynamically learned addresses can ++ * also be added for switches that don't ++ * automatically learn SA from CPU-injected ++ * traffic. + */ + struct net_device *br_dev; + struct dsa_slave_priv *p; +@@ -1726,7 +1729,8 @@ static int dsa_slave_switchdev_event(str + + dp = p->dp->cpu_dp; + +- if (!dp->ds->assisted_learning_on_cpu_port) ++ if (!fdb_info->added_by_user && ++ !dp->ds->assisted_learning_on_cpu_port) + return NOTIFY_DONE; + } + diff --git a/target/linux/generic/pending-5.4/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-5.4/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch new file mode 100644 index 0000000000..cb421f164b --- /dev/null +++ b/target/linux/generic/pending-5.4/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch @@ -0,0 +1,27 @@ +From: Tobias Waldekranz <tobias@waldekranz.com> +Subject: [RFC net-next 7/7] net: dsa: mv88e6xxx: Request assisted learning on CPU port +Date: Sat, 16 Jan 2021 02:25:15 +0100 +Archived-At: <https://lore.kernel.org/netdev/20210116012515.3152-8-tobias@waldekranz.com/> + +While the hardware is capable of performing learning on the CPU port, +it requires alot of additions to the bridge's forwarding path in order +to handle multi-destination traffic correctly. + +Until that is in place, opt for the next best thing and let DSA sync +the relevant addresses down to the hardware FDB. + +Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com> +--- + drivers/net/dsa/mv88e6xxx/chip.c | 1 + + 1 file changed, 1 insertion(+) + +--- a/drivers/net/dsa/mv88e6xxx/chip.c ++++ b/drivers/net/dsa/mv88e6xxx/chip.c +@@ -5080,6 +5080,7 @@ static int mv88e6xxx_register_switch(str + ds->ops = &mv88e6xxx_switch_ops; + ds->ageing_time_min = chip->info->age_time_coeff; + ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; ++ ds->assisted_learning_on_cpu_port = true; + + dev_set_drvdata(dev, ds); + |