/*- * BSD LICENSE * * Copyright(c) 2010-2017 Intel Corporation. All rights reserved. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of Intel Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include "base/i40e_prototype.h" #include "base/i40e_dcb.h" #include "i40e_ethdev.h" #include "i40e_pf.h" #include "i40e_rxtx.h" #include "rte_pmd_i40e.h" int rte_pmd_i40e_ping_vfs(uint16_t port, uint16_t vf) { struct rte_eth_dev *dev; struct i40e_pf *pf; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid argument."); return -EINVAL; } i40e_notify_vf_link_status(dev, &pf->vfs[vf]); return 0; } int rte_pmd_i40e_set_vf_mac_anti_spoof(uint16_t port, uint16_t vf_id, uint8_t on) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; struct i40e_hw *hw; struct i40e_vsi_context ctxt; int ret; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid argument."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } /* Check if it has been already on or off */ if (vsi->info.valid_sections & rte_cpu_to_le_16(I40E_AQ_VSI_PROP_SECURITY_VALID)) { if (on) { if ((vsi->info.sec_flags & I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK) == I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK) return 0; /* already on */ } else { if ((vsi->info.sec_flags & I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK) == 0) return 0; /* already off */ } } vsi->info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID); if (on) vsi->info.sec_flags |= I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK; else vsi->info.sec_flags &= ~I40E_AQ_VSI_SEC_FLAG_ENABLE_MAC_CHK; memset(&ctxt, 0, sizeof(ctxt)); rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info)); ctxt.seid = vsi->seid; hw = I40E_VSI_TO_HW(vsi); ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL); if (ret != I40E_SUCCESS) { ret = -ENOTSUP; PMD_DRV_LOG(ERR, "Failed to update VSI params"); } return ret; } static int i40e_add_rm_all_vlan_filter(struct i40e_vsi *vsi, uint8_t add) { uint32_t j, k; uint16_t vlan_id; struct i40e_hw *hw = I40E_VSI_TO_HW(vsi); struct i40e_aqc_add_remove_vlan_element_data vlan_data = {0}; int ret; for (j = 0; j < I40E_VFTA_SIZE; j++) { if (!vsi->vfta[j]) continue; for (k = 0; k < I40E_UINT32_BIT_SIZE; k++) { if (!(vsi->vfta[j] & (1 << k))) continue; vlan_id = j * I40E_UINT32_BIT_SIZE + k; if (!vlan_id) continue; vlan_data.vlan_tag = rte_cpu_to_le_16(vlan_id); if (add) ret = i40e_aq_add_vlan(hw, vsi->seid, &vlan_data, 1, NULL); else ret = i40e_aq_remove_vlan(hw, vsi->seid, &vlan_data, 1, NULL); if (ret != I40E_SUCCESS) { PMD_DRV_LOG(ERR, "Failed to add/rm vlan filter"); return ret; } } } return I40E_SUCCESS; } int rte_pmd_i40e_set_vf_vlan_anti_spoof(uint16_t port, uint16_t vf_id, uint8_t on) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; struct i40e_hw *hw; struct i40e_vsi_context ctxt; int ret; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid argument."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } /* Check if it has been already on or off */ if (vsi->vlan_anti_spoof_on == on) return 0; /* already on or off */ vsi->vlan_anti_spoof_on = on; if (!vsi->vlan_filter_on) { ret = i40e_add_rm_all_vlan_filter(vsi, on); if (ret) { PMD_DRV_LOG(ERR, "Failed to add/remove VLAN filters."); return -ENOTSUP; } } vsi->info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SECURITY_VALID); if (on) vsi->info.sec_flags |= I40E_AQ_VSI_SEC_FLAG_ENABLE_VLAN_CHK; else vsi->info.sec_flags &= ~I40E_AQ_VSI_SEC_FLAG_ENABLE_VLAN_CHK; memset(&ctxt, 0, sizeof(ctxt)); rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info)); ctxt.seid = vsi->seid; hw = I40E_VSI_TO_HW(vsi); ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL); if (ret != I40E_SUCCESS) { ret = -ENOTSUP; PMD_DRV_LOG(ERR, "Failed to update VSI params"); } return ret; } static int i40e_vsi_rm_mac_filter(struct i40e_vsi *vsi) { struct i40e_mac_filter *f; struct i40e_macvlan_filter *mv_f; int i, vlan_num; enum rte_mac_filter_type filter_type; int ret = I40E_SUCCESS; void *temp; /* remove all the MACs */ TAILQ_FOREACH_SAFE(f, &vsi->mac_list, next, temp) { vlan_num = vsi->vlan_num; filter_type = f->mac_info.filter_type; if (filter_type == RTE_MACVLAN_PERFECT_MATCH || filter_type == RTE_MACVLAN_HASH_MATCH) { if (vlan_num == 0) { PMD_DRV_LOG(ERR, "VLAN number shouldn't be 0"); return I40E_ERR_PARAM; } } else if (filter_type == RTE_MAC_PERFECT_MATCH || filter_type == RTE_MAC_HASH_MATCH) vlan_num = 1; mv_f = rte_zmalloc("macvlan_data", vlan_num * sizeof(*mv_f), 0); if (!mv_f) { PMD_DRV_LOG(ERR, "failed to allocate memory"); return I40E_ERR_NO_MEMORY; } for (i = 0; i < vlan_num; i++) { mv_f[i].filter_type = filter_type; rte_memcpy(&mv_f[i].macaddr, &f->mac_info.mac_addr, ETH_ADDR_LEN); } if (filter_type == RTE_MACVLAN_PERFECT_MATCH || filter_type == RTE_MACVLAN_HASH_MATCH) { ret = i40e_find_all_vlan_for_mac(vsi, mv_f, vlan_num, &f->mac_info.mac_addr); if (ret != I40E_SUCCESS) { rte_free(mv_f); return ret; } } ret = i40e_remove_macvlan_filters(vsi, mv_f, vlan_num); if (ret != I40E_SUCCESS) { rte_free(mv_f); return ret; } rte_free(mv_f); ret = I40E_SUCCESS; } return ret; } static int i40e_vsi_restore_mac_filter(struct i40e_vsi *vsi) { struct i40e_mac_filter *f; struct i40e_macvlan_filter *mv_f; int i, vlan_num = 0; int ret = I40E_SUCCESS; void *temp; /* restore all the MACs */ TAILQ_FOREACH_SAFE(f, &vsi->mac_list, next, temp) { if ((f->mac_info.filter_type == RTE_MACVLAN_PERFECT_MATCH) || (f->mac_info.filter_type == RTE_MACVLAN_HASH_MATCH)) { /** * If vlan_num is 0, that's the first time to add mac, * set mask for vlan_id 0. */ if (vsi->vlan_num == 0) { i40e_set_vlan_filter(vsi, 0, 1); vsi->vlan_num = 1; } vlan_num = vsi->vlan_num; } else if ((f->mac_info.filter_type == RTE_MAC_PERFECT_MATCH) || (f->mac_info.filter_type == RTE_MAC_HASH_MATCH)) vlan_num = 1; mv_f = rte_zmalloc("macvlan_data", vlan_num * sizeof(*mv_f), 0); if (!mv_f) { PMD_DRV_LOG(ERR, "failed to allocate memory"); return I40E_ERR_NO_MEMORY; } for (i = 0; i < vlan_num; i++) { mv_f[i].filter_type = f->mac_info.filter_type; rte_memcpy(&mv_f[i].macaddr, &f->mac_info.mac_addr, ETH_ADDR_LEN); } if (f->mac_info.filter_type == RTE_MACVLAN_PERFECT_MATCH || f->mac_info.filter_type == RTE_MACVLAN_HASH_MATCH) { ret = i40e_find_all_vlan_for_mac(vsi, mv_f, vlan_num, &f->mac_info.mac_addr); if (ret != I40E_SUCCESS) { rte_free(mv_f); return ret; } } ret = i40e_add_macvlan_filters(vsi, mv_f, vlan_num); if (ret != I40E_SUCCESS) { rte_free(mv_f); return ret; } rte_free(mv_f); ret = I40E_SUCCESS; } return ret; } static int i40e_vsi_set_tx_loopback(struct i40e_vsi *vsi, uint8_t on) { struct i40e_vsi_context ctxt; struct i40e_hw *hw; int ret; if (!vsi) return -EINVAL; hw = I40E_VSI_TO_HW(vsi); /* Use the FW API if FW >= v5.0 */ if (hw->aq.fw_maj_ver < 5) { PMD_INIT_LOG(ERR, "FW < v5.0, cannot enable loopback"); return -ENOTSUP; } /* Check if it has been already on or off */ if (vsi->info.valid_sections & rte_cpu_to_le_16(I40E_AQ_VSI_PROP_SWITCH_VALID)) { if (on) { if ((vsi->info.switch_id & I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB) == I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB) return 0; /* already on */ } else { if ((vsi->info.switch_id & I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB) == 0) return 0; /* already off */ } } /* remove all the MAC and VLAN first */ ret = i40e_vsi_rm_mac_filter(vsi); if (ret) { PMD_INIT_LOG(ERR, "Failed to remove MAC filters."); return ret; } if (vsi->vlan_anti_spoof_on || vsi->vlan_filter_on) { ret = i40e_add_rm_all_vlan_filter(vsi, 0); if (ret) { PMD_INIT_LOG(ERR, "Failed to remove VLAN filters."); return ret; } } vsi->info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_SWITCH_VALID); if (on) vsi->info.switch_id |= I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB; else vsi->info.switch_id &= ~I40E_AQ_VSI_SW_ID_FLAG_ALLOW_LB; memset(&ctxt, 0, sizeof(ctxt)); rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info)); ctxt.seid = vsi->seid; ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL); if (ret != I40E_SUCCESS) { PMD_DRV_LOG(ERR, "Failed to update VSI params"); return ret; } /* add all the MAC and VLAN back */ ret = i40e_vsi_restore_mac_filter(vsi); if (ret) return ret; if (vsi->vlan_anti_spoof_on || vsi->vlan_filter_on) { ret = i40e_add_rm_all_vlan_filter(vsi, 1); if (ret) return ret; } return ret; } int rte_pmd_i40e_set_tx_loopback(uint16_t port, uint8_t on) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_pf_vf *vf; struct i40e_vsi *vsi; uint16_t vf_id; int ret; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); /* setup PF TX loopback */ vsi = pf->main_vsi; ret = i40e_vsi_set_tx_loopback(vsi, on); if (ret) return -ENOTSUP; /* setup TX loopback for all the VFs */ if (!pf->vfs) { /* if no VF, do nothing. */ return 0; } for (vf_id = 0; vf_id < pf->vf_num; vf_id++) { vf = &pf->vfs[vf_id]; vsi = vf->vsi; ret = i40e_vsi_set_tx_loopback(vsi, on); if (ret) return -ENOTSUP; } return ret; } int rte_pmd_i40e_set_vf_unicast_promisc(uint16_t port, uint16_t vf_id, uint8_t on) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; struct i40e_hw *hw; int ret; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid argument."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } hw = I40E_VSI_TO_HW(vsi); ret = i40e_aq_set_vsi_unicast_promiscuous(hw, vsi->seid, on, NULL, true); if (ret != I40E_SUCCESS) { ret = -ENOTSUP; PMD_DRV_LOG(ERR, "Failed to set unicast promiscuous mode"); } return ret; } int rte_pmd_i40e_set_vf_multicast_promisc(uint16_t port, uint16_t vf_id, uint8_t on) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; struct i40e_hw *hw; int ret; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid argument."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } hw = I40E_VSI_TO_HW(vsi); ret = i40e_aq_set_vsi_multicast_promiscuous(hw, vsi->seid, on, NULL); if (ret != I40E_SUCCESS) { ret = -ENOTSUP; PMD_DRV_LOG(ERR, "Failed to set multicast promiscuous mode"); } return ret; } int rte_pmd_i40e_set_vf_mac_addr(uint16_t port, uint16_t vf_id, struct ether_addr *mac_addr) { struct i40e_mac_filter *f; struct rte_eth_dev *dev; struct i40e_pf_vf *vf; struct i40e_vsi *vsi; struct i40e_pf *pf; void *temp; if (i40e_validate_mac_addr((u8 *)mac_addr) != I40E_SUCCESS) return -EINVAL; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) return -EINVAL; vf = &pf->vfs[vf_id]; vsi = vf->vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } ether_addr_copy(mac_addr, &vf->mac_addr); /* Remove all existing mac */ TAILQ_FOREACH_SAFE(f, &vsi->mac_list, next, temp) if (i40e_vsi_delete_mac(vsi, &f->mac_info.mac_addr) != I40E_SUCCESS) PMD_DRV_LOG(WARNING, "Delete MAC failed"); return 0; } /* Set vlan strip on/off for specific VF from host */ int rte_pmd_i40e_set_vf_vlan_stripq(uint16_t port, uint16_t vf_id, uint8_t on) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; int ret; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid argument."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) return -EINVAL; ret = i40e_vsi_config_vlan_stripping(vsi, !!on); if (ret != I40E_SUCCESS) { ret = -ENOTSUP; PMD_DRV_LOG(ERR, "Failed to set VLAN stripping!"); } return ret; } int rte_pmd_i40e_set_vf_vlan_insert(uint16_t port, uint16_t vf_id, uint16_t vlan_id) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_hw *hw; struct i40e_vsi *vsi; struct i40e_vsi_context ctxt; int ret; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); if (vlan_id > ETHER_MAX_VLAN_ID) { PMD_DRV_LOG(ERR, "Invalid VLAN ID."); return -EINVAL; } dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); hw = I40E_PF_TO_HW(pf); /** * return -ENODEV if SRIOV not enabled, VF number not configured * or no queue assigned. */ if (!hw->func_caps.sr_iov_1_1 || pf->vf_num == 0 || pf->vf_nb_qps == 0) return -ENODEV; if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid VF ID."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } vsi->info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID); vsi->info.pvid = vlan_id; if (vlan_id > 0) vsi->info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_INSERT_PVID; else vsi->info.port_vlan_flags &= ~I40E_AQ_VSI_PVLAN_INSERT_PVID; memset(&ctxt, 0, sizeof(ctxt)); rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info)); ctxt.seid = vsi->seid; hw = I40E_VSI_TO_HW(vsi); ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL); if (ret != I40E_SUCCESS) { ret = -ENOTSUP; PMD_DRV_LOG(ERR, "Failed to update VSI params"); } return ret; } int rte_pmd_i40e_set_vf_broadcast(uint16_t port, uint16_t vf_id, uint8_t on) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; struct i40e_hw *hw; struct i40e_mac_filter_info filter; struct ether_addr broadcast = { .addr_bytes = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} }; int ret; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); if (on > 1) { PMD_DRV_LOG(ERR, "on should be 0 or 1."); return -EINVAL; } dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); hw = I40E_PF_TO_HW(pf); if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid VF ID."); return -EINVAL; } /** * return -ENODEV if SRIOV not enabled, VF number not configured * or no queue assigned. */ if (!hw->func_caps.sr_iov_1_1 || pf->vf_num == 0 || pf->vf_nb_qps == 0) { PMD_DRV_LOG(ERR, "SRIOV is not enabled or no queue."); return -ENODEV; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } if (on) { rte_memcpy(&filter.mac_addr, &broadcast, ETHER_ADDR_LEN); filter.filter_type = RTE_MACVLAN_PERFECT_MATCH; ret = i40e_vsi_add_mac(vsi, &filter); } else { ret = i40e_vsi_delete_mac(vsi, &broadcast); } if (ret != I40E_SUCCESS && ret != I40E_ERR_PARAM) { ret = -ENOTSUP; PMD_DRV_LOG(ERR, "Failed to set VSI broadcast"); } else { ret = 0; } return ret; } int rte_pmd_i40e_set_vf_vlan_tag(uint16_t port, uint16_t vf_id, uint8_t on) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_hw *hw; struct i40e_vsi *vsi; struct i40e_vsi_context ctxt; int ret; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); if (on > 1) { PMD_DRV_LOG(ERR, "on should be 0 or 1."); return -EINVAL; } dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); hw = I40E_PF_TO_HW(pf); /** * return -ENODEV if SRIOV not enabled, VF number not configured * or no queue assigned. */ if (!hw->func_caps.sr_iov_1_1 || pf->vf_num == 0 || pf->vf_nb_qps == 0) { PMD_DRV_LOG(ERR, "SRIOV is not enabled or no queue."); return -ENODEV; } if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid VF ID."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } vsi->info.valid_sections = cpu_to_le16(I40E_AQ_VSI_PROP_VLAN_VALID); if (on) { vsi->info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_MODE_TAGGED; vsi->info.port_vlan_flags &= ~I40E_AQ_VSI_PVLAN_MODE_UNTAGGED; } else { vsi->info.port_vlan_flags |= I40E_AQ_VSI_PVLAN_MODE_UNTAGGED; vsi->info.port_vlan_flags &= ~I40E_AQ_VSI_PVLAN_MODE_TAGGED; } memset(&ctxt, 0, sizeof(ctxt)); rte_memcpy(&ctxt.info, &vsi->info, sizeof(vsi->info)); ctxt.seid = vsi->seid; hw = I40E_VSI_TO_HW(vsi); ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL); if (ret != I40E_SUCCESS) { ret = -ENOTSUP; PMD_DRV_LOG(ERR, "Failed to update VSI params"); } return ret; } static int i40e_vlan_filter_count(struct i40e_vsi *vsi) { uint32_t j, k; uint16_t vlan_id; int count = 0; for (j = 0; j < I40E_VFTA_SIZE; j++) { if (!vsi->vfta[j]) continue; for (k = 0; k < I40E_UINT32_BIT_SIZE; k++) { if (!(vsi->vfta[j] & (1 << k))) continue; vlan_id = j * I40E_UINT32_BIT_SIZE + k; if (!vlan_id) continue; count++; } } return count; } int rte_pmd_i40e_set_vf_vlan_filter(uint16_t port, uint16_t vlan_id, uint64_t vf_mask, uint8_t on) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_hw *hw; struct i40e_vsi *vsi; uint16_t vf_idx; int ret = I40E_SUCCESS; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; if (vlan_id > ETHER_MAX_VLAN_ID || !vlan_id) { PMD_DRV_LOG(ERR, "Invalid VLAN ID."); return -EINVAL; } if (vf_mask == 0) { PMD_DRV_LOG(ERR, "No VF."); return -EINVAL; } if (on > 1) { PMD_DRV_LOG(ERR, "on is should be 0 or 1."); return -EINVAL; } pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); hw = I40E_PF_TO_HW(pf); /** * return -ENODEV if SRIOV not enabled, VF number not configured * or no queue assigned. */ if (!hw->func_caps.sr_iov_1_1 || pf->vf_num == 0 || pf->vf_nb_qps == 0) { PMD_DRV_LOG(ERR, "SRIOV is not enabled or no queue."); return -ENODEV; } for (vf_idx = 0; vf_idx < pf->vf_num && ret == I40E_SUCCESS; vf_idx++) { if (vf_mask & ((uint64_t)(1ULL << vf_idx))) { vsi = pf->vfs[vf_idx].vsi; if (on) { if (!vsi->vlan_filter_on) { vsi->vlan_filter_on = true; i40e_aq_set_vsi_vlan_promisc(hw, vsi->seid, false, NULL); if (!vsi->vlan_anti_spoof_on) i40e_add_rm_all_vlan_filter( vsi, true); } ret = i40e_vsi_add_vlan(vsi, vlan_id); } else { ret = i40e_vsi_delete_vlan(vsi, vlan_id); if (!i40e_vlan_filter_count(vsi)) { vsi->vlan_filter_on = false; i40e_aq_set_vsi_vlan_promisc(hw, vsi->seid, true, NULL); } } } } if (ret != I40E_SUCCESS) { ret = -ENOTSUP; PMD_DRV_LOG(ERR, "Failed to set VF VLAN filter, on = %d", on); } return ret; } int rte_pmd_i40e_get_vf_stats(uint16_t port, uint16_t vf_id, struct rte_eth_stats *stats) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid VF ID."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } i40e_update_vsi_stats(vsi); stats->ipackets = vsi->eth_stats.rx_unicast + vsi->eth_stats.rx_multicast + vsi->eth_stats.rx_broadcast; stats->opackets = vsi->eth_stats.tx_unicast + vsi->eth_stats.tx_multicast + vsi->eth_stats.tx_broadcast; stats->ibytes = vsi->eth_stats.rx_bytes; stats->obytes = vsi->eth_stats.tx_bytes; stats->ierrors = vsi->eth_stats.rx_discards; stats->oerrors = vsi->eth_stats.tx_errors + vsi->eth_stats.tx_discards; return 0; } int rte_pmd_i40e_reset_vf_stats(uint16_t port, uint16_t vf_id) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid VF ID."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } vsi->offset_loaded = false; i40e_update_vsi_stats(vsi); return 0; } int rte_pmd_i40e_set_vf_max_bw(uint16_t port, uint16_t vf_id, uint32_t bw) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; struct i40e_hw *hw; int ret = 0; int i; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid VF ID."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } if (bw > I40E_QOS_BW_MAX) { PMD_DRV_LOG(ERR, "Bandwidth should not be larger than %dMbps.", I40E_QOS_BW_MAX); return -EINVAL; } if (bw % I40E_QOS_BW_GRANULARITY) { PMD_DRV_LOG(ERR, "Bandwidth should be the multiple of %dMbps.", I40E_QOS_BW_GRANULARITY); return -EINVAL; } bw /= I40E_QOS_BW_GRANULARITY; hw = I40E_VSI_TO_HW(vsi); /* No change. */ if (bw == vsi->bw_info.bw_limit) { PMD_DRV_LOG(INFO, "No change for VF max bandwidth. Nothing to do."); return 0; } /** * VF bandwidth limitation and TC bandwidth limitation cannot be * enabled in parallel, quit if TC bandwidth limitation is enabled. * * If bw is 0, means disable bandwidth limitation. Then no need to * check TC bandwidth limitation. */ if (bw) { for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { if ((vsi->enabled_tc & BIT_ULL(i)) && vsi->bw_info.bw_ets_credits[i]) break; } if (i != I40E_MAX_TRAFFIC_CLASS) { PMD_DRV_LOG(ERR, "TC max bandwidth has been set on this VF," " please disable it first."); return -EINVAL; } } ret = i40e_aq_config_vsi_bw_limit(hw, vsi->seid, (uint16_t)bw, 0, NULL); if (ret) { PMD_DRV_LOG(ERR, "Failed to set VF %d bandwidth, err(%d).", vf_id, ret); return -EINVAL; } /* Store the configuration. */ vsi->bw_info.bw_limit = (uint16_t)bw; vsi->bw_info.bw_max = 0; return 0; } int rte_pmd_i40e_set_vf_tc_bw_alloc(uint16_t port, uint16_t vf_id, uint8_t tc_num, uint8_t *bw_weight) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; struct i40e_hw *hw; struct i40e_aqc_configure_vsi_tc_bw_data tc_bw; int ret = 0; int i, j; uint16_t sum; bool b_change = false; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid VF ID."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } if (tc_num > I40E_MAX_TRAFFIC_CLASS) { PMD_DRV_LOG(ERR, "TCs should be no more than %d.", I40E_MAX_TRAFFIC_CLASS); return -EINVAL; } sum = 0; for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { if (vsi->enabled_tc & BIT_ULL(i)) sum++; } if (sum != tc_num) { PMD_DRV_LOG(ERR, "Weight should be set for all %d enabled TCs.", sum); return -EINVAL; } sum = 0; for (i = 0; i < tc_num; i++) { if (!bw_weight[i]) { PMD_DRV_LOG(ERR, "The weight should be 1 at least."); return -EINVAL; } sum += bw_weight[i]; } if (sum != 100) { PMD_DRV_LOG(ERR, "The summary of the TC weight should be 100."); return -EINVAL; } /** * Create the configuration for all the TCs. */ memset(&tc_bw, 0, sizeof(tc_bw)); tc_bw.tc_valid_bits = vsi->enabled_tc; j = 0; for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { if (vsi->enabled_tc & BIT_ULL(i)) { if (bw_weight[j] != vsi->bw_info.bw_ets_share_credits[i]) b_change = true; tc_bw.tc_bw_credits[i] = bw_weight[j]; j++; } } /* No change. */ if (!b_change) { PMD_DRV_LOG(INFO, "No change for TC allocated bandwidth." " Nothing to do."); return 0; } hw = I40E_VSI_TO_HW(vsi); ret = i40e_aq_config_vsi_tc_bw(hw, vsi->seid, &tc_bw, NULL); if (ret) { PMD_DRV_LOG(ERR, "Failed to set VF %d TC bandwidth weight, err(%d).", vf_id, ret); return -EINVAL; } /* Store the configuration. */ j = 0; for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { if (vsi->enabled_tc & BIT_ULL(i)) { vsi->bw_info.bw_ets_share_credits[i] = bw_weight[j]; j++; } } return 0; } int rte_pmd_i40e_set_vf_tc_max_bw(uint16_t port, uint16_t vf_id, uint8_t tc_no, uint32_t bw) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; struct i40e_hw *hw; struct i40e_aqc_configure_vsi_ets_sla_bw_data tc_bw; int ret = 0; int i; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) { PMD_DRV_LOG(ERR, "Invalid VF ID."); return -EINVAL; } vsi = pf->vfs[vf_id].vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } if (bw > I40E_QOS_BW_MAX) { PMD_DRV_LOG(ERR, "Bandwidth should not be larger than %dMbps.", I40E_QOS_BW_MAX); return -EINVAL; } if (bw % I40E_QOS_BW_GRANULARITY) { PMD_DRV_LOG(ERR, "Bandwidth should be the multiple of %dMbps.", I40E_QOS_BW_GRANULARITY); return -EINVAL; } bw /= I40E_QOS_BW_GRANULARITY; if (tc_no >= I40E_MAX_TRAFFIC_CLASS) { PMD_DRV_LOG(ERR, "TC No. should be less than %d.", I40E_MAX_TRAFFIC_CLASS); return -EINVAL; } hw = I40E_VSI_TO_HW(vsi); if (!(vsi->enabled_tc & BIT_ULL(tc_no))) { PMD_DRV_LOG(ERR, "VF %d TC %d isn't enabled.", vf_id, tc_no); return -EINVAL; } /* No change. */ if (bw == vsi->bw_info.bw_ets_credits[tc_no]) { PMD_DRV_LOG(INFO, "No change for TC max bandwidth. Nothing to do."); return 0; } /** * VF bandwidth limitation and TC bandwidth limitation cannot be * enabled in parallel, disable VF bandwidth limitation if it's * enabled. * If bw is 0, means disable bandwidth limitation. Then no need to * care about VF bandwidth limitation configuration. */ if (bw && vsi->bw_info.bw_limit) { ret = i40e_aq_config_vsi_bw_limit(hw, vsi->seid, 0, 0, NULL); if (ret) { PMD_DRV_LOG(ERR, "Failed to disable VF(%d)" " bandwidth limitation, err(%d).", vf_id, ret); return -EINVAL; } PMD_DRV_LOG(INFO, "VF max bandwidth is disabled according" " to TC max bandwidth setting."); } /** * Get all the TCs' info to create a whole picture. * Because the incremental change isn't permitted. */ memset(&tc_bw, 0, sizeof(tc_bw)); tc_bw.tc_valid_bits = vsi->enabled_tc; for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { if (vsi->enabled_tc & BIT_ULL(i)) { tc_bw.tc_bw_credits[i] = rte_cpu_to_le_16( vsi->bw_info.bw_ets_credits[i]); } } tc_bw.tc_bw_credits[tc_no] = rte_cpu_to_le_16((uint16_t)bw); ret = i40e_aq_config_vsi_ets_sla_bw_limit(hw, vsi->seid, &tc_bw, NULL); if (ret) { PMD_DRV_LOG(ERR, "Failed to set VF %d TC %d max bandwidth, err(%d).", vf_id, tc_no, ret); return -EINVAL; } /* Store the configuration. */ vsi->bw_info.bw_ets_credits[tc_no] = (uint16_t)bw; return 0; } int rte_pmd_i40e_set_tc_strict_prio(uint16_t port, uint8_t tc_map) { struct rte_eth_dev *dev; struct i40e_pf *pf; struct i40e_vsi *vsi; struct i40e_veb *veb; struct i40e_hw *hw; struct i40e_aqc_configure_switching_comp_ets_data ets_data; int i; int ret; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); vsi = pf->main_vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } veb = vsi->veb; if (!veb) { PMD_DRV_LOG(ERR, "Invalid VEB."); return -EINVAL; } if ((tc_map & veb->enabled_tc) != tc_map) { PMD_DRV_LOG(ERR, "TC bitmap isn't the subset of enabled TCs 0x%x.", veb->enabled_tc); return -EINVAL; } if (tc_map == veb->strict_prio_tc) { PMD_DRV_LOG(INFO, "No change for TC bitmap. Nothing to do."); return 0; } hw = I40E_VSI_TO_HW(vsi); /* Disable DCBx if it's the first time to set strict priority. */ if (!veb->strict_prio_tc) { ret = i40e_aq_stop_lldp(hw, true, NULL); if (ret) PMD_DRV_LOG(INFO, "Failed to disable DCBx as it's already" " disabled."); else PMD_DRV_LOG(INFO, "DCBx is disabled according to strict" " priority setting."); } memset(&ets_data, 0, sizeof(ets_data)); ets_data.tc_valid_bits = veb->enabled_tc; ets_data.seepage = I40E_AQ_ETS_SEEPAGE_EN_MASK; ets_data.tc_strict_priority_flags = tc_map; /* Get all TCs' bandwidth. */ for (i = 0; i < I40E_MAX_TRAFFIC_CLASS; i++) { if (veb->enabled_tc & BIT_ULL(i)) { /* For rubust, if bandwidth is 0, use 1 instead. */ if (veb->bw_info.bw_ets_share_credits[i]) ets_data.tc_bw_share_credits[i] = veb->bw_info.bw_ets_share_credits[i]; else ets_data.tc_bw_share_credits[i] = I40E_QOS_BW_WEIGHT_MIN; } } if (!veb->strict_prio_tc) ret = i40e_aq_config_switch_comp_ets( hw, veb->uplink_seid, &ets_data, i40e_aqc_opc_enable_switching_comp_ets, NULL); else if (tc_map) ret = i40e_aq_config_switch_comp_ets( hw, veb->uplink_seid, &ets_data, i40e_aqc_opc_modify_switching_comp_ets, NULL); else ret = i40e_aq_config_switch_comp_ets( hw, veb->uplink_seid, &ets_data, i40e_aqc_opc_disable_switching_comp_ets, NULL); if (ret) { PMD_DRV_LOG(ERR, "Failed to set TCs' strict priority mode." " err (%d)", ret); return -EINVAL; } veb->strict_prio_tc = tc_map; /* Enable DCBx again, if all the TCs' strict priority disabled. */ if (!tc_map) { ret = i40e_aq_start_lldp(hw, NULL); if (ret) { PMD_DRV_LOG(ERR, "Failed to enable DCBx, err(%d).", ret); return -EINVAL; } PMD_DRV_LOG(INFO, "DCBx is enabled again according to strict" " priority setting."); } return ret; } #define I40E_PROFILE_INFO_SIZE sizeof(struct rte_pmd_i40e_profile_info) #define I40E_MAX_PROFILE_NUM 16 static void i40e_generate_profile_info_sec(char *name, struct i40e_ddp_version *version, uint32_t track_id, uint8_t *profile_info_sec, bool add) { struct i40e_profile_section_header *sec = NULL; struct i40e_profile_info *pinfo; sec = (struct i40e_profile_section_header *)profile_info_sec; sec->tbl_size = 1; sec->data_end = sizeof(struct i40e_profile_section_header) + sizeof(struct i40e_profile_info); sec->section.type = SECTION_TYPE_INFO; sec->section.offset = sizeof(struct i40e_profile_section_header); sec->section.size = sizeof(struct i40e_profile_info); pinfo = (struct i40e_profile_info *)(profile_info_sec + sec->section.offset); pinfo->track_id = track_id; memcpy(pinfo->name, name, I40E_DDP_NAME_SIZE); memcpy(&pinfo->version, version, sizeof(struct i40e_ddp_version)); if (add) pinfo->op = I40E_DDP_ADD_TRACKID; else pinfo->op = I40E_DDP_REMOVE_TRACKID; } static enum i40e_status_code i40e_add_rm_profile_info(struct i40e_hw *hw, uint8_t *profile_info_sec) { enum i40e_status_code status = I40E_SUCCESS; struct i40e_profile_section_header *sec; uint32_t track_id; uint32_t offset = 0; uint32_t info = 0; sec = (struct i40e_profile_section_header *)profile_info_sec; track_id = ((struct i40e_profile_info *)(profile_info_sec + sec->section.offset))->track_id; status = i40e_aq_write_ddp(hw, (void *)sec, sec->data_end, track_id, &offset, &info, NULL); if (status) PMD_DRV_LOG(ERR, "Failed to add/remove profile info: " "offset %d, info %d", offset, info); return status; } /* Check if the profile info exists */ static int i40e_check_profile_info(uint16_t port, uint8_t *profile_info_sec) { struct rte_eth_dev *dev = &rte_eth_devices[port]; struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private); uint8_t *buff; struct rte_pmd_i40e_profile_list *p_list; struct rte_pmd_i40e_profile_info *pinfo, *p; uint32_t i; int ret; static const uint32_t group_mask = 0x00ff0000; pinfo = (struct rte_pmd_i40e_profile_info *)(profile_info_sec + sizeof(struct i40e_profile_section_header)); if (pinfo->track_id == 0) { PMD_DRV_LOG(INFO, "Read-only profile."); return 0; } buff = rte_zmalloc("pinfo_list", (I40E_PROFILE_INFO_SIZE * I40E_MAX_PROFILE_NUM + 4), 0); if (!buff) { PMD_DRV_LOG(ERR, "failed to allocate memory"); return -1; } ret = i40e_aq_get_ddp_list( hw, (void *)buff, (I40E_PROFILE_INFO_SIZE * I40E_MAX_PROFILE_NUM + 4), 0, NULL); if (ret) { PMD_DRV_LOG(ERR, "Failed to get profile info list."); rte_free(buff); return -1; } p_list = (struct rte_pmd_i40e_profile_list *)buff; for (i = 0; i < p_list->p_count; i++) { p = &p_list->p_info[i]; if (pinfo->track_id == p->track_id) { PMD_DRV_LOG(INFO, "Profile exists."); rte_free(buff); return 1; } } for (i = 0; i < p_list->p_count; i++) { p = &p_list->p_info[i]; if ((p->track_id & group_mask) == 0) { PMD_DRV_LOG(INFO, "Profile of the group 0 exists."); rte_free(buff); return 2; } } for (i = 0; i < p_list->p_count; i++) { p = &p_list->p_info[i]; if ((pinfo->track_id & group_mask) != (p->track_id & group_mask)) { PMD_DRV_LOG(INFO, "Profile of different group exists."); rte_free(buff); return 3; } } rte_free(buff); return 0; } int rte_pmd_i40e_process_ddp_package(uint16_t port, uint8_t *buff, uint32_t size, enum rte_pmd_i40e_package_op op) { struct rte_eth_dev *dev; struct i40e_hw *hw; struct i40e_package_header *pkg_hdr; struct i40e_generic_seg_header *profile_seg_hdr; struct i40e_generic_seg_header *metadata_seg_hdr; uint32_t track_id; uint8_t *profile_info_sec; int is_exist; enum i40e_status_code status = I40E_SUCCESS; static const uint32_t type_mask = 0xff000000; if (op != RTE_PMD_I40E_PKG_OP_WR_ADD && op != RTE_PMD_I40E_PKG_OP_WR_ONLY && op != RTE_PMD_I40E_PKG_OP_WR_DEL) { PMD_DRV_LOG(ERR, "Operation not supported."); return -ENOTSUP; } RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private); if (size < (sizeof(struct i40e_package_header) + sizeof(struct i40e_metadata_segment) + sizeof(uint32_t) * 2)) { PMD_DRV_LOG(ERR, "Buff is invalid."); return -EINVAL; } pkg_hdr = (struct i40e_package_header *)buff; if (!pkg_hdr) { PMD_DRV_LOG(ERR, "Failed to fill the package structure"); return -EINVAL; } if (pkg_hdr->segment_count < 2) { PMD_DRV_LOG(ERR, "Segment_count should be 2 at least."); return -EINVAL; } /* Find metadata segment */ metadata_seg_hdr = i40e_find_segment_in_package(SEGMENT_TYPE_METADATA, pkg_hdr); if (!metadata_seg_hdr) { PMD_DRV_LOG(ERR, "Failed to find metadata segment header"); return -EINVAL; } track_id = ((struct i40e_metadata_segment *)metadata_seg_hdr)->track_id; if (track_id == I40E_DDP_TRACKID_INVALID) { PMD_DRV_LOG(ERR, "Invalid track_id"); return -EINVAL; } /* force read-only track_id for type 0 */ if ((track_id & type_mask) == 0) track_id = 0; /* Find profile segment */ profile_seg_hdr = i40e_find_segment_in_package(SEGMENT_TYPE_I40E, pkg_hdr); if (!profile_seg_hdr) { PMD_DRV_LOG(ERR, "Failed to find profile segment header"); return -EINVAL; } profile_info_sec = rte_zmalloc( "i40e_profile_info", sizeof(struct i40e_profile_section_header) + sizeof(struct i40e_profile_info), 0); if (!profile_info_sec) { PMD_DRV_LOG(ERR, "Failed to allocate memory"); return -EINVAL; } /* Check if the profile already loaded */ i40e_generate_profile_info_sec( ((struct i40e_profile_segment *)profile_seg_hdr)->name, &((struct i40e_profile_segment *)profile_seg_hdr)->version, track_id, profile_info_sec, op == RTE_PMD_I40E_PKG_OP_WR_ADD); is_exist = i40e_check_profile_info(port, profile_info_sec); if (is_exist < 0) { PMD_DRV_LOG(ERR, "Failed to check profile."); rte_free(profile_info_sec); return -EINVAL; } if (op == RTE_PMD_I40E_PKG_OP_WR_ADD) { if (is_exist) { if (is_exist == 1) PMD_DRV_LOG(ERR, "Profile already exists."); else if (is_exist == 2) PMD_DRV_LOG(ERR, "Profile of group 0 already exists."); else if (is_exist == 3) PMD_DRV_LOG(ERR, "Profile of different group already exists"); i40e_update_customized_info(dev, buff, size, op); rte_free(profile_info_sec); return -EEXIST; } } else if (op == RTE_PMD_I40E_PKG_OP_WR_DEL) { if (is_exist != 1) { PMD_DRV_LOG(ERR, "Profile does not exist."); rte_free(profile_info_sec); return -EACCES; } } if (op == RTE_PMD_I40E_PKG_OP_WR_DEL) { status = i40e_rollback_profile( hw, (struct i40e_profile_segment *)profile_seg_hdr, track_id); if (status) { PMD_DRV_LOG(ERR, "Failed to write profile for delete."); rte_free(profile_info_sec); return status; } } else { status = i40e_write_profile( hw, (struct i40e_profile_segment *)profile_seg_hdr, track_id); if (status) { if (op == RTE_PMD_I40E_PKG_OP_WR_ADD) PMD_DRV_LOG(ERR, "Failed to write profile for add."); else PMD_DRV_LOG(ERR, "Failed to write profile."); rte_free(profile_info_sec); return status; } } if (track_id && (op != RTE_PMD_I40E_PKG_OP_WR_ONLY)) { /* Modify loaded profiles info list */ status = i40e_add_rm_profile_info(hw, profile_info_sec); if (status) { if (op == RTE_PMD_I40E_PKG_OP_WR_ADD) PMD_DRV_LOG(ERR, "Failed to add profile to info list."); else PMD_DRV_LOG(ERR, "Failed to delete profile from info list."); } } if (op == RTE_PMD_I40E_PKG_OP_WR_ADD || op == RTE_PMD_I40E_PKG_OP_WR_DEL) i40e_update_customized_info(dev, buff, size, op); rte_free(profile_info_sec); return status; } /* Get number of tvl records in the section */ static unsigned int i40e_get_tlv_section_size(struct i40e_profile_section_header *sec) { unsigned int i, nb_rec, nb_tlv = 0; struct i40e_profile_tlv_section_record *tlv; if (!sec) return nb_tlv; /* get number of records in the section */ nb_rec = sec->section.size / sizeof(struct i40e_profile_tlv_section_record); for (i = 0; i < nb_rec; ) { tlv = (struct i40e_profile_tlv_section_record *)&sec[1 + i]; i += tlv->len; nb_tlv++; } return nb_tlv; } int rte_pmd_i40e_get_ddp_info(uint8_t *pkg_buff, uint32_t pkg_size, uint8_t *info_buff, uint32_t info_size, enum rte_pmd_i40e_package_info type) { uint32_t ret_size; struct i40e_package_header *pkg_hdr; struct i40e_generic_seg_header *i40e_seg_hdr; struct i40e_generic_seg_header *note_seg_hdr; struct i40e_generic_seg_header *metadata_seg_hdr; if (!info_buff) { PMD_DRV_LOG(ERR, "Output info buff is invalid."); return -EINVAL; } if (!pkg_buff || pkg_size < (sizeof(struct i40e_package_header) + sizeof(struct i40e_metadata_segment) + sizeof(uint32_t) * 2)) { PMD_DRV_LOG(ERR, "Package buff is invalid."); return -EINVAL; } pkg_hdr = (struct i40e_package_header *)pkg_buff; if (pkg_hdr->segment_count < 2) { PMD_DRV_LOG(ERR, "Segment_count should be 2 at least."); return -EINVAL; } /* Find metadata segment */ metadata_seg_hdr = i40e_find_segment_in_package(SEGMENT_TYPE_METADATA, pkg_hdr); /* Find global notes segment */ note_seg_hdr = i40e_find_segment_in_package(SEGMENT_TYPE_NOTES, pkg_hdr); /* Find i40e profile segment */ i40e_seg_hdr = i40e_find_segment_in_package(SEGMENT_TYPE_I40E, pkg_hdr); /* get global header info */ if (type == RTE_PMD_I40E_PKG_INFO_GLOBAL_HEADER) { struct rte_pmd_i40e_profile_info *info = (struct rte_pmd_i40e_profile_info *)info_buff; if (info_size < sizeof(struct rte_pmd_i40e_profile_info)) { PMD_DRV_LOG(ERR, "Output info buff size is invalid."); return -EINVAL; } if (!metadata_seg_hdr) { PMD_DRV_LOG(ERR, "Failed to find metadata segment header"); return -EINVAL; } memset(info, 0, sizeof(struct rte_pmd_i40e_profile_info)); info->owner = RTE_PMD_I40E_DDP_OWNER_UNKNOWN; info->track_id = ((struct i40e_metadata_segment *)metadata_seg_hdr)->track_id; memcpy(info->name, ((struct i40e_metadata_segment *)metadata_seg_hdr)->name, I40E_DDP_NAME_SIZE); memcpy(&info->version, &((struct i40e_metadata_segment *)metadata_seg_hdr)->version, sizeof(struct i40e_ddp_version)); return I40E_SUCCESS; } /* get global note size */ if (type == RTE_PMD_I40E_PKG_INFO_GLOBAL_NOTES_SIZE) { if (info_size < sizeof(uint32_t)) { PMD_DRV_LOG(ERR, "Invalid information buffer size"); return -EINVAL; } if (note_seg_hdr == NULL) ret_size = 0; else ret_size = note_seg_hdr->size; *(uint32_t *)info_buff = ret_size; return I40E_SUCCESS; } /* get global note */ if (type == RTE_PMD_I40E_PKG_INFO_GLOBAL_NOTES) { if (note_seg_hdr == NULL) return -ENOTSUP; if (info_size < note_seg_hdr->size) { PMD_DRV_LOG(ERR, "Information buffer size is too small"); return -EINVAL; } memcpy(info_buff, ¬e_seg_hdr[1], note_seg_hdr->size); return I40E_SUCCESS; } /* get i40e segment header info */ if (type == RTE_PMD_I40E_PKG_INFO_HEADER) { struct rte_pmd_i40e_profile_info *info = (struct rte_pmd_i40e_profile_info *)info_buff; if (info_size < sizeof(struct rte_pmd_i40e_profile_info)) { PMD_DRV_LOG(ERR, "Output info buff size is invalid."); return -EINVAL; } if (!metadata_seg_hdr) { PMD_DRV_LOG(ERR, "Failed to find metadata segment header"); return -EINVAL; } if (!i40e_seg_hdr) { PMD_DRV_LOG(ERR, "Failed to find i40e segment header"); return -EINVAL; } memset(info, 0, sizeof(struct rte_pmd_i40e_profile_info)); info->owner = RTE_PMD_I40E_DDP_OWNER_UNKNOWN; info->track_id = ((struct i40e_metadata_segment *)metadata_seg_hdr)->track_id; memcpy(info->name, ((struct i40e_profile_segment *)i40e_seg_hdr)->name, I40E_DDP_NAME_SIZE); memcpy(&info->version, &((struct i40e_profile_segment *)i40e_seg_hdr)->version, sizeof(struct i40e_ddp_version)); return I40E_SUCCESS; } /* get number of devices */ if (type == RTE_PMD_I40E_PKG_INFO_DEVID_NUM) { if (info_size < sizeof(uint32_t)) { PMD_DRV_LOG(ERR, "Invalid information buffer size"); return -EINVAL; } *(uint32_t *)info_buff = ((struct i40e_profile_segment *)i40e_seg_hdr)->device_table_count; return I40E_SUCCESS; } /* get list of devices */ if (type == RTE_PMD_I40E_PKG_INFO_DEVID_LIST) { uint32_t dev_num; dev_num = ((struct i40e_profile_segment *)i40e_seg_hdr)->device_table_count; if (info_size < sizeof(struct rte_pmd_i40e_ddp_device_id) * dev_num) { PMD_DRV_LOG(ERR, "Invalid information buffer size"); return -EINVAL; } memcpy(info_buff, ((struct i40e_profile_segment *)i40e_seg_hdr)->device_table, sizeof(struct rte_pmd_i40e_ddp_device_id) * dev_num); return I40E_SUCCESS; } /* get number of protocols */ if (type == RTE_PMD_I40E_PKG_INFO_PROTOCOL_NUM) { struct i40e_profile_section_header *proto; if (info_size < sizeof(uint32_t)) { PMD_DRV_LOG(ERR, "Invalid information buffer size"); return -EINVAL; } proto = i40e_find_section_in_profile(SECTION_TYPE_PROTO, (struct i40e_profile_segment *)i40e_seg_hdr); *(uint32_t *)info_buff = i40e_get_tlv_section_size(proto); return I40E_SUCCESS; } /* get list of protocols */ if (type == RTE_PMD_I40E_PKG_INFO_PROTOCOL_LIST) { uint32_t i, j, nb_tlv, nb_rec, nb_proto_info; struct rte_pmd_i40e_proto_info *pinfo; struct i40e_profile_section_header *proto; struct i40e_profile_tlv_section_record *tlv; pinfo = (struct rte_pmd_i40e_proto_info *)info_buff; nb_proto_info = info_size / sizeof(struct rte_pmd_i40e_proto_info); for (i = 0; i < nb_proto_info; i++) { pinfo[i].proto_id = RTE_PMD_I40E_PROTO_UNUSED; memset(pinfo[i].name, 0, RTE_PMD_I40E_DDP_NAME_SIZE); } proto = i40e_find_section_in_profile(SECTION_TYPE_PROTO, (struct i40e_profile_segment *)i40e_seg_hdr); nb_tlv = i40e_get_tlv_section_size(proto); if (nb_tlv == 0) return I40E_SUCCESS; if (nb_proto_info < nb_tlv) { PMD_DRV_LOG(ERR, "Invalid information buffer size"); return -EINVAL; } /* get number of records in the section */ nb_rec = proto->section.size / sizeof(struct i40e_profile_tlv_section_record); tlv = (struct i40e_profile_tlv_section_record *)&proto[1]; for (i = j = 0; i < nb_rec; j++) { pinfo[j].proto_id = tlv->data[0]; snprintf(pinfo[j].name, I40E_DDP_NAME_SIZE, "%s", (const char *)&tlv->data[1]); i += tlv->len; tlv = &tlv[tlv->len]; } return I40E_SUCCESS; } /* get number of packet classification types */ if (type == RTE_PMD_I40E_PKG_INFO_PCTYPE_NUM) { struct i40e_profile_section_header *pctype; if (info_size < sizeof(uint32_t)) { PMD_DRV_LOG(ERR, "Invalid information buffer size"); return -EINVAL; } pctype = i40e_find_section_in_profile(SECTION_TYPE_PCTYPE, (struct i40e_profile_segment *)i40e_seg_hdr); *(uint32_t *)info_buff = i40e_get_tlv_section_size(pctype); return I40E_SUCCESS; } /* get list of packet classification types */ if (type == RTE_PMD_I40E_PKG_INFO_PCTYPE_LIST) { uint32_t i, j, nb_tlv, nb_rec, nb_proto_info; struct rte_pmd_i40e_ptype_info *pinfo; struct i40e_profile_section_header *pctype; struct i40e_profile_tlv_section_record *tlv; pinfo = (struct rte_pmd_i40e_ptype_info *)info_buff; nb_proto_info = info_size / sizeof(struct rte_pmd_i40e_ptype_info); for (i = 0; i < nb_proto_info; i++) memset(&pinfo[i], RTE_PMD_I40E_PROTO_UNUSED, sizeof(struct rte_pmd_i40e_ptype_info)); pctype = i40e_find_section_in_profile(SECTION_TYPE_PCTYPE, (struct i40e_profile_segment *)i40e_seg_hdr); nb_tlv = i40e_get_tlv_section_size(pctype); if (nb_tlv == 0) return I40E_SUCCESS; if (nb_proto_info < nb_tlv) { PMD_DRV_LOG(ERR, "Invalid information buffer size"); return -EINVAL; } /* get number of records in the section */ nb_rec = pctype->section.size / sizeof(struct i40e_profile_tlv_section_record); tlv = (struct i40e_profile_tlv_section_record *)&pctype[1]; for (i = j = 0; i < nb_rec; j++) { memcpy(&pinfo[j], tlv->data, sizeof(struct rte_pmd_i40e_ptype_info)); i += tlv->len; tlv = &tlv[tlv->len]; } return I40E_SUCCESS; } /* get number of packet types */ if (type == RTE_PMD_I40E_PKG_INFO_PTYPE_NUM) { struct i40e_profile_section_header *ptype; if (info_size < sizeof(uint32_t)) { PMD_DRV_LOG(ERR, "Invalid information buffer size"); return -EINVAL; } ptype = i40e_find_section_in_profile(SECTION_TYPE_PTYPE, (struct i40e_profile_segment *)i40e_seg_hdr); *(uint32_t *)info_buff = i40e_get_tlv_section_size(ptype); return I40E_SUCCESS; } /* get list of packet types */ if (type == RTE_PMD_I40E_PKG_INFO_PTYPE_LIST) { uint32_t i, j, nb_tlv, nb_rec, nb_proto_info; struct rte_pmd_i40e_ptype_info *pinfo; struct i40e_profile_section_header *ptype; struct i40e_profile_tlv_section_record *tlv; pinfo = (struct rte_pmd_i40e_ptype_info *)info_buff; nb_proto_info = info_size / sizeof(struct rte_pmd_i40e_ptype_info); for (i = 0; i < nb_proto_info; i++) memset(&pinfo[i], RTE_PMD_I40E_PROTO_UNUSED, sizeof(struct rte_pmd_i40e_ptype_info)); ptype = i40e_find_section_in_profile(SECTION_TYPE_PTYPE, (struct i40e_profile_segment *)i40e_seg_hdr); nb_tlv = i40e_get_tlv_section_size(ptype); if (nb_tlv == 0) return I40E_SUCCESS; if (nb_proto_info < nb_tlv) { PMD_DRV_LOG(ERR, "Invalid information buffer size"); return -EINVAL; } /* get number of records in the section */ nb_rec = ptype->section.size / sizeof(struct i40e_profile_tlv_section_record); for (i = j = 0; i < nb_rec; j++) { tlv = (struct i40e_profile_tlv_section_record *) &ptype[1 + i]; memcpy(&pinfo[j], tlv->data, sizeof(struct rte_pmd_i40e_ptype_info)); i += tlv->len; } return I40E_SUCCESS; } PMD_DRV_LOG(ERR, "Info type %u is invalid.", type); return -EINVAL; } int rte_pmd_i40e_get_ddp_list(uint16_t port, uint8_t *buff, uint32_t size) { struct rte_eth_dev *dev; struct i40e_hw *hw; enum i40e_status_code status = I40E_SUCCESS; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; if (size < (I40E_PROFILE_INFO_SIZE * I40E_MAX_PROFILE_NUM + 4)) return -EINVAL; hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private); status = i40e_aq_get_ddp_list(hw, (void *)buff, size, 0, NULL); return status; } static int check_invalid_pkt_type(uint32_t pkt_type) { uint32_t l2, l3, l4, tnl, il2, il3, il4; l2 = pkt_type & RTE_PTYPE_L2_MASK; l3 = pkt_type & RTE_PTYPE_L3_MASK; l4 = pkt_type & RTE_PTYPE_L4_MASK; tnl = pkt_type & RTE_PTYPE_TUNNEL_MASK; il2 = pkt_type & RTE_PTYPE_INNER_L2_MASK; il3 = pkt_type & RTE_PTYPE_INNER_L3_MASK; il4 = pkt_type & RTE_PTYPE_INNER_L4_MASK; if (l2 && l2 != RTE_PTYPE_L2_ETHER && l2 != RTE_PTYPE_L2_ETHER_TIMESYNC && l2 != RTE_PTYPE_L2_ETHER_ARP && l2 != RTE_PTYPE_L2_ETHER_LLDP && l2 != RTE_PTYPE_L2_ETHER_NSH && l2 != RTE_PTYPE_L2_ETHER_VLAN && l2 != RTE_PTYPE_L2_ETHER_QINQ) return -1; if (l3 && l3 != RTE_PTYPE_L3_IPV4 && l3 != RTE_PTYPE_L3_IPV4_EXT && l3 != RTE_PTYPE_L3_IPV6 && l3 != RTE_PTYPE_L3_IPV4_EXT_UNKNOWN && l3 != RTE_PTYPE_L3_IPV6_EXT && l3 != RTE_PTYPE_L3_IPV6_EXT_UNKNOWN) return -1; if (l4 && l4 != RTE_PTYPE_L4_TCP && l4 != RTE_PTYPE_L4_UDP && l4 != RTE_PTYPE_L4_FRAG && l4 != RTE_PTYPE_L4_SCTP && l4 != RTE_PTYPE_L4_ICMP && l4 != RTE_PTYPE_L4_NONFRAG) return -1; if (tnl && tnl != RTE_PTYPE_TUNNEL_IP && tnl != RTE_PTYPE_TUNNEL_GRENAT && tnl != RTE_PTYPE_TUNNEL_VXLAN && tnl != RTE_PTYPE_TUNNEL_NVGRE && tnl != RTE_PTYPE_TUNNEL_GENEVE && tnl != RTE_PTYPE_TUNNEL_GRENAT && tnl != RTE_PTYPE_TUNNEL_GTPC && tnl != RTE_PTYPE_TUNNEL_GTPU) return -1; if (il2 && il2 != RTE_PTYPE_INNER_L2_ETHER && il2 != RTE_PTYPE_INNER_L2_ETHER_VLAN && il2 != RTE_PTYPE_INNER_L2_ETHER_QINQ) return -1; if (il3 && il3 != RTE_PTYPE_INNER_L3_IPV4 && il3 != RTE_PTYPE_INNER_L3_IPV4_EXT && il3 != RTE_PTYPE_INNER_L3_IPV6 && il3 != RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN && il3 != RTE_PTYPE_INNER_L3_IPV6_EXT && il3 != RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN) return -1; if (il4 && il4 != RTE_PTYPE_INNER_L4_TCP && il4 != RTE_PTYPE_INNER_L4_UDP && il4 != RTE_PTYPE_INNER_L4_FRAG && il4 != RTE_PTYPE_INNER_L4_SCTP && il4 != RTE_PTYPE_INNER_L4_ICMP && il4 != RTE_PTYPE_INNER_L4_NONFRAG) return -1; return 0; } static int check_invalid_ptype_mapping( struct rte_pmd_i40e_ptype_mapping *mapping_table, uint16_t count) { int i; for (i = 0; i < count; i++) { uint16_t ptype = mapping_table[i].hw_ptype; uint32_t pkt_type = mapping_table[i].sw_ptype; if (ptype >= I40E_MAX_PKT_TYPE) return -1; if (pkt_type == RTE_PTYPE_UNKNOWN) continue; if (pkt_type & RTE_PMD_I40E_PTYPE_USER_DEFINE_MASK) continue; if (check_invalid_pkt_type(pkt_type)) return -1; } return 0; } int rte_pmd_i40e_ptype_mapping_update( uint16_t port, struct rte_pmd_i40e_ptype_mapping *mapping_items, uint16_t count, uint8_t exclusive) { struct rte_eth_dev *dev; struct i40e_adapter *ad; int i; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; if (count > I40E_MAX_PKT_TYPE) return -EINVAL; if (check_invalid_ptype_mapping(mapping_items, count)) return -EINVAL; ad = I40E_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private); if (exclusive) { for (i = 0; i < I40E_MAX_PKT_TYPE; i++) ad->ptype_tbl[i] = RTE_PTYPE_UNKNOWN; } for (i = 0; i < count; i++) ad->ptype_tbl[mapping_items[i].hw_ptype] = mapping_items[i].sw_ptype; return 0; } int rte_pmd_i40e_ptype_mapping_reset(uint16_t port) { struct rte_eth_dev *dev; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; i40e_set_default_ptype_table(dev); return 0; } int rte_pmd_i40e_ptype_mapping_get( uint16_t port, struct rte_pmd_i40e_ptype_mapping *mapping_items, uint16_t size, uint16_t *count, uint8_t valid_only) { struct rte_eth_dev *dev; struct i40e_adapter *ad; int n = 0; uint16_t i; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; ad = I40E_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private); for (i = 0; i < I40E_MAX_PKT_TYPE; i++) { if (n >= size) break; if (valid_only && ad->ptype_tbl[i] == RTE_PTYPE_UNKNOWN) continue; mapping_items[n].hw_ptype = i; mapping_items[n].sw_ptype = ad->ptype_tbl[i]; n++; } *count = n; return 0; } int rte_pmd_i40e_ptype_mapping_replace(uint16_t port, uint32_t target, uint8_t mask, uint32_t pkt_type) { struct rte_eth_dev *dev; struct i40e_adapter *ad; uint16_t i; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; if (!mask && check_invalid_pkt_type(target)) return -EINVAL; if (check_invalid_pkt_type(pkt_type)) return -EINVAL; ad = I40E_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private); for (i = 0; i < I40E_MAX_PKT_TYPE; i++) { if (mask) { if ((target | ad->ptype_tbl[i]) == target && (target & ad->ptype_tbl[i])) ad->ptype_tbl[i] = pkt_type; } else { if (ad->ptype_tbl[i] == target) ad->ptype_tbl[i] = pkt_type; } } return 0; } int rte_pmd_i40e_add_vf_mac_addr(uint16_t port, uint16_t vf_id, struct ether_addr *mac_addr) { struct rte_eth_dev *dev; struct i40e_pf_vf *vf; struct i40e_vsi *vsi; struct i40e_pf *pf; struct i40e_mac_filter_info mac_filter; int ret; if (i40e_validate_mac_addr((u8 *)mac_addr) != I40E_SUCCESS) return -EINVAL; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); if (vf_id >= pf->vf_num || !pf->vfs) return -EINVAL; vf = &pf->vfs[vf_id]; vsi = vf->vsi; if (!vsi) { PMD_DRV_LOG(ERR, "Invalid VSI."); return -EINVAL; } mac_filter.filter_type = RTE_MACVLAN_PERFECT_MATCH; ether_addr_copy(mac_addr, &mac_filter.mac_addr); ret = i40e_vsi_add_mac(vsi, &mac_filter); if (ret != I40E_SUCCESS) { PMD_DRV_LOG(ERR, "Failed to add MAC filter."); return -1; } return 0; } int rte_pmd_i40e_flow_type_mapping_reset(uint16_t port) { struct rte_eth_dev *dev; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; i40e_set_default_pctype_table(dev); return 0; } int rte_pmd_i40e_flow_type_mapping_get( uint16_t port, struct rte_pmd_i40e_flow_type_mapping *mapping_items) { struct rte_eth_dev *dev; struct i40e_adapter *ad; uint16_t i; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; ad = I40E_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private); for (i = 0; i < I40E_FLOW_TYPE_MAX; i++) { mapping_items[i].flow_type = i; mapping_items[i].pctype = ad->pctypes_tbl[i]; } return 0; } int rte_pmd_i40e_flow_type_mapping_update( uint16_t port, struct rte_pmd_i40e_flow_type_mapping *mapping_items, uint16_t count, uint8_t exclusive) { struct rte_eth_dev *dev; struct i40e_adapter *ad; int i; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; if (count > I40E_FLOW_TYPE_MAX) return -EINVAL; for (i = 0; i < count; i++) if (mapping_items[i].flow_type >= I40E_FLOW_TYPE_MAX || mapping_items[i].flow_type == RTE_ETH_FLOW_UNKNOWN || (mapping_items[i].pctype & (1ULL << I40E_FILTER_PCTYPE_INVALID))) return -EINVAL; ad = I40E_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private); if (exclusive) { for (i = 0; i < I40E_FLOW_TYPE_MAX; i++) ad->pctypes_tbl[i] = 0ULL; ad->flow_types_mask = 0ULL; } for (i = 0; i < count; i++) { ad->pctypes_tbl[mapping_items[i].flow_type] = mapping_items[i].pctype; if (mapping_items[i].pctype) ad->flow_types_mask |= (1ULL << mapping_items[i].flow_type); else ad->flow_types_mask &= ~(1ULL << mapping_items[i].flow_type); } for (i = 0, ad->pctypes_mask = 0ULL; i < I40E_FLOW_TYPE_MAX; i++) ad->pctypes_mask |= ad->pctypes_tbl[i]; return 0; } int rte_pmd_i40e_query_vfid_by_mac(uint16_t port, const struct ether_addr *vf_mac) { struct rte_eth_dev *dev; struct ether_addr *mac; struct i40e_pf *pf; int vf_id; struct i40e_pf_vf *vf; uint16_t vf_num; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); dev = &rte_eth_devices[port]; if (!is_i40e_supported(dev)) return -ENOTSUP; pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); vf_num = pf->vf_num; for (vf_id = 0; vf_id < vf_num; vf_id++) { vf = &pf->vfs[vf_id]; mac = &vf->mac_addr; if (is_same_ether_addr(mac, vf_mac)) return vf_id; } return -EINVAL; } static int i40e_vsi_update_queue_region_mapping(struct i40e_hw *hw, struct i40e_pf *pf) { uint16_t i; struct i40e_vsi *vsi = pf->main_vsi; uint16_t queue_offset, bsf, tc_index; struct i40e_vsi_context ctxt; struct i40e_aqc_vsi_properties_data *vsi_info; struct i40e_queue_regions *region_info = &pf->queue_region; int32_t ret = -EINVAL; if (!region_info->queue_region_number) { PMD_INIT_LOG(ERR, "there is no that region id been set before"); return ret; } memset(&ctxt, 0, sizeof(struct i40e_vsi_context)); /* Update Queue Pairs Mapping for currently enabled UPs */ ctxt.seid = vsi->seid; ctxt.pf_num = hw->pf_id; ctxt.vf_num = 0; ctxt.uplink_seid = vsi->uplink_seid; ctxt.info = vsi->info; vsi_info = &ctxt.info; memset(vsi_info->tc_mapping, 0, sizeof(uint16_t) * 8); memset(vsi_info->queue_mapping, 0, sizeof(uint16_t) * 16); /* Configure queue region and queue mapping parameters, * for enabled queue region, allocate queues to this region. */ for (i = 0; i < region_info->queue_region_number; i++) { tc_index = region_info->region[i].region_id; bsf = rte_bsf32(region_info->region[i].queue_num); queue_offset = region_info->region[i].queue_start_index; vsi_info->tc_mapping[tc_index] = rte_cpu_to_le_16( (queue_offset << I40E_AQ_VSI_TC_QUE_OFFSET_SHIFT) | (bsf << I40E_AQ_VSI_TC_QUE_NUMBER_SHIFT)); } /* Associate queue number with VSI, Keep vsi->nb_qps unchanged */ vsi_info->mapping_flags |= rte_cpu_to_le_16(I40E_AQ_VSI_QUE_MAP_CONTIG); vsi_info->queue_mapping[0] = rte_cpu_to_le_16(vsi->base_queue); vsi_info->valid_sections |= rte_cpu_to_le_16(I40E_AQ_VSI_PROP_QUEUE_MAP_VALID); /* Update the VSI after updating the VSI queue-mapping information */ ret = i40e_aq_update_vsi_params(hw, &ctxt, NULL); if (ret) { PMD_DRV_LOG(ERR, "Failed to configure queue region mapping = %d ", hw->aq.asq_last_status); return ret; } /* update the local VSI info with updated queue map */ rte_memcpy(&vsi->info.tc_mapping, &ctxt.info.tc_mapping, sizeof(vsi->info.tc_mapping)); rte_memcpy(&vsi->info.queue_mapping, &ctxt.info.queue_mapping, sizeof(vsi->info.queue_mapping)); vsi->info.mapping_flags = ctxt.info.mapping_flags; vsi->info.valid_sections = 0; return 0; } static int i40e_queue_region_set_region(struct i40e_pf *pf, struct rte_pmd_i40e_queue_region_conf *conf_ptr) { uint16_t i; struct i40e_vsi *main_vsi = pf->main_vsi; struct i40e_queue_regions *info = &pf->queue_region; int32_t ret = -EINVAL; if (!((rte_is_power_of_2(conf_ptr->queue_num)) && conf_ptr->queue_num <= 64)) { PMD_DRV_LOG(ERR, "The region sizes should be any of the following values: 1, 2, 4, 8, 16, 32, 64 as long as the " "total number of queues do not exceed the VSI allocation"); return ret; } if (conf_ptr->region_id > I40E_REGION_MAX_INDEX) { PMD_DRV_LOG(ERR, "the queue region max index is 7"); return ret; } if ((conf_ptr->queue_start_index + conf_ptr->queue_num) > main_vsi->nb_used_qps) { PMD_DRV_LOG(ERR, "the queue index exceeds the VSI range"); return ret; } for (i = 0; i < info->queue_region_number; i++) if (conf_ptr->region_id == info->region[i].region_id) break; if (i == info->queue_region_number && i <= I40E_REGION_MAX_INDEX) { info->region[i].region_id = conf_ptr->region_id; info->region[i].queue_num = conf_ptr->queue_num; info->region[i].queue_start_index = conf_ptr->queue_start_index; info->queue_region_number++; } else { PMD_DRV_LOG(ERR, "queue region number exceeds maxnum 8 or the queue region id has been set before"); return ret; } return 0; } static int i40e_queue_region_set_flowtype(struct i40e_pf *pf, struct rte_pmd_i40e_queue_region_conf *rss_region_conf) { int32_t ret = -EINVAL; struct i40e_queue_regions *info = &pf->queue_region; uint16_t i, j; uint16_t region_index, flowtype_index; /* For the pctype or hardware flowtype of packet, * the specific index for each type has been defined * in file i40e_type.h as enum i40e_filter_pctype. */ if (rss_region_conf->region_id > I40E_PFQF_HREGION_MAX_INDEX) { PMD_DRV_LOG(ERR, "the queue region max index is 7"); return ret; } if (rss_region_conf->hw_flowtype >= I40E_FILTER_PCTYPE_MAX) { PMD_DRV_LOG(ERR, "the hw_flowtype or PCTYPE max index is 63"); return ret; } for (i = 0; i < info->queue_region_number; i++) if (rss_region_conf->region_id == info->region[i].region_id) break; if (i == info->queue_region_number) { PMD_DRV_LOG(ERR, "that region id has not been set before"); ret = -EINVAL; return ret; } region_index = i; for (i = 0; i < info->queue_region_number; i++) { for (j = 0; j < info->region[i].flowtype_num; j++) { if (rss_region_conf->hw_flowtype == info->region[i].hw_flowtype[j]) { PMD_DRV_LOG(ERR, "that hw_flowtype has been set before"); return 0; } } } flowtype_index = info->region[region_index].flowtype_num; info->region[region_index].hw_flowtype[flowtype_index] = rss_region_conf->hw_flowtype; info->region[region_index].flowtype_num++; return 0; } static void i40e_queue_region_pf_flowtype_conf(struct i40e_hw *hw, struct i40e_pf *pf) { uint8_t hw_flowtype; uint32_t pfqf_hregion; uint16_t i, j, index; struct i40e_queue_regions *info = &pf->queue_region; /* For the pctype or hardware flowtype of packet, * the specific index for each type has been defined * in file i40e_type.h as enum i40e_filter_pctype. */ for (i = 0; i < info->queue_region_number; i++) { for (j = 0; j < info->region[i].flowtype_num; j++) { hw_flowtype = info->region[i].hw_flowtype[j]; index = hw_flowtype >> 3; pfqf_hregion = i40e_read_rx_ctl(hw, I40E_PFQF_HREGION(index)); if ((hw_flowtype & 0x7) == 0) { pfqf_hregion |= info->region[i].region_id << I40E_PFQF_HREGION_REGION_0_SHIFT; pfqf_hregion |= 1 << I40E_PFQF_HREGION_OVERRIDE_ENA_0_SHIFT; } else if ((hw_flowtype & 0x7) == 1) { pfqf_hregion |= info->region[i].region_id << I40E_PFQF_HREGION_REGION_1_SHIFT; pfqf_hregion |= 1 << I40E_PFQF_HREGION_OVERRIDE_ENA_1_SHIFT; } else if ((hw_flowtype & 0x7) == 2) { pfqf_hregion |= info->region[i].region_id << I40E_PFQF_HREGION_REGION_2_SHIFT; pfqf_hregion |= 1 << I40E_PFQF_HREGION_OVERRIDE_ENA_2_SHIFT; } else if ((hw_flowtype & 0x7) == 3) { pfqf_hregion |= info->region[i].region_id << I40E_PFQF_HREGION_REGION_3_SHIFT; pfqf_hregion |= 1 << I40E_PFQF_HREGION_OVERRIDE_ENA_3_SHIFT; } else if ((hw_flowtype & 0x7) == 4) { pfqf_hregion |= info->region[i].region_id << I40E_PFQF_HREGION_REGION_4_SHIFT; pfqf_hregion |= 1 << I40E_PFQF_HREGION_OVERRIDE_ENA_4_SHIFT; } else if ((hw_flowtype & 0x7) == 5) { pfqf_hregion |= info->region[i].region_id << I40E_PFQF_HREGION_REGION_5_SHIFT; pfqf_hregion |= 1 << I40E_PFQF_HREGION_OVERRIDE_ENA_5_SHIFT; } else if ((hw_flowtype & 0x7) == 6) { pfqf_hregion |= info->region[i].region_id << I40E_PFQF_HREGION_REGION_6_SHIFT; pfqf_hregion |= 1 << I40E_PFQF_HREGION_OVERRIDE_ENA_6_SHIFT; } else { pfqf_hregion |= info->region[i].region_id << I40E_PFQF_HREGION_REGION_7_SHIFT; pfqf_hregion |= 1 << I40E_PFQF_HREGION_OVERRIDE_ENA_7_SHIFT; } i40e_write_rx_ctl(hw, I40E_PFQF_HREGION(index), pfqf_hregion); } } } static int i40e_queue_region_set_user_priority(struct i40e_pf *pf, struct rte_pmd_i40e_queue_region_conf *rss_region_conf) { struct i40e_queue_regions *info = &pf->queue_region; int32_t ret = -EINVAL; uint16_t i, j, region_index; if (rss_region_conf->user_priority >= I40E_MAX_USER_PRIORITY) { PMD_DRV_LOG(ERR, "the queue region max index is 7"); return ret; } if (rss_region_conf->region_id > I40E_REGION_MAX_INDEX) { PMD_DRV_LOG(ERR, "the region_id max index is 7"); return ret; } for (i = 0; i < info->queue_region_number; i++) if (rss_region_conf->region_id == info->region[i].region_id) break; if (i == info->queue_region_number) { PMD_DRV_LOG(ERR, "that region id has not been set before"); ret = -EINVAL; return ret; } region_index = i; for (i = 0; i < info->queue_region_number; i++) { for (j = 0; j < info->region[i].user_priority_num; j++) { if (info->region[i].user_priority[j] == rss_region_conf->user_priority) { PMD_DRV_LOG(ERR, "that user priority has been set before"); return 0; } } } j = info->region[region_index].user_priority_num; info->region[region_index].user_priority[j] = rss_region_conf->user_priority; info->region[region_index].user_priority_num++; return 0; } static int i40e_queue_region_dcb_configure(struct i40e_hw *hw, struct i40e_pf *pf) { struct i40e_dcbx_config dcb_cfg_local; struct i40e_dcbx_config *dcb_cfg; struct i40e_queue_regions *info = &pf->queue_region; struct i40e_dcbx_config *old_cfg = &hw->local_dcbx_config; int32_t ret = -EINVAL; uint16_t i, j, prio_index, region_index; uint8_t tc_map, tc_bw, bw_lf; if (!info->queue_region_number) { PMD_DRV_LOG(ERR, "No queue region been set before"); return ret; } dcb_cfg = &dcb_cfg_local; memset(dcb_cfg, 0, sizeof(struct i40e_dcbx_config)); /* assume each tc has the same bw */ tc_bw = I40E_MAX_PERCENT / info->queue_region_number; for (i = 0; i < info->queue_region_number; i++) dcb_cfg->etscfg.tcbwtable[i] = tc_bw; /* to ensure the sum of tcbw is equal to 100 */ bw_lf = I40E_MAX_PERCENT % info->queue_region_number; for (i = 0; i < bw_lf; i++) dcb_cfg->etscfg.tcbwtable[i]++; /* assume each tc has the same Transmission Selection Algorithm */ for (i = 0; i < info->queue_region_number; i++) dcb_cfg->etscfg.tsatable[i] = I40E_IEEE_TSA_ETS; for (i = 0; i < info->queue_region_number; i++) { for (j = 0; j < info->region[i].user_priority_num; j++) { prio_index = info->region[i].user_priority[j]; region_index = info->region[i].region_id; dcb_cfg->etscfg.prioritytable[prio_index] = region_index; } } /* FW needs one App to configure HW */ dcb_cfg->numapps = I40E_DEFAULT_DCB_APP_NUM; dcb_cfg->app[0].selector = I40E_APP_SEL_ETHTYPE; dcb_cfg->app[0].priority = I40E_DEFAULT_DCB_APP_PRIO; dcb_cfg->app[0].protocolid = I40E_APP_PROTOID_FCOE; tc_map = RTE_LEN2MASK(info->queue_region_number, uint8_t); dcb_cfg->pfc.willing = 0; dcb_cfg->pfc.pfccap = I40E_MAX_TRAFFIC_CLASS; dcb_cfg->pfc.pfcenable = tc_map; /* Copy the new config to the current config */ *old_cfg = *dcb_cfg; old_cfg->etsrec = old_cfg->etscfg; ret = i40e_set_dcb_config(hw); if (ret) { PMD_DRV_LOG(ERR, "Set queue region DCB Config failed, err %s aq_err %s", i40e_stat_str(hw, ret), i40e_aq_str(hw, hw->aq.asq_last_status)); return ret; } return 0; } int i40e_flush_queue_region_all_conf(struct rte_eth_dev *dev, struct i40e_hw *hw, struct i40e_pf *pf, uint16_t on) { int32_t ret = -EINVAL; struct i40e_queue_regions *info = &pf->queue_region; struct i40e_vsi *main_vsi = pf->main_vsi; if (on) { i40e_queue_region_pf_flowtype_conf(hw, pf); ret = i40e_vsi_update_queue_region_mapping(hw, pf); if (ret != I40E_SUCCESS) { PMD_DRV_LOG(INFO, "Failed to flush queue region mapping."); return ret; } ret = i40e_queue_region_dcb_configure(hw, pf); if (ret != I40E_SUCCESS) { PMD_DRV_LOG(INFO, "Failed to flush dcb."); return ret; } return 0; } if (info->queue_region_number) { info->queue_region_number = 1; info->region[0].queue_num = main_vsi->nb_used_qps; info->region[0].queue_start_index = 0; ret = i40e_vsi_update_queue_region_mapping(hw, pf); if (ret != I40E_SUCCESS) PMD_DRV_LOG(INFO, "Failed to flush queue region mapping."); ret = i40e_dcb_init_configure(dev, TRUE); if (ret != I40E_SUCCESS) { PMD_DRV_LOG(INFO, "Failed to flush dcb."); pf->flags &= ~I40E_FLAG_DCB; } i40e_init_queue_region_conf(dev); } return 0; } static int i40e_queue_region_pf_check_rss(struct i40e_pf *pf) { struct i40e_hw *hw = I40E_PF_TO_HW(pf); uint64_t hena; hena = (uint64_t)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(0)); hena |= ((uint64_t)i40e_read_rx_ctl(hw, I40E_PFQF_HENA(1))) << 32; if (!hena) return -ENOTSUP; return 0; } static int i40e_queue_region_get_all_info(struct i40e_pf *pf, struct i40e_queue_regions *regions_ptr) { struct i40e_queue_regions *info = &pf->queue_region; rte_memcpy(regions_ptr, info, sizeof(struct i40e_queue_regions)); return 0; } int rte_pmd_i40e_rss_queue_region_conf(uint16_t port_id, enum rte_pmd_i40e_queue_region_op op_type, void *arg) { struct rte_eth_dev *dev = &rte_eth_devices[port_id]; struct i40e_pf *pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private); struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private); int32_t ret; RTE_ETH_VALID_PORTID_OR_ERR_RET(port_id, -ENODEV); if (!is_i40e_supported(dev)) return -ENOTSUP; if (!(!i40e_queue_region_pf_check_rss(pf))) return -ENOTSUP; /* This queue region feature only support pf by now. It should * be called after dev_start, and will be clear after dev_stop. * "RTE_PMD_I40E_RSS_QUEUE_REGION_ALL_FLUSH_ON" * is just an enable function which server for other configuration, * it is for all configuration about queue region from up layer, * at first will only keep in DPDK softwarestored in driver, * only after "FLUSH_ON", it commit all configuration to HW. * Because PMD had to set hardware configuration at a time, so * it will record all up layer command at first. * "RTE_PMD_I40E_RSS_QUEUE_REGION_ALL_FLUSH_OFF" is * just clean all configuration about queue region just now, * and restore all to DPDK i40e driver default * config when start up. */ switch (op_type) { case RTE_PMD_I40E_RSS_QUEUE_REGION_SET: ret = i40e_queue_region_set_region(pf, (struct rte_pmd_i40e_queue_region_conf *)arg); break; case RTE_PMD_I40E_RSS_QUEUE_REGION_FLOWTYPE_SET: ret = i40e_queue_region_set_flowtype(pf, (struct rte_pmd_i40e_queue_region_conf *)arg); break; case RTE_PMD_I40E_RSS_QUEUE_REGION_USER_PRIORITY_SET: ret = i40e_queue_region_set_user_priority(pf, (struct rte_pmd_i40e_queue_region_conf *)arg); break; case RTE_PMD_I40E_RSS_QUEUE_REGION_ALL_FLUSH_ON: ret = i40e_flush_queue_region_all_conf(dev, hw, pf, 1); break; case RTE_PMD_I40E_RSS_QUEUE_REGION_ALL_FLUSH_OFF: ret = i40e_flush_queue_region_all_conf(dev, hw, pf, 0); break; case RTE_PMD_I40E_RSS_QUEUE_REGION_INFO_GET: ret = i40e_queue_region_get_all_info(pf, (struct i40e_queue_regions *)arg); break; default: PMD_DRV_LOG(WARNING, "op type (%d) not supported", op_type); ret = -EINVAL; } I40E_WRITE_FLUSH(hw); return ret; } int rte_pmd_i40e_flow_add_del_packet_template( uint16_t port, const struct rte_pmd_i40e_pkt_template_conf *conf, uint8_t add) { struct rte_eth_dev *dev = &rte_eth_devices[port]; struct i40e_fdir_filter_conf filter_conf; RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV); if (!is_i40e_supported(dev)) return -ENOTSUP; memset(&filter_conf, 0, sizeof(filter_conf)); filter_conf.soft_id = conf->soft_id; filter_conf.input.flow.raw_flow.pctype = conf->input.pctype; filter_conf.input.flow.raw_flow.packet = conf->input.packet; filter_conf.input.flow.raw_flow.length = conf->input.length; filter_conf.input.flow_ext.pkt_template = true; filter_conf.action.rx_queue = conf->action.rx_queue; filter_conf.action.behavior = (enum i40e_fdir_behavior)conf->action.behavior; filter_conf.action.report_status = (enum i40e_fdir_status)conf->action.report_status; filter_conf.action.flex_off = conf->action.flex_off; return i40e_flow_add_del_fdir_filter(dev, &filter_conf, add); }