aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/generic/backport-5.10/722-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/generic/backport-5.10/722-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch')
-rw-r--r--target/linux/generic/backport-5.10/722-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch226
1 files changed, 226 insertions, 0 deletions
diff --git a/target/linux/generic/backport-5.10/722-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch b/target/linux/generic/backport-5.10/722-v5.12-net-dsa-don-t-use-switchdev_notifier_fdb_info-in-dsa.patch
new file mode 100644
index 0000000000..c1aa8fda82
--- /dev/null
+++ b/target/linux/generic/backport-5.10/722-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
+@@ -2087,76 +2087,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() */
+@@ -2164,7 +2154,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) {
+@@ -2177,20 +2169,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:
+@@ -2200,10 +2204,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,