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>
.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 |
.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,
.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 |
.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,
.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,
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,
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,
.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,
* 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
* 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
* @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
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;
}
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;
}
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];
}
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);
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: