Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jikos/trivial
[linux-2.6-block.git] / drivers / net / ethernet / intel / i40evf / i40evf_main.c
index 6b84c0d7f1207e74b7e58cd291fdb18c8c268a97..94da913b151da615f751d287b33a65de92d95829 100644 (file)
@@ -34,7 +34,15 @@ char i40evf_driver_name[] = "i40evf";
 static const char i40evf_driver_string[] =
        "Intel(R) XL710/X710 Virtual Function Network Driver";
 
-#define DRV_VERSION "1.3.33"
+#define DRV_KERN "-k"
+
+#define DRV_VERSION_MAJOR 1
+#define DRV_VERSION_MINOR 4
+#define DRV_VERSION_BUILD 4
+#define DRV_VERSION __stringify(DRV_VERSION_MAJOR) "." \
+            __stringify(DRV_VERSION_MINOR) "." \
+            __stringify(DRV_VERSION_BUILD) \
+            DRV_KERN
 const char i40evf_driver_version[] = DRV_VERSION;
 static const char i40evf_copyright[] =
        "Copyright (c) 2013 - 2015 Intel Corporation.";
@@ -259,7 +267,7 @@ static void i40evf_fire_sw_int(struct i40evf_adapter *adapter, u32 mask)
 {
        struct i40e_hw *hw = &adapter->hw;
        int i;
-       uint32_t dyn_ctl;
+       u32 dyn_ctl;
 
        if (mask & 1) {
                dyn_ctl = rd32(hw, I40E_VFINT_DYN_CTL01);
@@ -307,10 +315,9 @@ static irqreturn_t i40evf_msix_aq(int irq, void *data)
        struct i40e_hw *hw = &adapter->hw;
        u32 val;
 
-       /* handle non-queue interrupts */
-       rd32(hw, I40E_VFINT_ICR01);
-       rd32(hw, I40E_VFINT_ICR0_ENA1);
-
+       /* handle non-queue interrupts, these reads clear the registers */
+       val = rd32(hw, I40E_VFINT_ICR01);
+       val = rd32(hw, I40E_VFINT_ICR0_ENA1);
 
        val = rd32(hw, I40E_VFINT_DYN_CTL01) |
              I40E_VFINT_DYN_CTL01_CLEARPBA_MASK;
@@ -348,8 +355,8 @@ static irqreturn_t i40evf_msix_clean_rings(int irq, void *data)
 static void
 i40evf_map_vector_to_rxq(struct i40evf_adapter *adapter, int v_idx, int r_idx)
 {
-       struct i40e_q_vector *q_vector = adapter->q_vector[v_idx];
-       struct i40e_ring *rx_ring = adapter->rx_rings[r_idx];
+       struct i40e_q_vector *q_vector = &adapter->q_vectors[v_idx];
+       struct i40e_ring *rx_ring = &adapter->rx_rings[r_idx];
 
        rx_ring->q_vector = q_vector;
        rx_ring->next = q_vector->rx.ring;
@@ -369,8 +376,8 @@ i40evf_map_vector_to_rxq(struct i40evf_adapter *adapter, int v_idx, int r_idx)
 static void
 i40evf_map_vector_to_txq(struct i40evf_adapter *adapter, int v_idx, int t_idx)
 {
-       struct i40e_q_vector *q_vector = adapter->q_vector[v_idx];
-       struct i40e_ring *tx_ring = adapter->tx_rings[t_idx];
+       struct i40e_q_vector *q_vector = &adapter->q_vectors[v_idx];
+       struct i40e_ring *tx_ring = &adapter->tx_rings[t_idx];
 
        tx_ring->q_vector = q_vector;
        tx_ring->next = q_vector->tx.ring;
@@ -465,7 +472,7 @@ static void i40evf_netpoll(struct net_device *netdev)
                return;
 
        for (i = 0; i < q_vectors; i++)
-               i40evf_msix_clean_rings(0, adapter->q_vector[i]);
+               i40evf_msix_clean_rings(0, &adapter->q_vectors[i]);
 }
 
 #endif
@@ -487,7 +494,7 @@ i40evf_request_traffic_irqs(struct i40evf_adapter *adapter, char *basename)
        q_vectors = adapter->num_msix_vectors - NONQ_VECS;
 
        for (vector = 0; vector < q_vectors; vector++) {
-               struct i40e_q_vector *q_vector = adapter->q_vector[vector];
+               struct i40e_q_vector *q_vector = &adapter->q_vectors[vector];
 
                if (q_vector->tx.ring && q_vector->rx.ring) {
                        snprintf(q_vector->name, sizeof(q_vector->name) - 1,
@@ -532,7 +539,7 @@ free_queue_irqs:
                        adapter->msix_entries[vector + NONQ_VECS].vector,
                        NULL);
                free_irq(adapter->msix_entries[vector + NONQ_VECS].vector,
-                        adapter->q_vector[vector]);
+                        &adapter->q_vectors[vector]);
        }
        return err;
 }
@@ -582,7 +589,7 @@ static void i40evf_free_traffic_irqs(struct i40evf_adapter *adapter)
                irq_set_affinity_hint(adapter->msix_entries[i+1].vector,
                                      NULL);
                free_irq(adapter->msix_entries[i+1].vector,
-                        adapter->q_vector[i]);
+                        &adapter->q_vectors[i]);
        }
 }
 
@@ -611,7 +618,7 @@ static void i40evf_configure_tx(struct i40evf_adapter *adapter)
        int i;
 
        for (i = 0; i < adapter->num_active_queues; i++)
-               adapter->tx_rings[i]->tail = hw->hw_addr + I40E_QTX_TAIL1(i);
+               adapter->tx_rings[i].tail = hw->hw_addr + I40E_QTX_TAIL1(i);
 }
 
 /**
@@ -656,8 +663,8 @@ static void i40evf_configure_rx(struct i40evf_adapter *adapter)
        }
 
        for (i = 0; i < adapter->num_active_queues; i++) {
-               adapter->rx_rings[i]->tail = hw->hw_addr + I40E_QRX_TAIL1(i);
-               adapter->rx_rings[i]->rx_buf_len = rx_buf_len;
+               adapter->rx_rings[i].tail = hw->hw_addr + I40E_QRX_TAIL1(i);
+               adapter->rx_rings[i].rx_buf_len = rx_buf_len;
        }
 }
 
@@ -954,7 +961,7 @@ static void i40evf_napi_enable_all(struct i40evf_adapter *adapter)
        for (q_idx = 0; q_idx < q_vectors; q_idx++) {
                struct napi_struct *napi;
 
-               q_vector = adapter->q_vector[q_idx];
+               q_vector = &adapter->q_vectors[q_idx];
                napi = &q_vector->napi;
                napi_enable(napi);
        }
@@ -971,7 +978,7 @@ static void i40evf_napi_disable_all(struct i40evf_adapter *adapter)
        int q_vectors = adapter->num_msix_vectors - NONQ_VECS;
 
        for (q_idx = 0; q_idx < q_vectors; q_idx++) {
-               q_vector = adapter->q_vector[q_idx];
+               q_vector = &adapter->q_vectors[q_idx];
                napi_disable(&q_vector->napi);
        }
 }
@@ -992,7 +999,7 @@ static void i40evf_configure(struct i40evf_adapter *adapter)
        adapter->aq_required |= I40EVF_FLAG_AQ_CONFIGURE_QUEUES;
 
        for (i = 0; i < adapter->num_active_queues; i++) {
-               struct i40e_ring *ring = adapter->rx_rings[i];
+               struct i40e_ring *ring = &adapter->rx_rings[i];
 
                i40evf_alloc_rx_buffers_1buf(ring, ring->count);
                ring->next_to_use = ring->count - 1;
@@ -1112,16 +1119,10 @@ i40evf_acquire_msix_vectors(struct i40evf_adapter *adapter, int vectors)
  **/
 static void i40evf_free_queues(struct i40evf_adapter *adapter)
 {
-       int i;
-
        if (!adapter->vsi_res)
                return;
-       for (i = 0; i < adapter->num_active_queues; i++) {
-               if (adapter->tx_rings[i])
-                       kfree_rcu(adapter->tx_rings[i], rcu);
-               adapter->tx_rings[i] = NULL;
-               adapter->rx_rings[i] = NULL;
-       }
+       kfree(adapter->tx_rings);
+       kfree(adapter->rx_rings);
 }
 
 /**
@@ -1136,13 +1137,20 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
 {
        int i;
 
+       adapter->tx_rings = kcalloc(adapter->num_active_queues,
+                                   sizeof(struct i40e_ring), GFP_KERNEL);
+       if (!adapter->tx_rings)
+               goto err_out;
+       adapter->rx_rings = kcalloc(adapter->num_active_queues,
+                                   sizeof(struct i40e_ring), GFP_KERNEL);
+       if (!adapter->rx_rings)
+               goto err_out;
+
        for (i = 0; i < adapter->num_active_queues; i++) {
                struct i40e_ring *tx_ring;
                struct i40e_ring *rx_ring;
 
-               tx_ring = kzalloc(sizeof(*tx_ring) * 2, GFP_KERNEL);
-               if (!tx_ring)
-                       goto err_out;
+               tx_ring = &adapter->tx_rings[i];
 
                tx_ring->queue_index = i;
                tx_ring->netdev = adapter->netdev;
@@ -1150,14 +1158,12 @@ static int i40evf_alloc_queues(struct i40evf_adapter *adapter)
                tx_ring->count = adapter->tx_desc_count;
                if (adapter->flags & I40E_FLAG_WB_ON_ITR_CAPABLE)
                        tx_ring->flags |= I40E_TXR_FLAGS_WB_ON_ITR;
-               adapter->tx_rings[i] = tx_ring;
 
-               rx_ring = &tx_ring[1];
+               rx_ring = &adapter->rx_rings[i];
                rx_ring->queue_index = i;
                rx_ring->netdev = adapter->netdev;
                rx_ring->dev = &adapter->pdev->dev;
                rx_ring->count = adapter->rx_desc_count;
-               adapter->rx_rings[i] = rx_ring;
        }
 
        return 0;
@@ -1207,115 +1213,273 @@ static int i40evf_set_interrupt_capability(struct i40evf_adapter *adapter)
        err = i40evf_acquire_msix_vectors(adapter, v_budget);
 
 out:
-       adapter->netdev->real_num_tx_queues = pairs;
+       netif_set_real_num_rx_queues(adapter->netdev, pairs);
+       netif_set_real_num_tx_queues(adapter->netdev, pairs);
        return err;
 }
 
 /**
- * i40e_configure_rss_aq - Prepare for RSS using AQ commands
+ * i40e_config_rss_aq - Prepare for RSS using AQ commands
  * @vsi: vsi structure
  * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Return 0 on success, negative on failure
  **/
-static void i40evf_configure_rss_aq(struct i40e_vsi *vsi, const u8 *seed)
+static int i40evf_config_rss_aq(struct i40e_vsi *vsi, const u8 *seed,
+                               u8 *lut, u16 lut_size)
 {
-       struct i40e_aqc_get_set_rss_key_data rss_key;
        struct i40evf_adapter *adapter = vsi->back;
        struct i40e_hw *hw = &adapter->hw;
-       int ret = 0, i;
-       u8 *rss_lut;
+       int ret = 0;
 
        if (!vsi->id)
-               return;
+               return -EINVAL;
 
        if (adapter->current_op != I40E_VIRTCHNL_OP_UNKNOWN) {
                /* bail because we already have a command pending */
                dev_err(&adapter->pdev->dev, "Cannot configure RSS, command %d pending\n",
                        adapter->current_op);
-               return;
+               return -EBUSY;
        }
 
-       memset(&rss_key, 0, sizeof(rss_key));
-       memcpy(&rss_key, seed, sizeof(rss_key));
+       if (seed) {
+               struct i40e_aqc_get_set_rss_key_data *rss_key =
+                       (struct i40e_aqc_get_set_rss_key_data *)seed;
+               ret = i40evf_aq_set_rss_key(hw, vsi->id, rss_key);
+               if (ret) {
+                       dev_err(&adapter->pdev->dev, "Cannot set RSS key, err %s aq_err %s\n",
+                               i40evf_stat_str(hw, ret),
+                               i40evf_aq_str(hw, hw->aq.asq_last_status));
+                       return ret;
+               }
+       }
 
-       rss_lut = kzalloc(((I40E_VFQF_HLUT_MAX_INDEX + 1) * 4), GFP_KERNEL);
-       if (!rss_lut)
-               return;
+       if (lut) {
+               ret = i40evf_aq_set_rss_lut(hw, vsi->id, false, lut, lut_size);
+               if (ret) {
+                       dev_err(&adapter->pdev->dev,
+                               "Cannot set RSS lut, err %s aq_err %s\n",
+                               i40evf_stat_str(hw, ret),
+                               i40evf_aq_str(hw, hw->aq.asq_last_status));
+                       return ret;
+               }
+       }
 
-       /* Populate the LUT with max no. PF queues in round robin fashion */
-       for (i = 0; i <= (I40E_VFQF_HLUT_MAX_INDEX * 4); i++)
-               rss_lut[i] = i % adapter->num_active_queues;
+       return ret;
+}
 
-       ret = i40evf_aq_set_rss_key(hw, vsi->id, &rss_key);
-       if (ret) {
-               dev_err(&adapter->pdev->dev,
-                       "Cannot set RSS key, err %s aq_err %s\n",
-                       i40evf_stat_str(hw, ret),
-                       i40evf_aq_str(hw, hw->aq.asq_last_status));
-               return;
+/**
+ * i40evf_config_rss_reg - Configure RSS keys and lut by writing registers
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+static int i40evf_config_rss_reg(struct i40e_vsi *vsi, const u8 *seed,
+                                const u8 *lut, u16 lut_size)
+{
+       struct i40evf_adapter *adapter = vsi->back;
+       struct i40e_hw *hw = &adapter->hw;
+       u16 i;
+
+       if (seed) {
+               u32 *seed_dw = (u32 *)seed;
+
+               for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
+                       wr32(hw, I40E_VFQF_HKEY(i), seed_dw[i]);
        }
 
-       ret = i40evf_aq_set_rss_lut(hw, vsi->id, false, rss_lut,
-                                   (I40E_VFQF_HLUT_MAX_INDEX + 1) * 4);
-       if (ret)
-               dev_err(&adapter->pdev->dev,
-                       "Cannot set RSS lut, err %s aq_err %s\n",
-                       i40evf_stat_str(hw, ret),
-                       i40evf_aq_str(hw, hw->aq.asq_last_status));
+       if (lut) {
+               u32 *lut_dw = (u32 *)lut;
+
+               if (lut_size != I40EVF_HLUT_ARRAY_SIZE)
+                       return -EINVAL;
+
+               for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++)
+                       wr32(hw, I40E_VFQF_HLUT(i), lut_dw[i]);
+       }
+       i40e_flush(hw);
+
+       return 0;
 }
 
 /**
- * i40e_configure_rss_reg - Prepare for RSS if used
- * @adapter: board private structure
- * @seed: RSS hash seed
+ *  * i40evf_get_rss_aq - Get RSS keys and lut by using AQ commands
+ *  @vsi: Pointer to vsi structure
+ *  @seed: RSS hash seed
+ *  @lut: Lookup table
+ *  @lut_size: Lookup table size
+ *
+ *  Return 0 on success, negative on failure
  **/
-static void i40evf_configure_rss_reg(struct i40evf_adapter *adapter,
-                                    const u8 *seed)
+static int i40evf_get_rss_aq(struct i40e_vsi *vsi, const u8 *seed,
+                            u8 *lut, u16 lut_size)
 {
+       struct i40evf_adapter *adapter = vsi->back;
        struct i40e_hw *hw = &adapter->hw;
-       u32 *seed_dw = (u32 *)seed;
-       u32 cqueue = 0;
-       u32 lut = 0;
-       int i, j;
+       int ret = 0;
 
-       /* Fill out hash function seed */
-       for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
-               wr32(hw, I40E_VFQF_HKEY(i), seed_dw[i]);
-
-       /* Populate the LUT with max no. PF queues in round robin fashion */
-       for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++) {
-               lut = 0;
-               for (j = 0; j < 4; j++) {
-                       if (cqueue == adapter->num_active_queues)
-                               cqueue = 0;
-                       lut |= ((cqueue) << (8 * j));
-                       cqueue++;
+       if (seed) {
+               ret = i40evf_aq_get_rss_key(hw, vsi->id,
+                       (struct i40e_aqc_get_set_rss_key_data *)seed);
+               if (ret) {
+                       dev_err(&adapter->pdev->dev,
+                               "Cannot get RSS key, err %s aq_err %s\n",
+                               i40evf_stat_str(hw, ret),
+                               i40evf_aq_str(hw, hw->aq.asq_last_status));
+                       return ret;
                }
-               wr32(hw, I40E_VFQF_HLUT(i), lut);
        }
-       i40e_flush(hw);
+
+       if (lut) {
+               ret = i40evf_aq_get_rss_lut(hw, vsi->id, seed, lut, lut_size);
+               if (ret) {
+                       dev_err(&adapter->pdev->dev,
+                               "Cannot get RSS lut, err %s aq_err %s\n",
+                               i40evf_stat_str(hw, ret),
+                               i40evf_aq_str(hw, hw->aq.asq_last_status));
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
+/**
+ *  * i40evf_get_rss_reg - Get RSS keys and lut by reading registers
+ *  @vsi: Pointer to vsi structure
+ *  @seed: RSS hash seed
+ *  @lut: Lookup table
+ *  @lut_size: Lookup table size
+ *
+ *  Returns 0 on success, negative on failure
+ **/
+static int i40evf_get_rss_reg(struct i40e_vsi *vsi, const u8 *seed,
+                             const u8 *lut, u16 lut_size)
+{
+       struct i40evf_adapter *adapter = vsi->back;
+       struct i40e_hw *hw = &adapter->hw;
+       u16 i;
+
+       if (seed) {
+               u32 *seed_dw = (u32 *)seed;
+
+               for (i = 0; i <= I40E_VFQF_HKEY_MAX_INDEX; i++)
+                       seed_dw[i] = rd32(hw, I40E_VFQF_HKEY(i));
+       }
+
+       if (lut) {
+               u32 *lut_dw = (u32 *)lut;
+
+               if (lut_size != I40EVF_HLUT_ARRAY_SIZE)
+                       return -EINVAL;
+
+               for (i = 0; i <= I40E_VFQF_HLUT_MAX_INDEX; i++)
+                       lut_dw[i] = rd32(hw, I40E_VFQF_HLUT(i));
+       }
+
+       return 0;
+}
+
+/**
+ * i40evf_config_rss - Configure RSS keys and lut
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+int i40evf_config_rss(struct i40e_vsi *vsi, const u8 *seed,
+                     u8 *lut, u16 lut_size)
+{
+       struct i40evf_adapter *adapter = vsi->back;
+
+       if (RSS_AQ(adapter))
+               return i40evf_config_rss_aq(vsi, seed, lut, lut_size);
+       else
+               return i40evf_config_rss_reg(vsi, seed, lut, lut_size);
+}
+
+/**
+ * i40evf_get_rss - Get RSS keys and lut
+ * @vsi: Pointer to vsi structure
+ * @seed: RSS hash seed
+ * @lut: Lookup table
+ * @lut_size: Lookup table size
+ *
+ * Returns 0 on success, negative on failure
+ **/
+int i40evf_get_rss(struct i40e_vsi *vsi, const u8 *seed, u8 *lut, u16 lut_size)
+{
+       struct i40evf_adapter *adapter = vsi->back;
+
+       if (RSS_AQ(adapter))
+               return i40evf_get_rss_aq(vsi, seed, lut, lut_size);
+       else
+               return i40evf_get_rss_reg(vsi, seed, lut, lut_size);
+}
+
+/**
+ * i40evf_fill_rss_lut - Fill the lut with default values
+ * @lut: Lookup table to be filled with
+ * @rss_table_size: Lookup table size
+ * @rss_size: Range of queue number for hashing
+ **/
+static void i40evf_fill_rss_lut(u8 *lut, u16 rss_table_size, u16 rss_size)
+{
+       u16 i;
+
+       for (i = 0; i < rss_table_size; i++)
+               lut[i] = i % rss_size;
 }
 
 /**
- * i40evf_configure_rss - Prepare for RSS
+ * i40evf_init_rss - Prepare for RSS
  * @adapter: board private structure
+ *
+ * Return 0 on success, negative on failure
  **/
-static void i40evf_configure_rss(struct i40evf_adapter *adapter)
+static int i40evf_init_rss(struct i40evf_adapter *adapter)
 {
+       struct i40e_vsi *vsi = &adapter->vsi;
        struct i40e_hw *hw = &adapter->hw;
        u8 seed[I40EVF_HKEY_ARRAY_SIZE];
        u64 hena;
-
-       netdev_rss_key_fill((void *)seed, I40EVF_HKEY_ARRAY_SIZE);
+       u8 *lut;
+       int ret;
 
        /* Enable PCTYPES for RSS, TCP/UDP with IPv4/IPv6 */
        hena = I40E_DEFAULT_RSS_HENA;
        wr32(hw, I40E_VFQF_HENA(0), (u32)hena);
        wr32(hw, I40E_VFQF_HENA(1), (u32)(hena >> 32));
 
-       if (RSS_AQ(adapter))
-               i40evf_configure_rss_aq(&adapter->vsi, seed);
+       lut = kzalloc(I40EVF_HLUT_ARRAY_SIZE, GFP_KERNEL);
+       if (!lut)
+               return -ENOMEM;
+
+       /* Use user configured lut if there is one, otherwise use default */
+       if (vsi->rss_lut_user)
+               memcpy(lut, vsi->rss_lut_user, I40EVF_HLUT_ARRAY_SIZE);
        else
-               i40evf_configure_rss_reg(adapter, seed);
+               i40evf_fill_rss_lut(lut, I40EVF_HLUT_ARRAY_SIZE,
+                                   adapter->num_active_queues);
+
+       /* Use user configured hash key if there is one, otherwise
+        * user default.
+        */
+       if (vsi->rss_hkey_user)
+               memcpy(seed, vsi->rss_hkey_user, I40EVF_HKEY_ARRAY_SIZE);
+       else
+               netdev_rss_key_fill((void *)seed, I40EVF_HKEY_ARRAY_SIZE);
+       ret = i40evf_config_rss(vsi, seed, lut, I40EVF_HLUT_ARRAY_SIZE);
+       kfree(lut);
+
+       return ret;
 }
 
 /**
@@ -1327,21 +1491,22 @@ static void i40evf_configure_rss(struct i40evf_adapter *adapter)
  **/
 static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter)
 {
-       int q_idx, num_q_vectors;
+       int q_idx = 0, num_q_vectors;
        struct i40e_q_vector *q_vector;
 
        num_q_vectors = adapter->num_msix_vectors - NONQ_VECS;
+       adapter->q_vectors = kcalloc(num_q_vectors, sizeof(*q_vector),
+                                    GFP_KERNEL);
+       if (!adapter->q_vectors)
+               goto err_out;
 
        for (q_idx = 0; q_idx < num_q_vectors; q_idx++) {
-               q_vector = kzalloc(sizeof(*q_vector), GFP_KERNEL);
-               if (!q_vector)
-                       goto err_out;
+               q_vector = &adapter->q_vectors[q_idx];
                q_vector->adapter = adapter;
                q_vector->vsi = &adapter->vsi;
                q_vector->v_idx = q_idx;
                netif_napi_add(adapter->netdev, &q_vector->napi,
                               i40evf_napi_poll, NAPI_POLL_WEIGHT);
-               adapter->q_vector[q_idx] = q_vector;
        }
 
        return 0;
@@ -1349,11 +1514,10 @@ static int i40evf_alloc_q_vectors(struct i40evf_adapter *adapter)
 err_out:
        while (q_idx) {
                q_idx--;
-               q_vector = adapter->q_vector[q_idx];
+               q_vector = &adapter->q_vectors[q_idx];
                netif_napi_del(&q_vector->napi);
-               kfree(q_vector);
-               adapter->q_vector[q_idx] = NULL;
        }
+       kfree(adapter->q_vectors);
        return -ENOMEM;
 }
 
@@ -1374,13 +1538,11 @@ static void i40evf_free_q_vectors(struct i40evf_adapter *adapter)
        napi_vectors = adapter->num_active_queues;
 
        for (q_idx = 0; q_idx < num_q_vectors; q_idx++) {
-               struct i40e_q_vector *q_vector = adapter->q_vector[q_idx];
-
-               adapter->q_vector[q_idx] = NULL;
+               struct i40e_q_vector *q_vector = &adapter->q_vectors[q_idx];
                if (q_idx < napi_vectors)
                        netif_napi_del(&q_vector->napi);
-               kfree(q_vector);
        }
+       kfree(adapter->q_vectors);
 }
 
 /**
@@ -1438,6 +1600,22 @@ err_set_interrupt:
        return err;
 }
 
+/**
+ * i40evf_clear_rss_config_user - Clear user configurations of RSS
+ * @vsi: Pointer to VSI structure
+ **/
+static void i40evf_clear_rss_config_user(struct i40e_vsi *vsi)
+{
+       if (!vsi)
+               return;
+
+       kfree(vsi->rss_hkey_user);
+       vsi->rss_hkey_user = NULL;
+
+       kfree(vsi->rss_lut_user);
+       vsi->rss_lut_user = NULL;
+}
+
 /**
  * i40evf_watchdog_timer - Periodic call-back timer
  * @data: pointer to adapter disguised as unsigned long
@@ -1565,7 +1743,7 @@ static void i40evf_watchdog_task(struct work_struct *work)
                 * PF, so we don't have to set current_op as we will
                 * not get a response through the ARQ.
                 */
-               i40evf_configure_rss(adapter);
+               i40evf_init_rss(adapter);
                adapter->aq_required &= ~I40EVF_FLAG_AQ_CONFIGURE_RSS;
                goto watchdog_done;
        }
@@ -1864,9 +2042,12 @@ void i40evf_free_all_tx_resources(struct i40evf_adapter *adapter)
 {
        int i;
 
+       if (!adapter->tx_rings)
+               return;
+
        for (i = 0; i < adapter->num_active_queues; i++)
-               if (adapter->tx_rings[i]->desc)
-                       i40evf_free_tx_resources(adapter->tx_rings[i]);
+               if (adapter->tx_rings[i].desc)
+                       i40evf_free_tx_resources(&adapter->tx_rings[i]);
 }
 
 /**
@@ -1884,8 +2065,8 @@ static int i40evf_setup_all_tx_resources(struct i40evf_adapter *adapter)
        int i, err = 0;
 
        for (i = 0; i < adapter->num_active_queues; i++) {
-               adapter->tx_rings[i]->count = adapter->tx_desc_count;
-               err = i40evf_setup_tx_descriptors(adapter->tx_rings[i]);
+               adapter->tx_rings[i].count = adapter->tx_desc_count;
+               err = i40evf_setup_tx_descriptors(&adapter->tx_rings[i]);
                if (!err)
                        continue;
                dev_err(&adapter->pdev->dev,
@@ -1911,8 +2092,8 @@ static int i40evf_setup_all_rx_resources(struct i40evf_adapter *adapter)
        int i, err = 0;
 
        for (i = 0; i < adapter->num_active_queues; i++) {
-               adapter->rx_rings[i]->count = adapter->rx_desc_count;
-               err = i40evf_setup_rx_descriptors(adapter->rx_rings[i]);
+               adapter->rx_rings[i].count = adapter->rx_desc_count;
+               err = i40evf_setup_rx_descriptors(&adapter->rx_rings[i]);
                if (!err)
                        continue;
                dev_err(&adapter->pdev->dev,
@@ -1932,9 +2113,12 @@ void i40evf_free_all_rx_resources(struct i40evf_adapter *adapter)
 {
        int i;
 
+       if (!adapter->rx_rings)
+               return;
+
        for (i = 0; i < adapter->num_active_queues; i++)
-               if (adapter->rx_rings[i]->desc)
-                       i40evf_free_rx_resources(adapter->rx_rings[i]);
+               if (adapter->rx_rings[i].desc)
+                       i40evf_free_rx_resources(&adapter->rx_rings[i]);
 }
 
 /**
@@ -2137,7 +2321,7 @@ int i40evf_process_config(struct i40evf_adapter *adapter)
        netdev->features |= NETIF_F_HIGHDMA |
                            NETIF_F_SG |
                            NETIF_F_IP_CSUM |
-                           NETIF_F_SCTP_CSUM |
+                           NETIF_F_SCTP_CRC |
                            NETIF_F_IPV6_CSUM |
                            NETIF_F_TSO |
                            NETIF_F_TSO6 |
@@ -2263,6 +2447,14 @@ static void i40evf_init_task(struct work_struct *work)
                if (err == I40E_ERR_ADMIN_QUEUE_NO_WORK) {
                        err = i40evf_send_vf_config_msg(adapter);
                        goto err;
+               } else if (err == I40E_ERR_PARAM) {
+                       /* We only get ERR_PARAM if the device is in a very bad
+                        * state or if we've been disabled for previous bad
+                        * behavior. Either way, we're done now.
+                        */
+                       i40evf_shutdown_adminq(hw);
+                       dev_err(&pdev->dev, "Unable to get VF config due to PF error condition, not retrying\n");
+                       return;
                }
                if (err) {
                        dev_err(&pdev->dev, "Unable to get VF config (%d)\n",
@@ -2313,7 +2505,7 @@ static void i40evf_init_task(struct work_struct *work)
                    I40E_VIRTCHNL_VF_OFFLOAD_WB_ON_ITR)
                adapter->flags |= I40EVF_FLAG_WB_ON_ITR_CAPABLE;
        if (!RSS_AQ(adapter))
-               i40evf_configure_rss(adapter);
+               i40evf_init_rss(adapter);
        err = i40evf_request_misc_irq(adapter);
        if (err)
                goto err_sw_init;
@@ -2334,7 +2526,6 @@ static void i40evf_init_task(struct work_struct *work)
        if (netdev->features & NETIF_F_GRO)
                dev_info(&pdev->dev, "GRO is enabled\n");
 
-       dev_info(&pdev->dev, "%s\n", i40evf_driver_string);
        adapter->state = __I40EVF_DOWN;
        set_bit(__I40E_DOWN, &adapter->vsi.state);
        i40evf_misc_irq_enable(adapter);
@@ -2343,7 +2534,7 @@ static void i40evf_init_task(struct work_struct *work)
                adapter->aq_required |= I40EVF_FLAG_AQ_CONFIGURE_RSS;
                mod_timer_pending(&adapter->watchdog_timer, jiffies + 1);
        } else {
-               i40evf_configure_rss(adapter);
+               i40evf_init_rss(adapter);
        }
        return;
 restart:
@@ -2438,8 +2629,7 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        pci_set_master(pdev);
 
-       netdev = alloc_etherdev_mq(sizeof(struct i40evf_adapter),
-                                  MAX_TX_QUEUES);
+       netdev = alloc_etherdev_mq(sizeof(struct i40evf_adapter), MAX_QUEUES);
        if (!netdev) {
                err = -ENOMEM;
                goto err_alloc_etherdev;
@@ -2476,6 +2666,12 @@ static int i40evf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
        hw->bus.device = PCI_SLOT(pdev->devfn);
        hw->bus.func = PCI_FUNC(pdev->devfn);
 
+       /* set up the locks for the AQ, do this only once in probe
+        * and destroy them only once in remove
+        */
+       mutex_init(&hw->aq.asq_mutex);
+       mutex_init(&hw->aq.arq_mutex);
+
        INIT_LIST_HEAD(&adapter->mac_filter_list);
        INIT_LIST_HEAD(&adapter->vlan_filter_list);
 
@@ -2626,9 +2822,16 @@ static void i40evf_remove(struct pci_dev *pdev)
 
        flush_scheduled_work();
 
+       /* Clear user configurations for RSS */
+       i40evf_clear_rss_config_user(&adapter->vsi);
+
        if (hw->aq.asq.count)
                i40evf_shutdown_adminq(hw);
 
+       /* destroy the locks only once, here */
+       mutex_destroy(&hw->aq.arq_mutex);
+       mutex_destroy(&hw->aq.asq_mutex);
+
        iounmap(hw->hw_addr);
        pci_release_regions(pdev);