diff options
| author | Paolo Abeni <pabeni@redhat.com> | 2026-09-08 15:33:46 +0200 |
|---|---|---|
| committer | Paolo Abeni <pabeni@redhat.com> | 2026-09-08 15:33:47 +0200 |
| commit | ab217fbb9b2169ce677b09a66558d5c3adcfbb76 (patch) | |
| tree | 81ba059b6f7301f8dce628ab2bad0a42b58448a9 | |
| parent | bf6f89ccd9748d895fa861da0edaccb9194e41be (diff) | |
| parent | 5d074ded6714fe44edd10b5f20cc43d74612579f (diff) | |
| download | linux-next-ab217fbb9b2169ce677b09a66558d5c3adcfbb76.tar.gz linux-next-ab217fbb9b2169ce677b09a66558d5c3adcfbb76.zip | |
Merge branch 'net-sysfs-use-ops-lock-for-speed-and-duplex'
Wang Zhan says:
====================
net: sysfs: use ops lock for speed and duplex
Reading speed and duplex from sysfs currently holds RTNL across the
get_link_ksettings callback. This unnecessarily serializes monitoring
reads with unrelated rtnetlink operations.
On CPU-throttled hosts, a periodic reader such as node-exporter can hold
RTNL for hundreds of milliseconds while an mlx5 callback runs, delaying
unrelated rtnetlink operations.
Factor the shared link settings read first, then change only the helper's
locking. Legacy devices and callbacks which request
ETHTOOL_OP_NEEDS_RTNL_LINKSETTINGS retain the existing RTNL path.
v1: https://lore.kernel.org/netdev/20260831080623.1064001-1-wang.zhan@smartx.com/
====================
Link: https://patch.msgid.link/20260903133314.3703381-1-wang.zhan@smartx.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
| -rw-r--r-- | net/core/net-sysfs.c | 112 |
1 files changed, 63 insertions, 49 deletions
diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 352173df7578..fa790e442570 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -118,6 +118,49 @@ unbreak: return ret; } +static int sysfs_get_link_ksettings(struct device *dev, + struct device_attribute *attr, + struct ethtool_link_ksettings *cmd) +{ + struct net_device *netdev = to_net_dev(dev); + bool need_rtnl; + int ret; + + /* + * The check is also done in netif_get_link_ksettings; this helps + * returning early without hitting the locking section below. + */ + if (!netdev->ethtool_ops->get_link_ksettings) + return -EINVAL; + + need_rtnl = !netdev_need_ops_lock(netdev) || + (netdev->ethtool_ops->op_needs_rtnl & + ETHTOOL_OP_NEEDS_RTNL_LINKSETTINGS); + if (need_rtnl) { + ret = sysfs_rtnl_lock(&dev->kobj, &attr->attr, netdev); + if (ret) + return ret; + } + netdev_lock_ops(netdev); + + if (!dev_isalive(netdev)) { + ret = -ENODEV; + goto unlock; + } + + ret = -EINVAL; + if (netif_running(netdev)) { + if (!netif_get_link_ksettings(netdev, cmd)) + ret = 0; + } + +unlock: + netdev_unlock_ops(netdev); + if (need_rtnl) + rtnl_unlock(); + return ret; +} + /* use same locking rules as GIF* ioctl's */ static ssize_t netdev_show(const struct device *dev, struct device_attribute *attr, char *buf, @@ -332,70 +375,41 @@ static DEVICE_ATTR_RW(carrier); static ssize_t speed_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - int ret = -EINVAL; - - /* The check is also done in __ethtool_get_link_ksettings; this helps - * returning early without hitting the locking section below. - */ - if (!netdev->ethtool_ops->get_link_ksettings) - return ret; + struct ethtool_link_ksettings cmd; + int ret; - ret = sysfs_rtnl_lock(&dev->kobj, &attr->attr, netdev); + ret = sysfs_get_link_ksettings(dev, attr, &cmd); if (ret) return ret; - ret = -EINVAL; - if (netif_running(netdev)) { - struct ethtool_link_ksettings cmd; - - if (!__ethtool_get_link_ksettings(netdev, &cmd)) - ret = sysfs_emit(buf, fmt_dec, cmd.base.speed); - } - rtnl_unlock(); - return ret; + return sysfs_emit(buf, fmt_dec, cmd.base.speed); } static DEVICE_ATTR_RO(speed); static ssize_t duplex_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct net_device *netdev = to_net_dev(dev); - int ret = -EINVAL; - - /* The check is also done in __ethtool_get_link_ksettings; this helps - * returning early without hitting the locking section below. - */ - if (!netdev->ethtool_ops->get_link_ksettings) - return ret; + struct ethtool_link_ksettings cmd; + const char *duplex; + int ret; - ret = sysfs_rtnl_lock(&dev->kobj, &attr->attr, netdev); + ret = sysfs_get_link_ksettings(dev, attr, &cmd); if (ret) return ret; - ret = -EINVAL; - if (netif_running(netdev)) { - struct ethtool_link_ksettings cmd; - - if (!__ethtool_get_link_ksettings(netdev, &cmd)) { - const char *duplex; - - switch (cmd.base.duplex) { - case DUPLEX_HALF: - duplex = "half"; - break; - case DUPLEX_FULL: - duplex = "full"; - break; - default: - duplex = "unknown"; - break; - } - ret = sysfs_emit(buf, "%s\n", duplex); - } + switch (cmd.base.duplex) { + case DUPLEX_HALF: + duplex = "half"; + break; + case DUPLEX_FULL: + duplex = "full"; + break; + default: + duplex = "unknown"; + break; } - rtnl_unlock(); - return ret; + + return sysfs_emit(buf, "%s\n", duplex); } static DEVICE_ATTR_RO(duplex); |
