#include "ice_sched.h"
static int
ice_sched_add_root_node(struct ice_port_info *pi,
struct ice_aqc_txsched_elem_data *info)
{
struct ice_sched_node *root;
struct ice_hw *hw;
if (!pi)
return ICE_ERR_PARAM;
hw = pi->hw;
root = (struct ice_sched_node *)ice_malloc(hw, sizeof(*root));
if (!root)
return ICE_ERR_NO_MEMORY;
root->children = (struct ice_sched_node **)
ice_calloc(hw, hw->max_children[0], sizeof(*root->children));
if (!root->children) {
ice_free(hw, root);
return ICE_ERR_NO_MEMORY;
}
ice_memcpy(&root->info, info, sizeof(*info), ICE_NONDMA_TO_NONDMA);
pi->root = root;
return 0;
}
struct ice_sched_node *
ice_sched_find_node_by_teid(struct ice_sched_node *start_node, u32 teid)
{
u16 i;
if (!start_node)
return NULL;
if (ICE_TXSCHED_GET_NODE_TEID(start_node) == teid)
return start_node;
if (!start_node->num_children ||
start_node->tx_sched_layer >= ICE_AQC_TOPO_MAX_LEVEL_NUM ||
start_node->info.data.elem_type == ICE_AQC_ELEM_TYPE_LEAF)
return NULL;
for (i = 0; i < start_node->num_children; i++)
if (ICE_TXSCHED_GET_NODE_TEID(start_node->children[i]) == teid)
return start_node->children[i];
for (i = 0; i < start_node->num_children; i++) {
struct ice_sched_node *tmp;
tmp = ice_sched_find_node_by_teid(start_node->children[i],
teid);
if (tmp)
return tmp;
}
return NULL;
}
static int
ice_aqc_send_sched_elem_cmd(struct ice_hw *hw, enum ice_adminq_opc cmd_opc,
u16 elems_req, void *buf, u16 buf_size,
u16 *elems_resp, struct ice_sq_cd *cd)
{
struct ice_aqc_sched_elem_cmd *cmd;
struct ice_aq_desc desc;
int status;
cmd = &desc.params.sched_elem_cmd;
ice_fill_dflt_direct_cmd_desc(&desc, cmd_opc);
cmd->num_elem_req = CPU_TO_LE16(elems_req);
desc.flags |= CPU_TO_LE16(ICE_AQ_FLAG_RD);
status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
if (!status && elems_resp)
*elems_resp = LE16_TO_CPU(cmd->num_elem_resp);
return status;
}
int
ice_aq_query_sched_elems(struct ice_hw *hw, u16 elems_req,
struct ice_aqc_txsched_elem_data *buf, u16 buf_size,
u16 *elems_ret, struct ice_sq_cd *cd)
{
return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_get_sched_elems,
elems_req, (void *)buf, buf_size,
elems_ret, cd);
}
int
ice_sched_add_node(struct ice_port_info *pi, u8 layer,
struct ice_aqc_txsched_elem_data *info,
struct ice_sched_node *prealloc_node)
{
struct ice_aqc_txsched_elem_data elem;
struct ice_sched_node *parent;
struct ice_sched_node *node;
struct ice_hw *hw;
int status;
if (!pi)
return ICE_ERR_PARAM;
hw = pi->hw;
parent = ice_sched_find_node_by_teid(pi->root,
LE32_TO_CPU(info->parent_teid));
if (!parent) {
ice_debug(hw, ICE_DBG_SCHED, "Parent Node not found for parent_teid=0x%x\n",
LE32_TO_CPU(info->parent_teid));
return ICE_ERR_PARAM;
}
status = ice_sched_query_elem(hw, LE32_TO_CPU(info->node_teid), &elem);
if (status)
return status;
if (prealloc_node)
node = prealloc_node;
else
node = (struct ice_sched_node *)ice_malloc(hw, sizeof(*node));
if (!node)
return ICE_ERR_NO_MEMORY;
if (hw->max_children[layer]) {
node->children = (struct ice_sched_node **)
ice_calloc(hw, hw->max_children[layer],
sizeof(*node->children));
if (!node->children) {
ice_free(hw, node);
return ICE_ERR_NO_MEMORY;
}
}
node->in_use = true;
node->parent = parent;
node->tx_sched_layer = layer;
parent->children[parent->num_children++] = node;
node->info = elem;
return 0;
}
static int
ice_aq_delete_sched_elems(struct ice_hw *hw, u16 grps_req,
struct ice_aqc_delete_elem *buf, u16 buf_size,
u16 *grps_del, struct ice_sq_cd *cd)
{
return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_delete_sched_elems,
grps_req, (void *)buf, buf_size,
grps_del, cd);
}
static int
ice_sched_remove_elems(struct ice_hw *hw, struct ice_sched_node *parent,
u16 num_nodes, u32 *node_teids)
{
struct ice_aqc_delete_elem *buf;
u16 i, num_groups_removed = 0;
u16 buf_size;
int status;
buf_size = ice_struct_size(buf, teid, num_nodes);
buf = (struct ice_aqc_delete_elem *)ice_malloc(hw, buf_size);
if (!buf)
return ICE_ERR_NO_MEMORY;
buf->hdr.parent_teid = parent->info.node_teid;
buf->hdr.num_elems = CPU_TO_LE16(num_nodes);
for (i = 0; i < num_nodes; i++)
buf->teid[i] = CPU_TO_LE32(node_teids[i]);
status = ice_aq_delete_sched_elems(hw, 1, buf, buf_size,
&num_groups_removed, NULL);
if (status || num_groups_removed != 1)
ice_debug(hw, ICE_DBG_SCHED, "remove node failed FW error %d\n",
hw->adminq.sq_last_status);
ice_free(hw, buf);
return status;
}
static struct ice_sched_node *
ice_sched_get_first_node(struct ice_port_info *pi,
struct ice_sched_node *parent, u8 layer)
{
return pi->sib_head[parent->tc_num][layer];
}
struct ice_sched_node *ice_sched_get_tc_node(struct ice_port_info *pi, u8 tc)
{
u8 i;
if (!pi || !pi->root)
return NULL;
for (i = 0; i < pi->root->num_children; i++)
if (pi->root->children[i]->tc_num == tc)
return pi->root->children[i];
return NULL;
}
void ice_free_sched_node(struct ice_port_info *pi, struct ice_sched_node *node)
{
struct ice_sched_node *parent;
struct ice_hw *hw = pi->hw;
u8 i, j;
while (node->num_children)
ice_free_sched_node(pi, node->children[0]);
if (node->tx_sched_layer >= hw->sw_entry_point_layer &&
node->info.data.elem_type != ICE_AQC_ELEM_TYPE_TC &&
node->info.data.elem_type != ICE_AQC_ELEM_TYPE_ROOT_PORT &&
node->info.data.elem_type != ICE_AQC_ELEM_TYPE_LEAF) {
u32 teid = LE32_TO_CPU(node->info.node_teid);
ice_sched_remove_elems(hw, node->parent, 1, &teid);
}
parent = node->parent;
if (parent) {
struct ice_sched_node *p;
for (i = 0; i < parent->num_children; i++)
if (parent->children[i] == node) {
for (j = i + 1; j < parent->num_children; j++)
parent->children[j - 1] =
parent->children[j];
parent->num_children--;
break;
}
p = ice_sched_get_first_node(pi, node, node->tx_sched_layer);
while (p) {
if (p->sibling == node) {
p->sibling = node->sibling;
break;
}
p = p->sibling;
}
if (pi->sib_head[node->tc_num][node->tx_sched_layer] == node)
pi->sib_head[node->tc_num][node->tx_sched_layer] =
node->sibling;
}
if (node->children)
ice_free(hw, node->children);
ice_free(hw, node);
}
static int
ice_aq_get_dflt_topo(struct ice_hw *hw, u8 lport,
struct ice_aqc_get_topo_elem *buf, u16 buf_size,
u8 *num_branches, struct ice_sq_cd *cd)
{
struct ice_aqc_get_topo *cmd;
struct ice_aq_desc desc;
int status;
cmd = &desc.params.get_topo;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_dflt_topo);
cmd->port_num = lport;
status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
if (!status && num_branches)
*num_branches = cmd->num_branches;
return status;
}
static int
ice_aq_add_sched_elems(struct ice_hw *hw, u16 grps_req,
struct ice_aqc_add_elem *buf, u16 buf_size,
u16 *grps_added, struct ice_sq_cd *cd)
{
return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_add_sched_elems,
grps_req, (void *)buf, buf_size,
grps_added, cd);
}
static int
ice_aq_cfg_sched_elems(struct ice_hw *hw, u16 elems_req,
struct ice_aqc_txsched_elem_data *buf, u16 buf_size,
u16 *elems_cfgd, struct ice_sq_cd *cd)
{
return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_cfg_sched_elems,
elems_req, (void *)buf, buf_size,
elems_cfgd, cd);
}
int
ice_aq_move_sched_elems(struct ice_hw *hw, u16 grps_req,
struct ice_aqc_move_elem *buf, u16 buf_size,
u16 *grps_movd, struct ice_sq_cd *cd)
{
return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_move_sched_elems,
grps_req, (void *)buf, buf_size,
grps_movd, cd);
}
static int
ice_aq_suspend_sched_elems(struct ice_hw *hw, u16 elems_req, __le32 *buf,
u16 buf_size, u16 *elems_ret, struct ice_sq_cd *cd)
{
return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_suspend_sched_elems,
elems_req, (void *)buf, buf_size,
elems_ret, cd);
}
static int
ice_aq_resume_sched_elems(struct ice_hw *hw, u16 elems_req, __le32 *buf,
u16 buf_size, u16 *elems_ret, struct ice_sq_cd *cd)
{
return ice_aqc_send_sched_elem_cmd(hw, ice_aqc_opc_resume_sched_elems,
elems_req, (void *)buf, buf_size,
elems_ret, cd);
}
static int
ice_aq_query_sched_res(struct ice_hw *hw, u16 buf_size,
struct ice_aqc_query_txsched_res_resp *buf,
struct ice_sq_cd *cd)
{
struct ice_aq_desc desc;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_query_sched_res);
return ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
}
static int
ice_sched_suspend_resume_elems(struct ice_hw *hw, u8 num_nodes, u32 *node_teids,
bool suspend)
{
u16 i, buf_size, num_elem_ret = 0;
__le32 *buf;
int status;
buf_size = sizeof(*buf) * num_nodes;
buf = (__le32 *)ice_malloc(hw, buf_size);
if (!buf)
return ICE_ERR_NO_MEMORY;
for (i = 0; i < num_nodes; i++)
buf[i] = CPU_TO_LE32(node_teids[i]);
if (suspend)
status = ice_aq_suspend_sched_elems(hw, num_nodes, buf,
buf_size, &num_elem_ret,
NULL);
else
status = ice_aq_resume_sched_elems(hw, num_nodes, buf,
buf_size, &num_elem_ret,
NULL);
if (status || num_elem_ret != num_nodes)
ice_debug(hw, ICE_DBG_SCHED, "suspend/resume failed\n");
ice_free(hw, buf);
return status;
}
static int
ice_alloc_lan_q_ctx(struct ice_hw *hw, u16 vsi_handle, u8 tc, u16 new_numqs)
{
struct ice_vsi_ctx *vsi_ctx;
struct ice_q_ctx *q_ctx;
vsi_ctx = ice_get_vsi_ctx(hw, vsi_handle);
if (!vsi_ctx)
return ICE_ERR_PARAM;
if (!vsi_ctx->lan_q_ctx[tc]) {
vsi_ctx->lan_q_ctx[tc] = (struct ice_q_ctx *)
ice_calloc(hw, new_numqs, sizeof(*q_ctx));
if (!vsi_ctx->lan_q_ctx[tc])
return ICE_ERR_NO_MEMORY;
vsi_ctx->num_lan_q_entries[tc] = new_numqs;
return 0;
}
if (new_numqs > vsi_ctx->num_lan_q_entries[tc]) {
u16 prev_num = vsi_ctx->num_lan_q_entries[tc];
q_ctx = (struct ice_q_ctx *)
ice_calloc(hw, new_numqs, sizeof(*q_ctx));
if (!q_ctx)
return ICE_ERR_NO_MEMORY;
ice_memcpy(q_ctx, vsi_ctx->lan_q_ctx[tc],
prev_num * sizeof(*q_ctx), ICE_DMA_TO_NONDMA);
ice_free(hw, vsi_ctx->lan_q_ctx[tc]);
vsi_ctx->lan_q_ctx[tc] = q_ctx;
vsi_ctx->num_lan_q_entries[tc] = new_numqs;
}
return 0;
}
static int
ice_alloc_rdma_q_ctx(struct ice_hw *hw, u16 vsi_handle, u8 tc, u16 new_numqs)
{
struct ice_vsi_ctx *vsi_ctx;
struct ice_q_ctx *q_ctx;
vsi_ctx = ice_get_vsi_ctx(hw, vsi_handle);
if (!vsi_ctx)
return ICE_ERR_PARAM;
if (!vsi_ctx->rdma_q_ctx[tc]) {
vsi_ctx->rdma_q_ctx[tc] = (struct ice_q_ctx *)
ice_calloc(hw, new_numqs, sizeof(*q_ctx));
if (!vsi_ctx->rdma_q_ctx[tc])
return ICE_ERR_NO_MEMORY;
vsi_ctx->num_rdma_q_entries[tc] = new_numqs;
return 0;
}
if (new_numqs > vsi_ctx->num_rdma_q_entries[tc]) {
u16 prev_num = vsi_ctx->num_rdma_q_entries[tc];
q_ctx = (struct ice_q_ctx *)
ice_calloc(hw, new_numqs, sizeof(*q_ctx));
if (!q_ctx)
return ICE_ERR_NO_MEMORY;
ice_memcpy(q_ctx, vsi_ctx->rdma_q_ctx[tc],
prev_num * sizeof(*q_ctx), ICE_DMA_TO_NONDMA);
ice_free(hw, vsi_ctx->rdma_q_ctx[tc]);
vsi_ctx->rdma_q_ctx[tc] = q_ctx;
vsi_ctx->num_rdma_q_entries[tc] = new_numqs;
}
return 0;
}
static int
ice_aq_rl_profile(struct ice_hw *hw, enum ice_adminq_opc opcode,
u16 num_profiles, struct ice_aqc_rl_profile_elem *buf,
u16 buf_size, u16 *num_processed, struct ice_sq_cd *cd)
{
struct ice_aqc_rl_profile *cmd;
struct ice_aq_desc desc;
int status;
cmd = &desc.params.rl_profile;
ice_fill_dflt_direct_cmd_desc(&desc, opcode);
desc.flags |= CPU_TO_LE16(ICE_AQ_FLAG_RD);
cmd->num_profiles = CPU_TO_LE16(num_profiles);
status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
if (!status && num_processed)
*num_processed = LE16_TO_CPU(cmd->num_processed);
return status;
}
static int
ice_aq_add_rl_profile(struct ice_hw *hw, u16 num_profiles,
struct ice_aqc_rl_profile_elem *buf, u16 buf_size,
u16 *num_profiles_added, struct ice_sq_cd *cd)
{
return ice_aq_rl_profile(hw, ice_aqc_opc_add_rl_profiles, num_profiles,
buf, buf_size, num_profiles_added, cd);
}
int
ice_aq_query_rl_profile(struct ice_hw *hw, u16 num_profiles,
struct ice_aqc_rl_profile_elem *buf, u16 buf_size,
struct ice_sq_cd *cd)
{
return ice_aq_rl_profile(hw, ice_aqc_opc_query_rl_profiles,
num_profiles, buf, buf_size, NULL, cd);
}
static int
ice_aq_remove_rl_profile(struct ice_hw *hw, u16 num_profiles,
struct ice_aqc_rl_profile_elem *buf, u16 buf_size,
u16 *num_profiles_removed, struct ice_sq_cd *cd)
{
return ice_aq_rl_profile(hw, ice_aqc_opc_remove_rl_profiles,
num_profiles, buf, buf_size,
num_profiles_removed, cd);
}
static int
ice_sched_del_rl_profile(struct ice_hw *hw,
struct ice_aqc_rl_profile_info *rl_info)
{
struct ice_aqc_rl_profile_elem *buf;
u16 num_profiles_removed;
u16 num_profiles = 1;
int status;
if (rl_info->prof_id_ref != 0)
return ICE_ERR_IN_USE;
buf = &rl_info->profile;
status = ice_aq_remove_rl_profile(hw, num_profiles, buf, sizeof(*buf),
&num_profiles_removed, NULL);
if (status || num_profiles_removed != num_profiles)
return ICE_ERR_CFG;
LIST_DEL(&rl_info->list_entry);
ice_free(hw, rl_info);
return status;
}
static void ice_sched_clear_rl_prof(struct ice_port_info *pi)
{
u16 ln;
struct ice_hw *hw = pi->hw;
for (ln = 0; ln < hw->num_tx_sched_layers; ln++) {
struct ice_aqc_rl_profile_info *rl_prof_elem;
struct ice_aqc_rl_profile_info *rl_prof_tmp;
LIST_FOR_EACH_ENTRY_SAFE(rl_prof_elem, rl_prof_tmp,
&hw->rl_prof_list[ln],
ice_aqc_rl_profile_info, list_entry) {
int status;
rl_prof_elem->prof_id_ref = 0;
status = ice_sched_del_rl_profile(hw, rl_prof_elem);
if (status) {
ice_debug(hw, ICE_DBG_SCHED, "Remove rl profile failed\n");
LIST_DEL(&rl_prof_elem->list_entry);
ice_free(hw, rl_prof_elem);
}
}
}
}
void ice_sched_clear_agg(struct ice_hw *hw)
{
struct ice_sched_agg_info *agg_info;
struct ice_sched_agg_info *atmp;
LIST_FOR_EACH_ENTRY_SAFE(agg_info, atmp, &hw->agg_list,
ice_sched_agg_info,
list_entry) {
struct ice_sched_agg_vsi_info *agg_vsi_info;
struct ice_sched_agg_vsi_info *vtmp;
LIST_FOR_EACH_ENTRY_SAFE(agg_vsi_info, vtmp,
&agg_info->agg_vsi_list,
ice_sched_agg_vsi_info, list_entry) {
LIST_DEL(&agg_vsi_info->list_entry);
ice_free(hw, agg_vsi_info);
}
LIST_DEL(&agg_info->list_entry);
ice_free(hw, agg_info);
}
}
static void ice_sched_clear_tx_topo(struct ice_port_info *pi)
{
if (!pi)
return;
ice_sched_clear_rl_prof(pi);
if (pi->root) {
ice_free_sched_node(pi, pi->root);
pi->root = NULL;
}
}
void ice_sched_clear_port(struct ice_port_info *pi)
{
if (!pi || pi->port_state != ICE_SCHED_PORT_STATE_READY)
return;
pi->port_state = ICE_SCHED_PORT_STATE_INIT;
ice_acquire_lock(&pi->sched_lock);
ice_sched_clear_tx_topo(pi);
ice_release_lock(&pi->sched_lock);
ice_destroy_lock(&pi->sched_lock);
}
void ice_sched_cleanup_all(struct ice_hw *hw)
{
if (!hw)
return;
if (hw->layer_info) {
ice_free(hw, hw->layer_info);
hw->layer_info = NULL;
}
ice_sched_clear_port(hw->port_info);
hw->num_tx_sched_layers = 0;
hw->num_tx_sched_phys_layers = 0;
hw->flattened_layers = 0;
hw->max_cgds = 0;
}
int
ice_aq_cfg_node_attr(struct ice_hw *hw, u16 num_nodes,
struct ice_aqc_node_attr_elem *buf, u16 buf_size,
struct ice_sq_cd *cd)
{
struct ice_aqc_node_attr *cmd;
struct ice_aq_desc desc;
cmd = &desc.params.node_attr;
ice_fill_dflt_direct_cmd_desc(&desc,
ice_aqc_opc_cfg_node_attr);
desc.flags |= CPU_TO_LE16(ICE_AQ_FLAG_RD);
cmd->num_entries = CPU_TO_LE16(num_nodes);
return ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
}
int
ice_aq_cfg_l2_node_cgd(struct ice_hw *hw, u16 num_l2_nodes,
struct ice_aqc_cfg_l2_node_cgd_elem *buf,
u16 buf_size, struct ice_sq_cd *cd)
{
struct ice_aqc_cfg_l2_node_cgd *cmd;
struct ice_aq_desc desc;
cmd = &desc.params.cfg_l2_node_cgd;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_cfg_l2_node_cgd);
desc.flags |= CPU_TO_LE16(ICE_AQ_FLAG_RD);
cmd->num_l2_nodes = CPU_TO_LE16(num_l2_nodes);
return ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
}
int
ice_sched_add_elems(struct ice_port_info *pi, struct ice_sched_node *tc_node,
struct ice_sched_node *parent, u8 layer, u16 num_nodes,
u16 *num_nodes_added, u32 *first_node_teid,
struct ice_sched_node **prealloc_nodes)
{
struct ice_sched_node *prev, *new_node;
struct ice_aqc_add_elem *buf;
u16 i, num_groups_added = 0;
struct ice_hw *hw = pi->hw;
int status = 0;
u16 buf_size;
u32 teid;
buf_size = ice_struct_size(buf, generic, num_nodes);
buf = (struct ice_aqc_add_elem *)ice_malloc(hw, buf_size);
if (!buf)
return ICE_ERR_NO_MEMORY;
buf->hdr.parent_teid = parent->info.node_teid;
buf->hdr.num_elems = CPU_TO_LE16(num_nodes);
for (i = 0; i < num_nodes; i++) {
buf->generic[i].parent_teid = parent->info.node_teid;
buf->generic[i].data.elem_type = ICE_AQC_ELEM_TYPE_SE_GENERIC;
buf->generic[i].data.valid_sections =
ICE_AQC_ELEM_VALID_GENERIC | ICE_AQC_ELEM_VALID_CIR |
ICE_AQC_ELEM_VALID_EIR;
buf->generic[i].data.generic = 0;
buf->generic[i].data.cir_bw.bw_profile_idx =
CPU_TO_LE16(ICE_SCHED_DFLT_RL_PROF_ID);
buf->generic[i].data.cir_bw.bw_alloc =
CPU_TO_LE16(ICE_SCHED_DFLT_BW_WT);
buf->generic[i].data.eir_bw.bw_profile_idx =
CPU_TO_LE16(ICE_SCHED_DFLT_RL_PROF_ID);
buf->generic[i].data.eir_bw.bw_alloc =
CPU_TO_LE16(ICE_SCHED_DFLT_BW_WT);
}
status = ice_aq_add_sched_elems(hw, 1, buf, buf_size,
&num_groups_added, NULL);
if (status || num_groups_added != 1) {
ice_debug(hw, ICE_DBG_SCHED, "add node failed FW Error %d\n",
hw->adminq.sq_last_status);
ice_free(hw, buf);
return ICE_ERR_CFG;
}
*num_nodes_added = num_nodes;
for (i = 0; i < num_nodes; i++) {
if (prealloc_nodes)
status = ice_sched_add_node(pi, layer, &buf->generic[i], prealloc_nodes[i]);
else
status = ice_sched_add_node(pi, layer, &buf->generic[i], NULL);
if (status) {
ice_debug(hw, ICE_DBG_SCHED, "add nodes in SW DB failed status =%d\n",
status);
break;
}
teid = LE32_TO_CPU(buf->generic[i].node_teid);
new_node = ice_sched_find_node_by_teid(parent, teid);
if (!new_node) {
ice_debug(hw, ICE_DBG_SCHED, "Node is missing for teid =%d\n", teid);
break;
}
new_node->sibling = NULL;
new_node->tc_num = tc_node->tc_num;
prev = ice_sched_get_first_node(pi, tc_node, layer);
if (prev && prev != new_node) {
while (prev->sibling)
prev = prev->sibling;
prev->sibling = new_node;
}
if (!pi->sib_head[tc_node->tc_num][layer])
pi->sib_head[tc_node->tc_num][layer] = new_node;
if (i == 0)
*first_node_teid = teid;
}
ice_free(hw, buf);
return status;
}
static int
ice_sched_add_nodes_to_hw_layer(struct ice_port_info *pi,
struct ice_sched_node *tc_node,
struct ice_sched_node *parent, u8 layer,
u16 num_nodes, u32 *first_node_teid,
u16 *num_nodes_added)
{
u16 max_child_nodes;
*num_nodes_added = 0;
if (!num_nodes)
return 0;
if (!parent || layer < pi->hw->sw_entry_point_layer)
return ICE_ERR_PARAM;
max_child_nodes = pi->hw->max_children[parent->tx_sched_layer];
if ((parent->num_children + num_nodes) > max_child_nodes) {
if (parent == tc_node)
return ICE_ERR_CFG;
return ICE_ERR_MAX_LIMIT;
}
return ice_sched_add_elems(pi, tc_node, parent, layer, num_nodes,
num_nodes_added, first_node_teid, NULL);
}
static int
ice_sched_add_nodes_to_layer(struct ice_port_info *pi,
struct ice_sched_node *tc_node,
struct ice_sched_node *parent, u8 layer,
u16 num_nodes, u32 *first_node_teid,
u16 *num_nodes_added)
{
u32 *first_teid_ptr = first_node_teid;
u16 new_num_nodes = num_nodes;
int status = 0;
u32 temp;
*num_nodes_added = 0;
while (*num_nodes_added < num_nodes) {
u16 max_child_nodes, num_added = 0;
status = ice_sched_add_nodes_to_hw_layer(pi, tc_node, parent,
layer, new_num_nodes,
first_teid_ptr,
&num_added);
if (!status)
*num_nodes_added += num_added;
if (*num_nodes_added > num_nodes) {
ice_debug(pi->hw, ICE_DBG_SCHED, "added extra nodes %d %d\n", num_nodes,
*num_nodes_added);
status = ICE_ERR_CFG;
break;
}
if (!status && (*num_nodes_added == num_nodes))
break;
if (status && status != ICE_ERR_MAX_LIMIT)
break;
max_child_nodes = pi->hw->max_children[parent->tx_sched_layer];
if (parent->num_children < max_child_nodes) {
new_num_nodes = max_child_nodes - parent->num_children;
} else {
parent = parent->sibling;
if (num_added)
first_teid_ptr = &temp;
new_num_nodes = num_nodes - *num_nodes_added;
}
}
return status;
}
static u8 ice_sched_get_qgrp_layer(struct ice_hw *hw)
{
return hw->num_tx_sched_layers - ICE_QGRP_LAYER_OFFSET;
}
static u8 ice_sched_get_vsi_layer(struct ice_hw *hw)
{
if (hw->num_tx_sched_layers == ICE_SCHED_9_LAYERS)
return hw->num_tx_sched_layers - ICE_VSI_LAYER_OFFSET;
else if (hw->num_tx_sched_layers == ICE_SCHED_5_LAYERS)
return hw->num_tx_sched_layers - ICE_QGRP_LAYER_OFFSET;
return hw->sw_entry_point_layer;
}
static u8 ice_sched_get_agg_layer(struct ice_hw *hw)
{
if (hw->num_tx_sched_layers == ICE_SCHED_9_LAYERS)
return hw->num_tx_sched_layers - ICE_AGG_LAYER_OFFSET;
return hw->sw_entry_point_layer;
}
static void ice_rm_dflt_leaf_node(struct ice_port_info *pi)
{
struct ice_sched_node *node;
node = pi->root;
while (node) {
if (!node->num_children)
break;
node = node->children[0];
}
if (node && node->info.data.elem_type == ICE_AQC_ELEM_TYPE_LEAF) {
u32 teid = LE32_TO_CPU(node->info.node_teid);
int status;
status = ice_sched_remove_elems(pi->hw, node->parent, 1, &teid);
if (!status)
ice_free_sched_node(pi, node);
}
}
static void ice_sched_rm_dflt_nodes(struct ice_port_info *pi)
{
struct ice_sched_node *node;
ice_rm_dflt_leaf_node(pi);
node = pi->root;
while (node) {
if (node->tx_sched_layer >= pi->hw->sw_entry_point_layer &&
node->info.data.elem_type != ICE_AQC_ELEM_TYPE_TC &&
node->info.data.elem_type != ICE_AQC_ELEM_TYPE_ROOT_PORT) {
ice_free_sched_node(pi, node);
break;
}
if (!node->num_children)
break;
node = node->children[0];
}
}
int ice_sched_init_port(struct ice_port_info *pi)
{
struct ice_aqc_get_topo_elem *buf;
struct ice_hw *hw;
u8 num_branches;
u16 num_elems;
int status;
u8 i, j;
if (!pi)
return ICE_ERR_PARAM;
hw = pi->hw;
buf = (struct ice_aqc_get_topo_elem *)ice_malloc(hw,
ICE_AQ_MAX_BUF_LEN);
if (!buf)
return ICE_ERR_NO_MEMORY;
status = ice_aq_get_dflt_topo(hw, pi->lport, buf, ICE_AQ_MAX_BUF_LEN,
&num_branches, NULL);
if (status)
goto err_init_port;
if (num_branches < 1 || num_branches > ICE_TXSCHED_MAX_BRANCHES) {
ice_debug(hw, ICE_DBG_SCHED, "num_branches unexpected %d\n",
num_branches);
status = ICE_ERR_PARAM;
goto err_init_port;
}
num_elems = LE16_TO_CPU(buf[0].hdr.num_elems);
if (num_elems < 1 || num_elems > ICE_AQC_TOPO_MAX_LEVEL_NUM) {
ice_debug(hw, ICE_DBG_SCHED, "num_elems unexpected %d\n",
num_elems);
status = ICE_ERR_PARAM;
goto err_init_port;
}
if (num_elems > 2 && buf[0].generic[num_elems - 1].data.elem_type ==
ICE_AQC_ELEM_TYPE_LEAF)
pi->last_node_teid =
LE32_TO_CPU(buf[0].generic[num_elems - 2].node_teid);
else
pi->last_node_teid =
LE32_TO_CPU(buf[0].generic[num_elems - 1].node_teid);
status = ice_sched_add_root_node(pi, &buf[0].generic[0]);
if (status)
goto err_init_port;
for (i = 0; i < num_branches; i++) {
num_elems = LE16_TO_CPU(buf[i].hdr.num_elems);
for (j = 1; j < num_elems; j++) {
if (buf[0].generic[j].data.elem_type ==
ICE_AQC_ELEM_TYPE_ENTRY_POINT)
hw->sw_entry_point_layer = j;
status = ice_sched_add_node(pi, j, &buf[i].generic[j], NULL);
if (status)
goto err_init_port;
}
}
if (pi->root)
ice_sched_rm_dflt_nodes(pi);
pi->port_state = ICE_SCHED_PORT_STATE_READY;
ice_init_lock(&pi->sched_lock);
for (i = 0; i < ICE_AQC_TOPO_MAX_LEVEL_NUM; i++)
INIT_LIST_HEAD(&hw->rl_prof_list[i]);
err_init_port:
if (status && pi->root) {
ice_free_sched_node(pi, pi->root);
pi->root = NULL;
}
ice_free(hw, buf);
return status;
}
struct ice_sched_node *ice_sched_get_node(struct ice_port_info *pi, u32 teid)
{
struct ice_sched_node *node;
if (!pi)
return NULL;
ice_acquire_lock(&pi->sched_lock);
node = ice_sched_find_node_by_teid(pi->root, teid);
ice_release_lock(&pi->sched_lock);
if (!node)
ice_debug(pi->hw, ICE_DBG_SCHED, "Node not found for teid=0x%x\n", teid);
return node;
}
int ice_sched_query_res_alloc(struct ice_hw *hw)
{
struct ice_aqc_query_txsched_res_resp *buf;
__le16 max_sibl;
int status = 0;
u16 i;
if (hw->layer_info)
return status;
buf = (struct ice_aqc_query_txsched_res_resp *)
ice_malloc(hw, sizeof(*buf));
if (!buf)
return ICE_ERR_NO_MEMORY;
status = ice_aq_query_sched_res(hw, sizeof(*buf), buf, NULL);
if (status)
goto sched_query_out;
hw->num_tx_sched_layers =
(u8)LE16_TO_CPU(buf->sched_props.logical_levels);
hw->num_tx_sched_phys_layers =
(u8)LE16_TO_CPU(buf->sched_props.phys_levels);
hw->flattened_layers = buf->sched_props.flattening_bitmap;
hw->max_cgds = buf->sched_props.max_pf_cgds;
for (i = 0; i < hw->num_tx_sched_layers - 1; i++) {
max_sibl = buf->layer_props[i + 1].max_sibl_grp_sz;
hw->max_children[i] = LE16_TO_CPU(max_sibl);
}
hw->layer_info = (struct ice_aqc_layer_props *)
ice_memdup(hw, buf->layer_props,
(hw->num_tx_sched_layers *
sizeof(*hw->layer_info)),
ICE_NONDMA_TO_NONDMA);
if (!hw->layer_info) {
status = ICE_ERR_NO_MEMORY;
goto sched_query_out;
}
sched_query_out:
ice_free(hw, buf);
return status;
}
void ice_sched_get_psm_clk_freq(struct ice_hw *hw)
{
u32 val, clk_src;
val = rd32(hw, GLGEN_CLKSTAT_SRC);
clk_src = (val & GLGEN_CLKSTAT_SRC_PSM_CLK_SRC_M) >>
GLGEN_CLKSTAT_SRC_PSM_CLK_SRC_S;
switch (clk_src) {
case PSM_CLK_SRC_367_MHZ:
hw->psm_clk_freq = ICE_PSM_CLK_367MHZ_IN_HZ;
break;
case PSM_CLK_SRC_416_MHZ:
hw->psm_clk_freq = ICE_PSM_CLK_416MHZ_IN_HZ;
break;
case PSM_CLK_SRC_446_MHZ:
hw->psm_clk_freq = ICE_PSM_CLK_446MHZ_IN_HZ;
break;
case PSM_CLK_SRC_390_MHZ:
hw->psm_clk_freq = ICE_PSM_CLK_390MHZ_IN_HZ;
break;
}
}
bool
ice_sched_find_node_in_subtree(struct ice_hw *hw, struct ice_sched_node *base,
struct ice_sched_node *node)
{
u8 i;
for (i = 0; i < base->num_children; i++) {
struct ice_sched_node *child = base->children[i];
if (node == child)
return true;
if (child->tx_sched_layer > node->tx_sched_layer)
return false;
if (ice_sched_find_node_in_subtree(hw, child, node))
return true;
}
return false;
}
static struct ice_sched_node *
ice_sched_get_free_qgrp(struct ice_port_info *pi,
struct ice_sched_node *vsi_node,
struct ice_sched_node *qgrp_node, u8 owner)
{
struct ice_sched_node *min_qgrp;
u8 min_children;
if (!qgrp_node)
return qgrp_node;
min_children = qgrp_node->num_children;
if (!min_children)
return qgrp_node;
min_qgrp = qgrp_node;
while (qgrp_node) {
if (ice_sched_find_node_in_subtree(pi->hw, vsi_node, qgrp_node))
if (qgrp_node->num_children < min_children &&
qgrp_node->owner == owner) {
min_qgrp = qgrp_node;
min_children = min_qgrp->num_children;
if (!min_children)
break;
}
qgrp_node = qgrp_node->sibling;
}
return min_qgrp;
}
struct ice_sched_node *
ice_sched_get_free_qparent(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
u8 owner)
{
struct ice_sched_node *vsi_node, *qgrp_node;
struct ice_vsi_ctx *vsi_ctx;
u8 qgrp_layer, vsi_layer;
u16 max_children;
qgrp_layer = ice_sched_get_qgrp_layer(pi->hw);
vsi_layer = ice_sched_get_vsi_layer(pi->hw);
max_children = pi->hw->max_children[qgrp_layer];
vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
if (!vsi_ctx)
return NULL;
vsi_node = vsi_ctx->sched.vsi_node[tc];
if (!vsi_node)
return NULL;
if (qgrp_layer == vsi_layer)
return vsi_node;
qgrp_node = ice_sched_get_first_node(pi, vsi_node, qgrp_layer);
while (qgrp_node) {
if (ice_sched_find_node_in_subtree(pi->hw, vsi_node, qgrp_node))
if (qgrp_node->num_children < max_children &&
qgrp_node->owner == owner)
break;
qgrp_node = qgrp_node->sibling;
}
return ice_sched_get_free_qgrp(pi, vsi_node, qgrp_node, owner);
}
struct ice_sched_node *
ice_sched_get_vsi_node(struct ice_port_info *pi, struct ice_sched_node *tc_node,
u16 vsi_handle)
{
struct ice_sched_node *node;
u8 vsi_layer;
vsi_layer = ice_sched_get_vsi_layer(pi->hw);
node = ice_sched_get_first_node(pi, tc_node, vsi_layer);
while (node) {
if (node->vsi_handle == vsi_handle)
return node;
node = node->sibling;
}
return node;
}
static struct ice_sched_node *
ice_sched_get_agg_node(struct ice_port_info *pi, struct ice_sched_node *tc_node,
u32 agg_id)
{
struct ice_sched_node *node;
struct ice_hw *hw = pi->hw;
u8 agg_layer;
if (!hw)
return NULL;
agg_layer = ice_sched_get_agg_layer(hw);
node = ice_sched_get_first_node(pi, tc_node, agg_layer);
while (node) {
if (node->agg_id == agg_id)
return node;
node = node->sibling;
}
return node;
}
static bool ice_sched_check_node(struct ice_hw *hw, struct ice_sched_node *node)
{
struct ice_aqc_txsched_elem_data buf;
u32 node_teid;
int status;
node_teid = LE32_TO_CPU(node->info.node_teid);
status = ice_sched_query_elem(hw, node_teid, &buf);
if (status)
return false;
if (memcmp(&buf, &node->info, sizeof(buf))) {
ice_debug(hw, ICE_DBG_SCHED, "Node mismatch for teid=0x%x\n",
node_teid);
return false;
}
return true;
}
static void
ice_sched_calc_vsi_child_nodes(struct ice_hw *hw, u16 num_qs, u16 *num_nodes)
{
u16 num = num_qs;
u8 i, qgl, vsil;
qgl = ice_sched_get_qgrp_layer(hw);
vsil = ice_sched_get_vsi_layer(hw);
for (i = qgl; i > vsil; i--) {
num = DIVIDE_AND_ROUND_UP(num, hw->max_children[i]);
num_nodes[i] = num ? num : 1;
}
}
static int
ice_sched_add_vsi_child_nodes(struct ice_port_info *pi, u16 vsi_handle,
struct ice_sched_node *tc_node, u16 *num_nodes,
u8 owner)
{
struct ice_sched_node *parent, *node;
struct ice_hw *hw = pi->hw;
u32 first_node_teid;
u16 num_added = 0;
u8 i, qgl, vsil;
qgl = ice_sched_get_qgrp_layer(hw);
vsil = ice_sched_get_vsi_layer(hw);
parent = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
for (i = vsil + 1; i <= qgl; i++) {
int status;
if (!parent)
return ICE_ERR_CFG;
status = ice_sched_add_nodes_to_layer(pi, tc_node, parent, i,
num_nodes[i],
&first_node_teid,
&num_added);
if (status || num_nodes[i] != num_added)
return ICE_ERR_CFG;
if (num_added) {
parent = ice_sched_find_node_by_teid(tc_node,
first_node_teid);
node = parent;
while (node) {
node->owner = owner;
node = node->sibling;
}
} else {
parent = parent->children[0];
}
}
return 0;
}
static void
ice_sched_calc_vsi_support_nodes(struct ice_port_info *pi,
struct ice_sched_node *tc_node, u16 *num_nodes)
{
struct ice_sched_node *node;
u8 vsil;
int i;
vsil = ice_sched_get_vsi_layer(pi->hw);
for (i = vsil; i >= pi->hw->sw_entry_point_layer; i--)
if (!tc_node->num_children || i == vsil) {
num_nodes[i]++;
} else {
node = ice_sched_get_first_node(pi, tc_node, (u8)i);
while (node) {
if (node->num_children <
pi->hw->max_children[i])
break;
node = node->sibling;
}
if (node)
break;
num_nodes[i]++;
}
}
static int
ice_sched_add_vsi_support_nodes(struct ice_port_info *pi, u16 vsi_handle,
struct ice_sched_node *tc_node, u16 *num_nodes)
{
struct ice_sched_node *parent = tc_node;
u32 first_node_teid;
u16 num_added = 0;
u8 i, vsil;
if (!pi)
return ICE_ERR_PARAM;
vsil = ice_sched_get_vsi_layer(pi->hw);
for (i = pi->hw->sw_entry_point_layer; i <= vsil; i++) {
int status;
status = ice_sched_add_nodes_to_layer(pi, tc_node, parent,
i, num_nodes[i],
&first_node_teid,
&num_added);
if (status || num_nodes[i] != num_added)
return ICE_ERR_CFG;
if (num_added)
parent = ice_sched_find_node_by_teid(tc_node,
first_node_teid);
else
parent = parent->children[0];
if (!parent)
return ICE_ERR_CFG;
if (i == vsil)
parent->vsi_handle = vsi_handle;
}
return 0;
}
static int
ice_sched_add_vsi_to_topo(struct ice_port_info *pi, u16 vsi_handle, u8 tc)
{
u16 num_nodes[ICE_AQC_TOPO_MAX_LEVEL_NUM] = { 0 };
struct ice_sched_node *tc_node;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
return ICE_ERR_PARAM;
ice_sched_calc_vsi_support_nodes(pi, tc_node, num_nodes);
return ice_sched_add_vsi_support_nodes(pi, vsi_handle, tc_node,
num_nodes);
}
static int
ice_sched_update_vsi_child_nodes(struct ice_port_info *pi, u16 vsi_handle,
u8 tc, u16 new_numqs, u8 owner)
{
u16 new_num_nodes[ICE_AQC_TOPO_MAX_LEVEL_NUM] = { 0 };
struct ice_sched_node *vsi_node;
struct ice_sched_node *tc_node;
struct ice_vsi_ctx *vsi_ctx;
struct ice_hw *hw = pi->hw;
int status = 0;
u16 prev_numqs;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
return ICE_ERR_CFG;
vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
if (!vsi_node)
return ICE_ERR_CFG;
vsi_ctx = ice_get_vsi_ctx(hw, vsi_handle);
if (!vsi_ctx)
return ICE_ERR_PARAM;
if (owner == ICE_SCHED_NODE_OWNER_LAN)
prev_numqs = vsi_ctx->sched.max_lanq[tc];
else
prev_numqs = vsi_ctx->sched.max_rdmaq[tc];
if (new_numqs <= prev_numqs)
return status;
if (owner == ICE_SCHED_NODE_OWNER_LAN) {
status = ice_alloc_lan_q_ctx(hw, vsi_handle, tc, new_numqs);
if (status)
return status;
} else {
status = ice_alloc_rdma_q_ctx(hw, vsi_handle, tc, new_numqs);
if (status)
return status;
}
if (new_numqs)
ice_sched_calc_vsi_child_nodes(hw, new_numqs, new_num_nodes);
status = ice_sched_add_vsi_child_nodes(pi, vsi_handle, tc_node,
new_num_nodes, owner);
if (status)
return status;
if (owner == ICE_SCHED_NODE_OWNER_LAN)
vsi_ctx->sched.max_lanq[tc] = new_numqs;
else
vsi_ctx->sched.max_rdmaq[tc] = new_numqs;
return 0;
}
int
ice_sched_cfg_vsi(struct ice_port_info *pi, u16 vsi_handle, u8 tc, u16 maxqs,
u8 owner, bool enable)
{
struct ice_sched_node *vsi_node, *tc_node;
struct ice_vsi_ctx *vsi_ctx;
struct ice_hw *hw = pi->hw;
int status = 0;
ice_debug(pi->hw, ICE_DBG_SCHED, "add/config VSI %d\n", vsi_handle);
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
return ICE_ERR_PARAM;
vsi_ctx = ice_get_vsi_ctx(hw, vsi_handle);
if (!vsi_ctx)
return ICE_ERR_PARAM;
vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
if (!enable) {
if (vsi_node && vsi_node->in_use) {
u32 teid = LE32_TO_CPU(vsi_node->info.node_teid);
status = ice_sched_suspend_resume_elems(hw, 1, &teid,
true);
if (!status)
vsi_node->in_use = false;
}
return status;
}
if (!vsi_node) {
status = ice_sched_add_vsi_to_topo(pi, vsi_handle, tc);
if (status)
return status;
vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
if (!vsi_node)
return ICE_ERR_CFG;
vsi_ctx->sched.vsi_node[tc] = vsi_node;
vsi_node->in_use = true;
vsi_ctx->sched.max_lanq[tc] = 0;
vsi_ctx->sched.max_rdmaq[tc] = 0;
}
status = ice_sched_update_vsi_child_nodes(pi, vsi_handle, tc, maxqs,
owner);
if (status)
return status;
if (!vsi_node->in_use) {
u32 teid = LE32_TO_CPU(vsi_node->info.node_teid);
status = ice_sched_suspend_resume_elems(hw, 1, &teid, false);
if (!status)
vsi_node->in_use = true;
}
return status;
}
static void ice_sched_rm_agg_vsi_info(struct ice_port_info *pi, u16 vsi_handle)
{
struct ice_sched_agg_info *agg_info;
struct ice_sched_agg_info *atmp;
LIST_FOR_EACH_ENTRY_SAFE(agg_info, atmp, &pi->hw->agg_list,
ice_sched_agg_info,
list_entry) {
struct ice_sched_agg_vsi_info *agg_vsi_info;
struct ice_sched_agg_vsi_info *vtmp;
LIST_FOR_EACH_ENTRY_SAFE(agg_vsi_info, vtmp,
&agg_info->agg_vsi_list,
ice_sched_agg_vsi_info, list_entry)
if (agg_vsi_info->vsi_handle == vsi_handle) {
LIST_DEL(&agg_vsi_info->list_entry);
ice_free(pi->hw, agg_vsi_info);
return;
}
}
}
static bool ice_sched_is_leaf_node_present(struct ice_sched_node *node)
{
u8 i;
for (i = 0; i < node->num_children; i++)
if (ice_sched_is_leaf_node_present(node->children[i]))
return true;
return (node->info.data.elem_type == ICE_AQC_ELEM_TYPE_LEAF);
}
static int
ice_sched_rm_vsi_cfg(struct ice_port_info *pi, u16 vsi_handle, u8 owner)
{
struct ice_vsi_ctx *vsi_ctx;
int status = ICE_ERR_PARAM;
u8 i;
ice_debug(pi->hw, ICE_DBG_SCHED, "removing VSI %d\n", vsi_handle);
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
return status;
ice_acquire_lock(&pi->sched_lock);
vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
if (!vsi_ctx)
goto exit_sched_rm_vsi_cfg;
ice_for_each_traffic_class(i) {
struct ice_sched_node *vsi_node, *tc_node;
u8 j = 0;
tc_node = ice_sched_get_tc_node(pi, i);
if (!tc_node)
continue;
vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
if (!vsi_node)
continue;
if (ice_sched_is_leaf_node_present(vsi_node)) {
ice_debug(pi->hw, ICE_DBG_SCHED, "VSI has leaf nodes in TC %d\n", i);
status = ICE_ERR_IN_USE;
goto exit_sched_rm_vsi_cfg;
}
while (j < vsi_node->num_children) {
if (vsi_node->children[j]->owner == owner) {
ice_free_sched_node(pi, vsi_node->children[j]);
j = 0;
} else {
j++;
}
}
if (!vsi_node->num_children) {
ice_free_sched_node(pi, vsi_node);
vsi_ctx->sched.vsi_node[i] = NULL;
ice_sched_rm_agg_vsi_info(pi, vsi_handle);
}
if (owner == ICE_SCHED_NODE_OWNER_LAN)
vsi_ctx->sched.max_lanq[i] = 0;
else
vsi_ctx->sched.max_rdmaq[i] = 0;
}
status = 0;
exit_sched_rm_vsi_cfg:
ice_release_lock(&pi->sched_lock);
return status;
}
int ice_rm_vsi_lan_cfg(struct ice_port_info *pi, u16 vsi_handle)
{
return ice_sched_rm_vsi_cfg(pi, vsi_handle, ICE_SCHED_NODE_OWNER_LAN);
}
int ice_rm_vsi_rdma_cfg(struct ice_port_info *pi, u16 vsi_handle)
{
return ice_sched_rm_vsi_cfg(pi, vsi_handle, ICE_SCHED_NODE_OWNER_RDMA);
}
bool ice_sched_is_tree_balanced(struct ice_hw *hw, struct ice_sched_node *node)
{
u8 i;
for (i = 0; i < node->num_children; i++)
if (!ice_sched_is_tree_balanced(hw, node->children[i]))
return false;
return ice_sched_check_node(hw, node);
}
int
ice_aq_query_node_to_root(struct ice_hw *hw, u32 node_teid,
struct ice_aqc_txsched_elem_data *buf, u16 buf_size,
struct ice_sq_cd *cd)
{
struct ice_aqc_query_node_to_root *cmd;
struct ice_aq_desc desc;
cmd = &desc.params.query_node_to_root;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_query_node_to_root);
cmd->teid = CPU_TO_LE32(node_teid);
return ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
}
static struct ice_sched_agg_info *
ice_get_agg_info(struct ice_hw *hw, u32 agg_id)
{
struct ice_sched_agg_info *agg_info;
LIST_FOR_EACH_ENTRY(agg_info, &hw->agg_list, ice_sched_agg_info,
list_entry)
if (agg_info->agg_id == agg_id)
return agg_info;
return NULL;
}
static struct ice_sched_node *
ice_sched_get_free_vsi_parent(struct ice_hw *hw, struct ice_sched_node *node,
u16 *num_nodes)
{
u8 l = node->tx_sched_layer;
u8 vsil, i;
vsil = ice_sched_get_vsi_layer(hw);
if (l == vsil - 1)
return (node->num_children < hw->max_children[l]) ? node : NULL;
if (node->num_children < hw->max_children[l])
num_nodes[l] = 0;
for (i = 0; i < node->num_children; i++) {
struct ice_sched_node *parent;
parent = ice_sched_get_free_vsi_parent(hw, node->children[i],
num_nodes);
if (parent)
return parent;
}
return NULL;
}
void
ice_sched_update_parent(struct ice_sched_node *new_parent,
struct ice_sched_node *node)
{
struct ice_sched_node *old_parent;
u8 i, j;
old_parent = node->parent;
for (i = 0; i < old_parent->num_children; i++)
if (old_parent->children[i] == node) {
for (j = i + 1; j < old_parent->num_children; j++)
old_parent->children[j - 1] =
old_parent->children[j];
old_parent->num_children--;
break;
}
new_parent->children[new_parent->num_children++] = node;
node->parent = new_parent;
node->info.parent_teid = new_parent->info.node_teid;
}
int
ice_sched_move_nodes(struct ice_port_info *pi, struct ice_sched_node *parent,
u16 num_items, u32 *list)
{
struct ice_aqc_move_elem *buf;
struct ice_sched_node *node;
u16 i, grps_movd = 0;
struct ice_hw *hw;
int status = 0;
u16 buf_len;
hw = pi->hw;
if (!parent || !num_items)
return ICE_ERR_PARAM;
if (parent->num_children + num_items >
hw->max_children[parent->tx_sched_layer])
return ICE_ERR_AQ_FULL;
buf_len = ice_struct_size(buf, teid, 1);
buf = (struct ice_aqc_move_elem *)ice_malloc(hw, buf_len);
if (!buf)
return ICE_ERR_NO_MEMORY;
for (i = 0; i < num_items; i++) {
node = ice_sched_find_node_by_teid(pi->root, list[i]);
if (!node) {
status = ICE_ERR_PARAM;
goto move_err_exit;
}
buf->hdr.src_parent_teid = node->info.parent_teid;
buf->hdr.dest_parent_teid = parent->info.node_teid;
buf->teid[0] = node->info.node_teid;
buf->hdr.num_elems = CPU_TO_LE16(1);
status = ice_aq_move_sched_elems(hw, 1, buf, buf_len,
&grps_movd, NULL);
if (status && grps_movd != 1) {
status = ICE_ERR_CFG;
goto move_err_exit;
}
ice_sched_update_parent(parent, node);
}
move_err_exit:
ice_free(hw, buf);
return status;
}
static int
ice_sched_move_vsi_to_agg(struct ice_port_info *pi, u16 vsi_handle, u32 agg_id,
u8 tc)
{
struct ice_sched_node *vsi_node, *agg_node, *tc_node, *parent;
u16 num_nodes[ICE_AQC_TOPO_MAX_LEVEL_NUM] = { 0 };
u32 first_node_teid, vsi_teid;
u16 num_nodes_added;
u8 aggl, vsil, i;
int status;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
return ICE_ERR_CFG;
agg_node = ice_sched_get_agg_node(pi, tc_node, agg_id);
if (!agg_node)
return ICE_ERR_DOES_NOT_EXIST;
vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
if (!vsi_node)
return ICE_ERR_DOES_NOT_EXIST;
if (ice_sched_find_node_in_subtree(pi->hw, agg_node, vsi_node))
return 0;
aggl = ice_sched_get_agg_layer(pi->hw);
vsil = ice_sched_get_vsi_layer(pi->hw);
for (i = aggl + 1; i < vsil; i++)
num_nodes[i] = 1;
for (i = 0; i < agg_node->num_children; i++) {
parent = ice_sched_get_free_vsi_parent(pi->hw,
agg_node->children[i],
num_nodes);
if (parent)
goto move_nodes;
}
parent = agg_node;
for (i = aggl + 1; i < vsil; i++) {
status = ice_sched_add_nodes_to_layer(pi, tc_node, parent, i,
num_nodes[i],
&first_node_teid,
&num_nodes_added);
if (status || num_nodes[i] != num_nodes_added)
return ICE_ERR_CFG;
if (num_nodes_added)
parent = ice_sched_find_node_by_teid(tc_node,
first_node_teid);
else
parent = parent->children[0];
if (!parent)
return ICE_ERR_CFG;
}
move_nodes:
vsi_teid = LE32_TO_CPU(vsi_node->info.node_teid);
return ice_sched_move_nodes(pi, parent, 1, &vsi_teid);
}
static int
ice_move_all_vsi_to_dflt_agg(struct ice_port_info *pi,
struct ice_sched_agg_info *agg_info, u8 tc,
bool rm_vsi_info)
{
struct ice_sched_agg_vsi_info *agg_vsi_info;
struct ice_sched_agg_vsi_info *tmp;
int status = 0;
LIST_FOR_EACH_ENTRY_SAFE(agg_vsi_info, tmp, &agg_info->agg_vsi_list,
ice_sched_agg_vsi_info, list_entry) {
u16 vsi_handle = agg_vsi_info->vsi_handle;
if (!ice_is_tc_ena(agg_vsi_info->tc_bitmap[0], tc))
continue;
status = ice_sched_move_vsi_to_agg(pi, vsi_handle,
ICE_DFLT_AGG_ID, tc);
if (status)
break;
ice_clear_bit(tc, agg_vsi_info->tc_bitmap);
if (rm_vsi_info && !agg_vsi_info->tc_bitmap[0]) {
LIST_DEL(&agg_vsi_info->list_entry);
ice_free(pi->hw, agg_vsi_info);
}
}
return status;
}
static bool
ice_sched_is_agg_inuse(struct ice_port_info *pi, struct ice_sched_node *node)
{
u8 vsil, i;
vsil = ice_sched_get_vsi_layer(pi->hw);
if (node->tx_sched_layer < vsil - 1) {
for (i = 0; i < node->num_children; i++)
if (ice_sched_is_agg_inuse(pi, node->children[i]))
return true;
return false;
} else {
return node->num_children ? true : false;
}
}
static int
ice_sched_rm_agg_cfg(struct ice_port_info *pi, u32 agg_id, u8 tc)
{
struct ice_sched_node *tc_node, *agg_node;
struct ice_hw *hw = pi->hw;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
return ICE_ERR_CFG;
agg_node = ice_sched_get_agg_node(pi, tc_node, agg_id);
if (!agg_node)
return ICE_ERR_DOES_NOT_EXIST;
if (ice_sched_is_agg_inuse(pi, agg_node))
return ICE_ERR_IN_USE;
while (agg_node->tx_sched_layer > hw->sw_entry_point_layer) {
struct ice_sched_node *parent = agg_node->parent;
if (!parent)
return ICE_ERR_CFG;
if (parent->num_children > 1)
break;
agg_node = parent;
}
ice_free_sched_node(pi, agg_node);
return 0;
}
static int
ice_rm_agg_cfg_tc(struct ice_port_info *pi, struct ice_sched_agg_info *agg_info,
u8 tc, bool rm_vsi_info)
{
int status = 0;
if (!ice_is_tc_ena(agg_info->tc_bitmap[0], tc))
goto exit_rm_agg_cfg_tc;
status = ice_move_all_vsi_to_dflt_agg(pi, agg_info, tc, rm_vsi_info);
if (status)
goto exit_rm_agg_cfg_tc;
status = ice_sched_rm_agg_cfg(pi, agg_info->agg_id, tc);
if (status)
goto exit_rm_agg_cfg_tc;
ice_clear_bit(tc, agg_info->tc_bitmap);
exit_rm_agg_cfg_tc:
return status;
}
static int
ice_save_agg_tc_bitmap(struct ice_port_info *pi, u32 agg_id,
ice_bitmap_t *tc_bitmap)
{
struct ice_sched_agg_info *agg_info;
agg_info = ice_get_agg_info(pi->hw, agg_id);
if (!agg_info)
return ICE_ERR_PARAM;
ice_cp_bitmap(agg_info->replay_tc_bitmap, tc_bitmap,
ICE_MAX_TRAFFIC_CLASS);
return 0;
}
static int
ice_sched_add_agg_cfg(struct ice_port_info *pi, u32 agg_id, u8 tc)
{
struct ice_sched_node *parent, *agg_node, *tc_node;
u16 num_nodes[ICE_AQC_TOPO_MAX_LEVEL_NUM] = { 0 };
struct ice_hw *hw = pi->hw;
u32 first_node_teid;
u16 num_nodes_added;
int status = 0;
u8 i, aggl;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
return ICE_ERR_CFG;
agg_node = ice_sched_get_agg_node(pi, tc_node, agg_id);
if (agg_node)
return status;
aggl = ice_sched_get_agg_layer(hw);
num_nodes[aggl] = 1;
for (i = hw->sw_entry_point_layer; i < aggl; i++) {
parent = ice_sched_get_first_node(pi, tc_node, i);
while (parent) {
if (parent->num_children < hw->max_children[i])
break;
parent = parent->sibling;
}
if (!parent)
num_nodes[i]++;
}
parent = tc_node;
for (i = hw->sw_entry_point_layer; i <= aggl; i++) {
if (!parent)
return ICE_ERR_CFG;
status = ice_sched_add_nodes_to_layer(pi, tc_node, parent, i,
num_nodes[i],
&first_node_teid,
&num_nodes_added);
if (status || num_nodes[i] != num_nodes_added)
return ICE_ERR_CFG;
if (num_nodes_added) {
parent = ice_sched_find_node_by_teid(tc_node,
first_node_teid);
if (parent && i == aggl)
parent->agg_id = agg_id;
} else {
parent = parent->children[0];
}
}
return 0;
}
static int
ice_sched_cfg_agg(struct ice_port_info *pi, u32 agg_id,
enum ice_agg_type agg_type, ice_bitmap_t *tc_bitmap)
{
struct ice_sched_agg_info *agg_info;
struct ice_hw *hw = pi->hw;
int status = 0;
u8 tc;
agg_info = ice_get_agg_info(hw, agg_id);
if (!agg_info) {
agg_info = (struct ice_sched_agg_info *)
ice_malloc(hw, sizeof(*agg_info));
if (!agg_info)
return ICE_ERR_NO_MEMORY;
agg_info->agg_id = agg_id;
agg_info->agg_type = agg_type;
agg_info->tc_bitmap[0] = 0;
INIT_LIST_HEAD(&agg_info->agg_vsi_list);
LIST_ADD(&agg_info->list_entry, &hw->agg_list);
}
ice_for_each_traffic_class(tc) {
if (!ice_is_tc_ena(*tc_bitmap, tc)) {
status = ice_rm_agg_cfg_tc(pi, agg_info, tc, false);
if (status)
break;
continue;
}
if (ice_is_tc_ena(agg_info->tc_bitmap[0], tc))
continue;
status = ice_sched_add_agg_cfg(pi, agg_id, tc);
if (status)
break;
ice_set_bit(tc, agg_info->tc_bitmap);
}
return status;
}
int
ice_cfg_agg(struct ice_port_info *pi, u32 agg_id, enum ice_agg_type agg_type,
u8 tc_bitmap)
{
ice_bitmap_t bitmap = tc_bitmap;
int status;
ice_acquire_lock(&pi->sched_lock);
status = ice_sched_cfg_agg(pi, agg_id, agg_type,
(ice_bitmap_t *)&bitmap);
if (!status)
status = ice_save_agg_tc_bitmap(pi, agg_id,
(ice_bitmap_t *)&bitmap);
ice_release_lock(&pi->sched_lock);
return status;
}
static struct ice_sched_agg_vsi_info *
ice_get_agg_vsi_info(struct ice_sched_agg_info *agg_info, u16 vsi_handle)
{
struct ice_sched_agg_vsi_info *agg_vsi_info;
LIST_FOR_EACH_ENTRY(agg_vsi_info, &agg_info->agg_vsi_list,
ice_sched_agg_vsi_info, list_entry)
if (agg_vsi_info->vsi_handle == vsi_handle)
return agg_vsi_info;
return NULL;
}
static struct ice_sched_agg_info *
ice_get_vsi_agg_info(struct ice_hw *hw, u16 vsi_handle)
{
struct ice_sched_agg_info *agg_info;
LIST_FOR_EACH_ENTRY(agg_info, &hw->agg_list, ice_sched_agg_info,
list_entry) {
struct ice_sched_agg_vsi_info *agg_vsi_info;
agg_vsi_info = ice_get_agg_vsi_info(agg_info, vsi_handle);
if (agg_vsi_info)
return agg_info;
}
return NULL;
}
static int
ice_save_agg_vsi_tc_bitmap(struct ice_port_info *pi, u32 agg_id, u16 vsi_handle,
ice_bitmap_t *tc_bitmap)
{
struct ice_sched_agg_vsi_info *agg_vsi_info;
struct ice_sched_agg_info *agg_info;
agg_info = ice_get_agg_info(pi->hw, agg_id);
if (!agg_info)
return ICE_ERR_PARAM;
agg_vsi_info = ice_get_agg_vsi_info(agg_info, vsi_handle);
if (!agg_vsi_info)
return ICE_ERR_PARAM;
ice_cp_bitmap(agg_vsi_info->replay_tc_bitmap, tc_bitmap,
ICE_MAX_TRAFFIC_CLASS);
return 0;
}
static int
ice_sched_assoc_vsi_to_agg(struct ice_port_info *pi, u32 agg_id,
u16 vsi_handle, ice_bitmap_t *tc_bitmap)
{
struct ice_sched_agg_vsi_info *agg_vsi_info, *old_agg_vsi_info = NULL;
struct ice_sched_agg_info *agg_info, *old_agg_info;
struct ice_hw *hw = pi->hw;
int status = 0;
u8 tc;
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
return ICE_ERR_PARAM;
agg_info = ice_get_agg_info(hw, agg_id);
if (!agg_info)
return ICE_ERR_PARAM;
old_agg_info = ice_get_vsi_agg_info(hw, vsi_handle);
if (old_agg_info && old_agg_info != agg_info) {
struct ice_sched_agg_vsi_info *vtmp;
LIST_FOR_EACH_ENTRY_SAFE(old_agg_vsi_info, vtmp,
&old_agg_info->agg_vsi_list,
ice_sched_agg_vsi_info, list_entry)
if (old_agg_vsi_info->vsi_handle == vsi_handle)
break;
}
agg_vsi_info = ice_get_agg_vsi_info(agg_info, vsi_handle);
if (!agg_vsi_info) {
agg_vsi_info = (struct ice_sched_agg_vsi_info *)
ice_malloc(hw, sizeof(*agg_vsi_info));
if (!agg_vsi_info)
return ICE_ERR_PARAM;
agg_vsi_info->vsi_handle = vsi_handle;
LIST_ADD(&agg_vsi_info->list_entry, &agg_info->agg_vsi_list);
}
ice_for_each_traffic_class(tc) {
if (!ice_is_tc_ena(*tc_bitmap, tc))
continue;
status = ice_sched_move_vsi_to_agg(pi, vsi_handle, agg_id, tc);
if (status)
break;
ice_set_bit(tc, agg_vsi_info->tc_bitmap);
if (old_agg_vsi_info)
ice_clear_bit(tc, old_agg_vsi_info->tc_bitmap);
}
if (old_agg_vsi_info && !old_agg_vsi_info->tc_bitmap[0]) {
LIST_DEL(&old_agg_vsi_info->list_entry);
ice_free(pi->hw, old_agg_vsi_info);
}
return status;
}
static void ice_sched_rm_unused_rl_prof(struct ice_hw *hw)
{
u16 ln;
for (ln = 0; ln < hw->num_tx_sched_layers; ln++) {
struct ice_aqc_rl_profile_info *rl_prof_elem;
struct ice_aqc_rl_profile_info *rl_prof_tmp;
LIST_FOR_EACH_ENTRY_SAFE(rl_prof_elem, rl_prof_tmp,
&hw->rl_prof_list[ln],
ice_aqc_rl_profile_info, list_entry) {
if (!ice_sched_del_rl_profile(hw, rl_prof_elem))
ice_debug(hw, ICE_DBG_SCHED, "Removed rl profile\n");
}
}
}
static int
ice_sched_update_elem(struct ice_hw *hw, struct ice_sched_node *node,
struct ice_aqc_txsched_elem_data *info)
{
struct ice_aqc_txsched_elem_data buf;
u16 elem_cfgd = 0;
u16 num_elems = 1;
int status;
buf = *info;
if (node->info.data.elem_type == ICE_AQC_ELEM_TYPE_TC)
buf.data.valid_sections &= ~ICE_AQC_ELEM_VALID_CIR;
buf.parent_teid = 0;
buf.data.elem_type = 0;
buf.data.flags = 0;
status = ice_aq_cfg_sched_elems(hw, num_elems, &buf, sizeof(buf),
&elem_cfgd, NULL);
if (status || elem_cfgd != num_elems) {
ice_debug(hw, ICE_DBG_SCHED, "Config sched elem error\n");
return ICE_ERR_CFG;
}
node->info.data = info->data;
return status;
}
static int
ice_sched_cfg_node_bw_alloc(struct ice_hw *hw, struct ice_sched_node *node,
enum ice_rl_type rl_type, u16 bw_alloc)
{
struct ice_aqc_txsched_elem_data buf;
struct ice_aqc_txsched_elem *data;
int status;
buf = node->info;
data = &buf.data;
if (rl_type == ICE_MIN_BW) {
data->valid_sections |= ICE_AQC_ELEM_VALID_CIR;
data->cir_bw.bw_alloc = CPU_TO_LE16(bw_alloc);
} else if (rl_type == ICE_MAX_BW) {
data->valid_sections |= ICE_AQC_ELEM_VALID_EIR;
data->eir_bw.bw_alloc = CPU_TO_LE16(bw_alloc);
} else {
return ICE_ERR_PARAM;
}
status = ice_sched_update_elem(hw, node, &buf);
return status;
}
int
ice_move_vsi_to_agg(struct ice_port_info *pi, u32 agg_id, u16 vsi_handle,
u8 tc_bitmap)
{
ice_bitmap_t bitmap = tc_bitmap;
int status;
ice_acquire_lock(&pi->sched_lock);
status = ice_sched_assoc_vsi_to_agg(pi, agg_id, vsi_handle,
(ice_bitmap_t *)&bitmap);
if (!status)
status = ice_save_agg_vsi_tc_bitmap(pi, agg_id, vsi_handle,
(ice_bitmap_t *)&bitmap);
ice_release_lock(&pi->sched_lock);
return status;
}
int ice_rm_agg_cfg(struct ice_port_info *pi, u32 agg_id)
{
struct ice_sched_agg_info *agg_info;
int status = 0;
u8 tc;
ice_acquire_lock(&pi->sched_lock);
agg_info = ice_get_agg_info(pi->hw, agg_id);
if (!agg_info) {
status = ICE_ERR_DOES_NOT_EXIST;
goto exit_ice_rm_agg_cfg;
}
ice_for_each_traffic_class(tc) {
status = ice_rm_agg_cfg_tc(pi, agg_info, tc, true);
if (status)
goto exit_ice_rm_agg_cfg;
}
if (ice_is_any_bit_set(agg_info->tc_bitmap, ICE_MAX_TRAFFIC_CLASS)) {
status = ICE_ERR_IN_USE;
goto exit_ice_rm_agg_cfg;
}
LIST_DEL(&agg_info->list_entry);
ice_free(pi->hw, agg_info);
ice_sched_rm_unused_rl_prof(pi->hw);
exit_ice_rm_agg_cfg:
ice_release_lock(&pi->sched_lock);
return status;
}
static void
ice_set_clear_cir_bw_alloc(struct ice_bw_type_info *bw_t_info, u16 bw_alloc)
{
bw_t_info->cir_bw.bw_alloc = bw_alloc;
if (bw_t_info->cir_bw.bw_alloc)
ice_set_bit(ICE_BW_TYPE_CIR_WT, bw_t_info->bw_t_bitmap);
else
ice_clear_bit(ICE_BW_TYPE_CIR_WT, bw_t_info->bw_t_bitmap);
}
static void
ice_set_clear_eir_bw_alloc(struct ice_bw_type_info *bw_t_info, u16 bw_alloc)
{
bw_t_info->eir_bw.bw_alloc = bw_alloc;
if (bw_t_info->eir_bw.bw_alloc)
ice_set_bit(ICE_BW_TYPE_EIR_WT, bw_t_info->bw_t_bitmap);
else
ice_clear_bit(ICE_BW_TYPE_EIR_WT, bw_t_info->bw_t_bitmap);
}
static int
ice_sched_save_vsi_bw_alloc(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
enum ice_rl_type rl_type, u16 bw_alloc)
{
struct ice_vsi_ctx *vsi_ctx;
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
return ICE_ERR_PARAM;
vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
if (!vsi_ctx)
return ICE_ERR_PARAM;
switch (rl_type) {
case ICE_MIN_BW:
ice_set_clear_cir_bw_alloc(&vsi_ctx->sched.bw_t_info[tc],
bw_alloc);
break;
case ICE_MAX_BW:
ice_set_clear_eir_bw_alloc(&vsi_ctx->sched.bw_t_info[tc],
bw_alloc);
break;
default:
return ICE_ERR_PARAM;
}
return 0;
}
static void ice_set_clear_cir_bw(struct ice_bw_type_info *bw_t_info, u32 bw)
{
if (bw == ICE_SCHED_DFLT_BW) {
ice_clear_bit(ICE_BW_TYPE_CIR, bw_t_info->bw_t_bitmap);
bw_t_info->cir_bw.bw = 0;
} else {
ice_set_bit(ICE_BW_TYPE_CIR, bw_t_info->bw_t_bitmap);
bw_t_info->cir_bw.bw = bw;
}
}
static void ice_set_clear_eir_bw(struct ice_bw_type_info *bw_t_info, u32 bw)
{
if (bw == ICE_SCHED_DFLT_BW) {
ice_clear_bit(ICE_BW_TYPE_EIR, bw_t_info->bw_t_bitmap);
bw_t_info->eir_bw.bw = 0;
} else {
ice_set_bit(ICE_BW_TYPE_EIR, bw_t_info->bw_t_bitmap);
bw_t_info->eir_bw.bw = bw;
}
}
static void ice_set_clear_shared_bw(struct ice_bw_type_info *bw_t_info, u32 bw)
{
if (bw == ICE_SCHED_DFLT_BW) {
ice_clear_bit(ICE_BW_TYPE_SHARED, bw_t_info->bw_t_bitmap);
bw_t_info->shared_bw = 0;
} else {
ice_set_bit(ICE_BW_TYPE_SHARED, bw_t_info->bw_t_bitmap);
bw_t_info->shared_bw = bw;
}
}
static int
ice_sched_save_vsi_bw(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
enum ice_rl_type rl_type, u32 bw)
{
struct ice_vsi_ctx *vsi_ctx;
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
return ICE_ERR_PARAM;
vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
if (!vsi_ctx)
return ICE_ERR_PARAM;
switch (rl_type) {
case ICE_MIN_BW:
ice_set_clear_cir_bw(&vsi_ctx->sched.bw_t_info[tc], bw);
break;
case ICE_MAX_BW:
ice_set_clear_eir_bw(&vsi_ctx->sched.bw_t_info[tc], bw);
break;
case ICE_SHARED_BW:
ice_set_clear_shared_bw(&vsi_ctx->sched.bw_t_info[tc], bw);
break;
default:
return ICE_ERR_PARAM;
}
return 0;
}
static void ice_set_clear_prio(struct ice_bw_type_info *bw_t_info, u8 prio)
{
bw_t_info->generic = prio;
if (bw_t_info->generic)
ice_set_bit(ICE_BW_TYPE_PRIO, bw_t_info->bw_t_bitmap);
else
ice_clear_bit(ICE_BW_TYPE_PRIO, bw_t_info->bw_t_bitmap);
}
static int
ice_sched_save_vsi_prio(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
u8 prio)
{
struct ice_vsi_ctx *vsi_ctx;
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
return ICE_ERR_PARAM;
vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
if (!vsi_ctx)
return ICE_ERR_PARAM;
if (tc >= ICE_MAX_TRAFFIC_CLASS)
return ICE_ERR_PARAM;
ice_set_clear_prio(&vsi_ctx->sched.bw_t_info[tc], prio);
return 0;
}
static int
ice_sched_save_agg_bw_alloc(struct ice_port_info *pi, u32 agg_id, u8 tc,
enum ice_rl_type rl_type, u16 bw_alloc)
{
struct ice_sched_agg_info *agg_info;
agg_info = ice_get_agg_info(pi->hw, agg_id);
if (!agg_info)
return ICE_ERR_PARAM;
if (!ice_is_tc_ena(agg_info->tc_bitmap[0], tc))
return ICE_ERR_PARAM;
switch (rl_type) {
case ICE_MIN_BW:
ice_set_clear_cir_bw_alloc(&agg_info->bw_t_info[tc], bw_alloc);
break;
case ICE_MAX_BW:
ice_set_clear_eir_bw_alloc(&agg_info->bw_t_info[tc], bw_alloc);
break;
default:
return ICE_ERR_PARAM;
}
return 0;
}
static int
ice_sched_save_agg_bw(struct ice_port_info *pi, u32 agg_id, u8 tc,
enum ice_rl_type rl_type, u32 bw)
{
struct ice_sched_agg_info *agg_info;
agg_info = ice_get_agg_info(pi->hw, agg_id);
if (!agg_info)
return ICE_ERR_PARAM;
if (!ice_is_tc_ena(agg_info->tc_bitmap[0], tc))
return ICE_ERR_PARAM;
switch (rl_type) {
case ICE_MIN_BW:
ice_set_clear_cir_bw(&agg_info->bw_t_info[tc], bw);
break;
case ICE_MAX_BW:
ice_set_clear_eir_bw(&agg_info->bw_t_info[tc], bw);
break;
case ICE_SHARED_BW:
ice_set_clear_shared_bw(&agg_info->bw_t_info[tc], bw);
break;
default:
return ICE_ERR_PARAM;
}
return 0;
}
int
ice_cfg_vsi_bw_lmt_per_tc(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
enum ice_rl_type rl_type, u32 bw)
{
int status;
status = ice_sched_set_node_bw_lmt_per_tc(pi, vsi_handle,
ICE_AGG_TYPE_VSI,
tc, rl_type, bw);
if (!status) {
ice_acquire_lock(&pi->sched_lock);
status = ice_sched_save_vsi_bw(pi, vsi_handle, tc, rl_type, bw);
ice_release_lock(&pi->sched_lock);
}
return status;
}
int
ice_cfg_vsi_bw_dflt_lmt_per_tc(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
enum ice_rl_type rl_type)
{
int status;
status = ice_sched_set_node_bw_lmt_per_tc(pi, vsi_handle,
ICE_AGG_TYPE_VSI,
tc, rl_type,
ICE_SCHED_DFLT_BW);
if (!status) {
ice_acquire_lock(&pi->sched_lock);
status = ice_sched_save_vsi_bw(pi, vsi_handle, tc, rl_type,
ICE_SCHED_DFLT_BW);
ice_release_lock(&pi->sched_lock);
}
return status;
}
int
ice_cfg_agg_bw_lmt_per_tc(struct ice_port_info *pi, u32 agg_id, u8 tc,
enum ice_rl_type rl_type, u32 bw)
{
int status;
status = ice_sched_set_node_bw_lmt_per_tc(pi, agg_id, ICE_AGG_TYPE_AGG,
tc, rl_type, bw);
if (!status) {
ice_acquire_lock(&pi->sched_lock);
status = ice_sched_save_agg_bw(pi, agg_id, tc, rl_type, bw);
ice_release_lock(&pi->sched_lock);
}
return status;
}
int
ice_cfg_agg_bw_dflt_lmt_per_tc(struct ice_port_info *pi, u32 agg_id, u8 tc,
enum ice_rl_type rl_type)
{
int status;
status = ice_sched_set_node_bw_lmt_per_tc(pi, agg_id, ICE_AGG_TYPE_AGG,
tc, rl_type,
ICE_SCHED_DFLT_BW);
if (!status) {
ice_acquire_lock(&pi->sched_lock);
status = ice_sched_save_agg_bw(pi, agg_id, tc, rl_type,
ICE_SCHED_DFLT_BW);
ice_release_lock(&pi->sched_lock);
}
return status;
}
int
ice_cfg_vsi_bw_shared_lmt(struct ice_port_info *pi, u16 vsi_handle, u32 min_bw,
u32 max_bw, u32 shared_bw)
{
return ice_sched_set_vsi_bw_shared_lmt(pi, vsi_handle, min_bw, max_bw,
shared_bw);
}
int
ice_cfg_vsi_bw_no_shared_lmt(struct ice_port_info *pi, u16 vsi_handle)
{
return ice_sched_set_vsi_bw_shared_lmt(pi, vsi_handle,
ICE_SCHED_DFLT_BW,
ICE_SCHED_DFLT_BW,
ICE_SCHED_DFLT_BW);
}
int
ice_cfg_agg_bw_shared_lmt(struct ice_port_info *pi, u32 agg_id, u32 min_bw,
u32 max_bw, u32 shared_bw)
{
return ice_sched_set_agg_bw_shared_lmt(pi, agg_id, min_bw, max_bw,
shared_bw);
}
int
ice_cfg_agg_bw_no_shared_lmt(struct ice_port_info *pi, u32 agg_id)
{
return ice_sched_set_agg_bw_shared_lmt(pi, agg_id, ICE_SCHED_DFLT_BW,
ICE_SCHED_DFLT_BW,
ICE_SCHED_DFLT_BW);
}
int
ice_cfg_agg_bw_shared_lmt_per_tc(struct ice_port_info *pi, u32 agg_id, u8 tc,
u32 min_bw, u32 max_bw, u32 shared_bw)
{
return ice_sched_set_agg_bw_shared_lmt_per_tc(pi, agg_id, tc, min_bw,
max_bw, shared_bw);
}
int
ice_cfg_agg_bw_no_shared_lmt_per_tc(struct ice_port_info *pi, u32 agg_id, u8 tc)
{
return ice_sched_set_agg_bw_shared_lmt_per_tc(pi, agg_id, tc,
ICE_SCHED_DFLT_BW,
ICE_SCHED_DFLT_BW,
ICE_SCHED_DFLT_BW);
}
int
ice_cfg_vsi_q_priority(struct ice_port_info *pi, u16 num_qs, u32 *q_ids,
u8 *q_prio)
{
int status = ICE_ERR_PARAM;
u16 i;
ice_acquire_lock(&pi->sched_lock);
for (i = 0; i < num_qs; i++) {
struct ice_sched_node *node;
node = ice_sched_find_node_by_teid(pi->root, q_ids[i]);
if (!node || node->info.data.elem_type !=
ICE_AQC_ELEM_TYPE_LEAF) {
status = ICE_ERR_PARAM;
break;
}
status = ice_sched_cfg_sibl_node_prio(pi, node, q_prio[i]);
if (status)
break;
}
ice_release_lock(&pi->sched_lock);
return status;
}
int
ice_cfg_agg_vsi_priority_per_tc(struct ice_port_info *pi, u32 agg_id,
u16 num_vsis, u16 *vsi_handle_arr,
u8 *node_prio, u8 tc)
{
struct ice_sched_agg_vsi_info *agg_vsi_info;
struct ice_sched_node *tc_node, *agg_node;
struct ice_sched_agg_info *agg_info;
bool agg_id_present = false;
struct ice_hw *hw = pi->hw;
int status = ICE_ERR_PARAM;
u16 i;
ice_acquire_lock(&pi->sched_lock);
LIST_FOR_EACH_ENTRY(agg_info, &hw->agg_list, ice_sched_agg_info,
list_entry)
if (agg_info->agg_id == agg_id) {
agg_id_present = true;
break;
}
if (!agg_id_present)
goto exit_agg_priority_per_tc;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
goto exit_agg_priority_per_tc;
agg_node = ice_sched_get_agg_node(pi, tc_node, agg_id);
if (!agg_node)
goto exit_agg_priority_per_tc;
if (num_vsis > hw->max_children[agg_node->tx_sched_layer])
goto exit_agg_priority_per_tc;
for (i = 0; i < num_vsis; i++) {
struct ice_sched_node *vsi_node;
bool vsi_handle_valid = false;
u16 vsi_handle;
status = ICE_ERR_PARAM;
vsi_handle = vsi_handle_arr[i];
if (!ice_is_vsi_valid(hw, vsi_handle))
goto exit_agg_priority_per_tc;
LIST_FOR_EACH_ENTRY(agg_vsi_info, &agg_info->agg_vsi_list,
ice_sched_agg_vsi_info, list_entry)
if (agg_vsi_info->vsi_handle == vsi_handle) {
vsi_handle_valid = true;
break;
}
if (!vsi_handle_valid)
goto exit_agg_priority_per_tc;
vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
if (!vsi_node)
goto exit_agg_priority_per_tc;
if (ice_sched_find_node_in_subtree(hw, agg_node, vsi_node)) {
status = ice_sched_cfg_sibl_node_prio(pi, vsi_node,
node_prio[i]);
if (status)
break;
status = ice_sched_save_vsi_prio(pi, vsi_handle, tc,
node_prio[i]);
if (status)
break;
}
}
exit_agg_priority_per_tc:
ice_release_lock(&pi->sched_lock);
return status;
}
int
ice_cfg_vsi_bw_alloc(struct ice_port_info *pi, u16 vsi_handle, u8 ena_tcmap,
enum ice_rl_type rl_type, u8 *bw_alloc)
{
int status = 0;
u8 tc;
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
return ICE_ERR_PARAM;
ice_acquire_lock(&pi->sched_lock);
ice_for_each_traffic_class(tc) {
struct ice_sched_node *tc_node, *vsi_node;
if (!ice_is_tc_ena(ena_tcmap, tc))
continue;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
continue;
vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
if (!vsi_node)
continue;
status = ice_sched_cfg_node_bw_alloc(pi->hw, vsi_node, rl_type,
bw_alloc[tc]);
if (status)
break;
status = ice_sched_save_vsi_bw_alloc(pi, vsi_handle, tc,
rl_type, bw_alloc[tc]);
if (status)
break;
}
ice_release_lock(&pi->sched_lock);
return status;
}
int
ice_cfg_agg_bw_alloc(struct ice_port_info *pi, u32 agg_id, u8 ena_tcmap,
enum ice_rl_type rl_type, u8 *bw_alloc)
{
struct ice_sched_agg_info *agg_info;
bool agg_id_present = false;
struct ice_hw *hw = pi->hw;
int status = 0;
u8 tc;
ice_acquire_lock(&pi->sched_lock);
LIST_FOR_EACH_ENTRY(agg_info, &hw->agg_list, ice_sched_agg_info,
list_entry)
if (agg_info->agg_id == agg_id) {
agg_id_present = true;
break;
}
if (!agg_id_present) {
status = ICE_ERR_PARAM;
goto exit_cfg_agg_bw_alloc;
}
ice_for_each_traffic_class(tc) {
struct ice_sched_node *tc_node, *agg_node;
if (!ice_is_tc_ena(ena_tcmap, tc))
continue;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
continue;
agg_node = ice_sched_get_agg_node(pi, tc_node, agg_id);
if (!agg_node)
continue;
status = ice_sched_cfg_node_bw_alloc(hw, agg_node, rl_type,
bw_alloc[tc]);
if (status)
break;
status = ice_sched_save_agg_bw_alloc(pi, agg_id, tc, rl_type,
bw_alloc[tc]);
if (status)
break;
}
exit_cfg_agg_bw_alloc:
ice_release_lock(&pi->sched_lock);
return status;
}
static u16 ice_sched_calc_wakeup(struct ice_hw *hw, s32 bw)
{
s64 bytes_per_sec, wakeup_int, wakeup_a, wakeup_b, wakeup_f;
s32 wakeup_f_int;
u16 wakeup = 0;
bytes_per_sec = DIV_S64((s64)bw * 1000, BITS_PER_BYTE);
wakeup_int = DIV_S64(hw->psm_clk_freq, bytes_per_sec);
if (wakeup_int > 63) {
wakeup = (u16)((1 << 15) | wakeup_int);
} else {
wakeup_b = (s64)ICE_RL_PROF_MULTIPLIER * wakeup_int;
wakeup_a = DIV_S64((s64)ICE_RL_PROF_MULTIPLIER *
hw->psm_clk_freq, bytes_per_sec);
wakeup_f = wakeup_a - wakeup_b;
if (wakeup_f > DIV_S64(ICE_RL_PROF_MULTIPLIER, 2))
wakeup_f += 1;
wakeup_f_int = (s32)DIV_S64(wakeup_f * ICE_RL_PROF_FRACTION,
ICE_RL_PROF_MULTIPLIER);
wakeup |= (u16)(wakeup_int << 9);
wakeup |= (u16)(0x1ff & wakeup_f_int);
}
return wakeup;
}
static int
ice_sched_bw_to_rl_profile(struct ice_hw *hw, u32 bw,
struct ice_aqc_rl_profile_elem *profile)
{
s64 bytes_per_sec, ts_rate, mv_tmp;
int status = ICE_ERR_PARAM;
bool found = false;
s32 encode = 0;
s64 mv = 0;
s32 i;
if (bw < ICE_SCHED_MIN_BW || bw > ICE_SCHED_MAX_BW)
return status;
bytes_per_sec = DIV_S64((s64)bw * 1000, BITS_PER_BYTE);
for (i = 0; i < 64; i++) {
u64 pow_result = BIT_ULL(i);
ts_rate = DIV_S64((s64)hw->psm_clk_freq,
pow_result * ICE_RL_PROF_TS_MULTIPLIER);
if (ts_rate <= 0)
continue;
mv_tmp = DIV_S64(bytes_per_sec * ICE_RL_PROF_MULTIPLIER,
ts_rate);
mv = round_up_64bit(mv_tmp, ICE_RL_PROF_MULTIPLIER);
if (mv > ICE_RL_PROF_ACCURACY_BYTES) {
encode = i;
found = true;
break;
}
}
if (found) {
u16 wm;
wm = ice_sched_calc_wakeup(hw, bw);
profile->rl_multiply = CPU_TO_LE16(mv);
profile->wake_up_calc = CPU_TO_LE16(wm);
profile->rl_encode = CPU_TO_LE16(encode);
status = 0;
} else {
status = ICE_ERR_DOES_NOT_EXIST;
}
return status;
}
static struct ice_aqc_rl_profile_info *
ice_sched_add_rl_profile(struct ice_hw *hw, enum ice_rl_type rl_type,
u32 bw, u8 layer_num)
{
struct ice_aqc_rl_profile_info *rl_prof_elem;
u16 profiles_added = 0, num_profiles = 1;
struct ice_aqc_rl_profile_elem *buf;
u8 profile_type;
int status;
if (!hw || layer_num >= hw->num_tx_sched_layers)
return NULL;
switch (rl_type) {
case ICE_MIN_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_CIR;
break;
case ICE_MAX_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_EIR;
break;
case ICE_SHARED_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_SRL;
break;
default:
return NULL;
}
LIST_FOR_EACH_ENTRY(rl_prof_elem, &hw->rl_prof_list[layer_num],
ice_aqc_rl_profile_info, list_entry)
if ((rl_prof_elem->profile.flags & ICE_AQC_RL_PROFILE_TYPE_M) ==
profile_type && rl_prof_elem->bw == bw)
return rl_prof_elem;
rl_prof_elem = (struct ice_aqc_rl_profile_info *)
ice_malloc(hw, sizeof(*rl_prof_elem));
if (!rl_prof_elem)
return NULL;
status = ice_sched_bw_to_rl_profile(hw, bw, &rl_prof_elem->profile);
if (status)
goto exit_add_rl_prof;
rl_prof_elem->bw = bw;
rl_prof_elem->profile.level = layer_num + 1;
rl_prof_elem->profile.flags = profile_type;
rl_prof_elem->profile.max_burst_size = CPU_TO_LE16(hw->max_burst_size);
buf = &rl_prof_elem->profile;
status = ice_aq_add_rl_profile(hw, num_profiles, buf, sizeof(*buf),
&profiles_added, NULL);
if (status || profiles_added != num_profiles)
goto exit_add_rl_prof;
rl_prof_elem->prof_id_ref = 0;
LIST_ADD(&rl_prof_elem->list_entry, &hw->rl_prof_list[layer_num]);
return rl_prof_elem;
exit_add_rl_prof:
ice_free(hw, rl_prof_elem);
return NULL;
}
static int
ice_sched_cfg_node_bw_lmt(struct ice_hw *hw, struct ice_sched_node *node,
enum ice_rl_type rl_type, u16 rl_prof_id)
{
struct ice_aqc_txsched_elem_data buf;
struct ice_aqc_txsched_elem *data;
buf = node->info;
data = &buf.data;
switch (rl_type) {
case ICE_MIN_BW:
data->valid_sections |= ICE_AQC_ELEM_VALID_CIR;
data->cir_bw.bw_profile_idx = CPU_TO_LE16(rl_prof_id);
break;
case ICE_MAX_BW:
data->valid_sections |= ICE_AQC_ELEM_VALID_EIR;
data->eir_bw.bw_profile_idx = CPU_TO_LE16(rl_prof_id);
break;
case ICE_SHARED_BW:
data->valid_sections |= ICE_AQC_ELEM_VALID_SHARED;
data->srl_id = CPU_TO_LE16(rl_prof_id);
break;
default:
return ICE_ERR_PARAM;
}
return ice_sched_update_elem(hw, node, &buf);
}
static u16
ice_sched_get_node_rl_prof_id(struct ice_sched_node *node,
enum ice_rl_type rl_type)
{
u16 rl_prof_id = ICE_SCHED_INVAL_PROF_ID;
struct ice_aqc_txsched_elem *data;
data = &node->info.data;
switch (rl_type) {
case ICE_MIN_BW:
if (data->valid_sections & ICE_AQC_ELEM_VALID_CIR)
rl_prof_id = LE16_TO_CPU(data->cir_bw.bw_profile_idx);
break;
case ICE_MAX_BW:
if (data->valid_sections & ICE_AQC_ELEM_VALID_EIR)
rl_prof_id = LE16_TO_CPU(data->eir_bw.bw_profile_idx);
break;
case ICE_SHARED_BW:
if (data->valid_sections & ICE_AQC_ELEM_VALID_SHARED)
rl_prof_id = LE16_TO_CPU(data->srl_id);
break;
default:
break;
}
return rl_prof_id;
}
static u8
ice_sched_get_rl_prof_layer(struct ice_port_info *pi, enum ice_rl_type rl_type,
u8 layer_index)
{
struct ice_hw *hw = pi->hw;
if (layer_index >= hw->num_tx_sched_layers)
return ICE_SCHED_INVAL_LAYER_NUM;
switch (rl_type) {
case ICE_MIN_BW:
if (hw->layer_info[layer_index].max_cir_rl_profiles)
return layer_index;
break;
case ICE_MAX_BW:
if (hw->layer_info[layer_index].max_eir_rl_profiles)
return layer_index;
break;
case ICE_SHARED_BW:
if (hw->layer_info[layer_index].max_srl_profiles)
return layer_index;
else if (layer_index < hw->num_tx_sched_layers - 1 &&
hw->layer_info[layer_index + 1].max_srl_profiles)
return layer_index + 1;
else if (layer_index > 0 &&
hw->layer_info[layer_index - 1].max_srl_profiles)
return layer_index - 1;
break;
default:
break;
}
return ICE_SCHED_INVAL_LAYER_NUM;
}
static struct ice_sched_node *
ice_sched_get_srl_node(struct ice_sched_node *node, u8 srl_layer)
{
if (srl_layer > node->tx_sched_layer)
return node->children[0];
else if (srl_layer < node->tx_sched_layer)
return node->parent;
else
return node;
}
static int
ice_sched_rm_rl_profile(struct ice_hw *hw, u8 layer_num, u8 profile_type,
u16 profile_id)
{
struct ice_aqc_rl_profile_info *rl_prof_elem;
int status = 0;
if (!hw || layer_num >= hw->num_tx_sched_layers)
return ICE_ERR_PARAM;
LIST_FOR_EACH_ENTRY(rl_prof_elem, &hw->rl_prof_list[layer_num],
ice_aqc_rl_profile_info, list_entry)
if ((rl_prof_elem->profile.flags & ICE_AQC_RL_PROFILE_TYPE_M) ==
profile_type &&
LE16_TO_CPU(rl_prof_elem->profile.profile_id) ==
profile_id) {
if (rl_prof_elem->prof_id_ref)
rl_prof_elem->prof_id_ref--;
status = ice_sched_del_rl_profile(hw, rl_prof_elem);
if (status && status != ICE_ERR_IN_USE)
ice_debug(hw, ICE_DBG_SCHED, "Remove rl profile failed\n");
break;
}
if (status == ICE_ERR_IN_USE)
status = 0;
return status;
}
static int
ice_sched_set_node_bw_dflt(struct ice_port_info *pi,
struct ice_sched_node *node,
enum ice_rl_type rl_type, u8 layer_num)
{
struct ice_hw *hw;
u8 profile_type;
u16 rl_prof_id;
int status;
u16 old_id;
hw = pi->hw;
switch (rl_type) {
case ICE_MIN_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_CIR;
rl_prof_id = ICE_SCHED_DFLT_RL_PROF_ID;
break;
case ICE_MAX_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_EIR;
rl_prof_id = ICE_SCHED_DFLT_RL_PROF_ID;
break;
case ICE_SHARED_BW:
profile_type = ICE_AQC_RL_PROFILE_TYPE_SRL;
rl_prof_id = ICE_SCHED_NO_SHARED_RL_PROF_ID;
break;
default:
return ICE_ERR_PARAM;
}
old_id = ice_sched_get_node_rl_prof_id(node, rl_type);
status = ice_sched_cfg_node_bw_lmt(hw, node, rl_type, rl_prof_id);
if (status)
return status;
if (old_id == ICE_SCHED_DFLT_RL_PROF_ID ||
old_id == ICE_SCHED_INVAL_PROF_ID)
return 0;
return ice_sched_rm_rl_profile(hw, layer_num, profile_type, old_id);
}
int
ice_sched_set_node_bw(struct ice_port_info *pi, struct ice_sched_node *node,
enum ice_rl_type rl_type, u32 bw, u8 layer_num)
{
struct ice_aqc_rl_profile_info *rl_prof_info;
struct ice_hw *hw = pi->hw;
u16 old_id, rl_prof_id;
int status = ICE_ERR_PARAM;
rl_prof_info = ice_sched_add_rl_profile(hw, rl_type, bw, layer_num);
if (!rl_prof_info)
return status;
rl_prof_id = LE16_TO_CPU(rl_prof_info->profile.profile_id);
old_id = ice_sched_get_node_rl_prof_id(node, rl_type);
status = ice_sched_cfg_node_bw_lmt(hw, node, rl_type, rl_prof_id);
if (status)
return status;
rl_prof_info->prof_id_ref++;
if ((old_id == ICE_SCHED_DFLT_RL_PROF_ID && rl_type != ICE_SHARED_BW) ||
old_id == ICE_SCHED_INVAL_PROF_ID || old_id == rl_prof_id)
return 0;
return ice_sched_rm_rl_profile(hw, layer_num,
rl_prof_info->profile.flags &
ICE_AQC_RL_PROFILE_TYPE_M, old_id);
}
int
ice_sched_set_node_priority(struct ice_port_info *pi, struct ice_sched_node *node,
u16 priority)
{
struct ice_aqc_txsched_elem_data buf;
struct ice_aqc_txsched_elem *data;
buf = node->info;
data = &buf.data;
data->valid_sections |= ICE_AQC_ELEM_VALID_GENERIC;
data->generic |= ICE_AQC_ELEM_GENERIC_PRIO_M &
(priority << ICE_AQC_ELEM_GENERIC_PRIO_S);
return ice_sched_update_elem(pi->hw, node, &buf);
}
int
ice_sched_set_node_weight(struct ice_port_info *pi, struct ice_sched_node *node, u16 weight)
{
struct ice_aqc_txsched_elem_data buf;
struct ice_aqc_txsched_elem *data;
buf = node->info;
data = &buf.data;
data->valid_sections = ICE_AQC_ELEM_VALID_CIR | ICE_AQC_ELEM_VALID_EIR |
ICE_AQC_ELEM_VALID_GENERIC;
data->cir_bw.bw_alloc = CPU_TO_LE16(weight);
data->eir_bw.bw_alloc = CPU_TO_LE16(weight);
data->generic |= ICE_AQC_ELEM_GENERIC_SP_M &
(0x0 << ICE_AQC_ELEM_GENERIC_SP_S);
return ice_sched_update_elem(pi->hw, node, &buf);
}
int
ice_sched_set_node_bw_lmt(struct ice_port_info *pi, struct ice_sched_node *node,
enum ice_rl_type rl_type, u32 bw)
{
struct ice_hw *hw;
u8 layer_num;
if (!pi)
return ICE_ERR_PARAM;
hw = pi->hw;
ice_sched_rm_unused_rl_prof(hw);
layer_num = ice_sched_get_rl_prof_layer(pi, rl_type,
node->tx_sched_layer);
if (layer_num >= hw->num_tx_sched_layers)
return ICE_ERR_PARAM;
if (bw == ICE_SCHED_DFLT_BW)
return ice_sched_set_node_bw_dflt(pi, node, rl_type, layer_num);
return ice_sched_set_node_bw(pi, node, rl_type, bw, layer_num);
}
static int
ice_sched_set_node_bw_dflt_lmt(struct ice_port_info *pi,
struct ice_sched_node *node,
enum ice_rl_type rl_type)
{
return ice_sched_set_node_bw_lmt(pi, node, rl_type,
ICE_SCHED_DFLT_BW);
}
static int
ice_sched_validate_srl_node(struct ice_sched_node *node, u8 sel_layer)
{
if (sel_layer == node->tx_sched_layer ||
((sel_layer == node->tx_sched_layer + 1) &&
node->num_children == 1) ||
((sel_layer == node->tx_sched_layer - 1) &&
(node->parent && node->parent->num_children == 1)))
return 0;
return ICE_ERR_CFG;
}
static int
ice_sched_save_q_bw(struct ice_q_ctx *q_ctx, enum ice_rl_type rl_type, u32 bw)
{
switch (rl_type) {
case ICE_MIN_BW:
ice_set_clear_cir_bw(&q_ctx->bw_t_info, bw);
break;
case ICE_MAX_BW:
ice_set_clear_eir_bw(&q_ctx->bw_t_info, bw);
break;
case ICE_SHARED_BW:
ice_set_clear_shared_bw(&q_ctx->bw_t_info, bw);
break;
default:
return ICE_ERR_PARAM;
}
return 0;
}
static int
ice_sched_set_q_bw_lmt(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
u16 q_handle, enum ice_rl_type rl_type, u32 bw)
{
struct ice_sched_node *node;
struct ice_q_ctx *q_ctx;
int status = ICE_ERR_PARAM;
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
return ICE_ERR_PARAM;
ice_acquire_lock(&pi->sched_lock);
q_ctx = ice_get_lan_q_ctx(pi->hw, vsi_handle, tc, q_handle);
if (!q_ctx)
goto exit_q_bw_lmt;
node = ice_sched_find_node_by_teid(pi->root, q_ctx->q_teid);
if (!node) {
ice_debug(pi->hw, ICE_DBG_SCHED, "Wrong q_teid\n");
goto exit_q_bw_lmt;
}
if (node->info.data.elem_type != ICE_AQC_ELEM_TYPE_LEAF)
goto exit_q_bw_lmt;
if (rl_type == ICE_SHARED_BW) {
u8 sel_layer;
sel_layer = ice_sched_get_rl_prof_layer(pi, rl_type,
node->tx_sched_layer);
if (sel_layer >= pi->hw->num_tx_sched_layers) {
status = ICE_ERR_PARAM;
goto exit_q_bw_lmt;
}
status = ice_sched_validate_srl_node(node, sel_layer);
if (status)
goto exit_q_bw_lmt;
}
if (bw == ICE_SCHED_DFLT_BW)
status = ice_sched_set_node_bw_dflt_lmt(pi, node, rl_type);
else
status = ice_sched_set_node_bw_lmt(pi, node, rl_type, bw);
if (!status)
status = ice_sched_save_q_bw(q_ctx, rl_type, bw);
exit_q_bw_lmt:
ice_release_lock(&pi->sched_lock);
return status;
}
int
ice_cfg_q_bw_lmt(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
u16 q_handle, enum ice_rl_type rl_type, u32 bw)
{
return ice_sched_set_q_bw_lmt(pi, vsi_handle, tc, q_handle, rl_type,
bw);
}
int
ice_cfg_q_bw_dflt_lmt(struct ice_port_info *pi, u16 vsi_handle, u8 tc,
u16 q_handle, enum ice_rl_type rl_type)
{
return ice_sched_set_q_bw_lmt(pi, vsi_handle, tc, q_handle, rl_type,
ICE_SCHED_DFLT_BW);
}
static int
ice_sched_save_tc_node_bw(struct ice_port_info *pi, u8 tc,
enum ice_rl_type rl_type, u32 bw)
{
if (tc >= ICE_MAX_TRAFFIC_CLASS)
return ICE_ERR_PARAM;
switch (rl_type) {
case ICE_MIN_BW:
ice_set_clear_cir_bw(&pi->tc_node_bw_t_info[tc], bw);
break;
case ICE_MAX_BW:
ice_set_clear_eir_bw(&pi->tc_node_bw_t_info[tc], bw);
break;
case ICE_SHARED_BW:
ice_set_clear_shared_bw(&pi->tc_node_bw_t_info[tc], bw);
break;
default:
return ICE_ERR_PARAM;
}
return 0;
}
#define ICE_SCHED_GENERIC_STRICT_MODE BIT(4)
#define ICE_SCHED_GENERIC_PRIO_S 1
static int
ice_sched_set_tc_node_bw_lmt(struct ice_port_info *pi, u8 tc,
enum ice_rl_type rl_type, u32 bw)
{
struct ice_aqc_txsched_elem_data buf;
struct ice_aqc_txsched_elem *data;
struct ice_sched_node *tc_node;
int status = ICE_ERR_PARAM;
if (tc >= ICE_MAX_TRAFFIC_CLASS)
return status;
ice_acquire_lock(&pi->sched_lock);
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
goto exit_set_tc_node_bw;
buf = tc_node->info;
data = &buf.data;
data->valid_sections = ICE_AQC_ELEM_VALID_GENERIC;
data->generic = (tc << ICE_SCHED_GENERIC_PRIO_S) |
ICE_SCHED_GENERIC_STRICT_MODE;
status = ice_sched_update_elem(pi->hw, tc_node, &buf);
if (status)
goto exit_set_tc_node_bw;
if (bw == ICE_SCHED_DFLT_BW)
status = ice_sched_set_node_bw_dflt_lmt(pi, tc_node, rl_type);
else
status = ice_sched_set_node_bw_lmt(pi, tc_node, rl_type, bw);
if (!status)
status = ice_sched_save_tc_node_bw(pi, tc, rl_type, bw);
exit_set_tc_node_bw:
ice_release_lock(&pi->sched_lock);
return status;
}
int
ice_cfg_tc_node_bw_lmt(struct ice_port_info *pi, u8 tc,
enum ice_rl_type rl_type, u32 bw)
{
return ice_sched_set_tc_node_bw_lmt(pi, tc, rl_type, bw);
}
int
ice_cfg_tc_node_bw_dflt_lmt(struct ice_port_info *pi, u8 tc,
enum ice_rl_type rl_type)
{
return ice_sched_set_tc_node_bw_lmt(pi, tc, rl_type, ICE_SCHED_DFLT_BW);
}
static int
ice_sched_save_tc_node_bw_alloc(struct ice_port_info *pi, u8 tc,
enum ice_rl_type rl_type, u16 bw_alloc)
{
if (tc >= ICE_MAX_TRAFFIC_CLASS)
return ICE_ERR_PARAM;
switch (rl_type) {
case ICE_MIN_BW:
ice_set_clear_cir_bw_alloc(&pi->tc_node_bw_t_info[tc],
bw_alloc);
break;
case ICE_MAX_BW:
ice_set_clear_eir_bw_alloc(&pi->tc_node_bw_t_info[tc],
bw_alloc);
break;
default:
return ICE_ERR_PARAM;
}
return 0;
}
static int
ice_sched_set_tc_node_bw_alloc(struct ice_port_info *pi, u8 tc,
enum ice_rl_type rl_type, u8 bw_alloc)
{
struct ice_sched_node *tc_node;
int status = ICE_ERR_PARAM;
if (tc >= ICE_MAX_TRAFFIC_CLASS)
return status;
ice_acquire_lock(&pi->sched_lock);
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
goto exit_set_tc_node_bw_alloc;
status = ice_sched_cfg_node_bw_alloc(pi->hw, tc_node, rl_type,
bw_alloc);
if (status)
goto exit_set_tc_node_bw_alloc;
status = ice_sched_save_tc_node_bw_alloc(pi, tc, rl_type, bw_alloc);
exit_set_tc_node_bw_alloc:
ice_release_lock(&pi->sched_lock);
return status;
}
int
ice_cfg_tc_node_bw_alloc(struct ice_port_info *pi, u8 tc,
enum ice_rl_type rl_type, u8 bw_alloc)
{
return ice_sched_set_tc_node_bw_alloc(pi, tc, rl_type, bw_alloc);
}
int
ice_sched_set_agg_bw_dflt_lmt(struct ice_port_info *pi, u16 vsi_handle)
{
struct ice_vsi_ctx *vsi_ctx;
int status = 0;
u8 tc;
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
return ICE_ERR_PARAM;
vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
if (!vsi_ctx)
return ICE_ERR_PARAM;
ice_for_each_traffic_class(tc) {
struct ice_sched_node *node;
node = vsi_ctx->sched.ag_node[tc];
if (!node)
continue;
status = ice_sched_set_node_bw_dflt_lmt(pi, node, ICE_MIN_BW);
if (status)
break;
status = ice_sched_set_node_bw_dflt_lmt(pi, node, ICE_MAX_BW);
if (status)
break;
status = ice_sched_set_node_bw_dflt_lmt(pi, node,
ICE_SHARED_BW);
if (status)
break;
}
return status;
}
static struct ice_sched_node *
ice_sched_get_node_by_id_type(struct ice_port_info *pi, u32 id,
enum ice_agg_type agg_type, u8 tc)
{
struct ice_sched_node *node = NULL;
switch (agg_type) {
case ICE_AGG_TYPE_VSI: {
struct ice_vsi_ctx *vsi_ctx;
u16 vsi_handle = (u16)id;
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
break;
vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
if (!vsi_ctx)
break;
node = vsi_ctx->sched.vsi_node[tc];
break;
}
case ICE_AGG_TYPE_AGG: {
struct ice_sched_node *tc_node;
tc_node = ice_sched_get_tc_node(pi, tc);
if (tc_node)
node = ice_sched_get_agg_node(pi, tc_node, id);
break;
}
case ICE_AGG_TYPE_Q:
node = ice_sched_find_node_by_teid(pi->root, id);
break;
case ICE_AGG_TYPE_QG: {
struct ice_sched_node *child_node;
child_node = ice_sched_find_node_by_teid(pi->root, id);
if (!child_node)
break;
node = child_node->parent;
break;
}
default:
break;
}
return node;
}
int
ice_sched_set_node_bw_lmt_per_tc(struct ice_port_info *pi, u32 id,
enum ice_agg_type agg_type, u8 tc,
enum ice_rl_type rl_type, u32 bw)
{
struct ice_sched_node *node;
int status = ICE_ERR_PARAM;
if (!pi)
return status;
if (rl_type == ICE_UNKNOWN_BW)
return status;
ice_acquire_lock(&pi->sched_lock);
node = ice_sched_get_node_by_id_type(pi, id, agg_type, tc);
if (!node) {
ice_debug(pi->hw, ICE_DBG_SCHED, "Wrong id, agg type, or tc\n");
goto exit_set_node_bw_lmt_per_tc;
}
if (bw == ICE_SCHED_DFLT_BW)
status = ice_sched_set_node_bw_dflt_lmt(pi, node, rl_type);
else
status = ice_sched_set_node_bw_lmt(pi, node, rl_type, bw);
exit_set_node_bw_lmt_per_tc:
ice_release_lock(&pi->sched_lock);
return status;
}
static int
ice_sched_validate_vsi_srl_node(struct ice_port_info *pi, u16 vsi_handle)
{
u8 sel_layer = ICE_SCHED_INVAL_LAYER_NUM;
u8 tc;
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
return ICE_ERR_PARAM;
ice_for_each_traffic_class(tc) {
struct ice_sched_node *tc_node, *vsi_node;
enum ice_rl_type rl_type = ICE_SHARED_BW;
int status;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
continue;
vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
if (!vsi_node)
continue;
if (sel_layer == ICE_SCHED_INVAL_LAYER_NUM) {
u8 node_layer = vsi_node->tx_sched_layer;
u8 layer_num;
layer_num = ice_sched_get_rl_prof_layer(pi, rl_type,
node_layer);
if (layer_num >= pi->hw->num_tx_sched_layers)
return ICE_ERR_PARAM;
sel_layer = layer_num;
}
status = ice_sched_validate_srl_node(vsi_node, sel_layer);
if (status)
return status;
}
return 0;
}
static int
ice_sched_set_save_vsi_srl_node_bw(struct ice_port_info *pi, u16 vsi_handle,
u8 tc, struct ice_sched_node *srl_node,
enum ice_rl_type rl_type, u32 bw)
{
int status;
if (bw == ICE_SCHED_DFLT_BW) {
status = ice_sched_set_node_bw_dflt_lmt(pi, srl_node, rl_type);
} else {
status = ice_sched_set_node_bw_lmt(pi, srl_node, rl_type, bw);
if (status)
return status;
status = ice_sched_save_vsi_bw(pi, vsi_handle, tc, rl_type, bw);
}
return status;
}
static int
ice_sched_set_vsi_node_srl_per_tc(struct ice_port_info *pi, u16 vsi_handle,
u8 tc, u32 min_bw, u32 max_bw, u32 shared_bw)
{
struct ice_sched_node *tc_node, *vsi_node, *cfg_node;
u8 layer_num;
int status;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
return ICE_ERR_CFG;
vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
if (!vsi_node)
return ICE_ERR_CFG;
layer_num = ice_sched_get_rl_prof_layer(pi, ICE_SHARED_BW,
vsi_node->tx_sched_layer);
if (layer_num >= pi->hw->num_tx_sched_layers)
return ICE_ERR_PARAM;
cfg_node = ice_sched_get_srl_node(vsi_node, layer_num);
if (!cfg_node)
return ICE_ERR_CFG;
status = ice_sched_set_save_vsi_srl_node_bw(pi, vsi_handle, tc,
cfg_node, ICE_MIN_BW,
min_bw);
if (status)
return status;
status = ice_sched_set_save_vsi_srl_node_bw(pi, vsi_handle, tc,
cfg_node, ICE_MAX_BW,
max_bw);
if (status)
return status;
return ice_sched_set_save_vsi_srl_node_bw(pi, vsi_handle, tc, cfg_node,
ICE_SHARED_BW, shared_bw);
}
int
ice_sched_set_vsi_bw_shared_lmt(struct ice_port_info *pi, u16 vsi_handle,
u32 min_bw, u32 max_bw, u32 shared_bw)
{
int status = 0;
u8 tc;
if (!pi)
return ICE_ERR_PARAM;
if (!ice_is_vsi_valid(pi->hw, vsi_handle))
return ICE_ERR_PARAM;
ice_acquire_lock(&pi->sched_lock);
status = ice_sched_validate_vsi_srl_node(pi, vsi_handle);
if (status)
goto exit_set_vsi_bw_shared_lmt;
ice_for_each_traffic_class(tc) {
struct ice_sched_node *tc_node, *vsi_node;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
continue;
vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
if (!vsi_node)
continue;
status = ice_sched_set_vsi_node_srl_per_tc(pi, vsi_handle, tc,
min_bw, max_bw,
shared_bw);
if (status)
break;
}
exit_set_vsi_bw_shared_lmt:
ice_release_lock(&pi->sched_lock);
return status;
}
static int
ice_sched_validate_agg_srl_node(struct ice_port_info *pi, u32 agg_id)
{
u8 sel_layer = ICE_SCHED_INVAL_LAYER_NUM;
struct ice_sched_agg_info *agg_info;
bool agg_id_present = false;
int status = 0;
u8 tc;
LIST_FOR_EACH_ENTRY(agg_info, &pi->hw->agg_list, ice_sched_agg_info,
list_entry)
if (agg_info->agg_id == agg_id) {
agg_id_present = true;
break;
}
if (!agg_id_present)
return ICE_ERR_PARAM;
ice_for_each_traffic_class(tc) {
struct ice_sched_node *tc_node, *agg_node;
enum ice_rl_type rl_type = ICE_SHARED_BW;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
continue;
agg_node = ice_sched_get_agg_node(pi, tc_node, agg_id);
if (!agg_node)
continue;
if (sel_layer == ICE_SCHED_INVAL_LAYER_NUM) {
u8 node_layer = agg_node->tx_sched_layer;
u8 layer_num;
layer_num = ice_sched_get_rl_prof_layer(pi, rl_type,
node_layer);
if (layer_num >= pi->hw->num_tx_sched_layers)
return ICE_ERR_PARAM;
sel_layer = layer_num;
}
status = ice_sched_validate_srl_node(agg_node, sel_layer);
if (status)
break;
}
return status;
}
static int
ice_sched_validate_agg_id(struct ice_port_info *pi, u32 agg_id)
{
struct ice_sched_agg_info *agg_info;
struct ice_sched_agg_info *tmp;
bool agg_id_present = false;
int status;
status = ice_sched_validate_agg_srl_node(pi, agg_id);
if (status)
return status;
LIST_FOR_EACH_ENTRY_SAFE(agg_info, tmp, &pi->hw->agg_list,
ice_sched_agg_info, list_entry)
if (agg_info->agg_id == agg_id) {
agg_id_present = true;
break;
}
if (!agg_id_present)
return ICE_ERR_PARAM;
return 0;
}
static int
ice_sched_set_save_agg_srl_node_bw(struct ice_port_info *pi, u32 agg_id, u8 tc,
struct ice_sched_node *srl_node,
enum ice_rl_type rl_type, u32 bw)
{
int status;
if (bw == ICE_SCHED_DFLT_BW) {
status = ice_sched_set_node_bw_dflt_lmt(pi, srl_node, rl_type);
} else {
status = ice_sched_set_node_bw_lmt(pi, srl_node, rl_type, bw);
if (status)
return status;
status = ice_sched_save_agg_bw(pi, agg_id, tc, rl_type, bw);
}
return status;
}
static int
ice_sched_set_agg_node_srl_per_tc(struct ice_port_info *pi, u32 agg_id,
u8 tc, u32 min_bw, u32 max_bw, u32 shared_bw)
{
struct ice_sched_node *tc_node, *agg_node, *cfg_node;
enum ice_rl_type rl_type = ICE_SHARED_BW;
int status = ICE_ERR_CFG;
u8 layer_num;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
return ICE_ERR_CFG;
agg_node = ice_sched_get_agg_node(pi, tc_node, agg_id);
if (!agg_node)
return ICE_ERR_CFG;
layer_num = ice_sched_get_rl_prof_layer(pi, rl_type,
agg_node->tx_sched_layer);
if (layer_num >= pi->hw->num_tx_sched_layers)
return ICE_ERR_PARAM;
cfg_node = ice_sched_get_srl_node(agg_node, layer_num);
if (!cfg_node)
return ICE_ERR_CFG;
status = ice_sched_set_save_agg_srl_node_bw(pi, agg_id, tc, cfg_node,
ICE_MIN_BW, min_bw);
if (status)
return status;
status = ice_sched_set_save_agg_srl_node_bw(pi, agg_id, tc, cfg_node,
ICE_MAX_BW, max_bw);
if (status)
return status;
status = ice_sched_set_save_agg_srl_node_bw(pi, agg_id, tc, cfg_node,
ICE_SHARED_BW, shared_bw);
return status;
}
int
ice_sched_set_agg_bw_shared_lmt(struct ice_port_info *pi, u32 agg_id,
u32 min_bw, u32 max_bw, u32 shared_bw)
{
int status;
u8 tc;
if (!pi)
return ICE_ERR_PARAM;
ice_acquire_lock(&pi->sched_lock);
status = ice_sched_validate_agg_id(pi, agg_id);
if (status)
goto exit_agg_bw_shared_lmt;
ice_for_each_traffic_class(tc) {
struct ice_sched_node *tc_node, *agg_node;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
continue;
agg_node = ice_sched_get_agg_node(pi, tc_node, agg_id);
if (!agg_node)
continue;
status = ice_sched_set_agg_node_srl_per_tc(pi, agg_id, tc,
min_bw, max_bw,
shared_bw);
if (status)
break;
}
exit_agg_bw_shared_lmt:
ice_release_lock(&pi->sched_lock);
return status;
}
int
ice_sched_set_agg_bw_shared_lmt_per_tc(struct ice_port_info *pi, u32 agg_id,
u8 tc, u32 min_bw, u32 max_bw,
u32 shared_bw)
{
int status;
if (!pi)
return ICE_ERR_PARAM;
ice_acquire_lock(&pi->sched_lock);
status = ice_sched_validate_agg_id(pi, agg_id);
if (status)
goto exit_agg_bw_shared_lmt_per_tc;
status = ice_sched_set_agg_node_srl_per_tc(pi, agg_id, tc, min_bw,
max_bw, shared_bw);
exit_agg_bw_shared_lmt_per_tc:
ice_release_lock(&pi->sched_lock);
return status;
}
int
ice_sched_cfg_sibl_node_prio(struct ice_port_info *pi,
struct ice_sched_node *node, u8 priority)
{
struct ice_aqc_txsched_elem_data buf;
struct ice_aqc_txsched_elem *data;
struct ice_hw *hw = pi->hw;
int status;
if (!hw)
return ICE_ERR_PARAM;
buf = node->info;
data = &buf.data;
data->valid_sections |= ICE_AQC_ELEM_VALID_GENERIC;
priority = (priority << ICE_AQC_ELEM_GENERIC_PRIO_S) &
ICE_AQC_ELEM_GENERIC_PRIO_M;
data->generic &= ~ICE_AQC_ELEM_GENERIC_PRIO_M;
data->generic |= priority;
status = ice_sched_update_elem(hw, node, &buf);
return status;
}
int ice_cfg_rl_burst_size(struct ice_hw *hw, u32 bytes)
{
u16 burst_size_to_prog;
if (bytes < ICE_MIN_BURST_SIZE_ALLOWED ||
bytes > ICE_MAX_BURST_SIZE_ALLOWED)
return ICE_ERR_PARAM;
if (ice_round_to_num(bytes, 64) <=
ICE_MAX_BURST_SIZE_64_BYTE_GRANULARITY) {
burst_size_to_prog = ICE_64_BYTE_GRANULARITY;
bytes = ice_round_to_num(bytes, 64);
burst_size_to_prog |= (u16)(bytes / 64);
} else {
burst_size_to_prog = ICE_KBYTE_GRANULARITY;
bytes = ice_round_to_num(bytes, 1024);
if (bytes > ICE_MAX_BURST_SIZE_KBYTE_GRANULARITY)
bytes = ICE_MAX_BURST_SIZE_KBYTE_GRANULARITY;
burst_size_to_prog |= (u16)(bytes / 1024);
}
hw->max_burst_size = burst_size_to_prog;
return 0;
}
static int
ice_sched_replay_node_prio(struct ice_hw *hw, struct ice_sched_node *node,
u8 priority)
{
struct ice_aqc_txsched_elem_data buf;
struct ice_aqc_txsched_elem *data;
int status;
buf = node->info;
data = &buf.data;
data->valid_sections |= ICE_AQC_ELEM_VALID_GENERIC;
data->generic = priority;
status = ice_sched_update_elem(hw, node, &buf);
return status;
}
static int
ice_sched_replay_node_bw(struct ice_hw *hw, struct ice_sched_node *node,
struct ice_bw_type_info *bw_t_info)
{
struct ice_port_info *pi = hw->port_info;
int status = ICE_ERR_PARAM;
u16 bw_alloc;
if (!node)
return status;
if (!ice_is_any_bit_set(bw_t_info->bw_t_bitmap, ICE_BW_TYPE_CNT))
return 0;
if (ice_is_bit_set(bw_t_info->bw_t_bitmap, ICE_BW_TYPE_PRIO)) {
status = ice_sched_replay_node_prio(hw, node,
bw_t_info->generic);
if (status)
return status;
}
if (ice_is_bit_set(bw_t_info->bw_t_bitmap, ICE_BW_TYPE_CIR)) {
status = ice_sched_set_node_bw_lmt(pi, node, ICE_MIN_BW,
bw_t_info->cir_bw.bw);
if (status)
return status;
}
if (ice_is_bit_set(bw_t_info->bw_t_bitmap, ICE_BW_TYPE_CIR_WT)) {
bw_alloc = bw_t_info->cir_bw.bw_alloc;
status = ice_sched_cfg_node_bw_alloc(hw, node, ICE_MIN_BW,
bw_alloc);
if (status)
return status;
}
if (ice_is_bit_set(bw_t_info->bw_t_bitmap, ICE_BW_TYPE_EIR)) {
status = ice_sched_set_node_bw_lmt(pi, node, ICE_MAX_BW,
bw_t_info->eir_bw.bw);
if (status)
return status;
}
if (ice_is_bit_set(bw_t_info->bw_t_bitmap, ICE_BW_TYPE_EIR_WT)) {
bw_alloc = bw_t_info->eir_bw.bw_alloc;
status = ice_sched_cfg_node_bw_alloc(hw, node, ICE_MAX_BW,
bw_alloc);
if (status)
return status;
}
if (ice_is_bit_set(bw_t_info->bw_t_bitmap, ICE_BW_TYPE_SHARED))
status = ice_sched_set_node_bw_lmt(pi, node, ICE_SHARED_BW,
bw_t_info->shared_bw);
return status;
}
static int
ice_sched_replay_agg_bw(struct ice_hw *hw, struct ice_sched_agg_info *agg_info)
{
struct ice_sched_node *tc_node, *agg_node;
int status = 0;
u8 tc;
if (!agg_info)
return ICE_ERR_PARAM;
ice_for_each_traffic_class(tc) {
if (!ice_is_any_bit_set(agg_info->bw_t_info[tc].bw_t_bitmap,
ICE_BW_TYPE_CNT))
continue;
tc_node = ice_sched_get_tc_node(hw->port_info, tc);
if (!tc_node) {
status = ICE_ERR_PARAM;
break;
}
agg_node = ice_sched_get_agg_node(hw->port_info, tc_node,
agg_info->agg_id);
if (!agg_node) {
status = ICE_ERR_PARAM;
break;
}
status = ice_sched_replay_node_bw(hw, agg_node,
&agg_info->bw_t_info[tc]);
if (status)
break;
}
return status;
}
static void
ice_sched_get_ena_tc_bitmap(struct ice_port_info *pi, ice_bitmap_t *tc_bitmap,
ice_bitmap_t *ena_tc_bitmap)
{
u8 tc;
ice_for_each_traffic_class(tc)
if (ice_is_tc_ena(*tc_bitmap, tc) &&
(ice_sched_get_tc_node(pi, tc)))
ice_set_bit(tc, ena_tc_bitmap);
}
void ice_sched_replay_agg(struct ice_hw *hw)
{
struct ice_port_info *pi = hw->port_info;
struct ice_sched_agg_info *agg_info;
ice_acquire_lock(&pi->sched_lock);
LIST_FOR_EACH_ENTRY(agg_info, &hw->agg_list, ice_sched_agg_info,
list_entry)
if (!ice_cmp_bitmap(agg_info->tc_bitmap,
agg_info->replay_tc_bitmap,
ICE_MAX_TRAFFIC_CLASS)) {
ice_declare_bitmap(replay_bitmap,
ICE_MAX_TRAFFIC_CLASS);
int status;
ice_zero_bitmap(replay_bitmap, ICE_MAX_TRAFFIC_CLASS);
ice_sched_get_ena_tc_bitmap(pi,
agg_info->replay_tc_bitmap,
replay_bitmap);
status = ice_sched_cfg_agg(hw->port_info,
agg_info->agg_id,
ICE_AGG_TYPE_AGG,
replay_bitmap);
if (status) {
ice_info(hw, "Replay agg id[%d] failed\n",
agg_info->agg_id);
continue;
}
status = ice_sched_replay_agg_bw(hw, agg_info);
if (status)
ice_info(hw, "Replay agg bw [id=%d] failed\n",
agg_info->agg_id);
}
ice_release_lock(&pi->sched_lock);
}
void ice_sched_replay_agg_vsi_preinit(struct ice_hw *hw)
{
struct ice_port_info *pi = hw->port_info;
struct ice_sched_agg_info *agg_info;
ice_acquire_lock(&pi->sched_lock);
LIST_FOR_EACH_ENTRY(agg_info, &hw->agg_list, ice_sched_agg_info,
list_entry) {
struct ice_sched_agg_vsi_info *agg_vsi_info;
agg_info->tc_bitmap[0] = 0;
LIST_FOR_EACH_ENTRY(agg_vsi_info, &agg_info->agg_vsi_list,
ice_sched_agg_vsi_info, list_entry)
agg_vsi_info->tc_bitmap[0] = 0;
}
ice_release_lock(&pi->sched_lock);
}
int ice_sched_replay_root_node_bw(struct ice_port_info *pi)
{
int status = 0;
if (!pi->hw)
return ICE_ERR_PARAM;
ice_acquire_lock(&pi->sched_lock);
status = ice_sched_replay_node_bw(pi->hw, pi->root,
&pi->root_node_bw_t_info);
ice_release_lock(&pi->sched_lock);
return status;
}
int ice_sched_replay_tc_node_bw(struct ice_port_info *pi)
{
int status = 0;
u8 tc;
if (!pi->hw)
return ICE_ERR_PARAM;
ice_acquire_lock(&pi->sched_lock);
ice_for_each_traffic_class(tc) {
struct ice_sched_node *tc_node;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
continue;
status = ice_sched_replay_node_bw(pi->hw, tc_node,
&pi->tc_node_bw_t_info[tc]);
if (status)
break;
}
ice_release_lock(&pi->sched_lock);
return status;
}
static int
ice_sched_replay_vsi_bw(struct ice_hw *hw, u16 vsi_handle,
ice_bitmap_t *tc_bitmap)
{
struct ice_sched_node *vsi_node, *tc_node;
struct ice_port_info *pi = hw->port_info;
struct ice_bw_type_info *bw_t_info;
struct ice_vsi_ctx *vsi_ctx;
int status = 0;
u8 tc;
vsi_ctx = ice_get_vsi_ctx(pi->hw, vsi_handle);
if (!vsi_ctx)
return ICE_ERR_PARAM;
ice_for_each_traffic_class(tc) {
if (!ice_is_tc_ena(*tc_bitmap, tc))
continue;
tc_node = ice_sched_get_tc_node(pi, tc);
if (!tc_node)
continue;
vsi_node = ice_sched_get_vsi_node(pi, tc_node, vsi_handle);
if (!vsi_node)
continue;
bw_t_info = &vsi_ctx->sched.bw_t_info[tc];
status = ice_sched_replay_node_bw(hw, vsi_node, bw_t_info);
if (status)
break;
}
return status;
}
static int
ice_sched_replay_vsi_agg(struct ice_hw *hw, u16 vsi_handle)
{
ice_declare_bitmap(replay_bitmap, ICE_MAX_TRAFFIC_CLASS);
struct ice_sched_agg_vsi_info *agg_vsi_info;
struct ice_port_info *pi = hw->port_info;
struct ice_sched_agg_info *agg_info;
int status;
ice_zero_bitmap(replay_bitmap, ICE_MAX_TRAFFIC_CLASS);
if (!ice_is_vsi_valid(hw, vsi_handle))
return ICE_ERR_PARAM;
agg_info = ice_get_vsi_agg_info(hw, vsi_handle);
if (!agg_info)
return 0;
agg_vsi_info = ice_get_agg_vsi_info(agg_info, vsi_handle);
if (!agg_vsi_info)
return 0;
ice_sched_get_ena_tc_bitmap(pi, agg_info->replay_tc_bitmap,
replay_bitmap);
status = ice_sched_cfg_agg(hw->port_info, agg_info->agg_id,
ICE_AGG_TYPE_AGG, replay_bitmap);
if (status)
return status;
status = ice_sched_replay_agg_bw(hw, agg_info);
if (status)
return status;
ice_zero_bitmap(replay_bitmap, ICE_MAX_TRAFFIC_CLASS);
ice_sched_get_ena_tc_bitmap(pi, agg_vsi_info->replay_tc_bitmap,
replay_bitmap);
status = ice_sched_assoc_vsi_to_agg(pi, agg_info->agg_id, vsi_handle,
replay_bitmap);
if (status)
return status;
return ice_sched_replay_vsi_bw(hw, vsi_handle,
agg_vsi_info->tc_bitmap);
}
int ice_replay_vsi_agg(struct ice_hw *hw, u16 vsi_handle)
{
struct ice_port_info *pi = hw->port_info;
int status;
ice_acquire_lock(&pi->sched_lock);
status = ice_sched_replay_vsi_agg(hw, vsi_handle);
ice_release_lock(&pi->sched_lock);
return status;
}
int
ice_sched_replay_q_bw(struct ice_port_info *pi, struct ice_q_ctx *q_ctx)
{
struct ice_sched_node *q_node;
q_node = ice_sched_find_node_by_teid(pi->root, q_ctx->q_teid);
if (!q_node)
return ICE_ERR_PARAM;
return ice_sched_replay_node_bw(pi->hw, q_node, &q_ctx->bw_t_info);
}