i40evf: support AQ based RSS config
[dpdk.git] / drivers / net / i40e / i40e_ethdev_vf.c
index 48b904e..57ea8b6 100644 (file)
@@ -115,6 +115,9 @@ static int i40evf_dev_link_update(struct rte_eth_dev *dev,
                                  __rte_unused int wait_to_complete);
 static void i40evf_dev_stats_get(struct rte_eth_dev *dev,
                                struct rte_eth_stats *stats);
+static int i40evf_dev_xstats_get(struct rte_eth_dev *dev,
+                                struct rte_eth_xstats *xstats, unsigned n);
+static void i40evf_dev_xstats_reset(struct rte_eth_dev *dev);
 static int i40evf_vlan_filter_set(struct rte_eth_dev *dev,
                                  uint16_t vlan_id, int on);
 static void i40evf_vlan_offload_set(struct rte_eth_dev *dev, int mask);
@@ -151,6 +154,30 @@ static int i40evf_dev_rss_hash_conf_get(struct rte_eth_dev *dev,
 /* Default hash key buffer for RSS */
 static uint32_t rss_key_default[I40E_VFQF_HKEY_MAX_INDEX + 1];
 
+struct rte_i40evf_xstats_name_off {
+       char name[RTE_ETH_XSTATS_NAME_SIZE];
+       unsigned offset;
+};
+
+static const struct rte_i40evf_xstats_name_off rte_i40evf_stats_strings[] = {
+       {"rx_bytes", offsetof(struct i40e_eth_stats, rx_bytes)},
+       {"rx_unicast_packets", offsetof(struct i40e_eth_stats, rx_unicast)},
+       {"rx_multicast_packets", offsetof(struct i40e_eth_stats, rx_multicast)},
+       {"rx_broadcast_packets", offsetof(struct i40e_eth_stats, rx_broadcast)},
+       {"rx_dropped_packets", offsetof(struct i40e_eth_stats, rx_discards)},
+       {"rx_unknown_protocol_packets", offsetof(struct i40e_eth_stats,
+               rx_unknown_protocol)},
+       {"tx_bytes", offsetof(struct i40e_eth_stats, tx_bytes)},
+       {"tx_unicast_packets", offsetof(struct i40e_eth_stats, tx_bytes)},
+       {"tx_multicast_packets", offsetof(struct i40e_eth_stats, tx_bytes)},
+       {"tx_broadcast_packets", offsetof(struct i40e_eth_stats, tx_bytes)},
+       {"tx_dropped_packets", offsetof(struct i40e_eth_stats, tx_bytes)},
+       {"tx_error_packets", offsetof(struct i40e_eth_stats, tx_bytes)},
+};
+
+#define I40EVF_NB_XSTATS (sizeof(rte_i40evf_stats_strings) / \
+               sizeof(rte_i40evf_stats_strings[0]))
+
 static const struct eth_dev_ops i40evf_eth_dev_ops = {
        .dev_configure        = i40evf_dev_configure,
        .dev_start            = i40evf_dev_start,
@@ -161,6 +188,8 @@ static const struct eth_dev_ops i40evf_eth_dev_ops = {
        .allmulticast_disable = i40evf_dev_allmulticast_disable,
        .link_update          = i40evf_dev_link_update,
        .stats_get            = i40evf_dev_stats_get,
+       .xstats_get           = i40evf_dev_xstats_get,
+       .xstats_reset         = i40evf_dev_xstats_reset,
        .dev_close            = i40evf_dev_close,
        .dev_infos_get        = i40evf_dev_info_get,
        .vlan_filter_set      = i40evf_vlan_filter_set,
@@ -903,11 +932,10 @@ i40evf_del_mac_addr(struct rte_eth_dev *dev, struct ether_addr *addr)
 }
 
 static int
-i40evf_get_statics(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
+i40evf_update_stats(struct rte_eth_dev *dev, struct i40e_eth_stats **pstats)
 {
        struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
        struct i40e_virtchnl_queue_select q_stats;
-       struct i40e_eth_stats *pstats;
        int err;
        struct vf_cmd_info args;
 
@@ -922,9 +950,23 @@ i40evf_get_statics(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
        err = i40evf_execute_vf_cmd(dev, &args);
        if (err) {
                PMD_DRV_LOG(ERR, "fail to execute command OP_GET_STATS");
+               *pstats = NULL;
                return err;
        }
-       pstats = (struct i40e_eth_stats *)args.out_buffer;
+       *pstats = (struct i40e_eth_stats *)args.out_buffer;
+       return 0;
+}
+
+static int
+i40evf_get_statics(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
+{
+       int ret;
+       struct i40e_eth_stats *pstats = NULL;
+
+       ret = i40evf_update_stats(dev, &pstats);
+       if (ret != 0)
+               return 0;
+
        stats->ipackets = pstats->rx_unicast + pstats->rx_multicast +
                                                pstats->rx_broadcast;
        stats->opackets = pstats->tx_broadcast + pstats->tx_multicast +
@@ -937,6 +979,47 @@ i40evf_get_statics(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
        return 0;
 }
 
+static void
+i40evf_dev_xstats_reset(struct rte_eth_dev *dev)
+{
+       struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
+       struct i40e_eth_stats *pstats = NULL;
+
+       /* read stat values to clear hardware registers */
+       i40evf_update_stats(dev, &pstats);
+
+       /* set stats offset base on current values */
+       vf->vsi.eth_stats_offset = vf->vsi.eth_stats;
+}
+
+static int i40evf_dev_xstats_get(struct rte_eth_dev *dev,
+                                struct rte_eth_xstats *xstats, unsigned n)
+{
+       int ret;
+       unsigned i;
+       struct i40e_eth_stats *pstats = NULL;
+
+       if (n < I40EVF_NB_XSTATS)
+               return I40EVF_NB_XSTATS;
+
+       ret = i40evf_update_stats(dev, &pstats);
+       if (ret != 0)
+               return 0;
+
+       if (!xstats)
+               return 0;
+
+       /* loop over xstats array and values from pstats */
+       for (i = 0; i < I40EVF_NB_XSTATS; i++) {
+               snprintf(xstats[i].name, sizeof(xstats[i].name),
+                        "%s", rte_i40evf_stats_strings[i].name);
+               xstats[i].value = *(uint64_t *)(((char *)pstats) +
+                       rte_i40evf_stats_strings[i].offset);
+       }
+
+       return I40EVF_NB_XSTATS;
+}
+
 static int
 i40evf_add_vlan(struct rte_eth_dev *dev, uint16_t vlanid)
 {
@@ -1141,9 +1224,12 @@ i40evf_init_vf(struct rte_eth_dev *dev)
                goto err_alloc;
        }
 
+       if (hw->mac.type == I40E_MAC_X722_VF)
+               vf->flags = I40E_FLAG_RSS_AQ_CAPABLE;
        vf->vsi.vsi_id = vf->vsi_res->vsi_id;
        vf->vsi.type = vf->vsi_res->vsi_type;
        vf->vsi.nb_qps = vf->vsi_res->num_queue_pairs;
+       vf->vsi.adapter = I40E_DEV_PRIVATE_TO_ADAPTER(dev->data->dev_private);
 
        /* check mac addr, if it's not valid, genrate one */
        if (I40E_SUCCESS != i40e_validate_mac_addr(\
@@ -1803,6 +1889,18 @@ i40evf_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
                .txq_flags = ETH_TXQ_FLAGS_NOMULTSEGS |
                                ETH_TXQ_FLAGS_NOOFFLOADS,
        };
+
+       dev_info->rx_desc_lim = (struct rte_eth_desc_lim) {
+               .nb_max = I40E_MAX_RING_DESC,
+               .nb_min = I40E_MIN_RING_DESC,
+               .nb_align = I40E_ALIGN_RING_DESC,
+       };
+
+       dev_info->tx_desc_lim = (struct rte_eth_desc_lim) {
+               .nb_max = I40E_MAX_RING_DESC,
+               .nb_min = I40E_MIN_RING_DESC,
+               .nb_align = I40E_ALIGN_RING_DESC,
+       };
 }
 
 static void
@@ -1824,16 +1922,72 @@ i40evf_dev_close(struct rte_eth_dev *dev)
        i40e_shutdown_adminq(hw);
 }
 
+static int
+i40evf_get_rss_lut(struct i40e_vsi *vsi, uint8_t *lut, uint16_t lut_size)
+{
+       struct i40e_vf *vf = I40E_VSI_TO_VF(vsi);
+       struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+       int ret;
+
+       if (!lut)
+               return -EINVAL;
+
+       if (vf->flags & I40E_FLAG_RSS_AQ_CAPABLE) {
+               ret = i40e_aq_get_rss_lut(hw, vsi->vsi_id, FALSE,
+                                         lut, lut_size);
+               if (ret) {
+                       PMD_DRV_LOG(ERR, "Failed to get RSS lookup table");
+                       return ret;
+               }
+       } else {
+               uint32_t *lut_dw = (uint32_t *)lut;
+               uint16_t i, lut_size_dw = lut_size / 4;
+
+               for (i = 0; i < lut_size_dw; i++)
+                       lut_dw[i] = I40E_READ_REG(hw, I40E_VFQF_HLUT(i));
+       }
+
+       return 0;
+}
+
+static int
+i40evf_set_rss_lut(struct i40e_vsi *vsi, uint8_t *lut, uint16_t lut_size)
+{
+       struct i40e_vf *vf = I40E_VSI_TO_VF(vsi);
+       struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+       int ret;
+
+       if (!vsi || !lut)
+               return -EINVAL;
+
+       if (vf->flags & I40E_FLAG_RSS_AQ_CAPABLE) {
+               ret = i40e_aq_set_rss_lut(hw, vsi->vsi_id, FALSE,
+                                         lut, lut_size);
+               if (ret) {
+                       PMD_DRV_LOG(ERR, "Failed to set RSS lookup table");
+                       return ret;
+               }
+       } else {
+               uint32_t *lut_dw = (uint32_t *)lut;
+               uint16_t i, lut_size_dw = lut_size / 4;
+
+               for (i = 0; i < lut_size_dw; i++)
+                       I40E_WRITE_REG(hw, I40E_VFQF_HLUT(i), lut_dw[i]);
+               I40EVF_WRITE_FLUSH(hw);
+       }
+
+       return 0;
+}
+
 static int
 i40evf_dev_rss_reta_update(struct rte_eth_dev *dev,
                           struct rte_eth_rss_reta_entry64 *reta_conf,
                           uint16_t reta_size)
 {
-       struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-       uint32_t lut, l;
-       uint16_t i, j;
-       uint16_t idx, shift;
-       uint8_t mask;
+       struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
+       uint8_t *lut;
+       uint16_t i, idx, shift;
+       int ret;
 
        if (reta_size != ETH_RSS_RETA_SIZE_64) {
                PMD_DRV_LOG(ERR, "The size of hash lookup table configured "
@@ -1842,29 +1996,26 @@ i40evf_dev_rss_reta_update(struct rte_eth_dev *dev,
                return -EINVAL;
        }
 
-       for (i = 0; i < reta_size; i += I40E_4_BIT_WIDTH) {
+       lut = rte_zmalloc("i40e_rss_lut", reta_size, 0);
+       if (!lut) {
+               PMD_DRV_LOG(ERR, "No memory can be allocated");
+               return -ENOMEM;
+       }
+       ret = i40evf_get_rss_lut(&vf->vsi, lut, reta_size);
+       if (ret)
+               goto out;
+       for (i = 0; i < reta_size; i++) {
                idx = i / RTE_RETA_GROUP_SIZE;
                shift = i % RTE_RETA_GROUP_SIZE;
-               mask = (uint8_t)((reta_conf[idx].mask >> shift) &
-                                               I40E_4_BIT_MASK);
-               if (!mask)
-                       continue;
-               if (mask == I40E_4_BIT_MASK)
-                       l = 0;
-               else
-                       l = I40E_READ_REG(hw, I40E_VFQF_HLUT(i >> 2));
-
-               for (j = 0, lut = 0; j < I40E_4_BIT_WIDTH; j++) {
-                       if (mask & (0x1 << j))
-                               lut |= reta_conf[idx].reta[shift + j] <<
-                                                       (CHAR_BIT * j);
-                       else
-                               lut |= l & (I40E_8_BIT_MASK << (CHAR_BIT * j));
-               }
-               I40E_WRITE_REG(hw, I40E_VFQF_HLUT(i >> 2), lut);
+               if (reta_conf[idx].mask & (1ULL << shift))
+                       lut[i] = reta_conf[idx].reta[shift];
        }
+       ret = i40evf_set_rss_lut(&vf->vsi, lut, reta_size);
 
-       return 0;
+out:
+       rte_free(lut);
+
+       return ret;
 }
 
 static int
@@ -1872,11 +2023,10 @@ i40evf_dev_rss_reta_query(struct rte_eth_dev *dev,
                          struct rte_eth_rss_reta_entry64 *reta_conf,
                          uint16_t reta_size)
 {
-       struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-       uint32_t lut;
-       uint16_t i, j;
-       uint16_t idx, shift;
-       uint8_t mask;
+       struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
+       uint16_t i, idx, shift;
+       uint8_t *lut;
+       int ret;
 
        if (reta_size != ETH_RSS_RETA_SIZE_64) {
                PMD_DRV_LOG(ERR, "The size of hash lookup table configured "
@@ -1885,43 +2035,100 @@ i40evf_dev_rss_reta_query(struct rte_eth_dev *dev,
                return -EINVAL;
        }
 
-       for (i = 0; i < reta_size; i += I40E_4_BIT_WIDTH) {
+       lut = rte_zmalloc("i40e_rss_lut", reta_size, 0);
+       if (!lut) {
+               PMD_DRV_LOG(ERR, "No memory can be allocated");
+               return -ENOMEM;
+       }
+
+       ret = i40evf_get_rss_lut(&vf->vsi, lut, reta_size);
+       if (ret)
+               goto out;
+       for (i = 0; i < reta_size; i++) {
                idx = i / RTE_RETA_GROUP_SIZE;
                shift = i % RTE_RETA_GROUP_SIZE;
-               mask = (uint8_t)((reta_conf[idx].mask >> shift) &
-                                               I40E_4_BIT_MASK);
-               if (!mask)
-                       continue;
-
-               lut = I40E_READ_REG(hw, I40E_VFQF_HLUT(i >> 2));
-               for (j = 0; j < I40E_4_BIT_WIDTH; j++) {
-                       if (mask & (0x1 << j))
-                               reta_conf[idx].reta[shift + j] =
-                                       ((lut >> (CHAR_BIT * j)) &
-                                               I40E_8_BIT_MASK);
-               }
+               if (reta_conf[idx].mask & (1ULL << shift))
+                       reta_conf[idx].reta[shift] = lut[i];
        }
 
-       return 0;
+out:
+       rte_free(lut);
+
+       return ret;
 }
 
 static int
-i40evf_hw_rss_hash_set(struct i40e_hw *hw, struct rte_eth_rss_conf *rss_conf)
+i40evf_set_rss_key(struct i40e_vsi *vsi, uint8_t *key, uint8_t key_len)
 {
-       uint32_t *hash_key;
-       uint8_t hash_key_len;
-       uint64_t rss_hf, hena;
+       struct i40e_vf *vf = I40E_VSI_TO_VF(vsi);
+       struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+       int ret = 0;
+
+       if (!key || key_len != ((I40E_VFQF_HKEY_MAX_INDEX + 1) *
+               sizeof(uint32_t)))
+               return -EINVAL;
+
+       if (vf->flags & I40E_FLAG_RSS_AQ_CAPABLE) {
+               struct i40e_aqc_get_set_rss_key_data *key_dw =
+                       (struct i40e_aqc_get_set_rss_key_data *)key;
 
-       hash_key = (uint32_t *)(rss_conf->rss_key);
-       hash_key_len = rss_conf->rss_key_len;
-       if (hash_key != NULL && hash_key_len >=
-               (I40E_VFQF_HKEY_MAX_INDEX + 1) * sizeof(uint32_t)) {
+               ret = i40e_aq_set_rss_key(hw, vsi->vsi_id, key_dw);
+               if (ret)
+                       PMD_INIT_LOG(ERR, "Failed to configure RSS key "
+                                    "via AQ");
+       } else {
+               uint32_t *hash_key = (uint32_t *)key;
                uint16_t i;
 
                for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
                        I40E_WRITE_REG(hw, I40E_VFQF_HKEY(i), hash_key[i]);
+               I40EVF_WRITE_FLUSH(hw);
        }
 
+       return ret;
+}
+
+static int
+i40evf_get_rss_key(struct i40e_vsi *vsi, uint8_t *key, uint8_t *key_len)
+{
+       struct i40e_vf *vf = I40E_VSI_TO_VF(vsi);
+       struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+       int ret;
+
+       if (!key || !key_len)
+               return -EINVAL;
+
+       if (vf->flags & I40E_FLAG_RSS_AQ_CAPABLE) {
+               ret = i40e_aq_get_rss_key(hw, vsi->vsi_id,
+                       (struct i40e_aqc_get_set_rss_key_data *)key);
+               if (ret) {
+                       PMD_INIT_LOG(ERR, "Failed to get RSS key via AQ");
+                       return ret;
+               }
+       } else {
+               uint32_t *key_dw = (uint32_t *)key;
+               uint16_t i;
+
+               for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
+                       key_dw[i] = I40E_READ_REG(hw, I40E_VFQF_HKEY(i));
+       }
+       *key_len = (I40E_VFQF_HKEY_MAX_INDEX + 1) * sizeof(uint32_t);
+
+       return 0;
+}
+
+static int
+i40evf_hw_rss_hash_set(struct i40e_vf *vf, struct rte_eth_rss_conf *rss_conf)
+{
+       struct i40e_hw *hw = I40E_VF_TO_HW(vf);
+       uint64_t rss_hf, hena;
+       int ret;
+
+       ret = i40evf_set_rss_key(&vf->vsi, rss_conf->rss_key,
+                                rss_conf->rss_key_len);
+       if (ret)
+               return ret;
+
        rss_hf = rss_conf->rss_hf;
        hena = (uint64_t)I40E_READ_REG(hw, I40E_VFQF_HENA(0));
        hena |= ((uint64_t)I40E_READ_REG(hw, I40E_VFQF_HENA(1))) << 32;
@@ -1987,13 +2194,14 @@ i40evf_config_rss(struct i40e_vf *vf)
                rss_conf.rss_key_len = nb_q;
        }
 
-       return i40evf_hw_rss_hash_set(hw, &rss_conf);
+       return i40evf_hw_rss_hash_set(vf, &rss_conf);
 }
 
 static int
 i40evf_dev_rss_hash_update(struct rte_eth_dev *dev,
                           struct rte_eth_rss_conf *rss_conf)
 {
+       struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
        struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
        uint64_t rss_hf = rss_conf->rss_hf & I40E_RSS_OFFLOAD_ALL;
        uint64_t hena;
@@ -2010,23 +2218,20 @@ i40evf_dev_rss_hash_update(struct rte_eth_dev *dev,
        if (rss_hf == 0) /* Disable RSS */
                return -EINVAL;
 
-       return i40evf_hw_rss_hash_set(hw, rss_conf);
+       return i40evf_hw_rss_hash_set(vf, rss_conf);
 }
 
 static int
 i40evf_dev_rss_hash_conf_get(struct rte_eth_dev *dev,
                             struct rte_eth_rss_conf *rss_conf)
 {
+       struct i40e_vf *vf = I40EVF_DEV_PRIVATE_TO_VF(dev->data->dev_private);
        struct i40e_hw *hw = I40E_DEV_PRIVATE_TO_HW(dev->data->dev_private);
-       uint32_t *hash_key = (uint32_t *)(rss_conf->rss_key);
        uint64_t hena;
-       uint16_t i;
 
-       if (hash_key) {
-               for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
-                       hash_key[i] = I40E_READ_REG(hw, I40E_VFQF_HKEY(i));
-               rss_conf->rss_key_len = i * sizeof(uint32_t);
-       }
+       i40evf_get_rss_key(&vf->vsi, rss_conf->rss_key,
+                          &rss_conf->rss_key_len);
+
        hena = (uint64_t)I40E_READ_REG(hw, I40E_VFQF_HENA(0));
        hena |= ((uint64_t)I40E_READ_REG(hw, I40E_VFQF_HENA(1))) << 32;
        rss_conf->rss_hf = i40e_parse_hena(hena);