xref: /freebsd/sys/dev/irdma/irdma_ws.c (revision 38a52bd3)
1 /*-
2  * SPDX-License-Identifier: GPL-2.0 or Linux-OpenIB
3  *
4  * Copyright (c) 2017 - 2021 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_ATOMIC);
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 }
448