Imported Upstream version 16.07-rc1
[deb_dpdk.git] / drivers / net / mlx5 / mlx5_rxtx.c
index 9d1380a..0c352f3 100644 (file)
@@ -42,6 +42,8 @@
 #pragma GCC diagnostic ignored "-pedantic"
 #endif
 #include <infiniband/verbs.h>
+#include <infiniband/mlx5_hw.h>
+#include <infiniband/arch.h>
 #ifdef PEDANTIC
 #pragma GCC diagnostic error "-pedantic"
 #endif
@@ -55,7 +57,7 @@
 #include <rte_prefetch.h>
 #include <rte_common.h>
 #include <rte_branch_prediction.h>
-#include <rte_memory.h>
+#include <rte_ether.h>
 #ifdef PEDANTIC
 #pragma GCC diagnostic error "-pedantic"
 #endif
 #include "mlx5_rxtx.h"
 #include "mlx5_autoconf.h"
 #include "mlx5_defs.h"
+#include "mlx5_prm.h"
+
+#ifndef NDEBUG
 
 /**
- * Manage TX completions.
- *
- * When sending a burst, mlx5_tx_burst() posts several WRs.
- * To improve performance, a completion event is only required once every
- * MLX5_PMD_TX_PER_COMP_REQ sends. Doing so discards completion information
- * for other WRs, but this information would not be used anyway.
+ * Verify or set magic value in CQE.
  *
- * @param txq
- *   Pointer to TX queue structure.
+ * @param cqe
+ *   Pointer to CQE.
  *
  * @return
- *   0 on success, -1 on failure.
+ *   0 the first time.
  */
-static int
-txq_complete(struct txq *txq)
+static inline int
+check_cqe64_seen(volatile struct mlx5_cqe64 *cqe)
 {
-       unsigned int elts_comp = txq->elts_comp;
-       unsigned int elts_tail = txq->elts_tail;
-       unsigned int elts_free = txq->elts_tail;
-       const unsigned int elts_n = txq->elts_n;
-       int wcs_n;
+       static const uint8_t magic[] = "seen";
+       volatile uint8_t (*buf)[sizeof(cqe->rsvd40)] = &cqe->rsvd40;
+       int ret = 1;
+       unsigned int i;
 
-       if (unlikely(elts_comp == 0))
-               return 0;
-#ifdef DEBUG_SEND
-       DEBUG("%p: processing %u work requests completions",
-             (void *)txq, elts_comp);
-#endif
-       wcs_n = txq->poll_cnt(txq->cq, elts_comp);
-       if (unlikely(wcs_n == 0))
-               return 0;
-       if (unlikely(wcs_n < 0)) {
-               DEBUG("%p: ibv_poll_cq() failed (wcs_n=%d)",
-                     (void *)txq, wcs_n);
-               return -1;
-       }
-       elts_comp -= wcs_n;
-       assert(elts_comp <= txq->elts_comp);
-       /*
-        * Assume WC status is successful as nothing can be done about it
-        * anyway.
-        */
-       elts_tail += wcs_n * txq->elts_comp_cd_init;
-       if (elts_tail >= elts_n)
-               elts_tail -= elts_n;
+       for (i = 0; i < sizeof(magic) && i < sizeof(*buf); ++i)
+               if (!ret || (*buf)[i] != magic[i]) {
+                       ret = 0;
+                       (*buf)[i] = magic[i];
+               }
+       return ret;
+}
 
-       while (elts_free != elts_tail) {
-               struct txq_elt *elt = &(*txq->elts)[elts_free];
-               unsigned int elts_free_next =
-                       (((elts_free + 1) == elts_n) ? 0 : elts_free + 1);
-               struct rte_mbuf *tmp = elt->buf;
-               struct txq_elt *elt_next = &(*txq->elts)[elts_free_next];
+#endif /* NDEBUG */
+
+static inline int
+check_cqe64(volatile struct mlx5_cqe64 *cqe,
+           unsigned int cqes_n, const uint16_t ci)
+           __attribute__((always_inline));
+
+/**
+ * Check whether CQE is valid.
+ *
+ * @param cqe
+ *   Pointer to CQE.
+ * @param cqes_n
+ *   Size of completion queue.
+ * @param ci
+ *   Consumer index.
+ *
+ * @return
+ *   0 on success, 1 on failure.
+ */
+static inline int
+check_cqe64(volatile struct mlx5_cqe64 *cqe,
+               unsigned int cqes_n, const uint16_t ci)
+{
+       uint16_t idx = ci & cqes_n;
+       uint8_t op_own = cqe->op_own;
+       uint8_t op_owner = MLX5_CQE_OWNER(op_own);
+       uint8_t op_code = MLX5_CQE_OPCODE(op_own);
 
+       if (unlikely((op_owner != (!!(idx))) || (op_code == MLX5_CQE_INVALID)))
+               return 1; /* No CQE. */
 #ifndef NDEBUG
-               /* Poisoning. */
-               memset(elt, 0x66, sizeof(*elt));
-#endif
-               RTE_MBUF_PREFETCH_TO_FREE(elt_next->buf);
-               /* Faster than rte_pktmbuf_free(). */
-               do {
-                       struct rte_mbuf *next = NEXT(tmp);
+       if ((op_code == MLX5_CQE_RESP_ERR) ||
+           (op_code == MLX5_CQE_REQ_ERR)) {
+               volatile struct mlx5_err_cqe *err_cqe = (volatile void *)cqe;
+               uint8_t syndrome = err_cqe->syndrome;
 
-                       rte_pktmbuf_free_seg(tmp);
-                       tmp = next;
-               } while (tmp != NULL);
-               elts_free = elts_free_next;
+               if ((syndrome == MLX5_CQE_SYNDROME_LOCAL_LENGTH_ERR) ||
+                   (syndrome == MLX5_CQE_SYNDROME_REMOTE_ABORTED_ERR))
+                       return 0;
+               if (!check_cqe64_seen(cqe))
+                       ERROR("unexpected CQE error %u (0x%02x)"
+                             " syndrome 0x%02x",
+                             op_code, op_code, syndrome);
+               return 1;
+       } else if ((op_code != MLX5_CQE_RESP_SEND) &&
+                  (op_code != MLX5_CQE_REQ)) {
+               if (!check_cqe64_seen(cqe))
+                       ERROR("unexpected CQE opcode %u (0x%02x)",
+                             op_code, op_code);
+               return 1;
        }
-
-       txq->elts_tail = elts_tail;
-       txq->elts_comp = elts_comp;
+#endif /* NDEBUG */
        return 0;
 }
 
-/* For best performance, this function should not be inlined. */
-struct ibv_mr *mlx5_mp2mr(struct ibv_pd *, const struct rte_mempool *)
-       __attribute__((noinline));
-
 /**
- * Register mempool as a memory region.
+ * Manage TX completions.
  *
- * @param pd
- *   Pointer to protection domain.
- * @param mp
- *   Pointer to memory pool.
+ * When sending a burst, mlx5_tx_burst() posts several WRs.
  *
- * @return
- *   Memory region pointer, NULL in case of error.
+ * @param txq
+ *   Pointer to TX queue structure.
  */
-struct ibv_mr *
-mlx5_mp2mr(struct ibv_pd *pd, const struct rte_mempool *mp)
+static void
+txq_complete(struct txq *txq)
 {
-       const struct rte_memseg *ms = rte_eal_get_physmem_layout();
-       uintptr_t start = mp->elt_va_start;
-       uintptr_t end = mp->elt_va_end;
-       unsigned int i;
+       const unsigned int elts_n = txq->elts_n;
+       const unsigned int cqe_n = txq->cqe_n;
+       const unsigned int cqe_cnt = cqe_n - 1;
+       uint16_t elts_free = txq->elts_tail;
+       uint16_t elts_tail;
+       uint16_t cq_ci = txq->cq_ci;
+       volatile struct mlx5_cqe64 *cqe = NULL;
+       volatile union mlx5_wqe *wqe;
+
+       do {
+               volatile struct mlx5_cqe64 *tmp;
 
-       DEBUG("mempool %p area start=%p end=%p size=%zu",
-             (const void *)mp, (void *)start, (void *)end,
-             (size_t)(end - start));
-       /* Round start and end to page boundary if found in memory segments. */
-       for (i = 0; (i < RTE_MAX_MEMSEG) && (ms[i].addr != NULL); ++i) {
-               uintptr_t addr = (uintptr_t)ms[i].addr;
-               size_t len = ms[i].len;
-               unsigned int align = ms[i].hugepage_sz;
-
-               if ((start > addr) && (start < addr + len))
-                       start = RTE_ALIGN_FLOOR(start, align);
-               if ((end > addr) && (end < addr + len))
-                       end = RTE_ALIGN_CEIL(end, align);
+               tmp = &(*txq->cqes)[cq_ci & cqe_cnt].cqe64;
+               if (check_cqe64(tmp, cqe_n, cq_ci))
+                       break;
+               cqe = tmp;
+#ifndef NDEBUG
+               if (MLX5_CQE_FORMAT(cqe->op_own) == MLX5_COMPRESSED) {
+                       if (!check_cqe64_seen(cqe))
+                               ERROR("unexpected compressed CQE, TX stopped");
+                       return;
+               }
+               if ((MLX5_CQE_OPCODE(cqe->op_own) == MLX5_CQE_RESP_ERR) ||
+                   (MLX5_CQE_OPCODE(cqe->op_own) == MLX5_CQE_REQ_ERR)) {
+                       if (!check_cqe64_seen(cqe))
+                               ERROR("unexpected error CQE, TX stopped");
+                       return;
+               }
+#endif /* NDEBUG */
+               ++cq_ci;
+       } while (1);
+       if (unlikely(cqe == NULL))
+               return;
+       wqe = &(*txq->wqes)[htons(cqe->wqe_counter) & (txq->wqe_n - 1)];
+       elts_tail = wqe->wqe.ctrl.data[3];
+       assert(elts_tail < txq->wqe_n);
+       /* Free buffers. */
+       while (elts_free != elts_tail) {
+               struct rte_mbuf *elt = (*txq->elts)[elts_free];
+               unsigned int elts_free_next =
+                       (elts_free + 1) & (elts_n - 1);
+               struct rte_mbuf *elt_next = (*txq->elts)[elts_free_next];
+
+#ifndef NDEBUG
+               /* Poisoning. */
+               memset(&(*txq->elts)[elts_free],
+                      0x66,
+                      sizeof((*txq->elts)[elts_free]));
+#endif
+               RTE_MBUF_PREFETCH_TO_FREE(elt_next);
+               /* Only one segment needs to be freed. */
+               rte_pktmbuf_free_seg(elt);
+               elts_free = elts_free_next;
        }
-       DEBUG("mempool %p using start=%p end=%p size=%zu for MR",
-             (const void *)mp, (void *)start, (void *)end,
-             (size_t)(end - start));
-       return ibv_reg_mr(pd,
-                         (void *)start,
-                         end - start,
-                         IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE);
+       txq->cq_ci = cq_ci;
+       txq->elts_tail = elts_tail;
+       /* Update the consumer index. */
+       rte_wmb();
+       *txq->cq_db = htonl(cq_ci);
 }
 
 /**
@@ -204,6 +242,10 @@ txq_mb2mp(struct rte_mbuf *buf)
        return buf->pool;
 }
 
+static inline uint32_t
+txq_mp2mr(struct txq *txq, struct rte_mempool *mp)
+       __attribute__((always_inline));
+
 /**
  * Get Memory Region (MR) <-> Memory Pool (MP) association from txq->mp2mr[].
  * Add MP to txq->mp2mr[] if it's not registered yet. If mp2mr[] is full,
@@ -217,11 +259,11 @@ txq_mb2mp(struct rte_mbuf *buf)
  * @return
  *   mr->lkey on success, (uint32_t)-1 on failure.
  */
-static uint32_t
-txq_mp2mr(struct txq *txq, const struct rte_mempool *mp)
+static inline uint32_t
+txq_mp2mr(struct txq *txq, struct rte_mempool *mp)
 {
        unsigned int i;
-       struct ibv_mr *mr;
+       uint32_t lkey = (uint32_t)-1;
 
        for (i = 0; (i != RTE_DIM(txq->mp2mr)); ++i) {
                if (unlikely(txq->mp2mr[i].mp == NULL)) {
@@ -230,295 +272,681 @@ txq_mp2mr(struct txq *txq, const struct rte_mempool *mp)
                }
                if (txq->mp2mr[i].mp == mp) {
                        assert(txq->mp2mr[i].lkey != (uint32_t)-1);
-                       assert(txq->mp2mr[i].mr->lkey == txq->mp2mr[i].lkey);
-                       return txq->mp2mr[i].lkey;
+                       assert(htonl(txq->mp2mr[i].mr->lkey) ==
+                              txq->mp2mr[i].lkey);
+                       lkey = txq->mp2mr[i].lkey;
+                       break;
                }
        }
-       /* Add a new entry, register MR first. */
-       DEBUG("%p: discovered new memory pool \"%s\" (%p)",
-             (void *)txq, mp->name, (const void *)mp);
-       mr = mlx5_mp2mr(txq->priv->pd, mp);
-       if (unlikely(mr == NULL)) {
-               DEBUG("%p: unable to configure MR, ibv_reg_mr() failed.",
-                     (void *)txq);
-               return (uint32_t)-1;
-       }
-       if (unlikely(i == RTE_DIM(txq->mp2mr))) {
-               /* Table is full, remove oldest entry. */
-               DEBUG("%p: MR <-> MP table full, dropping oldest entry.",
-                     (void *)txq);
-               --i;
-               claim_zero(ibv_dereg_mr(txq->mp2mr[0].mr));
-               memmove(&txq->mp2mr[0], &txq->mp2mr[1],
-                       (sizeof(txq->mp2mr) - sizeof(txq->mp2mr[0])));
-       }
-       /* Store the new entry. */
-       txq->mp2mr[i].mp = mp;
-       txq->mp2mr[i].mr = mr;
-       txq->mp2mr[i].lkey = mr->lkey;
-       DEBUG("%p: new MR lkey for MP \"%s\" (%p): 0x%08" PRIu32,
-             (void *)txq, mp->name, (const void *)mp, txq->mp2mr[i].lkey);
-       return txq->mp2mr[i].lkey;
+       if (unlikely(lkey == (uint32_t)-1))
+               lkey = txq_mp2mr_reg(txq, mp, i);
+       return lkey;
 }
 
-struct txq_mp2mr_mbuf_check_data {
-       const struct rte_mempool *mp;
-       int ret;
-};
-
 /**
- * Callback function for rte_mempool_obj_iter() to check whether a given
- * mempool object looks like a mbuf.
- *
- * @param[in, out] arg
- *   Context data (struct txq_mp2mr_mbuf_check_data). Contains mempool pointer
- *   and return value.
- * @param[in] start
- *   Object start address.
- * @param[in] end
- *   Object end address.
- * @param index
- *   Unused.
+ * Write a regular WQE.
  *
- * @return
- *   Nonzero value when object is not a mbuf.
+ * @param txq
+ *   Pointer to TX queue structure.
+ * @param wqe
+ *   Pointer to the WQE to fill.
+ * @param addr
+ *   Buffer data address.
+ * @param length
+ *   Packet length.
+ * @param lkey
+ *   Memory region lkey.
  */
-static void
-txq_mp2mr_mbuf_check(void *arg, void *start, void *end,
-                    uint32_t index __rte_unused)
+static inline void
+mlx5_wqe_write(struct txq *txq, volatile union mlx5_wqe *wqe,
+              uintptr_t addr, uint32_t length, uint32_t lkey)
 {
-       struct txq_mp2mr_mbuf_check_data *data = arg;
-       struct rte_mbuf *buf =
-               (void *)((uintptr_t)start + data->mp->header_size);
-
-       (void)index;
-       /* Check whether mbuf structure fits element size and whether mempool
-        * pointer is valid. */
-       if (((uintptr_t)end >= (uintptr_t)(buf + 1)) &&
-           (buf->pool == data->mp))
-               data->ret = 0;
-       else
-               data->ret = -1;
+       wqe->wqe.ctrl.data[0] = htonl((txq->wqe_ci << 8) | MLX5_OPCODE_SEND);
+       wqe->wqe.ctrl.data[1] = htonl((txq->qp_num_8s) | 4);
+       wqe->wqe.ctrl.data[2] = 0;
+       wqe->wqe.ctrl.data[3] = 0;
+       wqe->inl.eseg.rsvd0 = 0;
+       wqe->inl.eseg.rsvd1 = 0;
+       wqe->inl.eseg.mss = 0;
+       wqe->inl.eseg.rsvd2 = 0;
+       wqe->wqe.eseg.inline_hdr_sz = htons(MLX5_ETH_INLINE_HEADER_SIZE);
+       /* Copy the first 16 bytes into inline header. */
+       rte_memcpy((uint8_t *)(uintptr_t)wqe->wqe.eseg.inline_hdr_start,
+                  (uint8_t *)(uintptr_t)addr,
+                  MLX5_ETH_INLINE_HEADER_SIZE);
+       addr += MLX5_ETH_INLINE_HEADER_SIZE;
+       length -= MLX5_ETH_INLINE_HEADER_SIZE;
+       /* Store remaining data in data segment. */
+       wqe->wqe.dseg.byte_count = htonl(length);
+       wqe->wqe.dseg.lkey = lkey;
+       wqe->wqe.dseg.addr = htonll(addr);
+       /* Increment consumer index. */
+       ++txq->wqe_ci;
 }
 
 /**
- * Iterator function for rte_mempool_walk() to register existing mempools and
- * fill the MP to MR cache of a TX queue.
+ * Write a regular WQE with VLAN.
  *
- * @param[in] mp
- *   Memory Pool to register.
- * @param *arg
+ * @param txq
  *   Pointer to TX queue structure.
+ * @param wqe
+ *   Pointer to the WQE to fill.
+ * @param addr
+ *   Buffer data address.
+ * @param length
+ *   Packet length.
+ * @param lkey
+ *   Memory region lkey.
+ * @param vlan_tci
+ *   VLAN field to insert in packet.
  */
-void
-txq_mp2mr_iter(const struct rte_mempool *mp, void *arg)
+static inline void
+mlx5_wqe_write_vlan(struct txq *txq, volatile union mlx5_wqe *wqe,
+                   uintptr_t addr, uint32_t length, uint32_t lkey,
+                   uint16_t vlan_tci)
 {
-       struct txq *txq = arg;
-       struct txq_mp2mr_mbuf_check_data data = {
-               .mp = mp,
-               .ret = -1,
-       };
+       uint32_t vlan = htonl(0x81000000 | vlan_tci);
 
-       /* Discard empty mempools. */
-       if (mp->size == 0)
-               return;
-       /* Register mempool only if the first element looks like a mbuf. */
-       rte_mempool_obj_iter((void *)mp->elt_va_start,
-                            1,
-                            mp->header_size + mp->elt_size + mp->trailer_size,
-                            1,
-                            mp->elt_pa,
-                            mp->pg_num,
-                            mp->pg_shift,
-                            txq_mp2mr_mbuf_check,
-                            &data);
-       if (data.ret)
-               return;
-       txq_mp2mr(txq, mp);
+       wqe->wqe.ctrl.data[0] = htonl((txq->wqe_ci << 8) | MLX5_OPCODE_SEND);
+       wqe->wqe.ctrl.data[1] = htonl((txq->qp_num_8s) | 4);
+       wqe->wqe.ctrl.data[2] = 0;
+       wqe->wqe.ctrl.data[3] = 0;
+       wqe->inl.eseg.rsvd0 = 0;
+       wqe->inl.eseg.rsvd1 = 0;
+       wqe->inl.eseg.mss = 0;
+       wqe->inl.eseg.rsvd2 = 0;
+       wqe->wqe.eseg.inline_hdr_sz = htons(MLX5_ETH_VLAN_INLINE_HEADER_SIZE);
+       /*
+        * Copy 12 bytes of source & destination MAC address.
+        * Copy 4 bytes of VLAN.
+        * Copy 2 bytes of Ether type.
+        */
+       rte_memcpy((uint8_t *)(uintptr_t)wqe->wqe.eseg.inline_hdr_start,
+                  (uint8_t *)(uintptr_t)addr, 12);
+       rte_memcpy((uint8_t *)((uintptr_t)wqe->wqe.eseg.inline_hdr_start + 12),
+                  &vlan, sizeof(vlan));
+       rte_memcpy((uint8_t *)((uintptr_t)wqe->wqe.eseg.inline_hdr_start + 16),
+                  (uint8_t *)((uintptr_t)addr + 12), 2);
+       addr += MLX5_ETH_VLAN_INLINE_HEADER_SIZE - sizeof(vlan);
+       length -= MLX5_ETH_VLAN_INLINE_HEADER_SIZE - sizeof(vlan);
+       /* Store remaining data in data segment. */
+       wqe->wqe.dseg.byte_count = htonl(length);
+       wqe->wqe.dseg.lkey = lkey;
+       wqe->wqe.dseg.addr = htonll(addr);
+       /* Increment consumer index. */
+       ++txq->wqe_ci;
 }
 
 /**
- * Insert VLAN using mbuf headroom space.
+ * Write a inline WQE.
  *
- * @param buf
- *   Buffer for VLAN insertion.
+ * @param txq
+ *   Pointer to TX queue structure.
+ * @param wqe
+ *   Pointer to the WQE to fill.
+ * @param addr
+ *   Buffer data address.
+ * @param length
+ *   Packet length.
+ * @param lkey
+ *   Memory region lkey.
+ */
+static inline void
+mlx5_wqe_write_inline(struct txq *txq, volatile union mlx5_wqe *wqe,
+                     uintptr_t addr, uint32_t length)
+{
+       uint32_t size;
+       uint16_t wqe_cnt = txq->wqe_n - 1;
+       uint16_t wqe_ci = txq->wqe_ci + 1;
+
+       /* Copy the first 16 bytes into inline header. */
+       rte_memcpy((void *)(uintptr_t)wqe->inl.eseg.inline_hdr_start,
+                  (void *)(uintptr_t)addr,
+                  MLX5_ETH_INLINE_HEADER_SIZE);
+       addr += MLX5_ETH_INLINE_HEADER_SIZE;
+       length -= MLX5_ETH_INLINE_HEADER_SIZE;
+       size = 3 + ((4 + length + 15) / 16);
+       wqe->inl.byte_cnt = htonl(length | MLX5_INLINE_SEG);
+       rte_memcpy((void *)(uintptr_t)&wqe->inl.data[0],
+                  (void *)addr, MLX5_WQE64_INL_DATA);
+       addr += MLX5_WQE64_INL_DATA;
+       length -= MLX5_WQE64_INL_DATA;
+       while (length) {
+               volatile union mlx5_wqe *wqe_next =
+                       &(*txq->wqes)[wqe_ci & wqe_cnt];
+               uint32_t copy_bytes = (length > sizeof(*wqe)) ?
+                                     sizeof(*wqe) :
+                                     length;
+
+               rte_mov64((uint8_t *)(uintptr_t)&wqe_next->data[0],
+                         (uint8_t *)addr);
+               addr += copy_bytes;
+               length -= copy_bytes;
+               ++wqe_ci;
+       }
+       assert(size < 64);
+       wqe->inl.ctrl.data[0] = htonl((txq->wqe_ci << 8) | MLX5_OPCODE_SEND);
+       wqe->inl.ctrl.data[1] = htonl(txq->qp_num_8s | size);
+       wqe->inl.ctrl.data[2] = 0;
+       wqe->inl.ctrl.data[3] = 0;
+       wqe->inl.eseg.rsvd0 = 0;
+       wqe->inl.eseg.rsvd1 = 0;
+       wqe->inl.eseg.mss = 0;
+       wqe->inl.eseg.rsvd2 = 0;
+       wqe->inl.eseg.inline_hdr_sz = htons(MLX5_ETH_INLINE_HEADER_SIZE);
+       /* Increment consumer index. */
+       txq->wqe_ci = wqe_ci;
+}
+
+/**
+ * Write a inline WQE with VLAN.
  *
- * @return
- *   0 on success, errno value on failure.
+ * @param txq
+ *   Pointer to TX queue structure.
+ * @param wqe
+ *   Pointer to the WQE to fill.
+ * @param addr
+ *   Buffer data address.
+ * @param length
+ *   Packet length.
+ * @param lkey
+ *   Memory region lkey.
+ * @param vlan_tci
+ *   VLAN field to insert in packet.
  */
-static inline int
-insert_vlan_sw(struct rte_mbuf *buf)
+static inline void
+mlx5_wqe_write_inline_vlan(struct txq *txq, volatile union mlx5_wqe *wqe,
+                          uintptr_t addr, uint32_t length, uint16_t vlan_tci)
 {
-       uintptr_t addr;
-       uint32_t vlan;
-       uint16_t head_room_len = rte_pktmbuf_headroom(buf);
+       uint32_t size;
+       uint32_t wqe_cnt = txq->wqe_n - 1;
+       uint16_t wqe_ci = txq->wqe_ci + 1;
+       uint32_t vlan = htonl(0x81000000 | vlan_tci);
 
-       if (head_room_len < 4)
-               return EINVAL;
+       /*
+        * Copy 12 bytes of source & destination MAC address.
+        * Copy 4 bytes of VLAN.
+        * Copy 2 bytes of Ether type.
+        */
+       rte_memcpy((uint8_t *)(uintptr_t)wqe->inl.eseg.inline_hdr_start,
+                  (uint8_t *)addr, 12);
+       rte_memcpy((uint8_t *)(uintptr_t)wqe->inl.eseg.inline_hdr_start + 12,
+                  &vlan, sizeof(vlan));
+       rte_memcpy((uint8_t *)(uintptr_t)wqe->inl.eseg.inline_hdr_start + 16,
+                  ((uint8_t *)addr + 12), 2);
+       addr += MLX5_ETH_VLAN_INLINE_HEADER_SIZE - sizeof(vlan);
+       length -= MLX5_ETH_VLAN_INLINE_HEADER_SIZE - sizeof(vlan);
+       size = (sizeof(wqe->inl.ctrl.ctrl) +
+               sizeof(wqe->inl.eseg) +
+               sizeof(wqe->inl.byte_cnt) +
+               length + 15) / 16;
+       wqe->inl.byte_cnt = htonl(length | MLX5_INLINE_SEG);
+       rte_memcpy((void *)(uintptr_t)&wqe->inl.data[0],
+                  (void *)addr, MLX5_WQE64_INL_DATA);
+       addr += MLX5_WQE64_INL_DATA;
+       length -= MLX5_WQE64_INL_DATA;
+       while (length) {
+               volatile union mlx5_wqe *wqe_next =
+                       &(*txq->wqes)[wqe_ci & wqe_cnt];
+               uint32_t copy_bytes = (length > sizeof(*wqe)) ?
+                                     sizeof(*wqe) :
+                                     length;
 
-       addr = rte_pktmbuf_mtod(buf, uintptr_t);
-       vlan = htonl(0x81000000 | buf->vlan_tci);
-       memmove((void *)(addr - 4), (void *)addr, 12);
-       memcpy((void *)(addr + 8), &vlan, sizeof(vlan));
+               rte_mov64((uint8_t *)(uintptr_t)&wqe_next->data[0],
+                         (uint8_t *)addr);
+               addr += copy_bytes;
+               length -= copy_bytes;
+               ++wqe_ci;
+       }
+       assert(size < 64);
+       wqe->inl.ctrl.data[0] = htonl((txq->wqe_ci << 8) | MLX5_OPCODE_SEND);
+       wqe->inl.ctrl.data[1] = htonl(txq->qp_num_8s | size);
+       wqe->inl.ctrl.data[2] = 0;
+       wqe->inl.ctrl.data[3] = 0;
+       wqe->inl.eseg.rsvd0 = 0;
+       wqe->inl.eseg.rsvd1 = 0;
+       wqe->inl.eseg.mss = 0;
+       wqe->inl.eseg.rsvd2 = 0;
+       wqe->inl.eseg.inline_hdr_sz = htons(MLX5_ETH_VLAN_INLINE_HEADER_SIZE);
+       /* Increment consumer index. */
+       txq->wqe_ci = wqe_ci;
+}
+
+/**
+ * Ring TX queue doorbell.
+ *
+ * @param txq
+ *   Pointer to TX queue structure.
+ */
+static inline void
+mlx5_tx_dbrec(struct txq *txq)
+{
+       uint8_t *dst = (uint8_t *)((uintptr_t)txq->bf_reg + txq->bf_offset);
+       uint32_t data[4] = {
+               htonl((txq->wqe_ci << 8) | MLX5_OPCODE_SEND),
+               htonl(txq->qp_num_8s),
+               0,
+               0,
+       };
+       rte_wmb();
+       *txq->qp_db = htonl(txq->wqe_ci);
+       /* Ensure ordering between DB record and BF copy. */
+       rte_wmb();
+       rte_mov16(dst, (uint8_t *)data);
+       txq->bf_offset ^= txq->bf_buf_size;
+}
 
-       SET_DATA_OFF(buf, head_room_len - 4);
-       DATA_LEN(buf) += 4;
+/**
+ * Prefetch a CQE.
+ *
+ * @param txq
+ *   Pointer to TX queue structure.
+ * @param cqe_ci
+ *   CQE consumer index.
+ */
+static inline void
+tx_prefetch_cqe(struct txq *txq, uint16_t ci)
+{
+       volatile struct mlx5_cqe64 *cqe;
 
-       return 0;
+       cqe = &(*txq->cqes)[ci & (txq->cqe_n - 1)].cqe64;
+       rte_prefetch0(cqe);
 }
 
-#if MLX5_PMD_SGE_WR_N > 1
+/**
+ * Prefetch a WQE.
+ *
+ * @param txq
+ *   Pointer to TX queue structure.
+ * @param  wqe_ci
+ *   WQE consumer index.
+ */
+static inline void
+tx_prefetch_wqe(struct txq *txq, uint16_t ci)
+{
+       volatile union mlx5_wqe *wqe;
+
+       wqe = &(*txq->wqes)[ci & (txq->wqe_n - 1)];
+       rte_prefetch0(wqe);
+}
 
 /**
- * Copy scattered mbuf contents to a single linear buffer.
+ * DPDK callback for TX.
  *
- * @param[out] linear
- *   Linear output buffer.
- * @param[in] buf
- *   Scattered input buffer.
+ * @param dpdk_txq
+ *   Generic pointer to TX queue structure.
+ * @param[in] pkts
+ *   Packets to transmit.
+ * @param pkts_n
+ *   Number of packets in array.
  *
  * @return
- *   Number of bytes copied to the output buffer or 0 if not large enough.
+ *   Number of packets successfully transmitted (<= pkts_n).
  */
-static unsigned int
-linearize_mbuf(linear_t *linear, struct rte_mbuf *buf)
+uint16_t
+mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
 {
-       unsigned int size = 0;
-       unsigned int offset;
+       struct txq *txq = (struct txq *)dpdk_txq;
+       uint16_t elts_head = txq->elts_head;
+       const unsigned int elts_n = txq->elts_n;
+       unsigned int i = 0;
+       unsigned int j = 0;
+       unsigned int max;
+       unsigned int comp;
+       volatile union mlx5_wqe *wqe = NULL;
 
+       if (unlikely(!pkts_n))
+               return 0;
+       /* Prefetch first packet cacheline. */
+       tx_prefetch_cqe(txq, txq->cq_ci);
+       tx_prefetch_cqe(txq, txq->cq_ci + 1);
+       rte_prefetch0(*pkts);
+       /* Start processing. */
+       txq_complete(txq);
+       max = (elts_n - (elts_head - txq->elts_tail));
+       if (max > elts_n)
+               max -= elts_n;
        do {
-               unsigned int len = DATA_LEN(buf);
+               struct rte_mbuf *buf = *(pkts++);
+               unsigned int elts_head_next;
+               uintptr_t addr;
+               uint32_t length;
+               uint32_t lkey;
+               unsigned int segs_n = buf->nb_segs;
+               volatile struct mlx5_wqe_data_seg *dseg;
+               unsigned int ds = sizeof(*wqe) / 16;
 
-               offset = size;
-               size += len;
-               if (unlikely(size > sizeof(*linear)))
-                       return 0;
-               memcpy(&(*linear)[offset],
-                      rte_pktmbuf_mtod(buf, uint8_t *),
-                      len);
-               buf = NEXT(buf);
-       } while (buf != NULL);
-       return size;
+               /*
+                * Make sure there is enough room to store this packet and
+                * that one ring entry remains unused.
+                */
+               assert(segs_n);
+               if (max < segs_n + 1)
+                       break;
+               max -= segs_n;
+               --pkts_n;
+               elts_head_next = (elts_head + 1) & (elts_n - 1);
+               wqe = &(*txq->wqes)[txq->wqe_ci & (txq->wqe_n - 1)];
+               dseg = &wqe->wqe.dseg;
+               rte_prefetch0(wqe);
+               if (pkts_n)
+                       rte_prefetch0(*pkts);
+               /* Retrieve buffer information. */
+               addr = rte_pktmbuf_mtod(buf, uintptr_t);
+               length = DATA_LEN(buf);
+               /* Update element. */
+               (*txq->elts)[elts_head] = buf;
+               /* Prefetch next buffer data. */
+               if (pkts_n)
+                       rte_prefetch0(rte_pktmbuf_mtod(*pkts,
+                                                      volatile void *));
+               /* Retrieve Memory Region key for this memory pool. */
+               lkey = txq_mp2mr(txq, txq_mb2mp(buf));
+               if (buf->ol_flags & PKT_TX_VLAN_PKT)
+                       mlx5_wqe_write_vlan(txq, wqe, addr, length, lkey,
+                                           buf->vlan_tci);
+               else
+                       mlx5_wqe_write(txq, wqe, addr, length, lkey);
+               /* Should we enable HW CKSUM offload */
+               if (buf->ol_flags &
+                   (PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_UDP_CKSUM)) {
+                       wqe->wqe.eseg.cs_flags =
+                               MLX5_ETH_WQE_L3_CSUM |
+                               MLX5_ETH_WQE_L4_CSUM;
+               } else {
+                       wqe->wqe.eseg.cs_flags = 0;
+               }
+               while (--segs_n) {
+                       /*
+                        * Spill on next WQE when the current one does not have
+                        * enough room left. Size of WQE must a be a multiple
+                        * of data segment size.
+                        */
+                       assert(!(sizeof(*wqe) % sizeof(*dseg)));
+                       if (!(ds % (sizeof(*wqe) / 16)))
+                               dseg = (volatile void *)
+                                       &(*txq->wqes)[txq->wqe_ci++ &
+                                                     (txq->wqe_n - 1)];
+                       else
+                               ++dseg;
+                       ++ds;
+                       buf = buf->next;
+                       assert(buf);
+                       /* Store segment information. */
+                       dseg->byte_count = htonl(DATA_LEN(buf));
+                       dseg->lkey = txq_mp2mr(txq, txq_mb2mp(buf));
+                       dseg->addr = htonll(rte_pktmbuf_mtod(buf, uintptr_t));
+                       (*txq->elts)[elts_head_next] = buf;
+                       elts_head_next = (elts_head_next + 1) & (elts_n - 1);
+#ifdef MLX5_PMD_SOFT_COUNTERS
+                       length += DATA_LEN(buf);
+#endif
+                       ++j;
+               }
+               /* Update DS field in WQE. */
+               wqe->wqe.ctrl.data[1] &= htonl(0xffffffc0);
+               wqe->wqe.ctrl.data[1] |= htonl(ds & 0x3f);
+               elts_head = elts_head_next;
+#ifdef MLX5_PMD_SOFT_COUNTERS
+               /* Increment sent bytes counter. */
+               txq->stats.obytes += length;
+#endif
+               elts_head = elts_head_next;
+               ++i;
+       } while (pkts_n);
+       /* Take a shortcut if nothing must be sent. */
+       if (unlikely(i == 0))
+               return 0;
+       /* Check whether completion threshold has been reached. */
+       comp = txq->elts_comp + i + j;
+       if (comp >= MLX5_TX_COMP_THRESH) {
+               /* Request completion on last WQE. */
+               wqe->wqe.ctrl.data[2] = htonl(8);
+               /* Save elts_head in unused "immediate" field of WQE. */
+               wqe->wqe.ctrl.data[3] = elts_head;
+               txq->elts_comp = 0;
+       } else {
+               txq->elts_comp = comp;
+       }
+#ifdef MLX5_PMD_SOFT_COUNTERS
+       /* Increment sent packets counter. */
+       txq->stats.opackets += i;
+#endif
+       /* Ring QP doorbell. */
+       mlx5_tx_dbrec(txq);
+       txq->elts_head = elts_head;
+       return i;
 }
 
 /**
- * Handle scattered buffers for mlx5_tx_burst().
+ * DPDK callback for TX with inline support.
  *
- * @param txq
- *   TX queue structure.
- * @param segs
- *   Number of segments in buf.
- * @param elt
- *   TX queue element to fill.
- * @param[in] buf
- *   Buffer to process.
- * @param elts_head
- *   Index of the linear buffer to use if necessary (normally txq->elts_head).
- * @param[out] sges
- *   Array filled with SGEs on success.
+ * @param dpdk_txq
+ *   Generic pointer to TX queue structure.
+ * @param[in] pkts
+ *   Packets to transmit.
+ * @param pkts_n
+ *   Number of packets in array.
  *
  * @return
- *   A structure containing the processed packet size in bytes and the
- *   number of SGEs. Both fields are set to (unsigned int)-1 in case of
- *   failure.
+ *   Number of packets successfully transmitted (<= pkts_n).
  */
-static struct tx_burst_sg_ret {
-       unsigned int length;
-       unsigned int num;
-}
-tx_burst_sg(struct txq *txq, unsigned int segs, struct txq_elt *elt,
-           struct rte_mbuf *buf, unsigned int elts_head,
-           struct ibv_sge (*sges)[MLX5_PMD_SGE_WR_N])
+uint16_t
+mlx5_tx_burst_inline(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
 {
-       unsigned int sent_size = 0;
-       unsigned int j;
-       int linearize = 0;
-
-       /* When there are too many segments, extra segments are
-        * linearized in the last SGE. */
-       if (unlikely(segs > RTE_DIM(*sges))) {
-               segs = (RTE_DIM(*sges) - 1);
-               linearize = 1;
-       }
-       /* Update element. */
-       elt->buf = buf;
-       /* Register segments as SGEs. */
-       for (j = 0; (j != segs); ++j) {
-               struct ibv_sge *sge = &(*sges)[j];
+       struct txq *txq = (struct txq *)dpdk_txq;
+       uint16_t elts_head = txq->elts_head;
+       const unsigned int elts_n = txq->elts_n;
+       unsigned int i = 0;
+       unsigned int j = 0;
+       unsigned int max;
+       unsigned int comp;
+       volatile union mlx5_wqe *wqe = NULL;
+       unsigned int max_inline = txq->max_inline;
+
+       if (unlikely(!pkts_n))
+               return 0;
+       /* Prefetch first packet cacheline. */
+       tx_prefetch_cqe(txq, txq->cq_ci);
+       tx_prefetch_cqe(txq, txq->cq_ci + 1);
+       rte_prefetch0(*pkts);
+       /* Start processing. */
+       txq_complete(txq);
+       max = (elts_n - (elts_head - txq->elts_tail));
+       if (max > elts_n)
+               max -= elts_n;
+       do {
+               struct rte_mbuf *buf = *(pkts++);
+               unsigned int elts_head_next;
+               uintptr_t addr;
+               uint32_t length;
                uint32_t lkey;
+               unsigned int segs_n = buf->nb_segs;
+               volatile struct mlx5_wqe_data_seg *dseg;
+               unsigned int ds = sizeof(*wqe) / 16;
 
-               /* Retrieve Memory Region key for this memory pool. */
-               lkey = txq_mp2mr(txq, txq_mb2mp(buf));
-               if (unlikely(lkey == (uint32_t)-1)) {
-                       /* MR does not exist. */
-                       DEBUG("%p: unable to get MP <-> MR association",
-                             (void *)txq);
-                       /* Clean up TX element. */
-                       elt->buf = NULL;
-                       goto stop;
+               /*
+                * Make sure there is enough room to store this packet and
+                * that one ring entry remains unused.
+                */
+               assert(segs_n);
+               if (max < segs_n + 1)
+                       break;
+               max -= segs_n;
+               --pkts_n;
+               elts_head_next = (elts_head + 1) & (elts_n - 1);
+               wqe = &(*txq->wqes)[txq->wqe_ci & (txq->wqe_n - 1)];
+               dseg = &wqe->wqe.dseg;
+               tx_prefetch_wqe(txq, txq->wqe_ci);
+               tx_prefetch_wqe(txq, txq->wqe_ci + 1);
+               if (pkts_n)
+                       rte_prefetch0(*pkts);
+               /* Should we enable HW CKSUM offload */
+               if (buf->ol_flags &
+                   (PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_UDP_CKSUM)) {
+                       wqe->inl.eseg.cs_flags =
+                               MLX5_ETH_WQE_L3_CSUM |
+                               MLX5_ETH_WQE_L4_CSUM;
+               } else {
+                       wqe->inl.eseg.cs_flags = 0;
                }
-               /* Update SGE. */
-               sge->addr = rte_pktmbuf_mtod(buf, uintptr_t);
-               if (txq->priv->vf)
-                       rte_prefetch0((volatile void *)
-                                     (uintptr_t)sge->addr);
-               sge->length = DATA_LEN(buf);
-               sge->lkey = lkey;
-               sent_size += sge->length;
-               buf = NEXT(buf);
-       }
-       /* If buf is not NULL here and is not going to be linearized,
-        * nb_segs is not valid. */
-       assert(j == segs);
-       assert((buf == NULL) || (linearize));
-       /* Linearize extra segments. */
-       if (linearize) {
-               struct ibv_sge *sge = &(*sges)[segs];
-               linear_t *linear = &(*txq->elts_linear)[elts_head];
-               unsigned int size = linearize_mbuf(linear, buf);
-
-               assert(segs == (RTE_DIM(*sges) - 1));
-               if (size == 0) {
-                       /* Invalid packet. */
-                       DEBUG("%p: packet too large to be linearized.",
-                             (void *)txq);
-                       /* Clean up TX element. */
-                       elt->buf = NULL;
-                       goto stop;
+               /* Retrieve buffer information. */
+               addr = rte_pktmbuf_mtod(buf, uintptr_t);
+               length = DATA_LEN(buf);
+               /* Update element. */
+               (*txq->elts)[elts_head] = buf;
+               /* Prefetch next buffer data. */
+               if (pkts_n)
+                       rte_prefetch0(rte_pktmbuf_mtod(*pkts,
+                                                      volatile void *));
+               if ((length <= max_inline) && (segs_n == 1)) {
+                       if (buf->ol_flags & PKT_TX_VLAN_PKT)
+                               mlx5_wqe_write_inline_vlan(txq, wqe,
+                                                          addr, length,
+                                                          buf->vlan_tci);
+                       else
+                               mlx5_wqe_write_inline(txq, wqe, addr, length);
+                       goto skip_segs;
+               } else {
+                       /* Retrieve Memory Region key for this memory pool. */
+                       lkey = txq_mp2mr(txq, txq_mb2mp(buf));
+                       if (buf->ol_flags & PKT_TX_VLAN_PKT)
+                               mlx5_wqe_write_vlan(txq, wqe, addr, length,
+                                                   lkey, buf->vlan_tci);
+                       else
+                               mlx5_wqe_write(txq, wqe, addr, length, lkey);
                }
-               /* If MLX5_PMD_SGE_WR_N is 1, free mbuf immediately. */
-               if (RTE_DIM(*sges) == 1) {
-                       do {
-                               struct rte_mbuf *next = NEXT(buf);
-
-                               rte_pktmbuf_free_seg(buf);
-                               buf = next;
-                       } while (buf != NULL);
-                       elt->buf = NULL;
+               while (--segs_n) {
+                       /*
+                        * Spill on next WQE when the current one does not have
+                        * enough room left. Size of WQE must a be a multiple
+                        * of data segment size.
+                        */
+                       assert(!(sizeof(*wqe) % sizeof(*dseg)));
+                       if (!(ds % (sizeof(*wqe) / 16)))
+                               dseg = (volatile void *)
+                                       &(*txq->wqes)[txq->wqe_ci++ &
+                                                     (txq->wqe_n - 1)];
+                       else
+                               ++dseg;
+                       ++ds;
+                       buf = buf->next;
+                       assert(buf);
+                       /* Store segment information. */
+                       dseg->byte_count = htonl(DATA_LEN(buf));
+                       dseg->lkey = txq_mp2mr(txq, txq_mb2mp(buf));
+                       dseg->addr = htonll(rte_pktmbuf_mtod(buf, uintptr_t));
+                       (*txq->elts)[elts_head_next] = buf;
+                       elts_head_next = (elts_head_next + 1) & (elts_n - 1);
+#ifdef MLX5_PMD_SOFT_COUNTERS
+                       length += DATA_LEN(buf);
+#endif
+                       ++j;
                }
-               /* Update SGE. */
-               sge->addr = (uintptr_t)&(*linear)[0];
-               sge->length = size;
-               sge->lkey = txq->mr_linear->lkey;
-               sent_size += size;
-               /* Include last segment. */
-               segs++;
+               /* Update DS field in WQE. */
+               wqe->inl.ctrl.data[1] &= htonl(0xffffffc0);
+               wqe->inl.ctrl.data[1] |= htonl(ds & 0x3f);
+skip_segs:
+               elts_head = elts_head_next;
+#ifdef MLX5_PMD_SOFT_COUNTERS
+               /* Increment sent bytes counter. */
+               txq->stats.obytes += length;
+#endif
+               ++i;
+       } while (pkts_n);
+       /* Take a shortcut if nothing must be sent. */
+       if (unlikely(i == 0))
+               return 0;
+       /* Check whether completion threshold has been reached. */
+       comp = txq->elts_comp + i + j;
+       if (comp >= MLX5_TX_COMP_THRESH) {
+               /* Request completion on last WQE. */
+               wqe->inl.ctrl.data[2] = htonl(8);
+               /* Save elts_head in unused "immediate" field of WQE. */
+               wqe->inl.ctrl.data[3] = elts_head;
+               txq->elts_comp = 0;
+       } else {
+               txq->elts_comp = comp;
        }
-       return (struct tx_burst_sg_ret){
-               .length = sent_size,
-               .num = segs,
-       };
-stop:
-       return (struct tx_burst_sg_ret){
-               .length = -1,
-               .num = -1,
-       };
+#ifdef MLX5_PMD_SOFT_COUNTERS
+       /* Increment sent packets counter. */
+       txq->stats.opackets += i;
+#endif
+       /* Ring QP doorbell. */
+       mlx5_tx_dbrec(txq);
+       txq->elts_head = elts_head;
+       return i;
 }
 
-#endif /* MLX5_PMD_SGE_WR_N > 1 */
+/**
+ * Open a MPW session.
+ *
+ * @param txq
+ *   Pointer to TX queue structure.
+ * @param mpw
+ *   Pointer to MPW session structure.
+ * @param length
+ *   Packet length.
+ */
+static inline void
+mlx5_mpw_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
+{
+       uint16_t idx = txq->wqe_ci & (txq->wqe_n - 1);
+       volatile struct mlx5_wqe_data_seg (*dseg)[MLX5_MPW_DSEG_MAX] =
+               (volatile struct mlx5_wqe_data_seg (*)[])
+               (uintptr_t)&(*txq->wqes)[(idx + 1) & (txq->wqe_n - 1)];
+
+       mpw->state = MLX5_MPW_STATE_OPENED;
+       mpw->pkts_n = 0;
+       mpw->len = length;
+       mpw->total_len = 0;
+       mpw->wqe = &(*txq->wqes)[idx];
+       mpw->wqe->mpw.eseg.mss = htons(length);
+       mpw->wqe->mpw.eseg.inline_hdr_sz = 0;
+       mpw->wqe->mpw.eseg.rsvd0 = 0;
+       mpw->wqe->mpw.eseg.rsvd1 = 0;
+       mpw->wqe->mpw.eseg.rsvd2 = 0;
+       mpw->wqe->mpw.ctrl.data[0] = htonl((MLX5_OPC_MOD_MPW << 24) |
+                                          (txq->wqe_ci << 8) |
+                                          MLX5_OPCODE_LSO_MPW);
+       mpw->wqe->mpw.ctrl.data[2] = 0;
+       mpw->wqe->mpw.ctrl.data[3] = 0;
+       mpw->data.dseg[0] = &mpw->wqe->mpw.dseg[0];
+       mpw->data.dseg[1] = &mpw->wqe->mpw.dseg[1];
+       mpw->data.dseg[2] = &(*dseg)[0];
+       mpw->data.dseg[3] = &(*dseg)[1];
+       mpw->data.dseg[4] = &(*dseg)[2];
+}
 
 /**
- * DPDK callback for TX.
+ * Close a MPW session.
+ *
+ * @param txq
+ *   Pointer to TX queue structure.
+ * @param mpw
+ *   Pointer to MPW session structure.
+ */
+static inline void
+mlx5_mpw_close(struct txq *txq, struct mlx5_mpw *mpw)
+{
+       unsigned int num = mpw->pkts_n;
+
+       /*
+        * Store size in multiple of 16 bytes. Control and Ethernet segments
+        * count as 2.
+        */
+       mpw->wqe->mpw.ctrl.data[1] = htonl(txq->qp_num_8s | (2 + num));
+       mpw->state = MLX5_MPW_STATE_CLOSED;
+       if (num < 3)
+               ++txq->wqe_ci;
+       else
+               txq->wqe_ci += 2;
+       tx_prefetch_wqe(txq, txq->wqe_ci);
+       tx_prefetch_wqe(txq, txq->wqe_ci + 1);
+}
+
+/**
+ * DPDK callback for TX with MPW support.
  *
  * @param dpdk_txq
  *   Generic pointer to TX queue structure.
@@ -531,224 +959,399 @@ stop:
  *   Number of packets successfully transmitted (<= pkts_n).
  */
 uint16_t
-mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
+mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
 {
        struct txq *txq = (struct txq *)dpdk_txq;
-       unsigned int elts_head = txq->elts_head;
+       uint16_t elts_head = txq->elts_head;
        const unsigned int elts_n = txq->elts_n;
-       unsigned int elts_comp_cd = txq->elts_comp_cd;
-       unsigned int elts_comp = 0;
-       unsigned int i;
+       unsigned int i = 0;
+       unsigned int j = 0;
        unsigned int max;
-       int err;
-       struct rte_mbuf *buf = pkts[0];
+       unsigned int comp;
+       struct mlx5_mpw mpw = {
+               .state = MLX5_MPW_STATE_CLOSED,
+       };
 
-       assert(elts_comp_cd != 0);
+       if (unlikely(!pkts_n))
+               return 0;
        /* Prefetch first packet cacheline. */
-       rte_prefetch0(buf);
+       tx_prefetch_cqe(txq, txq->cq_ci);
+       tx_prefetch_wqe(txq, txq->wqe_ci);
+       tx_prefetch_wqe(txq, txq->wqe_ci + 1);
+       /* Start processing. */
        txq_complete(txq);
        max = (elts_n - (elts_head - txq->elts_tail));
        if (max > elts_n)
                max -= elts_n;
-       assert(max >= 1);
-       assert(max <= elts_n);
-       /* Always leave one free entry in the ring. */
-       --max;
-       if (max == 0)
+       do {
+               struct rte_mbuf *buf = *(pkts++);
+               unsigned int elts_head_next;
+               uint32_t length;
+               unsigned int segs_n = buf->nb_segs;
+               uint32_t cs_flags = 0;
+
+               /*
+                * Make sure there is enough room to store this packet and
+                * that one ring entry remains unused.
+                */
+               assert(segs_n);
+               if (max < segs_n + 1)
+                       break;
+               /* Do not bother with large packets MPW cannot handle. */
+               if (segs_n > MLX5_MPW_DSEG_MAX)
+                       break;
+               max -= segs_n;
+               --pkts_n;
+               /* Should we enable HW CKSUM offload */
+               if (buf->ol_flags &
+                   (PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_UDP_CKSUM))
+                       cs_flags = MLX5_ETH_WQE_L3_CSUM | MLX5_ETH_WQE_L4_CSUM;
+               /* Retrieve packet information. */
+               length = PKT_LEN(buf);
+               assert(length);
+               /* Start new session if packet differs. */
+               if ((mpw.state == MLX5_MPW_STATE_OPENED) &&
+                   ((mpw.len != length) ||
+                    (segs_n != 1) ||
+                    (mpw.wqe->mpw.eseg.cs_flags != cs_flags)))
+                       mlx5_mpw_close(txq, &mpw);
+               if (mpw.state == MLX5_MPW_STATE_CLOSED) {
+                       mlx5_mpw_new(txq, &mpw, length);
+                       mpw.wqe->mpw.eseg.cs_flags = cs_flags;
+               }
+               /* Multi-segment packets must be alone in their MPW. */
+               assert((segs_n == 1) || (mpw.pkts_n == 0));
+#if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
+               length = 0;
+#endif
+               do {
+                       volatile struct mlx5_wqe_data_seg *dseg;
+                       uintptr_t addr;
+
+                       elts_head_next = (elts_head + 1) & (elts_n - 1);
+                       assert(buf);
+                       (*txq->elts)[elts_head] = buf;
+                       dseg = mpw.data.dseg[mpw.pkts_n];
+                       addr = rte_pktmbuf_mtod(buf, uintptr_t);
+                       *dseg = (struct mlx5_wqe_data_seg){
+                               .byte_count = htonl(DATA_LEN(buf)),
+                               .lkey = txq_mp2mr(txq, txq_mb2mp(buf)),
+                               .addr = htonll(addr),
+                       };
+                       elts_head = elts_head_next;
+#if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
+                       length += DATA_LEN(buf);
+#endif
+                       buf = buf->next;
+                       ++mpw.pkts_n;
+                       ++j;
+               } while (--segs_n);
+               assert(length == mpw.len);
+               if (mpw.pkts_n == MLX5_MPW_DSEG_MAX)
+                       mlx5_mpw_close(txq, &mpw);
+               elts_head = elts_head_next;
+#ifdef MLX5_PMD_SOFT_COUNTERS
+               /* Increment sent bytes counter. */
+               txq->stats.obytes += length;
+#endif
+               ++i;
+       } while (pkts_n);
+       /* Take a shortcut if nothing must be sent. */
+       if (unlikely(i == 0))
                return 0;
-       if (max > pkts_n)
-               max = pkts_n;
-       for (i = 0; (i != max); ++i) {
-               struct rte_mbuf *buf_next = pkts[i + 1];
-               unsigned int elts_head_next =
-                       (((elts_head + 1) == elts_n) ? 0 : elts_head + 1);
-               struct txq_elt *elt = &(*txq->elts)[elts_head];
-               unsigned int segs = NB_SEGS(buf);
+       /* Check whether completion threshold has been reached. */
+       /* "j" includes both packets and segments. */
+       comp = txq->elts_comp + j;
+       if (comp >= MLX5_TX_COMP_THRESH) {
+               volatile union mlx5_wqe *wqe = mpw.wqe;
+
+               /* Request completion on last WQE. */
+               wqe->mpw.ctrl.data[2] = htonl(8);
+               /* Save elts_head in unused "immediate" field of WQE. */
+               wqe->mpw.ctrl.data[3] = elts_head;
+               txq->elts_comp = 0;
+       } else {
+               txq->elts_comp = comp;
+       }
 #ifdef MLX5_PMD_SOFT_COUNTERS
-               unsigned int sent_size = 0;
+       /* Increment sent packets counter. */
+       txq->stats.opackets += i;
 #endif
-               uint32_t send_flags = 0;
-#ifdef HAVE_VERBS_VLAN_INSERTION
-               int insert_vlan = 0;
-#endif /* HAVE_VERBS_VLAN_INSERTION */
-
-               if (i + 1 < max)
-                       rte_prefetch0(buf_next);
-               /* Request TX completion. */
-               if (unlikely(--elts_comp_cd == 0)) {
-                       elts_comp_cd = txq->elts_comp_cd_init;
-                       ++elts_comp;
-                       send_flags |= IBV_EXP_QP_BURST_SIGNALED;
-               }
+       /* Ring QP doorbell. */
+       if (mpw.state == MLX5_MPW_STATE_OPENED)
+               mlx5_mpw_close(txq, &mpw);
+       mlx5_tx_dbrec(txq);
+       txq->elts_head = elts_head;
+       return i;
+}
+
+/**
+ * Open a MPW inline session.
+ *
+ * @param txq
+ *   Pointer to TX queue structure.
+ * @param mpw
+ *   Pointer to MPW session structure.
+ * @param length
+ *   Packet length.
+ */
+static inline void
+mlx5_mpw_inline_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
+{
+       uint16_t idx = txq->wqe_ci & (txq->wqe_n - 1);
+
+       mpw->state = MLX5_MPW_INL_STATE_OPENED;
+       mpw->pkts_n = 0;
+       mpw->len = length;
+       mpw->total_len = 0;
+       mpw->wqe = &(*txq->wqes)[idx];
+       mpw->wqe->mpw_inl.ctrl.data[0] = htonl((MLX5_OPC_MOD_MPW << 24) |
+                                              (txq->wqe_ci << 8) |
+                                              MLX5_OPCODE_LSO_MPW);
+       mpw->wqe->mpw_inl.ctrl.data[2] = 0;
+       mpw->wqe->mpw_inl.ctrl.data[3] = 0;
+       mpw->wqe->mpw_inl.eseg.mss = htons(length);
+       mpw->wqe->mpw_inl.eseg.inline_hdr_sz = 0;
+       mpw->wqe->mpw_inl.eseg.cs_flags = 0;
+       mpw->wqe->mpw_inl.eseg.rsvd0 = 0;
+       mpw->wqe->mpw_inl.eseg.rsvd1 = 0;
+       mpw->wqe->mpw_inl.eseg.rsvd2 = 0;
+       mpw->data.raw = &mpw->wqe->mpw_inl.data[0];
+}
+
+/**
+ * Close a MPW inline session.
+ *
+ * @param txq
+ *   Pointer to TX queue structure.
+ * @param mpw
+ *   Pointer to MPW session structure.
+ */
+static inline void
+mlx5_mpw_inline_close(struct txq *txq, struct mlx5_mpw *mpw)
+{
+       unsigned int size;
+
+       size = sizeof(*mpw->wqe) - MLX5_MWQE64_INL_DATA + mpw->total_len;
+       /*
+        * Store size in multiple of 16 bytes. Control and Ethernet segments
+        * count as 2.
+        */
+       mpw->wqe->mpw_inl.ctrl.data[1] =
+               htonl(txq->qp_num_8s | ((size + 15) / 16));
+       mpw->state = MLX5_MPW_STATE_CLOSED;
+       mpw->wqe->mpw_inl.byte_cnt = htonl(mpw->total_len | MLX5_INLINE_SEG);
+       txq->wqe_ci += (size + (sizeof(*mpw->wqe) - 1)) / sizeof(*mpw->wqe);
+}
+
+/**
+ * DPDK callback for TX with MPW inline support.
+ *
+ * @param dpdk_txq
+ *   Generic pointer to TX queue structure.
+ * @param[in] pkts
+ *   Packets to transmit.
+ * @param pkts_n
+ *   Number of packets in array.
+ *
+ * @return
+ *   Number of packets successfully transmitted (<= pkts_n).
+ */
+uint16_t
+mlx5_tx_burst_mpw_inline(void *dpdk_txq, struct rte_mbuf **pkts,
+                        uint16_t pkts_n)
+{
+       struct txq *txq = (struct txq *)dpdk_txq;
+       uint16_t elts_head = txq->elts_head;
+       const unsigned int elts_n = txq->elts_n;
+       unsigned int i = 0;
+       unsigned int j = 0;
+       unsigned int max;
+       unsigned int comp;
+       unsigned int inline_room = txq->max_inline;
+       struct mlx5_mpw mpw = {
+               .state = MLX5_MPW_STATE_CLOSED,
+       };
+
+       if (unlikely(!pkts_n))
+               return 0;
+       /* Prefetch first packet cacheline. */
+       tx_prefetch_cqe(txq, txq->cq_ci);
+       tx_prefetch_wqe(txq, txq->wqe_ci);
+       tx_prefetch_wqe(txq, txq->wqe_ci + 1);
+       /* Start processing. */
+       txq_complete(txq);
+       max = (elts_n - (elts_head - txq->elts_tail));
+       if (max > elts_n)
+               max -= elts_n;
+       do {
+               struct rte_mbuf *buf = *(pkts++);
+               unsigned int elts_head_next;
+               uintptr_t addr;
+               uint32_t length;
+               unsigned int segs_n = buf->nb_segs;
+               uint32_t cs_flags = 0;
+
+               /*
+                * Make sure there is enough room to store this packet and
+                * that one ring entry remains unused.
+                */
+               assert(segs_n);
+               if (max < segs_n + 1)
+                       break;
+               /* Do not bother with large packets MPW cannot handle. */
+               if (segs_n > MLX5_MPW_DSEG_MAX)
+                       break;
+               max -= segs_n;
+               --pkts_n;
                /* Should we enable HW CKSUM offload */
                if (buf->ol_flags &
-                   (PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_UDP_CKSUM)) {
-                       send_flags |= IBV_EXP_QP_BURST_IP_CSUM;
-                       /* HW does not support checksum offloads at arbitrary
-                        * offsets but automatically recognizes the packet
-                        * type. For inner L3/L4 checksums, only VXLAN (UDP)
-                        * tunnels are currently supported. */
-                       if (RTE_ETH_IS_TUNNEL_PKT(buf->packet_type))
-                               send_flags |= IBV_EXP_QP_BURST_TUNNEL;
+                   (PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_UDP_CKSUM))
+                       cs_flags = MLX5_ETH_WQE_L3_CSUM | MLX5_ETH_WQE_L4_CSUM;
+               /* Retrieve packet information. */
+               length = PKT_LEN(buf);
+               /* Start new session if packet differs. */
+               if (mpw.state == MLX5_MPW_STATE_OPENED) {
+                       if ((mpw.len != length) ||
+                           (segs_n != 1) ||
+                           (mpw.wqe->mpw.eseg.cs_flags != cs_flags))
+                               mlx5_mpw_close(txq, &mpw);
+               } else if (mpw.state == MLX5_MPW_INL_STATE_OPENED) {
+                       if ((mpw.len != length) ||
+                           (segs_n != 1) ||
+                           (length > inline_room) ||
+                           (mpw.wqe->mpw_inl.eseg.cs_flags != cs_flags)) {
+                               mlx5_mpw_inline_close(txq, &mpw);
+                               inline_room = txq->max_inline;
+                       }
                }
-               if (buf->ol_flags & PKT_TX_VLAN_PKT) {
-#ifdef HAVE_VERBS_VLAN_INSERTION
-                       if (!txq->priv->mps)
-                               insert_vlan = 1;
-                       else
-#endif /* HAVE_VERBS_VLAN_INSERTION */
-                       {
-                               err = insert_vlan_sw(buf);
-                               if (unlikely(err))
-                                       goto stop;
+               if (mpw.state == MLX5_MPW_STATE_CLOSED) {
+                       if ((segs_n != 1) ||
+                           (length > inline_room)) {
+                               mlx5_mpw_new(txq, &mpw, length);
+                               mpw.wqe->mpw.eseg.cs_flags = cs_flags;
+                       } else {
+                               mlx5_mpw_inline_new(txq, &mpw, length);
+                               mpw.wqe->mpw_inl.eseg.cs_flags = cs_flags;
                        }
                }
-               if (likely(segs == 1)) {
-                       uintptr_t addr;
-                       uint32_t length;
-                       uint32_t lkey;
-                       uintptr_t buf_next_addr;
+               /* Multi-segment packets must be alone in their MPW. */
+               assert((segs_n == 1) || (mpw.pkts_n == 0));
+               if (mpw.state == MLX5_MPW_STATE_OPENED) {
+                       assert(inline_room == txq->max_inline);
+#if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
+                       length = 0;
+#endif
+                       do {
+                               volatile struct mlx5_wqe_data_seg *dseg;
+
+                               elts_head_next =
+                                       (elts_head + 1) & (elts_n - 1);
+                               assert(buf);
+                               (*txq->elts)[elts_head] = buf;
+                               dseg = mpw.data.dseg[mpw.pkts_n];
+                               addr = rte_pktmbuf_mtod(buf, uintptr_t);
+                               *dseg = (struct mlx5_wqe_data_seg){
+                                       .byte_count = htonl(DATA_LEN(buf)),
+                                       .lkey = txq_mp2mr(txq, txq_mb2mp(buf)),
+                                       .addr = htonll(addr),
+                               };
+                               elts_head = elts_head_next;
+#if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
+                               length += DATA_LEN(buf);
+#endif
+                               buf = buf->next;
+                               ++mpw.pkts_n;
+                               ++j;
+                       } while (--segs_n);
+                       assert(length == mpw.len);
+                       if (mpw.pkts_n == MLX5_MPW_DSEG_MAX)
+                               mlx5_mpw_close(txq, &mpw);
+               } else {
+                       unsigned int max;
 
-                       /* Retrieve buffer information. */
+                       assert(mpw.state == MLX5_MPW_INL_STATE_OPENED);
+                       assert(length <= inline_room);
+                       assert(length == DATA_LEN(buf));
+                       elts_head_next = (elts_head + 1) & (elts_n - 1);
                        addr = rte_pktmbuf_mtod(buf, uintptr_t);
-                       length = DATA_LEN(buf);
-                       /* Update element. */
-                       elt->buf = buf;
-                       if (txq->priv->vf)
-                               rte_prefetch0((volatile void *)
-                                             (uintptr_t)addr);
-                       /* Prefetch next buffer data. */
-                       if (i + 1 < max) {
-                               buf_next_addr =
-                                       rte_pktmbuf_mtod(buf_next, uintptr_t);
-                               rte_prefetch0((volatile void *)
-                                             (uintptr_t)buf_next_addr);
+                       (*txq->elts)[elts_head] = buf;
+                       /* Maximum number of bytes before wrapping. */
+                       max = ((uintptr_t)&(*txq->wqes)[txq->wqe_n] -
+                              (uintptr_t)mpw.data.raw);
+                       if (length > max) {
+                               rte_memcpy((void *)(uintptr_t)mpw.data.raw,
+                                          (void *)addr,
+                                          max);
+                               mpw.data.raw =
+                                       (volatile void *)&(*txq->wqes)[0];
+                               rte_memcpy((void *)(uintptr_t)mpw.data.raw,
+                                          (void *)(addr + max),
+                                          length - max);
+                               mpw.data.raw += length - max;
+                       } else {
+                               rte_memcpy((void *)(uintptr_t)mpw.data.raw,
+                                          (void *)addr,
+                                          length);
+                               mpw.data.raw += length;
                        }
-                       /* Put packet into send queue. */
-#if MLX5_PMD_MAX_INLINE > 0
-                       if (length <= txq->max_inline) {
-#ifdef HAVE_VERBS_VLAN_INSERTION
-                               if (insert_vlan)
-                                       err = txq->send_pending_inline_vlan
-                                               (txq->qp,
-                                                (void *)addr,
-                                                length,
-                                                send_flags,
-                                                &buf->vlan_tci);
-                               else
-#endif /* HAVE_VERBS_VLAN_INSERTION */
-                                       err = txq->send_pending_inline
-                                               (txq->qp,
-                                                (void *)addr,
-                                                length,
-                                                send_flags);
-                       } else
-#endif
-                       {
-                               /* Retrieve Memory Region key for this
-                                * memory pool. */
-                               lkey = txq_mp2mr(txq, txq_mb2mp(buf));
-                               if (unlikely(lkey == (uint32_t)-1)) {
-                                       /* MR does not exist. */
-                                       DEBUG("%p: unable to get MP <-> MR"
-                                             " association", (void *)txq);
-                                       /* Clean up TX element. */
-                                       elt->buf = NULL;
-                                       goto stop;
-                               }
-#ifdef HAVE_VERBS_VLAN_INSERTION
-                               if (insert_vlan)
-                                       err = txq->send_pending_vlan
-                                               (txq->qp,
-                                                addr,
-                                                length,
-                                                lkey,
-                                                send_flags,
-                                                &buf->vlan_tci);
-                               else
-#endif /* HAVE_VERBS_VLAN_INSERTION */
-                                       err = txq->send_pending
-                                               (txq->qp,
-                                                addr,
-                                                length,
-                                                lkey,
-                                                send_flags);
+                       if ((uintptr_t)mpw.data.raw ==
+                           (uintptr_t)&(*txq->wqes)[txq->wqe_n])
+                               mpw.data.raw =
+                                       (volatile void *)&(*txq->wqes)[0];
+                       ++mpw.pkts_n;
+                       ++j;
+                       if (mpw.pkts_n == MLX5_MPW_DSEG_MAX) {
+                               mlx5_mpw_inline_close(txq, &mpw);
+                               inline_room = txq->max_inline;
+                       } else {
+                               inline_room -= length;
                        }
-                       if (unlikely(err))
-                               goto stop;
-#ifdef MLX5_PMD_SOFT_COUNTERS
-                       sent_size += length;
-#endif
-               } else {
-#if MLX5_PMD_SGE_WR_N > 1
-                       struct ibv_sge sges[MLX5_PMD_SGE_WR_N];
-                       struct tx_burst_sg_ret ret;
-
-                       ret = tx_burst_sg(txq, segs, elt, buf, elts_head,
-                                         &sges);
-                       if (ret.length == (unsigned int)-1)
-                               goto stop;
-                       /* Put SG list into send queue. */
-#ifdef HAVE_VERBS_VLAN_INSERTION
-                       if (insert_vlan)
-                               err = txq->send_pending_sg_list_vlan
-                                       (txq->qp,
-                                        sges,
-                                        ret.num,
-                                        send_flags,
-                                        &buf->vlan_tci);
-                       else
-#endif /* HAVE_VERBS_VLAN_INSERTION */
-                               err = txq->send_pending_sg_list
-                                       (txq->qp,
-                                        sges,
-                                        ret.num,
-                                        send_flags);
-                       if (unlikely(err))
-                               goto stop;
-#ifdef MLX5_PMD_SOFT_COUNTERS
-                       sent_size += ret.length;
-#endif
-#else /* MLX5_PMD_SGE_WR_N > 1 */
-                       DEBUG("%p: TX scattered buffers support not"
-                             " compiled in", (void *)txq);
-                       goto stop;
-#endif /* MLX5_PMD_SGE_WR_N > 1 */
                }
+               mpw.total_len += length;
                elts_head = elts_head_next;
-               buf = buf_next;
 #ifdef MLX5_PMD_SOFT_COUNTERS
                /* Increment sent bytes counter. */
-               txq->stats.obytes += sent_size;
+               txq->stats.obytes += length;
 #endif
-       }
-stop:
+               ++i;
+       } while (pkts_n);
        /* Take a shortcut if nothing must be sent. */
        if (unlikely(i == 0))
                return 0;
+       /* Check whether completion threshold has been reached. */
+       /* "j" includes both packets and segments. */
+       comp = txq->elts_comp + j;
+       if (comp >= MLX5_TX_COMP_THRESH) {
+               volatile union mlx5_wqe *wqe = mpw.wqe;
+
+               /* Request completion on last WQE. */
+               wqe->mpw_inl.ctrl.data[2] = htonl(8);
+               /* Save elts_head in unused "immediate" field of WQE. */
+               wqe->mpw_inl.ctrl.data[3] = elts_head;
+               txq->elts_comp = 0;
+       } else {
+               txq->elts_comp = comp;
+       }
 #ifdef MLX5_PMD_SOFT_COUNTERS
        /* Increment sent packets counter. */
        txq->stats.opackets += i;
 #endif
        /* Ring QP doorbell. */
-       err = txq->send_flush(txq->qp);
-       if (unlikely(err)) {
-               /* A nonzero value is not supposed to be returned.
-                * Nothing can be done about it. */
-               DEBUG("%p: send_flush() failed with error %d",
-                     (void *)txq, err);
-       }
+       if (mpw.state == MLX5_MPW_INL_STATE_OPENED)
+               mlx5_mpw_inline_close(txq, &mpw);
+       else if (mpw.state == MLX5_MPW_STATE_OPENED)
+               mlx5_mpw_close(txq, &mpw);
+       mlx5_tx_dbrec(txq);
        txq->elts_head = elts_head;
-       txq->elts_comp += elts_comp;
-       txq->elts_comp_cd = elts_comp_cd;
        return i;
 }
 
 /**
  * Translate RX completion flags to packet type.
  *
- * @param flags
- *   RX completion flags returned by poll_length_flags().
+ * @param[in] cqe
+ *   Pointer to CQE.
  *
  * @note: fix mlx5_dev_supported_ptypes_get() if any change here.
  *
@@ -756,11 +1359,13 @@ stop:
  *   Packet type for struct rte_mbuf.
  */
 static inline uint32_t
-rxq_cq_to_pkt_type(uint32_t flags)
+rxq_cq_to_pkt_type(volatile struct mlx5_cqe64 *cqe)
 {
        uint32_t pkt_type;
+       uint8_t flags = cqe->l4_hdr_type_etc;
+       uint8_t info = cqe->rsvd0[0];
 
-       if (flags & IBV_EXP_CQ_RX_TUNNEL_PACKET)
+       if (info & IBV_EXP_CQ_RX_TUNNEL_PACKET)
                pkt_type =
                        TRANSPOSE(flags,
                                  IBV_EXP_CQ_RX_OUTER_IPV4_PACKET,
@@ -777,66 +1382,157 @@ rxq_cq_to_pkt_type(uint32_t flags)
        else
                pkt_type =
                        TRANSPOSE(flags,
-                                 IBV_EXP_CQ_RX_IPV4_PACKET,
-                                 RTE_PTYPE_L3_IPV4) |
+                                 MLX5_CQE_L3_HDR_TYPE_IPV6,
+                                 RTE_PTYPE_L3_IPV6) |
                        TRANSPOSE(flags,
-                                 IBV_EXP_CQ_RX_IPV6_PACKET,
-                                 RTE_PTYPE_L3_IPV6);
+                                 MLX5_CQE_L3_HDR_TYPE_IPV4,
+                                 RTE_PTYPE_L3_IPV4);
        return pkt_type;
 }
 
+/**
+ * Get size of the next packet for a given CQE. For compressed CQEs, the
+ * consumer index is updated only once all packets of the current one have
+ * been processed.
+ *
+ * @param rxq
+ *   Pointer to RX queue.
+ * @param cqe
+ *   CQE to process.
+ *
+ * @return
+ *   Packet size in bytes (0 if there is none), -1 in case of completion
+ *   with error.
+ */
+static inline int
+mlx5_rx_poll_len(struct rxq *rxq, volatile struct mlx5_cqe64 *cqe,
+                uint16_t cqe_cnt)
+{
+       struct rxq_zip *zip = &rxq->zip;
+       uint16_t cqe_n = cqe_cnt + 1;
+       int len = 0;
+
+       /* Process compressed data in the CQE and mini arrays. */
+       if (zip->ai) {
+               volatile struct mlx5_mini_cqe8 (*mc)[8] =
+                       (volatile struct mlx5_mini_cqe8 (*)[8])
+                       (uintptr_t)(&(*rxq->cqes)[zip->ca & cqe_cnt].cqe64);
+
+               len = ntohl((*mc)[zip->ai & 7].byte_cnt);
+               if ((++zip->ai & 7) == 0) {
+                       /*
+                        * Increment consumer index to skip the number of
+                        * CQEs consumed. Hardware leaves holes in the CQ
+                        * ring for software use.
+                        */
+                       zip->ca = zip->na;
+                       zip->na += 8;
+               }
+               if (unlikely(rxq->zip.ai == rxq->zip.cqe_cnt)) {
+                       uint16_t idx = rxq->cq_ci;
+                       uint16_t end = zip->cq_ci;
+
+                       while (idx != end) {
+                               (*rxq->cqes)[idx & cqe_cnt].cqe64.op_own =
+                                       MLX5_CQE_INVALIDATE;
+                               ++idx;
+                       }
+                       rxq->cq_ci = zip->cq_ci;
+                       zip->ai = 0;
+               }
+       /* No compressed data, get next CQE and verify if it is compressed. */
+       } else {
+               int ret;
+               int8_t op_own;
+
+               ret = check_cqe64(cqe, cqe_n, rxq->cq_ci);
+               if (unlikely(ret == 1))
+                       return 0;
+               ++rxq->cq_ci;
+               op_own = cqe->op_own;
+               if (MLX5_CQE_FORMAT(op_own) == MLX5_COMPRESSED) {
+                       volatile struct mlx5_mini_cqe8 (*mc)[8] =
+                               (volatile struct mlx5_mini_cqe8 (*)[8])
+                               (uintptr_t)(&(*rxq->cqes)[rxq->cq_ci &
+                                                         cqe_cnt].cqe64);
+
+                       /* Fix endianness. */
+                       zip->cqe_cnt = ntohl(cqe->byte_cnt);
+                       /*
+                        * Current mini array position is the one returned by
+                        * check_cqe64().
+                        *
+                        * If completion comprises several mini arrays, as a
+                        * special case the second one is located 7 CQEs after
+                        * the initial CQE instead of 8 for subsequent ones.
+                        */
+                       zip->ca = rxq->cq_ci & cqe_cnt;
+                       zip->na = zip->ca + 7;
+                       /* Compute the next non compressed CQE. */
+                       --rxq->cq_ci;
+                       zip->cq_ci = rxq->cq_ci + zip->cqe_cnt;
+                       /* Get packet size to return. */
+                       len = ntohl((*mc)[0].byte_cnt);
+                       zip->ai = 1;
+               } else {
+                       len = ntohl(cqe->byte_cnt);
+               }
+               /* Error while receiving packet. */
+               if (unlikely(MLX5_CQE_OPCODE(op_own) == MLX5_CQE_RESP_ERR))
+                       return -1;
+       }
+       return len;
+}
+
 /**
  * Translate RX completion flags to offload flags.
  *
  * @param[in] rxq
  *   Pointer to RX queue structure.
- * @param flags
- *   RX completion flags returned by poll_length_flags().
+ * @param[in] cqe
+ *   Pointer to CQE.
  *
  * @return
  *   Offload flags (ol_flags) for struct rte_mbuf.
  */
 static inline uint32_t
-rxq_cq_to_ol_flags(const struct rxq *rxq, uint32_t flags)
+rxq_cq_to_ol_flags(struct rxq *rxq, volatile struct mlx5_cqe64 *cqe)
 {
        uint32_t ol_flags = 0;
+       uint8_t l3_hdr = (cqe->l4_hdr_type_etc) & MLX5_CQE_L3_HDR_TYPE_MASK;
+       uint8_t l4_hdr = (cqe->l4_hdr_type_etc) & MLX5_CQE_L4_HDR_TYPE_MASK;
+       uint8_t info = cqe->rsvd0[0];
 
-       if (rxq->csum) {
-               /* Set IP checksum flag only for IPv4/IPv6 packets. */
-               if (flags &
-                   (IBV_EXP_CQ_RX_IPV4_PACKET | IBV_EXP_CQ_RX_IPV6_PACKET))
-                       ol_flags |=
-                               TRANSPOSE(~flags,
-                                       IBV_EXP_CQ_RX_IP_CSUM_OK,
-                                       PKT_RX_IP_CKSUM_BAD);
-#ifdef HAVE_EXP_CQ_RX_TCP_PACKET
-               /* Set L4 checksum flag only for TCP/UDP packets. */
-               if (flags &
-                   (IBV_EXP_CQ_RX_TCP_PACKET | IBV_EXP_CQ_RX_UDP_PACKET))
-#endif /* HAVE_EXP_CQ_RX_TCP_PACKET */
-                       ol_flags |=
-                               TRANSPOSE(~flags,
-                                       IBV_EXP_CQ_RX_TCP_UDP_CSUM_OK,
-                                       PKT_RX_L4_CKSUM_BAD);
-       }
+       if ((l3_hdr == MLX5_CQE_L3_HDR_TYPE_IPV4) ||
+           (l3_hdr == MLX5_CQE_L3_HDR_TYPE_IPV6))
+               ol_flags |=
+                       (!(cqe->hds_ip_ext & MLX5_CQE_L3_OK) *
+                        PKT_RX_IP_CKSUM_BAD);
+       if ((l4_hdr == MLX5_CQE_L4_HDR_TYPE_TCP) ||
+           (l4_hdr == MLX5_CQE_L4_HDR_TYPE_TCP_EMP_ACK) ||
+           (l4_hdr == MLX5_CQE_L4_HDR_TYPE_TCP_ACK) ||
+           (l4_hdr == MLX5_CQE_L4_HDR_TYPE_UDP))
+               ol_flags |=
+                       (!(cqe->hds_ip_ext & MLX5_CQE_L4_OK) *
+                        PKT_RX_L4_CKSUM_BAD);
        /*
         * PKT_RX_IP_CKSUM_BAD and PKT_RX_L4_CKSUM_BAD are used in place
         * of PKT_RX_EIP_CKSUM_BAD because the latter is not functional
         * (its value is 0).
         */
-       if ((flags & IBV_EXP_CQ_RX_TUNNEL_PACKET) && (rxq->csum_l2tun))
+       if ((info & IBV_EXP_CQ_RX_TUNNEL_PACKET) && (rxq->csum_l2tun))
                ol_flags |=
-                       TRANSPOSE(~flags,
+                       TRANSPOSE(~cqe->l4_hdr_type_etc,
                                  IBV_EXP_CQ_RX_OUTER_IP_CSUM_OK,
                                  PKT_RX_IP_CKSUM_BAD) |
-                       TRANSPOSE(~flags,
+                       TRANSPOSE(~cqe->l4_hdr_type_etc,
                                  IBV_EXP_CQ_RX_OUTER_TCP_UDP_CSUM_OK,
                                  PKT_RX_L4_CKSUM_BAD);
        return ol_flags;
 }
 
 /**
- * DPDK callback for RX with scattered packets support.
+ * DPDK callback for RX.
  *
  * @param dpdk_rxq
  *   Generic pointer to RX queue structure.
@@ -849,353 +1545,127 @@ rxq_cq_to_ol_flags(const struct rxq *rxq, uint32_t flags)
  *   Number of packets successfully received (<= pkts_n).
  */
 uint16_t
-mlx5_rx_burst_sp(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
+mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
 {
-       struct rxq *rxq = (struct rxq *)dpdk_rxq;
-       struct rxq_elt_sp (*elts)[rxq->elts_n] = rxq->elts.sp;
-       const unsigned int elts_n = rxq->elts_n;
-       unsigned int elts_head = rxq->elts_head;
-       unsigned int i;
-       unsigned int pkts_ret = 0;
-       int ret;
+       struct rxq *rxq = dpdk_rxq;
+       const unsigned int wqe_cnt = rxq->elts_n - 1;
+       const unsigned int cqe_cnt = rxq->cqe_n - 1;
+       const unsigned int sges_n = rxq->sges_n;
+       struct rte_mbuf *pkt = NULL;
+       struct rte_mbuf *seg = NULL;
+       volatile struct mlx5_cqe64 *cqe =
+               &(*rxq->cqes)[rxq->cq_ci & cqe_cnt].cqe64;
+       unsigned int i = 0;
+       unsigned int rq_ci = rxq->rq_ci << sges_n;
+       int len;
 
-       if (unlikely(!rxq->sp))
-               return mlx5_rx_burst(dpdk_rxq, pkts, pkts_n);
-       if (unlikely(elts == NULL)) /* See RTE_DEV_CMD_SET_MTU. */
-               return 0;
-       for (i = 0; (i != pkts_n); ++i) {
-               struct rxq_elt_sp *elt = &(*elts)[elts_head];
-               unsigned int len;
-               unsigned int pkt_buf_len;
-               struct rte_mbuf *pkt_buf = NULL; /* Buffer returned in pkts. */
-               struct rte_mbuf **pkt_buf_next = &pkt_buf;
-               unsigned int seg_headroom = RTE_PKTMBUF_HEADROOM;
-               unsigned int j = 0;
-               uint32_t flags;
-               uint16_t vlan_tci;
-
-               /* Sanity checks. */
-               assert(elts_head < rxq->elts_n);
-               assert(rxq->elts_head < rxq->elts_n);
-               ret = rxq->poll(rxq->cq, NULL, NULL, &flags, &vlan_tci);
-               if (unlikely(ret < 0)) {
-                       struct ibv_wc wc;
-                       int wcs_n;
-
-                       DEBUG("rxq=%p, poll_length() failed (ret=%d)",
-                             (void *)rxq, ret);
-                       /* ibv_poll_cq() must be used in case of failure. */
-                       wcs_n = ibv_poll_cq(rxq->cq, 1, &wc);
-                       if (unlikely(wcs_n == 0))
-                               break;
-                       if (unlikely(wcs_n < 0)) {
-                               DEBUG("rxq=%p, ibv_poll_cq() failed (wcs_n=%d)",
-                                     (void *)rxq, wcs_n);
+       while (pkts_n) {
+               unsigned int idx = rq_ci & wqe_cnt;
+               volatile struct mlx5_wqe_data_seg *wqe = &(*rxq->wqes)[idx];
+               struct rte_mbuf *rep = (*rxq->elts)[idx];
+
+               if (pkt)
+                       NEXT(seg) = rep;
+               seg = rep;
+               rte_prefetch0(seg);
+               rte_prefetch0(cqe);
+               rte_prefetch0(wqe);
+               rep = rte_mbuf_raw_alloc(rxq->mp);
+               if (unlikely(rep == NULL)) {
+                       while (pkt) {
+                               seg = NEXT(pkt);
+                               rte_mbuf_refcnt_set(pkt, 0);
+                               __rte_mbuf_raw_free(pkt);
+                               pkt = seg;
+                       }
+                       ++rxq->stats.rx_nombuf;
+                       break;
+               }
+               if (!pkt) {
+                       cqe = &(*rxq->cqes)[rxq->cq_ci & cqe_cnt].cqe64;
+                       len = mlx5_rx_poll_len(rxq, cqe, cqe_cnt);
+                       if (len == 0) {
+                               rte_mbuf_refcnt_set(rep, 0);
+                               __rte_mbuf_raw_free(rep);
                                break;
                        }
-                       assert(wcs_n == 1);
-                       if (unlikely(wc.status != IBV_WC_SUCCESS)) {
-                               /* Whatever, just repost the offending WR. */
-                               DEBUG("rxq=%p, wr_id=%" PRIu64 ": bad work"
-                                     " completion status (%d): %s",
-                                     (void *)rxq, wc.wr_id, wc.status,
-                                     ibv_wc_status_str(wc.status));
-#ifdef MLX5_PMD_SOFT_COUNTERS
-                               /* Increment dropped packets counter. */
+                       if (unlikely(len == -1)) {
+                               /* RX error, packet is likely too large. */
+                               rte_mbuf_refcnt_set(rep, 0);
+                               __rte_mbuf_raw_free(rep);
                                ++rxq->stats.idropped;
-#endif
-                               goto repost;
+                               goto skip;
                        }
-                       ret = wc.byte_len;
-               }
-               if (ret == 0)
-                       break;
-               assert(ret >= (rxq->crc_present << 2));
-               len = ret - (rxq->crc_present << 2);
-               pkt_buf_len = len;
-               /*
-                * Replace spent segments with new ones, concatenate and
-                * return them as pkt_buf.
-                */
-               while (1) {
-                       struct ibv_sge *sge = &elt->sges[j];
-                       struct rte_mbuf *seg = elt->bufs[j];
-                       struct rte_mbuf *rep;
-                       unsigned int seg_tailroom;
-
-                       assert(seg != NULL);
-                       /*
-                        * Fetch initial bytes of packet descriptor into a
-                        * cacheline while allocating rep.
-                        */
-                       rte_prefetch0(seg);
-                       rep = __rte_mbuf_raw_alloc(rxq->mp);
-                       if (unlikely(rep == NULL)) {
-                               /*
-                                * Unable to allocate a replacement mbuf,
-                                * repost WR.
-                                */
-                               DEBUG("rxq=%p: can't allocate a new mbuf",
-                                     (void *)rxq);
-                               if (pkt_buf != NULL) {
-                                       *pkt_buf_next = NULL;
-                                       rte_pktmbuf_free(pkt_buf);
+                       pkt = seg;
+                       assert(len >= (rxq->crc_present << 2));
+                       /* Update packet information. */
+                       if (rxq->csum | rxq->csum_l2tun | rxq->vlan_strip |
+                           rxq->crc_present) {
+                               if (rxq->csum) {
+                                       pkt->packet_type =
+                                               rxq_cq_to_pkt_type(cqe);
+                                       pkt->ol_flags =
+                                               rxq_cq_to_ol_flags(rxq, cqe);
                                }
-                               /* Increment out of memory counters. */
-                               ++rxq->stats.rx_nombuf;
-                               ++rxq->priv->dev->data->rx_mbuf_alloc_failed;
-                               goto repost;
-                       }
-#ifndef NDEBUG
-                       /* Poison user-modifiable fields in rep. */
-                       NEXT(rep) = (void *)((uintptr_t)-1);
-                       SET_DATA_OFF(rep, 0xdead);
-                       DATA_LEN(rep) = 0xd00d;
-                       PKT_LEN(rep) = 0xdeadd00d;
-                       NB_SEGS(rep) = 0x2a;
-                       PORT(rep) = 0x2a;
-                       rep->ol_flags = -1;
-#endif
-                       assert(rep->buf_len == seg->buf_len);
-                       assert(rep->buf_len == rxq->mb_len);
-                       /* Reconfigure sge to use rep instead of seg. */
-                       assert(sge->lkey == rxq->mr->lkey);
-                       sge->addr = ((uintptr_t)rep->buf_addr + seg_headroom);
-                       elt->bufs[j] = rep;
-                       ++j;
-                       /* Update pkt_buf if it's the first segment, or link
-                        * seg to the previous one and update pkt_buf_next. */
-                       *pkt_buf_next = seg;
-                       pkt_buf_next = &NEXT(seg);
-                       /* Update seg information. */
-                       seg_tailroom = (seg->buf_len - seg_headroom);
-                       assert(sge->length == seg_tailroom);
-                       SET_DATA_OFF(seg, seg_headroom);
-                       if (likely(len <= seg_tailroom)) {
-                               /* Last segment. */
-                               DATA_LEN(seg) = len;
-                               PKT_LEN(seg) = len;
-                               /* Sanity check. */
-                               assert(rte_pktmbuf_headroom(seg) ==
-                                      seg_headroom);
-                               assert(rte_pktmbuf_tailroom(seg) ==
-                                      (seg_tailroom - len));
-                               break;
-                       }
-                       DATA_LEN(seg) = seg_tailroom;
-                       PKT_LEN(seg) = seg_tailroom;
-                       /* Sanity check. */
-                       assert(rte_pktmbuf_headroom(seg) == seg_headroom);
-                       assert(rte_pktmbuf_tailroom(seg) == 0);
-                       /* Fix len and clear headroom for next segments. */
-                       len -= seg_tailroom;
-                       seg_headroom = 0;
-               }
-               /* Update head and tail segments. */
-               *pkt_buf_next = NULL;
-               assert(pkt_buf != NULL);
-               assert(j != 0);
-               NB_SEGS(pkt_buf) = j;
-               PORT(pkt_buf) = rxq->port_id;
-               PKT_LEN(pkt_buf) = pkt_buf_len;
-               if (rxq->csum | rxq->csum_l2tun | rxq->vlan_strip) {
-                       pkt_buf->packet_type = rxq_cq_to_pkt_type(flags);
-                       pkt_buf->ol_flags = rxq_cq_to_ol_flags(rxq, flags);
-#ifdef HAVE_EXP_DEVICE_ATTR_VLAN_OFFLOADS
-                       if (flags & IBV_EXP_CQ_RX_CVLAN_STRIPPED_V1) {
-                               pkt_buf->ol_flags |= PKT_RX_VLAN_PKT;
-                               pkt_buf->vlan_tci = vlan_tci;
+                               if (cqe->l4_hdr_type_etc &
+                                   MLX5_CQE_VLAN_STRIPPED) {
+                                       pkt->ol_flags |= PKT_RX_VLAN_PKT |
+                                               PKT_RX_VLAN_STRIPPED;
+                                       pkt->vlan_tci = ntohs(cqe->vlan_info);
+                               }
+                               if (rxq->crc_present)
+                                       len -= ETHER_CRC_LEN;
                        }
-#endif /* HAVE_EXP_DEVICE_ATTR_VLAN_OFFLOADS */
+                       PKT_LEN(pkt) = len;
                }
-
-               /* Return packet. */
-               *(pkts++) = pkt_buf;
-               ++pkts_ret;
-#ifdef MLX5_PMD_SOFT_COUNTERS
-               /* Increment bytes counter. */
-               rxq->stats.ibytes += pkt_buf_len;
-#endif
-repost:
-               ret = rxq->recv(rxq->wq, elt->sges, RTE_DIM(elt->sges));
-               if (unlikely(ret)) {
-                       /* Inability to repost WRs is fatal. */
-                       DEBUG("%p: recv_sg_list(): failed (ret=%d)",
-                             (void *)rxq->priv,
-                             ret);
-                       abort();
-               }
-               if (++elts_head >= elts_n)
-                       elts_head = 0;
-               continue;
-       }
-       if (unlikely(i == 0))
-               return 0;
-       rxq->elts_head = elts_head;
-#ifdef MLX5_PMD_SOFT_COUNTERS
-       /* Increment packets counter. */
-       rxq->stats.ipackets += pkts_ret;
-#endif
-       return pkts_ret;
-}
-
-/**
- * DPDK callback for RX.
- *
- * The following function is the same as mlx5_rx_burst_sp(), except it doesn't
- * manage scattered packets. Improves performance when MRU is lower than the
- * size of the first segment.
- *
- * @param dpdk_rxq
- *   Generic pointer to RX queue structure.
- * @param[out] pkts
- *   Array to store received packets.
- * @param pkts_n
- *   Maximum number of packets in array.
- *
- * @return
- *   Number of packets successfully received (<= pkts_n).
- */
-uint16_t
-mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
-{
-       struct rxq *rxq = (struct rxq *)dpdk_rxq;
-       struct rxq_elt (*elts)[rxq->elts_n] = rxq->elts.no_sp;
-       const unsigned int elts_n = rxq->elts_n;
-       unsigned int elts_head = rxq->elts_head;
-       struct ibv_sge sges[pkts_n];
-       unsigned int i;
-       unsigned int pkts_ret = 0;
-       int ret;
-
-       if (unlikely(rxq->sp))
-               return mlx5_rx_burst_sp(dpdk_rxq, pkts, pkts_n);
-       for (i = 0; (i != pkts_n); ++i) {
-               struct rxq_elt *elt = &(*elts)[elts_head];
-               unsigned int len;
-               struct rte_mbuf *seg = elt->buf;
-               struct rte_mbuf *rep;
-               uint32_t flags;
-               uint16_t vlan_tci;
-
-               /* Sanity checks. */
-               assert(seg != NULL);
-               assert(elts_head < rxq->elts_n);
-               assert(rxq->elts_head < rxq->elts_n);
+               DATA_LEN(rep) = DATA_LEN(seg);
+               PKT_LEN(rep) = PKT_LEN(seg);
+               SET_DATA_OFF(rep, DATA_OFF(seg));
+               NB_SEGS(rep) = NB_SEGS(seg);
+               PORT(rep) = PORT(seg);
+               NEXT(rep) = NULL;
+               (*rxq->elts)[idx] = rep;
                /*
-                * Fetch initial bytes of packet descriptor into a
-                * cacheline while allocating rep.
+                * Fill NIC descriptor with the new buffer.  The lkey and size
+                * of the buffers are already known, only the buffer address
+                * changes.
                 */
-               rte_prefetch0(seg);
-               rte_prefetch0(&seg->cacheline1);
-               ret = rxq->poll(rxq->cq, NULL, NULL, &flags, &vlan_tci);
-               if (unlikely(ret < 0)) {
-                       struct ibv_wc wc;
-                       int wcs_n;
-
-                       DEBUG("rxq=%p, poll_length() failed (ret=%d)",
-                             (void *)rxq, ret);
-                       /* ibv_poll_cq() must be used in case of failure. */
-                       wcs_n = ibv_poll_cq(rxq->cq, 1, &wc);
-                       if (unlikely(wcs_n == 0))
-                               break;
-                       if (unlikely(wcs_n < 0)) {
-                               DEBUG("rxq=%p, ibv_poll_cq() failed (wcs_n=%d)",
-                                     (void *)rxq, wcs_n);
-                               break;
-                       }
-                       assert(wcs_n == 1);
-                       if (unlikely(wc.status != IBV_WC_SUCCESS)) {
-                               /* Whatever, just repost the offending WR. */
-                               DEBUG("rxq=%p, wr_id=%" PRIu64 ": bad work"
-                                     " completion status (%d): %s",
-                                     (void *)rxq, wc.wr_id, wc.status,
-                                     ibv_wc_status_str(wc.status));
-#ifdef MLX5_PMD_SOFT_COUNTERS
-                               /* Increment dropped packets counter. */
-                               ++rxq->stats.idropped;
-#endif
-                               /* Add SGE to array for repost. */
-                               sges[i] = elt->sge;
-                               goto repost;
-                       }
-                       ret = wc.byte_len;
-               }
-               if (ret == 0)
-                       break;
-               assert(ret >= (rxq->crc_present << 2));
-               len = ret - (rxq->crc_present << 2);
-               rep = __rte_mbuf_raw_alloc(rxq->mp);
-               if (unlikely(rep == NULL)) {
-                       /*
-                        * Unable to allocate a replacement mbuf,
-                        * repost WR.
-                        */
-                       DEBUG("rxq=%p: can't allocate a new mbuf",
-                             (void *)rxq);
-                       /* Increment out of memory counters. */
-                       ++rxq->stats.rx_nombuf;
-                       ++rxq->priv->dev->data->rx_mbuf_alloc_failed;
-                       goto repost;
+               wqe->addr = htonll(rte_pktmbuf_mtod(rep, uintptr_t));
+               if (len > DATA_LEN(seg)) {
+                       len -= DATA_LEN(seg);
+                       ++NB_SEGS(pkt);
+                       ++rq_ci;
+                       continue;
                }
-
-               /* Reconfigure sge to use rep instead of seg. */
-               elt->sge.addr = (uintptr_t)rep->buf_addr + RTE_PKTMBUF_HEADROOM;
-               assert(elt->sge.lkey == rxq->mr->lkey);
-               elt->buf = rep;
-
-               /* Add SGE to array for repost. */
-               sges[i] = elt->sge;
-
-               /* Update seg information. */
-               SET_DATA_OFF(seg, RTE_PKTMBUF_HEADROOM);
-               NB_SEGS(seg) = 1;
-               PORT(seg) = rxq->port_id;
-               NEXT(seg) = NULL;
-               PKT_LEN(seg) = len;
                DATA_LEN(seg) = len;
-               if (rxq->csum | rxq->csum_l2tun | rxq->vlan_strip) {
-                       seg->packet_type = rxq_cq_to_pkt_type(flags);
-                       seg->ol_flags = rxq_cq_to_ol_flags(rxq, flags);
-#ifdef HAVE_EXP_DEVICE_ATTR_VLAN_OFFLOADS
-                       if (flags & IBV_EXP_CQ_RX_CVLAN_STRIPPED_V1) {
-                               seg->ol_flags |= PKT_RX_VLAN_PKT;
-                               seg->vlan_tci = vlan_tci;
-                       }
-#endif /* HAVE_EXP_DEVICE_ATTR_VLAN_OFFLOADS */
-               }
-               /* Return packet. */
-               *(pkts++) = seg;
-               ++pkts_ret;
 #ifdef MLX5_PMD_SOFT_COUNTERS
                /* Increment bytes counter. */
-               rxq->stats.ibytes += len;
+               rxq->stats.ibytes += PKT_LEN(pkt);
 #endif
-repost:
-               if (++elts_head >= elts_n)
-                       elts_head = 0;
-               continue;
+               /* Return packet. */
+               *(pkts++) = pkt;
+               pkt = NULL;
+               --pkts_n;
+               ++i;
+skip:
+               /* Align consumer index to the next stride. */
+               rq_ci >>= sges_n;
+               ++rq_ci;
+               rq_ci <<= sges_n;
        }
-       if (unlikely(i == 0))
+       if (unlikely((i == 0) && ((rq_ci >> sges_n) == rxq->rq_ci)))
                return 0;
-       /* Repost WRs. */
-#ifdef DEBUG_RECV
-       DEBUG("%p: reposting %u WRs", (void *)rxq, i);
-#endif
-       ret = rxq->recv(rxq->wq, sges, i);
-       if (unlikely(ret)) {
-               /* Inability to repost WRs is fatal. */
-               DEBUG("%p: recv_burst(): failed (ret=%d)",
-                     (void *)rxq->priv,
-                     ret);
-               abort();
-       }
-       rxq->elts_head = elts_head;
+       /* Update the consumer index. */
+       rxq->rq_ci = rq_ci >> sges_n;
+       rte_wmb();
+       *rxq->cq_db = htonl(rxq->cq_ci);
+       rte_wmb();
+       *rxq->rq_db = htonl(rxq->rq_ci);
 #ifdef MLX5_PMD_SOFT_COUNTERS
        /* Increment packets counter. */
-       rxq->stats.ipackets += pkts_ret;
+       rxq->stats.ipackets += i;
 #endif
-       return pkts_ret;
+       return i;
 }
 
 /**