From nobody Mon Apr 6 18:22:59 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4C4623BA25B; Wed, 18 Mar 2026 12:26:17 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773836777; cv=none; b=kc3ltfLbxQgCTQqEhwomyp33dwAhSu3lE72LiAQ7n+miOlaTx7jJNl7xeSPnwbDyyHeM++RxyfKDPzxQxj3W3GCVSPEiYgwh9ICUuUXbY4PBRcl0zXKcb3K2BtuaKdg54Rgs6UEB6i6s8oBsnO0grPz7S8kFWe+tA21QnCKYR0k= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773836777; c=relaxed/simple; bh=yXlwMeBvYYc1Zb2quhC1vDJ9PdESoBqLNJ0u8zZ3Lig=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=HQAc/Ytvxh2bX0B19Azs7BQAC+ymIuQhHmWgI1Q4VdfdL4ng256H2KGgqWCE69pUMNGameKvahzDH1eWhsznsNMGuDA8so8ammMCgwLNou4BpkAVQczEBekHSZt6hun8yu8xiOwRzxcVKBqp9GOcrCFiwb7xZSgqSlGvLmXv6GQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=i0Azya60; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="i0Azya60" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 51205C2BCAF; Wed, 18 Mar 2026 12:26:12 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773836776; bh=yXlwMeBvYYc1Zb2quhC1vDJ9PdESoBqLNJ0u8zZ3Lig=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=i0Azya60rMs34l0J2+rcTOeUE/u63g0z5K53V6oXdGF08Lr7W/aFqIiNixaIwejsc BrTC8NkKX/3+Y5fgZZVnyaFxTBrsWgoZ5rryUiatHQV1AUDPblZU4vnfTaMy1OE8h8 I32JpdarYlgTR/B4LEaAuSjvHwZEB8XSQUuE5XXwiRaZMaGOEQasNv5hQ5547TBEFM HyA2kTzPHcJQLAIh7mmPcS8y0i7rsvhEJRdUtZRHdydYBKdTQ7dAlHNLhTaHC4xJRH bNEmxXBoFLPS/c04Xs581dDCb65mSF04TT7CaZPfVEhS693hDXs8YDCsyZ9tC/PJro PYCYQ2LxesEtg== From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= To: Michael Chan , Pavan Chebbi , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Saeed Mahameed , Tariq Toukan , Mark Bloch , Leon Romanovsky , Simon Horman , Shuah Khan , netdev@vger.kernel.org Cc: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= , Maxime Chevallier , Gal Pressman , Willem de Bruijn , linux-kernel@vger.kernel.org, linux-rdma@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH net-next v6 1/4] ethtool: Track user-provided RSS indirection table size Date: Wed, 18 Mar 2026 13:25:58 +0100 Message-ID: <20260318122603.264550-2-bjorn@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260318122603.264550-1-bjorn@kernel.org> References: <20260318122603.264550-1-bjorn@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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_clear() for drivers that must reset the indirection table. Convert bnxt and mlx5 to use it. Signed-off-by: Bj=C3=B6rn T=C3=B6pel --- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 2 +- .../net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- include/linux/ethtool.h | 7 ++++++ include/linux/netdevice.h | 7 +----- net/ethtool/common.c | 20 ++++++++++++++++ net/ethtool/ioctl.c | 9 +++---- net/ethtool/rss.c | 24 ++++++++++++------- 7 files changed, 51 insertions(+), 20 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethern= et/broadcom/bnxt/bnxt.c index c982aac714d1..04da3f708b4b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -8117,7 +8117,7 @@ static int __bnxt_reserve_rings(struct bnxt *bp) bnxt_get_nr_rss_ctxs(bp, rx_rings) || bnxt_get_max_rss_ring(bp) >=3D rx_rings)) { netdev_warn(bp->dev, "RSS table entries reverting to default\n"); - bp->dev->priv_flags &=3D ~IFF_RXFH_CONFIGURED; + ethtool_rxfh_indir_clear(bp->dev); } } bp->rx_nr_rings =3D rx_rings; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/ne= t/ethernet/mellanox/mlx5/core/en_main.c index f7009da94f0b..1a010a115cff 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -6488,7 +6488,7 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv) /* Reducing the number of channels - RXFH has to be reset, and * mlx5e_num_channels_changed below will build the RQT. */ - priv->netdev->priv_flags &=3D ~IFF_RXFH_CONFIGURED; + ethtool_rxfh_indir_clear(priv->netdev); priv->channels.params.num_channels =3D max_nch; if (priv->channels.params.mqprio.mode =3D=3D TC_MQPRIO_MODE_CHANNEL) { mlx5_core_warn(priv->mdev, "MLX5E: Disabling MQPRIO channel mode\n"); diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 83c375840835..d4d3c57bc7c0 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 ar= e 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 ethto= ol_rxfh_context *ctx) } =20 void ethtool_rxfh_context_lost(struct net_device *dev, u32 context_id); +void ethtool_rxfh_indir_clear(struct net_device *dev); =20 struct link_mode_info { int speed; @@ -1333,12 +1337,15 @@ int ethtool_virtdev_set_link_ksettings(struct net_d= evice *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 ae269a2e7f4d..dc28954d4df6 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1714,7 +1714,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 configur= ed * @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 @@ -1750,7 +1749,6 @@ enum netdev_priv_flags { IFF_OPENVSWITCH =3D 1<<20, IFF_L3MDEV_SLAVE =3D 1<<21, IFF_TEAM =3D 1<<22, - IFF_RXFH_CONFIGURED =3D 1<<23, IFF_PHONY_HEADROOM =3D 1<<24, IFF_MACSEC =3D 1<<25, IFF_NO_RX_HANDLER =3D 1<<26, @@ -5568,10 +5566,7 @@ static inline bool netif_is_lag_port(const struct ne= t_device *dev) return netif_is_bond_slave(dev) || netif_is_team_port(dev); } =20 -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); =20 static inline bool netif_is_failover(const struct net_device *dev) { diff --git a/net/ethtool/common.c b/net/ethtool/common.c index e252cf20c22f..ee91f1155830 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -1204,6 +1204,26 @@ void ethtool_rxfh_context_lost(struct net_device *de= v, u32 context_id) } EXPORT_SYMBOL(ethtool_rxfh_context_lost); =20 +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_clear - Clear user indirection table config + * @dev: network device + * + * Mark the default RSS context indirection table as unconfigured and + * send an %ETHTOOL_MSG_RSS_NTF notification. + */ +void ethtool_rxfh_indir_clear(struct net_device *dev) +{ + dev->ethtool->rss_indir_user_size =3D 0; + ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_NTF, 0); +} +EXPORT_SYMBOL(ethtool_rxfh_indir_clear); + 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 ff4b4780d6af..3d31a5a041e3 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1404,9 +1404,9 @@ static noinline_for_stack int ethtool_set_rxfh_indir(= struct net_device *dev, =20 /* indicate whether rxfh was set to default */ if (user_size =3D=3D 0) - dev->priv_flags &=3D ~IFF_RXFH_CONFIGURED; + dev->ethtool->rss_indir_user_size =3D 0; else - dev->priv_flags |=3D IFF_RXFH_CONFIGURED; + dev->ethtool->rss_indir_user_size =3D rxfh_dev.indir_size; =20 out_unlock: mutex_unlock(&dev->ethtool->rss_lock); @@ -1721,9 +1721,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 =3D=3D 0) - dev->priv_flags &=3D ~IFF_RXFH_CONFIGURED; + dev->ethtool->rss_indir_user_size =3D 0; else if (rxfh.indir_size !=3D ETH_RXFH_INDIR_NO_CHANGE) - dev->priv_flags |=3D IFF_RXFH_CONFIGURED; + dev->ethtool->rss_indir_user_size =3D dev_indir_size; } /* Update rss_ctx tracking */ if (rxfh_dev.rss_delete) { @@ -1736,6 +1736,7 @@ static noinline_for_stack int ethtool_set_rxfh(struct= net_device *dev, ctx->indir_configured =3D rxfh.indir_size && rxfh.indir_size !=3D ETH_RXFH_INDIR_NO_CHANGE; + ctx->indir_user_size =3D 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 da5934cceb07..e6fc6e64fb27 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, =20 *mod |=3D memcmp(rxfh->indir, data->indir_table, data->indir_size); =20 - return 0; + return user_size; =20 err_free: kfree(rxfh->indir); @@ -833,6 +833,7 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct g= enl_info *info) struct nlattr **tb =3D info->attrs; struct rss_reply_data data =3D {}; const struct ethtool_ops *ops; + u32 indir_user_size; int ret; =20 ops =3D dev->ethtool_ops; @@ -845,8 +846,9 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct g= enl_info *info) rxfh.rss_context =3D request->rss_context; =20 ret =3D rss_set_prep_indir(dev, info, &data, &rxfh, &indir_reset, &mod); - if (ret) + if (ret < 0) goto exit_clean_data; + indir_user_size =3D ret; indir_mod =3D !!tb[ETHTOOL_A_RSS_INDIR]; =20 rxfh.hfunc =3D data.hfunc; @@ -889,12 +891,15 @@ ethnl_rss_set(struct ethnl_req_info *req_info, struct= genl_info *info) if (ret) goto exit_unlock; =20 - if (ctx) + if (ctx) { rss_set_ctx_update(ctx, tb, &data, &rxfh); - else if (indir_reset) - dev->priv_flags &=3D ~IFF_RXFH_CONFIGURED; - else if (indir_mod) - dev->priv_flags |=3D IFF_RXFH_CONFIGURED; + if (indir_user_size) + ctx->indir_user_size =3D indir_user_size; + } else if (indir_reset) { + dev->ethtool->rss_indir_user_size =3D 0; + } else if (indir_mod) { + dev->ethtool->rss_indir_user_size =3D indir_user_size; + } =20 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 =3D {}; 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; =20 ret =3D rss_set_prep_indir(dev, info, &data, &rxfh, &indir_dflt, &mod); - if (ret) + if (ret < 0) goto exit_clean_data; + indir_user_size =3D ret; =20 ethnl_update_u8(&rxfh.hfunc, tb[ETHTOOL_A_RSS_HFUNC], &mod); =20 @@ -1080,6 +1087,7 @@ int ethnl_rss_create_doit(struct sk_buff *skb, struct= genl_info *info) =20 /* Store the config from rxfh to Xarray.. */ rss_set_ctx_update(ctx, tb, &data, &rxfh); + ctx->indir_user_size =3D indir_user_size; /* .. copy from Xarray to data. */ __rss_prepare_ctx(dev, &data, ctx); =20 --=20 2.53.0 From nobody Mon Apr 6 18:22:59 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 285DE3BA25B; Wed, 18 Mar 2026 12:26:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773836782; cv=none; b=IX1jPfUfElA/Tfwc3cTWLfQ1DtrWM4cRJnNtnXPj7mAmg7T94GlJugkzR/Pbul4zP5KV2WOnf1Mu7SJDSkmsYUlKo0ds58ZlsAMb3Ng40jROrUMNmZNGdsb1M1CFZRGdGDkXQZuRbRIHquAMRZHWCzkOnPsLryWY5B8LxCoJerE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773836782; c=relaxed/simple; bh=uE10lGHHiHqiFzRyE1cjeLFAi3qrysOE5m57at1b8P8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=XGmUAIVb3AVttZmJmJxv/cJy496m4YBO6FzvMfbhR9Hb5SLnZJ+ID35aov4e9sWqg2Bg6w4YJLVGW2SnNzrk37brddcH0fEekZUDE7xoSZa+HKQLc4mRJvf9Z/TH9wEhGu7xGJAwdH52QSdj5Z+edw0ht5PqDsmiM44Jan7uPKY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=efR4udBb; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="efR4udBb" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 677A5C19421; Wed, 18 Mar 2026 12:26:17 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773836781; bh=uE10lGHHiHqiFzRyE1cjeLFAi3qrysOE5m57at1b8P8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=efR4udBbB7gBcuj6x9qVJ7QvrBBtg3GKxgIaaYtlZU9y0ogfQcznQbI140r6waSrm 329TNHu+T46ucDCiV9jqw+JQ80euLVRR9rBQyzoqQZ8/29riwRzG0XQmdX7eCobUcb 8dkvYTI9rh8QDrAtAnIlQXQqraXrozuSJD1dCt72toHzVwSHHZRsPmk0FiLGwwol69 8CJ6HEp8l22W0xpO4ywljK0sGQEY0tlcV3uZGTip2mCJT3QRYdOr2qIuFkEmx9GZav E/XkwMu4bl0bdCBu6n/fSMMoprRTXpf8eCZoMgsPuf78/LjJN8lGF0RvF7O8YQBugF 94tic+zSv0qzw== From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= To: Michael Chan , Pavan Chebbi , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Saeed Mahameed , Tariq Toukan , Mark Bloch , Leon Romanovsky , Simon Horman , Shuah Khan , netdev@vger.kernel.org Cc: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= , Maxime Chevallier , Gal Pressman , Willem de Bruijn , linux-kernel@vger.kernel.org, linux-rdma@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH net-next v6 2/4] ethtool: Add RSS indirection table resize helpers Date: Wed, 18 Mar 2026 13:25:59 +0100 Message-ID: <20260318122603.264550-3-bjorn@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260318122603.264550-1-bjorn@kernel.org> References: <20260318122603.264550-1-bjorn@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable 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 driver helpers to resize indirection tables: ethtool_rxfh_indir_can_resize() checks whether the default context indirection table can be resized. ethtool_rxfh_indir_resize() resizes the default context 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_ctxs_can_resize() validates all non-default RSS contexts can be resized. ethtool_rxfh_ctxs_resize() applies the resize. Signed-off-by: Bj=C3=B6rn T=C3=B6pel --- include/linux/ethtool.h | 6 ++ net/ethtool/common.c | 155 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index d4d3c57bc7c0..51107b7e739e 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -218,6 +218,12 @@ static inline u8 *ethtool_rxfh_context_key(struct etht= ool_rxfh_context *ctx) =20 void ethtool_rxfh_context_lost(struct net_device *dev, u32 context_id); void ethtool_rxfh_indir_clear(struct net_device *dev); +bool ethtool_rxfh_indir_can_resize(struct net_device *dev, const u32 *tbl, + u32 old_size, u32 new_size); +void ethtool_rxfh_indir_resize(struct net_device *dev, u32 *tbl, + u32 old_size, u32 new_size); +int ethtool_rxfh_ctxs_can_resize(struct net_device *dev, u32 new_indir_siz= e); +void ethtool_rxfh_ctxs_resize(struct net_device *dev, u32 new_indir_size); =20 struct link_mode_info { int speed; diff --git a/net/ethtool/common.c b/net/ethtool/common.c index ee91f1155830..bf795766f526 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -1224,6 +1224,161 @@ void ethtool_rxfh_indir_clear(struct net_device *de= v) } EXPORT_SYMBOL(ethtool_rxfh_indir_clear); =20 +static bool ethtool_rxfh_is_periodic(const u32 *tbl, u32 old_size, u32 new= _size) +{ + u32 i; + + for (i =3D new_size; i < old_size; i++) + if (tbl[i] !=3D tbl[i % new_size]) + return false; + return true; +} + +static bool ethtool_rxfh_can_resize(const u32 *tbl, u32 old_size, u32 new_= size, + u32 user_size) +{ + if (new_size =3D=3D old_size) + return true; + + if (!user_size) + return true; + + if (new_size < old_size) { + if (new_size < user_size) + return false; + if (old_size % new_size) + return false; + if (!ethtool_rxfh_is_periodic(tbl, old_size, new_size)) + return false; + return true; + } + + if (new_size % old_size) + return false; + return true; +} + +/* Resize without validation; caller must have called can_resize first */ +static void ethtool_rxfh_resize(u32 *tbl, u32 old_size, u32 new_size) +{ + u32 i; + + /* Grow: replicate existing pattern; shrink is a no-op on the data */ + for (i =3D old_size; i < new_size; i++) + tbl[i] =3D tbl[i % old_size]; +} + +/** + * ethtool_rxfh_indir_can_resize - Check if context 0 indir table can resi= ze + * @dev: network device + * @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. Uses the user_size floor from context 0. When user_size is + * zero the table is not user-configured and resize always succeeds. + * Read-only; does not modify the table. + * + * Return: true if resize is possible, false otherwise. + */ +bool ethtool_rxfh_indir_can_resize(struct net_device *dev, const u32 *tbl, + u32 old_size, u32 new_size) +{ + return ethtool_rxfh_can_resize(tbl, old_size, new_size, + dev->ethtool->rss_indir_user_size); +} +EXPORT_SYMBOL(ethtool_rxfh_indir_can_resize); + +/** + * ethtool_rxfh_indir_resize - Fold or unfold context 0 indirection table + * @dev: network device + * @tbl: indirection table (must have room for max(old_size, new_size) ent= ries) + * @old_size: current number of entries in the table + * @new_size: desired number of entries + * + * Resize the default RSS context indirection table in place. Caller + * must have validated with ethtool_rxfh_indir_can_resize() first. + */ +void ethtool_rxfh_indir_resize(struct net_device *dev, u32 *tbl, + u32 old_size, u32 new_size) +{ + if (!dev->ethtool->rss_indir_user_size) + return; + + ethtool_rxfh_resize(tbl, old_size, new_size); +} +EXPORT_SYMBOL(ethtool_rxfh_indir_resize); + +/** + * ethtool_rxfh_ctxs_can_resize - Validate resize for all RSS contexts + * @dev: network device + * @new_indir_size: new indirection table size + * + * Validate that the indirection tables of all non-default RSS contexts + * can be resized to @new_indir_size. Read-only; does not modify any + * context. Intended to be paired with ethtool_rxfh_ctxs_resize(). + * + * Return: 0 if all contexts can be resized, negative errno on failure. + */ +int ethtool_rxfh_ctxs_can_resize(struct net_device *dev, u32 new_indir_siz= e) +{ + struct ethtool_rxfh_context *ctx; + unsigned long context; + int ret =3D 0; + + if (!dev->ethtool_ops->rxfh_indir_space || + new_indir_size > dev->ethtool_ops->rxfh_indir_space) + return -EINVAL; + + mutex_lock(&dev->ethtool->rss_lock); + xa_for_each(&dev->ethtool->rss_ctx, context, ctx) { + u32 *indir =3D ethtool_rxfh_context_indir(ctx); + + if (!ethtool_rxfh_can_resize(indir, ctx->indir_size, + new_indir_size, + ctx->indir_user_size)) { + ret =3D -EINVAL; + goto unlock; + } + } +unlock: + mutex_unlock(&dev->ethtool->rss_lock); + return ret; +} +EXPORT_SYMBOL(ethtool_rxfh_ctxs_can_resize); + +/** + * ethtool_rxfh_ctxs_resize - 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. Caller must have validated with + * ethtool_rxfh_ctxs_can_resize() first. An %ETHTOOL_MSG_RSS_NTF is + * sent for each resized context. + * + * Notifications are sent outside the RSS lock to avoid holding the + * mutex during notification delivery. + */ +void ethtool_rxfh_ctxs_resize(struct net_device *dev, u32 new_indir_size) +{ + struct ethtool_rxfh_context *ctx; + unsigned long context; + + mutex_lock(&dev->ethtool->rss_lock); + xa_for_each(&dev->ethtool->rss_ctx, context, ctx) { + ethtool_rxfh_resize(ethtool_rxfh_context_indir(ctx), + ctx->indir_size, new_indir_size); + ctx->indir_size =3D new_indir_size; + } + mutex_unlock(&dev->ethtool->rss_lock); + + xa_for_each(&dev->ethtool->rss_ctx, context, ctx) + ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_NTF, context); +} +EXPORT_SYMBOL(ethtool_rxfh_ctxs_resize); + enum ethtool_link_medium ethtool_str_to_medium(const char *str) { int i; --=20 2.53.0 From nobody Mon Apr 6 18:22:59 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id C42043B5304; Wed, 18 Mar 2026 12:26:26 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773836786; cv=none; b=CoBjwEqGbKOE7gtu57cUW1bCiNHGWbPFKwLG6m7CVaGxri1ev9sL+WM+tLtX2To703FtD5k5oB1zMFQl6GxB1qyFmNBTiIPiNZNRePKM9KGKQSF0+q+/o6zVCkDz351jHFBalyGnzOpCY3HzlRh87FCxy7ljgDb0nJ7cPiiTmTs= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773836786; c=relaxed/simple; bh=X4usbgd93DhF/Pun7LV5jczbbXgnNbjO8K4pi1eRcrc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=SHXJawx3YemvGiGO7XHxNrNWJ/tb6a1AhP1tqqeIVnffXI4S35gDNjT5shn2+C7B8V9Wr4pVyVonUDTpBLJNYhtbRkZpahf+I1b6TA2pE/dLUx+Nw6wvsUNFG375keyroSvW4ILgfEOLCxi40wGIKOb9eUjnYoN4ndr5DPXUQuE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=la2ylehX; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="la2ylehX" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 47E62C19424; Wed, 18 Mar 2026 12:26:22 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773836786; bh=X4usbgd93DhF/Pun7LV5jczbbXgnNbjO8K4pi1eRcrc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=la2ylehXepoF9v6qSeUQD3+Uae1/6+pd0K2EXRYKBv1dNSWJEpAQZv71LO3BRo5jw cUjIUkN8WVF9+2gzNo31ilLG2yMYfMA7CCA3VBHIuT5mEnCGlqg3Mh9ESaPJLyzzAo q7anuYBITaoYxetKQMjujOalLDUyr01AlGBE/GeymnBqFhT99Ie/NdyGBbmvJhCJrP yIQkadLGOCdLnpNi0Ugc+Y8mxt4V7UAU9XU+jMYbY5E9CEPXyoxfB02gAjjEACptz8 onn8RXzyA718F6AqteRD3n8/8O1SAmNhuZv42pF31txSBwjhC4i3SYp4LxJSsLUgoG UIg3AqeVAphHg== From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= To: Michael Chan , Pavan Chebbi , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Saeed Mahameed , Tariq Toukan , Mark Bloch , Leon Romanovsky , Simon Horman , Shuah Khan , netdev@vger.kernel.org Cc: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= , Maxime Chevallier , Gal Pressman , Willem de Bruijn , linux-kernel@vger.kernel.org, linux-rdma@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH net-next v6 3/4] bnxt_en: Resize RSS contexts on channel count change Date: Wed, 18 Mar 2026 13:26:00 +0100 Message-ID: <20260318122603.264550-4-bjorn@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260318122603.264550-1-bjorn@kernel.org> References: <20260318122603.264550-1-bjorn@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable bnxt_set_channels() previously rejected channel changes that alter the RSS table size when RSS contexts exist, because non-default context sizes were locked at creation. Replace the rejection with the new resize helpers. RSS table size only changes on P5 chips with older firmware; newer firmware always uses the largest table size. Signed-off-by: Bj=C3=B6rn T=C3=B6pel --- .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/ne= t/ethernet/broadcom/bnxt/bnxt_ethtool.c index 48e8e3be70d3..b87ac2bb43dd 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -942,6 +942,7 @@ static int bnxt_set_channels(struct net_device *dev, { struct bnxt *bp =3D netdev_priv(dev); int req_tx_rings, req_rx_rings, tcs; + u32 new_tbl_size =3D 0, old_tbl_size; bool sh =3D false; int tx_xdp =3D 0; int rc =3D 0; @@ -977,19 +978,33 @@ static int bnxt_set_channels(struct net_device *dev, tx_xdp =3D req_rx_rings; } =20 - if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) !=3D - bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) && - (netif_is_rxfh_configured(dev) || bp->num_rss_ctx)) { - netdev_warn(dev, "RSS table size change required, RSS table entries must= be default (with no additional RSS contexts present) to proceed\n"); - return -EINVAL; - } - rc =3D bnxt_check_rings(bp, req_tx_rings, req_rx_rings, sh, tcs, tx_xdp); if (rc) { netdev_warn(dev, "Unable to allocate the requested rings\n"); return rc; } =20 + /* RSS table size only changes on P5 chips with older firmware; + * newer firmware always uses the largest table size. + */ + if (bnxt_get_nr_rss_ctxs(bp, req_rx_rings) !=3D + bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings)) { + new_tbl_size =3D bnxt_get_nr_rss_ctxs(bp, req_rx_rings) * + BNXT_RSS_TABLE_ENTRIES_P5; + old_tbl_size =3D bnxt_get_rxfh_indir_size(dev); + + if (!ethtool_rxfh_indir_can_resize(dev, bp->rss_indir_tbl, + old_tbl_size, + new_tbl_size)) { + netdev_warn(dev, "RSS table resize not possible\n"); + return -EINVAL; + } + + rc =3D ethtool_rxfh_ctxs_can_resize(dev, new_tbl_size); + if (rc) + return rc; + } + if (netif_running(dev)) { if (BNXT_PF(bp)) { /* TODO CHIMP_FW: Send message to all VF's @@ -999,6 +1014,12 @@ static int bnxt_set_channels(struct net_device *dev, bnxt_close_nic(bp, true, false); } =20 + if (new_tbl_size) { + ethtool_rxfh_indir_resize(dev, bp->rss_indir_tbl, + old_tbl_size, new_tbl_size); + ethtool_rxfh_ctxs_resize(dev, new_tbl_size); + } + if (sh) { bp->flags |=3D BNXT_FLAG_SHARED_RINGS; bp->rx_nr_rings =3D channel->combined_count; --=20 2.53.0 From nobody Mon Apr 6 18:22:59 2026 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EB4C93B5304; Wed, 18 Mar 2026 12:26:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773836792; cv=none; b=KNrm9EkgpenXWrEg0SBNJno0BNymiaekAD3UHWD3126j6EkNhj4ZzAm5h4VujTqwzA2UxiBw58P1w06h0T/P7OhcGUfQQusWvDdThh+y/xHr0i14EFFx4H1+U6Qw9/P9cs6yKy1I+ABIqNJ80VwQDlHHvUFTPrARKvKDTvPyVfg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773836792; c=relaxed/simple; bh=n31d8NYjTDg4u78LNNFct5Yi6pJSfLNaOmI/4D6Nfd8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=VjG+Osh1en//Q72XC4IF0GzKiRdTaFdSWLwhpPwIP8b3GjwyRRgyihl1Do719s72cQtLStw41k88lR4JcUqXm+/PxtF7kMzSyEGLOINqTTTdnB+hRXPt3NrBTy57rmzJlXX+x/2IRgcMKYVxTUH/bzTtHjQ3JETCu6t93gA8u2U= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ub5/4M1E; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ub5/4M1E" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 2BE1BC19424; Wed, 18 Mar 2026 12:26:26 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773836791; bh=n31d8NYjTDg4u78LNNFct5Yi6pJSfLNaOmI/4D6Nfd8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ub5/4M1Ey+kvsbJSYdzQS9wC6jUY79/M91IARgDNiSlppQl8zZEwvPPV7ilv/JebF x8Mz2SDv6Kh3TbKB+EzAo4qGHak/bNH/VGczjjXJdpe2O3IwNMlUBxuufpae9mMdCB MaU3p+sxyI6g2c5fuCgoVod4SULwxrvVzBPfr+aD/KVX+0Cm1wvtIUo4jMfL8AAZQP DGC49R3SpY29j/pdeAmlb0FPyj6n9WfLIuzJRQiFZzDGCmzAvjtCtW5y7Z4SLH8Q2z Bu315PKEIog/0Un+KCIECkgwgTJfompPU/38sKiy1HJ9KKJiwup2ocj4/sIsbFpxo9 l7NQhdRcL64Hw== From: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= To: Michael Chan , Pavan Chebbi , Andrew Lunn , "David S. Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Saeed Mahameed , Tariq Toukan , Mark Bloch , Leon Romanovsky , Simon Horman , Shuah Khan , netdev@vger.kernel.org Cc: =?UTF-8?q?Bj=C3=B6rn=20T=C3=B6pel?= , Maxime Chevallier , Gal Pressman , Willem de Bruijn , linux-kernel@vger.kernel.org, linux-rdma@vger.kernel.org, linux-kselftest@vger.kernel.org Subject: [PATCH net-next v6 4/4] selftests: rss_drv: Add RSS indirection table resize tests Date: Wed, 18 Mar 2026 13:26:01 +0100 Message-ID: <20260318122603.264550-5-bjorn@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260318122603.264550-1-bjorn@kernel.org> References: <20260318122603.264550-1-bjorn@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Add resize tests to rss_drv.py. Devices without dynamic table sizing are skipped via _require_dynamic_indir_size(). resize_periodic: set a periodic 4-entry table, shrink channels to fold, grow back to unfold. Check the exact pattern is preserved. Has main and non-default context variants. resize_below_user_size_reject: send a periodic table with user_size between the big and small device table sizes. Verify that shrinking below user_size is rejected even though the table is periodic. Has main and non-default context variants. resize_nonperiodic_reject: set a non-periodic table (equal N), verify that channel reduction is rejected. resize_nonperiodic_no_corruption: verify a failed resize leaves both the indirection table contents and the channel count unchanged. Signed-off-by: Bj=C3=B6rn T=C3=B6pel --- .../selftests/drivers/net/hw/rss_drv.py | 233 +++++++++++++++++- 1 file changed, 229 insertions(+), 4 deletions(-) diff --git a/tools/testing/selftests/drivers/net/hw/rss_drv.py b/tools/test= ing/selftests/drivers/net/hw/rss_drv.py index 2d1a33189076..bd59dace6e15 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. """ =20 -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, ksft_raises +from lib.py import defer, ethtool, CmdExitFailure from lib.py import EthtoolFamily, NlError from lib.py import NetDrvEnv =20 @@ -45,6 +45,18 @@ def _maybe_create_context(cfg, create_context): return ctx_id =20 =20 +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 =3D len(_get_rss(cfg)['rss-indirection-table']) + ethtool(f"-L {cfg.ifname} combined {ch_max}") + large =3D len(_get_rss(cfg)['rss-indirection-table']) + + if small =3D=3D large: + raise KsftSkipEx("Device does not dynamically size indirection tab= le") + + @ksft_variants([ KsftNamedVariant("main", False), KsftNamedVariant("ctx", True), @@ -76,11 +88,224 @@ def indir_size_4x(cfg, create_context): _test_rss_indir_size(cfg, test_max, context=3Dctx_id) =20 =20 +@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 non-default periodic table ([3, 2, 1, 0] x N) via netlink, + reduce channels to trigger a fold, then increase to trigger an + unfold. Using a reversed pattern (instead of [0, 1, 2, 3]) ensures + the test can distinguish a correct fold from a driver that silently + resets the table to defaults. Verify the exact pattern is preserved + and the size tracks the channel count. + """ + channels =3D cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifind= ex}}) + ch_max =3D channels.get('combined-max', 0) + qcnt =3D channels['combined-count'] + + if ch_max < 4: + raise KsftSkipEx(f"Not enough queues for the test: max=3D{ch_max}") + + defer(ethtool, f"-L {cfg.ifname} combined {qcnt}") + + _require_dynamic_indir_size(cfg, ch_max) + + ctx_id =3D _maybe_create_context(cfg, create_context) + + # Set a non-default periodic pattern via netlink. + # Send only 4 entries (user_size=3D4) so the kernel replicates it + # to fill the device table. This allows folding down to 4 entries. + rss =3D _get_rss(cfg, context=3Dctx_id) + orig_size =3D len(rss['rss-indirection-table']) + pattern =3D [3, 2, 1, 0] + req =3D {'header': {'dev-index': cfg.ifindex}, 'indir': pattern} + if ctx_id: + req['context'] =3D ctx_id + else: + defer(ethtool, f"-X {cfg.ifname} default") + cfg.ethnl.rss_set(req) + + # Shrink =E2=80=94 should fold + ethtool(f"-L {cfg.ifname} combined 4") + rss =3D _get_rss(cfg, context=3Dctx_id) + indir =3D rss['rss-indirection-table'] + + ksft_ge(orig_size, len(indir), "Table did not shrink") + ksft_eq(indir, [3, 2, 1, 0] * (len(indir) // 4), + "Folded table has wrong pattern") + + # Grow back =E2=80=94 should unfold + ethtool(f"-L {cfg.ifname} combined {ch_max}") + rss =3D _get_rss(cfg, context=3Dctx_id) + indir =3D rss['rss-indirection-table'] + + ksft_eq(len(indir), orig_size, "Table size not restored") + ksft_eq(indir, [3, 2, 1, 0] * (len(indir) // 4), + "Unfolded table has wrong pattern") + + +@ksft_variants([ + KsftNamedVariant("main", False), + KsftNamedVariant("ctx", True), +]) +def resize_below_user_size_reject(cfg, create_context): + """Test that shrinking below user_size is rejected. + + Send a table via netlink whose size (user_size) sits between + the small and large device table sizes. The table is periodic, + so folding would normally succeed, but the user_size floor must + prevent it. + """ + channels =3D cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifind= ex}}) + ch_max =3D channels.get('combined-max', 0) + qcnt =3D channels['combined-count'] + + if ch_max < 4: + raise KsftSkipEx(f"Not enough queues for the test: max=3D{ch_max}") + + defer(ethtool, f"-L {cfg.ifname} combined {qcnt}") + + _require_dynamic_indir_size(cfg, ch_max) + + ctx_id =3D _maybe_create_context(cfg, create_context) + + # Measure the table size at max channels + rss =3D _get_rss(cfg, context=3Dctx_id) + big_size =3D len(rss['rss-indirection-table']) + + # Measure the table size at reduced channels + ethtool(f"-L {cfg.ifname} combined 4") + rss =3D _get_rss(cfg, context=3Dctx_id) + small_size =3D len(rss['rss-indirection-table']) + ethtool(f"-L {cfg.ifname} combined {ch_max}") + + if small_size >=3D big_size: + raise KsftSkipEx("Table did not shrink at reduced channels") + + # Find a user_size + user_size =3D None + for div in [2, 4]: + candidate =3D big_size // div + if candidate > small_size and big_size % candidate =3D=3D 0: + user_size =3D candidate + break + if user_size is None: + raise KsftSkipEx("No suitable user_size between small and big tabl= e") + + # Send a periodic sub-table of exactly user_size entries. + # Pattern safe for 4 channels. + pattern =3D [0, 1, 2, 3] * (user_size // 4) + if len(pattern) !=3D user_size: + raise KsftSkipEx(f"user_size ({user_size}) not divisible by 4") + req =3D {'header': {'dev-index': cfg.ifindex}, 'indir': pattern} + if ctx_id: + req['context'] =3D ctx_id + else: + defer(ethtool, f"-X {cfg.ifname} default") + cfg.ethnl.rss_set(req) + + # Shrink channels =E2=80=94 table would go to small_size < user_size. + # The table is periodic so folding would work, but user_size + # floor must reject it. + with ksft_raises(CmdExitFailure): + ethtool(f"-L {cfg.ifname} combined 4") + + +@ksft_variants([ + KsftNamedVariant("main", False), + KsftNamedVariant("ctx", True), +]) +def resize_nonperiodic_reject(cfg, create_context): + """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. + An additional context with a periodic table is created to verify + that validation catches the non-periodic one even when others + are fine. + """ + channels =3D cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifind= ex}}) + ch_max =3D channels.get('combined-max', 0) + qcnt =3D channels['combined-count'] + + if ch_max < 4: + raise KsftSkipEx(f"Not enough queues for the test: max=3D{ch_max}") + + defer(ethtool, f"-L {cfg.ifname} combined {qcnt}") + + _require_dynamic_indir_size(cfg, ch_max) + + ctx_id =3D _maybe_create_context(cfg, create_context) + ctx_ref =3D f"context {ctx_id}" if ctx_id else "" + + # Create an extra context with a periodic (foldable) table so that + # the validation must iterate all contexts to find the bad one. + extra_ctx =3D _maybe_create_context(cfg, True) + ethtool(f"-X {cfg.ifname} context {extra_ctx} equal 2") + + ethtool(f"-X {cfg.ifname} {ctx_ref} equal {ch_max}") + if not create_context: + defer(ethtool, f"-X {cfg.ifname} default") + + with ksft_raises(CmdExitFailure): + ethtool(f"-L {cfg.ifname} combined 2") + + +@ksft_variants([ + KsftNamedVariant("main", False), + KsftNamedVariant("ctx", True), +]) +def resize_nonperiodic_no_corruption(cfg, create_context): + """Test that a failed resize does not corrupt table or channel count. + + Set a non-periodic table, attempt a channel reduction (which must + fail), then verify both the indirection table contents and the + channel count are unchanged. + """ + channels =3D cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifind= ex}}) + ch_max =3D channels.get('combined-max', 0) + qcnt =3D channels['combined-count'] + + if ch_max < 4: + raise KsftSkipEx(f"Not enough queues for the test: max=3D{ch_max}") + + defer(ethtool, f"-L {cfg.ifname} combined {qcnt}") + + _require_dynamic_indir_size(cfg, ch_max) + + ctx_id =3D _maybe_create_context(cfg, create_context) + ctx_ref =3D f"context {ctx_id}" if ctx_id else "" + + ethtool(f"-X {cfg.ifname} {ctx_ref} equal {ch_max}") + if not create_context: + defer(ethtool, f"-X {cfg.ifname} default") + + rss_before =3D _get_rss(cfg, context=3Dctx_id) + + with ksft_raises(CmdExitFailure): + ethtool(f"-L {cfg.ifname} combined 2") + + rss_after =3D _get_rss(cfg, context=3Dctx_id) + ksft_eq(rss_after['rss-indirection-table'], + rss_before['rss-indirection-table'], + "Indirection table corrupted after failed resize") + + channels =3D cfg.ethnl.channels_get({'header': {'dev-index': cfg.ifind= ex}}) + ksft_eq(channels['combined-count'], ch_max, + "Channel count changed after failed resize") + + def main() -> None: """ Ksft boiler plate main """ with NetDrvEnv(__file__) as cfg: cfg.ethnl =3D EthtoolFamily() - ksft_run([indir_size_4x], args=3D(cfg, )) + ksft_run([indir_size_4x, resize_periodic, + resize_below_user_size_reject, + resize_nonperiodic_reject, + resize_nonperiodic_no_corruption], args=3D(cfg, )) ksft_exit() =20 =20 --=20 2.53.0