This patch implements rx_pkt_burst() and tx_pkt_burst()
to hanld IO packets.

For Tx packets, this commit implements parsing ol_flags of
mbuf and filling those offload info on wqe so that hardware
can process the packets correctly. Furthermore, this commit
allocates a mempool to cover scenes with too many mbufs for
one packet.

For Rx packets, this commit implements filling ol_flags of
mbuf and rearming new mbuf and rq wqe.

Signed-off-by: Yanling Song <son...@ramaxel.com>
---
 drivers/net/spnic/spnic_ethdev.c |  48 +++
 drivers/net/spnic/spnic_ethdev.h |   7 +
 drivers/net/spnic/spnic_rx.c     | 209 ++++++++++++
 drivers/net/spnic/spnic_rx.h     | 137 ++++++++
 drivers/net/spnic/spnic_tx.c     | 524 +++++++++++++++++++++++++++++++
 drivers/net/spnic/spnic_tx.h     |   7 +
 6 files changed, 932 insertions(+)

diff --git a/drivers/net/spnic/spnic_ethdev.c b/drivers/net/spnic/spnic_ethdev.c
index 0459959ca5..7bd9aa0b9e 100644
--- a/drivers/net/spnic/spnic_ethdev.c
+++ b/drivers/net/spnic/spnic_ethdev.c
@@ -970,6 +970,32 @@ static int spnic_dev_start(struct rte_eth_dev *eth_dev)
        return err;
 }
 
+static int spnic_copy_mempool_init(struct spnic_nic_dev *nic_dev)
+{
+       nic_dev->cpy_mpool = rte_mempool_lookup(nic_dev->dev_name);
+       if (nic_dev->cpy_mpool == NULL) {
+               nic_dev->cpy_mpool =
+               rte_pktmbuf_pool_create(nic_dev->dev_name,
+                                       SPNIC_COPY_MEMPOOL_DEPTH, 0, 0,
+                                       SPNIC_COPY_MBUF_SIZE, rte_socket_id());
+               if (nic_dev->cpy_mpool == NULL) {
+                       PMD_DRV_LOG(ERR, "Create copy mempool failed, errno: 
%d, dev_name: %s",
+                                   rte_errno, nic_dev->dev_name);
+                       return -ENOMEM;
+               }
+       }
+
+       return 0;
+}
+
+static void spnic_copy_mempool_uninit(struct spnic_nic_dev *nic_dev)
+{
+       if (nic_dev->cpy_mpool != NULL) {
+               rte_mempool_free(nic_dev->cpy_mpool);
+               nic_dev->cpy_mpool = NULL;
+       }
+}
+
 /**
  * Stop the device.
  *
@@ -986,6 +1012,9 @@ static int spnic_dev_stop(struct rte_eth_dev *dev)
        int err;
 
        nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(dev);
+       if (!nic_dev || !spnic_support_nic(nic_dev->hwdev))
+               return 0;
+
        if (!rte_bit_relaxed_test_and_clear32(SPNIC_DEV_START, 
&nic_dev->dev_status)) {
                PMD_DRV_LOG(INFO, "Device %s already stopped",
                            nic_dev->dev_name);
@@ -1014,6 +1043,11 @@ static int spnic_dev_stop(struct rte_eth_dev *dev)
 
        spnic_flush_qps_res(nic_dev->hwdev);
 
+       /*
+        * After set vport disable 100ms, no packets will be send to host
+        */
+       rte_delay_ms(100);
+
        /* Clean RSS table and rx_mode */
        spnic_remove_rxtx_configure(dev);
 
@@ -1054,6 +1088,7 @@ static int spnic_dev_close(struct rte_eth_dev *eth_dev)
        for (qid = 0; qid < nic_dev->num_rqs; qid++)
                spnic_rx_queue_release(eth_dev, qid);
 
+       spnic_copy_mempool_uninit(nic_dev);
        spnic_deinit_sw_rxtxqs(nic_dev);
        spnic_deinit_mac_addr(eth_dev);
        rte_free(nic_dev->mc_list);
@@ -1067,6 +1102,8 @@ static int spnic_dev_close(struct rte_eth_dev *eth_dev)
        spnic_free_nic_hwdev(nic_dev->hwdev);
        spnic_free_hwdev(nic_dev->hwdev);
 
+       eth_dev->rx_pkt_burst = NULL;
+       eth_dev->tx_pkt_burst = NULL;
        eth_dev->dev_ops = NULL;
 
        rte_free(nic_dev->hwdev);
@@ -1548,6 +1585,13 @@ static int spnic_func_init(struct rte_eth_dev *eth_dev)
                goto set_default_feature_fail;
        }
 
+       err = spnic_copy_mempool_init(nic_dev);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Create copy mempool failed, dev_name: %s",
+                        eth_dev->data->name);
+               goto init_mpool_fail;
+       }
+
        spnic_mutex_init(&nic_dev->rx_mode_mutex, NULL);
 
        rte_bit_relaxed_set32(SPNIC_DEV_INTR_EN, &nic_dev->dev_status);
@@ -1558,6 +1602,7 @@ static int spnic_func_init(struct rte_eth_dev *eth_dev)
 
        return 0;
 
+init_mpool_fail:
 set_default_feature_fail:
        spnic_deinit_mac_addr(eth_dev);
 
@@ -1602,6 +1647,9 @@ static int spnic_dev_init(struct rte_eth_dev *eth_dev)
                    (rte_eal_process_type() == RTE_PROC_PRIMARY) ?
                    "primary" : "secondary");
 
+       eth_dev->rx_pkt_burst = spnic_recv_pkts;
+       eth_dev->tx_pkt_burst = spnic_xmit_pkts;
+
        return spnic_func_init(eth_dev);
 }
 
diff --git a/drivers/net/spnic/spnic_ethdev.h b/drivers/net/spnic/spnic_ethdev.h
index 996b4e4b8f..2b59886942 100644
--- a/drivers/net/spnic/spnic_ethdev.h
+++ b/drivers/net/spnic/spnic_ethdev.h
@@ -4,6 +4,9 @@
 
 #ifndef _SPNIC_ETHDEV_H_
 #define _SPNIC_ETHDEV_H_
+
+#define SPNIC_COPY_MEMPOOL_DEPTH       128
+#define SPNIC_COPY_MBUF_SIZE           4096
 #define SPNIC_DEV_NAME_LEN             32
 
 #define SPNIC_UINT32_BIT_SIZE          (CHAR_BIT * sizeof(uint32_t))
@@ -17,6 +20,10 @@ enum spnic_dev_status {
        SPNIC_DEV_INTR_EN
 };
 
+enum spnic_tx_cvlan_type {
+       SPNIC_TX_TPID0,
+};
+
 enum nic_feature_cap {
        NIC_F_CSUM = BIT(0),
        NIC_F_SCTP_CRC = BIT(1),
diff --git a/drivers/net/spnic/spnic_rx.c b/drivers/net/spnic/spnic_rx.c
index e135b90162..119b547516 100644
--- a/drivers/net/spnic/spnic_rx.c
+++ b/drivers/net/spnic/spnic_rx.c
@@ -467,6 +467,117 @@ void spnic_remove_rq_from_rx_queue_list(struct 
spnic_nic_dev *nic_dev,
        nic_dev->num_rss = rss_queue_count;
 }
 
+
+static inline uint64_t spnic_rx_vlan(uint32_t offload_type, uint32_t vlan_len,
+                                     uint16_t *vlan_tci)
+{
+       uint16_t vlan_tag;
+
+       vlan_tag = SPNIC_GET_RX_VLAN_TAG(vlan_len);
+       if (!SPNIC_GET_RX_VLAN_OFFLOAD_EN(offload_type) || vlan_tag == 0) {
+               *vlan_tci = 0;
+               return 0;
+       }
+
+       *vlan_tci = vlan_tag;
+
+       return RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED;
+}
+
+static inline uint64_t spnic_rx_csum(uint32_t status, struct spnic_rxq *rxq)
+{
+       struct spnic_nic_dev *nic_dev = rxq->nic_dev;
+       uint32_t csum_err;
+       uint64_t flags;
+
+       if (unlikely(!(nic_dev->rx_csum_en & SPNIC_DEFAULT_RX_CSUM_OFFLOAD)))
+               return RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN;
+
+       /* Most case checksum is ok */
+       csum_err = SPNIC_GET_RX_CSUM_ERR(status);
+       if (likely(csum_err == 0))
+               return (RTE_MBUF_F_RX_IP_CKSUM_GOOD | 
RTE_MBUF_F_RX_L4_CKSUM_GOOD);
+
+       /*
+        * If bypass bit is set, all other err status indications should be
+        * ignored
+        */
+       if (unlikely(csum_err & SPNIC_RX_CSUM_HW_CHECK_NONE))
+               return RTE_MBUF_F_RX_IP_CKSUM_UNKNOWN;
+
+       flags = 0;
+
+       /* IP checksum error */
+       if (csum_err & SPNIC_RX_CSUM_IP_CSUM_ERR) {
+               flags |= RTE_MBUF_F_RX_IP_CKSUM_BAD;
+               rxq->rxq_stats.errors++;
+       }
+
+       /* L4 checksum error */
+       if (csum_err & SPNIC_RX_CSUM_TCP_CSUM_ERR ||
+           csum_err & SPNIC_RX_CSUM_UDP_CSUM_ERR ||
+           csum_err & SPNIC_RX_CSUM_SCTP_CRC_ERR) {
+               flags |= RTE_MBUF_F_RX_L4_CKSUM_BAD;
+               rxq->rxq_stats.errors++;
+       }
+
+       if (unlikely(csum_err == SPNIC_RX_CSUM_IPSU_OTHER_ERR))
+               rxq->rxq_stats.other_errors++;
+
+       return flags;
+}
+
+static inline uint64_t spnic_rx_rss_hash(uint32_t offload_type,
+                                        uint32_t rss_hash_value,
+                                        uint32_t *rss_hash)
+{
+       uint32_t rss_type;
+
+       rss_type = SPNIC_GET_RSS_TYPES(offload_type);
+       if (likely(rss_type != 0)) {
+               *rss_hash = rss_hash_value;
+               return RTE_MBUF_F_RX_RSS_HASH;
+       }
+
+       return 0;
+}
+
+static void spnic_recv_jumbo_pkt(struct spnic_rxq *rxq,
+                                struct rte_mbuf *head_mbuf,
+                                u32 remain_pkt_len)
+{
+       struct rte_mbuf *cur_mbuf = NULL;
+       struct rte_mbuf *rxm = NULL;
+       struct spnic_rx_info *rx_info = NULL;
+       u16 sw_ci, rx_buf_len = rxq->buf_len;
+       u32 pkt_len;
+
+       while (remain_pkt_len > 0) {
+               sw_ci = spnic_get_rq_local_ci(rxq);
+               rx_info = &rxq->rx_info[sw_ci];
+
+               spnic_update_rq_local_ci(rxq, 1);
+
+               pkt_len = remain_pkt_len > rx_buf_len ?
+                       rx_buf_len : remain_pkt_len;
+               remain_pkt_len -= pkt_len;
+
+               cur_mbuf = rx_info->mbuf;
+               cur_mbuf->data_len = (u16)pkt_len;
+               cur_mbuf->next = NULL;
+
+               head_mbuf->pkt_len += cur_mbuf->data_len;
+               head_mbuf->nb_segs++;
+
+               if (!rxm)
+                       head_mbuf->next = cur_mbuf;
+               else
+                       rxm->next = cur_mbuf;
+
+               rxm = cur_mbuf;
+       }
+}
+
 int spnic_start_all_rqs(struct rte_eth_dev *eth_dev)
 {
        struct spnic_nic_dev *nic_dev = NULL;
@@ -502,3 +613,101 @@ int spnic_start_all_rqs(struct rte_eth_dev *eth_dev)
        }
        return err;
 }
+
+u16 spnic_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, u16 nb_pkts)
+{
+       struct spnic_rxq *rxq = rx_queue;
+       struct spnic_rx_info *rx_info = NULL;
+       volatile struct spnic_rq_cqe *rx_cqe = NULL;
+       struct rte_mbuf *rxm = NULL;
+       u16 sw_ci, wqebb_cnt = 0;
+       u32 status, pkt_len, vlan_len, offload_type, hash_value;
+       u32 lro_num;
+       u64 rx_bytes = 0;
+       u16 rx_buf_len, pkts = 0;
+
+       rx_buf_len = rxq->buf_len;
+       sw_ci = spnic_get_rq_local_ci(rxq);
+
+       while (pkts < nb_pkts) {
+               rx_cqe = &rxq->rx_cqe[sw_ci];
+               status = rx_cqe->status;
+               if (!SPNIC_GET_RX_DONE(status))
+                       break;
+
+               /* Make sure rx_done is read before packet length */
+               rte_rmb();
+
+               vlan_len = rx_cqe->vlan_len;
+               pkt_len = SPNIC_GET_RX_PKT_LEN(vlan_len);
+
+               rx_info = &rxq->rx_info[sw_ci];
+               rxm = rx_info->mbuf;
+
+               /* 1. Next ci point and prefetch */
+               sw_ci++;
+               sw_ci &= rxq->q_mask;
+
+               /* 2. Prefetch next mbuf first 64B */
+               rte_prefetch0(rxq->rx_info[sw_ci].mbuf);
+
+               /* 3. Jumbo frame process */
+               if (likely(pkt_len <= rx_buf_len)) {
+                       rxm->data_len = pkt_len;
+                       rxm->pkt_len = pkt_len;
+                       wqebb_cnt++;
+               } else {
+                       rxm->data_len = rx_buf_len;
+                       rxm->pkt_len = rx_buf_len;
+
+                       /* If receive jumbo, updating ci will be done by
+                        * spnic_recv_jumbo_pkt function.
+                        */
+                       spnic_update_rq_local_ci(rxq, wqebb_cnt + 1);
+                       wqebb_cnt = 0;
+                       spnic_recv_jumbo_pkt(rxq, rxm, pkt_len - rx_buf_len);
+                       sw_ci = spnic_get_rq_local_ci(rxq);
+               }
+
+               rxm->data_off = RTE_PKTMBUF_HEADROOM;
+               rxm->port = rxq->port_id;
+
+               /* 4. Rx checksum offload */
+               rxm->ol_flags |= spnic_rx_csum(status, rxq);
+
+               /* 5. Vlan offload */
+               offload_type = rx_cqe->offload_type;
+               rxm->ol_flags |= spnic_rx_vlan(offload_type, vlan_len,
+                                               &rxm->vlan_tci);
+               /* 6. RSS */
+               hash_value = rx_cqe->hash_val;
+               rxm->ol_flags |= spnic_rx_rss_hash(offload_type, hash_value,
+                                                   &rxm->hash.rss);
+               /* 7. LRO */
+               lro_num = SPNIC_GET_RX_NUM_LRO(status);
+               if (unlikely(lro_num != 0)) {
+                       rxm->ol_flags |= RTE_MBUF_F_RX_LRO;
+                       rxm->tso_segsz = pkt_len / lro_num;
+               }
+
+               rx_cqe->status = 0;
+
+               rx_bytes += pkt_len;
+               rx_pkts[pkts++] = rxm;
+       }
+
+       if (pkts) {
+               /* 8. Update local ci */
+               spnic_update_rq_local_ci(rxq, wqebb_cnt);
+
+               /* Update packet stats */
+               rxq->rxq_stats.packets += pkts;
+               rxq->rxq_stats.bytes += rx_bytes;
+       }
+       rxq->rxq_stats.burst_pkts = pkts;
+
+       /* 9. Rearm mbuf to rxq */
+       spnic_rearm_rxq_mbuf(rxq);
+
+       return pkts;
+}
diff --git a/drivers/net/spnic/spnic_rx.h b/drivers/net/spnic/spnic_rx.h
index 0b534f1904..79cb092db0 100644
--- a/drivers/net/spnic/spnic_rx.h
+++ b/drivers/net/spnic/spnic_rx.h
@@ -5,6 +5,135 @@
 #ifndef _SPNIC_RX_H_
 #define _SPNIC_RX_H_
 
+#define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_SHIFT             0
+#define RQ_CQE_OFFOLAD_TYPE_PKT_UMBCAST_SHIFT          19
+#define RQ_CQE_OFFOLAD_TYPE_VLAN_EN_SHIFT              21
+#define RQ_CQE_OFFOLAD_TYPE_RSS_TYPE_SHIFT             24
+
+#define RQ_CQE_OFFOLAD_TYPE_PKT_TYPE_MASK              0xFFFU
+#define RQ_CQE_OFFOLAD_TYPE_PKT_UMBCAST_MASK           0x3U
+#define RQ_CQE_OFFOLAD_TYPE_VLAN_EN_MASK               0x1U
+#define RQ_CQE_OFFOLAD_TYPE_RSS_TYPE_MASK              0xFFU
+
+#define RQ_CQE_OFFOLAD_TYPE_GET(val, member)           (((val) >> \
+                               RQ_CQE_OFFOLAD_TYPE_##member##_SHIFT) & \
+                               RQ_CQE_OFFOLAD_TYPE_##member##_MASK)
+
+#define SPNIC_GET_RX_PKT_TYPE(offload_type)    \
+               RQ_CQE_OFFOLAD_TYPE_GET(offload_type, PKT_TYPE)
+
+#define SPNIC_GET_RX_PKT_UMBCAST(offload_type) \
+               RQ_CQE_OFFOLAD_TYPE_GET(offload_type, PKT_UMBCAST)
+
+#define SPNIC_GET_RX_VLAN_OFFLOAD_EN(offload_type)     \
+               RQ_CQE_OFFOLAD_TYPE_GET(offload_type, VLAN_EN)
+
+#define SPNIC_GET_RSS_TYPES(offload_type)      \
+               RQ_CQE_OFFOLAD_TYPE_GET(offload_type, RSS_TYPE)
+
+#define RQ_CQE_SGE_VLAN_SHIFT                          0
+#define RQ_CQE_SGE_LEN_SHIFT                           16
+
+#define RQ_CQE_SGE_VLAN_MASK                           0xFFFFU
+#define RQ_CQE_SGE_LEN_MASK                            0xFFFFU
+
+#define RQ_CQE_SGE_GET(val, member)                    (((val) >> \
+                                       RQ_CQE_SGE_##member##_SHIFT) & \
+                                       RQ_CQE_SGE_##member##_MASK)
+
+#define SPNIC_GET_RX_VLAN_TAG(vlan_len)        RQ_CQE_SGE_GET(vlan_len, VLAN)
+
+#define SPNIC_GET_RX_PKT_LEN(vlan_len)         RQ_CQE_SGE_GET(vlan_len, LEN)
+
+#define RQ_CQE_STATUS_CSUM_ERR_SHIFT           0
+#define RQ_CQE_STATUS_NUM_LRO_SHIFT            16
+#define RQ_CQE_STATUS_LRO_PUSH_SHIFT           25
+#define RQ_CQE_STATUS_LRO_ENTER_SHIFT          26
+#define RQ_CQE_STATUS_LRO_INTR_SHIFT           27
+
+#define RQ_CQE_STATUS_BP_EN_SHIFT              30
+#define RQ_CQE_STATUS_RXDONE_SHIFT             31
+#define RQ_CQE_STATUS_DECRY_PKT_SHIFT          29
+#define RQ_CQE_STATUS_FLUSH_SHIFT              28
+
+#define RQ_CQE_STATUS_CSUM_ERR_MASK            0xFFFFU
+#define RQ_CQE_STATUS_NUM_LRO_MASK             0xFFU
+#define RQ_CQE_STATUS_LRO_PUSH_MASK            0X1U
+#define RQ_CQE_STATUS_LRO_ENTER_MASK           0X1U
+#define RQ_CQE_STATUS_LRO_INTR_MASK            0X1U
+#define RQ_CQE_STATUS_BP_EN_MASK               0X1U
+#define RQ_CQE_STATUS_RXDONE_MASK              0x1U
+#define RQ_CQE_STATUS_FLUSH_MASK               0x1U
+#define RQ_CQE_STATUS_DECRY_PKT_MASK           0x1U
+
+#define RQ_CQE_STATUS_GET(val, member)                 (((val) >> \
+                                       RQ_CQE_STATUS_##member##_SHIFT) & \
+                                       RQ_CQE_STATUS_##member##_MASK)
+
+#define SPNIC_GET_RX_CSUM_ERR(status)  RQ_CQE_STATUS_GET(status, CSUM_ERR)
+
+#define SPNIC_GET_RX_DONE(status)      RQ_CQE_STATUS_GET(status, RXDONE)
+
+#define SPNIC_GET_RX_FLUSH(status)     RQ_CQE_STATUS_GET(status, FLUSH)
+
+#define SPNIC_GET_RX_BP_EN(status)     RQ_CQE_STATUS_GET(status, BP_EN)
+
+#define SPNIC_GET_RX_NUM_LRO(status)   RQ_CQE_STATUS_GET(status, NUM_LRO)
+
+#define SPNIC_RX_IS_DECRY_PKT(status)  RQ_CQE_STATUS_GET(status, DECRY_PKT)
+
+#define RQ_CQE_SUPER_CQE_EN_SHIFT                      0
+#define RQ_CQE_PKT_NUM_SHIFT                           1
+#define RQ_CQE_PKT_LAST_LEN_SHIFT                      6
+#define RQ_CQE_PKT_FIRST_LEN_SHIFT                     19
+
+#define RQ_CQE_SUPER_CQE_EN_MASK                       0x1
+#define RQ_CQE_PKT_NUM_MASK                            0x1FU
+#define RQ_CQE_PKT_FIRST_LEN_MASK                      0x1FFFU
+#define RQ_CQE_PKT_LAST_LEN_MASK                       0x1FFFU
+
+#define RQ_CQE_PKT_NUM_GET(val, member)                        (((val) >> \
+                                       RQ_CQE_PKT_##member##_SHIFT) & \
+                                       RQ_CQE_PKT_##member##_MASK)
+#define SPNIC_GET_RQ_CQE_PKT_NUM(pkt_info) RQ_CQE_PKT_NUM_GET(pkt_info, NUM)
+
+#define RQ_CQE_SUPER_CQE_EN_GET(val, member)           (((val) >> \
+                                       RQ_CQE_##member##_SHIFT) & \
+                                       RQ_CQE_##member##_MASK)
+#define SPNIC_GET_SUPER_CQE_EN(pkt_info)       \
+       RQ_CQE_SUPER_CQE_EN_GET(pkt_info, SUPER_CQE_EN)
+
+#define RQ_CQE_PKT_LEN_GET(val, member)                        (((val) >> \
+                                               RQ_CQE_PKT_##member##_SHIFT) & \
+                                               RQ_CQE_PKT_##member##_MASK)
+
+#define RQ_CQE_DECRY_INFO_DECRY_STATUS_SHIFT   8
+#define RQ_CQE_DECRY_INFO_ESP_NEXT_HEAD_SHIFT  0
+
+#define RQ_CQE_DECRY_INFO_DECRY_STATUS_MASK    0xFFU
+#define RQ_CQE_DECRY_INFO_ESP_NEXT_HEAD_MASK   0xFFU
+
+#define RQ_CQE_DECRY_INFO_GET(val, member)             (((val) >> \
+                               RQ_CQE_DECRY_INFO_##member##_SHIFT) & \
+                               RQ_CQE_DECRY_INFO_##member##_MASK)
+
+#define SPNIC_GET_DECRYPT_STATUS(decry_info)   \
+       RQ_CQE_DECRY_INFO_GET(decry_info, DECRY_STATUS)
+
+#define SPNIC_GET_ESP_NEXT_HEAD(decry_info)    \
+       RQ_CQE_DECRY_INFO_GET(decry_info, ESP_NEXT_HEAD)
+
+/* Rx cqe checksum err */
+#define SPNIC_RX_CSUM_IP_CSUM_ERR      BIT(0)
+#define SPNIC_RX_CSUM_TCP_CSUM_ERR     BIT(1)
+#define SPNIC_RX_CSUM_UDP_CSUM_ERR     BIT(2)
+#define SPNIC_RX_CSUM_IGMP_CSUM_ERR    BIT(3)
+#define SPNIC_RX_CSUM_ICMPV4_CSUM_ERR  BIT(4)
+#define SPNIC_RX_CSUM_ICMPV6_CSUM_ERR  BIT(5)
+#define SPNIC_RX_CSUM_SCTP_CRC_ERR     BIT(6)
+#define SPNIC_RX_CSUM_HW_CHECK_NONE    BIT(7)
+#define SPNIC_RX_CSUM_IPSU_OTHER_ERR   BIT(8)
+
 #define SPNIC_DEFAULT_RX_CSUM_OFFLOAD  0xFFF
 
 #define SPNIC_RSS_OFFLOAD_ALL ( \
@@ -138,8 +267,16 @@ void spnic_free_all_rxq_mbufs(struct spnic_nic_dev 
*nic_dev);
 int spnic_update_rss_config(struct rte_eth_dev *dev,
                            struct rte_eth_rss_conf *rss_conf);
 
+int spnic_poll_rq_empty(struct spnic_rxq *rxq);
+
+void spnic_dump_cqe_status(struct spnic_rxq *rxq, u32 *cqe_done_cnt,
+                           u32 *cqe_hole_cnt, u32 *head_ci,
+                           u32 *head_done);
+
 int spnic_start_all_rqs(struct rte_eth_dev *eth_dev);
 
+u16 spnic_recv_pkts(void *rx_queue, struct rte_mbuf **rx_pkts, u16 nb_pkts);
+
 void spnic_add_rq_to_rx_queue_list(struct spnic_nic_dev *nic_dev,
                                    u16 queue_id);
 
diff --git a/drivers/net/spnic/spnic_tx.c b/drivers/net/spnic/spnic_tx.c
index b0b2053596..f893d6cb26 100644
--- a/drivers/net/spnic/spnic_tx.c
+++ b/drivers/net/spnic/spnic_tx.c
@@ -30,6 +30,18 @@
 #define SPNIC_TX_OUTER_CHECKSUM_FLAG_SET       1
 #define SPNIC_TX_OUTER_CHECKSUM_FLAG_NO_SET    0
 
+#define SPNIC_TX_OFFLOAD_MASK (        \
+               SPNIC_TX_CKSUM_OFFLOAD_MASK | \
+               RTE_MBUF_F_TX_VLAN)
+
+#define SPNIC_TX_CKSUM_OFFLOAD_MASK ( \
+               RTE_MBUF_F_TX_IP_CKSUM | \
+               RTE_MBUF_F_TX_TCP_CKSUM | \
+               RTE_MBUF_F_TX_UDP_CKSUM | \
+               RTE_MBUF_F_TX_SCTP_CKSUM | \
+               RTE_MBUF_F_TX_OUTER_IP_CKSUM | \
+               RTE_MBUF_F_TX_TCP_SEG)
+
 /**
  * Get send queue free wqebb cnt
  *
@@ -289,6 +301,518 @@ static int spnic_tx_done_cleanup(void *txq, u32 free_cnt)
 
        return spnic_xmit_mbuf_cleanup(tx_queue, try_free_cnt);
 }
+
+static inline int spnic_tx_offload_pkt_prepare(struct rte_mbuf *mbuf,
+                                              u16 *inner_l3_offset)
+{
+       uint64_t ol_flags = mbuf->ol_flags;
+
+       /* Only support vxlan offload */
+       if ((ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK) &&
+               (!(ol_flags & RTE_MBUF_F_TX_TUNNEL_VXLAN)))
+               return -EINVAL;
+
+#ifdef RTE_LIBRTE_ETHDEV_DEBUG
+       if (rte_validate_tx_offload(mbuf) != 0)
+               return -EINVAL;
+#endif
+       if ((ol_flags & RTE_MBUF_F_TX_TUNNEL_VXLAN))  {
+               if ((ol_flags & RTE_MBUF_F_TX_OUTER_IP_CKSUM) ||
+                   (ol_flags & RTE_MBUF_F_TX_OUTER_IPV6) ||
+                   (ol_flags & RTE_MBUF_F_TX_TCP_SEG)) {
+                       /*
+                        * For this senmatic, l2_len of mbuf means
+                        * len(out_udp + vxlan + in_eth)
+                        */
+                       *inner_l3_offset = mbuf->l2_len + mbuf->outer_l2_len +
+                                          mbuf->outer_l3_len;
+               } else {
+                       /*
+                        * For this senmatic, l2_len of mbuf means
+                        * len(out_eth + out_ip + out_udp + vxlan + in_eth)
+                        */
+                       *inner_l3_offset = mbuf->l2_len;
+               }
+       } else {
+               /* For non-tunnel type pkts */
+               *inner_l3_offset = mbuf->l2_len;
+       }
+
+       return 0;
+}
+
+/**
+ * Set vlan offload info
+ *
+ * @param[in] task
+ *   Send queue wqe task section
+ * @param[in] vlan_tag
+ *   Vlan tag info
+ * @param[in] vlan_type
+ *   Vlan type in hardware
+ */
+static inline void spnic_set_vlan_tx_offload(struct spnic_sq_task *task,
+                                            u16 vlan_tag, u8 vlan_type)
+{
+       task->vlan_offload = SQ_TASK_INFO3_SET(vlan_tag, VLAN_TAG) |
+                            SQ_TASK_INFO3_SET(vlan_type, VLAN_TYPE) |
+                            SQ_TASK_INFO3_SET(1U, VLAN_TAG_VALID);
+}
+
+static inline int spnic_set_tx_offload(struct rte_mbuf *mbuf,
+                                      struct spnic_sq_task *task,
+                                      struct spnic_wqe_info *wqe_info)
+{
+       uint64_t ol_flags = mbuf->ol_flags;
+       u16 pld_offset = 0;
+       u32 queue_info = 0;
+       u16 vlan_tag;
+
+       task->pkt_info0 = 0;
+       task->ip_identify = 0;
+       task->pkt_info2 = 0;
+       task->vlan_offload = 0;
+
+       /* Vlan offload */
+       if (unlikely(ol_flags & RTE_MBUF_F_TX_VLAN)) {
+               vlan_tag = mbuf->vlan_tci;
+               spnic_set_vlan_tx_offload(task, vlan_tag, SPNIC_TX_TPID0);
+       }
+
+       if (!(ol_flags & SPNIC_TX_CKSUM_OFFLOAD_MASK))
+               return 0;
+
+       /* Tso offload */
+       if (ol_flags & RTE_MBUF_F_TX_TCP_SEG) {
+               pld_offset = wqe_info->payload_offset;
+               if ((pld_offset >> 1) > MAX_PAYLOAD_OFFSET)
+                       return -EINVAL;
+
+               task->pkt_info0 |= SQ_TASK_INFO0_SET(1U, INNER_L4_EN);
+               task->pkt_info0 |= SQ_TASK_INFO0_SET(1U, INNER_L3_EN);
+
+               queue_info |= SQ_CTRL_QUEUE_INFO_SET(1U, TSO);
+               queue_info |= SQ_CTRL_QUEUE_INFO_SET(pld_offset >> 1, PLDOFF);
+
+               /* Set MSS value */
+               queue_info = SQ_CTRL_QUEUE_INFO_CLEAR(queue_info, MSS);
+               queue_info |= SQ_CTRL_QUEUE_INFO_SET(mbuf->tso_segsz, MSS);
+       } else {
+               if (ol_flags & RTE_MBUF_F_TX_IP_CKSUM)
+                       task->pkt_info0 |= SQ_TASK_INFO0_SET(1U, INNER_L3_EN);
+
+               switch (ol_flags & RTE_MBUF_F_TX_L4_MASK) {
+               case RTE_MBUF_F_TX_TCP_CKSUM:
+               case RTE_MBUF_F_TX_UDP_CKSUM:
+               case RTE_MBUF_F_TX_SCTP_CKSUM:
+                       task->pkt_info0 |= SQ_TASK_INFO0_SET(1U, INNER_L4_EN);
+
+                       break;
+
+               case RTE_MBUF_F_TX_L4_NO_CKSUM:
+                       break;
+
+               default:
+                       PMD_DRV_LOG(INFO, "not support pkt type");
+                       return -EINVAL;
+               }
+       }
+
+       /* For vxlan, also can support PKT_TX_TUNNEL_GRE, etc */
+       switch (ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK) {
+       case RTE_MBUF_F_TX_TUNNEL_VXLAN:
+               task->pkt_info0 |= SQ_TASK_INFO0_SET(1U, TUNNEL_FLAG);
+               break;
+
+       case 0:
+               break;
+
+       default:
+               /* For non UDP/GRE tunneling, drop the tunnel packet */
+               PMD_DRV_LOG(INFO, "not support tunnel pkt type");
+               return -EINVAL;
+       }
+
+       if (ol_flags & RTE_MBUF_F_TX_OUTER_IP_CKSUM)
+               task->pkt_info0 |= SQ_TASK_INFO0_SET(1U, OUT_L3_EN);
+
+       wqe_info->queue_info = queue_info;
+       return 0;
+}
+
+static inline bool spnic_is_tso_sge_valid(struct rte_mbuf *mbuf,
+                                          struct spnic_wqe_info *wqe_info)
+{
+       u32 total_len, limit_len, checked_len, left_len, adjust_mss;
+       u32 i, max_sges, left_sges, first_len;
+       struct rte_mbuf *mbuf_head, *mbuf_pre, *mbuf_first;
+
+       left_sges = mbuf->nb_segs;
+       mbuf_head = mbuf;
+       mbuf_first = mbuf;
+
+       /* tso sge number validation */
+       if (unlikely(left_sges >= SPNIC_NONTSO_PKT_MAX_SGE)) {
+               checked_len = 0;
+               total_len = 0;
+               first_len = 0;
+               adjust_mss = mbuf->tso_segsz >= TX_MSS_MIN ?
+                            mbuf->tso_segsz : TX_MSS_MIN;
+               max_sges = SPNIC_NONTSO_PKT_MAX_SGE - 1;
+               limit_len = adjust_mss + wqe_info->payload_offset;
+
+               for (i = 0; (i < max_sges) && (total_len < limit_len); i++) {
+                       total_len += mbuf->data_len;
+                       mbuf_pre = mbuf;
+                       mbuf = mbuf->next;
+               }
+
+               while (left_sges >= SPNIC_NONTSO_PKT_MAX_SGE) {
+                       if (total_len >= limit_len) {
+                               /* update the limit len */
+                               limit_len = adjust_mss;
+                               /* update checked len */
+                               checked_len += first_len;
+                               /* record the first len */
+                               first_len = mbuf_first->data_len;
+                               /* first mbuf move to the next */
+                               mbuf_first = mbuf_first->next;
+                               /* update total len */
+                               total_len -= first_len;
+                               left_sges--;
+                               i--;
+                               for (; (i < max_sges) &&
+                                    (total_len < limit_len); i++) {
+                                       total_len += mbuf->data_len;
+                                       mbuf_pre = mbuf;
+                                       mbuf = mbuf->next;
+                               }
+                       } else {
+                               /* try to copy if not valid */
+                               checked_len += (total_len - mbuf_pre->data_len);
+
+                               left_len = mbuf_head->pkt_len - checked_len;
+                               if (left_len > SPNIC_COPY_MBUF_SIZE)
+                                       return false;
+                               wqe_info->sge_cnt = (u16)(mbuf_head->nb_segs +
+                                                   i - left_sges);
+                               wqe_info->cpy_mbuf_cnt = 1;
+
+                               return true;
+                       }
+               }
+       }
+
+       wqe_info->sge_cnt = mbuf_head->nb_segs;
+       return true;
+}
+
+static inline int spnic_get_tx_offload(struct rte_mbuf *mbuf,
+                                       struct spnic_wqe_info *wqe_info)
+{
+       uint64_t ol_flags = mbuf->ol_flags;
+       u16 i, total_len, inner_l3_offset = 0;
+       struct rte_mbuf *mbuf_pkt = NULL;
+       int err;
+
+       wqe_info->sge_cnt = mbuf->nb_segs;
+       if (!(ol_flags & SPNIC_TX_OFFLOAD_MASK)) {
+               wqe_info->offload = 0;
+               return 0;
+       }
+
+       wqe_info->offload = 1;
+       err = spnic_tx_offload_pkt_prepare(mbuf, &inner_l3_offset);
+       if (err)
+               return err;
+
+       /* non tso mbuf */
+       if (likely(!(mbuf->ol_flags & RTE_MBUF_F_TX_TCP_SEG))) {
+               if (unlikely(mbuf->pkt_len > MAX_SINGLE_SGE_SIZE))
+                       return -EINVAL;
+
+               if (likely(SPNIC_NONTSO_SEG_NUM_VALID(mbuf->nb_segs)))
+                       return 0;
+
+               total_len = 0;
+               mbuf_pkt = mbuf;
+               for (i = 0; i < (SPNIC_NONTSO_PKT_MAX_SGE - 1); i++) {
+                       total_len += mbuf_pkt->data_len;
+                       mbuf_pkt = mbuf_pkt->next;
+               }
+
+               if ((u32)(total_len + (u16)SPNIC_COPY_MBUF_SIZE) <
+                   mbuf->pkt_len)
+                       return -EINVAL;
+
+               wqe_info->sge_cnt = SPNIC_NONTSO_PKT_MAX_SGE;
+               wqe_info->cpy_mbuf_cnt = 1;
+               return 0;
+       }
+
+       /* tso mbuf */
+       wqe_info->payload_offset = inner_l3_offset + mbuf->l3_len +
+                                  mbuf->l4_len;
+
+       if (unlikely(SPNIC_TSO_SEG_NUM_INVALID(mbuf->nb_segs)))
+               return -EINVAL;
+
+       if (unlikely(!spnic_is_tso_sge_valid(mbuf, wqe_info)))
+               return -EINVAL;
+
+       return 0;
+}
+
+static inline void spnic_set_buf_desc(struct spnic_sq_bufdesc *buf_descs,
+                                     rte_iova_t addr, u32 len)
+{
+       buf_descs->hi_addr = upper_32_bits(addr);
+       buf_descs->lo_addr = lower_32_bits(addr);
+       buf_descs->len  = len;
+}
+
+static inline void *spnic_copy_tx_mbuf(struct spnic_nic_dev *nic_dev,
+                                       struct rte_mbuf *mbuf, u16 sge_cnt)
+{
+       struct rte_mbuf *dst_mbuf;
+       u32 offset = 0;
+       u16 i;
+
+       if (unlikely(!nic_dev->cpy_mpool))
+               return NULL;
+
+       dst_mbuf = rte_pktmbuf_alloc(nic_dev->cpy_mpool);
+       if (unlikely(!dst_mbuf))
+               return NULL;
+
+       dst_mbuf->data_off = 0;
+       dst_mbuf->data_len = 0;
+       for (i = 0; i < sge_cnt; i++) {
+               rte_memcpy((u8 *)dst_mbuf->buf_addr + offset,
+                          (u8 *)mbuf->buf_addr + mbuf->data_off,
+                          mbuf->data_len);
+               dst_mbuf->data_len += mbuf->data_len;
+               offset += mbuf->data_len;
+               mbuf = mbuf->next;
+       }
+       dst_mbuf->pkt_len = dst_mbuf->data_len;
+       return dst_mbuf;
+}
+
+static int spnic_mbuf_dma_map_sge(struct spnic_txq *txq, struct rte_mbuf *mbuf,
+                                 struct spnic_sq_wqe_combo *wqe_combo,
+                                 struct spnic_wqe_info *wqe_info)
+{
+       struct spnic_sq_wqe_desc *wqe_desc = wqe_combo->hdr;
+       struct spnic_sq_bufdesc *buf_desc = wqe_combo->bds_head;
+       uint16_t nb_segs = wqe_info->sge_cnt - wqe_info->cpy_mbuf_cnt;
+       uint16_t real_segs = mbuf->nb_segs;
+
+       rte_iova_t dma_addr;
+       u32 i;
+
+       for (i = 0; i < nb_segs; i++) {
+               if (unlikely(mbuf == NULL)) {
+                       txq->txq_stats.mbuf_null++;
+                       return -EINVAL;
+               }
+
+               if (unlikely(mbuf->data_len == 0)) {
+                       txq->txq_stats.sge_len0++;
+                       return -EINVAL;
+               }
+
+               dma_addr = rte_mbuf_data_iova(mbuf);
+               if (i == 0) {
+                       if (wqe_combo->wqe_type == SQ_WQE_COMPACT_TYPE &&
+                           mbuf->data_len > COMPACT_WQE_MAX_CTRL_LEN) {
+                               txq->txq_stats.sge_len_too_large++;
+                               return -EINVAL;
+                       }
+                       wqe_desc->hi_addr = upper_32_bits(dma_addr);
+                       wqe_desc->lo_addr = lower_32_bits(dma_addr);
+                       wqe_desc->ctrl_len = mbuf->data_len;
+               } else {
+                       /*
+                        * Parts of wqe is in sq bottom while parts
+                        * of wqe is in sq head
+                        */
+                       if (unlikely(wqe_info->wrapped &&
+                           (u64)buf_desc == txq->sq_bot_sge_addr))
+                               buf_desc = (struct spnic_sq_bufdesc *)
+                                          (void *)txq->sq_head_addr;
+
+                       spnic_set_buf_desc(buf_desc, dma_addr, mbuf->data_len);
+                       buf_desc++;
+               }
+
+               mbuf = mbuf->next;
+       }
+
+       if (unlikely(wqe_info->cpy_mbuf_cnt != 0)) {
+               /* copy invalid mbuf segs to a valid buffer, lost performance */
+               txq->txq_stats.cpy_pkts += 1;
+               mbuf = spnic_copy_tx_mbuf(txq->nic_dev, mbuf,
+                                          real_segs - nb_segs);
+               if (unlikely(!mbuf))
+                       return -EINVAL;
+
+               txq->tx_info[wqe_info->pi].cpy_mbuf = mbuf;
+
+               /* deal with the last mbuf */
+               dma_addr = rte_mbuf_data_iova(mbuf);
+               if (unlikely(mbuf->data_len == 0)) {
+                       txq->txq_stats.sge_len0++;
+                       return -EINVAL;
+               }
+
+               if (unlikely(wqe_info->wrapped &&
+                   ((u64)buf_desc == txq->sq_bot_sge_addr)))
+                       buf_desc =
+                               (struct spnic_sq_bufdesc *)txq->sq_head_addr;
+
+               spnic_set_buf_desc(buf_desc, dma_addr, mbuf->data_len);
+       }
+       return 0;
+}
+
+static inline void spnic_prepare_sq_ctrl(struct spnic_sq_wqe_combo *wqe_combo,
+                                        struct spnic_wqe_info *wqe_info)
+{
+       struct spnic_sq_wqe_desc *wqe_desc = wqe_combo->hdr;
+
+       if (wqe_combo->wqe_type == SQ_WQE_COMPACT_TYPE) {
+               wqe_desc->ctrl_len |= SQ_CTRL_SET(SQ_NORMAL_WQE, DATA_FORMAT) |
+                               SQ_CTRL_SET(wqe_combo->wqe_type, EXTENDED) |
+                               SQ_CTRL_SET(wqe_info->owner, OWNER);
+               /* Compact wqe queue_info will transfer to ucode */
+               wqe_desc->queue_info = 0;
+               return;
+       }
+
+       wqe_desc->ctrl_len |= SQ_CTRL_SET(wqe_info->sge_cnt, BUFDESC_NUM) |
+                       SQ_CTRL_SET(wqe_combo->task_type, TASKSECT_LEN) |
+                       SQ_CTRL_SET(SQ_NORMAL_WQE, DATA_FORMAT) |
+                       SQ_CTRL_SET(wqe_combo->wqe_type, EXTENDED) |
+                       SQ_CTRL_SET(wqe_info->owner, OWNER);
+
+       wqe_desc->queue_info = wqe_info->queue_info;
+       wqe_desc->queue_info |= SQ_CTRL_QUEUE_INFO_SET(1U, UC);
+
+       if (!SQ_CTRL_QUEUE_INFO_GET(wqe_desc->queue_info, MSS)) {
+               wqe_desc->queue_info |=
+                       SQ_CTRL_QUEUE_INFO_SET(TX_MSS_DEFAULT, MSS);
+       } else if (SQ_CTRL_QUEUE_INFO_GET(wqe_desc->queue_info, MSS) <
+                  TX_MSS_MIN) {
+               /* Mss should not less than 80 */
+               wqe_desc->queue_info =
+                       SQ_CTRL_QUEUE_INFO_CLEAR(wqe_desc->queue_info, MSS);
+               wqe_desc->queue_info |= SQ_CTRL_QUEUE_INFO_SET(TX_MSS_MIN, MSS);
+       }
+}
+
+u16 spnic_xmit_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, u16 nb_pkts)
+{
+       struct spnic_txq *txq = tx_queue;
+       struct spnic_tx_info *tx_info = NULL;
+       struct rte_mbuf *mbuf_pkt = NULL;
+       struct spnic_sq_wqe_combo wqe_combo = {0};
+       struct spnic_sq_wqe *sq_wqe = NULL;
+       struct spnic_wqe_info wqe_info = {0};
+       u32 offload_err, free_cnt;
+       u64 tx_bytes = 0;
+       u16 free_wqebb_cnt, nb_tx;
+       int err;
+
+       free_cnt = txq->tx_free_thresh;
+       /* Reclaim tx mbuf before xmit new packets */
+       spnic_xmit_mbuf_cleanup(txq, free_cnt);
+
+       /* Tx loop routine */
+       for (nb_tx = 0; nb_tx < nb_pkts; nb_tx++) {
+               mbuf_pkt = *tx_pkts++;
+               if (spnic_get_tx_offload(mbuf_pkt, &wqe_info)) {
+                       txq->txq_stats.off_errs++;
+                       break;
+               }
+
+               if (!wqe_info.offload)
+                       /*
+                        * Use extended sq wqe with small TS, which can include
+                        * multi sges, or compact sq normal wqe, which just
+                        * supports one sge
+                        */
+                       wqe_info.wqebb_cnt = mbuf_pkt->nb_segs;
+               else
+                       /* Use extended sq wqe with normal TS */
+                       wqe_info.wqebb_cnt = mbuf_pkt->nb_segs + 1;
+
+               free_wqebb_cnt = spnic_get_sq_free_wqebbs(txq);
+               if (unlikely(wqe_info.wqebb_cnt > free_wqebb_cnt)) {
+                       /* Reclaim again */
+                       spnic_xmit_mbuf_cleanup(txq, free_cnt);
+                       free_wqebb_cnt = spnic_get_sq_free_wqebbs(txq);
+                       if (unlikely(wqe_info.wqebb_cnt > free_wqebb_cnt)) {
+                               txq->txq_stats.tx_busy += (nb_pkts - nb_tx);
+                               break;
+                       }
+               }
+
+               /* Get sq wqe address from wqe_page */
+               sq_wqe = spnic_get_sq_wqe(txq, &wqe_info);
+               if (unlikely(!sq_wqe)) {
+                       txq->txq_stats.tx_busy++;
+                       break;
+               }
+
+               /* Task or bd section maybe warpped for one wqe */
+               spnic_set_wqe_combo(txq, &wqe_combo, sq_wqe, &wqe_info);
+
+               wqe_info.queue_info = 0;
+               /* Fill tx packet offload into qsf and task field */
+               if (wqe_info.offload) {
+                       offload_err = spnic_set_tx_offload(mbuf_pkt,
+                                                          wqe_combo.task,
+                                                          &wqe_info);
+                       if (unlikely(offload_err)) {
+                               spnic_put_sq_wqe(txq, &wqe_info);
+                               txq->txq_stats.off_errs++;
+                               break;
+                       }
+               }
+
+               /* Fill sq_wqe buf_desc and bd_desc */
+               err = spnic_mbuf_dma_map_sge(txq, mbuf_pkt, &wqe_combo,
+                                            &wqe_info);
+               if (err) {
+                       spnic_put_sq_wqe(txq, &wqe_info);
+                       txq->txq_stats.off_errs++;
+                       break;
+               }
+
+               /* Record tx info */
+               tx_info = &txq->tx_info[wqe_info.pi];
+               tx_info->mbuf = mbuf_pkt;
+               tx_info->wqebb_cnt = wqe_info.wqebb_cnt;
+
+               spnic_prepare_sq_ctrl(&wqe_combo, &wqe_info);
+
+               spnic_write_db(txq->db_addr, txq->q_id, txq->cos, SQ_CFLAG_DP,
+                              MASKED_QUEUE_IDX(txq, txq->prod_idx));
+
+               tx_bytes += mbuf_pkt->pkt_len;
+       }
+
+       /* Update txq stats */
+       if (nb_tx) {
+               txq->txq_stats.packets += nb_tx;
+               txq->txq_stats.bytes += tx_bytes;
+       }
+       txq->txq_stats.burst_pkts = nb_tx;
+
+       return nb_tx;
+}
+
 int spnic_stop_sq(struct spnic_txq *txq)
 {
        struct spnic_nic_dev *nic_dev = txq->nic_dev;
diff --git a/drivers/net/spnic/spnic_tx.h b/drivers/net/spnic/spnic_tx.h
index d770b15c21..4c2d587104 100644
--- a/drivers/net/spnic/spnic_tx.h
+++ b/drivers/net/spnic/spnic_tx.h
@@ -4,6 +4,13 @@
 
 #ifndef _SPNIC_TX_H_
 #define _SPNIC_TX_H_
+#define MAX_SINGLE_SGE_SIZE            65536
+#define SPNIC_NONTSO_PKT_MAX_SGE       38
+#define SPNIC_NONTSO_SEG_NUM_VALID(num)        \
+       ((num) <= SPNIC_NONTSO_PKT_MAX_SGE)
+
+#define SPNIC_TSO_PKT_MAX_SGE          127
+#define SPNIC_TSO_SEG_NUM_INVALID(num) ((num) > SPNIC_TSO_PKT_MAX_SGE)
 /* Tx offload info */
 struct spnic_tx_offload_info {
        u8 outer_l2_len;
-- 
2.32.0

Reply via email to