2e17c71095
Without this patch we have to manually bring up the CPU interface in failsafe mode. This was backported from kernel 5.12. Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de> Tested-by: Rafał Miłecki <rafal@milecki.pl>
227 lines
6.7 KiB
Diff
227 lines
6.7 KiB
Diff
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
|
|
@@ -1570,76 +1570,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() */
|
|
@@ -1647,7 +1637,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) {
|
|
@@ -1660,20 +1652,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:
|
|
@@ -1683,10 +1687,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,
|