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