xref: /openbmc/linux/drivers/infiniband/hw/irdma/ws.c (revision d87c25e8)
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 }
408