a075ce2b3e
The summarized global register change info will be logged no matter if there's real global register change. Since only real changes are logged now, there's no need to summarize global register change info, otherwise will cause misunderstanding. Signed-off-by: Beilei Xing <beilei.xing@intel.com> Acked-by: Qi Zhang <qi.z.zhang@intel.com>
3192 lines
78 KiB
C
3192 lines
78 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2010-2017 Intel Corporation
|
|
*/
|
|
|
|
#include <rte_malloc.h>
|
|
#include <rte_tailq.h>
|
|
|
|
#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;
|
|
}
|
|
|
|
static const struct ether_addr null_mac_addr;
|
|
|
|
int
|
|
rte_pmd_i40e_remove_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;
|
|
|
|
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;
|
|
}
|
|
|
|
if (is_same_ether_addr(mac_addr, &vf->mac_addr))
|
|
/* Reset the mac with NULL address */
|
|
ether_addr_copy(&null_mac_addr, &vf->mac_addr);
|
|
|
|
/* Remove the mac */
|
|
i40e_vsi_delete_mac(vsi, mac_addr);
|
|
|
|
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;
|
|
}
|
|
}
|
|
/* profile with group id 0xff is compatible with any other profile */
|
|
if ((pinfo->track_id & group_mask) == group_mask) {
|
|
rte_free(buff);
|
|
return 0;
|
|
}
|
|
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 ((p->track_id & group_mask) == group_mask)
|
|
continue;
|
|
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");
|
|
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 &&
|
|
l2 != RTE_PTYPE_L2_ETHER_PPPOE)
|
|
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 &&
|
|
tnl != RTE_PTYPE_TUNNEL_L2TP)
|
|
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);
|
|
}
|
|
|
|
int
|
|
rte_pmd_i40e_inset_get(uint16_t port, uint8_t pctype,
|
|
struct rte_pmd_i40e_inset *inset,
|
|
enum rte_pmd_i40e_inset_type inset_type)
|
|
{
|
|
struct rte_eth_dev *dev;
|
|
struct i40e_hw *hw;
|
|
uint64_t inset_reg;
|
|
uint32_t mask_reg[2];
|
|
int i;
|
|
|
|
RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
|
|
|
|
dev = &rte_eth_devices[port];
|
|
|
|
if (!is_i40e_supported(dev))
|
|
return -ENOTSUP;
|
|
|
|
if (pctype > 63)
|
|
return -EINVAL;
|
|
|
|
hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
|
|
memset(inset, 0, sizeof(struct rte_pmd_i40e_inset));
|
|
|
|
switch (inset_type) {
|
|
case INSET_HASH:
|
|
/* Get input set */
|
|
inset_reg =
|
|
i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(1, pctype));
|
|
inset_reg <<= I40E_32_BIT_WIDTH;
|
|
inset_reg |=
|
|
i40e_read_rx_ctl(hw, I40E_GLQF_HASH_INSET(0, pctype));
|
|
/* Get field mask */
|
|
mask_reg[0] =
|
|
i40e_read_rx_ctl(hw, I40E_GLQF_HASH_MSK(0, pctype));
|
|
mask_reg[1] =
|
|
i40e_read_rx_ctl(hw, I40E_GLQF_HASH_MSK(1, pctype));
|
|
break;
|
|
case INSET_FDIR:
|
|
inset_reg =
|
|
i40e_read_rx_ctl(hw, I40E_PRTQF_FD_INSET(pctype, 1));
|
|
inset_reg <<= I40E_32_BIT_WIDTH;
|
|
inset_reg |=
|
|
i40e_read_rx_ctl(hw, I40E_PRTQF_FD_INSET(pctype, 0));
|
|
mask_reg[0] =
|
|
i40e_read_rx_ctl(hw, I40E_GLQF_FD_MSK(0, pctype));
|
|
mask_reg[1] =
|
|
i40e_read_rx_ctl(hw, I40E_GLQF_FD_MSK(1, pctype));
|
|
break;
|
|
case INSET_FDIR_FLX:
|
|
inset_reg =
|
|
i40e_read_rx_ctl(hw, I40E_PRTQF_FD_FLXINSET(pctype));
|
|
mask_reg[0] =
|
|
i40e_read_rx_ctl(hw, I40E_PRTQF_FD_MSK(pctype, 0));
|
|
mask_reg[1] =
|
|
i40e_read_rx_ctl(hw, I40E_PRTQF_FD_MSK(pctype, 1));
|
|
break;
|
|
default:
|
|
PMD_DRV_LOG(ERR, "Unsupported input set type.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
inset->inset = inset_reg;
|
|
|
|
for (i = 0; i < 2; i++) {
|
|
inset->mask[i].field_idx = ((mask_reg[i] >> 16) & 0x3F);
|
|
inset->mask[i].mask = mask_reg[i] & 0xFFFF;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_pmd_i40e_inset_set(uint16_t port, uint8_t pctype,
|
|
struct rte_pmd_i40e_inset *inset,
|
|
enum rte_pmd_i40e_inset_type inset_type)
|
|
{
|
|
struct rte_eth_dev *dev;
|
|
struct i40e_hw *hw;
|
|
struct i40e_pf *pf;
|
|
uint64_t inset_reg;
|
|
uint32_t mask_reg[2];
|
|
int i;
|
|
|
|
RTE_ETH_VALID_PORTID_OR_ERR_RET(port, -ENODEV);
|
|
|
|
dev = &rte_eth_devices[port];
|
|
|
|
if (!is_i40e_supported(dev))
|
|
return -ENOTSUP;
|
|
|
|
if (pctype > 63)
|
|
return -EINVAL;
|
|
|
|
hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
|
|
pf = I40E_DEV_PRIVATE_TO_PF(dev->data->dev_private);
|
|
|
|
if (pf->support_multi_driver) {
|
|
PMD_DRV_LOG(ERR, "Input set configuration is not supported.");
|
|
return -ENOTSUP;
|
|
}
|
|
|
|
inset_reg = inset->inset;
|
|
for (i = 0; i < 2; i++)
|
|
mask_reg[i] = (inset->mask[i].field_idx << 16) |
|
|
inset->mask[i].mask;
|
|
|
|
switch (inset_type) {
|
|
case INSET_HASH:
|
|
i40e_check_write_global_reg(hw, I40E_GLQF_HASH_INSET(0, pctype),
|
|
(uint32_t)(inset_reg & UINT32_MAX));
|
|
i40e_check_write_global_reg(hw, I40E_GLQF_HASH_INSET(1, pctype),
|
|
(uint32_t)((inset_reg >>
|
|
I40E_32_BIT_WIDTH) & UINT32_MAX));
|
|
for (i = 0; i < 2; i++)
|
|
i40e_check_write_global_reg(hw,
|
|
I40E_GLQF_HASH_MSK(i, pctype),
|
|
mask_reg[i]);
|
|
break;
|
|
case INSET_FDIR:
|
|
i40e_check_write_reg(hw, I40E_PRTQF_FD_INSET(pctype, 0),
|
|
(uint32_t)(inset_reg & UINT32_MAX));
|
|
i40e_check_write_reg(hw, I40E_PRTQF_FD_INSET(pctype, 1),
|
|
(uint32_t)((inset_reg >>
|
|
I40E_32_BIT_WIDTH) & UINT32_MAX));
|
|
for (i = 0; i < 2; i++)
|
|
i40e_check_write_global_reg(hw,
|
|
I40E_GLQF_FD_MSK(i, pctype),
|
|
mask_reg[i]);
|
|
break;
|
|
case INSET_FDIR_FLX:
|
|
i40e_check_write_reg(hw, I40E_PRTQF_FD_FLXINSET(pctype),
|
|
(uint32_t)(inset_reg & UINT32_MAX));
|
|
for (i = 0; i < 2; i++)
|
|
i40e_check_write_reg(hw, I40E_PRTQF_FD_MSK(pctype, i),
|
|
mask_reg[i]);
|
|
break;
|
|
default:
|
|
PMD_DRV_LOG(ERR, "Unsupported input set type.");
|
|
return -EINVAL;
|
|
}
|
|
|
|
I40E_WRITE_FLUSH(hw);
|
|
return 0;
|
|
}
|