1 // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2 /* Copyright (c) 2017 - 2021 Intel Corporation */
12 * irdma_alloc_node - Allocate a WS node and init
14 * @user_pri: user priority
15 * @node_type: Type of node, leaf or parent
16 * @parent: parent node pointer
18 static struct irdma_ws_node *irdma_alloc_node(struct irdma_sc_vsi *vsi,
20 enum irdma_ws_node_type node_type,
21 struct irdma_ws_node *parent)
23 struct irdma_virt_mem ws_mem;
24 struct irdma_ws_node *node;
27 ws_mem.size = sizeof(struct irdma_ws_node);
28 ws_mem.va = kzalloc(ws_mem.size, GFP_KERNEL);
33 node_index = irdma_alloc_ws_node_id(vsi->dev);
34 if (node_index == IRDMA_WS_NODE_INVALID) {
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;
52 node->lan_qs_handle = vsi->qos[user_pri].lan_qos_handle;
53 node->prio_type = IRDMA_PRIO_WEIGHTED_RR;
56 node->prio_type = IRDMA_PRIO_WEIGHTED_RR;
60 node->parent = parent;
66 * irdma_free_node - Free a WS node
67 * @vsi: VSI stricture of device
68 * @node: Pointer to node to free
70 static void irdma_free_node(struct irdma_sc_vsi *vsi,
71 struct irdma_ws_node *node)
73 struct irdma_virt_mem ws_mem;
76 irdma_free_ws_node_id(vsi->dev, node->index);
79 ws_mem.size = sizeof(struct irdma_ws_node);
84 * irdma_ws_cqp_cmd - Post CQP work scheduler node cmd
86 * @node: pointer to node
87 * @cmd: add, remove or modify
89 static int irdma_ws_cqp_cmd(struct irdma_sc_vsi *vsi,
90 struct irdma_ws_node *node, u8 cmd)
92 struct irdma_ws_node_info node_info = {};
94 node_info.id = node->index;
95 node_info.vsi = node->vsi_index;
97 node_info.parent_id = node->parent->index;
99 node_info.parent_id = node_info.id;
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");
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;
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
125 static struct irdma_ws_node *ws_find_node(struct irdma_ws_node *parent,
127 enum irdma_ws_match_type type)
129 struct irdma_ws_node *node;
132 case WS_MATCH_TYPE_VSI:
133 list_for_each_entry(node, &parent->child_list_head, siblings) {
134 if (node->vsi_index == match_val)
138 case WS_MATCH_TYPE_TC:
139 list_for_each_entry(node, &parent->child_list_head, siblings) {
140 if (node->traffic_class == match_val)
152 * irdma_tc_in_use - Checks to see if a leaf node is in use
154 * @user_pri: user priority
156 static bool irdma_tc_in_use(struct irdma_sc_vsi *vsi, u8 user_pri)
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);
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
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);
176 mutex_unlock(&vsi->qos[user_pri].qos_mutex);
182 * irdma_remove_leaf - Remove leaf node unconditionally
184 * @user_pri: user priority
186 static void irdma_remove_leaf(struct irdma_sc_vsi *vsi, u8 user_pri)
188 struct irdma_ws_node *ws_tree_root, *vsi_node, *tc_node;
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;
197 ws_tree_root = vsi->dev->ws_tree_root;
201 vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx,
206 tc_node = ws_find_node(vsi_node,
207 vsi->qos[user_pri].traffic_class,
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;
232 * irdma_ws_add - Build work scheduler tree, set RDMA qs_handle
234 * @user_pri: user priority
236 int irdma_ws_add(struct irdma_sc_vsi *vsi, u8 user_pri)
238 struct irdma_ws_node *ws_tree_root;
239 struct irdma_ws_node *vsi_node;
240 struct irdma_ws_node *tc_node;
245 mutex_lock(&vsi->dev->ws_mutex);
246 if (vsi->tc_change_pending) {
251 if (vsi->qos[user_pri].valid)
254 ws_tree_root = vsi->dev->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);
264 ret = irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_ADD_NODE);
266 irdma_free_node(vsi, ws_tree_root);
270 vsi->dev->ws_tree_root = ws_tree_root;
273 /* Find a second tier node that matches the VSI */
274 vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx,
277 /* If VSI node doesn't exist, add one */
279 ibdev_dbg(to_ibdev(vsi->dev),
280 "WS: Node not found matching VSI %d\n",
282 vsi_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_PARENT,
289 ret = irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_ADD_NODE);
291 irdma_free_node(vsi, vsi_node);
295 list_add(&vsi_node->siblings, &ws_tree_root->child_list_head);
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,
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,
316 ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_ADD_NODE);
318 irdma_free_node(vsi, tc_node);
322 list_add(&tc_node->siblings, &vsi_node->child_list_head);
324 * callback to LAN to update the LAN tree with our node
326 ret = vsi->register_qset(vsi, tc_node);
330 tc_node->enable = true;
331 ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_MODIFY_NODE);
333 vsi->unregister_qset(vsi, tc_node);
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);
341 * Iterate through other UPs and update the QS handle if they have
342 * a matching traffic class.
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;
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);
359 if (list_empty(&vsi_node->child_list_head)) {
360 if (irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE))
362 list_del(&vsi_node->siblings);
363 irdma_free_node(vsi, vsi_node);
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);
375 mutex_unlock(&vsi->dev->ws_mutex);
380 * irdma_ws_remove - Free WS scheduler node, update WS tree
382 * @user_pri: user priority
384 void irdma_ws_remove(struct irdma_sc_vsi *vsi, u8 user_pri)
386 mutex_lock(&vsi->dev->ws_mutex);
387 if (irdma_tc_in_use(vsi, user_pri))
389 irdma_remove_leaf(vsi, user_pri);
391 mutex_unlock(&vsi->dev->ws_mutex);
395 * irdma_ws_reset - Reset entire WS tree
398 void irdma_ws_reset(struct irdma_sc_vsi *vsi)
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);