net: dsa: mv88e6xxx: flush switchdev FDB workqueue before removing VLAN
authorVladimir Oltean <vladimir.oltean@nxp.com>
Fri, 11 Feb 2022 17:45:06 +0000 (19:45 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 14 Feb 2022 13:31:12 +0000 (13:31 +0000)
mv88e6xxx is special among DSA drivers in that it requires the VTU to
contain the VID of the FDB entry it modifies in
mv88e6xxx_port_db_load_purge(), otherwise it will return -EOPNOTSUPP.

Sometimes due to races this is not always satisfied even if external
code does everything right (first deletes the FDB entries, then the
VLAN), because DSA commits to hardware FDB entries asynchronously since
commit c9eb3e0f8701 ("net: dsa: Add support for learning FDB through
notification").

Therefore, the mv88e6xxx driver must close this race condition by
itself, by asking DSA to flush the switchdev workqueue of any FDB
deletions in progress, prior to exiting a VLAN.

Fixes: c9eb3e0f8701 ("net: dsa: Add support for learning FDB through notification")
Reported-by: Rafael Richter <rafael.richter@gin.de>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/dsa/mv88e6xxx/chip.c
include/net/dsa.h
net/dsa/dsa.c
net/dsa/dsa_priv.h

index 8530dbe403f4dc6c970d2c19bbf104aec6dc7419..ab1676553714cdfeaf0d3f17d2d515efc585212d 100644 (file)
@@ -2284,6 +2284,13 @@ static int mv88e6xxx_port_vlan_del(struct dsa_switch *ds, int port,
        if (!mv88e6xxx_max_vid(chip))
                return -EOPNOTSUPP;
 
+       /* The ATU removal procedure needs the FID to be mapped in the VTU,
+        * but FDB deletion runs concurrently with VLAN deletion. Flush the DSA
+        * switchdev workqueue to ensure that all FDB entries are deleted
+        * before we remove the VLAN.
+        */
+       dsa_flush_workqueue();
+
        mv88e6xxx_reg_lock(chip);
 
        err = mv88e6xxx_port_get_pvid(chip, port, &pvid);
index 57b3e4e7413bfbdfe6d9de31cfa98d3c3ac7d3fd..85a5ba3772f5530d00b3cf09e2f9f84c8f2229ff 100644 (file)
@@ -1187,6 +1187,7 @@ void dsa_unregister_switch(struct dsa_switch *ds);
 int dsa_register_switch(struct dsa_switch *ds);
 void dsa_switch_shutdown(struct dsa_switch *ds);
 struct dsa_switch *dsa_switch_find(int tree_index, int sw_index);
+void dsa_flush_workqueue(void);
 #ifdef CONFIG_PM_SLEEP
 int dsa_switch_suspend(struct dsa_switch *ds);
 int dsa_switch_resume(struct dsa_switch *ds);
index d9d0d227092c8c09acff4c936004d7b825300412..c43f7446a75d12f2e38d73d7db0fdf229509d4dd 100644 (file)
@@ -349,6 +349,7 @@ void dsa_flush_workqueue(void)
 {
        flush_workqueue(dsa_owq);
 }
+EXPORT_SYMBOL_GPL(dsa_flush_workqueue);
 
 int dsa_devlink_param_get(struct devlink *dl, u32 id,
                          struct devlink_param_gset_ctx *ctx)
index 760306f0012fde416556227717965b44269279b1..23c79e91ac676448e63621c4e2f0d25f98ebe5d1 100644 (file)
@@ -147,7 +147,6 @@ 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);
-void dsa_flush_workqueue(void);
 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)