net/i40e: set VF max bandwidth from PF
[dpdk.git] / drivers / net / i40e / i40e_ethdev.c
index a128e7c..bfcbc4b 100644 (file)
 /* Bit mask of Extended Tag enable/disable */
 #define PCI_DEV_CTRL_EXT_TAG_MASK  (1 << PCI_DEV_CTRL_EXT_TAG_SHIFT)
 
+/* The max bandwidth of i40e is 40Gbps. */
+#define I40E_QOS_BW_MAX 40000
+/* The bandwidth should be the multiple of 50Mbps. */
+#define I40E_QOS_BW_GRANULARITY 50
+
 static int eth_i40e_dev_init(struct rte_eth_dev *eth_dev);
 static int eth_i40e_dev_uninit(struct rte_eth_dev *eth_dev);
 static int i40e_dev_configure(struct rte_eth_dev *dev);
@@ -1877,6 +1882,7 @@ i40e_dev_start(struct rte_eth_dev *dev)
        struct rte_pci_device *pci_dev = I40E_DEV_TO_PCI(dev);
        struct rte_intr_handle *intr_handle = &pci_dev->intr_handle;
        uint32_t intr_vector = 0;
+       struct i40e_vsi *vsi;
 
        hw->adapter_stopped = 0;
 
@@ -1955,6 +1961,15 @@ i40e_dev_start(struct rte_eth_dev *dev)
                        PMD_DRV_LOG(INFO, "fail to set vsi broadcast");
        }
 
+       /* Enable the VLAN promiscuous mode. */
+       if (pf->vfs) {
+               for (i = 0; i < pf->vf_num; i++) {
+                       vsi = pf->vfs[i].vsi;
+                       i40e_aq_set_vsi_vlan_promisc(hw, vsi->seid,
+                                                    true, NULL);
+               }
+       }
+
        /* Apply link configure */
        if (dev->data->dev_conf.link_speeds & ~(ETH_LINK_SPEED_100M |
                                ETH_LINK_SPEED_1G | ETH_LINK_SPEED_10G |
@@ -4670,6 +4685,7 @@ i40e_vsi_setup(struct i40e_pf *pf,
        vsi->parent_vsi = uplink_vsi ? uplink_vsi : pf->main_vsi;
        vsi->user_param = user_param;
        vsi->vlan_anti_spoof_on = 0;
+       vsi->vlan_filter_on = 0;
        /* Allocate queues */
        switch (vsi->type) {
        case I40E_VSI_MAIN  :
@@ -6039,7 +6055,7 @@ i40e_set_vlan_filter(struct i40e_vsi *vsi,
 
        i40e_store_vlan_filter(vsi, vlan_id, on);
 
-       if (!vsi->vlan_anti_spoof_on || !vlan_id)
+       if ((!vsi->vlan_anti_spoof_on && !vsi->vlan_filter_on) || !vlan_id)
                return;
 
        vlan_data.vlan_tag = rte_cpu_to_le_16(vlan_id);
@@ -10472,10 +10488,12 @@ rte_pmd_i40e_set_vf_vlan_anti_spoof(uint8_t port, uint16_t vf_id, uint8_t 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;
+       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);
@@ -10658,7 +10676,7 @@ i40e_vsi_set_tx_loopback(struct i40e_vsi *vsi, uint8_t on)
                PMD_INIT_LOG(ERR, "Failed to remove MAC filters.");
                return ret;
        }
-       if (vsi->vlan_anti_spoof_on) {
+       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.");
@@ -10686,7 +10704,7 @@ i40e_vsi_set_tx_loopback(struct i40e_vsi *vsi, uint8_t on)
        ret = i40e_vsi_restore_mac_filter(vsi);
        if (ret)
                return ret;
-       if (vsi->vlan_anti_spoof_on) {
+       if (vsi->vlan_anti_spoof_on || vsi->vlan_filter_on) {
                ret = i40e_add_rm_all_vlan_filter(vsi, 1);
                if (ret)
                        return ret;
@@ -10971,6 +10989,9 @@ int rte_pmd_i40e_set_vf_broadcast(uint8_t port, uint16_t vf_id,
        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);
@@ -11009,12 +11030,19 @@ int rte_pmd_i40e_set_vf_broadcast(uint8_t port, uint16_t vf_id,
                return -EINVAL;
        }
 
-       hw = I40E_VSI_TO_HW(vsi);
+       if (on) {
+               (void)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);
+       }
 
-       ret = i40e_aq_set_vsi_broadcast(hw, vsi->seid, on, NULL);
-       if (ret != I40E_SUCCESS) {
+       if (ret != I40E_SUCCESS && ret != I40E_ERR_PARAM) {
                ret = -ENOTSUP;
                PMD_DRV_LOG(ERR, "Failed to set VSI broadcast");
+       } else {
+               ret = 0;
        }
 
        return ret;
@@ -11094,6 +11122,7 @@ int rte_pmd_i40e_set_vf_vlan_filter(uint8_t port, uint16_t vlan_id,
        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;
 
@@ -11132,14 +11161,22 @@ int rte_pmd_i40e_set_vf_vlan_filter(uint8_t port, uint16_t vlan_id,
                return -ENODEV;
        }
 
-       for (vf_idx = 0; vf_idx < 64 && ret == I40E_SUCCESS; vf_idx++) {
+       for (vf_idx = 0; vf_idx < pf->vf_num && 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);
+                       vsi = pf->vfs[vf_idx].vsi;
+                       if (on) {
+                               if (!vsi->vlan_filter_on) {
+                                       vsi->vlan_filter_on = true;
+                                       if (!vsi->vlan_anti_spoof_on)
+                                               i40e_add_rm_all_vlan_filter(
+                                                       vsi, true);
+                               }
+                               i40e_aq_set_vsi_vlan_promisc(hw, vsi->seid,
+                                                            false, NULL);
+                               ret = i40e_vsi_add_vlan(vsi, vlan_id);
+                       } else {
+                               ret = i40e_vsi_delete_vlan(vsi, vlan_id);
+                       }
                }
        }
 
@@ -11229,3 +11266,92 @@ rte_pmd_i40e_reset_vf_stats(uint8_t port,
 
        return 0;
 }
+
+int
+rte_pmd_i40e_set_vf_max_bw(uint8_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_device_supported(dev, &rte_i40e_pmd))
+               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;
+}