提交 1071a835 编写于 作者: A Anirudh Venkataramanan 提交者: Jeff Kirsher

ice: Implement virtchnl commands for AVF support

virtchnl is a protocol/interface specification that allows the Intel
"Adaptive Virtual Function (AVF)" driver (iavf.ko) to work with more than
one physical function driver. The AVF driver sends "virtchnl commands"
(control plane only) to the PF driver over mailbox queues and the PF driver
executes these commands and returns a result to the VF, again over mailbox.

This patch adds AVF support for the ice PF driver by implementing the
following virtchnl commands:

VIRTCHNL_OP_VERSION
VIRTCHNL_OP_GET_VF_RESOURCES
VIRTCHNL_OP_RESET_VF
VIRTCHNL_OP_ADD_ETH_ADDR
VIRTCHNL_OP_DEL_ETH_ADDR
VIRTCHNL_OP_CONFIG_VSI_QUEUES
VIRTCHNL_OP_ENABLE_QUEUES
VIRTCHNL_OP_DISABLE_QUEUES
VIRTCHNL_OP_ADD_ETH_ADDR
VIRTCHNL_OP_DEL_ETH_ADDR
VIRTCHNL_OP_CONFIG_VSI_QUEUES
VIRTCHNL_OP_ENABLE_QUEUES
VIRTCHNL_OP_DISABLE_QUEUES
VIRTCHNL_OP_REQUEST_QUEUES
VIRTCHNL_OP_CONFIG_IRQ_MAP
VIRTCHNL_OP_CONFIG_RSS_KEY
VIRTCHNL_OP_CONFIG_RSS_LUT
VIRTCHNL_OP_GET_STATS
VIRTCHNL_OP_ADD_VLAN
VIRTCHNL_OP_DEL_VLAN
VIRTCHNL_OP_ENABLE_VLAN_STRIPPING
VIRTCHNL_OP_DISABLE_VLAN_STRIPPING
Signed-off-by: NAnirudh Venkataramanan <anirudh.venkataramanan@intel.com>
Tested-by: NAndrew Bowers <andrewx.bowers@intel.com>
Signed-off-by: NJeff Kirsher <jeffrey.t.kirsher@intel.com>
上级 7c710869
......@@ -71,6 +71,7 @@ extern const char ice_drv_ver[];
#define ICE_MAX_QS_PER_VF 256
#define ICE_MIN_QS_PER_VF 1
#define ICE_DFLT_QS_PER_VF 4
#define ICE_MAX_BASE_QS_PER_VF 16
#define ICE_MAX_INTR_PER_VF 65
#define ICE_MIN_INTR_PER_VF (ICE_MIN_QS_PER_VF + 1)
#define ICE_DFLT_INTR_PER_VF (ICE_DFLT_QS_PER_VF + 1)
......
......@@ -1446,6 +1446,7 @@ enum ice_adminq_opc {
ice_aqc_opc_nvm_read = 0x0701,
/* PF/VF mailbox commands */
ice_mbx_opc_send_msg_to_pf = 0x0801,
ice_mbx_opc_send_msg_to_vf = 0x0802,
/* RSS commands */
......
......@@ -800,6 +800,9 @@ static int __ice_clean_ctrlq(struct ice_pf *pf, enum ice_ctl_q q_type)
dev_err(&pf->pdev->dev,
"Could not handle link event\n");
break;
case ice_mbx_opc_send_msg_to_pf:
ice_vc_process_vf_msg(pf, &event);
break;
case ice_aqc_opc_fw_logging:
ice_output_fw_log(hw, &event.desc, event.msg_buf);
break;
......
......@@ -187,6 +187,7 @@ ice_aq_add_vsi(struct ice_hw *hw, struct ice_vsi_ctx *vsi_ctx,
if (!vsi_ctx->alloc_from_pool)
cmd->vsi_num = cpu_to_le16(vsi_ctx->vsi_num |
ICE_AQ_VSI_IS_VALID);
cmd->vf_id = vsi_ctx->vf_num;
cmd->vsi_flags = cpu_to_le16(vsi_ctx->flags);
......
......@@ -79,6 +79,35 @@ ice_set_pfe_link_forced(struct ice_vf *vf, struct virtchnl_pf_event *pfe,
ice_set_pfe_link(vf, pfe, link_speed, link_up);
}
/**
* ice_vc_notify_vf_link_state - Inform a VF of link status
* @vf: pointer to the VF structure
*
* send a link status message to a single VF
*/
static void ice_vc_notify_vf_link_state(struct ice_vf *vf)
{
struct virtchnl_pf_event pfe = { 0 };
struct ice_link_status *ls;
struct ice_pf *pf = vf->pf;
struct ice_hw *hw;
hw = &pf->hw;
ls = &hw->port_info->phy.link_info;
pfe.event = VIRTCHNL_EVENT_LINK_CHANGE;
pfe.severity = PF_EVENT_SEVERITY_INFO;
if (vf->link_forced)
ice_set_pfe_link_forced(vf, &pfe, vf->link_up);
else
ice_set_pfe_link(vf, &pfe, ls->link_speed, ls->link_info &
ICE_AQ_LINK_UP);
ice_aq_send_msg_to_vf(hw, vf->vf_id, VIRTCHNL_OP_EVENT, 0, (u8 *)&pfe,
sizeof(pfe), NULL);
}
/**
* ice_get_vf_vector - get VF interrupt vector register offset
* @vf_msix: number of MSIx vector per VF on a PF
......@@ -1107,6 +1136,866 @@ static void ice_vc_dis_vf(struct ice_vf *vf)
ice_reset_vf(vf, false);
}
/**
* ice_vc_send_msg_to_vf - Send message to VF
* @vf: pointer to the VF info
* @v_opcode: virtual channel opcode
* @v_retval: virtual channel return value
* @msg: pointer to the msg buffer
* @msglen: msg length
*
* send msg to VF
*/
static int ice_vc_send_msg_to_vf(struct ice_vf *vf, u32 v_opcode,
enum ice_status v_retval, u8 *msg, u16 msglen)
{
enum ice_status aq_ret;
struct ice_pf *pf;
/* validate the request */
if (!vf || vf->vf_id >= vf->pf->num_alloc_vfs)
return -EINVAL;
pf = vf->pf;
/* single place to detect unsuccessful return values */
if (v_retval) {
vf->num_inval_msgs++;
dev_info(&pf->pdev->dev, "VF %d failed opcode %d, retval: %d\n",
vf->vf_id, v_opcode, v_retval);
if (vf->num_inval_msgs > ICE_DFLT_NUM_INVAL_MSGS_ALLOWED) {
dev_err(&pf->pdev->dev,
"Number of invalid messages exceeded for VF %d\n",
vf->vf_id);
dev_err(&pf->pdev->dev, "Use PF Control I/F to enable the VF\n");
set_bit(ICE_VF_STATE_DIS, vf->vf_states);
return -EIO;
}
} else {
vf->num_valid_msgs++;
/* reset the invalid counter, if a valid message is received. */
vf->num_inval_msgs = 0;
}
aq_ret = ice_aq_send_msg_to_vf(&pf->hw, vf->vf_id, v_opcode, v_retval,
msg, msglen, NULL);
if (aq_ret) {
dev_info(&pf->pdev->dev,
"Unable to send the message to VF %d aq_err %d\n",
vf->vf_id, pf->hw.mailboxq.sq_last_status);
return -EIO;
}
return 0;
}
/**
* ice_vc_get_ver_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* called from the VF to request the API version used by the PF
*/
static int ice_vc_get_ver_msg(struct ice_vf *vf, u8 *msg)
{
struct virtchnl_version_info info = {
VIRTCHNL_VERSION_MAJOR, VIRTCHNL_VERSION_MINOR
};
vf->vf_ver = *(struct virtchnl_version_info *)msg;
/* VFs running the 1.0 API expect to get 1.0 back or they will cry. */
if (VF_IS_V10(&vf->vf_ver))
info.minor = VIRTCHNL_VERSION_MINOR_NO_VF_CAPS;
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_VERSION, ICE_SUCCESS,
(u8 *)&info,
sizeof(struct virtchnl_version_info));
}
/**
* ice_vc_get_vf_res_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* called from the VF to request its resources
*/
static int ice_vc_get_vf_res_msg(struct ice_vf *vf, u8 *msg)
{
struct virtchnl_vf_resource *vfres = NULL;
enum ice_status aq_ret = 0;
struct ice_pf *pf = vf->pf;
struct ice_vsi *vsi;
int len = 0;
int ret;
if (!test_bit(ICE_VF_STATE_INIT, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto err;
}
len = sizeof(struct virtchnl_vf_resource);
vfres = devm_kzalloc(&pf->pdev->dev, len, GFP_KERNEL);
if (!vfres) {
aq_ret = ICE_ERR_NO_MEMORY;
len = 0;
goto err;
}
if (VF_IS_V11(&vf->vf_ver))
vf->driver_caps = *(u32 *)msg;
else
vf->driver_caps = VIRTCHNL_VF_OFFLOAD_L2 |
VIRTCHNL_VF_OFFLOAD_RSS_REG |
VIRTCHNL_VF_OFFLOAD_VLAN;
vfres->vf_cap_flags = VIRTCHNL_VF_OFFLOAD_L2;
vsi = pf->vsi[vf->lan_vsi_idx];
if (!vsi->info.pvid)
vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_VLAN;
if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PF) {
vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PF;
} else {
if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_AQ)
vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_AQ;
else
vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_REG;
}
if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2)
vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RSS_PCTYPE_V2;
if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP)
vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP;
if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM)
vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_ENCAP_CSUM;
if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_RX_POLLING)
vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_RX_POLLING;
if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_WB_ON_ITR)
vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_WB_ON_ITR;
if (vf->driver_caps & VIRTCHNL_VF_OFFLOAD_REQ_QUEUES)
vfres->vf_cap_flags |= VIRTCHNL_VF_OFFLOAD_REQ_QUEUES;
if (vf->driver_caps & VIRTCHNL_VF_CAP_ADV_LINK_SPEED)
vfres->vf_cap_flags |= VIRTCHNL_VF_CAP_ADV_LINK_SPEED;
vfres->num_vsis = 1;
/* Tx and Rx queue are equal for VF */
vfres->num_queue_pairs = vsi->num_txq;
vfres->max_vectors = pf->num_vf_msix;
vfres->rss_key_size = ICE_VSIQF_HKEY_ARRAY_SIZE;
vfres->rss_lut_size = ICE_VSIQF_HLUT_ARRAY_SIZE;
vfres->vsi_res[0].vsi_id = vf->lan_vsi_num;
vfres->vsi_res[0].vsi_type = VIRTCHNL_VSI_SRIOV;
vfres->vsi_res[0].num_queue_pairs = vsi->num_txq;
ether_addr_copy(vfres->vsi_res[0].default_mac_addr,
vf->dflt_lan_addr.addr);
set_bit(ICE_VF_STATE_ACTIVE, vf->vf_states);
err:
/* send the response back to the VF */
ret = ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_VF_RESOURCES, aq_ret,
(u8 *)vfres, len);
devm_kfree(&pf->pdev->dev, vfres);
return ret;
}
/**
* ice_vc_reset_vf_msg
* @vf: pointer to the VF info
*
* called from the VF to reset itself,
* unlike other virtchnl messages, PF driver
* doesn't send the response back to the VF
*/
static void ice_vc_reset_vf_msg(struct ice_vf *vf)
{
if (test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states))
ice_reset_vf(vf, false);
}
/**
* ice_find_vsi_from_id
* @pf: the pf structure to search for the VSI
* @id: id of the VSI it is searching for
*
* searches for the VSI with the given id
*/
static struct ice_vsi *ice_find_vsi_from_id(struct ice_pf *pf, u16 id)
{
int i;
for (i = 0; i < pf->num_alloc_vsi; i++)
if (pf->vsi[i] && pf->vsi[i]->vsi_num == id)
return pf->vsi[i];
return NULL;
}
/**
* ice_vc_isvalid_vsi_id
* @vf: pointer to the VF info
* @vsi_id: VF relative VSI id
*
* check for the valid VSI id
*/
static bool ice_vc_isvalid_vsi_id(struct ice_vf *vf, u16 vsi_id)
{
struct ice_pf *pf = vf->pf;
struct ice_vsi *vsi;
vsi = ice_find_vsi_from_id(pf, vsi_id);
return (vsi && (vsi->vf_id == vf->vf_id));
}
/**
* ice_vc_isvalid_q_id
* @vf: pointer to the VF info
* @vsi_id: VSI id
* @qid: VSI relative queue id
*
* check for the valid queue id
*/
static bool ice_vc_isvalid_q_id(struct ice_vf *vf, u16 vsi_id, u8 qid)
{
struct ice_vsi *vsi = ice_find_vsi_from_id(vf->pf, vsi_id);
/* allocated Tx and Rx queues should be always equal for VF VSI */
return (vsi && (qid < vsi->alloc_txq));
}
/**
* ice_vc_config_rss_key
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* Configure the VF's RSS key
*/
static int ice_vc_config_rss_key(struct ice_vf *vf, u8 *msg)
{
struct virtchnl_rss_key *vrk =
(struct virtchnl_rss_key *)msg;
struct ice_vsi *vsi = NULL;
enum ice_status aq_ret;
int ret;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (!ice_vc_isvalid_vsi_id(vf, vrk->vsi_id)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi = ice_find_vsi_from_id(vf->pf, vrk->vsi_id);
if (!vsi) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (vrk->key_len != ICE_VSIQF_HKEY_ARRAY_SIZE) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
ret = ice_set_rss(vsi, vrk->key, NULL, 0);
aq_ret = ret ? ICE_ERR_PARAM : ICE_SUCCESS;
error_param:
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_KEY, aq_ret,
NULL, 0);
}
/**
* ice_vc_config_rss_lut
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* Configure the VF's RSS LUT
*/
static int ice_vc_config_rss_lut(struct ice_vf *vf, u8 *msg)
{
struct virtchnl_rss_lut *vrl = (struct virtchnl_rss_lut *)msg;
struct ice_vsi *vsi = NULL;
enum ice_status aq_ret;
int ret;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (!ice_vc_isvalid_vsi_id(vf, vrl->vsi_id)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi = ice_find_vsi_from_id(vf->pf, vrl->vsi_id);
if (!vsi) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (vrl->lut_entries != ICE_VSIQF_HLUT_ARRAY_SIZE) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (!test_bit(ICE_FLAG_RSS_ENA, vf->pf->flags)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
ret = ice_set_rss(vsi, NULL, vrl->lut, ICE_VSIQF_HLUT_ARRAY_SIZE);
aq_ret = ret ? ICE_ERR_PARAM : ICE_SUCCESS;
error_param:
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_RSS_LUT, aq_ret,
NULL, 0);
}
/**
* ice_vc_get_stats_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* called from the VF to get VSI stats
*/
static int ice_vc_get_stats_msg(struct ice_vf *vf, u8 *msg)
{
struct virtchnl_queue_select *vqs =
(struct virtchnl_queue_select *)msg;
enum ice_status aq_ret = 0;
struct ice_eth_stats stats;
struct ice_vsi *vsi;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (!ice_vc_isvalid_vsi_id(vf, vqs->vsi_id)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi = ice_find_vsi_from_id(vf->pf, vqs->vsi_id);
if (!vsi) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
memset(&stats, 0, sizeof(struct ice_eth_stats));
ice_update_eth_stats(vsi);
stats = vsi->eth_stats;
error_param:
/* send the response to the VF */
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_GET_STATS, aq_ret,
(u8 *)&stats, sizeof(stats));
}
/**
* ice_vc_ena_qs_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* called from the VF to enable all or specific queue(s)
*/
static int ice_vc_ena_qs_msg(struct ice_vf *vf, u8 *msg)
{
struct virtchnl_queue_select *vqs =
(struct virtchnl_queue_select *)msg;
enum ice_status aq_ret = 0;
struct ice_vsi *vsi;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (!ice_vc_isvalid_vsi_id(vf, vqs->vsi_id)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (!vqs->rx_queues && !vqs->tx_queues) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi = ice_find_vsi_from_id(vf->pf, vqs->vsi_id);
if (!vsi) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
/* Enable only Rx rings, Tx rings were enabled by the FW when the
* Tx queue group list was configured and the context bits were
* programmed using ice_vsi_cfg_txqs
*/
if (ice_vsi_start_rx_rings(vsi))
aq_ret = ICE_ERR_PARAM;
/* Set flag to indicate that queues are enabled */
if (!aq_ret)
set_bit(ICE_VF_STATE_ENA, vf->vf_states);
error_param:
/* send the response to the VF */
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_ENABLE_QUEUES, aq_ret,
NULL, 0);
}
/**
* ice_vc_dis_qs_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* called from the VF to disable all or specific
* queue(s)
*/
static int ice_vc_dis_qs_msg(struct ice_vf *vf, u8 *msg)
{
struct virtchnl_queue_select *vqs =
(struct virtchnl_queue_select *)msg;
enum ice_status aq_ret = 0;
struct ice_vsi *vsi;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states) &&
!test_bit(ICE_VF_STATE_ENA, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (!ice_vc_isvalid_vsi_id(vf, vqs->vsi_id)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (!vqs->rx_queues && !vqs->tx_queues) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi = ice_find_vsi_from_id(vf->pf, vqs->vsi_id);
if (!vsi) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (ice_vsi_stop_tx_rings(vsi, ICE_NO_RESET, vf->vf_id)) {
dev_err(&vsi->back->pdev->dev,
"Failed to stop tx rings on VSI %d\n",
vsi->vsi_num);
aq_ret = ICE_ERR_PARAM;
}
if (ice_vsi_stop_rx_rings(vsi)) {
dev_err(&vsi->back->pdev->dev,
"Failed to stop rx rings on VSI %d\n",
vsi->vsi_num);
aq_ret = ICE_ERR_PARAM;
}
/* Clear enabled queues flag */
if (!aq_ret)
clear_bit(ICE_VF_STATE_ENA, vf->vf_states);
error_param:
/* send the response to the VF */
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_DISABLE_QUEUES, aq_ret,
NULL, 0);
}
/**
* ice_vc_cfg_irq_map_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* called from the VF to configure the IRQ to queue map
*/
static int ice_vc_cfg_irq_map_msg(struct ice_vf *vf, u8 *msg)
{
struct virtchnl_irq_map_info *irqmap_info =
(struct virtchnl_irq_map_info *)msg;
u16 vsi_id, vsi_q_id, vector_id;
struct virtchnl_vector_map *map;
struct ice_vsi *vsi = NULL;
struct ice_pf *pf = vf->pf;
enum ice_status aq_ret = 0;
unsigned long qmap;
int i;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
for (i = 0; i < irqmap_info->num_vectors; i++) {
map = &irqmap_info->vecmap[i];
vector_id = map->vector_id;
vsi_id = map->vsi_id;
/* validate msg params */
if (!(vector_id < pf->hw.func_caps.common_cap
.num_msix_vectors) || !ice_vc_isvalid_vsi_id(vf, vsi_id)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi = ice_find_vsi_from_id(vf->pf, vsi_id);
if (!vsi) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
/* lookout for the invalid queue index */
qmap = map->rxq_map;
for_each_set_bit(vsi_q_id, &qmap, ICE_MAX_BASE_QS_PER_VF) {
if (!ice_vc_isvalid_q_id(vf, vsi_id, vsi_q_id)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi->q_vectors[i]->num_ring_rx++;
vsi->rx_rings[vsi_q_id]->itr_setting =
map->rxitr_idx;
vsi->rx_rings[vsi_q_id]->q_vector = vsi->q_vectors[i];
}
qmap = map->txq_map;
for_each_set_bit(vsi_q_id, &qmap, ICE_MAX_BASE_QS_PER_VF) {
if (!ice_vc_isvalid_q_id(vf, vsi_id, vsi_q_id)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi->q_vectors[i]->num_ring_tx++;
vsi->tx_rings[vsi_q_id]->itr_setting =
map->txitr_idx;
vsi->tx_rings[vsi_q_id]->q_vector = vsi->q_vectors[i];
}
}
if (vsi)
ice_vsi_cfg_msix(vsi);
error_param:
/* send the response to the VF */
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_IRQ_MAP, aq_ret,
NULL, 0);
}
/**
* ice_vc_cfg_qs_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* called from the VF to configure the Rx/Tx queues
*/
static int ice_vc_cfg_qs_msg(struct ice_vf *vf, u8 *msg)
{
struct virtchnl_vsi_queue_config_info *qci =
(struct virtchnl_vsi_queue_config_info *)msg;
struct virtchnl_queue_pair_info *qpi;
enum ice_status aq_ret = 0;
struct ice_vsi *vsi;
int i;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (!ice_vc_isvalid_vsi_id(vf, qci->vsi_id)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi = ice_find_vsi_from_id(vf->pf, qci->vsi_id);
if (!vsi) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
for (i = 0; i < qci->num_queue_pairs; i++) {
qpi = &qci->qpair[i];
if (qpi->txq.vsi_id != qci->vsi_id ||
qpi->rxq.vsi_id != qci->vsi_id ||
qpi->rxq.queue_id != qpi->txq.queue_id ||
!ice_vc_isvalid_q_id(vf, qci->vsi_id, qpi->txq.queue_id)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
/* copy Tx queue info from VF into VSI */
vsi->tx_rings[i]->dma = qpi->txq.dma_ring_addr;
vsi->tx_rings[i]->count = qpi->txq.ring_len;
/* copy Rx queue info from VF into vsi */
vsi->rx_rings[i]->dma = qpi->rxq.dma_ring_addr;
vsi->rx_rings[i]->count = qpi->rxq.ring_len;
if (qpi->rxq.databuffer_size > ((16 * 1024) - 128)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi->rx_buf_len = qpi->rxq.databuffer_size;
if (qpi->rxq.max_pkt_size >= (16 * 1024) ||
qpi->rxq.max_pkt_size < 64) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi->max_frame = qpi->rxq.max_pkt_size;
}
/* VF can request to configure less than allocated queues
* or default allocated queues. So update the VSI with new number
*/
vsi->num_txq = qci->num_queue_pairs;
vsi->num_rxq = qci->num_queue_pairs;
if (!ice_vsi_cfg_txqs(vsi) && !ice_vsi_cfg_rxqs(vsi))
aq_ret = 0;
else
aq_ret = ICE_ERR_PARAM;
error_param:
/* send the response to the VF */
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_CONFIG_VSI_QUEUES, aq_ret,
NULL, 0);
}
/**
* ice_is_vf_trusted
* @vf: pointer to the VF info
*/
static bool ice_is_vf_trusted(struct ice_vf *vf)
{
return test_bit(ICE_VIRTCHNL_VF_CAP_PRIVILEGE, &vf->vf_caps);
}
/**
* ice_can_vf_change_mac
* @vf: pointer to the VF info
*
* Return true if the VF is allowed to change its MAC filters, false otherwise
*/
static bool ice_can_vf_change_mac(struct ice_vf *vf)
{
/* If the VF MAC address has been set administratively (via the
* ndo_set_vf_mac command), then deny permission to the VF to
* add/delete unicast MAC addresses, unless the VF is trusted
*/
if (vf->pf_set_mac && !ice_is_vf_trusted(vf))
return false;
return true;
}
/**
* ice_vc_handle_mac_addr_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
* @set: true if mac filters are being set, false otherwise
*
* add guest mac address filter
*/
static int
ice_vc_handle_mac_addr_msg(struct ice_vf *vf, u8 *msg, bool set)
{
struct virtchnl_ether_addr_list *al =
(struct virtchnl_ether_addr_list *)msg;
struct ice_pf *pf = vf->pf;
enum virtchnl_ops vc_op;
enum ice_status ret;
LIST_HEAD(mac_list);
struct ice_vsi *vsi;
int mac_count = 0;
int i;
if (set)
vc_op = VIRTCHNL_OP_ADD_ETH_ADDR;
else
vc_op = VIRTCHNL_OP_DEL_ETH_ADDR;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states) ||
!ice_vc_isvalid_vsi_id(vf, al->vsi_id)) {
ret = ICE_ERR_PARAM;
goto handle_mac_exit;
}
if (set && !ice_is_vf_trusted(vf) &&
(vf->num_mac + al->num_elements) > ICE_MAX_MACADDR_PER_VF) {
dev_err(&pf->pdev->dev,
"Can't add more MAC addresses, because VF is not trusted, switch the VF to trusted mode in order to add more functionalities\n");
ret = ICE_ERR_PARAM;
goto handle_mac_exit;
}
vsi = pf->vsi[vf->lan_vsi_idx];
for (i = 0; i < al->num_elements; i++) {
u8 *maddr = al->list[i].addr;
if (ether_addr_equal(maddr, vf->dflt_lan_addr.addr) ||
is_broadcast_ether_addr(maddr)) {
if (set) {
/* VF is trying to add filters that the PF
* already added. Just continue.
*/
dev_info(&pf->pdev->dev,
"mac %pM already set for VF %d\n",
maddr, vf->vf_id);
continue;
} else {
/* VF can't remove dflt_lan_addr/bcast mac */
dev_err(&pf->pdev->dev,
"can't remove mac %pM for VF %d\n",
maddr, vf->vf_id);
ret = ICE_ERR_PARAM;
goto handle_mac_exit;
}
}
/* check for the invalid cases and bail if necessary */
if (is_zero_ether_addr(maddr)) {
dev_err(&pf->pdev->dev,
"invalid mac %pM provided for VF %d\n",
maddr, vf->vf_id);
ret = ICE_ERR_PARAM;
goto handle_mac_exit;
}
if (is_unicast_ether_addr(maddr) &&
!ice_can_vf_change_mac(vf)) {
dev_err(&pf->pdev->dev,
"can't change unicast mac for untrusted VF %d\n",
vf->vf_id);
ret = ICE_ERR_PARAM;
goto handle_mac_exit;
}
/* get here if maddr is multicast or if VF can change mac */
if (ice_add_mac_to_list(vsi, &mac_list, al->list[i].addr)) {
ret = ICE_ERR_NO_MEMORY;
goto handle_mac_exit;
}
mac_count++;
}
/* program the updated filter list */
if (set)
ret = ice_add_mac(&pf->hw, &mac_list);
else
ret = ice_remove_mac(&pf->hw, &mac_list);
if (ret) {
dev_err(&pf->pdev->dev,
"can't update mac filters for VF %d, error %d\n",
vf->vf_id, ret);
} else {
if (set)
vf->num_mac += mac_count;
else
vf->num_mac -= mac_count;
}
handle_mac_exit:
ice_free_fltr_list(&pf->pdev->dev, &mac_list);
/* send the response to the VF */
return ice_vc_send_msg_to_vf(vf, vc_op, ret, NULL, 0);
}
/**
* ice_vc_add_mac_addr_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* add guest MAC address filter
*/
static int ice_vc_add_mac_addr_msg(struct ice_vf *vf, u8 *msg)
{
return ice_vc_handle_mac_addr_msg(vf, msg, true);
}
/**
* ice_vc_del_mac_addr_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* remove guest MAC address filter
*/
static int ice_vc_del_mac_addr_msg(struct ice_vf *vf, u8 *msg)
{
return ice_vc_handle_mac_addr_msg(vf, msg, false);
}
/**
* ice_vc_request_qs_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* VFs get a default number of queues but can use this message to request a
* different number. If the request is successful, PF will reset the VF and
* return 0. If unsuccessful, PF will send message informing VF of number of
* available queue pairs via virtchnl message response to VF.
*/
static int ice_vc_request_qs_msg(struct ice_vf *vf, u8 *msg)
{
struct virtchnl_vf_res_request *vfres =
(struct virtchnl_vf_res_request *)msg;
int req_queues = vfres->num_queue_pairs;
enum ice_status aq_ret = 0;
struct ice_pf *pf = vf->pf;
int tx_rx_queue_left;
int cur_queues;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
cur_queues = pf->num_vf_qps;
tx_rx_queue_left = min_t(int, pf->q_left_tx, pf->q_left_rx);
if (req_queues <= 0) {
dev_err(&pf->pdev->dev,
"VF %d tried to request %d queues. Ignoring.\n",
vf->vf_id, req_queues);
} else if (req_queues > ICE_MAX_QS_PER_VF) {
dev_err(&pf->pdev->dev,
"VF %d tried to request more than %d queues.\n",
vf->vf_id, ICE_MAX_QS_PER_VF);
vfres->num_queue_pairs = ICE_MAX_QS_PER_VF;
} else if (req_queues - cur_queues > tx_rx_queue_left) {
dev_warn(&pf->pdev->dev,
"VF %d requested %d more queues, but only %d left.\n",
vf->vf_id, req_queues - cur_queues, tx_rx_queue_left);
vfres->num_queue_pairs = tx_rx_queue_left + cur_queues;
} else {
/* request is successful, then reset VF */
vf->num_req_qs = req_queues;
ice_vc_dis_vf(vf);
dev_info(&pf->pdev->dev,
"VF %d granted request of %d queues.\n",
vf->vf_id, req_queues);
return 0;
}
error_param:
/* send the response to the VF */
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_REQUEST_QUEUES,
aq_ret, (u8 *)vfres, sizeof(*vfres));
}
/**
* ice_set_vf_port_vlan
* @netdev: network interface device structure
......@@ -1190,6 +2079,321 @@ ice_set_vf_port_vlan(struct net_device *netdev, int vf_id, u16 vlan_id, u8 qos,
return ret;
}
/**
* ice_vc_process_vlan_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
* @add_v: Add VLAN if true, otherwise delete VLAN
*
* Process virtchnl op to add or remove programmed guest VLAN id
*/
static int ice_vc_process_vlan_msg(struct ice_vf *vf, u8 *msg, bool add_v)
{
struct virtchnl_vlan_filter_list *vfl =
(struct virtchnl_vlan_filter_list *)msg;
enum ice_status aq_ret = 0;
struct ice_pf *pf = vf->pf;
struct ice_vsi *vsi;
int i;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (!ice_vc_isvalid_vsi_id(vf, vfl->vsi_id)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (add_v && !ice_is_vf_trusted(vf) &&
vf->num_vlan >= ICE_MAX_VLAN_PER_VF) {
dev_info(&pf->pdev->dev,
"VF is not trusted, switch the VF to trusted mode, in order to add more VLAN addresses\n");
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
for (i = 0; i < vfl->num_elements; i++) {
if (vfl->vlan_id[i] > ICE_MAX_VLANID) {
aq_ret = ICE_ERR_PARAM;
dev_err(&pf->pdev->dev,
"invalid VF VLAN id %d\n", vfl->vlan_id[i]);
goto error_param;
}
}
vsi = ice_find_vsi_from_id(vf->pf, vfl->vsi_id);
if (!vsi) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (vsi->info.pvid) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (ice_vsi_manage_vlan_stripping(vsi, add_v)) {
dev_err(&pf->pdev->dev,
"%sable VLAN stripping failed for VSI %i\n",
add_v ? "en" : "dis", vsi->vsi_num);
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
if (add_v) {
for (i = 0; i < vfl->num_elements; i++) {
u16 vid = vfl->vlan_id[i];
if (!ice_vsi_add_vlan(vsi, vid)) {
vf->num_vlan++;
set_bit(vid, vsi->active_vlans);
/* Enable VLAN pruning when VLAN 0 is added */
if (unlikely(!vid))
if (ice_cfg_vlan_pruning(vsi, true))
aq_ret = ICE_ERR_PARAM;
} else {
aq_ret = ICE_ERR_PARAM;
}
}
} else {
for (i = 0; i < vfl->num_elements; i++) {
u16 vid = vfl->vlan_id[i];
/* Make sure ice_vsi_kill_vlan is successful before
* updating VLAN information
*/
if (!ice_vsi_kill_vlan(vsi, vid)) {
vf->num_vlan--;
clear_bit(vid, vsi->active_vlans);
/* Disable VLAN pruning when removing VLAN 0 */
if (unlikely(!vid))
ice_cfg_vlan_pruning(vsi, false);
}
}
}
error_param:
/* send the response to the VF */
if (add_v)
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_ADD_VLAN, aq_ret,
NULL, 0);
else
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_DEL_VLAN, aq_ret,
NULL, 0);
}
/**
* ice_vc_add_vlan_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* Add and program guest VLAN id
*/
static int ice_vc_add_vlan_msg(struct ice_vf *vf, u8 *msg)
{
return ice_vc_process_vlan_msg(vf, msg, true);
}
/**
* ice_vc_remove_vlan_msg
* @vf: pointer to the VF info
* @msg: pointer to the msg buffer
*
* remove programmed guest VLAN id
*/
static int ice_vc_remove_vlan_msg(struct ice_vf *vf, u8 *msg)
{
return ice_vc_process_vlan_msg(vf, msg, false);
}
/**
* ice_vc_ena_vlan_stripping
* @vf: pointer to the VF info
*
* Enable VLAN header stripping for a given VF
*/
static int ice_vc_ena_vlan_stripping(struct ice_vf *vf)
{
enum ice_status aq_ret = 0;
struct ice_pf *pf = vf->pf;
struct ice_vsi *vsi;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi = pf->vsi[vf->lan_vsi_idx];
if (ice_vsi_manage_vlan_stripping(vsi, true))
aq_ret = ICE_ERR_AQ_ERROR;
error_param:
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_ENABLE_VLAN_STRIPPING,
aq_ret, NULL, 0);
}
/**
* ice_vc_dis_vlan_stripping
* @vf: pointer to the VF info
*
* Disable VLAN header stripping for a given VF
*/
static int ice_vc_dis_vlan_stripping(struct ice_vf *vf)
{
enum ice_status aq_ret = 0;
struct ice_pf *pf = vf->pf;
struct ice_vsi *vsi;
if (!test_bit(ICE_VF_STATE_ACTIVE, vf->vf_states)) {
aq_ret = ICE_ERR_PARAM;
goto error_param;
}
vsi = pf->vsi[vf->lan_vsi_idx];
if (ice_vsi_manage_vlan_stripping(vsi, false))
aq_ret = ICE_ERR_AQ_ERROR;
error_param:
return ice_vc_send_msg_to_vf(vf, VIRTCHNL_OP_DISABLE_VLAN_STRIPPING,
aq_ret, NULL, 0);
}
/**
* ice_vc_process_vf_msg - Process request from VF
* @pf: pointer to the PF structure
* @event: pointer to the AQ event
*
* called from the common asq/arq handler to
* process request from VF
*/
void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event)
{
u32 v_opcode = le32_to_cpu(event->desc.cookie_high);
s16 vf_id = le16_to_cpu(event->desc.retval);
u16 msglen = event->msg_len;
u8 *msg = event->msg_buf;
struct ice_vf *vf = NULL;
int err = 0;
if (vf_id >= pf->num_alloc_vfs) {
err = -EINVAL;
goto error_handler;
}
vf = &pf->vf[vf_id];
/* Check if VF is disabled. */
if (test_bit(ICE_VF_STATE_DIS, vf->vf_states)) {
err = -EPERM;
goto error_handler;
}
/* Perform basic checks on the msg */
err = virtchnl_vc_validate_vf_msg(&vf->vf_ver, v_opcode, msg, msglen);
if (err) {
if (err == VIRTCHNL_ERR_PARAM)
err = -EPERM;
else
err = -EINVAL;
goto error_handler;
}
/* Perform additional checks specific to RSS and Virtchnl */
if (v_opcode == VIRTCHNL_OP_CONFIG_RSS_KEY) {
struct virtchnl_rss_key *vrk = (struct virtchnl_rss_key *)msg;
if (vrk->key_len != ICE_VSIQF_HKEY_ARRAY_SIZE)
err = -EINVAL;
} else if (v_opcode == VIRTCHNL_OP_CONFIG_RSS_LUT) {
struct virtchnl_rss_lut *vrl = (struct virtchnl_rss_lut *)msg;
if (vrl->lut_entries != ICE_VSIQF_HLUT_ARRAY_SIZE)
err = -EINVAL;
}
error_handler:
if (err) {
ice_vc_send_msg_to_vf(vf, v_opcode, ICE_ERR_PARAM, NULL, 0);
dev_err(&pf->pdev->dev, "Invalid message from VF %d, opcode %d, len %d, error %d\n",
vf_id, v_opcode, msglen, err);
return;
}
switch (v_opcode) {
case VIRTCHNL_OP_VERSION:
err = ice_vc_get_ver_msg(vf, msg);
break;
case VIRTCHNL_OP_GET_VF_RESOURCES:
err = ice_vc_get_vf_res_msg(vf, msg);
break;
case VIRTCHNL_OP_RESET_VF:
ice_vc_reset_vf_msg(vf);
break;
case VIRTCHNL_OP_ADD_ETH_ADDR:
err = ice_vc_add_mac_addr_msg(vf, msg);
break;
case VIRTCHNL_OP_DEL_ETH_ADDR:
err = ice_vc_del_mac_addr_msg(vf, msg);
break;
case VIRTCHNL_OP_CONFIG_VSI_QUEUES:
err = ice_vc_cfg_qs_msg(vf, msg);
break;
case VIRTCHNL_OP_ENABLE_QUEUES:
err = ice_vc_ena_qs_msg(vf, msg);
ice_vc_notify_vf_link_state(vf);
break;
case VIRTCHNL_OP_DISABLE_QUEUES:
err = ice_vc_dis_qs_msg(vf, msg);
break;
case VIRTCHNL_OP_REQUEST_QUEUES:
err = ice_vc_request_qs_msg(vf, msg);
break;
case VIRTCHNL_OP_CONFIG_IRQ_MAP:
err = ice_vc_cfg_irq_map_msg(vf, msg);
break;
case VIRTCHNL_OP_CONFIG_RSS_KEY:
err = ice_vc_config_rss_key(vf, msg);
break;
case VIRTCHNL_OP_CONFIG_RSS_LUT:
err = ice_vc_config_rss_lut(vf, msg);
break;
case VIRTCHNL_OP_GET_STATS:
err = ice_vc_get_stats_msg(vf, msg);
break;
case VIRTCHNL_OP_ADD_VLAN:
err = ice_vc_add_vlan_msg(vf, msg);
break;
case VIRTCHNL_OP_DEL_VLAN:
err = ice_vc_remove_vlan_msg(vf, msg);
break;
case VIRTCHNL_OP_ENABLE_VLAN_STRIPPING:
err = ice_vc_ena_vlan_stripping(vf);
break;
case VIRTCHNL_OP_DISABLE_VLAN_STRIPPING:
err = ice_vc_dis_vlan_stripping(vf);
break;
case VIRTCHNL_OP_UNKNOWN:
default:
dev_err(&pf->pdev->dev, "Unsupported opcode %d from VF %d\n",
v_opcode, vf_id);
err = ice_vc_send_msg_to_vf(vf, v_opcode, ICE_ERR_NOT_IMPL,
NULL, 0);
break;
}
if (err) {
/* Helper function cares less about error return values here
* as it is busy with pending work.
*/
dev_info(&pf->pdev->dev,
"PF failed to honor VF %d, opcode %d\n, error %d\n",
vf_id, v_opcode, err);
}
}
/**
* ice_get_vf_cfg
* @netdev: network interface device structure
......
......@@ -9,6 +9,11 @@
#define ICE_VLAN_PRIORITY_S 12
#define ICE_VLAN_M 0xFFF
#define ICE_PRIORITY_M 0x7000
#define ICE_MAX_VLAN_PER_VF 8 /* restriction for non-trusted VF */
/* Restrict number of MACs a non-trusted VF can program */
#define ICE_MAX_MACADDR_PER_VF 12
#define ICE_DFLT_NUM_INVAL_MSGS_ALLOWED 10
/* Static VF transaction/status register def */
#define VF_DEVICE_STATUS 0xAA
......@@ -44,12 +49,15 @@ struct ice_vf {
u32 driver_caps; /* reported by VF driver */
int first_vector_idx; /* first vector index of this VF */
struct ice_sw *vf_sw_id; /* switch id the VF VSIs connect to */
struct virtchnl_version_info vf_ver;
struct virtchnl_ether_addr dflt_lan_addr;
u16 port_vlan_id;
u8 pf_set_mac; /* VF MAC address set by VMM admin */
u8 trusted;
u16 lan_vsi_idx; /* index into PF struct */
u16 lan_vsi_num; /* ID as used by firmware */
u64 num_inval_msgs; /* number of continuous invalid msgs */
u64 num_valid_msgs; /* number of valid msgs detected */
unsigned long vf_caps; /* vf's adv. capabilities */
DECLARE_BITMAP(vf_states, ICE_VF_STATES_NBITS); /* VF runtime states */
unsigned int tx_rate; /* Tx bandwidth limit in Mbps */
......@@ -58,6 +66,7 @@ struct ice_vf {
u8 spoofchk;
u16 num_mac;
u16 num_vlan;
u8 num_req_qs; /* num of queue pairs requested by VF */
};
#ifdef CONFIG_PCI_IOV
......@@ -68,6 +77,7 @@ int ice_get_vf_cfg(struct net_device *netdev, int vf_id,
struct ifla_vf_info *ivi);
void ice_free_vfs(struct ice_pf *pf);
void ice_vc_process_vf_msg(struct ice_pf *pf, struct ice_rq_event_info *event);
void ice_vc_notify_reset(struct ice_pf *pf);
bool ice_reset_all_vfs(struct ice_pf *pf, bool is_vflr);
......@@ -85,6 +95,7 @@ int ice_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool ena);
#else /* CONFIG_PCI_IOV */
#define ice_process_vflr_event(pf) do {} while (0)
#define ice_free_vfs(pf) do {} while (0)
#define ice_vc_process_vf_msg(pf, event) do {} while (0)
#define ice_vc_notify_reset(pf) do {} while (0)
static inline bool
ice_reset_all_vfs(struct ice_pf __always_unused *pf,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册