i40e: support AQ based RSS config
[dpdk.git] / drivers / net / i40e / i40e_ethdev.c
index 480dd57..5a9f11d 100644 (file)
@@ -153,7 +153,10 @@ static int i40e_dev_set_link_up(struct rte_eth_dev *dev);
 static int i40e_dev_set_link_down(struct rte_eth_dev *dev);
 static void i40e_dev_stats_get(struct rte_eth_dev *dev,
                               struct rte_eth_stats *stats);
+static int i40e_dev_xstats_get(struct rte_eth_dev *dev,
+                              struct rte_eth_xstats *xstats, unsigned n);
 static void i40e_dev_stats_reset(struct rte_eth_dev *dev);
+static void i40e_dev_xstats_reset(struct rte_eth_dev *dev);
 static int i40e_dev_queue_stats_mapping_set(struct rte_eth_dev *dev,
                                            uint16_t queue_id,
                                            uint8_t stat_idx,
@@ -264,6 +267,8 @@ static int i40e_timesync_read_rx_timestamp(struct rte_eth_dev *dev,
                                           uint32_t flags);
 static int i40e_timesync_read_tx_timestamp(struct rte_eth_dev *dev,
                                           struct timespec *timestamp);
+static void i40e_read_stats_registers(struct i40e_pf *pf, struct i40e_hw *hw);
+
 
 static const struct rte_pci_id pci_id_i40e_map[] = {
 #define RTE_PCI_DEV_ID_DECL_I40E(vend, dev) {RTE_PCI_DEVICE(vend, dev)},
@@ -284,7 +289,9 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
        .dev_set_link_down            = i40e_dev_set_link_down,
        .link_update                  = i40e_dev_link_update,
        .stats_get                    = i40e_dev_stats_get,
+       .xstats_get                   = i40e_dev_xstats_get,
        .stats_reset                  = i40e_dev_stats_reset,
+       .xstats_reset                 = i40e_dev_xstats_reset,
        .queue_stats_mapping_set      = i40e_dev_queue_stats_mapping_set,
        .dev_infos_get                = i40e_dev_info_get,
        .vlan_filter_set              = i40e_vlan_filter_set,
@@ -316,6 +323,8 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
        .udp_tunnel_add               = i40e_dev_udp_tunnel_add,
        .udp_tunnel_del               = i40e_dev_udp_tunnel_del,
        .filter_ctrl                  = i40e_dev_filter_ctrl,
+       .rxq_info_get                 = i40e_rxq_info_get,
+       .txq_info_get                 = i40e_txq_info_get,
        .mirror_rule_set              = i40e_mirror_rule_set,
        .mirror_rule_reset            = i40e_mirror_rule_reset,
        .timesync_enable              = i40e_timesync_enable,
@@ -325,6 +334,101 @@ static const struct eth_dev_ops i40e_eth_dev_ops = {
        .get_dcb_info                 = i40e_dev_get_dcb_info,
 };
 
+/* store statistics names and its offset in stats structure */
+struct rte_i40e_xstats_name_off {
+       char name[RTE_ETH_XSTATS_NAME_SIZE];
+       unsigned offset;
+};
+
+static const struct rte_i40e_xstats_name_off rte_i40e_stats_strings[] = {
+       {"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", offsetof(struct i40e_eth_stats, rx_discards)},
+       {"rx_unknown_protocol_packets", offsetof(struct i40e_eth_stats,
+               rx_unknown_protocol)},
+       {"tx_unicast_packets", offsetof(struct i40e_eth_stats, tx_unicast)},
+       {"tx_multicast_packets", offsetof(struct i40e_eth_stats, tx_multicast)},
+       {"tx_broadcast_packets", offsetof(struct i40e_eth_stats, tx_broadcast)},
+       {"tx_dropped", offsetof(struct i40e_eth_stats, tx_discards)},
+};
+
+static const struct rte_i40e_xstats_name_off rte_i40e_hw_port_strings[] = {
+       {"tx_link_down_dropped", offsetof(struct i40e_hw_port_stats,
+               tx_dropped_link_down)},
+       {"rx_crc_errors", offsetof(struct i40e_hw_port_stats, crc_errors)},
+       {"rx_illegal_byte_errors", offsetof(struct i40e_hw_port_stats,
+               illegal_bytes)},
+       {"rx_error_bytes", offsetof(struct i40e_hw_port_stats, error_bytes)},
+       {"mac_local_errors", offsetof(struct i40e_hw_port_stats,
+               mac_local_faults)},
+       {"mac_remote_errors", offsetof(struct i40e_hw_port_stats,
+               mac_remote_faults)},
+       {"rx_length_errors", offsetof(struct i40e_hw_port_stats,
+               rx_length_errors)},
+       {"tx_xon_packets", offsetof(struct i40e_hw_port_stats, link_xon_tx)},
+       {"rx_xon_packets", offsetof(struct i40e_hw_port_stats, link_xon_rx)},
+       {"tx_xoff_packets", offsetof(struct i40e_hw_port_stats, link_xoff_tx)},
+       {"rx_xoff_packets", offsetof(struct i40e_hw_port_stats, link_xoff_rx)},
+       {"rx_size_64_packets", offsetof(struct i40e_hw_port_stats, rx_size_64)},
+       {"rx_size_65_to_127_packets", offsetof(struct i40e_hw_port_stats,
+               rx_size_127)},
+       {"rx_size_128_to_255_packets", offsetof(struct i40e_hw_port_stats,
+               rx_size_255)},
+       {"rx_size_256_to_511_packets", offsetof(struct i40e_hw_port_stats,
+               rx_size_511)},
+       {"rx_size_512_to_1023_packets", offsetof(struct i40e_hw_port_stats,
+               rx_size_1023)},
+       {"rx_size_1024_to_1522_packets", offsetof(struct i40e_hw_port_stats,
+               rx_size_1522)},
+       {"rx_size_1523_to_max_packets", offsetof(struct i40e_hw_port_stats,
+               rx_size_big)},
+       {"rx_undersized_errors", offsetof(struct i40e_hw_port_stats,
+               rx_undersize)},
+       {"rx_oversize_errors", offsetof(struct i40e_hw_port_stats,
+               rx_oversize)},
+       {"rx_mac_short_dropped", offsetof(struct i40e_hw_port_stats,
+               mac_short_packet_dropped)},
+       {"rx_fragmented_errors", offsetof(struct i40e_hw_port_stats,
+               rx_fragments)},
+       {"rx_jabber_errors", offsetof(struct i40e_hw_port_stats, rx_jabber)},
+       {"tx_size_64_packets", offsetof(struct i40e_hw_port_stats, tx_size_64)},
+       {"tx_size_65_to_127_packets", offsetof(struct i40e_hw_port_stats,
+               tx_size_127)},
+       {"tx_size_128_to_255_packets", offsetof(struct i40e_hw_port_stats,
+               tx_size_255)},
+       {"tx_size_256_to_511_packets", offsetof(struct i40e_hw_port_stats,
+               tx_size_511)},
+       {"tx_size_512_to_1023_packets", offsetof(struct i40e_hw_port_stats,
+               tx_size_1023)},
+       {"tx_size_1024_to_1522_packets", offsetof(struct i40e_hw_port_stats,
+               tx_size_1522)},
+       {"tx_size_1523_to_max_packets", offsetof(struct i40e_hw_port_stats,
+               tx_size_big)},
+       {"rx_flow_director_atr_match_packets",
+               offsetof(struct i40e_hw_port_stats, fd_atr_match)},
+       {"rx_flow_director_sb_match_packets",
+               offsetof(struct i40e_hw_port_stats, fd_sb_match)},
+       {"tx_low_power_idle_status", offsetof(struct i40e_hw_port_stats,
+               tx_lpi_status)},
+       {"rx_low_power_idle_status", offsetof(struct i40e_hw_port_stats,
+               rx_lpi_status)},
+       {"tx_low_power_idle_count", offsetof(struct i40e_hw_port_stats,
+               tx_lpi_count)},
+       {"rx_low_power_idle_count", offsetof(struct i40e_hw_port_stats,
+               rx_lpi_count)},
+};
+
+/* Q Stats: 5 stats are exposed for each queue, implemented in xstats_get() */
+#define I40E_NB_HW_PORT_Q_STATS (8 * 5)
+
+#define I40E_NB_ETH_XSTATS (sizeof(rte_i40e_stats_strings) / \
+               sizeof(rte_i40e_stats_strings[0]))
+#define I40E_NB_HW_PORT_XSTATS (sizeof(rte_i40e_hw_port_strings) / \
+               sizeof(rte_i40e_hw_port_strings[0]))
+#define I40E_NB_XSTATS (I40E_NB_ETH_XSTATS + I40E_NB_HW_PORT_XSTATS + \
+               I40E_NB_HW_PORT_Q_STATS)
+
 static struct eth_driver rte_i40e_pmd = {
        .pci_drv = {
                .name = "rte_i40e_pmd",
@@ -1417,16 +1521,12 @@ i40e_update_vsi_stats(struct i40e_vsi *vsi)
                    vsi->vsi_id);
 }
 
-/* Get all statistics of a port */
 static void
-i40e_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
+i40e_read_stats_registers(struct i40e_pf *pf, struct i40e_hw *hw)
 {
-       uint32_t i;
-       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);
+       unsigned int i;
        struct i40e_hw_port_stats *ns = &pf->stats; /* new stats */
        struct i40e_hw_port_stats *os = &pf->stats_offset; /* old stats */
-
        /* Get statistics of struct i40e_eth_stats */
        i40e_stat_update_48(hw, I40E_GLPRT_GORCH(hw->port),
                            I40E_GLPRT_GORCL(hw->port),
@@ -1605,6 +1705,19 @@ i40e_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
 
        if (pf->main_vsi)
                i40e_update_vsi_stats(pf->main_vsi);
+}
+
+/* Get all statistics of a port */
+static void
+i40e_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
+{
+       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);
+       struct i40e_hw_port_stats *ns = &pf->stats; /* new stats */
+       unsigned i;
+
+       /* call read registers - updates values, now write them to struct */
+       i40e_read_stats_registers(pf, hw);
 
        stats->ipackets = ns->eth.rx_unicast + ns->eth.rx_multicast +
                                                ns->eth.rx_broadcast;
@@ -1694,6 +1807,98 @@ i40e_dev_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats)
        PMD_DRV_LOG(DEBUG, "***************** PF stats end ********************");
 }
 
+static void
+i40e_dev_xstats_reset(struct rte_eth_dev *dev)
+{
+       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);
+       struct i40e_hw_port_stats *hw_stats = &pf->stats;
+
+       /* The hw registers are cleared on read */
+       pf->offset_loaded = false;
+       i40e_read_stats_registers(pf, hw);
+
+       /* reset software counters */
+       memset(hw_stats, 0, sizeof(*hw_stats));
+}
+
+static int
+i40e_dev_xstats_get(struct rte_eth_dev *dev, struct rte_eth_xstats *xstats,
+                   unsigned n)
+{
+       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);
+       unsigned i, count = 0;
+       struct i40e_hw_port_stats *hw_stats = &pf->stats;
+
+       if (n < I40E_NB_XSTATS)
+               return I40E_NB_XSTATS;
+
+       i40e_read_stats_registers(pf, hw);
+
+       /* Reset */
+       if (xstats == NULL)
+               return 0;
+
+       /* Get stats from i40e_eth_stats struct */
+       for (i = 0; i < I40E_NB_ETH_XSTATS; i++) {
+               snprintf(xstats[count].name, sizeof(xstats[count].name),
+                        "%s", rte_i40e_stats_strings[i].name);
+               xstats[count].value = *(uint64_t *)(((char *)&hw_stats->eth) +
+                       rte_i40e_stats_strings[i].offset);
+               count++;
+       }
+
+       /* Get individiual stats from i40e_hw_port struct */
+       for (i = 0; i < I40E_NB_HW_PORT_XSTATS; i++) {
+               snprintf(xstats[count].name, sizeof(xstats[count].name),
+                        "%s", rte_i40e_hw_port_strings[i].name);
+               xstats[count].value = *(uint64_t *)(((char *)hw_stats) +
+                               rte_i40e_hw_port_strings[i].offset);
+               count++;
+       }
+
+       /* Get per-queue stats from i40e_hw_port struct */
+       for (i = 0; i < 8; i++) {
+               snprintf(xstats[count].name, sizeof(xstats[count].name),
+                        "rx_q%u_xon_priority_packets", i);
+               xstats[count].value = *(uint64_t *)(((char *)hw_stats) +
+                               offsetof(struct i40e_hw_port_stats,
+                                        priority_xon_rx[i]));
+               count++;
+
+               snprintf(xstats[count].name, sizeof(xstats[count].name),
+                        "rx_q%u_xoff_priority_packets", i);
+               xstats[count].value = *(uint64_t *)(((char *)hw_stats) +
+                               offsetof(struct i40e_hw_port_stats,
+                                        priority_xoff_rx[i]));
+               count++;
+
+               snprintf(xstats[count].name, sizeof(xstats[count].name),
+                        "tx_q%u_xon_priority_packets", i);
+               xstats[count].value = *(uint64_t *)(((char *)hw_stats) +
+                               offsetof(struct i40e_hw_port_stats,
+                                        priority_xon_tx[i]));
+               count++;
+
+               snprintf(xstats[count].name, sizeof(xstats[count].name),
+                        "tx_q%u_xoff_priority_packets", i);
+               xstats[count].value = *(uint64_t *)(((char *)hw_stats) +
+                               offsetof(struct i40e_hw_port_stats,
+                                        priority_xoff_tx[i]));
+               count++;
+
+               snprintf(xstats[count].name, sizeof(xstats[count].name),
+                        "xx_q%u_xon_to_xoff_priority_packets", i);
+               xstats[count].value = *(uint64_t *)(((char *)hw_stats) +
+                               offsetof(struct i40e_hw_port_stats,
+                                        priority_xon_2_xoff[i]));
+               count++;
+       }
+
+       return I40E_NB_XSTATS;
+}
+
 /* Reset the statistics */
 static void
 i40e_dev_stats_reset(struct rte_eth_dev *dev)
@@ -1769,6 +1974,18 @@ i40e_dev_info_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info)
                                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,
+       };
+
        if (pf->flags & I40E_FLAG_VMDQ) {
                dev_info->max_vmdq_pools = pf->max_nb_vmdq_vsi;
                dev_info->vmdq_queue_base = dev_info->max_rx_queues;
@@ -2224,17 +2441,73 @@ i40e_mac_filter_handle(struct rte_eth_dev *dev, enum rte_filter_op filter_op,
        return ret;
 }
 
+static int
+i40e_get_rss_lut(struct i40e_vsi *vsi, uint8_t *lut, uint16_t lut_size)
+{
+       struct i40e_pf *pf = I40E_VSI_TO_PF(vsi);
+       struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+       int ret;
+
+       if (!lut)
+               return -EINVAL;
+
+       if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) {
+               ret = i40e_aq_get_rss_lut(hw, vsi->vsi_id, TRUE,
+                                         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_PFQF_HLUT(i));
+       }
+
+       return 0;
+}
+
+static int
+i40e_set_rss_lut(struct i40e_vsi *vsi, uint8_t *lut, uint16_t lut_size)
+{
+       struct i40e_pf *pf = I40E_VSI_TO_PF(vsi);
+       struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+       int ret;
+
+       if (!vsi || !lut)
+               return -EINVAL;
+
+       if (pf->flags & I40E_FLAG_RSS_AQ_CAPABLE) {
+               ret = i40e_aq_set_rss_lut(hw, vsi->vsi_id, TRUE,
+                                         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_PFQF_HLUT(i), lut_dw[i]);
+               I40E_WRITE_FLUSH(hw);
+       }
+
+       return 0;
+}
+
 static int
 i40e_dev_rss_reta_update(struct rte_eth_dev *dev,
                         struct rte_eth_rss_reta_entry64 *reta_conf,
                         uint16_t reta_size)
 {
        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);
-       uint32_t lut, l;
-       uint16_t i, j, lut_size = pf->hash_lut_size;
+       uint16_t i, lut_size = pf->hash_lut_size;
        uint16_t idx, shift;
-       uint8_t mask;
+       uint8_t *lut;
+       int ret;
 
        if (reta_size != lut_size ||
                reta_size > ETH_RSS_RETA_SIZE_512) {
@@ -2244,28 +2517,26 @@ i40e_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 = i40e_get_rss_lut(pf->main_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_PFQF_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_PFQF_HLUT(i >> 2), lut);
+               if (reta_conf[idx].mask & (1ULL << shift))
+                       lut[i] = reta_conf[idx].reta[shift];
        }
+       ret = i40e_set_rss_lut(pf->main_vsi, lut, reta_size);
 
-       return 0;
+out:
+       rte_free(lut);
+
+       return ret;
 }
 
 static int
@@ -2274,11 +2545,10 @@ i40e_dev_rss_reta_query(struct rte_eth_dev *dev,
                        uint16_t reta_size)
 {
        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);
-       uint32_t lut;
-       uint16_t i, j, lut_size = pf->hash_lut_size;
+       uint16_t i, lut_size = pf->hash_lut_size;
        uint16_t idx, shift;
-       uint8_t mask;
+       uint8_t *lut;
+       int ret;
 
        if (reta_size != lut_size ||
                reta_size > ETH_RSS_RETA_SIZE_512) {
@@ -2288,23 +2558,26 @@ i40e_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 = i40e_get_rss_lut(pf->main_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_PFQF_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;
 }
 
 /**
@@ -2489,6 +2762,8 @@ i40e_pf_parameter_init(struct rte_eth_dev *dev)
        /* Allocate queues for pf */
        if (hw->func_caps.rss) {
                pf->flags |= I40E_FLAG_RSS;
+               if (hw->mac.type == I40E_MAC_X722)
+                       pf->flags |= I40E_FLAG_RSS_AQ_CAPABLE;
                pf->lan_nb_qps = RTE_MIN(hw->func_caps.num_tx_qp,
                        (uint32_t)(1 << hw->func_caps.rss_table_entry_width));
                pf->lan_nb_qps = i40e_align_floor(pf->lan_nb_qps);
@@ -5036,23 +5311,78 @@ i40e_pf_disable_rss(struct i40e_pf *pf)
 }
 
 static int
-i40e_hw_rss_hash_set(struct i40e_hw *hw, struct rte_eth_rss_conf *rss_conf)
+i40e_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;
-       uint16_t i;
-       uint64_t hena;
+       struct i40e_pf *pf = I40E_VSI_TO_PF(vsi);
+       struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+       int ret = 0;
+
+       if (!key || key_len != ((I40E_PFQF_HKEY_MAX_INDEX + 1) *
+               sizeof(uint32_t)))
+               return -EINVAL;
+
+       if (pf->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;
+
+               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;
 
-       hash_key = (uint32_t *)(rss_conf->rss_key);
-       hash_key_len = rss_conf->rss_key_len;
-       if (hash_key != NULL && hash_key_len >=
-               (I40E_PFQF_HKEY_MAX_INDEX + 1) * sizeof(uint32_t)) {
-               /* Fill in RSS hash key */
                for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++)
                        I40E_WRITE_REG(hw, I40E_PFQF_HKEY(i), hash_key[i]);
+               I40E_WRITE_FLUSH(hw);
        }
 
+       return ret;
+}
+
+static int
+i40e_get_rss_key(struct i40e_vsi *vsi, uint8_t *key, uint8_t *key_len)
+{
+       struct i40e_pf *pf = I40E_VSI_TO_PF(vsi);
+       struct i40e_hw *hw = I40E_VSI_TO_HW(vsi);
+       int ret;
+
+       if (!key || !key_len)
+               return -EINVAL;
+
+       if (pf->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_PFQF_HKEY_MAX_INDEX; i++)
+                       key_dw[i] = I40E_READ_REG(hw, I40E_PFQF_HKEY(i));
+       }
+       *key_len = (I40E_PFQF_HKEY_MAX_INDEX + 1) * sizeof(uint32_t);
+
+       return 0;
+}
+
+static int
+i40e_hw_rss_hash_set(struct i40e_pf *pf, struct rte_eth_rss_conf *rss_conf)
+{
+       struct i40e_hw *hw = I40E_PF_TO_HW(pf);
+       uint64_t rss_hf;
+       uint64_t hena;
+       int ret;
+
+       ret = i40e_set_rss_key(pf->main_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_PFQF_HENA(0));
        hena |= ((uint64_t)I40E_READ_REG(hw, I40E_PFQF_HENA(1))) << 32;
@@ -5069,6 +5399,7 @@ static int
 i40e_dev_rss_hash_update(struct rte_eth_dev *dev,
                         struct rte_eth_rss_conf *rss_conf)
 {
+       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);
        uint64_t rss_hf = rss_conf->rss_hf & I40E_RSS_OFFLOAD_ALL;
        uint64_t hena;
@@ -5084,23 +5415,20 @@ i40e_dev_rss_hash_update(struct rte_eth_dev *dev,
        if (rss_hf == 0) /* Disable RSS */
                return -EINVAL;
 
-       return i40e_hw_rss_hash_set(hw, rss_conf);
+       return i40e_hw_rss_hash_set(pf, rss_conf);
 }
 
 static int
 i40e_dev_rss_hash_conf_get(struct rte_eth_dev *dev,
                           struct rte_eth_rss_conf *rss_conf)
 {
+       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);
-       uint32_t *hash_key = (uint32_t *)(rss_conf->rss_key);
        uint64_t hena;
-       uint16_t i;
 
-       if (hash_key != NULL) {
-               for (i = 0; i <= I40E_PFQF_HKEY_MAX_INDEX; i++)
-                       hash_key[i] = I40E_READ_REG(hw, I40E_PFQF_HKEY(i));
-               rss_conf->rss_key_len = i * sizeof(uint32_t);
-       }
+       i40e_get_rss_key(pf->main_vsi, rss_conf->rss_key,
+                        &rss_conf->rss_key_len);
+
        hena = (uint64_t)I40E_READ_REG(hw, I40E_PFQF_HENA(0));
        hena |= ((uint64_t)I40E_READ_REG(hw, I40E_PFQF_HENA(1))) << 32;
        rss_conf->rss_hf = i40e_parse_hena(hena);
@@ -5438,7 +5766,7 @@ i40e_pf_config_rss(struct i40e_pf *pf)
                                                        sizeof(uint32_t);
        }
 
-       return i40e_hw_rss_hash_set(hw, &rss_conf);
+       return i40e_hw_rss_hash_set(pf, &rss_conf);
 }
 
 static int