Inter-revision diff: patch 2

Comparing v5 (message) to v7 (message)

--- v5
+++ v7
@@ -1,99 +1,212 @@
-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 the device is closed; actual resize is deferred until after
-bnxt_close_nic():
-
- 1. ethtool_rxfh_can_resize() checks context 0, passing
-    dev->ethtool->rss_indir_user_size as the user_size floor.
- 2. ethtool_rxfh_ctxs_can_resize() validates all non-default contexts.
- 3. After bnxt_close_nic(), ethtool_rxfh_resize() applies context 0
-    changes, and ethtool_rxfh_ctxs_resize() resizes non-default
-    contexts.
-
-RSS table size only changes on P5 chips with older firmware; newer
-firmware always uses the largest table size.
-
-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().
+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örn Töpel <bjorn@kernel.org>
 ---
- .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 38 +++++++++++++++----
- 1 file changed, 31 insertions(+), 7 deletions(-)
-
-diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c
-index 48e8e3be70d3..469f4720c1d7 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,
+ 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 ethtool_rxfh_context *ctx)
+ 
+ void ethtool_rxfh_context_lost(struct net_device *dev, u32 context_id);
+ void ethtool_rxfh_indir_lost(struct net_device *dev);
++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_size);
++void ethtool_rxfh_ctxs_resize(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 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);
+ 
++static bool ethtool_rxfh_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;
++}
++
++static bool ethtool_rxfh_can_resize(const u32 *tbl, u32 old_size, u32 new_size,
++				    u32 user_size)
++{
++	if (new_size == 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 = old_size; i < new_size; i++)
++		tbl[i] = tbl[i % old_size];
++}
++
++/**
++ * ethtool_rxfh_indir_can_resize - Check if context 0 indir table can resize
++ * @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) entries)
++ * @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_size)
++{
++	struct ethtool_rxfh_context *ctx;
++	unsigned long context;
++	int ret = 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 = ethtool_rxfh_context_indir(ctx);
++
++		if (!ethtool_rxfh_can_resize(indir, ctx->indir_size,
++					     new_indir_size,
++					     ctx->indir_user_size)) {
++			ret = -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 = 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)
  {
- 	struct bnxt *bp = netdev_priv(dev);
- 	int req_tx_rings, req_rx_rings, tcs;
-+	u32 new_tbl_size = 0, old_tbl_size;
- 	bool sh = false;
- 	int tx_xdp = 0;
- 	int rc = 0;
-@@ -977,19 +978,34 @@ 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) || 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 = 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;
- 	}
- 
-+	/* 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) !=
-+	    bnxt_get_nr_rss_ctxs(bp, bp->rx_nr_rings)) {
-+		new_tbl_size = bnxt_get_nr_rss_ctxs(bp, req_rx_rings) *
-+			       BNXT_RSS_TABLE_ENTRIES_P5;
-+		old_tbl_size = bnxt_get_rxfh_indir_size(dev);
-+
-+		if (netif_is_rxfh_configured(dev) &&
-+		    !ethtool_rxfh_can_resize(bp->rss_indir_tbl,
-+					old_tbl_size, new_tbl_size,
-+					dev->ethtool->rss_indir_user_size)) {
-+			netdev_warn(dev, "RSS table resize not possible\n");
-+			return -EINVAL;
-+		}
-+
-+		rc = 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 +1015,14 @@ static int bnxt_set_channels(struct net_device *dev,
- 		bnxt_close_nic(bp, true, false);
- 	}
- 
-+	if (new_tbl_size) {
-+		if (netif_is_rxfh_configured(dev))
-+			ethtool_rxfh_resize(bp->rss_indir_tbl,
-+					    old_tbl_size, new_tbl_size,
-+					    dev->ethtool->rss_indir_user_size);
-+		ethtool_rxfh_ctxs_resize(dev, new_tbl_size);
-+	}
-+
- 	if (sh) {
- 		bp->flags |= BNXT_FLAG_SHARED_RINGS;
- 		bp->rx_nr_rings = channel->combined_count;
+ 	int i;
 -- 
 2.53.0
 
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help