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

source code of linux/drivers/infiniband/hw/irdma/ws.c