net/i40e: fix deletion of all macvlan filters
[dpdk.git] / drivers / net / i40e / i40e_ethdev.c
index cc44582..a818998 100644 (file)
@@ -4656,6 +4656,7 @@ i40e_vsi_setup(struct i40e_pf *pf,
        vsi->max_macaddrs = I40E_NUM_MACADDR_MAX;
        vsi->parent_vsi = uplink_vsi ? uplink_vsi : pf->main_vsi;
        vsi->user_param = user_param;
+       vsi->vlan_anti_spoof_on = 0;
        /* Allocate queues */
        switch (vsi->type) {
        case I40E_VSI_MAIN  :
@@ -5997,14 +5998,11 @@ i40e_find_vlan_filter(struct i40e_vsi *vsi,
 }
 
 static void
-i40e_set_vlan_filter(struct i40e_vsi *vsi,
-                        uint16_t vlan_id, bool on)
+i40e_store_vlan_filter(struct i40e_vsi *vsi,
+                      uint16_t vlan_id, bool on)
 {
        uint32_t vid_idx, vid_bit;
 
-       if (vlan_id > ETH_VLAN_ID_MAX)
-               return;
-
        vid_idx = I40E_VFTA_IDX(vlan_id);
        vid_bit = I40E_VFTA_BIT(vlan_id);
 
@@ -6014,6 +6012,38 @@ i40e_set_vlan_filter(struct i40e_vsi *vsi,
                vsi->vfta[vid_idx] &= ~vid_bit;
 }
 
+static void
+i40e_set_vlan_filter(struct i40e_vsi *vsi,
+                    uint16_t vlan_id, bool on)
+{
+       struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+       struct i40e_aqc_add_remove_vlan_element_data vlan_data = {0};
+       int ret;
+
+       if (vlan_id > ETH_VLAN_ID_MAX)
+               return;
+
+       i40e_store_vlan_filter(vsi, vlan_id, on);
+
+       if (!vsi->vlan_anti_spoof_on || !vlan_id)
+               return;
+
+       vlan_data.vlan_tag = rte_cpu_to_le_16(vlan_id);
+
+       if (on) {
+               ret = i40e_aq_add_vlan(hw, vsi->seid,
+                                      &vlan_data, 1, NULL);
+               if (ret != I40E_SUCCESS)
+                       PMD_DRV_LOG(ERR, "Failed to add vlan filter");
+       } else {
+               ret = i40e_aq_remove_vlan(hw, vsi->seid,
+                                         &vlan_data, 1, NULL);
+               if (ret != I40E_SUCCESS)
+                       PMD_DRV_LOG(ERR,
+                                   "Failed to remove vlan filter");
+       }
+}
+
 /**
  * Find all vlan options for specific mac addr,
  * return with actual vlan found.
@@ -6085,7 +6115,7 @@ i40e_find_all_mac_for_vlan(struct i40e_vsi *vsi,
 static int
 i40e_vsi_remove_all_macvlan_filter(struct i40e_vsi *vsi)
 {
-       int i, num;
+       int i, j, num;
        struct i40e_mac_filter *f;
        struct i40e_macvlan_filter *mv_f;
        int ret = I40E_SUCCESS;
@@ -6110,6 +6140,7 @@ i40e_vsi_remove_all_macvlan_filter(struct i40e_vsi *vsi)
                TAILQ_FOREACH(f, &vsi->mac_list, next) {
                        (void)rte_memcpy(&mv_f[i].macaddr,
                                &f->mac_info.mac_addr, ETH_ADDR_LEN);
+                       mv_f[i].filter_type = f->mac_info.filter_type;
                        mv_f[i].vlan_id = 0;
                        i++;
                }
@@ -6119,6 +6150,8 @@ i40e_vsi_remove_all_macvlan_filter(struct i40e_vsi *vsi)
                                        vsi->vlan_num, &f->mac_info.mac_addr);
                        if (ret != I40E_SUCCESS)
                                goto DONE;
+                       for (j = i; j < i + vsi->vlan_num; j++)
+                               mv_f[j].filter_type = f->mac_info.filter_type;
                        i += vsi->vlan_num;
                }
        }
@@ -10257,3 +10290,901 @@ rte_pmd_i40e_ping_vfs(uint8_t port, uint16_t vf)
 
        return 0;
 }
+
+int
+rte_pmd_i40e_set_vf_mac_anti_spoof(uint8_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_pmd(dev->data->drv_name))
+               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));
+       (void)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(uint8_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_pmd(dev->data->drv_name))
+               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;
+       ret = i40e_add_rm_all_vlan_filter(vsi, on);
+       if (ret) {
+               PMD_DRV_LOG(ERR, "Failed to 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));
+       (void)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\n");
+                               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;
+                       (void)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;
+                       (void)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) {
+               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));
+       (void)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) {
+               ret = i40e_add_rm_all_vlan_filter(vsi, 1);
+               if (ret)
+                       return ret;
+       }
+
+       return ret;
+}
+
+int
+rte_pmd_i40e_set_tx_loopback(uint8_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_pmd(dev->data->drv_name))
+               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(uint8_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_pmd(dev->data->drv_name))
+               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(uint8_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_pmd(dev->data->drv_name))
+               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(uint8_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_pmd(dev->data->drv_name))
+               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)
+               i40e_vsi_delete_mac(vsi, &f->mac_info.mac_addr);
+
+       return 0;
+}
+
+/* Set vlan strip on/off for specific VF from host */
+int
+rte_pmd_i40e_set_vf_vlan_stripq(uint8_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_pmd(dev->data->drv_name))
+               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(uint8_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_pmd(dev->data->drv_name))
+               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));
+       (void)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(uint8_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);
+
+       if (on > 1) {
+               PMD_DRV_LOG(ERR, "on should be 0 or 1.");
+               return -EINVAL;
+       }
+
+       dev = &rte_eth_devices[port];
+
+       if (is_i40e_pmd(dev->data->drv_name))
+               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;
+       }
+
+       hw = I40E_VSI_TO_HW(vsi);
+
+       ret = i40e_aq_set_vsi_broadcast(hw, vsi->seid, on, NULL);
+       if (ret != I40E_SUCCESS) {
+               ret = -ENOTSUP;
+               PMD_DRV_LOG(ERR, "Failed to set VSI broadcast");
+       }
+
+       return ret;
+}
+
+int rte_pmd_i40e_set_vf_vlan_tag(uint8_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_pmd(dev->data->drv_name))
+               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));
+       (void)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_vlan_filter(uint8_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;
+       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_pmd(dev->data->drv_name))
+               return -ENOTSUP;
+
+       if (vlan_id > ETHER_MAX_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 < 64 && ret == I40E_SUCCESS; vf_idx++) {
+               if (vf_mask & ((uint64_t)(1ULL << vf_idx))) {
+                       if (on)
+                               ret = i40e_vsi_add_vlan(pf->vfs[vf_idx].vsi,
+                                                       vlan_id);
+                       else
+                               ret = i40e_vsi_delete_vlan(pf->vfs[vf_idx].vsi,
+                                                          vlan_id);
+               }
+       }
+
+       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(uint8_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_pmd(dev->data->drv_name))
+               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(uint8_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_pmd(dev->data->drv_name))
+               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;
+}