summaryrefslogtreecommitdiff
path: root/net/ethtool
diff options
context:
space:
mode:
authorJakub Kicinski <kuba@kernel.org>2026-06-04 17:29:05 -0700
committerJakub Kicinski <kuba@kernel.org>2026-06-09 10:13:05 -0700
commitf9a3e05114b85d63452e7f9c172b53d6a1736fe0 (patch)
tree5b7754e541525360bb9cecee974711de163dcf45 /net/ethtool
parent45079e00133ee78fd216ccc4285534044ea69173 (diff)
downloadlinux-next-f9a3e05114b85d63452e7f9c172b53d6a1736fe0.tar.gz
linux-next-f9a3e05114b85d63452e7f9c172b53d6a1736fe0.zip
net: ethtool: optionally skip rtnl_lock on Netlink path for SET ops
Make ethtool not take rtnl_lock for SET commands when operation is performed on an ops-locked driver. cfg/cfg_pending are now ops-locked, since only ethtool modifies them. Some SET driver callbacks will still need rtnl_lock, most notably those which may end up calling netdev_update_features() or the qdisc layer (via netif_set_real_num_tx_queues()). Let drivers selectively opt back into the rtnl_lock with a new bitfield in ops. We need two helpers since Netlink and ioctl cmds have different values. Keep the helpers side by side in common.h to make sure they get updated together, even tho they will only get called from ioctl.c and netlink.c. SET commands which don't use ethnl_default_set_doit() are converted by subsequent commits. Reviewed-by: Eric Dumazet <edumazet@google.com> Acked-by: Stanislav Fomichev <sdf@fomichev.me> Reviewed-by: Jacob Keller <jacob.e.keller@intel.com> Link: https://patch.msgid.link/20260605002912.3456868-6-kuba@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'net/ethtool')
-rw-r--r--net/ethtool/common.h30
-rw-r--r--net/ethtool/netlink.c9
2 files changed, 37 insertions, 2 deletions
diff --git a/net/ethtool/common.h b/net/ethtool/common.h
index 391c41ca56be..e3052972f953 100644
--- a/net/ethtool/common.h
+++ b/net/ethtool/common.h
@@ -95,10 +95,24 @@ ethtool_nl_msg_needs_rtnl(const struct net_device *dev, u8 cmd)
switch (cmd) {
case ETHTOOL_MSG_LINKINFO_GET:
+ case ETHTOOL_MSG_LINKINFO_SET:
case ETHTOOL_MSG_LINKMODES_GET:
+ case ETHTOOL_MSG_LINKMODES_SET:
return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_LINKSETTINGS;
+ case ETHTOOL_MSG_PRIVFLAGS_SET:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SPFLAGS;
+ case ETHTOOL_MSG_RINGS_SET:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM;
+ case ETHTOOL_MSG_CHANNELS_SET:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SCHANNELS;
+ case ETHTOOL_MSG_COALESCE_SET:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SCOALESCE;
case ETHTOOL_MSG_PAUSE_GET:
return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_GPAUSEPARAM;
+ case ETHTOOL_MSG_PAUSE_SET:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SPAUSEPARAM;
+ case ETHTOOL_MSG_RSS_SET:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_RSS;
}
return false;
}
@@ -119,9 +133,25 @@ ethtool_ioctl_needs_rtnl(const struct net_device *dev, u32 ethcmd)
switch (ethcmd) {
case ETHTOOL_GLINKSETTINGS:
case ETHTOOL_GSET:
+ case ETHTOOL_SLINKSETTINGS:
+ case ETHTOOL_SSET:
return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_LINKSETTINGS;
+ case ETHTOOL_SPFLAGS:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SPFLAGS;
+ case ETHTOOL_SRINGPARAM:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM;
+ case ETHTOOL_SCHANNELS:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SCHANNELS;
+ case ETHTOOL_SCOALESCE:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SCOALESCE;
case ETHTOOL_GPAUSEPARAM:
return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_GPAUSEPARAM;
+ case ETHTOOL_SPAUSEPARAM:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_SPAUSEPARAM;
+ case ETHTOOL_SRSSH:
+ case ETHTOOL_SRXFH:
+ case ETHTOOL_SRXFHINDIR:
+ return ops->op_needs_rtnl & ETHTOOL_OP_NEEDS_RTNL_RSS;
}
return false;
}
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
index 2c30eb1f4666..1af395b54330 100644
--- a/net/ethtool/netlink.c
+++ b/net/ethtool/netlink.c
@@ -903,6 +903,7 @@ static int ethnl_default_set_doit(struct sk_buff *skb, struct genl_info *info)
const u8 cmd = info->genlhdr->cmd;
struct ethnl_req_info *req_info;
struct net_device *dev;
+ bool need_rtnl;
int ret;
ops = ethnl_default_requests[cmd];
@@ -927,8 +928,11 @@ static int ethnl_default_set_doit(struct sk_buff *skb, struct genl_info *info)
}
dev = req_info->dev;
+ need_rtnl = !netdev_need_ops_lock(dev) ||
+ ethtool_nl_msg_needs_rtnl(dev, cmd);
- rtnl_lock();
+ if (need_rtnl)
+ rtnl_lock();
netdev_lock_ops(dev);
dev->cfg_pending = kmemdup(dev->cfg, sizeof(*dev->cfg),
GFP_KERNEL_ACCOUNT);
@@ -958,7 +962,8 @@ out_free_cfg:
out_tie_cfg:
dev->cfg_pending = dev->cfg;
netdev_unlock_ops(dev);
- rtnl_unlock();
+ if (need_rtnl)
+ rtnl_unlock();
out_dev:
ethnl_parse_header_dev_put(req_info);
out_free_req: