From: Jie Liu <[email protected]> This patch implements the traffic management ops for example PMD. It supports a 4-level hierarchy: port, vsi, queue group and queue.
- Support node add/delete and hierarchy commit. - Support private shaper and rate limiting on each node. The hardware requires all nodes to be configured before the hierarchy is committed to the global registers. Signed-off-by: Jie Liu <[email protected]> --- drivers/net/sxe2/meson.build | 3 +- drivers/net/sxe2/sxe2_cmd_chnl.c | 163 +++++ drivers/net/sxe2/sxe2_cmd_chnl.h | 6 + drivers/net/sxe2/sxe2_drv_cmd.h | 24 + drivers/net/sxe2/sxe2_ethdev.c | 82 +++ drivers/net/sxe2/sxe2_ethdev.h | 6 + drivers/net/sxe2/sxe2_tm.c | 1151 ++++++++++++++++++++++++++++++ drivers/net/sxe2/sxe2_tm.h | 76 ++ drivers/net/sxe2/sxe2_tx.c | 1 - 9 files changed, 1510 insertions(+), 2 deletions(-) create mode 100644 drivers/net/sxe2/sxe2_tm.c create mode 100644 drivers/net/sxe2/sxe2_tm.h diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build index dfd31bfc97..d0aa7fecf0 100644 --- a/drivers/net/sxe2/meson.build +++ b/drivers/net/sxe2/meson.build @@ -63,6 +63,7 @@ sources += files( 'sxe2_mac.c', 'sxe2_filter.c', 'sxe2_rss.c', + 'sxe2_tm.c', ) -allow_internal_get_api = true +allow_internal_get_api = true \ No newline at end of file diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.c b/drivers/net/sxe2/sxe2_cmd_chnl.c index b997e7b044..19323ffcc4 100644 --- a/drivers/net/sxe2/sxe2_cmd_chnl.c +++ b/drivers/net/sxe2/sxe2_cmd_chnl.c @@ -230,6 +230,7 @@ static void sxe2_txq_ctxt_cfg_fill(struct sxe2_tx_queue *txq, struct sxe2_drv_txq_cfg_req *req, uint16_t txq_cnt) { + struct sxe2_adapter *adapter = txq->vsi->adapter; struct sxe2_drv_txq_ctxt *ctxt = req->cfg; uint16_t q_idx = 0; @@ -241,6 +242,8 @@ static void sxe2_txq_ctxt_cfg_fill(struct sxe2_tx_queue *txq, ctxt->depth = txq[q_idx].ring_depth; ctxt->dma_addr = txq[q_idx].base_addr; ctxt->queue_id = txq[q_idx].queue_id; + + ctxt->sched_mode = sxe2_sched_mode_get(adapter); } } @@ -310,6 +313,7 @@ int32_t sxe2_drv_txq_switch(struct sxe2_adapter *adapter, struct sxe2_tx_queue * req.q_idx = txq->queue_id; req.is_enable = (uint8_t)enable; + req.sched_mode = sxe2_sched_mode_get(adapter); sxe2_drv_cmd_params_fill(adapter, ¶m, SXE2_DRV_CMD_TXQ_DISABLE, &req, sizeof(req), NULL, 0); @@ -714,3 +718,162 @@ int32_t sxe2_drv_ptp_gettime(struct sxe2_adapter *adapter, struct sxe2_rx_queue (void)rxq; return 0; } + +int32_t sxe2_drv_root_tree_alloc(struct rte_eth_dev *dev) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt; + struct sxe2_common_device *cdev = adapter->cdev; + struct sxe2_drv_cmd_params param = {0}; + struct sxe2_tm_res tm_resp; + int32_t ret; + + sxe2_drv_cmd_params_fill(adapter, ¶m, SXE2_DRV_CMD_SCHED_ROOT_TREE_ALLOC, + NULL, 0, + &tm_resp, sizeof(tm_resp)); + ret = sxe2_drv_cmd_exec(cdev, ¶m); + if (ret) { + PMD_LOG_ERR(DRV, "add sched root failed, ret:%d", ret); + goto l_end; + } + + tm_ctxt->root_teid = tm_resp.teid; + +l_end: + return ret; +} + +int32_t sxe2_drv_root_tree_release(struct rte_eth_dev *dev) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + struct sxe2_common_device *cdev = adapter->cdev; + struct sxe2_drv_cmd_params param = {0}; + struct sxe2_tm_res tm_res = {0}; + int32_t ret; + + tm_res.teid = adapter->tm_ctxt.root_teid; + + sxe2_drv_cmd_params_fill(adapter, ¶m, SXE2_DRV_CMD_SCHED_ROOT_TREE_RELEASE, + &tm_res, sizeof(tm_res), + NULL, 0); + ret = sxe2_drv_cmd_exec(cdev, ¶m); + if (ret) { + PMD_LOG_ERR(DRV, "release sched root failed, ret:%d", ret); + goto l_end; + } + +l_end: + return ret; +} + +static void sxe2_drv_tm_node_to_info(struct sxe2_adapter *adapter, + struct sxe2_tm_node *node, struct sxe2_tm_info *info) +{ + uint32_t rate = 0; + + if (node->shaper_profile->profile.committed.rate == UINT64_MAX) + rate = UINT32_MAX; + else + rate = (uint32_t)(node->shaper_profile->profile.committed.rate * 8 / 1000); + + info->committed = rte_cpu_to_le_32(rate); + + if (node->shaper_profile->profile.peak.rate == UINT64_MAX) + rate = UINT32_MAX; + else + rate = (uint32_t)(node->shaper_profile->profile.peak.rate * 8 / 1000); + + info->peak = rte_cpu_to_le_32(rate); + + info->priority = (adapter->tm_ctxt.prio_max - 1 - node->priority); + + info->weight = rte_cpu_to_le_16(node->hw_weight); +} + +static int32_t sxe2_drv_tm_commit_node(struct sxe2_adapter *adapter, + struct sxe2_tm_node *tm_node) +{ + struct sxe2_drv_cmd_params cmd = { 0 }; + struct sxe2_tm_add_mid_msg msg_mid = {0}; + struct sxe2_tm_add_queue_msg msg_queue = {0}; + struct sxe2_tm_res res = {0}; + struct sxe2_common_device *cdev = adapter->cdev; + int32_t ret; + uint32_t i; + + if (tm_node->type == SXE2_TM_NODE_TYPE_VSIG) { + goto l_add; + } else if (tm_node->type == SXE2_TM_NODE_TYPE_MID) { + sxe2_drv_tm_node_to_info(adapter, tm_node, &msg_mid.info); + msg_mid.parent_teid = rte_cpu_to_le_16(tm_node->parent->teid); + msg_mid.adj_lvl = adapter->sched_ctxt.adj_lvl; + + sxe2_drv_cmd_params_fill(adapter, &cmd, + SXE2_DRV_CMD_SCHED_TM_ADD_MID_NODE, + &msg_mid, sizeof(msg_mid), + &res, sizeof(res)); + ret = sxe2_drv_cmd_exec(cdev, &cmd); + if (ret) { + PMD_LOG_ERR(DRV, "add tm mid node failed, ret:%d", ret); + goto l_end; + } + } else if (tm_node->type == SXE2_TM_NODE_TYPE_QUEUE) { + sxe2_drv_tm_node_to_info(adapter, tm_node, &msg_queue.info); + msg_queue.parent_teid = rte_cpu_to_le_16(tm_node->parent->teid); + msg_queue.queue_id = rte_cpu_to_le_16(tm_node->id); + msg_queue.adj_lvl = adapter->sched_ctxt.adj_lvl; + + sxe2_drv_cmd_params_fill(adapter, &cmd, + SXE2_DRV_CMD_SCHED_TM_ADD_QUEUE_NODE, + &msg_queue, sizeof(msg_queue), + &res, sizeof(res)); + ret = sxe2_drv_cmd_exec(cdev, &cmd); + if (ret) { + PMD_LOG_ERR(DRV, "add tm queue failed, ret:%d", ret); + goto l_end; + } + } else { + PMD_LOG_ERR(DRV, "commit tm node failed, type:%d", tm_node->type); + ret = -EINVAL; + goto l_end; + } + + tm_node->teid = rte_le_to_cpu_16(res.teid); + +l_add: + for (i = 0; i < tm_node->child_cnt; i++) { + ret = sxe2_drv_tm_commit_node(adapter, tm_node->children[i]); + if (ret) + goto l_end; + } +l_end: + return ret; +} + +int32_t sxe2_drv_tm_commit(struct sxe2_adapter *adapter) +{ + struct sxe2_drv_cmd_params cmd = { 0 }; + struct sxe2_common_device *cdev = adapter->cdev; + struct sxe2_tm_res tm_res = {0}; + int32_t ret; + + tm_res.teid = adapter->tm_ctxt.root_teid; + sxe2_drv_cmd_params_fill(adapter, &cmd, SXE2_DRV_CMD_SCHED_ROOT_CHILDREN_DELETE, + &tm_res, sizeof(tm_res), + NULL, 0); + ret = sxe2_drv_cmd_exec(cdev, &cmd); + if (ret) { + PMD_LOG_ERR(DRV, "del tm root failed, ret:%d", ret); + goto l_end; + } + + ret = sxe2_drv_tm_commit_node(adapter, adapter->tm_ctxt.root); + if (ret) { + PMD_LOG_ERR(DRV, "commit tm node failed, ret:%d", ret); + goto l_end; + } + + PMD_LOG_DEBUG(DRV, "commit tm success"); +l_end: + return ret; +} diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.h b/drivers/net/sxe2/sxe2_cmd_chnl.h index 3274db6551..6e209377c7 100644 --- a/drivers/net/sxe2/sxe2_cmd_chnl.h +++ b/drivers/net/sxe2/sxe2_cmd_chnl.h @@ -38,6 +38,12 @@ int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter); int32_t sxe2_drv_promisc_config(struct sxe2_adapter *adapter, bool set); +int32_t sxe2_drv_root_tree_release(struct rte_eth_dev *dev); + +int32_t sxe2_drv_root_tree_alloc(struct rte_eth_dev *dev); + +int32_t sxe2_drv_tm_commit(struct sxe2_adapter *adapter); + int32_t sxe2_drv_allmulti_config(struct sxe2_adapter *adapter, bool set); int32_t sxe2_drv_uc_config(struct sxe2_adapter *adapter, struct rte_ether_addr *addr, bool add); diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h index ced5a4b1a0..07f083644c 100644 --- a/drivers/net/sxe2/sxe2_drv_cmd.h +++ b/drivers/net/sxe2/sxe2_drv_cmd.h @@ -349,6 +349,30 @@ struct sxe2_rss_hf_req { uint8_t rsv1[3]; }; +struct sxe2_tm_res { + __le16 teid; +}; + +struct sxe2_tm_info { + uint32_t committed; + uint32_t peak; + uint8_t priority; + uint8_t reserve; + __le16 weight; +}; + +struct sxe2_tm_add_mid_msg { + __le16 parent_teid; + uint8_t adj_lvl; + struct sxe2_tm_info info; +}; +struct sxe2_tm_add_queue_msg { + __le16 parent_teid; + __le16 queue_id; + uint8_t adj_lvl; + struct sxe2_tm_info info; +}; + enum sxe2_drv_cmd_module { SXE2_DRV_CMD_MODULE_HANDSHAKE = 0, SXE2_DRV_CMD_MODULE_DEV = 1, diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c index d48841b8e4..a095888c00 100644 --- a/drivers/net/sxe2/sxe2_ethdev.c +++ b/drivers/net/sxe2/sxe2_ethdev.c @@ -130,6 +130,8 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = { .reta_query = sxe2_dev_rss_reta_query, .rss_hash_update = sxe2_dev_rss_hash_update, .rss_hash_conf_get = sxe2_dev_rss_hash_conf_get, + + .tm_ops_get = sxe2_tm_ops_get, }; static int32_t sxe2_dev_configure(struct rte_eth_dev *dev) @@ -575,6 +577,14 @@ static void sxe2_drv_dev_caps_set(struct sxe2_adapter *adapter, adapter->cap_flags |= SXE2_DEV_CAPS_OFFLOAD_FC_STATE; } +static void sxe2_sw_sched_hw_cap_set(struct sxe2_adapter *adapter, + struct sxe2_txsch_caps *txsch_caps) +{ + adapter->sched_ctxt.tm_layers = txsch_caps->layer_cap; + adapter->sched_ctxt.root_max_children = txsch_caps->tm_mid_node_num; + adapter->sched_ctxt.prio_max = txsch_caps->prio_num; +} + static int32_t sxe2_func_caps_get(struct sxe2_adapter *adapter) { int32_t ret = -1; @@ -594,6 +604,8 @@ static int32_t sxe2_func_caps_get(struct sxe2_adapter *adapter) sxe2_sw_vsi_ctx_hw_cap_set(adapter, &dev_caps.vsi_caps); + sxe2_sw_sched_hw_cap_set(adapter, &dev_caps.txsch_caps); + l_end: return ret; } @@ -930,6 +942,68 @@ void sxe2_dev_pci_map_uinit(struct rte_eth_dev *dev) adapter->dev_info.dev_data = NULL; } +uint32_t sxe2_sched_mode_get(struct sxe2_adapter *adapter) +{ + uint32_t ret_mode = SXE2_SCHED_MODE_INVALID; + struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt; + + if (adapter->devargs.high_performance_mode) + ret_mode = SXE2_SCHED_MODE_HIGH_PERFORMANCE; + else if (tm_ctxt->committed) + ret_mode = SXE2_SCHED_MODE_TM; + else + ret_mode = SXE2_SCHED_MODE_DEFAULT; + + return ret_mode; +} + +static int32_t sxe2_sched_init(struct rte_eth_dev *dev) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + int32_t ret = 0; + + adapter->sched_ctxt.adj_lvl = adapter->devargs.sched_layer_mode; + + if (adapter->devargs.high_performance_mode) { + PMD_LOG_DEBUG(DRV, "TM feature will be disabled in high-performance mode."); + adapter->cap_flags &= ~(SXE2_DEV_CAPS_OFFLOAD_TM); + } else { + ret = sxe2_tm_init(dev); + if (ret) + goto l_end; + + ret = sxe2_drv_root_tree_alloc(dev); + if (ret) + goto l_end; + } + +l_end: + return ret; +} + +static int32_t sxe2_sched_uinit(struct rte_eth_dev *dev) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + int32_t ret = 0; + + if (adapter->devargs.high_performance_mode == 0) { + ret = sxe2_tm_uninit(dev); + if (ret) { + PMD_LOG_ERR(INIT, "Failed to uninit tm, ret=%d", ret); + goto l_end; + } + + ret = sxe2_drv_root_tree_release(dev); + if (ret) { + PMD_LOG_ERR(INIT, "Failed to release root tree, ret=%d", ret); + goto l_end; + } + } + +l_end: + return ret; +} + static int32_t sxe2_dev_init(struct rte_eth_dev *dev, struct sxe2_dev_kvargs_info *kvargs __rte_unused) { @@ -985,8 +1059,15 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev, goto init_rss_err; } + ret = sxe2_sched_init(dev); + if (ret) { + PMD_LOG_ERR(INIT, "Failed to init sched, ret=%d", ret); + goto init_sched_err; + } + goto l_end; +init_sched_err: init_rss_err: init_eth_err: init_dev_info_err: @@ -1002,6 +1083,7 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev) (void)sxe2_dev_stop(dev); (void)sxe2_queues_release(dev); (void)sxe2_rss_disable(dev); + (void)sxe2_sched_uinit(dev); sxe2_vsi_uninit(dev); sxe2_dev_pci_map_uinit(dev); sxe2_eth_uinit(dev); diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h index 556a11cc77..609e1e92ba 100644 --- a/drivers/net/sxe2/sxe2_ethdev.h +++ b/drivers/net/sxe2/sxe2_ethdev.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd. */ + #ifndef __SXE2_ETHDEV_H__ #define __SXE2_ETHDEV_H__ #include <rte_compat.h> @@ -18,6 +19,7 @@ #include "sxe2_queue.h" #include "sxe2_mac.h" #include "sxe2_osal.h" +#include "sxe2_tm.h" #include "sxe2_filter.h" struct sxe2_link_msg { @@ -307,6 +309,8 @@ struct sxe2_adapter { struct sxe2_rss_context rss_ctxt; struct sxe2_link_context link_ctxt; struct sxe2_ptp_context ptp_ctxt; + struct sxe2_sched_hw_cap sched_ctxt; + struct sxe2_tm_context tm_ctxt; struct sxe2_devargs devargs; struct sxe2_switchdev_info switchdev_info; bool rule_started; @@ -333,6 +337,8 @@ void *sxe2_pci_map_addr_get(struct sxe2_adapter *adapter, enum sxe2_pci_map_resource res_type, uint16_t idx_in_func); +uint32_t sxe2_sched_mode_get(struct sxe2_adapter *adapter); + struct sxe2_pci_map_bar_info *sxe2_dev_get_bar_info(struct sxe2_adapter *adapter, enum sxe2_pci_map_resource res_type); diff --git a/drivers/net/sxe2/sxe2_tm.c b/drivers/net/sxe2/sxe2_tm.c new file mode 100644 index 0000000000..4c4f793cd5 --- /dev/null +++ b/drivers/net/sxe2/sxe2_tm.c @@ -0,0 +1,1151 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd. + */ + +#include <rte_ethdev.h> +#include <rte_tm_driver.h> + +#include "sxe2_tm.h" +#include "sxe2_ethdev.h" +#include "sxe2_common_log.h" +#include "sxe2_cmd_chnl.h" + +static uint16_t sxe2_tm_level_node_num_get(uint8_t level) +{ + uint16_t node_num = 0; + + switch (level) { + case 0: + node_num = SXE2_TM_1L_NODE_NUM_MAX; + break; + case 1: + node_num = SXE2_TM_2L_NODE_NUM_MAX; + break; + case 2: + node_num = SXE2_TM_3L_NODE_NUM_MAX; + break; + case 3: + node_num = SXE2_TM_4L_NODE_NUM_MAX; + break; + } + return node_num; +} + +static int32_t sxe2_tm_capabilities_get(struct rte_eth_dev *dev, + struct rte_tm_capabilities *cap, + struct rte_tm_error *error) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + int32_t ret = 0; + uint32_t i; + + if (!cap || !error) { + PMD_LOG_ERR(DRV, "sxe2 get tm cap failed, cap or error is NULL."); + ret = -EINVAL; + goto l_end; + } + + if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) { + PMD_LOG_ERR(DRV, "The TM capability is not supported."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "The TM capability is not supported."; + ret = ENOTSUP; + goto l_end; + } + + error->type = RTE_TM_ERROR_TYPE_NONE; + memset(cap, 0, sizeof(struct rte_tm_capabilities)); + + for (i = 0; i < adapter->tm_ctxt.tm_layers; i++) + cap->n_nodes_max += sxe2_tm_level_node_num_get(i); + + cap->n_levels_max = adapter->tm_ctxt.tm_layers; + + cap->non_leaf_nodes_identical = 1; + + cap->leaf_nodes_identical = 1; + + cap->shaper_n_max = cap->n_nodes_max; + + cap->shaper_private_n_max = cap->n_nodes_max; + + cap->shaper_private_dual_rate_n_max = cap->n_nodes_max; + + cap->shaper_private_rate_min = 0; + + cap->shaper_private_rate_max = 12500000000ull; + + cap->shaper_private_packet_mode_supported = 0; + cap->shaper_private_byte_mode_supported = 1; + + cap->shaper_pkt_length_adjust_min = RTE_TM_ETH_FRAMING_OVERHEAD; + + cap->shaper_pkt_length_adjust_max = RTE_TM_ETH_FRAMING_OVERHEAD_FCS; + + cap->shaper_shared_n_max = 0; + cap->shaper_shared_n_nodes_per_shaper_max = 0; + cap->shaper_shared_n_shapers_per_node_max = 0; + cap->shaper_shared_dual_rate_n_max = 0; + cap->shaper_shared_rate_min = 0; + cap->shaper_shared_rate_max = 0; + cap->shaper_shared_packet_mode_supported = 0; + cap->shaper_shared_byte_mode_supported = 0; + + cap->sched_n_children_max = dev->data->nb_tx_queues; + + cap->sched_sp_n_priorities_max = 7; + + cap->sched_wfq_n_children_per_group_max = 1; + cap->sched_wfq_n_groups_max = 0; + cap->sched_wfq_weight_max = 0; + cap->sched_wfq_packet_mode_supported = 0; + cap->sched_wfq_byte_mode_supported = 0; + + cap->cman_wred_packet_mode_supported = 0; + cap->cman_wred_byte_mode_supported = 0; + cap->cman_head_drop_supported = 0; + cap->cman_wred_context_n_max = 0; + cap->cman_wred_context_private_n_max = 0; + cap->cman_wred_context_shared_n_max = 0; + cap->cman_wred_context_shared_n_nodes_per_context_max = 0; + cap->cman_wred_context_shared_n_contexts_per_node_max = 0; + + cap->dynamic_update_mask = 0; + + cap->stats_mask = 0; +l_end: + return ret; +} + +static int32_t sxe2_level_capabilities_get(struct rte_eth_dev *dev, + uint32_t level_id, struct rte_tm_level_capabilities *cap, + struct rte_tm_error *error) +{ + int32_t ret = 0; + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + + if (!cap || !error) { + PMD_LOG_ERR(DRV, "sxe2 get tm cap failed, cap or error is NULL."); + ret = -EINVAL; + goto l_end; + } + if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) { + PMD_LOG_ERR(DRV, "The TM capability is not supported."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "The TM capability is not supported."; + ret = ENOTSUP; + goto l_end; + } + + if (level_id >= adapter->tm_ctxt.tm_layers) { + ret = -EINVAL; + error->type = RTE_TM_ERROR_TYPE_LEVEL_ID; + error->message = "too deep level"; + goto l_end; + } + + cap->n_nodes_max = sxe2_tm_level_node_num_get(level_id); + + cap->non_leaf_nodes_identical = true; + + cap->leaf_nodes_identical = true; + + if (level_id != adapter->tm_ctxt.tm_layers - 1) { + cap->n_nodes_nonleaf_max = cap->n_nodes_max; + cap->n_nodes_leaf_max = 0; + + cap->nonleaf.shaper_private_supported = true; + cap->nonleaf.shaper_private_dual_rate_supported = true; + cap->nonleaf.shaper_private_rate_min = 0; + + cap->nonleaf.shaper_private_rate_max = 12500000000ull; + cap->nonleaf.shaper_private_packet_mode_supported = 0; + cap->nonleaf.shaper_private_byte_mode_supported = 1; + + cap->nonleaf.shaper_shared_n_max = 0; + cap->nonleaf.shaper_shared_packet_mode_supported = 0; + cap->nonleaf.shaper_shared_byte_mode_supported = 0; + + cap->nonleaf.sched_n_children_max = SXE2_TM_MAX_CHILDREN_COUNT; + cap->nonleaf.sched_sp_n_priorities_max = 7; + + cap->nonleaf.sched_wfq_n_children_per_group_max = 0; + cap->nonleaf.sched_wfq_n_groups_max = 0; + cap->nonleaf.sched_wfq_weight_max = 0; + cap->nonleaf.sched_wfq_packet_mode_supported = 0; + cap->nonleaf.sched_wfq_byte_mode_supported = 0; + + cap->nonleaf.stats_mask = 0; + } else { + cap->n_nodes_nonleaf_max = 0; + cap->n_nodes_leaf_max = cap->n_nodes_max; + + cap->leaf.shaper_private_supported = true; + cap->leaf.shaper_private_dual_rate_supported = true; + cap->leaf.shaper_private_rate_min = 0; + cap->leaf.shaper_private_rate_max = 12500000000ull; + cap->leaf.shaper_private_packet_mode_supported = 0; + cap->leaf.shaper_private_byte_mode_supported = 1; + + cap->leaf.shaper_shared_n_max = 0; + cap->leaf.shaper_shared_packet_mode_supported = 0; + cap->leaf.shaper_shared_byte_mode_supported = 0; + + cap->leaf.cman_head_drop_supported = false; + cap->leaf.cman_wred_context_private_supported = false; + cap->leaf.cman_wred_context_shared_n_max = 0; + + cap->leaf.stats_mask = 0; + } + +l_end: + return ret; +} + +static struct sxe2_tm_node *sxe2_tm_find_node(struct sxe2_tm_node *parent, uint32_t id) +{ + struct sxe2_tm_node *node = NULL; + uint32_t i; + + if (parent == NULL || parent->id == id) { + node = parent; + goto l_end; + } + + for (i = 0; i < parent->child_cnt; i++) { + node = sxe2_tm_find_node(parent->children[i], id); + if (node) + goto l_end; + } + +l_end: + return node; +} + +static int32_t sxe2_node_capabilities_get(struct rte_eth_dev *dev, uint32_t node_id, + struct rte_tm_node_capabilities *cap, + struct rte_tm_error *error) +{ + int32_t ret = -EINVAL; + struct sxe2_tm_node *tm_node; + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + + if (!cap || !error) { + PMD_LOG_ERR(DRV, "sxe2 get tm cap failed, cap or error is NULL."); + ret = -EINVAL; + goto l_end; + } + + if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) { + PMD_LOG_ERR(DRV, "The TM capability is not supported."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "The TM capability is not supported."; + ret = ENOTSUP; + goto l_end; + } + + if (node_id == RTE_TM_NODE_ID_NULL) { + PMD_LOG_ERR(DRV, "invalid node id"); + error->type = RTE_TM_ERROR_TYPE_NODE_ID; + error->message = "invalid node id"; + goto l_end; + } + + tm_node = sxe2_tm_find_node(adapter->tm_ctxt.root, node_id); + if (!tm_node) { + PMD_LOG_ERR(DRV, "no such node"); + error->type = RTE_TM_ERROR_TYPE_NODE_ID; + error->message = "no such node"; + goto l_end; + } + + cap->shaper_private_supported = true; + cap->shaper_private_dual_rate_supported = true; + cap->shaper_private_rate_min = 0; + cap->shaper_private_rate_max = 12500000000ull; + cap->shaper_private_packet_mode_supported = 0; + cap->shaper_private_byte_mode_supported = 1; + + cap->shaper_shared_n_max = 0; + cap->shaper_shared_packet_mode_supported = 0; + cap->shaper_shared_byte_mode_supported = 0; + + if (tm_node->level == adapter->tm_ctxt.tm_layers - 1) { + cap->leaf.cman_head_drop_supported = false; + cap->leaf.cman_wred_context_private_supported = false; + cap->leaf.cman_wred_context_shared_n_max = 0; + } else { + cap->nonleaf.sched_n_children_max = SXE2_TM_MAX_CHILDREN_COUNT; + cap->nonleaf.sched_sp_n_priorities_max = 7; + cap->nonleaf.sched_wfq_n_children_per_group_max = 0; + cap->nonleaf.sched_wfq_n_groups_max = 0; + cap->nonleaf.sched_wfq_weight_max = 0; + cap->nonleaf.sched_wfq_packet_mode_supported = 0; + cap->nonleaf.sched_wfq_byte_mode_supported = 0; + } + cap->stats_mask = 0; + + ret = 0; +l_end: + return ret; +} + +static int32_t sxe2_tm_shaper_profile_param_check(const struct rte_tm_shaper_params *profile, + struct rte_tm_error *error) +{ + int32_t ret = 0; + + if (profile->committed.size) { + PMD_LOG_ERR(DRV, "committed bucket size not supported."); + error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_COMMITTED_SIZE; + error->message = "committed bucket size not supported"; + ret = -EINVAL; + goto l_end; + } + + if (profile->peak.size) { + PMD_LOG_ERR(DRV, "peak bucket size not supported."); + error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PEAK_SIZE; + error->message = "peak bucket size not supported"; + ret = -EINVAL; + goto l_end; + } + + if (profile->pkt_length_adjust) { + PMD_LOG_ERR(DRV, "packet length adjustment not supported."); + error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PKT_ADJUST_LEN; + error->message = "packet length adjustment not supported"; + ret = -EINVAL; + goto l_end; + } + + if (profile->committed.rate > SXE2_HW_RATE_MAX || + profile->committed.rate < SXE2_HW_RATE_MIN) { + PMD_LOG_ERR(DRV, "The committed rate limit value is required to be in " + "the range [%" PRIu64 ", %" PRIu64 "].", + (uint64_t)SXE2_HW_RATE_MIN, (uint64_t)SXE2_HW_RATE_MAX); + error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_COMMITTED_RATE; + error->message = "invalid rate limit: value out of range."; + ret = -EINVAL; + goto l_end; + } + + if (profile->peak.rate > SXE2_HW_RATE_MAX || + profile->peak.rate < SXE2_HW_RATE_MIN) { + PMD_LOG_ERR(DRV, "The peak rate limit value is required to be in " + "the range [%" PRIu64 ", %" PRIu64 "].", + (uint64_t)SXE2_HW_RATE_MIN, (uint64_t)SXE2_HW_RATE_MAX); + error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PEAK_RATE; + error->message = "invalid rate limit: value out of range."; + ret = -EINVAL; + goto l_end; + } + + if (profile->committed.rate > profile->peak.rate) { + PMD_LOG_ERR(DRV, "committed rate can't be greater than peak rate."); + error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_COMMITTED_RATE; + error->message = "committed rate can't be greater than peak rate."; + ret = -EINVAL; + goto l_end; + } +l_end: + return ret; +} + +static inline struct sxe2_tm_shaper_profile * +sxe2_tm_shaper_profile_search(struct rte_eth_dev *dev, uint32_t id) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + struct sxe2_shaper_profile_list *shaper_profile_list = + &adapter->tm_ctxt.profile_list; + struct sxe2_tm_shaper_profile *shaper_profile = NULL; + + TAILQ_FOREACH(shaper_profile, shaper_profile_list, node) { + if (id == shaper_profile->id) + goto l_end; + } + + shaper_profile = NULL; + +l_end: + return shaper_profile; +} + +static int32_t sxe2_tm_shaper_profile_add(struct rte_eth_dev *dev, uint32_t shaper_profile_id, + const struct rte_tm_shaper_params *profile, + struct rte_tm_error *error) +{ + int32_t ret = -EINVAL; + struct sxe2_tm_shaper_profile *shaper_profile = NULL; + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + + if (!profile || !error) { + PMD_LOG_ERR(DRV, "Invalid input: profile:0x%p or error:0x%p is null.", + profile, error); + if (error) { + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "Invalid input: profile or error is null."; + } + ret = -EINVAL; + goto l_end; + } + + if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) { + PMD_LOG_ERR(DRV, "The TM capability is not supported."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "The TM capability is not supported."; + ret = ENOTSUP; + goto l_end; + } + + ret = sxe2_tm_shaper_profile_param_check(profile, error); + if (ret) + goto l_end; + + shaper_profile = sxe2_tm_shaper_profile_search(dev, shaper_profile_id); + if (shaper_profile) { + PMD_LOG_ERR(DRV, "profile ID exist."); + error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID; + error->message = "profile ID exist"; + ret = -EINVAL; + goto l_end; + } + + shaper_profile = rte_zmalloc("sxe2_tm_shaper_profile", + sizeof(struct sxe2_tm_shaper_profile), 0); + if (!shaper_profile) { + PMD_LOG_ERR(DRV, "Alloc shaper_profile memory failed."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "Alloc shaper_profile memory failed"; + ret = -ENOMEM; + goto l_end; + } + + rte_memcpy(&shaper_profile->profile, profile, + sizeof(struct rte_tm_shaper_params)); + shaper_profile->id = shaper_profile_id; + + TAILQ_INSERT_TAIL(&adapter->tm_ctxt.profile_list, shaper_profile, node); +l_end: + return ret; +} + +static int32_t sxe2_tm_shaper_profile_del(struct rte_eth_dev *dev, + uint32_t id, struct rte_tm_error *error) +{ + int32_t ret = -EINVAL; + struct sxe2_tm_shaper_profile *shaper_profile = NULL; + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + + if (!error) { + PMD_LOG_ERR(DRV, "Error param is null."); + goto l_end; + } + + if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) { + PMD_LOG_ERR(DRV, "The TM capability is not supported."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "The TM capability is not supported."; + ret = ENOTSUP; + goto l_end; + } + + shaper_profile = sxe2_tm_shaper_profile_search(dev, id); + if (!shaper_profile) { + PMD_LOG_ERR(DRV, "profile ID not exist."); + error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID; + error->message = "profile ID not exist"; + ret = -EINVAL; + goto l_end; + } + + if (shaper_profile->ref_cnt) { + PMD_LOG_ERR(DRV, "profile in use."); + error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE; + error->message = "profile in use"; + ret = -EINVAL; + goto l_end; + } + + TAILQ_REMOVE(&adapter->tm_ctxt.profile_list, shaper_profile, node); + rte_free(shaper_profile); + + ret = 0; +l_end: + return ret; +} + +static int32_t sxe2_tm_node_param_check(struct rte_eth_dev *dev, + uint32_t parent_node_type, + uint32_t node_id, uint32_t priority, uint32_t weight, + const struct rte_tm_node_params *params, + bool is_leaf, struct rte_tm_error *error) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt; + int32_t ret = -EINVAL; + + if (node_id == RTE_TM_NODE_ID_NULL) { + PMD_LOG_ERR(DRV, "Invalid node id."); + error->type = RTE_TM_ERROR_TYPE_NODE_ID; + error->message = "invalid node id"; + goto l_end; + } + + if (parent_node_type == SXE2_TM_NODE_TYPE_VSIG && + priority >= tm_ctxt->prio_max) { + PMD_LOG_ERR(DRV, "Priority should be less than %u.", tm_ctxt->prio_max); + error->type = RTE_TM_ERROR_TYPE_NODE_PRIORITY; + error->message = "The priority is too high."; + goto l_end; + } + + if (priority > SXE2_TM_PRIO_MAX) { + PMD_LOG_ERR(DRV, "Priority should be less than %u.", SXE2_TM_PRIO_MAX); + error->type = RTE_TM_ERROR_TYPE_NODE_PRIORITY; + error->message = "The priority is too high."; + goto l_end; + } + + if (weight > SXE2_TM_WEIGHT_MAX || weight < SXE2_TM_WEIGHT_MIN) { + PMD_LOG_ERR(DRV, "Weight must be between 1 and 200."); + error->type = RTE_TM_ERROR_TYPE_NODE_WEIGHT; + error->message = "weight must be between 1 and 200"; + goto l_end; + } + + if (params->shared_shaper_id) { + PMD_LOG_ERR(DRV, "Shared shaper not supported."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_SHARED_SHAPER_ID; + error->message = "shared shaper not supported"; + goto l_end; + } + if (params->n_shared_shapers) { + PMD_LOG_ERR(DRV, "Shared shaper not supported.."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_N_SHARED_SHAPERS; + error->message = "shared shaper not supported"; + goto l_end; + } + + if (!is_leaf) { + if (node_id <= dev->data->nb_tx_queues) { + PMD_LOG_ERR(DRV, "no leaf node id must bigger than queue id."); + error->type = RTE_TM_ERROR_TYPE_NODE_ID; + error->message = "no leaf node id must bigger than queue id."; + goto l_end; + } + + if (params->nonleaf.wfq_weight_mode) { + PMD_LOG_ERR(DRV, "WFQ not supported."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_WFQ_WEIGHT_MODE; + error->message = "WFQ not supported"; + goto l_end; + } + + if (params->nonleaf.n_sp_priorities != 1) { + PMD_LOG_ERR(DRV, "SP priority not supported."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_N_SP_PRIORITIES; + error->message = "SP priority not supported"; + goto l_end; + } + } else { + if (node_id >= dev->data->nb_tx_queues) { + PMD_LOG_ERR(DRV, "leaf node id must be queue id."); + error->type = RTE_TM_ERROR_TYPE_NODE_ID; + error->message = "leaf node id must be queue id."; + goto l_end; + } + + if (params->leaf.cman) { + PMD_LOG_ERR(DRV, "Congestion management not supported."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_CMAN; + error->message = "Congestion management not supported"; + goto l_end; + } + if (params->leaf.wred.wred_profile_id != RTE_TM_WRED_PROFILE_ID_NONE) { + PMD_LOG_ERR(DRV, "WRED not supported."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_WRED_PROFILE_ID; + error->message = "WRED not supported"; + goto l_end; + } + if (params->leaf.wred.shared_wred_context_id) { + PMD_LOG_ERR(DRV, "WRED not supported."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_SHARED_WRED_CONTEXT_ID; + error->message = "WRED not supported"; + goto l_end; + } + if (params->leaf.wred.n_shared_wred_contexts) { + PMD_LOG_ERR(DRV, "WRED not supported."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_N_SHARED_WRED_CONTEXTS; + error->message = "WRED not supported"; + goto l_end; + } + } + ret = 0; +l_end: + return ret; +} + +static int32_t sxe2_tm_add_child(struct sxe2_tm_node *parent, + struct sxe2_tm_node *child) +{ + int32_t ret = -1; + uint32_t i; + for (i = 0; i < SXE2_TM_MAX_CHILDREN_COUNT; i++) { + if (parent->children[i] == NULL) { + parent->children[i] = child; + child->index_in_parent = i; + parent->child_cnt++; + ret = 0; + break; + } + } + return ret; +} + +static int32_t sxe2_tm_node_add(struct rte_eth_dev *dev, uint32_t node_id, uint32_t parent_node_id, + uint32_t priority, uint32_t weight, uint32_t level_id, + const struct rte_tm_node_params *params, + struct rte_tm_error *error) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + struct sxe2_tm_shaper_profile *shaper_profile = NULL; + struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt; + struct sxe2_tm_node *tm_node = NULL; + struct sxe2_tm_node *parent_node = NULL; + int32_t ret = -EINVAL; + bool is_leaf; + + if (!params || !error) { + PMD_LOG_ERR(DRV, "Invalid input: params:0x%p or error:0x%p is null.", + params, error); + if (error) { + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "Invalid input: params or error is null."; + } + ret = -EINVAL; + goto l_end; + } + + if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) { + PMD_LOG_ERR(DRV, "The TM capability is not supported."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "The TM capability is not supported."; + ret = ENOTSUP; + goto l_end; + } + + shaper_profile = sxe2_tm_shaper_profile_search(dev, params->shaper_profile_id); + if (!shaper_profile) { + PMD_LOG_ERR(DRV, "Shaper profile does not exist."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_SHAPER_PROFILE_ID; + error->message = "shaper profile does not exist"; + ret = -EINVAL; + goto l_end; + } + + if (parent_node_id == RTE_TM_NODE_ID_NULL) { + if (level_id != 0) { + PMD_LOG_ERR(DRV, "Wrong level, root node (NULL parent) must be at level 0."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS; + error->message = "Wrong level, root node (NULL parent) must be at level 0"; + ret = -EINVAL; + goto l_end; + } + + if (tm_ctxt->root) { + PMD_LOG_ERR(DRV, "Already have a root."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID; + error->message = "already have a root"; + ret = -EINVAL; + goto l_end; + } + + ret = sxe2_tm_node_param_check(dev, SXE2_TM_NODE_TYPE_INVALID, node_id, priority, + weight, params, false, error); + if (ret) + goto l_end; + + tm_node = rte_zmalloc("tm_node_root", sizeof(struct sxe2_tm_node), 0); + if (!tm_node) { + PMD_LOG_ERR(DRV, "Alloc tm_node memory failed."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "Alloc tm_node memory failed"; + ret = -ENOMEM; + goto l_end; + } + + tm_node->id = node_id; + tm_node->level = 0; + tm_node->parent = NULL; + tm_node->child_cnt = 0; + tm_node->weight = weight; + tm_node->hw_weight = SXE2_TM_WEIGHT_SUM; + tm_node->type = SXE2_TM_NODE_TYPE_VSIG; + tm_node->priority = priority; + tm_node->shaper_profile = shaper_profile; + + tm_node->teid = tm_ctxt->root_teid; + + shaper_profile->ref_cnt++; + tm_ctxt->root = tm_node; + ret = 0; + goto l_end; + } + + parent_node = sxe2_tm_find_node(tm_ctxt->root, parent_node_id); + if (!parent_node) { + PMD_LOG_ERR(DRV, "Parent not exist."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID; + error->message = "parent not exist"; + ret = -EINVAL; + goto l_end; + } + + if (parent_node->child_cnt >= SXE2_TM_MAX_CHILDREN_COUNT || + (parent_node->type == SXE2_TM_NODE_TYPE_VSIG && + parent_node->child_cnt >= tm_ctxt->root_max_children)) { + PMD_LOG_ERR(DRV, "Parent node is full."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS; + error->message = "parent node is full"; + ret = -EINVAL; + goto l_end; + } + + if (level_id == RTE_TM_NODE_LEVEL_ID_ANY) { + level_id = parent_node->level + 1; + } else if (level_id != parent_node->level + 1) { + PMD_LOG_ERR(DRV, "Wrong level."); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS; + error->message = "Wrong level"; + ret = -EINVAL; + goto l_end; + } + + if (level_id >= tm_ctxt->tm_layers) { + PMD_LOG_ERR(DRV, "The maximum number of TM configuration levels is %d", + tm_ctxt->tm_layers); + error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS; + error->message = "TM level exceeds supported hardware limit"; + ret = -EINVAL; + goto l_end; + } + + if (level_id + 1 == tm_ctxt->tm_layers) + is_leaf = true; + else + is_leaf = false; + + ret = sxe2_tm_node_param_check(dev, parent_node->type, node_id, priority, weight, + params, is_leaf, error); + if (ret) + goto l_end; + + if (sxe2_tm_find_node(tm_ctxt->root, node_id)) { + PMD_LOG_ERR(DRV, "Node id already used."); + error->type = RTE_TM_ERROR_TYPE_NODE_ID; + error->message = "node id already used"; + ret = -EINVAL; + goto l_end; + } + + tm_node = rte_zmalloc("tm_node_no_root", sizeof(struct sxe2_tm_node), 0); + if (!tm_node) { + PMD_LOG_ERR(DRV, "Alloc tm_node memory failed."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "Alloc tm_node memory failed"; + ret = -ENOMEM; + goto l_end; + } + + if (level_id + 1 != tm_ctxt->tm_layers) + tm_node->type = SXE2_TM_NODE_TYPE_MID; + else + tm_node->type = SXE2_TM_NODE_TYPE_QUEUE; + tm_node->id = node_id; + tm_node->level = level_id; + tm_node->parent = parent_node; + tm_node->child_cnt = 0; + tm_node->weight = weight; + tm_node->priority = priority; + tm_node->shaper_profile = shaper_profile; + shaper_profile->ref_cnt++; + + ret = sxe2_tm_add_child(parent_node, tm_node); + if (ret) { + shaper_profile->ref_cnt--; + rte_free(tm_node); + } +l_end: + return ret; +} + +static int32_t sxe2_tm_tree_delete(struct sxe2_tm_node *tm_node) +{ + int32_t ret = 0; + uint32_t i, j; + struct sxe2_tm_node *parent = NULL; + + if (!tm_node) + goto l_end; + + parent = tm_node->parent; + + if (tm_node->child_cnt != 0) { + for (i = SXE2_TM_MAX_CHILDREN_COUNT; i > 0; i--) { + if (tm_node->children[i - 1]) + ret = sxe2_tm_tree_delete(tm_node->children[i - 1]); + } + } + + if (tm_node->shaper_profile) + tm_node->shaper_profile->ref_cnt--; + + if (tm_node->type != SXE2_TM_NODE_TYPE_VSIG && parent) { + for (i = 0; i < parent->child_cnt; i++) { + if (parent->children[i] == tm_node) + break; + } + for (j = i; j < parent->child_cnt - 1; j++) + parent->children[j] = parent->children[j + 1]; + + parent->children[parent->child_cnt - 1] = NULL; + parent->child_cnt--; + } + rte_free(tm_node); + tm_node = NULL; +l_end: + return ret; +} + +static int32_t sxe2_tm_node_delete(struct rte_eth_dev *dev, uint32_t node_id, + struct rte_tm_error *error) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt; + struct sxe2_tm_node *tm_node = NULL; + int32_t ret = -EINVAL; + + if (!error) { + PMD_LOG_ERR(DRV, "Invalid input: error is null."); + ret = -EINVAL; + goto l_end; + } + + if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) { + PMD_LOG_ERR(DRV, "The TM capability is not supported."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "The TM capability is not supported."; + ret = ENOTSUP; + goto l_end; + } + + if (node_id == RTE_TM_NODE_ID_NULL) { + PMD_LOG_ERR(DRV, "Invalid node id. node_id = %u", node_id); + error->type = RTE_TM_ERROR_TYPE_NODE_ID; + error->message = "invalid node id"; + goto l_end; + } + + tm_node = sxe2_tm_find_node(tm_ctxt->root, node_id); + if (!tm_node) { + PMD_LOG_ERR(DRV, "No such node."); + error->type = RTE_TM_ERROR_TYPE_NODE_ID; + error->message = "no such node"; + ret = -EINVAL; + goto l_end; + } + + ret = sxe2_tm_tree_delete(tm_node); + if (ret) { + PMD_LOG_ERR(DRV, "Delete node failed."); + error->type = RTE_TM_ERROR_TYPE_NODE_ID; + error->message = "Delete node failed"; + goto l_end; + } + + if (tm_node == tm_ctxt->root) + tm_ctxt->root = NULL; + +l_end: + return ret; +} + +static int32_t sxe2_tm_node_type_get(struct rte_eth_dev *dev, uint32_t node_id, + int32_t *is_leaf, struct rte_tm_error *error) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt; + struct sxe2_tm_node *tm_node = NULL; + int32_t ret = -EINVAL; + + if (!is_leaf || !error) { + PMD_LOG_ERR(DRV, "Invalid input: is_leaf:0x%p or error:0x%p is null.", + is_leaf, error); + if (error) { + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "Invalid input: is_leaf or error is null"; + } + goto l_end; + } + + if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) { + PMD_LOG_ERR(DRV, "The TM capability is not supported."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "The TM capability is not supported."; + ret = ENOTSUP; + goto l_end; + } + + if (node_id == RTE_TM_NODE_ID_NULL) { + PMD_LOG_ERR(DRV, "Invalid node id."); + error->type = RTE_TM_ERROR_TYPE_NODE_ID; + error->message = "invalid node id"; + goto l_end; + } + + tm_node = sxe2_tm_find_node(tm_ctxt->root, node_id); + if (!tm_node) { + PMD_LOG_ERR(DRV, "No such node."); + error->type = RTE_TM_ERROR_TYPE_NODE_ID; + error->message = "no such node"; + goto l_end; + } + + if (tm_node->level + 1 == tm_ctxt->tm_layers) + *is_leaf = true; + else + *is_leaf = false; + ret = 0; +l_end: + return ret; +} + +int32_t sxe2_tm_uninit(struct rte_eth_dev *dev) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt; + struct sxe2_tm_shaper_profile *shaper_profile = NULL; + int32_t ret = 0; + + if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) { + ret = 0; + goto l_end; + } + + tm_ctxt->tm_layers = 0; + tm_ctxt->root_max_children = 0; + tm_ctxt->committed = false; + + (void)sxe2_tm_tree_delete(tm_ctxt->root); + + while ((shaper_profile = TAILQ_FIRST(&tm_ctxt->profile_list))) { + TAILQ_REMOVE(&tm_ctxt->profile_list, shaper_profile, node); + rte_free(shaper_profile); + } +l_end: + return ret; +} + +int32_t sxe2_tm_init(struct rte_eth_dev *dev) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + struct sxe2_sched_hw_cap *sched_ctxt = &adapter->sched_ctxt; + struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt; + int32_t ret = 0; + struct sxe2_tm_shaper_profile *shaper_profile = NULL; + + if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) + goto l_end; + + tm_ctxt->tm_layers = sched_ctxt->tm_layers; + tm_ctxt->root_max_children = sched_ctxt->root_max_children; + tm_ctxt->prio_max = sched_ctxt->prio_max; + tm_ctxt->committed = false; + TAILQ_INIT(&tm_ctxt->profile_list); + tm_ctxt->root = NULL; + + shaper_profile = rte_zmalloc("sxe2_tm_shaper_profile", + sizeof(struct sxe2_tm_shaper_profile), 0); + if (!shaper_profile) { + PMD_LOG_ERR(DRV, "Alloc shaper_profile memory failed."); + ret = -ENOMEM; + goto l_end; + } + shaper_profile->id = RTE_TM_SHAPER_PROFILE_ID_NONE; + shaper_profile->ref_cnt = 1; + shaper_profile->profile.committed.rate = UINT64_MAX; + shaper_profile->profile.peak.rate = UINT64_MAX; + TAILQ_INSERT_TAIL(&tm_ctxt->profile_list, shaper_profile, node); + +l_end: + return ret; +} + +static int32_t sxe2_tm_chk_all_leaf(struct rte_eth_dev *dev) +{ + int32_t ret = 0; + uint32_t i = 0; + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + for (i = 0; i < dev->data->nb_tx_queues; i++) { + if (!sxe2_tm_find_node(adapter->tm_ctxt.root, i)) { + ret = -1; + break; + } + } + return ret; +} + +static int32_t sxe2_tm_weight_calc(struct sxe2_tm_node *tm_node) +{ + int32_t ret = 0; + int32_t total_weight = 0; + int32_t total_weight2 = 0; + uint32_t i = 0; + uint32_t j = 0; + uint32_t k = 0; + uint32_t maxindex = 0; + uint32_t maxweight = 0; + struct sxe2_tm_node *cacl_node[SXE2_TM_MAX_CHILDREN_COUNT] = {NULL}; + + if (!tm_node) { + PMD_LOG_ERR(DRV, "Invalid input: tm_node is null."); + ret = -EINVAL; + goto l_end; + } + + if (tm_node->child_cnt == 0) + goto l_end; + + for (j = SXE2_TM_PRIO_MIN; j <= SXE2_TM_PRIO_MAX; j++) { + k = 0; + total_weight = 0; + total_weight2 = 0; + maxindex = 0; + maxweight = 0; + + for (i = 0; i < tm_node->child_cnt; i++) { + if (tm_node->children[i]->priority == j) + cacl_node[k++] = tm_node->children[i]; + } + if (k == 0) + continue; + + for (i = 0; i < k; i++) + total_weight += cacl_node[i]->weight; + + for (i = 0; i < k; i++) { + cacl_node[i]->hw_weight = cacl_node[i]->weight * + SXE2_TM_WEIGHT_SUM / total_weight; + total_weight2 += cacl_node[i]->hw_weight; + if (cacl_node[i]->hw_weight > maxweight) { + maxweight = cacl_node[i]->hw_weight; + maxindex = i; + } + } + + cacl_node[maxindex]->hw_weight += SXE2_TM_WEIGHT_SUM - total_weight2; + } + + for (i = 0; i < tm_node->child_cnt; i++) { + ret = sxe2_tm_weight_calc(tm_node->children[i]); + if (ret) + goto l_end; + } + +l_end: + return ret; +} + +static int32_t sxe2_tm_hierarchy_commit(struct rte_eth_dev *dev, + int32_t clear_on_fail, struct rte_tm_error *error) +{ + struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev); + int32_t ret = -EINVAL; + + if (!error) { + PMD_LOG_ERR(DRV, "Invalid input: error is null."); + ret = -EINVAL; + goto l_clear_on_fail; + } + + if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) { + PMD_LOG_ERR(DRV, "The TM capability is not supported."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "The TM capability is not supported."; + ret = ENOTSUP; + goto l_clear_on_fail; + } + + if (dev->data->dev_started) { + PMD_LOG_ERR(DRV, "Device failed to Stop."); + error->message = "Device failed to Stop"; + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + ret = -EPERM; + goto l_clear_on_fail; + } + + ret = sxe2_tm_chk_all_leaf(dev); + if (ret) { + PMD_LOG_ERR(DRV, "All tx queues need config."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "All tx queues need config."; + goto l_clear_on_fail; + } + + ret = sxe2_tm_weight_calc(adapter->tm_ctxt.root); + if (ret) { + PMD_LOG_ERR(DRV, "The weight in tree is wrong."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "The weight in tree is wrong."; + goto l_clear_on_fail; + } + + ret = sxe2_drv_tm_commit(adapter); + if (ret) { + PMD_LOG_ERR(DRV, "Commit tree to fw failed."); + error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED; + error->message = "Commit tree to fw failed."; + goto l_clear_on_fail; + } + + adapter->tm_ctxt.committed = true; + ret = 0; + goto l_end; + +l_clear_on_fail: + if (clear_on_fail) { + (void)sxe2_tm_uninit(dev); + (void)sxe2_tm_init(dev); + } + +l_end: + return ret; +} + +static const struct rte_tm_ops sxe2_tm_ops = { + .capabilities_get = sxe2_tm_capabilities_get, + .level_capabilities_get = sxe2_level_capabilities_get, + .node_capabilities_get = sxe2_node_capabilities_get, + .shaper_profile_add = sxe2_tm_shaper_profile_add, + .shaper_profile_delete = sxe2_tm_shaper_profile_del, + .node_add = sxe2_tm_node_add, + .node_delete = sxe2_tm_node_delete, + .node_type_get = sxe2_tm_node_type_get, + + .hierarchy_commit = sxe2_tm_hierarchy_commit, +}; + +int32_t sxe2_tm_ops_get(struct rte_eth_dev *dev __rte_unused, void *arg) +{ + int32_t ret = 0; + + if (!arg) { + ret = -EINVAL; + PMD_LOG_ERR(DRV, "%s failed because arg is NULL", __func__); + goto l_end; + } + *(const void **)arg = &sxe2_tm_ops; +l_end: + return ret; +} diff --git a/drivers/net/sxe2/sxe2_tm.h b/drivers/net/sxe2/sxe2_tm.h new file mode 100644 index 0000000000..c4f8da6a8e --- /dev/null +++ b/drivers/net/sxe2/sxe2_tm.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd. + */ + +#ifndef __SXE2_TM_H__ +#define __SXE2_TM_H__ +#include <ethdev_driver.h> +#include <rte_flow_driver.h> +#include "sxe2_osal.h" + +#define SXE2_TM_MAX_LEVEL 7 +#define SXE2_TM_1L_NODE_NUM_MAX 1 +#define SXE2_TM_2L_NODE_NUM_MAX 8 +#define SXE2_TM_3L_NODE_NUM_MAX 64 +#define SXE2_TM_4L_NODE_NUM_MAX 256 + +#define SXE2_TM_MAX_CHILDREN_COUNT 8 + +#define SXE2_TM_WEIGHT_MAX (200) +#define SXE2_TM_WEIGHT_MIN (1) +#define SXE2_TM_WEIGHT_SUM (32768) + +#define SXE2_HW_RATE_MIN 62500ull +#define SXE2_HW_RATE_MAX 12500000000ull + +#define SXE2_TM_PRIO_MAX (7) +#define SXE2_TM_PRIO_MIN (0) + +enum sxe2_tm_node_type { + SXE2_TM_NODE_TYPE_VSIG = 0, + SXE2_TM_NODE_TYPE_MID, + SXE2_TM_NODE_TYPE_QUEUE, + SXE2_TM_NODE_TYPE_INVALID, +}; + +struct sxe2_tm_shaper_profile { + TAILQ_ENTRY(sxe2_tm_shaper_profile) node; + uint32_t id; + uint32_t ref_cnt; + struct rte_tm_shaper_params profile; +}; + +TAILQ_HEAD(sxe2_shaper_profile_list, sxe2_tm_shaper_profile); + +struct sxe2_tm_node { + uint16_t id; + uint16_t teid; + uint32_t level; + uint32_t child_cnt; + uint32_t type; + uint16_t hw_weight; + uint16_t weight; + uint8_t priority; + struct sxe2_tm_node *parent; + uint8_t index_in_parent; + struct sxe2_tm_node *children[SXE2_TM_MAX_CHILDREN_COUNT]; + struct sxe2_tm_shaper_profile *shaper_profile; +}; + +struct sxe2_tm_context { + uint32_t tm_layers; + uint16_t root_teid; + uint8_t root_max_children; + uint8_t prio_max; + bool committed; + struct sxe2_tm_node *root; + struct sxe2_shaper_profile_list profile_list; +}; + +int32_t sxe2_tm_ops_get(struct rte_eth_dev *dev __rte_unused, void *arg); + +int32_t sxe2_tm_init(struct rte_eth_dev *dev); + +int32_t sxe2_tm_uninit(struct rte_eth_dev *dev); + +#endif /* __SXE2_TM_H__ */ diff --git a/drivers/net/sxe2/sxe2_tx.c b/drivers/net/sxe2/sxe2_tx.c index a05beb8c7a..a280edc9c5 100644 --- a/drivers/net/sxe2/sxe2_tx.c +++ b/drivers/net/sxe2/sxe2_tx.c @@ -304,7 +304,6 @@ int32_t __rte_cold sxe2_tx_queue_setup(struct rte_eth_dev *dev, } offloads = tx_conf->offloads | dev->data->dev_conf.txmode.offloads; - txq = sxe2_tx_queue_alloc(dev, queue_idx, nb_desc, socket_id); if (txq == NULL) { PMD_LOG_ERR(TX, "failed to alloc sxe2vf tx queue:%u resource", queue_idx); -- 2.47.3

