summaryrefslogtreecommitdiff
path: root/net
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2021-09-19 12:08:37 +0100
committerDavid S. Miller <davem@davemloft.net>2021-09-19 12:08:37 +0100
commit564df7ab10ad900c1494dc99d9d3710258d07ba2 (patch)
tree95ab75ae6b74022bac3b565dfa37d7b1248fe927 /net
parent02319bf15acf54004216e40ac9c171437f24be24 (diff)
parenta68e9da48568a0adf5dc817ef81971c0d1aa0672 (diff)
Merge branch 'dsa-shutdown'
Vladimir Oltean says: ==================== Make DSA switch drivers compatible with masters which unregister on shutdown Changes in v2: - fix build for b53_mmap - use unregister_netdevice_many It was reported by Lino here: https://lore.kernel.org/netdev/20210909095324.12978-1-LinoSanfilippo@gmx.de/ that when the DSA master attempts to unregister its net_device on shutdown, DSA should prevent that operation from succeeding because it holds a reference to it. This hangs the shutdown process. This issue was essentially introduced in commit 2f1e8ea726e9 ("net: dsa: link interfaces with the DSA master to get rid of lockdep warnings"). The present series patches all DSA drivers to handle that case, depending on whether those drivers were introduced before or after the offending commit, a different Fixes: tag is specified for them. The approach taken by this series solves the issue in essentially the same way as Lino's patches, except for three key differences: - this series takes a more minimal approach in what is done on shutdown, we do not attempt a full tree teardown as that is not strictly necessary. I might revisit this if there are compelling reasons to do otherwise - this series fixes the issues for all DSA drivers, not just KSZ9897 - this series works even if the ->remove driver method gets called for the same device too, not just ->shutdown. This is really possible to happen for SPI device drivers, and potentially possible for other bus device drivers too. ==================== Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/dsa/dsa2.c50
1 files changed, 50 insertions, 0 deletions
diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c
index eef13cd20f19..fa88e58705f0 100644
--- a/net/dsa/dsa2.c
+++ b/net/dsa/dsa2.c
@@ -1562,3 +1562,53 @@ void dsa_unregister_switch(struct dsa_switch *ds)
mutex_unlock(&dsa2_mutex);
}
EXPORT_SYMBOL_GPL(dsa_unregister_switch);
+
+/* If the DSA master chooses to unregister its net_device on .shutdown, DSA is
+ * blocking that operation from completion, due to the dev_hold taken inside
+ * netdev_upper_dev_link. Unlink the DSA slave interfaces from being uppers of
+ * the DSA master, so that the system can reboot successfully.
+ */
+void dsa_switch_shutdown(struct dsa_switch *ds)
+{
+ struct net_device *master, *slave_dev;
+ LIST_HEAD(unregister_list);
+ struct dsa_port *dp;
+
+ mutex_lock(&dsa2_mutex);
+ rtnl_lock();
+
+ list_for_each_entry(dp, &ds->dst->ports, list) {
+ if (dp->ds != ds)
+ continue;
+
+ if (!dsa_port_is_user(dp))
+ continue;
+
+ master = dp->cpu_dp->master;
+ slave_dev = dp->slave;
+
+ netdev_upper_dev_unlink(master, slave_dev);
+ /* Just unlinking ourselves as uppers of the master is not
+ * sufficient. When the master net device unregisters, that will
+ * also call dev_close, which we will catch as NETDEV_GOING_DOWN
+ * and trigger a dev_close on our own devices (dsa_slave_close).
+ * In turn, that will call dev_mc_unsync on the master's net
+ * device. If the master is also a DSA switch port, this will
+ * trigger dsa_slave_set_rx_mode which will call dev_mc_sync on
+ * its own master. Lockdep will complain about the fact that
+ * all cascaded masters have the same dsa_master_addr_list_lock_key,
+ * which it normally would not do if the cascaded masters would
+ * be in a proper upper/lower relationship, which we've just
+ * destroyed.
+ * To suppress the lockdep warnings, let's actually unregister
+ * the DSA slave interfaces too, to avoid the nonsensical
+ * multicast address list synchronization on shutdown.
+ */
+ unregister_netdevice_queue(slave_dev, &unregister_list);
+ }
+ unregister_netdevice_many(&unregister_list);
+
+ rtnl_unlock();
+ mutex_unlock(&dsa2_mutex);
+}
+EXPORT_SYMBOL_GPL(dsa_switch_shutdown);