162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/* Copyright (c) 2018, Intel Corporation. */
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci#include <net/devlink.h>
562306a36Sopenharmony_ci#include "ice_sched.h"
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci/**
862306a36Sopenharmony_ci * ice_sched_add_root_node - Insert the Tx scheduler root node in SW DB
962306a36Sopenharmony_ci * @pi: port information structure
1062306a36Sopenharmony_ci * @info: Scheduler element information from firmware
1162306a36Sopenharmony_ci *
1262306a36Sopenharmony_ci * This function inserts the root node of the scheduling tree topology
1362306a36Sopenharmony_ci * to the SW DB.
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_cistatic int
1662306a36Sopenharmony_ciice_sched_add_root_node(struct ice_port_info *pi,
1762306a36Sopenharmony_ci			struct ice_aqc_txsched_elem_data *info)
1862306a36Sopenharmony_ci{
1962306a36Sopenharmony_ci	struct ice_sched_node *root;
2062306a36Sopenharmony_ci	struct ice_hw *hw;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci	if (!pi)
2362306a36Sopenharmony_ci		return -EINVAL;
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci	hw = pi->hw;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	root = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*root), GFP_KERNEL);
2862306a36Sopenharmony_ci	if (!root)
2962306a36Sopenharmony_ci		return -ENOMEM;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	/* coverity[suspicious_sizeof] */
3262306a36Sopenharmony_ci	root->children = devm_kcalloc(ice_hw_to_dev(hw), hw->max_children[0],
3362306a36Sopenharmony_ci				      sizeof(*root), GFP_KERNEL);
3462306a36Sopenharmony_ci	if (!root->children) {
3562306a36Sopenharmony_ci		devm_kfree(ice_hw_to_dev(hw), root);
3662306a36Sopenharmony_ci		return -ENOMEM;
3762306a36Sopenharmony_ci	}
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	memcpy(&root->info, info, sizeof(*info));
4062306a36Sopenharmony_ci	pi->root = root;
4162306a36Sopenharmony_ci	return 0;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/**
4562306a36Sopenharmony_ci * ice_sched_find_node_by_teid - Find the Tx scheduler node in SW DB
4662306a36Sopenharmony_ci * @start_node: pointer to the starting ice_sched_node struct in a sub-tree
4762306a36Sopenharmony_ci * @teid: node TEID to search
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci * This function searches for a node matching the TEID in the scheduling tree
5062306a36Sopenharmony_ci * from the SW DB. The search is recursive and is restricted by the number of
5162306a36Sopenharmony_ci * layers it has searched through; stopping at the max supported layer.
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci * This function needs to be called when holding the port_info->sched_lock
5462306a36Sopenharmony_ci */
5562306a36Sopenharmony_cistruct ice_sched_node *
5662306a36Sopenharmony_ciice_sched_find_node_by_teid(struct ice_sched_node *start_node, u32 teid)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	u16 i;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	/* The TEID is same as that of the start_node */
6162306a36Sopenharmony_ci	if (ICE_TXSCHED_GET_NODE_TEID(start_node) == teid)
6262306a36Sopenharmony_ci		return start_node;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	/* The node has no children or is at the max layer */
6562306a36Sopenharmony_ci	if (!start_node->num_children ||
6662306a36Sopenharmony_ci	    start_node->tx_sched_layer >= ICE_AQC_TOPO_MAX_LEVEL_NUM ||
6762306a36Sopenharmony_ci	    start_node->info.data.elem_type == ICE_AQC_ELEM_TYPE_LEAF)
6862306a36Sopenharmony_ci		return NULL;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/* Check if TEID matches to any of the children nodes */
7162306a36Sopenharmony_ci	for (i = 0; i < start_node->num_children; i++)
7262306a36Sopenharmony_ci		if (ICE_TXSCHED_GET_NODE_TEID(start_node->children[i]) == teid)
7362306a36Sopenharmony_ci			return start_node->children[i];
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	/* Search within each child's sub-tree */
7662306a36Sopenharmony_ci	for (i = 0; i < start_node->num_children; i++) {
7762306a36Sopenharmony_ci		struct ice_sched_node *tmp;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci		tmp = ice_sched_find_node_by_teid(start_node->children[i],
8062306a36Sopenharmony_ci						  teid);
8162306a36Sopenharmony_ci		if (tmp)
8262306a36Sopenharmony_ci			return tmp;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return NULL;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/**
8962306a36Sopenharmony_ci * ice_aqc_send_sched_elem_cmd - send scheduling elements cmd
9062306a36Sopenharmony_ci * @hw: pointer to the HW struct
9162306a36Sopenharmony_ci * @cmd_opc: cmd opcode
9262306a36Sopenharmony_ci * @elems_req: number of elements to request
9362306a36Sopenharmony_ci * @buf: pointer to buffer
9462306a36Sopenharmony_ci * @buf_size: buffer size in bytes
9562306a36Sopenharmony_ci * @elems_resp: returns total number of elements response
9662306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
9762306a36Sopenharmony_ci *
9862306a36Sopenharmony_ci * This function sends a scheduling elements cmd (cmd_opc)
9962306a36Sopenharmony_ci */
10062306a36Sopenharmony_cistatic int
10162306a36Sopenharmony_ciice_aqc_send_sched_elem_cmd(struct ice_hw *hw, enum ice_adminq_opc cmd_opc,
10262306a36Sopenharmony_ci			    u16 elems_req, void *buf, u16 buf_size,
10362306a36Sopenharmony_ci			    u16 *elems_resp, struct ice_sq_cd *cd)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct ice_aqc_sched_elem_cmd *cmd;
10662306a36Sopenharmony_ci	struct ice_aq_desc desc;
10762306a36Sopenharmony_ci	int status;
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	cmd = &desc.params.sched_elem_cmd;
11062306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, cmd_opc);
11162306a36Sopenharmony_ci	cmd->num_elem_req = cpu_to_le16(elems_req);
11262306a36Sopenharmony_ci	desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
11362306a36Sopenharmony_ci	status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
11462306a36Sopenharmony_ci	if (!status && elems_resp)
11562306a36Sopenharmony_ci		*elems_resp = le16_to_cpu(cmd->num_elem_resp);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return status;
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci/**
12162306a36Sopenharmony_ci * ice_aq_query_sched_elems - query scheduler elements
12262306a36Sopenharmony_ci * @hw: pointer to the HW struct
12362306a36Sopenharmony_ci * @elems_req: number of elements to query
12462306a36Sopenharmony_ci * @buf: pointer to buffer
12562306a36Sopenharmony_ci * @buf_size: buffer size in bytes
12662306a36Sopenharmony_ci * @elems_ret: returns total number of elements returned
12762306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
12862306a36Sopenharmony_ci *
12962306a36Sopenharmony_ci * Query scheduling elements (0x0404)
13062306a36Sopenharmony_ci */
13162306a36Sopenharmony_ciint
13262306a36Sopenharmony_ciice_aq_query_sched_elems(struct ice_hw *hw, u16 elems_req,
13362306a36Sopenharmony_ci			 struct ice_aqc_txsched_elem_data *buf, u16 buf_size,
13462306a36Sopenharmony_ci			 u16 *elems_ret, struct ice_sq_cd *cd)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_get_sched_elems,
13762306a36Sopenharmony_ci					   elems_req, (void *)buf, buf_size,
13862306a36Sopenharmony_ci					   elems_ret, cd);
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci/**
14262306a36Sopenharmony_ci * ice_sched_add_node - Insert the Tx scheduler node in SW DB
14362306a36Sopenharmony_ci * @pi: port information structure
14462306a36Sopenharmony_ci * @layer: Scheduler layer of the node
14562306a36Sopenharmony_ci * @info: Scheduler element information from firmware
14662306a36Sopenharmony_ci * @prealloc_node: preallocated ice_sched_node struct for SW DB
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci * This function inserts a scheduler node to the SW DB.
14962306a36Sopenharmony_ci */
15062306a36Sopenharmony_ciint
15162306a36Sopenharmony_ciice_sched_add_node(struct ice_port_info *pi, u8 layer,
15262306a36Sopenharmony_ci		   struct ice_aqc_txsched_elem_data *info,
15362306a36Sopenharmony_ci		   struct ice_sched_node *prealloc_node)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct ice_aqc_txsched_elem_data elem;
15662306a36Sopenharmony_ci	struct ice_sched_node *parent;
15762306a36Sopenharmony_ci	struct ice_sched_node *node;
15862306a36Sopenharmony_ci	struct ice_hw *hw;
15962306a36Sopenharmony_ci	int status;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (!pi)
16262306a36Sopenharmony_ci		return -EINVAL;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	hw = pi->hw;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	/* A valid parent node should be there */
16762306a36Sopenharmony_ci	parent = ice_sched_find_node_by_teid(pi->root,
16862306a36Sopenharmony_ci					     le32_to_cpu(info->parent_teid));
16962306a36Sopenharmony_ci	if (!parent) {
17062306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_SCHED, "Parent Node not found for parent_teid=0x%x\n",
17162306a36Sopenharmony_ci			  le32_to_cpu(info->parent_teid));
17262306a36Sopenharmony_ci		return -EINVAL;
17362306a36Sopenharmony_ci	}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	/* query the current node information from FW before adding it
17662306a36Sopenharmony_ci	 * to the SW DB
17762306a36Sopenharmony_ci	 */
17862306a36Sopenharmony_ci	status = ice_sched_query_elem(hw, le32_to_cpu(info->node_teid), &elem);
17962306a36Sopenharmony_ci	if (status)
18062306a36Sopenharmony_ci		return status;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (prealloc_node)
18362306a36Sopenharmony_ci		node = prealloc_node;
18462306a36Sopenharmony_ci	else
18562306a36Sopenharmony_ci		node = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*node), GFP_KERNEL);
18662306a36Sopenharmony_ci	if (!node)
18762306a36Sopenharmony_ci		return -ENOMEM;
18862306a36Sopenharmony_ci	if (hw->max_children[layer]) {
18962306a36Sopenharmony_ci		/* coverity[suspicious_sizeof] */
19062306a36Sopenharmony_ci		node->children = devm_kcalloc(ice_hw_to_dev(hw),
19162306a36Sopenharmony_ci					      hw->max_children[layer],
19262306a36Sopenharmony_ci					      sizeof(*node), GFP_KERNEL);
19362306a36Sopenharmony_ci		if (!node->children) {
19462306a36Sopenharmony_ci			devm_kfree(ice_hw_to_dev(hw), node);
19562306a36Sopenharmony_ci			return -ENOMEM;
19662306a36Sopenharmony_ci		}
19762306a36Sopenharmony_ci	}
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	node->in_use = true;
20062306a36Sopenharmony_ci	node->parent = parent;
20162306a36Sopenharmony_ci	node->tx_sched_layer = layer;
20262306a36Sopenharmony_ci	parent->children[parent->num_children++] = node;
20362306a36Sopenharmony_ci	node->info = elem;
20462306a36Sopenharmony_ci	return 0;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci/**
20862306a36Sopenharmony_ci * ice_aq_delete_sched_elems - delete scheduler elements
20962306a36Sopenharmony_ci * @hw: pointer to the HW struct
21062306a36Sopenharmony_ci * @grps_req: number of groups to delete
21162306a36Sopenharmony_ci * @buf: pointer to buffer
21262306a36Sopenharmony_ci * @buf_size: buffer size in bytes
21362306a36Sopenharmony_ci * @grps_del: returns total number of elements deleted
21462306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
21562306a36Sopenharmony_ci *
21662306a36Sopenharmony_ci * Delete scheduling elements (0x040F)
21762306a36Sopenharmony_ci */
21862306a36Sopenharmony_cistatic int
21962306a36Sopenharmony_ciice_aq_delete_sched_elems(struct ice_hw *hw, u16 grps_req,
22062306a36Sopenharmony_ci			  struct ice_aqc_delete_elem *buf, u16 buf_size,
22162306a36Sopenharmony_ci			  u16 *grps_del, struct ice_sq_cd *cd)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_delete_sched_elems,
22462306a36Sopenharmony_ci					   grps_req, (void *)buf, buf_size,
22562306a36Sopenharmony_ci					   grps_del, cd);
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci/**
22962306a36Sopenharmony_ci * ice_sched_remove_elems - remove nodes from HW
23062306a36Sopenharmony_ci * @hw: pointer to the HW struct
23162306a36Sopenharmony_ci * @parent: pointer to the parent node
23262306a36Sopenharmony_ci * @num_nodes: number of nodes
23362306a36Sopenharmony_ci * @node_teids: array of node teids to be deleted
23462306a36Sopenharmony_ci *
23562306a36Sopenharmony_ci * This function remove nodes from HW
23662306a36Sopenharmony_ci */
23762306a36Sopenharmony_cistatic int
23862306a36Sopenharmony_ciice_sched_remove_elems(struct ice_hw *hw, struct ice_sched_node *parent,
23962306a36Sopenharmony_ci		       u16 num_nodes, u32 *node_teids)
24062306a36Sopenharmony_ci{
24162306a36Sopenharmony_ci	struct ice_aqc_delete_elem *buf;
24262306a36Sopenharmony_ci	u16 i, num_groups_removed = 0;
24362306a36Sopenharmony_ci	u16 buf_size;
24462306a36Sopenharmony_ci	int status;
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	buf_size = struct_size(buf, teid, num_nodes);
24762306a36Sopenharmony_ci	buf = devm_kzalloc(ice_hw_to_dev(hw), buf_size, GFP_KERNEL);
24862306a36Sopenharmony_ci	if (!buf)
24962306a36Sopenharmony_ci		return -ENOMEM;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	buf->hdr.parent_teid = parent->info.node_teid;
25262306a36Sopenharmony_ci	buf->hdr.num_elems = cpu_to_le16(num_nodes);
25362306a36Sopenharmony_ci	for (i = 0; i < num_nodes; i++)
25462306a36Sopenharmony_ci		buf->teid[i] = cpu_to_le32(node_teids[i]);
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	status = ice_aq_delete_sched_elems(hw, 1, buf, buf_size,
25762306a36Sopenharmony_ci					   &num_groups_removed, NULL);
25862306a36Sopenharmony_ci	if (status || num_groups_removed != 1)
25962306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_SCHED, "remove node failed FW error %d\n",
26062306a36Sopenharmony_ci			  hw->adminq.sq_last_status);
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	devm_kfree(ice_hw_to_dev(hw), buf);
26362306a36Sopenharmony_ci	return status;
26462306a36Sopenharmony_ci}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci/**
26762306a36Sopenharmony_ci * ice_sched_get_first_node - get the first node of the given layer
26862306a36Sopenharmony_ci * @pi: port information structure
26962306a36Sopenharmony_ci * @parent: pointer the base node of the subtree
27062306a36Sopenharmony_ci * @layer: layer number
27162306a36Sopenharmony_ci *
27262306a36Sopenharmony_ci * This function retrieves the first node of the given layer from the subtree
27362306a36Sopenharmony_ci */
27462306a36Sopenharmony_cistatic struct ice_sched_node *
27562306a36Sopenharmony_ciice_sched_get_first_node(struct ice_port_info *pi,
27662306a36Sopenharmony_ci			 struct ice_sched_node *parent, u8 layer)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	return pi->sib_head[parent->tc_num][layer];
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci/**
28262306a36Sopenharmony_ci * ice_sched_get_tc_node - get pointer to TC node
28362306a36Sopenharmony_ci * @pi: port information structure
28462306a36Sopenharmony_ci * @tc: TC number
28562306a36Sopenharmony_ci *
28662306a36Sopenharmony_ci * This function returns the TC node pointer
28762306a36Sopenharmony_ci */
28862306a36Sopenharmony_cistruct ice_sched_node *ice_sched_get_tc_node(struct ice_port_info *pi, u8 tc)
28962306a36Sopenharmony_ci{
29062306a36Sopenharmony_ci	u8 i;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (!pi || !pi->root)
29362306a36Sopenharmony_ci		return NULL;
29462306a36Sopenharmony_ci	for (i = 0; i < pi->root->num_children; i++)
29562306a36Sopenharmony_ci		if (pi->root->children[i]->tc_num == tc)
29662306a36Sopenharmony_ci			return pi->root->children[i];
29762306a36Sopenharmony_ci	return NULL;
29862306a36Sopenharmony_ci}
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci/**
30162306a36Sopenharmony_ci * ice_free_sched_node - Free a Tx scheduler node from SW DB
30262306a36Sopenharmony_ci * @pi: port information structure
30362306a36Sopenharmony_ci * @node: pointer to the ice_sched_node struct
30462306a36Sopenharmony_ci *
30562306a36Sopenharmony_ci * This function frees up a node from SW DB as well as from HW
30662306a36Sopenharmony_ci *
30762306a36Sopenharmony_ci * This function needs to be called with the port_info->sched_lock held
30862306a36Sopenharmony_ci */
30962306a36Sopenharmony_civoid ice_free_sched_node(struct ice_port_info *pi, struct ice_sched_node *node)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	struct ice_sched_node *parent;
31262306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
31362306a36Sopenharmony_ci	u8 i, j;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	/* Free the children before freeing up the parent node
31662306a36Sopenharmony_ci	 * The parent array is updated below and that shifts the nodes
31762306a36Sopenharmony_ci	 * in the array. So always pick the first child if num children > 0
31862306a36Sopenharmony_ci	 */
31962306a36Sopenharmony_ci	while (node->num_children)
32062306a36Sopenharmony_ci		ice_free_sched_node(pi, node->children[0]);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	/* Leaf, TC and root nodes can't be deleted by SW */
32362306a36Sopenharmony_ci	if (node->tx_sched_layer >= hw->sw_entry_point_layer &&
32462306a36Sopenharmony_ci	    node->info.data.elem_type != ICE_AQC_ELEM_TYPE_TC &&
32562306a36Sopenharmony_ci	    node->info.data.elem_type != ICE_AQC_ELEM_TYPE_ROOT_PORT &&
32662306a36Sopenharmony_ci	    node->info.data.elem_type != ICE_AQC_ELEM_TYPE_LEAF) {
32762306a36Sopenharmony_ci		u32 teid = le32_to_cpu(node->info.node_teid);
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci		ice_sched_remove_elems(hw, node->parent, 1, &teid);
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci	parent = node->parent;
33262306a36Sopenharmony_ci	/* root has no parent */
33362306a36Sopenharmony_ci	if (parent) {
33462306a36Sopenharmony_ci		struct ice_sched_node *p;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		/* update the parent */
33762306a36Sopenharmony_ci		for (i = 0; i < parent->num_children; i++)
33862306a36Sopenharmony_ci			if (parent->children[i] == node) {
33962306a36Sopenharmony_ci				for (j = i + 1; j < parent->num_children; j++)
34062306a36Sopenharmony_ci					parent->children[j - 1] =
34162306a36Sopenharmony_ci						parent->children[j];
34262306a36Sopenharmony_ci				parent->num_children--;
34362306a36Sopenharmony_ci				break;
34462306a36Sopenharmony_ci			}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci		p = ice_sched_get_first_node(pi, node, node->tx_sched_layer);
34762306a36Sopenharmony_ci		while (p) {
34862306a36Sopenharmony_ci			if (p->sibling == node) {
34962306a36Sopenharmony_ci				p->sibling = node->sibling;
35062306a36Sopenharmony_ci				break;
35162306a36Sopenharmony_ci			}
35262306a36Sopenharmony_ci			p = p->sibling;
35362306a36Sopenharmony_ci		}
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci		/* update the sibling head if head is getting removed */
35662306a36Sopenharmony_ci		if (pi->sib_head[node->tc_num][node->tx_sched_layer] == node)
35762306a36Sopenharmony_ci			pi->sib_head[node->tc_num][node->tx_sched_layer] =
35862306a36Sopenharmony_ci				node->sibling;
35962306a36Sopenharmony_ci	}
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	devm_kfree(ice_hw_to_dev(hw), node->children);
36262306a36Sopenharmony_ci	kfree(node->name);
36362306a36Sopenharmony_ci	xa_erase(&pi->sched_node_ids, node->id);
36462306a36Sopenharmony_ci	devm_kfree(ice_hw_to_dev(hw), node);
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci/**
36862306a36Sopenharmony_ci * ice_aq_get_dflt_topo - gets default scheduler topology
36962306a36Sopenharmony_ci * @hw: pointer to the HW struct
37062306a36Sopenharmony_ci * @lport: logical port number
37162306a36Sopenharmony_ci * @buf: pointer to buffer
37262306a36Sopenharmony_ci * @buf_size: buffer size in bytes
37362306a36Sopenharmony_ci * @num_branches: returns total number of queue to port branches
37462306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
37562306a36Sopenharmony_ci *
37662306a36Sopenharmony_ci * Get default scheduler topology (0x400)
37762306a36Sopenharmony_ci */
37862306a36Sopenharmony_cistatic int
37962306a36Sopenharmony_ciice_aq_get_dflt_topo(struct ice_hw *hw, u8 lport,
38062306a36Sopenharmony_ci		     struct ice_aqc_get_topo_elem *buf, u16 buf_size,
38162306a36Sopenharmony_ci		     u8 *num_branches, struct ice_sq_cd *cd)
38262306a36Sopenharmony_ci{
38362306a36Sopenharmony_ci	struct ice_aqc_get_topo *cmd;
38462306a36Sopenharmony_ci	struct ice_aq_desc desc;
38562306a36Sopenharmony_ci	int status;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	cmd = &desc.params.get_topo;
38862306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_dflt_topo);
38962306a36Sopenharmony_ci	cmd->port_num = lport;
39062306a36Sopenharmony_ci	status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
39162306a36Sopenharmony_ci	if (!status && num_branches)
39262306a36Sopenharmony_ci		*num_branches = cmd->num_branches;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	return status;
39562306a36Sopenharmony_ci}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci/**
39862306a36Sopenharmony_ci * ice_aq_add_sched_elems - adds scheduling element
39962306a36Sopenharmony_ci * @hw: pointer to the HW struct
40062306a36Sopenharmony_ci * @grps_req: the number of groups that are requested to be added
40162306a36Sopenharmony_ci * @buf: pointer to buffer
40262306a36Sopenharmony_ci * @buf_size: buffer size in bytes
40362306a36Sopenharmony_ci * @grps_added: returns total number of groups added
40462306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
40562306a36Sopenharmony_ci *
40662306a36Sopenharmony_ci * Add scheduling elements (0x0401)
40762306a36Sopenharmony_ci */
40862306a36Sopenharmony_cistatic int
40962306a36Sopenharmony_ciice_aq_add_sched_elems(struct ice_hw *hw, u16 grps_req,
41062306a36Sopenharmony_ci		       struct ice_aqc_add_elem *buf, u16 buf_size,
41162306a36Sopenharmony_ci		       u16 *grps_added, struct ice_sq_cd *cd)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_add_sched_elems,
41462306a36Sopenharmony_ci					   grps_req, (void *)buf, buf_size,
41562306a36Sopenharmony_ci					   grps_added, cd);
41662306a36Sopenharmony_ci}
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci/**
41962306a36Sopenharmony_ci * ice_aq_cfg_sched_elems - configures scheduler elements
42062306a36Sopenharmony_ci * @hw: pointer to the HW struct
42162306a36Sopenharmony_ci * @elems_req: number of elements to configure
42262306a36Sopenharmony_ci * @buf: pointer to buffer
42362306a36Sopenharmony_ci * @buf_size: buffer size in bytes
42462306a36Sopenharmony_ci * @elems_cfgd: returns total number of elements configured
42562306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
42662306a36Sopenharmony_ci *
42762306a36Sopenharmony_ci * Configure scheduling elements (0x0403)
42862306a36Sopenharmony_ci */
42962306a36Sopenharmony_cistatic int
43062306a36Sopenharmony_ciice_aq_cfg_sched_elems(struct ice_hw *hw, u16 elems_req,
43162306a36Sopenharmony_ci		       struct ice_aqc_txsched_elem_data *buf, u16 buf_size,
43262306a36Sopenharmony_ci		       u16 *elems_cfgd, struct ice_sq_cd *cd)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_cfg_sched_elems,
43562306a36Sopenharmony_ci					   elems_req, (void *)buf, buf_size,
43662306a36Sopenharmony_ci					   elems_cfgd, cd);
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci/**
44062306a36Sopenharmony_ci * ice_aq_move_sched_elems - move scheduler elements
44162306a36Sopenharmony_ci * @hw: pointer to the HW struct
44262306a36Sopenharmony_ci * @grps_req: number of groups to move
44362306a36Sopenharmony_ci * @buf: pointer to buffer
44462306a36Sopenharmony_ci * @buf_size: buffer size in bytes
44562306a36Sopenharmony_ci * @grps_movd: returns total number of groups moved
44662306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
44762306a36Sopenharmony_ci *
44862306a36Sopenharmony_ci * Move scheduling elements (0x0408)
44962306a36Sopenharmony_ci */
45062306a36Sopenharmony_ciint
45162306a36Sopenharmony_ciice_aq_move_sched_elems(struct ice_hw *hw, u16 grps_req,
45262306a36Sopenharmony_ci			struct ice_aqc_move_elem *buf, u16 buf_size,
45362306a36Sopenharmony_ci			u16 *grps_movd, struct ice_sq_cd *cd)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_move_sched_elems,
45662306a36Sopenharmony_ci					   grps_req, (void *)buf, buf_size,
45762306a36Sopenharmony_ci					   grps_movd, cd);
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci/**
46162306a36Sopenharmony_ci * ice_aq_suspend_sched_elems - suspend scheduler elements
46262306a36Sopenharmony_ci * @hw: pointer to the HW struct
46362306a36Sopenharmony_ci * @elems_req: number of elements to suspend
46462306a36Sopenharmony_ci * @buf: pointer to buffer
46562306a36Sopenharmony_ci * @buf_size: buffer size in bytes
46662306a36Sopenharmony_ci * @elems_ret: returns total number of elements suspended
46762306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
46862306a36Sopenharmony_ci *
46962306a36Sopenharmony_ci * Suspend scheduling elements (0x0409)
47062306a36Sopenharmony_ci */
47162306a36Sopenharmony_cistatic int
47262306a36Sopenharmony_ciice_aq_suspend_sched_elems(struct ice_hw *hw, u16 elems_req, __le32 *buf,
47362306a36Sopenharmony_ci			   u16 buf_size, u16 *elems_ret, struct ice_sq_cd *cd)
47462306a36Sopenharmony_ci{
47562306a36Sopenharmony_ci	return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_suspend_sched_elems,
47662306a36Sopenharmony_ci					   elems_req, (void *)buf, buf_size,
47762306a36Sopenharmony_ci					   elems_ret, cd);
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci/**
48162306a36Sopenharmony_ci * ice_aq_resume_sched_elems - resume scheduler elements
48262306a36Sopenharmony_ci * @hw: pointer to the HW struct
48362306a36Sopenharmony_ci * @elems_req: number of elements to resume
48462306a36Sopenharmony_ci * @buf: pointer to buffer
48562306a36Sopenharmony_ci * @buf_size: buffer size in bytes
48662306a36Sopenharmony_ci * @elems_ret: returns total number of elements resumed
48762306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
48862306a36Sopenharmony_ci *
48962306a36Sopenharmony_ci * resume scheduling elements (0x040A)
49062306a36Sopenharmony_ci */
49162306a36Sopenharmony_cistatic int
49262306a36Sopenharmony_ciice_aq_resume_sched_elems(struct ice_hw *hw, u16 elems_req, __le32 *buf,
49362306a36Sopenharmony_ci			  u16 buf_size, u16 *elems_ret, struct ice_sq_cd *cd)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_resume_sched_elems,
49662306a36Sopenharmony_ci					   elems_req, (void *)buf, buf_size,
49762306a36Sopenharmony_ci					   elems_ret, cd);
49862306a36Sopenharmony_ci}
49962306a36Sopenharmony_ci
50062306a36Sopenharmony_ci/**
50162306a36Sopenharmony_ci * ice_aq_query_sched_res - query scheduler resource
50262306a36Sopenharmony_ci * @hw: pointer to the HW struct
50362306a36Sopenharmony_ci * @buf_size: buffer size in bytes
50462306a36Sopenharmony_ci * @buf: pointer to buffer
50562306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
50662306a36Sopenharmony_ci *
50762306a36Sopenharmony_ci * Query scheduler resource allocation (0x0412)
50862306a36Sopenharmony_ci */
50962306a36Sopenharmony_cistatic int
51062306a36Sopenharmony_ciice_aq_query_sched_res(struct ice_hw *hw, u16 buf_size,
51162306a36Sopenharmony_ci		       struct ice_aqc_query_txsched_res_resp *buf,
51262306a36Sopenharmony_ci		       struct ice_sq_cd *cd)
51362306a36Sopenharmony_ci{
51462306a36Sopenharmony_ci	struct ice_aq_desc desc;
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_query_sched_res);
51762306a36Sopenharmony_ci	return ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
51862306a36Sopenharmony_ci}
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci/**
52162306a36Sopenharmony_ci * ice_sched_suspend_resume_elems - suspend or resume HW nodes
52262306a36Sopenharmony_ci * @hw: pointer to the HW struct
52362306a36Sopenharmony_ci * @num_nodes: number of nodes
52462306a36Sopenharmony_ci * @node_teids: array of node teids to be suspended or resumed
52562306a36Sopenharmony_ci * @suspend: true means suspend / false means resume
52662306a36Sopenharmony_ci *
52762306a36Sopenharmony_ci * This function suspends or resumes HW nodes
52862306a36Sopenharmony_ci */
52962306a36Sopenharmony_ciint
53062306a36Sopenharmony_ciice_sched_suspend_resume_elems(struct ice_hw *hw, u8 num_nodes, u32 *node_teids,
53162306a36Sopenharmony_ci			       bool suspend)
53262306a36Sopenharmony_ci{
53362306a36Sopenharmony_ci	u16 i, buf_size, num_elem_ret = 0;
53462306a36Sopenharmony_ci	__le32 *buf;
53562306a36Sopenharmony_ci	int status;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	buf_size = sizeof(*buf) * num_nodes;
53862306a36Sopenharmony_ci	buf = devm_kzalloc(ice_hw_to_dev(hw), buf_size, GFP_KERNEL);
53962306a36Sopenharmony_ci	if (!buf)
54062306a36Sopenharmony_ci		return -ENOMEM;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	for (i = 0; i < num_nodes; i++)
54362306a36Sopenharmony_ci		buf[i] = cpu_to_le32(node_teids[i]);
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_ci	if (suspend)
54662306a36Sopenharmony_ci		status = ice_aq_suspend_sched_elems(hw, num_nodes, buf,
54762306a36Sopenharmony_ci						    buf_size, &num_elem_ret,
54862306a36Sopenharmony_ci						    NULL);
54962306a36Sopenharmony_ci	else
55062306a36Sopenharmony_ci		status = ice_aq_resume_sched_elems(hw, num_nodes, buf,
55162306a36Sopenharmony_ci						   buf_size, &num_elem_ret,
55262306a36Sopenharmony_ci						   NULL);
55362306a36Sopenharmony_ci	if (status || num_elem_ret != num_nodes)
55462306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_SCHED, "suspend/resume failed\n");
55562306a36Sopenharmony_ci
55662306a36Sopenharmony_ci	devm_kfree(ice_hw_to_dev(hw), buf);
55762306a36Sopenharmony_ci	return status;
55862306a36Sopenharmony_ci}
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci/**
56162306a36Sopenharmony_ci * ice_alloc_lan_q_ctx - allocate LAN queue contexts for the given VSI and TC
56262306a36Sopenharmony_ci * @hw: pointer to the HW struct
56362306a36Sopenharmony_ci * @vsi_handle: VSI handle
56462306a36Sopenharmony_ci * @tc: TC number
56562306a36Sopenharmony_ci * @new_numqs: number of queues
56662306a36Sopenharmony_ci */
56762306a36Sopenharmony_cistatic int
56862306a36Sopenharmony_ciice_alloc_lan_q_ctx(struct ice_hw *hw, u16 vsi_handle, u8 tc, u16 new_numqs)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct ice_vsi_ctx *vsi_ctx;
57162306a36Sopenharmony_ci	struct ice_q_ctx *q_ctx;
57262306a36Sopenharmony_ci	u16 idx;
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	vsi_ctx = ice_get_vsi_ctx(hw, vsi_handle);
57562306a36Sopenharmony_ci	if (!vsi_ctx)
57662306a36Sopenharmony_ci		return -EINVAL;
57762306a36Sopenharmony_ci	/* allocate LAN queue contexts */
57862306a36Sopenharmony_ci	if (!vsi_ctx->lan_q_ctx[tc]) {
57962306a36Sopenharmony_ci		q_ctx = devm_kcalloc(ice_hw_to_dev(hw), new_numqs,
58062306a36Sopenharmony_ci				     sizeof(*q_ctx), GFP_KERNEL);
58162306a36Sopenharmony_ci		if (!q_ctx)
58262306a36Sopenharmony_ci			return -ENOMEM;
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_ci		for (idx = 0; idx < new_numqs; idx++) {
58562306a36Sopenharmony_ci			q_ctx[idx].q_handle = ICE_INVAL_Q_HANDLE;
58662306a36Sopenharmony_ci			q_ctx[idx].q_teid = ICE_INVAL_TEID;
58762306a36Sopenharmony_ci		}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci		vsi_ctx->lan_q_ctx[tc] = q_ctx;
59062306a36Sopenharmony_ci		vsi_ctx->num_lan_q_entries[tc] = new_numqs;
59162306a36Sopenharmony_ci		return 0;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci	/* num queues are increased, update the queue contexts */
59462306a36Sopenharmony_ci	if (new_numqs > vsi_ctx->num_lan_q_entries[tc]) {
59562306a36Sopenharmony_ci		u16 prev_num = vsi_ctx->num_lan_q_entries[tc];
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci		q_ctx = devm_kcalloc(ice_hw_to_dev(hw), new_numqs,
59862306a36Sopenharmony_ci				     sizeof(*q_ctx), GFP_KERNEL);
59962306a36Sopenharmony_ci		if (!q_ctx)
60062306a36Sopenharmony_ci			return -ENOMEM;
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci		memcpy(q_ctx, vsi_ctx->lan_q_ctx[tc],
60362306a36Sopenharmony_ci		       prev_num * sizeof(*q_ctx));
60462306a36Sopenharmony_ci		devm_kfree(ice_hw_to_dev(hw), vsi_ctx->lan_q_ctx[tc]);
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		for (idx = prev_num; idx < new_numqs; idx++) {
60762306a36Sopenharmony_ci			q_ctx[idx].q_handle = ICE_INVAL_Q_HANDLE;
60862306a36Sopenharmony_ci			q_ctx[idx].q_teid = ICE_INVAL_TEID;
60962306a36Sopenharmony_ci		}
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		vsi_ctx->lan_q_ctx[tc] = q_ctx;
61262306a36Sopenharmony_ci		vsi_ctx->num_lan_q_entries[tc] = new_numqs;
61362306a36Sopenharmony_ci	}
61462306a36Sopenharmony_ci	return 0;
61562306a36Sopenharmony_ci}
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci/**
61862306a36Sopenharmony_ci * ice_alloc_rdma_q_ctx - allocate RDMA queue contexts for the given VSI and TC
61962306a36Sopenharmony_ci * @hw: pointer to the HW struct
62062306a36Sopenharmony_ci * @vsi_handle: VSI handle
62162306a36Sopenharmony_ci * @tc: TC number
62262306a36Sopenharmony_ci * @new_numqs: number of queues
62362306a36Sopenharmony_ci */
62462306a36Sopenharmony_cistatic int
62562306a36Sopenharmony_ciice_alloc_rdma_q_ctx(struct ice_hw *hw, u16 vsi_handle, u8 tc, u16 new_numqs)
62662306a36Sopenharmony_ci{
62762306a36Sopenharmony_ci	struct ice_vsi_ctx *vsi_ctx;
62862306a36Sopenharmony_ci	struct ice_q_ctx *q_ctx;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	vsi_ctx = ice_get_vsi_ctx(hw, vsi_handle);
63162306a36Sopenharmony_ci	if (!vsi_ctx)
63262306a36Sopenharmony_ci		return -EINVAL;
63362306a36Sopenharmony_ci	/* allocate RDMA queue contexts */
63462306a36Sopenharmony_ci	if (!vsi_ctx->rdma_q_ctx[tc]) {
63562306a36Sopenharmony_ci		vsi_ctx->rdma_q_ctx[tc] = devm_kcalloc(ice_hw_to_dev(hw),
63662306a36Sopenharmony_ci						       new_numqs,
63762306a36Sopenharmony_ci						       sizeof(*q_ctx),
63862306a36Sopenharmony_ci						       GFP_KERNEL);
63962306a36Sopenharmony_ci		if (!vsi_ctx->rdma_q_ctx[tc])
64062306a36Sopenharmony_ci			return -ENOMEM;
64162306a36Sopenharmony_ci		vsi_ctx->num_rdma_q_entries[tc] = new_numqs;
64262306a36Sopenharmony_ci		return 0;
64362306a36Sopenharmony_ci	}
64462306a36Sopenharmony_ci	/* num queues are increased, update the queue contexts */
64562306a36Sopenharmony_ci	if (new_numqs > vsi_ctx->num_rdma_q_entries[tc]) {
64662306a36Sopenharmony_ci		u16 prev_num = vsi_ctx->num_rdma_q_entries[tc];
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci		q_ctx = devm_kcalloc(ice_hw_to_dev(hw), new_numqs,
64962306a36Sopenharmony_ci				     sizeof(*q_ctx), GFP_KERNEL);
65062306a36Sopenharmony_ci		if (!q_ctx)
65162306a36Sopenharmony_ci			return -ENOMEM;
65262306a36Sopenharmony_ci		memcpy(q_ctx, vsi_ctx->rdma_q_ctx[tc],
65362306a36Sopenharmony_ci		       prev_num * sizeof(*q_ctx));
65462306a36Sopenharmony_ci		devm_kfree(ice_hw_to_dev(hw), vsi_ctx->rdma_q_ctx[tc]);
65562306a36Sopenharmony_ci		vsi_ctx->rdma_q_ctx[tc] = q_ctx;
65662306a36Sopenharmony_ci		vsi_ctx->num_rdma_q_entries[tc] = new_numqs;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci	return 0;
65962306a36Sopenharmony_ci}
66062306a36Sopenharmony_ci
66162306a36Sopenharmony_ci/**
66262306a36Sopenharmony_ci * ice_aq_rl_profile - performs a rate limiting task
66362306a36Sopenharmony_ci * @hw: pointer to the HW struct
66462306a36Sopenharmony_ci * @opcode: opcode for add, query, or remove profile(s)
66562306a36Sopenharmony_ci * @num_profiles: the number of profiles
66662306a36Sopenharmony_ci * @buf: pointer to buffer
66762306a36Sopenharmony_ci * @buf_size: buffer size in bytes
66862306a36Sopenharmony_ci * @num_processed: number of processed add or remove profile(s) to return
66962306a36Sopenharmony_ci * @cd: pointer to command details structure
67062306a36Sopenharmony_ci *
67162306a36Sopenharmony_ci * RL profile function to add, query, or remove profile(s)
67262306a36Sopenharmony_ci */
67362306a36Sopenharmony_cistatic int
67462306a36Sopenharmony_ciice_aq_rl_profile(struct ice_hw *hw, enum ice_adminq_opc opcode,
67562306a36Sopenharmony_ci		  u16 num_profiles, struct ice_aqc_rl_profile_elem *buf,
67662306a36Sopenharmony_ci		  u16 buf_size, u16 *num_processed, struct ice_sq_cd *cd)
67762306a36Sopenharmony_ci{
67862306a36Sopenharmony_ci	struct ice_aqc_rl_profile *cmd;
67962306a36Sopenharmony_ci	struct ice_aq_desc desc;
68062306a36Sopenharmony_ci	int status;
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	cmd = &desc.params.rl_profile;
68362306a36Sopenharmony_ci
68462306a36Sopenharmony_ci	ice_fill_dflt_direct_cmd_desc(&desc, opcode);
68562306a36Sopenharmony_ci	desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
68662306a36Sopenharmony_ci	cmd->num_profiles = cpu_to_le16(num_profiles);
68762306a36Sopenharmony_ci	status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
68862306a36Sopenharmony_ci	if (!status && num_processed)
68962306a36Sopenharmony_ci		*num_processed = le16_to_cpu(cmd->num_processed);
69062306a36Sopenharmony_ci	return status;
69162306a36Sopenharmony_ci}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci/**
69462306a36Sopenharmony_ci * ice_aq_add_rl_profile - adds rate limiting profile(s)
69562306a36Sopenharmony_ci * @hw: pointer to the HW struct
69662306a36Sopenharmony_ci * @num_profiles: the number of profile(s) to be add
69762306a36Sopenharmony_ci * @buf: pointer to buffer
69862306a36Sopenharmony_ci * @buf_size: buffer size in bytes
69962306a36Sopenharmony_ci * @num_profiles_added: total number of profiles added to return
70062306a36Sopenharmony_ci * @cd: pointer to command details structure
70162306a36Sopenharmony_ci *
70262306a36Sopenharmony_ci * Add RL profile (0x0410)
70362306a36Sopenharmony_ci */
70462306a36Sopenharmony_cistatic int
70562306a36Sopenharmony_ciice_aq_add_rl_profile(struct ice_hw *hw, u16 num_profiles,
70662306a36Sopenharmony_ci		      struct ice_aqc_rl_profile_elem *buf, u16 buf_size,
70762306a36Sopenharmony_ci		      u16 *num_profiles_added, struct ice_sq_cd *cd)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	return ice_aq_rl_profile(hw, ice_aqc_opc_add_rl_profiles, num_profiles,
71062306a36Sopenharmony_ci				 buf, buf_size, num_profiles_added, cd);
71162306a36Sopenharmony_ci}
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci/**
71462306a36Sopenharmony_ci * ice_aq_remove_rl_profile - removes RL profile(s)
71562306a36Sopenharmony_ci * @hw: pointer to the HW struct
71662306a36Sopenharmony_ci * @num_profiles: the number of profile(s) to remove
71762306a36Sopenharmony_ci * @buf: pointer to buffer
71862306a36Sopenharmony_ci * @buf_size: buffer size in bytes
71962306a36Sopenharmony_ci * @num_profiles_removed: total number of profiles removed to return
72062306a36Sopenharmony_ci * @cd: pointer to command details structure or NULL
72162306a36Sopenharmony_ci *
72262306a36Sopenharmony_ci * Remove RL profile (0x0415)
72362306a36Sopenharmony_ci */
72462306a36Sopenharmony_cistatic int
72562306a36Sopenharmony_ciice_aq_remove_rl_profile(struct ice_hw *hw, u16 num_profiles,
72662306a36Sopenharmony_ci			 struct ice_aqc_rl_profile_elem *buf, u16 buf_size,
72762306a36Sopenharmony_ci			 u16 *num_profiles_removed, struct ice_sq_cd *cd)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	return ice_aq_rl_profile(hw, ice_aqc_opc_remove_rl_profiles,
73062306a36Sopenharmony_ci				 num_profiles, buf, buf_size,
73162306a36Sopenharmony_ci				 num_profiles_removed, cd);
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci/**
73562306a36Sopenharmony_ci * ice_sched_del_rl_profile - remove RL profile
73662306a36Sopenharmony_ci * @hw: pointer to the HW struct
73762306a36Sopenharmony_ci * @rl_info: rate limit profile information
73862306a36Sopenharmony_ci *
73962306a36Sopenharmony_ci * If the profile ID is not referenced anymore, it removes profile ID with
74062306a36Sopenharmony_ci * its associated parameters from HW DB,and locally. The caller needs to
74162306a36Sopenharmony_ci * hold scheduler lock.
74262306a36Sopenharmony_ci */
74362306a36Sopenharmony_cistatic int
74462306a36Sopenharmony_ciice_sched_del_rl_profile(struct ice_hw *hw,
74562306a36Sopenharmony_ci			 struct ice_aqc_rl_profile_info *rl_info)
74662306a36Sopenharmony_ci{
74762306a36Sopenharmony_ci	struct ice_aqc_rl_profile_elem *buf;
74862306a36Sopenharmony_ci	u16 num_profiles_removed;
74962306a36Sopenharmony_ci	u16 num_profiles = 1;
75062306a36Sopenharmony_ci	int status;
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	if (rl_info->prof_id_ref != 0)
75362306a36Sopenharmony_ci		return -EBUSY;
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_ci	/* Safe to remove profile ID */
75662306a36Sopenharmony_ci	buf = &rl_info->profile;
75762306a36Sopenharmony_ci	status = ice_aq_remove_rl_profile(hw, num_profiles, buf, sizeof(*buf),
75862306a36Sopenharmony_ci					  &num_profiles_removed, NULL);
75962306a36Sopenharmony_ci	if (status || num_profiles_removed != num_profiles)
76062306a36Sopenharmony_ci		return -EIO;
76162306a36Sopenharmony_ci
76262306a36Sopenharmony_ci	/* Delete stale entry now */
76362306a36Sopenharmony_ci	list_del(&rl_info->list_entry);
76462306a36Sopenharmony_ci	devm_kfree(ice_hw_to_dev(hw), rl_info);
76562306a36Sopenharmony_ci	return status;
76662306a36Sopenharmony_ci}
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci/**
76962306a36Sopenharmony_ci * ice_sched_clear_rl_prof - clears RL prof entries
77062306a36Sopenharmony_ci * @pi: port information structure
77162306a36Sopenharmony_ci *
77262306a36Sopenharmony_ci * This function removes all RL profile from HW as well as from SW DB.
77362306a36Sopenharmony_ci */
77462306a36Sopenharmony_cistatic void ice_sched_clear_rl_prof(struct ice_port_info *pi)
77562306a36Sopenharmony_ci{
77662306a36Sopenharmony_ci	u16 ln;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	for (ln = 0; ln < pi->hw->num_tx_sched_layers; ln++) {
77962306a36Sopenharmony_ci		struct ice_aqc_rl_profile_info *rl_prof_elem;
78062306a36Sopenharmony_ci		struct ice_aqc_rl_profile_info *rl_prof_tmp;
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci		list_for_each_entry_safe(rl_prof_elem, rl_prof_tmp,
78362306a36Sopenharmony_ci					 &pi->rl_prof_list[ln], list_entry) {
78462306a36Sopenharmony_ci			struct ice_hw *hw = pi->hw;
78562306a36Sopenharmony_ci			int status;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci			rl_prof_elem->prof_id_ref = 0;
78862306a36Sopenharmony_ci			status = ice_sched_del_rl_profile(hw, rl_prof_elem);
78962306a36Sopenharmony_ci			if (status) {
79062306a36Sopenharmony_ci				ice_debug(hw, ICE_DBG_SCHED, "Remove rl profile failed\n");
79162306a36Sopenharmony_ci				/* On error, free mem required */
79262306a36Sopenharmony_ci				list_del(&rl_prof_elem->list_entry);
79362306a36Sopenharmony_ci				devm_kfree(ice_hw_to_dev(hw), rl_prof_elem);
79462306a36Sopenharmony_ci			}
79562306a36Sopenharmony_ci		}
79662306a36Sopenharmony_ci	}
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci/**
80062306a36Sopenharmony_ci * ice_sched_clear_agg - clears the aggregator related information
80162306a36Sopenharmony_ci * @hw: pointer to the hardware structure
80262306a36Sopenharmony_ci *
80362306a36Sopenharmony_ci * This function removes aggregator list and free up aggregator related memory
80462306a36Sopenharmony_ci * previously allocated.
80562306a36Sopenharmony_ci */
80662306a36Sopenharmony_civoid ice_sched_clear_agg(struct ice_hw *hw)
80762306a36Sopenharmony_ci{
80862306a36Sopenharmony_ci	struct ice_sched_agg_info *agg_info;
80962306a36Sopenharmony_ci	struct ice_sched_agg_info *atmp;
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci	list_for_each_entry_safe(agg_info, atmp, &hw->agg_list, list_entry) {
81262306a36Sopenharmony_ci		struct ice_sched_agg_vsi_info *agg_vsi_info;
81362306a36Sopenharmony_ci		struct ice_sched_agg_vsi_info *vtmp;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_ci		list_for_each_entry_safe(agg_vsi_info, vtmp,
81662306a36Sopenharmony_ci					 &agg_info->agg_vsi_list, list_entry) {
81762306a36Sopenharmony_ci			list_del(&agg_vsi_info->list_entry);
81862306a36Sopenharmony_ci			devm_kfree(ice_hw_to_dev(hw), agg_vsi_info);
81962306a36Sopenharmony_ci		}
82062306a36Sopenharmony_ci		list_del(&agg_info->list_entry);
82162306a36Sopenharmony_ci		devm_kfree(ice_hw_to_dev(hw), agg_info);
82262306a36Sopenharmony_ci	}
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ci/**
82662306a36Sopenharmony_ci * ice_sched_clear_tx_topo - clears the scheduler tree nodes
82762306a36Sopenharmony_ci * @pi: port information structure
82862306a36Sopenharmony_ci *
82962306a36Sopenharmony_ci * This function removes all the nodes from HW as well as from SW DB.
83062306a36Sopenharmony_ci */
83162306a36Sopenharmony_cistatic void ice_sched_clear_tx_topo(struct ice_port_info *pi)
83262306a36Sopenharmony_ci{
83362306a36Sopenharmony_ci	if (!pi)
83462306a36Sopenharmony_ci		return;
83562306a36Sopenharmony_ci	/* remove RL profiles related lists */
83662306a36Sopenharmony_ci	ice_sched_clear_rl_prof(pi);
83762306a36Sopenharmony_ci	if (pi->root) {
83862306a36Sopenharmony_ci		ice_free_sched_node(pi, pi->root);
83962306a36Sopenharmony_ci		pi->root = NULL;
84062306a36Sopenharmony_ci	}
84162306a36Sopenharmony_ci}
84262306a36Sopenharmony_ci
84362306a36Sopenharmony_ci/**
84462306a36Sopenharmony_ci * ice_sched_clear_port - clear the scheduler elements from SW DB for a port
84562306a36Sopenharmony_ci * @pi: port information structure
84662306a36Sopenharmony_ci *
84762306a36Sopenharmony_ci * Cleanup scheduling elements from SW DB
84862306a36Sopenharmony_ci */
84962306a36Sopenharmony_civoid ice_sched_clear_port(struct ice_port_info *pi)
85062306a36Sopenharmony_ci{
85162306a36Sopenharmony_ci	if (!pi || pi->port_state != ICE_SCHED_PORT_STATE_READY)
85262306a36Sopenharmony_ci		return;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	pi->port_state = ICE_SCHED_PORT_STATE_INIT;
85562306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
85662306a36Sopenharmony_ci	ice_sched_clear_tx_topo(pi);
85762306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
85862306a36Sopenharmony_ci	mutex_destroy(&pi->sched_lock);
85962306a36Sopenharmony_ci}
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci/**
86262306a36Sopenharmony_ci * ice_sched_cleanup_all - cleanup scheduler elements from SW DB for all ports
86362306a36Sopenharmony_ci * @hw: pointer to the HW struct
86462306a36Sopenharmony_ci *
86562306a36Sopenharmony_ci * Cleanup scheduling elements from SW DB for all the ports
86662306a36Sopenharmony_ci */
86762306a36Sopenharmony_civoid ice_sched_cleanup_all(struct ice_hw *hw)
86862306a36Sopenharmony_ci{
86962306a36Sopenharmony_ci	if (!hw)
87062306a36Sopenharmony_ci		return;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	devm_kfree(ice_hw_to_dev(hw), hw->layer_info);
87362306a36Sopenharmony_ci	hw->layer_info = NULL;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	ice_sched_clear_port(hw->port_info);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	hw->num_tx_sched_layers = 0;
87862306a36Sopenharmony_ci	hw->num_tx_sched_phys_layers = 0;
87962306a36Sopenharmony_ci	hw->flattened_layers = 0;
88062306a36Sopenharmony_ci	hw->max_cgds = 0;
88162306a36Sopenharmony_ci}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci/**
88462306a36Sopenharmony_ci * ice_sched_add_elems - add nodes to HW and SW DB
88562306a36Sopenharmony_ci * @pi: port information structure
88662306a36Sopenharmony_ci * @tc_node: pointer to the branch node
88762306a36Sopenharmony_ci * @parent: pointer to the parent node
88862306a36Sopenharmony_ci * @layer: layer number to add nodes
88962306a36Sopenharmony_ci * @num_nodes: number of nodes
89062306a36Sopenharmony_ci * @num_nodes_added: pointer to num nodes added
89162306a36Sopenharmony_ci * @first_node_teid: if new nodes are added then return the TEID of first node
89262306a36Sopenharmony_ci * @prealloc_nodes: preallocated nodes struct for software DB
89362306a36Sopenharmony_ci *
89462306a36Sopenharmony_ci * This function add nodes to HW as well as to SW DB for a given layer
89562306a36Sopenharmony_ci */
89662306a36Sopenharmony_ciint
89762306a36Sopenharmony_ciice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node,
89862306a36Sopenharmony_ci		    struct ice_sched_node *parent, u8 layer, u16 num_nodes,
89962306a36Sopenharmony_ci		    u16 *num_nodes_added, u32 *first_node_teid,
90062306a36Sopenharmony_ci		    struct ice_sched_node **prealloc_nodes)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	struct ice_sched_node *prev, *new_node;
90362306a36Sopenharmony_ci	struct ice_aqc_add_elem *buf;
90462306a36Sopenharmony_ci	u16 i, num_groups_added = 0;
90562306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
90662306a36Sopenharmony_ci	size_t buf_size;
90762306a36Sopenharmony_ci	int status = 0;
90862306a36Sopenharmony_ci	u32 teid;
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_ci	buf_size = struct_size(buf, generic, num_nodes);
91162306a36Sopenharmony_ci	buf = devm_kzalloc(ice_hw_to_dev(hw), buf_size, GFP_KERNEL);
91262306a36Sopenharmony_ci	if (!buf)
91362306a36Sopenharmony_ci		return -ENOMEM;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	buf->hdr.parent_teid = parent->info.node_teid;
91662306a36Sopenharmony_ci	buf->hdr.num_elems = cpu_to_le16(num_nodes);
91762306a36Sopenharmony_ci	for (i = 0; i < num_nodes; i++) {
91862306a36Sopenharmony_ci		buf->generic[i].parent_teid = parent->info.node_teid;
91962306a36Sopenharmony_ci		buf->generic[i].data.elem_type = ICE_AQC_ELEM_TYPE_SE_GENERIC;
92062306a36Sopenharmony_ci		buf->generic[i].data.valid_sections =
92162306a36Sopenharmony_ci			ICE_AQC_ELEM_VALID_GENERIC | ICE_AQC_ELEM_VALID_CIR |
92262306a36Sopenharmony_ci			ICE_AQC_ELEM_VALID_EIR;
92362306a36Sopenharmony_ci		buf->generic[i].data.generic = 0;
92462306a36Sopenharmony_ci		buf->generic[i].data.cir_bw.bw_profile_idx =
92562306a36Sopenharmony_ci			cpu_to_le16(ICE_SCHED_DFLT_RL_PROF_ID);
92662306a36Sopenharmony_ci		buf->generic[i].data.cir_bw.bw_alloc =
92762306a36Sopenharmony_ci			cpu_to_le16(ICE_SCHED_DFLT_BW_WT);
92862306a36Sopenharmony_ci		buf->generic[i].data.eir_bw.bw_profile_idx =
92962306a36Sopenharmony_ci			cpu_to_le16(ICE_SCHED_DFLT_RL_PROF_ID);
93062306a36Sopenharmony_ci		buf->generic[i].data.eir_bw.bw_alloc =
93162306a36Sopenharmony_ci			cpu_to_le16(ICE_SCHED_DFLT_BW_WT);
93262306a36Sopenharmony_ci	}
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	status = ice_aq_add_sched_elems(hw, 1, buf, buf_size,
93562306a36Sopenharmony_ci					&num_groups_added, NULL);
93662306a36Sopenharmony_ci	if (status || num_groups_added != 1) {
93762306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_SCHED, "add node failed FW Error %d\n",
93862306a36Sopenharmony_ci			  hw->adminq.sq_last_status);
93962306a36Sopenharmony_ci		devm_kfree(ice_hw_to_dev(hw), buf);
94062306a36Sopenharmony_ci		return -EIO;
94162306a36Sopenharmony_ci	}
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	*num_nodes_added = num_nodes;
94462306a36Sopenharmony_ci	/* add nodes to the SW DB */
94562306a36Sopenharmony_ci	for (i = 0; i < num_nodes; i++) {
94662306a36Sopenharmony_ci		if (prealloc_nodes)
94762306a36Sopenharmony_ci			status = ice_sched_add_node(pi, layer, &buf->generic[i], prealloc_nodes[i]);
94862306a36Sopenharmony_ci		else
94962306a36Sopenharmony_ci			status = ice_sched_add_node(pi, layer, &buf->generic[i], NULL);
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci		if (status) {
95262306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_SCHED, "add nodes in SW DB failed status =%d\n",
95362306a36Sopenharmony_ci				  status);
95462306a36Sopenharmony_ci			break;
95562306a36Sopenharmony_ci		}
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci		teid = le32_to_cpu(buf->generic[i].node_teid);
95862306a36Sopenharmony_ci		new_node = ice_sched_find_node_by_teid(parent, teid);
95962306a36Sopenharmony_ci		if (!new_node) {
96062306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_SCHED, "Node is missing for teid =%d\n", teid);
96162306a36Sopenharmony_ci			break;
96262306a36Sopenharmony_ci		}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci		new_node->sibling = NULL;
96562306a36Sopenharmony_ci		new_node->tc_num = tc_node->tc_num;
96662306a36Sopenharmony_ci		new_node->tx_weight = ICE_SCHED_DFLT_BW_WT;
96762306a36Sopenharmony_ci		new_node->tx_share = ICE_SCHED_DFLT_BW;
96862306a36Sopenharmony_ci		new_node->tx_max = ICE_SCHED_DFLT_BW;
96962306a36Sopenharmony_ci		new_node->name = kzalloc(SCHED_NODE_NAME_MAX_LEN, GFP_KERNEL);
97062306a36Sopenharmony_ci		if (!new_node->name)
97162306a36Sopenharmony_ci			return -ENOMEM;
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_ci		status = xa_alloc(&pi->sched_node_ids, &new_node->id, NULL, XA_LIMIT(0, UINT_MAX),
97462306a36Sopenharmony_ci				  GFP_KERNEL);
97562306a36Sopenharmony_ci		if (status) {
97662306a36Sopenharmony_ci			ice_debug(hw, ICE_DBG_SCHED, "xa_alloc failed for sched node status =%d\n",
97762306a36Sopenharmony_ci				  status);
97862306a36Sopenharmony_ci			break;
97962306a36Sopenharmony_ci		}
98062306a36Sopenharmony_ci
98162306a36Sopenharmony_ci		snprintf(new_node->name, SCHED_NODE_NAME_MAX_LEN, "node_%u", new_node->id);
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci		/* add it to previous node sibling pointer */
98462306a36Sopenharmony_ci		/* Note: siblings are not linked across branches */
98562306a36Sopenharmony_ci		prev = ice_sched_get_first_node(pi, tc_node, layer);
98662306a36Sopenharmony_ci		if (prev && prev != new_node) {
98762306a36Sopenharmony_ci			while (prev->sibling)
98862306a36Sopenharmony_ci				prev = prev->sibling;
98962306a36Sopenharmony_ci			prev->sibling = new_node;
99062306a36Sopenharmony_ci		}
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci		/* initialize the sibling head */
99362306a36Sopenharmony_ci		if (!pi->sib_head[tc_node->tc_num][layer])
99462306a36Sopenharmony_ci			pi->sib_head[tc_node->tc_num][layer] = new_node;
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci		if (i == 0)
99762306a36Sopenharmony_ci			*first_node_teid = teid;
99862306a36Sopenharmony_ci	}
99962306a36Sopenharmony_ci
100062306a36Sopenharmony_ci	devm_kfree(ice_hw_to_dev(hw), buf);
100162306a36Sopenharmony_ci	return status;
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci/**
100562306a36Sopenharmony_ci * ice_sched_add_nodes_to_hw_layer - Add nodes to HW layer
100662306a36Sopenharmony_ci * @pi: port information structure
100762306a36Sopenharmony_ci * @tc_node: pointer to TC node
100862306a36Sopenharmony_ci * @parent: pointer to parent node
100962306a36Sopenharmony_ci * @layer: layer number to add nodes
101062306a36Sopenharmony_ci * @num_nodes: number of nodes to be added
101162306a36Sopenharmony_ci * @first_node_teid: pointer to the first node TEID
101262306a36Sopenharmony_ci * @num_nodes_added: pointer to number of nodes added
101362306a36Sopenharmony_ci *
101462306a36Sopenharmony_ci * Add nodes into specific HW layer.
101562306a36Sopenharmony_ci */
101662306a36Sopenharmony_cistatic int
101762306a36Sopenharmony_ciice_sched_add_nodes_to_hw_layer(struct ice_port_info *pi,
101862306a36Sopenharmony_ci				struct ice_sched_node *tc_node,
101962306a36Sopenharmony_ci				struct ice_sched_node *parent, u8 layer,
102062306a36Sopenharmony_ci				u16 num_nodes, u32 *first_node_teid,
102162306a36Sopenharmony_ci				u16 *num_nodes_added)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	u16 max_child_nodes;
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	*num_nodes_added = 0;
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_ci	if (!num_nodes)
102862306a36Sopenharmony_ci		return 0;
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	if (!parent || layer < pi->hw->sw_entry_point_layer)
103162306a36Sopenharmony_ci		return -EINVAL;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	/* max children per node per layer */
103462306a36Sopenharmony_ci	max_child_nodes = pi->hw->max_children[parent->tx_sched_layer];
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	/* current number of children + required nodes exceed max children */
103762306a36Sopenharmony_ci	if ((parent->num_children + num_nodes) > max_child_nodes) {
103862306a36Sopenharmony_ci		/* Fail if the parent is a TC node */
103962306a36Sopenharmony_ci		if (parent == tc_node)
104062306a36Sopenharmony_ci			return -EIO;
104162306a36Sopenharmony_ci		return -ENOSPC;
104262306a36Sopenharmony_ci	}
104362306a36Sopenharmony_ci
104462306a36Sopenharmony_ci	return ice_sched_add_elems(pi, tc_node, parent, layer, num_nodes,
104562306a36Sopenharmony_ci				   num_nodes_added, first_node_teid, NULL);
104662306a36Sopenharmony_ci}
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci/**
104962306a36Sopenharmony_ci * ice_sched_add_nodes_to_layer - Add nodes to a given layer
105062306a36Sopenharmony_ci * @pi: port information structure
105162306a36Sopenharmony_ci * @tc_node: pointer to TC node
105262306a36Sopenharmony_ci * @parent: pointer to parent node
105362306a36Sopenharmony_ci * @layer: layer number to add nodes
105462306a36Sopenharmony_ci * @num_nodes: number of nodes to be added
105562306a36Sopenharmony_ci * @first_node_teid: pointer to the first node TEID
105662306a36Sopenharmony_ci * @num_nodes_added: pointer to number of nodes added
105762306a36Sopenharmony_ci *
105862306a36Sopenharmony_ci * This function add nodes to a given layer.
105962306a36Sopenharmony_ci */
106062306a36Sopenharmony_ciint
106162306a36Sopenharmony_ciice_sched_add_nodes_to_layer(struct ice_port_info *pi,
106262306a36Sopenharmony_ci			     struct ice_sched_node *tc_node,
106362306a36Sopenharmony_ci			     struct ice_sched_node *parent, u8 layer,
106462306a36Sopenharmony_ci			     u16 num_nodes, u32 *first_node_teid,
106562306a36Sopenharmony_ci			     u16 *num_nodes_added)
106662306a36Sopenharmony_ci{
106762306a36Sopenharmony_ci	u32 *first_teid_ptr = first_node_teid;
106862306a36Sopenharmony_ci	u16 new_num_nodes = num_nodes;
106962306a36Sopenharmony_ci	int status = 0;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	*num_nodes_added = 0;
107262306a36Sopenharmony_ci	while (*num_nodes_added < num_nodes) {
107362306a36Sopenharmony_ci		u16 max_child_nodes, num_added = 0;
107462306a36Sopenharmony_ci		u32 temp;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci		status = ice_sched_add_nodes_to_hw_layer(pi, tc_node, parent,
107762306a36Sopenharmony_ci							 layer,	new_num_nodes,
107862306a36Sopenharmony_ci							 first_teid_ptr,
107962306a36Sopenharmony_ci							 &num_added);
108062306a36Sopenharmony_ci		if (!status)
108162306a36Sopenharmony_ci			*num_nodes_added += num_added;
108262306a36Sopenharmony_ci		/* added more nodes than requested ? */
108362306a36Sopenharmony_ci		if (*num_nodes_added > num_nodes) {
108462306a36Sopenharmony_ci			ice_debug(pi->hw, ICE_DBG_SCHED, "added extra nodes %d %d\n", num_nodes,
108562306a36Sopenharmony_ci				  *num_nodes_added);
108662306a36Sopenharmony_ci			status = -EIO;
108762306a36Sopenharmony_ci			break;
108862306a36Sopenharmony_ci		}
108962306a36Sopenharmony_ci		/* break if all the nodes are added successfully */
109062306a36Sopenharmony_ci		if (!status && (*num_nodes_added == num_nodes))
109162306a36Sopenharmony_ci			break;
109262306a36Sopenharmony_ci		/* break if the error is not max limit */
109362306a36Sopenharmony_ci		if (status && status != -ENOSPC)
109462306a36Sopenharmony_ci			break;
109562306a36Sopenharmony_ci		/* Exceeded the max children */
109662306a36Sopenharmony_ci		max_child_nodes = pi->hw->max_children[parent->tx_sched_layer];
109762306a36Sopenharmony_ci		/* utilize all the spaces if the parent is not full */
109862306a36Sopenharmony_ci		if (parent->num_children < max_child_nodes) {
109962306a36Sopenharmony_ci			new_num_nodes = max_child_nodes - parent->num_children;
110062306a36Sopenharmony_ci		} else {
110162306a36Sopenharmony_ci			/* This parent is full, try the next sibling */
110262306a36Sopenharmony_ci			parent = parent->sibling;
110362306a36Sopenharmony_ci			/* Don't modify the first node TEID memory if the
110462306a36Sopenharmony_ci			 * first node was added already in the above call.
110562306a36Sopenharmony_ci			 * Instead send some temp memory for all other
110662306a36Sopenharmony_ci			 * recursive calls.
110762306a36Sopenharmony_ci			 */
110862306a36Sopenharmony_ci			if (num_added)
110962306a36Sopenharmony_ci				first_teid_ptr = &temp;
111062306a36Sopenharmony_ci
111162306a36Sopenharmony_ci			new_num_nodes = num_nodes - *num_nodes_added;
111262306a36Sopenharmony_ci		}
111362306a36Sopenharmony_ci	}
111462306a36Sopenharmony_ci	return status;
111562306a36Sopenharmony_ci}
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci/**
111862306a36Sopenharmony_ci * ice_sched_get_qgrp_layer - get the current queue group layer number
111962306a36Sopenharmony_ci * @hw: pointer to the HW struct
112062306a36Sopenharmony_ci *
112162306a36Sopenharmony_ci * This function returns the current queue group layer number
112262306a36Sopenharmony_ci */
112362306a36Sopenharmony_cistatic u8 ice_sched_get_qgrp_layer(struct ice_hw *hw)
112462306a36Sopenharmony_ci{
112562306a36Sopenharmony_ci	/* It's always total layers - 1, the array is 0 relative so -2 */
112662306a36Sopenharmony_ci	return hw->num_tx_sched_layers - ICE_QGRP_LAYER_OFFSET;
112762306a36Sopenharmony_ci}
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci/**
113062306a36Sopenharmony_ci * ice_sched_get_vsi_layer - get the current VSI layer number
113162306a36Sopenharmony_ci * @hw: pointer to the HW struct
113262306a36Sopenharmony_ci *
113362306a36Sopenharmony_ci * This function returns the current VSI layer number
113462306a36Sopenharmony_ci */
113562306a36Sopenharmony_ciu8 ice_sched_get_vsi_layer(struct ice_hw *hw)
113662306a36Sopenharmony_ci{
113762306a36Sopenharmony_ci	/* Num Layers       VSI layer
113862306a36Sopenharmony_ci	 *     9               6
113962306a36Sopenharmony_ci	 *     7               4
114062306a36Sopenharmony_ci	 *     5 or less       sw_entry_point_layer
114162306a36Sopenharmony_ci	 */
114262306a36Sopenharmony_ci	/* calculate the VSI layer based on number of layers. */
114362306a36Sopenharmony_ci	if (hw->num_tx_sched_layers > ICE_VSI_LAYER_OFFSET + 1) {
114462306a36Sopenharmony_ci		u8 layer = hw->num_tx_sched_layers - ICE_VSI_LAYER_OFFSET;
114562306a36Sopenharmony_ci
114662306a36Sopenharmony_ci		if (layer > hw->sw_entry_point_layer)
114762306a36Sopenharmony_ci			return layer;
114862306a36Sopenharmony_ci	}
114962306a36Sopenharmony_ci	return hw->sw_entry_point_layer;
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_ci/**
115362306a36Sopenharmony_ci * ice_sched_get_agg_layer - get the current aggregator layer number
115462306a36Sopenharmony_ci * @hw: pointer to the HW struct
115562306a36Sopenharmony_ci *
115662306a36Sopenharmony_ci * This function returns the current aggregator layer number
115762306a36Sopenharmony_ci */
115862306a36Sopenharmony_ciu8 ice_sched_get_agg_layer(struct ice_hw *hw)
115962306a36Sopenharmony_ci{
116062306a36Sopenharmony_ci	/* Num Layers       aggregator layer
116162306a36Sopenharmony_ci	 *     9               4
116262306a36Sopenharmony_ci	 *     7 or less       sw_entry_point_layer
116362306a36Sopenharmony_ci	 */
116462306a36Sopenharmony_ci	/* calculate the aggregator layer based on number of layers. */
116562306a36Sopenharmony_ci	if (hw->num_tx_sched_layers > ICE_AGG_LAYER_OFFSET + 1) {
116662306a36Sopenharmony_ci		u8 layer = hw->num_tx_sched_layers - ICE_AGG_LAYER_OFFSET;
116762306a36Sopenharmony_ci
116862306a36Sopenharmony_ci		if (layer > hw->sw_entry_point_layer)
116962306a36Sopenharmony_ci			return layer;
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci	return hw->sw_entry_point_layer;
117262306a36Sopenharmony_ci}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_ci/**
117562306a36Sopenharmony_ci * ice_rm_dflt_leaf_node - remove the default leaf node in the tree
117662306a36Sopenharmony_ci * @pi: port information structure
117762306a36Sopenharmony_ci *
117862306a36Sopenharmony_ci * This function removes the leaf node that was created by the FW
117962306a36Sopenharmony_ci * during initialization
118062306a36Sopenharmony_ci */
118162306a36Sopenharmony_cistatic void ice_rm_dflt_leaf_node(struct ice_port_info *pi)
118262306a36Sopenharmony_ci{
118362306a36Sopenharmony_ci	struct ice_sched_node *node;
118462306a36Sopenharmony_ci
118562306a36Sopenharmony_ci	node = pi->root;
118662306a36Sopenharmony_ci	while (node) {
118762306a36Sopenharmony_ci		if (!node->num_children)
118862306a36Sopenharmony_ci			break;
118962306a36Sopenharmony_ci		node = node->children[0];
119062306a36Sopenharmony_ci	}
119162306a36Sopenharmony_ci	if (node && node->info.data.elem_type == ICE_AQC_ELEM_TYPE_LEAF) {
119262306a36Sopenharmony_ci		u32 teid = le32_to_cpu(node->info.node_teid);
119362306a36Sopenharmony_ci		int status;
119462306a36Sopenharmony_ci
119562306a36Sopenharmony_ci		/* remove the default leaf node */
119662306a36Sopenharmony_ci		status = ice_sched_remove_elems(pi->hw, node->parent, 1, &teid);
119762306a36Sopenharmony_ci		if (!status)
119862306a36Sopenharmony_ci			ice_free_sched_node(pi, node);
119962306a36Sopenharmony_ci	}
120062306a36Sopenharmony_ci}
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci/**
120362306a36Sopenharmony_ci * ice_sched_rm_dflt_nodes - free the default nodes in the tree
120462306a36Sopenharmony_ci * @pi: port information structure
120562306a36Sopenharmony_ci *
120662306a36Sopenharmony_ci * This function frees all the nodes except root and TC that were created by
120762306a36Sopenharmony_ci * the FW during initialization
120862306a36Sopenharmony_ci */
120962306a36Sopenharmony_cistatic void ice_sched_rm_dflt_nodes(struct ice_port_info *pi)
121062306a36Sopenharmony_ci{
121162306a36Sopenharmony_ci	struct ice_sched_node *node;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	ice_rm_dflt_leaf_node(pi);
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	/* remove the default nodes except TC and root nodes */
121662306a36Sopenharmony_ci	node = pi->root;
121762306a36Sopenharmony_ci	while (node) {
121862306a36Sopenharmony_ci		if (node->tx_sched_layer >= pi->hw->sw_entry_point_layer &&
121962306a36Sopenharmony_ci		    node->info.data.elem_type != ICE_AQC_ELEM_TYPE_TC &&
122062306a36Sopenharmony_ci		    node->info.data.elem_type != ICE_AQC_ELEM_TYPE_ROOT_PORT) {
122162306a36Sopenharmony_ci			ice_free_sched_node(pi, node);
122262306a36Sopenharmony_ci			break;
122362306a36Sopenharmony_ci		}
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci		if (!node->num_children)
122662306a36Sopenharmony_ci			break;
122762306a36Sopenharmony_ci		node = node->children[0];
122862306a36Sopenharmony_ci	}
122962306a36Sopenharmony_ci}
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci/**
123262306a36Sopenharmony_ci * ice_sched_init_port - Initialize scheduler by querying information from FW
123362306a36Sopenharmony_ci * @pi: port info structure for the tree to cleanup
123462306a36Sopenharmony_ci *
123562306a36Sopenharmony_ci * This function is the initial call to find the total number of Tx scheduler
123662306a36Sopenharmony_ci * resources, default topology created by firmware and storing the information
123762306a36Sopenharmony_ci * in SW DB.
123862306a36Sopenharmony_ci */
123962306a36Sopenharmony_ciint ice_sched_init_port(struct ice_port_info *pi)
124062306a36Sopenharmony_ci{
124162306a36Sopenharmony_ci	struct ice_aqc_get_topo_elem *buf;
124262306a36Sopenharmony_ci	struct ice_hw *hw;
124362306a36Sopenharmony_ci	u8 num_branches;
124462306a36Sopenharmony_ci	u16 num_elems;
124562306a36Sopenharmony_ci	int status;
124662306a36Sopenharmony_ci	u8 i, j;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	if (!pi)
124962306a36Sopenharmony_ci		return -EINVAL;
125062306a36Sopenharmony_ci	hw = pi->hw;
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci	/* Query the Default Topology from FW */
125362306a36Sopenharmony_ci	buf = kzalloc(ICE_AQ_MAX_BUF_LEN, GFP_KERNEL);
125462306a36Sopenharmony_ci	if (!buf)
125562306a36Sopenharmony_ci		return -ENOMEM;
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	/* Query default scheduling tree topology */
125862306a36Sopenharmony_ci	status = ice_aq_get_dflt_topo(hw, pi->lport, buf, ICE_AQ_MAX_BUF_LEN,
125962306a36Sopenharmony_ci				      &num_branches, NULL);
126062306a36Sopenharmony_ci	if (status)
126162306a36Sopenharmony_ci		goto err_init_port;
126262306a36Sopenharmony_ci
126362306a36Sopenharmony_ci	/* num_branches should be between 1-8 */
126462306a36Sopenharmony_ci	if (num_branches < 1 || num_branches > ICE_TXSCHED_MAX_BRANCHES) {
126562306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_SCHED, "num_branches unexpected %d\n",
126662306a36Sopenharmony_ci			  num_branches);
126762306a36Sopenharmony_ci		status = -EINVAL;
126862306a36Sopenharmony_ci		goto err_init_port;
126962306a36Sopenharmony_ci	}
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	/* get the number of elements on the default/first branch */
127262306a36Sopenharmony_ci	num_elems = le16_to_cpu(buf[0].hdr.num_elems);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	/* num_elems should always be between 1-9 */
127562306a36Sopenharmony_ci	if (num_elems < 1 || num_elems > ICE_AQC_TOPO_MAX_LEVEL_NUM) {
127662306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_SCHED, "num_elems unexpected %d\n",
127762306a36Sopenharmony_ci			  num_elems);
127862306a36Sopenharmony_ci		status = -EINVAL;
127962306a36Sopenharmony_ci		goto err_init_port;
128062306a36Sopenharmony_ci	}
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	/* If the last node is a leaf node then the index of the queue group
128362306a36Sopenharmony_ci	 * layer is two less than the number of elements.
128462306a36Sopenharmony_ci	 */
128562306a36Sopenharmony_ci	if (num_elems > 2 && buf[0].generic[num_elems - 1].data.elem_type ==
128662306a36Sopenharmony_ci	    ICE_AQC_ELEM_TYPE_LEAF)
128762306a36Sopenharmony_ci		pi->last_node_teid =
128862306a36Sopenharmony_ci			le32_to_cpu(buf[0].generic[num_elems - 2].node_teid);
128962306a36Sopenharmony_ci	else
129062306a36Sopenharmony_ci		pi->last_node_teid =
129162306a36Sopenharmony_ci			le32_to_cpu(buf[0].generic[num_elems - 1].node_teid);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci	/* Insert the Tx Sched root node */
129462306a36Sopenharmony_ci	status = ice_sched_add_root_node(pi, &buf[0].generic[0]);
129562306a36Sopenharmony_ci	if (status)
129662306a36Sopenharmony_ci		goto err_init_port;
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	/* Parse the default tree and cache the information */
129962306a36Sopenharmony_ci	for (i = 0; i < num_branches; i++) {
130062306a36Sopenharmony_ci		num_elems = le16_to_cpu(buf[i].hdr.num_elems);
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci		/* Skip root element as already inserted */
130362306a36Sopenharmony_ci		for (j = 1; j < num_elems; j++) {
130462306a36Sopenharmony_ci			/* update the sw entry point */
130562306a36Sopenharmony_ci			if (buf[0].generic[j].data.elem_type ==
130662306a36Sopenharmony_ci			    ICE_AQC_ELEM_TYPE_ENTRY_POINT)
130762306a36Sopenharmony_ci				hw->sw_entry_point_layer = j;
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci			status = ice_sched_add_node(pi, j, &buf[i].generic[j], NULL);
131062306a36Sopenharmony_ci			if (status)
131162306a36Sopenharmony_ci				goto err_init_port;
131262306a36Sopenharmony_ci		}
131362306a36Sopenharmony_ci	}
131462306a36Sopenharmony_ci
131562306a36Sopenharmony_ci	/* Remove the default nodes. */
131662306a36Sopenharmony_ci	if (pi->root)
131762306a36Sopenharmony_ci		ice_sched_rm_dflt_nodes(pi);
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_ci	/* initialize the port for handling the scheduler tree */
132062306a36Sopenharmony_ci	pi->port_state = ICE_SCHED_PORT_STATE_READY;
132162306a36Sopenharmony_ci	mutex_init(&pi->sched_lock);
132262306a36Sopenharmony_ci	for (i = 0; i < ICE_AQC_TOPO_MAX_LEVEL_NUM; i++)
132362306a36Sopenharmony_ci		INIT_LIST_HEAD(&pi->rl_prof_list[i]);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_cierr_init_port:
132662306a36Sopenharmony_ci	if (status && pi->root) {
132762306a36Sopenharmony_ci		ice_free_sched_node(pi, pi->root);
132862306a36Sopenharmony_ci		pi->root = NULL;
132962306a36Sopenharmony_ci	}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ci	kfree(buf);
133262306a36Sopenharmony_ci	return status;
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ci/**
133662306a36Sopenharmony_ci * ice_sched_query_res_alloc - query the FW for num of logical sched layers
133762306a36Sopenharmony_ci * @hw: pointer to the HW struct
133862306a36Sopenharmony_ci *
133962306a36Sopenharmony_ci * query FW for allocated scheduler resources and store in HW struct
134062306a36Sopenharmony_ci */
134162306a36Sopenharmony_ciint ice_sched_query_res_alloc(struct ice_hw *hw)
134262306a36Sopenharmony_ci{
134362306a36Sopenharmony_ci	struct ice_aqc_query_txsched_res_resp *buf;
134462306a36Sopenharmony_ci	__le16 max_sibl;
134562306a36Sopenharmony_ci	int status = 0;
134662306a36Sopenharmony_ci	u16 i;
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	if (hw->layer_info)
134962306a36Sopenharmony_ci		return status;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_ci	buf = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*buf), GFP_KERNEL);
135262306a36Sopenharmony_ci	if (!buf)
135362306a36Sopenharmony_ci		return -ENOMEM;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	status = ice_aq_query_sched_res(hw, sizeof(*buf), buf, NULL);
135662306a36Sopenharmony_ci	if (status)
135762306a36Sopenharmony_ci		goto sched_query_out;
135862306a36Sopenharmony_ci
135962306a36Sopenharmony_ci	hw->num_tx_sched_layers = le16_to_cpu(buf->sched_props.logical_levels);
136062306a36Sopenharmony_ci	hw->num_tx_sched_phys_layers =
136162306a36Sopenharmony_ci		le16_to_cpu(buf->sched_props.phys_levels);
136262306a36Sopenharmony_ci	hw->flattened_layers = buf->sched_props.flattening_bitmap;
136362306a36Sopenharmony_ci	hw->max_cgds = buf->sched_props.max_pf_cgds;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	/* max sibling group size of current layer refers to the max children
136662306a36Sopenharmony_ci	 * of the below layer node.
136762306a36Sopenharmony_ci	 * layer 1 node max children will be layer 2 max sibling group size
136862306a36Sopenharmony_ci	 * layer 2 node max children will be layer 3 max sibling group size
136962306a36Sopenharmony_ci	 * and so on. This array will be populated from root (index 0) to
137062306a36Sopenharmony_ci	 * qgroup layer 7. Leaf node has no children.
137162306a36Sopenharmony_ci	 */
137262306a36Sopenharmony_ci	for (i = 0; i < hw->num_tx_sched_layers - 1; i++) {
137362306a36Sopenharmony_ci		max_sibl = buf->layer_props[i + 1].max_sibl_grp_sz;
137462306a36Sopenharmony_ci		hw->max_children[i] = le16_to_cpu(max_sibl);
137562306a36Sopenharmony_ci	}
137662306a36Sopenharmony_ci
137762306a36Sopenharmony_ci	hw->layer_info = devm_kmemdup(ice_hw_to_dev(hw), buf->layer_props,
137862306a36Sopenharmony_ci				      (hw->num_tx_sched_layers *
137962306a36Sopenharmony_ci				       sizeof(*hw->layer_info)),
138062306a36Sopenharmony_ci				      GFP_KERNEL);
138162306a36Sopenharmony_ci	if (!hw->layer_info) {
138262306a36Sopenharmony_ci		status = -ENOMEM;
138362306a36Sopenharmony_ci		goto sched_query_out;
138462306a36Sopenharmony_ci	}
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_cisched_query_out:
138762306a36Sopenharmony_ci	devm_kfree(ice_hw_to_dev(hw), buf);
138862306a36Sopenharmony_ci	return status;
138962306a36Sopenharmony_ci}
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci/**
139262306a36Sopenharmony_ci * ice_sched_get_psm_clk_freq - determine the PSM clock frequency
139362306a36Sopenharmony_ci * @hw: pointer to the HW struct
139462306a36Sopenharmony_ci *
139562306a36Sopenharmony_ci * Determine the PSM clock frequency and store in HW struct
139662306a36Sopenharmony_ci */
139762306a36Sopenharmony_civoid ice_sched_get_psm_clk_freq(struct ice_hw *hw)
139862306a36Sopenharmony_ci{
139962306a36Sopenharmony_ci	u32 val, clk_src;
140062306a36Sopenharmony_ci
140162306a36Sopenharmony_ci	val = rd32(hw, GLGEN_CLKSTAT_SRC);
140262306a36Sopenharmony_ci	clk_src = (val & GLGEN_CLKSTAT_SRC_PSM_CLK_SRC_M) >>
140362306a36Sopenharmony_ci		GLGEN_CLKSTAT_SRC_PSM_CLK_SRC_S;
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci#define PSM_CLK_SRC_367_MHZ 0x0
140662306a36Sopenharmony_ci#define PSM_CLK_SRC_416_MHZ 0x1
140762306a36Sopenharmony_ci#define PSM_CLK_SRC_446_MHZ 0x2
140862306a36Sopenharmony_ci#define PSM_CLK_SRC_390_MHZ 0x3
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	switch (clk_src) {
141162306a36Sopenharmony_ci	case PSM_CLK_SRC_367_MHZ:
141262306a36Sopenharmony_ci		hw->psm_clk_freq = ICE_PSM_CLK_367MHZ_IN_HZ;
141362306a36Sopenharmony_ci		break;
141462306a36Sopenharmony_ci	case PSM_CLK_SRC_416_MHZ:
141562306a36Sopenharmony_ci		hw->psm_clk_freq = ICE_PSM_CLK_416MHZ_IN_HZ;
141662306a36Sopenharmony_ci		break;
141762306a36Sopenharmony_ci	case PSM_CLK_SRC_446_MHZ:
141862306a36Sopenharmony_ci		hw->psm_clk_freq = ICE_PSM_CLK_446MHZ_IN_HZ;
141962306a36Sopenharmony_ci		break;
142062306a36Sopenharmony_ci	case PSM_CLK_SRC_390_MHZ:
142162306a36Sopenharmony_ci		hw->psm_clk_freq = ICE_PSM_CLK_390MHZ_IN_HZ;
142262306a36Sopenharmony_ci		break;
142362306a36Sopenharmony_ci	default:
142462306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_SCHED, "PSM clk_src unexpected %u\n",
142562306a36Sopenharmony_ci			  clk_src);
142662306a36Sopenharmony_ci		/* fall back to a safe default */
142762306a36Sopenharmony_ci		hw->psm_clk_freq = ICE_PSM_CLK_446MHZ_IN_HZ;
142862306a36Sopenharmony_ci	}
142962306a36Sopenharmony_ci}
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci/**
143262306a36Sopenharmony_ci * ice_sched_find_node_in_subtree - Find node in part of base node subtree
143362306a36Sopenharmony_ci * @hw: pointer to the HW struct
143462306a36Sopenharmony_ci * @base: pointer to the base node
143562306a36Sopenharmony_ci * @node: pointer to the node to search
143662306a36Sopenharmony_ci *
143762306a36Sopenharmony_ci * This function checks whether a given node is part of the base node
143862306a36Sopenharmony_ci * subtree or not
143962306a36Sopenharmony_ci */
144062306a36Sopenharmony_cistatic bool
144162306a36Sopenharmony_ciice_sched_find_node_in_subtree(struct ice_hw *hw, struct ice_sched_node *base,
144262306a36Sopenharmony_ci			       struct ice_sched_node *node)
144362306a36Sopenharmony_ci{
144462306a36Sopenharmony_ci	u8 i;
144562306a36Sopenharmony_ci
144662306a36Sopenharmony_ci	for (i = 0; i < base->num_children; i++) {
144762306a36Sopenharmony_ci		struct ice_sched_node *child = base->children[i];
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci		if (node == child)
145062306a36Sopenharmony_ci			return true;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci		if (child->tx_sched_layer > node->tx_sched_layer)
145362306a36Sopenharmony_ci			return false;
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci		/* this recursion is intentional, and wouldn't
145662306a36Sopenharmony_ci		 * go more than 8 calls
145762306a36Sopenharmony_ci		 */
145862306a36Sopenharmony_ci		if (ice_sched_find_node_in_subtree(hw, child, node))
145962306a36Sopenharmony_ci			return true;
146062306a36Sopenharmony_ci	}
146162306a36Sopenharmony_ci	return false;
146262306a36Sopenharmony_ci}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci/**
146562306a36Sopenharmony_ci * ice_sched_get_free_qgrp - Scan all queue group siblings and find a free node
146662306a36Sopenharmony_ci * @pi: port information structure
146762306a36Sopenharmony_ci * @vsi_node: software VSI handle
146862306a36Sopenharmony_ci * @qgrp_node: first queue group node identified for scanning
146962306a36Sopenharmony_ci * @owner: LAN or RDMA
147062306a36Sopenharmony_ci *
147162306a36Sopenharmony_ci * This function retrieves a free LAN or RDMA queue group node by scanning
147262306a36Sopenharmony_ci * qgrp_node and its siblings for the queue group with the fewest number
147362306a36Sopenharmony_ci * of queues currently assigned.
147462306a36Sopenharmony_ci */
147562306a36Sopenharmony_cistatic struct ice_sched_node *
147662306a36Sopenharmony_ciice_sched_get_free_qgrp(struct ice_port_info *pi,
147762306a36Sopenharmony_ci			struct ice_sched_node *vsi_node,
147862306a36Sopenharmony_ci			struct ice_sched_node *qgrp_node, u8 owner)
147962306a36Sopenharmony_ci{
148062306a36Sopenharmony_ci	struct ice_sched_node *min_qgrp;
148162306a36Sopenharmony_ci	u8 min_children;
148262306a36Sopenharmony_ci
148362306a36Sopenharmony_ci	if (!qgrp_node)
148462306a36Sopenharmony_ci		return qgrp_node;
148562306a36Sopenharmony_ci	min_children = qgrp_node->num_children;
148662306a36Sopenharmony_ci	if (!min_children)
148762306a36Sopenharmony_ci		return qgrp_node;
148862306a36Sopenharmony_ci	min_qgrp = qgrp_node;
148962306a36Sopenharmony_ci	/* scan all queue groups until find a node which has less than the
149062306a36Sopenharmony_ci	 * minimum number of children. This way all queue group nodes get
149162306a36Sopenharmony_ci	 * equal number of shares and active. The bandwidth will be equally
149262306a36Sopenharmony_ci	 * distributed across all queues.
149362306a36Sopenharmony_ci	 */
149462306a36Sopenharmony_ci	while (qgrp_node) {
149562306a36Sopenharmony_ci		/* make sure the qgroup node is part of the VSI subtree */
149662306a36Sopenharmony_ci		if (ice_sched_find_node_in_subtree(pi->hw, vsi_node, qgrp_node))
149762306a36Sopenharmony_ci			if (qgrp_node->num_children < min_children &&
149862306a36Sopenharmony_ci			    qgrp_node->owner == owner) {
149962306a36Sopenharmony_ci				/* replace the new min queue group node */
150062306a36Sopenharmony_ci				min_qgrp = qgrp_node;
150162306a36Sopenharmony_ci				min_children = min_qgrp->num_children;
150262306a36Sopenharmony_ci				/* break if it has no children, */
150362306a36Sopenharmony_ci				if (!min_children)
150462306a36Sopenharmony_ci					break;
150562306a36Sopenharmony_ci			}
150662306a36Sopenharmony_ci		qgrp_node = qgrp_node->sibling;
150762306a36Sopenharmony_ci	}
150862306a36Sopenharmony_ci	return min_qgrp;
150962306a36Sopenharmony_ci}
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci/**
151262306a36Sopenharmony_ci * ice_sched_get_free_qparent - Get a free LAN or RDMA queue group node
151362306a36Sopenharmony_ci * @pi: port information structure
151462306a36Sopenharmony_ci * @vsi_handle: software VSI handle
151562306a36Sopenharmony_ci * @tc: branch number
151662306a36Sopenharmony_ci * @owner: LAN or RDMA
151762306a36Sopenharmony_ci *
151862306a36Sopenharmony_ci * This function retrieves a free LAN or RDMA queue group node
151962306a36Sopenharmony_ci */
152062306a36Sopenharmony_cistruct ice_sched_node *
152162306a36Sopenharmony_ciice_sched_get_free_qparent(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
152262306a36Sopenharmony_ci			   u8 owner)
152362306a36Sopenharmony_ci{
152462306a36Sopenharmony_ci	struct ice_sched_node *vsi_node, *qgrp_node;
152562306a36Sopenharmony_ci	struct ice_vsi_ctx *vsi_ctx;
152662306a36Sopenharmony_ci	u16 max_children;
152762306a36Sopenharmony_ci	u8 qgrp_layer;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	qgrp_layer = ice_sched_get_qgrp_layer(pi->hw);
153062306a36Sopenharmony_ci	max_children = pi->hw->max_children[qgrp_layer];
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
153362306a36Sopenharmony_ci	if (!vsi_ctx)
153462306a36Sopenharmony_ci		return NULL;
153562306a36Sopenharmony_ci	vsi_node = vsi_ctx->sched.vsi_node[tc];
153662306a36Sopenharmony_ci	/* validate invalid VSI ID */
153762306a36Sopenharmony_ci	if (!vsi_node)
153862306a36Sopenharmony_ci		return NULL;
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	/* get the first queue group node from VSI sub-tree */
154162306a36Sopenharmony_ci	qgrp_node = ice_sched_get_first_node(pi, vsi_node, qgrp_layer);
154262306a36Sopenharmony_ci	while (qgrp_node) {
154362306a36Sopenharmony_ci		/* make sure the qgroup node is part of the VSI subtree */
154462306a36Sopenharmony_ci		if (ice_sched_find_node_in_subtree(pi->hw, vsi_node, qgrp_node))
154562306a36Sopenharmony_ci			if (qgrp_node->num_children < max_children &&
154662306a36Sopenharmony_ci			    qgrp_node->owner == owner)
154762306a36Sopenharmony_ci				break;
154862306a36Sopenharmony_ci		qgrp_node = qgrp_node->sibling;
154962306a36Sopenharmony_ci	}
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci	/* Select the best queue group */
155262306a36Sopenharmony_ci	return ice_sched_get_free_qgrp(pi, vsi_node, qgrp_node, owner);
155362306a36Sopenharmony_ci}
155462306a36Sopenharmony_ci
155562306a36Sopenharmony_ci/**
155662306a36Sopenharmony_ci * ice_sched_get_vsi_node - Get a VSI node based on VSI ID
155762306a36Sopenharmony_ci * @pi: pointer to the port information structure
155862306a36Sopenharmony_ci * @tc_node: pointer to the TC node
155962306a36Sopenharmony_ci * @vsi_handle: software VSI handle
156062306a36Sopenharmony_ci *
156162306a36Sopenharmony_ci * This function retrieves a VSI node for a given VSI ID from a given
156262306a36Sopenharmony_ci * TC branch
156362306a36Sopenharmony_ci */
156462306a36Sopenharmony_cistatic struct ice_sched_node *
156562306a36Sopenharmony_ciice_sched_get_vsi_node(struct ice_port_info *pi, struct ice_sched_node *tc_node,
156662306a36Sopenharmony_ci		       u16 vsi_handle)
156762306a36Sopenharmony_ci{
156862306a36Sopenharmony_ci	struct ice_sched_node *node;
156962306a36Sopenharmony_ci	u8 vsi_layer;
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	vsi_layer = ice_sched_get_vsi_layer(pi->hw);
157262306a36Sopenharmony_ci	node = ice_sched_get_first_node(pi, tc_node, vsi_layer);
157362306a36Sopenharmony_ci
157462306a36Sopenharmony_ci	/* Check whether it already exists */
157562306a36Sopenharmony_ci	while (node) {
157662306a36Sopenharmony_ci		if (node->vsi_handle == vsi_handle)
157762306a36Sopenharmony_ci			return node;
157862306a36Sopenharmony_ci		node = node->sibling;
157962306a36Sopenharmony_ci	}
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	return node;
158262306a36Sopenharmony_ci}
158362306a36Sopenharmony_ci
158462306a36Sopenharmony_ci/**
158562306a36Sopenharmony_ci * ice_sched_get_agg_node - Get an aggregator node based on aggregator ID
158662306a36Sopenharmony_ci * @pi: pointer to the port information structure
158762306a36Sopenharmony_ci * @tc_node: pointer to the TC node
158862306a36Sopenharmony_ci * @agg_id: aggregator ID
158962306a36Sopenharmony_ci *
159062306a36Sopenharmony_ci * This function retrieves an aggregator node for a given aggregator ID from
159162306a36Sopenharmony_ci * a given TC branch
159262306a36Sopenharmony_ci */
159362306a36Sopenharmony_cistruct ice_sched_node *
159462306a36Sopenharmony_ciice_sched_get_agg_node(struct ice_port_info *pi, struct ice_sched_node *tc_node,
159562306a36Sopenharmony_ci		       u32 agg_id)
159662306a36Sopenharmony_ci{
159762306a36Sopenharmony_ci	struct ice_sched_node *node;
159862306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
159962306a36Sopenharmony_ci	u8 agg_layer;
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	if (!hw)
160262306a36Sopenharmony_ci		return NULL;
160362306a36Sopenharmony_ci	agg_layer = ice_sched_get_agg_layer(hw);
160462306a36Sopenharmony_ci	node = ice_sched_get_first_node(pi, tc_node, agg_layer);
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	/* Check whether it already exists */
160762306a36Sopenharmony_ci	while (node) {
160862306a36Sopenharmony_ci		if (node->agg_id == agg_id)
160962306a36Sopenharmony_ci			return node;
161062306a36Sopenharmony_ci		node = node->sibling;
161162306a36Sopenharmony_ci	}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	return node;
161462306a36Sopenharmony_ci}
161562306a36Sopenharmony_ci
161662306a36Sopenharmony_ci/**
161762306a36Sopenharmony_ci * ice_sched_calc_vsi_child_nodes - calculate number of VSI child nodes
161862306a36Sopenharmony_ci * @hw: pointer to the HW struct
161962306a36Sopenharmony_ci * @num_qs: number of queues
162062306a36Sopenharmony_ci * @num_nodes: num nodes array
162162306a36Sopenharmony_ci *
162262306a36Sopenharmony_ci * This function calculates the number of VSI child nodes based on the
162362306a36Sopenharmony_ci * number of queues.
162462306a36Sopenharmony_ci */
162562306a36Sopenharmony_cistatic void
162662306a36Sopenharmony_ciice_sched_calc_vsi_child_nodes(struct ice_hw *hw, u16 num_qs, u16 *num_nodes)
162762306a36Sopenharmony_ci{
162862306a36Sopenharmony_ci	u16 num = num_qs;
162962306a36Sopenharmony_ci	u8 i, qgl, vsil;
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	qgl = ice_sched_get_qgrp_layer(hw);
163262306a36Sopenharmony_ci	vsil = ice_sched_get_vsi_layer(hw);
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	/* calculate num nodes from queue group to VSI layer */
163562306a36Sopenharmony_ci	for (i = qgl; i > vsil; i--) {
163662306a36Sopenharmony_ci		/* round to the next integer if there is a remainder */
163762306a36Sopenharmony_ci		num = DIV_ROUND_UP(num, hw->max_children[i]);
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci		/* need at least one node */
164062306a36Sopenharmony_ci		num_nodes[i] = num ? num : 1;
164162306a36Sopenharmony_ci	}
164262306a36Sopenharmony_ci}
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci/**
164562306a36Sopenharmony_ci * ice_sched_add_vsi_child_nodes - add VSI child nodes to tree
164662306a36Sopenharmony_ci * @pi: port information structure
164762306a36Sopenharmony_ci * @vsi_handle: software VSI handle
164862306a36Sopenharmony_ci * @tc_node: pointer to the TC node
164962306a36Sopenharmony_ci * @num_nodes: pointer to the num nodes that needs to be added per layer
165062306a36Sopenharmony_ci * @owner: node owner (LAN or RDMA)
165162306a36Sopenharmony_ci *
165262306a36Sopenharmony_ci * This function adds the VSI child nodes to tree. It gets called for
165362306a36Sopenharmony_ci * LAN and RDMA separately.
165462306a36Sopenharmony_ci */
165562306a36Sopenharmony_cistatic int
165662306a36Sopenharmony_ciice_sched_add_vsi_child_nodes(struct ice_port_info *pi, u16 vsi_handle,
165762306a36Sopenharmony_ci			      struct ice_sched_node *tc_node, u16 *num_nodes,
165862306a36Sopenharmony_ci			      u8 owner)
165962306a36Sopenharmony_ci{
166062306a36Sopenharmony_ci	struct ice_sched_node *parent, *node;
166162306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
166262306a36Sopenharmony_ci	u32 first_node_teid;
166362306a36Sopenharmony_ci	u16 num_added = 0;
166462306a36Sopenharmony_ci	u8 i, qgl, vsil;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	qgl = ice_sched_get_qgrp_layer(hw);
166762306a36Sopenharmony_ci	vsil = ice_sched_get_vsi_layer(hw);
166862306a36Sopenharmony_ci	parent = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
166962306a36Sopenharmony_ci	for (i = vsil + 1; i <= qgl; i++) {
167062306a36Sopenharmony_ci		int status;
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci		if (!parent)
167362306a36Sopenharmony_ci			return -EIO;
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci		status = ice_sched_add_nodes_to_layer(pi, tc_node, parent, i,
167662306a36Sopenharmony_ci						      num_nodes[i],
167762306a36Sopenharmony_ci						      &first_node_teid,
167862306a36Sopenharmony_ci						      &num_added);
167962306a36Sopenharmony_ci		if (status || num_nodes[i] != num_added)
168062306a36Sopenharmony_ci			return -EIO;
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci		/* The newly added node can be a new parent for the next
168362306a36Sopenharmony_ci		 * layer nodes
168462306a36Sopenharmony_ci		 */
168562306a36Sopenharmony_ci		if (num_added) {
168662306a36Sopenharmony_ci			parent = ice_sched_find_node_by_teid(tc_node,
168762306a36Sopenharmony_ci							     first_node_teid);
168862306a36Sopenharmony_ci			node = parent;
168962306a36Sopenharmony_ci			while (node) {
169062306a36Sopenharmony_ci				node->owner = owner;
169162306a36Sopenharmony_ci				node = node->sibling;
169262306a36Sopenharmony_ci			}
169362306a36Sopenharmony_ci		} else {
169462306a36Sopenharmony_ci			parent = parent->children[0];
169562306a36Sopenharmony_ci		}
169662306a36Sopenharmony_ci	}
169762306a36Sopenharmony_ci
169862306a36Sopenharmony_ci	return 0;
169962306a36Sopenharmony_ci}
170062306a36Sopenharmony_ci
170162306a36Sopenharmony_ci/**
170262306a36Sopenharmony_ci * ice_sched_calc_vsi_support_nodes - calculate number of VSI support nodes
170362306a36Sopenharmony_ci * @pi: pointer to the port info structure
170462306a36Sopenharmony_ci * @tc_node: pointer to TC node
170562306a36Sopenharmony_ci * @num_nodes: pointer to num nodes array
170662306a36Sopenharmony_ci *
170762306a36Sopenharmony_ci * This function calculates the number of supported nodes needed to add this
170862306a36Sopenharmony_ci * VSI into Tx tree including the VSI, parent and intermediate nodes in below
170962306a36Sopenharmony_ci * layers
171062306a36Sopenharmony_ci */
171162306a36Sopenharmony_cistatic void
171262306a36Sopenharmony_ciice_sched_calc_vsi_support_nodes(struct ice_port_info *pi,
171362306a36Sopenharmony_ci				 struct ice_sched_node *tc_node, u16 *num_nodes)
171462306a36Sopenharmony_ci{
171562306a36Sopenharmony_ci	struct ice_sched_node *node;
171662306a36Sopenharmony_ci	u8 vsil;
171762306a36Sopenharmony_ci	int i;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	vsil = ice_sched_get_vsi_layer(pi->hw);
172062306a36Sopenharmony_ci	for (i = vsil; i >= pi->hw->sw_entry_point_layer; i--)
172162306a36Sopenharmony_ci		/* Add intermediate nodes if TC has no children and
172262306a36Sopenharmony_ci		 * need at least one node for VSI
172362306a36Sopenharmony_ci		 */
172462306a36Sopenharmony_ci		if (!tc_node->num_children || i == vsil) {
172562306a36Sopenharmony_ci			num_nodes[i]++;
172662306a36Sopenharmony_ci		} else {
172762306a36Sopenharmony_ci			/* If intermediate nodes are reached max children
172862306a36Sopenharmony_ci			 * then add a new one.
172962306a36Sopenharmony_ci			 */
173062306a36Sopenharmony_ci			node = ice_sched_get_first_node(pi, tc_node, (u8)i);
173162306a36Sopenharmony_ci			/* scan all the siblings */
173262306a36Sopenharmony_ci			while (node) {
173362306a36Sopenharmony_ci				if (node->num_children < pi->hw->max_children[i])
173462306a36Sopenharmony_ci					break;
173562306a36Sopenharmony_ci				node = node->sibling;
173662306a36Sopenharmony_ci			}
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci			/* tree has one intermediate node to add this new VSI.
173962306a36Sopenharmony_ci			 * So no need to calculate supported nodes for below
174062306a36Sopenharmony_ci			 * layers.
174162306a36Sopenharmony_ci			 */
174262306a36Sopenharmony_ci			if (node)
174362306a36Sopenharmony_ci				break;
174462306a36Sopenharmony_ci			/* all the nodes are full, allocate a new one */
174562306a36Sopenharmony_ci			num_nodes[i]++;
174662306a36Sopenharmony_ci		}
174762306a36Sopenharmony_ci}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ci/**
175062306a36Sopenharmony_ci * ice_sched_add_vsi_support_nodes - add VSI supported nodes into Tx tree
175162306a36Sopenharmony_ci * @pi: port information structure
175262306a36Sopenharmony_ci * @vsi_handle: software VSI handle
175362306a36Sopenharmony_ci * @tc_node: pointer to TC node
175462306a36Sopenharmony_ci * @num_nodes: pointer to num nodes array
175562306a36Sopenharmony_ci *
175662306a36Sopenharmony_ci * This function adds the VSI supported nodes into Tx tree including the
175762306a36Sopenharmony_ci * VSI, its parent and intermediate nodes in below layers
175862306a36Sopenharmony_ci */
175962306a36Sopenharmony_cistatic int
176062306a36Sopenharmony_ciice_sched_add_vsi_support_nodes(struct ice_port_info *pi, u16 vsi_handle,
176162306a36Sopenharmony_ci				struct ice_sched_node *tc_node, u16 *num_nodes)
176262306a36Sopenharmony_ci{
176362306a36Sopenharmony_ci	struct ice_sched_node *parent = tc_node;
176462306a36Sopenharmony_ci	u32 first_node_teid;
176562306a36Sopenharmony_ci	u16 num_added = 0;
176662306a36Sopenharmony_ci	u8 i, vsil;
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_ci	if (!pi)
176962306a36Sopenharmony_ci		return -EINVAL;
177062306a36Sopenharmony_ci
177162306a36Sopenharmony_ci	vsil = ice_sched_get_vsi_layer(pi->hw);
177262306a36Sopenharmony_ci	for (i = pi->hw->sw_entry_point_layer; i <= vsil; i++) {
177362306a36Sopenharmony_ci		int status;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci		status = ice_sched_add_nodes_to_layer(pi, tc_node, parent,
177662306a36Sopenharmony_ci						      i, num_nodes[i],
177762306a36Sopenharmony_ci						      &first_node_teid,
177862306a36Sopenharmony_ci						      &num_added);
177962306a36Sopenharmony_ci		if (status || num_nodes[i] != num_added)
178062306a36Sopenharmony_ci			return -EIO;
178162306a36Sopenharmony_ci
178262306a36Sopenharmony_ci		/* The newly added node can be a new parent for the next
178362306a36Sopenharmony_ci		 * layer nodes
178462306a36Sopenharmony_ci		 */
178562306a36Sopenharmony_ci		if (num_added)
178662306a36Sopenharmony_ci			parent = ice_sched_find_node_by_teid(tc_node,
178762306a36Sopenharmony_ci							     first_node_teid);
178862306a36Sopenharmony_ci		else
178962306a36Sopenharmony_ci			parent = parent->children[0];
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci		if (!parent)
179262306a36Sopenharmony_ci			return -EIO;
179362306a36Sopenharmony_ci
179462306a36Sopenharmony_ci		if (i == vsil)
179562306a36Sopenharmony_ci			parent->vsi_handle = vsi_handle;
179662306a36Sopenharmony_ci	}
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	return 0;
179962306a36Sopenharmony_ci}
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci/**
180262306a36Sopenharmony_ci * ice_sched_add_vsi_to_topo - add a new VSI into tree
180362306a36Sopenharmony_ci * @pi: port information structure
180462306a36Sopenharmony_ci * @vsi_handle: software VSI handle
180562306a36Sopenharmony_ci * @tc: TC number
180662306a36Sopenharmony_ci *
180762306a36Sopenharmony_ci * This function adds a new VSI into scheduler tree
180862306a36Sopenharmony_ci */
180962306a36Sopenharmony_cistatic int
181062306a36Sopenharmony_ciice_sched_add_vsi_to_topo(struct ice_port_info *pi, u16 vsi_handle, u8 tc)
181162306a36Sopenharmony_ci{
181262306a36Sopenharmony_ci	u16 num_nodes[ICE_AQC_TOPO_MAX_LEVEL_NUM] = { 0 };
181362306a36Sopenharmony_ci	struct ice_sched_node *tc_node;
181462306a36Sopenharmony_ci
181562306a36Sopenharmony_ci	tc_node = ice_sched_get_tc_node(pi, tc);
181662306a36Sopenharmony_ci	if (!tc_node)
181762306a36Sopenharmony_ci		return -EINVAL;
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	/* calculate number of supported nodes needed for this VSI */
182062306a36Sopenharmony_ci	ice_sched_calc_vsi_support_nodes(pi, tc_node, num_nodes);
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	/* add VSI supported nodes to TC subtree */
182362306a36Sopenharmony_ci	return ice_sched_add_vsi_support_nodes(pi, vsi_handle, tc_node,
182462306a36Sopenharmony_ci					       num_nodes);
182562306a36Sopenharmony_ci}
182662306a36Sopenharmony_ci
182762306a36Sopenharmony_ci/**
182862306a36Sopenharmony_ci * ice_sched_update_vsi_child_nodes - update VSI child nodes
182962306a36Sopenharmony_ci * @pi: port information structure
183062306a36Sopenharmony_ci * @vsi_handle: software VSI handle
183162306a36Sopenharmony_ci * @tc: TC number
183262306a36Sopenharmony_ci * @new_numqs: new number of max queues
183362306a36Sopenharmony_ci * @owner: owner of this subtree
183462306a36Sopenharmony_ci *
183562306a36Sopenharmony_ci * This function updates the VSI child nodes based on the number of queues
183662306a36Sopenharmony_ci */
183762306a36Sopenharmony_cistatic int
183862306a36Sopenharmony_ciice_sched_update_vsi_child_nodes(struct ice_port_info *pi, u16 vsi_handle,
183962306a36Sopenharmony_ci				 u8 tc, u16 new_numqs, u8 owner)
184062306a36Sopenharmony_ci{
184162306a36Sopenharmony_ci	u16 new_num_nodes[ICE_AQC_TOPO_MAX_LEVEL_NUM] = { 0 };
184262306a36Sopenharmony_ci	struct ice_sched_node *vsi_node;
184362306a36Sopenharmony_ci	struct ice_sched_node *tc_node;
184462306a36Sopenharmony_ci	struct ice_vsi_ctx *vsi_ctx;
184562306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
184662306a36Sopenharmony_ci	u16 prev_numqs;
184762306a36Sopenharmony_ci	int status = 0;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	tc_node = ice_sched_get_tc_node(pi, tc);
185062306a36Sopenharmony_ci	if (!tc_node)
185162306a36Sopenharmony_ci		return -EIO;
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_ci	vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
185462306a36Sopenharmony_ci	if (!vsi_node)
185562306a36Sopenharmony_ci		return -EIO;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	vsi_ctx = ice_get_vsi_ctx(hw, vsi_handle);
185862306a36Sopenharmony_ci	if (!vsi_ctx)
185962306a36Sopenharmony_ci		return -EINVAL;
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	if (owner == ICE_SCHED_NODE_OWNER_LAN)
186262306a36Sopenharmony_ci		prev_numqs = vsi_ctx->sched.max_lanq[tc];
186362306a36Sopenharmony_ci	else
186462306a36Sopenharmony_ci		prev_numqs = vsi_ctx->sched.max_rdmaq[tc];
186562306a36Sopenharmony_ci	/* num queues are not changed or less than the previous number */
186662306a36Sopenharmony_ci	if (new_numqs <= prev_numqs)
186762306a36Sopenharmony_ci		return status;
186862306a36Sopenharmony_ci	if (owner == ICE_SCHED_NODE_OWNER_LAN) {
186962306a36Sopenharmony_ci		status = ice_alloc_lan_q_ctx(hw, vsi_handle, tc, new_numqs);
187062306a36Sopenharmony_ci		if (status)
187162306a36Sopenharmony_ci			return status;
187262306a36Sopenharmony_ci	} else {
187362306a36Sopenharmony_ci		status = ice_alloc_rdma_q_ctx(hw, vsi_handle, tc, new_numqs);
187462306a36Sopenharmony_ci		if (status)
187562306a36Sopenharmony_ci			return status;
187662306a36Sopenharmony_ci	}
187762306a36Sopenharmony_ci
187862306a36Sopenharmony_ci	if (new_numqs)
187962306a36Sopenharmony_ci		ice_sched_calc_vsi_child_nodes(hw, new_numqs, new_num_nodes);
188062306a36Sopenharmony_ci	/* Keep the max number of queue configuration all the time. Update the
188162306a36Sopenharmony_ci	 * tree only if number of queues > previous number of queues. This may
188262306a36Sopenharmony_ci	 * leave some extra nodes in the tree if number of queues < previous
188362306a36Sopenharmony_ci	 * number but that wouldn't harm anything. Removing those extra nodes
188462306a36Sopenharmony_ci	 * may complicate the code if those nodes are part of SRL or
188562306a36Sopenharmony_ci	 * individually rate limited.
188662306a36Sopenharmony_ci	 */
188762306a36Sopenharmony_ci	status = ice_sched_add_vsi_child_nodes(pi, vsi_handle, tc_node,
188862306a36Sopenharmony_ci					       new_num_nodes, owner);
188962306a36Sopenharmony_ci	if (status)
189062306a36Sopenharmony_ci		return status;
189162306a36Sopenharmony_ci	if (owner == ICE_SCHED_NODE_OWNER_LAN)
189262306a36Sopenharmony_ci		vsi_ctx->sched.max_lanq[tc] = new_numqs;
189362306a36Sopenharmony_ci	else
189462306a36Sopenharmony_ci		vsi_ctx->sched.max_rdmaq[tc] = new_numqs;
189562306a36Sopenharmony_ci
189662306a36Sopenharmony_ci	return 0;
189762306a36Sopenharmony_ci}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_ci/**
190062306a36Sopenharmony_ci * ice_sched_cfg_vsi - configure the new/existing VSI
190162306a36Sopenharmony_ci * @pi: port information structure
190262306a36Sopenharmony_ci * @vsi_handle: software VSI handle
190362306a36Sopenharmony_ci * @tc: TC number
190462306a36Sopenharmony_ci * @maxqs: max number of queues
190562306a36Sopenharmony_ci * @owner: LAN or RDMA
190662306a36Sopenharmony_ci * @enable: TC enabled or disabled
190762306a36Sopenharmony_ci *
190862306a36Sopenharmony_ci * This function adds/updates VSI nodes based on the number of queues. If TC is
190962306a36Sopenharmony_ci * enabled and VSI is in suspended state then resume the VSI back. If TC is
191062306a36Sopenharmony_ci * disabled then suspend the VSI if it is not already.
191162306a36Sopenharmony_ci */
191262306a36Sopenharmony_ciint
191362306a36Sopenharmony_ciice_sched_cfg_vsi(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u16 maxqs,
191462306a36Sopenharmony_ci		  u8 owner, bool enable)
191562306a36Sopenharmony_ci{
191662306a36Sopenharmony_ci	struct ice_sched_node *vsi_node, *tc_node;
191762306a36Sopenharmony_ci	struct ice_vsi_ctx *vsi_ctx;
191862306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
191962306a36Sopenharmony_ci	int status = 0;
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	ice_debug(pi->hw, ICE_DBG_SCHED, "add/config VSI %d\n", vsi_handle);
192262306a36Sopenharmony_ci	tc_node = ice_sched_get_tc_node(pi, tc);
192362306a36Sopenharmony_ci	if (!tc_node)
192462306a36Sopenharmony_ci		return -EINVAL;
192562306a36Sopenharmony_ci	vsi_ctx = ice_get_vsi_ctx(hw, vsi_handle);
192662306a36Sopenharmony_ci	if (!vsi_ctx)
192762306a36Sopenharmony_ci		return -EINVAL;
192862306a36Sopenharmony_ci	vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	/* suspend the VSI if TC is not enabled */
193162306a36Sopenharmony_ci	if (!enable) {
193262306a36Sopenharmony_ci		if (vsi_node && vsi_node->in_use) {
193362306a36Sopenharmony_ci			u32 teid = le32_to_cpu(vsi_node->info.node_teid);
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci			status = ice_sched_suspend_resume_elems(hw, 1, &teid,
193662306a36Sopenharmony_ci								true);
193762306a36Sopenharmony_ci			if (!status)
193862306a36Sopenharmony_ci				vsi_node->in_use = false;
193962306a36Sopenharmony_ci		}
194062306a36Sopenharmony_ci		return status;
194162306a36Sopenharmony_ci	}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	/* TC is enabled, if it is a new VSI then add it to the tree */
194462306a36Sopenharmony_ci	if (!vsi_node) {
194562306a36Sopenharmony_ci		status = ice_sched_add_vsi_to_topo(pi, vsi_handle, tc);
194662306a36Sopenharmony_ci		if (status)
194762306a36Sopenharmony_ci			return status;
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci		vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
195062306a36Sopenharmony_ci		if (!vsi_node)
195162306a36Sopenharmony_ci			return -EIO;
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci		vsi_ctx->sched.vsi_node[tc] = vsi_node;
195462306a36Sopenharmony_ci		vsi_node->in_use = true;
195562306a36Sopenharmony_ci		/* invalidate the max queues whenever VSI gets added first time
195662306a36Sopenharmony_ci		 * into the scheduler tree (boot or after reset). We need to
195762306a36Sopenharmony_ci		 * recreate the child nodes all the time in these cases.
195862306a36Sopenharmony_ci		 */
195962306a36Sopenharmony_ci		vsi_ctx->sched.max_lanq[tc] = 0;
196062306a36Sopenharmony_ci		vsi_ctx->sched.max_rdmaq[tc] = 0;
196162306a36Sopenharmony_ci	}
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	/* update the VSI child nodes */
196462306a36Sopenharmony_ci	status = ice_sched_update_vsi_child_nodes(pi, vsi_handle, tc, maxqs,
196562306a36Sopenharmony_ci						  owner);
196662306a36Sopenharmony_ci	if (status)
196762306a36Sopenharmony_ci		return status;
196862306a36Sopenharmony_ci
196962306a36Sopenharmony_ci	/* TC is enabled, resume the VSI if it is in the suspend state */
197062306a36Sopenharmony_ci	if (!vsi_node->in_use) {
197162306a36Sopenharmony_ci		u32 teid = le32_to_cpu(vsi_node->info.node_teid);
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci		status = ice_sched_suspend_resume_elems(hw, 1, &teid, false);
197462306a36Sopenharmony_ci		if (!status)
197562306a36Sopenharmony_ci			vsi_node->in_use = true;
197662306a36Sopenharmony_ci	}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_ci	return status;
197962306a36Sopenharmony_ci}
198062306a36Sopenharmony_ci
198162306a36Sopenharmony_ci/**
198262306a36Sopenharmony_ci * ice_sched_rm_agg_vsi_info - remove aggregator related VSI info entry
198362306a36Sopenharmony_ci * @pi: port information structure
198462306a36Sopenharmony_ci * @vsi_handle: software VSI handle
198562306a36Sopenharmony_ci *
198662306a36Sopenharmony_ci * This function removes single aggregator VSI info entry from
198762306a36Sopenharmony_ci * aggregator list.
198862306a36Sopenharmony_ci */
198962306a36Sopenharmony_cistatic void ice_sched_rm_agg_vsi_info(struct ice_port_info *pi, u16 vsi_handle)
199062306a36Sopenharmony_ci{
199162306a36Sopenharmony_ci	struct ice_sched_agg_info *agg_info;
199262306a36Sopenharmony_ci	struct ice_sched_agg_info *atmp;
199362306a36Sopenharmony_ci
199462306a36Sopenharmony_ci	list_for_each_entry_safe(agg_info, atmp, &pi->hw->agg_list,
199562306a36Sopenharmony_ci				 list_entry) {
199662306a36Sopenharmony_ci		struct ice_sched_agg_vsi_info *agg_vsi_info;
199762306a36Sopenharmony_ci		struct ice_sched_agg_vsi_info *vtmp;
199862306a36Sopenharmony_ci
199962306a36Sopenharmony_ci		list_for_each_entry_safe(agg_vsi_info, vtmp,
200062306a36Sopenharmony_ci					 &agg_info->agg_vsi_list, list_entry)
200162306a36Sopenharmony_ci			if (agg_vsi_info->vsi_handle == vsi_handle) {
200262306a36Sopenharmony_ci				list_del(&agg_vsi_info->list_entry);
200362306a36Sopenharmony_ci				devm_kfree(ice_hw_to_dev(pi->hw),
200462306a36Sopenharmony_ci					   agg_vsi_info);
200562306a36Sopenharmony_ci				return;
200662306a36Sopenharmony_ci			}
200762306a36Sopenharmony_ci	}
200862306a36Sopenharmony_ci}
200962306a36Sopenharmony_ci
201062306a36Sopenharmony_ci/**
201162306a36Sopenharmony_ci * ice_sched_is_leaf_node_present - check for a leaf node in the sub-tree
201262306a36Sopenharmony_ci * @node: pointer to the sub-tree node
201362306a36Sopenharmony_ci *
201462306a36Sopenharmony_ci * This function checks for a leaf node presence in a given sub-tree node.
201562306a36Sopenharmony_ci */
201662306a36Sopenharmony_cistatic bool ice_sched_is_leaf_node_present(struct ice_sched_node *node)
201762306a36Sopenharmony_ci{
201862306a36Sopenharmony_ci	u8 i;
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	for (i = 0; i < node->num_children; i++)
202162306a36Sopenharmony_ci		if (ice_sched_is_leaf_node_present(node->children[i]))
202262306a36Sopenharmony_ci			return true;
202362306a36Sopenharmony_ci	/* check for a leaf node */
202462306a36Sopenharmony_ci	return (node->info.data.elem_type == ICE_AQC_ELEM_TYPE_LEAF);
202562306a36Sopenharmony_ci}
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci/**
202862306a36Sopenharmony_ci * ice_sched_rm_vsi_cfg - remove the VSI and its children nodes
202962306a36Sopenharmony_ci * @pi: port information structure
203062306a36Sopenharmony_ci * @vsi_handle: software VSI handle
203162306a36Sopenharmony_ci * @owner: LAN or RDMA
203262306a36Sopenharmony_ci *
203362306a36Sopenharmony_ci * This function removes the VSI and its LAN or RDMA children nodes from the
203462306a36Sopenharmony_ci * scheduler tree.
203562306a36Sopenharmony_ci */
203662306a36Sopenharmony_cistatic int
203762306a36Sopenharmony_ciice_sched_rm_vsi_cfg(struct ice_port_info *pi, u16 vsi_handle, u8 owner)
203862306a36Sopenharmony_ci{
203962306a36Sopenharmony_ci	struct ice_vsi_ctx *vsi_ctx;
204062306a36Sopenharmony_ci	int status = -EINVAL;
204162306a36Sopenharmony_ci	u8 i;
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_ci	ice_debug(pi->hw, ICE_DBG_SCHED, "removing VSI %d\n", vsi_handle);
204462306a36Sopenharmony_ci	if (!ice_is_vsi_valid(pi->hw, vsi_handle))
204562306a36Sopenharmony_ci		return status;
204662306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
204762306a36Sopenharmony_ci	vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
204862306a36Sopenharmony_ci	if (!vsi_ctx)
204962306a36Sopenharmony_ci		goto exit_sched_rm_vsi_cfg;
205062306a36Sopenharmony_ci
205162306a36Sopenharmony_ci	ice_for_each_traffic_class(i) {
205262306a36Sopenharmony_ci		struct ice_sched_node *vsi_node, *tc_node;
205362306a36Sopenharmony_ci		u8 j = 0;
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_ci		tc_node = ice_sched_get_tc_node(pi, i);
205662306a36Sopenharmony_ci		if (!tc_node)
205762306a36Sopenharmony_ci			continue;
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci		vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
206062306a36Sopenharmony_ci		if (!vsi_node)
206162306a36Sopenharmony_ci			continue;
206262306a36Sopenharmony_ci
206362306a36Sopenharmony_ci		if (ice_sched_is_leaf_node_present(vsi_node)) {
206462306a36Sopenharmony_ci			ice_debug(pi->hw, ICE_DBG_SCHED, "VSI has leaf nodes in TC %d\n", i);
206562306a36Sopenharmony_ci			status = -EBUSY;
206662306a36Sopenharmony_ci			goto exit_sched_rm_vsi_cfg;
206762306a36Sopenharmony_ci		}
206862306a36Sopenharmony_ci		while (j < vsi_node->num_children) {
206962306a36Sopenharmony_ci			if (vsi_node->children[j]->owner == owner) {
207062306a36Sopenharmony_ci				ice_free_sched_node(pi, vsi_node->children[j]);
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci				/* reset the counter again since the num
207362306a36Sopenharmony_ci				 * children will be updated after node removal
207462306a36Sopenharmony_ci				 */
207562306a36Sopenharmony_ci				j = 0;
207662306a36Sopenharmony_ci			} else {
207762306a36Sopenharmony_ci				j++;
207862306a36Sopenharmony_ci			}
207962306a36Sopenharmony_ci		}
208062306a36Sopenharmony_ci		/* remove the VSI if it has no children */
208162306a36Sopenharmony_ci		if (!vsi_node->num_children) {
208262306a36Sopenharmony_ci			ice_free_sched_node(pi, vsi_node);
208362306a36Sopenharmony_ci			vsi_ctx->sched.vsi_node[i] = NULL;
208462306a36Sopenharmony_ci
208562306a36Sopenharmony_ci			/* clean up aggregator related VSI info if any */
208662306a36Sopenharmony_ci			ice_sched_rm_agg_vsi_info(pi, vsi_handle);
208762306a36Sopenharmony_ci		}
208862306a36Sopenharmony_ci		if (owner == ICE_SCHED_NODE_OWNER_LAN)
208962306a36Sopenharmony_ci			vsi_ctx->sched.max_lanq[i] = 0;
209062306a36Sopenharmony_ci		else
209162306a36Sopenharmony_ci			vsi_ctx->sched.max_rdmaq[i] = 0;
209262306a36Sopenharmony_ci	}
209362306a36Sopenharmony_ci	status = 0;
209462306a36Sopenharmony_ci
209562306a36Sopenharmony_ciexit_sched_rm_vsi_cfg:
209662306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
209762306a36Sopenharmony_ci	return status;
209862306a36Sopenharmony_ci}
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci/**
210162306a36Sopenharmony_ci * ice_rm_vsi_lan_cfg - remove VSI and its LAN children nodes
210262306a36Sopenharmony_ci * @pi: port information structure
210362306a36Sopenharmony_ci * @vsi_handle: software VSI handle
210462306a36Sopenharmony_ci *
210562306a36Sopenharmony_ci * This function clears the VSI and its LAN children nodes from scheduler tree
210662306a36Sopenharmony_ci * for all TCs.
210762306a36Sopenharmony_ci */
210862306a36Sopenharmony_ciint ice_rm_vsi_lan_cfg(struct ice_port_info *pi, u16 vsi_handle)
210962306a36Sopenharmony_ci{
211062306a36Sopenharmony_ci	return ice_sched_rm_vsi_cfg(pi, vsi_handle, ICE_SCHED_NODE_OWNER_LAN);
211162306a36Sopenharmony_ci}
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci/**
211462306a36Sopenharmony_ci * ice_rm_vsi_rdma_cfg - remove VSI and its RDMA children nodes
211562306a36Sopenharmony_ci * @pi: port information structure
211662306a36Sopenharmony_ci * @vsi_handle: software VSI handle
211762306a36Sopenharmony_ci *
211862306a36Sopenharmony_ci * This function clears the VSI and its RDMA children nodes from scheduler tree
211962306a36Sopenharmony_ci * for all TCs.
212062306a36Sopenharmony_ci */
212162306a36Sopenharmony_ciint ice_rm_vsi_rdma_cfg(struct ice_port_info *pi, u16 vsi_handle)
212262306a36Sopenharmony_ci{
212362306a36Sopenharmony_ci	return ice_sched_rm_vsi_cfg(pi, vsi_handle, ICE_SCHED_NODE_OWNER_RDMA);
212462306a36Sopenharmony_ci}
212562306a36Sopenharmony_ci
212662306a36Sopenharmony_ci/**
212762306a36Sopenharmony_ci * ice_get_agg_info - get the aggregator ID
212862306a36Sopenharmony_ci * @hw: pointer to the hardware structure
212962306a36Sopenharmony_ci * @agg_id: aggregator ID
213062306a36Sopenharmony_ci *
213162306a36Sopenharmony_ci * This function validates aggregator ID. The function returns info if
213262306a36Sopenharmony_ci * aggregator ID is present in list otherwise it returns null.
213362306a36Sopenharmony_ci */
213462306a36Sopenharmony_cistatic struct ice_sched_agg_info *
213562306a36Sopenharmony_ciice_get_agg_info(struct ice_hw *hw, u32 agg_id)
213662306a36Sopenharmony_ci{
213762306a36Sopenharmony_ci	struct ice_sched_agg_info *agg_info;
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	list_for_each_entry(agg_info, &hw->agg_list, list_entry)
214062306a36Sopenharmony_ci		if (agg_info->agg_id == agg_id)
214162306a36Sopenharmony_ci			return agg_info;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	return NULL;
214462306a36Sopenharmony_ci}
214562306a36Sopenharmony_ci
214662306a36Sopenharmony_ci/**
214762306a36Sopenharmony_ci * ice_sched_get_free_vsi_parent - Find a free parent node in aggregator subtree
214862306a36Sopenharmony_ci * @hw: pointer to the HW struct
214962306a36Sopenharmony_ci * @node: pointer to a child node
215062306a36Sopenharmony_ci * @num_nodes: num nodes count array
215162306a36Sopenharmony_ci *
215262306a36Sopenharmony_ci * This function walks through the aggregator subtree to find a free parent
215362306a36Sopenharmony_ci * node
215462306a36Sopenharmony_ci */
215562306a36Sopenharmony_cistruct ice_sched_node *
215662306a36Sopenharmony_ciice_sched_get_free_vsi_parent(struct ice_hw *hw, struct ice_sched_node *node,
215762306a36Sopenharmony_ci			      u16 *num_nodes)
215862306a36Sopenharmony_ci{
215962306a36Sopenharmony_ci	u8 l = node->tx_sched_layer;
216062306a36Sopenharmony_ci	u8 vsil, i;
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	vsil = ice_sched_get_vsi_layer(hw);
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	/* Is it VSI parent layer ? */
216562306a36Sopenharmony_ci	if (l == vsil - 1)
216662306a36Sopenharmony_ci		return (node->num_children < hw->max_children[l]) ? node : NULL;
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	/* We have intermediate nodes. Let's walk through the subtree. If the
216962306a36Sopenharmony_ci	 * intermediate node has space to add a new node then clear the count
217062306a36Sopenharmony_ci	 */
217162306a36Sopenharmony_ci	if (node->num_children < hw->max_children[l])
217262306a36Sopenharmony_ci		num_nodes[l] = 0;
217362306a36Sopenharmony_ci	/* The below recursive call is intentional and wouldn't go more than
217462306a36Sopenharmony_ci	 * 2 or 3 iterations.
217562306a36Sopenharmony_ci	 */
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	for (i = 0; i < node->num_children; i++) {
217862306a36Sopenharmony_ci		struct ice_sched_node *parent;
217962306a36Sopenharmony_ci
218062306a36Sopenharmony_ci		parent = ice_sched_get_free_vsi_parent(hw, node->children[i],
218162306a36Sopenharmony_ci						       num_nodes);
218262306a36Sopenharmony_ci		if (parent)
218362306a36Sopenharmony_ci			return parent;
218462306a36Sopenharmony_ci	}
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	return NULL;
218762306a36Sopenharmony_ci}
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci/**
219062306a36Sopenharmony_ci * ice_sched_update_parent - update the new parent in SW DB
219162306a36Sopenharmony_ci * @new_parent: pointer to a new parent node
219262306a36Sopenharmony_ci * @node: pointer to a child node
219362306a36Sopenharmony_ci *
219462306a36Sopenharmony_ci * This function removes the child from the old parent and adds it to a new
219562306a36Sopenharmony_ci * parent
219662306a36Sopenharmony_ci */
219762306a36Sopenharmony_civoid
219862306a36Sopenharmony_ciice_sched_update_parent(struct ice_sched_node *new_parent,
219962306a36Sopenharmony_ci			struct ice_sched_node *node)
220062306a36Sopenharmony_ci{
220162306a36Sopenharmony_ci	struct ice_sched_node *old_parent;
220262306a36Sopenharmony_ci	u8 i, j;
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	old_parent = node->parent;
220562306a36Sopenharmony_ci
220662306a36Sopenharmony_ci	/* update the old parent children */
220762306a36Sopenharmony_ci	for (i = 0; i < old_parent->num_children; i++)
220862306a36Sopenharmony_ci		if (old_parent->children[i] == node) {
220962306a36Sopenharmony_ci			for (j = i + 1; j < old_parent->num_children; j++)
221062306a36Sopenharmony_ci				old_parent->children[j - 1] =
221162306a36Sopenharmony_ci					old_parent->children[j];
221262306a36Sopenharmony_ci			old_parent->num_children--;
221362306a36Sopenharmony_ci			break;
221462306a36Sopenharmony_ci		}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	/* now move the node to a new parent */
221762306a36Sopenharmony_ci	new_parent->children[new_parent->num_children++] = node;
221862306a36Sopenharmony_ci	node->parent = new_parent;
221962306a36Sopenharmony_ci	node->info.parent_teid = new_parent->info.node_teid;
222062306a36Sopenharmony_ci}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_ci/**
222362306a36Sopenharmony_ci * ice_sched_move_nodes - move child nodes to a given parent
222462306a36Sopenharmony_ci * @pi: port information structure
222562306a36Sopenharmony_ci * @parent: pointer to parent node
222662306a36Sopenharmony_ci * @num_items: number of child nodes to be moved
222762306a36Sopenharmony_ci * @list: pointer to child node teids
222862306a36Sopenharmony_ci *
222962306a36Sopenharmony_ci * This function move the child nodes to a given parent.
223062306a36Sopenharmony_ci */
223162306a36Sopenharmony_ciint
223262306a36Sopenharmony_ciice_sched_move_nodes(struct ice_port_info *pi, struct ice_sched_node *parent,
223362306a36Sopenharmony_ci		     u16 num_items, u32 *list)
223462306a36Sopenharmony_ci{
223562306a36Sopenharmony_ci	struct ice_aqc_move_elem *buf;
223662306a36Sopenharmony_ci	struct ice_sched_node *node;
223762306a36Sopenharmony_ci	u16 i, grps_movd = 0;
223862306a36Sopenharmony_ci	struct ice_hw *hw;
223962306a36Sopenharmony_ci	int status = 0;
224062306a36Sopenharmony_ci	u16 buf_len;
224162306a36Sopenharmony_ci
224262306a36Sopenharmony_ci	hw = pi->hw;
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci	if (!parent || !num_items)
224562306a36Sopenharmony_ci		return -EINVAL;
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_ci	/* Does parent have enough space */
224862306a36Sopenharmony_ci	if (parent->num_children + num_items >
224962306a36Sopenharmony_ci	    hw->max_children[parent->tx_sched_layer])
225062306a36Sopenharmony_ci		return -ENOSPC;
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	buf_len = struct_size(buf, teid, 1);
225362306a36Sopenharmony_ci	buf = kzalloc(buf_len, GFP_KERNEL);
225462306a36Sopenharmony_ci	if (!buf)
225562306a36Sopenharmony_ci		return -ENOMEM;
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_ci	for (i = 0; i < num_items; i++) {
225862306a36Sopenharmony_ci		node = ice_sched_find_node_by_teid(pi->root, list[i]);
225962306a36Sopenharmony_ci		if (!node) {
226062306a36Sopenharmony_ci			status = -EINVAL;
226162306a36Sopenharmony_ci			goto move_err_exit;
226262306a36Sopenharmony_ci		}
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci		buf->hdr.src_parent_teid = node->info.parent_teid;
226562306a36Sopenharmony_ci		buf->hdr.dest_parent_teid = parent->info.node_teid;
226662306a36Sopenharmony_ci		buf->teid[0] = node->info.node_teid;
226762306a36Sopenharmony_ci		buf->hdr.num_elems = cpu_to_le16(1);
226862306a36Sopenharmony_ci		status = ice_aq_move_sched_elems(hw, 1, buf, buf_len,
226962306a36Sopenharmony_ci						 &grps_movd, NULL);
227062306a36Sopenharmony_ci		if (status && grps_movd != 1) {
227162306a36Sopenharmony_ci			status = -EIO;
227262306a36Sopenharmony_ci			goto move_err_exit;
227362306a36Sopenharmony_ci		}
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci		/* update the SW DB */
227662306a36Sopenharmony_ci		ice_sched_update_parent(parent, node);
227762306a36Sopenharmony_ci	}
227862306a36Sopenharmony_ci
227962306a36Sopenharmony_cimove_err_exit:
228062306a36Sopenharmony_ci	kfree(buf);
228162306a36Sopenharmony_ci	return status;
228262306a36Sopenharmony_ci}
228362306a36Sopenharmony_ci
228462306a36Sopenharmony_ci/**
228562306a36Sopenharmony_ci * ice_sched_move_vsi_to_agg - move VSI to aggregator node
228662306a36Sopenharmony_ci * @pi: port information structure
228762306a36Sopenharmony_ci * @vsi_handle: software VSI handle
228862306a36Sopenharmony_ci * @agg_id: aggregator ID
228962306a36Sopenharmony_ci * @tc: TC number
229062306a36Sopenharmony_ci *
229162306a36Sopenharmony_ci * This function moves a VSI to an aggregator node or its subtree.
229262306a36Sopenharmony_ci * Intermediate nodes may be created if required.
229362306a36Sopenharmony_ci */
229462306a36Sopenharmony_cistatic int
229562306a36Sopenharmony_ciice_sched_move_vsi_to_agg(struct ice_port_info *pi, u16 vsi_handle, u32 agg_id,
229662306a36Sopenharmony_ci			  u8 tc)
229762306a36Sopenharmony_ci{
229862306a36Sopenharmony_ci	struct ice_sched_node *vsi_node, *agg_node, *tc_node, *parent;
229962306a36Sopenharmony_ci	u16 num_nodes[ICE_AQC_TOPO_MAX_LEVEL_NUM] = { 0 };
230062306a36Sopenharmony_ci	u32 first_node_teid, vsi_teid;
230162306a36Sopenharmony_ci	u16 num_nodes_added;
230262306a36Sopenharmony_ci	u8 aggl, vsil, i;
230362306a36Sopenharmony_ci	int status;
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	tc_node = ice_sched_get_tc_node(pi, tc);
230662306a36Sopenharmony_ci	if (!tc_node)
230762306a36Sopenharmony_ci		return -EIO;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	agg_node = ice_sched_get_agg_node(pi, tc_node, agg_id);
231062306a36Sopenharmony_ci	if (!agg_node)
231162306a36Sopenharmony_ci		return -ENOENT;
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
231462306a36Sopenharmony_ci	if (!vsi_node)
231562306a36Sopenharmony_ci		return -ENOENT;
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_ci	/* Is this VSI already part of given aggregator? */
231862306a36Sopenharmony_ci	if (ice_sched_find_node_in_subtree(pi->hw, agg_node, vsi_node))
231962306a36Sopenharmony_ci		return 0;
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	aggl = ice_sched_get_agg_layer(pi->hw);
232262306a36Sopenharmony_ci	vsil = ice_sched_get_vsi_layer(pi->hw);
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	/* set intermediate node count to 1 between aggregator and VSI layers */
232562306a36Sopenharmony_ci	for (i = aggl + 1; i < vsil; i++)
232662306a36Sopenharmony_ci		num_nodes[i] = 1;
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci	/* Check if the aggregator subtree has any free node to add the VSI */
232962306a36Sopenharmony_ci	for (i = 0; i < agg_node->num_children; i++) {
233062306a36Sopenharmony_ci		parent = ice_sched_get_free_vsi_parent(pi->hw,
233162306a36Sopenharmony_ci						       agg_node->children[i],
233262306a36Sopenharmony_ci						       num_nodes);
233362306a36Sopenharmony_ci		if (parent)
233462306a36Sopenharmony_ci			goto move_nodes;
233562306a36Sopenharmony_ci	}
233662306a36Sopenharmony_ci
233762306a36Sopenharmony_ci	/* add new nodes */
233862306a36Sopenharmony_ci	parent = agg_node;
233962306a36Sopenharmony_ci	for (i = aggl + 1; i < vsil; i++) {
234062306a36Sopenharmony_ci		status = ice_sched_add_nodes_to_layer(pi, tc_node, parent, i,
234162306a36Sopenharmony_ci						      num_nodes[i],
234262306a36Sopenharmony_ci						      &first_node_teid,
234362306a36Sopenharmony_ci						      &num_nodes_added);
234462306a36Sopenharmony_ci		if (status || num_nodes[i] != num_nodes_added)
234562306a36Sopenharmony_ci			return -EIO;
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci		/* The newly added node can be a new parent for the next
234862306a36Sopenharmony_ci		 * layer nodes
234962306a36Sopenharmony_ci		 */
235062306a36Sopenharmony_ci		if (num_nodes_added)
235162306a36Sopenharmony_ci			parent = ice_sched_find_node_by_teid(tc_node,
235262306a36Sopenharmony_ci							     first_node_teid);
235362306a36Sopenharmony_ci		else
235462306a36Sopenharmony_ci			parent = parent->children[0];
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci		if (!parent)
235762306a36Sopenharmony_ci			return -EIO;
235862306a36Sopenharmony_ci	}
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_cimove_nodes:
236162306a36Sopenharmony_ci	vsi_teid = le32_to_cpu(vsi_node->info.node_teid);
236262306a36Sopenharmony_ci	return ice_sched_move_nodes(pi, parent, 1, &vsi_teid);
236362306a36Sopenharmony_ci}
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci/**
236662306a36Sopenharmony_ci * ice_move_all_vsi_to_dflt_agg - move all VSI(s) to default aggregator
236762306a36Sopenharmony_ci * @pi: port information structure
236862306a36Sopenharmony_ci * @agg_info: aggregator info
236962306a36Sopenharmony_ci * @tc: traffic class number
237062306a36Sopenharmony_ci * @rm_vsi_info: true or false
237162306a36Sopenharmony_ci *
237262306a36Sopenharmony_ci * This function move all the VSI(s) to the default aggregator and delete
237362306a36Sopenharmony_ci * aggregator VSI info based on passed in boolean parameter rm_vsi_info. The
237462306a36Sopenharmony_ci * caller holds the scheduler lock.
237562306a36Sopenharmony_ci */
237662306a36Sopenharmony_cistatic int
237762306a36Sopenharmony_ciice_move_all_vsi_to_dflt_agg(struct ice_port_info *pi,
237862306a36Sopenharmony_ci			     struct ice_sched_agg_info *agg_info, u8 tc,
237962306a36Sopenharmony_ci			     bool rm_vsi_info)
238062306a36Sopenharmony_ci{
238162306a36Sopenharmony_ci	struct ice_sched_agg_vsi_info *agg_vsi_info;
238262306a36Sopenharmony_ci	struct ice_sched_agg_vsi_info *tmp;
238362306a36Sopenharmony_ci	int status = 0;
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	list_for_each_entry_safe(agg_vsi_info, tmp, &agg_info->agg_vsi_list,
238662306a36Sopenharmony_ci				 list_entry) {
238762306a36Sopenharmony_ci		u16 vsi_handle = agg_vsi_info->vsi_handle;
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci		/* Move VSI to default aggregator */
239062306a36Sopenharmony_ci		if (!ice_is_tc_ena(agg_vsi_info->tc_bitmap[0], tc))
239162306a36Sopenharmony_ci			continue;
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_ci		status = ice_sched_move_vsi_to_agg(pi, vsi_handle,
239462306a36Sopenharmony_ci						   ICE_DFLT_AGG_ID, tc);
239562306a36Sopenharmony_ci		if (status)
239662306a36Sopenharmony_ci			break;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci		clear_bit(tc, agg_vsi_info->tc_bitmap);
239962306a36Sopenharmony_ci		if (rm_vsi_info && !agg_vsi_info->tc_bitmap[0]) {
240062306a36Sopenharmony_ci			list_del(&agg_vsi_info->list_entry);
240162306a36Sopenharmony_ci			devm_kfree(ice_hw_to_dev(pi->hw), agg_vsi_info);
240262306a36Sopenharmony_ci		}
240362306a36Sopenharmony_ci	}
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci	return status;
240662306a36Sopenharmony_ci}
240762306a36Sopenharmony_ci
240862306a36Sopenharmony_ci/**
240962306a36Sopenharmony_ci * ice_sched_is_agg_inuse - check whether the aggregator is in use or not
241062306a36Sopenharmony_ci * @pi: port information structure
241162306a36Sopenharmony_ci * @node: node pointer
241262306a36Sopenharmony_ci *
241362306a36Sopenharmony_ci * This function checks whether the aggregator is attached with any VSI or not.
241462306a36Sopenharmony_ci */
241562306a36Sopenharmony_cistatic bool
241662306a36Sopenharmony_ciice_sched_is_agg_inuse(struct ice_port_info *pi, struct ice_sched_node *node)
241762306a36Sopenharmony_ci{
241862306a36Sopenharmony_ci	u8 vsil, i;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	vsil = ice_sched_get_vsi_layer(pi->hw);
242162306a36Sopenharmony_ci	if (node->tx_sched_layer < vsil - 1) {
242262306a36Sopenharmony_ci		for (i = 0; i < node->num_children; i++)
242362306a36Sopenharmony_ci			if (ice_sched_is_agg_inuse(pi, node->children[i]))
242462306a36Sopenharmony_ci				return true;
242562306a36Sopenharmony_ci		return false;
242662306a36Sopenharmony_ci	} else {
242762306a36Sopenharmony_ci		return node->num_children ? true : false;
242862306a36Sopenharmony_ci	}
242962306a36Sopenharmony_ci}
243062306a36Sopenharmony_ci
243162306a36Sopenharmony_ci/**
243262306a36Sopenharmony_ci * ice_sched_rm_agg_cfg - remove the aggregator node
243362306a36Sopenharmony_ci * @pi: port information structure
243462306a36Sopenharmony_ci * @agg_id: aggregator ID
243562306a36Sopenharmony_ci * @tc: TC number
243662306a36Sopenharmony_ci *
243762306a36Sopenharmony_ci * This function removes the aggregator node and intermediate nodes if any
243862306a36Sopenharmony_ci * from the given TC
243962306a36Sopenharmony_ci */
244062306a36Sopenharmony_cistatic int
244162306a36Sopenharmony_ciice_sched_rm_agg_cfg(struct ice_port_info *pi, u32 agg_id, u8 tc)
244262306a36Sopenharmony_ci{
244362306a36Sopenharmony_ci	struct ice_sched_node *tc_node, *agg_node;
244462306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
244562306a36Sopenharmony_ci
244662306a36Sopenharmony_ci	tc_node = ice_sched_get_tc_node(pi, tc);
244762306a36Sopenharmony_ci	if (!tc_node)
244862306a36Sopenharmony_ci		return -EIO;
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	agg_node = ice_sched_get_agg_node(pi, tc_node, agg_id);
245162306a36Sopenharmony_ci	if (!agg_node)
245262306a36Sopenharmony_ci		return -ENOENT;
245362306a36Sopenharmony_ci
245462306a36Sopenharmony_ci	/* Can't remove the aggregator node if it has children */
245562306a36Sopenharmony_ci	if (ice_sched_is_agg_inuse(pi, agg_node))
245662306a36Sopenharmony_ci		return -EBUSY;
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	/* need to remove the whole subtree if aggregator node is the
245962306a36Sopenharmony_ci	 * only child.
246062306a36Sopenharmony_ci	 */
246162306a36Sopenharmony_ci	while (agg_node->tx_sched_layer > hw->sw_entry_point_layer) {
246262306a36Sopenharmony_ci		struct ice_sched_node *parent = agg_node->parent;
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci		if (!parent)
246562306a36Sopenharmony_ci			return -EIO;
246662306a36Sopenharmony_ci
246762306a36Sopenharmony_ci		if (parent->num_children > 1)
246862306a36Sopenharmony_ci			break;
246962306a36Sopenharmony_ci
247062306a36Sopenharmony_ci		agg_node = parent;
247162306a36Sopenharmony_ci	}
247262306a36Sopenharmony_ci
247362306a36Sopenharmony_ci	ice_free_sched_node(pi, agg_node);
247462306a36Sopenharmony_ci	return 0;
247562306a36Sopenharmony_ci}
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci/**
247862306a36Sopenharmony_ci * ice_rm_agg_cfg_tc - remove aggregator configuration for TC
247962306a36Sopenharmony_ci * @pi: port information structure
248062306a36Sopenharmony_ci * @agg_info: aggregator ID
248162306a36Sopenharmony_ci * @tc: TC number
248262306a36Sopenharmony_ci * @rm_vsi_info: bool value true or false
248362306a36Sopenharmony_ci *
248462306a36Sopenharmony_ci * This function removes aggregator reference to VSI of given TC. It removes
248562306a36Sopenharmony_ci * the aggregator configuration completely for requested TC. The caller needs
248662306a36Sopenharmony_ci * to hold the scheduler lock.
248762306a36Sopenharmony_ci */
248862306a36Sopenharmony_cistatic int
248962306a36Sopenharmony_ciice_rm_agg_cfg_tc(struct ice_port_info *pi, struct ice_sched_agg_info *agg_info,
249062306a36Sopenharmony_ci		  u8 tc, bool rm_vsi_info)
249162306a36Sopenharmony_ci{
249262306a36Sopenharmony_ci	int status = 0;
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci	/* If nothing to remove - return success */
249562306a36Sopenharmony_ci	if (!ice_is_tc_ena(agg_info->tc_bitmap[0], tc))
249662306a36Sopenharmony_ci		goto exit_rm_agg_cfg_tc;
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	status = ice_move_all_vsi_to_dflt_agg(pi, agg_info, tc, rm_vsi_info);
249962306a36Sopenharmony_ci	if (status)
250062306a36Sopenharmony_ci		goto exit_rm_agg_cfg_tc;
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci	/* Delete aggregator node(s) */
250362306a36Sopenharmony_ci	status = ice_sched_rm_agg_cfg(pi, agg_info->agg_id, tc);
250462306a36Sopenharmony_ci	if (status)
250562306a36Sopenharmony_ci		goto exit_rm_agg_cfg_tc;
250662306a36Sopenharmony_ci
250762306a36Sopenharmony_ci	clear_bit(tc, agg_info->tc_bitmap);
250862306a36Sopenharmony_ciexit_rm_agg_cfg_tc:
250962306a36Sopenharmony_ci	return status;
251062306a36Sopenharmony_ci}
251162306a36Sopenharmony_ci
251262306a36Sopenharmony_ci/**
251362306a36Sopenharmony_ci * ice_save_agg_tc_bitmap - save aggregator TC bitmap
251462306a36Sopenharmony_ci * @pi: port information structure
251562306a36Sopenharmony_ci * @agg_id: aggregator ID
251662306a36Sopenharmony_ci * @tc_bitmap: 8 bits TC bitmap
251762306a36Sopenharmony_ci *
251862306a36Sopenharmony_ci * Save aggregator TC bitmap. This function needs to be called with scheduler
251962306a36Sopenharmony_ci * lock held.
252062306a36Sopenharmony_ci */
252162306a36Sopenharmony_cistatic int
252262306a36Sopenharmony_ciice_save_agg_tc_bitmap(struct ice_port_info *pi, u32 agg_id,
252362306a36Sopenharmony_ci		       unsigned long *tc_bitmap)
252462306a36Sopenharmony_ci{
252562306a36Sopenharmony_ci	struct ice_sched_agg_info *agg_info;
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	agg_info = ice_get_agg_info(pi->hw, agg_id);
252862306a36Sopenharmony_ci	if (!agg_info)
252962306a36Sopenharmony_ci		return -EINVAL;
253062306a36Sopenharmony_ci	bitmap_copy(agg_info->replay_tc_bitmap, tc_bitmap,
253162306a36Sopenharmony_ci		    ICE_MAX_TRAFFIC_CLASS);
253262306a36Sopenharmony_ci	return 0;
253362306a36Sopenharmony_ci}
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci/**
253662306a36Sopenharmony_ci * ice_sched_add_agg_cfg - create an aggregator node
253762306a36Sopenharmony_ci * @pi: port information structure
253862306a36Sopenharmony_ci * @agg_id: aggregator ID
253962306a36Sopenharmony_ci * @tc: TC number
254062306a36Sopenharmony_ci *
254162306a36Sopenharmony_ci * This function creates an aggregator node and intermediate nodes if required
254262306a36Sopenharmony_ci * for the given TC
254362306a36Sopenharmony_ci */
254462306a36Sopenharmony_cistatic int
254562306a36Sopenharmony_ciice_sched_add_agg_cfg(struct ice_port_info *pi, u32 agg_id, u8 tc)
254662306a36Sopenharmony_ci{
254762306a36Sopenharmony_ci	struct ice_sched_node *parent, *agg_node, *tc_node;
254862306a36Sopenharmony_ci	u16 num_nodes[ICE_AQC_TOPO_MAX_LEVEL_NUM] = { 0 };
254962306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
255062306a36Sopenharmony_ci	u32 first_node_teid;
255162306a36Sopenharmony_ci	u16 num_nodes_added;
255262306a36Sopenharmony_ci	int status = 0;
255362306a36Sopenharmony_ci	u8 i, aggl;
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci	tc_node = ice_sched_get_tc_node(pi, tc);
255662306a36Sopenharmony_ci	if (!tc_node)
255762306a36Sopenharmony_ci		return -EIO;
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	agg_node = ice_sched_get_agg_node(pi, tc_node, agg_id);
256062306a36Sopenharmony_ci	/* Does Agg node already exist ? */
256162306a36Sopenharmony_ci	if (agg_node)
256262306a36Sopenharmony_ci		return status;
256362306a36Sopenharmony_ci
256462306a36Sopenharmony_ci	aggl = ice_sched_get_agg_layer(hw);
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	/* need one node in Agg layer */
256762306a36Sopenharmony_ci	num_nodes[aggl] = 1;
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_ci	/* Check whether the intermediate nodes have space to add the
257062306a36Sopenharmony_ci	 * new aggregator. If they are full, then SW needs to allocate a new
257162306a36Sopenharmony_ci	 * intermediate node on those layers
257262306a36Sopenharmony_ci	 */
257362306a36Sopenharmony_ci	for (i = hw->sw_entry_point_layer; i < aggl; i++) {
257462306a36Sopenharmony_ci		parent = ice_sched_get_first_node(pi, tc_node, i);
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci		/* scan all the siblings */
257762306a36Sopenharmony_ci		while (parent) {
257862306a36Sopenharmony_ci			if (parent->num_children < hw->max_children[i])
257962306a36Sopenharmony_ci				break;
258062306a36Sopenharmony_ci			parent = parent->sibling;
258162306a36Sopenharmony_ci		}
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci		/* all the nodes are full, reserve one for this layer */
258462306a36Sopenharmony_ci		if (!parent)
258562306a36Sopenharmony_ci			num_nodes[i]++;
258662306a36Sopenharmony_ci	}
258762306a36Sopenharmony_ci
258862306a36Sopenharmony_ci	/* add the aggregator node */
258962306a36Sopenharmony_ci	parent = tc_node;
259062306a36Sopenharmony_ci	for (i = hw->sw_entry_point_layer; i <= aggl; i++) {
259162306a36Sopenharmony_ci		if (!parent)
259262306a36Sopenharmony_ci			return -EIO;
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci		status = ice_sched_add_nodes_to_layer(pi, tc_node, parent, i,
259562306a36Sopenharmony_ci						      num_nodes[i],
259662306a36Sopenharmony_ci						      &first_node_teid,
259762306a36Sopenharmony_ci						      &num_nodes_added);
259862306a36Sopenharmony_ci		if (status || num_nodes[i] != num_nodes_added)
259962306a36Sopenharmony_ci			return -EIO;
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci		/* The newly added node can be a new parent for the next
260262306a36Sopenharmony_ci		 * layer nodes
260362306a36Sopenharmony_ci		 */
260462306a36Sopenharmony_ci		if (num_nodes_added) {
260562306a36Sopenharmony_ci			parent = ice_sched_find_node_by_teid(tc_node,
260662306a36Sopenharmony_ci							     first_node_teid);
260762306a36Sopenharmony_ci			/* register aggregator ID with the aggregator node */
260862306a36Sopenharmony_ci			if (parent && i == aggl)
260962306a36Sopenharmony_ci				parent->agg_id = agg_id;
261062306a36Sopenharmony_ci		} else {
261162306a36Sopenharmony_ci			parent = parent->children[0];
261262306a36Sopenharmony_ci		}
261362306a36Sopenharmony_ci	}
261462306a36Sopenharmony_ci
261562306a36Sopenharmony_ci	return 0;
261662306a36Sopenharmony_ci}
261762306a36Sopenharmony_ci
261862306a36Sopenharmony_ci/**
261962306a36Sopenharmony_ci * ice_sched_cfg_agg - configure aggregator node
262062306a36Sopenharmony_ci * @pi: port information structure
262162306a36Sopenharmony_ci * @agg_id: aggregator ID
262262306a36Sopenharmony_ci * @agg_type: aggregator type queue, VSI, or aggregator group
262362306a36Sopenharmony_ci * @tc_bitmap: bits TC bitmap
262462306a36Sopenharmony_ci *
262562306a36Sopenharmony_ci * It registers a unique aggregator node into scheduler services. It
262662306a36Sopenharmony_ci * allows a user to register with a unique ID to track it's resources.
262762306a36Sopenharmony_ci * The aggregator type determines if this is a queue group, VSI group
262862306a36Sopenharmony_ci * or aggregator group. It then creates the aggregator node(s) for requested
262962306a36Sopenharmony_ci * TC(s) or removes an existing aggregator node including its configuration
263062306a36Sopenharmony_ci * if indicated via tc_bitmap. Call ice_rm_agg_cfg to release aggregator
263162306a36Sopenharmony_ci * resources and remove aggregator ID.
263262306a36Sopenharmony_ci * This function needs to be called with scheduler lock held.
263362306a36Sopenharmony_ci */
263462306a36Sopenharmony_cistatic int
263562306a36Sopenharmony_ciice_sched_cfg_agg(struct ice_port_info *pi, u32 agg_id,
263662306a36Sopenharmony_ci		  enum ice_agg_type agg_type, unsigned long *tc_bitmap)
263762306a36Sopenharmony_ci{
263862306a36Sopenharmony_ci	struct ice_sched_agg_info *agg_info;
263962306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
264062306a36Sopenharmony_ci	int status = 0;
264162306a36Sopenharmony_ci	u8 tc;
264262306a36Sopenharmony_ci
264362306a36Sopenharmony_ci	agg_info = ice_get_agg_info(hw, agg_id);
264462306a36Sopenharmony_ci	if (!agg_info) {
264562306a36Sopenharmony_ci		/* Create new entry for new aggregator ID */
264662306a36Sopenharmony_ci		agg_info = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*agg_info),
264762306a36Sopenharmony_ci					GFP_KERNEL);
264862306a36Sopenharmony_ci		if (!agg_info)
264962306a36Sopenharmony_ci			return -ENOMEM;
265062306a36Sopenharmony_ci
265162306a36Sopenharmony_ci		agg_info->agg_id = agg_id;
265262306a36Sopenharmony_ci		agg_info->agg_type = agg_type;
265362306a36Sopenharmony_ci		agg_info->tc_bitmap[0] = 0;
265462306a36Sopenharmony_ci
265562306a36Sopenharmony_ci		/* Initialize the aggregator VSI list head */
265662306a36Sopenharmony_ci		INIT_LIST_HEAD(&agg_info->agg_vsi_list);
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci		/* Add new entry in aggregator list */
265962306a36Sopenharmony_ci		list_add(&agg_info->list_entry, &hw->agg_list);
266062306a36Sopenharmony_ci	}
266162306a36Sopenharmony_ci	/* Create aggregator node(s) for requested TC(s) */
266262306a36Sopenharmony_ci	ice_for_each_traffic_class(tc) {
266362306a36Sopenharmony_ci		if (!ice_is_tc_ena(*tc_bitmap, tc)) {
266462306a36Sopenharmony_ci			/* Delete aggregator cfg TC if it exists previously */
266562306a36Sopenharmony_ci			status = ice_rm_agg_cfg_tc(pi, agg_info, tc, false);
266662306a36Sopenharmony_ci			if (status)
266762306a36Sopenharmony_ci				break;
266862306a36Sopenharmony_ci			continue;
266962306a36Sopenharmony_ci		}
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci		/* Check if aggregator node for TC already exists */
267262306a36Sopenharmony_ci		if (ice_is_tc_ena(agg_info->tc_bitmap[0], tc))
267362306a36Sopenharmony_ci			continue;
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ci		/* Create new aggregator node for TC */
267662306a36Sopenharmony_ci		status = ice_sched_add_agg_cfg(pi, agg_id, tc);
267762306a36Sopenharmony_ci		if (status)
267862306a36Sopenharmony_ci			break;
267962306a36Sopenharmony_ci
268062306a36Sopenharmony_ci		/* Save aggregator node's TC information */
268162306a36Sopenharmony_ci		set_bit(tc, agg_info->tc_bitmap);
268262306a36Sopenharmony_ci	}
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	return status;
268562306a36Sopenharmony_ci}
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci/**
268862306a36Sopenharmony_ci * ice_cfg_agg - config aggregator node
268962306a36Sopenharmony_ci * @pi: port information structure
269062306a36Sopenharmony_ci * @agg_id: aggregator ID
269162306a36Sopenharmony_ci * @agg_type: aggregator type queue, VSI, or aggregator group
269262306a36Sopenharmony_ci * @tc_bitmap: bits TC bitmap
269362306a36Sopenharmony_ci *
269462306a36Sopenharmony_ci * This function configures aggregator node(s).
269562306a36Sopenharmony_ci */
269662306a36Sopenharmony_ciint
269762306a36Sopenharmony_ciice_cfg_agg(struct ice_port_info *pi, u32 agg_id, enum ice_agg_type agg_type,
269862306a36Sopenharmony_ci	    u8 tc_bitmap)
269962306a36Sopenharmony_ci{
270062306a36Sopenharmony_ci	unsigned long bitmap = tc_bitmap;
270162306a36Sopenharmony_ci	int status;
270262306a36Sopenharmony_ci
270362306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
270462306a36Sopenharmony_ci	status = ice_sched_cfg_agg(pi, agg_id, agg_type, &bitmap);
270562306a36Sopenharmony_ci	if (!status)
270662306a36Sopenharmony_ci		status = ice_save_agg_tc_bitmap(pi, agg_id, &bitmap);
270762306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
270862306a36Sopenharmony_ci	return status;
270962306a36Sopenharmony_ci}
271062306a36Sopenharmony_ci
271162306a36Sopenharmony_ci/**
271262306a36Sopenharmony_ci * ice_get_agg_vsi_info - get the aggregator ID
271362306a36Sopenharmony_ci * @agg_info: aggregator info
271462306a36Sopenharmony_ci * @vsi_handle: software VSI handle
271562306a36Sopenharmony_ci *
271662306a36Sopenharmony_ci * The function returns aggregator VSI info based on VSI handle. This function
271762306a36Sopenharmony_ci * needs to be called with scheduler lock held.
271862306a36Sopenharmony_ci */
271962306a36Sopenharmony_cistatic struct ice_sched_agg_vsi_info *
272062306a36Sopenharmony_ciice_get_agg_vsi_info(struct ice_sched_agg_info *agg_info, u16 vsi_handle)
272162306a36Sopenharmony_ci{
272262306a36Sopenharmony_ci	struct ice_sched_agg_vsi_info *agg_vsi_info;
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci	list_for_each_entry(agg_vsi_info, &agg_info->agg_vsi_list, list_entry)
272562306a36Sopenharmony_ci		if (agg_vsi_info->vsi_handle == vsi_handle)
272662306a36Sopenharmony_ci			return agg_vsi_info;
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci	return NULL;
272962306a36Sopenharmony_ci}
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci/**
273262306a36Sopenharmony_ci * ice_get_vsi_agg_info - get the aggregator info of VSI
273362306a36Sopenharmony_ci * @hw: pointer to the hardware structure
273462306a36Sopenharmony_ci * @vsi_handle: Sw VSI handle
273562306a36Sopenharmony_ci *
273662306a36Sopenharmony_ci * The function returns aggregator info of VSI represented via vsi_handle. The
273762306a36Sopenharmony_ci * VSI has in this case a different aggregator than the default one. This
273862306a36Sopenharmony_ci * function needs to be called with scheduler lock held.
273962306a36Sopenharmony_ci */
274062306a36Sopenharmony_cistatic struct ice_sched_agg_info *
274162306a36Sopenharmony_ciice_get_vsi_agg_info(struct ice_hw *hw, u16 vsi_handle)
274262306a36Sopenharmony_ci{
274362306a36Sopenharmony_ci	struct ice_sched_agg_info *agg_info;
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	list_for_each_entry(agg_info, &hw->agg_list, list_entry) {
274662306a36Sopenharmony_ci		struct ice_sched_agg_vsi_info *agg_vsi_info;
274762306a36Sopenharmony_ci
274862306a36Sopenharmony_ci		agg_vsi_info = ice_get_agg_vsi_info(agg_info, vsi_handle);
274962306a36Sopenharmony_ci		if (agg_vsi_info)
275062306a36Sopenharmony_ci			return agg_info;
275162306a36Sopenharmony_ci	}
275262306a36Sopenharmony_ci	return NULL;
275362306a36Sopenharmony_ci}
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci/**
275662306a36Sopenharmony_ci * ice_save_agg_vsi_tc_bitmap - save aggregator VSI TC bitmap
275762306a36Sopenharmony_ci * @pi: port information structure
275862306a36Sopenharmony_ci * @agg_id: aggregator ID
275962306a36Sopenharmony_ci * @vsi_handle: software VSI handle
276062306a36Sopenharmony_ci * @tc_bitmap: TC bitmap of enabled TC(s)
276162306a36Sopenharmony_ci *
276262306a36Sopenharmony_ci * Save VSI to aggregator TC bitmap. This function needs to call with scheduler
276362306a36Sopenharmony_ci * lock held.
276462306a36Sopenharmony_ci */
276562306a36Sopenharmony_cistatic int
276662306a36Sopenharmony_ciice_save_agg_vsi_tc_bitmap(struct ice_port_info *pi, u32 agg_id, u16 vsi_handle,
276762306a36Sopenharmony_ci			   unsigned long *tc_bitmap)
276862306a36Sopenharmony_ci{
276962306a36Sopenharmony_ci	struct ice_sched_agg_vsi_info *agg_vsi_info;
277062306a36Sopenharmony_ci	struct ice_sched_agg_info *agg_info;
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	agg_info = ice_get_agg_info(pi->hw, agg_id);
277362306a36Sopenharmony_ci	if (!agg_info)
277462306a36Sopenharmony_ci		return -EINVAL;
277562306a36Sopenharmony_ci	/* check if entry already exist */
277662306a36Sopenharmony_ci	agg_vsi_info = ice_get_agg_vsi_info(agg_info, vsi_handle);
277762306a36Sopenharmony_ci	if (!agg_vsi_info)
277862306a36Sopenharmony_ci		return -EINVAL;
277962306a36Sopenharmony_ci	bitmap_copy(agg_vsi_info->replay_tc_bitmap, tc_bitmap,
278062306a36Sopenharmony_ci		    ICE_MAX_TRAFFIC_CLASS);
278162306a36Sopenharmony_ci	return 0;
278262306a36Sopenharmony_ci}
278362306a36Sopenharmony_ci
278462306a36Sopenharmony_ci/**
278562306a36Sopenharmony_ci * ice_sched_assoc_vsi_to_agg - associate/move VSI to new/default aggregator
278662306a36Sopenharmony_ci * @pi: port information structure
278762306a36Sopenharmony_ci * @agg_id: aggregator ID
278862306a36Sopenharmony_ci * @vsi_handle: software VSI handle
278962306a36Sopenharmony_ci * @tc_bitmap: TC bitmap of enabled TC(s)
279062306a36Sopenharmony_ci *
279162306a36Sopenharmony_ci * This function moves VSI to a new or default aggregator node. If VSI is
279262306a36Sopenharmony_ci * already associated to the aggregator node then no operation is performed on
279362306a36Sopenharmony_ci * the tree. This function needs to be called with scheduler lock held.
279462306a36Sopenharmony_ci */
279562306a36Sopenharmony_cistatic int
279662306a36Sopenharmony_ciice_sched_assoc_vsi_to_agg(struct ice_port_info *pi, u32 agg_id,
279762306a36Sopenharmony_ci			   u16 vsi_handle, unsigned long *tc_bitmap)
279862306a36Sopenharmony_ci{
279962306a36Sopenharmony_ci	struct ice_sched_agg_vsi_info *agg_vsi_info, *iter, *old_agg_vsi_info = NULL;
280062306a36Sopenharmony_ci	struct ice_sched_agg_info *agg_info, *old_agg_info;
280162306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
280262306a36Sopenharmony_ci	int status = 0;
280362306a36Sopenharmony_ci	u8 tc;
280462306a36Sopenharmony_ci
280562306a36Sopenharmony_ci	if (!ice_is_vsi_valid(pi->hw, vsi_handle))
280662306a36Sopenharmony_ci		return -EINVAL;
280762306a36Sopenharmony_ci	agg_info = ice_get_agg_info(hw, agg_id);
280862306a36Sopenharmony_ci	if (!agg_info)
280962306a36Sopenharmony_ci		return -EINVAL;
281062306a36Sopenharmony_ci	/* If the VSI is already part of another aggregator then update
281162306a36Sopenharmony_ci	 * its VSI info list
281262306a36Sopenharmony_ci	 */
281362306a36Sopenharmony_ci	old_agg_info = ice_get_vsi_agg_info(hw, vsi_handle);
281462306a36Sopenharmony_ci	if (old_agg_info && old_agg_info != agg_info) {
281562306a36Sopenharmony_ci		struct ice_sched_agg_vsi_info *vtmp;
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci		list_for_each_entry_safe(iter, vtmp,
281862306a36Sopenharmony_ci					 &old_agg_info->agg_vsi_list,
281962306a36Sopenharmony_ci					 list_entry)
282062306a36Sopenharmony_ci			if (iter->vsi_handle == vsi_handle) {
282162306a36Sopenharmony_ci				old_agg_vsi_info = iter;
282262306a36Sopenharmony_ci				break;
282362306a36Sopenharmony_ci			}
282462306a36Sopenharmony_ci	}
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci	/* check if entry already exist */
282762306a36Sopenharmony_ci	agg_vsi_info = ice_get_agg_vsi_info(agg_info, vsi_handle);
282862306a36Sopenharmony_ci	if (!agg_vsi_info) {
282962306a36Sopenharmony_ci		/* Create new entry for VSI under aggregator list */
283062306a36Sopenharmony_ci		agg_vsi_info = devm_kzalloc(ice_hw_to_dev(hw),
283162306a36Sopenharmony_ci					    sizeof(*agg_vsi_info), GFP_KERNEL);
283262306a36Sopenharmony_ci		if (!agg_vsi_info)
283362306a36Sopenharmony_ci			return -EINVAL;
283462306a36Sopenharmony_ci
283562306a36Sopenharmony_ci		/* add VSI ID into the aggregator list */
283662306a36Sopenharmony_ci		agg_vsi_info->vsi_handle = vsi_handle;
283762306a36Sopenharmony_ci		list_add(&agg_vsi_info->list_entry, &agg_info->agg_vsi_list);
283862306a36Sopenharmony_ci	}
283962306a36Sopenharmony_ci	/* Move VSI node to new aggregator node for requested TC(s) */
284062306a36Sopenharmony_ci	ice_for_each_traffic_class(tc) {
284162306a36Sopenharmony_ci		if (!ice_is_tc_ena(*tc_bitmap, tc))
284262306a36Sopenharmony_ci			continue;
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci		/* Move VSI to new aggregator */
284562306a36Sopenharmony_ci		status = ice_sched_move_vsi_to_agg(pi, vsi_handle, agg_id, tc);
284662306a36Sopenharmony_ci		if (status)
284762306a36Sopenharmony_ci			break;
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci		set_bit(tc, agg_vsi_info->tc_bitmap);
285062306a36Sopenharmony_ci		if (old_agg_vsi_info)
285162306a36Sopenharmony_ci			clear_bit(tc, old_agg_vsi_info->tc_bitmap);
285262306a36Sopenharmony_ci	}
285362306a36Sopenharmony_ci	if (old_agg_vsi_info && !old_agg_vsi_info->tc_bitmap[0]) {
285462306a36Sopenharmony_ci		list_del(&old_agg_vsi_info->list_entry);
285562306a36Sopenharmony_ci		devm_kfree(ice_hw_to_dev(pi->hw), old_agg_vsi_info);
285662306a36Sopenharmony_ci	}
285762306a36Sopenharmony_ci	return status;
285862306a36Sopenharmony_ci}
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_ci/**
286162306a36Sopenharmony_ci * ice_sched_rm_unused_rl_prof - remove unused RL profile
286262306a36Sopenharmony_ci * @pi: port information structure
286362306a36Sopenharmony_ci *
286462306a36Sopenharmony_ci * This function removes unused rate limit profiles from the HW and
286562306a36Sopenharmony_ci * SW DB. The caller needs to hold scheduler lock.
286662306a36Sopenharmony_ci */
286762306a36Sopenharmony_cistatic void ice_sched_rm_unused_rl_prof(struct ice_port_info *pi)
286862306a36Sopenharmony_ci{
286962306a36Sopenharmony_ci	u16 ln;
287062306a36Sopenharmony_ci
287162306a36Sopenharmony_ci	for (ln = 0; ln < pi->hw->num_tx_sched_layers; ln++) {
287262306a36Sopenharmony_ci		struct ice_aqc_rl_profile_info *rl_prof_elem;
287362306a36Sopenharmony_ci		struct ice_aqc_rl_profile_info *rl_prof_tmp;
287462306a36Sopenharmony_ci
287562306a36Sopenharmony_ci		list_for_each_entry_safe(rl_prof_elem, rl_prof_tmp,
287662306a36Sopenharmony_ci					 &pi->rl_prof_list[ln], list_entry) {
287762306a36Sopenharmony_ci			if (!ice_sched_del_rl_profile(pi->hw, rl_prof_elem))
287862306a36Sopenharmony_ci				ice_debug(pi->hw, ICE_DBG_SCHED, "Removed rl profile\n");
287962306a36Sopenharmony_ci		}
288062306a36Sopenharmony_ci	}
288162306a36Sopenharmony_ci}
288262306a36Sopenharmony_ci
288362306a36Sopenharmony_ci/**
288462306a36Sopenharmony_ci * ice_sched_update_elem - update element
288562306a36Sopenharmony_ci * @hw: pointer to the HW struct
288662306a36Sopenharmony_ci * @node: pointer to node
288762306a36Sopenharmony_ci * @info: node info to update
288862306a36Sopenharmony_ci *
288962306a36Sopenharmony_ci * Update the HW DB, and local SW DB of node. Update the scheduling
289062306a36Sopenharmony_ci * parameters of node from argument info data buffer (Info->data buf) and
289162306a36Sopenharmony_ci * returns success or error on config sched element failure. The caller
289262306a36Sopenharmony_ci * needs to hold scheduler lock.
289362306a36Sopenharmony_ci */
289462306a36Sopenharmony_cistatic int
289562306a36Sopenharmony_ciice_sched_update_elem(struct ice_hw *hw, struct ice_sched_node *node,
289662306a36Sopenharmony_ci		      struct ice_aqc_txsched_elem_data *info)
289762306a36Sopenharmony_ci{
289862306a36Sopenharmony_ci	struct ice_aqc_txsched_elem_data buf;
289962306a36Sopenharmony_ci	u16 elem_cfgd = 0;
290062306a36Sopenharmony_ci	u16 num_elems = 1;
290162306a36Sopenharmony_ci	int status;
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_ci	buf = *info;
290462306a36Sopenharmony_ci	/* Parent TEID is reserved field in this aq call */
290562306a36Sopenharmony_ci	buf.parent_teid = 0;
290662306a36Sopenharmony_ci	/* Element type is reserved field in this aq call */
290762306a36Sopenharmony_ci	buf.data.elem_type = 0;
290862306a36Sopenharmony_ci	/* Flags is reserved field in this aq call */
290962306a36Sopenharmony_ci	buf.data.flags = 0;
291062306a36Sopenharmony_ci
291162306a36Sopenharmony_ci	/* Update HW DB */
291262306a36Sopenharmony_ci	/* Configure element node */
291362306a36Sopenharmony_ci	status = ice_aq_cfg_sched_elems(hw, num_elems, &buf, sizeof(buf),
291462306a36Sopenharmony_ci					&elem_cfgd, NULL);
291562306a36Sopenharmony_ci	if (status || elem_cfgd != num_elems) {
291662306a36Sopenharmony_ci		ice_debug(hw, ICE_DBG_SCHED, "Config sched elem error\n");
291762306a36Sopenharmony_ci		return -EIO;
291862306a36Sopenharmony_ci	}
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	/* Config success case */
292162306a36Sopenharmony_ci	/* Now update local SW DB */
292262306a36Sopenharmony_ci	/* Only copy the data portion of info buffer */
292362306a36Sopenharmony_ci	node->info.data = info->data;
292462306a36Sopenharmony_ci	return status;
292562306a36Sopenharmony_ci}
292662306a36Sopenharmony_ci
292762306a36Sopenharmony_ci/**
292862306a36Sopenharmony_ci * ice_sched_cfg_node_bw_alloc - configure node BW weight/alloc params
292962306a36Sopenharmony_ci * @hw: pointer to the HW struct
293062306a36Sopenharmony_ci * @node: sched node to configure
293162306a36Sopenharmony_ci * @rl_type: rate limit type CIR, EIR, or shared
293262306a36Sopenharmony_ci * @bw_alloc: BW weight/allocation
293362306a36Sopenharmony_ci *
293462306a36Sopenharmony_ci * This function configures node element's BW allocation.
293562306a36Sopenharmony_ci */
293662306a36Sopenharmony_cistatic int
293762306a36Sopenharmony_ciice_sched_cfg_node_bw_alloc(struct ice_hw *hw, struct ice_sched_node *node,
293862306a36Sopenharmony_ci			    enum ice_rl_type rl_type, u16 bw_alloc)
293962306a36Sopenharmony_ci{
294062306a36Sopenharmony_ci	struct ice_aqc_txsched_elem_data buf;
294162306a36Sopenharmony_ci	struct ice_aqc_txsched_elem *data;
294262306a36Sopenharmony_ci
294362306a36Sopenharmony_ci	buf = node->info;
294462306a36Sopenharmony_ci	data = &buf.data;
294562306a36Sopenharmony_ci	if (rl_type == ICE_MIN_BW) {
294662306a36Sopenharmony_ci		data->valid_sections |= ICE_AQC_ELEM_VALID_CIR;
294762306a36Sopenharmony_ci		data->cir_bw.bw_alloc = cpu_to_le16(bw_alloc);
294862306a36Sopenharmony_ci	} else if (rl_type == ICE_MAX_BW) {
294962306a36Sopenharmony_ci		data->valid_sections |= ICE_AQC_ELEM_VALID_EIR;
295062306a36Sopenharmony_ci		data->eir_bw.bw_alloc = cpu_to_le16(bw_alloc);
295162306a36Sopenharmony_ci	} else {
295262306a36Sopenharmony_ci		return -EINVAL;
295362306a36Sopenharmony_ci	}
295462306a36Sopenharmony_ci
295562306a36Sopenharmony_ci	/* Configure element */
295662306a36Sopenharmony_ci	return ice_sched_update_elem(hw, node, &buf);
295762306a36Sopenharmony_ci}
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_ci/**
296062306a36Sopenharmony_ci * ice_move_vsi_to_agg - moves VSI to new or default aggregator
296162306a36Sopenharmony_ci * @pi: port information structure
296262306a36Sopenharmony_ci * @agg_id: aggregator ID
296362306a36Sopenharmony_ci * @vsi_handle: software VSI handle
296462306a36Sopenharmony_ci * @tc_bitmap: TC bitmap of enabled TC(s)
296562306a36Sopenharmony_ci *
296662306a36Sopenharmony_ci * Move or associate VSI to a new or default aggregator node.
296762306a36Sopenharmony_ci */
296862306a36Sopenharmony_ciint
296962306a36Sopenharmony_ciice_move_vsi_to_agg(struct ice_port_info *pi, u32 agg_id, u16 vsi_handle,
297062306a36Sopenharmony_ci		    u8 tc_bitmap)
297162306a36Sopenharmony_ci{
297262306a36Sopenharmony_ci	unsigned long bitmap = tc_bitmap;
297362306a36Sopenharmony_ci	int status;
297462306a36Sopenharmony_ci
297562306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
297662306a36Sopenharmony_ci	status = ice_sched_assoc_vsi_to_agg(pi, agg_id, vsi_handle,
297762306a36Sopenharmony_ci					    (unsigned long *)&bitmap);
297862306a36Sopenharmony_ci	if (!status)
297962306a36Sopenharmony_ci		status = ice_save_agg_vsi_tc_bitmap(pi, agg_id, vsi_handle,
298062306a36Sopenharmony_ci						    (unsigned long *)&bitmap);
298162306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
298262306a36Sopenharmony_ci	return status;
298362306a36Sopenharmony_ci}
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci/**
298662306a36Sopenharmony_ci * ice_set_clear_cir_bw - set or clear CIR BW
298762306a36Sopenharmony_ci * @bw_t_info: bandwidth type information structure
298862306a36Sopenharmony_ci * @bw: bandwidth in Kbps - Kilo bits per sec
298962306a36Sopenharmony_ci *
299062306a36Sopenharmony_ci * Save or clear CIR bandwidth (BW) in the passed param bw_t_info.
299162306a36Sopenharmony_ci */
299262306a36Sopenharmony_cistatic void ice_set_clear_cir_bw(struct ice_bw_type_info *bw_t_info, u32 bw)
299362306a36Sopenharmony_ci{
299462306a36Sopenharmony_ci	if (bw == ICE_SCHED_DFLT_BW) {
299562306a36Sopenharmony_ci		clear_bit(ICE_BW_TYPE_CIR, bw_t_info->bw_t_bitmap);
299662306a36Sopenharmony_ci		bw_t_info->cir_bw.bw = 0;
299762306a36Sopenharmony_ci	} else {
299862306a36Sopenharmony_ci		/* Save type of BW information */
299962306a36Sopenharmony_ci		set_bit(ICE_BW_TYPE_CIR, bw_t_info->bw_t_bitmap);
300062306a36Sopenharmony_ci		bw_t_info->cir_bw.bw = bw;
300162306a36Sopenharmony_ci	}
300262306a36Sopenharmony_ci}
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci/**
300562306a36Sopenharmony_ci * ice_set_clear_eir_bw - set or clear EIR BW
300662306a36Sopenharmony_ci * @bw_t_info: bandwidth type information structure
300762306a36Sopenharmony_ci * @bw: bandwidth in Kbps - Kilo bits per sec
300862306a36Sopenharmony_ci *
300962306a36Sopenharmony_ci * Save or clear EIR bandwidth (BW) in the passed param bw_t_info.
301062306a36Sopenharmony_ci */
301162306a36Sopenharmony_cistatic void ice_set_clear_eir_bw(struct ice_bw_type_info *bw_t_info, u32 bw)
301262306a36Sopenharmony_ci{
301362306a36Sopenharmony_ci	if (bw == ICE_SCHED_DFLT_BW) {
301462306a36Sopenharmony_ci		clear_bit(ICE_BW_TYPE_EIR, bw_t_info->bw_t_bitmap);
301562306a36Sopenharmony_ci		bw_t_info->eir_bw.bw = 0;
301662306a36Sopenharmony_ci	} else {
301762306a36Sopenharmony_ci		/* EIR BW and Shared BW profiles are mutually exclusive and
301862306a36Sopenharmony_ci		 * hence only one of them may be set for any given element.
301962306a36Sopenharmony_ci		 * First clear earlier saved shared BW information.
302062306a36Sopenharmony_ci		 */
302162306a36Sopenharmony_ci		clear_bit(ICE_BW_TYPE_SHARED, bw_t_info->bw_t_bitmap);
302262306a36Sopenharmony_ci		bw_t_info->shared_bw = 0;
302362306a36Sopenharmony_ci		/* save EIR BW information */
302462306a36Sopenharmony_ci		set_bit(ICE_BW_TYPE_EIR, bw_t_info->bw_t_bitmap);
302562306a36Sopenharmony_ci		bw_t_info->eir_bw.bw = bw;
302662306a36Sopenharmony_ci	}
302762306a36Sopenharmony_ci}
302862306a36Sopenharmony_ci
302962306a36Sopenharmony_ci/**
303062306a36Sopenharmony_ci * ice_set_clear_shared_bw - set or clear shared BW
303162306a36Sopenharmony_ci * @bw_t_info: bandwidth type information structure
303262306a36Sopenharmony_ci * @bw: bandwidth in Kbps - Kilo bits per sec
303362306a36Sopenharmony_ci *
303462306a36Sopenharmony_ci * Save or clear shared bandwidth (BW) in the passed param bw_t_info.
303562306a36Sopenharmony_ci */
303662306a36Sopenharmony_cistatic void ice_set_clear_shared_bw(struct ice_bw_type_info *bw_t_info, u32 bw)
303762306a36Sopenharmony_ci{
303862306a36Sopenharmony_ci	if (bw == ICE_SCHED_DFLT_BW) {
303962306a36Sopenharmony_ci		clear_bit(ICE_BW_TYPE_SHARED, bw_t_info->bw_t_bitmap);
304062306a36Sopenharmony_ci		bw_t_info->shared_bw = 0;
304162306a36Sopenharmony_ci	} else {
304262306a36Sopenharmony_ci		/* EIR BW and Shared BW profiles are mutually exclusive and
304362306a36Sopenharmony_ci		 * hence only one of them may be set for any given element.
304462306a36Sopenharmony_ci		 * First clear earlier saved EIR BW information.
304562306a36Sopenharmony_ci		 */
304662306a36Sopenharmony_ci		clear_bit(ICE_BW_TYPE_EIR, bw_t_info->bw_t_bitmap);
304762306a36Sopenharmony_ci		bw_t_info->eir_bw.bw = 0;
304862306a36Sopenharmony_ci		/* save shared BW information */
304962306a36Sopenharmony_ci		set_bit(ICE_BW_TYPE_SHARED, bw_t_info->bw_t_bitmap);
305062306a36Sopenharmony_ci		bw_t_info->shared_bw = bw;
305162306a36Sopenharmony_ci	}
305262306a36Sopenharmony_ci}
305362306a36Sopenharmony_ci
305462306a36Sopenharmony_ci/**
305562306a36Sopenharmony_ci * ice_sched_save_vsi_bw - save VSI node's BW information
305662306a36Sopenharmony_ci * @pi: port information structure
305762306a36Sopenharmony_ci * @vsi_handle: sw VSI handle
305862306a36Sopenharmony_ci * @tc: traffic class
305962306a36Sopenharmony_ci * @rl_type: rate limit type min, max, or shared
306062306a36Sopenharmony_ci * @bw: bandwidth in Kbps - Kilo bits per sec
306162306a36Sopenharmony_ci *
306262306a36Sopenharmony_ci * Save BW information of VSI type node for post replay use.
306362306a36Sopenharmony_ci */
306462306a36Sopenharmony_cistatic int
306562306a36Sopenharmony_ciice_sched_save_vsi_bw(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
306662306a36Sopenharmony_ci		      enum ice_rl_type rl_type, u32 bw)
306762306a36Sopenharmony_ci{
306862306a36Sopenharmony_ci	struct ice_vsi_ctx *vsi_ctx;
306962306a36Sopenharmony_ci
307062306a36Sopenharmony_ci	if (!ice_is_vsi_valid(pi->hw, vsi_handle))
307162306a36Sopenharmony_ci		return -EINVAL;
307262306a36Sopenharmony_ci	vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
307362306a36Sopenharmony_ci	if (!vsi_ctx)
307462306a36Sopenharmony_ci		return -EINVAL;
307562306a36Sopenharmony_ci	switch (rl_type) {
307662306a36Sopenharmony_ci	case ICE_MIN_BW:
307762306a36Sopenharmony_ci		ice_set_clear_cir_bw(&vsi_ctx->sched.bw_t_info[tc], bw);
307862306a36Sopenharmony_ci		break;
307962306a36Sopenharmony_ci	case ICE_MAX_BW:
308062306a36Sopenharmony_ci		ice_set_clear_eir_bw(&vsi_ctx->sched.bw_t_info[tc], bw);
308162306a36Sopenharmony_ci		break;
308262306a36Sopenharmony_ci	case ICE_SHARED_BW:
308362306a36Sopenharmony_ci		ice_set_clear_shared_bw(&vsi_ctx->sched.bw_t_info[tc], bw);
308462306a36Sopenharmony_ci		break;
308562306a36Sopenharmony_ci	default:
308662306a36Sopenharmony_ci		return -EINVAL;
308762306a36Sopenharmony_ci	}
308862306a36Sopenharmony_ci	return 0;
308962306a36Sopenharmony_ci}
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci/**
309262306a36Sopenharmony_ci * ice_sched_calc_wakeup - calculate RL profile wakeup parameter
309362306a36Sopenharmony_ci * @hw: pointer to the HW struct
309462306a36Sopenharmony_ci * @bw: bandwidth in Kbps
309562306a36Sopenharmony_ci *
309662306a36Sopenharmony_ci * This function calculates the wakeup parameter of RL profile.
309762306a36Sopenharmony_ci */
309862306a36Sopenharmony_cistatic u16 ice_sched_calc_wakeup(struct ice_hw *hw, s32 bw)
309962306a36Sopenharmony_ci{
310062306a36Sopenharmony_ci	s64 bytes_per_sec, wakeup_int, wakeup_a, wakeup_b, wakeup_f;
310162306a36Sopenharmony_ci	s32 wakeup_f_int;
310262306a36Sopenharmony_ci	u16 wakeup = 0;
310362306a36Sopenharmony_ci
310462306a36Sopenharmony_ci	/* Get the wakeup integer value */
310562306a36Sopenharmony_ci	bytes_per_sec = div64_long(((s64)bw * 1000), BITS_PER_BYTE);
310662306a36Sopenharmony_ci	wakeup_int = div64_long(hw->psm_clk_freq, bytes_per_sec);
310762306a36Sopenharmony_ci	if (wakeup_int > 63) {
310862306a36Sopenharmony_ci		wakeup = (u16)((1 << 15) | wakeup_int);
310962306a36Sopenharmony_ci	} else {
311062306a36Sopenharmony_ci		/* Calculate fraction value up to 4 decimals
311162306a36Sopenharmony_ci		 * Convert Integer value to a constant multiplier
311262306a36Sopenharmony_ci		 */
311362306a36Sopenharmony_ci		wakeup_b = (s64)ICE_RL_PROF_MULTIPLIER * wakeup_int;
311462306a36Sopenharmony_ci		wakeup_a = div64_long((s64)ICE_RL_PROF_MULTIPLIER *
311562306a36Sopenharmony_ci					   hw->psm_clk_freq, bytes_per_sec);
311662306a36Sopenharmony_ci
311762306a36Sopenharmony_ci		/* Get Fraction value */
311862306a36Sopenharmony_ci		wakeup_f = wakeup_a - wakeup_b;
311962306a36Sopenharmony_ci
312062306a36Sopenharmony_ci		/* Round up the Fractional value via Ceil(Fractional value) */
312162306a36Sopenharmony_ci		if (wakeup_f > div64_long(ICE_RL_PROF_MULTIPLIER, 2))
312262306a36Sopenharmony_ci			wakeup_f += 1;
312362306a36Sopenharmony_ci
312462306a36Sopenharmony_ci		wakeup_f_int = (s32)div64_long(wakeup_f * ICE_RL_PROF_FRACTION,
312562306a36Sopenharmony_ci					       ICE_RL_PROF_MULTIPLIER);
312662306a36Sopenharmony_ci		wakeup |= (u16)(wakeup_int << 9);
312762306a36Sopenharmony_ci		wakeup |= (u16)(0x1ff & wakeup_f_int);
312862306a36Sopenharmony_ci	}
312962306a36Sopenharmony_ci
313062306a36Sopenharmony_ci	return wakeup;
313162306a36Sopenharmony_ci}
313262306a36Sopenharmony_ci
313362306a36Sopenharmony_ci/**
313462306a36Sopenharmony_ci * ice_sched_bw_to_rl_profile - convert BW to profile parameters
313562306a36Sopenharmony_ci * @hw: pointer to the HW struct
313662306a36Sopenharmony_ci * @bw: bandwidth in Kbps
313762306a36Sopenharmony_ci * @profile: profile parameters to return
313862306a36Sopenharmony_ci *
313962306a36Sopenharmony_ci * This function converts the BW to profile structure format.
314062306a36Sopenharmony_ci */
314162306a36Sopenharmony_cistatic int
314262306a36Sopenharmony_ciice_sched_bw_to_rl_profile(struct ice_hw *hw, u32 bw,
314362306a36Sopenharmony_ci			   struct ice_aqc_rl_profile_elem *profile)
314462306a36Sopenharmony_ci{
314562306a36Sopenharmony_ci	s64 bytes_per_sec, ts_rate, mv_tmp;
314662306a36Sopenharmony_ci	int status = -EINVAL;
314762306a36Sopenharmony_ci	bool found = false;
314862306a36Sopenharmony_ci	s32 encode = 0;
314962306a36Sopenharmony_ci	s64 mv = 0;
315062306a36Sopenharmony_ci	s32 i;
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	/* Bw settings range is from 0.5Mb/sec to 100Gb/sec */
315362306a36Sopenharmony_ci	if (bw < ICE_SCHED_MIN_BW || bw > ICE_SCHED_MAX_BW)
315462306a36Sopenharmony_ci		return status;
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	/* Bytes per second from Kbps */
315762306a36Sopenharmony_ci	bytes_per_sec = div64_long(((s64)bw * 1000), BITS_PER_BYTE);
315862306a36Sopenharmony_ci
315962306a36Sopenharmony_ci	/* encode is 6 bits but really useful are 5 bits */
316062306a36Sopenharmony_ci	for (i = 0; i < 64; i++) {
316162306a36Sopenharmony_ci		u64 pow_result = BIT_ULL(i);
316262306a36Sopenharmony_ci
316362306a36Sopenharmony_ci		ts_rate = div64_long((s64)hw->psm_clk_freq,
316462306a36Sopenharmony_ci				     pow_result * ICE_RL_PROF_TS_MULTIPLIER);
316562306a36Sopenharmony_ci		if (ts_rate <= 0)
316662306a36Sopenharmony_ci			continue;
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ci		/* Multiplier value */
316962306a36Sopenharmony_ci		mv_tmp = div64_long(bytes_per_sec * ICE_RL_PROF_MULTIPLIER,
317062306a36Sopenharmony_ci				    ts_rate);
317162306a36Sopenharmony_ci
317262306a36Sopenharmony_ci		/* Round to the nearest ICE_RL_PROF_MULTIPLIER */
317362306a36Sopenharmony_ci		mv = round_up_64bit(mv_tmp, ICE_RL_PROF_MULTIPLIER);
317462306a36Sopenharmony_ci
317562306a36Sopenharmony_ci		/* First multiplier value greater than the given
317662306a36Sopenharmony_ci		 * accuracy bytes
317762306a36Sopenharmony_ci		 */
317862306a36Sopenharmony_ci		if (mv > ICE_RL_PROF_ACCURACY_BYTES) {
317962306a36Sopenharmony_ci			encode = i;
318062306a36Sopenharmony_ci			found = true;
318162306a36Sopenharmony_ci			break;
318262306a36Sopenharmony_ci		}
318362306a36Sopenharmony_ci	}
318462306a36Sopenharmony_ci	if (found) {
318562306a36Sopenharmony_ci		u16 wm;
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci		wm = ice_sched_calc_wakeup(hw, bw);
318862306a36Sopenharmony_ci		profile->rl_multiply = cpu_to_le16(mv);
318962306a36Sopenharmony_ci		profile->wake_up_calc = cpu_to_le16(wm);
319062306a36Sopenharmony_ci		profile->rl_encode = cpu_to_le16(encode);
319162306a36Sopenharmony_ci		status = 0;
319262306a36Sopenharmony_ci	} else {
319362306a36Sopenharmony_ci		status = -ENOENT;
319462306a36Sopenharmony_ci	}
319562306a36Sopenharmony_ci
319662306a36Sopenharmony_ci	return status;
319762306a36Sopenharmony_ci}
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci/**
320062306a36Sopenharmony_ci * ice_sched_add_rl_profile - add RL profile
320162306a36Sopenharmony_ci * @pi: port information structure
320262306a36Sopenharmony_ci * @rl_type: type of rate limit BW - min, max, or shared
320362306a36Sopenharmony_ci * @bw: bandwidth in Kbps - Kilo bits per sec
320462306a36Sopenharmony_ci * @layer_num: specifies in which layer to create profile
320562306a36Sopenharmony_ci *
320662306a36Sopenharmony_ci * This function first checks the existing list for corresponding BW
320762306a36Sopenharmony_ci * parameter. If it exists, it returns the associated profile otherwise
320862306a36Sopenharmony_ci * it creates a new rate limit profile for requested BW, and adds it to
320962306a36Sopenharmony_ci * the HW DB and local list. It returns the new profile or null on error.
321062306a36Sopenharmony_ci * The caller needs to hold the scheduler lock.
321162306a36Sopenharmony_ci */
321262306a36Sopenharmony_cistatic struct ice_aqc_rl_profile_info *
321362306a36Sopenharmony_ciice_sched_add_rl_profile(struct ice_port_info *pi,
321462306a36Sopenharmony_ci			 enum ice_rl_type rl_type, u32 bw, u8 layer_num)
321562306a36Sopenharmony_ci{
321662306a36Sopenharmony_ci	struct ice_aqc_rl_profile_info *rl_prof_elem;
321762306a36Sopenharmony_ci	u16 profiles_added = 0, num_profiles = 1;
321862306a36Sopenharmony_ci	struct ice_aqc_rl_profile_elem *buf;
321962306a36Sopenharmony_ci	struct ice_hw *hw;
322062306a36Sopenharmony_ci	u8 profile_type;
322162306a36Sopenharmony_ci	int status;
322262306a36Sopenharmony_ci
322362306a36Sopenharmony_ci	if (layer_num >= ICE_AQC_TOPO_MAX_LEVEL_NUM)
322462306a36Sopenharmony_ci		return NULL;
322562306a36Sopenharmony_ci	switch (rl_type) {
322662306a36Sopenharmony_ci	case ICE_MIN_BW:
322762306a36Sopenharmony_ci		profile_type = ICE_AQC_RL_PROFILE_TYPE_CIR;
322862306a36Sopenharmony_ci		break;
322962306a36Sopenharmony_ci	case ICE_MAX_BW:
323062306a36Sopenharmony_ci		profile_type = ICE_AQC_RL_PROFILE_TYPE_EIR;
323162306a36Sopenharmony_ci		break;
323262306a36Sopenharmony_ci	case ICE_SHARED_BW:
323362306a36Sopenharmony_ci		profile_type = ICE_AQC_RL_PROFILE_TYPE_SRL;
323462306a36Sopenharmony_ci		break;
323562306a36Sopenharmony_ci	default:
323662306a36Sopenharmony_ci		return NULL;
323762306a36Sopenharmony_ci	}
323862306a36Sopenharmony_ci
323962306a36Sopenharmony_ci	if (!pi)
324062306a36Sopenharmony_ci		return NULL;
324162306a36Sopenharmony_ci	hw = pi->hw;
324262306a36Sopenharmony_ci	list_for_each_entry(rl_prof_elem, &pi->rl_prof_list[layer_num],
324362306a36Sopenharmony_ci			    list_entry)
324462306a36Sopenharmony_ci		if ((rl_prof_elem->profile.flags & ICE_AQC_RL_PROFILE_TYPE_M) ==
324562306a36Sopenharmony_ci		    profile_type && rl_prof_elem->bw == bw)
324662306a36Sopenharmony_ci			/* Return existing profile ID info */
324762306a36Sopenharmony_ci			return rl_prof_elem;
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_ci	/* Create new profile ID */
325062306a36Sopenharmony_ci	rl_prof_elem = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*rl_prof_elem),
325162306a36Sopenharmony_ci				    GFP_KERNEL);
325262306a36Sopenharmony_ci
325362306a36Sopenharmony_ci	if (!rl_prof_elem)
325462306a36Sopenharmony_ci		return NULL;
325562306a36Sopenharmony_ci
325662306a36Sopenharmony_ci	status = ice_sched_bw_to_rl_profile(hw, bw, &rl_prof_elem->profile);
325762306a36Sopenharmony_ci	if (status)
325862306a36Sopenharmony_ci		goto exit_add_rl_prof;
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci	rl_prof_elem->bw = bw;
326162306a36Sopenharmony_ci	/* layer_num is zero relative, and fw expects level from 1 to 9 */
326262306a36Sopenharmony_ci	rl_prof_elem->profile.level = layer_num + 1;
326362306a36Sopenharmony_ci	rl_prof_elem->profile.flags = profile_type;
326462306a36Sopenharmony_ci	rl_prof_elem->profile.max_burst_size = cpu_to_le16(hw->max_burst_size);
326562306a36Sopenharmony_ci
326662306a36Sopenharmony_ci	/* Create new entry in HW DB */
326762306a36Sopenharmony_ci	buf = &rl_prof_elem->profile;
326862306a36Sopenharmony_ci	status = ice_aq_add_rl_profile(hw, num_profiles, buf, sizeof(*buf),
326962306a36Sopenharmony_ci				       &profiles_added, NULL);
327062306a36Sopenharmony_ci	if (status || profiles_added != num_profiles)
327162306a36Sopenharmony_ci		goto exit_add_rl_prof;
327262306a36Sopenharmony_ci
327362306a36Sopenharmony_ci	/* Good entry - add in the list */
327462306a36Sopenharmony_ci	rl_prof_elem->prof_id_ref = 0;
327562306a36Sopenharmony_ci	list_add(&rl_prof_elem->list_entry, &pi->rl_prof_list[layer_num]);
327662306a36Sopenharmony_ci	return rl_prof_elem;
327762306a36Sopenharmony_ci
327862306a36Sopenharmony_ciexit_add_rl_prof:
327962306a36Sopenharmony_ci	devm_kfree(ice_hw_to_dev(hw), rl_prof_elem);
328062306a36Sopenharmony_ci	return NULL;
328162306a36Sopenharmony_ci}
328262306a36Sopenharmony_ci
328362306a36Sopenharmony_ci/**
328462306a36Sopenharmony_ci * ice_sched_cfg_node_bw_lmt - configure node sched params
328562306a36Sopenharmony_ci * @hw: pointer to the HW struct
328662306a36Sopenharmony_ci * @node: sched node to configure
328762306a36Sopenharmony_ci * @rl_type: rate limit type CIR, EIR, or shared
328862306a36Sopenharmony_ci * @rl_prof_id: rate limit profile ID
328962306a36Sopenharmony_ci *
329062306a36Sopenharmony_ci * This function configures node element's BW limit.
329162306a36Sopenharmony_ci */
329262306a36Sopenharmony_cistatic int
329362306a36Sopenharmony_ciice_sched_cfg_node_bw_lmt(struct ice_hw *hw, struct ice_sched_node *node,
329462306a36Sopenharmony_ci			  enum ice_rl_type rl_type, u16 rl_prof_id)
329562306a36Sopenharmony_ci{
329662306a36Sopenharmony_ci	struct ice_aqc_txsched_elem_data buf;
329762306a36Sopenharmony_ci	struct ice_aqc_txsched_elem *data;
329862306a36Sopenharmony_ci
329962306a36Sopenharmony_ci	buf = node->info;
330062306a36Sopenharmony_ci	data = &buf.data;
330162306a36Sopenharmony_ci	switch (rl_type) {
330262306a36Sopenharmony_ci	case ICE_MIN_BW:
330362306a36Sopenharmony_ci		data->valid_sections |= ICE_AQC_ELEM_VALID_CIR;
330462306a36Sopenharmony_ci		data->cir_bw.bw_profile_idx = cpu_to_le16(rl_prof_id);
330562306a36Sopenharmony_ci		break;
330662306a36Sopenharmony_ci	case ICE_MAX_BW:
330762306a36Sopenharmony_ci		/* EIR BW and Shared BW profiles are mutually exclusive and
330862306a36Sopenharmony_ci		 * hence only one of them may be set for any given element
330962306a36Sopenharmony_ci		 */
331062306a36Sopenharmony_ci		if (data->valid_sections & ICE_AQC_ELEM_VALID_SHARED)
331162306a36Sopenharmony_ci			return -EIO;
331262306a36Sopenharmony_ci		data->valid_sections |= ICE_AQC_ELEM_VALID_EIR;
331362306a36Sopenharmony_ci		data->eir_bw.bw_profile_idx = cpu_to_le16(rl_prof_id);
331462306a36Sopenharmony_ci		break;
331562306a36Sopenharmony_ci	case ICE_SHARED_BW:
331662306a36Sopenharmony_ci		/* Check for removing shared BW */
331762306a36Sopenharmony_ci		if (rl_prof_id == ICE_SCHED_NO_SHARED_RL_PROF_ID) {
331862306a36Sopenharmony_ci			/* remove shared profile */
331962306a36Sopenharmony_ci			data->valid_sections &= ~ICE_AQC_ELEM_VALID_SHARED;
332062306a36Sopenharmony_ci			data->srl_id = 0; /* clear SRL field */
332162306a36Sopenharmony_ci
332262306a36Sopenharmony_ci			/* enable back EIR to default profile */
332362306a36Sopenharmony_ci			data->valid_sections |= ICE_AQC_ELEM_VALID_EIR;
332462306a36Sopenharmony_ci			data->eir_bw.bw_profile_idx =
332562306a36Sopenharmony_ci				cpu_to_le16(ICE_SCHED_DFLT_RL_PROF_ID);
332662306a36Sopenharmony_ci			break;
332762306a36Sopenharmony_ci		}
332862306a36Sopenharmony_ci		/* EIR BW and Shared BW profiles are mutually exclusive and
332962306a36Sopenharmony_ci		 * hence only one of them may be set for any given element
333062306a36Sopenharmony_ci		 */
333162306a36Sopenharmony_ci		if ((data->valid_sections & ICE_AQC_ELEM_VALID_EIR) &&
333262306a36Sopenharmony_ci		    (le16_to_cpu(data->eir_bw.bw_profile_idx) !=
333362306a36Sopenharmony_ci			    ICE_SCHED_DFLT_RL_PROF_ID))
333462306a36Sopenharmony_ci			return -EIO;
333562306a36Sopenharmony_ci		/* EIR BW is set to default, disable it */
333662306a36Sopenharmony_ci		data->valid_sections &= ~ICE_AQC_ELEM_VALID_EIR;
333762306a36Sopenharmony_ci		/* Okay to enable shared BW now */
333862306a36Sopenharmony_ci		data->valid_sections |= ICE_AQC_ELEM_VALID_SHARED;
333962306a36Sopenharmony_ci		data->srl_id = cpu_to_le16(rl_prof_id);
334062306a36Sopenharmony_ci		break;
334162306a36Sopenharmony_ci	default:
334262306a36Sopenharmony_ci		/* Unknown rate limit type */
334362306a36Sopenharmony_ci		return -EINVAL;
334462306a36Sopenharmony_ci	}
334562306a36Sopenharmony_ci
334662306a36Sopenharmony_ci	/* Configure element */
334762306a36Sopenharmony_ci	return ice_sched_update_elem(hw, node, &buf);
334862306a36Sopenharmony_ci}
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci/**
335162306a36Sopenharmony_ci * ice_sched_get_node_rl_prof_id - get node's rate limit profile ID
335262306a36Sopenharmony_ci * @node: sched node
335362306a36Sopenharmony_ci * @rl_type: rate limit type
335462306a36Sopenharmony_ci *
335562306a36Sopenharmony_ci * If existing profile matches, it returns the corresponding rate
335662306a36Sopenharmony_ci * limit profile ID, otherwise it returns an invalid ID as error.
335762306a36Sopenharmony_ci */
335862306a36Sopenharmony_cistatic u16
335962306a36Sopenharmony_ciice_sched_get_node_rl_prof_id(struct ice_sched_node *node,
336062306a36Sopenharmony_ci			      enum ice_rl_type rl_type)
336162306a36Sopenharmony_ci{
336262306a36Sopenharmony_ci	u16 rl_prof_id = ICE_SCHED_INVAL_PROF_ID;
336362306a36Sopenharmony_ci	struct ice_aqc_txsched_elem *data;
336462306a36Sopenharmony_ci
336562306a36Sopenharmony_ci	data = &node->info.data;
336662306a36Sopenharmony_ci	switch (rl_type) {
336762306a36Sopenharmony_ci	case ICE_MIN_BW:
336862306a36Sopenharmony_ci		if (data->valid_sections & ICE_AQC_ELEM_VALID_CIR)
336962306a36Sopenharmony_ci			rl_prof_id = le16_to_cpu(data->cir_bw.bw_profile_idx);
337062306a36Sopenharmony_ci		break;
337162306a36Sopenharmony_ci	case ICE_MAX_BW:
337262306a36Sopenharmony_ci		if (data->valid_sections & ICE_AQC_ELEM_VALID_EIR)
337362306a36Sopenharmony_ci			rl_prof_id = le16_to_cpu(data->eir_bw.bw_profile_idx);
337462306a36Sopenharmony_ci		break;
337562306a36Sopenharmony_ci	case ICE_SHARED_BW:
337662306a36Sopenharmony_ci		if (data->valid_sections & ICE_AQC_ELEM_VALID_SHARED)
337762306a36Sopenharmony_ci			rl_prof_id = le16_to_cpu(data->srl_id);
337862306a36Sopenharmony_ci		break;
337962306a36Sopenharmony_ci	default:
338062306a36Sopenharmony_ci		break;
338162306a36Sopenharmony_ci	}
338262306a36Sopenharmony_ci
338362306a36Sopenharmony_ci	return rl_prof_id;
338462306a36Sopenharmony_ci}
338562306a36Sopenharmony_ci
338662306a36Sopenharmony_ci/**
338762306a36Sopenharmony_ci * ice_sched_get_rl_prof_layer - selects rate limit profile creation layer
338862306a36Sopenharmony_ci * @pi: port information structure
338962306a36Sopenharmony_ci * @rl_type: type of rate limit BW - min, max, or shared
339062306a36Sopenharmony_ci * @layer_index: layer index
339162306a36Sopenharmony_ci *
339262306a36Sopenharmony_ci * This function returns requested profile creation layer.
339362306a36Sopenharmony_ci */
339462306a36Sopenharmony_cistatic u8
339562306a36Sopenharmony_ciice_sched_get_rl_prof_layer(struct ice_port_info *pi, enum ice_rl_type rl_type,
339662306a36Sopenharmony_ci			    u8 layer_index)
339762306a36Sopenharmony_ci{
339862306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ci	if (layer_index >= hw->num_tx_sched_layers)
340162306a36Sopenharmony_ci		return ICE_SCHED_INVAL_LAYER_NUM;
340262306a36Sopenharmony_ci	switch (rl_type) {
340362306a36Sopenharmony_ci	case ICE_MIN_BW:
340462306a36Sopenharmony_ci		if (hw->layer_info[layer_index].max_cir_rl_profiles)
340562306a36Sopenharmony_ci			return layer_index;
340662306a36Sopenharmony_ci		break;
340762306a36Sopenharmony_ci	case ICE_MAX_BW:
340862306a36Sopenharmony_ci		if (hw->layer_info[layer_index].max_eir_rl_profiles)
340962306a36Sopenharmony_ci			return layer_index;
341062306a36Sopenharmony_ci		break;
341162306a36Sopenharmony_ci	case ICE_SHARED_BW:
341262306a36Sopenharmony_ci		/* if current layer doesn't support SRL profile creation
341362306a36Sopenharmony_ci		 * then try a layer up or down.
341462306a36Sopenharmony_ci		 */
341562306a36Sopenharmony_ci		if (hw->layer_info[layer_index].max_srl_profiles)
341662306a36Sopenharmony_ci			return layer_index;
341762306a36Sopenharmony_ci		else if (layer_index < hw->num_tx_sched_layers - 1 &&
341862306a36Sopenharmony_ci			 hw->layer_info[layer_index + 1].max_srl_profiles)
341962306a36Sopenharmony_ci			return layer_index + 1;
342062306a36Sopenharmony_ci		else if (layer_index > 0 &&
342162306a36Sopenharmony_ci			 hw->layer_info[layer_index - 1].max_srl_profiles)
342262306a36Sopenharmony_ci			return layer_index - 1;
342362306a36Sopenharmony_ci		break;
342462306a36Sopenharmony_ci	default:
342562306a36Sopenharmony_ci		break;
342662306a36Sopenharmony_ci	}
342762306a36Sopenharmony_ci	return ICE_SCHED_INVAL_LAYER_NUM;
342862306a36Sopenharmony_ci}
342962306a36Sopenharmony_ci
343062306a36Sopenharmony_ci/**
343162306a36Sopenharmony_ci * ice_sched_get_srl_node - get shared rate limit node
343262306a36Sopenharmony_ci * @node: tree node
343362306a36Sopenharmony_ci * @srl_layer: shared rate limit layer
343462306a36Sopenharmony_ci *
343562306a36Sopenharmony_ci * This function returns SRL node to be used for shared rate limit purpose.
343662306a36Sopenharmony_ci * The caller needs to hold scheduler lock.
343762306a36Sopenharmony_ci */
343862306a36Sopenharmony_cistatic struct ice_sched_node *
343962306a36Sopenharmony_ciice_sched_get_srl_node(struct ice_sched_node *node, u8 srl_layer)
344062306a36Sopenharmony_ci{
344162306a36Sopenharmony_ci	if (srl_layer > node->tx_sched_layer)
344262306a36Sopenharmony_ci		return node->children[0];
344362306a36Sopenharmony_ci	else if (srl_layer < node->tx_sched_layer)
344462306a36Sopenharmony_ci		/* Node can't be created without a parent. It will always
344562306a36Sopenharmony_ci		 * have a valid parent except root node.
344662306a36Sopenharmony_ci		 */
344762306a36Sopenharmony_ci		return node->parent;
344862306a36Sopenharmony_ci	else
344962306a36Sopenharmony_ci		return node;
345062306a36Sopenharmony_ci}
345162306a36Sopenharmony_ci
345262306a36Sopenharmony_ci/**
345362306a36Sopenharmony_ci * ice_sched_rm_rl_profile - remove RL profile ID
345462306a36Sopenharmony_ci * @pi: port information structure
345562306a36Sopenharmony_ci * @layer_num: layer number where profiles are saved
345662306a36Sopenharmony_ci * @profile_type: profile type like EIR, CIR, or SRL
345762306a36Sopenharmony_ci * @profile_id: profile ID to remove
345862306a36Sopenharmony_ci *
345962306a36Sopenharmony_ci * This function removes rate limit profile from layer 'layer_num' of type
346062306a36Sopenharmony_ci * 'profile_type' and profile ID as 'profile_id'. The caller needs to hold
346162306a36Sopenharmony_ci * scheduler lock.
346262306a36Sopenharmony_ci */
346362306a36Sopenharmony_cistatic int
346462306a36Sopenharmony_ciice_sched_rm_rl_profile(struct ice_port_info *pi, u8 layer_num, u8 profile_type,
346562306a36Sopenharmony_ci			u16 profile_id)
346662306a36Sopenharmony_ci{
346762306a36Sopenharmony_ci	struct ice_aqc_rl_profile_info *rl_prof_elem;
346862306a36Sopenharmony_ci	int status = 0;
346962306a36Sopenharmony_ci
347062306a36Sopenharmony_ci	if (layer_num >= ICE_AQC_TOPO_MAX_LEVEL_NUM)
347162306a36Sopenharmony_ci		return -EINVAL;
347262306a36Sopenharmony_ci	/* Check the existing list for RL profile */
347362306a36Sopenharmony_ci	list_for_each_entry(rl_prof_elem, &pi->rl_prof_list[layer_num],
347462306a36Sopenharmony_ci			    list_entry)
347562306a36Sopenharmony_ci		if ((rl_prof_elem->profile.flags & ICE_AQC_RL_PROFILE_TYPE_M) ==
347662306a36Sopenharmony_ci		    profile_type &&
347762306a36Sopenharmony_ci		    le16_to_cpu(rl_prof_elem->profile.profile_id) ==
347862306a36Sopenharmony_ci		    profile_id) {
347962306a36Sopenharmony_ci			if (rl_prof_elem->prof_id_ref)
348062306a36Sopenharmony_ci				rl_prof_elem->prof_id_ref--;
348162306a36Sopenharmony_ci
348262306a36Sopenharmony_ci			/* Remove old profile ID from database */
348362306a36Sopenharmony_ci			status = ice_sched_del_rl_profile(pi->hw, rl_prof_elem);
348462306a36Sopenharmony_ci			if (status && status != -EBUSY)
348562306a36Sopenharmony_ci				ice_debug(pi->hw, ICE_DBG_SCHED, "Remove rl profile failed\n");
348662306a36Sopenharmony_ci			break;
348762306a36Sopenharmony_ci		}
348862306a36Sopenharmony_ci	if (status == -EBUSY)
348962306a36Sopenharmony_ci		status = 0;
349062306a36Sopenharmony_ci	return status;
349162306a36Sopenharmony_ci}
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_ci/**
349462306a36Sopenharmony_ci * ice_sched_set_node_bw_dflt - set node's bandwidth limit to default
349562306a36Sopenharmony_ci * @pi: port information structure
349662306a36Sopenharmony_ci * @node: pointer to node structure
349762306a36Sopenharmony_ci * @rl_type: rate limit type min, max, or shared
349862306a36Sopenharmony_ci * @layer_num: layer number where RL profiles are saved
349962306a36Sopenharmony_ci *
350062306a36Sopenharmony_ci * This function configures node element's BW rate limit profile ID of
350162306a36Sopenharmony_ci * type CIR, EIR, or SRL to default. This function needs to be called
350262306a36Sopenharmony_ci * with the scheduler lock held.
350362306a36Sopenharmony_ci */
350462306a36Sopenharmony_cistatic int
350562306a36Sopenharmony_ciice_sched_set_node_bw_dflt(struct ice_port_info *pi,
350662306a36Sopenharmony_ci			   struct ice_sched_node *node,
350762306a36Sopenharmony_ci			   enum ice_rl_type rl_type, u8 layer_num)
350862306a36Sopenharmony_ci{
350962306a36Sopenharmony_ci	struct ice_hw *hw;
351062306a36Sopenharmony_ci	u8 profile_type;
351162306a36Sopenharmony_ci	u16 rl_prof_id;
351262306a36Sopenharmony_ci	u16 old_id;
351362306a36Sopenharmony_ci	int status;
351462306a36Sopenharmony_ci
351562306a36Sopenharmony_ci	hw = pi->hw;
351662306a36Sopenharmony_ci	switch (rl_type) {
351762306a36Sopenharmony_ci	case ICE_MIN_BW:
351862306a36Sopenharmony_ci		profile_type = ICE_AQC_RL_PROFILE_TYPE_CIR;
351962306a36Sopenharmony_ci		rl_prof_id = ICE_SCHED_DFLT_RL_PROF_ID;
352062306a36Sopenharmony_ci		break;
352162306a36Sopenharmony_ci	case ICE_MAX_BW:
352262306a36Sopenharmony_ci		profile_type = ICE_AQC_RL_PROFILE_TYPE_EIR;
352362306a36Sopenharmony_ci		rl_prof_id = ICE_SCHED_DFLT_RL_PROF_ID;
352462306a36Sopenharmony_ci		break;
352562306a36Sopenharmony_ci	case ICE_SHARED_BW:
352662306a36Sopenharmony_ci		profile_type = ICE_AQC_RL_PROFILE_TYPE_SRL;
352762306a36Sopenharmony_ci		/* No SRL is configured for default case */
352862306a36Sopenharmony_ci		rl_prof_id = ICE_SCHED_NO_SHARED_RL_PROF_ID;
352962306a36Sopenharmony_ci		break;
353062306a36Sopenharmony_ci	default:
353162306a36Sopenharmony_ci		return -EINVAL;
353262306a36Sopenharmony_ci	}
353362306a36Sopenharmony_ci	/* Save existing RL prof ID for later clean up */
353462306a36Sopenharmony_ci	old_id = ice_sched_get_node_rl_prof_id(node, rl_type);
353562306a36Sopenharmony_ci	/* Configure BW scheduling parameters */
353662306a36Sopenharmony_ci	status = ice_sched_cfg_node_bw_lmt(hw, node, rl_type, rl_prof_id);
353762306a36Sopenharmony_ci	if (status)
353862306a36Sopenharmony_ci		return status;
353962306a36Sopenharmony_ci
354062306a36Sopenharmony_ci	/* Remove stale RL profile ID */
354162306a36Sopenharmony_ci	if (old_id == ICE_SCHED_DFLT_RL_PROF_ID ||
354262306a36Sopenharmony_ci	    old_id == ICE_SCHED_INVAL_PROF_ID)
354362306a36Sopenharmony_ci		return 0;
354462306a36Sopenharmony_ci
354562306a36Sopenharmony_ci	return ice_sched_rm_rl_profile(pi, layer_num, profile_type, old_id);
354662306a36Sopenharmony_ci}
354762306a36Sopenharmony_ci
354862306a36Sopenharmony_ci/**
354962306a36Sopenharmony_ci * ice_sched_set_eir_srl_excl - set EIR/SRL exclusiveness
355062306a36Sopenharmony_ci * @pi: port information structure
355162306a36Sopenharmony_ci * @node: pointer to node structure
355262306a36Sopenharmony_ci * @layer_num: layer number where rate limit profiles are saved
355362306a36Sopenharmony_ci * @rl_type: rate limit type min, max, or shared
355462306a36Sopenharmony_ci * @bw: bandwidth value
355562306a36Sopenharmony_ci *
355662306a36Sopenharmony_ci * This function prepares node element's bandwidth to SRL or EIR exclusively.
355762306a36Sopenharmony_ci * EIR BW and Shared BW profiles are mutually exclusive and hence only one of
355862306a36Sopenharmony_ci * them may be set for any given element. This function needs to be called
355962306a36Sopenharmony_ci * with the scheduler lock held.
356062306a36Sopenharmony_ci */
356162306a36Sopenharmony_cistatic int
356262306a36Sopenharmony_ciice_sched_set_eir_srl_excl(struct ice_port_info *pi,
356362306a36Sopenharmony_ci			   struct ice_sched_node *node,
356462306a36Sopenharmony_ci			   u8 layer_num, enum ice_rl_type rl_type, u32 bw)
356562306a36Sopenharmony_ci{
356662306a36Sopenharmony_ci	if (rl_type == ICE_SHARED_BW) {
356762306a36Sopenharmony_ci		/* SRL node passed in this case, it may be different node */
356862306a36Sopenharmony_ci		if (bw == ICE_SCHED_DFLT_BW)
356962306a36Sopenharmony_ci			/* SRL being removed, ice_sched_cfg_node_bw_lmt()
357062306a36Sopenharmony_ci			 * enables EIR to default. EIR is not set in this
357162306a36Sopenharmony_ci			 * case, so no additional action is required.
357262306a36Sopenharmony_ci			 */
357362306a36Sopenharmony_ci			return 0;
357462306a36Sopenharmony_ci
357562306a36Sopenharmony_ci		/* SRL being configured, set EIR to default here.
357662306a36Sopenharmony_ci		 * ice_sched_cfg_node_bw_lmt() disables EIR when it
357762306a36Sopenharmony_ci		 * configures SRL
357862306a36Sopenharmony_ci		 */
357962306a36Sopenharmony_ci		return ice_sched_set_node_bw_dflt(pi, node, ICE_MAX_BW,
358062306a36Sopenharmony_ci						  layer_num);
358162306a36Sopenharmony_ci	} else if (rl_type == ICE_MAX_BW &&
358262306a36Sopenharmony_ci		   node->info.data.valid_sections & ICE_AQC_ELEM_VALID_SHARED) {
358362306a36Sopenharmony_ci		/* Remove Shared profile. Set default shared BW call
358462306a36Sopenharmony_ci		 * removes shared profile for a node.
358562306a36Sopenharmony_ci		 */
358662306a36Sopenharmony_ci		return ice_sched_set_node_bw_dflt(pi, node,
358762306a36Sopenharmony_ci						  ICE_SHARED_BW,
358862306a36Sopenharmony_ci						  layer_num);
358962306a36Sopenharmony_ci	}
359062306a36Sopenharmony_ci	return 0;
359162306a36Sopenharmony_ci}
359262306a36Sopenharmony_ci
359362306a36Sopenharmony_ci/**
359462306a36Sopenharmony_ci * ice_sched_set_node_bw - set node's bandwidth
359562306a36Sopenharmony_ci * @pi: port information structure
359662306a36Sopenharmony_ci * @node: tree node
359762306a36Sopenharmony_ci * @rl_type: rate limit type min, max, or shared
359862306a36Sopenharmony_ci * @bw: bandwidth in Kbps - Kilo bits per sec
359962306a36Sopenharmony_ci * @layer_num: layer number
360062306a36Sopenharmony_ci *
360162306a36Sopenharmony_ci * This function adds new profile corresponding to requested BW, configures
360262306a36Sopenharmony_ci * node's RL profile ID of type CIR, EIR, or SRL, and removes old profile
360362306a36Sopenharmony_ci * ID from local database. The caller needs to hold scheduler lock.
360462306a36Sopenharmony_ci */
360562306a36Sopenharmony_ciint
360662306a36Sopenharmony_ciice_sched_set_node_bw(struct ice_port_info *pi, struct ice_sched_node *node,
360762306a36Sopenharmony_ci		      enum ice_rl_type rl_type, u32 bw, u8 layer_num)
360862306a36Sopenharmony_ci{
360962306a36Sopenharmony_ci	struct ice_aqc_rl_profile_info *rl_prof_info;
361062306a36Sopenharmony_ci	struct ice_hw *hw = pi->hw;
361162306a36Sopenharmony_ci	u16 old_id, rl_prof_id;
361262306a36Sopenharmony_ci	int status = -EINVAL;
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ci	rl_prof_info = ice_sched_add_rl_profile(pi, rl_type, bw, layer_num);
361562306a36Sopenharmony_ci	if (!rl_prof_info)
361662306a36Sopenharmony_ci		return status;
361762306a36Sopenharmony_ci
361862306a36Sopenharmony_ci	rl_prof_id = le16_to_cpu(rl_prof_info->profile.profile_id);
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci	/* Save existing RL prof ID for later clean up */
362162306a36Sopenharmony_ci	old_id = ice_sched_get_node_rl_prof_id(node, rl_type);
362262306a36Sopenharmony_ci	/* Configure BW scheduling parameters */
362362306a36Sopenharmony_ci	status = ice_sched_cfg_node_bw_lmt(hw, node, rl_type, rl_prof_id);
362462306a36Sopenharmony_ci	if (status)
362562306a36Sopenharmony_ci		return status;
362662306a36Sopenharmony_ci
362762306a36Sopenharmony_ci	/* New changes has been applied */
362862306a36Sopenharmony_ci	/* Increment the profile ID reference count */
362962306a36Sopenharmony_ci	rl_prof_info->prof_id_ref++;
363062306a36Sopenharmony_ci
363162306a36Sopenharmony_ci	/* Check for old ID removal */
363262306a36Sopenharmony_ci	if ((old_id == ICE_SCHED_DFLT_RL_PROF_ID && rl_type != ICE_SHARED_BW) ||
363362306a36Sopenharmony_ci	    old_id == ICE_SCHED_INVAL_PROF_ID || old_id == rl_prof_id)
363462306a36Sopenharmony_ci		return 0;
363562306a36Sopenharmony_ci
363662306a36Sopenharmony_ci	return ice_sched_rm_rl_profile(pi, layer_num,
363762306a36Sopenharmony_ci				       rl_prof_info->profile.flags &
363862306a36Sopenharmony_ci				       ICE_AQC_RL_PROFILE_TYPE_M, old_id);
363962306a36Sopenharmony_ci}
364062306a36Sopenharmony_ci
364162306a36Sopenharmony_ci/**
364262306a36Sopenharmony_ci * ice_sched_set_node_priority - set node's priority
364362306a36Sopenharmony_ci * @pi: port information structure
364462306a36Sopenharmony_ci * @node: tree node
364562306a36Sopenharmony_ci * @priority: number 0-7 representing priority among siblings
364662306a36Sopenharmony_ci *
364762306a36Sopenharmony_ci * This function sets priority of a node among it's siblings.
364862306a36Sopenharmony_ci */
364962306a36Sopenharmony_ciint
365062306a36Sopenharmony_ciice_sched_set_node_priority(struct ice_port_info *pi, struct ice_sched_node *node,
365162306a36Sopenharmony_ci			    u16 priority)
365262306a36Sopenharmony_ci{
365362306a36Sopenharmony_ci	struct ice_aqc_txsched_elem_data buf;
365462306a36Sopenharmony_ci	struct ice_aqc_txsched_elem *data;
365562306a36Sopenharmony_ci
365662306a36Sopenharmony_ci	buf = node->info;
365762306a36Sopenharmony_ci	data = &buf.data;
365862306a36Sopenharmony_ci
365962306a36Sopenharmony_ci	data->valid_sections |= ICE_AQC_ELEM_VALID_GENERIC;
366062306a36Sopenharmony_ci	data->generic |= FIELD_PREP(ICE_AQC_ELEM_GENERIC_PRIO_M, priority);
366162306a36Sopenharmony_ci
366262306a36Sopenharmony_ci	return ice_sched_update_elem(pi->hw, node, &buf);
366362306a36Sopenharmony_ci}
366462306a36Sopenharmony_ci
366562306a36Sopenharmony_ci/**
366662306a36Sopenharmony_ci * ice_sched_set_node_weight - set node's weight
366762306a36Sopenharmony_ci * @pi: port information structure
366862306a36Sopenharmony_ci * @node: tree node
366962306a36Sopenharmony_ci * @weight: number 1-200 representing weight for WFQ
367062306a36Sopenharmony_ci *
367162306a36Sopenharmony_ci * This function sets weight of the node for WFQ algorithm.
367262306a36Sopenharmony_ci */
367362306a36Sopenharmony_ciint
367462306a36Sopenharmony_ciice_sched_set_node_weight(struct ice_port_info *pi, struct ice_sched_node *node, u16 weight)
367562306a36Sopenharmony_ci{
367662306a36Sopenharmony_ci	struct ice_aqc_txsched_elem_data buf;
367762306a36Sopenharmony_ci	struct ice_aqc_txsched_elem *data;
367862306a36Sopenharmony_ci
367962306a36Sopenharmony_ci	buf = node->info;
368062306a36Sopenharmony_ci	data = &buf.data;
368162306a36Sopenharmony_ci
368262306a36Sopenharmony_ci	data->valid_sections = ICE_AQC_ELEM_VALID_CIR | ICE_AQC_ELEM_VALID_EIR |
368362306a36Sopenharmony_ci			       ICE_AQC_ELEM_VALID_GENERIC;
368462306a36Sopenharmony_ci	data->cir_bw.bw_alloc = cpu_to_le16(weight);
368562306a36Sopenharmony_ci	data->eir_bw.bw_alloc = cpu_to_le16(weight);
368662306a36Sopenharmony_ci
368762306a36Sopenharmony_ci	data->generic |= FIELD_PREP(ICE_AQC_ELEM_GENERIC_SP_M, 0x0);
368862306a36Sopenharmony_ci
368962306a36Sopenharmony_ci	return ice_sched_update_elem(pi->hw, node, &buf);
369062306a36Sopenharmony_ci}
369162306a36Sopenharmony_ci
369262306a36Sopenharmony_ci/**
369362306a36Sopenharmony_ci * ice_sched_set_node_bw_lmt - set node's BW limit
369462306a36Sopenharmony_ci * @pi: port information structure
369562306a36Sopenharmony_ci * @node: tree node
369662306a36Sopenharmony_ci * @rl_type: rate limit type min, max, or shared
369762306a36Sopenharmony_ci * @bw: bandwidth in Kbps - Kilo bits per sec
369862306a36Sopenharmony_ci *
369962306a36Sopenharmony_ci * It updates node's BW limit parameters like BW RL profile ID of type CIR,
370062306a36Sopenharmony_ci * EIR, or SRL. The caller needs to hold scheduler lock.
370162306a36Sopenharmony_ci */
370262306a36Sopenharmony_ciint
370362306a36Sopenharmony_ciice_sched_set_node_bw_lmt(struct ice_port_info *pi, struct ice_sched_node *node,
370462306a36Sopenharmony_ci			  enum ice_rl_type rl_type, u32 bw)
370562306a36Sopenharmony_ci{
370662306a36Sopenharmony_ci	struct ice_sched_node *cfg_node = node;
370762306a36Sopenharmony_ci	int status;
370862306a36Sopenharmony_ci
370962306a36Sopenharmony_ci	struct ice_hw *hw;
371062306a36Sopenharmony_ci	u8 layer_num;
371162306a36Sopenharmony_ci
371262306a36Sopenharmony_ci	if (!pi)
371362306a36Sopenharmony_ci		return -EINVAL;
371462306a36Sopenharmony_ci	hw = pi->hw;
371562306a36Sopenharmony_ci	/* Remove unused RL profile IDs from HW and SW DB */
371662306a36Sopenharmony_ci	ice_sched_rm_unused_rl_prof(pi);
371762306a36Sopenharmony_ci	layer_num = ice_sched_get_rl_prof_layer(pi, rl_type,
371862306a36Sopenharmony_ci						node->tx_sched_layer);
371962306a36Sopenharmony_ci	if (layer_num >= hw->num_tx_sched_layers)
372062306a36Sopenharmony_ci		return -EINVAL;
372162306a36Sopenharmony_ci
372262306a36Sopenharmony_ci	if (rl_type == ICE_SHARED_BW) {
372362306a36Sopenharmony_ci		/* SRL node may be different */
372462306a36Sopenharmony_ci		cfg_node = ice_sched_get_srl_node(node, layer_num);
372562306a36Sopenharmony_ci		if (!cfg_node)
372662306a36Sopenharmony_ci			return -EIO;
372762306a36Sopenharmony_ci	}
372862306a36Sopenharmony_ci	/* EIR BW and Shared BW profiles are mutually exclusive and
372962306a36Sopenharmony_ci	 * hence only one of them may be set for any given element
373062306a36Sopenharmony_ci	 */
373162306a36Sopenharmony_ci	status = ice_sched_set_eir_srl_excl(pi, cfg_node, layer_num, rl_type,
373262306a36Sopenharmony_ci					    bw);
373362306a36Sopenharmony_ci	if (status)
373462306a36Sopenharmony_ci		return status;
373562306a36Sopenharmony_ci	if (bw == ICE_SCHED_DFLT_BW)
373662306a36Sopenharmony_ci		return ice_sched_set_node_bw_dflt(pi, cfg_node, rl_type,
373762306a36Sopenharmony_ci						  layer_num);
373862306a36Sopenharmony_ci	return ice_sched_set_node_bw(pi, cfg_node, rl_type, bw, layer_num);
373962306a36Sopenharmony_ci}
374062306a36Sopenharmony_ci
374162306a36Sopenharmony_ci/**
374262306a36Sopenharmony_ci * ice_sched_set_node_bw_dflt_lmt - set node's BW limit to default
374362306a36Sopenharmony_ci * @pi: port information structure
374462306a36Sopenharmony_ci * @node: pointer to node structure
374562306a36Sopenharmony_ci * @rl_type: rate limit type min, max, or shared
374662306a36Sopenharmony_ci *
374762306a36Sopenharmony_ci * This function configures node element's BW rate limit profile ID of
374862306a36Sopenharmony_ci * type CIR, EIR, or SRL to default. This function needs to be called
374962306a36Sopenharmony_ci * with the scheduler lock held.
375062306a36Sopenharmony_ci */
375162306a36Sopenharmony_cistatic int
375262306a36Sopenharmony_ciice_sched_set_node_bw_dflt_lmt(struct ice_port_info *pi,
375362306a36Sopenharmony_ci			       struct ice_sched_node *node,
375462306a36Sopenharmony_ci			       enum ice_rl_type rl_type)
375562306a36Sopenharmony_ci{
375662306a36Sopenharmony_ci	return ice_sched_set_node_bw_lmt(pi, node, rl_type,
375762306a36Sopenharmony_ci					 ICE_SCHED_DFLT_BW);
375862306a36Sopenharmony_ci}
375962306a36Sopenharmony_ci
376062306a36Sopenharmony_ci/**
376162306a36Sopenharmony_ci * ice_sched_validate_srl_node - Check node for SRL applicability
376262306a36Sopenharmony_ci * @node: sched node to configure
376362306a36Sopenharmony_ci * @sel_layer: selected SRL layer
376462306a36Sopenharmony_ci *
376562306a36Sopenharmony_ci * This function checks if the SRL can be applied to a selected layer node on
376662306a36Sopenharmony_ci * behalf of the requested node (first argument). This function needs to be
376762306a36Sopenharmony_ci * called with scheduler lock held.
376862306a36Sopenharmony_ci */
376962306a36Sopenharmony_cistatic int
377062306a36Sopenharmony_ciice_sched_validate_srl_node(struct ice_sched_node *node, u8 sel_layer)
377162306a36Sopenharmony_ci{
377262306a36Sopenharmony_ci	/* SRL profiles are not available on all layers. Check if the
377362306a36Sopenharmony_ci	 * SRL profile can be applied to a node above or below the
377462306a36Sopenharmony_ci	 * requested node. SRL configuration is possible only if the
377562306a36Sopenharmony_ci	 * selected layer's node has single child.
377662306a36Sopenharmony_ci	 */
377762306a36Sopenharmony_ci	if (sel_layer == node->tx_sched_layer ||
377862306a36Sopenharmony_ci	    ((sel_layer == node->tx_sched_layer + 1) &&
377962306a36Sopenharmony_ci	    node->num_children == 1) ||
378062306a36Sopenharmony_ci	    ((sel_layer == node->tx_sched_layer - 1) &&
378162306a36Sopenharmony_ci	    (node->parent && node->parent->num_children == 1)))
378262306a36Sopenharmony_ci		return 0;
378362306a36Sopenharmony_ci
378462306a36Sopenharmony_ci	return -EIO;
378562306a36Sopenharmony_ci}
378662306a36Sopenharmony_ci
378762306a36Sopenharmony_ci/**
378862306a36Sopenharmony_ci * ice_sched_save_q_bw - save queue node's BW information
378962306a36Sopenharmony_ci * @q_ctx: queue context structure
379062306a36Sopenharmony_ci * @rl_type: rate limit type min, max, or shared
379162306a36Sopenharmony_ci * @bw: bandwidth in Kbps - Kilo bits per sec
379262306a36Sopenharmony_ci *
379362306a36Sopenharmony_ci * Save BW information of queue type node for post replay use.
379462306a36Sopenharmony_ci */
379562306a36Sopenharmony_cistatic int
379662306a36Sopenharmony_ciice_sched_save_q_bw(struct ice_q_ctx *q_ctx, enum ice_rl_type rl_type, u32 bw)
379762306a36Sopenharmony_ci{
379862306a36Sopenharmony_ci	switch (rl_type) {
379962306a36Sopenharmony_ci	case ICE_MIN_BW:
380062306a36Sopenharmony_ci		ice_set_clear_cir_bw(&q_ctx->bw_t_info, bw);
380162306a36Sopenharmony_ci		break;
380262306a36Sopenharmony_ci	case ICE_MAX_BW:
380362306a36Sopenharmony_ci		ice_set_clear_eir_bw(&q_ctx->bw_t_info, bw);
380462306a36Sopenharmony_ci		break;
380562306a36Sopenharmony_ci	case ICE_SHARED_BW:
380662306a36Sopenharmony_ci		ice_set_clear_shared_bw(&q_ctx->bw_t_info, bw);
380762306a36Sopenharmony_ci		break;
380862306a36Sopenharmony_ci	default:
380962306a36Sopenharmony_ci		return -EINVAL;
381062306a36Sopenharmony_ci	}
381162306a36Sopenharmony_ci	return 0;
381262306a36Sopenharmony_ci}
381362306a36Sopenharmony_ci
381462306a36Sopenharmony_ci/**
381562306a36Sopenharmony_ci * ice_sched_set_q_bw_lmt - sets queue BW limit
381662306a36Sopenharmony_ci * @pi: port information structure
381762306a36Sopenharmony_ci * @vsi_handle: sw VSI handle
381862306a36Sopenharmony_ci * @tc: traffic class
381962306a36Sopenharmony_ci * @q_handle: software queue handle
382062306a36Sopenharmony_ci * @rl_type: min, max, or shared
382162306a36Sopenharmony_ci * @bw: bandwidth in Kbps
382262306a36Sopenharmony_ci *
382362306a36Sopenharmony_ci * This function sets BW limit of queue scheduling node.
382462306a36Sopenharmony_ci */
382562306a36Sopenharmony_cistatic int
382662306a36Sopenharmony_ciice_sched_set_q_bw_lmt(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
382762306a36Sopenharmony_ci		       u16 q_handle, enum ice_rl_type rl_type, u32 bw)
382862306a36Sopenharmony_ci{
382962306a36Sopenharmony_ci	struct ice_sched_node *node;
383062306a36Sopenharmony_ci	struct ice_q_ctx *q_ctx;
383162306a36Sopenharmony_ci	int status = -EINVAL;
383262306a36Sopenharmony_ci
383362306a36Sopenharmony_ci	if (!ice_is_vsi_valid(pi->hw, vsi_handle))
383462306a36Sopenharmony_ci		return -EINVAL;
383562306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
383662306a36Sopenharmony_ci	q_ctx = ice_get_lan_q_ctx(pi->hw, vsi_handle, tc, q_handle);
383762306a36Sopenharmony_ci	if (!q_ctx)
383862306a36Sopenharmony_ci		goto exit_q_bw_lmt;
383962306a36Sopenharmony_ci	node = ice_sched_find_node_by_teid(pi->root, q_ctx->q_teid);
384062306a36Sopenharmony_ci	if (!node) {
384162306a36Sopenharmony_ci		ice_debug(pi->hw, ICE_DBG_SCHED, "Wrong q_teid\n");
384262306a36Sopenharmony_ci		goto exit_q_bw_lmt;
384362306a36Sopenharmony_ci	}
384462306a36Sopenharmony_ci
384562306a36Sopenharmony_ci	/* Return error if it is not a leaf node */
384662306a36Sopenharmony_ci	if (node->info.data.elem_type != ICE_AQC_ELEM_TYPE_LEAF)
384762306a36Sopenharmony_ci		goto exit_q_bw_lmt;
384862306a36Sopenharmony_ci
384962306a36Sopenharmony_ci	/* SRL bandwidth layer selection */
385062306a36Sopenharmony_ci	if (rl_type == ICE_SHARED_BW) {
385162306a36Sopenharmony_ci		u8 sel_layer; /* selected layer */
385262306a36Sopenharmony_ci
385362306a36Sopenharmony_ci		sel_layer = ice_sched_get_rl_prof_layer(pi, rl_type,
385462306a36Sopenharmony_ci							node->tx_sched_layer);
385562306a36Sopenharmony_ci		if (sel_layer >= pi->hw->num_tx_sched_layers) {
385662306a36Sopenharmony_ci			status = -EINVAL;
385762306a36Sopenharmony_ci			goto exit_q_bw_lmt;
385862306a36Sopenharmony_ci		}
385962306a36Sopenharmony_ci		status = ice_sched_validate_srl_node(node, sel_layer);
386062306a36Sopenharmony_ci		if (status)
386162306a36Sopenharmony_ci			goto exit_q_bw_lmt;
386262306a36Sopenharmony_ci	}
386362306a36Sopenharmony_ci
386462306a36Sopenharmony_ci	if (bw == ICE_SCHED_DFLT_BW)
386562306a36Sopenharmony_ci		status = ice_sched_set_node_bw_dflt_lmt(pi, node, rl_type);
386662306a36Sopenharmony_ci	else
386762306a36Sopenharmony_ci		status = ice_sched_set_node_bw_lmt(pi, node, rl_type, bw);
386862306a36Sopenharmony_ci
386962306a36Sopenharmony_ci	if (!status)
387062306a36Sopenharmony_ci		status = ice_sched_save_q_bw(q_ctx, rl_type, bw);
387162306a36Sopenharmony_ci
387262306a36Sopenharmony_ciexit_q_bw_lmt:
387362306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
387462306a36Sopenharmony_ci	return status;
387562306a36Sopenharmony_ci}
387662306a36Sopenharmony_ci
387762306a36Sopenharmony_ci/**
387862306a36Sopenharmony_ci * ice_cfg_q_bw_lmt - configure queue BW limit
387962306a36Sopenharmony_ci * @pi: port information structure
388062306a36Sopenharmony_ci * @vsi_handle: sw VSI handle
388162306a36Sopenharmony_ci * @tc: traffic class
388262306a36Sopenharmony_ci * @q_handle: software queue handle
388362306a36Sopenharmony_ci * @rl_type: min, max, or shared
388462306a36Sopenharmony_ci * @bw: bandwidth in Kbps
388562306a36Sopenharmony_ci *
388662306a36Sopenharmony_ci * This function configures BW limit of queue scheduling node.
388762306a36Sopenharmony_ci */
388862306a36Sopenharmony_ciint
388962306a36Sopenharmony_ciice_cfg_q_bw_lmt(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
389062306a36Sopenharmony_ci		 u16 q_handle, enum ice_rl_type rl_type, u32 bw)
389162306a36Sopenharmony_ci{
389262306a36Sopenharmony_ci	return ice_sched_set_q_bw_lmt(pi, vsi_handle, tc, q_handle, rl_type,
389362306a36Sopenharmony_ci				      bw);
389462306a36Sopenharmony_ci}
389562306a36Sopenharmony_ci
389662306a36Sopenharmony_ci/**
389762306a36Sopenharmony_ci * ice_cfg_q_bw_dflt_lmt - configure queue BW default limit
389862306a36Sopenharmony_ci * @pi: port information structure
389962306a36Sopenharmony_ci * @vsi_handle: sw VSI handle
390062306a36Sopenharmony_ci * @tc: traffic class
390162306a36Sopenharmony_ci * @q_handle: software queue handle
390262306a36Sopenharmony_ci * @rl_type: min, max, or shared
390362306a36Sopenharmony_ci *
390462306a36Sopenharmony_ci * This function configures BW default limit of queue scheduling node.
390562306a36Sopenharmony_ci */
390662306a36Sopenharmony_ciint
390762306a36Sopenharmony_ciice_cfg_q_bw_dflt_lmt(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
390862306a36Sopenharmony_ci		      u16 q_handle, enum ice_rl_type rl_type)
390962306a36Sopenharmony_ci{
391062306a36Sopenharmony_ci	return ice_sched_set_q_bw_lmt(pi, vsi_handle, tc, q_handle, rl_type,
391162306a36Sopenharmony_ci				      ICE_SCHED_DFLT_BW);
391262306a36Sopenharmony_ci}
391362306a36Sopenharmony_ci
391462306a36Sopenharmony_ci/**
391562306a36Sopenharmony_ci * ice_sched_get_node_by_id_type - get node from ID type
391662306a36Sopenharmony_ci * @pi: port information structure
391762306a36Sopenharmony_ci * @id: identifier
391862306a36Sopenharmony_ci * @agg_type: type of aggregator
391962306a36Sopenharmony_ci * @tc: traffic class
392062306a36Sopenharmony_ci *
392162306a36Sopenharmony_ci * This function returns node identified by ID of type aggregator, and
392262306a36Sopenharmony_ci * based on traffic class (TC). This function needs to be called with
392362306a36Sopenharmony_ci * the scheduler lock held.
392462306a36Sopenharmony_ci */
392562306a36Sopenharmony_cistatic struct ice_sched_node *
392662306a36Sopenharmony_ciice_sched_get_node_by_id_type(struct ice_port_info *pi, u32 id,
392762306a36Sopenharmony_ci			      enum ice_agg_type agg_type, u8 tc)
392862306a36Sopenharmony_ci{
392962306a36Sopenharmony_ci	struct ice_sched_node *node = NULL;
393062306a36Sopenharmony_ci
393162306a36Sopenharmony_ci	switch (agg_type) {
393262306a36Sopenharmony_ci	case ICE_AGG_TYPE_VSI: {
393362306a36Sopenharmony_ci		struct ice_vsi_ctx *vsi_ctx;
393462306a36Sopenharmony_ci		u16 vsi_handle = (u16)id;
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci		if (!ice_is_vsi_valid(pi->hw, vsi_handle))
393762306a36Sopenharmony_ci			break;
393862306a36Sopenharmony_ci		/* Get sched_vsi_info */
393962306a36Sopenharmony_ci		vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
394062306a36Sopenharmony_ci		if (!vsi_ctx)
394162306a36Sopenharmony_ci			break;
394262306a36Sopenharmony_ci		node = vsi_ctx->sched.vsi_node[tc];
394362306a36Sopenharmony_ci		break;
394462306a36Sopenharmony_ci	}
394562306a36Sopenharmony_ci
394662306a36Sopenharmony_ci	case ICE_AGG_TYPE_AGG: {
394762306a36Sopenharmony_ci		struct ice_sched_node *tc_node;
394862306a36Sopenharmony_ci
394962306a36Sopenharmony_ci		tc_node = ice_sched_get_tc_node(pi, tc);
395062306a36Sopenharmony_ci		if (tc_node)
395162306a36Sopenharmony_ci			node = ice_sched_get_agg_node(pi, tc_node, id);
395262306a36Sopenharmony_ci		break;
395362306a36Sopenharmony_ci	}
395462306a36Sopenharmony_ci
395562306a36Sopenharmony_ci	default:
395662306a36Sopenharmony_ci		break;
395762306a36Sopenharmony_ci	}
395862306a36Sopenharmony_ci
395962306a36Sopenharmony_ci	return node;
396062306a36Sopenharmony_ci}
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_ci/**
396362306a36Sopenharmony_ci * ice_sched_set_node_bw_lmt_per_tc - set node BW limit per TC
396462306a36Sopenharmony_ci * @pi: port information structure
396562306a36Sopenharmony_ci * @id: ID (software VSI handle or AGG ID)
396662306a36Sopenharmony_ci * @agg_type: aggregator type (VSI or AGG type node)
396762306a36Sopenharmony_ci * @tc: traffic class
396862306a36Sopenharmony_ci * @rl_type: min or max
396962306a36Sopenharmony_ci * @bw: bandwidth in Kbps
397062306a36Sopenharmony_ci *
397162306a36Sopenharmony_ci * This function sets BW limit of VSI or Aggregator scheduling node
397262306a36Sopenharmony_ci * based on TC information from passed in argument BW.
397362306a36Sopenharmony_ci */
397462306a36Sopenharmony_cistatic int
397562306a36Sopenharmony_ciice_sched_set_node_bw_lmt_per_tc(struct ice_port_info *pi, u32 id,
397662306a36Sopenharmony_ci				 enum ice_agg_type agg_type, u8 tc,
397762306a36Sopenharmony_ci				 enum ice_rl_type rl_type, u32 bw)
397862306a36Sopenharmony_ci{
397962306a36Sopenharmony_ci	struct ice_sched_node *node;
398062306a36Sopenharmony_ci	int status = -EINVAL;
398162306a36Sopenharmony_ci
398262306a36Sopenharmony_ci	if (!pi)
398362306a36Sopenharmony_ci		return status;
398462306a36Sopenharmony_ci
398562306a36Sopenharmony_ci	if (rl_type == ICE_UNKNOWN_BW)
398662306a36Sopenharmony_ci		return status;
398762306a36Sopenharmony_ci
398862306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
398962306a36Sopenharmony_ci	node = ice_sched_get_node_by_id_type(pi, id, agg_type, tc);
399062306a36Sopenharmony_ci	if (!node) {
399162306a36Sopenharmony_ci		ice_debug(pi->hw, ICE_DBG_SCHED, "Wrong id, agg type, or tc\n");
399262306a36Sopenharmony_ci		goto exit_set_node_bw_lmt_per_tc;
399362306a36Sopenharmony_ci	}
399462306a36Sopenharmony_ci	if (bw == ICE_SCHED_DFLT_BW)
399562306a36Sopenharmony_ci		status = ice_sched_set_node_bw_dflt_lmt(pi, node, rl_type);
399662306a36Sopenharmony_ci	else
399762306a36Sopenharmony_ci		status = ice_sched_set_node_bw_lmt(pi, node, rl_type, bw);
399862306a36Sopenharmony_ci
399962306a36Sopenharmony_ciexit_set_node_bw_lmt_per_tc:
400062306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
400162306a36Sopenharmony_ci	return status;
400262306a36Sopenharmony_ci}
400362306a36Sopenharmony_ci
400462306a36Sopenharmony_ci/**
400562306a36Sopenharmony_ci * ice_cfg_vsi_bw_lmt_per_tc - configure VSI BW limit per TC
400662306a36Sopenharmony_ci * @pi: port information structure
400762306a36Sopenharmony_ci * @vsi_handle: software VSI handle
400862306a36Sopenharmony_ci * @tc: traffic class
400962306a36Sopenharmony_ci * @rl_type: min or max
401062306a36Sopenharmony_ci * @bw: bandwidth in Kbps
401162306a36Sopenharmony_ci *
401262306a36Sopenharmony_ci * This function configures BW limit of VSI scheduling node based on TC
401362306a36Sopenharmony_ci * information.
401462306a36Sopenharmony_ci */
401562306a36Sopenharmony_ciint
401662306a36Sopenharmony_ciice_cfg_vsi_bw_lmt_per_tc(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
401762306a36Sopenharmony_ci			  enum ice_rl_type rl_type, u32 bw)
401862306a36Sopenharmony_ci{
401962306a36Sopenharmony_ci	int status;
402062306a36Sopenharmony_ci
402162306a36Sopenharmony_ci	status = ice_sched_set_node_bw_lmt_per_tc(pi, vsi_handle,
402262306a36Sopenharmony_ci						  ICE_AGG_TYPE_VSI,
402362306a36Sopenharmony_ci						  tc, rl_type, bw);
402462306a36Sopenharmony_ci	if (!status) {
402562306a36Sopenharmony_ci		mutex_lock(&pi->sched_lock);
402662306a36Sopenharmony_ci		status = ice_sched_save_vsi_bw(pi, vsi_handle, tc, rl_type, bw);
402762306a36Sopenharmony_ci		mutex_unlock(&pi->sched_lock);
402862306a36Sopenharmony_ci	}
402962306a36Sopenharmony_ci	return status;
403062306a36Sopenharmony_ci}
403162306a36Sopenharmony_ci
403262306a36Sopenharmony_ci/**
403362306a36Sopenharmony_ci * ice_cfg_vsi_bw_dflt_lmt_per_tc - configure default VSI BW limit per TC
403462306a36Sopenharmony_ci * @pi: port information structure
403562306a36Sopenharmony_ci * @vsi_handle: software VSI handle
403662306a36Sopenharmony_ci * @tc: traffic class
403762306a36Sopenharmony_ci * @rl_type: min or max
403862306a36Sopenharmony_ci *
403962306a36Sopenharmony_ci * This function configures default BW limit of VSI scheduling node based on TC
404062306a36Sopenharmony_ci * information.
404162306a36Sopenharmony_ci */
404262306a36Sopenharmony_ciint
404362306a36Sopenharmony_ciice_cfg_vsi_bw_dflt_lmt_per_tc(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
404462306a36Sopenharmony_ci			       enum ice_rl_type rl_type)
404562306a36Sopenharmony_ci{
404662306a36Sopenharmony_ci	int status;
404762306a36Sopenharmony_ci
404862306a36Sopenharmony_ci	status = ice_sched_set_node_bw_lmt_per_tc(pi, vsi_handle,
404962306a36Sopenharmony_ci						  ICE_AGG_TYPE_VSI,
405062306a36Sopenharmony_ci						  tc, rl_type,
405162306a36Sopenharmony_ci						  ICE_SCHED_DFLT_BW);
405262306a36Sopenharmony_ci	if (!status) {
405362306a36Sopenharmony_ci		mutex_lock(&pi->sched_lock);
405462306a36Sopenharmony_ci		status = ice_sched_save_vsi_bw(pi, vsi_handle, tc, rl_type,
405562306a36Sopenharmony_ci					       ICE_SCHED_DFLT_BW);
405662306a36Sopenharmony_ci		mutex_unlock(&pi->sched_lock);
405762306a36Sopenharmony_ci	}
405862306a36Sopenharmony_ci	return status;
405962306a36Sopenharmony_ci}
406062306a36Sopenharmony_ci
406162306a36Sopenharmony_ci/**
406262306a36Sopenharmony_ci * ice_cfg_rl_burst_size - Set burst size value
406362306a36Sopenharmony_ci * @hw: pointer to the HW struct
406462306a36Sopenharmony_ci * @bytes: burst size in bytes
406562306a36Sopenharmony_ci *
406662306a36Sopenharmony_ci * This function configures/set the burst size to requested new value. The new
406762306a36Sopenharmony_ci * burst size value is used for future rate limit calls. It doesn't change the
406862306a36Sopenharmony_ci * existing or previously created RL profiles.
406962306a36Sopenharmony_ci */
407062306a36Sopenharmony_ciint ice_cfg_rl_burst_size(struct ice_hw *hw, u32 bytes)
407162306a36Sopenharmony_ci{
407262306a36Sopenharmony_ci	u16 burst_size_to_prog;
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci	if (bytes < ICE_MIN_BURST_SIZE_ALLOWED ||
407562306a36Sopenharmony_ci	    bytes > ICE_MAX_BURST_SIZE_ALLOWED)
407662306a36Sopenharmony_ci		return -EINVAL;
407762306a36Sopenharmony_ci	if (ice_round_to_num(bytes, 64) <=
407862306a36Sopenharmony_ci	    ICE_MAX_BURST_SIZE_64_BYTE_GRANULARITY) {
407962306a36Sopenharmony_ci		/* 64 byte granularity case */
408062306a36Sopenharmony_ci		/* Disable MSB granularity bit */
408162306a36Sopenharmony_ci		burst_size_to_prog = ICE_64_BYTE_GRANULARITY;
408262306a36Sopenharmony_ci		/* round number to nearest 64 byte granularity */
408362306a36Sopenharmony_ci		bytes = ice_round_to_num(bytes, 64);
408462306a36Sopenharmony_ci		/* The value is in 64 byte chunks */
408562306a36Sopenharmony_ci		burst_size_to_prog |= (u16)(bytes / 64);
408662306a36Sopenharmony_ci	} else {
408762306a36Sopenharmony_ci		/* k bytes granularity case */
408862306a36Sopenharmony_ci		/* Enable MSB granularity bit */
408962306a36Sopenharmony_ci		burst_size_to_prog = ICE_KBYTE_GRANULARITY;
409062306a36Sopenharmony_ci		/* round number to nearest 1024 granularity */
409162306a36Sopenharmony_ci		bytes = ice_round_to_num(bytes, 1024);
409262306a36Sopenharmony_ci		/* check rounding doesn't go beyond allowed */
409362306a36Sopenharmony_ci		if (bytes > ICE_MAX_BURST_SIZE_KBYTE_GRANULARITY)
409462306a36Sopenharmony_ci			bytes = ICE_MAX_BURST_SIZE_KBYTE_GRANULARITY;
409562306a36Sopenharmony_ci		/* The value is in k bytes */
409662306a36Sopenharmony_ci		burst_size_to_prog |= (u16)(bytes / 1024);
409762306a36Sopenharmony_ci	}
409862306a36Sopenharmony_ci	hw->max_burst_size = burst_size_to_prog;
409962306a36Sopenharmony_ci	return 0;
410062306a36Sopenharmony_ci}
410162306a36Sopenharmony_ci
410262306a36Sopenharmony_ci/**
410362306a36Sopenharmony_ci * ice_sched_replay_node_prio - re-configure node priority
410462306a36Sopenharmony_ci * @hw: pointer to the HW struct
410562306a36Sopenharmony_ci * @node: sched node to configure
410662306a36Sopenharmony_ci * @priority: priority value
410762306a36Sopenharmony_ci *
410862306a36Sopenharmony_ci * This function configures node element's priority value. It
410962306a36Sopenharmony_ci * needs to be called with scheduler lock held.
411062306a36Sopenharmony_ci */
411162306a36Sopenharmony_cistatic int
411262306a36Sopenharmony_ciice_sched_replay_node_prio(struct ice_hw *hw, struct ice_sched_node *node,
411362306a36Sopenharmony_ci			   u8 priority)
411462306a36Sopenharmony_ci{
411562306a36Sopenharmony_ci	struct ice_aqc_txsched_elem_data buf;
411662306a36Sopenharmony_ci	struct ice_aqc_txsched_elem *data;
411762306a36Sopenharmony_ci	int status;
411862306a36Sopenharmony_ci
411962306a36Sopenharmony_ci	buf = node->info;
412062306a36Sopenharmony_ci	data = &buf.data;
412162306a36Sopenharmony_ci	data->valid_sections |= ICE_AQC_ELEM_VALID_GENERIC;
412262306a36Sopenharmony_ci	data->generic = priority;
412362306a36Sopenharmony_ci
412462306a36Sopenharmony_ci	/* Configure element */
412562306a36Sopenharmony_ci	status = ice_sched_update_elem(hw, node, &buf);
412662306a36Sopenharmony_ci	return status;
412762306a36Sopenharmony_ci}
412862306a36Sopenharmony_ci
412962306a36Sopenharmony_ci/**
413062306a36Sopenharmony_ci * ice_sched_replay_node_bw - replay node(s) BW
413162306a36Sopenharmony_ci * @hw: pointer to the HW struct
413262306a36Sopenharmony_ci * @node: sched node to configure
413362306a36Sopenharmony_ci * @bw_t_info: BW type information
413462306a36Sopenharmony_ci *
413562306a36Sopenharmony_ci * This function restores node's BW from bw_t_info. The caller needs
413662306a36Sopenharmony_ci * to hold the scheduler lock.
413762306a36Sopenharmony_ci */
413862306a36Sopenharmony_cistatic int
413962306a36Sopenharmony_ciice_sched_replay_node_bw(struct ice_hw *hw, struct ice_sched_node *node,
414062306a36Sopenharmony_ci			 struct ice_bw_type_info *bw_t_info)
414162306a36Sopenharmony_ci{
414262306a36Sopenharmony_ci	struct ice_port_info *pi = hw->port_info;
414362306a36Sopenharmony_ci	int status = -EINVAL;
414462306a36Sopenharmony_ci	u16 bw_alloc;
414562306a36Sopenharmony_ci
414662306a36Sopenharmony_ci	if (!node)
414762306a36Sopenharmony_ci		return status;
414862306a36Sopenharmony_ci	if (bitmap_empty(bw_t_info->bw_t_bitmap, ICE_BW_TYPE_CNT))
414962306a36Sopenharmony_ci		return 0;
415062306a36Sopenharmony_ci	if (test_bit(ICE_BW_TYPE_PRIO, bw_t_info->bw_t_bitmap)) {
415162306a36Sopenharmony_ci		status = ice_sched_replay_node_prio(hw, node,
415262306a36Sopenharmony_ci						    bw_t_info->generic);
415362306a36Sopenharmony_ci		if (status)
415462306a36Sopenharmony_ci			return status;
415562306a36Sopenharmony_ci	}
415662306a36Sopenharmony_ci	if (test_bit(ICE_BW_TYPE_CIR, bw_t_info->bw_t_bitmap)) {
415762306a36Sopenharmony_ci		status = ice_sched_set_node_bw_lmt(pi, node, ICE_MIN_BW,
415862306a36Sopenharmony_ci						   bw_t_info->cir_bw.bw);
415962306a36Sopenharmony_ci		if (status)
416062306a36Sopenharmony_ci			return status;
416162306a36Sopenharmony_ci	}
416262306a36Sopenharmony_ci	if (test_bit(ICE_BW_TYPE_CIR_WT, bw_t_info->bw_t_bitmap)) {
416362306a36Sopenharmony_ci		bw_alloc = bw_t_info->cir_bw.bw_alloc;
416462306a36Sopenharmony_ci		status = ice_sched_cfg_node_bw_alloc(hw, node, ICE_MIN_BW,
416562306a36Sopenharmony_ci						     bw_alloc);
416662306a36Sopenharmony_ci		if (status)
416762306a36Sopenharmony_ci			return status;
416862306a36Sopenharmony_ci	}
416962306a36Sopenharmony_ci	if (test_bit(ICE_BW_TYPE_EIR, bw_t_info->bw_t_bitmap)) {
417062306a36Sopenharmony_ci		status = ice_sched_set_node_bw_lmt(pi, node, ICE_MAX_BW,
417162306a36Sopenharmony_ci						   bw_t_info->eir_bw.bw);
417262306a36Sopenharmony_ci		if (status)
417362306a36Sopenharmony_ci			return status;
417462306a36Sopenharmony_ci	}
417562306a36Sopenharmony_ci	if (test_bit(ICE_BW_TYPE_EIR_WT, bw_t_info->bw_t_bitmap)) {
417662306a36Sopenharmony_ci		bw_alloc = bw_t_info->eir_bw.bw_alloc;
417762306a36Sopenharmony_ci		status = ice_sched_cfg_node_bw_alloc(hw, node, ICE_MAX_BW,
417862306a36Sopenharmony_ci						     bw_alloc);
417962306a36Sopenharmony_ci		if (status)
418062306a36Sopenharmony_ci			return status;
418162306a36Sopenharmony_ci	}
418262306a36Sopenharmony_ci	if (test_bit(ICE_BW_TYPE_SHARED, bw_t_info->bw_t_bitmap))
418362306a36Sopenharmony_ci		status = ice_sched_set_node_bw_lmt(pi, node, ICE_SHARED_BW,
418462306a36Sopenharmony_ci						   bw_t_info->shared_bw);
418562306a36Sopenharmony_ci	return status;
418662306a36Sopenharmony_ci}
418762306a36Sopenharmony_ci
418862306a36Sopenharmony_ci/**
418962306a36Sopenharmony_ci * ice_sched_get_ena_tc_bitmap - get enabled TC bitmap
419062306a36Sopenharmony_ci * @pi: port info struct
419162306a36Sopenharmony_ci * @tc_bitmap: 8 bits TC bitmap to check
419262306a36Sopenharmony_ci * @ena_tc_bitmap: 8 bits enabled TC bitmap to return
419362306a36Sopenharmony_ci *
419462306a36Sopenharmony_ci * This function returns enabled TC bitmap in variable ena_tc_bitmap. Some TCs
419562306a36Sopenharmony_ci * may be missing, it returns enabled TCs. This function needs to be called with
419662306a36Sopenharmony_ci * scheduler lock held.
419762306a36Sopenharmony_ci */
419862306a36Sopenharmony_cistatic void
419962306a36Sopenharmony_ciice_sched_get_ena_tc_bitmap(struct ice_port_info *pi,
420062306a36Sopenharmony_ci			    unsigned long *tc_bitmap,
420162306a36Sopenharmony_ci			    unsigned long *ena_tc_bitmap)
420262306a36Sopenharmony_ci{
420362306a36Sopenharmony_ci	u8 tc;
420462306a36Sopenharmony_ci
420562306a36Sopenharmony_ci	/* Some TC(s) may be missing after reset, adjust for replay */
420662306a36Sopenharmony_ci	ice_for_each_traffic_class(tc)
420762306a36Sopenharmony_ci		if (ice_is_tc_ena(*tc_bitmap, tc) &&
420862306a36Sopenharmony_ci		    (ice_sched_get_tc_node(pi, tc)))
420962306a36Sopenharmony_ci			set_bit(tc, ena_tc_bitmap);
421062306a36Sopenharmony_ci}
421162306a36Sopenharmony_ci
421262306a36Sopenharmony_ci/**
421362306a36Sopenharmony_ci * ice_sched_replay_agg - recreate aggregator node(s)
421462306a36Sopenharmony_ci * @hw: pointer to the HW struct
421562306a36Sopenharmony_ci *
421662306a36Sopenharmony_ci * This function recreate aggregator type nodes which are not replayed earlier.
421762306a36Sopenharmony_ci * It also replay aggregator BW information. These aggregator nodes are not
421862306a36Sopenharmony_ci * associated with VSI type node yet.
421962306a36Sopenharmony_ci */
422062306a36Sopenharmony_civoid ice_sched_replay_agg(struct ice_hw *hw)
422162306a36Sopenharmony_ci{
422262306a36Sopenharmony_ci	struct ice_port_info *pi = hw->port_info;
422362306a36Sopenharmony_ci	struct ice_sched_agg_info *agg_info;
422462306a36Sopenharmony_ci
422562306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
422662306a36Sopenharmony_ci	list_for_each_entry(agg_info, &hw->agg_list, list_entry)
422762306a36Sopenharmony_ci		/* replay aggregator (re-create aggregator node) */
422862306a36Sopenharmony_ci		if (!bitmap_equal(agg_info->tc_bitmap, agg_info->replay_tc_bitmap,
422962306a36Sopenharmony_ci				  ICE_MAX_TRAFFIC_CLASS)) {
423062306a36Sopenharmony_ci			DECLARE_BITMAP(replay_bitmap, ICE_MAX_TRAFFIC_CLASS);
423162306a36Sopenharmony_ci			int status;
423262306a36Sopenharmony_ci
423362306a36Sopenharmony_ci			bitmap_zero(replay_bitmap, ICE_MAX_TRAFFIC_CLASS);
423462306a36Sopenharmony_ci			ice_sched_get_ena_tc_bitmap(pi,
423562306a36Sopenharmony_ci						    agg_info->replay_tc_bitmap,
423662306a36Sopenharmony_ci						    replay_bitmap);
423762306a36Sopenharmony_ci			status = ice_sched_cfg_agg(hw->port_info,
423862306a36Sopenharmony_ci						   agg_info->agg_id,
423962306a36Sopenharmony_ci						   ICE_AGG_TYPE_AGG,
424062306a36Sopenharmony_ci						   replay_bitmap);
424162306a36Sopenharmony_ci			if (status) {
424262306a36Sopenharmony_ci				dev_info(ice_hw_to_dev(hw),
424362306a36Sopenharmony_ci					 "Replay agg id[%d] failed\n",
424462306a36Sopenharmony_ci					 agg_info->agg_id);
424562306a36Sopenharmony_ci				/* Move on to next one */
424662306a36Sopenharmony_ci				continue;
424762306a36Sopenharmony_ci			}
424862306a36Sopenharmony_ci		}
424962306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
425062306a36Sopenharmony_ci}
425162306a36Sopenharmony_ci
425262306a36Sopenharmony_ci/**
425362306a36Sopenharmony_ci * ice_sched_replay_agg_vsi_preinit - Agg/VSI replay pre initialization
425462306a36Sopenharmony_ci * @hw: pointer to the HW struct
425562306a36Sopenharmony_ci *
425662306a36Sopenharmony_ci * This function initialize aggregator(s) TC bitmap to zero. A required
425762306a36Sopenharmony_ci * preinit step for replaying aggregators.
425862306a36Sopenharmony_ci */
425962306a36Sopenharmony_civoid ice_sched_replay_agg_vsi_preinit(struct ice_hw *hw)
426062306a36Sopenharmony_ci{
426162306a36Sopenharmony_ci	struct ice_port_info *pi = hw->port_info;
426262306a36Sopenharmony_ci	struct ice_sched_agg_info *agg_info;
426362306a36Sopenharmony_ci
426462306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
426562306a36Sopenharmony_ci	list_for_each_entry(agg_info, &hw->agg_list, list_entry) {
426662306a36Sopenharmony_ci		struct ice_sched_agg_vsi_info *agg_vsi_info;
426762306a36Sopenharmony_ci
426862306a36Sopenharmony_ci		agg_info->tc_bitmap[0] = 0;
426962306a36Sopenharmony_ci		list_for_each_entry(agg_vsi_info, &agg_info->agg_vsi_list,
427062306a36Sopenharmony_ci				    list_entry)
427162306a36Sopenharmony_ci			agg_vsi_info->tc_bitmap[0] = 0;
427262306a36Sopenharmony_ci	}
427362306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
427462306a36Sopenharmony_ci}
427562306a36Sopenharmony_ci
427662306a36Sopenharmony_ci/**
427762306a36Sopenharmony_ci * ice_sched_replay_vsi_agg - replay aggregator & VSI to aggregator node(s)
427862306a36Sopenharmony_ci * @hw: pointer to the HW struct
427962306a36Sopenharmony_ci * @vsi_handle: software VSI handle
428062306a36Sopenharmony_ci *
428162306a36Sopenharmony_ci * This function replays aggregator node, VSI to aggregator type nodes, and
428262306a36Sopenharmony_ci * their node bandwidth information. This function needs to be called with
428362306a36Sopenharmony_ci * scheduler lock held.
428462306a36Sopenharmony_ci */
428562306a36Sopenharmony_cistatic int ice_sched_replay_vsi_agg(struct ice_hw *hw, u16 vsi_handle)
428662306a36Sopenharmony_ci{
428762306a36Sopenharmony_ci	DECLARE_BITMAP(replay_bitmap, ICE_MAX_TRAFFIC_CLASS);
428862306a36Sopenharmony_ci	struct ice_sched_agg_vsi_info *agg_vsi_info;
428962306a36Sopenharmony_ci	struct ice_port_info *pi = hw->port_info;
429062306a36Sopenharmony_ci	struct ice_sched_agg_info *agg_info;
429162306a36Sopenharmony_ci	int status;
429262306a36Sopenharmony_ci
429362306a36Sopenharmony_ci	bitmap_zero(replay_bitmap, ICE_MAX_TRAFFIC_CLASS);
429462306a36Sopenharmony_ci	if (!ice_is_vsi_valid(hw, vsi_handle))
429562306a36Sopenharmony_ci		return -EINVAL;
429662306a36Sopenharmony_ci	agg_info = ice_get_vsi_agg_info(hw, vsi_handle);
429762306a36Sopenharmony_ci	if (!agg_info)
429862306a36Sopenharmony_ci		return 0; /* Not present in list - default Agg case */
429962306a36Sopenharmony_ci	agg_vsi_info = ice_get_agg_vsi_info(agg_info, vsi_handle);
430062306a36Sopenharmony_ci	if (!agg_vsi_info)
430162306a36Sopenharmony_ci		return 0; /* Not present in list - default Agg case */
430262306a36Sopenharmony_ci	ice_sched_get_ena_tc_bitmap(pi, agg_info->replay_tc_bitmap,
430362306a36Sopenharmony_ci				    replay_bitmap);
430462306a36Sopenharmony_ci	/* Replay aggregator node associated to vsi_handle */
430562306a36Sopenharmony_ci	status = ice_sched_cfg_agg(hw->port_info, agg_info->agg_id,
430662306a36Sopenharmony_ci				   ICE_AGG_TYPE_AGG, replay_bitmap);
430762306a36Sopenharmony_ci	if (status)
430862306a36Sopenharmony_ci		return status;
430962306a36Sopenharmony_ci
431062306a36Sopenharmony_ci	bitmap_zero(replay_bitmap, ICE_MAX_TRAFFIC_CLASS);
431162306a36Sopenharmony_ci	ice_sched_get_ena_tc_bitmap(pi, agg_vsi_info->replay_tc_bitmap,
431262306a36Sopenharmony_ci				    replay_bitmap);
431362306a36Sopenharmony_ci	/* Move this VSI (vsi_handle) to above aggregator */
431462306a36Sopenharmony_ci	return ice_sched_assoc_vsi_to_agg(pi, agg_info->agg_id, vsi_handle,
431562306a36Sopenharmony_ci					  replay_bitmap);
431662306a36Sopenharmony_ci}
431762306a36Sopenharmony_ci
431862306a36Sopenharmony_ci/**
431962306a36Sopenharmony_ci * ice_replay_vsi_agg - replay VSI to aggregator node
432062306a36Sopenharmony_ci * @hw: pointer to the HW struct
432162306a36Sopenharmony_ci * @vsi_handle: software VSI handle
432262306a36Sopenharmony_ci *
432362306a36Sopenharmony_ci * This function replays association of VSI to aggregator type nodes, and
432462306a36Sopenharmony_ci * node bandwidth information.
432562306a36Sopenharmony_ci */
432662306a36Sopenharmony_ciint ice_replay_vsi_agg(struct ice_hw *hw, u16 vsi_handle)
432762306a36Sopenharmony_ci{
432862306a36Sopenharmony_ci	struct ice_port_info *pi = hw->port_info;
432962306a36Sopenharmony_ci	int status;
433062306a36Sopenharmony_ci
433162306a36Sopenharmony_ci	mutex_lock(&pi->sched_lock);
433262306a36Sopenharmony_ci	status = ice_sched_replay_vsi_agg(hw, vsi_handle);
433362306a36Sopenharmony_ci	mutex_unlock(&pi->sched_lock);
433462306a36Sopenharmony_ci	return status;
433562306a36Sopenharmony_ci}
433662306a36Sopenharmony_ci
433762306a36Sopenharmony_ci/**
433862306a36Sopenharmony_ci * ice_sched_replay_q_bw - replay queue type node BW
433962306a36Sopenharmony_ci * @pi: port information structure
434062306a36Sopenharmony_ci * @q_ctx: queue context structure
434162306a36Sopenharmony_ci *
434262306a36Sopenharmony_ci * This function replays queue type node bandwidth. This function needs to be
434362306a36Sopenharmony_ci * called with scheduler lock held.
434462306a36Sopenharmony_ci */
434562306a36Sopenharmony_ciint ice_sched_replay_q_bw(struct ice_port_info *pi, struct ice_q_ctx *q_ctx)
434662306a36Sopenharmony_ci{
434762306a36Sopenharmony_ci	struct ice_sched_node *q_node;
434862306a36Sopenharmony_ci
434962306a36Sopenharmony_ci	/* Following also checks the presence of node in tree */
435062306a36Sopenharmony_ci	q_node = ice_sched_find_node_by_teid(pi->root, q_ctx->q_teid);
435162306a36Sopenharmony_ci	if (!q_node)
435262306a36Sopenharmony_ci		return -EINVAL;
435362306a36Sopenharmony_ci	return ice_sched_replay_node_bw(pi->hw, q_node, &q_ctx->bw_t_info);
435462306a36Sopenharmony_ci}
4355