[PATCH v2 net-next 5/5] net: dsa: handle SWITCHDEV_FDB_{ADD,DEL}_TO_DEVICE synchronously

Vladimir Oltean
2021-08-19T16:08:16Z

Since the switchdev FDB entry notifications are now blocking and
deferred by switchdev and not by us, switchdev will also wait for us to
finish, which means we can proceed with our FDB isolation mechanism
based on dp->bridge_num.

It also means that the ordered workqueue is no longer needed, drop it
and simply call the driver.

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
---
net/dsa/dsa.c | 15 -------
net/dsa/dsa_priv.h | 15 -------
net/dsa/slave.c | 110 ++++++++++++---------------------------------
3 files changed, 28 insertions(+), 112 deletions(-)

diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c
index 1dc45e40f961..b2126334387f 100644
--- a/net/dsa/dsa.c
+++ b/net/dsa/dsa.c
@@ -338,13 +338,6 @@ static struct packet_type dsa_pack_type __read_mostly = {
.func = dsa_switch_rcv,
};

-static struct workqueue_struct *dsa_owq;
-
-bool dsa_schedule_work(struct work_struct *work)
-{
- return queue_work(dsa_owq, work);
-}
-
int dsa_devlink_param_get(struct devlink *dl, u32 id,
struct devlink_param_gset_ctx *ctx)
{
@@ -465,11 +458,6 @@ static int __init dsa_init_module(void)
{
int rc;

- dsa_owq = alloc_ordered_workqueue("dsa_ordered",
- WQ_MEM_RECLAIM);
- if (!dsa_owq)
- return -ENOMEM;
-
rc = dsa_slave_register_notifier();
if (rc)
goto register_notifier_fail;
@@ -482,8 +470,6 @@ static int __init dsa_init_module(void)
return 0;

register_notifier_fail:
- destroy_workqueue(dsa_owq);
-
return rc;
}
module_init(dsa_init_module);
@@ -494,7 +480,6 @@ static void __exit dsa_cleanup_module(void)

dsa_slave_unregister_notifier();
dev_remove_pack(&dsa_pack_type);
- destroy_workqueue(dsa_owq);
}
module_exit(dsa_cleanup_module);

diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h
index b7a269e0513f..f759abceeb18 100644
--- a/net/dsa/dsa_priv.h
+++ b/net/dsa/dsa_priv.h
@@ -125,20 +125,6 @@ struct dsa_notifier_tag_8021q_vlan_info {
u16 vid;
};

-struct dsa_switchdev_event_work {
- struct dsa_switch *ds;
- int port;
- struct net_device *dev;
- 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;
- bool host_addr;
-};
-
/* DSA_NOTIFIER_HSR_* */
struct dsa_notifier_hsr_info {
struct net_device *hsr;
@@ -169,7 +155,6 @@ const struct dsa_device_ops *dsa_tag_driver_get(int tag_protocol);
void dsa_tag_driver_put(const struct dsa_device_ops *ops);
const struct dsa_device_ops *dsa_find_tagger_by_name(const char *buf);

-bool dsa_schedule_work(struct work_struct *work);
const char *dsa_tag_protocol_to_str(const struct dsa_device_ops *ops);

static inline int dsa_tag_protocol_overhead(const struct dsa_device_ops *ops)
diff --git a/net/dsa/slave.c b/net/dsa/slave.c
index b6a94861cddd..faa08e6d8651 100644
--- a/net/dsa/slave.c
+++ b/net/dsa/slave.c
@@ -2278,73 +2278,18 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb,
return NOTIFY_DONE;
}

-static void
-dsa_fdb_offload_notify(struct dsa_switchdev_event_work *switchdev_work)
+static void dsa_fdb_offload_notify(struct net_device *dev,
+ const unsigned char *addr,
+ u16 vid)
{
struct switchdev_notifier_fdb_info info = {};
- struct dsa_switch *ds = switchdev_work->ds;
- 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.addr = addr;
+ info.vid = 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 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:
- if (switchdev_work->host_addr)
- err = dsa_port_host_fdb_add(dp, switchdev_work->addr,
- switchdev_work->vid);
- else
- err = dsa_port_fdb_add(dp, switchdev_work->addr,
- switchdev_work->vid);
- if (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;
- }
- dsa_fdb_offload_notify(switchdev_work);
- break;
-
- case SWITCHDEV_FDB_DEL_TO_DEVICE:
- if (switchdev_work->host_addr)
- err = dsa_port_host_fdb_del(dp, switchdev_work->addr,
- switchdev_work->vid);
- else
- err = dsa_port_fdb_del(dp, switchdev_work->addr,
- switchdev_work->vid);
- if (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();

- dev_put(switchdev_work->dev);
- kfree(switchdev_work);
+ call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev, &info.info,
+ NULL);
}

static bool dsa_foreign_dev_check(const struct net_device *dev,
@@ -2369,10 +2314,12 @@ static int dsa_slave_fdb_event(struct net_device *dev,
const struct switchdev_notifier_fdb_info *fdb_info,
unsigned long event)
{
- struct dsa_switchdev_event_work *switchdev_work;
struct dsa_port *dp = dsa_slave_to_port(dev);
+ const unsigned char *addr = fdb_info->addr;
bool host_addr = fdb_info->is_local;
struct dsa_switch *ds = dp->ds;
+ u16 vid = fdb_info->vid;
+ int err;

if (ctx && ctx != dp)
return 0;
@@ -2397,30 +2344,29 @@ static int dsa_slave_fdb_event(struct net_device *dev,
if (dsa_foreign_dev_check(dev, orig_dev))
host_addr = true;

- switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
- if (!switchdev_work)
- return -ENOMEM;
-
netdev_dbg(dev, "%s FDB entry towards %s, addr %pM vid %d%s\n",
event == SWITCHDEV_FDB_ADD_TO_DEVICE ? "Adding" : "Deleting",
- orig_dev->name, fdb_info->addr, fdb_info->vid,
- host_addr ? " as host address" : "");
-
- INIT_WORK(&switchdev_work->work, dsa_slave_switchdev_event_work);
- switchdev_work->ds = ds;
- switchdev_work->port = dp->index;
- switchdev_work->event = event;
- switchdev_work->dev = dev;
+ orig_dev->name, addr, vid, host_addr ? " as host address" : "");

- ether_addr_copy(switchdev_work->addr, fdb_info->addr);
- switchdev_work->vid = fdb_info->vid;
- switchdev_work->host_addr = host_addr;
+ switch (event) {
+ case SWITCHDEV_FDB_ADD_TO_DEVICE:
+ if (host_addr)
+ err = dsa_port_host_fdb_add(dp, addr, vid);
+ else
+ err = dsa_port_fdb_add(dp, addr, vid);
+ if (!err)
+ dsa_fdb_offload_notify(dev, addr, vid);
+ break;

- /* Hold a reference for dsa_fdb_offload_notify */
- dev_hold(dev);
- dsa_schedule_work(&switchdev_work->work);
+ case SWITCHDEV_FDB_DEL_TO_DEVICE:
+ if (host_addr)
+ err = dsa_port_host_fdb_del(dp, addr, vid);
+ else
+ err = dsa_port_fdb_del(dp, addr, vid);
+ break;
+ }

- return 0;
+ return err;
}

static int
--
2.25.1