*/
                        rx_rings[i].tail = hw->hw_addr + I40E_PRTGEN_STATUS;
                        err = i40e_setup_rx_descriptors(&rx_rings[i]);
-                       if (err)
-                               goto rx_unwind;
-                       err = i40e_alloc_rx_bi(&rx_rings[i]);
                        if (err)
                                goto rx_unwind;
 
 
        if (ring->vsi->type == I40E_VSI_MAIN)
                xdp_rxq_info_unreg_mem_model(&ring->xdp_rxq);
 
-       kfree(ring->rx_bi);
        ring->xsk_pool = i40e_xsk_pool(ring);
        if (ring->xsk_pool) {
-               ret = i40e_alloc_rx_bi_zc(ring);
-               if (ret)
-                       return ret;
                ring->rx_buf_len =
                  xsk_pool_get_rx_frame_size(ring->xsk_pool);
                /* For AF_XDP ZC, we disallow packets to span on
                         ring->queue_index);
 
        } else {
-               ret = i40e_alloc_rx_bi(ring);
-               if (ret)
-                       return ret;
                ring->rx_buf_len = vsi->rx_buf_len;
                if (ring->vsi->type == I40E_VSI_MAIN) {
                        ret = xdp_rxq_info_reg_mem_model(&ring->xdp_rxq,
                i40e_reset_and_rebuild(pf, true, true);
        }
 
+       if (!i40e_enabled_xdp_vsi(vsi) && prog) {
+               if (i40e_realloc_rx_bi_zc(vsi, true))
+                       return -ENOMEM;
+       } else if (i40e_enabled_xdp_vsi(vsi) && !prog) {
+               if (i40e_realloc_rx_bi_zc(vsi, false))
+                       return -ENOMEM;
+       }
+
        for (i = 0; i < vsi->num_queue_pairs; i++)
                WRITE_ONCE(vsi->rx_rings[i]->xdp_prog, vsi->xdp_prog);
 
 
        i40e_queue_pair_disable_irq(vsi, queue_pair);
        err = i40e_queue_pair_toggle_rings(vsi, queue_pair, false /* off */);
+       i40e_clean_rx_ring(vsi->rx_rings[queue_pair]);
        i40e_queue_pair_toggle_napi(vsi, queue_pair, false /* off */);
        i40e_queue_pair_clean_rings(vsi, queue_pair);
        i40e_queue_pair_reset_stats(vsi, queue_pair);
 
        return -ENOMEM;
 }
 
-int i40e_alloc_rx_bi(struct i40e_ring *rx_ring)
-{
-       unsigned long sz = sizeof(*rx_ring->rx_bi) * rx_ring->count;
-
-       rx_ring->rx_bi = kzalloc(sz, GFP_KERNEL);
-       return rx_ring->rx_bi ? 0 : -ENOMEM;
-}
-
 static void i40e_clear_rx_bi(struct i40e_ring *rx_ring)
 {
        memset(rx_ring->rx_bi, 0, sizeof(*rx_ring->rx_bi) * rx_ring->count);
 
        rx_ring->xdp_prog = rx_ring->vsi->xdp_prog;
 
+       rx_ring->rx_bi =
+               kcalloc(rx_ring->count, sizeof(*rx_ring->rx_bi), GFP_KERNEL);
+       if (!rx_ring->rx_bi)
+               return -ENOMEM;
+
        return 0;
 }
 
 
 bool __i40e_chk_linearize(struct sk_buff *skb);
 int i40e_xdp_xmit(struct net_device *dev, int n, struct xdp_frame **frames,
                  u32 flags);
-int i40e_alloc_rx_bi(struct i40e_ring *rx_ring);
 
 /**
  * i40e_get_head - Retrieve head from head writeback
 
 #include "i40e_txrx_common.h"
 #include "i40e_xsk.h"
 
-int i40e_alloc_rx_bi_zc(struct i40e_ring *rx_ring)
-{
-       unsigned long sz = sizeof(*rx_ring->rx_bi_zc) * rx_ring->count;
-
-       rx_ring->rx_bi_zc = kzalloc(sz, GFP_KERNEL);
-       return rx_ring->rx_bi_zc ? 0 : -ENOMEM;
-}
-
 void i40e_clear_rx_bi_zc(struct i40e_ring *rx_ring)
 {
        memset(rx_ring->rx_bi_zc, 0,
        return &rx_ring->rx_bi_zc[idx];
 }
 
+/**
+ * i40e_realloc_rx_xdp_bi - reallocate SW ring for either XSK or normal buffer
+ * @rx_ring: Current rx ring
+ * @pool_present: is pool for XSK present
+ *
+ * Try allocating memory and return ENOMEM, if failed to allocate.
+ * If allocation was successful, substitute buffer with allocated one.
+ * Returns 0 on success, negative on failure
+ */
+static int i40e_realloc_rx_xdp_bi(struct i40e_ring *rx_ring, bool pool_present)
+{
+       size_t elem_size = pool_present ? sizeof(*rx_ring->rx_bi_zc) :
+                                         sizeof(*rx_ring->rx_bi);
+       void *sw_ring = kcalloc(rx_ring->count, elem_size, GFP_KERNEL);
+
+       if (!sw_ring)
+               return -ENOMEM;
+
+       if (pool_present) {
+               kfree(rx_ring->rx_bi);
+               rx_ring->rx_bi = NULL;
+               rx_ring->rx_bi_zc = sw_ring;
+       } else {
+               kfree(rx_ring->rx_bi_zc);
+               rx_ring->rx_bi_zc = NULL;
+               rx_ring->rx_bi = sw_ring;
+       }
+       return 0;
+}
+
+/**
+ * i40e_realloc_rx_bi_zc - reallocate rx SW rings
+ * @vsi: Current VSI
+ * @zc: is zero copy set
+ *
+ * Reallocate buffer for rx_rings that might be used by XSK.
+ * XDP requires more memory, than rx_buf provides.
+ * Returns 0 on success, negative on failure
+ */
+int i40e_realloc_rx_bi_zc(struct i40e_vsi *vsi, bool zc)
+{
+       struct i40e_ring *rx_ring;
+       unsigned long q;
+
+       for_each_set_bit(q, vsi->af_xdp_zc_qps, vsi->alloc_queue_pairs) {
+               rx_ring = vsi->rx_rings[q];
+               if (i40e_realloc_rx_xdp_bi(rx_ring, zc))
+                       return -ENOMEM;
+       }
+       return 0;
+}
+
 /**
  * i40e_xsk_pool_enable - Enable/associate an AF_XDP buffer pool to a
  * certain ring/qid
                if (err)
                        return err;
 
+               err = i40e_realloc_rx_xdp_bi(vsi->rx_rings[qid], true);
+               if (err)
+                       return err;
+
                err = i40e_queue_pair_enable(vsi, qid);
                if (err)
                        return err;
        xsk_pool_dma_unmap(pool, I40E_RX_DMA_ATTR);
 
        if (if_running) {
+               err = i40e_realloc_rx_xdp_bi(vsi->rx_rings[qid], false);
+               if (err)
+                       return err;
                err = i40e_queue_pair_enable(vsi, qid);
                if (err)
                        return err;
 
 
 bool i40e_clean_xdp_tx_irq(struct i40e_vsi *vsi, struct i40e_ring *tx_ring);
 int i40e_xsk_wakeup(struct net_device *dev, u32 queue_id, u32 flags);
-int i40e_alloc_rx_bi_zc(struct i40e_ring *rx_ring);
+int i40e_realloc_rx_bi_zc(struct i40e_vsi *vsi, bool zc);
 void i40e_clear_rx_bi_zc(struct i40e_ring *rx_ring);
 
 #endif /* _I40E_XSK_H_ */