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