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