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>
This commit is contained in:
Jakub Kicinski
2025-09-18 07:07:41 -07:00
6 changed files with 82 additions and 41 deletions
+3 -12
View File
@@ -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
View File
@@ -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 *);
+20
View File
@@ -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;
+2
View File
@@ -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,
+48 -21
View File
@@ -1208,18 +1208,41 @@ 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;
int ret;
info_size = sizeof(info);
ret = ethtool_rxnfc_copy_struct(cmd, &info, &info_size, useraddr);
if (ret)
return ret;
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);
}
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;
@@ -1246,8 +1269,8 @@ err_out:
}
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 +1279,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;
@@ -1327,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);
@@ -1353,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,
num_rx_rings,
rxfh_dev.indir_size);
if (ret)
goto out;
@@ -1508,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)
@@ -1571,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).
@@ -1587,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,
num_rx_rings,
rxfh.indir_size);
if (ret)
goto out_free;
@@ -1599,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;
}
@@ -3377,6 +3402,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:
+7 -8
View File
@@ -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);