162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or Linux-OpenIB
262306a36Sopenharmony_ci/* Copyright (c) 2017 - 2021 Intel Corporation */
362306a36Sopenharmony_ci#include "osdep.h"
462306a36Sopenharmony_ci#include "hmc.h"
562306a36Sopenharmony_ci#include "defs.h"
662306a36Sopenharmony_ci#include "type.h"
762306a36Sopenharmony_ci#include "protos.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "ws.h"
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/**
1262306a36Sopenharmony_ci * irdma_alloc_node - Allocate a WS node and init
1362306a36Sopenharmony_ci * @vsi: vsi pointer
1462306a36Sopenharmony_ci * @user_pri: user priority
1562306a36Sopenharmony_ci * @node_type: Type of node, leaf or parent
1662306a36Sopenharmony_ci * @parent: parent node pointer
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_cistatic struct irdma_ws_node *irdma_alloc_node(struct irdma_sc_vsi *vsi,
1962306a36Sopenharmony_ci					      u8 user_pri,
2062306a36Sopenharmony_ci					      enum irdma_ws_node_type node_type,
2162306a36Sopenharmony_ci					      struct irdma_ws_node *parent)
2262306a36Sopenharmony_ci{
2362306a36Sopenharmony_ci	struct irdma_virt_mem ws_mem;
2462306a36Sopenharmony_ci	struct irdma_ws_node *node;
2562306a36Sopenharmony_ci	u16 node_index = 0;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	ws_mem.size = sizeof(struct irdma_ws_node);
2862306a36Sopenharmony_ci	ws_mem.va = kzalloc(ws_mem.size, GFP_KERNEL);
2962306a36Sopenharmony_ci	if (!ws_mem.va)
3062306a36Sopenharmony_ci		return NULL;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (parent) {
3362306a36Sopenharmony_ci		node_index = irdma_alloc_ws_node_id(vsi->dev);
3462306a36Sopenharmony_ci		if (node_index == IRDMA_WS_NODE_INVALID) {
3562306a36Sopenharmony_ci			kfree(ws_mem.va);
3662306a36Sopenharmony_ci			return NULL;
3762306a36Sopenharmony_ci		}
3862306a36Sopenharmony_ci	}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	node = ws_mem.va;
4162306a36Sopenharmony_ci	node->index = node_index;
4262306a36Sopenharmony_ci	node->vsi_index = vsi->vsi_idx;
4362306a36Sopenharmony_ci	INIT_LIST_HEAD(&node->child_list_head);
4462306a36Sopenharmony_ci	if (node_type == WS_NODE_TYPE_LEAF) {
4562306a36Sopenharmony_ci		node->type_leaf = true;
4662306a36Sopenharmony_ci		node->traffic_class = vsi->qos[user_pri].traffic_class;
4762306a36Sopenharmony_ci		node->user_pri = user_pri;
4862306a36Sopenharmony_ci		node->rel_bw = vsi->qos[user_pri].rel_bw;
4962306a36Sopenharmony_ci		if (!node->rel_bw)
5062306a36Sopenharmony_ci			node->rel_bw = 1;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci		node->lan_qs_handle = vsi->qos[user_pri].lan_qos_handle;
5362306a36Sopenharmony_ci		node->prio_type = IRDMA_PRIO_WEIGHTED_RR;
5462306a36Sopenharmony_ci	} else {
5562306a36Sopenharmony_ci		node->rel_bw = 1;
5662306a36Sopenharmony_ci		node->prio_type = IRDMA_PRIO_WEIGHTED_RR;
5762306a36Sopenharmony_ci		node->enable = true;
5862306a36Sopenharmony_ci	}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	node->parent = parent;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	return node;
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci/**
6662306a36Sopenharmony_ci * irdma_free_node - Free a WS node
6762306a36Sopenharmony_ci * @vsi: VSI stricture of device
6862306a36Sopenharmony_ci * @node: Pointer to node to free
6962306a36Sopenharmony_ci */
7062306a36Sopenharmony_cistatic void irdma_free_node(struct irdma_sc_vsi *vsi,
7162306a36Sopenharmony_ci			    struct irdma_ws_node *node)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	struct irdma_virt_mem ws_mem;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	if (node->index)
7662306a36Sopenharmony_ci		irdma_free_ws_node_id(vsi->dev, node->index);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	ws_mem.va = node;
7962306a36Sopenharmony_ci	ws_mem.size = sizeof(struct irdma_ws_node);
8062306a36Sopenharmony_ci	kfree(ws_mem.va);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci/**
8462306a36Sopenharmony_ci * irdma_ws_cqp_cmd - Post CQP work scheduler node cmd
8562306a36Sopenharmony_ci * @vsi: vsi pointer
8662306a36Sopenharmony_ci * @node: pointer to node
8762306a36Sopenharmony_ci * @cmd: add, remove or modify
8862306a36Sopenharmony_ci */
8962306a36Sopenharmony_cistatic int irdma_ws_cqp_cmd(struct irdma_sc_vsi *vsi,
9062306a36Sopenharmony_ci			    struct irdma_ws_node *node, u8 cmd)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci	struct irdma_ws_node_info node_info = {};
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	node_info.id = node->index;
9562306a36Sopenharmony_ci	node_info.vsi = node->vsi_index;
9662306a36Sopenharmony_ci	if (node->parent)
9762306a36Sopenharmony_ci		node_info.parent_id = node->parent->index;
9862306a36Sopenharmony_ci	else
9962306a36Sopenharmony_ci		node_info.parent_id = node_info.id;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	node_info.weight = node->rel_bw;
10262306a36Sopenharmony_ci	node_info.tc = node->traffic_class;
10362306a36Sopenharmony_ci	node_info.prio_type = node->prio_type;
10462306a36Sopenharmony_ci	node_info.type_leaf = node->type_leaf;
10562306a36Sopenharmony_ci	node_info.enable = node->enable;
10662306a36Sopenharmony_ci	if (irdma_cqp_ws_node_cmd(vsi->dev, cmd, &node_info)) {
10762306a36Sopenharmony_ci		ibdev_dbg(to_ibdev(vsi->dev), "WS: CQP WS CMD failed\n");
10862306a36Sopenharmony_ci		return -ENOMEM;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	if (node->type_leaf && cmd == IRDMA_OP_WS_ADD_NODE) {
11262306a36Sopenharmony_ci		node->qs_handle = node_info.qs_handle;
11362306a36Sopenharmony_ci		vsi->qos[node->user_pri].qs_handle = node_info.qs_handle;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return 0;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci/**
12062306a36Sopenharmony_ci * ws_find_node - Find SC WS node based on VSI id or TC
12162306a36Sopenharmony_ci * @parent: parent node of First VSI or TC node
12262306a36Sopenharmony_ci * @match_val: value to match
12362306a36Sopenharmony_ci * @type: match type VSI/TC
12462306a36Sopenharmony_ci */
12562306a36Sopenharmony_cistatic struct irdma_ws_node *ws_find_node(struct irdma_ws_node *parent,
12662306a36Sopenharmony_ci					  u16 match_val,
12762306a36Sopenharmony_ci					  enum irdma_ws_match_type type)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	struct irdma_ws_node *node;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	switch (type) {
13262306a36Sopenharmony_ci	case WS_MATCH_TYPE_VSI:
13362306a36Sopenharmony_ci		list_for_each_entry(node, &parent->child_list_head, siblings) {
13462306a36Sopenharmony_ci			if (node->vsi_index == match_val)
13562306a36Sopenharmony_ci				return node;
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci		break;
13862306a36Sopenharmony_ci	case WS_MATCH_TYPE_TC:
13962306a36Sopenharmony_ci		list_for_each_entry(node, &parent->child_list_head, siblings) {
14062306a36Sopenharmony_ci			if (node->traffic_class == match_val)
14162306a36Sopenharmony_ci				return node;
14262306a36Sopenharmony_ci		}
14362306a36Sopenharmony_ci		break;
14462306a36Sopenharmony_ci	default:
14562306a36Sopenharmony_ci		break;
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_ci	return NULL;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci/**
15262306a36Sopenharmony_ci * irdma_tc_in_use - Checks to see if a leaf node is in use
15362306a36Sopenharmony_ci * @vsi: vsi pointer
15462306a36Sopenharmony_ci * @user_pri: user priority
15562306a36Sopenharmony_ci */
15662306a36Sopenharmony_cistatic bool irdma_tc_in_use(struct irdma_sc_vsi *vsi, u8 user_pri)
15762306a36Sopenharmony_ci{
15862306a36Sopenharmony_ci	int i;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	mutex_lock(&vsi->qos[user_pri].qos_mutex);
16162306a36Sopenharmony_ci	if (!list_empty(&vsi->qos[user_pri].qplist)) {
16262306a36Sopenharmony_ci		mutex_unlock(&vsi->qos[user_pri].qos_mutex);
16362306a36Sopenharmony_ci		return true;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* Check if the traffic class associated with the given user priority
16762306a36Sopenharmony_ci	 * is in use by any other user priority. If so, nothing left to do
16862306a36Sopenharmony_ci	 */
16962306a36Sopenharmony_ci	for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) {
17062306a36Sopenharmony_ci		if (vsi->qos[i].traffic_class == vsi->qos[user_pri].traffic_class &&
17162306a36Sopenharmony_ci		    !list_empty(&vsi->qos[i].qplist)) {
17262306a36Sopenharmony_ci			mutex_unlock(&vsi->qos[user_pri].qos_mutex);
17362306a36Sopenharmony_ci			return true;
17462306a36Sopenharmony_ci		}
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	mutex_unlock(&vsi->qos[user_pri].qos_mutex);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return false;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci/**
18262306a36Sopenharmony_ci * irdma_remove_leaf - Remove leaf node unconditionally
18362306a36Sopenharmony_ci * @vsi: vsi pointer
18462306a36Sopenharmony_ci * @user_pri: user priority
18562306a36Sopenharmony_ci */
18662306a36Sopenharmony_cistatic void irdma_remove_leaf(struct irdma_sc_vsi *vsi, u8 user_pri)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	struct irdma_ws_node *ws_tree_root, *vsi_node, *tc_node;
18962306a36Sopenharmony_ci	int i;
19062306a36Sopenharmony_ci	u16 traffic_class;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	traffic_class = vsi->qos[user_pri].traffic_class;
19362306a36Sopenharmony_ci	for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++)
19462306a36Sopenharmony_ci		if (vsi->qos[i].traffic_class == traffic_class)
19562306a36Sopenharmony_ci			vsi->qos[i].valid = false;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	ws_tree_root = vsi->dev->ws_tree_root;
19862306a36Sopenharmony_ci	if (!ws_tree_root)
19962306a36Sopenharmony_ci		return;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx,
20262306a36Sopenharmony_ci				WS_MATCH_TYPE_VSI);
20362306a36Sopenharmony_ci	if (!vsi_node)
20462306a36Sopenharmony_ci		return;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	tc_node = ws_find_node(vsi_node,
20762306a36Sopenharmony_ci			       vsi->qos[user_pri].traffic_class,
20862306a36Sopenharmony_ci			       WS_MATCH_TYPE_TC);
20962306a36Sopenharmony_ci	if (!tc_node)
21062306a36Sopenharmony_ci		return;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE);
21362306a36Sopenharmony_ci	vsi->unregister_qset(vsi, tc_node);
21462306a36Sopenharmony_ci	list_del(&tc_node->siblings);
21562306a36Sopenharmony_ci	irdma_free_node(vsi, tc_node);
21662306a36Sopenharmony_ci	/* Check if VSI node can be freed */
21762306a36Sopenharmony_ci	if (list_empty(&vsi_node->child_list_head)) {
21862306a36Sopenharmony_ci		irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE);
21962306a36Sopenharmony_ci		list_del(&vsi_node->siblings);
22062306a36Sopenharmony_ci		irdma_free_node(vsi, vsi_node);
22162306a36Sopenharmony_ci		/* Free head node there are no remaining VSI nodes */
22262306a36Sopenharmony_ci		if (list_empty(&ws_tree_root->child_list_head)) {
22362306a36Sopenharmony_ci			irdma_ws_cqp_cmd(vsi, ws_tree_root,
22462306a36Sopenharmony_ci					 IRDMA_OP_WS_DELETE_NODE);
22562306a36Sopenharmony_ci			irdma_free_node(vsi, ws_tree_root);
22662306a36Sopenharmony_ci			vsi->dev->ws_tree_root = NULL;
22762306a36Sopenharmony_ci		}
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci}
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci/**
23262306a36Sopenharmony_ci * irdma_ws_add - Build work scheduler tree, set RDMA qs_handle
23362306a36Sopenharmony_ci * @vsi: vsi pointer
23462306a36Sopenharmony_ci * @user_pri: user priority
23562306a36Sopenharmony_ci */
23662306a36Sopenharmony_ciint irdma_ws_add(struct irdma_sc_vsi *vsi, u8 user_pri)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	struct irdma_ws_node *ws_tree_root;
23962306a36Sopenharmony_ci	struct irdma_ws_node *vsi_node;
24062306a36Sopenharmony_ci	struct irdma_ws_node *tc_node;
24162306a36Sopenharmony_ci	u16 traffic_class;
24262306a36Sopenharmony_ci	int ret = 0;
24362306a36Sopenharmony_ci	int i;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	mutex_lock(&vsi->dev->ws_mutex);
24662306a36Sopenharmony_ci	if (vsi->tc_change_pending) {
24762306a36Sopenharmony_ci		ret = -EBUSY;
24862306a36Sopenharmony_ci		goto exit;
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	if (vsi->qos[user_pri].valid)
25262306a36Sopenharmony_ci		goto exit;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	ws_tree_root = vsi->dev->ws_tree_root;
25562306a36Sopenharmony_ci	if (!ws_tree_root) {
25662306a36Sopenharmony_ci		ibdev_dbg(to_ibdev(vsi->dev), "WS: Creating root node\n");
25762306a36Sopenharmony_ci		ws_tree_root = irdma_alloc_node(vsi, user_pri,
25862306a36Sopenharmony_ci						WS_NODE_TYPE_PARENT, NULL);
25962306a36Sopenharmony_ci		if (!ws_tree_root) {
26062306a36Sopenharmony_ci			ret = -ENOMEM;
26162306a36Sopenharmony_ci			goto exit;
26262306a36Sopenharmony_ci		}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		ret = irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_ADD_NODE);
26562306a36Sopenharmony_ci		if (ret) {
26662306a36Sopenharmony_ci			irdma_free_node(vsi, ws_tree_root);
26762306a36Sopenharmony_ci			goto exit;
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci		vsi->dev->ws_tree_root = ws_tree_root;
27162306a36Sopenharmony_ci	}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/* Find a second tier node that matches the VSI */
27462306a36Sopenharmony_ci	vsi_node = ws_find_node(ws_tree_root, vsi->vsi_idx,
27562306a36Sopenharmony_ci				WS_MATCH_TYPE_VSI);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	/* If VSI node doesn't exist, add one */
27862306a36Sopenharmony_ci	if (!vsi_node) {
27962306a36Sopenharmony_ci		ibdev_dbg(to_ibdev(vsi->dev),
28062306a36Sopenharmony_ci			  "WS: Node not found matching VSI %d\n",
28162306a36Sopenharmony_ci			  vsi->vsi_idx);
28262306a36Sopenharmony_ci		vsi_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_PARENT,
28362306a36Sopenharmony_ci					    ws_tree_root);
28462306a36Sopenharmony_ci		if (!vsi_node) {
28562306a36Sopenharmony_ci			ret = -ENOMEM;
28662306a36Sopenharmony_ci			goto vsi_add_err;
28762306a36Sopenharmony_ci		}
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci		ret = irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_ADD_NODE);
29062306a36Sopenharmony_ci		if (ret) {
29162306a36Sopenharmony_ci			irdma_free_node(vsi, vsi_node);
29262306a36Sopenharmony_ci			goto vsi_add_err;
29362306a36Sopenharmony_ci		}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		list_add(&vsi_node->siblings, &ws_tree_root->child_list_head);
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	ibdev_dbg(to_ibdev(vsi->dev),
29962306a36Sopenharmony_ci		  "WS: Using node %d which represents VSI %d\n",
30062306a36Sopenharmony_ci		  vsi_node->index, vsi->vsi_idx);
30162306a36Sopenharmony_ci	traffic_class = vsi->qos[user_pri].traffic_class;
30262306a36Sopenharmony_ci	tc_node = ws_find_node(vsi_node, traffic_class,
30362306a36Sopenharmony_ci			       WS_MATCH_TYPE_TC);
30462306a36Sopenharmony_ci	if (!tc_node) {
30562306a36Sopenharmony_ci		/* Add leaf node */
30662306a36Sopenharmony_ci		ibdev_dbg(to_ibdev(vsi->dev),
30762306a36Sopenharmony_ci			  "WS: Node not found matching VSI %d and TC %d\n",
30862306a36Sopenharmony_ci			  vsi->vsi_idx, traffic_class);
30962306a36Sopenharmony_ci		tc_node = irdma_alloc_node(vsi, user_pri, WS_NODE_TYPE_LEAF,
31062306a36Sopenharmony_ci					   vsi_node);
31162306a36Sopenharmony_ci		if (!tc_node) {
31262306a36Sopenharmony_ci			ret = -ENOMEM;
31362306a36Sopenharmony_ci			goto leaf_add_err;
31462306a36Sopenharmony_ci		}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci		ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_ADD_NODE);
31762306a36Sopenharmony_ci		if (ret) {
31862306a36Sopenharmony_ci			irdma_free_node(vsi, tc_node);
31962306a36Sopenharmony_ci			goto leaf_add_err;
32062306a36Sopenharmony_ci		}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci		list_add(&tc_node->siblings, &vsi_node->child_list_head);
32362306a36Sopenharmony_ci		/*
32462306a36Sopenharmony_ci		 * callback to LAN to update the LAN tree with our node
32562306a36Sopenharmony_ci		 */
32662306a36Sopenharmony_ci		ret = vsi->register_qset(vsi, tc_node);
32762306a36Sopenharmony_ci		if (ret)
32862306a36Sopenharmony_ci			goto reg_err;
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci		tc_node->enable = true;
33162306a36Sopenharmony_ci		ret = irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_MODIFY_NODE);
33262306a36Sopenharmony_ci		if (ret) {
33362306a36Sopenharmony_ci			vsi->unregister_qset(vsi, tc_node);
33462306a36Sopenharmony_ci			goto reg_err;
33562306a36Sopenharmony_ci		}
33662306a36Sopenharmony_ci	}
33762306a36Sopenharmony_ci	ibdev_dbg(to_ibdev(vsi->dev),
33862306a36Sopenharmony_ci		  "WS: Using node %d which represents VSI %d TC %d\n",
33962306a36Sopenharmony_ci		  tc_node->index, vsi->vsi_idx, traffic_class);
34062306a36Sopenharmony_ci	/*
34162306a36Sopenharmony_ci	 * Iterate through other UPs and update the QS handle if they have
34262306a36Sopenharmony_ci	 * a matching traffic class.
34362306a36Sopenharmony_ci	 */
34462306a36Sopenharmony_ci	for (i = 0; i < IRDMA_MAX_USER_PRIORITY; i++) {
34562306a36Sopenharmony_ci		if (vsi->qos[i].traffic_class == traffic_class) {
34662306a36Sopenharmony_ci			vsi->qos[i].qs_handle = tc_node->qs_handle;
34762306a36Sopenharmony_ci			vsi->qos[i].lan_qos_handle = tc_node->lan_qs_handle;
34862306a36Sopenharmony_ci			vsi->qos[i].l2_sched_node_id = tc_node->l2_sched_node_id;
34962306a36Sopenharmony_ci			vsi->qos[i].valid = true;
35062306a36Sopenharmony_ci		}
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci	goto exit;
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cireg_err:
35562306a36Sopenharmony_ci	irdma_ws_cqp_cmd(vsi, tc_node, IRDMA_OP_WS_DELETE_NODE);
35662306a36Sopenharmony_ci	list_del(&tc_node->siblings);
35762306a36Sopenharmony_ci	irdma_free_node(vsi, tc_node);
35862306a36Sopenharmony_cileaf_add_err:
35962306a36Sopenharmony_ci	if (list_empty(&vsi_node->child_list_head)) {
36062306a36Sopenharmony_ci		if (irdma_ws_cqp_cmd(vsi, vsi_node, IRDMA_OP_WS_DELETE_NODE))
36162306a36Sopenharmony_ci			goto exit;
36262306a36Sopenharmony_ci		list_del(&vsi_node->siblings);
36362306a36Sopenharmony_ci		irdma_free_node(vsi, vsi_node);
36462306a36Sopenharmony_ci	}
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_civsi_add_err:
36762306a36Sopenharmony_ci	/* Free head node there are no remaining VSI nodes */
36862306a36Sopenharmony_ci	if (list_empty(&ws_tree_root->child_list_head)) {
36962306a36Sopenharmony_ci		irdma_ws_cqp_cmd(vsi, ws_tree_root, IRDMA_OP_WS_DELETE_NODE);
37062306a36Sopenharmony_ci		vsi->dev->ws_tree_root = NULL;
37162306a36Sopenharmony_ci		irdma_free_node(vsi, ws_tree_root);
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ciexit:
37562306a36Sopenharmony_ci	mutex_unlock(&vsi->dev->ws_mutex);
37662306a36Sopenharmony_ci	return ret;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci/**
38062306a36Sopenharmony_ci * irdma_ws_remove - Free WS scheduler node, update WS tree
38162306a36Sopenharmony_ci * @vsi: vsi pointer
38262306a36Sopenharmony_ci * @user_pri: user priority
38362306a36Sopenharmony_ci */
38462306a36Sopenharmony_civoid irdma_ws_remove(struct irdma_sc_vsi *vsi, u8 user_pri)
38562306a36Sopenharmony_ci{
38662306a36Sopenharmony_ci	mutex_lock(&vsi->dev->ws_mutex);
38762306a36Sopenharmony_ci	if (irdma_tc_in_use(vsi, user_pri))
38862306a36Sopenharmony_ci		goto exit;
38962306a36Sopenharmony_ci	irdma_remove_leaf(vsi, user_pri);
39062306a36Sopenharmony_ciexit:
39162306a36Sopenharmony_ci	mutex_unlock(&vsi->dev->ws_mutex);
39262306a36Sopenharmony_ci}
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci/**
39562306a36Sopenharmony_ci * irdma_ws_reset - Reset entire WS tree
39662306a36Sopenharmony_ci * @vsi: vsi pointer
39762306a36Sopenharmony_ci */
39862306a36Sopenharmony_civoid irdma_ws_reset(struct irdma_sc_vsi *vsi)
39962306a36Sopenharmony_ci{
40062306a36Sopenharmony_ci	u8 i;
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	mutex_lock(&vsi->dev->ws_mutex);
40362306a36Sopenharmony_ci	for (i = 0; i < IRDMA_MAX_USER_PRIORITY; ++i)
40462306a36Sopenharmony_ci		irdma_remove_leaf(vsi, i);
40562306a36Sopenharmony_ci	mutex_unlock(&vsi->dev->ws_mutex);
40662306a36Sopenharmony_ci}
407