xref: /openbmc/linux/drivers/infiniband/hw/irdma/ws.c (revision 2c4b14ea)
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  */
irdma_alloc_node(struct irdma_sc_vsi * vsi,u8 user_pri,enum irdma_ws_node_type node_type,struct irdma_ws_node * parent)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  */
irdma_free_node(struct irdma_sc_vsi * vsi,struct irdma_ws_node * node)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  */
irdma_ws_cqp_cmd(struct irdma_sc_vsi * vsi,struct irdma_ws_node * node,u8 cmd)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  */
ws_find_node(struct irdma_ws_node * parent,u16 match_val,enum irdma_ws_match_type type)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  */
irdma_tc_in_use(struct irdma_sc_vsi * vsi,u8 user_pri)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  */
irdma_remove_leaf(struct irdma_sc_vsi * vsi,u8 user_pri)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  */
irdma_ws_add(struct irdma_sc_vsi * vsi,u8 user_pri)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  */
irdma_ws_remove(struct irdma_sc_vsi * vsi,u8 user_pri)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  */
irdma_ws_reset(struct irdma_sc_vsi * vsi)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 }
407