ws.c (10232B)
1// SPDX-License-Identifier: GPL-2.0 or Linux-OpenIB 2/* 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 init 13 * @vsi: vsi pointer 14 * @user_pri: user priority 15 * @node_type: Type of node, leaf or parent 16 * @parent: parent node pointer 17 */ 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 node 67 * @vsi: VSI stricture of device 68 * @node: Pointer to node to free 69 */ 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 cmd 85 * @vsi: vsi pointer 86 * @node: pointer to node 87 * @cmd: add, remove or modify 88 */ 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 else 99 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 TC 121 * @parent: parent node of First VSI or TC node 122 * @match_val: value to match 123 * @type: match type VSI/TC 124 */ 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 use 153 * @vsi: vsi pointer 154 * @user_pri: user priority 155 */ 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 priority 167 * is in use by any other user priority. If so, nothing left to do 168 */ 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 unconditionally 183 * @vsi: vsi pointer 184 * @user_pri: user priority 185 */ 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_handle 233 * @vsi: vsi pointer 234 * @user_pri: user priority 235 */ 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 node 325 */ 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 have 342 * 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 tree 381 * @vsi: vsi pointer 382 * @user_pri: user priority 383 */ 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 tree 396 * @vsi: vsi pointer 397 */ 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}