Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

net: ethtool: fix ioctl confusing drivers about desired HDS user config

The legacy ioctl path does not have support for extended attributes.
So we issue a GET to fetch the current settings from the driver,
in an attempt to keep them unchanged. HDS is a bit "special" as
the GET only returns on/off while the SET takes a "ternary" argument
(on/off/default). If the driver was in the "default" setting -
executing the ioctl path binds it to on or off, even tho the user
did not intend to change HDS config.

Factor the relevant logic out of the netlink code and reuse it.

Fixes: 87c8f8496a05 ("bnxt_en: add support for tcp-data-split ethtool command")
Acked-by: Stanislav Fomichev <sdf@fomichev.me>
Tested-by: Daniel Xu <dxu@dxuuu.xyz>
Tested-by: Taehee Yoo <ap420073@gmail.com>
Link: https://patch.msgid.link/20250221025141.1132944-1-kuba@kernel.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+28 -7
+16
net/ethtool/common.c
··· 6 6 #include <linux/rtnetlink.h> 7 7 #include <linux/ptp_clock_kernel.h> 8 8 #include <linux/phy_link_topology.h> 9 + #include <net/netdev_queues.h> 9 10 10 11 #include "netlink.h" 11 12 #include "common.h" ··· 770 769 * mean the ops attached to a netdev later on are sane. 771 770 */ 772 771 return 0; 772 + } 773 + 774 + void ethtool_ringparam_get_cfg(struct net_device *dev, 775 + struct ethtool_ringparam *param, 776 + struct kernel_ethtool_ringparam *kparam, 777 + struct netlink_ext_ack *extack) 778 + { 779 + memset(param, 0, sizeof(*param)); 780 + memset(kparam, 0, sizeof(*kparam)); 781 + 782 + param->cmd = ETHTOOL_GRINGPARAM; 783 + dev->ethtool_ops->get_ringparam(dev, param, kparam, extack); 784 + 785 + /* Driver gives us current state, we want to return current config */ 786 + kparam->tcp_data_split = dev->cfg->hds_config; 773 787 } 774 788 775 789 static void ethtool_init_tsinfo(struct kernel_ethtool_ts_info *info)
+6
net/ethtool/common.h
··· 51 51 struct ethtool_channels channels, 52 52 struct genl_info *info); 53 53 int ethtool_check_rss_ctx_busy(struct net_device *dev, u32 rss_context); 54 + 55 + void ethtool_ringparam_get_cfg(struct net_device *dev, 56 + struct ethtool_ringparam *param, 57 + struct kernel_ethtool_ringparam *kparam, 58 + struct netlink_ext_ack *extack); 59 + 54 60 int __ethtool_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info *info); 55 61 int ethtool_get_ts_info_by_phc(struct net_device *dev, 56 62 struct kernel_ethtool_ts_info *info,
+2 -2
net/ethtool/ioctl.c
··· 2059 2059 2060 2060 static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr) 2061 2061 { 2062 - struct ethtool_ringparam ringparam, max = { .cmd = ETHTOOL_GRINGPARAM }; 2063 2062 struct kernel_ethtool_ringparam kernel_ringparam; 2063 + struct ethtool_ringparam ringparam, max; 2064 2064 int ret; 2065 2065 2066 2066 if (!dev->ethtool_ops->set_ringparam || !dev->ethtool_ops->get_ringparam) ··· 2069 2069 if (copy_from_user(&ringparam, useraddr, sizeof(ringparam))) 2070 2070 return -EFAULT; 2071 2071 2072 - dev->ethtool_ops->get_ringparam(dev, &max, &kernel_ringparam, NULL); 2072 + ethtool_ringparam_get_cfg(dev, &max, &kernel_ringparam, NULL); 2073 2073 2074 2074 /* ensure new ring parameters are within the maximums */ 2075 2075 if (ringparam.rx_pending > max.rx_max_pending ||
+4 -5
net/ethtool/rings.c
··· 215 215 static int 216 216 ethnl_set_rings(struct ethnl_req_info *req_info, struct genl_info *info) 217 217 { 218 - struct kernel_ethtool_ringparam kernel_ringparam = {}; 219 - struct ethtool_ringparam ringparam = {}; 218 + struct kernel_ethtool_ringparam kernel_ringparam; 220 219 struct net_device *dev = req_info->dev; 220 + struct ethtool_ringparam ringparam; 221 221 struct nlattr **tb = info->attrs; 222 222 const struct nlattr *err_attr; 223 223 bool mod = false; 224 224 int ret; 225 225 226 - dev->ethtool_ops->get_ringparam(dev, &ringparam, 227 - &kernel_ringparam, info->extack); 228 - kernel_ringparam.tcp_data_split = dev->cfg->hds_config; 226 + ethtool_ringparam_get_cfg(dev, &ringparam, &kernel_ringparam, 227 + info->extack); 229 228 230 229 ethnl_update_u32(&ringparam.rx_pending, tb[ETHTOOL_A_RINGS_RX], &mod); 231 230 ethnl_update_u32(&ringparam.rx_mini_pending,