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

Merge branch 'net-ethtool-add-dedicated-grxrings-driver-callbacks'

Breno Leitao says:

====================
net: ethtool: add dedicated GRXRINGS driver callbacks

This patchset introduces a new dedicated ethtool_ops callback,
.get_rx_ring_count, which enables drivers to provide the number of RX
rings directly, improving efficiency and clarity in RX ring queries and
RSS configuration.

Number of drivers implements .get_rxnfc callback just to report the ring
count, so, having a proper callback makes sense and simplify .get_rxnfc
(in some cases remove it completely).

This has been suggested by Jakub, and follow the same idea as RXFH
driver callbacks [1].

This also port virtio_net to this new callback. Once there is consensus
on this approach, I can start moving the drivers to this new callback.

Link: https://lore.kernel.org/all/20250611145949.2674086-1-kuba@kernel.org/ [1]

v3: https://lore.kernel.org/20250915-gxrings-v3-0-bfd717dbcaad@debian.org
v2: https://lore.kernel.org/20250912-gxrings-v2-0-3c7a60bbeebf@debian.org
v1: https://lore.kernel.org/20250909-gxrings-v1-0-634282f06a54@debian.org
RFC: https://lore.kernel.org/20250905-gxrings-v1-0-984fc471f28f@debian.org
====================

Link: https://patch.msgid.link/20250917-gxrings-v4-0-dae520e2e1cb@debian.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+82 -41
+3 -12
drivers/net/virtio_net.c
··· 5609 5609 return 0; 5610 5610 } 5611 5611 5612 - static int virtnet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, u32 *rule_locs) 5612 + static u32 virtnet_get_rx_ring_count(struct net_device *dev) 5613 5613 { 5614 5614 struct virtnet_info *vi = netdev_priv(dev); 5615 - int rc = 0; 5616 5615 5617 - switch (info->cmd) { 5618 - case ETHTOOL_GRXRINGS: 5619 - info->data = vi->curr_queue_pairs; 5620 - break; 5621 - default: 5622 - rc = -EOPNOTSUPP; 5623 - } 5624 - 5625 - return rc; 5616 + return vi->curr_queue_pairs; 5626 5617 } 5627 5618 5628 5619 static const struct ethtool_ops virtnet_ethtool_ops = { ··· 5641 5650 .set_rxfh = virtnet_set_rxfh, 5642 5651 .get_rxfh_fields = virtnet_get_hashflow, 5643 5652 .set_rxfh_fields = virtnet_set_hashflow, 5644 - .get_rxnfc = virtnet_get_rxnfc, 5653 + .get_rx_ring_count = virtnet_get_rx_ring_count, 5645 5654 }; 5646 5655 5647 5656 static void virtnet_get_queue_stats_rx(struct net_device *dev, int i,
+2
include/linux/ethtool.h
··· 968 968 * @reset: Reset (part of) the device, as specified by a bitmask of 969 969 * flags from &enum ethtool_reset_flags. Returns a negative 970 970 * error code or zero. 971 + * @get_rx_ring_count: Return the number of RX rings 971 972 * @get_rxfh_key_size: Get the size of the RX flow hash key. 972 973 * Returns zero if not supported for this specific device. 973 974 * @get_rxfh_indir_size: Get the size of the RX flow hash indirection table. ··· 1163 1162 int (*set_rxnfc)(struct net_device *, struct ethtool_rxnfc *); 1164 1163 int (*flash_device)(struct net_device *, struct ethtool_flash *); 1165 1164 int (*reset)(struct net_device *, u32 *); 1165 + u32 (*get_rx_ring_count)(struct net_device *dev); 1166 1166 u32 (*get_rxfh_key_size)(struct net_device *); 1167 1167 u32 (*get_rxfh_indir_size)(struct net_device *); 1168 1168 int (*get_rxfh)(struct net_device *, struct ethtool_rxfh_param *);
+20
net/ethtool/common.c
··· 577 577 return netif_running(dev) && dev->ethtool_ops->get_link(dev); 578 578 } 579 579 580 + int ethtool_get_rx_ring_count(struct net_device *dev) 581 + { 582 + const struct ethtool_ops *ops = dev->ethtool_ops; 583 + struct ethtool_rxnfc rx_rings = {}; 584 + int ret; 585 + 586 + if (ops->get_rx_ring_count) 587 + return ops->get_rx_ring_count(dev); 588 + 589 + if (!ops->get_rxnfc) 590 + return -EOPNOTSUPP; 591 + 592 + rx_rings.cmd = ETHTOOL_GRXRINGS; 593 + ret = ops->get_rxnfc(dev, &rx_rings, NULL); 594 + if (ret < 0) 595 + return ret; 596 + 597 + return rx_rings.data; 598 + } 599 + 580 600 static int ethtool_get_rxnfc_rule_count(struct net_device *dev) 581 601 { 582 602 const struct ethtool_ops *ops = dev->ethtool_ops;
+2
net/ethtool/common.h
··· 54 54 struct kernel_ethtool_ringparam *kparam, 55 55 struct netlink_ext_ack *extack); 56 56 57 + int ethtool_get_rx_ring_count(struct net_device *dev); 58 + 57 59 int __ethtool_get_ts_info(struct net_device *dev, struct kernel_ethtool_ts_info *info); 58 60 int ethtool_get_ts_info_by_phc(struct net_device *dev, 59 61 struct kernel_ethtool_ts_info *info,
+48 -21
net/ethtool/ioctl.c
··· 1208 1208 return 0; 1209 1209 } 1210 1210 1211 + static noinline_for_stack int ethtool_get_rxrings(struct net_device *dev, 1212 + u32 cmd, 1213 + void __user *useraddr) 1214 + { 1215 + struct ethtool_rxnfc info; 1216 + size_t info_size; 1217 + int ret; 1218 + 1219 + info_size = sizeof(info); 1220 + ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr); 1221 + if (ret) 1222 + return ret; 1223 + 1224 + ret = ethtool_get_rx_ring_count(dev); 1225 + if (ret < 0) 1226 + return ret; 1227 + 1228 + info.data = ret; 1229 + 1230 + return ethtool_rxnfc_copy_to_user(useraddr, &info, info_size, NULL); 1231 + } 1232 + 1211 1233 static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev, 1212 1234 u32 cmd, void __user *useraddr) 1213 1235 { 1214 - struct ethtool_rxnfc info; 1215 - size_t info_size = sizeof(info); 1216 1236 const struct ethtool_ops *ops = dev->ethtool_ops; 1217 - int ret; 1237 + struct ethtool_rxnfc info; 1218 1238 void *rule_buf = NULL; 1239 + size_t info_size; 1240 + int ret; 1219 1241 1220 1242 if (!ops->get_rxnfc) 1221 1243 return -EOPNOTSUPP; 1222 1244 1245 + info_size = sizeof(info); 1223 1246 ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr); 1224 1247 if (ret) 1225 1248 return ret; ··· 1269 1246 } 1270 1247 1271 1248 static int ethtool_copy_validate_indir(u32 *indir, void __user *useraddr, 1272 - struct ethtool_rxnfc *rx_rings, 1273 - u32 size) 1249 + int num_rx_rings, 1250 + u32 size) 1274 1251 { 1275 1252 int i; 1276 1253 ··· 1279 1256 1280 1257 /* Validate ring indices */ 1281 1258 for (i = 0; i < size; i++) 1282 - if (indir[i] >= rx_rings->data) 1259 + if (indir[i] >= num_rx_rings) 1283 1260 return -EINVAL; 1284 1261 1285 1262 return 0; ··· 1350 1327 const struct ethtool_ops *ops = dev->ethtool_ops; 1351 1328 struct ethtool_rxfh_param rxfh_dev = {}; 1352 1329 struct netlink_ext_ack *extack = NULL; 1353 - struct ethtool_rxnfc rx_rings; 1330 + int num_rx_rings; 1354 1331 u32 user_size, i; 1355 1332 int ret; 1356 1333 u32 ringidx_offset = offsetof(struct ethtool_rxfh_indir, ring_index[0]); 1357 1334 1358 - if (!ops->get_rxfh_indir_size || !ops->set_rxfh || 1359 - !ops->get_rxnfc) 1335 + if (!ops->get_rxfh_indir_size || !ops->set_rxfh) 1360 1336 return -EOPNOTSUPP; 1361 1337 1362 1338 rxfh_dev.indir_size = ops->get_rxfh_indir_size(dev); ··· 1375 1353 if (!rxfh_dev.indir) 1376 1354 return -ENOMEM; 1377 1355 1378 - rx_rings.cmd = ETHTOOL_GRXRINGS; 1379 - ret = ops->get_rxnfc(dev, &rx_rings, NULL); 1380 - if (ret) 1356 + num_rx_rings = ethtool_get_rx_ring_count(dev); 1357 + if (num_rx_rings < 0) { 1358 + ret = num_rx_rings; 1381 1359 goto out; 1360 + } 1382 1361 1383 1362 if (user_size == 0) { 1384 1363 u32 *indir = rxfh_dev.indir; 1385 1364 1386 1365 for (i = 0; i < rxfh_dev.indir_size; i++) 1387 - indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); 1366 + indir[i] = ethtool_rxfh_indir_default(i, num_rx_rings); 1388 1367 } else { 1389 1368 ret = ethtool_copy_validate_indir(rxfh_dev.indir, 1390 1369 useraddr + ringidx_offset, 1391 - &rx_rings, 1370 + num_rx_rings, 1392 1371 rxfh_dev.indir_size); 1393 1372 if (ret) 1394 1373 goto out; ··· 1531 1508 struct ethtool_rxfh_param rxfh_dev = {}; 1532 1509 struct ethtool_rxfh_context *ctx = NULL; 1533 1510 struct netlink_ext_ack *extack = NULL; 1534 - struct ethtool_rxnfc rx_rings; 1535 1511 struct ethtool_rxfh rxfh; 1536 1512 bool create = false; 1513 + int num_rx_rings; 1537 1514 u8 *rss_config; 1538 1515 int ntf = 0; 1539 1516 int ret; 1540 1517 1541 - if (!ops->get_rxnfc || !ops->set_rxfh) 1518 + if (!ops->set_rxfh) 1542 1519 return -EOPNOTSUPP; 1543 1520 1544 1521 if (ops->get_rxfh_indir_size) ··· 1594 1571 if (!rss_config) 1595 1572 return -ENOMEM; 1596 1573 1597 - rx_rings.cmd = ETHTOOL_GRXRINGS; 1598 - ret = ops->get_rxnfc(dev, &rx_rings, NULL); 1599 - if (ret) 1574 + num_rx_rings = ethtool_get_rx_ring_count(dev); 1575 + if (num_rx_rings < 0) { 1576 + ret = num_rx_rings; 1600 1577 goto out_free; 1578 + } 1601 1579 1602 1580 /* rxfh.indir_size == 0 means reset the indir table to default (master 1603 1581 * context) or delete the context (other RSS contexts). ··· 1611 1587 rxfh_dev.indir_size = dev_indir_size; 1612 1588 ret = ethtool_copy_validate_indir(rxfh_dev.indir, 1613 1589 useraddr + rss_cfg_offset, 1614 - &rx_rings, 1590 + num_rx_rings, 1615 1591 rxfh.indir_size); 1616 1592 if (ret) 1617 1593 goto out_free; ··· 1623 1599 rxfh_dev.indir_size = dev_indir_size; 1624 1600 indir = rxfh_dev.indir; 1625 1601 for (i = 0; i < dev_indir_size; i++) 1626 - indir[i] = ethtool_rxfh_indir_default(i, rx_rings.data); 1602 + indir[i] = 1603 + ethtool_rxfh_indir_default(i, num_rx_rings); 1627 1604 } else { 1628 1605 rxfh_dev.rss_delete = true; 1629 1606 } ··· 3402 3377 rc = ethtool_set_rxfh_fields(dev, ethcmd, useraddr); 3403 3378 break; 3404 3379 case ETHTOOL_GRXRINGS: 3380 + rc = ethtool_get_rxrings(dev, ethcmd, useraddr); 3381 + break; 3405 3382 case ETHTOOL_GRXCLSRLCNT: 3406 3383 case ETHTOOL_GRXCLSRULE: 3407 3384 case ETHTOOL_GRXCLSRLALL:
+7 -8
net/ethtool/rss.c
··· 620 620 struct rss_reply_data *data, struct ethtool_rxfh_param *rxfh, 621 621 bool *reset, bool *mod) 622 622 { 623 - const struct ethtool_ops *ops = dev->ethtool_ops; 624 623 struct netlink_ext_ack *extack = info->extack; 625 624 struct nlattr **tb = info->attrs; 626 - struct ethtool_rxnfc rx_rings; 627 625 size_t alloc_size; 626 + int num_rx_rings; 628 627 u32 user_size; 629 628 int i, err; 630 629 631 630 if (!tb[ETHTOOL_A_RSS_INDIR]) 632 631 return 0; 633 - if (!data->indir_size || !ops->get_rxnfc) 632 + if (!data->indir_size) 634 633 return -EOPNOTSUPP; 635 634 636 - rx_rings.cmd = ETHTOOL_GRXRINGS; 637 - err = ops->get_rxnfc(dev, &rx_rings, NULL); 638 - if (err) 635 + err = ethtool_get_rx_ring_count(dev); 636 + if (err < 0) 639 637 return err; 638 + num_rx_rings = err; 640 639 641 640 if (nla_len(tb[ETHTOOL_A_RSS_INDIR]) % 4) { 642 641 NL_SET_BAD_ATTR(info->extack, tb[ETHTOOL_A_RSS_INDIR]); ··· 664 665 665 666 nla_memcpy(rxfh->indir, tb[ETHTOOL_A_RSS_INDIR], alloc_size); 666 667 for (i = 0; i < user_size; i++) { 667 - if (rxfh->indir[i] < rx_rings.data) 668 + if (rxfh->indir[i] < num_rx_rings) 668 669 continue; 669 670 670 671 NL_SET_ERR_MSG_ATTR_FMT(extack, tb[ETHTOOL_A_RSS_INDIR], ··· 681 682 } else { 682 683 for (i = 0; i < data->indir_size; i++) 683 684 rxfh->indir[i] = 684 - ethtool_rxfh_indir_default(i, rx_rings.data); 685 + ethtool_rxfh_indir_default(i, num_rx_rings); 685 686 } 686 687 687 688 *mod |= memcmp(rxfh->indir, data->indir_table, data->indir_size);