123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright (c) 2018, Intel Corporation. */
- #include "ice_sched.h"
- /**
- * ice_aq_delete_sched_elems - delete scheduler elements
- * @hw: pointer to the hw struct
- * @grps_req: number of groups to delete
- * @buf: pointer to buffer
- * @buf_size: buffer size in bytes
- * @grps_del: returns total number of elements deleted
- * @cd: pointer to command details structure or NULL
- *
- * Delete scheduling elements (0x040F)
- */
- static enum ice_status
- 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)
- {
- struct ice_aqc_add_move_delete_elem *cmd;
- struct ice_aq_desc desc;
- enum ice_status status;
- cmd = &desc.params.add_move_delete_elem;
- ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_delete_sched_elems);
- desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
- cmd->num_grps_req = cpu_to_le16(grps_req);
- status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
- if (!status && grps_del)
- *grps_del = le16_to_cpu(cmd->num_grps_updated);
- return status;
- }
- /**
- * ice_sched_remove_elems - remove nodes from hw
- * @hw: pointer to the hw struct
- * @parent: pointer to the parent node
- * @num_nodes: number of nodes
- * @node_teids: array of node teids to be deleted
- *
- * This function remove nodes from hw
- */
- static enum ice_status
- 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;
- enum ice_status status;
- u16 buf_size;
- buf_size = sizeof(*buf) + sizeof(u32) * (num_nodes - 1);
- buf = devm_kzalloc(ice_hw_to_dev(hw), buf_size, GFP_KERNEL);
- 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 elements failed\n");
- devm_kfree(ice_hw_to_dev(hw), buf);
- return status;
- }
- /**
- * ice_sched_get_first_node - get the first node of the given layer
- * @hw: pointer to the hw struct
- * @parent: pointer the base node of the subtree
- * @layer: layer number
- *
- * This function retrieves the first node of the given layer from the subtree
- */
- static struct ice_sched_node *
- ice_sched_get_first_node(struct ice_hw *hw, struct ice_sched_node *parent,
- u8 layer)
- {
- u8 i;
- if (layer < hw->sw_entry_point_layer)
- return NULL;
- for (i = 0; i < parent->num_children; i++) {
- struct ice_sched_node *node = parent->children[i];
- if (node) {
- if (node->tx_sched_layer == layer)
- return node;
- /* this recursion is intentional, and wouldn't
- * go more than 9 calls
- */
- return ice_sched_get_first_node(hw, node, layer);
- }
- }
- return NULL;
- }
- /**
- * ice_sched_get_tc_node - get pointer to TC node
- * @pi: port information structure
- * @tc: TC number
- *
- * This function returns the TC node pointer
- */
- struct ice_sched_node *ice_sched_get_tc_node(struct ice_port_info *pi, u8 tc)
- {
- u8 i;
- if (!pi)
- 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;
- }
- /**
- * ice_free_sched_node - Free a Tx scheduler node from SW DB
- * @pi: port information structure
- * @node: pointer to the ice_sched_node struct
- *
- * This function frees up a node from SW DB as well as from HW
- *
- * This function needs to be called with the port_info->sched_lock held
- */
- 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;
- /* Free the children before freeing up the parent node
- * The parent array is updated below and that shifts the nodes
- * in the array. So always pick the first child if num children > 0
- */
- while (node->num_children)
- ice_free_sched_node(pi, node->children[0]);
- /* Leaf, TC and root nodes can't be deleted by SW */
- 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);
- enum ice_status status;
- status = ice_sched_remove_elems(hw, node->parent, 1, &teid);
- if (status)
- ice_debug(hw, ICE_DBG_SCHED,
- "remove element failed %d\n", status);
- }
- parent = node->parent;
- /* root has no parent */
- if (parent) {
- struct ice_sched_node *p, *tc_node;
- /* update the parent */
- 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;
- }
- /* search for previous sibling that points to this node and
- * remove the reference
- */
- tc_node = ice_sched_get_tc_node(pi, node->tc_num);
- if (!tc_node) {
- ice_debug(hw, ICE_DBG_SCHED,
- "Invalid TC number %d\n", node->tc_num);
- goto err_exit;
- }
- p = ice_sched_get_first_node(hw, tc_node, node->tx_sched_layer);
- while (p) {
- if (p->sibling == node) {
- p->sibling = node->sibling;
- break;
- }
- p = p->sibling;
- }
- }
- err_exit:
- /* leaf nodes have no children */
- if (node->children)
- devm_kfree(ice_hw_to_dev(hw), node->children);
- devm_kfree(ice_hw_to_dev(hw), node);
- }
- /**
- * ice_aq_query_sched_res - query scheduler resource
- * @hw: pointer to the hw struct
- * @buf_size: buffer size in bytes
- * @buf: pointer to buffer
- * @cd: pointer to command details structure or NULL
- *
- * Query scheduler resource allocation (0x0412)
- */
- static enum ice_status
- 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);
- }
- /**
- * ice_sched_clear_tx_topo - clears the schduler tree nodes
- * @pi: port information structure
- *
- * This function removes all the nodes from HW as well as from SW DB.
- */
- static void ice_sched_clear_tx_topo(struct ice_port_info *pi)
- {
- struct ice_sched_agg_info *agg_info;
- struct ice_sched_vsi_info *vsi_elem;
- struct ice_sched_agg_info *atmp;
- struct ice_sched_vsi_info *tmp;
- struct ice_hw *hw;
- if (!pi)
- return;
- hw = pi->hw;
- list_for_each_entry_safe(agg_info, atmp, &pi->agg_list, 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, list_entry) {
- list_del(&agg_vsi_info->list_entry);
- devm_kfree(ice_hw_to_dev(hw), agg_vsi_info);
- }
- }
- /* remove the vsi list */
- list_for_each_entry_safe(vsi_elem, tmp, &pi->vsi_info_list,
- list_entry) {
- list_del(&vsi_elem->list_entry);
- devm_kfree(ice_hw_to_dev(hw), vsi_elem);
- }
- if (pi->root) {
- ice_free_sched_node(pi, pi->root);
- pi->root = NULL;
- }
- }
- /**
- * ice_sched_clear_port - clear the scheduler elements from SW DB for a port
- * @pi: port information structure
- *
- * Cleanup scheduling elements from SW DB
- */
- static 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;
- mutex_lock(&pi->sched_lock);
- ice_sched_clear_tx_topo(pi);
- mutex_unlock(&pi->sched_lock);
- mutex_destroy(&pi->sched_lock);
- }
- /**
- * ice_sched_cleanup_all - cleanup scheduler elements from SW DB for all ports
- * @hw: pointer to the hw struct
- *
- * Cleanup scheduling elements from SW DB for all the ports
- */
- void ice_sched_cleanup_all(struct ice_hw *hw)
- {
- if (!hw || !hw->port_info)
- return;
- if (hw->layer_info)
- devm_kfree(ice_hw_to_dev(hw), hw->layer_info);
- 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;
- }
- /**
- * ice_sched_query_res_alloc - query the FW for num of logical sched layers
- * @hw: pointer to the HW struct
- *
- * query FW for allocated scheduler resources and store in HW struct
- */
- enum ice_status ice_sched_query_res_alloc(struct ice_hw *hw)
- {
- struct ice_aqc_query_txsched_res_resp *buf;
- enum ice_status status = 0;
- if (hw->layer_info)
- return status;
- buf = devm_kzalloc(ice_hw_to_dev(hw), sizeof(*buf), GFP_KERNEL);
- 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 = le16_to_cpu(buf->sched_props.logical_levels);
- hw->num_tx_sched_phys_layers =
- 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;
- hw->layer_info = devm_kmemdup(ice_hw_to_dev(hw), buf->layer_props,
- (hw->num_tx_sched_layers *
- sizeof(*hw->layer_info)),
- GFP_KERNEL);
- if (!hw->layer_info) {
- status = ICE_ERR_NO_MEMORY;
- goto sched_query_out;
- }
- sched_query_out:
- devm_kfree(ice_hw_to_dev(hw), buf);
- return status;
- }
|