summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjörn Töpel <bjorn@kernel.org>2026-03-20 09:58:21 +0100
committerJakub Kicinski <kuba@kernel.org>2026-03-23 17:59:53 -0700
commit0475f9e779b456f934adbc44eeb98e3080a1893f (patch)
tree916a2b62c3758febaa7d7e5d427d7220a474f2bb
parent9027497a25e3c92b5053b2643e0c18f910865625 (diff)
ethtool: Track user-provided RSS indirection table size
Track the number of indirection table entries the user originally provided (context 0/default as well!). Replace IFF_RXFH_CONFIGURED with rss_indir_user_size: the flag is redundant now that user_size captures the same information. Add ethtool_rxfh_indir_lost() for drivers that must reset the indirection table. Convert bnxt and mlx5 to use it. Signed-off-by: Björn Töpel <bjorn@kernel.org> Link: https://patch.msgid.link/20260320085826.1957255-2-bjorn@kernel.org Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-rw-r--r--drivers/net/ethernet/broadcom/bnxt/bnxt.c3
-rw-r--r--drivers/net/ethernet/mellanox/mlx5/core/en_main.c21
-rw-r--r--include/linux/ethtool.h7
-rw-r--r--include/linux/netdevice.h7
-rw-r--r--net/ethtool/common.c28
-rw-r--r--net/ethtool/ioctl.c9
-rw-r--r--net/ethtool/rss.c24
7 files changed, 70 insertions, 29 deletions
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
index 604966a398f5..84eb53b4172b 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
@@ -8118,8 +8118,7 @@ static int __bnxt_reserve_rings(struct bnxt *bp)
(bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) !=
bnxt_get_nr_rss_ctxs(bp, rx_rings) ||
bnxt_get_max_rss_ring(bp) >= rx_rings)) {
- netdev_warn(bp->dev, "RSS table entries reverting to default\n");
- bp->dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
+ ethtool_rxfh_indir_lost(bp->dev);
}
}
bp->rx_nr_rings = rx_rings;
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
index 3eebdf402129..1238e5356012 100644
--- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
+++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c
@@ -6480,12 +6480,23 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv)
/* max number of channels may have changed */
max_nch = mlx5e_calc_max_nch(priv->mdev, priv->netdev, profile);
+
+ /* Locking is required by ethtool_rxfh_indir_lost() (sends
+ * ETHTOOL_MSG_RSS_NTF) and by netif_set_real_num_*_queues in case
+ * the netdev has been registered by this point (if this function
+ * was called in the reload or resume flow).
+ */
+ if (need_lock) {
+ rtnl_lock();
+ netdev_lock(priv->netdev);
+ }
+
if (priv->channels.params.num_channels > max_nch) {
mlx5_core_warn(priv->mdev, "MLX5E: Reducing number of channels to %d\n", max_nch);
/* Reducing the number of channels - RXFH has to be reset, and
* mlx5e_num_channels_changed below will build the RQT.
*/
- priv->netdev->priv_flags &= ~IFF_RXFH_CONFIGURED;
+ ethtool_rxfh_indir_lost(priv->netdev);
priv->channels.params.num_channels = max_nch;
if (priv->channels.params.mqprio.mode == TC_MQPRIO_MODE_CHANNEL) {
mlx5_core_warn(priv->mdev, "MLX5E: Disabling MQPRIO channel mode\n");
@@ -6502,15 +6513,7 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv)
/* 1. Set the real number of queues in the kernel the first time.
* 2. Set our default XPS cpumask.
* 3. Build the RQT.
- *
- * Locking is required by netif_set_real_num_*_queues in case the
- * netdev has been registered by this point (if this function was called
- * in the reload or resume flow).
*/
- if (need_lock) {
- rtnl_lock();
- netdev_lock(priv->netdev);
- }
err = mlx5e_num_channels_changed(priv);
if (need_lock) {
netdev_unlock(priv->netdev);
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 656d465bcd06..34ca9261de82 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -176,6 +176,8 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
* struct ethtool_rxfh_context - a custom RSS context configuration
* @indir_size: Number of u32 entries in indirection table
* @key_size: Size of hash key, in bytes
+ * @indir_user_size: number of user provided entries for the
+ * indirection table
* @priv_size: Size of driver private data, in bytes
* @hfunc: RSS hash function identifier. One of the %ETH_RSS_HASH_*
* @input_xfrm: Defines how the input data is transformed. Valid values are one
@@ -186,6 +188,7 @@ static inline u32 ethtool_rxfh_indir_default(u32 index, u32 n_rx_rings)
struct ethtool_rxfh_context {
u32 indir_size;
u32 key_size;
+ u32 indir_user_size;
u16 priv_size;
u8 hfunc;
u8 input_xfrm;
@@ -214,6 +217,7 @@ static inline u8 *ethtool_rxfh_context_key(struct ethtool_rxfh_context *ctx)
}
void ethtool_rxfh_context_lost(struct net_device *dev, u32 context_id);
+void ethtool_rxfh_indir_lost(struct net_device *dev);
struct link_mode_info {
int speed;
@@ -1337,12 +1341,15 @@ int ethtool_virtdev_set_link_ksettings(struct net_device *dev,
* @rss_ctx: XArray of custom RSS contexts
* @rss_lock: Protects entries in @rss_ctx. May be taken from
* within RTNL.
+ * @rss_indir_user_size: Number of user provided entries for the default
+ * (context 0) indirection table.
* @wol_enabled: Wake-on-LAN is enabled
* @module_fw_flash_in_progress: Module firmware flashing is in progress.
*/
struct ethtool_netdev_state {
struct xarray rss_ctx;
struct mutex rss_lock;
+ u32 rss_indir_user_size;
unsigned wol_enabled:1;
unsigned module_fw_flash_in_progress:1;
};
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h
index 6882b41bb3e8..e15367373f7c 100644
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -1716,7 +1716,6 @@ struct net_device_ops {
* @IFF_OPENVSWITCH: device is a Open vSwitch master
* @IFF_L3MDEV_SLAVE: device is enslaved to an L3 master device
* @IFF_TEAM: device is a team device
- * @IFF_RXFH_CONFIGURED: device has had Rx Flow indirection table configured
* @IFF_PHONY_HEADROOM: the headroom value is controlled by an external
* entity (i.e. the master device for bridged veth)
* @IFF_MACSEC: device is a MACsec device
@@ -1752,7 +1751,6 @@ enum netdev_priv_flags {
IFF_OPENVSWITCH = 1<<20,
IFF_L3MDEV_SLAVE = 1<<21,
IFF_TEAM = 1<<22,
- IFF_RXFH_CONFIGURED = 1<<23,
IFF_PHONY_HEADROOM = 1<<24,
IFF_MACSEC = 1<<25,
IFF_NO_RX_HANDLER = 1<<26,
@@ -5580,10 +5578,7 @@ static inline bool netif_is_lag_port(const struct net_device *dev)
return netif_is_bond_slave(dev) || netif_is_team_port(dev);
}
-static inline bool netif_is_rxfh_configured(const struct net_device *dev)
-{
- return dev->priv_flags & IFF_RXFH_CONFIGURED;
-}
+bool netif_is_rxfh_configured(const struct net_device *dev);
static inline bool netif_is_failover(const struct net_device *dev)
{
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index 6a4a3797a812..98e85f393f8c 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -1204,6 +1204,34 @@ void ethtool_rxfh_context_lost(struct net_device *dev, u32 context_id)
}
EXPORT_SYMBOL(ethtool_rxfh_context_lost);
+bool netif_is_rxfh_configured(const struct net_device *dev)
+{
+ return dev->ethtool->rss_indir_user_size;
+}
+EXPORT_SYMBOL(netif_is_rxfh_configured);
+
+/**
+ * ethtool_rxfh_indir_lost - Notify core that the RSS indirection table was lost
+ * @dev: network device
+ *
+ * Drivers should call this when the device can no longer maintain the
+ * user-configured indirection table, typically after a HW fault recovery
+ * that reduced the maximum queue count. Marks the default RSS context
+ * indirection table as unconfigured and sends an %ETHTOOL_MSG_RSS_NTF
+ * notification.
+ */
+void ethtool_rxfh_indir_lost(struct net_device *dev)
+{
+ WARN_ONCE(!rtnl_is_locked() &&
+ !lockdep_is_held_type(&dev->ethtool->rss_lock, -1),
+ "RSS context lock assertion failed\n");
+
+ netdev_err(dev, "device error, RSS indirection table lost\n");
+ dev->ethtool->rss_indir_user_size = 0;
+ ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_NTF, 0);
+}
+EXPORT_SYMBOL(ethtool_rxfh_indir_lost);
+
enum ethtool_link_medium ethtool_str_to_medium(const char *str)
{
int i;
diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c
index 11dfbf076b6d..3c713a91ad0d 100644
--- a/net/ethtool/ioctl.c
+++ b/net/ethtool/ioctl.c
@@ -1405,9 +1405,9 @@ static noinline_for_stack int ethtool_set_rxfh_indir(struct net_device *dev,
/* indicate whether rxfh was set to default */
if (user_size == 0)
- dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
+ dev->ethtool->rss_indir_user_size = 0;
else
- dev->priv_flags |= IFF_RXFH_CONFIGURED;
+ dev->ethtool->rss_indir_user_size = rxfh_dev.indir_size;
out_unlock:
mutex_unlock(&dev->ethtool->rss_lock);
@@ -1722,9 +1722,9 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
if (!rxfh_dev.rss_context) {
/* indicate whether rxfh was set to default */
if (rxfh.indir_size == 0)
- dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
+ dev->ethtool->rss_indir_user_size = 0;
else if (rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE)
- dev->priv_flags |= IFF_RXFH_CONFIGURED;
+ dev->ethtool->rss_indir_user_size = dev_indir_size;
}
/* Update rss_ctx tracking */
if (rxfh_dev.rss_delete) {
@@ -1737,6 +1737,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct net_device *dev,
ctx->indir_configured =
rxfh.indir_size &&
rxfh.indir_size != ETH_RXFH_INDIR_NO_CHANGE;
+ ctx->indir_user_size = dev_indir_size;
}
if (rxfh_dev.key) {
memcpy(ethtool_rxfh_context_key(ctx), rxfh_dev.key,
diff --git a/net/ethtool/rss.c b/net/ethtool/rss.c
index 0f4e5cd2ac71..5cf90d73e70b 100644
--- a/net/ethtool/rss.c
+++ b/net/ethtool/rss.c
@@ -686,7 +686,7 @@ rss_set_prep_indir(struct net_device *dev, struct genl_info *info,
*mod |= memcmp(rxfh->indir, data->indir_table, data->indir_size);
- return 0;
+ return user_size;
err_free:
kfree(rxfh->indir);
@@ -833,6 +833,7 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
struct nlattr **tb = info->attrs;
struct rss_reply_data data = {};
const struct ethtool_ops *ops;
+ u32 indir_user_size;
int ret;
ops = dev->ethtool_ops;
@@ -845,8 +846,9 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
rxfh.rss_context = request->rss_context;
ret = rss_set_prep_indir(dev, info, &data, &rxfh, &indir_reset, &mod);
- if (ret)
+ if (ret < 0)
goto exit_clean_data;
+ indir_user_size = ret;
indir_mod = !!tb[ETHTOOL_A_RSS_INDIR];
rxfh.hfunc = data.hfunc;
@@ -889,12 +891,15 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct genl_info *info)
if (ret)
goto exit_unlock;
- if (ctx)
+ if (ctx) {
rss_set_ctx_update(ctx, tb, &data, &rxfh);
- else if (indir_reset)
- dev->priv_flags &= ~IFF_RXFH_CONFIGURED;
- else if (indir_mod)
- dev->priv_flags |= IFF_RXFH_CONFIGURED;
+ if (indir_user_size)
+ ctx->indir_user_size = indir_user_size;
+ } else if (indir_reset) {
+ dev->ethtool->rss_indir_user_size = 0;
+ } else if (indir_mod) {
+ dev->ethtool->rss_indir_user_size = indir_user_size;
+ }
exit_unlock:
mutex_unlock(&dev->ethtool->rss_lock);
@@ -999,6 +1004,7 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
const struct ethtool_ops *ops;
struct rss_req_info req = {};
struct net_device *dev;
+ u32 indir_user_size;
struct sk_buff *rsp;
void *hdr;
u32 limit;
@@ -1035,8 +1041,9 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
goto exit_ops;
ret = rss_set_prep_indir(dev, info, &data, &rxfh, &indir_dflt, &mod);
- if (ret)
+ if (ret < 0)
goto exit_clean_data;
+ indir_user_size = ret;
ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod);
@@ -1080,6 +1087,7 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct genl_info *info)
/* Store the config from rxfh to Xarray.. */
rss_set_ctx_update(ctx, tb, &data, &rxfh);
+ ctx->indir_user_size = indir_user_size;
/* .. copy from Xarray to data. */
__rss_prepare_ctx(dev, &data, ctx);