On Fri, Sep 04, 2026 at 10:51:39AM +0000, Ciara Loftus wrote:
> Currently, ice_dev_info_get() assigns the value of `vsi->nb_qps` to both
> `max_rx_queues` and `max_tx_queues`.
>
> After a Tx scheduler hierarchy is committed, the value of nb_qps may grow
> to reflect the VSI's larger Tx scheduler capacity. However the Tx scheduler
> hierarchy change has no effect on the VSI's Rx queue allocation,
> which stays fixed at its original size for the life of the port. An
> application can then request more Rx queues than the VSI's actual Rx
> resources support when the inflated nb_qps is assigned to `max_rx_queues`.
>
> `vsi->nb_qps` also feeds `ice_vsi_disable_queues_intr()`, which clears
> both Rx and Tx queue interrupt registers by absolute queue index on
> every port stop. Once `nb_qps` grows past the VSI's fixed queue window,
> this can clear interrupt registers belonging to a different VSI.
>
> Fix this by introducing `vsi->nb_tm_qps` to track the Tx scheduler
Just a nit on naming here. For the tm block it doesn't deal with
queue-pairs so much as Tx queues, so the variable should probably be named
nb_tm_txqs instead.
However, that opens a wider renaming question - rather than tracking a
generic nb_qps and nb_tm_qps(nb_tm_txqs), might it be better to have vsi
variables separately called "nb_rxqs" and "nb_txqs" and ignore the whole
"tm" part of it?
> capacity as it grows, leaving `vsi->nb_qps` as the VSI's fixed
> queue allocation for the life of the port. `ice_vsi_disable_queues_intr()`
> is switched to `nb_used_qps`, which already tracks the actually configured
> queue count.
>
> Fixes: 715d449a965b ("net/ice: enhance Tx scheduler hierarchy support")
> Cc: [email protected]
>
> Signed-off-by: Ciara Loftus <[email protected]>
> ---
> drivers/net/intel/ice/ice_ethdev.c | 5 +++--
> drivers/net/intel/ice/ice_ethdev.h | 1 +
> drivers/net/intel/ice/ice_tm.c | 8 ++++----
> 3 files changed, 8 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/net/intel/ice/ice_ethdev.c
> b/drivers/net/intel/ice/ice_ethdev.c
> index fd148848d3c..149872816d8 100644
> --- a/drivers/net/intel/ice/ice_ethdev.c
> +++ b/drivers/net/intel/ice/ice_ethdev.c
> @@ -1816,6 +1816,7 @@ ice_setup_vsi(struct ice_pf *pf, enum ice_vsi_type type)
> ret);
> goto fail_mem;
> }
> + vsi->nb_tm_qps = vsi->nb_qps;
>
> break;
> case ICE_VSI_CTRL:
> @@ -2924,7 +2925,7 @@ ice_vsi_disable_queues_intr(struct ice_vsi *vsi)
> uint16_t msix_intr, i;
>
> /* disable interrupt and also clear all the exist config */
> - for (i = 0; i < vsi->nb_qps; i++) {
> + for (i = 0; i < vsi->nb_used_qps; i++) {
> ICE_WRITE_REG(hw, QINT_TQCTL(vsi->base_queue + i), 0);
> ICE_WRITE_REG(hw, QINT_RQCTL(vsi->base_queue + i), 0);
> rte_wmb();
> @@ -4628,7 +4629,7 @@ ice_dev_info_get(struct rte_eth_dev *dev, struct
> rte_eth_dev_info *dev_info)
> dev_info->min_rx_bufsize = ICE_BUF_SIZE_MIN;
> dev_info->max_rx_pktlen = ICE_FRAME_SIZE_MAX;
> dev_info->max_rx_queues = vsi->nb_qps;
> - dev_info->max_tx_queues = vsi->nb_qps;
> + dev_info->max_tx_queues = vsi->nb_tm_qps;
> dev_info->max_mac_addrs = vsi->max_macaddrs;
> dev_info->max_vfs = pci_dev->max_vfs;
> dev_info->max_mtu = dev_info->max_rx_pktlen - ICE_ETH_OVERHEAD;
> diff --git a/drivers/net/intel/ice/ice_ethdev.h
> b/drivers/net/intel/ice/ice_ethdev.h
> index 5914454c7c2..3160c82c5e3 100644
> --- a/drivers/net/intel/ice/ice_ethdev.h
> +++ b/drivers/net/intel/ice/ice_ethdev.h
> @@ -330,6 +330,7 @@ struct ice_vsi {
> struct ice_mac_filter_list mac_list; /* macvlan filter list */
> struct ice_vlan_filter_list vlan_list; /* vlan filter list */
> uint16_t nb_qps; /* Number of queue pairs VSI can occupy */
> + uint16_t nb_tm_qps; /* Number of Tx queues usable by the committed
> TM hierarchy */
> uint16_t nb_used_qps; /* Number of queue pairs VSI uses */
> uint16_t max_macaddrs; /* Maximum number of MAC addresses */
> uint16_t base_queue; /* The first queue index of this VSI */
> diff --git a/drivers/net/intel/ice/ice_tm.c b/drivers/net/intel/ice/ice_tm.c
> index 651d6aa932d..7afe31738ba 100644
> --- a/drivers/net/intel/ice/ice_tm.c
> +++ b/drivers/net/intel/ice/ice_tm.c
> @@ -859,9 +859,9 @@ commit_new_hierarchy(struct rte_eth_dev *dev)
> }
> /* TM hierarchy deleted. Restore default scheduler state. */
> reset_hw_node_recursive(hw,
> hw->vsi_ctx[pf->main_vsi->idx]->sched.vsi_node[0]);
> - pf->main_vsi->nb_qps = pf->lan_nb_qps;
> + pf->main_vsi->nb_tm_qps = pf->lan_nb_qps;
> pf->tm_conf.committed = false;
> - return ice_alloc_lan_q_ctx(hw, 0, 0, pf->main_vsi->nb_qps);
> + return ice_alloc_lan_q_ctx(hw, 0, 0, pf->main_vsi->nb_tm_qps);
> }
>
> /* handle case where VSI node needs to move DOWN the hierarchy */
> @@ -889,13 +889,13 @@ commit_new_hierarchy(struct rte_eth_dev *dev)
> nodes_created_per_level[i], i);
> hw->vsi_ctx[pf->main_vsi->idx]->sched.vsi_node[0] = new_vsi_root;
>
> - pf->main_vsi->nb_qps =
> + pf->main_vsi->nb_tm_qps =
> RTE_MIN(nodes_created_per_level[qg_lvl] *
> hw->max_children[qg_lvl],
> hw->layer_info[q_lvl].max_device_nodes);
>
> pf->tm_conf.committed = true; /* set flag to be checks on queue start */
>
> - return ice_alloc_lan_q_ctx(hw, 0, 0, pf->main_vsi->nb_qps);
> + return ice_alloc_lan_q_ctx(hw, 0, 0, pf->main_vsi->nb_tm_qps);
> }
>
> static int