Modify ethtool_copy_validate_indir() and callers to validate indirection table entries against the number of RX rings as an integer instead of accessing rx_rings->data. This will be useful in the future, given that struct ethtool_rxnfc might not exist for native GRXRINGS call. Signed-off-by: Breno Leitao --- net/ethtool/ioctl.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 0b2a4d0573b38..15627afa4424f 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1246,8 +1246,8 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, } static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr, - struct ethtool_rxnfc *rx_rings, - u32 size) + int num_rx_rings, + u32 size) { int i; @@ -1256,7 +1256,7 @@ static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr, /* Validate ring indices */ for (i = 0; i < size; i++) - if (indir[i] >= rx_rings->data) + if (indir[i] >= num_rx_rings) return -EINVAL; return 0; @@ -1366,7 +1366,7 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, } else { ret = ethtool_copy_validate_indir(rxfh_dev.indir, useraddr + ringidx_offset, - &rx_rings, + rx_rings.data, rxfh_dev.indir_size); if (ret) goto out; @@ -1587,7 +1587,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, rxfh_dev.indir_size = dev_indir_size; ret = ethtool_copy_validate_indir(rxfh_dev.indir, useraddr + rss_cfg_offset, - &rx_rings, + rx_rings.data, rxfh.indir_size); if (ret) goto out_free; -- 2.47.3 This patch adds handling for the ETHTOOL_GRXRINGS ioctl command in the ethtool ioctl dispatcher. It introduces a new helper function ethtool_get_rxrings() that calls the driver's get_rxnfc() callback with appropriate parameters to retrieve the number of RX rings supported by the device. By explicitly handling ETHTOOL_GRXRINGS, userspace queries through ethtool can now obtain RX ring information in a structured manner. In this patch, ethtool_get_rxrings() is a simply copy of ethtool_get_rxnfc(). Signed-off-by: Breno Leitao --- net/ethtool/ioctl.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 15627afa4424f..4214ab33c3c81 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1208,6 +1208,44 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, return 0; } +static noinline_for_stack int ethtool_get_rxrings(struct net_device *dev, + u32 cmd, + void __user *useraddr) +{ + struct ethtool_rxnfc info; + size_t info_size = sizeof(info); + const struct ethtool_ops *ops = dev->ethtool_ops; + int ret; + void *rule_buf = NULL; + + if (!ops->get_rxnfc) + return -EOPNOTSUPP; + + ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr); + if (ret) + return ret; + + if (info.cmd == ETHTOOL_GRXCLSRLALL) { + if (info.rule_cnt > 0) { + if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32)) + rule_buf = kcalloc(info.rule_cnt, sizeof(u32), + GFP_USER); + if (!rule_buf) + return -ENOMEM; + } + } + + ret = ops->get_rxnfc(dev, &info, rule_buf); + if (ret < 0) + goto err_out; + + ret = ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, rule_buf); +err_out: + kfree(rule_buf); + + return ret; +} + static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, u32 cmd, void __user *useraddr) { @@ -3377,6 +3415,8 @@ __dev_ethtool(struct net *net, struct ifreq *ifr, void __user *useraddr, rc = ethtool_set_rxfh_fields(dev, ethcmd, useraddr); break; case ETHTOOL_GRXRINGS: + rc = ethtool_get_rxrings(dev, ethcmd, useraddr); + break; case ETHTOOL_GRXCLSRLCNT: case ETHTOOL_GRXCLSRULE: case ETHTOOL_GRXCLSRLALL: -- 2.47.3 ethtool_get_rxrings() was a copy of ethtool_get_rxnfc(). Clean the code that will never be executed for GRXRINGS specifically. Signed-off-by: Breno Leitao --- net/ethtool/ioctl.c | 33 ++++++++++----------------------- 1 file changed, 10 insertions(+), 23 deletions(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 4214ab33c3c81..a0f3de76cea03 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1212,52 +1212,39 @@ static noinline_for_stack int ethtool_get_rxrings(struct net_device *dev, u32 cmd, void __user *useraddr) { - struct ethtool_rxnfc info; - size_t info_size = sizeof(info); const struct ethtool_ops *ops = dev->ethtool_ops; + struct ethtool_rxnfc info; + size_t info_size; int ret; - void *rule_buf = NULL; if (!ops->get_rxnfc) return -EOPNOTSUPP; + info_size = sizeof(info); ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr); if (ret) return ret; - if (info.cmd == ETHTOOL_GRXCLSRLALL) { - if (info.rule_cnt > 0) { - if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32)) - rule_buf = kcalloc(info.rule_cnt, sizeof(u32), - GFP_USER); - if (!rule_buf) - return -ENOMEM; - } - } - - ret = ops->get_rxnfc(dev, &info, rule_buf); + ret = ops->get_rxnfc(dev, &info, NULL); if (ret < 0) - goto err_out; - - ret = ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, rule_buf); -err_out: - kfree(rule_buf); + return ret; - return ret; + return ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, NULL); } static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, u32 cmd, void __user *useraddr) { - struct ethtool_rxnfc info; - size_t info_size = sizeof(info); const struct ethtool_ops *ops = dev->ethtool_ops; - int ret; + struct ethtool_rxnfc info; void *rule_buf = NULL; + size_t info_size; + int ret; if (!ops->get_rxnfc) return -EOPNOTSUPP; + info_size = sizeof(info); ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr); if (ret) return ret; -- 2.47.3 Add a new optional get_rx_ring_count callback in ethtool_ops to allow drivers to provide the number of RX rings directly without going through the full get_rxnfc flow classification interface. Create ethtool_get_rx_ring_count() to use .get_rx_ring_count if available, falling back to get_rxnfc() otherwise. It needs to be non-static, given it will be called by other ethtool functions laters, as those calling get_rxfh(). Signed-off-by: Breno Leitao --- include/linux/ethtool.h | 2 ++ net/ethtool/common.c | 20 ++++++++++++++++++++ net/ethtool/common.h | 2 ++ net/ethtool/ioctl.c | 8 +++----- 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index d7d757e72554e..c869b7f8bce8b 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -968,6 +968,7 @@ struct kernel_ethtool_ts_info { * @reset: Reset (part of) the device, as specified by a bitmask of * flags from &enum ethtool_reset_flags. Returns a negative * error code or zero. + * @get_rx_ring_count: Return the number of RX rings * @get_rxfh_key_size: Get the size of the RX flow hash key. * Returns zero if not supported for this specific device. * @get_rxfh_indir_size: Get the size of the RX flow hash indirection table. @@ -1162,6 +1163,7 @@ struct ethtool_ops { int (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *); int (*flash_device)(struct net_device *, struct ethtool_flash *); int (*reset)(struct net_device *, u32 *); + u32 (*get_rx_ring_count)(struct net_device *dev); u32 (*get_rxfh_key_size)(struct net_device *); u32 (*get_rxfh_indir_size)(struct net_device *); int (*get_rxfh)(struct net_device *, struct ethtool_rxfh_param *); diff --git a/net/ethtool/common.c b/net/ethtool/common.c index 4f58648a27ad6..10460ea3717ca 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -577,6 +577,26 @@ int __ethtool_get_link(struct net_device *dev) return netif_running(dev) && dev->ethtool_ops->get_link(dev); } +int ethtool_get_rx_ring_count(struct net_device *dev) +{ + const struct ethtool_ops *ops = dev->ethtool_ops; + struct ethtool_rxnfc rx_rings = {}; + int ret; + + if (ops->get_rx_ring_count) + return ops->get_rx_ring_count(dev); + + if (!ops->get_rxnfc) + return -EOPNOTSUPP; + + rx_rings.cmd = ETHTOOL_GRXRINGS; + ret = ops->get_rxnfc(dev, &rx_rings, NULL); + if (ret < 0) + return ret; + + return rx_rings.data; +} + static int ethtool_get_rxnfc_rule_count(struct net_device *dev) { const struct ethtool_ops *ops = dev->ethtool_ops; diff --git a/net/ethtool/common.h b/net/ethtool/common.h index c4d084dde5bf4..1609cf4e53ebb 100644 --- a/net/ethtool/common.h +++ b/net/ethtool/common.h @@ -54,6 +54,8 @@ void ethtool_ringparam_get_cfg(struct net_device *dev, struct kernel_ethtool_ringparam *kparam, struct netlink_ext_ack *extack); +int ethtool_get_rx_ring_count(struct net_device *dev); + int __ethtool_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info *info); int ethtool_get_ts_info_by_phc(struct net_device *dev, struct kernel_ethtool_ts_info *info, diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index a0f3de76cea03..8493ee200601e 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1212,23 +1212,21 @@ static noinline_for_stack int ethtool_get_rxrings(struct net_device *dev, u32 cmd, void __user *useraddr) { - const struct ethtool_ops *ops = dev->ethtool_ops; struct ethtool_rxnfc info; size_t info_size; int ret; - if (!ops->get_rxnfc) - return -EOPNOTSUPP; - info_size = sizeof(info); ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr); if (ret) return ret; - ret = ops->get_rxnfc(dev, &info, NULL); + ret = ethtool_get_rx_ring_count(dev); if (ret < 0) return ret; + info.data = ret; + return ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, NULL); } -- 2.47.3 Modify ethtool_set_rxfh() to use the new ethtool_get_rx_ring_count() helper function for retrieving the number of RX rings instead of directly calling get_rxnfc with ETHTOOL_GRXRINGS. This way, we can leverage the new helper if it is available in ethtool_ops. Signed-off-by: Breno Leitao --- net/ethtool/ioctl.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 8493ee200601e..d61e34751adc8 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1531,14 +1531,14 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, struct ethtool_rxfh_param rxfh_dev = {}; struct ethtool_rxfh_context *ctx = NULL; struct netlink_ext_ack *extack = NULL; - struct ethtool_rxnfc rx_rings; struct ethtool_rxfh rxfh; bool create = false; + int num_rx_rings; u8 *rss_config; int ntf = 0; int ret; - if (!ops->get_rxnfc || !ops->set_rxfh) + if (!ops->set_rxfh) return -EOPNOTSUPP; if (ops->get_rxfh_indir_size) @@ -1594,10 +1594,11 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, if (!rss_config) return -ENOMEM; - rx_rings.cmd = ETHTOOL_GRXRINGS; - ret = ops->get_rxnfc(dev, &rx_rings, NULL); - if (ret) + num_rx_rings = ethtool_get_rx_ring_count(dev); + if (num_rx_rings < 0) { + ret = num_rx_rings; goto out_free; + } /* rxfh.indir_size == 0 means reset the indir table to default (master * context) or delete the context (other RSS contexts). @@ -1610,7 +1611,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, rxfh_dev.indir_size = dev_indir_size; ret = ethtool_copy_validate_indir(rxfh_dev.indir, useraddr + rss_cfg_offset, - rx_rings.data, + num_rx_rings, rxfh.indir_size); if (ret) goto out_free; @@ -1622,7 +1623,8 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev, rxfh_dev.indir_size = dev_indir_size; indir = rxfh_dev.indir; for (i = 0; i < dev_indir_size; i++) - indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); + indir[i] = + ethtool_rxfh_indir_default(i, num_rx_rings); } else { rxfh_dev.rss_delete = true; } -- 2.47.3 Modify ethtool_set_rxfh() to use the new ethtool_get_rx_ring_count() helper function for retrieving the number of RX rings instead of directly calling get_rxnfc with ETHTOOL_GRXRINGS. This way, we can leverage the new helper if it is available in ethtool_ops. Signed-off-by: Breno Leitao --- net/ethtool/ioctl.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index d61e34751adc8..fa83ddade4f81 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1350,13 +1350,12 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, const struct ethtool_ops *ops = dev->ethtool_ops; struct ethtool_rxfh_param rxfh_dev = {}; struct netlink_ext_ack *extack = NULL; - struct ethtool_rxnfc rx_rings; + int num_rx_rings; u32 user_size, i; int ret; u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]); - if (!ops->get_rxfh_indir_size || !ops->set_rxfh || - !ops->get_rxnfc) + if (!ops->get_rxfh_indir_size || !ops->set_rxfh) return -EOPNOTSUPP; rxfh_dev.indir_size = ops->get_rxfh_indir_size(dev); @@ -1376,20 +1375,21 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev, if (!rxfh_dev.indir) return -ENOMEM; - rx_rings.cmd = ETHTOOL_GRXRINGS; - ret = ops->get_rxnfc(dev, &rx_rings, NULL); - if (ret) + num_rx_rings = ethtool_get_rx_ring_count(dev); + if (num_rx_rings < 0) { + ret = num_rx_rings; goto out; + } if (user_size == 0) { u32 *indir = rxfh_dev.indir; for (i = 0; i < rxfh_dev.indir_size; i++) - indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); + indir[i] = ethtool_rxfh_indir_default(i, num_rx_rings); } else { ret = ethtool_copy_validate_indir(rxfh_dev.indir, useraddr + ringidx_offset, - rx_rings.data, + num_rx_rings, rxfh_dev.indir_size); if (ret) goto out; -- 2.47.3 Refactor rss_set_prep_indir() to utilize the new ethtool_get_rx_ring_count() helper for determining the number of RX rings, replacing the direct use of get_rxnfc with ETHTOOL_GRXRINGS. This ensures compatibility with both legacy and new ethtool_ops interfaces by transparently multiplexing between them. Signed-off-by: Breno Leitao --- net/ethtool/rss.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c index 202d95e8bf3e1..4dced53be4b3b 100644 --- a/net/ethtool/rss.c +++ b/net/ethtool/rss.c @@ -620,23 +620,22 @@ rss_set_prep_indir(struct net_device *dev, struct genl_info *info, struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh, bool *reset, bool *mod) { - const struct ethtool_ops *ops = dev->ethtool_ops; struct netlink_ext_ack *extack = info->extack; struct nlattr **tb = info->attrs; - struct ethtool_rxnfc rx_rings; size_t alloc_size; + int num_rx_rings; u32 user_size; int i, err; if (!tb[ETHTOOL_A_RSS_INDIR]) return 0; - if (!data->indir_size || !ops->get_rxnfc) + if (!data->indir_size) return -EOPNOTSUPP; - rx_rings.cmd = ETHTOOL_GRXRINGS; - err = ops->get_rxnfc(dev, &rx_rings, NULL); - if (err) + err = ethtool_get_rx_ring_count(dev); + if (err < 0) return err; + num_rx_rings = err; if (nla_len(tb[ETHTOOL_A_RSS_INDIR]) % 4) { NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_INDIR]); @@ -665,7 +664,7 @@ rss_set_prep_indir(struct net_device *dev, struct genl_info *info, nla_memcpy(rxfh->indir, tb[ETHTOOL_A_RSS_INDIR], alloc_size); for (i = 0; i < user_size; i++) { - if (rxfh->indir[i] < rx_rings.data) + if (rxfh->indir[i] < num_rx_rings) continue; NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR], @@ -682,7 +681,7 @@ rss_set_prep_indir(struct net_device *dev, struct genl_info *info, } else { for (i = 0; i < data->indir_size; i++) rxfh->indir[i] = - ethtool_rxfh_indir_default(i, rx_rings.data); + ethtool_rxfh_indir_default(i, num_rx_rings); } *mod |= memcmp(rxfh->indir, data->indir_table, data->indir_size); -- 2.47.3 Replace the existing virtnet_get_rxnfc callback with a dedicated virtnet_get_rxrings implementation to provide the number of RX rings directly via the new ethtool_ops get_rx_ring_count pointer. This simplifies the RX ring count retrieval and aligns virtio_net with the new ethtool API for querying RX ring parameters. Signed-off-by: Breno Leitao --- drivers/net/virtio_net.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 06708c9a979e6..7da5a37917e92 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -5609,20 +5609,11 @@ static int virtnet_set_rxfh(struct net_device *dev, return 0; } -static int virtnet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, u32 *rule_locs) +static u32 virtnet_get_rx_ring_count(struct net_device *dev) { struct virtnet_info *vi = netdev_priv(dev); - int rc = 0; - switch (info->cmd) { - case ETHTOOL_GRXRINGS: - info->data = vi->curr_queue_pairs; - break; - default: - rc = -EOPNOTSUPP; - } - - return rc; + return vi->curr_queue_pairs; } static const struct ethtool_ops virtnet_ethtool_ops = { @@ -5650,7 +5641,7 @@ static const struct ethtool_ops virtnet_ethtool_ops = { .set_rxfh = virtnet_set_rxfh, .get_rxfh_fields = virtnet_get_hashflow, .set_rxfh_fields = virtnet_set_hashflow, - .get_rxnfc = virtnet_get_rxnfc, + .get_rx_ring_count = virtnet_get_rx_ring_count, }; static void virtnet_get_queue_stats_rx(struct net_device *dev, int i, -- 2.47.3