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 | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 4214ab33c3c81..1a9ad47f60313 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1216,7 +1216,6 @@ static noinline_for_stack int ethtool_get_rxrings(struct net_device *dev, 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; @@ -1225,25 +1224,11 @@ static noinline_for_stack int ethtool_get_rxrings(struct net_device *dev, 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, -- 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. Modify ethtool_get_rxrings() to use get_rx_ring_count() if available, falling back to get_rxnfc() otherwise. Signed-off-by: Breno Leitao --- include/linux/ethtool.h | 2 ++ net/ethtool/ioctl.c | 22 ++++++++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index de5bd76a400ca..2d91fd3102c14 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/ioctl.c b/net/ethtool/ioctl.c index 1a9ad47f60313..aba483bc9fd85 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1208,6 +1208,22 @@ static noinline_for_stack int ethtool_set_rxnfc(struct net_device *dev, return 0; } +static int get_num_rxrings(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); + + ret = ops->get_rxnfc(dev, &rx_rings, NULL); + if (ret < 0) + return ret; + + return rx_rings.data; +} + static noinline_for_stack int ethtool_get_rxrings(struct net_device *dev, u32 cmd, void __user *useraddr) @@ -1217,16 +1233,14 @@ static noinline_for_stack int ethtool_get_rxrings(struct net_device *dev, const struct ethtool_ops *ops = dev->ethtool_ops; int ret; - if (!ops->get_rxnfc) + if (!ops->get_rxnfc && !ops->get_rx_ring_count) return -EOPNOTSUPP; ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr); if (ret) return ret; - ret = ops->get_rxnfc(dev, &info, NULL); - if (ret < 0) - return ret; + info.data = get_num_rxrings(dev); return ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, NULL); } -- 2.47.3 Modify ethtool_set_rxfh() to use the new get_num_rxrings() 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 | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index aba483bc9fd85..227083a8ab860 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1545,9 +1545,9 @@ 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; @@ -1608,10 +1608,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 = get_num_rxrings(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). @@ -1624,7 +1625,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; @@ -1636,7 +1637,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 get_num_rxrings() 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 | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 227083a8ab860..752f185d2d7ea 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1364,7 +1364,7 @@ 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]); @@ -1390,20 +1390,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 = get_num_rxrings(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 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 975bdc5dab84b..e35b6ef015c05 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -5610,20 +5610,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 = { @@ -5651,7 +5642,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