1 // SPDX-License-Identifier: GPL-2.0 or Linux-OpenIB
2 /* Copyright (c) 2017 - 2021 Intel Corporation */
13 * irdma_alloc_node - Allocate a WS node and init
15 * @user_pri: user priority
16 * @node_type: Type of node, leaf or parent
17 * @parent: parent node pointer
19 static struct irdma_ws_node
*irdma_alloc_node(struct irdma_sc_vsi
*vsi
,
21 enum irdma_ws_node_type node_type
,
22 struct irdma_ws_node
*parent
)
24 struct irdma_virt_mem ws_mem
;
25 struct irdma_ws_node
*node
;
28 ws_mem
.size
= sizeof(struct irdma_ws_node
);
29 ws_mem
.va
= kzalloc(ws_mem
.size
, GFP_KERNEL
);
34 node_index
= irdma_alloc_ws_node_id(vsi
->dev
);
35 if (node_index
== IRDMA_WS_NODE_INVALID
) {
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
;
53 node
->lan_qs_handle
= vsi
->qos
[user_pri
].lan_qos_handle
;
54 node
->prio_type
= IRDMA_PRIO_WEIGHTED_RR
;
57 node
->prio_type
= IRDMA_PRIO_WEIGHTED_RR
;
61 node
->parent
= parent
;
67 * irdma_free_node - Free a WS node
68 * @vsi: VSI stricture of device
69 * @node: Pointer to node to free
71 static void irdma_free_node(struct irdma_sc_vsi
*vsi
,
72 struct irdma_ws_node
*node
)
74 struct irdma_virt_mem ws_mem
;
77 irdma_free_ws_node_id(vsi
->dev
, node
->index
);
80 ws_mem
.size
= sizeof(struct irdma_ws_node
);
85 * irdma_ws_cqp_cmd - Post CQP work scheduler node cmd
87 * @node: pointer to node
88 * @cmd: add, remove or modify
90 static enum irdma_status_code
91 irdma_ws_cqp_cmd(struct irdma_sc_vsi
*vsi
, struct irdma_ws_node
*node
, u8 cmd
)
93 struct irdma_ws_node_info node_info
= {};
95 node_info
.id
= node
->index
;
96 node_info
.vsi
= node
->vsi_index
;
98 node_info
.parent_id
= node
->parent
->index
;
100 node_info
.parent_id
= node_info
.id
;
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
;
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
;
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
126 static struct irdma_ws_node
*ws_find_node(struct irdma_ws_node
*parent
,
128 enum irdma_ws_match_type type
)
130 struct irdma_ws_node
*node
;
133 case WS_MATCH_TYPE_VSI
:
134 list_for_each_entry(node
, &parent
->child_list_head
, siblings
) {
135 if (node
->vsi_index
== match_val
)
139 case WS_MATCH_TYPE_TC
:
140 list_for_each_entry(node
, &parent
->child_list_head
, siblings
) {
141 if (node
->traffic_class
== match_val
)
153 * irdma_tc_in_use - Checks to see if a leaf node is in use
155 * @user_pri: user priority
157 static bool irdma_tc_in_use(struct irdma_sc_vsi
*vsi
, u8 user_pri
)
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
);
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
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
);
177 mutex_unlock(&vsi
->qos
[user_pri
].qos_mutex
);
183 * irdma_remove_leaf - Remove leaf node unconditionally
185 * @user_pri: user priority
187 static void irdma_remove_leaf(struct irdma_sc_vsi
*vsi
, u8 user_pri
)
189 struct irdma_ws_node
*ws_tree_root
, *vsi_node
, *tc_node
;
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;
198 ws_tree_root
= vsi
->dev
->ws_tree_root
;
202 vsi_node
= ws_find_node(ws_tree_root
, vsi
->vsi_idx
,
207 tc_node
= ws_find_node(vsi_node
,
208 vsi
->qos
[user_pri
].traffic_class
,
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
;
233 * irdma_ws_add - Build work scheduler tree, set RDMA qs_handle
235 * @user_pri: user priority
237 enum irdma_status_code
irdma_ws_add(struct irdma_sc_vsi
*vsi
, u8 user_pri
)
239 struct irdma_ws_node
*ws_tree_root
;
240 struct irdma_ws_node
*vsi_node
;
241 struct irdma_ws_node
*tc_node
;
243 enum irdma_status_code ret
= 0;
246 mutex_lock(&vsi
->dev
->ws_mutex
);
247 if (vsi
->tc_change_pending
) {
248 ret
= IRDMA_ERR_NOT_READY
;
252 if (vsi
->qos
[user_pri
].valid
)
255 ws_tree_root
= vsi
->dev
->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
);
261 ret
= IRDMA_ERR_NO_MEMORY
;
265 ret
= irdma_ws_cqp_cmd(vsi
, ws_tree_root
, IRDMA_OP_WS_ADD_NODE
);
267 irdma_free_node(vsi
, ws_tree_root
);
271 vsi
->dev
->ws_tree_root
= ws_tree_root
;
274 /* Find a second tier node that matches the VSI */
275 vsi_node
= ws_find_node(ws_tree_root
, vsi
->vsi_idx
,
278 /* If VSI node doesn't exist, add one */
280 ibdev_dbg(to_ibdev(vsi
->dev
),
281 "WS: Node not found matching VSI %d\n",
283 vsi_node
= irdma_alloc_node(vsi
, user_pri
, WS_NODE_TYPE_PARENT
,
286 ret
= IRDMA_ERR_NO_MEMORY
;
290 ret
= irdma_ws_cqp_cmd(vsi
, vsi_node
, IRDMA_OP_WS_ADD_NODE
);
292 irdma_free_node(vsi
, vsi_node
);
296 list_add(&vsi_node
->siblings
, &ws_tree_root
->child_list_head
);
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
,
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
,
313 ret
= IRDMA_ERR_NO_MEMORY
;
317 ret
= irdma_ws_cqp_cmd(vsi
, tc_node
, IRDMA_OP_WS_ADD_NODE
);
319 irdma_free_node(vsi
, tc_node
);
323 list_add(&tc_node
->siblings
, &vsi_node
->child_list_head
);
325 * callback to LAN to update the LAN tree with our node
327 ret
= vsi
->register_qset(vsi
, tc_node
);
331 tc_node
->enable
= true;
332 ret
= irdma_ws_cqp_cmd(vsi
, tc_node
, IRDMA_OP_WS_MODIFY_NODE
);
334 vsi
->unregister_qset(vsi
, tc_node
);
338 ibdev_dbg(to_ibdev(vsi
->dev
),
339 "WS: Using node %d which represents VSI %d TC %d\n",
340 tc_node
->index
, vsi
->vsi_idx
, traffic_class
);
342 * Iterate through other UPs and update the QS handle if they have
343 * a matching traffic class.
345 for (i
= 0; i
< IRDMA_MAX_USER_PRIORITY
; i
++) {
346 if (vsi
->qos
[i
].traffic_class
== traffic_class
) {
347 vsi
->qos
[i
].qs_handle
= tc_node
->qs_handle
;
348 vsi
->qos
[i
].lan_qos_handle
= tc_node
->lan_qs_handle
;
349 vsi
->qos
[i
].l2_sched_node_id
= tc_node
->l2_sched_node_id
;
350 vsi
->qos
[i
].valid
= true;
356 irdma_ws_cqp_cmd(vsi
, tc_node
, IRDMA_OP_WS_DELETE_NODE
);
357 list_del(&tc_node
->siblings
);
358 irdma_free_node(vsi
, tc_node
);
360 if (list_empty(&vsi_node
->child_list_head
)) {
361 if (irdma_ws_cqp_cmd(vsi
, vsi_node
, IRDMA_OP_WS_DELETE_NODE
))
363 list_del(&vsi_node
->siblings
);
364 irdma_free_node(vsi
, vsi_node
);
368 /* Free head node there are no remaining VSI nodes */
369 if (list_empty(&ws_tree_root
->child_list_head
)) {
370 irdma_ws_cqp_cmd(vsi
, ws_tree_root
, IRDMA_OP_WS_DELETE_NODE
);
371 vsi
->dev
->ws_tree_root
= NULL
;
372 irdma_free_node(vsi
, ws_tree_root
);
376 mutex_unlock(&vsi
->dev
->ws_mutex
);
381 * irdma_ws_remove - Free WS scheduler node, update WS tree
383 * @user_pri: user priority
385 void irdma_ws_remove(struct irdma_sc_vsi
*vsi
, u8 user_pri
)
387 mutex_lock(&vsi
->dev
->ws_mutex
);
388 if (irdma_tc_in_use(vsi
, user_pri
))
390 irdma_remove_leaf(vsi
, user_pri
);
392 mutex_unlock(&vsi
->dev
->ws_mutex
);
396 * irdma_ws_reset - Reset entire WS tree
399 void irdma_ws_reset(struct irdma_sc_vsi
*vsi
)
403 mutex_lock(&vsi
->dev
->ws_mutex
);
404 for (i
= 0; i
< IRDMA_MAX_USER_PRIORITY
; ++i
)
405 irdma_remove_leaf(vsi
, i
);
406 mutex_unlock(&vsi
->dev
->ws_mutex
);