This patch optimizes the ZXDH PMD's receive and transmit path for better<br />performance through several improvements:<br /> <br />- Add simple TX/RX burst functions (zxdh_xmit_pkts_simple and<br />  zxdh_recv_single_pkts) for single-segment packet scenarios.<br />- Remove RX software ring (sw_ring) to reduce memory allocation and<br />  copy.<br />- Optimize descriptor management with prefetching and simplified<br />  cleanup.<br />- Reorganize structure fields for better cache locality.<br /> <br />These changes reduce CPU cycles and memory bandwidth consumption,<br />resulting in improved packet processing throughput.<br /> <br />Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn> <br />---<br /> drivers/net/zxdh/zxdh_ethdev.c     |  95 +++---<br /> drivers/net/zxdh/zxdh_ethdev_ops.c |  24 +-<br /> drivers/net/zxdh/zxdh_ethdev_ops.h |   4 +<br /> drivers/net/zxdh/zxdh_pci.c        |   2 +-<br /> drivers/net/zxdh/zxdh_queue.c      |  11 +-<br /> drivers/net/zxdh/zxdh_queue.h      | 120 ++++---<br /> drivers/net/zxdh/zxdh_rxtx.c       | 518 +++++++++++++++++++++--------<br /> drivers/net/zxdh/zxdh_rxtx.h       |  27 +-<br /> 8 files changed, 534 insertions(+), 267 deletions(-)<br /> <br />diff --git a/drivers/net/zxdh/zxdh_ethdev.c b/drivers/net/zxdh/zxdh_ethdev.c<br />index aeb01f4652..a5238fc6f8 100644<br />--- a/drivers/net/zxdh/zxdh_ethdev.c<br />+++ b/drivers/net/zxdh/zxdh_ethdev.c<br />@@ -490,7 +490,7 @@ zxdh_dev_free_mbufs(struct rte_eth_dev *dev)<br />         if (!vq)<br />             continue;<br />         while ((buf = zxdh_queue_detach_unused(vq)) != NULL)<br />-            rte_pktmbuf_free(buf);<br />+            rte_pktmbuf_free_seg(buf);<br />         PMD_DRV_LOG(DEBUG, "freeing %s[%d] used and unused buf",<br />         "rxq", i * 2);<br />     }<br />@@ -499,7 +499,7 @@ zxdh_dev_free_mbufs(struct rte_eth_dev *dev)<br />         if (!vq)<br />             continue;<br />         while ((buf = zxdh_queue_detach_unused(vq)) != NULL)<br />-            rte_pktmbuf_free(buf);<br />+            rte_pktmbuf_free_seg(buf);<br />         PMD_DRV_LOG(DEBUG, "freeing %s[%d] used and unused buf",<br />         "txq", i * 2 + 1);<br />     }<br />@@ -644,7 +644,6 @@ zxdh_init_queue(struct rte_eth_dev *dev, uint16_t vtpci_logic_qidx)<br />     struct zxdh_virtnet_tx *txvq = NULL;<br />     struct zxdh_virtqueue *vq = NULL;<br />     size_t sz_hdr_mz = 0;<br />-    void *sw_ring = NULL;<br />     int32_t queue_type = zxdh_get_queue_type(vtpci_logic_qidx);<br />     int32_t numa_node = dev->device->numa_node;<br />     uint16_t vtpci_phy_qidx = 0;<br />@@ -692,11 +691,10 @@ zxdh_init_queue(struct rte_eth_dev *dev, uint16_t vtpci_logic_qidx)<br />     vq->vq_queue_index = vtpci_phy_qidx;<br />     vq->vq_nentries = vq_size;<br />  <br />-    vq->vq_packed.used_wrap_counter = 1;<br />-    vq->vq_packed.cached_flags = ZXDH_VRING_PACKED_DESC_F_AVAIL;<br />-    vq->vq_packed.event_flags_shadow = 0;<br />+    vq->used_wrap_counter = 1;<br />+    vq->cached_flags = ZXDH_VRING_PACKED_DESC_F_AVAIL;<br />     if (queue_type == ZXDH_VTNET_RQ)<br />-        vq->vq_packed.cached_flags |= ZXDH_VRING_DESC_F_WRITE;<br />+        vq->cached_flags |= ZXDH_VRING_DESC_F_WRITE;<br />  <br />     /*<br />      * Reserve a memzone for vring elements<br />@@ -741,46 +739,28 @@ zxdh_init_queue(struct rte_eth_dev *dev, uint16_t vtpci_logic_qidx)<br />     }<br />  <br />     if (queue_type == ZXDH_VTNET_RQ) {<br />-        size_t sz_sw = (ZXDH_MBUF_BURST_SZ + vq_size) * sizeof(vq->sw_ring[0]);<br />-<br />-        sw_ring = rte_zmalloc_socket("sw_ring", sz_sw, RTE_CACHE_LINE_SIZE, numa_node);<br />-        if (!sw_ring) {<br />-            PMD_DRV_LOG(ERR, "can not allocate RX soft ring");<br />-            ret = -ENOMEM;<br />-            goto fail_q_alloc;<br />-        }<br />-<br />-        vq->sw_ring = sw_ring;<br />         rxvq = &vq->rxq;<br />         rxvq->vq = vq;<br />         rxvq->port_id = dev->data->port_id;<br />         rxvq->mz = mz;<br />     } else {             /* queue_type == VTNET_TQ */<br />+        if (hdr_mz == NULL) {<br />+            ret = -ENOMEM;<br />+            PMD_DRV_LOG(ERR, "can not allocate TX soft ring: %d", ret);<br />+            goto fail_q_alloc;<br />+        }<br />         txvq = &vq->txq;<br />         txvq->vq = vq;<br />         txvq->port_id = dev->data->port_id;<br />         txvq->mz = mz;<br />         txvq->zxdh_net_hdr_mz = hdr_mz;<br />-        txvq->zxdh_net_hdr_mem = hdr_mz->iova;<br />+        if (hdr_mz)<br />+            txvq->zxdh_net_hdr_mem = hdr_mz->iova;<br />     }<br />  <br />-    vq->offset = offsetof(struct rte_mbuf, buf_iova);<br />-    if (queue_type == ZXDH_VTNET_TQ) {<br />+    if (queue_type == ZXDH_VTNET_TQ && hdr_mz) {<br />         struct zxdh_tx_region *txr = hdr_mz->addr;<br />-        uint32_t i;<br />-<br />         memset(txr, 0, vq_size * sizeof(*txr));<br />-        for (i = 0; i < vq_size; i++) {<br />-            /* first indirect descriptor is always the tx header */<br />-            struct zxdh_vring_packed_desc *start_dp = txr[i].tx_packed_indir;<br />-<br />-            zxdh_vring_desc_init_indirect_packed(start_dp,<br />-                    RTE_DIM(txr[i].tx_packed_indir));<br />-            start_dp->addr = txvq->zxdh_net_hdr_mem + i * sizeof(*txr) +<br />-                    offsetof(struct zxdh_tx_region, tx_hdr);<br />-            /* length will be updated to actual pi hdr size when xmit pkt */<br />-            start_dp->len = 0;<br />-        }<br />     }<br />     if (ZXDH_VTPCI_OPS(hw)->setup_queue(hw, vq) < 0) {<br />         PMD_DRV_LOG(ERR, "setup_queue failed");<br />@@ -788,8 +768,8 @@ zxdh_init_queue(struct rte_eth_dev *dev, uint16_t vtpci_logic_qidx)<br />     }<br />     return 0;<br /> fail_q_alloc:<br />-    rte_free(sw_ring);<br />-    rte_memzone_free(hdr_mz);<br />+    if (hdr_mz)<br />+        rte_memzone_free(hdr_mz);<br />     rte_memzone_free(mz);<br />     rte_free(vq);<br />     return ret;<br />@@ -1290,18 +1270,49 @@ zxdh_dev_close(struct rte_eth_dev *dev)<br />     return ret;<br /> }<br />  <br />+/*<br />+ * Determine whether the current configuration requires support for scattered<br />+ * receive; return 1 if scattered receive is required and 0 if not.<br />+ */<br />+static int zxdh_scattered_rx(struct rte_eth_dev *eth_dev)<br />+{<br />+    uint16_t buf_size;<br />+<br />+    if (eth_dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_TCP_LRO) {<br />+        eth_dev->data->lro = 1;<br />+        return 1;<br />+    }<br />+<br />+    if (eth_dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_SCATTER)<br />+        return 1;<br />+<br />+<br />+    PMD_DRV_LOG(DEBUG, "port %d min_rx_buf_size %d",<br />+        eth_dev->data->port_id, eth_dev->data->min_rx_buf_size);<br />+    buf_size = eth_dev->data->min_rx_buf_size - RTE_PKTMBUF_HEADROOM;<br />+    if (eth_dev->data->mtu + ZXDH_ETH_OVERHEAD > buf_size)<br />+        return 1;<br />+<br />+    return 0;<br />+}<br />+<br /> static int32_t<br /> zxdh_set_rxtx_funcs(struct rte_eth_dev *eth_dev)<br /> {<br />-    struct zxdh_hw *hw = eth_dev->data->dev_private;<br />+    uint64_t tx_offloads = eth_dev->data->dev_conf.txmode.offloads;<br />  <br />-    if (!zxdh_pci_with_feature(hw, ZXDH_NET_F_MRG_RXBUF)) {<br />-        PMD_DRV_LOG(ERR, "port %u not support rx mergeable", eth_dev->data->port_id);<br />-        return -1;<br />-    }<br />     eth_dev->tx_pkt_prepare = zxdh_xmit_pkts_prepare;<br />-    eth_dev->tx_pkt_burst = &zxdh_xmit_pkts_packed;<br />-    eth_dev->rx_pkt_burst = &zxdh_recv_pkts_packed;<br />+    eth_dev->data->scattered_rx = zxdh_scattered_rx(eth_dev);<br />+<br />+    if (!(tx_offloads & RTE_ETH_TX_OFFLOAD_MULTI_SEGS))<br />+        eth_dev->tx_pkt_burst = &zxdh_xmit_pkts_simple;<br />+    else<br />+        eth_dev->tx_pkt_burst = &zxdh_xmit_pkts_packed;<br />+<br />+    if (eth_dev->data->scattered_rx)<br />+        eth_dev->rx_pkt_burst = &zxdh_recv_pkts_packed;<br />+    else<br />+        eth_dev->rx_pkt_burst = &zxdh_recv_single_pkts;<br />  <br />     return 0;<br /> }<br />diff --git a/drivers/net/zxdh/zxdh_ethdev_ops.c b/drivers/net/zxdh/zxdh_ethdev_ops.c<br />index 50247116d9..e2c2885add 100644<br />--- a/drivers/net/zxdh/zxdh_ethdev_ops.c<br />+++ b/drivers/net/zxdh/zxdh_ethdev_ops.c<br />@@ -95,10 +95,6 @@ static const struct rte_zxdh_xstats_name_off zxdh_rxq_stat_strings[] = {<br />     {"good_bytes",             offsetof(struct zxdh_virtnet_rx, stats.bytes)},<br />     {"errors",                 offsetof(struct zxdh_virtnet_rx, stats.errors)},<br />     {"idle",                   offsetof(struct zxdh_virtnet_rx, stats.idle)},<br />-    {"full",                   offsetof(struct zxdh_virtnet_rx, stats.full)},<br />-    {"norefill",               offsetof(struct zxdh_virtnet_rx, stats.norefill)},<br />-    {"multicast_packets",      offsetof(struct zxdh_virtnet_rx, stats.multicast)},<br />-    {"broadcast_packets",      offsetof(struct zxdh_virtnet_rx, stats.broadcast)},<br />     {"truncated_err",          offsetof(struct zxdh_virtnet_rx, stats.truncated_err)},<br />     {"offload_cfg_err",        offsetof(struct zxdh_virtnet_rx, stats.offload_cfg_err)},<br />     {"invalid_hdr_len_err",    offsetof(struct zxdh_virtnet_rx, stats.invalid_hdr_len_err)},<br />@@ -117,14 +113,12 @@ static const struct rte_zxdh_xstats_name_off zxdh_txq_stat_strings[] = {<br />     {"good_packets",           offsetof(struct zxdh_virtnet_tx, stats.packets)},<br />     {"good_bytes",             offsetof(struct zxdh_virtnet_tx, stats.bytes)},<br />     {"errors",                 offsetof(struct zxdh_virtnet_tx, stats.errors)},<br />-    {"idle",                   offsetof(struct zxdh_virtnet_tx, stats.idle)},<br />-    {"norefill",               offsetof(struct zxdh_virtnet_tx, stats.norefill)},<br />-    {"multicast_packets",      offsetof(struct zxdh_virtnet_tx, stats.multicast)},<br />-    {"broadcast_packets",      offsetof(struct zxdh_virtnet_tx, stats.broadcast)},<br />+    {"idle",                 offsetof(struct zxdh_virtnet_tx, stats.idle)},<br />     {"truncated_err",          offsetof(struct zxdh_virtnet_tx, stats.truncated_err)},<br />     {"offload_cfg_err",        offsetof(struct zxdh_virtnet_tx, stats.offload_cfg_err)},<br />     {"invalid_hdr_len_err",    offsetof(struct zxdh_virtnet_tx, stats.invalid_hdr_len_err)},<br />     {"no_segs_err",            offsetof(struct zxdh_virtnet_tx, stats.no_segs_err)},<br />+    {"no_free_tx_desc_err",    offsetof(struct zxdh_virtnet_tx, stats.no_free_tx_desc_err)},<br />     {"undersize_packets",      offsetof(struct zxdh_virtnet_tx, stats.size_bins[0])},<br />     {"size_64_packets",        offsetof(struct zxdh_virtnet_tx, stats.size_bins[1])},<br />     {"size_65_127_packets",    offsetof(struct zxdh_virtnet_tx, stats.size_bins[2])},<br />@@ -2026,6 +2020,20 @@ int zxdh_dev_mtu_set(struct rte_eth_dev *dev, uint16_t new_mtu)<br />     uint16_t vfid = zxdh_vport_to_vfid(hw->vport);<br />     int ret;<br />  <br />+    /* If device is started, refuse mtu that requires the support of<br />+     * scattered packets when this feature has not been enabled before.<br />+     */<br />+    if (dev->data->dev_started && <br />+        ((!dev->data->scattered_rx && <br />+         ((uint32_t)ZXDH_MTU_TO_PKTLEN(new_mtu) > <br />+         (dev->data->min_rx_buf_size - RTE_PKTMBUF_HEADROOM))) ||<br />+         (dev->data->scattered_rx && <br />+         ((uint32_t)ZXDH_MTU_TO_PKTLEN(new_mtu) <=<br />+         (dev->data->min_rx_buf_size - RTE_PKTMBUF_HEADROOM))))) {<br />+        PMD_DRV_LOG(ERR, "Stop port first.");<br />+        return -EINVAL;<br />+    }<br />+<br />     if (hw->is_pf) {<br />         ret = zxdh_get_panel_attr(dev, &panel);<br />         if (ret != 0) {<br />diff --git a/drivers/net/zxdh/zxdh_ethdev_ops.h b/drivers/net/zxdh/zxdh_ethdev_ops.h<br />index 6dfe4be473..c49d79c232 100644<br />--- a/drivers/net/zxdh/zxdh_ethdev_ops.h<br />+++ b/drivers/net/zxdh/zxdh_ethdev_ops.h<br />@@ -40,6 +40,10 @@<br /> #define ZXDH_SPM_SPEED_4X_100G         RTE_BIT32(10)<br /> #define ZXDH_SPM_SPEED_4X_200G         RTE_BIT32(11)<br />  <br />+#define ZXDH_VLAN_TAG_LEN   4<br />+#define ZXDH_ETH_OVERHEAD  (RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN + ZXDH_VLAN_TAG_LEN * 2)<br />+#define ZXDH_MTU_TO_PKTLEN(mtu) ((mtu) + ZXDH_ETH_OVERHEAD)<br />+<br /> struct zxdh_np_stats_data {<br />     uint64_t n_pkts_dropped;<br />     uint64_t n_bytes_dropped;<br />diff --git a/drivers/net/zxdh/zxdh_pci.c b/drivers/net/zxdh/zxdh_pci.c<br />index 4ba31905fc..0bc27ed111 100644<br />--- a/drivers/net/zxdh/zxdh_pci.c<br />+++ b/drivers/net/zxdh/zxdh_pci.c<br />@@ -231,7 +231,7 @@ zxdh_notify_queue(struct zxdh_hw *hw, struct zxdh_virtqueue *vq)<br />  <br />     notify_data = ((uint32_t)vq->vq_avail_idx << 16) | vq->vq_queue_index;<br />     if (zxdh_pci_with_feature(hw, ZXDH_F_RING_PACKED) && <br />-            (vq->vq_packed.cached_flags & ZXDH_VRING_PACKED_DESC_F_AVAIL))<br />+            (vq->cached_flags & ZXDH_VRING_PACKED_DESC_F_AVAIL))<br />         notify_data |= RTE_BIT32(31);<br />  <br />     PMD_DRV_LOG(DEBUG, "queue:%d notify_data 0x%x notify_addr 0x%p",<br />diff --git a/drivers/net/zxdh/zxdh_queue.c b/drivers/net/zxdh/zxdh_queue.c<br />index 7162593b16..4668cb5d13 100644<br />--- a/drivers/net/zxdh/zxdh_queue.c<br />+++ b/drivers/net/zxdh/zxdh_queue.c<br />@@ -407,7 +407,7 @@ int32_t zxdh_enqueue_recv_refill_packed(struct zxdh_virtqueue *vq,<br /> {<br />     struct zxdh_vring_packed_desc *start_dp = vq->vq_packed.ring.desc;<br />     struct zxdh_vq_desc_extra *dxp;<br />-    uint16_t flags = vq->vq_packed.cached_flags;<br />+    uint16_t flags = vq->cached_flags;<br />     int32_t i;<br />     uint16_t idx;<br />  <br />@@ -415,7 +415,6 @@ int32_t zxdh_enqueue_recv_refill_packed(struct zxdh_virtqueue *vq,<br />         idx = vq->vq_avail_idx;<br />         dxp = &vq->vq_descx[idx];<br />         dxp->cookie = (void *)cookie[i];<br />-        dxp->ndescs = 1;<br />         /* rx pkt fill in data_off */<br />         start_dp[idx].addr = rte_mbuf_iova_get(cookie[i]) + RTE_PKTMBUF_HEADROOM;<br />         start_dp[idx].len = cookie[i]->buf_len - RTE_PKTMBUF_HEADROOM;<br />@@ -423,8 +422,8 @@ int32_t zxdh_enqueue_recv_refill_packed(struct zxdh_virtqueue *vq,<br />         zxdh_queue_store_flags_packed(&start_dp[idx], flags);<br />         if (++vq->vq_avail_idx >= vq->vq_nentries) {<br />             vq->vq_avail_idx -= vq->vq_nentries;<br />-            vq->vq_packed.cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;<br />-            flags = vq->vq_packed.cached_flags;<br />+            vq->cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;<br />+            flags = vq->cached_flags;<br />         }<br />     }<br />     vq->vq_free_cnt = (uint16_t)(vq->vq_free_cnt - num);<br />@@ -467,7 +466,7 @@ void zxdh_queue_rxvq_flush(struct zxdh_virtqueue *vq)<br />     int32_t cnt = 0;<br />  <br />     i = vq->vq_used_cons_idx;<br />-    while (zxdh_desc_used(&descs[i], vq) && cnt++ < vq->vq_nentries) {<br />+    while (desc_is_used(&descs[i], vq) && cnt++ < vq->vq_nentries) {<br />         dxp = &vq->vq_descx[descs[i].id];<br />         if (dxp->cookie != NULL) {<br />             rte_pktmbuf_free(dxp->cookie);<br />@@ -477,7 +476,7 @@ void zxdh_queue_rxvq_flush(struct zxdh_virtqueue *vq)<br />         vq->vq_used_cons_idx++;<br />         if (vq->vq_used_cons_idx >= vq->vq_nentries) {<br />             vq->vq_used_cons_idx -= vq->vq_nentries;<br />-            vq->vq_packed.used_wrap_counter ^= 1;<br />+            vq->used_wrap_counter ^= 1;<br />         }<br />         i = vq->vq_used_cons_idx;<br />     }<br />diff --git a/drivers/net/zxdh/zxdh_queue.h b/drivers/net/zxdh/zxdh_queue.h<br />index 1a0c8a0d90..94101c8269 100644<br />--- a/drivers/net/zxdh/zxdh_queue.h<br />+++ b/drivers/net/zxdh/zxdh_queue.h<br />@@ -9,6 +9,7 @@<br />  <br /> #include <rte_common.h> <br /> #include <rte_atomic.h> <br />+#include <rte_io.h> <br />  <br /> #include "zxdh_ethdev.h" <br /> #include "zxdh_rxtx.h" <br />@@ -117,7 +118,6 @@ struct zxdh_vring_packed_desc_event {<br /> };<br />  <br /> struct zxdh_vring_packed {<br />-    uint32_t num;<br />     struct zxdh_vring_packed_desc *desc;<br />     struct zxdh_vring_packed_desc_event *driver;<br />     struct zxdh_vring_packed_desc_event *device;<br />@@ -129,50 +129,59 @@ struct zxdh_vq_desc_extra {<br />     uint16_t next;<br /> };<br />  <br />+struct zxdh_vring {<br />+    uint32_t num;<br />+    struct zxdh_vring_desc  *desc;<br />+    struct zxdh_vring_avail *avail;<br />+    struct zxdh_vring_used  *used;<br />+};<br />+<br /> struct zxdh_virtqueue {<br />+    union {<br />+        struct {<br />+            struct zxdh_vring ring; /**< vring keeping desc, used and avail */<br />+        } vq_split;<br />+        struct __rte_packed_begin {<br />+            struct zxdh_vring_packed ring;<br />+        } __rte_packed_end vq_packed;<br />+    };<br />     struct zxdh_hw  *hw; /* < zxdh_hw structure pointer. */<br />  <br />-    struct {<br />-        /* vring keeping descs and events */<br />-        struct zxdh_vring_packed ring;<br />-        uint8_t used_wrap_counter;<br />-        uint8_t rsv;<br />-        uint16_t cached_flags; /* < cached flags for descs */<br />-        uint16_t event_flags_shadow;<br />-        uint16_t rsv1;<br />-    } vq_packed;<br />-<br />-    uint16_t vq_used_cons_idx; /* < last consumed descriptor */<br />-    uint16_t vq_nentries;  /* < vring desc numbers */<br />-    uint16_t vq_free_cnt;  /* < num of desc available */<br />-    uint16_t vq_avail_idx; /* < sync until needed */<br />-    uint16_t vq_free_thresh; /* < free threshold */<br />-    uint16_t rsv2;<br />-<br />-    void *vq_ring_virt_mem;  /* < linear address of vring */<br />-    uint32_t vq_ring_size;<br />+    uint16_t vq_used_cons_idx; /**< last consumed descriptor */<br />+    uint16_t vq_avail_idx; /**< sync until needed */<br />+    uint16_t vq_nentries;  /**< vring desc numbers */<br />+    uint16_t vq_free_cnt;  /**< num of desc available */<br />+<br />+    uint16_t cached_flags; /**< cached flags for descs */<br />+    uint8_t used_wrap_counter;<br />+    uint8_t rsv;<br />+    uint16_t vq_free_thresh; /**< free threshold */<br />+    uint16_t next_qidx;<br />+<br />+    void *notify_addr;<br />  <br />     union {<br />         struct zxdh_virtnet_rx rxq;<br />         struct zxdh_virtnet_tx txq;<br />     };<br />  <br />-    /*<br />-     * physical address of vring, or virtual address<br />-     */<br />-    rte_iova_t vq_ring_mem;<br />+    uint16_t vq_queue_index; /* PACKED: phy_idx, SPLIT: logic_idx */<br />+    uint16_t event_flags_shadow;<br />+    uint32_t vq_ring_size;<br />  <br />-    /*<br />+    /**<br />      * Head of the free chain in the descriptor table. If<br />      * there are no free descriptors, this will be set to<br />      * VQ_RING_DESC_CHAIN_END.<br />-     */<br />+     **/<br />     uint16_t  vq_desc_head_idx;<br />     uint16_t  vq_desc_tail_idx;<br />-    uint16_t  vq_queue_index;   /* < PCI queue index */<br />-    uint16_t  offset; /* < relative offset to obtain addr in mbuf */<br />-    uint16_t *notify_addr;<br />-    struct rte_mbuf **sw_ring;  /* < RX software ring. */<br />+    uint32_t rsv_8B;<br />+<br />+    void *vq_ring_virt_mem;  /**< linear address of vring*/<br />+    /* physical address of vring, or virtual address for virtio_user. */<br />+    rte_iova_t vq_ring_mem;<br />+<br />     struct zxdh_vq_desc_extra vq_descx[];<br /> };<br />  <br />@@ -296,10 +305,9 @@ static inline void<br /> zxdh_vring_init_packed(struct zxdh_vring_packed *vr, uint8_t *p,<br />         unsigned long align, uint32_t num)<br /> {<br />-    vr->num    = num;<br />     vr->desc   = (struct zxdh_vring_packed_desc *)p;<br />     vr->driver = (struct zxdh_vring_packed_desc_event *)(p +<br />-                 vr->num * sizeof(struct zxdh_vring_packed_desc));<br />+                 num * sizeof(struct zxdh_vring_packed_desc));<br />     vr->device = (struct zxdh_vring_packed_desc_event *)RTE_ALIGN_CEIL(((uintptr_t)vr->driver +<br />                  sizeof(struct zxdh_vring_packed_desc_event)), align);<br /> }<br />@@ -331,30 +339,21 @@ zxdh_vring_desc_init_indirect_packed(struct zxdh_vring_packed_desc *dp, int32_t<br /> static inline void<br /> zxdh_queue_disable_intr(struct zxdh_virtqueue *vq)<br /> {<br />-    if (vq->vq_packed.event_flags_shadow != ZXDH_RING_EVENT_FLAGS_DISABLE) {<br />-        vq->vq_packed.event_flags_shadow = ZXDH_RING_EVENT_FLAGS_DISABLE;<br />-        vq->vq_packed.ring.driver->desc_event_flags = vq->vq_packed.event_flags_shadow;<br />+    if (vq->event_flags_shadow != ZXDH_RING_EVENT_FLAGS_DISABLE) {<br />+        vq->event_flags_shadow = ZXDH_RING_EVENT_FLAGS_DISABLE;<br />+        vq->vq_packed.ring.driver->desc_event_flags = vq->event_flags_shadow;<br />     }<br /> }<br />  <br /> static inline void<br /> zxdh_queue_enable_intr(struct zxdh_virtqueue *vq)<br /> {<br />-    if (vq->vq_packed.event_flags_shadow == ZXDH_RING_EVENT_FLAGS_DISABLE) {<br />-        vq->vq_packed.event_flags_shadow = ZXDH_RING_EVENT_FLAGS_DISABLE;<br />-        vq->vq_packed.ring.driver->desc_event_flags = vq->vq_packed.event_flags_shadow;<br />+    if (vq->event_flags_shadow == ZXDH_RING_EVENT_FLAGS_DISABLE) {<br />+        vq->event_flags_shadow = ZXDH_RING_EVENT_FLAGS_DISABLE;<br />+        vq->vq_packed.ring.driver->desc_event_flags = vq->event_flags_shadow;<br />     }<br /> }<br />  <br />-static inline void<br />-zxdh_mb(uint8_t weak_barriers)<br />-{<br />-    if (weak_barriers)<br />-        rte_atomic_thread_fence(rte_memory_order_seq_cst);<br />-    else<br />-        rte_mb();<br />-}<br />-<br /> static inline<br /> int32_t desc_is_used(struct zxdh_vring_packed_desc *desc, struct zxdh_virtqueue *vq)<br /> {<br />@@ -365,7 +364,7 @@ int32_t desc_is_used(struct zxdh_vring_packed_desc *desc, struct zxdh_virtqueue<br />     rte_io_rmb();<br />     used = !!(flags & ZXDH_VRING_PACKED_DESC_F_USED);<br />     avail = !!(flags & ZXDH_VRING_PACKED_DESC_F_AVAIL);<br />-    return avail == used && used == vq->vq_packed.used_wrap_counter;<br />+    return avail == used && used == vq->used_wrap_counter;<br /> }<br />  <br /> static inline int32_t<br />@@ -381,22 +380,17 @@ zxdh_queue_store_flags_packed(struct zxdh_vring_packed_desc *dp, uint16_t flags)<br />     dp->flags = flags;<br /> }<br />  <br />-static inline int32_t<br />-zxdh_desc_used(struct zxdh_vring_packed_desc *desc, struct zxdh_virtqueue *vq)<br />-{<br />-    uint16_t flags;<br />-    uint16_t used, avail;<br />-<br />-    flags = desc->flags;<br />-    rte_io_rmb();<br />-    used = !!(flags & ZXDH_VRING_PACKED_DESC_F_USED);<br />-    avail = !!(flags & ZXDH_VRING_PACKED_DESC_F_AVAIL);<br />-    return avail == used && used == vq->vq_packed.used_wrap_counter;<br />-}<br />-<br /> static inline void zxdh_queue_notify(struct zxdh_virtqueue *vq)<br /> {<br />-    ZXDH_VTPCI_OPS(vq->hw)->notify_queue(vq->hw, vq);<br />+    /* Bit[0:15]: vq queue index<br />+     * Bit[16:30]: avail index<br />+     * Bit[31]: avail wrap counter<br />+     */<br />+    uint32_t notify_data = ((uint32_t)(!!(vq->cached_flags & <br />+        ZXDH_VRING_PACKED_DESC_F_AVAIL)) << 31) |<br />+        ((uint32_t)vq->vq_avail_idx << 16) |<br />+        vq->vq_queue_index;<br />+    rte_write32(notify_data, vq->notify_addr);<br /> }<br />  <br /> static inline int32_t<br />@@ -404,7 +398,7 @@ zxdh_queue_kick_prepare_packed(struct zxdh_virtqueue *vq)<br /> {<br />     uint16_t flags = 0;<br />  <br />-    zxdh_mb(1);<br />+    rte_mb();<br />     flags = vq->vq_packed.ring.device->desc_event_flags;<br />  <br />     return (flags != ZXDH_RING_EVENT_FLAGS_DISABLE);<br />diff --git a/drivers/net/zxdh/zxdh_rxtx.c b/drivers/net/zxdh/zxdh_rxtx.c<br />index db86922aea..111cf54b0d 100644<br />--- a/drivers/net/zxdh/zxdh_rxtx.c<br />+++ b/drivers/net/zxdh/zxdh_rxtx.c<br />@@ -114,6 +114,22 @@<br />         RTE_MBUF_F_TX_SEC_OFFLOAD |     \<br />         RTE_MBUF_F_TX_UDP_SEG)<br />  <br />+#if RTE_CACHE_LINE_SIZE == 128<br />+#define NEXT_CACHELINE_OFF_16B   8<br />+#define NEXT_CACHELINE_OFF_8B   16<br />+#elif RTE_CACHE_LINE_SIZE == 64<br />+#define NEXT_CACHELINE_OFF_16B   4<br />+#define NEXT_CACHELINE_OFF_8B    8<br />+#else<br />+#define NEXT_CACHELINE_OFF_16B  (RTE_CACHE_LINE_SIZE / 16)<br />+#define NEXT_CACHELINE_OFF_8B   (RTE_CACHE_LINE_SIZE / 8)<br />+#endif<br />+#define N_PER_LOOP  NEXT_CACHELINE_OFF_8B<br />+#define N_PER_LOOP_MASK (N_PER_LOOP - 1)<br />+<br />+#define rxq_get_vq(q) ((q)->vq)<br />+#define txq_get_vq(q) ((q)->vq)<br />+<br /> uint32_t zxdh_outer_l2_type[16] = {<br />     0,<br />     RTE_PTYPE_L2_ETHER,<br />@@ -201,43 +217,6 @@ uint32_t zxdh_inner_l4_type[16] = {<br />     0,<br /> };<br />  <br />-static void<br />-zxdh_xmit_cleanup_inorder_packed(struct zxdh_virtqueue *vq, int32_t num)<br />-{<br />-    uint16_t used_idx = 0;<br />-    uint16_t id       = 0;<br />-    uint16_t curr_id  = 0;<br />-    uint16_t free_cnt = 0;<br />-    uint16_t size     = vq->vq_nentries;<br />-    struct zxdh_vring_packed_desc *desc = vq->vq_packed.ring.desc;<br />-    struct zxdh_vq_desc_extra     *dxp  = NULL;<br />-<br />-    used_idx = vq->vq_used_cons_idx;<br />-    /* desc_is_used has a load-acquire or rte_io_rmb inside<br />-     * and wait for used desc in virtqueue.<br />-     */<br />-    while (num > 0 && zxdh_desc_used(&desc[used_idx], vq)) {<br />-        id = desc[used_idx].id;<br />-        do {<br />-            curr_id = used_idx;<br />-            dxp = &vq->vq_descx[used_idx];<br />-            used_idx += dxp->ndescs;<br />-            free_cnt += dxp->ndescs;<br />-            num -= dxp->ndescs;<br />-            if (used_idx >= size) {<br />-                used_idx -= size;<br />-                vq->vq_packed.used_wrap_counter ^= 1;<br />-            }<br />-            if (dxp->cookie != NULL) {<br />-                rte_pktmbuf_free(dxp->cookie);<br />-                dxp->cookie = NULL;<br />-            }<br />-        } while (curr_id != id);<br />-    }<br />-    vq->vq_used_cons_idx = used_idx;<br />-    vq->vq_free_cnt += free_cnt;<br />-}<br />-<br /> static inline uint16_t<br /> zxdh_get_mtu(struct zxdh_virtqueue *vq)<br /> {<br />@@ -334,18 +313,17 @@ zxdh_xmit_fill_net_hdr(struct zxdh_virtqueue *vq, struct rte_mbuf *cookie,<br /> }<br />  <br /> static inline void<br />-zxdh_enqueue_xmit_packed_fast(struct zxdh_virtnet_tx *txvq,<br />+zxdh_xmit_enqueue_push(struct zxdh_virtnet_tx *txvq,<br />                         struct rte_mbuf *cookie)<br /> {<br />     struct zxdh_virtqueue *vq = txvq->vq;<br />     uint16_t id = vq->vq_avail_idx;<br />     struct zxdh_vq_desc_extra *dxp = &vq->vq_descx[id];<br />-    uint16_t flags = vq->vq_packed.cached_flags;<br />+    uint16_t flags = vq->cached_flags;<br />     struct zxdh_net_hdr_dl *hdr = NULL;<br />     uint8_t hdr_len = vq->hw->dl_net_hdr_len;<br />     struct zxdh_vring_packed_desc *dp = &vq->vq_packed.ring.desc[id];<br />  <br />-    dxp->ndescs = 1;<br />     dxp->cookie = cookie;<br />     hdr = rte_pktmbuf_mtod_offset(cookie, struct zxdh_net_hdr_dl *, -hdr_len);<br />     zxdh_xmit_fill_net_hdr(vq, cookie, hdr);<br />@@ -355,69 +333,65 @@ zxdh_enqueue_xmit_packed_fast(struct zxdh_virtnet_tx *txvq,<br />     dp->id   = id;<br />     if (++vq->vq_avail_idx >= vq->vq_nentries) {<br />         vq->vq_avail_idx -= vq->vq_nentries;<br />-        vq->vq_packed.cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;<br />+        vq->cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;<br />     }<br />     vq->vq_free_cnt--;<br />     zxdh_queue_store_flags_packed(dp, flags);<br /> }<br />  <br /> static inline void<br />-zxdh_enqueue_xmit_packed(struct zxdh_virtnet_tx *txvq,<br />+zxdh_xmit_enqueue_append(struct zxdh_virtnet_tx *txvq,<br />                         struct rte_mbuf *cookie,<br />                         uint16_t needed)<br /> {<br />     struct zxdh_tx_region *txr = txvq->zxdh_net_hdr_mz->addr;<br />     struct zxdh_virtqueue *vq = txvq->vq;<br />-    uint16_t id = vq->vq_avail_idx;<br />-    struct zxdh_vq_desc_extra *dxp = &vq->vq_descx[id];<br />+    struct zxdh_vq_desc_extra *dep = &vq->vq_descx[0];<br />     uint16_t head_idx = vq->vq_avail_idx;<br />     uint16_t idx = head_idx;<br />     struct zxdh_vring_packed_desc *start_dp = vq->vq_packed.ring.desc;<br />     struct zxdh_vring_packed_desc *head_dp = &vq->vq_packed.ring.desc[idx];<br />     struct zxdh_net_hdr_dl *hdr = NULL;<br />-<br />-    uint16_t head_flags = cookie->next ? ZXDH_VRING_DESC_F_NEXT : 0;<br />+    uint16_t id = vq->vq_avail_idx;<br />+    struct zxdh_vq_desc_extra *dxp = &vq->vq_descx[id];<br />     uint8_t hdr_len = vq->hw->dl_net_hdr_len;<br />+    uint16_t head_flags = 0;<br />  <br />-    dxp->ndescs = needed;<br />-    dxp->cookie = cookie;<br />-    head_flags |= vq->vq_packed.cached_flags;<br />+    dxp->cookie = NULL;<br />  <br />+    /* setup first tx ring slot to point to header stored in reserved region. */<br />     start_dp[idx].addr = txvq->zxdh_net_hdr_mem + RTE_PTR_DIFF(&txr[idx].tx_hdr, txr);<br />     start_dp[idx].len  = hdr_len;<br />-    head_flags |= ZXDH_VRING_DESC_F_NEXT;<br />+    start_dp[idx].id = idx;<br />+    head_flags |= vq->cached_flags | ZXDH_VRING_DESC_F_NEXT;<br />     hdr = (void *)&txr[idx].tx_hdr;<br />  <br />-    rte_prefetch1(hdr);<br />+    zxdh_xmit_fill_net_hdr(vq, cookie, hdr);<br />+<br />     idx++;<br />     if (idx >= vq->vq_nentries) {<br />         idx -= vq->vq_nentries;<br />-        vq->vq_packed.cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;<br />+        vq->cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;<br />     }<br />  <br />-    zxdh_xmit_fill_net_hdr(vq, cookie, hdr);<br />-<br />     do {<br />         start_dp[idx].addr = rte_pktmbuf_iova(cookie);<br />         start_dp[idx].len  = cookie->data_len;<br />-        start_dp[idx].id = id;<br />-        if (likely(idx != head_idx)) {<br />-            uint16_t flags = cookie->next ? ZXDH_VRING_DESC_F_NEXT : 0;<br />-<br />-            flags |= vq->vq_packed.cached_flags;<br />-            start_dp[idx].flags = flags;<br />-        }<br />+        start_dp[idx].id = idx;<br />  <br />+        dep[idx].cookie = cookie;<br />+        uint16_t flags = cookie->next ? ZXDH_VRING_DESC_F_NEXT : 0;<br />+        flags |= vq->cached_flags;<br />+        start_dp[idx].flags = flags;<br />         idx++;<br />         if (idx >= vq->vq_nentries) {<br />             idx -= vq->vq_nentries;<br />-            vq->vq_packed.cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;<br />+            vq->cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;<br />         }<br />     } while ((cookie = cookie->next) != NULL);<br />  <br />     vq->vq_free_cnt = (uint16_t)(vq->vq_free_cnt - needed);<br />     vq->vq_avail_idx = idx;<br />-<br />     zxdh_queue_store_flags_packed(head_dp, head_flags);<br /> }<br />  <br />@@ -456,7 +430,7 @@ zxdh_update_packet_stats(struct zxdh_virtnet_stats *stats, struct rte_mbuf *mbuf<br /> }<br />  <br /> static void<br />-zxdh_xmit_flush(struct zxdh_virtqueue *vq)<br />+zxdh_xmit_fast_flush(struct zxdh_virtqueue *vq)<br /> {<br />     uint16_t id       = 0;<br />     uint16_t curr_id  = 0;<br />@@ -472,20 +446,22 @@ zxdh_xmit_flush(struct zxdh_virtqueue *vq)<br />      * for a used descriptor in the virtqueue.<br />      */<br />     while (desc_is_used(&desc[used_idx], vq)) {<br />+        rte_prefetch0(&desc[used_idx + NEXT_CACHELINE_OFF_16B]);<br />         id = desc[used_idx].id;<br />         do {<br />+            desc[used_idx].id = used_idx;<br />             curr_id = used_idx;<br />             dxp = &vq->vq_descx[used_idx];<br />-            used_idx += dxp->ndescs;<br />-            free_cnt += dxp->ndescs;<br />-            if (used_idx >= size) {<br />-                used_idx -= size;<br />-                vq->vq_packed.used_wrap_counter ^= 1;<br />-            }<br />             if (dxp->cookie != NULL) {<br />-                rte_pktmbuf_free(dxp->cookie);<br />+                rte_pktmbuf_free_seg(dxp->cookie);<br />                 dxp->cookie = NULL;<br />             }<br />+            used_idx += 1;<br />+            free_cnt += 1;<br />+            if (unlikely(used_idx == size)) {<br />+                used_idx = 0;<br />+                vq->used_wrap_counter ^= 1;<br />+            }<br />         } while (curr_id != id);<br />     }<br />     vq->vq_used_cons_idx = used_idx;<br />@@ -499,13 +475,12 @@ zxdh_xmit_pkts_packed(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkt<br />     struct zxdh_virtqueue  *vq   = txvq->vq;<br />     uint16_t nb_tx = 0;<br />  <br />-    zxdh_xmit_flush(vq);<br />+    zxdh_xmit_fast_flush(vq);<br />  <br />     for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) {<br />         struct rte_mbuf *txm = tx_pkts[nb_tx];<br />         int32_t can_push     = 0;<br />         int32_t slots        = 0;<br />-        int32_t need         = 0;<br />  <br />         rte_prefetch0(txm);<br />         /* optimize ring usage */<br />@@ -522,26 +497,15 @@ zxdh_xmit_pkts_packed(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkt<br />          * default    => number of segments + 1<br />          **/<br />         slots = txm->nb_segs + !can_push;<br />-        need = slots - vq->vq_free_cnt;<br />         /* Positive value indicates it need free vring descriptors */<br />-        if (unlikely(need > 0)) {<br />-            zxdh_xmit_cleanup_inorder_packed(vq, need);<br />-            need = slots - vq->vq_free_cnt;<br />-            if (unlikely(need > 0)) {<br />-                PMD_TX_LOG(ERR,<br />-                        " No enough %d free tx descriptors to transmit." <br />-                        "freecnt %d",<br />-                        need,<br />-                        vq->vq_free_cnt);<br />-                break;<br />-            }<br />-        }<br />+        if (unlikely(slots >  vq->vq_free_cnt))<br />+            break;<br />  <br />         /* Enqueue Packet buffers */<br />         if (can_push)<br />-            zxdh_enqueue_xmit_packed_fast(txvq, txm);<br />+            zxdh_xmit_enqueue_push(txvq, txm);<br />         else<br />-            zxdh_enqueue_xmit_packed(txvq, txm, slots);<br />+            zxdh_xmit_enqueue_append(txvq, txm, slots);<br />         zxdh_update_packet_stats(&txvq->stats, txm);<br />     }<br />     txvq->stats.packets += nb_tx;<br />@@ -579,11 +543,6 @@ uint16_t zxdh_xmit_pkts_prepare(void *tx_queue, struct rte_mbuf **tx_pkts,<br />         }<br /> #endif<br />  <br />-        error = rte_net_intel_cksum_prepare(m);<br />-        if (unlikely(error)) {<br />-            rte_errno = -error;<br />-            break;<br />-        }<br />         if (m->nb_segs > ZXDH_TX_MAX_SEGS) {<br />             PMD_TX_LOG(ERR, "%d segs dropped", m->nb_segs);<br />             txvq->stats.truncated_err += nb_pkts - nb_tx;<br />@@ -613,13 +572,15 @@ zxdh_dequeue_burst_rx_packed(struct zxdh_virtqueue *vq,<br />     uint16_t i, used_idx;<br />     uint16_t id;<br />  <br />+    used_idx = vq->vq_used_cons_idx;<br />+    rte_prefetch0(&desc[used_idx]);<br />+<br />     for (i = 0; i < num; i++) {<br />         used_idx = vq->vq_used_cons_idx;<br />-        /**<br />-         * desc_is_used has a load-acquire or rte_io_rmb inside<br />+        /* desc_is_used has a load-acquire or rte_io_rmb inside<br />          * and wait for used desc in virtqueue.<br />          */<br />-        if (!zxdh_desc_used(&desc[used_idx], vq))<br />+        if (!desc_is_used(&desc[used_idx], vq))<br />             return i;<br />         len[i] = desc[used_idx].len;<br />         id = desc[used_idx].id;<br />@@ -637,7 +598,7 @@ zxdh_dequeue_burst_rx_packed(struct zxdh_virtqueue *vq,<br />         vq->vq_used_cons_idx++;<br />         if (vq->vq_used_cons_idx >= vq->vq_nentries) {<br />             vq->vq_used_cons_idx -= vq->vq_nentries;<br />-            vq->vq_packed.used_wrap_counter ^= 1;<br />+            vq->used_wrap_counter ^= 1;<br />         }<br />     }<br />     return i;<br />@@ -823,17 +784,52 @@ zxdh_rx_update_mbuf(struct zxdh_hw *hw, struct rte_mbuf *m, struct zxdh_net_hdr_<br />     }<br /> }<br />  <br />-static void zxdh_discard_rxbuf(struct zxdh_virtqueue *vq, struct rte_mbuf *m)<br />+static void refill_desc_unwrap(struct zxdh_virtqueue *vq,<br />+        struct rte_mbuf **cookie, uint16_t nb_pkts)<br /> {<br />-    int32_t error = 0;<br />-    /*<br />-     * Requeue the discarded mbuf. This should always be<br />-     * successful since it was just dequeued.<br />-     */<br />-    error = zxdh_enqueue_recv_refill_packed(vq, &m, 1);<br />-    if (unlikely(error)) {<br />-        PMD_RX_LOG(ERR, "cannot enqueue discarded mbuf");<br />-        rte_pktmbuf_free(m);<br />+    struct zxdh_vring_packed_desc *start_dp = vq->vq_packed.ring.desc;<br />+    struct zxdh_vq_desc_extra *dxp;<br />+    uint16_t flags = vq->cached_flags;<br />+    int32_t i;<br />+    uint16_t idx;<br />+<br />+    idx = vq->vq_avail_idx;<br />+    for (i = 0; i < nb_pkts; i++) {<br />+        dxp = &vq->vq_descx[idx];<br />+        dxp->cookie = (void *)cookie[i];<br />+        start_dp[idx].addr = rte_mbuf_iova_get(cookie[i]) + RTE_PKTMBUF_HEADROOM;<br />+        start_dp[idx].len = cookie[i]->buf_len - RTE_PKTMBUF_HEADROOM;<br />+        start_dp[idx].flags = flags;<br />+        idx++;<br />+    }<br />+    vq->vq_avail_idx += nb_pkts;<br />+    vq->vq_free_cnt = vq->vq_free_cnt - nb_pkts;<br />+}<br />+<br />+static void refill_que_descs(struct zxdh_virtqueue *vq, struct rte_eth_dev *dev)<br />+{<br />+    /* free_cnt may include mrg descs */<br />+    struct rte_mbuf *new_pkts[ZXDH_MBUF_BURST_SZ];<br />+    uint16_t free_cnt = RTE_MIN(ZXDH_MBUF_BURST_SZ, vq->vq_free_cnt);<br />+    struct zxdh_virtnet_rx *rxvq = &vq->rxq;<br />+    uint16_t  unwrap_cnt, left_cnt;<br />+<br />+    if (!rte_pktmbuf_alloc_bulk(rxvq->mpool, new_pkts, free_cnt)) {<br />+        left_cnt = free_cnt;<br />+        unwrap_cnt = 0;<br />+        if ((vq->vq_avail_idx + free_cnt) >= vq->vq_nentries) {<br />+            unwrap_cnt = vq->vq_nentries - vq->vq_avail_idx;<br />+            left_cnt = free_cnt - unwrap_cnt;<br />+            refill_desc_unwrap(vq, new_pkts, unwrap_cnt);<br />+            vq->vq_avail_idx = 0;<br />+            vq->cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;<br />+        }<br />+        if (left_cnt)<br />+            refill_desc_unwrap(vq, new_pkts + unwrap_cnt, left_cnt);<br />+<br />+        rte_io_wmb();<br />+    } else {<br />+        dev->data->rx_mbuf_alloc_failed += free_cnt;<br />     }<br /> }<br />  <br />@@ -842,7 +838,7 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,<br />                 uint16_t nb_pkts)<br /> {<br />     struct zxdh_virtnet_rx *rxvq = rx_queue;<br />-    struct zxdh_virtqueue *vq = rxvq->vq;<br />+    struct zxdh_virtqueue *vq = rxq_get_vq(rxvq);<br />     struct zxdh_hw *hw = vq->hw;<br />     struct rte_mbuf *rxm = NULL;<br />     struct rte_mbuf *prev = NULL;<br />@@ -852,7 +848,6 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,<br />     uint16_t len = 0;<br />     uint32_t seg_num = 0;<br />     uint32_t seg_res = 0;<br />-    uint32_t error = 0;<br />     uint16_t hdr_size = 0;<br />     uint16_t nb_rx = 0;<br />     uint16_t i;<br />@@ -873,7 +868,8 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,<br />         rx_pkts[nb_rx] = rxm;<br />         prev = rxm;<br />         len = lens[i];<br />-        header = rte_pktmbuf_mtod(rxm, struct zxdh_net_hdr_ul *);<br />+        header = (struct zxdh_net_hdr_ul *)((char *)<br />+                    rxm->buf_addr + RTE_PKTMBUF_HEADROOM);<br />  <br />         seg_num  = header->type_hdr.num_buffers;<br />  <br />@@ -886,7 +882,7 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,<br />             rxvq->stats.invalid_hdr_len_err++;<br />             continue;<br />         }<br />-        rxm->data_off += hdr_size;<br />+        rxm->data_off = RTE_PKTMBUF_HEADROOM + hdr_size;<br />         rxm->nb_segs = seg_num;<br />         rxm->ol_flags = 0;<br />         rcvd_pkt_len = len - hdr_size;<br />@@ -902,18 +898,19 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,<br />             len = lens[i];<br />             rxm = rcv_pkts[i];<br />             rxm->data_len = len;<br />+            rxm->data_off = RTE_PKTMBUF_HEADROOM;<br />             rcvd_pkt_len += len;<br />             prev->next = rxm;<br />             prev = rxm;<br />             rxm->next = NULL;<br />-            seg_res -= 1;<br />+            seg_res--;<br />         }<br />  <br />         if (!seg_res) {<br />             if (rcvd_pkt_len != rx_pkts[nb_rx]->pkt_len) {<br />                 PMD_RX_LOG(ERR, "dropped rcvd_pkt_len %d pktlen %d",<br />                     rcvd_pkt_len, rx_pkts[nb_rx]->pkt_len);<br />-                zxdh_discard_rxbuf(vq, rx_pkts[nb_rx]);<br />+                rte_pktmbuf_free(rx_pkts[nb_rx]);<br />                 rxvq->stats.errors++;<br />                 rxvq->stats.truncated_err++;<br />                 continue;<br />@@ -942,14 +939,14 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,<br />             prev->next = rxm;<br />             prev = rxm;<br />             rxm->next = NULL;<br />-            extra_idx += 1;<br />+            extra_idx++;<br />         }<br />         seg_res -= rcv_cnt;<br />         if (!seg_res) {<br />             if (unlikely(rcvd_pkt_len != rx_pkts[nb_rx]->pkt_len)) {<br />                 PMD_RX_LOG(ERR, "dropped rcvd_pkt_len %d pktlen %d",<br />                     rcvd_pkt_len, rx_pkts[nb_rx]->pkt_len);<br />-                zxdh_discard_rxbuf(vq, rx_pkts[nb_rx]);<br />+                rte_pktmbuf_free(rx_pkts[nb_rx]);<br />                 rxvq->stats.errors++;<br />                 rxvq->stats.truncated_err++;<br />                 continue;<br />@@ -961,26 +958,285 @@ zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts,<br />     rxvq->stats.packets += nb_rx;<br />  <br /> refill:<br />-    /* Allocate new mbuf for the used descriptor */<br />-    if (likely(!zxdh_queue_full(vq))) {<br />-        struct rte_mbuf *new_pkts[ZXDH_MBUF_BURST_SZ];<br />-        /* free_cnt may include mrg descs */<br />-        uint16_t free_cnt = RTE_MIN(vq->vq_free_cnt, ZXDH_MBUF_BURST_SZ);<br />-<br />-        if (!rte_pktmbuf_alloc_bulk(rxvq->mpool, new_pkts, free_cnt)) {<br />-            error = zxdh_enqueue_recv_refill_packed(vq, new_pkts, free_cnt);<br />-            if (unlikely(error)) {<br />-                for (i = 0; i < free_cnt; i++)<br />-                    rte_pktmbuf_free(new_pkts[i]);<br />-            }<br />+    if (vq->vq_free_cnt > 0) {<br />+        struct rte_eth_dev *dev = hw->eth_dev;<br />+        refill_que_descs(vq, dev);<br />+        zxdh_queue_notify(vq);<br />+    }<br />+<br />+    return nb_rx;<br />+}<br />+<br />+static inline int pkt_padding(struct rte_mbuf *cookie, struct zxdh_hw *hw)<br />+{<br />+    uint16_t mtu_or_mss = 0;<br />+    uint16_t pkt_flag_lw16 = ZXDH_NO_IPID_UPDATE;<br />+    uint16_t l3_offset;<br />+    uint8_t pcode = ZXDH_PCODE_NO_IP_PKT_TYPE;<br />+    uint8_t l3_ptype = ZXDH_PI_L3TYPE_NOIP;<br />+    struct zxdh_pi_hdr *pi_hdr;<br />+    struct zxdh_pd_hdr_dl *pd_hdr;<br />+    struct zxdh_net_hdr_dl *net_hdr_dl = hw->net_hdr_dl;<br />+    uint8_t hdr_len = hw->dl_net_hdr_len;<br />+    uint16_t ol_flag = 0;<br />+    struct zxdh_net_hdr_dl *hdr = NULL;<br />+    hdr = (struct zxdh_net_hdr_dl *)rte_pktmbuf_prepend(cookie, hdr_len);<br />+    if (unlikely(hdr == NULL))<br />+        return -1;<br />+<br />+    rte_memcpy(hdr, net_hdr_dl, hdr_len);<br />+<br />+    if (hw->has_tx_offload) {<br />+        pi_hdr = &hdr->pipd_hdr_dl.pi_hdr;<br />+        pd_hdr = &hdr->pipd_hdr_dl.pd_hdr;<br />+<br />+        pcode = ZXDH_PCODE_IP_PKT_TYPE;<br />+        if (cookie->ol_flags & RTE_MBUF_F_TX_IPV6)<br />+            l3_ptype = ZXDH_PI_L3TYPE_IPV6;<br />+        else if (cookie->ol_flags & RTE_MBUF_F_TX_IPV4)<br />+            l3_ptype = ZXDH_PI_L3TYPE_IP;<br />+        else<br />+            pcode = ZXDH_PCODE_NO_IP_PKT_TYPE;<br />  <br />-            if (unlikely(zxdh_queue_kick_prepare_packed(vq)))<br />-                zxdh_queue_notify(vq);<br />+        if (cookie->ol_flags & RTE_MBUF_F_TX_TCP_SEG) {<br />+            mtu_or_mss = (cookie->tso_segsz >= ZXDH_MIN_MSS) ?<br />+                cookie->tso_segsz : ZXDH_MIN_MSS;<br />+            pi_hdr->pkt_flag_hi8  |= ZXDH_TX_TCPUDP_CKSUM_CAL;<br />+            pkt_flag_lw16 |= ZXDH_NO_IP_FRAGMENT | ZXDH_TX_IP_CKSUM_CAL;<br />+            pcode = ZXDH_PCODE_TCP_PKT_TYPE;<br />+        } else if (cookie->ol_flags & RTE_MBUF_F_TX_UDP_SEG) {<br />+            mtu_or_mss = hw->eth_dev->data->mtu;<br />+            mtu_or_mss = (mtu_or_mss >= ZXDH_MIN_MSS) ? mtu_or_mss : ZXDH_MIN_MSS;<br />+            pkt_flag_lw16 |= ZXDH_TX_IP_CKSUM_CAL;<br />+            pi_hdr->pkt_flag_hi8 |= ZXDH_NO_TCP_FRAGMENT | ZXDH_TX_TCPUDP_CKSUM_CAL;<br />+            pcode = ZXDH_PCODE_UDP_PKT_TYPE;<br />         } else {<br />-            struct rte_eth_dev *dev = hw->eth_dev;<br />+            pkt_flag_lw16 |= ZXDH_NO_IP_FRAGMENT;<br />+            pi_hdr->pkt_flag_hi8 |= ZXDH_NO_TCP_FRAGMENT;<br />+        }<br />+<br />+        if (cookie->ol_flags & RTE_MBUF_F_TX_IP_CKSUM)<br />+            pkt_flag_lw16 |= ZXDH_TX_IP_CKSUM_CAL;<br />+<br />+        if ((cookie->ol_flags & RTE_MBUF_F_TX_UDP_CKSUM) == RTE_MBUF_F_TX_UDP_CKSUM) {<br />+            pcode = ZXDH_PCODE_UDP_PKT_TYPE;<br />+            pi_hdr->pkt_flag_hi8 |= ZXDH_TX_TCPUDP_CKSUM_CAL;<br />+        } else if ((cookie->ol_flags & RTE_MBUF_F_TX_TCP_CKSUM) ==<br />+            RTE_MBUF_F_TX_TCP_CKSUM) {<br />+            pcode = ZXDH_PCODE_TCP_PKT_TYPE;<br />+            pi_hdr->pkt_flag_hi8 |= ZXDH_TX_TCPUDP_CKSUM_CAL;<br />+        }<br />+        pkt_flag_lw16 |= (mtu_or_mss >> ZXDH_MTU_MSS_UNIT_SHIFTBIT) & ZXDH_MTU_MSS_MASK;<br />+        pi_hdr->pkt_flag_lw16 = rte_be_to_cpu_16(pkt_flag_lw16);<br />+        pi_hdr->pkt_type = l3_ptype | ZXDH_PKT_FORM_CPU | pcode;<br />+<br />+        l3_offset = hdr_len + cookie->l2_len;<br />+        l3_offset += (cookie->ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK) ?<br />+                    cookie->outer_l2_len + cookie->outer_l3_len : 0;<br />+        pi_hdr->l3_offset = rte_be_to_cpu_16(l3_offset);<br />+        pi_hdr->l4_offset = rte_be_to_cpu_16(l3_offset + cookie->l3_len);<br />+        if (cookie->ol_flags & RTE_MBUF_F_TX_OUTER_IP_CKSUM)<br />+            ol_flag |= ZXDH_PD_OFFLOAD_OUTER_IPCSUM;<br />+    } else {<br />+        pd_hdr = &hdr->pd_hdr;<br />+    }<br />+<br />+    pd_hdr->dst_vfid = rte_be_to_cpu_16(cookie->port);<br />+<br />+    if (cookie->ol_flags & (RTE_MBUF_F_TX_VLAN | RTE_MBUF_F_TX_QINQ)) {<br />+        ol_flag |= ZXDH_PD_OFFLOAD_CVLAN_INSERT;<br />+        pd_hdr->cvlan_insert = rte_be_to_cpu_16(cookie->vlan_tci);<br />+        if (cookie->ol_flags & RTE_MBUF_F_TX_QINQ) {<br />+            ol_flag |= ZXDH_PD_OFFLOAD_SVLAN_INSERT;<br />+            pd_hdr->svlan_insert = rte_be_to_cpu_16(cookie->vlan_tci_outer);<br />+        }<br />+    }<br />+<br />+    pd_hdr->ol_flag = rte_be_to_cpu_16(ol_flag);<br />+    return 0;<br />+}<br />+<br />+/* Populate 4 descriptors with data from 4 mbufs */<br />+static inline void<br />+tx_bunch(struct zxdh_virtqueue *vq, volatile struct zxdh_vring_packed_desc *txdp,<br />+        struct rte_mbuf **pkts)<br />+{<br />+    uint16_t flags = vq->cached_flags;<br />+    int i;<br />+    for (i = 0; i < N_PER_LOOP; ++i, ++txdp, ++pkts) {<br />+        /* write data to descriptor */<br />+        txdp->addr = rte_mbuf_data_iova(*pkts);<br />+        txdp->len = (*pkts)->data_len;<br />+        txdp->flags = flags;<br />+    }<br />+}<br />+<br />+/* Populate 1 descriptor with data from 1 mbuf */<br />+static inline void<br />+tx1(struct zxdh_virtqueue *vq, volatile struct zxdh_vring_packed_desc *txdp,<br />+        struct rte_mbuf *pkts)<br />+{<br />+    uint16_t flags = vq->cached_flags;<br />+    txdp->addr = rte_mbuf_data_iova(pkts);<br />+    txdp->len = pkts->data_len;<br />+    txdp->flags = flags;<br />+}<br />+<br />+static void submit_to_backend_simple(struct zxdh_virtqueue  *vq,<br />+            struct rte_mbuf **tx_pkts, uint16_t nb_pkts)<br />+{<br />+    struct zxdh_hw *hw = vq->hw;<br />+    struct rte_mbuf *m = NULL;<br />+    uint16_t id =  vq->vq_avail_idx;<br />+    struct zxdh_vring_packed_desc *txdp = &vq->vq_packed.ring.desc[id];<br />+    struct zxdh_vq_desc_extra *dxp = &vq->vq_descx[id];<br />+    int mainpart, leftover;<br />+    int i, j;<br />+<br />+    /*<br />+     * Process most of the packets in chunks of N pkts.  Any<br />+     * leftover packets will get processed one at a time.<br />+     */<br />+    mainpart = (nb_pkts & ((uint32_t)~N_PER_LOOP_MASK));<br />+    leftover = (nb_pkts & ((uint32_t)N_PER_LOOP_MASK));<br />+<br />+    for (i = 0; i < mainpart; i += N_PER_LOOP) {<br />+        rte_prefetch0(dxp + i);<br />+        rte_prefetch0(tx_pkts + i);<br />+        for (j = 0; j < N_PER_LOOP; ++j) {<br />+            m  = *(tx_pkts + i + j);<br />+            pkt_padding(m, hw);<br />+            (dxp + i + j)->cookie = (void *)m;<br />+        }<br />+        /* write data to descriptor */<br />+        tx_bunch(vq, txdp + i, tx_pkts + i);<br />+    }<br />  <br />-            dev->data->rx_mbuf_alloc_failed += free_cnt;<br />+    if (leftover > 0) {<br />+        rte_prefetch0(dxp + mainpart);<br />+        rte_prefetch0(tx_pkts + mainpart);<br />+<br />+        for (i = 0; i < leftover; ++i) {<br />+            m =  *(tx_pkts + mainpart + i);<br />+            pkt_padding(m, hw);<br />+            (dxp + mainpart + i)->cookie = m;<br />+            tx1(vq, txdp + mainpart + i, *(tx_pkts + mainpart + i));<br />         }<br />     }<br />+}<br />+<br />+uint16_t zxdh_xmit_pkts_simple(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)<br />+{<br />+    struct zxdh_virtnet_tx *txvq = tx_queue;<br />+    struct zxdh_virtqueue  *vq   = txq_get_vq(txvq);<br />+    uint16_t nb_tx = 0, nb_tx_left;<br />+<br />+    zxdh_xmit_fast_flush(vq);<br />+<br />+    nb_pkts = (uint16_t)RTE_MIN(nb_pkts, vq->vq_free_cnt);<br />+    if (unlikely(nb_pkts == 0)) {<br />+        txvq->stats.idle++;<br />+        return 0;<br />+    }<br />+<br />+    nb_tx_left = nb_pkts;<br />+    if ((vq->vq_avail_idx + nb_pkts) >= vq->vq_nentries) {<br />+        nb_tx = vq->vq_nentries - vq->vq_avail_idx;<br />+        nb_tx_left = nb_pkts - nb_tx;<br />+        submit_to_backend_simple(vq, tx_pkts, nb_tx);<br />+        vq->vq_avail_idx = 0;<br />+        vq->cached_flags ^= ZXDH_VRING_PACKED_DESC_F_AVAIL_USED;<br />+<br />+        vq->vq_free_cnt  -= nb_tx;<br />+        tx_pkts += nb_tx;<br />+    }<br />+    if (nb_tx_left) {<br />+        submit_to_backend_simple(vq, tx_pkts, nb_tx_left);<br />+        vq->vq_avail_idx  += nb_tx_left;<br />+        vq->vq_free_cnt  -= nb_tx_left;<br />+    }<br />+<br />+    zxdh_queue_notify(vq);<br />+    txvq->stats.packets += nb_pkts;<br />+<br />+    return nb_pkts;<br />+}<br />+<br />+static inline int zxdh_init_mbuf(struct rte_mbuf *rxm, uint16_t len,<br />+        struct zxdh_hw *hw, struct zxdh_virtnet_rx *rxvq)<br />+{<br />+    uint16_t hdr_size = 0;<br />+    struct zxdh_net_hdr_ul *header;<br />+<br />+    header = (struct zxdh_net_hdr_ul *)((char *)<br />+                    rxm->buf_addr + RTE_PKTMBUF_HEADROOM);<br />+    rxm->ol_flags = 0;<br />+    rxm->vlan_tci = 0;<br />+    rxm->vlan_tci_outer = 0;<br />+<br />+    hdr_size = header->type_hdr.pd_len << 1;<br />+    if (unlikely(header->type_hdr.num_buffers != 1)) {<br />+        PMD_RX_LOG(DEBUG, "hdr_size:%u nb_segs %d is invalid",<br />+            hdr_size, header->type_hdr.num_buffers);<br />+        rte_pktmbuf_free(rxm);<br />+        rxvq->stats.invalid_hdr_len_err++;<br />+        return -1;<br />+    }<br />+    zxdh_rx_update_mbuf(hw, rxm, header);<br />+<br />+    rxm->nb_segs = 1;<br />+    rxm->data_off = RTE_PKTMBUF_HEADROOM + hdr_size;<br />+    rxm->data_len = len - hdr_size;<br />+    rxm->port = hw->port_id;<br />+<br />+    if (rxm->data_len != rxm->pkt_len) {<br />+        PMD_RX_LOG(ERR, "dropped rcvd_pkt_len %d pktlen %d  bufaddr %p.",<br />+                    rxm->data_len, rxm->pkt_len, rxm->buf_addr);<br />+        rte_pktmbuf_dump(stdout, rxm, 40);<br />+        rte_pktmbuf_free(rxm);<br />+        rxvq->stats.truncated_err++;<br />+        rxvq->stats.errors++;<br />+        return -1;<br />+    }<br />+    return 0;<br />+}<br />+<br />+uint16_t zxdh_recv_single_pkts(void *rx_queue, struct rte_mbuf **rcv_pkts, uint16_t nb_pkts)<br />+{<br />+    struct zxdh_virtnet_rx *rxvq = rx_queue;<br />+    struct zxdh_virtqueue *vq = rxq_get_vq(rxvq);<br />+    struct zxdh_hw *hw = vq->hw;<br />+    struct rte_mbuf *rxm;<br />+    uint32_t lens[ZXDH_MBUF_BURST_SZ];<br />+    uint16_t len = 0;<br />+    uint16_t nb_rx = 0;<br />+    uint16_t num;<br />+    uint16_t i = 0;<br />+<br />+    num = nb_pkts;<br />+    if (unlikely(num > ZXDH_MBUF_BURST_SZ))<br />+        num = ZXDH_MBUF_BURST_SZ;<br />+    num = zxdh_dequeue_burst_rx_packed(vq, rcv_pkts, lens, num);<br />+    if (num == 0) {<br />+        rxvq->stats.idle++;<br />+        goto refill;<br />+    }<br />+<br />+    for (i = 0; i < num; i++) {<br />+        rxm = rcv_pkts[i];<br />+        len = lens[i];<br />+        if (unlikely(zxdh_init_mbuf(rxm, len, hw, &vq->rxq) < 0))<br />+            break;<br />+<br />+        nb_rx++;<br />+    }<br />+    rxvq->stats.packets += nb_rx;<br />+<br />+refill:<br />+    if (vq->vq_free_cnt > 0) {<br />+        struct rte_eth_dev *dev = hw->eth_dev;<br />+        refill_que_descs(vq, dev);<br />+        zxdh_queue_notify(vq);<br />+    }<br />     return nb_rx;<br /> }<br />diff --git a/drivers/net/zxdh/zxdh_rxtx.h b/drivers/net/zxdh/zxdh_rxtx.h<br />index 424048607e..6fce04b803 100644<br />--- a/drivers/net/zxdh/zxdh_rxtx.h<br />+++ b/drivers/net/zxdh/zxdh_rxtx.h<br />@@ -36,44 +36,39 @@ struct zxdh_virtnet_stats {<br />     uint64_t bytes;<br />     uint64_t errors;<br />     uint64_t idle;<br />-    uint64_t full;<br />-    uint64_t norefill;<br />-    uint64_t multicast;<br />-    uint64_t broadcast;<br />     uint64_t truncated_err;<br />     uint64_t offload_cfg_err;<br />     uint64_t invalid_hdr_len_err;<br />     uint64_t no_segs_err;<br />+    uint64_t no_free_tx_desc_err;<br />     uint64_t size_bins[8];<br /> };<br />  <br /> struct __rte_cache_aligned zxdh_virtnet_rx {<br />     struct zxdh_virtqueue         *vq;<br />-<br />-    uint64_t                  mbuf_initializer; /* value to init mbufs. */<br />     struct rte_mempool       *mpool;            /* mempool for mbuf allocation */<br />-    uint16_t                  queue_id;         /* DPDK queue index. */<br />-    uint16_t                  port_id;          /* Device port identifier. */<br />     struct zxdh_virtnet_stats      stats;<br />     const struct rte_memzone *mz;               /* mem zone to populate RX ring. */<br />-<br />-    /* dummy mbuf, for wraparound when processing RX ring. */<br />-    struct rte_mbuf           fake_mbuf;<br />+    uint64_t offloads;<br />+    uint16_t                  queue_id;         /* DPDK queue index. */<br />+    uint16_t                  port_id;          /* Device port identifier. */<br /> };<br />  <br /> struct __rte_cache_aligned zxdh_virtnet_tx {<br />     struct zxdh_virtqueue         *vq;<br />-<br />-    rte_iova_t                zxdh_net_hdr_mem; /* hdr for each xmit packet */<br />-    uint16_t                  queue_id;           /* DPDK queue index. */<br />-    uint16_t                  port_id;            /* Device port identifier. */<br />+    const struct rte_memzone *zxdh_net_hdr_mz;  /* memzone to populate hdr. */<br />+    rte_iova_t               zxdh_net_hdr_mem; /* hdr for each xmit packet */<br />     struct zxdh_virtnet_stats      stats;<br />     const struct rte_memzone *mz;                 /* mem zone to populate TX ring. */<br />-    const struct rte_memzone *zxdh_net_hdr_mz;  /* memzone to populate hdr. */<br />+    uint64_t offloads;<br />+    uint16_t                  queue_id;           /* DPDK queue index. */<br />+    uint16_t                  port_id;            /* Device port identifier. */<br /> };<br />  <br /> uint16_t zxdh_xmit_pkts_packed(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);<br /> uint16_t zxdh_xmit_pkts_prepare(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);<br /> uint16_t zxdh_recv_pkts_packed(void *rx_queue, struct rte_mbuf **rx_pkts, uint16_t nb_pkts);<br />+uint16_t zxdh_xmit_pkts_simple(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);<br />+uint16_t zxdh_recv_single_pkts(void *rx_queue, struct rte_mbuf **rcv_pkts, uint16_t nb_pkts);<br />  <br /> #endif  /* ZXDH_RXTX_H */<br />--  <br />2.27.0<br />