summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Abeni <pabeni@redhat.com>2026-09-08 15:33:46 +0200
committerPaolo Abeni <pabeni@redhat.com>2026-09-08 15:33:47 +0200
commitab217fbb9b2169ce677b09a66558d5c3adcfbb76 (patch)
tree81ba059b6f7301f8dce628ab2bad0a42b58448a9
parentbf6f89ccd9748d895fa861da0edaccb9194e41be (diff)
parent5d074ded6714fe44edd10b5f20cc43d74612579f (diff)
downloadlinux-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.c112
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);