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