407 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB2/* Copyright (c) 2017 - 2021 Intel Corporation */3#include "osdep.h"4#include "hmc.h"5#include "defs.h"6#include "type.h"7#include "protos.h"8 9#include "ws.h"10 11/**12 * irdma_alloc_node - Allocate a WS node and init13 * @vsi: vsi pointer14 * @user_pri: user priority15 * @node_type: Type of node, leaf or parent16 * @parent: parent node pointer17 */18static struct irdma_ws_node *irdma_alloc_node(struct irdma_sc_vsi *vsi,19 u8 user_pri,20 enum irdma_ws_node_type node_type,21 struct irdma_ws_node *parent)22{23 struct irdma_virt_mem ws_mem;24 struct irdma_ws_node *node;25 u16 node_index = 0;26 27 ws_mem.size = sizeof(struct irdma_ws_node);28 ws_mem.va = kzalloc(ws_mem.size, GFP_KERNEL);29 if (!ws_mem.va)30 return NULL;31 32 if (parent) {33 node_index = irdma_alloc_ws_node_id(vsi->dev);34 if (node_index == IRDMA_WS_NODE_INVALID) {35 kfree(ws_mem.va);36 return NULL;37 }38 }39 40 node = ws_mem.va;41 node->index = node_index;42 node->vsi_index = vsi->vsi_idx;43 INIT_LIST_HEAD(&node->child_list_head);44 if (node_type == WS_NODE_TYPE_LEAF) {45 node->type_leaf = true;46 node->traffic_class = vsi->qos[user_pri].traffic_class;47 node->user_pri = user_pri;48 node->rel_bw = vsi->qos[user_pri].rel_bw;49 if (!node->rel_bw)50 node->rel_bw = 1;51 52 node->lan_qs_handle = vsi->qos[user_pri].lan_qos_handle;53 node->prio_type = IRDMA_PRIO_WEIGHTED_RR;54 } else {55 node->rel_bw = 1;56 node->prio_type = IRDMA_PRIO_WEIGHTED_RR;57 node->enable = true;58 }59 60 node->parent = parent;61 62 return node;63}64 65/**66 * irdma_free_node - Free a WS node67 * @vsi: VSI stricture of device68 * @node: Pointer to node to free69 */70static void irdma_free_node(struct irdma_sc_vsi *vsi,71 struct irdma_ws_node *node)72{73 struct irdma_virt_mem ws_mem;74 75 if (node->index)76 irdma_free_ws_node_id(vsi->dev, node->index);77 78 ws_mem.va = node;79 ws_mem.size = sizeof(struct irdma_ws_node);80 kfree(ws_mem.va);81}82 83/**84 * irdma_ws_cqp_cmd - Post CQP work scheduler node cmd85 * @vsi: vsi pointer86 * @node: pointer to node87 * @cmd: add, remove or modify88 */89static int irdma_ws_cqp_cmd(struct irdma_sc_vsi *vsi,90 struct irdma_ws_node *node, u8 cmd)91{92 struct irdma_ws_node_info node_info = {};93 94 node_info.id = node->index;95 node_info.vsi = node->vsi_index;96 if (node->parent)97 node_info.parent_id = node->parent->index;98 else99 node_info.parent_id = node_info.id;100 101 node_info.weight = node->rel_bw;102 node_info.tc = node->traffic_class;103 node_info.prio_type = node->prio_type;104 node_info.type_leaf = node->type_leaf;105 node_info.enable = node->enable;106 if (irdma_cqp_ws_node_cmd(vsi->dev, cmd, &node_info)) {107 ibdev_dbg(to_ibdev(vsi->dev), "WS: CQP WS CMD failed\n");108 return -ENOMEM;109 }110 111 if (node->type_leaf && cmd == IRDMA_OP_WS_ADD_NODE) {112 node->qs_handle = node_info.qs_handle;113 vsi->qos[node->user_pri].qs_handle = node_info.qs_handle;114 }115 116 return 0;117}118 119/**120 * ws_find_node - Find SC WS node based on VSI id or TC121 * @parent: parent node of First VSI or TC node122 * @match_val: value to match123 * @type: match type VSI/TC124 */125static struct irdma_ws_node *ws_find_node(struct irdma_ws_node *parent,126 u16 match_val,127 enum irdma_ws_match_type type)128{129 struct irdma_ws_node *node;130 131 switch (type) {132 case WS_MATCH_TYPE_VSI:133 list_for_each_entry(node, &parent->child_list_head, siblings) {134 if (node->vsi_index == match_val)135 return node;136 }137 break;138 case WS_MATCH_TYPE_TC:139 list_for_each_entry(node, &parent->child_list_head, siblings) {140 if (node->traffic_class == match_val)141 return node;142 }143 break;144 default:145 break;146 }147 148 return NULL;149}150 151/**152 * irdma_tc_in_use - Checks to see if a leaf node is in use153 * @vsi: vsi pointer154 * @user_pri: user priority155 */156static bool irdma_tc_in_use(struct irdma_sc_vsi *vsi, u8 user_pri)157{158 int i;159 160 mutex_lock(&vsi->qos[user_pri].qos_mutex);161 if (!list_empty(&vsi->qos[user_pri].qplist)) {162 mutex_unlock(&vsi->qos[user_pri].qos_mutex);163 return true;164 }165 166 /* Check if the traffic class associated with the given user priority167 * is in use by any other user priority. If so, nothing left to do168 */169 for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) {170 if (vsi->qos[i].traffic_class == vsi->qos[user_pri].traffic_class &&171 !list_empty(&vsi->qos[i].qplist)) {172 mutex_unlock(&vsi->qos[user_pri].qos_mutex);173 return true;174 }175 }176 mutex_unlock(&vsi->qos[user_pri].qos_mutex);177 178 return false;179}180 181/**182 * irdma_remove_leaf - Remove leaf node unconditionally183 * @vsi: vsi pointer184 * @user_pri: user priority185 */186static void irdma_remove_leaf(struct irdma_sc_vsi *vsi, u8 user_pri)187{188 struct irdma_ws_node *ws_tree_root, *vsi_node, *tc_node;189 int i;190 u16 traffic_class;191 192 traffic_class = vsi->qos[user_pri].traffic_class;193 for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++)194 if (vsi->qos[i].traffic_class == traffic_class)195 vsi->qos[i].valid = false;196 197 ws_tree_root = vsi->dev->ws_tree_root;198 if (!ws_tree_root)199 return;200 201 vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx,202 WS_MATCH_TYPE_VSI);203 if (!vsi_node)204 return;205 206 tc_node = ws_find_node(vsi_node,207 vsi->qos[user_pri].traffic_class,208 WS_MATCH_TYPE_TC);209 if (!tc_node)210 return;211 212 irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE);213 vsi->unregister_qset(vsi, tc_node);214 list_del(&tc_node->siblings);215 irdma_free_node(vsi, tc_node);216 /* Check if VSI node can be freed */217 if (list_empty(&vsi_node->child_list_head)) {218 irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE);219 list_del(&vsi_node->siblings);220 irdma_free_node(vsi, vsi_node);221 /* Free head node there are no remaining VSI nodes */222 if (list_empty(&ws_tree_root->child_list_head)) {223 irdma_ws_cqp_cmd(vsi, ws_tree_root,224 IRDMA_OP_WS_DELETE_NODE);225 irdma_free_node(vsi, ws_tree_root);226 vsi->dev->ws_tree_root = NULL;227 }228 }229}230 231/**232 * irdma_ws_add - Build work scheduler tree, set RDMA qs_handle233 * @vsi: vsi pointer234 * @user_pri: user priority235 */236int irdma_ws_add(struct irdma_sc_vsi *vsi, u8 user_pri)237{238 struct irdma_ws_node *ws_tree_root;239 struct irdma_ws_node *vsi_node;240 struct irdma_ws_node *tc_node;241 u16 traffic_class;242 int ret = 0;243 int i;244 245 mutex_lock(&vsi->dev->ws_mutex);246 if (vsi->tc_change_pending) {247 ret = -EBUSY;248 goto exit;249 }250 251 if (vsi->qos[user_pri].valid)252 goto exit;253 254 ws_tree_root = vsi->dev->ws_tree_root;255 if (!ws_tree_root) {256 ibdev_dbg(to_ibdev(vsi->dev), "WS: Creating root node\n");257 ws_tree_root = irdma_alloc_node(vsi, user_pri,258 WS_NODE_TYPE_PARENT, NULL);259 if (!ws_tree_root) {260 ret = -ENOMEM;261 goto exit;262 }263 264 ret = irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_ADD_NODE);265 if (ret) {266 irdma_free_node(vsi, ws_tree_root);267 goto exit;268 }269 270 vsi->dev->ws_tree_root = ws_tree_root;271 }272 273 /* Find a second tier node that matches the VSI */274 vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx,275 WS_MATCH_TYPE_VSI);276 277 /* If VSI node doesn't exist, add one */278 if (!vsi_node) {279 ibdev_dbg(to_ibdev(vsi->dev),280 "WS: Node not found matching VSI %d\n",281 vsi->vsi_idx);282 vsi_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_PARENT,283 ws_tree_root);284 if (!vsi_node) {285 ret = -ENOMEM;286 goto vsi_add_err;287 }288 289 ret = irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_ADD_NODE);290 if (ret) {291 irdma_free_node(vsi, vsi_node);292 goto vsi_add_err;293 }294 295 list_add(&vsi_node->siblings, &ws_tree_root->child_list_head);296 }297 298 ibdev_dbg(to_ibdev(vsi->dev),299 "WS: Using node %d which represents VSI %d\n",300 vsi_node->index, vsi->vsi_idx);301 traffic_class = vsi->qos[user_pri].traffic_class;302 tc_node = ws_find_node(vsi_node, traffic_class,303 WS_MATCH_TYPE_TC);304 if (!tc_node) {305 /* Add leaf node */306 ibdev_dbg(to_ibdev(vsi->dev),307 "WS: Node not found matching VSI %d and TC %d\n",308 vsi->vsi_idx, traffic_class);309 tc_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_LEAF,310 vsi_node);311 if (!tc_node) {312 ret = -ENOMEM;313 goto leaf_add_err;314 }315 316 ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_ADD_NODE);317 if (ret) {318 irdma_free_node(vsi, tc_node);319 goto leaf_add_err;320 }321 322 list_add(&tc_node->siblings, &vsi_node->child_list_head);323 /*324 * callback to LAN to update the LAN tree with our node325 */326 ret = vsi->register_qset(vsi, tc_node);327 if (ret)328 goto reg_err;329 330 tc_node->enable = true;331 ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_MODIFY_NODE);332 if (ret) {333 vsi->unregister_qset(vsi, tc_node);334 goto reg_err;335 }336 }337 ibdev_dbg(to_ibdev(vsi->dev),338 "WS: Using node %d which represents VSI %d TC %d\n",339 tc_node->index, vsi->vsi_idx, traffic_class);340 /*341 * Iterate through other UPs and update the QS handle if they have342 * a matching traffic class.343 */344 for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) {345 if (vsi->qos[i].traffic_class == traffic_class) {346 vsi->qos[i].qs_handle = tc_node->qs_handle;347 vsi->qos[i].lan_qos_handle = tc_node->lan_qs_handle;348 vsi->qos[i].l2_sched_node_id = tc_node->l2_sched_node_id;349 vsi->qos[i].valid = true;350 }351 }352 goto exit;353 354reg_err:355 irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE);356 list_del(&tc_node->siblings);357 irdma_free_node(vsi, tc_node);358leaf_add_err:359 if (list_empty(&vsi_node->child_list_head)) {360 if (irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE))361 goto exit;362 list_del(&vsi_node->siblings);363 irdma_free_node(vsi, vsi_node);364 }365 366vsi_add_err:367 /* Free head node there are no remaining VSI nodes */368 if (list_empty(&ws_tree_root->child_list_head)) {369 irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_DELETE_NODE);370 vsi->dev->ws_tree_root = NULL;371 irdma_free_node(vsi, ws_tree_root);372 }373 374exit:375 mutex_unlock(&vsi->dev->ws_mutex);376 return ret;377}378 379/**380 * irdma_ws_remove - Free WS scheduler node, update WS tree381 * @vsi: vsi pointer382 * @user_pri: user priority383 */384void irdma_ws_remove(struct irdma_sc_vsi *vsi, u8 user_pri)385{386 mutex_lock(&vsi->dev->ws_mutex);387 if (irdma_tc_in_use(vsi, user_pri))388 goto exit;389 irdma_remove_leaf(vsi, user_pri);390exit:391 mutex_unlock(&vsi->dev->ws_mutex);392}393 394/**395 * irdma_ws_reset - Reset entire WS tree396 * @vsi: vsi pointer397 */398void irdma_ws_reset(struct irdma_sc_vsi *vsi)399{400 u8 i;401 402 mutex_lock(&vsi->dev->ws_mutex);403 for (i = 0; i < IRDMA_MAX_USER_PRIORITY; ++i)404 irdma_remove_leaf(vsi, i);405 mutex_unlock(&vsi->dev->ws_mutex);406}407