The Tx rate can be limited per queue with ethdev operation ``rte_eth_set_queue_rate_limit()`` and can be read through ``rte_eth_get_queue_rate_limit()``.
This feature uses the hardware packet pacing mechanism to enforce a data rate on individual Tx queues without tearing down the queue. The rate is specified in Mbps. ice_set_queue_rate_limit() applies the requested rate as the EIR (maximum bandwidth) limit of the queue scheduler node using ice_cfg_q_bw_lmt(), converting the Mbps value taken by the API to the Kbps expected by the scheduler. A rate of 0 removes the limit and restores the default bandwidth via ice_cfg_q_bw_dflt_lmt(). ice_get_queue_rate_limit() reads back the value cached in the queue context by the scheduler on a successful set, and reports 0 when the queue runs unlimited. Signed-off-by: Anurag Mandal <[email protected]> --- doc/guides/nics/features/ice.ini | 1 + doc/guides/rel_notes/release_26_11.rst | 3 + drivers/net/intel/ice/ice_ethdev.c | 77 ++++++++++++++++++++++++++ 3 files changed, 81 insertions(+) diff --git a/doc/guides/nics/features/ice.ini b/doc/guides/nics/features/ice.ini index 893d09e9ec..6b21d56144 100644 --- a/doc/guides/nics/features/ice.ini +++ b/doc/guides/nics/features/ice.ini @@ -30,6 +30,7 @@ RSS hash = Y RSS key update = Y RSS reta update = Y VLAN filter = Y +Rate limitation = Y Traffic manager = Y CRC offload = Y VLAN offload = Y diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst index 907f9013ff..1567f93563 100644 --- a/doc/guides/rel_notes/release_26_11.rst +++ b/doc/guides/rel_notes/release_26_11.rst @@ -64,6 +64,9 @@ New Features * Renamed the ``enable_ptype_lldp`` devarg to ``enable_lldp``. The old name is no longer accepted. +* **Updated Intel ice driver.** + + * Added support for Tx rate limiting per queue. Removed Items ------------- diff --git a/drivers/net/intel/ice/ice_ethdev.c b/drivers/net/intel/ice/ice_ethdev.c index 76b8ff0a72..8ae2842098 100644 --- a/drivers/net/intel/ice/ice_ethdev.c +++ b/drivers/net/intel/ice/ice_ethdev.c @@ -212,6 +212,10 @@ static const uint32_t *ice_buffer_split_supported_hdr_ptypes_get(struct rte_eth_ size_t *no_of_elements); static int ice_get_dcb_info(struct rte_eth_dev *dev, struct rte_eth_dcb_info *dcb_info); static int ice_priority_flow_ctrl_set(struct rte_eth_dev *dev, struct rte_eth_pfc_conf *pfc_conf); +static int ice_set_queue_rate_limit(struct rte_eth_dev *dev, uint16_t queue_idx, + uint32_t tx_rate); +static int ice_get_queue_rate_limit(struct rte_eth_dev *dev, uint16_t queue_idx, + uint32_t *tx_rate); static const struct rte_pci_id pci_id_ice_map[] = { { RTE_PCI_DEVICE(ICE_INTEL_VENDOR_ID, ICE_DEV_ID_E823L_BACKPLANE) }, @@ -353,6 +357,8 @@ static const struct eth_dev_ops ice_eth_dev_ops = { .buffer_split_supported_hdr_ptypes_get = ice_buffer_split_supported_hdr_ptypes_get, .get_dcb_info = ice_get_dcb_info, .priority_flow_ctrl_set = ice_priority_flow_ctrl_set, + .set_queue_rate_limit = ice_set_queue_rate_limit, + .get_queue_rate_limit = ice_get_queue_rate_limit, }; /* store statistics names and its offset in stats structure */ @@ -4205,6 +4211,77 @@ ice_priority_flow_ctrl_set(struct rte_eth_dev *dev, struct rte_eth_pfc_conf *pfc return 0; } +static int +ice_set_queue_rate_limit(struct rte_eth_dev *dev, uint16_t queue_idx, + uint32_t tx_rate) +{ + struct ice_pf *pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private); + struct ice_hw *hw = ICE_PF_TO_HW(pf); + struct ice_vsi *vsi = pf->main_vsi; + int ret; + + if (queue_idx >= dev->data->nb_tx_queues) { + PMD_DRV_LOG(ERR, "Tx queue %u is out of range (%u configured)", + queue_idx, dev->data->nb_tx_queues); + return -EINVAL; + } + + /* The scheduler node of a Tx queue only exists once the queue has been + * added to the Tx scheduler tree, which happens on queue start. + */ + if (dev->data->tx_queue_state[queue_idx] != RTE_ETH_QUEUE_STATE_STARTED) { + PMD_DRV_LOG(ERR, "Tx queue %u must be started before setting its rate limit", + queue_idx); + return -EINVAL; + } + + /* Rate is expressed in Mbps by the API, the scheduler uses Kbps. */ + if (tx_rate > ICE_SCHED_MAX_BW / 1000) { + PMD_DRV_LOG(ERR, "Invalid Tx rate %u Mbps for queue %u, maximum is %u Mbps", + tx_rate, queue_idx, (uint32_t)(ICE_SCHED_MAX_BW / 1000)); + return -EINVAL; + } + + /* A rate of 0 removes the limit and restores the default bandwidth. */ + if (tx_rate == 0) + ret = ice_cfg_q_bw_dflt_lmt(hw->port_info, vsi->idx, 0, + queue_idx, ICE_MAX_BW); + else + ret = ice_cfg_q_bw_lmt(hw->port_info, vsi->idx, 0, queue_idx, + ICE_MAX_BW, tx_rate * 1000); + if (ret) { + PMD_DRV_LOG(ERR, "Failed to set Tx rate limit on queue %u, error %d", + queue_idx, ret); + return -EIO; + } + + return 0; +} + +static int +ice_get_queue_rate_limit(struct rte_eth_dev *dev, uint16_t queue_idx, + uint32_t *tx_rate) +{ + struct ice_pf *pf = ICE_DEV_PRIVATE_TO_PF(dev->data->dev_private); + struct ice_hw *hw = ICE_PF_TO_HW(pf); + struct ice_vsi *vsi = pf->main_vsi; + struct ice_q_ctx *q_ctx; + + q_ctx = ice_get_lan_q_ctx(hw, vsi->idx, 0, queue_idx); + if (q_ctx == NULL) { + PMD_DRV_LOG(ERR, "Failed to get the context of Tx queue %u", + queue_idx); + return -EINVAL; + } + + /* The scheduler caches the EIR limit in Kbps, and stores 0 when the + * queue runs at the default (unlimited) bandwidth. + */ + *tx_rate = q_ctx->bw_t_info.eir_bw.bw / 1000; + + return 0; +} + static void __vsi_queues_bind_intr(struct ice_vsi *vsi, uint16_t msix_vect, int base_queue, int nb_queue) -- 2.34.1

