From nobody Sat Apr 4 07:47: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 AE4882D7DE4; Fri, 20 Mar 2026 08:58:37 +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=1773997117; cv=none; b=Ag/Zalcal4f61GmjWXIjS2dSRjGu7AWawhst+hr3pVg4NhezSAnIy5KCsiw8FEGsdd0mB8b09vLZeC+RtU3DVKrv4O2ezbCS36xhBAZQKgoBZtQQvnCHcGARClixdW8A+zGvfxx1YIJUuRdYDjIFgC+zMkumIivnuY0yWvBwdzI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773997117; c=relaxed/simple; bh=6m9tkWvyIEYbQh7eeAnxli8ETjP8g2pfc5BE2dXjep8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=GickDQVSJavbtSY2SqRiRWFKZpOwkxtwgHNk51e4muh/VuYz8LSXMTYSKg4R5wiU0we2ZdiD20zdraZRXantCcbG+LMcBxlbYf+LSw21ep7xKexqf2OPWqKNIiYe0FCUDgwdHwcMiATGQsfw6dTeMAw2y2JSuHRuMofWeRhrBU8= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=GpwrH3Su; 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="GpwrH3Su" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 9586CC2BC87; Fri, 20 Mar 2026 08:58:33 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773997117; bh=6m9tkWvyIEYbQh7eeAnxli8ETjP8g2pfc5BE2dXjep8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=GpwrH3Su8Gs8xSvUZsFamsQfWniXVOcYohwIvHN/XOtCDFz7No+YjrTWXKhotBq87 F0knPum+a24xy2tCwVT/Futtpw9lxhyWJDKUh1vTYrSNDCvrb7/JxZE0WGbx6EIY+G fHSwWZQvHIIKlc8gySFkCN8vXld4n9o+RCFE2qMD5uNOINLmLYhZg5CgtKgMBdGzFr Oqk2b+MPdClPxeY9l9xitxJVyLN1KQSsPMbFEyA+/9MzqGzc4E5Rzv/do9s6i3Fug0 BlMRELkY72H1dyZT4JEdIf+ygsEuGgvHbuVs1Wp7q2wx/+6m3Jsy0/Gp6xbF7q9TZZ +nZ/tjtdgTp1A== 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 v7 1/4] ethtool: Track user-provided RSS indirection table size Date: Fri, 20 Mar 2026 09:58:21 +0100 Message-ID: <20260320085826.1957255-2-bjorn@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260320085826.1957255-1-bjorn@kernel.org> References: <20260320085826.1957255-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_lost() 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 | 3 +- .../net/ethernet/mellanox/mlx5/core/en_main.c | 21 ++++++++------ include/linux/ethtool.h | 7 +++++ include/linux/netdevice.h | 7 +---- net/ethtool/common.c | 28 +++++++++++++++++++ net/ethtool/ioctl.c | 9 +++--- net/ethtool/rss.c | 24 ++++++++++------ 7 files changed, 70 insertions(+), 29 deletions(-) diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethern= et/broadcom/bnxt/bnxt.c index 604966a398f5..84eb53b4172b 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -8118,8 +8118,7 @@ static int __bnxt_reserve_rings(struct bnxt *bp) (bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings) !=3D 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_lost(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..4429b4058daa 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -6483,12 +6483,23 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv) =20 /* max number of channels may have changed */ max_nch =3D mlx5e_calc_max_nch(priv->mdev, priv->netdev, profile); + + /* Locking is required by ethtool_rxfh_indir_lost() (sends + * ETHTOOL_MSG_RSS_NTF) and by netif_set_real_num_*_queues in case + * the netdev has been registered by this point (if this function + * was called in the reload or resume flow). + */ + if (need_lock) { + rtnl_lock(); + netdev_lock(priv->netdev); + } + if (priv->channels.params.num_channels > max_nch) { mlx5_core_warn(priv->mdev, "MLX5E: Reducing number of channels to %d\n",= max_nch); /* Reducing the number of channels - RXFH has to be reset, and * mlx5e_num_channels_changed below will build the RQT. */ - priv->netdev->priv_flags &=3D ~IFF_RXFH_CONFIGURED; + ethtool_rxfh_indir_lost(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"); @@ -6505,15 +6516,7 @@ int mlx5e_attach_netdev(struct mlx5e_priv *priv) /* 1. Set the real number of queues in the kernel the first time. * 2. Set our default XPS cpumask. * 3. Build the RQT. - * - * Locking is required by netif_set_real_num_*_queues in case the - * netdev has been registered by this point (if this function was called - * in the reload or resume flow). */ - if (need_lock) { - rtnl_lock(); - netdev_lock(priv->netdev); - } err =3D mlx5e_num_channels_changed(priv); if (need_lock) { netdev_unlock(priv->netdev); diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 656d465bcd06..34ca9261de82 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -176,6 +176,8 @@ static inline u32 ethtool_rxfh_indir_default(u32 index,= u32 n_rx_rings) * struct ethtool_rxfh_context - a custom RSS context configuration * @indir_size: Number of u32 entries in indirection table * @key_size: Size of hash key, in bytes + * @indir_user_size: number of user provided entries for the + * indirection table * @priv_size: Size of driver private data, in bytes * @hfunc: RSS hash function identifier. One of the %ETH_RSS_HASH_* * @input_xfrm: Defines how the input data is transformed. Valid values 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_lost(struct net_device *dev); =20 struct link_mode_info { int speed; @@ -1337,12 +1341,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 7ca01eb3f7d2..e82de6831e05 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1716,7 +1716,6 @@ struct net_device_ops { * @IFF_OPENVSWITCH: device is a Open vSwitch master * @IFF_L3MDEV_SLAVE: device is enslaved to an L3 master device * @IFF_TEAM: device is a team device - * @IFF_RXFH_CONFIGURED: device has had Rx Flow indirection table 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 @@ -1752,7 +1751,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, @@ -5569,10 +5567,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..d7d832fa9e00 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -1204,6 +1204,34 @@ 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_lost - Notify core that the RSS indirection table wa= s lost + * @dev: network device + * + * Drivers should call this when the device can no longer maintain the + * user-configured indirection table, typically after a HW fault recovery + * that reduced the maximum queue count. Marks the default RSS context + * indirection table as unconfigured and sends an %ETHTOOL_MSG_RSS_NTF + * notification. + */ +void ethtool_rxfh_indir_lost(struct net_device *dev) +{ + WARN_ONCE(!rtnl_is_locked() && + !lockdep_is_held_type(&dev->ethtool->rss_lock, -1), + "RSS context lock assertion failed\n"); + + netdev_err(dev, "device error, RSS indirection table lost\n"); + dev->ethtool->rss_indir_user_size =3D 0; + ethtool_rss_notify(dev, ETHTOOL_MSG_RSS_NTF, 0); +} +EXPORT_SYMBOL(ethtool_rxfh_indir_lost); + enum ethtool_link_medium ethtool_str_to_medium(const char *str) { int i; diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 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 Sat Apr 4 07:47: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 06C09349AF4; Fri, 20 Mar 2026 08:58:41 +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=1773997122; cv=none; b=tBBfpPCjp9Mdy9erMG2ZIGv6iSULzboY6Bhtnqk9Pb0CkhmQLt6n+r3bSLs/hnqnT4/A+qGu7VET8+8fGywUDNSQ1p8cYe8c/htVNXu8rsjnFrNMa1QDJKZYXYf0eWHYHqe3/V1HA36v++LlG4JYE3bvxiXBOt1bMNpX6OubU/o= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773997122; c=relaxed/simple; bh=UCZU47xUTkuidjKDjsLx518TuD1e6dVmduvG2zfO1Gg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=RVFG00y1asG1enE4wCnw1OeWGo9gIphYMtSFqpligBFKVuA/O5IPr8uUiPl8NuV9BpiPgCUdxEzgE1XKbf4ryipffeQcVhzz56LsBbSiptsyzIYDiHmK8BHc9bo2gmhXeccmaunwVwzc7GXPUc81RBNtgtOVs9l/0kgOrOENBdc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XdC1rSKM; 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="XdC1rSKM" Received: by smtp.kernel.org (Postfix) with ESMTPSA id CB61FC2BCB0; Fri, 20 Mar 2026 08:58:37 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773997121; bh=UCZU47xUTkuidjKDjsLx518TuD1e6dVmduvG2zfO1Gg=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XdC1rSKMJSnxSaVxtXpvBzgM+9VPTmQTz2sHm/KaxZAO1xUAeBxkV+ZDNVZQzylkp 0at9Q3HxdpX/9xc7SsMPp626LMLOrxHOmOkXSNjjjfCcLCGJjwvMoGZFtkdu6ZYpe0 1L3iqftAvJDiIkkZutRQ2/SPUOF3nZoRaIzR4NB2EzZIEagyCSMl0kL04QlX+D5bQz i6q17ISOZypGBaZEfVb9UTy7Ficc2gezM70jW9hsfhjuSXIdQEn1pqK7z1SK08vl7g AER9YkAa2HDXAKoNOqNGQV9uNw90pZtzKik99BfuaA1wekNxQwunRBloxrnpS/OY2l PQmRXoCDSbVIQ== 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 v7 2/4] ethtool: Add RSS indirection table resize helpers Date: Fri, 20 Mar 2026 09:58:22 +0100 Message-ID: <20260320085826.1957255-3-bjorn@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260320085826.1957255-1-bjorn@kernel.org> References: <20260320085826.1957255-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 34ca9261de82..1cb0740ba331 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_lost(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 d7d832fa9e00..ec9e45b5cc89 100644 --- a/net/ethtool/common.c +++ b/net/ethtool/common.c @@ -1232,6 +1232,161 @@ void ethtool_rxfh_indir_lost(struct net_device *dev) } EXPORT_SYMBOL(ethtool_rxfh_indir_lost); =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 Sat Apr 4 07:47: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 1F569377036; Fri, 20 Mar 2026 08:58:45 +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=1773997126; cv=none; b=gps25jq567zennPxIvTDx06rEKekN7thEPOgq0RJZNXYrZpK/5SKlREC20Bq+EzPGdbUuEqA6+KeVIefLvOSRZogWtveytiO0nIZ9/6zho55cEI1wn8BiDYQpbMeY8s4Eqr/G12+C3uG4HH1dvckY8wxrBwBM2YDR6PJDzt8UPg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773997126; c=relaxed/simple; bh=X4usbgd93DhF/Pun7LV5jczbbXgnNbjO8K4pi1eRcrc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=UCfrBTWhLqd+TLlqzKImmpkAhjRFUeqbDzc9fxNKXQD8h03icICGkvC+4u98/XqQz6Mc8jri+JOzr2PsaoN3cje1lXetcfcErhzXZX8NJovmwZ8cN60X8BwuoOJUmO44hCYHRckYFkMIUUYnbZ5QyHf2iXiT1E3umlMqiDpFmOE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=XOvIxQDc; 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="XOvIxQDc" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 0C6EDC2BCB1; Fri, 20 Mar 2026 08:58:41 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773997125; bh=X4usbgd93DhF/Pun7LV5jczbbXgnNbjO8K4pi1eRcrc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=XOvIxQDcjk+rDriEk7mJuTCOEh217TKkAyRnknVIZMQY/DkmHN0kwmnjS3BzHrWIL XXjbB8BS1CpBcO3EIy01CIDXs8aolXlLeVxlbbFIQJ2IkBbGAHXP6IewKtb/9gVDpt RKKnabDUJOO4DoYPPED8RrpOG+9xTLosGWfv7ub/EXqq1xlmesjqLvlaJQkhs4qOg+ G8nXEDxpH2oARq4f+pzOj2YIFXalcZ7Ga5MsC4YAzFOIUzynzk2KBlLay8Io7u0LVK 9L51GT7qjeaahzuEPyCMJ2B+gJvJ4etHuSvOKzadW2MeU6h+N4uybUopJVKZrkfH1h XrvEFITQPl48A== 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 v7 3/4] bnxt_en: Resize RSS contexts on channel count change Date: Fri, 20 Mar 2026 09:58:23 +0100 Message-ID: <20260320085826.1957255-4-bjorn@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260320085826.1957255-1-bjorn@kernel.org> References: <20260320085826.1957255-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 Sat Apr 4 07:47: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 59601376BFD; Fri, 20 Mar 2026 08:58:50 +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=1773997130; cv=none; b=ZHM9EfLWqHWV/kS9oN3HBs62qlg0JlzGQMkWwypHAH8r3fJhSgaVzhQg3lf2E2n2Voigf9xtaIGdOMiXBDLvKwt89HHjCJuEFOPFGJCTSWUH0HCcXJ7E88OkIVdy0OySS/twhUbxOVr1RbRfmy3OLGpILZvgiwwd9HSAB2ZmYBk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1773997130; c=relaxed/simple; bh=n31d8NYjTDg4u78LNNFct5Yi6pJSfLNaOmI/4D6Nfd8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=MROhQWoE/B/15DjKDxObCCBjU1KZwwj1kJPkbWPGZN4EcXGb0LBgqaV7oMuNOflUcryKO/GMrN+4ESvMAHZkhopfVBus2Ek+XvhWlCRELak8LvyKmVecxtrL+W1wZ2nfkwG9URl5iZjxqIj3MR1Rioza/ipotRpF4tKIFKKt3vo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=Z8dV2r4B; 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="Z8dV2r4B" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 43224C4CEF7; Fri, 20 Mar 2026 08:58:46 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1773997130; bh=n31d8NYjTDg4u78LNNFct5Yi6pJSfLNaOmI/4D6Nfd8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Z8dV2r4Boh1D1IuphrQlWfJnmQpdloXi1stlxvXmqkpYR6hPrWdzARWamdzUfnhKs YG+2Z08r1+zpyIVsOkQaoz9tB+tJCjk/L2fwF0daCQE63Wm4bRTFUUJsK1kNOEHNUq CYutHtVbRpiAa5c7HWiri+OeL2xK91WStI4k20NpWUBLolEH+K4AZv0aEZW85p92cK v2MV6UYacKg9MB4QZhVONNjF2KkAHU/p1tvGUsw4Zc3v4WAgwDGhFelBvyJg2VHSJC xFSPn4mnGQdT2FEOzGokyqvcNYfFUl5h+2Y79CYi3spDxkGNF/nuyZyPCQmNrqpVym m1VImU44ndqnw== 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 v7 4/4] selftests: rss_drv: Add RSS indirection table resize tests Date: Fri, 20 Mar 2026 09:58:24 +0100 Message-ID: <20260320085826.1957255-5-bjorn@kernel.org> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260320085826.1957255-1-bjorn@kernel.org> References: <20260320085826.1957255-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