1 /*- 2 * SPDX-License-Identifier: GPL-2.0 or Linux-OpenIB 3 * 4 * Copyright (c) 2017 - 2023 Intel Corporation 5 * 6 * This software is available to you under a choice of one of two 7 * licenses. You may choose to be licensed under the terms of the GNU 8 * General Public License (GPL) Version 2, available from the file 9 * COPYING in the main directory of this source tree, or the 10 * OpenFabrics.org BSD license below: 11 * 12 * Redistribution and use in source and binary forms, with or 13 * without modification, are permitted provided that the following 14 * conditions are met: 15 * 16 * - Redistributions of source code must retain the above 17 * copyright notice, this list of conditions and the following 18 * disclaimer. 19 * 20 * - Redistributions in binary form must reproduce the above 21 * copyright notice, this list of conditions and the following 22 * disclaimer in the documentation and/or other materials 23 * provided with the distribution. 24 * 25 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 26 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 28 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 29 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 30 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 31 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 32 * SOFTWARE. 33 */ 34 35 #include "osdep.h" 36 #include "irdma_hmc.h" 37 #include "irdma_defs.h" 38 #include "irdma_type.h" 39 #include "irdma_protos.h" 40 41 #include "irdma_ws.h" 42 43 /** 44 * irdma_alloc_node - Allocate a WS node and init 45 * @vsi: vsi pointer 46 * @user_pri: user priority 47 * @node_type: Type of node, leaf or parent 48 * @parent: parent node pointer 49 */ 50 static struct irdma_ws_node * 51 irdma_alloc_node(struct irdma_sc_vsi *vsi, 52 u8 user_pri, 53 enum irdma_ws_node_type node_type, 54 struct irdma_ws_node *parent) 55 { 56 struct irdma_virt_mem ws_mem; 57 struct irdma_ws_node *node; 58 u16 node_index = 0; 59 60 ws_mem.size = sizeof(*node); 61 ws_mem.va = kzalloc(ws_mem.size, GFP_KERNEL); 62 if (!ws_mem.va) 63 return NULL; 64 65 if (parent) { 66 node_index = irdma_alloc_ws_node_id(vsi->dev); 67 if (node_index == IRDMA_WS_NODE_INVALID) { 68 kfree(ws_mem.va); 69 return NULL; 70 } 71 } 72 73 node = ws_mem.va; 74 node->index = node_index; 75 node->vsi_index = vsi->vsi_idx; 76 INIT_LIST_HEAD(&node->child_list_head); 77 if (node_type == WS_NODE_TYPE_LEAF) { 78 node->type_leaf = true; 79 node->traffic_class = vsi->qos[user_pri].traffic_class; 80 node->user_pri = user_pri; 81 node->rel_bw = vsi->qos[user_pri].rel_bw; 82 if (!node->rel_bw) 83 node->rel_bw = 1; 84 85 node->prio_type = IRDMA_PRIO_WEIGHTED_RR; 86 } else { 87 node->rel_bw = 1; 88 node->prio_type = IRDMA_PRIO_WEIGHTED_RR; 89 node->enable = true; 90 } 91 92 node->parent = parent; 93 94 return node; 95 } 96 97 /** 98 * irdma_free_node - Free a WS node 99 * @vsi: VSI stricture of device 100 * @node: Pointer to node to free 101 */ 102 static void 103 irdma_free_node(struct irdma_sc_vsi *vsi, 104 struct irdma_ws_node *node) 105 { 106 struct irdma_virt_mem ws_mem; 107 108 if (node->index) 109 irdma_free_ws_node_id(vsi->dev, node->index); 110 111 ws_mem.va = node; 112 ws_mem.size = sizeof(*node); 113 kfree(ws_mem.va); 114 } 115 116 /** 117 * irdma_ws_cqp_cmd - Post CQP work scheduler node cmd 118 * @vsi: vsi pointer 119 * @node: pointer to node 120 * @cmd: add, remove or modify 121 */ 122 static int 123 irdma_ws_cqp_cmd(struct irdma_sc_vsi *vsi, 124 struct irdma_ws_node *node, u8 cmd) 125 { 126 struct irdma_ws_node_info node_info = {0}; 127 128 node_info.id = node->index; 129 node_info.vsi = node->vsi_index; 130 if (node->parent) 131 node_info.parent_id = node->parent->index; 132 else 133 node_info.parent_id = node_info.id; 134 135 node_info.weight = node->rel_bw; 136 node_info.tc = node->traffic_class; 137 node_info.prio_type = node->prio_type; 138 node_info.type_leaf = node->type_leaf; 139 node_info.enable = node->enable; 140 if (irdma_cqp_ws_node_cmd(vsi->dev, cmd, &node_info)) { 141 irdma_debug(vsi->dev, IRDMA_DEBUG_WS, "CQP WS CMD failed\n"); 142 return -ENOMEM; 143 } 144 145 if (node->type_leaf && cmd == IRDMA_OP_WS_ADD_NODE) { 146 node->qs_handle = node_info.qs_handle; 147 vsi->qos[node->user_pri].qs_handle = node_info.qs_handle; 148 } 149 150 return 0; 151 } 152 153 /** 154 * ws_find_node - Find SC WS node based on VSI id or TC 155 * @parent: parent node of First VSI or TC node 156 * @match_val: value to match 157 * @type: match type VSI/TC 158 */ 159 static struct irdma_ws_node * 160 ws_find_node(struct irdma_ws_node *parent, 161 u16 match_val, 162 enum irdma_ws_match_type type) 163 { 164 struct irdma_ws_node *node; 165 166 switch (type) { 167 case WS_MATCH_TYPE_VSI: 168 list_for_each_entry(node, &parent->child_list_head, siblings) { 169 if (node->vsi_index == match_val) 170 return node; 171 } 172 break; 173 case WS_MATCH_TYPE_TC: 174 list_for_each_entry(node, &parent->child_list_head, siblings) { 175 if (node->traffic_class == match_val) 176 return node; 177 } 178 break; 179 default: 180 break; 181 } 182 183 return NULL; 184 } 185 186 /** 187 * irdma_ws_in_use - Checks to see if a leaf node is in use 188 * @vsi: vsi pointer 189 * @user_pri: user priority 190 */ 191 static bool 192 irdma_ws_in_use(struct irdma_sc_vsi *vsi, u8 user_pri) 193 { 194 int i; 195 196 mutex_lock(&vsi->qos[user_pri].qos_mutex); 197 if (!list_empty(&vsi->qos[user_pri].qplist)) { 198 mutex_unlock(&vsi->qos[user_pri].qos_mutex); 199 return true; 200 } 201 202 /* 203 * Check if the qs handle associated with the given user priority is in use by any other user priority. If so, 204 * nothing left to do 205 */ 206 for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) { 207 if (vsi->qos[i].qs_handle == vsi->qos[user_pri].qs_handle && 208 !list_empty(&vsi->qos[i].qplist)) { 209 mutex_unlock(&vsi->qos[user_pri].qos_mutex); 210 return true; 211 } 212 } 213 mutex_unlock(&vsi->qos[user_pri].qos_mutex); 214 215 return false; 216 } 217 218 /** 219 * irdma_remove_leaf - Remove leaf node unconditionally 220 * @vsi: vsi pointer 221 * @user_pri: user priority 222 */ 223 static void 224 irdma_remove_leaf(struct irdma_sc_vsi *vsi, u8 user_pri) 225 { 226 struct irdma_ws_node *ws_tree_root, *vsi_node, *tc_node; 227 u16 qs_handle; 228 int i; 229 230 qs_handle = vsi->qos[user_pri].qs_handle; 231 for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) 232 if (vsi->qos[i].qs_handle == qs_handle) 233 vsi->qos[i].valid = false; 234 235 ws_tree_root = vsi->dev->ws_tree_root; 236 if (!ws_tree_root) 237 return; 238 239 vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx, 240 WS_MATCH_TYPE_VSI); 241 if (!vsi_node) 242 return; 243 244 tc_node = ws_find_node(vsi_node, 245 vsi->qos[user_pri].traffic_class, 246 WS_MATCH_TYPE_TC); 247 if (!tc_node) 248 return; 249 250 irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE); 251 vsi->unregister_qset(vsi, tc_node); 252 list_del(&tc_node->siblings); 253 irdma_free_node(vsi, tc_node); 254 /* Check if VSI node can be freed */ 255 if (list_empty(&vsi_node->child_list_head)) { 256 irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE); 257 list_del(&vsi_node->siblings); 258 irdma_free_node(vsi, vsi_node); 259 /* Free head node there are no remaining VSI nodes */ 260 if (list_empty(&ws_tree_root->child_list_head)) { 261 irdma_ws_cqp_cmd(vsi, ws_tree_root, 262 IRDMA_OP_WS_DELETE_NODE); 263 irdma_free_node(vsi, ws_tree_root); 264 vsi->dev->ws_tree_root = NULL; 265 } 266 } 267 } 268 269 /** 270 * irdma_ws_add - Build work scheduler tree, set RDMA qs_handle 271 * @vsi: vsi pointer 272 * @user_pri: user priority 273 */ 274 int 275 irdma_ws_add(struct irdma_sc_vsi *vsi, u8 user_pri) 276 { 277 struct irdma_ws_node *ws_tree_root; 278 struct irdma_ws_node *vsi_node; 279 struct irdma_ws_node *tc_node; 280 u16 traffic_class; 281 int ret = 0; 282 int i; 283 284 mutex_lock(&vsi->dev->ws_mutex); 285 if (vsi->tc_change_pending) { 286 ret = -EBUSY; 287 goto exit; 288 } 289 290 if (vsi->qos[user_pri].valid) 291 goto exit; 292 293 ws_tree_root = vsi->dev->ws_tree_root; 294 if (!ws_tree_root) { 295 ws_tree_root = irdma_alloc_node(vsi, user_pri, 296 WS_NODE_TYPE_PARENT, NULL); 297 if (!ws_tree_root) { 298 ret = -ENOMEM; 299 goto exit; 300 } 301 irdma_debug(vsi->dev, IRDMA_DEBUG_WS, "Creating root node = %d\n", ws_tree_root->index); 302 303 ret = irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_ADD_NODE); 304 if (ret) { 305 irdma_free_node(vsi, ws_tree_root); 306 goto exit; 307 } 308 309 vsi->dev->ws_tree_root = ws_tree_root; 310 } 311 312 /* Find a second tier node that matches the VSI */ 313 vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx, 314 WS_MATCH_TYPE_VSI); 315 316 /* If VSI node doesn't exist, add one */ 317 if (!vsi_node) { 318 irdma_debug(vsi->dev, IRDMA_DEBUG_WS, 319 "Node not found matching VSI %d\n", vsi->vsi_idx); 320 vsi_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_PARENT, 321 ws_tree_root); 322 if (!vsi_node) { 323 ret = -ENOMEM; 324 goto vsi_add_err; 325 } 326 327 ret = irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_ADD_NODE); 328 if (ret) { 329 irdma_free_node(vsi, vsi_node); 330 goto vsi_add_err; 331 } 332 333 list_add(&vsi_node->siblings, &ws_tree_root->child_list_head); 334 } 335 336 irdma_debug(vsi->dev, IRDMA_DEBUG_WS, 337 "Using node %d which represents VSI %d\n", vsi_node->index, 338 vsi->vsi_idx); 339 traffic_class = vsi->qos[user_pri].traffic_class; 340 tc_node = ws_find_node(vsi_node, traffic_class, 341 WS_MATCH_TYPE_TC); 342 if (!tc_node) { 343 /* Add leaf node */ 344 irdma_debug(vsi->dev, IRDMA_DEBUG_WS, 345 "Node not found matching VSI %d and TC %d\n", 346 vsi->vsi_idx, traffic_class); 347 tc_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_LEAF, 348 vsi_node); 349 if (!tc_node) { 350 ret = -ENOMEM; 351 goto leaf_add_err; 352 } 353 354 ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_ADD_NODE); 355 if (ret) { 356 irdma_free_node(vsi, tc_node); 357 goto leaf_add_err; 358 } 359 360 list_add(&tc_node->siblings, &vsi_node->child_list_head); 361 /* 362 * callback to LAN to update the LAN tree with our node 363 */ 364 ret = vsi->register_qset(vsi, tc_node); 365 if (ret) 366 goto reg_err; 367 368 tc_node->enable = true; 369 ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_MODIFY_NODE); 370 if (ret) { 371 vsi->unregister_qset(vsi, tc_node); 372 goto reg_err; 373 } 374 } 375 irdma_debug(vsi->dev, IRDMA_DEBUG_WS, 376 "Using node %d which represents VSI %d TC %d\n", 377 tc_node->index, vsi->vsi_idx, traffic_class); 378 /* 379 * Iterate through other UPs and update the QS handle if they have a matching traffic class. 380 */ 381 for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) { 382 if (vsi->qos[i].traffic_class == traffic_class) { 383 vsi->qos[i].qs_handle = tc_node->qs_handle; 384 vsi->qos[i].l2_sched_node_id = tc_node->l2_sched_node_id; 385 vsi->qos[i].valid = true; 386 } 387 } 388 goto exit; 389 390 reg_err: 391 irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE); 392 list_del(&tc_node->siblings); 393 irdma_free_node(vsi, tc_node); 394 leaf_add_err: 395 if (list_empty(&vsi_node->child_list_head)) { 396 if (irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE)) 397 goto exit; 398 list_del(&vsi_node->siblings); 399 irdma_free_node(vsi, vsi_node); 400 } 401 402 vsi_add_err: 403 /* Free head node there are no remaining VSI nodes */ 404 if (list_empty(&ws_tree_root->child_list_head)) { 405 irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_DELETE_NODE); 406 vsi->dev->ws_tree_root = NULL; 407 irdma_free_node(vsi, ws_tree_root); 408 } 409 410 exit: 411 mutex_unlock(&vsi->dev->ws_mutex); 412 return ret; 413 } 414 415 /** 416 * irdma_ws_remove - Free WS scheduler node, update WS tree 417 * @vsi: vsi pointer 418 * @user_pri: user priority 419 */ 420 void 421 irdma_ws_remove(struct irdma_sc_vsi *vsi, u8 user_pri) 422 { 423 mutex_lock(&vsi->dev->ws_mutex); 424 if (irdma_ws_in_use(vsi, user_pri)) 425 goto exit; 426 irdma_remove_leaf(vsi, user_pri); 427 exit: 428 mutex_unlock(&vsi->dev->ws_mutex); 429 } 430 431 /** 432 * irdma_ws_reset - Reset entire WS tree 433 * @vsi: vsi pointer 434 */ 435 void 436 irdma_ws_reset(struct irdma_sc_vsi *vsi) 437 { 438 u8 i; 439 440 mutex_lock(&vsi->dev->ws_mutex); 441 for (i = 0; i < IRDMA_MAX_USER_PRIORITY; ++i) 442 irdma_remove_leaf(vsi, i); 443 mutex_unlock(&vsi->dev->ws_mutex); 444 } 445