mana_rss_table_init() overwrites the indirection table with the driver default every time the queues are rebuilt. Both rebuild paths do it: mana_alloc_qset() for the ethtool/MTU/XDP queue-set swap, and mana_alloc_queues() for ndo_open and for the TX-timeout reset.
A table the user installed with "ethtool -X" is therefore lost by operations that have nothing to do with RSS. Resizing the rings, changing the MTU, toggling a private flag, attaching an XDP program, or simply taking the port down and up again all silently reset the steering: # ethtool -X ens1 equal 1 # everything to queue 0 # ethtool -G ens1 rx 1024 # ethtool -x ens1 # back to 0..15, silently The entries are queue indices, so they stay meaningful as long as the queue count does not change, and mana_config_rss() already maps them onto whichever RX objects the new set has. Carry the table over instead of regenerating it. Only a user-configured table is preserved, which netif_is_rxfh_configured() reports: a driver-generated table must still be rebuilt so that it spreads over all the queues of the new set. ethtool_check_max_channel() refuses a channel-count reduction that would leave a user table pointing past the last queue, so the entries are in range by construction; the bounds check is a safety net for the rebuild paths that do not come from ethtool, and reports the table as lost rather than steering to a queue that is gone. Signed-off-by: Long Li <[email protected]> --- drivers/net/ethernet/microsoft/mana/mana_en.c | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microsoft/mana/mana_en.c b/drivers/net/ethernet/microsoft/mana/mana_en.c index 0d87440fbfbee7ac5729945d101eaa0c37745fbe..4cab3f658f2487671d26243d4e91f834580b3c5c 100644 --- a/drivers/net/ethernet/microsoft/mana/mana_en.c +++ b/drivers/net/ethernet/microsoft/mana/mana_en.c @@ -3545,6 +3545,38 @@ static void mana_rss_table_init(struct mana_port_context *apc) ethtool_rxfh_indir_default(i, apc->num_queues); } +/* Decide whether @apc's indirection table can be carried over to a queue set + * with @num_queues queues, instead of being rebuilt from the driver default. + * + * Only a table the user installed with "ethtool -X" is worth preserving: a + * driver-generated one has to be rebuilt so that it spreads over all the + * queues the new set actually has. + * + * ethtool_check_max_channel() already refuses a channel-count reduction that + * would leave a user-configured table pointing past the last queue, so the + * bounds check below is only a safety net for the rebuild paths that do not + * originate from ethtool. If it ever trips, the table cannot be honoured for + * the new queue count, so tell the core the user's table is gone rather than + * silently steering to queues that no longer exist. + */ +static bool mana_rss_table_keep(struct mana_port_context *apc, + unsigned int num_queues) +{ + u32 i; + + if (!netif_is_rxfh_configured(apc->ndev)) + return false; + + for (i = 0; i < apc->indir_table_sz; i++) { + if (apc->indir_table[i] >= num_queues) { + ethtool_rxfh_indir_lost(apc->ndev); + return false; + } + } + + return true; +} + int mana_disable_vport_rx(struct mana_port_context *apc) { return mana_cfg_vport_steering(apc, TRI_STATE_FALSE, false, false, @@ -3860,7 +3892,12 @@ int mana_alloc_queues(struct net_device *ndev) goto destroy_rxq; } - mana_rss_table_init(apc); + /* Keep a user-configured RSS table across a rebuild; the entries are + * queue indices, so they stay meaningful as long as the queue count + * is unchanged. Only a driver-generated table is regenerated here. + */ + if (!mana_rss_table_keep(apc, apc->num_queues)) + mana_rss_table_init(apc); err = mana_config_rss(apc, TRI_STATE_TRUE, true, true); if (err) { @@ -4317,7 +4354,16 @@ int mana_alloc_qset(struct mana_port_context *apc, if (err) goto cleanup_rxq; - mana_rss_table_init(scratch); + /* Carry a user-configured RSS table over to the new set. The entries + * are queue indices, so mana_config_rss() in mana_publish_qset() maps + * them onto the new set's RX objects. A driver-generated table is + * rebuilt instead, so it covers every queue of the new set. + */ + if (mana_rss_table_keep(apc, num_queues)) + memcpy(scratch->indir_table, apc->indir_table, + apc->indir_table_sz * sizeof(*apc->indir_table)); + else + mana_rss_table_init(scratch); mana_qset_snapshot(scratch, out); return 0; -- 2.43.0

