diff options
author | DENG Qingfang <dqfext@gmail.com> | 2021-02-22 13:38:11 +0800 |
---|---|---|
committer | Hauke Mehrtens <hauke@hauke-m.de> | 2021-02-23 21:10:56 +0100 |
commit | 920eaab1d8179035d0ae1047e75cf9a50da6a6eb (patch) | |
tree | 14c29956bb33e682f304c643247aa023dd7ec211 /target/linux/generic/backport-5.10/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch | |
parent | 90b1524f75023ad0f441e82773ca9e9b5d513e1a (diff) | |
download | upstream-920eaab1d8179035d0ae1047e75cf9a50da6a6eb.tar.gz upstream-920eaab1d8179035d0ae1047e75cf9a50da6a6eb.tar.bz2 upstream-920eaab1d8179035d0ae1047e75cf9a50da6a6eb.zip |
kernel: DSA roaming fix for Marvell mv88e6xxx
Marvell mv88e6xxx switch series cannot perform MAC learning from
CPU-injected (FROM_CPU) DSA frames, which results in 2 issues.
- 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
Backport those patch series from netdev mailing list, which solve these
issues by adding and clearing static entries to the switch's FDB.
Add a hack patch to set default VID to 1 in port_fdb_{add,del}. Otherwise
the static entries will be added to the switch's private FDB if VLAN
filtering disabled, which will not work.
The switch may generate an "ATU violation" warning when a client moves
from the CPU port to a switch port because the static ATU entry added by
DSA core still points to the CPU port. DSA core will then clear the static
entry so it is not fatal. Disable the warning so it will not confuse users.
Link: https://lore.kernel.org/netdev/20210106095136.224739-1-olteanv@gmail.com/
Link: https://lore.kernel.org/netdev/20210116012515.3152-1-tobias@waldekranz.com/
Ref: https://gitlab.nic.cz/turris/turris-build/-/issues/165
Signed-off-by: DENG Qingfang <dqfext@gmail.com>
Diffstat (limited to 'target/linux/generic/backport-5.10/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch')
-rw-r--r-- | target/linux/generic/backport-5.10/772-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch | 226 |
1 files changed, 226 insertions, 0 deletions
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, |