]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net: dsa: remove redundant netdev_lock_ops() from conduit ethtool ops
authorStanislav Fomichev <sdf.kernel@gmail.com>
Tue, 14 Apr 2026 23:10:35 +0000 (16:10 -0700)
committerJakub Kicinski <kuba@kernel.org>
Fri, 17 Apr 2026 02:10:48 +0000 (19:10 -0700)
DSA replaces the conduit (master) device's ethtool_ops with its own
wrappers that aggregate stats from both the conduit and DSA switch
ports. Taking the lock again inside the DSA wrappers causes a deadlock.

Stumbled upon this when booting qemu with fbnic and CONFIG_NET_DSA_LOOP=y
(which looks like some kind of testing device that auto-populates the ports
of eth0). `ethtool -i` is enough to deadlock. This means we have basically zero
coverage for DSA stuff with real ops locked devs.

Remove the redundant netdev_lock_ops()/netdev_unlock_ops() calls from
the DSA conduit ethtool wrappers.

Fixes: 2bcf4772e45a ("net: ethtool: try to protect all callback with netdev instance lock")
Signed-off-by: Stanislav Fomichev <sdf@fomichev.me>
Reviewed-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Link: https://patch.msgid.link/20260414231035.1917035-1-sdf@fomichev.me
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/dsa/conduit.c

index a1b044467bd6fa01f7d92a211f8c0af9f5481ae8..8398d72d7e4d36894ac99cb0343951442fae1272 100644 (file)
@@ -27,9 +27,7 @@ static int dsa_conduit_get_regs_len(struct net_device *dev)
        int len;
 
        if (ops && ops->get_regs_len) {
-               netdev_lock_ops(dev);
                len = ops->get_regs_len(dev);
-               netdev_unlock_ops(dev);
                if (len < 0)
                        return len;
                ret += len;
@@ -60,15 +58,11 @@ static void dsa_conduit_get_regs(struct net_device *dev,
        int len;
 
        if (ops && ops->get_regs_len && ops->get_regs) {
-               netdev_lock_ops(dev);
                len = ops->get_regs_len(dev);
-               if (len < 0) {
-                       netdev_unlock_ops(dev);
+               if (len < 0)
                        return;
-               }
                regs->len = len;
                ops->get_regs(dev, regs, data);
-               netdev_unlock_ops(dev);
                data += regs->len;
        }
 
@@ -115,10 +109,8 @@ static void dsa_conduit_get_ethtool_stats(struct net_device *dev,
        int count, mcount = 0;
 
        if (ops && ops->get_sset_count && ops->get_ethtool_stats) {
-               netdev_lock_ops(dev);
                mcount = ops->get_sset_count(dev, ETH_SS_STATS);
                ops->get_ethtool_stats(dev, stats, data);
-               netdev_unlock_ops(dev);
        }
 
        list_for_each_entry(dp, &dst->ports, list) {
@@ -149,10 +141,8 @@ static void dsa_conduit_get_ethtool_phy_stats(struct net_device *dev,
                if (count >= 0)
                        phy_ethtool_get_stats(dev->phydev, stats, data);
        } else if (ops && ops->get_sset_count && ops->get_ethtool_phy_stats) {
-               netdev_lock_ops(dev);
                count = ops->get_sset_count(dev, ETH_SS_PHY_STATS);
                ops->get_ethtool_phy_stats(dev, stats, data);
-               netdev_unlock_ops(dev);
        }
 
        if (count < 0)
@@ -176,13 +166,11 @@ static int dsa_conduit_get_sset_count(struct net_device *dev, int sset)
        struct dsa_switch_tree *dst = cpu_dp->dst;
        int count = 0;
 
-       netdev_lock_ops(dev);
        if (sset == ETH_SS_PHY_STATS && dev->phydev &&
            (!ops || !ops->get_ethtool_phy_stats))
                count = phy_ethtool_get_sset_count(dev->phydev);
        else if (ops && ops->get_sset_count)
                count = ops->get_sset_count(dev, sset);
-       netdev_unlock_ops(dev);
 
        if (count < 0)
                count = 0;
@@ -239,7 +227,6 @@ static void dsa_conduit_get_strings(struct net_device *dev, u32 stringset,
        struct dsa_switch_tree *dst = cpu_dp->dst;
        int count, mcount = 0;
 
-       netdev_lock_ops(dev);
        if (stringset == ETH_SS_PHY_STATS && dev->phydev &&
            !ops->get_ethtool_phy_stats) {
                mcount = phy_ethtool_get_sset_count(dev->phydev);
@@ -253,7 +240,6 @@ static void dsa_conduit_get_strings(struct net_device *dev, u32 stringset,
                        mcount = 0;
                ops->get_strings(dev, stringset, data);
        }
-       netdev_unlock_ops(dev);
 
        list_for_each_entry(dp, &dst->ports, list) {
                if (!dsa_port_is_dsa(dp) && !dsa_port_is_cpu(dp))