Add simple Tx xmit functions (zxdh_xmit_pkts_simple)<br />for single-segment packet xmit.<br /> <br />Signed-off-by: Junlong Wang <wang.junlong1@zte.com.cn> <br />---<br /> drivers/net/zxdh/zxdh_ethdev.c |  11 +-<br /> drivers/net/zxdh/zxdh_rxtx.c   | 341 +++++++++++++++++++++++++--------<br /> drivers/net/zxdh/zxdh_rxtx.h   |  11 +-<br /> 3 files changed, 271 insertions(+), 92 deletions(-)<br /> <br />diff --git a/drivers/net/zxdh/zxdh_ethdev.c b/drivers/net/zxdh/zxdh_ethdev.c<br />index 0ab137189b..54d43b54d9 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 />@@ -1291,10 +1291,15 @@ static int zxdh_scattered_rx(struct rte_eth_dev *eth_dev)<br /> static int32_t<br /> zxdh_set_rxtx_funcs(struct rte_eth_dev *eth_dev)<br /> {<br />+    uint64_t tx_offloads = eth_dev->data->dev_conf.txmode.offloads;<br />+<br />     eth_dev->tx_pkt_prepare = zxdh_xmit_pkts_prepare;<br />     eth_dev->data->scattered_rx = zxdh_scattered_rx(eth_dev);<br />  <br />-    eth_dev->tx_pkt_burst = &zxdh_xmit_pkts_packed;<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 />diff --git a/drivers/net/zxdh/zxdh_rxtx.c b/drivers/net/zxdh/zxdh_rxtx.c<br />index 4723d4b1d2..e8f1cd65b0 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 && desc_is_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->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,7 +313,7 @@ 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 />@@ -345,7 +324,6 @@ zxdh_enqueue_xmit_packed_fast(struct zxdh_virtnet_tx *txvq,<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 />@@ -362,52 +340,57 @@ zxdh_enqueue_xmit_packed_fast(struct zxdh_virtnet_tx *txvq,<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->cached_flags;<br />+    /*<br />+     * IMPORTANT: For multi-seg packets, we set the head descriptor's cookie to NULL<br />+     * and store each segment's mbuf in its corresponding vq_descx[idx].cookie.<br />+     * This is required for the per-descriptor mbuf free in zxdh_xmit_fast_flush()<br />+     * which uses rte_pktmbuf_free_seg() to free individual segments.<br />+     * Any code path that attempts to read vq_descx[head_id].cookie will see NULL<br />+     * and must handle this case appropriately.<br />+     */<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->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->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 />@@ -417,7 +400,6 @@ zxdh_enqueue_xmit_packed(struct zxdh_virtnet_tx *txvq,<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 +438,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 +454,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->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 +483,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 +505,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 />@@ -1083,3 +1055,204 @@ uint16_t zxdh_recv_single_pkts(void *rx_queue, struct rte_mbuf **rcv_pkts, uint1<br />     }<br />     return nb_rx;<br /> }<br />+<br />+static inline void 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;<br />+<br />+    hdr = (struct zxdh_net_hdr_dl *)rte_pktmbuf_prepend(cookie, hdr_len);<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 (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 />+            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 />+}<br />+<br />+/*<br />+ * Populate N_PER_LOOP descriptors with data from N_PER_LOOP single-segment mbufs.<br />+ * Note: The simple transmit path (zxdh_xmit_pkts_simple) is selected only when<br />+ * RTE_ETH_TX_OFFLOAD_MULTI_SEGS is disabled, so all packets handled here are<br />+ * guaranteed to be single-segment.<br />+ */<br />+static inline void<br />+tx_bunch(struct zxdh_virtqueue *vq, volatile struct zxdh_vring_packed_desc *txdp,<br />+        struct rte_mbuf **pkts, uint16_t start_id)<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->id = start_id + i;<br />+        txdp->flags = flags;<br />+    }<br />+}<br />+<br />+/* Populate 1 descriptor with data from 1 single-segment mbuf */<br />+static inline void<br />+tx1(struct zxdh_virtqueue *vq, volatile struct zxdh_vring_packed_desc *txdp,<br />+        struct rte_mbuf *pkts, uint16_t id)<br />+{<br />+    uint16_t flags = vq->cached_flags;<br />+    txdp->addr = rte_mbuf_data_iova(pkts);<br />+    txdp->len = pkts->data_len;<br />+    txdp->id = id;<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 & ~N_PER_LOOP_MASK);<br />+    leftover = (nb_pkts & 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, id + i);<br />+    }<br />+<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), id + 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 />+    for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++)<br />+        zxdh_update_packet_stats(&txvq->stats, tx_pkts[nb_tx]);<br />+<br />+    return nb_pkts;<br />+}<br />diff --git a/drivers/net/zxdh/zxdh_rxtx.h b/drivers/net/zxdh/zxdh_rxtx.h<br />index dba9567414..783fb456de 100644<br />--- a/drivers/net/zxdh/zxdh_rxtx.h<br />+++ b/drivers/net/zxdh/zxdh_rxtx.h<br />@@ -56,18 +56,19 @@ struct __rte_cache_aligned zxdh_virtnet_rx {<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_recv_single_pkts(void *rx_queue, struct rte_mbuf **rcv_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 />  <br /> #endif  /* ZXDH_RXTX_H */<br />--  <br />2.27.0<br />