* [PATCH net-next 1/5] ethtool: Add RSS indirection table resize helpers
2026-03-03 18:15 [PATCH net-next 0/5] ethtool: Dynamic RSS context indirection table resizing Björn Töpel
@ 2026-03-03 18:15 ` Björn Töpel
2026-03-04 0:03 ` Jakub Kicinski
2026-03-03 18:15 ` [PATCH net-next 2/5] bnxt_en: Resize RSS contexts on channel count change Björn Töpel
` (4 subsequent siblings)
5 siblings, 1 reply; 18+ messages in thread
From: Björn Töpel @ 2026-03-03 18:15 UTC (permalink / raw)
To: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel, netdev, linux-kselftest
Cc: Björn Töpel, Willem de Bruijn
The core locks ctx->indir_size when an RSS context is created. Some
NICs (e.g. bnxt) change their indirection table size based on the
channel count, because the hardware table is a shared resource. This
forces drivers to reject channel changes when RSS contexts exist.
Add helpers to resize indirection tables:
ethtool_rxfh_indir_can_resize() checks whether a table can be resized
without modifying it.
ethtool_rxfh_indir_resize() resizes a raw u32 table in place. Folding
(shrink) requires the table to be periodic at the new size;
non-periodic tables are rejected. Unfolding (grow) replicates the
existing pattern. Sizes must be multiples of each other.
ethtool_rxfh_contexts_resize_all() resizes all non-default RSS
contexts. Validates every context before modifying any, so either all
succeed or none are changed. Sends ETHTOOL_MSG_RSS_NTF per resized
context after releasing rss_lock.
No reallocation is needed because ethtool_rxfh_ctx_alloc() reserves
space for rxfh_indir_space entries, and key_off is based on that
maximum.
Signed-off-by: Björn Töpel <bjorn@kernel.org>
---
include/linux/ethtool.h | 3 +
net/ethtool/common.c | 126 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 129 insertions(+)
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index 798abec67a1b..add7d16ca398 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -214,6 +214,9 @@ static inline u8 *ethtool_rxfh_context_key(struct ethtool_rxfh_context *ctx)
}
void ethtool_rxfh_context_lost(struct net_device *dev, u32 context_id);
+int ethtool_rxfh_indir_can_resize(const u32 *tbl, u32 old_size, u32 new_size);
+int ethtool_rxfh_indir_resize(u32 *tbl, u32 old_size, u32 new_size);
+int ethtool_rxfh_contexts_resize_all(struct net_device *dev, u32 new_indir_size);
struct link_mode_info {
int speed;
diff --git a/net/ethtool/common.c b/net/ethtool/common.c
index e252cf20c22f..8df84ff9efa4 100644
--- a/net/ethtool/common.c
+++ b/net/ethtool/common.c
@@ -1204,6 +1204,132 @@ void ethtool_rxfh_context_lost(struct net_device *dev, u32 context_id)
}
EXPORT_SYMBOL(ethtool_rxfh_context_lost);
+static bool ethtool_rxfh_indir_is_periodic(const u32 *tbl, u32 old_size, u32 new_size)
+{
+ u32 i;
+
+ for (i = new_size; i < old_size; i++)
+ if (tbl[i] != tbl[i % new_size])
+ return false;
+ return true;
+}
+
+/**
+ * ethtool_rxfh_indir_can_resize - Check if an indirection table can be resized
+ * @tbl: indirection table
+ * @old_size: current number of entries in the table
+ * @new_size: desired number of entries
+ *
+ * Validate that @tbl can be resized from @old_size to @new_size without
+ * data loss. Read-only; does not modify the table.
+ *
+ * Return: 0 if resize is possible, -%EINVAL otherwise.
+ */
+int ethtool_rxfh_indir_can_resize(const u32 *tbl, u32 old_size, u32 new_size)
+{
+ if (old_size == 0 || new_size == 0)
+ return -EINVAL;
+ if (new_size == old_size)
+ return 0;
+
+ if (new_size < old_size) {
+ if (old_size % new_size != 0)
+ return -EINVAL;
+ if (!ethtool_rxfh_indir_is_periodic(tbl, old_size, new_size))
+ return -EINVAL;
+ return 0;
+ }
+
+ if (new_size % old_size != 0)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(ethtool_rxfh_indir_can_resize);
+
+/* Resize without validation; caller must have called can_resize first */
+static void __ethtool_rxfh_indir_resize(u32 *tbl, u32 old_size, u32 new_size)
+{
+ u32 i;
+
+ /* Unfold (grow): replicate existing pattern; fold is a no-op on the data */
+ for (i = old_size; i < new_size; i++)
+ tbl[i] = tbl[i % old_size];
+}
+
+/**
+ * ethtool_rxfh_indir_resize - Fold or unfold an indirection table
+ * @tbl: indirection table (must have room for max(old_size, new_size) entries)
+ * @old_size: current number of entries in the table
+ * @new_size: desired number of entries
+ *
+ * Resize an RSS indirection table in place. When folding (shrinking),
+ * the table must be periodic with period @new_size; otherwise the
+ * operation is rejected. When unfolding (growing), the existing
+ * pattern is replicated. Both directions require the sizes to be
+ * multiples of each other.
+ *
+ * Return: 0 on success, -%EINVAL on failure (no mutation on failure).
+ */
+int ethtool_rxfh_indir_resize(u32 *tbl, u32 old_size, u32 new_size)
+{
+ int ret;
+
+ ret = ethtool_rxfh_indir_can_resize(tbl, old_size, new_size);
+ if (ret)
+ return ret;
+
+ __ethtool_rxfh_indir_resize(tbl, old_size, new_size);
+ return 0;
+}
+EXPORT_SYMBOL(ethtool_rxfh_indir_resize);
+
+/**
+ * ethtool_rxfh_contexts_resize_all - Resize all RSS context indirection tables
+ * @dev: network device
+ * @new_indir_size: new indirection table size
+ *
+ * Resize the indirection table of every non-default RSS context to
+ * @new_indir_size. Intended to be called from drivers when the
+ * device's indirection table size changes (e.g. on channel count
+ * change). An %ETHTOOL_MSG_RSS_NTF is sent for each resized context.
+ *
+ * All contexts are validated before any are modified, so either all
+ * contexts are resized or none are.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int ethtool_rxfh_contexts_resize_all(struct net_device *dev, u32 new_indir_size)
+{
+ struct ethtool_rxfh_context *ctx;
+ unsigned long context;
+ int ret;
+
+ if (dev->ethtool_ops->rxfh_indir_space == 0 ||
+ new_indir_size > dev->ethtool_ops->rxfh_indir_space)
+ return -EINVAL;
+
+ scoped_guard(mutex, &dev->ethtool->rss_lock) {
+ xa_for_each(&dev->ethtool->rss_ctx, context, ctx) {
+ ret = ethtool_rxfh_indir_can_resize(ethtool_rxfh_context_indir(ctx),
+ ctx->indir_size, new_indir_size);
+ if (ret)
+ return ret;
+ }
+
+ xa_for_each(&dev->ethtool->rss_ctx, context, ctx) {
+ __ethtool_rxfh_indir_resize(ethtool_rxfh_context_indir(ctx),
+ ctx->indir_size, new_indir_size);
+ ctx->indir_size = new_indir_size;
+ }
+ }
+
+ xa_for_each(&dev->ethtool->rss_ctx, context, ctx)
+ ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_NTF, context);
+
+ return 0;
+}
+EXPORT_SYMBOL(ethtool_rxfh_contexts_resize_all);
+
enum ethtool_link_medium ethtool_str_to_medium(const char *str)
{
int i;
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH net-next 1/5] ethtool: Add RSS indirection table resize helpers
2026-03-03 18:15 ` [PATCH net-next 1/5] ethtool: Add RSS indirection table resize helpers Björn Töpel
@ 2026-03-04 0:03 ` Jakub Kicinski
2026-03-04 11:37 ` Björn Töpel
0 siblings, 1 reply; 18+ messages in thread
From: Jakub Kicinski @ 2026-03-04 0:03 UTC (permalink / raw)
To: Björn Töpel
Cc: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
On Tue, 3 Mar 2026 19:15:29 +0100 Björn Töpel wrote:
> +/**
> + * ethtool_rxfh_indir_can_resize - Check if an indirection table can be resized
> + * @tbl: indirection table
> + * @old_size: current number of entries in the table
> + * @new_size: desired number of entries
> + *
> + * Validate that @tbl can be resized from @old_size to @new_size without
> + * data loss. Read-only; does not modify the table.
> + *
> + * Return: 0 if resize is possible, -%EINVAL otherwise.
> + */
> +int ethtool_rxfh_indir_can_resize(const u32 *tbl, u32 old_size, u32 new_size)
maybe pass it extack so that all the callers don't have to set it
themslves?
> +{
> + if (old_size == 0 || new_size == 0)
> + return -EINVAL;
defensive programming?
> + if (new_size == old_size)
> + return 0;
> +
> + if (new_size < old_size) {
> + if (old_size % new_size != 0)
please try not to compare things to zero in C, it reads as double
negation
BTW I guess that this expression to cover both branches would be
unreadable right? :D
if (new_size + old_size % min(new_size, old_size))
return -EINVAL;
> + return -EINVAL;
> + if (!ethtool_rxfh_indir_is_periodic(tbl, old_size, new_size))
> + return -EINVAL;
> + return 0;
> + }
> +
> + if (new_size % old_size != 0)
> + return -EINVAL;
> + return 0;
> +}
> +EXPORT_SYMBOL(ethtool_rxfh_indir_can_resize);
> +
> +/* Resize without validation; caller must have called can_resize first */
> +static void __ethtool_rxfh_indir_resize(u32 *tbl, u32 old_size, u32 new_size)
> +{
> + u32 i;
> +
> + /* Unfold (grow): replicate existing pattern; fold is a no-op on the data */
> + for (i = old_size; i < new_size; i++)
> + tbl[i] = tbl[i % old_size];
> +}
> +/**
> + * ethtool_rxfh_contexts_resize_all - Resize all RSS context indirection tables
> + * @dev: network device
> + * @new_indir_size: new indirection table size
> + *
> + * Resize the indirection table of every non-default RSS context to
> + * @new_indir_size. Intended to be called from drivers when the
> + * device's indirection table size changes (e.g. on channel count
> + * change). An %ETHTOOL_MSG_RSS_NTF is sent for each resized context.
> + *
> + * All contexts are validated before any are modified, so either all
> + * contexts are resized or none are.
> + *
> + * Return: 0 on success, negative errno on failure.
> + */
> +int ethtool_rxfh_contexts_resize_all(struct net_device *dev, u32 new_indir_size)
> +{
> + struct ethtool_rxfh_context *ctx;
> + unsigned long context;
> + int ret;
> +
> + if (dev->ethtool_ops->rxfh_indir_space == 0 ||
!dev..
> + new_indir_size > dev->ethtool_ops->rxfh_indir_space)
extack needed?
> + return -EINVAL;
> +
> + scoped_guard(mutex, &dev->ethtool->rss_lock) {
> + xa_for_each(&dev->ethtool->rss_ctx, context, ctx) {
> + ret = ethtool_rxfh_indir_can_resize(ethtool_rxfh_context_indir(ctx),
80 char limit width limit is still king in netdev
please shrink the length of the helpers you're adding?
And/or take the locks in a normal way..
> + ctx->indir_size, new_indir_size);
> + if (ret)
> + return ret;
> + }
> +
> + xa_for_each(&dev->ethtool->rss_ctx, context, ctx) {
> + __ethtool_rxfh_indir_resize(ethtool_rxfh_context_indir(ctx),
> + ctx->indir_size, new_indir_size);
> + ctx->indir_size = new_indir_size;
> + }
> + }
> +
> + xa_for_each(&dev->ethtool->rss_ctx, context, ctx)
> + ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_NTF, context);
Why a separate loop ?
> + return 0;
> +}
> +EXPORT_SYMBOL(ethtool_rxfh_contexts_resize_all);
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH net-next 1/5] ethtool: Add RSS indirection table resize helpers
2026-03-04 0:03 ` Jakub Kicinski
@ 2026-03-04 11:37 ` Björn Töpel
2026-03-04 17:09 ` Jakub Kicinski
0 siblings, 1 reply; 18+ messages in thread
From: Björn Töpel @ 2026-03-04 11:37 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
On Wed, 4 Mar 2026 at 01:03, Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Tue, 3 Mar 2026 19:15:29 +0100 Björn Töpel wrote:
...
Thanks for hacking a look! I'll address all of the above in the
follow-up.
> > + return -EINVAL;
> > +
> > + scoped_guard(mutex, &dev->ethtool->rss_lock) {
> > + xa_for_each(&dev->ethtool->rss_ctx, context, ctx) {
> > + ret = ethtool_rxfh_indir_can_resize(ethtool_rxfh_context_indir(ctx),
>
> 80 char limit width limit is still king in netdev
> please shrink the length of the helpers you're adding?
> And/or take the locks in a normal way..
Hmm, is guard() and friends discouraged in netdev, or ethtool
specific? I'll fix the 100 chars lines (sad face)!
>
> > + ctx->indir_size, new_indir_size);
> > + if (ret)
> > + return ret;
> > + }
> > +
> > + xa_for_each(&dev->ethtool->rss_ctx, context, ctx) {
> > + __ethtool_rxfh_indir_resize(ethtool_rxfh_context_indir(ctx),
> > + ctx->indir_size, new_indir_size);
> > + ctx->indir_size = new_indir_size;
> > + }
> > + }
> > +
> > + xa_for_each(&dev->ethtool->rss_ctx, context, ctx)
> > + ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_NTF, context);
>
> Why a separate loop ?
We can't hold the rss_lock for the notify;
ethnl_default_notify->rss_prepare->... deadlock.
Björn
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH net-next 1/5] ethtool: Add RSS indirection table resize helpers
2026-03-04 11:37 ` Björn Töpel
@ 2026-03-04 17:09 ` Jakub Kicinski
0 siblings, 0 replies; 18+ messages in thread
From: Jakub Kicinski @ 2026-03-04 17:09 UTC (permalink / raw)
To: Björn Töpel
Cc: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
On Wed, 4 Mar 2026 12:37:09 +0100 Björn Töpel wrote:
> > > + return -EINVAL;
> > > +
> > > + scoped_guard(mutex, &dev->ethtool->rss_lock) {
> > > + xa_for_each(&dev->ethtool->rss_ctx, context, ctx) {
> > > + ret = ethtool_rxfh_indir_can_resize(ethtool_rxfh_context_indir(ctx),
> >
> > 80 char limit width limit is still king in netdev
> > please shrink the length of the helpers you're adding?
> > And/or take the locks in a normal way..
>
> Hmm, is guard() and friends discouraged in netdev, or ethtool
> specific? I'll fix the 100 chars lines (sad face)!
Yes, non-scoped guards are discouraged.
> > > + ctx->indir_size, new_indir_size);
> > > + if (ret)
> > > + return ret;
> > > + }
> > > +
> > > + xa_for_each(&dev->ethtool->rss_ctx, context, ctx) {
> > > + __ethtool_rxfh_indir_resize(ethtool_rxfh_context_indir(ctx),
> > > + ctx->indir_size, new_indir_size);
> > > + ctx->indir_size = new_indir_size;
> > > + }
> > > + }
> > > +
> > > + xa_for_each(&dev->ethtool->rss_ctx, context, ctx)
> > > + ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_NTF, context);
> >
> > Why a separate loop ?
>
> We can't hold the rss_lock for the notify;
> ethnl_default_notify->rss_prepare->... deadlock.
LOL I guess scoped guard also has negative impact on readability
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH net-next 2/5] bnxt_en: Resize RSS contexts on channel count change
2026-03-03 18:15 [PATCH net-next 0/5] ethtool: Dynamic RSS context indirection table resizing Björn Töpel
2026-03-03 18:15 ` [PATCH net-next 1/5] ethtool: Add RSS indirection table resize helpers Björn Töpel
@ 2026-03-03 18:15 ` Björn Töpel
2026-03-03 19:20 ` Michael Chan
2026-03-04 0:05 ` Jakub Kicinski
2026-03-03 18:15 ` [PATCH net-next 3/5] netdevsim: Add RSS context support with dynamic table sizing Björn Töpel
` (3 subsequent siblings)
5 siblings, 2 replies; 18+ messages in thread
From: Björn Töpel @ 2026-03-03 18:15 UTC (permalink / raw)
To: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel, netdev, linux-kselftest
Cc: Björn Töpel, Willem de Bruijn
bnxt_set_channels() rejects channel changes that alter the RSS table
size when IFF_RXFH_CONFIGURED is set, because non-default context
sizes were locked at creation.
Replace the rejection with the new resize helpers. All validation runs
before any mutation:
1. ethtool_rxfh_indir_can_resize() checks context 0.
2. ethtool_rxfh_contexts_resize_all() validates and resizes
all non-default contexts (all-or-none).
3. ethtool_rxfh_indir_resize() applies context 0 changes.
When context 0 uses defaults (!IFF_RXFH_CONFIGURED), steps 1 and 3 are
skipped; the driver regenerates the table via
bnxt_set_dflt_rss_indir_tbl().
Signed-off-by: Björn Töpel <bjorn@kernel.org>
---
.../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 23 +++++++++++++++----
1 file changed, 18 insertions(+), 5 deletions(-)
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
index 26fcd52c8a61..7608e5d95630 100644
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
@@ -977,11 +977,24 @@ static int bnxt_set_channels(struct net_device *dev,
tx_xdp = req_rx_rings;
}
- if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) !=
- bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) &&
- netif_is_rxfh_configured(dev)) {
- netdev_warn(dev, "RSS table size change required, RSS table entries must be default to proceed\n");
- return -EINVAL;
+ if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) != bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings)) {
+ u32 new_size = bnxt_get_nr_rss_ctxs(bp, req_rx_rings) * BNXT_RSS_TABLE_ENTRIES_P5;
+ u32 old_size = bnxt_get_rxfh_indir_size(dev);
+
+ /* Validate context 0 can be resized before mutating anything */
+ if (netif_is_rxfh_configured(dev) &&
+ ethtool_rxfh_indir_can_resize(bp->rss_indir_tbl, old_size, new_size)) {
+ netdev_warn(dev, "RSS table size change not supported with current indirection table\n");
+ return -EINVAL;
+ }
+
+ rc = ethtool_rxfh_contexts_resize_all(dev, new_size);
+ if (rc)
+ return rc;
+
+ /* All validated; apply context 0 resize */
+ if (netif_is_rxfh_configured(dev))
+ ethtool_rxfh_indir_resize(bp->rss_indir_tbl, old_size, new_size);
}
rc = bnxt_check_rings(bp, req_tx_rings, req_rx_rings, sh, tcs, tx_xdp);
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH net-next 2/5] bnxt_en: Resize RSS contexts on channel count change
2026-03-03 18:15 ` [PATCH net-next 2/5] bnxt_en: Resize RSS contexts on channel count change Björn Töpel
@ 2026-03-03 19:20 ` Michael Chan
2026-03-04 11:38 ` Björn Töpel
2026-03-04 0:05 ` Jakub Kicinski
1 sibling, 1 reply; 18+ messages in thread
From: Michael Chan @ 2026-03-03 19:20 UTC (permalink / raw)
To: Björn Töpel
Cc: Pavan Chebbi, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
[-- Attachment #1: Type: text/plain, Size: 2742 bytes --]
On Tue, Mar 3, 2026 at 10:15 AM Björn Töpel <bjorn@kernel.org> wrote:
>
> bnxt_set_channels() rejects channel changes that alter the RSS table
> size when IFF_RXFH_CONFIGURED is set, because non-default context
> sizes were locked at creation.
Thanks for the patch. I want to point out that when running newer FW,
the RSS table size will not change when the channel count changes. If
FW supports it, we'll always use the largest RSS table size for better
traffic balance. A few more comments below.
> diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
> index 26fcd52c8a61..7608e5d95630 100644
> --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
> +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
> @@ -977,11 +977,24 @@ static int bnxt_set_channels(struct net_device *dev,
> tx_xdp = req_rx_rings;
> }
>
> - if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) !=
> - bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) &&
> - netif_is_rxfh_configured(dev)) {
> - netdev_warn(dev, "RSS table size change required, RSS table entries must be default to proceed\n");
> - return -EINVAL;
> + if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) != bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings)) {
> + u32 new_size = bnxt_get_nr_rss_ctxs(bp, req_rx_rings) * BNXT_RSS_TABLE_ENTRIES_P5;
If the table size changes, it must be a P5 chip (and running older
FW). Addng a comment for this will be useful.
Also, this if statement should be moved below the bnxt_check_rings()
call. We don't want to change the RSS table unless it is confirmed
that we have enough rings to satisfy the new channels count.
> + u32 old_size = bnxt_get_rxfh_indir_size(dev);
> +
> + /* Validate context 0 can be resized before mutating anything */
> + if (netif_is_rxfh_configured(dev) &&
> + ethtool_rxfh_indir_can_resize(bp->rss_indir_tbl, old_size, new_size)) {
> + netdev_warn(dev, "RSS table size change not supported with current indirection table\n");
> + return -EINVAL;
> + }
> +
> + rc = ethtool_rxfh_contexts_resize_all(dev, new_size);
> + if (rc)
> + return rc;
> +
> + /* All validated; apply context 0 resize */
> + if (netif_is_rxfh_configured(dev))
> + ethtool_rxfh_indir_resize(bp->rss_indir_tbl, old_size, new_size);
> }
>
> rc = bnxt_check_rings(bp, req_tx_rings, req_rx_rings, sh, tcs, tx_xdp);
> --
> 2.53.0
>
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5469 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH net-next 2/5] bnxt_en: Resize RSS contexts on channel count change
2026-03-03 19:20 ` Michael Chan
@ 2026-03-04 11:38 ` Björn Töpel
0 siblings, 0 replies; 18+ messages in thread
From: Björn Töpel @ 2026-03-04 11:38 UTC (permalink / raw)
To: Michael Chan
Cc: Pavan Chebbi, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
On Tue, 3 Mar 2026 at 20:20, Michael Chan <michael.chan@broadcom.com> wrote:
>
> On Tue, Mar 3, 2026 at 10:15 AM Björn Töpel <bjorn@kernel.org> wrote:
> >
> > bnxt_set_channels() rejects channel changes that alter the RSS table
> > size when IFF_RXFH_CONFIGURED is set, because non-default context
> > sizes were locked at creation.
>
> Thanks for the patch. I want to point out that when running newer FW,
> the RSS table size will not change when the channel count changes. If
> FW supports it, we'll always use the largest RSS table size for better
> traffic balance. A few more comments below.
Ok! Is there a changelog somewhere, what FW version that fixes this issue?
> > diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
> > index 26fcd52c8a61..7608e5d95630 100644
> > --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
> > +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
> > @@ -977,11 +977,24 @@ static int bnxt_set_channels(struct net_device *dev,
> > tx_xdp = req_rx_rings;
> > }
> >
> > - if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) !=
> > - bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) &&
> > - netif_is_rxfh_configured(dev)) {
> > - netdev_warn(dev, "RSS table size change required, RSS table entries must be default to proceed\n");
> > - return -EINVAL;
> > + if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) != bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings)) {
> > + u32 new_size = bnxt_get_nr_rss_ctxs(bp, req_rx_rings) * BNXT_RSS_TABLE_ENTRIES_P5;
>
> If the table size changes, it must be a P5 chip (and running older
> FW). Addng a comment for this will be useful.
>
> Also, this if statement should be moved below the bnxt_check_rings()
> call. We don't want to change the RSS table unless it is confirmed
> that we have enough rings to satisfy the new channels count.
Will fix! Thanks for having a look!
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH net-next 2/5] bnxt_en: Resize RSS contexts on channel count change
2026-03-03 18:15 ` [PATCH net-next 2/5] bnxt_en: Resize RSS contexts on channel count change Björn Töpel
2026-03-03 19:20 ` Michael Chan
@ 2026-03-04 0:05 ` Jakub Kicinski
2026-03-04 11:39 ` Björn Töpel
1 sibling, 1 reply; 18+ messages in thread
From: Jakub Kicinski @ 2026-03-04 0:05 UTC (permalink / raw)
To: Björn Töpel
Cc: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
On Tue, 3 Mar 2026 19:15:30 +0100 Björn Töpel wrote:
> - if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) !=
> - bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) &&
> - netif_is_rxfh_configured(dev)) {
> - netdev_warn(dev, "RSS table size change required, RSS table entries must be default to proceed\n");
> - return -EINVAL;
> + if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) != bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings)) {
> + u32 new_size = bnxt_get_nr_rss_ctxs(bp, req_rx_rings) * BNXT_RSS_TABLE_ENTRIES_P5;
> + u32 old_size = bnxt_get_rxfh_indir_size(dev);
> +
> + /* Validate context 0 can be resized before mutating anything */
> + if (netif_is_rxfh_configured(dev) &&
> + ethtool_rxfh_indir_can_resize(bp->rss_indir_tbl, old_size, new_size)) {
> + netdev_warn(dev, "RSS table size change not supported with current indirection table\n");
> + return -EINVAL;
> + }
> +
> + rc = ethtool_rxfh_contexts_resize_all(dev, new_size);
> + if (rc)
> + return rc;
> +
> + /* All validated; apply context 0 resize */
> + if (netif_is_rxfh_configured(dev))
> + ethtool_rxfh_indir_resize(bp->rss_indir_tbl, old_size, new_size);
> }
>
> rc = bnxt_check_rings(bp, req_tx_rings, req_rx_rings, sh, tcs, tx_xdp);
You resize the tables even if something else fails later on.
I think either this if () should only contain the validation checks,
and the actual resizing has to happen later.
Or you need to "un-resize" (which should never fail so it's fine).
^ permalink raw reply [flat|nested] 18+ messages in thread* Re: [PATCH net-next 2/5] bnxt_en: Resize RSS contexts on channel count change
2026-03-04 0:05 ` Jakub Kicinski
@ 2026-03-04 11:39 ` Björn Töpel
0 siblings, 0 replies; 18+ messages in thread
From: Björn Töpel @ 2026-03-04 11:39 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
On Wed, 4 Mar 2026 at 01:05, Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Tue, 3 Mar 2026 19:15:30 +0100 Björn Töpel wrote:
> > - if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) !=
> > - bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) &&
> > - netif_is_rxfh_configured(dev)) {
> > - netdev_warn(dev, "RSS table size change required, RSS table entries must be default to proceed\n");
> > - return -EINVAL;
> > + if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) != bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings)) {
> > + u32 new_size = bnxt_get_nr_rss_ctxs(bp, req_rx_rings) * BNXT_RSS_TABLE_ENTRIES_P5;
> > + u32 old_size = bnxt_get_rxfh_indir_size(dev);
> > +
> > + /* Validate context 0 can be resized before mutating anything */
> > + if (netif_is_rxfh_configured(dev) &&
> > + ethtool_rxfh_indir_can_resize(bp->rss_indir_tbl, old_size, new_size)) {
> > + netdev_warn(dev, "RSS table size change not supported with current indirection table\n");
> > + return -EINVAL;
> > + }
> > +
> > + rc = ethtool_rxfh_contexts_resize_all(dev, new_size);
> > + if (rc)
> > + return rc;
> > +
> > + /* All validated; apply context 0 resize */
> > + if (netif_is_rxfh_configured(dev))
> > + ethtool_rxfh_indir_resize(bp->rss_indir_tbl, old_size, new_size);
> > }
> >
> > rc = bnxt_check_rings(bp, req_tx_rings, req_rx_rings, sh, tcs, tx_xdp);
>
> You resize the tables even if something else fails later on.
> I think either this if () should only contain the validation checks,
> and the actual resizing has to happen later.
> Or you need to "un-resize" (which should never fail so it's fine).
Good point!
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH net-next 3/5] netdevsim: Add RSS context support with dynamic table sizing
2026-03-03 18:15 [PATCH net-next 0/5] ethtool: Dynamic RSS context indirection table resizing Björn Töpel
2026-03-03 18:15 ` [PATCH net-next 1/5] ethtool: Add RSS indirection table resize helpers Björn Töpel
2026-03-03 18:15 ` [PATCH net-next 2/5] bnxt_en: Resize RSS contexts on channel count change Björn Töpel
@ 2026-03-03 18:15 ` Björn Töpel
2026-03-04 0:07 ` Jakub Kicinski
2026-03-03 18:15 ` [PATCH net-next 4/5] selftests: netdevsim: Add RSS indirection table resize test Björn Töpel
` (2 subsequent siblings)
5 siblings, 1 reply; 18+ messages in thread
From: Björn Töpel @ 2026-03-03 18:15 UTC (permalink / raw)
To: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel, netdev, linux-kselftest
Cc: Björn Töpel, Willem de Bruijn
Add RSS indirection table, hash key, and non-default RSS context
support to netdevsim. The create/modify/remove context callbacks are
no-ops; the core manages context state in its xarray.
The table size is dynamic: roundup_pow_of_two(channels) * 16, capped
at NSIM_RSS_INDIR_MAX (128). This mimics drivers like bnxt where the
table size changes with the queue count.
nsim_set_channels() uses the core resize helpers to fold/unfold tables
on channel count changes, exercising the full resize path.
Signed-off-by: Björn Töpel <bjorn@kernel.org>
---
drivers/net/netdevsim/ethtool.c | 119 +++++++++++++++++++++++++++++-
drivers/net/netdevsim/netdevsim.h | 4 +
2 files changed, 121 insertions(+), 2 deletions(-)
diff --git a/drivers/net/netdevsim/ethtool.c b/drivers/net/netdevsim/ethtool.c
index 36a201533aae..c6d60b467054 100644
--- a/drivers/net/netdevsim/ethtool.c
+++ b/drivers/net/netdevsim/ethtool.c
@@ -2,6 +2,7 @@
// Copyright (c) 2020 Facebook
#include <linux/debugfs.h>
+#include <linux/ethtool.h>
#include <linux/random.h>
#include <net/netdev_queues.h>
@@ -117,19 +118,121 @@ nsim_wake_queues(struct net_device *dev)
rcu_read_unlock();
}
+static u32 nsim_get_rx_ring_count(struct net_device *dev)
+{
+ struct netdevsim *ns = netdev_priv(dev);
+
+ return ns->ethtool.channels;
+}
+
+static u32 nsim_rss_indir_size(u32 channels)
+{
+ return min_t(u32, roundup_pow_of_two(channels) * 16, NSIM_RSS_INDIR_MAX);
+}
+
+static u32 nsim_get_rxfh_indir_size(struct net_device *dev)
+{
+ struct netdevsim *ns = netdev_priv(dev);
+
+ return nsim_rss_indir_size(ns->ethtool.channels);
+}
+
+static u32 nsim_get_rxfh_key_size(struct net_device *dev)
+{
+ return NSIM_RSS_HKEY_SIZE;
+}
+
+static int nsim_get_rxfh(struct net_device *dev, struct ethtool_rxfh_param *rxfh)
+{
+ u32 indir_size = nsim_get_rxfh_indir_size(dev);
+ struct netdevsim *ns = netdev_priv(dev);
+
+ rxfh->hfunc = ETH_RSS_HASH_TOP;
+
+ if (rxfh->indir)
+ memcpy(rxfh->indir, ns->ethtool.rss_indir_tbl, indir_size * sizeof(u32));
+ if (rxfh->key)
+ memcpy(rxfh->key, ns->ethtool.rss_hkey, NSIM_RSS_HKEY_SIZE);
+
+ return 0;
+}
+
+static int nsim_set_rxfh(struct net_device *dev, struct ethtool_rxfh_param *rxfh,
+ struct netlink_ext_ack *extack)
+{
+ u32 indir_size = nsim_get_rxfh_indir_size(dev);
+ struct netdevsim *ns = netdev_priv(dev);
+
+ if (rxfh->indir)
+ memcpy(ns->ethtool.rss_indir_tbl, rxfh->indir, indir_size * sizeof(u32));
+ if (rxfh->key)
+ memcpy(ns->ethtool.rss_hkey, rxfh->key, NSIM_RSS_HKEY_SIZE);
+
+ return 0;
+}
+
+static int nsim_create_rxfh_context(struct net_device *dev, struct ethtool_rxfh_context *ctx,
+ const struct ethtool_rxfh_param *rxfh,
+ struct netlink_ext_ack *extack)
+{
+ return 0;
+}
+
+static int nsim_modify_rxfh_context(struct net_device *dev, struct ethtool_rxfh_context *ctx,
+ const struct ethtool_rxfh_param *rxfh,
+ struct netlink_ext_ack *extack)
+{
+ return 0;
+}
+
+static int nsim_remove_rxfh_context(struct net_device *dev, struct ethtool_rxfh_context *ctx,
+ u32 rss_context, struct netlink_ext_ack *extack)
+{
+ return 0;
+}
+
+static void nsim_rss_init(struct netdevsim *ns)
+{
+ u32 i, indir_size = nsim_rss_indir_size(ns->ethtool.channels);
+
+ for (i = 0; i < indir_size; i++)
+ ns->ethtool.rss_indir_tbl[i] = ethtool_rxfh_indir_default(i, ns->ethtool.channels);
+}
+
static int
nsim_set_channels(struct net_device *dev, struct ethtool_channels *ch)
{
struct netdevsim *ns = netdev_priv(dev);
+ u32 old_indir_size, new_indir_size;
int err;
- err = netif_set_real_num_queues(dev, ch->combined_count,
- ch->combined_count);
+ old_indir_size = nsim_get_rxfh_indir_size(dev);
+ new_indir_size = nsim_rss_indir_size(ch->combined_count);
+
+ if (old_indir_size != new_indir_size) {
+ if (netif_is_rxfh_configured(dev) &&
+ ethtool_rxfh_indir_can_resize(ns->ethtool.rss_indir_tbl,
+ old_indir_size, new_indir_size))
+ return -EINVAL;
+
+ err = ethtool_rxfh_contexts_resize_all(dev, new_indir_size);
+ if (err)
+ return err;
+
+ if (netif_is_rxfh_configured(dev))
+ ethtool_rxfh_indir_resize(ns->ethtool.rss_indir_tbl,
+ old_indir_size, new_indir_size);
+ }
+
+ err = netif_set_real_num_queues(dev, ch->combined_count, ch->combined_count);
if (err)
return err;
ns->ethtool.channels = ch->combined_count;
+ if (old_indir_size != new_indir_size && !netif_is_rxfh_configured(dev))
+ nsim_rss_init(ns);
+
/* Only wake up queues if devices are linked */
if (rcu_access_pointer(ns->peer))
nsim_wake_queues(dev);
@@ -209,6 +312,7 @@ static const struct ethtool_ops nsim_ethtool_ops = {
.supported_coalesce_params = ETHTOOL_COALESCE_ALL_PARAMS,
.supported_ring_params = ETHTOOL_RING_USE_TCP_DATA_SPLIT |
ETHTOOL_RING_USE_HDS_THRS,
+ .rxfh_indir_space = NSIM_RSS_INDIR_MAX,
.get_pause_stats = nsim_get_pause_stats,
.get_pauseparam = nsim_get_pauseparam,
.set_pauseparam = nsim_set_pauseparam,
@@ -218,10 +322,18 @@ static const struct ethtool_ops nsim_ethtool_ops = {
.set_ringparam = nsim_set_ringparam,
.get_channels = nsim_get_channels,
.set_channels = nsim_set_channels,
+ .get_rx_ring_count = nsim_get_rx_ring_count,
.get_fecparam = nsim_get_fecparam,
.set_fecparam = nsim_set_fecparam,
.get_fec_stats = nsim_get_fec_stats,
.get_ts_info = nsim_get_ts_info,
+ .get_rxfh_indir_size = nsim_get_rxfh_indir_size,
+ .get_rxfh_key_size = nsim_get_rxfh_key_size,
+ .get_rxfh = nsim_get_rxfh,
+ .set_rxfh = nsim_set_rxfh,
+ .create_rxfh_context = nsim_create_rxfh_context,
+ .modify_rxfh_context = nsim_modify_rxfh_context,
+ .remove_rxfh_context = nsim_remove_rxfh_context,
};
static void nsim_ethtool_ring_init(struct netdevsim *ns)
@@ -250,6 +362,9 @@ void nsim_ethtool_init(struct netdevsim *ns)
ns->ethtool.channels = ns->nsim_bus_dev->num_queues;
+ nsim_rss_init(ns);
+ get_random_bytes(ns->ethtool.rss_hkey, NSIM_RSS_HKEY_SIZE);
+
ethtool = debugfs_create_dir("ethtool", ns->nsim_dev_port->ddir);
debugfs_create_u32("get_err", 0600, ethtool, &ns->ethtool.get_err);
diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h
index f767fc8a7505..3c6dd9a98deb 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -82,6 +82,8 @@ struct nsim_ethtool_pauseparam {
bool report_stats_tx;
};
+#define NSIM_RSS_INDIR_MAX 128
+#define NSIM_RSS_HKEY_SIZE 40
struct nsim_ethtool {
u32 get_err;
u32 set_err;
@@ -90,6 +92,8 @@ struct nsim_ethtool {
struct ethtool_coalesce coalesce;
struct ethtool_ringparam ring;
struct ethtool_fecparam fec;
+ u32 rss_indir_tbl[NSIM_RSS_INDIR_MAX];
+ u8 rss_hkey[NSIM_RSS_HKEY_SIZE];
};
struct nsim_rq {
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH net-next 3/5] netdevsim: Add RSS context support with dynamic table sizing
2026-03-03 18:15 ` [PATCH net-next 3/5] netdevsim: Add RSS context support with dynamic table sizing Björn Töpel
@ 2026-03-04 0:07 ` Jakub Kicinski
2026-03-04 11:40 ` Björn Töpel
0 siblings, 1 reply; 18+ messages in thread
From: Jakub Kicinski @ 2026-03-04 0:07 UTC (permalink / raw)
To: Björn Töpel
Cc: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
On Tue, 3 Mar 2026 19:15:31 +0100 Björn Töpel wrote:
> Add RSS indirection table, hash key, and non-default RSS context
> support to netdevsim. The create/modify/remove context callbacks are
> no-ops; the core manages context state in its xarray.
>
> The table size is dynamic: roundup_pow_of_two(channels) * 16, capped
> at NSIM_RSS_INDIR_MAX (128). This mimics drivers like bnxt where the
> table size changes with the queue count.
>
> nsim_set_channels() uses the core resize helpers to fold/unfold tables
> on channel count changes, exercising the full resize path.
Let's not bother with netdevsim support?
Let the HW tests cover this?
netdevsim was usual when we didn't have HW tests.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH net-next 3/5] netdevsim: Add RSS context support with dynamic table sizing
2026-03-04 0:07 ` Jakub Kicinski
@ 2026-03-04 11:40 ` Björn Töpel
0 siblings, 0 replies; 18+ messages in thread
From: Björn Töpel @ 2026-03-04 11:40 UTC (permalink / raw)
To: Jakub Kicinski
Cc: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
On Wed, 4 Mar 2026 at 01:07, Jakub Kicinski <kuba@kernel.org> wrote:
>
> On Tue, 3 Mar 2026 19:15:31 +0100 Björn Töpel wrote:
> > Add RSS indirection table, hash key, and non-default RSS context
> > support to netdevsim. The create/modify/remove context callbacks are
> > no-ops; the core manages context state in its xarray.
> >
> > The table size is dynamic: roundup_pow_of_two(channels) * 16, capped
> > at NSIM_RSS_INDIR_MAX (128). This mimics drivers like bnxt where the
> > table size changes with the queue count.
> >
> > nsim_set_channels() uses the core resize helpers to fold/unfold tables
> > on channel count changes, exercising the full resize path.
>
> Let's not bother with netdevsim support?
> Let the HW tests cover this?
> netdevsim was usual when we didn't have HW tests.
Fair enough!
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH net-next 4/5] selftests: netdevsim: Add RSS indirection table resize test
2026-03-03 18:15 [PATCH net-next 0/5] ethtool: Dynamic RSS context indirection table resizing Björn Töpel
` (2 preceding siblings ...)
2026-03-03 18:15 ` [PATCH net-next 3/5] netdevsim: Add RSS context support with dynamic table sizing Björn Töpel
@ 2026-03-03 18:15 ` Björn Töpel
2026-03-04 0:07 ` Jakub Kicinski
2026-03-03 18:15 ` [PATCH net-next 5/5] selftests: rss_drv: Add RSS indirection table resize tests Björn Töpel
2026-03-04 8:23 ` [PATCH net-next 0/5] ethtool: Dynamic RSS context indirection table resizing Pavan Chebbi
5 siblings, 1 reply; 18+ messages in thread
From: Björn Töpel @ 2026-03-03 18:15 UTC (permalink / raw)
To: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel, netdev, linux-kselftest
Cc: Björn Töpel, Willem de Bruijn
Test fold/unfold of RSS indirection tables on channel changes
using netdevsim:
- default table regenerates on channel change
- periodic table folds on shrink and unfolds on grow
- non-periodic table blocks channel reduction
- non-default RSS context resizes on channel change
- non-periodic non-default context blocks fold
- table and channel count unchanged after failed reduction
Signed-off-by: Björn Töpel <bjorn@kernel.org>
---
.../selftests/drivers/net/netdevsim/Makefile | 1 +
| 123 ++++++++++++++++++
2 files changed, 124 insertions(+)
create mode 100755 tools/testing/selftests/drivers/net/netdevsim/ethtool-rss.sh
diff --git a/tools/testing/selftests/drivers/net/netdevsim/Makefile b/tools/testing/selftests/drivers/net/netdevsim/Makefile
index 1a228c5430f5..d764d08dff1a 100644
--- a/tools/testing/selftests/drivers/net/netdevsim/Makefile
+++ b/tools/testing/selftests/drivers/net/netdevsim/Makefile
@@ -8,6 +8,7 @@ TEST_PROGS := \
ethtool-features.sh \
ethtool-fec.sh \
ethtool-pause.sh \
+ ethtool-rss.sh \
fib.sh \
fib_notifications.sh \
hw_stats_l3.sh \
--git a/tools/testing/selftests/drivers/net/netdevsim/ethtool-rss.sh b/tools/testing/selftests/drivers/net/netdevsim/ethtool-rss.sh
new file mode 100755
index 000000000000..1c1876f888ba
--- /dev/null
+++ b/tools/testing/selftests/drivers/net/netdevsim/ethtool-rss.sh
@@ -0,0 +1,123 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Test RSS indirection table resize on channel count changes.
+# Exercises ethtool_rxfh_indir_resize() and
+# ethtool_rxfh_contexts_resize_all() via netdevsim.
+
+source ethtool-common.sh
+
+# Create device with 8 queues
+NSIM_NETDEV=$(make_netdev 1 8)
+
+set -o pipefail
+
+# --- Test 1: Default table regenerates on channel change ---
+s=$(ethtool --json -x $NSIM_NETDEV | jq '.[0]["rss-indirection-table"] | length')
+check $? "$s" "128" # roundup_pow_of_two(8) * 16 = 128
+
+ethtool -L $NSIM_NETDEV combined 2
+s=$(ethtool --json -x $NSIM_NETDEV | jq '.[0]["rss-indirection-table"] | length')
+check $? "$s" "32" # roundup_pow_of_two(2) * 16 = 32
+
+ethtool -L $NSIM_NETDEV combined 8
+
+# --- Test 2: Periodic context 0 table folds on channel reduction ---
+ethtool -X $NSIM_NETDEV equal 2
+s=$(ethtool --json -x $NSIM_NETDEV | jq '[.[0]["rss-indirection-table"][]] | max')
+check $? "$s" "1"
+
+ethtool -L $NSIM_NETDEV combined 2
+s=$(ethtool --json -x $NSIM_NETDEV | jq '.[0]["rss-indirection-table"] | length')
+check $? "$s" "32"
+s=$(ethtool --json -x $NSIM_NETDEV | jq '[.[0]["rss-indirection-table"][]] | max')
+check $? "$s" "1"
+
+# Grow back — should unfold
+ethtool -L $NSIM_NETDEV combined 8
+s=$(ethtool --json -x $NSIM_NETDEV | jq '.[0]["rss-indirection-table"] | length')
+check $? "$s" "128"
+s=$(ethtool --json -x $NSIM_NETDEV | jq '[.[0]["rss-indirection-table"][]] | max')
+check $? "$s" "1"
+
+ethtool -X $NSIM_NETDEV default
+
+# --- Test 3: Non-periodic context 0 table blocks fold ---
+ethtool -X $NSIM_NETDEV equal 8
+
+ethtool -L $NSIM_NETDEV combined 2 2>/dev/null
+if [ $? -ne 0 ]; then
+ ((num_passes++))
+else
+ echo "Expected channel reduction to fail with non-periodic table"
+ ((num_errors++))
+fi
+
+ethtool -X $NSIM_NETDEV default
+
+# --- Test 4: Non-default context resizes on channel change ---
+ctx_id=$(ethtool -X $NSIM_NETDEV context new equal 2 2>/dev/null | awk '{print $NF}')
+if [ -z "$ctx_id" ]; then
+ echo "SKIP: context creation failed"
+else
+ s=$(ethtool --json -x $NSIM_NETDEV context $ctx_id | jq '.[0]["rss-indirection-table"] | length')
+ check $? "$s" "128"
+
+ ethtool -L $NSIM_NETDEV combined 2
+ s=$(ethtool --json -x $NSIM_NETDEV context $ctx_id | jq '.[0]["rss-indirection-table"] | length')
+ check $? "$s" "32"
+ s=$(ethtool --json -x $NSIM_NETDEV context $ctx_id | jq '[.[0]["rss-indirection-table"][]] | max')
+ check $? "$s" "1"
+
+ # Grow back
+ ethtool -L $NSIM_NETDEV combined 8
+ s=$(ethtool --json -x $NSIM_NETDEV context $ctx_id | jq '.[0]["rss-indirection-table"] | length')
+ check $? "$s" "128"
+ s=$(ethtool --json -x $NSIM_NETDEV context $ctx_id | jq '[.[0]["rss-indirection-table"][]] | max')
+ check $? "$s" "1"
+
+ ethtool -X $NSIM_NETDEV context $ctx_id delete
+fi
+
+# --- Test 5: Non-periodic non-default context blocks fold ---
+ctx_id=$(ethtool -X $NSIM_NETDEV context new equal 8 2>/dev/null | awk '{print $NF}')
+if [ -z "$ctx_id" ]; then
+ echo "SKIP: context creation failed"
+else
+ ethtool -L $NSIM_NETDEV combined 2 2>/dev/null
+ if [ $? -ne 0 ]; then
+ ((num_passes++))
+ else
+ echo "Expected channel reduction to fail with non-periodic context"
+ ((num_errors++))
+ fi
+
+ ethtool -X $NSIM_NETDEV context $ctx_id delete
+fi
+
+# --- Test 6: Table unchanged after failed channel reduction ---
+ethtool -X $NSIM_NETDEV equal 8
+s_before=$(ethtool --json -x $NSIM_NETDEV | jq '.[0]["rss-indirection-table"]')
+
+ethtool -L $NSIM_NETDEV combined 2 2>/dev/null
+s_after=$(ethtool --json -x $NSIM_NETDEV | jq '.[0]["rss-indirection-table"]')
+check $? "$s_after" "$s_before"
+
+ethtool -X $NSIM_NETDEV default
+
+# --- Test 7: Channel count unchanged after failed reduction ---
+ethtool -X $NSIM_NETDEV equal 8
+ethtool -L $NSIM_NETDEV combined 2 2>/dev/null
+s=$(ethtool --json -l $NSIM_NETDEV | jq '.[0]["combined-count"]')
+check $? "$s" "8"
+
+ethtool -X $NSIM_NETDEV default
+
+# --- Results ---
+if [ $num_errors -eq 0 ]; then
+ echo "PASSED all $((num_passes)) checks"
+ exit 0
+else
+ echo "FAILED $num_errors/$((num_errors+num_passes)) checks"
+ exit 1
+fi
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH net-next 4/5] selftests: netdevsim: Add RSS indirection table resize test
2026-03-03 18:15 ` [PATCH net-next 4/5] selftests: netdevsim: Add RSS indirection table resize test Björn Töpel
@ 2026-03-04 0:07 ` Jakub Kicinski
0 siblings, 0 replies; 18+ messages in thread
From: Jakub Kicinski @ 2026-03-04 0:07 UTC (permalink / raw)
To: Björn Töpel
Cc: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
On Tue, 3 Mar 2026 19:15:32 +0100 Björn Töpel wrote:
> Test fold/unfold of RSS indirection tables on channel changes
> using netdevsim:
>
> - default table regenerates on channel change
> - periodic table folds on shrink and unfolds on grow
> - non-periodic table blocks channel reduction
> - non-default RSS context resizes on channel change
> - non-periodic non-default context blocks fold
> - table and channel count unchanged after failed reduction
netdevsim-only tests are a thing of the past.
I should probably update our documentation to reflect that..
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH net-next 5/5] selftests: rss_drv: Add RSS indirection table resize tests
2026-03-03 18:15 [PATCH net-next 0/5] ethtool: Dynamic RSS context indirection table resizing Björn Töpel
` (3 preceding siblings ...)
2026-03-03 18:15 ` [PATCH net-next 4/5] selftests: netdevsim: Add RSS indirection table resize test Björn Töpel
@ 2026-03-03 18:15 ` Björn Töpel
2026-03-04 0:16 ` Jakub Kicinski
2026-03-04 8:23 ` [PATCH net-next 0/5] ethtool: Dynamic RSS context indirection table resizing Pavan Chebbi
5 siblings, 1 reply; 18+ messages in thread
From: Björn Töpel @ 2026-03-03 18:15 UTC (permalink / raw)
To: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
linux-kernel, netdev, linux-kselftest
Cc: Björn Töpel, Willem de Bruijn
Add resize tests to rss_drv.py. Devices without dynamic table
sizing are skipped via _require_dynamic_indir_size().
resize_periodic: set a periodic table (equal 2), shrink
channels to fold, grow back to unfold. Check the pattern is
preserved. Has main and non-default context variants.
resize_nonperiodic_reject: set a non-periodic table (equal N),
verify that channel reduction is rejected.
Pass queue_count=8 to NetDrvEnv so netdevsim is created with
enough queues for the resize tests.
Signed-off-by: Björn Töpel <bjorn@kernel.org>
---
| 101 +++++++++++++++++-
1 file changed, 96 insertions(+), 5 deletions(-)
--git a/tools/testing/selftests/drivers/net/hw/rss_drv.py b/tools/testing/selftests/drivers/net/hw/rss_drv.py
index 2d1a33189076..8725500f2955 100755
--- a/tools/testing/selftests/drivers/net/hw/rss_drv.py
+++ b/tools/testing/selftests/drivers/net/hw/rss_drv.py
@@ -5,9 +5,9 @@
Driver-related behavior tests for RSS.
"""
-from lib.py import ksft_run, ksft_exit, ksft_ge
-from lib.py import ksft_variants, KsftNamedVariant, KsftSkipEx
-from lib.py import defer, ethtool
+from lib.py import ksft_run, ksft_exit, ksft_eq, ksft_ge
+from lib.py import ksft_variants, KsftNamedVariant, KsftSkipEx, KsftFailEx
+from lib.py import defer, ethtool, CmdExitFailure
from lib.py import EthtoolFamily, NlError
from lib.py import NetDrvEnv
@@ -45,6 +45,18 @@ def _maybe_create_context(cfg, create_context):
return ctx_id
+def _require_dynamic_indir_size(cfg, ch_max):
+ """Skip if the device does not dynamically size its indirection table."""
+ ethtool(f"-X {cfg.ifname} default")
+ ethtool(f"-L {cfg.ifname} combined 2")
+ small = len(_get_rss(cfg)['rss-indirection-table'])
+ ethtool(f"-L {cfg.ifname} combined {ch_max}")
+ large = len(_get_rss(cfg)['rss-indirection-table'])
+
+ if small == large:
+ raise KsftSkipEx("Device does not dynamically size indirection table")
+
+
@ksft_variants([
KsftNamedVariant("main", False),
KsftNamedVariant("ctx", True),
@@ -76,11 +88,90 @@ def indir_size_4x(cfg, create_context):
_test_rss_indir_size(cfg, test_max, context=ctx_id)
+@ksft_variants([
+ KsftNamedVariant("main", False),
+ KsftNamedVariant("ctx", True),
+])
+def resize_periodic(cfg, create_context):
+ """Test that a periodic indirection table survives channel changes.
+
+ Set a periodic table (equal 2), reduce channels to trigger a
+ fold, then increase to trigger an unfold. Verify the table pattern
+ is preserved and the size tracks the channel count.
+ """
+ channels = cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifindex}})
+ ch_max = channels.get('combined-max', 0)
+ qcnt = channels['combined-count']
+
+ if ch_max < 4:
+ raise KsftSkipEx(f"Not enough queues for the test: max={ch_max}")
+
+ defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")
+ ethtool(f"-L {cfg.ifname} combined {ch_max}")
+
+ _require_dynamic_indir_size(cfg, ch_max)
+
+ ctx_id = _maybe_create_context(cfg, create_context)
+ ctx_ref = f"context {ctx_id}" if ctx_id else ""
+
+ ethtool(f"-X {cfg.ifname} {ctx_ref} equal 2")
+ if not create_context:
+ defer(ethtool, f"-X {cfg.ifname} default")
+
+ orig_size = len(_get_rss(cfg, context=ctx_id)['rss-indirection-table'])
+
+ # Shrink — should fold
+ ethtool(f"-L {cfg.ifname} combined 2")
+ rss = _get_rss(cfg, context=ctx_id)
+ indir = rss['rss-indirection-table']
+
+ ksft_ge(orig_size, len(indir), "Table did not shrink")
+ ksft_eq(set(indir), {0, 1}, "Folded table has wrong queues")
+
+ # Grow back — should unfold
+ ethtool(f"-L {cfg.ifname} combined {ch_max}")
+ rss = _get_rss(cfg, context=ctx_id)
+ indir = rss['rss-indirection-table']
+
+ ksft_eq(len(indir), orig_size, "Table size not restored")
+ ksft_eq(set(indir), {0, 1}, "Unfolded table has wrong queues")
+
+
+def resize_nonperiodic_reject(cfg):
+ """Test that a non-periodic table blocks channel reduction.
+
+ Set equal weight across all queues so the table is not periodic
+ at any smaller size, then verify channel reduction is rejected.
+ """
+ channels = cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifindex}})
+ ch_max = channels.get('combined-max', 0)
+ qcnt = channels['combined-count']
+
+ if ch_max < 4:
+ raise KsftSkipEx(f"Not enough queues for the test: max={ch_max}")
+
+ defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")
+ ethtool(f"-L {cfg.ifname} combined {ch_max}")
+
+ _require_dynamic_indir_size(cfg, ch_max)
+
+ ethtool(f"-X {cfg.ifname} equal {ch_max}")
+ defer(ethtool, f"-X {cfg.ifname} default")
+
+ try:
+ ethtool(f"-L {cfg.ifname} combined 2")
+ except CmdExitFailure:
+ pass
+ else:
+ raise KsftFailEx("Channel reduction should fail with non-periodic table")
+
+
def main() -> None:
""" Ksft boiler plate main """
- with NetDrvEnv(__file__) as cfg:
+ with NetDrvEnv(__file__, queue_count=8) as cfg:
cfg.ethnl = EthtoolFamily()
- ksft_run([indir_size_4x], args=(cfg, ))
+ ksft_run([indir_size_4x, resize_periodic,
+ resize_nonperiodic_reject], args=(cfg, ))
ksft_exit()
--
2.53.0
^ permalink raw reply related [flat|nested] 18+ messages in thread* Re: [PATCH net-next 5/5] selftests: rss_drv: Add RSS indirection table resize tests
2026-03-03 18:15 ` [PATCH net-next 5/5] selftests: rss_drv: Add RSS indirection table resize tests Björn Töpel
@ 2026-03-04 0:16 ` Jakub Kicinski
0 siblings, 0 replies; 18+ messages in thread
From: Jakub Kicinski @ 2026-03-04 0:16 UTC (permalink / raw)
To: Björn Töpel
Cc: Michael Chan, Pavan Chebbi, Andrew Lunn, David S. Miller,
Eric Dumazet, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
On Tue, 3 Mar 2026 19:15:33 +0100 Björn Töpel wrote:
> +def _require_dynamic_indir_size(cfg, ch_max):
> + """Skip if the device does not dynamically size its indirection table."""
> + ethtool(f"-X {cfg.ifname} default")
> + ethtool(f"-L {cfg.ifname} combined 2")
> + small = len(_get_rss(cfg)['rss-indirection-table'])
> + ethtool(f"-L {cfg.ifname} combined {ch_max}")
> + large = len(_get_rss(cfg)['rss-indirection-table'])
> +
> + if small == large:
> + raise KsftSkipEx("Device does not dynamically size indirection table")
> +
> +
> @ksft_variants([
> KsftNamedVariant("main", False),
> KsftNamedVariant("ctx", True),
> @@ -76,11 +88,90 @@ def indir_size_4x(cfg, create_context):
> _test_rss_indir_size(cfg, test_max, context=ctx_id)
>
>
> +@ksft_variants([
> + KsftNamedVariant("main", False),
> + KsftNamedVariant("ctx", True),
> +])
> +def resize_periodic(cfg, create_context):
> + """Test that a periodic indirection table survives channel changes.
> +
> + Set a periodic table (equal 2), reduce channels to trigger a
> + fold, then increase to trigger an unfold. Verify the table pattern
> + is preserved and the size tracks the channel count.
> + """
> + channels = cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifindex}})
> + ch_max = channels.get('combined-max', 0)
> + qcnt = channels['combined-count']
> +
> + if ch_max < 4:
> + raise KsftSkipEx(f"Not enough queues for the test: max={ch_max}")
> +
> + defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")
> + ethtool(f"-L {cfg.ifname} combined {ch_max}")
> +
> + _require_dynamic_indir_size(cfg, ch_max)
> +
> + ctx_id = _maybe_create_context(cfg, create_context)
> + ctx_ref = f"context {ctx_id}" if ctx_id else ""
> +
> + ethtool(f"-X {cfg.ifname} {ctx_ref} equal 2")
> + if not create_context:
> + defer(ethtool, f"-X {cfg.ifname} default")
> +
> + orig_size = len(_get_rss(cfg, context=ctx_id)['rss-indirection-table'])
> +
> + # Shrink — should fold
> + ethtool(f"-L {cfg.ifname} combined 2")
I wonder if we may miss some cases because we init the table to [0, 1]
and also only enable only 2 queues. Let's set it to 4 queues here to
potentially catch the driver resetting the table? 2 vs 4 are both tiny
values.
Alternatively we could use netlink to set the inidir table as
[1, 0] x N
rather than
[0, 1] x N
and..
> + rss = _get_rss(cfg, context=ctx_id)
> + indir = rss['rss-indirection-table']
> +
> + ksft_ge(orig_size, len(indir), "Table did not shrink")
> + ksft_eq(set(indir), {0, 1}, "Folded table has wrong queues")
here actually check
ksft_eq(indir, [1, 0] * (len / 2), "Folded table has wrong queues")
> + # Grow back — should unfold
> + ethtool(f"-L {cfg.ifname} combined {ch_max}")
> + rss = _get_rss(cfg, context=ctx_id)
> + indir = rss['rss-indirection-table']
> +
> + ksft_eq(len(indir), orig_size, "Table size not restored")
> + ksft_eq(set(indir), {0, 1}, "Unfolded table has wrong queues")
> +
> +
> +def resize_nonperiodic_reject(cfg):
> + """Test that a non-periodic table blocks channel reduction.
> +
> + Set equal weight across all queues so the table is not periodic
> + at any smaller size, then verify channel reduction is rejected.
> + """
> + channels = cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifindex}})
> + ch_max = channels.get('combined-max', 0)
> + qcnt = channels['combined-count']
> +
> + if ch_max < 4:
> + raise KsftSkipEx(f"Not enough queues for the test: max={ch_max}")
> +
> + defer(ethtool, f"-L {cfg.ifname} combined {qcnt}")
> + ethtool(f"-L {cfg.ifname} combined {ch_max}")
> +
> + _require_dynamic_indir_size(cfg, ch_max)
> +
> + ethtool(f"-X {cfg.ifname} equal {ch_max}")
> + defer(ethtool, f"-X {cfg.ifname} default")
> +
> + try:
> + ethtool(f"-L {cfg.ifname} combined 2")
> + except CmdExitFailure:
> + pass
> + else:
> + raise KsftFailEx("Channel reduction should fail with non-periodic table")
grep for ksft_raises()
> def main() -> None:
> """ Ksft boiler plate main """
> - with NetDrvEnv(__file__) as cfg:
> + with NetDrvEnv(__file__, queue_count=8) as cfg:
the queue_count=8 presumably only applies to netdevsim so it should go
> cfg.ethnl = EthtoolFamily()
> - ksft_run([indir_size_4x], args=(cfg, ))
> + ksft_run([indir_size_4x, resize_periodic,
> + resize_nonperiodic_reject], args=(cfg, ))
> ksft_exit()
>
>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH net-next 0/5] ethtool: Dynamic RSS context indirection table resizing
2026-03-03 18:15 [PATCH net-next 0/5] ethtool: Dynamic RSS context indirection table resizing Björn Töpel
` (4 preceding siblings ...)
2026-03-03 18:15 ` [PATCH net-next 5/5] selftests: rss_drv: Add RSS indirection table resize tests Björn Töpel
@ 2026-03-04 8:23 ` Pavan Chebbi
5 siblings, 0 replies; 18+ messages in thread
From: Pavan Chebbi @ 2026-03-04 8:23 UTC (permalink / raw)
To: Björn Töpel
Cc: Michael Chan, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel, netdev,
linux-kselftest, Willem de Bruijn
[-- Attachment #1: Type: text/plain, Size: 864 bytes --]
>
> Open items:
>
> - Pre-existing bug in bnxt: the IFF_RXFH_CONFIGURED guard in
> bnxt_set_channels() only protects context 0. If context 0 uses
> defaults but non-default contexts exist, channel changes that
> change the table size succeed without updating ctx->indir_size for
> those contexts. The core's ethtool_check_max_channel() prevents
> invalid queue references but not the size mismatch itself. This
> series fixes it by resizing all contexts. Should a separate fix be
> sent to net for the existing bug?
You are right. Our re-size requirement check should include checking
of presence of any additional contexts and fail the channel change.
So the net effect is that ring 0 would get included/overburdened for
the additional contexts and that's an issue for sure though its not
catastrophic.
So IMO we could have a fix for net..
[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5469 bytes --]
^ permalink raw reply [flat|nested] 18+ messages in thread