Hi, BTW, we have mlx5_tx_burst_mode_get(), all information about tx rate limit could be shown there. (I do not insist on, JFYI).
> -----Original Message----- > From: Vincent Jardin <[email protected]> > Sent: Friday, March 13, 2026 12:01 AM > To: [email protected] > Cc: Raslan Darawsheh <[email protected]>; NBU-Contact-Thomas Monjalon > (EXTERNAL) <[email protected]>; [email protected]; > Dariusz Sosnowski <[email protected]>; Slava Ovsiienko > <[email protected]>; Bing Zhao <[email protected]>; Ori Kam > <[email protected]>; Suanming Mou <[email protected]>; Matan Azrad > <[email protected]>; [email protected]; Vincent Jardin > <[email protected]> > Subject: [PATCH v3 07/9] net/mlx5: add testpmd command to query per-queue > rate limit > > Add a new testpmd command to display the per-queue packet pacing rate limit > state, including the PP index from both driver state and FW SQ context > readback: > > testpmd> mlx5 port <port_id> txq <queue_id> rate show > > This helps verify that the FW actually applied the PP index to the SQ after > setting > a per-queue rate limit. > > Expose a new PMD API rte_pmd_mlx5_txq_rate_limit_query() that queries > txq_ctrl->rl for driver state and mlx5_devx_cmd_query_sq() for the FW > packet_pacing_rate_limit_index field. > > Signed-off-by: Vincent Jardin <[email protected]> > --- > drivers/net/mlx5/mlx5_testpmd.c | 93 > +++++++++++++++++++++++++++++++++ > drivers/net/mlx5/mlx5_tx.c | 40 +++++++++++++- > drivers/net/mlx5/mlx5_txq.c | 19 +++++-- > drivers/net/mlx5/rte_pmd_mlx5.h | 30 +++++++++++ > 4 files changed, 178 insertions(+), 4 deletions(-) > > diff --git a/drivers/net/mlx5/mlx5_testpmd.c > b/drivers/net/mlx5/mlx5_testpmd.c index 1bb5a89559..fd3efecc5d 100644 > --- a/drivers/net/mlx5/mlx5_testpmd.c > +++ b/drivers/net/mlx5/mlx5_testpmd.c > @@ -1365,6 +1365,94 @@ cmdline_parse_inst_t > mlx5_cmd_dump_rq_context_options = { > } > }; > > +/* Show per-queue rate limit PP index for a given port/queue */ struct > +mlx5_cmd_show_rate_limit_options { > + cmdline_fixed_string_t mlx5; > + cmdline_fixed_string_t port; > + portid_t port_id; > + cmdline_fixed_string_t txq; > + queueid_t queue_id; > + cmdline_fixed_string_t rate; > + cmdline_fixed_string_t show; > +}; > + > +cmdline_parse_token_string_t mlx5_cmd_show_rate_limit_mlx5 = > + TOKEN_STRING_INITIALIZER(struct > mlx5_cmd_show_rate_limit_options, > + mlx5, "mlx5"); > +cmdline_parse_token_string_t mlx5_cmd_show_rate_limit_port = > + TOKEN_STRING_INITIALIZER(struct > mlx5_cmd_show_rate_limit_options, > + port, "port"); > +cmdline_parse_token_num_t mlx5_cmd_show_rate_limit_port_id = > + TOKEN_NUM_INITIALIZER(struct mlx5_cmd_show_rate_limit_options, > + port_id, RTE_UINT16); > +cmdline_parse_token_string_t mlx5_cmd_show_rate_limit_txq = > + TOKEN_STRING_INITIALIZER(struct > mlx5_cmd_show_rate_limit_options, > + txq, "txq"); > +cmdline_parse_token_num_t mlx5_cmd_show_rate_limit_queue_id = > + TOKEN_NUM_INITIALIZER(struct mlx5_cmd_show_rate_limit_options, > + queue_id, RTE_UINT16); > +cmdline_parse_token_string_t mlx5_cmd_show_rate_limit_rate = > + TOKEN_STRING_INITIALIZER(struct > mlx5_cmd_show_rate_limit_options, > + rate, "rate"); > +cmdline_parse_token_string_t mlx5_cmd_show_rate_limit_show = > + TOKEN_STRING_INITIALIZER(struct > mlx5_cmd_show_rate_limit_options, > + show, "show"); > + > +static void > +mlx5_cmd_show_rate_limit_parsed(void *parsed_result, > + __rte_unused struct cmdline *cl, > + __rte_unused void *data) > +{ > + struct mlx5_cmd_show_rate_limit_options *res = parsed_result; > + struct rte_pmd_mlx5_txq_rate_limit_info info; > + int ret; > + > + ret = rte_pmd_mlx5_txq_rate_limit_query(res->port_id, res- > >queue_id, > + &info); > + switch (ret) { > + case 0: > + break; > + case -ENODEV: > + fprintf(stderr, "invalid port_id %u\n", res->port_id); > + return; > + case -EINVAL: > + fprintf(stderr, "invalid queue index (%u), out of range\n", > + res->queue_id); > + return; > + case -EIO: > + fprintf(stderr, "failed to query SQ context\n"); > + return; > + default: > + fprintf(stderr, "query failed (%d)\n", ret); > + return; > + } > + fprintf(stdout, "Port %u Txq %u rate limit info:\n", > + res->port_id, res->queue_id); > + if (info.rate_mbps > 0) > + fprintf(stdout, " Configured rate: %u Mbps\n", > + info.rate_mbps); > + else > + fprintf(stdout, " Configured rate: disabled\n"); > + fprintf(stdout, " PP index (driver): %u\n", info.pp_index); > + fprintf(stdout, " PP index (FW readback): %u\n", info.fw_pp_index); } > + > +cmdline_parse_inst_t mlx5_cmd_show_rate_limit = { > + .f = mlx5_cmd_show_rate_limit_parsed, > + .data = NULL, > + .help_str = "mlx5 port <port_id> txq <queue_id> rate show", > + .tokens = { > + (void *)&mlx5_cmd_show_rate_limit_mlx5, > + (void *)&mlx5_cmd_show_rate_limit_port, > + (void *)&mlx5_cmd_show_rate_limit_port_id, > + (void *)&mlx5_cmd_show_rate_limit_txq, > + (void *)&mlx5_cmd_show_rate_limit_queue_id, > + (void *)&mlx5_cmd_show_rate_limit_rate, > + (void *)&mlx5_cmd_show_rate_limit_show, > + NULL, > + } > +}; > + > static struct testpmd_driver_commands mlx5_driver_cmds = { > .commands = { > { > @@ -1440,6 +1528,11 @@ static struct testpmd_driver_commands > mlx5_driver_cmds = { > .help = "mlx5 port (port_id) queue (queue_id) dump > rq_context (file_name)\n" > " Dump mlx5 RQ Context\n\n", > }, > + { > + .ctx = &mlx5_cmd_show_rate_limit, > + .help = "mlx5 port (port_id) txq (queue_id) rate > show\n" > + " Show per-queue rate limit PP index\n\n", > + }, > { > .ctx = NULL, > }, > diff --git a/drivers/net/mlx5/mlx5_tx.c b/drivers/net/mlx5/mlx5_tx.c index > 8085b5c306..fa57d3ef98 100644 > --- a/drivers/net/mlx5/mlx5_tx.c > +++ b/drivers/net/mlx5/mlx5_tx.c > @@ -800,7 +800,7 @@ int rte_pmd_mlx5_txq_dump_contexts(uint16_t > port_id, uint16_t queue_id, const ch > if (!rte_eth_dev_is_valid_port(port_id)) > return -ENODEV; > > - if (rte_eth_tx_queue_is_valid(port_id, queue_id)) > + if (rte_eth_tx_queue_is_valid(port_id, queue_id) != 0) > return -EINVAL; > > fd = fopen(path, "w"); > @@ -848,3 +848,41 @@ int rte_pmd_mlx5_txq_dump_contexts(uint16_t > port_id, uint16_t queue_id, const ch > fclose(fd); > return ret; > } > + > +RTE_EXPORT_EXPERIMENTAL_SYMBOL(rte_pmd_mlx5_txq_rate_limit_query, > +26.07) int rte_pmd_mlx5_txq_rate_limit_query(uint16_t port_id, uint16_t > queue_id, > + struct rte_pmd_mlx5_txq_rate_limit_info > *info) { > + struct rte_eth_dev *dev; > + struct mlx5_priv *priv; > + struct mlx5_txq_data *txq_data; > + struct mlx5_txq_ctrl *txq_ctrl; > + uint32_t sq_out[MLX5_ST_SZ_DW(query_sq_out)] = {0}; > + int ret; > + > + if (info == NULL) > + return -EINVAL; > + if (!rte_eth_dev_is_valid_port(port_id)) > + return -ENODEV; > + if (rte_eth_tx_queue_is_valid(port_id, queue_id) != 0) > + return -EINVAL; > + dev = &rte_eth_devices[port_id]; > + priv = dev->data->dev_private; > + txq_data = (*priv->txqs)[queue_id]; > + txq_ctrl = container_of(txq_data, struct mlx5_txq_ctrl, txq); > + info->rate_mbps = txq_ctrl->rl.rate_mbps; > + info->pp_index = txq_ctrl->rl.pp_id; > + if (txq_ctrl->obj == NULL) { > + info->fw_pp_index = 0; > + return 0; > + } > + ret = mlx5_devx_cmd_query_sq(txq_ctrl->obj->sq_obj.sq, > + sq_out, sizeof(sq_out)); > + if (ret) > + return -EIO; > + info->fw_pp_index = MLX5_GET(sqc, > + MLX5_ADDR_OF(query_sq_out, sq_out, > + sq_context), > + packet_pacing_rate_limit_index); > + return 0; > +} > diff --git a/drivers/net/mlx5/mlx5_txq.c b/drivers/net/mlx5/mlx5_txq.c index > f2ed2454a0..f0881f3560 100644 > --- a/drivers/net/mlx5/mlx5_txq.c > +++ b/drivers/net/mlx5/mlx5_txq.c > @@ -1406,7 +1406,20 @@ mlx5_set_queue_rate_limit(struct rte_eth_dev > *dev, uint16_t queue_idx, > rte_errno = EINVAL; > return -rte_errno; > } > - if (txq_ctrl->obj == NULL || txq_ctrl->obj->sq == NULL) { Should this code be in the commit introducing mlx5_set_queue_rate_limit() ? > + if (txq_ctrl->obj == NULL) { > + DRV_LOG(ERR, "Port %u Tx queue %u not initialized.", > + dev->data->port_id, queue_idx); > + rte_errno = EINVAL; > + return -rte_errno; > + } > + /* > + * For non-hairpin queues the SQ DevX object lives in > + * obj->sq_obj.sq (used by DevX/HWS mode), while hairpin > + * queues use obj->sq directly. These are different members > + * of a union inside mlx5_txq_obj. > + */ > + struct mlx5_devx_obj *sq_devx = txq_ctrl->obj->sq_obj.sq; Style nit - could you please mode variable declaration to the {-block beginning ? > + if (sq_devx == NULL) { > DRV_LOG(ERR, "Port %u Tx queue %u SQ not ready.", > dev->data->port_id, queue_idx); > rte_errno = EINVAL; > @@ -1420,7 +1433,7 @@ mlx5_set_queue_rate_limit(struct rte_eth_dev > *dev, uint16_t queue_idx, > sq_attr.state = MLX5_SQC_STATE_RDY; > sq_attr.rl_update = 1; > sq_attr.packet_pacing_rate_limit_index = 0; > - ret = mlx5_devx_cmd_modify_sq(txq_ctrl->obj->sq, &sq_attr); > + ret = mlx5_devx_cmd_modify_sq(sq_devx, &sq_attr); > if (ret) { > DRV_LOG(ERR, > "Port %u Tx queue %u failed to clear rate.", > @@ -1444,7 +1457,7 @@ mlx5_set_queue_rate_limit(struct rte_eth_dev > *dev, uint16_t queue_idx, > sq_attr.state = MLX5_SQC_STATE_RDY; > sq_attr.rl_update = 1; > sq_attr.packet_pacing_rate_limit_index = new_rl.pp_id; > - ret = mlx5_devx_cmd_modify_sq(txq_ctrl->obj->sq, &sq_attr); > + ret = mlx5_devx_cmd_modify_sq(sq_devx, &sq_attr); > if (ret) { > DRV_LOG(ERR, "Port %u Tx queue %u failed to set rate %u > Mbps.", > dev->data->port_id, queue_idx, tx_rate); diff --git > a/drivers/net/mlx5/rte_pmd_mlx5.h b/drivers/net/mlx5/rte_pmd_mlx5.h index > 7acfdae97d..698d7d2032 100644 > --- a/drivers/net/mlx5/rte_pmd_mlx5.h > +++ b/drivers/net/mlx5/rte_pmd_mlx5.h > @@ -420,6 +420,36 @@ __rte_experimental > int > rte_pmd_mlx5_txq_dump_contexts(uint16_t port_id, uint16_t queue_id, > const char *filename); > > +/** > + * Per-queue rate limit information. > + */ > +struct rte_pmd_mlx5_txq_rate_limit_info { > + uint32_t rate_mbps; /**< Configured rate in Mbps, 0 = disabled. */ > + uint16_t pp_index; /**< PP index from driver state. */ > + uint16_t fw_pp_index; /**< PP index read back from FW SQ context. > */ > +}; > + > +/** > + * Query per-queue rate limit state for a given Tx queue. > + * > + * @param[in] port_id > + * Port ID. > + * @param[in] queue_id > + * Tx queue ID. > + * @param[out] info > + * Rate limit information. > + * > + * @return > + * 0 on success, negative errno on failure: > + * - -ENODEV: invalid port_id. > + * - -EINVAL: invalid queue_id. > + * - -EIO: FW query failed. > + */ > +__rte_experimental > +int > +rte_pmd_mlx5_txq_rate_limit_query(uint16_t port_id, uint16_t queue_id, > + struct rte_pmd_mlx5_txq_rate_limit_info > *info); > + > /** Type of mlx5 driver event for which custom callback is called. */ enum > rte_pmd_mlx5_driver_event_cb_type { > /** Called after HW Rx queue is created. */ > -- > 2.43.0 With best regards, Slava

