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:
@@ -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,
|
||||
|
||||
@@ -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 *);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user