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