Mention branches and keyring.
[releases.git] / 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 "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  */
18 static 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(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(vsi->dev);
34                 if (node_index == IRDMA_WS_NODE_INVALID) {
35                         kfree(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(&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  */
70 static 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(vsi->dev, node->index);
77
78         ws_mem.va = node;
79         ws_mem.size = sizeof(struct irdma_ws_node);
80         kfree(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  */
89 static 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(vsi->dev, cmd, &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  */
125 static 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  */
156 static 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(&vsi->qos[user_pri].qplist)) {
162                 mutex_unlock(&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(&vsi->qos[i].qplist)) {
172                         mutex_unlock(&vsi->qos[user_pri].qos_mutex);
173                         return true;
174                 }
175         }
176         mutex_unlock(&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  */
186 static 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(ws_tree_root, vsi->vsi_idx,
202                                 WS_MATCH_TYPE_VSI);
203         if (!vsi_node)
204                 return;
205
206         tc_node = ws_find_node(vsi_node,
207                                vsi->qos[user_pri].traffic_class,
208                                WS_MATCH_TYPE_TC);
209         if (!tc_node)
210                 return;
211
212         irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE);
213         vsi->unregister_qset(vsi, tc_node);
214         list_del(&tc_node->siblings);
215         irdma_free_node(vsi, tc_node);
216         /* Check if VSI node can be freed */
217         if (list_empty(&vsi_node->child_list_head)) {
218                 irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE);
219                 list_del(&vsi_node->siblings);
220                 irdma_free_node(vsi, vsi_node);
221                 /* Free head node there are no remaining VSI nodes */
222                 if (list_empty(&ws_tree_root->child_list_head)) {
223                         irdma_ws_cqp_cmd(vsi, ws_tree_root,
224                                          IRDMA_OP_WS_DELETE_NODE);
225                         irdma_free_node(vsi, 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  */
236 int 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                                                 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, ws_tree_root, IRDMA_OP_WS_ADD_NODE);
265                 if (ret) {
266                         irdma_free_node(vsi, 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(ws_tree_root, vsi->vsi_idx,
275                                 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, WS_NODE_TYPE_PARENT,
283                                             ws_tree_root);
284                 if (!vsi_node) {
285                         ret = -ENOMEM;
286                         goto vsi_add_err;
287                 }
288
289                 ret = irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_ADD_NODE);
290                 if (ret) {
291                         irdma_free_node(vsi, vsi_node);
292                         goto vsi_add_err;
293                 }
294
295                 list_add(&vsi_node->siblings, &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(vsi_node, traffic_class,
303                                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, WS_NODE_TYPE_LEAF,
310                                            vsi_node);
311                 if (!tc_node) {
312                         ret = -ENOMEM;
313                         goto leaf_add_err;
314                 }
315
316                 ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_ADD_NODE);
317                 if (ret) {
318                         irdma_free_node(vsi, tc_node);
319                         goto leaf_add_err;
320                 }
321
322                 list_add(&tc_node->siblings, &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, tc_node, 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
354 reg_err:
355         irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE);
356         list_del(&tc_node->siblings);
357         irdma_free_node(vsi, tc_node);
358 leaf_add_err:
359         if (list_empty(&vsi_node->child_list_head)) {
360                 if (irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE))
361                         goto exit;
362                 list_del(&vsi_node->siblings);
363                 irdma_free_node(vsi, vsi_node);
364         }
365
366 vsi_add_err:
367         /* Free head node there are no remaining VSI nodes */
368         if (list_empty(&ws_tree_root->child_list_head)) {
369                 irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_DELETE_NODE);
370                 vsi->dev->ws_tree_root = NULL;
371                 irdma_free_node(vsi, ws_tree_root);
372         }
373
374 exit:
375         mutex_unlock(&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  */
384 void 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);
390 exit:
391         mutex_unlock(&vsi->dev->ws_mutex);
392 }
393
394 /**
395  * irdma_ws_reset - Reset entire WS tree
396  * @vsi: vsi pointer
397  */
398 void 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, i);
405         mutex_unlock(&vsi->dev->ws_mutex);
406 }