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 */
18static 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 */
70static 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 */
89static 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 */
125static 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 */
156static 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 */
186static 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 */
236int 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
354reg_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);
358leaf_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
366vsi_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
374exit:
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 */
384void 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);
390exit:
391	mutex_unlock(&vsi->dev->ws_mutex);
392}
393
394/**
395 * irdma_ws_reset - Reset entire WS tree
396 * @vsi: vsi pointer
397 */
398void 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