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