NVIDIA BlueField-2 Host to Arm Flow Control
This new capability enables NVIDIA BlueField-2 Host to Arm Flow Control by using an RX queue available descriptors threshold and host shaper.
For more information on how they interact to throttle Bluefield-2 host to Arm traffic, please refer to https://doc.dpdk.org/guides/nics/mlx5.html
In mlx5 PMD, LWM is interchangeable with the available descriptors threshold.
To set port 1 rxq 0 available descriptors threshold to 30% of RX queue size:
testpmd> set port 1 rxq 0 avail_thresh 30
To disable available descriptors threshold on port 1 rxq 0:
testpmd> set port 1 rxq 0 avail_thresh 0
To remove the current host shaper and enable available descriptors threshold triggered mode on port 1:
testpmd> mlx5 set port 1 host_shaper avail_thresh_triggered 1 rate 0
To remove the current host shaper and disable available descriptors threshold triggered mode on port 1:
testpmd> mlx5 set port 1 host_shaper avail_thresh_triggered 0 rate 0
To configure an immediate host shaper rate of 1Gbps and disable available descriptors threshold triggered mode on port 1 (note the rate unit is 100Mbps):
testpmd> mlx5 set port 1 host_shaper avail_thresh_triggered 0 rate 10
/* Enable host shaper avail-thresh-triggered mode. */
rte_pmd_mlx5_config_host_shaper(port_id, 1,
RTE_BIT32(MLX5_HOST_SHAPER_FLAG_AVAIL_THRESH_TRIGGERED));
/* Disable host shaper avail-thresh-triggered mode and set an immediate rate. */
rte_pmd_mlx5_config_host_shaper(port_id, rate, 0);
static uint8_t host_shaper_avail_thresh_triggered[RTE_MAX_ETHPORTS];
#define SHAPER_DISABLE_DELAY_US 100000 /* 100ms */
/* The delay handler to re-arm avail-thresh event and disable host shaper */
static void
mlx5_test_host_shaper_disable(void *args)
{
uint32_t port_rxq_id = (uint32_t)(uintptr_t)args;
uint16_t port_id = port_rxq_id & 0xffff;
uint16_t qid = (port_rxq_id >> 16) & 0xffff;
struct rte_eth_rxq_info qinfo;
printf("%s disable shaper\n", __func__);
if (rte_eth_rx_queue_info_get(port_id, qid, &qinfo)) {
printf("rx_queue_info_get returns error\n");
return;
}
/* Rearm the available descriptor threshold event. */
if (rte_eth_rx_avail_thresh_set(port_id, qid, qinfo.avail_thresh)) {
printf("config avail_thresh returns error\n");
return;
}
/* Only disable the shaper when avail_thresh_triggered is set. */
if (host_shaper_avail_thresh_triggered[port_id] &&
rte_pmd_mlx5_host_shaper_config(port_id, 0, 0))
printf("%s disable shaper returns error\n", __func__);
}
void
mlx5_test_avail_thresh_event_handler(uint16_t port_id, uint16_t rxq_id)
{
struct rte_eth_dev_info dev_info;
uint32_t port_rxq_id = port_id | (rxq_id << 16);
/* Ensure it's MLX5 port. */
if (rte_eth_dev_info_get(port_id, &dev_info) != 0 ||
(strncmp(dev_info.driver_name, "mlx5", 4) != 0))
return;
rte_eal_alarm_set(SHAPER_DISABLE_DELAY_US,
mlx5_test_host_shaper_disable,
(void *)(uintptr_t)port_rxq_id);
printf("%s port_id:%u rxq_id:%u\n", __func__, port_id, rxq_id);
}
/* The avail_thresh event handler */
static int
eth_event_callback(portid_t port_id, enum rte_eth_event_type type, void *param,
void *ret_param)
{
uint16_t rxq_id;
int ret;
if (type == RTE_ETH_EVENT_RX_AVAIL_THRESH) {
/* avail_thresh query API rewinds rxq_id, no need to check max RxQ num */
for (rxq_id = 0; ; rxq_id++) {
ret = rte_eth_rx_avail_thresh_query(port_id, &rxq_id,
NULL);
if (ret <= 0)
break;
printf("Received avail_thresh event, port: %u, rxq_id: %u\n",
port_id, rxq_id);
mlx5_test_avail_thresh_event_handler(port_id, rxq_id);
}
}
}
/* Configure avail_thresh to 30% of RX queue. */
rte_eth_rx_avail_thresh_set(port_id, qid, 30);
/* Register avail_thresh event handler. */
rte_eth_dev_callback_register(port_id, RTE_ETH_EVENT_RX_AVAIL_THRESH,
eth_event_callback, void *cb_arg)