linux/drivers/net/ethernet/intel/ice/ice_switch.c

260 lines
7.4 KiB
C
Raw Normal View History

2018-03-20 07:58:08 -07:00
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2018, Intel Corporation. */
#include "ice_switch.h"
/**
* ice_aq_get_sw_cfg - get switch configuration
* @hw: pointer to the hardware structure
* @buf: pointer to the result buffer
* @buf_size: length of the buffer available for response
* @req_desc: pointer to requested descriptor
* @num_elems: pointer to number of elements
* @cd: pointer to command details structure or NULL
*
* Get switch configuration (0x0200) to be placed in 'buff'.
* This admin command returns information such as initial VSI/port number
* and switch ID it belongs to.
*
* NOTE: *req_desc is both an input/output parameter.
* The caller of this function first calls this function with *request_desc set
* to 0. If the response from f/w has *req_desc set to 0, all the switch
* configuration information has been returned; if non-zero (meaning not all
* the information was returned), the caller should call this function again
* with *req_desc set to the previous value returned by f/w to get the
* next block of switch configuration information.
*
* *num_elems is output only parameter. This reflects the number of elements
* in response buffer. The caller of this function to use *num_elems while
* parsing the response buffer.
*/
static enum ice_status
ice_aq_get_sw_cfg(struct ice_hw *hw, struct ice_aqc_get_sw_cfg_resp *buf,
u16 buf_size, u16 *req_desc, u16 *num_elems,
struct ice_sq_cd *cd)
{
struct ice_aqc_get_sw_cfg *cmd;
enum ice_status status;
struct ice_aq_desc desc;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_get_sw_cfg);
cmd = &desc.params.get_sw_conf;
cmd->element = cpu_to_le16(*req_desc);
status = ice_aq_send_cmd(hw, &desc, buf, buf_size, cd);
if (!status) {
*req_desc = le16_to_cpu(cmd->element);
*num_elems = le16_to_cpu(cmd->num_elems);
}
return status;
}
ice: Add support for VSI allocation and deallocation This patch introduces data structures and functions to alloc/free VSIs. The driver represents a VSI using the ice_vsi structure. Some noteworthy points about VSI allocation: 1) A VSI is allocated in the firmware using the "add VSI" admin queue command (implemented as ice_aq_add_vsi). The firmware returns an identifier for the allocated VSI. The VSI context is used to program certain aspects (loopback, queue map, etc.) of the VSI's configuration. 2) A VSI is deleted using the "free VSI" admin queue command (implemented as ice_aq_free_vsi). 3) The driver represents a VSI using struct ice_vsi. This is allocated and initialized as part of the ice_vsi_alloc flow, and deallocated as part of the ice_vsi_delete flow. 4) Once the VSI is created, a netdev is allocated and associated with it. The VSI's ring and vector related data structures are also allocated and initialized. 5) A VSI's queues can either be contiguous or scattered. To do this, the driver maintains a bitmap (vsi->avail_txqs) which is kept in sync with the firmware's VSI queue allocation imap. If the VSI can't get a contiguous queue allocation, it will fallback to scatter. This is implemented in ice_vsi_get_qs which is called as part of the VSI setup flow. In the release flow, the VSI's queues are released and the bitmap is updated to reflect this by ice_vsi_put_qs. CC: Shannon Nelson <shannon.nelson@oracle.com> Signed-off-by: Anirudh Venkataramanan <anirudh.venkataramanan@intel.com> Acked-by: Shannon Nelson <shannon.nelson@oracle.com> Tested-by: Tony Brelinski <tonyx.brelinski@intel.com> Signed-off-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com>
2018-03-20 07:58:11 -07:00
/**
* ice_aq_add_vsi
* @hw: pointer to the hw struct
* @vsi_ctx: pointer to a VSI context struct
* @cd: pointer to command details structure or NULL
*
* Add a VSI context to the hardware (0x0210)
*/
enum ice_status
ice_aq_add_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
struct ice_sq_cd *cd)
{
struct ice_aqc_add_update_free_vsi_resp *res;
struct ice_aqc_add_get_update_free_vsi *cmd;
enum ice_status status;
struct ice_aq_desc desc;
cmd = &desc.params.vsi_cmd;
res = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_add_vsi);
if (!vsi_ctx->alloc_from_pool)
cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num |
ICE_AQ_VSI_IS_VALID);
cmd->vsi_flags = cpu_to_le16(vsi_ctx->flags);
desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
status = ice_aq_send_cmd(hw, &desc, &vsi_ctx->info,
sizeof(vsi_ctx->info), cd);
if (!status) {
vsi_ctx->vsi_num = le16_to_cpu(res->vsi_num) & ICE_AQ_VSI_NUM_M;
vsi_ctx->vsis_allocd = le16_to_cpu(res->vsi_used);
vsi_ctx->vsis_unallocated = le16_to_cpu(res->vsi_free);
}
return status;
}
/**
* ice_aq_update_vsi
* @hw: pointer to the hw struct
* @vsi_ctx: pointer to a VSI context struct
* @cd: pointer to command details structure or NULL
*
* Update VSI context in the hardware (0x0211)
*/
enum ice_status
ice_aq_update_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
struct ice_sq_cd *cd)
{
struct ice_aqc_add_update_free_vsi_resp *resp;
struct ice_aqc_add_get_update_free_vsi *cmd;
struct ice_aq_desc desc;
enum ice_status status;
cmd = &desc.params.vsi_cmd;
resp = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_update_vsi);
cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num | ICE_AQ_VSI_IS_VALID);
desc.flags |= cpu_to_le16(ICE_AQ_FLAG_RD);
status = ice_aq_send_cmd(hw, &desc, &vsi_ctx->info,
sizeof(vsi_ctx->info), cd);
if (!status) {
vsi_ctx->vsis_allocd = le16_to_cpu(resp->vsi_used);
vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free);
}
return status;
}
/**
* ice_aq_free_vsi
* @hw: pointer to the hw struct
* @vsi_ctx: pointer to a VSI context struct
* @keep_vsi_alloc: keep VSI allocation as part of this PF's resources
* @cd: pointer to command details structure or NULL
*
* Get VSI context info from hardware (0x0213)
*/
enum ice_status
ice_aq_free_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
bool keep_vsi_alloc, struct ice_sq_cd *cd)
{
struct ice_aqc_add_update_free_vsi_resp *resp;
struct ice_aqc_add_get_update_free_vsi *cmd;
struct ice_aq_desc desc;
enum ice_status status;
cmd = &desc.params.vsi_cmd;
resp = (struct ice_aqc_add_update_free_vsi_resp *)&desc.params.raw;
ice_fill_dflt_direct_cmd_desc(&desc, ice_aqc_opc_free_vsi);
cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num | ICE_AQ_VSI_IS_VALID);
if (keep_vsi_alloc)
cmd->cmd_flags = cpu_to_le16(ICE_AQ_VSI_KEEP_ALLOC);
status = ice_aq_send_cmd(hw, &desc, NULL, 0, cd);
if (!status) {
vsi_ctx->vsis_allocd = le16_to_cpu(resp->vsi_used);
vsi_ctx->vsis_unallocated = le16_to_cpu(resp->vsi_free);
}
return status;
}
2018-03-20 07:58:08 -07:00
/* ice_init_port_info - Initialize port_info with switch configuration data
* @pi: pointer to port_info
* @vsi_port_num: VSI number or port number
* @type: Type of switch element (port or VSI)
* @swid: switch ID of the switch the element is attached to
* @pf_vf_num: PF or VF number
* @is_vf: true if the element is a VF, false otherwise
*/
static void
ice_init_port_info(struct ice_port_info *pi, u16 vsi_port_num, u8 type,
u16 swid, u16 pf_vf_num, bool is_vf)
{
switch (type) {
case ICE_AQC_GET_SW_CONF_RESP_PHYS_PORT:
pi->lport = (u8)(vsi_port_num & ICE_LPORT_MASK);
pi->sw_id = swid;
pi->pf_vf_num = pf_vf_num;
pi->is_vf = is_vf;
pi->dflt_tx_vsi_num = ICE_DFLT_VSI_INVAL;
pi->dflt_rx_vsi_num = ICE_DFLT_VSI_INVAL;
break;
default:
ice_debug(pi->hw, ICE_DBG_SW,
"incorrect VSI/port type received\n");
break;
}
}
/* ice_get_initial_sw_cfg - Get initial port and default VSI data
* @hw: pointer to the hardware structure
*/
enum ice_status ice_get_initial_sw_cfg(struct ice_hw *hw)
{
struct ice_aqc_get_sw_cfg_resp *rbuf;
enum ice_status status;
u16 req_desc = 0;
u16 num_elems;
u16 i;
rbuf = devm_kzalloc(ice_hw_to_dev(hw), ICE_SW_CFG_MAX_BUF_LEN,
GFP_KERNEL);
if (!rbuf)
return ICE_ERR_NO_MEMORY;
/* Multiple calls to ice_aq_get_sw_cfg may be required
* to get all the switch configuration information. The need
* for additional calls is indicated by ice_aq_get_sw_cfg
* writing a non-zero value in req_desc
*/
do {
status = ice_aq_get_sw_cfg(hw, rbuf, ICE_SW_CFG_MAX_BUF_LEN,
&req_desc, &num_elems, NULL);
if (status)
break;
for (i = 0; i < num_elems; i++) {
struct ice_aqc_get_sw_cfg_resp_elem *ele;
u16 pf_vf_num, swid, vsi_port_num;
bool is_vf = false;
u8 type;
ele = rbuf[i].elements;
vsi_port_num = le16_to_cpu(ele->vsi_port_num) &
ICE_AQC_GET_SW_CONF_RESP_VSI_PORT_NUM_M;
pf_vf_num = le16_to_cpu(ele->pf_vf_num) &
ICE_AQC_GET_SW_CONF_RESP_FUNC_NUM_M;
swid = le16_to_cpu(ele->swid);
if (le16_to_cpu(ele->pf_vf_num) &
ICE_AQC_GET_SW_CONF_RESP_IS_VF)
is_vf = true;
type = le16_to_cpu(ele->vsi_port_num) >>
ICE_AQC_GET_SW_CONF_RESP_TYPE_S;
if (type == ICE_AQC_GET_SW_CONF_RESP_VSI) {
/* FW VSI is not needed. Just continue. */
continue;
}
ice_init_port_info(hw->port_info, vsi_port_num,
type, swid, pf_vf_num, is_vf);
}
} while (req_desc && !status);
devm_kfree(ice_hw_to_dev(hw), (void *)rbuf);
return status;
}