net: add rte prefix to ether defines
[dpdk.git] / drivers / net / mlx5 / mlx5_rxtx.c
index 3e6ef8b..7174ffc 100644 (file)
@@ -1,34 +1,6 @@
-/*-
- *   BSD LICENSE
- *
- *   Copyright 2015 6WIND S.A.
- *   Copyright 2015 Mellanox.
- *
- *   Redistribution and use in source and binary forms, with or without
- *   modification, are permitted provided that the following conditions
- *   are met:
- *
- *     * Redistributions of source code must retain the above copyright
- *       notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above copyright
- *       notice, this list of conditions and the following disclaimer in
- *       the documentation and/or other materials provided with the
- *       distribution.
- *     * Neither the name of 6WIND S.A. nor the names of its
- *       contributors may be used to endorse or promote products derived
- *       from this software without specific prior written permission.
- *
- *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright 2015 6WIND S.A.
+ * Copyright 2015 Mellanox Technologies, Ltd
  */
 
 #include <assert.h>
@@ -42,8 +14,7 @@
 #pragma GCC diagnostic ignored "-Wpedantic"
 #endif
 #include <infiniband/verbs.h>
-#include <infiniband/mlx5_hw.h>
-#include <infiniband/arch.h>
+#include <infiniband/mlx5dv.h>
 #ifdef PEDANTIC
 #pragma GCC diagnostic error "-Wpedantic"
 #endif
 #include "mlx5_prm.h"
 
 static __rte_always_inline uint32_t
-rxq_cq_to_pkt_type(volatile struct mlx5_cqe *cqe);
+rxq_cq_to_pkt_type(struct mlx5_rxq_data *rxq, volatile struct mlx5_cqe *cqe);
 
 static __rte_always_inline int
-mlx5_rx_poll_len(struct rxq *rxq, volatile struct mlx5_cqe *cqe,
-                uint16_t cqe_cnt, uint32_t *rss_hash);
+mlx5_rx_poll_len(struct mlx5_rxq_data *rxq, volatile struct mlx5_cqe *cqe,
+                uint16_t cqe_cnt, volatile struct mlx5_mini_cqe8 **mcqe);
 
 static __rte_always_inline uint32_t
-rxq_cq_to_ol_flags(struct rxq *rxq, volatile struct mlx5_cqe *cqe);
+rxq_cq_to_ol_flags(volatile struct mlx5_cqe *cqe);
+
+static __rte_always_inline void
+rxq_cq_to_mbuf(struct mlx5_rxq_data *rxq, struct rte_mbuf *pkt,
+              volatile struct mlx5_cqe *cqe, uint32_t rss_hash_res);
+
+static __rte_always_inline void
+mprq_buf_replace(struct mlx5_rxq_data *rxq, uint16_t rq_idx);
 
 uint32_t mlx5_ptype_table[] __rte_cache_aligned = {
        [0xff] = RTE_PTYPE_ALL_MASK, /* Last entry for errored packet. */
 };
 
+uint8_t mlx5_cksum_table[1 << 10] __rte_cache_aligned;
+uint8_t mlx5_swp_types_table[1 << 10] __rte_cache_aligned;
+
 /**
  * Build a table to translate Rx completion flags to packet type.
  *
@@ -98,6 +79,8 @@ mlx5_set_ptype_table(void)
         * bit[6] = tunneled
         * bit[7] = outer_l3_type
         */
+       /* L2 */
+       (*p)[0x00] = RTE_PTYPE_L2_ETHER;
        /* L3 */
        (*p)[0x01] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
                     RTE_PTYPE_L4_NONFRAG;
@@ -113,6 +96,14 @@ mlx5_set_ptype_table(void)
                     RTE_PTYPE_L4_TCP;
        (*p)[0x06] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
                     RTE_PTYPE_L4_TCP;
+       (*p)[0x0d] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_L4_TCP;
+       (*p)[0x0e] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_L4_TCP;
+       (*p)[0x11] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_L4_TCP;
+       (*p)[0x12] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_L4_TCP;
        /* UDP */
        (*p)[0x09] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
                     RTE_PTYPE_L4_UDP;
@@ -131,17 +122,27 @@ mlx5_set_ptype_table(void)
                     RTE_PTYPE_L4_TCP;
        (*p)[0x86] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
                     RTE_PTYPE_L4_TCP;
+       (*p)[0x8d] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_L4_TCP;
+       (*p)[0x8e] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_L4_TCP;
+       (*p)[0x91] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_L4_TCP;
+       (*p)[0x92] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_L4_TCP;
        (*p)[0x89] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
                     RTE_PTYPE_L4_UDP;
        (*p)[0x8a] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
                     RTE_PTYPE_L4_UDP;
        /* Tunneled - L3 */
+       (*p)[0x40] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN;
        (*p)[0x41] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L4_NONFRAG;
        (*p)[0x42] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L4_NONFRAG;
+       (*p)[0xc0] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN;
        (*p)[0xc1] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L4_NONFRAG;
@@ -164,29 +165,121 @@ mlx5_set_ptype_table(void)
        /* Tunneled - TCP */
        (*p)[0x45] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
-                    RTE_PTYPE_L4_TCP;
+                    RTE_PTYPE_INNER_L4_TCP;
        (*p)[0x46] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
-                    RTE_PTYPE_L4_TCP;
+                    RTE_PTYPE_INNER_L4_TCP;
+       (*p)[0x4d] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L4_TCP;
+       (*p)[0x4e] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L4_TCP;
+       (*p)[0x51] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L4_TCP;
+       (*p)[0x52] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L4_TCP;
        (*p)[0xc5] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
-                    RTE_PTYPE_L4_TCP;
+                    RTE_PTYPE_INNER_L4_TCP;
        (*p)[0xc6] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
-                    RTE_PTYPE_L4_TCP;
+                    RTE_PTYPE_INNER_L4_TCP;
+       (*p)[0xcd] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L4_TCP;
+       (*p)[0xce] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L4_TCP;
+       (*p)[0xd1] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L4_TCP;
+       (*p)[0xd2] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+                    RTE_PTYPE_INNER_L4_TCP;
        /* Tunneled - UDP */
        (*p)[0x49] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
-                    RTE_PTYPE_L4_UDP;
+                    RTE_PTYPE_INNER_L4_UDP;
        (*p)[0x4a] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
-                    RTE_PTYPE_L4_UDP;
+                    RTE_PTYPE_INNER_L4_UDP;
        (*p)[0xc9] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
-                    RTE_PTYPE_L4_UDP;
+                    RTE_PTYPE_INNER_L4_UDP;
        (*p)[0xca] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
                     RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
-                    RTE_PTYPE_L4_UDP;
+                    RTE_PTYPE_INNER_L4_UDP;
+}
+
+/**
+ * Build a table to translate packet to checksum type of Verbs.
+ */
+void
+mlx5_set_cksum_table(void)
+{
+       unsigned int i;
+       uint8_t v;
+
+       /*
+        * The index should have:
+        * bit[0] = PKT_TX_TCP_SEG
+        * bit[2:3] = PKT_TX_UDP_CKSUM, PKT_TX_TCP_CKSUM
+        * bit[4] = PKT_TX_IP_CKSUM
+        * bit[8] = PKT_TX_OUTER_IP_CKSUM
+        * bit[9] = tunnel
+        */
+       for (i = 0; i < RTE_DIM(mlx5_cksum_table); ++i) {
+               v = 0;
+               if (i & (1 << 9)) {
+                       /* Tunneled packet. */
+                       if (i & (1 << 8)) /* Outer IP. */
+                               v |= MLX5_ETH_WQE_L3_CSUM;
+                       if (i & (1 << 4)) /* Inner IP. */
+                               v |= MLX5_ETH_WQE_L3_INNER_CSUM;
+                       if (i & (3 << 2 | 1 << 0)) /* L4 or TSO. */
+                               v |= MLX5_ETH_WQE_L4_INNER_CSUM;
+               } else {
+                       /* No tunnel. */
+                       if (i & (1 << 4)) /* IP. */
+                               v |= MLX5_ETH_WQE_L3_CSUM;
+                       if (i & (3 << 2 | 1 << 0)) /* L4 or TSO. */
+                               v |= MLX5_ETH_WQE_L4_CSUM;
+               }
+               mlx5_cksum_table[i] = v;
+       }
+}
+
+/**
+ * Build a table to translate packet type of mbuf to SWP type of Verbs.
+ */
+void
+mlx5_set_swp_types_table(void)
+{
+       unsigned int i;
+       uint8_t v;
+
+       /*
+        * The index should have:
+        * bit[0:1] = PKT_TX_L4_MASK
+        * bit[4] = PKT_TX_IPV6
+        * bit[8] = PKT_TX_OUTER_IPV6
+        * bit[9] = PKT_TX_OUTER_UDP
+        */
+       for (i = 0; i < RTE_DIM(mlx5_swp_types_table); ++i) {
+               v = 0;
+               if (i & (1 << 8))
+                       v |= MLX5_ETH_WQE_L3_OUTER_IPV6;
+               if (i & (1 << 9))
+                       v |= MLX5_ETH_WQE_L4_OUTER_UDP;
+               if (i & (1 << 4))
+                       v |= MLX5_ETH_WQE_L3_INNER_IPV6;
+               if ((i & 3) == (PKT_TX_UDP_CKSUM >> 52))
+                       v |= MLX5_ETH_WQE_L4_INNER_UDP;
+               mlx5_swp_types_table[i] = v;
+       }
 }
 
 /**
@@ -201,7 +294,7 @@ mlx5_set_ptype_table(void)
  *   Size of tailroom.
  */
 static inline size_t
-tx_mlx5_wq_tailroom(struct txq *txq, void *addr)
+tx_mlx5_wq_tailroom(struct mlx5_txq_data *txq, void *addr)
 {
        size_t tailroom;
        tailroom = (uintptr_t)(txq->wqes) +
@@ -245,6 +338,60 @@ mlx5_copy_to_wq(void *dst, const void *src, size_t n,
        return ret;
 }
 
+/**
+ * Inline TSO headers into WQE.
+ *
+ * @return
+ *   0 on success, negative errno value on failure.
+ */
+static int
+inline_tso(struct mlx5_txq_data *txq, struct rte_mbuf *buf,
+          uint32_t *length,
+          uintptr_t *addr,
+          uint16_t *pkt_inline_sz,
+          uint8_t **raw,
+          uint16_t *max_wqe,
+          uint16_t *tso_segsz,
+          uint16_t *tso_header_sz)
+{
+       uintptr_t end = (uintptr_t)(((uintptr_t)txq->wqes) +
+                                   (1 << txq->wqe_n) * MLX5_WQE_SIZE);
+       unsigned int copy_b;
+       uint8_t vlan_sz = (buf->ol_flags & PKT_TX_VLAN_PKT) ? 4 : 0;
+       const uint8_t tunneled = txq->tunnel_en && (buf->ol_flags &
+                                PKT_TX_TUNNEL_MASK);
+       uint16_t n_wqe;
+
+       *tso_segsz = buf->tso_segsz;
+       *tso_header_sz = buf->l2_len + vlan_sz + buf->l3_len + buf->l4_len;
+       if (unlikely(*tso_segsz == 0 || *tso_header_sz == 0)) {
+               txq->stats.oerrors++;
+               return -EINVAL;
+       }
+       if (tunneled)
+               *tso_header_sz += buf->outer_l2_len + buf->outer_l3_len;
+       /* First seg must contain all TSO headers. */
+       if (unlikely(*tso_header_sz > MLX5_MAX_TSO_HEADER) ||
+                    *tso_header_sz > DATA_LEN(buf)) {
+               txq->stats.oerrors++;
+               return -EINVAL;
+       }
+       copy_b = *tso_header_sz - *pkt_inline_sz;
+       if (!copy_b || ((end - (uintptr_t)*raw) < copy_b))
+               return -EAGAIN;
+       n_wqe = (MLX5_WQE_DS(copy_b) - 1 + 3) / 4;
+       if (unlikely(*max_wqe < n_wqe))
+               return -EINVAL;
+       *max_wqe -= n_wqe;
+       rte_memcpy((void *)*raw, (void *)*addr, copy_b);
+       *length -= copy_b;
+       *addr += copy_b;
+       copy_b = MLX5_WQE_DS(copy_b) * MLX5_WQE_DWORD_SIZE;
+       *pkt_inline_sz += copy_b;
+       *raw += copy_b;
+       return 0;
+}
+
 /**
  * DPDK callback to check the status of a tx descriptor.
  *
@@ -259,7 +406,7 @@ mlx5_copy_to_wq(void *dst, const void *src, size_t n,
 int
 mlx5_tx_descriptor_status(void *tx_queue, uint16_t offset)
 {
-       struct txq *txq = tx_queue;
+       struct mlx5_txq_data *txq = tx_queue;
        uint16_t used;
 
        mlx5_tx_complete(txq);
@@ -270,20 +417,17 @@ mlx5_tx_descriptor_status(void *tx_queue, uint16_t offset)
 }
 
 /**
- * DPDK callback to check the status of a rx descriptor.
+ * Internal function to compute the number of used descriptors in an RX queue
  *
- * @param rx_queue
- *   The rx queue.
- * @param[in] offset
- *   The index of the descriptor in the ring.
+ * @param rxq
+ *   The Rx queue.
  *
  * @return
- *   The status of the tx descriptor.
+ *   The number of used rx descriptor.
  */
-int
-mlx5_rx_descriptor_status(void *rx_queue, uint16_t offset)
+static uint32_t
+rx_queue_count(struct mlx5_rxq_data *rxq)
 {
-       struct rxq *rxq = rx_queue;
        struct rxq_zip *zip = &rxq->zip;
        volatile struct mlx5_cqe *cqe;
        const unsigned int cqe_n = (1 << rxq->cqe_n);
@@ -306,7 +450,7 @@ mlx5_rx_descriptor_status(void *rx_queue, uint16_t offset)
 
                op_own = cqe->op_own;
                if (MLX5_CQE_FORMAT(op_own) == MLX5_COMPRESSED)
-                       n = ntohl(cqe->byte_cnt);
+                       n = rte_be_to_cpu_32(cqe->byte_cnt);
                else
                        n = 1;
                cq_ci += n;
@@ -314,11 +458,72 @@ mlx5_rx_descriptor_status(void *rx_queue, uint16_t offset)
                cqe = &(*rxq->cqes)[cq_ci & cqe_cnt];
        }
        used = RTE_MIN(used, (1U << rxq->elts_n) - 1);
-       if (offset < used)
+       return used;
+}
+
+/**
+ * DPDK callback to check the status of a rx descriptor.
+ *
+ * @param rx_queue
+ *   The Rx queue.
+ * @param[in] offset
+ *   The index of the descriptor in the ring.
+ *
+ * @return
+ *   The status of the tx descriptor.
+ */
+int
+mlx5_rx_descriptor_status(void *rx_queue, uint16_t offset)
+{
+       struct mlx5_rxq_data *rxq = rx_queue;
+       struct mlx5_rxq_ctrl *rxq_ctrl =
+                       container_of(rxq, struct mlx5_rxq_ctrl, rxq);
+       struct rte_eth_dev *dev = ETH_DEV(rxq_ctrl->priv);
+
+       if (dev->rx_pkt_burst != mlx5_rx_burst) {
+               rte_errno = ENOTSUP;
+               return -rte_errno;
+       }
+       if (offset >= (1 << rxq->elts_n)) {
+               rte_errno = EINVAL;
+               return -rte_errno;
+       }
+       if (offset < rx_queue_count(rxq))
                return RTE_ETH_RX_DESC_DONE;
        return RTE_ETH_RX_DESC_AVAIL;
 }
 
+/**
+ * DPDK callback to get the number of used descriptors in a RX queue
+ *
+ * @param dev
+ *   Pointer to the device structure.
+ *
+ * @param rx_queue_id
+ *   The Rx queue.
+ *
+ * @return
+ *   The number of used rx descriptor.
+ *   -EINVAL if the queue is invalid
+ */
+uint32_t
+mlx5_rx_queue_count(struct rte_eth_dev *dev, uint16_t rx_queue_id)
+{
+       struct mlx5_priv *priv = dev->data->dev_private;
+       struct mlx5_rxq_data *rxq;
+
+       if (dev->rx_pkt_burst != mlx5_rx_burst) {
+               rte_errno = ENOTSUP;
+               return -rte_errno;
+       }
+       rxq = (*priv->rxqs)[rx_queue_id];
+       if (!rxq) {
+               rte_errno = EINVAL;
+               return -rte_errno;
+       }
+       return rx_queue_count(rxq);
+}
+
 /**
  * DPDK callback for TX.
  *
@@ -335,7 +540,7 @@ mlx5_rx_descriptor_status(void *rx_queue, uint16_t offset)
 uint16_t
 mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
 {
-       struct txq *txq = (struct txq *)dpdk_txq;
+       struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq;
        uint16_t elts_head = txq->elts_head;
        const uint16_t elts_n = 1 << txq->elts_n;
        const uint16_t elts_m = elts_n - 1;
@@ -343,15 +548,12 @@ mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
        unsigned int j = 0;
        unsigned int k = 0;
        uint16_t max_elts;
-       unsigned int max_inline = txq->max_inline;
-       const unsigned int inline_en = !!max_inline && txq->inline_en;
        uint16_t max_wqe;
        unsigned int comp;
-       volatile struct mlx5_wqe_v *wqe = NULL;
        volatile struct mlx5_wqe_ctrl *last_wqe = NULL;
        unsigned int segs_n = 0;
-       struct rte_mbuf *buf = NULL;
-       uint8_t *raw;
+       const unsigned int max_inline = txq->max_inline;
+       uint64_t addr_64;
 
        if (unlikely(!pkts_n))
                return 0;
@@ -364,24 +566,28 @@ mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
        if (unlikely(!max_wqe))
                return 0;
        do {
+               struct rte_mbuf *buf = *pkts; /* First_seg. */
+               uint8_t *raw;
+               volatile struct mlx5_wqe_v *wqe = NULL;
                volatile rte_v128u32_t *dseg = NULL;
                uint32_t length;
                unsigned int ds = 0;
                unsigned int sg = 0; /* counter of additional segs attached. */
                uintptr_t addr;
-               uint64_t naddr;
                uint16_t pkt_inline_sz = MLX5_WQE_DWORD_SIZE + 2;
                uint16_t tso_header_sz = 0;
                uint16_t ehdr;
-               uint8_t cs_flags = 0;
-               uint64_t tso = 0;
+               uint8_t cs_flags;
+               uint8_t tso = txq->tso_en && (buf->ol_flags & PKT_TX_TCP_SEG);
+               uint32_t swp_offsets = 0;
+               uint8_t swp_types = 0;
+               rte_be32_t metadata;
                uint16_t tso_segsz = 0;
 #ifdef MLX5_PMD_SOFT_COUNTERS
                uint32_t total_length = 0;
 #endif
+               int ret;
 
-               /* first_seg */
-               buf = *pkts;
                segs_n = buf->nb_segs;
                /*
                 * Make sure there is enough room to store this packet and
@@ -391,7 +597,7 @@ mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                if (max_elts < segs_n)
                        break;
                max_elts -= segs_n;
-               --segs_n;
+               sg = --segs_n;
                if (unlikely(--max_wqe == 0))
                        break;
                wqe = (volatile struct mlx5_wqe_v *)
@@ -416,28 +622,17 @@ mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                if (pkts_n - i > 1)
                        rte_prefetch0(
                            rte_pktmbuf_mtod(*(pkts + 1), volatile void *));
-               /* Should we enable HW CKSUM offload */
-               if (buf->ol_flags &
-                   (PKT_TX_IP_CKSUM | PKT_TX_TCP_CKSUM | PKT_TX_UDP_CKSUM)) {
-                       const uint64_t is_tunneled = buf->ol_flags &
-                                                    (PKT_TX_TUNNEL_GRE |
-                                                     PKT_TX_TUNNEL_VXLAN);
-
-                       if (is_tunneled && txq->tunnel_en) {
-                               cs_flags = MLX5_ETH_WQE_L3_INNER_CSUM |
-                                          MLX5_ETH_WQE_L4_INNER_CSUM;
-                               if (buf->ol_flags & PKT_TX_OUTER_IP_CKSUM)
-                                       cs_flags |= MLX5_ETH_WQE_L3_CSUM;
-                       } else {
-                               cs_flags = MLX5_ETH_WQE_L3_CSUM |
-                                          MLX5_ETH_WQE_L4_CSUM;
-                       }
-               }
+               cs_flags = txq_ol_cksum_to_cs(buf);
+               txq_mbuf_to_swp(txq, buf, (uint8_t *)&swp_offsets, &swp_types);
                raw = ((uint8_t *)(uintptr_t)wqe) + 2 * MLX5_WQE_DWORD_SIZE;
+               /* Copy metadata from mbuf if valid */
+               metadata = buf->ol_flags & PKT_TX_METADATA ? buf->tx_metadata :
+                                                            0;
                /* Replace the Ethernet type by the VLAN if necessary. */
                if (buf->ol_flags & PKT_TX_VLAN_PKT) {
-                       uint32_t vlan = htonl(0x81000000 | buf->vlan_tci);
-                       unsigned int len = 2 * ETHER_ADDR_LEN - 2;
+                       uint32_t vlan = rte_cpu_to_be_32(0x81000000 |
+                                                        buf->vlan_tci);
+                       unsigned int len = 2 * RTE_ETHER_ADDR_LEN - 2;
 
                        addr += 2;
                        length -= 2;
@@ -457,75 +652,32 @@ mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                        addr += pkt_inline_sz;
                }
                raw += MLX5_WQE_DWORD_SIZE;
-               if (txq->tso_en) {
-                       tso = buf->ol_flags & PKT_TX_TCP_SEG;
-                       if (tso) {
-                               uintptr_t end = (uintptr_t)
-                                               (((uintptr_t)txq->wqes) +
-                                               (1 << txq->wqe_n) *
-                                               MLX5_WQE_SIZE);
-                               unsigned int copy_b;
-                               uint8_t vlan_sz = (buf->ol_flags &
-                                                 PKT_TX_VLAN_PKT) ? 4 : 0;
-                               const uint64_t is_tunneled =
-                                                       buf->ol_flags &
-                                                       (PKT_TX_TUNNEL_GRE |
-                                                        PKT_TX_TUNNEL_VXLAN);
-
-                               tso_header_sz = buf->l2_len + vlan_sz +
-                                               buf->l3_len + buf->l4_len;
-                               tso_segsz = buf->tso_segsz;
-
-                               if (is_tunneled && txq->tunnel_en) {
-                                       tso_header_sz += buf->outer_l2_len +
-                                                        buf->outer_l3_len;
-                                       cs_flags |= MLX5_ETH_WQE_L4_INNER_CSUM;
-                               } else {
-                                       cs_flags |= MLX5_ETH_WQE_L4_CSUM;
-                               }
-                               if (unlikely(tso_header_sz >
-                                            MLX5_MAX_TSO_HEADER)) {
-                                       txq->stats.oerrors++;
-                                       break;
-                               }
-                               copy_b = tso_header_sz - pkt_inline_sz;
-                               /* First seg must contain all headers. */
-                               assert(copy_b <= length);
-                               if (copy_b &&
-                                  ((end - (uintptr_t)raw) > copy_b)) {
-                                       uint16_t n = (MLX5_WQE_DS(copy_b) -
-                                                     1 + 3) / 4;
-
-                                       if (unlikely(max_wqe < n))
-                                               break;
-                                       max_wqe -= n;
-                                       rte_memcpy((void *)raw,
-                                                  (void *)addr, copy_b);
-                                       addr += copy_b;
-                                       length -= copy_b;
-                                       /* Include padding for TSO header. */
-                                       copy_b = MLX5_WQE_DS(copy_b) *
-                                                MLX5_WQE_DWORD_SIZE;
-                                       pkt_inline_sz += copy_b;
-                                       raw += copy_b;
-                               } else {
-                                       /* NOP WQE. */
-                                       wqe->ctrl = (rte_v128u32_t){
-                                                    htonl(txq->wqe_ci << 8),
-                                                    htonl(txq->qp_num_8s | 1),
-                                                    0,
-                                                    0,
-                                       };
-                                       ds = 1;
-                                       total_length = 0;
-                                       k++;
-                                       goto next_wqe;
-                               }
+               if (tso) {
+                       ret = inline_tso(txq, buf, &length,
+                                        &addr, &pkt_inline_sz,
+                                        &raw, &max_wqe,
+                                        &tso_segsz, &tso_header_sz);
+                       if (ret == -EINVAL) {
+                               break;
+                       } else if (ret == -EAGAIN) {
+                               /* NOP WQE. */
+                               wqe->ctrl = (rte_v128u32_t){
+                                       rte_cpu_to_be_32(txq->wqe_ci << 8),
+                                       rte_cpu_to_be_32(txq->qp_num_8s | 1),
+                                       0,
+                                       0,
+                               };
+                               ds = 1;
+#ifdef MLX5_PMD_SOFT_COUNTERS
+                               total_length = 0;
+#endif
+                               k++;
+                               goto next_wqe;
                        }
                }
                /* Inline if enough room. */
-               if (inline_en || tso) {
-                       uint32_t inl;
+               if (max_inline || tso) {
+                       uint32_t inl = 0;
                        uintptr_t end = (uintptr_t)
                                (((uintptr_t)txq->wqes) +
                                 (1 << txq->wqe_n) * MLX5_WQE_SIZE);
@@ -533,13 +685,16 @@ mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                                                   RTE_CACHE_LINE_SIZE -
                                                   (pkt_inline_sz - 2) -
                                                   !!tso * sizeof(inl);
-                       uintptr_t addr_end = (addr + inline_room) &
-                                            ~(RTE_CACHE_LINE_SIZE - 1);
-                       unsigned int copy_b = (addr_end > addr) ?
-                               RTE_MIN((addr_end - addr), length) :
-                               0;
-
-                       if (copy_b && ((end - (uintptr_t)raw) > copy_b)) {
+                       uintptr_t addr_end;
+                       unsigned int copy_b;
+
+pkt_inline:
+                       addr_end = RTE_ALIGN_FLOOR(addr + inline_room,
+                                                  RTE_CACHE_LINE_SIZE);
+                       copy_b = (addr_end > addr) ?
+                                RTE_MIN((addr_end - addr), length) : 0;
+                       if (copy_b && ((end - (uintptr_t)raw) >
+                                      (copy_b + sizeof(inl)))) {
                                /*
                                 * One Dseg remains in the current WQE.  To
                                 * keep the computation positive, it is
@@ -551,7 +706,9 @@ mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                                        break;
                                max_wqe -= n;
                                if (tso) {
-                                       inl = htonl(copy_b | MLX5_INLINE_SEG);
+                                       assert(inl == 0);
+                                       inl = rte_cpu_to_be_32(copy_b |
+                                                              MLX5_INLINE_SEG);
                                        rte_memcpy((void *)raw,
                                                   (void *)&inl, sizeof(inl));
                                        raw += sizeof(inl);
@@ -584,11 +741,27 @@ mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                        } else if (!segs_n) {
                                goto next_pkt;
                        } else {
-                               /* dseg will be advance as part of next_seg */
-                               dseg = (volatile rte_v128u32_t *)
-                                       ((uintptr_t)wqe +
-                                        ((ds - 1) * MLX5_WQE_DWORD_SIZE));
-                               goto next_seg;
+                               /*
+                                * Further inline the next segment only for
+                                * non-TSO packets.
+                                */
+                               if (!tso) {
+                                       raw += copy_b;
+                                       inline_room -= copy_b;
+                               } else {
+                                       inline_room = 0;
+                               }
+                               /* Move to the next segment. */
+                               --segs_n;
+                               buf = buf->next;
+                               assert(buf);
+                               addr = rte_pktmbuf_mtod(buf, uintptr_t);
+                               length = DATA_LEN(buf);
+#ifdef MLX5_PMD_SOFT_COUNTERS
+                               total_length += length;
+#endif
+                               (*txq->elts)[++elts_head & elts_m] = buf;
+                               goto pkt_inline;
                        }
                } else {
                        /*
@@ -600,12 +773,12 @@ mlx5_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                        ds = 3;
 use_dseg:
                        /* Add the remaining packet as a simple ds. */
-                       naddr = htonll(addr);
+                       addr_64 = rte_cpu_to_be_64(addr);
                        *dseg = (rte_v128u32_t){
-                               htonl(length),
+                               rte_cpu_to_be_32(length),
                                mlx5_tx_mb2mr(txq, buf),
-                               naddr,
-                               naddr >> 32,
+                               addr_64,
+                               addr_64 >> 32,
                        };
                        ++ds;
                        if (!segs_n)
@@ -639,50 +812,54 @@ next_seg:
                total_length += length;
 #endif
                /* Store segment information. */
-               naddr = htonll(rte_pktmbuf_mtod(buf, uintptr_t));
+               addr_64 = rte_cpu_to_be_64(rte_pktmbuf_mtod(buf, uintptr_t));
                *dseg = (rte_v128u32_t){
-                       htonl(length),
+                       rte_cpu_to_be_32(length),
                        mlx5_tx_mb2mr(txq, buf),
-                       naddr,
-                       naddr >> 32,
+                       addr_64,
+                       addr_64 >> 32,
                };
                (*txq->elts)[++elts_head & elts_m] = buf;
-               ++sg;
-               /* Advance counter only if all segs are successfully posted. */
-               if (sg < segs_n)
+               if (--segs_n)
                        goto next_seg;
-               else
-                       j += sg;
 next_pkt:
+               if (ds > MLX5_DSEG_MAX) {
+                       txq->stats.oerrors++;
+                       break;
+               }
                ++elts_head;
                ++pkts;
                ++i;
+               j += sg;
                /* Initialize known and common part of the WQE structure. */
                if (tso) {
                        wqe->ctrl = (rte_v128u32_t){
-                               htonl((txq->wqe_ci << 8) | MLX5_OPCODE_TSO),
-                               htonl(txq->qp_num_8s | ds),
+                               rte_cpu_to_be_32((txq->wqe_ci << 8) |
+                                                MLX5_OPCODE_TSO),
+                               rte_cpu_to_be_32(txq->qp_num_8s | ds),
                                0,
                                0,
                        };
                        wqe->eseg = (rte_v128u32_t){
-                               0,
-                               cs_flags | (htons(tso_segsz) << 16),
-                               0,
-                               (ehdr << 16) | htons(tso_header_sz),
+                               swp_offsets,
+                               cs_flags | (swp_types << 8) |
+                               (rte_cpu_to_be_16(tso_segsz) << 16),
+                               metadata,
+                               (ehdr << 16) | rte_cpu_to_be_16(tso_header_sz),
                        };
                } else {
                        wqe->ctrl = (rte_v128u32_t){
-                               htonl((txq->wqe_ci << 8) | MLX5_OPCODE_SEND),
-                               htonl(txq->qp_num_8s | ds),
+                               rte_cpu_to_be_32((txq->wqe_ci << 8) |
+                                                MLX5_OPCODE_SEND),
+                               rte_cpu_to_be_32(txq->qp_num_8s | ds),
                                0,
                                0,
                        };
                        wqe->eseg = (rte_v128u32_t){
-                               0,
-                               cs_flags,
-                               0,
-                               (ehdr << 16) | htons(pkt_inline_sz),
+                               swp_offsets,
+                               cs_flags | (swp_types << 8),
+                               metadata,
+                               (ehdr << 16) | rte_cpu_to_be_16(pkt_inline_sz),
                        };
                }
 next_wqe:
@@ -701,8 +878,10 @@ next_wqe:
        /* Check whether completion threshold has been reached. */
        comp = txq->elts_comp + i + j + k;
        if (comp >= MLX5_TX_COMP_THRESH) {
+               /* A CQE slot must always be available. */
+               assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci));
                /* Request completion on last WQE. */
-               last_wqe->ctrl2 = htonl(8);
+               last_wqe->ctrl2 = rte_cpu_to_be_32(8);
                /* Save elts_head in unused "immediate" field of WQE. */
                last_wqe->ctrl3 = txq->elts_head;
                txq->elts_comp = 0;
@@ -729,7 +908,7 @@ next_wqe:
  *   Packet length.
  */
 static inline void
-mlx5_mpw_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
+mlx5_mpw_new(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw, uint32_t length)
 {
        uint16_t idx = txq->wqe_ci & ((1 << txq->wqe_n) - 1);
        volatile struct mlx5_wqe_data_seg (*dseg)[MLX5_MPW_DSEG_MAX] =
@@ -741,13 +920,14 @@ mlx5_mpw_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
        mpw->len = length;
        mpw->total_len = 0;
        mpw->wqe = (volatile struct mlx5_wqe *)tx_mlx5_wqe(txq, idx);
-       mpw->wqe->eseg.mss = htons(length);
+       mpw->wqe->eseg.mss = rte_cpu_to_be_16(length);
        mpw->wqe->eseg.inline_hdr_sz = 0;
        mpw->wqe->eseg.rsvd0 = 0;
        mpw->wqe->eseg.rsvd1 = 0;
-       mpw->wqe->eseg.rsvd2 = 0;
-       mpw->wqe->ctrl[0] = htonl((MLX5_OPC_MOD_MPW << 24) |
-                                 (txq->wqe_ci << 8) | MLX5_OPCODE_TSO);
+       mpw->wqe->eseg.flow_table_metadata = 0;
+       mpw->wqe->ctrl[0] = rte_cpu_to_be_32((MLX5_OPC_MOD_MPW << 24) |
+                                            (txq->wqe_ci << 8) |
+                                            MLX5_OPCODE_TSO);
        mpw->wqe->ctrl[2] = 0;
        mpw->wqe->ctrl[3] = 0;
        mpw->data.dseg[0] = (volatile struct mlx5_wqe_data_seg *)
@@ -768,7 +948,7 @@ mlx5_mpw_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
  *   Pointer to MPW session structure.
  */
 static inline void
-mlx5_mpw_close(struct txq *txq, struct mlx5_mpw *mpw)
+mlx5_mpw_close(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw)
 {
        unsigned int num = mpw->pkts_n;
 
@@ -776,7 +956,7 @@ mlx5_mpw_close(struct txq *txq, struct mlx5_mpw *mpw)
         * Store size in multiple of 16 bytes. Control and Ethernet segments
         * count as 2.
         */
-       mpw->wqe->ctrl[1] = htonl(txq->qp_num_8s | (2 + num));
+       mpw->wqe->ctrl[1] = rte_cpu_to_be_32(txq->qp_num_8s | (2 + num));
        mpw->state = MLX5_MPW_STATE_CLOSED;
        if (num < 3)
                ++txq->wqe_ci;
@@ -802,7 +982,7 @@ mlx5_mpw_close(struct txq *txq, struct mlx5_mpw *mpw)
 uint16_t
 mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
 {
-       struct txq *txq = (struct txq *)dpdk_txq;
+       struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq;
        uint16_t elts_head = txq->elts_head;
        const uint16_t elts_n = 1 << txq->elts_n;
        const uint16_t elts_m = elts_n - 1;
@@ -830,7 +1010,8 @@ mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                struct rte_mbuf *buf = *(pkts++);
                uint32_t length;
                unsigned int segs_n = buf->nb_segs;
-               uint32_t cs_flags = 0;
+               uint32_t cs_flags;
+               rte_be32_t metadata;
 
                /*
                 * Make sure there is enough room to store this packet and
@@ -846,10 +1027,10 @@ mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                }
                max_elts -= 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;
+               cs_flags = txq_ol_cksum_to_cs(buf);
+               /* Copy metadata from mbuf if valid */
+               metadata = buf->ol_flags & PKT_TX_METADATA ? buf->tx_metadata :
+                                                            0;
                /* Retrieve packet information. */
                length = PKT_LEN(buf);
                assert(length);
@@ -857,6 +1038,7 @@ mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                if ((mpw.state == MLX5_MPW_STATE_OPENED) &&
                    ((mpw.len != length) ||
                     (segs_n != 1) ||
+                    (mpw.wqe->eseg.flow_table_metadata != metadata) ||
                     (mpw.wqe->eseg.cs_flags != cs_flags)))
                        mlx5_mpw_close(txq, &mpw);
                if (mpw.state == MLX5_MPW_STATE_CLOSED) {
@@ -870,6 +1052,7 @@ mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                        max_wqe -= 2;
                        mlx5_mpw_new(txq, &mpw, length);
                        mpw.wqe->eseg.cs_flags = cs_flags;
+                       mpw.wqe->eseg.flow_table_metadata = metadata;
                }
                /* Multi-segment packets must be alone in their MPW. */
                assert((segs_n == 1) || (mpw.pkts_n == 0));
@@ -885,9 +1068,9 @@ mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                        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)),
+                               .byte_count = rte_cpu_to_be_32(DATA_LEN(buf)),
                                .lkey = mlx5_tx_mb2mr(txq, buf),
-                               .addr = htonll(addr),
+                               .addr = rte_cpu_to_be_64(addr),
                        };
 #if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
                        length += DATA_LEN(buf);
@@ -914,8 +1097,10 @@ mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
        if (comp >= MLX5_TX_COMP_THRESH) {
                volatile struct mlx5_wqe *wqe = mpw.wqe;
 
+               /* A CQE slot must always be available. */
+               assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci));
                /* Request completion on last WQE. */
-               wqe->ctrl[2] = htonl(8);
+               wqe->ctrl[2] = rte_cpu_to_be_32(8);
                /* Save elts_head in unused "immediate" field of WQE. */
                wqe->ctrl[3] = elts_head;
                txq->elts_comp = 0;
@@ -945,7 +1130,8 @@ mlx5_tx_burst_mpw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
  *   Packet length.
  */
 static inline void
-mlx5_mpw_inline_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
+mlx5_mpw_inline_new(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw,
+                   uint32_t length)
 {
        uint16_t idx = txq->wqe_ci & ((1 << txq->wqe_n) - 1);
        struct mlx5_wqe_inl_small *inl;
@@ -955,17 +1141,17 @@ mlx5_mpw_inline_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
        mpw->len = length;
        mpw->total_len = 0;
        mpw->wqe = (volatile struct mlx5_wqe *)tx_mlx5_wqe(txq, idx);
-       mpw->wqe->ctrl[0] = htonl((MLX5_OPC_MOD_MPW << 24) |
-                                 (txq->wqe_ci << 8) |
-                                 MLX5_OPCODE_TSO);
+       mpw->wqe->ctrl[0] = rte_cpu_to_be_32((MLX5_OPC_MOD_MPW << 24) |
+                                            (txq->wqe_ci << 8) |
+                                            MLX5_OPCODE_TSO);
        mpw->wqe->ctrl[2] = 0;
        mpw->wqe->ctrl[3] = 0;
-       mpw->wqe->eseg.mss = htons(length);
+       mpw->wqe->eseg.mss = rte_cpu_to_be_16(length);
        mpw->wqe->eseg.inline_hdr_sz = 0;
        mpw->wqe->eseg.cs_flags = 0;
        mpw->wqe->eseg.rsvd0 = 0;
        mpw->wqe->eseg.rsvd1 = 0;
-       mpw->wqe->eseg.rsvd2 = 0;
+       mpw->wqe->eseg.flow_table_metadata = 0;
        inl = (struct mlx5_wqe_inl_small *)
                (((uintptr_t)mpw->wqe) + 2 * MLX5_WQE_DWORD_SIZE);
        mpw->data.raw = (uint8_t *)&inl->raw;
@@ -980,7 +1166,7 @@ mlx5_mpw_inline_new(struct txq *txq, struct mlx5_mpw *mpw, uint32_t length)
  *   Pointer to MPW session structure.
  */
 static inline void
-mlx5_mpw_inline_close(struct txq *txq, struct mlx5_mpw *mpw)
+mlx5_mpw_inline_close(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw)
 {
        unsigned int size;
        struct mlx5_wqe_inl_small *inl = (struct mlx5_wqe_inl_small *)
@@ -991,9 +1177,10 @@ mlx5_mpw_inline_close(struct txq *txq, struct mlx5_mpw *mpw)
         * Store size in multiple of 16 bytes. Control and Ethernet segments
         * count as 2.
         */
-       mpw->wqe->ctrl[1] = htonl(txq->qp_num_8s | MLX5_WQE_DS(size));
+       mpw->wqe->ctrl[1] = rte_cpu_to_be_32(txq->qp_num_8s |
+                                            MLX5_WQE_DS(size));
        mpw->state = MLX5_MPW_STATE_CLOSED;
-       inl->byte_cnt = htonl(mpw->total_len | MLX5_INLINE_SEG);
+       inl->byte_cnt = rte_cpu_to_be_32(mpw->total_len | MLX5_INLINE_SEG);
        txq->wqe_ci += (size + (MLX5_WQE_SIZE - 1)) / MLX5_WQE_SIZE;
 }
 
@@ -1014,7 +1201,7 @@ 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;
+       struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq;
        uint16_t elts_head = txq->elts_head;
        const uint16_t elts_n = 1 << txq->elts_n;
        const uint16_t elts_m = elts_n - 1;
@@ -1053,7 +1240,8 @@ mlx5_tx_burst_mpw_inline(void *dpdk_txq, struct rte_mbuf **pkts,
                uintptr_t addr;
                uint32_t length;
                unsigned int segs_n = buf->nb_segs;
-               uint32_t cs_flags = 0;
+               uint8_t cs_flags;
+               rte_be32_t metadata;
 
                /*
                 * Make sure there is enough room to store this packet and
@@ -1074,22 +1262,24 @@ mlx5_tx_burst_mpw_inline(void *dpdk_txq, struct rte_mbuf **pkts,
                 * iteration.
                 */
                max_wqe = (1u << txq->wqe_n) - (txq->wqe_ci - txq->wqe_pi);
-               /* 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;
+               cs_flags = txq_ol_cksum_to_cs(buf);
+               /* Copy metadata from mbuf if valid */
+               metadata = buf->ol_flags & PKT_TX_METADATA ? buf->tx_metadata :
+                                                            0;
                /* 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->eseg.flow_table_metadata != metadata) ||
                            (mpw.wqe->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->eseg.flow_table_metadata != metadata) ||
                            (mpw.wqe->eseg.cs_flags != cs_flags)) {
                                mlx5_mpw_inline_close(txq, &mpw);
                                inline_room =
@@ -1109,12 +1299,14 @@ mlx5_tx_burst_mpw_inline(void *dpdk_txq, struct rte_mbuf **pkts,
                                max_wqe -= 2;
                                mlx5_mpw_new(txq, &mpw, length);
                                mpw.wqe->eseg.cs_flags = cs_flags;
+                               mpw.wqe->eseg.flow_table_metadata = metadata;
                        } else {
                                if (unlikely(max_wqe < wqe_inl_n))
                                        break;
                                max_wqe -= wqe_inl_n;
                                mlx5_mpw_inline_new(txq, &mpw, length);
                                mpw.wqe->eseg.cs_flags = cs_flags;
+                               mpw.wqe->eseg.flow_table_metadata = metadata;
                        }
                }
                /* Multi-segment packets must be alone in their MPW. */
@@ -1133,9 +1325,10 @@ mlx5_tx_burst_mpw_inline(void *dpdk_txq, struct rte_mbuf **pkts,
                                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)),
+                                       .byte_count =
+                                              rte_cpu_to_be_32(DATA_LEN(buf)),
                                        .lkey = mlx5_tx_mb2mr(txq, buf),
-                                       .addr = htonll(addr),
+                                       .addr = rte_cpu_to_be_64(addr),
                                };
 #if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
                                length += DATA_LEN(buf);
@@ -1206,8 +1399,10 @@ mlx5_tx_burst_mpw_inline(void *dpdk_txq, struct rte_mbuf **pkts,
        if (comp >= MLX5_TX_COMP_THRESH) {
                volatile struct mlx5_wqe *wqe = mpw.wqe;
 
+               /* A CQE slot must always be available. */
+               assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci));
                /* Request completion on last WQE. */
-               wqe->ctrl[2] = htonl(8);
+               wqe->ctrl[2] = rte_cpu_to_be_32(8);
                /* Save elts_head in unused "immediate" field of WQE. */
                wqe->ctrl[3] = elts_head;
                txq->elts_comp = 0;
@@ -1239,7 +1434,7 @@ mlx5_tx_burst_mpw_inline(void *dpdk_txq, struct rte_mbuf **pkts,
  *   Packet length.
  */
 static inline void
-mlx5_empw_new(struct txq *txq, struct mlx5_mpw *mpw, int padding)
+mlx5_empw_new(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw, int padding)
 {
        uint16_t idx = txq->wqe_ci & ((1 << txq->wqe_n) - 1);
 
@@ -1247,9 +1442,10 @@ mlx5_empw_new(struct txq *txq, struct mlx5_mpw *mpw, int padding)
        mpw->pkts_n = 0;
        mpw->total_len = sizeof(struct mlx5_wqe);
        mpw->wqe = (volatile struct mlx5_wqe *)tx_mlx5_wqe(txq, idx);
-       mpw->wqe->ctrl[0] = htonl((MLX5_OPC_MOD_ENHANCED_MPSW << 24) |
-                                 (txq->wqe_ci << 8) |
-                                 MLX5_OPCODE_ENHANCED_MPSW);
+       mpw->wqe->ctrl[0] =
+               rte_cpu_to_be_32((MLX5_OPC_MOD_ENHANCED_MPSW << 24) |
+                                (txq->wqe_ci << 8) |
+                                MLX5_OPCODE_ENHANCED_MPSW);
        mpw->wqe->ctrl[2] = 0;
        mpw->wqe->ctrl[3] = 0;
        memset((void *)(uintptr_t)&mpw->wqe->eseg, 0, MLX5_WQE_DWORD_SIZE);
@@ -1257,9 +1453,9 @@ mlx5_empw_new(struct txq *txq, struct mlx5_mpw *mpw, int padding)
                uintptr_t addr = (uintptr_t)(mpw->wqe + 1);
 
                /* Pad the first 2 DWORDs with zero-length inline header. */
-               *(volatile uint32_t *)addr = htonl(MLX5_INLINE_SEG);
+               *(volatile uint32_t *)addr = rte_cpu_to_be_32(MLX5_INLINE_SEG);
                *(volatile uint32_t *)(addr + MLX5_WQE_DWORD_SIZE) =
-                       htonl(MLX5_INLINE_SEG);
+                       rte_cpu_to_be_32(MLX5_INLINE_SEG);
                mpw->total_len += 2 * MLX5_WQE_DWORD_SIZE;
                /* Start from the next WQEBB. */
                mpw->data.raw = (volatile void *)(tx_mlx5_wqe(txq, idx + 1));
@@ -1280,14 +1476,15 @@ mlx5_empw_new(struct txq *txq, struct mlx5_mpw *mpw, int padding)
  *   Number of consumed WQEs.
  */
 static inline uint16_t
-mlx5_empw_close(struct txq *txq, struct mlx5_mpw *mpw)
+mlx5_empw_close(struct mlx5_txq_data *txq, struct mlx5_mpw *mpw)
 {
        uint16_t ret;
 
        /* Store size in multiple of 16 bytes. Control and Ethernet segments
         * count as 2.
         */
-       mpw->wqe->ctrl[1] = htonl(txq->qp_num_8s | MLX5_WQE_DS(mpw->total_len));
+       mpw->wqe->ctrl[1] = rte_cpu_to_be_32(txq->qp_num_8s |
+                                            MLX5_WQE_DS(mpw->total_len));
        mpw->state = MLX5_MPW_STATE_CLOSED;
        ret = (mpw->total_len + (MLX5_WQE_SIZE - 1)) / MLX5_WQE_SIZE;
        txq->wqe_ci += ret;
@@ -1295,10 +1492,10 @@ mlx5_empw_close(struct txq *txq, struct mlx5_mpw *mpw)
 }
 
 /**
- * DPDK callback for TX with Enhanced MPW support.
+ * TX with Enhanced MPW support.
  *
- * @param dpdk_txq
- *   Generic pointer to TX queue structure.
+ * @param txq
+ *   Pointer to TX queue structure.
  * @param[in] pkts
  *   Packets to transmit.
  * @param pkts_n
@@ -1307,10 +1504,10 @@ mlx5_empw_close(struct txq *txq, struct mlx5_mpw *mpw)
  * @return
  *   Number of packets successfully transmitted (<= pkts_n).
  */
-uint16_t
-mlx5_tx_burst_empw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
+static inline uint16_t
+txq_burst_empw(struct mlx5_txq_data *txq, struct rte_mbuf **pkts,
+              uint16_t pkts_n)
 {
-       struct txq *txq = (struct txq *)dpdk_txq;
        uint16_t elts_head = txq->elts_head;
        const uint16_t elts_n = 1 << txq->elts_n;
        const uint16_t elts_m = elts_n - 1;
@@ -1322,6 +1519,7 @@ mlx5_tx_burst_empw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
        unsigned int mpw_room = 0;
        unsigned int inl_pad = 0;
        uint32_t inl_hdr;
+       uint64_t addr_64;
        struct mlx5_mpw mpw = {
                .state = MLX5_MPW_STATE_CLOSED,
        };
@@ -1331,37 +1529,26 @@ mlx5_tx_burst_empw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
        /* Start processing. */
        mlx5_tx_complete(txq);
        max_elts = (elts_n - (elts_head - txq->elts_tail));
-       /* A CQE slot must always be available. */
-       assert((1u << txq->cqe_n) - (txq->cq_pi - txq->cq_ci));
        max_wqe = (1u << txq->wqe_n) - (txq->wqe_ci - txq->wqe_pi);
        if (unlikely(!max_wqe))
                return 0;
        do {
                struct rte_mbuf *buf = *(pkts++);
                uintptr_t addr;
-               uint64_t naddr;
-               unsigned int n;
                unsigned int do_inline = 0; /* Whether inline is possible. */
                uint32_t length;
-               unsigned int segs_n = buf->nb_segs;
-               uint32_t cs_flags = 0;
+               uint8_t cs_flags;
+               rte_be32_t metadata;
 
-               /*
-                * Make sure there is enough room to store this packet and
-                * that one ring entry remains unused.
-                */
-               assert(segs_n);
-               if (max_elts - j < segs_n)
-                       break;
-               /* Do not bother with large packets MPW cannot handle. */
-               if (segs_n > MLX5_MPW_DSEG_MAX) {
-                       txq->stats.oerrors++;
+               /* Multi-segmented packet is handled in slow-path outside. */
+               assert(NB_SEGS(buf) == 1);
+               /* Make sure there is enough room to store this packet. */
+               if (max_elts - j == 0)
                        break;
-               }
-               /* 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;
+               cs_flags = txq_ol_cksum_to_cs(buf);
+               /* Copy metadata from mbuf if valid */
+               metadata = buf->ol_flags & PKT_TX_METADATA ? buf->tx_metadata :
+                                                            0;
                /* Retrieve packet information. */
                length = PKT_LEN(buf);
                /* Start new session if:
@@ -1369,51 +1556,38 @@ mlx5_tx_burst_empw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                 * - no space left even for a dseg
                 * - next packet can be inlined with a new WQE
                 * - cs_flag differs
-                * It can't be MLX5_MPW_STATE_OPENED as always have a single
-                * segmented packet.
                 */
                if (mpw.state == MLX5_MPW_ENHANCED_STATE_OPENED) {
-                       if ((segs_n != 1) ||
-                           (inl_pad + sizeof(struct mlx5_wqe_data_seg) >
-                             mpw_room) ||
+                       if ((inl_pad + sizeof(struct mlx5_wqe_data_seg) >
+                            mpw_room) ||
                            (length <= txq->inline_max_packet_sz &&
                             inl_pad + sizeof(inl_hdr) + length >
-                             mpw_room) ||
+                            mpw_room) ||
+                            (mpw.wqe->eseg.flow_table_metadata != metadata) ||
                            (mpw.wqe->eseg.cs_flags != cs_flags))
                                max_wqe -= mlx5_empw_close(txq, &mpw);
                }
                if (unlikely(mpw.state == MLX5_MPW_STATE_CLOSED)) {
-                       if (unlikely(segs_n != 1)) {
-                               /* Fall back to legacy MPW.
-                                * A MPW session consumes 2 WQEs at most to
-                                * include MLX5_MPW_DSEG_MAX pointers.
-                                */
-                               if (unlikely(max_wqe < 2))
-                                       break;
-                               mlx5_mpw_new(txq, &mpw, length);
-                       } else {
-                               /* In Enhanced MPW, inline as much as the budget
-                                * is allowed. The remaining space is to be
-                                * filled with dsegs. If the title WQEBB isn't
-                                * padded, it will have 2 dsegs there.
-                                */
-                               mpw_room = RTE_MIN(MLX5_WQE_SIZE_MAX,
-                                           (max_inline ? max_inline :
-                                            pkts_n * MLX5_WQE_DWORD_SIZE) +
-                                           MLX5_WQE_SIZE);
-                               if (unlikely(max_wqe * MLX5_WQE_SIZE <
-                                             mpw_room))
-                                       break;
-                               /* Don't pad the title WQEBB to not waste WQ. */
-                               mlx5_empw_new(txq, &mpw, 0);
-                               mpw_room -= mpw.total_len;
-                               inl_pad = 0;
-                               do_inline =
-                                       length <= txq->inline_max_packet_sz &&
-                                       sizeof(inl_hdr) + length <= mpw_room &&
-                                       !txq->mpw_hdr_dseg;
-                       }
+                       /* In Enhanced MPW, inline as much as the budget is
+                        * allowed. The remaining space is to be filled with
+                        * dsegs. If the title WQEBB isn't padded, it will have
+                        * 2 dsegs there.
+                        */
+                       mpw_room = RTE_MIN(MLX5_WQE_SIZE_MAX,
+                                          (max_inline ? max_inline :
+                                           pkts_n * MLX5_WQE_DWORD_SIZE) +
+                                          MLX5_WQE_SIZE);
+                       if (unlikely(max_wqe * MLX5_WQE_SIZE < mpw_room))
+                               break;
+                       /* Don't pad the title WQEBB to not waste WQ. */
+                       mlx5_empw_new(txq, &mpw, 0);
+                       mpw_room -= mpw.total_len;
+                       inl_pad = 0;
+                       do_inline = length <= txq->inline_max_packet_sz &&
+                                   sizeof(inl_hdr) + length <= mpw_room &&
+                                   !txq->mpw_hdr_dseg;
                        mpw.wqe->eseg.cs_flags = cs_flags;
+                       mpw.wqe->eseg.flow_table_metadata = metadata;
                } else {
                        /* Evaluate whether the next packet can be inlined.
                         * Inlininig is possible when:
@@ -1428,46 +1602,13 @@ mlx5_tx_burst_empw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                                (!txq->mpw_hdr_dseg ||
                                 mpw.total_len >= MLX5_WQE_SIZE);
                }
-               /* Multi-segment packets must be alone in their MPW. */
-               assert((segs_n == 1) || (mpw.pkts_n == 0));
-               if (unlikely(mpw.state == MLX5_MPW_STATE_OPENED)) {
-#if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
-                       length = 0;
-#endif
-                       do {
-                               volatile struct mlx5_wqe_data_seg *dseg;
-
-                               assert(buf);
-                               (*txq->elts)[elts_head++ & elts_m] = 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 = mlx5_tx_mb2mr(txq, buf),
-                                       .addr = htonll(addr),
-                               };
-#if defined(MLX5_PMD_SOFT_COUNTERS) || !defined(NDEBUG)
-                               length += DATA_LEN(buf);
-#endif
-                               buf = buf->next;
-                               ++j;
-                               ++mpw.pkts_n;
-                       } while (--segs_n);
-                       /* A multi-segmented packet takes one MPW session.
-                        * TODO: Pack more multi-segmented packets if possible.
-                        */
-                       mlx5_mpw_close(txq, &mpw);
-                       if (mpw.pkts_n < 3)
-                               max_wqe--;
-                       else
-                               max_wqe -= 2;
-               } else if (do_inline) {
+               if (max_inline && do_inline) {
                        /* Inline packet into WQE. */
                        unsigned int max;
 
                        assert(mpw.state == MLX5_MPW_ENHANCED_STATE_OPENED);
                        assert(length == DATA_LEN(buf));
-                       inl_hdr = htonl(length | MLX5_INLINE_SEG);
+                       inl_hdr = rte_cpu_to_be_32(length | MLX5_INLINE_SEG);
                        addr = rte_pktmbuf_mtod(buf, uintptr_t);
                        mpw.data.raw = (volatile void *)
                                ((uintptr_t)mpw.data.raw + inl_pad);
@@ -1519,16 +1660,13 @@ mlx5_tx_burst_empw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                                        ((uintptr_t)mpw.data.raw +
                                         inl_pad);
                        (*txq->elts)[elts_head++ & elts_m] = buf;
-                       addr = rte_pktmbuf_mtod(buf, uintptr_t);
-                       for (n = 0; n * RTE_CACHE_LINE_SIZE < length; n++)
-                               rte_prefetch2((void *)(addr +
-                                               n * RTE_CACHE_LINE_SIZE));
-                       naddr = htonll(addr);
+                       addr_64 = rte_cpu_to_be_64(rte_pktmbuf_mtod(buf,
+                                                                   uintptr_t));
                        *dseg = (rte_v128u32_t) {
-                               htonl(length),
+                               rte_cpu_to_be_32(length),
                                mlx5_tx_mb2mr(txq, buf),
-                               naddr,
-                               naddr >> 32,
+                               addr_64,
+                               addr_64 >> 32,
                        };
                        mpw.data.raw = (volatile void *)(dseg + 1);
                        mpw.total_len += (inl_pad + sizeof(*dseg));
@@ -1552,13 +1690,14 @@ mlx5_tx_burst_empw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
                         (1 << txq->wqe_n) / MLX5_TX_COMP_THRESH_INLINE_DIV) {
                volatile struct mlx5_wqe *wqe = mpw.wqe;
 
+               /* A CQE slot must always be available. */
+               assert((1u << txq->cqe_n) - (txq->cq_pi++ - txq->cq_ci));
                /* Request completion on last WQE. */
-               wqe->ctrl[2] = htonl(8);
+               wqe->ctrl[2] = rte_cpu_to_be_32(8);
                /* Save elts_head in unused "immediate" field of WQE. */
                wqe->ctrl[3] = elts_head;
                txq->elts_comp = 0;
                txq->mpw_comp = txq->wqe_ci;
-               txq->cq_pi++;
        } else {
                txq->elts_comp += j;
        }
@@ -1568,17 +1707,58 @@ mlx5_tx_burst_empw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
 #endif
        if (mpw.state == MLX5_MPW_ENHANCED_STATE_OPENED)
                mlx5_empw_close(txq, &mpw);
-       else if (mpw.state == MLX5_MPW_STATE_OPENED)
-               mlx5_mpw_close(txq, &mpw);
        /* Ring QP doorbell. */
        mlx5_tx_dbrec(txq, mpw.wqe);
        txq->elts_head = elts_head;
        return i;
 }
 
+/**
+ * DPDK callback for TX with Enhanced MPW 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_empw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
+{
+       struct mlx5_txq_data *txq = (struct mlx5_txq_data *)dpdk_txq;
+       uint16_t nb_tx = 0;
+
+       while (pkts_n > nb_tx) {
+               uint16_t n;
+               uint16_t ret;
+
+               n = txq_count_contig_multi_seg(&pkts[nb_tx], pkts_n - nb_tx);
+               if (n) {
+                       ret = mlx5_tx_burst(dpdk_txq, &pkts[nb_tx], n);
+                       if (!ret)
+                               break;
+                       nb_tx += ret;
+               }
+               n = txq_count_contig_single_seg(&pkts[nb_tx], pkts_n - nb_tx);
+               if (n) {
+                       ret = txq_burst_empw(txq, &pkts[nb_tx], n);
+                       if (!ret)
+                               break;
+                       nb_tx += ret;
+               }
+       }
+       return nb_tx;
+}
+
 /**
  * Translate RX completion flags to packet type.
  *
+ * @param[in] rxq
+ *   Pointer to RX queue structure.
  * @param[in] cqe
  *   Pointer to CQE.
  *
@@ -1588,7 +1768,7 @@ mlx5_tx_burst_empw(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
  *   Packet type for struct rte_mbuf.
  */
 static inline uint32_t
-rxq_cq_to_pkt_type(volatile struct mlx5_cqe *cqe)
+rxq_cq_to_pkt_type(struct mlx5_rxq_data *rxq, volatile struct mlx5_cqe *cqe)
 {
        uint8_t idx;
        uint8_t pinfo = cqe->pkt_info;
@@ -1603,7 +1783,7 @@ rxq_cq_to_pkt_type(volatile struct mlx5_cqe *cqe)
         * bit[7] = outer_l3_type
         */
        idx = ((pinfo & 0x3) << 6) | ((ptype & 0xfc00) >> 10);
-       return mlx5_ptype_table[idx];
+       return mlx5_ptype_table[idx] | rxq->tunnel * !!(idx & (1 << 6));
 }
 
 /**
@@ -1615,16 +1795,17 @@ rxq_cq_to_pkt_type(volatile struct mlx5_cqe *cqe)
  *   Pointer to RX queue.
  * @param cqe
  *   CQE to process.
- * @param[out] rss_hash
- *   Packet RSS Hash result.
+ * @param[out] mcqe
+ *   Store pointer to mini-CQE if compressed. Otherwise, the pointer is not
+ *   written.
  *
  * @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_cqe *cqe,
-                uint16_t cqe_cnt, uint32_t *rss_hash)
+mlx5_rx_poll_len(struct mlx5_rxq_data *rxq, volatile struct mlx5_cqe *cqe,
+                uint16_t cqe_cnt, volatile struct mlx5_mini_cqe8 **mcqe)
 {
        struct rxq_zip *zip = &rxq->zip;
        uint16_t cqe_n = cqe_cnt + 1;
@@ -1637,8 +1818,8 @@ mlx5_rx_poll_len(struct rxq *rxq, volatile struct mlx5_cqe *cqe,
                        (volatile struct mlx5_mini_cqe8 (*)[8])
                        (uintptr_t)(&(*rxq->cqes)[zip->ca & cqe_cnt].pkt_info);
 
-               len = ntohl((*mc)[zip->ai & 7].byte_cnt);
-               *rss_hash = ntohl((*mc)[zip->ai & 7].rx_hash_result);
+               len = rte_be_to_cpu_32((*mc)[zip->ai & 7].byte_cnt);
+               *mcqe = &(*mc)[zip->ai & 7];
                if ((++zip->ai & 7) == 0) {
                        /* Invalidate consumed CQEs */
                        idx = zip->ca;
@@ -1679,6 +1860,7 @@ mlx5_rx_poll_len(struct rxq *rxq, volatile struct mlx5_cqe *cqe,
                        return 0;
                ++rxq->cq_ci;
                op_own = cqe->op_own;
+               rte_cio_rmb();
                if (MLX5_CQE_FORMAT(op_own) == MLX5_COMPRESSED) {
                        volatile struct mlx5_mini_cqe8 (*mc)[8] =
                                (volatile struct mlx5_mini_cqe8 (*)[8])
@@ -1686,7 +1868,7 @@ mlx5_rx_poll_len(struct rxq *rxq, volatile struct mlx5_cqe *cqe,
                                                          cqe_cnt].pkt_info);
 
                        /* Fix endianness. */
-                       zip->cqe_cnt = ntohl(cqe->byte_cnt);
+                       zip->cqe_cnt = rte_be_to_cpu_32(cqe->byte_cnt);
                        /*
                         * Current mini array position is the one returned by
                         * check_cqe64().
@@ -1701,8 +1883,8 @@ mlx5_rx_poll_len(struct rxq *rxq, volatile struct mlx5_cqe *cqe,
                        --rxq->cq_ci;
                        zip->cq_ci = rxq->cq_ci + zip->cqe_cnt;
                        /* Get packet size to return. */
-                       len = ntohl((*mc)[0].byte_cnt);
-                       *rss_hash = ntohl((*mc)[0].rx_hash_result);
+                       len = rte_be_to_cpu_32((*mc)[0].byte_cnt);
+                       *mcqe = &(*mc)[0];
                        zip->ai = 1;
                        /* Prefetch all the entries to be invalidated */
                        idx = zip->ca;
@@ -1712,8 +1894,7 @@ mlx5_rx_poll_len(struct rxq *rxq, volatile struct mlx5_cqe *cqe,
                                ++idx;
                        }
                } else {
-                       len = ntohl(cqe->byte_cnt);
-                       *rss_hash = ntohl(cqe->rx_hash_res);
+                       len = rte_be_to_cpu_32(cqe->byte_cnt);
                }
                /* Error while receiving packet. */
                if (unlikely(MLX5_CQE_OPCODE(op_own) == MLX5_CQE_RESP_ERR))
@@ -1725,8 +1906,6 @@ mlx5_rx_poll_len(struct rxq *rxq, volatile struct mlx5_cqe *cqe,
 /**
  * Translate RX completion flags to offload flags.
  *
- * @param[in] rxq
- *   Pointer to RX queue structure.
  * @param[in] cqe
  *   Pointer to CQE.
  *
@@ -1734,10 +1913,10 @@ mlx5_rx_poll_len(struct rxq *rxq, volatile struct mlx5_cqe *cqe,
  *   Offload flags (ol_flags) for struct rte_mbuf.
  */
 static inline uint32_t
-rxq_cq_to_ol_flags(struct rxq *rxq, volatile struct mlx5_cqe *cqe)
+rxq_cq_to_ol_flags(volatile struct mlx5_cqe *cqe)
 {
        uint32_t ol_flags = 0;
-       uint16_t flags = ntohs(cqe->hdr_type_etc);
+       uint16_t flags = rte_be_to_cpu_16(cqe->hdr_type_etc);
 
        ol_flags =
                TRANSPOSE(flags,
@@ -1746,17 +1925,55 @@ rxq_cq_to_ol_flags(struct rxq *rxq, volatile struct mlx5_cqe *cqe)
                TRANSPOSE(flags,
                          MLX5_CQE_RX_L4_HDR_VALID,
                          PKT_RX_L4_CKSUM_GOOD);
-       if ((cqe->pkt_info & MLX5_CQE_RX_TUNNEL_PACKET) && (rxq->csum_l2tun))
-               ol_flags |=
-                       TRANSPOSE(flags,
-                                 MLX5_CQE_RX_L3_HDR_VALID,
-                                 PKT_RX_IP_CKSUM_GOOD) |
-                       TRANSPOSE(flags,
-                                 MLX5_CQE_RX_L4_HDR_VALID,
-                                 PKT_RX_L4_CKSUM_GOOD);
        return ol_flags;
 }
 
+/**
+ * Fill in mbuf fields from RX completion flags.
+ * Note that pkt->ol_flags should be initialized outside of this function.
+ *
+ * @param rxq
+ *   Pointer to RX queue.
+ * @param pkt
+ *   mbuf to fill.
+ * @param cqe
+ *   CQE to process.
+ * @param rss_hash_res
+ *   Packet RSS Hash result.
+ */
+static inline void
+rxq_cq_to_mbuf(struct mlx5_rxq_data *rxq, struct rte_mbuf *pkt,
+              volatile struct mlx5_cqe *cqe, uint32_t rss_hash_res)
+{
+       /* Update packet information. */
+       pkt->packet_type = rxq_cq_to_pkt_type(rxq, cqe);
+       if (rss_hash_res && rxq->rss_hash) {
+               pkt->hash.rss = rss_hash_res;
+               pkt->ol_flags |= PKT_RX_RSS_HASH;
+       }
+       if (rxq->mark && MLX5_FLOW_MARK_IS_VALID(cqe->sop_drop_qpn)) {
+               pkt->ol_flags |= PKT_RX_FDIR;
+               if (cqe->sop_drop_qpn !=
+                   rte_cpu_to_be_32(MLX5_FLOW_MARK_DEFAULT)) {
+                       uint32_t mark = cqe->sop_drop_qpn;
+
+                       pkt->ol_flags |= PKT_RX_FDIR_ID;
+                       pkt->hash.fdir.hi = mlx5_flow_mark_get(mark);
+               }
+       }
+       if (rxq->csum)
+               pkt->ol_flags |= rxq_cq_to_ol_flags(cqe);
+       if (rxq->vlan_strip &&
+           (cqe->hdr_type_etc & rte_cpu_to_be_16(MLX5_CQE_VLAN_STRIPPED))) {
+               pkt->ol_flags |= PKT_RX_VLAN | PKT_RX_VLAN_STRIPPED;
+               pkt->vlan_tci = rte_be_to_cpu_16(cqe->vlan_info);
+       }
+       if (rxq->hw_timestamp) {
+               pkt->timestamp = rte_be_to_cpu_64(cqe->timestamp);
+               pkt->ol_flags |= PKT_RX_TIMESTAMP;
+       }
+}
+
 /**
  * DPDK callback for RX.
  *
@@ -1773,7 +1990,7 @@ rxq_cq_to_ol_flags(struct rxq *rxq, volatile struct mlx5_cqe *cqe)
 uint16_t
 mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
 {
-       struct rxq *rxq = dpdk_rxq;
+       struct mlx5_rxq_data *rxq = dpdk_rxq;
        const unsigned int wqe_cnt = (1 << rxq->elts_n) - 1;
        const unsigned int cqe_cnt = (1 << rxq->cqe_n) - 1;
        const unsigned int sges_n = rxq->sges_n;
@@ -1787,9 +2004,11 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
 
        while (pkts_n) {
                unsigned int idx = rq_ci & wqe_cnt;
-               volatile struct mlx5_wqe_data_seg *wqe = &(*rxq->wqes)[idx];
+               volatile struct mlx5_wqe_data_seg *wqe =
+                       &((volatile struct mlx5_wqe_data_seg *)rxq->wqes)[idx];
                struct rte_mbuf *rep = (*rxq->elts)[idx];
-               uint32_t rss_hash_res = 0;
+               volatile struct mlx5_mini_cqe8 *mcqe = NULL;
+               uint32_t rss_hash_res;
 
                if (pkt)
                        NEXT(seg) = rep;
@@ -1819,8 +2038,7 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
                }
                if (!pkt) {
                        cqe = &(*rxq->cqes)[rxq->cq_ci & cqe_cnt];
-                       len = mlx5_rx_poll_len(rxq, cqe, cqe_cnt,
-                                              &rss_hash_res);
+                       len = mlx5_rx_poll_len(rxq, cqe, cqe_cnt, &mcqe);
                        if (!len) {
                                rte_mbuf_raw_free(rep);
                                break;
@@ -1833,36 +2051,14 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
                        }
                        pkt = seg;
                        assert(len >= (rxq->crc_present << 2));
-                       /* Update packet information. */
-                       pkt->packet_type = rxq_cq_to_pkt_type(cqe);
                        pkt->ol_flags = 0;
-                       if (rss_hash_res && rxq->rss_hash) {
-                               pkt->hash.rss = rss_hash_res;
-                               pkt->ol_flags = PKT_RX_RSS_HASH;
-                       }
-                       if (rxq->mark &&
-                           MLX5_FLOW_MARK_IS_VALID(cqe->sop_drop_qpn)) {
-                               pkt->ol_flags |= PKT_RX_FDIR;
-                               if (cqe->sop_drop_qpn !=
-                                   htonl(MLX5_FLOW_MARK_DEFAULT)) {
-                                       uint32_t mark = cqe->sop_drop_qpn;
-
-                                       pkt->ol_flags |= PKT_RX_FDIR_ID;
-                                       pkt->hash.fdir.hi =
-                                               mlx5_flow_mark_get(mark);
-                               }
-                       }
-                       if (rxq->csum | rxq->csum_l2tun)
-                               pkt->ol_flags |= rxq_cq_to_ol_flags(rxq, cqe);
-                       if (rxq->vlan_strip &&
-                           (cqe->hdr_type_etc &
-                            htons(MLX5_CQE_VLAN_STRIPPED))) {
-                               pkt->ol_flags |= PKT_RX_VLAN_PKT |
-                                       PKT_RX_VLAN_STRIPPED;
-                               pkt->vlan_tci = ntohs(cqe->vlan_info);
-                       }
+                       /* If compressed, take hash result from mini-CQE. */
+                       rss_hash_res = rte_be_to_cpu_32(mcqe == NULL ?
+                                                       cqe->rx_hash_res :
+                                                       mcqe->rx_hash_result);
+                       rxq_cq_to_mbuf(rxq, pkt, cqe, rss_hash_res);
                        if (rxq->crc_present)
-                               len -= ETHER_CRC_LEN;
+                               len -= RTE_ETHER_CRC_LEN;
                        PKT_LEN(pkt) = len;
                }
                DATA_LEN(rep) = DATA_LEN(seg);
@@ -1875,7 +2071,10 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
                 * of the buffers are already known, only the buffer address
                 * changes.
                 */
-               wqe->addr = htonll(rte_pktmbuf_mtod(rep, uintptr_t));
+               wqe->addr = rte_cpu_to_be_64(rte_pktmbuf_mtod(rep, uintptr_t));
+               /* If there's only one MR, no need to replace LKey in WQE. */
+               if (unlikely(mlx5_mr_btree_len(&rxq->mr_ctrl.cache_bh) > 1))
+                       wqe->lkey = mlx5_rx_mb2mr(rxq, rep);
                if (len > DATA_LEN(seg)) {
                        len -= DATA_LEN(seg);
                        ++NB_SEGS(pkt);
@@ -1902,10 +2101,250 @@ skip:
                return 0;
        /* 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);
+       rte_cio_wmb();
+       *rxq->cq_db = rte_cpu_to_be_32(rxq->cq_ci);
+       rte_cio_wmb();
+       *rxq->rq_db = rte_cpu_to_be_32(rxq->rq_ci);
+#ifdef MLX5_PMD_SOFT_COUNTERS
+       /* Increment packets counter. */
+       rxq->stats.ipackets += i;
+#endif
+       return i;
+}
+
+void
+mlx5_mprq_buf_free_cb(void *addr __rte_unused, void *opaque)
+{
+       struct mlx5_mprq_buf *buf = opaque;
+
+       if (rte_atomic16_read(&buf->refcnt) == 1) {
+               rte_mempool_put(buf->mp, buf);
+       } else if (rte_atomic16_add_return(&buf->refcnt, -1) == 0) {
+               rte_atomic16_set(&buf->refcnt, 1);
+               rte_mempool_put(buf->mp, buf);
+       }
+}
+
+void
+mlx5_mprq_buf_free(struct mlx5_mprq_buf *buf)
+{
+       mlx5_mprq_buf_free_cb(NULL, buf);
+}
+
+static inline void
+mprq_buf_replace(struct mlx5_rxq_data *rxq, uint16_t rq_idx)
+{
+       struct mlx5_mprq_buf *rep = rxq->mprq_repl;
+       volatile struct mlx5_wqe_data_seg *wqe =
+               &((volatile struct mlx5_wqe_mprq *)rxq->wqes)[rq_idx].dseg;
+       void *addr;
+
+       assert(rep != NULL);
+       /* Replace MPRQ buf. */
+       (*rxq->mprq_bufs)[rq_idx] = rep;
+       /* Replace WQE. */
+       addr = mlx5_mprq_buf_addr(rep);
+       wqe->addr = rte_cpu_to_be_64((uintptr_t)addr);
+       /* If there's only one MR, no need to replace LKey in WQE. */
+       if (unlikely(mlx5_mr_btree_len(&rxq->mr_ctrl.cache_bh) > 1))
+               wqe->lkey = mlx5_rx_addr2mr(rxq, (uintptr_t)addr);
+       /* Stash a mbuf for next replacement. */
+       if (likely(!rte_mempool_get(rxq->mprq_mp, (void **)&rep)))
+               rxq->mprq_repl = rep;
+       else
+               rxq->mprq_repl = NULL;
+}
+
+/**
+ * DPDK callback for RX with Multi-Packet RQ support.
+ *
+ * @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_mprq(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
+{
+       struct mlx5_rxq_data *rxq = dpdk_rxq;
+       const unsigned int strd_n = 1 << rxq->strd_num_n;
+       const unsigned int strd_sz = 1 << rxq->strd_sz_n;
+       const unsigned int strd_shift =
+               MLX5_MPRQ_STRIDE_SHIFT_BYTE * rxq->strd_shift_en;
+       const unsigned int cq_mask = (1 << rxq->cqe_n) - 1;
+       const unsigned int wq_mask = (1 << rxq->elts_n) - 1;
+       volatile struct mlx5_cqe *cqe = &(*rxq->cqes)[rxq->cq_ci & cq_mask];
+       unsigned int i = 0;
+       uint32_t rq_ci = rxq->rq_ci;
+       uint16_t consumed_strd = rxq->consumed_strd;
+       struct mlx5_mprq_buf *buf = (*rxq->mprq_bufs)[rq_ci & wq_mask];
+
+       while (i < pkts_n) {
+               struct rte_mbuf *pkt;
+               void *addr;
+               int ret;
+               unsigned int len;
+               uint16_t strd_cnt;
+               uint16_t strd_idx;
+               uint32_t offset;
+               uint32_t byte_cnt;
+               volatile struct mlx5_mini_cqe8 *mcqe = NULL;
+               uint32_t rss_hash_res = 0;
+
+               if (consumed_strd == strd_n) {
+                       /* Replace WQE only if the buffer is still in use. */
+                       if (rte_atomic16_read(&buf->refcnt) > 1) {
+                               mprq_buf_replace(rxq, rq_ci & wq_mask);
+                               /* Release the old buffer. */
+                               mlx5_mprq_buf_free(buf);
+                       } else if (unlikely(rxq->mprq_repl == NULL)) {
+                               struct mlx5_mprq_buf *rep;
+
+                               /*
+                                * Currently, the MPRQ mempool is out of buffer
+                                * and doing memcpy regardless of the size of Rx
+                                * packet. Retry allocation to get back to
+                                * normal.
+                                */
+                               if (!rte_mempool_get(rxq->mprq_mp,
+                                                    (void **)&rep))
+                                       rxq->mprq_repl = rep;
+                       }
+                       /* Advance to the next WQE. */
+                       consumed_strd = 0;
+                       ++rq_ci;
+                       buf = (*rxq->mprq_bufs)[rq_ci & wq_mask];
+               }
+               cqe = &(*rxq->cqes)[rxq->cq_ci & cq_mask];
+               ret = mlx5_rx_poll_len(rxq, cqe, cq_mask, &mcqe);
+               if (!ret)
+                       break;
+               if (unlikely(ret == -1)) {
+                       /* RX error, packet is likely too large. */
+                       ++rxq->stats.idropped;
+                       continue;
+               }
+               byte_cnt = ret;
+               strd_cnt = (byte_cnt & MLX5_MPRQ_STRIDE_NUM_MASK) >>
+                          MLX5_MPRQ_STRIDE_NUM_SHIFT;
+               assert(strd_cnt);
+               consumed_strd += strd_cnt;
+               if (byte_cnt & MLX5_MPRQ_FILLER_MASK)
+                       continue;
+               if (mcqe == NULL) {
+                       rss_hash_res = rte_be_to_cpu_32(cqe->rx_hash_res);
+                       strd_idx = rte_be_to_cpu_16(cqe->wqe_counter);
+               } else {
+                       /* mini-CQE for MPRQ doesn't have hash result. */
+                       strd_idx = rte_be_to_cpu_16(mcqe->stride_idx);
+               }
+               assert(strd_idx < strd_n);
+               assert(!((rte_be_to_cpu_16(cqe->wqe_id) ^ rq_ci) & wq_mask));
+               /*
+                * Currently configured to receive a packet per a stride. But if
+                * MTU is adjusted through kernel interface, device could
+                * consume multiple strides without raising an error. In this
+                * case, the packet should be dropped because it is bigger than
+                * the max_rx_pkt_len.
+                */
+               if (unlikely(strd_cnt > 1)) {
+                       ++rxq->stats.idropped;
+                       continue;
+               }
+               pkt = rte_pktmbuf_alloc(rxq->mp);
+               if (unlikely(pkt == NULL)) {
+                       ++rxq->stats.rx_nombuf;
+                       break;
+               }
+               len = (byte_cnt & MLX5_MPRQ_LEN_MASK) >> MLX5_MPRQ_LEN_SHIFT;
+               assert((int)len >= (rxq->crc_present << 2));
+               if (rxq->crc_present)
+                       len -= RTE_ETHER_CRC_LEN;
+               offset = strd_idx * strd_sz + strd_shift;
+               addr = RTE_PTR_ADD(mlx5_mprq_buf_addr(buf), offset);
+               /* Initialize the offload flag. */
+               pkt->ol_flags = 0;
+               /*
+                * Memcpy packets to the target mbuf if:
+                * - The size of packet is smaller than mprq_max_memcpy_len.
+                * - Out of buffer in the Mempool for Multi-Packet RQ.
+                */
+               if (len <= rxq->mprq_max_memcpy_len || rxq->mprq_repl == NULL) {
+                       /*
+                        * When memcpy'ing packet due to out-of-buffer, the
+                        * packet must be smaller than the target mbuf.
+                        */
+                       if (unlikely(rte_pktmbuf_tailroom(pkt) < len)) {
+                               rte_pktmbuf_free_seg(pkt);
+                               ++rxq->stats.idropped;
+                               continue;
+                       }
+                       rte_memcpy(rte_pktmbuf_mtod(pkt, void *), addr, len);
+               } else {
+                       rte_iova_t buf_iova;
+                       struct rte_mbuf_ext_shared_info *shinfo;
+                       uint16_t buf_len = strd_cnt * strd_sz;
+
+                       /* Increment the refcnt of the whole chunk. */
+                       rte_atomic16_add_return(&buf->refcnt, 1);
+                       assert((uint16_t)rte_atomic16_read(&buf->refcnt) <=
+                              strd_n + 1);
+                       addr = RTE_PTR_SUB(addr, RTE_PKTMBUF_HEADROOM);
+                       /*
+                        * MLX5 device doesn't use iova but it is necessary in a
+                        * case where the Rx packet is transmitted via a
+                        * different PMD.
+                        */
+                       buf_iova = rte_mempool_virt2iova(buf) +
+                                  RTE_PTR_DIFF(addr, buf);
+                       shinfo = rte_pktmbuf_ext_shinfo_init_helper(addr,
+                                       &buf_len, mlx5_mprq_buf_free_cb, buf);
+                       /*
+                        * EXT_ATTACHED_MBUF will be set to pkt->ol_flags when
+                        * attaching the stride to mbuf and more offload flags
+                        * will be added below by calling rxq_cq_to_mbuf().
+                        * Other fields will be overwritten.
+                        */
+                       rte_pktmbuf_attach_extbuf(pkt, addr, buf_iova, buf_len,
+                                                 shinfo);
+                       rte_pktmbuf_reset_headroom(pkt);
+                       assert(pkt->ol_flags == EXT_ATTACHED_MBUF);
+                       /*
+                        * Prevent potential overflow due to MTU change through
+                        * kernel interface.
+                        */
+                       if (unlikely(rte_pktmbuf_tailroom(pkt) < len)) {
+                               rte_pktmbuf_free_seg(pkt);
+                               ++rxq->stats.idropped;
+                               continue;
+                       }
+               }
+               rxq_cq_to_mbuf(rxq, pkt, cqe, rss_hash_res);
+               PKT_LEN(pkt) = len;
+               DATA_LEN(pkt) = len;
+               PORT(pkt) = rxq->port_id;
+#ifdef MLX5_PMD_SOFT_COUNTERS
+               /* Increment bytes counter. */
+               rxq->stats.ibytes += PKT_LEN(pkt);
+#endif
+               /* Return packet. */
+               *(pkts++) = pkt;
+               ++i;
+       }
+       /* Update the consumer indexes. */
+       rxq->consumed_strd = consumed_strd;
+       rte_cio_wmb();
+       *rxq->cq_db = rte_cpu_to_be_32(rxq->cq_ci);
+       if (rq_ci != rxq->rq_ci) {
+               rxq->rq_ci = rq_ci;
+               rte_cio_wmb();
+               *rxq->rq_db = rte_cpu_to_be_32(rxq->rq_ci);
+       }
 #ifdef MLX5_PMD_SOFT_COUNTERS
        /* Increment packets counter. */
        rxq->stats.ipackets += i;
@@ -1930,11 +2369,11 @@ skip:
  *   Number of packets successfully transmitted (<= pkts_n).
  */
 uint16_t
-removed_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
+removed_tx_burst(void *dpdk_txq __rte_unused,
+                struct rte_mbuf **pkts __rte_unused,
+                uint16_t pkts_n __rte_unused)
 {
-       (void)dpdk_txq;
-       (void)pkts;
-       (void)pkts_n;
+       rte_mb();
        return 0;
 }
 
@@ -1955,11 +2394,11 @@ removed_tx_burst(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
  *   Number of packets successfully received (<= pkts_n).
  */
 uint16_t
-removed_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
+removed_rx_burst(void *dpdk_txq __rte_unused,
+                struct rte_mbuf **pkts __rte_unused,
+                uint16_t pkts_n __rte_unused)
 {
-       (void)dpdk_rxq;
-       (void)pkts;
-       (void)pkts_n;
+       rte_mb();
        return 0;
 }
 
@@ -1970,57 +2409,50 @@ removed_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
  * (e.g.  mlx5_rxtx_vec_sse.c for x86).
  */
 
-uint16_t __attribute__((weak))
-mlx5_tx_burst_raw_vec(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
+__rte_weak uint16_t
+mlx5_tx_burst_raw_vec(void *dpdk_txq __rte_unused,
+                     struct rte_mbuf **pkts __rte_unused,
+                     uint16_t pkts_n __rte_unused)
 {
-       (void)dpdk_txq;
-       (void)pkts;
-       (void)pkts_n;
        return 0;
 }
 
-uint16_t __attribute__((weak))
-mlx5_tx_burst_vec(void *dpdk_txq, struct rte_mbuf **pkts, uint16_t pkts_n)
+__rte_weak uint16_t
+mlx5_tx_burst_vec(void *dpdk_txq __rte_unused,
+                 struct rte_mbuf **pkts __rte_unused,
+                 uint16_t pkts_n __rte_unused)
 {
-       (void)dpdk_txq;
-       (void)pkts;
-       (void)pkts_n;
        return 0;
 }
 
-uint16_t __attribute__((weak))
-mlx5_rx_burst_vec(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
+__rte_weak uint16_t
+mlx5_rx_burst_vec(void *dpdk_txq __rte_unused,
+                 struct rte_mbuf **pkts __rte_unused,
+                 uint16_t pkts_n __rte_unused)
 {
-       (void)dpdk_rxq;
-       (void)pkts;
-       (void)pkts_n;
        return 0;
 }
 
-int __attribute__((weak))
-priv_check_raw_vec_tx_support(struct priv *priv)
+__rte_weak int
+mlx5_check_raw_vec_tx_support(struct rte_eth_dev *dev __rte_unused)
 {
-       (void)priv;
        return -ENOTSUP;
 }
 
-int __attribute__((weak))
-priv_check_vec_tx_support(struct priv *priv)
+__rte_weak int
+mlx5_check_vec_tx_support(struct rte_eth_dev *dev __rte_unused)
 {
-       (void)priv;
        return -ENOTSUP;
 }
 
-int __attribute__((weak))
-rxq_check_vec_support(struct rxq *rxq)
+__rte_weak int
+mlx5_rxq_check_vec_support(struct mlx5_rxq_data *rxq __rte_unused)
 {
-       (void)rxq;
        return -ENOTSUP;
 }
 
-int __attribute__((weak))
-priv_check_vec_rx_support(struct priv *priv)
+__rte_weak int
+mlx5_check_vec_rx_support(struct rte_eth_dev *dev __rte_unused)
 {
-       (void)priv;
        return -ENOTSUP;
 }