162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or Linux-OpenIB 262306a36Sopenharmony_ci/* Copyright (c) 2017 - 2021 Intel Corporation */ 362306a36Sopenharmony_ci#include "osdep.h" 462306a36Sopenharmony_ci#include "hmc.h" 562306a36Sopenharmony_ci#include "defs.h" 662306a36Sopenharmony_ci#include "type.h" 762306a36Sopenharmony_ci#include "protos.h" 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include "ws.h" 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci/** 1262306a36Sopenharmony_ci * irdma_alloc_node - Allocate a WS node and init 1362306a36Sopenharmony_ci * @vsi: vsi pointer 1462306a36Sopenharmony_ci * @user_pri: user priority 1562306a36Sopenharmony_ci * @node_type: Type of node, leaf or parent 1662306a36Sopenharmony_ci * @parent: parent node pointer 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_cistatic struct irdma_ws_node *irdma_alloc_node(struct irdma_sc_vsi *vsi, 1962306a36Sopenharmony_ci u8 user_pri, 2062306a36Sopenharmony_ci enum irdma_ws_node_type node_type, 2162306a36Sopenharmony_ci struct irdma_ws_node *parent) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci struct irdma_virt_mem ws_mem; 2462306a36Sopenharmony_ci struct irdma_ws_node *node; 2562306a36Sopenharmony_ci u16 node_index = 0; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci ws_mem.size = sizeof(struct irdma_ws_node); 2862306a36Sopenharmony_ci ws_mem.va = kzalloc(ws_mem.size, GFP_KERNEL); 2962306a36Sopenharmony_ci if (!ws_mem.va) 3062306a36Sopenharmony_ci return NULL; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci if (parent) { 3362306a36Sopenharmony_ci node_index = irdma_alloc_ws_node_id(vsi->dev); 3462306a36Sopenharmony_ci if (node_index == IRDMA_WS_NODE_INVALID) { 3562306a36Sopenharmony_ci kfree(ws_mem.va); 3662306a36Sopenharmony_ci return NULL; 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci node = ws_mem.va; 4162306a36Sopenharmony_ci node->index = node_index; 4262306a36Sopenharmony_ci node->vsi_index = vsi->vsi_idx; 4362306a36Sopenharmony_ci INIT_LIST_HEAD(&node->child_list_head); 4462306a36Sopenharmony_ci if (node_type == WS_NODE_TYPE_LEAF) { 4562306a36Sopenharmony_ci node->type_leaf = true; 4662306a36Sopenharmony_ci node->traffic_class = vsi->qos[user_pri].traffic_class; 4762306a36Sopenharmony_ci node->user_pri = user_pri; 4862306a36Sopenharmony_ci node->rel_bw = vsi->qos[user_pri].rel_bw; 4962306a36Sopenharmony_ci if (!node->rel_bw) 5062306a36Sopenharmony_ci node->rel_bw = 1; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci node->lan_qs_handle = vsi->qos[user_pri].lan_qos_handle; 5362306a36Sopenharmony_ci node->prio_type = IRDMA_PRIO_WEIGHTED_RR; 5462306a36Sopenharmony_ci } else { 5562306a36Sopenharmony_ci node->rel_bw = 1; 5662306a36Sopenharmony_ci node->prio_type = IRDMA_PRIO_WEIGHTED_RR; 5762306a36Sopenharmony_ci node->enable = true; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci node->parent = parent; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci return node; 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci/** 6662306a36Sopenharmony_ci * irdma_free_node - Free a WS node 6762306a36Sopenharmony_ci * @vsi: VSI stricture of device 6862306a36Sopenharmony_ci * @node: Pointer to node to free 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_cistatic void irdma_free_node(struct irdma_sc_vsi *vsi, 7162306a36Sopenharmony_ci struct irdma_ws_node *node) 7262306a36Sopenharmony_ci{ 7362306a36Sopenharmony_ci struct irdma_virt_mem ws_mem; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci if (node->index) 7662306a36Sopenharmony_ci irdma_free_ws_node_id(vsi->dev, node->index); 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci ws_mem.va = node; 7962306a36Sopenharmony_ci ws_mem.size = sizeof(struct irdma_ws_node); 8062306a36Sopenharmony_ci kfree(ws_mem.va); 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci/** 8462306a36Sopenharmony_ci * irdma_ws_cqp_cmd - Post CQP work scheduler node cmd 8562306a36Sopenharmony_ci * @vsi: vsi pointer 8662306a36Sopenharmony_ci * @node: pointer to node 8762306a36Sopenharmony_ci * @cmd: add, remove or modify 8862306a36Sopenharmony_ci */ 8962306a36Sopenharmony_cistatic int irdma_ws_cqp_cmd(struct irdma_sc_vsi *vsi, 9062306a36Sopenharmony_ci struct irdma_ws_node *node, u8 cmd) 9162306a36Sopenharmony_ci{ 9262306a36Sopenharmony_ci struct irdma_ws_node_info node_info = {}; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci node_info.id = node->index; 9562306a36Sopenharmony_ci node_info.vsi = node->vsi_index; 9662306a36Sopenharmony_ci if (node->parent) 9762306a36Sopenharmony_ci node_info.parent_id = node->parent->index; 9862306a36Sopenharmony_ci else 9962306a36Sopenharmony_ci node_info.parent_id = node_info.id; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci node_info.weight = node->rel_bw; 10262306a36Sopenharmony_ci node_info.tc = node->traffic_class; 10362306a36Sopenharmony_ci node_info.prio_type = node->prio_type; 10462306a36Sopenharmony_ci node_info.type_leaf = node->type_leaf; 10562306a36Sopenharmony_ci node_info.enable = node->enable; 10662306a36Sopenharmony_ci if (irdma_cqp_ws_node_cmd(vsi->dev, cmd, &node_info)) { 10762306a36Sopenharmony_ci ibdev_dbg(to_ibdev(vsi->dev), "WS: CQP WS CMD failed\n"); 10862306a36Sopenharmony_ci return -ENOMEM; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (node->type_leaf && cmd == IRDMA_OP_WS_ADD_NODE) { 11262306a36Sopenharmony_ci node->qs_handle = node_info.qs_handle; 11362306a36Sopenharmony_ci vsi->qos[node->user_pri].qs_handle = node_info.qs_handle; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/** 12062306a36Sopenharmony_ci * ws_find_node - Find SC WS node based on VSI id or TC 12162306a36Sopenharmony_ci * @parent: parent node of First VSI or TC node 12262306a36Sopenharmony_ci * @match_val: value to match 12362306a36Sopenharmony_ci * @type: match type VSI/TC 12462306a36Sopenharmony_ci */ 12562306a36Sopenharmony_cistatic struct irdma_ws_node *ws_find_node(struct irdma_ws_node *parent, 12662306a36Sopenharmony_ci u16 match_val, 12762306a36Sopenharmony_ci enum irdma_ws_match_type type) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci struct irdma_ws_node *node; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci switch (type) { 13262306a36Sopenharmony_ci case WS_MATCH_TYPE_VSI: 13362306a36Sopenharmony_ci list_for_each_entry(node, &parent->child_list_head, siblings) { 13462306a36Sopenharmony_ci if (node->vsi_index == match_val) 13562306a36Sopenharmony_ci return node; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci case WS_MATCH_TYPE_TC: 13962306a36Sopenharmony_ci list_for_each_entry(node, &parent->child_list_head, siblings) { 14062306a36Sopenharmony_ci if (node->traffic_class == match_val) 14162306a36Sopenharmony_ci return node; 14262306a36Sopenharmony_ci } 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci default: 14562306a36Sopenharmony_ci break; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return NULL; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/** 15262306a36Sopenharmony_ci * irdma_tc_in_use - Checks to see if a leaf node is in use 15362306a36Sopenharmony_ci * @vsi: vsi pointer 15462306a36Sopenharmony_ci * @user_pri: user priority 15562306a36Sopenharmony_ci */ 15662306a36Sopenharmony_cistatic bool irdma_tc_in_use(struct irdma_sc_vsi *vsi, u8 user_pri) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci int i; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci mutex_lock(&vsi->qos[user_pri].qos_mutex); 16162306a36Sopenharmony_ci if (!list_empty(&vsi->qos[user_pri].qplist)) { 16262306a36Sopenharmony_ci mutex_unlock(&vsi->qos[user_pri].qos_mutex); 16362306a36Sopenharmony_ci return true; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci /* Check if the traffic class associated with the given user priority 16762306a36Sopenharmony_ci * is in use by any other user priority. If so, nothing left to do 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) { 17062306a36Sopenharmony_ci if (vsi->qos[i].traffic_class == vsi->qos[user_pri].traffic_class && 17162306a36Sopenharmony_ci !list_empty(&vsi->qos[i].qplist)) { 17262306a36Sopenharmony_ci mutex_unlock(&vsi->qos[user_pri].qos_mutex); 17362306a36Sopenharmony_ci return true; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci mutex_unlock(&vsi->qos[user_pri].qos_mutex); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci return false; 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci/** 18262306a36Sopenharmony_ci * irdma_remove_leaf - Remove leaf node unconditionally 18362306a36Sopenharmony_ci * @vsi: vsi pointer 18462306a36Sopenharmony_ci * @user_pri: user priority 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistatic void irdma_remove_leaf(struct irdma_sc_vsi *vsi, u8 user_pri) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci struct irdma_ws_node *ws_tree_root, *vsi_node, *tc_node; 18962306a36Sopenharmony_ci int i; 19062306a36Sopenharmony_ci u16 traffic_class; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci traffic_class = vsi->qos[user_pri].traffic_class; 19362306a36Sopenharmony_ci for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) 19462306a36Sopenharmony_ci if (vsi->qos[i].traffic_class == traffic_class) 19562306a36Sopenharmony_ci vsi->qos[i].valid = false; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci ws_tree_root = vsi->dev->ws_tree_root; 19862306a36Sopenharmony_ci if (!ws_tree_root) 19962306a36Sopenharmony_ci return; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx, 20262306a36Sopenharmony_ci WS_MATCH_TYPE_VSI); 20362306a36Sopenharmony_ci if (!vsi_node) 20462306a36Sopenharmony_ci return; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci tc_node = ws_find_node(vsi_node, 20762306a36Sopenharmony_ci vsi->qos[user_pri].traffic_class, 20862306a36Sopenharmony_ci WS_MATCH_TYPE_TC); 20962306a36Sopenharmony_ci if (!tc_node) 21062306a36Sopenharmony_ci return; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE); 21362306a36Sopenharmony_ci vsi->unregister_qset(vsi, tc_node); 21462306a36Sopenharmony_ci list_del(&tc_node->siblings); 21562306a36Sopenharmony_ci irdma_free_node(vsi, tc_node); 21662306a36Sopenharmony_ci /* Check if VSI node can be freed */ 21762306a36Sopenharmony_ci if (list_empty(&vsi_node->child_list_head)) { 21862306a36Sopenharmony_ci irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE); 21962306a36Sopenharmony_ci list_del(&vsi_node->siblings); 22062306a36Sopenharmony_ci irdma_free_node(vsi, vsi_node); 22162306a36Sopenharmony_ci /* Free head node there are no remaining VSI nodes */ 22262306a36Sopenharmony_ci if (list_empty(&ws_tree_root->child_list_head)) { 22362306a36Sopenharmony_ci irdma_ws_cqp_cmd(vsi, ws_tree_root, 22462306a36Sopenharmony_ci IRDMA_OP_WS_DELETE_NODE); 22562306a36Sopenharmony_ci irdma_free_node(vsi, ws_tree_root); 22662306a36Sopenharmony_ci vsi->dev->ws_tree_root = NULL; 22762306a36Sopenharmony_ci } 22862306a36Sopenharmony_ci } 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/** 23262306a36Sopenharmony_ci * irdma_ws_add - Build work scheduler tree, set RDMA qs_handle 23362306a36Sopenharmony_ci * @vsi: vsi pointer 23462306a36Sopenharmony_ci * @user_pri: user priority 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_ciint irdma_ws_add(struct irdma_sc_vsi *vsi, u8 user_pri) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci struct irdma_ws_node *ws_tree_root; 23962306a36Sopenharmony_ci struct irdma_ws_node *vsi_node; 24062306a36Sopenharmony_ci struct irdma_ws_node *tc_node; 24162306a36Sopenharmony_ci u16 traffic_class; 24262306a36Sopenharmony_ci int ret = 0; 24362306a36Sopenharmony_ci int i; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci mutex_lock(&vsi->dev->ws_mutex); 24662306a36Sopenharmony_ci if (vsi->tc_change_pending) { 24762306a36Sopenharmony_ci ret = -EBUSY; 24862306a36Sopenharmony_ci goto exit; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci if (vsi->qos[user_pri].valid) 25262306a36Sopenharmony_ci goto exit; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci ws_tree_root = vsi->dev->ws_tree_root; 25562306a36Sopenharmony_ci if (!ws_tree_root) { 25662306a36Sopenharmony_ci ibdev_dbg(to_ibdev(vsi->dev), "WS: Creating root node\n"); 25762306a36Sopenharmony_ci ws_tree_root = irdma_alloc_node(vsi, user_pri, 25862306a36Sopenharmony_ci WS_NODE_TYPE_PARENT, NULL); 25962306a36Sopenharmony_ci if (!ws_tree_root) { 26062306a36Sopenharmony_ci ret = -ENOMEM; 26162306a36Sopenharmony_ci goto exit; 26262306a36Sopenharmony_ci } 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci ret = irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_ADD_NODE); 26562306a36Sopenharmony_ci if (ret) { 26662306a36Sopenharmony_ci irdma_free_node(vsi, ws_tree_root); 26762306a36Sopenharmony_ci goto exit; 26862306a36Sopenharmony_ci } 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci vsi->dev->ws_tree_root = ws_tree_root; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci /* Find a second tier node that matches the VSI */ 27462306a36Sopenharmony_ci vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx, 27562306a36Sopenharmony_ci WS_MATCH_TYPE_VSI); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* If VSI node doesn't exist, add one */ 27862306a36Sopenharmony_ci if (!vsi_node) { 27962306a36Sopenharmony_ci ibdev_dbg(to_ibdev(vsi->dev), 28062306a36Sopenharmony_ci "WS: Node not found matching VSI %d\n", 28162306a36Sopenharmony_ci vsi->vsi_idx); 28262306a36Sopenharmony_ci vsi_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_PARENT, 28362306a36Sopenharmony_ci ws_tree_root); 28462306a36Sopenharmony_ci if (!vsi_node) { 28562306a36Sopenharmony_ci ret = -ENOMEM; 28662306a36Sopenharmony_ci goto vsi_add_err; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci ret = irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_ADD_NODE); 29062306a36Sopenharmony_ci if (ret) { 29162306a36Sopenharmony_ci irdma_free_node(vsi, vsi_node); 29262306a36Sopenharmony_ci goto vsi_add_err; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci list_add(&vsi_node->siblings, &ws_tree_root->child_list_head); 29662306a36Sopenharmony_ci } 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci ibdev_dbg(to_ibdev(vsi->dev), 29962306a36Sopenharmony_ci "WS: Using node %d which represents VSI %d\n", 30062306a36Sopenharmony_ci vsi_node->index, vsi->vsi_idx); 30162306a36Sopenharmony_ci traffic_class = vsi->qos[user_pri].traffic_class; 30262306a36Sopenharmony_ci tc_node = ws_find_node(vsi_node, traffic_class, 30362306a36Sopenharmony_ci WS_MATCH_TYPE_TC); 30462306a36Sopenharmony_ci if (!tc_node) { 30562306a36Sopenharmony_ci /* Add leaf node */ 30662306a36Sopenharmony_ci ibdev_dbg(to_ibdev(vsi->dev), 30762306a36Sopenharmony_ci "WS: Node not found matching VSI %d and TC %d\n", 30862306a36Sopenharmony_ci vsi->vsi_idx, traffic_class); 30962306a36Sopenharmony_ci tc_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_LEAF, 31062306a36Sopenharmony_ci vsi_node); 31162306a36Sopenharmony_ci if (!tc_node) { 31262306a36Sopenharmony_ci ret = -ENOMEM; 31362306a36Sopenharmony_ci goto leaf_add_err; 31462306a36Sopenharmony_ci } 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_ADD_NODE); 31762306a36Sopenharmony_ci if (ret) { 31862306a36Sopenharmony_ci irdma_free_node(vsi, tc_node); 31962306a36Sopenharmony_ci goto leaf_add_err; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci list_add(&tc_node->siblings, &vsi_node->child_list_head); 32362306a36Sopenharmony_ci /* 32462306a36Sopenharmony_ci * callback to LAN to update the LAN tree with our node 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci ret = vsi->register_qset(vsi, tc_node); 32762306a36Sopenharmony_ci if (ret) 32862306a36Sopenharmony_ci goto reg_err; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci tc_node->enable = true; 33162306a36Sopenharmony_ci ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_MODIFY_NODE); 33262306a36Sopenharmony_ci if (ret) { 33362306a36Sopenharmony_ci vsi->unregister_qset(vsi, tc_node); 33462306a36Sopenharmony_ci goto reg_err; 33562306a36Sopenharmony_ci } 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci ibdev_dbg(to_ibdev(vsi->dev), 33862306a36Sopenharmony_ci "WS: Using node %d which represents VSI %d TC %d\n", 33962306a36Sopenharmony_ci tc_node->index, vsi->vsi_idx, traffic_class); 34062306a36Sopenharmony_ci /* 34162306a36Sopenharmony_ci * Iterate through other UPs and update the QS handle if they have 34262306a36Sopenharmony_ci * a matching traffic class. 34362306a36Sopenharmony_ci */ 34462306a36Sopenharmony_ci for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) { 34562306a36Sopenharmony_ci if (vsi->qos[i].traffic_class == traffic_class) { 34662306a36Sopenharmony_ci vsi->qos[i].qs_handle = tc_node->qs_handle; 34762306a36Sopenharmony_ci vsi->qos[i].lan_qos_handle = tc_node->lan_qs_handle; 34862306a36Sopenharmony_ci vsi->qos[i].l2_sched_node_id = tc_node->l2_sched_node_id; 34962306a36Sopenharmony_ci vsi->qos[i].valid = true; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci } 35262306a36Sopenharmony_ci goto exit; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cireg_err: 35562306a36Sopenharmony_ci irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE); 35662306a36Sopenharmony_ci list_del(&tc_node->siblings); 35762306a36Sopenharmony_ci irdma_free_node(vsi, tc_node); 35862306a36Sopenharmony_cileaf_add_err: 35962306a36Sopenharmony_ci if (list_empty(&vsi_node->child_list_head)) { 36062306a36Sopenharmony_ci if (irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE)) 36162306a36Sopenharmony_ci goto exit; 36262306a36Sopenharmony_ci list_del(&vsi_node->siblings); 36362306a36Sopenharmony_ci irdma_free_node(vsi, vsi_node); 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_civsi_add_err: 36762306a36Sopenharmony_ci /* Free head node there are no remaining VSI nodes */ 36862306a36Sopenharmony_ci if (list_empty(&ws_tree_root->child_list_head)) { 36962306a36Sopenharmony_ci irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_DELETE_NODE); 37062306a36Sopenharmony_ci vsi->dev->ws_tree_root = NULL; 37162306a36Sopenharmony_ci irdma_free_node(vsi, ws_tree_root); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ciexit: 37562306a36Sopenharmony_ci mutex_unlock(&vsi->dev->ws_mutex); 37662306a36Sopenharmony_ci return ret; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/** 38062306a36Sopenharmony_ci * irdma_ws_remove - Free WS scheduler node, update WS tree 38162306a36Sopenharmony_ci * @vsi: vsi pointer 38262306a36Sopenharmony_ci * @user_pri: user priority 38362306a36Sopenharmony_ci */ 38462306a36Sopenharmony_civoid irdma_ws_remove(struct irdma_sc_vsi *vsi, u8 user_pri) 38562306a36Sopenharmony_ci{ 38662306a36Sopenharmony_ci mutex_lock(&vsi->dev->ws_mutex); 38762306a36Sopenharmony_ci if (irdma_tc_in_use(vsi, user_pri)) 38862306a36Sopenharmony_ci goto exit; 38962306a36Sopenharmony_ci irdma_remove_leaf(vsi, user_pri); 39062306a36Sopenharmony_ciexit: 39162306a36Sopenharmony_ci mutex_unlock(&vsi->dev->ws_mutex); 39262306a36Sopenharmony_ci} 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci/** 39562306a36Sopenharmony_ci * irdma_ws_reset - Reset entire WS tree 39662306a36Sopenharmony_ci * @vsi: vsi pointer 39762306a36Sopenharmony_ci */ 39862306a36Sopenharmony_civoid irdma_ws_reset(struct irdma_sc_vsi *vsi) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci u8 i; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci mutex_lock(&vsi->dev->ws_mutex); 40362306a36Sopenharmony_ci for (i = 0; i < IRDMA_MAX_USER_PRIORITY; ++i) 40462306a36Sopenharmony_ci irdma_remove_leaf(vsi, i); 40562306a36Sopenharmony_ci mutex_unlock(&vsi->dev->ws_mutex); 40662306a36Sopenharmony_ci} 407