]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: ethtool: optionally skip rtnl_lock on Netlink path for SET ops
authorJakub Kicinski <kuba@kernel.org>
Fri, 5 Jun 2026 00:29:05 +0000 (17:29 -0700)
committerJakub Kicinski <kuba@kernel.org>
Tue, 9 Jun 2026 17:13:05 +0000 (10:13 -0700)
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>
12 files changed:
drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
drivers/net/ethernet/google/gve/gve_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c
drivers/net/ethernet/mellanox/mlx5/core/en_rep.c
drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c
drivers/net/ethernet/meta/fbnic/fbnic_ethtool.c
drivers/net/ethernet/microsoft/mana/mana_ethtool.c
drivers/net/netdevsim/ethtool.c
include/linux/ethtool.h
include/linux/netdevice.h
net/ethtool/common.h
net/ethtool/netlink.c

index edafa79f636c7205f5a80f52cf492ea4f481e0ff..56d74a3c24b7c6cb95774a7e11088f65b0509ddf 100644 (file)
@@ -5729,6 +5729,10 @@ const struct ethtool_ops bnxt_ethtool_ops = {
        .rxfh_max_num_contexts          = BNXT_MAX_ETH_RSS_CTX + 1,
        .rxfh_indir_space               = BNXT_MAX_RSS_TABLE_ENTRIES_P5,
        .rxfh_priv_size                 = sizeof(struct bnxt_rss_ctx),
+       .op_needs_rtnl                  = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
+                                         ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM |
+                                         ETHTOOL_OP_NEEDS_RTNL_SCOALESCE |
+                                         ETHTOOL_OP_NEEDS_RTNL_RSS,
        .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
                                     ETHTOOL_COALESCE_MAX_FRAMES |
                                     ETHTOOL_COALESCE_USECS_IRQ |
index 7cd43e082b2a44b50129ad9ba6bb0e8c88abb00b..7cc22916852fb6ce5df6338d504c4747fe678350 100644 (file)
@@ -983,6 +983,8 @@ const struct ethtool_ops gve_ethtool_ops = {
        .supported_coalesce_params = ETHTOOL_COALESCE_USECS,
        .supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT |
                                 ETHTOOL_RING_USE_RX_BUF_LEN,
+       .op_needs_rtnl = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
+                        ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM,
        .get_drvinfo = gve_get_drvinfo,
        .get_strings = gve_get_strings,
        .get_sset_count = gve_get_sset_count,
index 61993485e4517e19976ac1c91991f5133fc48de6..2f5b626ba33fe525e22923a58589c26418ecc2fd 100644 (file)
@@ -2719,6 +2719,9 @@ const struct ethtool_ops mlx5e_ethtool_ops = {
        .rxfh_per_ctx_fields    = true,
        .rxfh_per_ctx_key       = true,
        .rxfh_max_num_contexts  = MLX5E_MAX_NUM_RSS,
+       .op_needs_rtnl          = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
+                                 ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM |
+                                 ETHTOOL_OP_NEEDS_RTNL_SPFLAGS,
        .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
                                     ETHTOOL_COALESCE_MAX_FRAMES |
                                     ETHTOOL_COALESCE_USE_ADAPTIVE |
index ba6c0f38cc7380dd3839918b45a394e2ff62c421..1a8a19f980d33588d66e0f2932044b024bc66d54 100644 (file)
@@ -418,6 +418,8 @@ static const struct ethtool_ops mlx5e_rep_ethtool_ops = {
        .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
                                     ETHTOOL_COALESCE_MAX_FRAMES |
                                     ETHTOOL_COALESCE_USE_ADAPTIVE,
+       .op_needs_rtnl     = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
+                            ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM,
        .get_drvinfo       = mlx5e_rep_get_drvinfo,
        .get_link          = ethtool_op_get_link,
        .get_strings       = mlx5e_rep_get_strings,
index 3b2f54ca30a805b16e5c5d6c0cedbe668a8e4d34..9b3b32408c64b73c9290eb5ce81f8eb7d4550fe7 100644 (file)
@@ -285,6 +285,8 @@ const struct ethtool_ops mlx5i_ethtool_ops = {
        .supported_coalesce_params = ETHTOOL_COALESCE_USECS |
                                     ETHTOOL_COALESCE_MAX_FRAMES |
                                     ETHTOOL_COALESCE_USE_ADAPTIVE,
+       .op_needs_rtnl      = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
+                             ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM,
        .get_drvinfo        = mlx5i_get_drvinfo,
        .get_strings        = mlx5i_get_strings,
        .get_sset_count     = mlx5i_get_sset_count,
index a2c16d5993897d9b504e8c23ecb327c75b3e7d92..cb34fc166ef9683ff1dd3c9cbb5fc4aeb68e70af 100644 (file)
@@ -2021,7 +2021,10 @@ static const struct ethtool_ops fbnic_ethtool_ops = {
                                          ETHTOOL_RING_USE_HDS_THRS,
        .rxfh_max_num_contexts          = FBNIC_RPC_RSS_TBL_COUNT,
        .op_needs_rtnl                  = ETHTOOL_OP_NEEDS_RTNL_LINKSETTINGS |
-                                         ETHTOOL_OP_NEEDS_RTNL_GPAUSEPARAM,
+                                         ETHTOOL_OP_NEEDS_RTNL_GPAUSEPARAM |
+                                         ETHTOOL_OP_NEEDS_RTNL_SPAUSEPARAM |
+                                         ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
+                                         ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM,
        .get_drvinfo                    = fbnic_get_drvinfo,
        .get_regs_len                   = fbnic_get_regs_len,
        .get_regs                       = fbnic_get_regs,
index 04350973e19e3f61d2c973cb47c373366f2c6f7a..55df44d728a0a8879d6b016f6c209de2f50a1916 100644 (file)
@@ -575,6 +575,8 @@ static int mana_get_link_ksettings(struct net_device *ndev,
 
 const struct ethtool_ops mana_ethtool_ops = {
        .supported_coalesce_params = ETHTOOL_COALESCE_RX_CQE_FRAMES,
+       .op_needs_rtnl          = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS |
+                                 ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM,
        .get_ethtool_stats      = mana_get_ethtool_stats,
        .get_sset_count         = mana_get_sset_count,
        .get_strings            = mana_get_strings,
index 36a201533aaea64cb33a48b1c2fd766df84a070b..9350ba48eb815632d315c673c424cd7fe884dd58 100644 (file)
@@ -209,6 +209,7 @@ static const struct ethtool_ops nsim_ethtool_ops = {
        .supported_coalesce_params      = ETHTOOL_COALESCE_ALL_PARAMS,
        .supported_ring_params          = ETHTOOL_RING_USE_TCP_DATA_SPLIT |
                                          ETHTOOL_RING_USE_HDS_THRS,
+       .op_needs_rtnl                  = ETHTOOL_OP_NEEDS_RTNL_SCHANNELS,
        .get_pause_stats                = nsim_get_pause_stats,
        .get_pauseparam                 = nsim_get_pauseparam,
        .set_pauseparam                 = nsim_set_pauseparam,
index 1da49161d36f1a3dd01e23ae99da945d596c777f..74c8109b0cf31b4907cdbef496a04b9ccc2f7df1 100644 (file)
@@ -935,7 +935,13 @@ struct kernel_ethtool_ts_info {
  * these bits separate, per GET and SET. GET is much easier to "unlock".
  */
 #define ETHTOOL_OP_NEEDS_RTNL_LINKSETTINGS     BIT(0)
-#define ETHTOOL_OP_NEEDS_RTNL_GPAUSEPARAM      BIT(1)
+#define ETHTOOL_OP_NEEDS_RTNL_SPFLAGS          BIT(1)
+#define ETHTOOL_OP_NEEDS_RTNL_SRINGPARAM       BIT(2)
+#define ETHTOOL_OP_NEEDS_RTNL_SCHANNELS                BIT(3)
+#define ETHTOOL_OP_NEEDS_RTNL_SCOALESCE                BIT(4)
+#define ETHTOOL_OP_NEEDS_RTNL_GPAUSEPARAM      BIT(5)
+#define ETHTOOL_OP_NEEDS_RTNL_SPAUSEPARAM      BIT(6)
+#define ETHTOOL_OP_NEEDS_RTNL_RSS              BIT(7)
 
 /**
  * struct ethtool_ops - optional netdev operations
@@ -970,6 +976,8 @@ struct kernel_ethtool_ts_info {
  *     The following commonly used core APIs currently require rtnl_lock
  *     (this list may not be exhaustive):
  *      - phylink helpers (note that phydev is currently unsupported!)
+ *      - netdev_update_features()
+ *      - netif_set_real_num_tx_queues()
  *
  * @get_drvinfo: Report driver/device information. Modern drivers no
  *     longer have to implement this callback. Most fields are
index a8709d0cc8d476cac6c74397c0ebc751032ab508..403b6d1c67f8f620898a91aa85821444b47573f3 100644 (file)
@@ -2584,7 +2584,7 @@ struct net_device {
         *      @up, @moving_ns, @nd_net, @xdp_features
         *
         * Ops protects:
-        *      @hwprov
+        *      @cfg, @cfg_pending, @hwprov
         *
         * Double ops protects:
         *      @real_num_rx_queues, @real_num_tx_queues
index 391c41ca56beebaab1bd5d77d5fb7c89ba47f5cb..e3052972f953bbf57836e150ea0d1ec5c4f10d5b 100644 (file)
@@ -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;
 }
index 2c30eb1f466642c7fb1f655584f9e0d99b35bcde..1af395b54330e263d606198bd79b2b60db6ba1ba 100644 (file)
@@ -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: