DPDK-dev Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v10 10/20] net/sxe2: add NEON vec Rx/Tx burst functions
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260606010726.2256170-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

- Implement sxe2_recv_pkts_vec_neon for bulk packet receiving.
- Implement sxe2_xmit_pkts_vec_neon for bulk packet transmission.
- Added logic to select the vectorized path based on runtime config
  and CPU flags (RTE_ARCH_ARM64).

Vectorized path improves throughput for small packets by processing
multiple descriptors simultaneously using SIMD instructions.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/meson.build            |   2 +
 drivers/net/sxe2/sxe2_txrx.c            |  39 +-
 drivers/net/sxe2/sxe2_txrx_vec.h        |  16 +-
 drivers/net/sxe2/sxe2_txrx_vec_common.h |   1 +
 drivers/net/sxe2/sxe2_txrx_vec_neon.c   | 721 ++++++++++++++++++++++++
 5 files changed, 774 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_neon.c

diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index fc4466556b..4565046eae 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -48,6 +48,8 @@ if arch_subdir == 'x86'
                 include_directories: includes,
                 c_args: [cflags, '-mavx2'])
         objs += sxe2_avx2_lib.extract_objects('sxe2_txrx_vec_avx2.c')
+elif arch_subdir == 'arm'
+        sources += files('sxe2_txrx_vec_neon.c')
 endif
 
 sources += files(
diff --git a/drivers/net/sxe2/sxe2_txrx.c b/drivers/net/sxe2/sxe2_txrx.c
index eaf95259a5..a919a84450 100644
--- a/drivers/net/sxe2/sxe2_txrx.c
+++ b/drivers/net/sxe2/sxe2_txrx.c
@@ -175,6 +175,9 @@ void sxe2_tx_mode_func_set(struct rte_eth_dev *dev)
 
 			if ((0 == (tx_mode_flags & SXE2_TX_MODE_VEC_SET_MASK)))
 				tx_mode_flags |=  SXE2_TX_MODE_VEC_SSE;
+#elif defined(RTE_ARCH_ARM64)
+			if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_NEON) == 1)
+				tx_mode_flags |= (vec_flags | SXE2_TX_MODE_VEC_NEON);
 #endif
 			if (tx_mode_flags & SXE2_TX_MODE_VEC_SET_MASK) {
 				ret = sxe2_tx_queues_vec_prepare(dev);
@@ -218,8 +221,15 @@ void sxe2_tx_mode_func_set(struct rte_eth_dev *dev)
 				dev->tx_pkt_burst = sxe2_tx_pkts_vec_sse_simple;
 			}
 		}
-	} else {
+#elif defined(RTE_ARCH_ARM64)
+		if (tx_mode_flags & SXE2_TX_MODE_VEC_NEON) {
+			dev->tx_pkt_prepare = sxe2_tx_pkts_prepare;
+			dev->tx_pkt_burst = sxe2_tx_pkts_vec_neon;
+		} else {
+			dev->tx_pkt_burst = sxe2_tx_pkts_vec_neon_simple;
+		}
 #endif
+	} else {
 		if (tx_mode_flags & SXE2_TX_MODE_SIMPLE_BATCH) {
 			dev->tx_pkt_prepare = NULL;
 			dev->tx_pkt_burst = sxe2_tx_pkts_simple;
@@ -227,9 +237,7 @@ void sxe2_tx_mode_func_set(struct rte_eth_dev *dev)
 			dev->tx_pkt_prepare = sxe2_tx_pkts_prepare;
 			dev->tx_pkt_burst = sxe2_tx_pkts;
 		}
-#ifdef RTE_ARCH_X86
 	}
-#endif
 }
 
 static const struct {
@@ -253,6 +261,12 @@ static const struct {
 	{ sxe2_tx_pkts_vec_sse_simple,
 	      "Vector SSE Simple" },
 #endif
+#ifdef RTE_ARCH_ARM64
+	{ sxe2_tx_pkts_vec_neon,
+	  "Vector NEON" },
+	{ sxe2_tx_pkts_vec_neon_simple,
+	  "Vector NEON Simple" },
+#endif
 };
 
 int32_t sxe2_tx_burst_mode_get(struct rte_eth_dev *dev,
@@ -356,6 +370,11 @@ void sxe2_rx_mode_func_set(struct rte_eth_dev *dev)
 			if (((rx_mode_flags & SXE2_RX_MODE_VEC_SET_MASK) == 0) &&
 				rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128)
 				rx_mode_flags |= SXE2_RX_MODE_VEC_SSE;
+
+#elif defined(RTE_ARCH_ARM64)
+			if (rte_cpu_get_flag_enabled(RTE_CPUFLAG_NEON) == 1) {
+				rx_mode_flags |= (vec_flags | SXE2_RX_MODE_VEC_NEON);
+			}
 #endif
 			if ((rx_mode_flags & SXE2_RX_MODE_VEC_SET_MASK) != 0) {
 				ret = sxe2_rx_queues_vec_prepare(dev);
@@ -387,6 +406,14 @@ void sxe2_rx_mode_func_set(struct rte_eth_dev *dev)
 		}
 		return;
 	}
+#elif defined(RTE_ARCH_ARM64)
+	if (rx_mode_flags & SXE2_RX_MODE_VEC_SET_MASK) {
+		if (rx_mode_flags & SXE2_RX_MODE_VEC_OFFLOAD)
+			dev->rx_pkt_burst = sxe2_rx_pkts_scattered_vec_neon_offload;
+		else
+			dev->rx_pkt_burst = sxe2_rx_pkts_scattered_vec_neon;
+		return;
+	}
 #endif
 	if (sxe2_rx_offload_en_check(dev, RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT))
 		dev->rx_pkt_burst = sxe2_rx_pkts_scattered_split;
@@ -416,6 +443,12 @@ static const struct {
 	{ sxe2_rx_pkts_scattered_vec_sse_offload,
 	      "Vector SSE Scattered" },
 #endif
+#ifdef RTE_ARCH_ARM64
+	{ sxe2_rx_pkts_scattered_vec_neon,
+	  "Vector NEON Scattered" },
+	{ sxe2_rx_pkts_scattered_vec_neon_offload,
+	  "Offload Vector NEON Scattered" },
+#endif
 };
 
 int32_t sxe2_rx_burst_mode_get(struct rte_eth_dev *dev,
diff --git a/drivers/net/sxe2/sxe2_txrx_vec.h b/drivers/net/sxe2/sxe2_txrx_vec.h
index 369777606f..c139aed776 100644
--- a/drivers/net/sxe2/sxe2_txrx_vec.h
+++ b/drivers/net/sxe2/sxe2_txrx_vec.h
@@ -13,19 +13,23 @@
 #define SXE2_RX_MODE_VEC_SSE       RTE_BIT32(2)
 #define SXE2_RX_MODE_VEC_AVX2      RTE_BIT32(3)
 #define SXE2_RX_MODE_VEC_AVX512    RTE_BIT32(4)
+#define SXE2_RX_MODE_VEC_NEON      RTE_BIT32(5)
 #define SXE2_RX_MODE_BATCH_ALLOC   RTE_BIT32(10)
 #define SXE2_RX_MODE_VEC_SET_MASK	(SXE2_RX_MODE_VEC_SIMPLE | \
 			SXE2_RX_MODE_VEC_OFFLOAD | SXE2_RX_MODE_VEC_SSE | \
-			SXE2_RX_MODE_VEC_AVX2 | SXE2_RX_MODE_VEC_AVX512)
+			SXE2_RX_MODE_VEC_AVX2 | SXE2_RX_MODE_VEC_AVX512 | \
+			SXE2_RX_MODE_VEC_NEON)
 #define SXE2_TX_MODE_VEC_SIMPLE   RTE_BIT32(0)
 #define SXE2_TX_MODE_VEC_OFFLOAD  RTE_BIT32(1)
 #define SXE2_TX_MODE_VEC_SSE      RTE_BIT32(2)
 #define SXE2_TX_MODE_VEC_AVX2     RTE_BIT32(3)
 #define SXE2_TX_MODE_VEC_AVX512   RTE_BIT32(4)
+#define SXE2_TX_MODE_VEC_NEON     RTE_BIT32(5)
 #define SXE2_TX_MODE_SIMPLE_BATCH RTE_BIT32(10)
 #define SXE2_TX_MODE_VEC_SET_MASK	(SXE2_TX_MODE_VEC_SIMPLE | \
 			SXE2_TX_MODE_VEC_OFFLOAD | SXE2_TX_MODE_VEC_SSE | \
-			SXE2_TX_MODE_VEC_AVX2 | SXE2_TX_MODE_VEC_AVX512)
+			SXE2_TX_MODE_VEC_AVX2 | SXE2_TX_MODE_VEC_AVX512 | \
+			SXE2_TX_MODE_VEC_NEON)
 #define SXE2_TX_VEC_NO_SUPPORT_OFFLOAD (		  \
 			RTE_ETH_TX_OFFLOAD_MULTI_SEGS |		  \
 			RTE_ETH_TX_OFFLOAD_QINQ_INSERT |	  \
@@ -76,6 +80,14 @@ uint16_t sxe2_rx_pkts_scattered_vec_avx2(void *rx_queue,
 		struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
 uint16_t sxe2_rx_pkts_scattered_vec_avx2_offload(void *rx_queue,
 		struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
+
+#elif defined(RTE_ARCH_ARM64)
+uint16_t sxe2_rx_pkts_scattered_vec_neon(void *rx_queue, struct rte_mbuf **rx_pkts,
+					 uint16_t nb_pkts);
+uint16_t sxe2_rx_pkts_scattered_vec_neon_offload(void *rx_queue, struct rte_mbuf **rx_pkts,
+						 uint16_t nb_pkts);
+uint16_t sxe2_tx_pkts_vec_neon_simple(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
+uint16_t sxe2_tx_pkts_vec_neon(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
 #endif
 int32_t __rte_cold sxe2_tx_vec_support_check(struct rte_eth_dev *dev, uint32_t *vec_flags);
 int32_t __rte_cold sxe2_tx_queues_vec_prepare(struct rte_eth_dev *dev);
diff --git a/drivers/net/sxe2/sxe2_txrx_vec_common.h b/drivers/net/sxe2/sxe2_txrx_vec_common.h
index 6b1649c390..c093c3c80c 100644
--- a/drivers/net/sxe2/sxe2_txrx_vec_common.h
+++ b/drivers/net/sxe2/sxe2_txrx_vec_common.h
@@ -2,6 +2,7 @@
  * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
  */
 
+
 #ifndef SXE2_TXRX_VEC_COMMON_H
 #define SXE2_TXRX_VEC_COMMON_H
 
diff --git a/drivers/net/sxe2/sxe2_txrx_vec_neon.c b/drivers/net/sxe2/sxe2_txrx_vec_neon.c
new file mode 100644
index 0000000000..26d3bef21a
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_txrx_vec_neon.c
@@ -0,0 +1,721 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifdef RTE_ARCH_ARM64
+#include <arm_neon.h>
+#include <rte_vect.h>
+
+#include "sxe2_txrx_vec_common.h"
+#include "sxe2_txrx_vec.h"
+#include "sxe2_common_log.h"
+
+#define PKTLEN_SHIFT     10
+#define SXE2_UINT16_BIT (CHAR_BIT * sizeof(uint16_t))
+
+static __rte_always_inline void
+sxe2_tx_desc_fill_one_neon(volatile union sxe2_tx_data_desc *desc,
+			struct rte_mbuf *pkt, uint64_t desc_cmd, bool with_offloads)
+{
+	uint64_t desc_qw1;
+	uint32_t desc_offset;
+
+	desc_qw1 = (SXE2_TX_DESC_DTYPE_DATA |
+				((uint64_t)desc_cmd) << SXE2_TX_DATA_DESC_CMD_SHIFT |
+				((uint64_t)pkt->data_len) << SXE2_TX_DATA_DESC_BUF_SZ_SHIFT);
+
+	desc_offset = SXE2_TX_DATA_DESC_MACLEN_VAL(pkt->l2_len);
+	desc_qw1 |= ((uint64_t)desc_offset) << SXE2_TX_DATA_DESC_OFFSET_SHIFT;
+	if (with_offloads)
+		sxe2_tx_desc_fill_offloads(pkt, &desc_qw1);
+
+	uint64x2_t data_desc = { rte_pktmbuf_iova(pkt), desc_qw1 };
+
+	vst1q_u64(RTE_CAST_PTR(uint64_t *, desc), data_desc);
+}
+
+static __rte_always_inline uint16_t
+sxe2_tx_pkts_vec_neon_batch(struct sxe2_tx_queue *txq, struct rte_mbuf **tx_pkts,
+			uint16_t nb_pkts, bool with_offloads)
+{
+	volatile union sxe2_tx_data_desc *desc;
+	struct sxe2_tx_buffer *buffer;
+	uint16_t next_use;
+	uint16_t res_num;
+	uint16_t tx_num;
+	uint16_t i;
+
+	if (txq->desc_free_num < txq->free_thresh)
+		(void)sxe2_tx_bufs_free_vec(txq);
+
+	nb_pkts = RTE_MIN(txq->desc_free_num, nb_pkts);
+	if (unlikely(nb_pkts == 0)) {
+		PMD_LOG_DEBUG(TX, "Tx pkts neon batch: may not enough free desc, "
+				"free_desc=%u, need_tx_pkts=%u",
+				txq->desc_free_num, nb_pkts);
+		goto l_end;
+	}
+	tx_num = nb_pkts;
+
+	next_use = txq->next_use;
+	desc     = &txq->desc_ring[next_use];
+	buffer   = &txq->buffer_ring[next_use];
+
+	txq->desc_free_num -= nb_pkts;
+
+	res_num = txq->ring_depth - txq->next_use;
+
+	if (tx_num >= res_num) {
+		sxe2_tx_pkts_mbuf_fill(buffer, tx_pkts, res_num);
+
+		for (i = 0; i < res_num - 1; ++i, ++tx_pkts, ++desc) {
+			sxe2_tx_desc_fill_one_neon(desc, *tx_pkts,
+					SXE2_TX_DATA_DESC_CMD_EOP, with_offloads);
+		}
+
+		sxe2_tx_desc_fill_one_neon(desc, *tx_pkts++,
+					(SXE2_TX_DATA_DESC_CMD_EOP | SXE2_TX_DATA_DESC_CMD_RS),
+					with_offloads);
+
+		tx_num -= res_num;
+
+		next_use     = 0;
+		txq->next_rs = txq->rs_thresh - 1;
+		desc         = &txq->desc_ring[next_use];
+		buffer       = &txq->buffer_ring[next_use];
+	}
+
+	sxe2_tx_pkts_mbuf_fill(buffer, tx_pkts, tx_num);
+
+	for (i = 0; i < tx_num; ++i, ++tx_pkts, ++desc) {
+		sxe2_tx_desc_fill_one_neon(desc, *tx_pkts,
+				SXE2_TX_DATA_DESC_CMD_EOP, with_offloads);
+	}
+
+	next_use += tx_num;
+	if (next_use > txq->next_rs) {
+		txq->desc_ring[txq->next_rs].read.type_cmd_off_bsz_l2t |=
+			rte_cpu_to_le_64(SXE2_TX_DATA_DESC_CMD_RS_MASK);
+
+		txq->next_rs += txq->rs_thresh;
+	}
+	txq->next_use = next_use;
+
+	SXE2_PCI_REG_WRITE_WC(txq->tdt_reg_addr, txq->next_use);
+
+l_end:
+	return nb_pkts;
+}
+
+static __rte_always_inline uint16_t
+sxe2_tx_pkts_vec_neon_common(struct sxe2_tx_queue *txq, struct rte_mbuf **tx_pkts,
+			uint16_t nb_pkts, bool with_offloads)
+{
+	uint16_t tx_done_num = 0;
+	uint16_t tx_once_num;
+	uint16_t tx_need_num;
+
+	while (nb_pkts) {
+		tx_need_num = RTE_MIN(nb_pkts, txq->rs_thresh);
+		tx_once_num = sxe2_tx_pkts_vec_neon_batch(txq,
+					tx_pkts + tx_done_num,
+					tx_need_num, with_offloads);
+
+		nb_pkts     -= tx_once_num;
+		tx_done_num += tx_once_num;
+
+		if (tx_once_num < tx_need_num)
+			break;
+	}
+
+	PMD_LOG_DEBUG(TX, "Tx pkts neon: port_id=%u, queue_id=%u, "
+			"nb_pkts=%u, tx_done_num=%u with_offloads=%u",
+			txq->port_id, txq->idx_in_func, nb_pkts, tx_done_num, with_offloads);
+
+	return tx_done_num;
+}
+
+uint16_t sxe2_tx_pkts_vec_neon_simple(void *tx_queue,
+			struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
+{
+	return sxe2_tx_pkts_vec_neon_common((struct sxe2_tx_queue *)tx_queue,
+				tx_pkts, nb_pkts, false);
+}
+
+uint16_t sxe2_tx_pkts_vec_neon(void *tx_queue,
+			struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
+{
+	return sxe2_tx_pkts_vec_neon_common((struct sxe2_tx_queue *)tx_queue,
+				tx_pkts, nb_pkts, true);
+}
+
+static __rte_always_inline void
+sxe2_rx_desc_ptype_fill_neon(uint16x8_t staterr, struct rte_mbuf **__rte_restrict rx_pkts,
+		const uint32_t *__rte_restrict ptype_tbl)
+{
+	uint16x8_t ptype_mask = {
+		0, 0x3FFULL,
+		0, 0x3FFULL,
+		0, 0x3FFULL,
+		0, 0x3FFULL,
+	};
+	uint16x8_t ptype_all;
+
+	ptype_all = vandq_u16(staterr, ptype_mask);
+
+	rx_pkts[3]->packet_type = ptype_tbl[vgetq_lane_u16(ptype_all, 3)];
+	rx_pkts[2]->packet_type = ptype_tbl[vgetq_lane_u16(ptype_all, 7)];
+	rx_pkts[1]->packet_type = ptype_tbl[vgetq_lane_u16(ptype_all, 1)];
+	rx_pkts[0]->packet_type = ptype_tbl[vgetq_lane_u16(ptype_all, 5)];
+}
+
+static __rte_always_inline uint32x4_t
+sxe2_rx_desc_fnav_flags_neon(uint64x2_t descs_arr[4])
+{
+	uint32x4_t descs_tmp1, descs_tmp2;
+	uint32x4_t descs_fnav_vld;
+	uint32x4_t v_zeros, v_ffff, v_u32_one;
+	uint32x4_t m_flags;
+
+	const uint32x4_t fdir_flags = vdupq_n_u32(RTE_MBUF_F_RX_FDIR |
+						  RTE_MBUF_F_RX_FDIR_ID);
+
+	uint32x4_t d0 = vreinterpretq_u32_u64(descs_arr[0]);
+	uint32x4_t d1 = vreinterpretq_u32_u64(descs_arr[1]);
+	uint32x4_t d2 = vreinterpretq_u32_u64(descs_arr[2]);
+	uint32x4_t d3 = vreinterpretq_u32_u64(descs_arr[3]);
+
+	descs_tmp1 = vzip1q_u32(d1, d0);
+	descs_tmp2 = vzip1q_u32(d3, d2);
+
+	uint64x2_t a = vreinterpretq_u64_u32(descs_tmp1);
+	uint64x2_t b = vreinterpretq_u64_u32(descs_tmp2);
+
+	descs_fnav_vld = vreinterpretq_u32_u64(vcombine_u64(vget_low_u64(a), vget_low_u64(b)));
+
+	descs_fnav_vld = vshlq_n_u32(descs_fnav_vld, 26);
+	descs_fnav_vld = vshrq_n_u32(descs_fnav_vld, 31);
+
+	v_zeros = vdupq_n_u32(0);
+	v_ffff = vceqq_u32(v_zeros, v_zeros);
+	v_u32_one = vshrq_n_u32(v_ffff, 31);
+
+	m_flags = vceqq_u32(descs_fnav_vld, v_u32_one);
+
+	m_flags = vandq_u32(m_flags, fdir_flags);
+	return m_flags;
+}
+
+static __rte_always_inline void
+sxe2_rx_desc_offloads_para_fill_neon(struct sxe2_rx_queue *rxq,
+			volatile union sxe2_rx_desc *desc,
+			uint64x2_t descs[4], struct rte_mbuf **rx_pkts)
+{
+	uint32x4_t desc_lo, desc_hi, flags, tmp_flags;
+	const uint64x2_t mbuf_init = {rxq->mbuf_init_value, 0};
+	uint64x2_t rearm0, rearm1, rearm2, rearm3;
+
+	const uint32x4_t desc_msk = {
+		0x00001C04, 0x00001C04, 0x00001C04, 0x00001C04};
+
+	const uint32x4_t rss_msk = {
+		0x20000000, 0x20000000, 0x20000000, 0x20000000};
+
+	const uint32x4_t vlan_msk = {
+		RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED,
+		RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED,
+		RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED,
+		RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED
+	};
+	const uint8x16_t vlan_flags = {
+		0, 0, 0, 0,
+		RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED, 0, 0, 0,
+		0, 0, 0, 0,
+		0, 0, 0, 0
+	};
+
+	const uint8x16_t rss_flags = {
+		0, 0, 0, 0,
+		RTE_MBUF_F_RX_RSS_HASH, 0, 0, 0,
+		0, 0, 0, 0,
+		0, 0, 0, 0
+	};
+
+	const uint32x4_t cksum_mask = {
+		RTE_MBUF_F_RX_IP_CKSUM_MASK | RTE_MBUF_F_RX_L4_CKSUM_MASK |
+		RTE_MBUF_F_RX_OUTER_L4_CKSUM_MASK | RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD,
+		RTE_MBUF_F_RX_IP_CKSUM_MASK | RTE_MBUF_F_RX_L4_CKSUM_MASK |
+		RTE_MBUF_F_RX_OUTER_L4_CKSUM_MASK | RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD,
+		RTE_MBUF_F_RX_IP_CKSUM_MASK | RTE_MBUF_F_RX_L4_CKSUM_MASK |
+		RTE_MBUF_F_RX_OUTER_L4_CKSUM_MASK | RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD,
+		RTE_MBUF_F_RX_IP_CKSUM_MASK | RTE_MBUF_F_RX_L4_CKSUM_MASK |
+		RTE_MBUF_F_RX_OUTER_L4_CKSUM_MASK | RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD,
+	};
+
+	const uint8x16_t cksum_flags = {
+		((RTE_MBUF_F_RX_L4_CKSUM_GOOD | RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+		((RTE_MBUF_F_RX_L4_CKSUM_GOOD | RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+		((RTE_MBUF_F_RX_L4_CKSUM_BAD | RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+		((RTE_MBUF_F_RX_L4_CKSUM_BAD | RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+		((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+			RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+		((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+			RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+		((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_BAD |
+			RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+		((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_BAD |
+			RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+		0, 0, 0, 0, 0, 0, 0, 0
+	};
+
+	{
+		uint32x4_t d0 = vreinterpretq_u32_u64(descs[0]);
+		uint32x4_t d1 = vreinterpretq_u32_u64(descs[1]);
+		uint32x4_t d2 = vreinterpretq_u32_u64(descs[2]);
+		uint32x4_t d3 = vreinterpretq_u32_u64(descs[3]);
+		uint64x2_t f64, t64;
+
+		flags = vzip2q_u32(d1, d0);
+		tmp_flags = vzip2q_u32(d3, d2);
+		f64 = vreinterpretq_u64_u32(flags);
+		t64 = vreinterpretq_u64_u32(tmp_flags);
+		desc_lo = vreinterpretq_u32_u64(vcombine_u64(vget_low_u64(f64),
+							     vget_low_u64(t64)));
+		desc_hi = vreinterpretq_u32_u64(vcombine_u64(vget_high_u64(f64),
+							     vget_high_u64(t64)));
+	}
+
+	desc_lo = vandq_u32(desc_lo, desc_msk);
+	desc_hi = vandq_u32(desc_hi, rss_msk);
+
+	tmp_flags = vreinterpretq_u32_u8(vqtbl1q_u8(vlan_flags,
+						vreinterpretq_u8_u32(desc_lo)));
+	flags = vandq_u32(tmp_flags, vlan_msk);
+
+	desc_lo = vshrq_n_u32(desc_lo, 10);
+	tmp_flags = vreinterpretq_u32_u8(vqtbl1q_u8(cksum_flags,
+					 vreinterpretq_u8_u32(desc_lo)));
+	tmp_flags = vshlq_n_u32(tmp_flags, 1);
+	tmp_flags = vandq_u32(tmp_flags, cksum_mask);
+	flags = vorrq_u32(flags, tmp_flags);
+
+	desc_hi = vshrq_n_u32(desc_hi, 27);
+	tmp_flags = vreinterpretq_u32_u8(vqtbl1q_u8(rss_flags,
+					 vreinterpretq_u8_u32(desc_hi)));
+	flags = vorrq_u32(flags, tmp_flags);
+
+#ifndef RTE_LIBRTE_SXE2_16BYTE_RX_DESC
+	if (rxq->fnav_enable) {
+		uint32x4_t tmp_fnav_flags = sxe2_rx_desc_fnav_flags_neon(descs);
+		flags = vorrq_u32(flags, tmp_fnav_flags);
+
+		rx_pkts[0]->hash.fdir.hi = desc[0].wb.fd_filter_id;
+		rx_pkts[1]->hash.fdir.hi = desc[1].wb.fd_filter_id;
+		rx_pkts[2]->hash.fdir.hi = desc[2].wb.fd_filter_id;
+		rx_pkts[3]->hash.fdir.hi = desc[3].wb.fd_filter_id;
+	}
+#endif
+
+	rearm0 = vsetq_lane_u64(vgetq_lane_u32(flags, 0), mbuf_init, 1);
+	rearm1 = vsetq_lane_u64(vgetq_lane_u32(flags, 1), mbuf_init, 1);
+	rearm2 = vsetq_lane_u64(vgetq_lane_u32(flags, 2), mbuf_init, 1);
+	rearm3 = vsetq_lane_u64(vgetq_lane_u32(flags, 3), mbuf_init, 1);
+
+	vst1q_u64((uint64_t *)&rx_pkts[0]->rearm_data, rearm0);
+	vst1q_u64((uint64_t *)&rx_pkts[1]->rearm_data, rearm1);
+	vst1q_u64((uint64_t *)&rx_pkts[2]->rearm_data, rearm2);
+	vst1q_u64((uint64_t *)&rx_pkts[3]->rearm_data, rearm3);
+}
+
+static inline void sxe2_rx_queue_rearm_neon(struct sxe2_rx_queue *rxq)
+{
+	volatile union sxe2_rx_desc *desc;
+	struct rte_mbuf **buffer;
+	struct rte_mbuf *mbuf0, *mbuf1;
+	uint64x2_t dma_addr0, dma_addr1;
+	uint64x2_t zero = vdupq_n_u64(0);
+	uint64x2_t virt_addr0, virt_addr1;
+	uint64x2_t hdr_room = vdupq_n_u64(RTE_PKTMBUF_HEADROOM);
+	int32_t ret;
+	uint16_t i;
+	uint16_t new_tail;
+
+	buffer = &rxq->buffer_ring[rxq->realloc_start];
+	desc = &rxq->desc_ring[rxq->realloc_start];
+
+	ret = rte_mempool_get_bulk(rxq->mb_pool, (void *)buffer,
+			SXE2_RX_REARM_THRESH_VEC);
+	if (ret != 0) {
+		PMD_LOG_INFO(RX, "Rx mbuf vec alloc failed port_id=%u "
+				"queue_id=%u", rxq->port_id, rxq->idx_in_func);
+
+		if ((rxq->realloc_num + SXE2_RX_REARM_THRESH_VEC) >= rxq->ring_depth) {
+			for (i = 0; i < SXE2_RX_NUM_PER_LOOP_NEON; ++i) {
+				buffer[i] = &rxq->fake_mbuf;
+				vst1q_u64(RTE_CAST_PTR(uint64_t *, &desc[i].read), zero);
+			}
+		}
+
+		rxq->vsi->adapter->dev_info.dev_data->rx_mbuf_alloc_failed +=
+				SXE2_RX_REARM_THRESH_VEC;
+		goto l_end;
+	}
+
+	for (i = 0; i < SXE2_RX_REARM_THRESH_VEC; i += 2, buffer += 2) {
+		mbuf0 = buffer[0];
+		mbuf1 = buffer[1];
+#if RTE_IOVA_IN_MBUF
+		RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, buf_iova) !=
+				 offsetof(struct rte_mbuf, buf_addr) + 8);
+#endif
+		virt_addr0 = vld1q_u64((uint64_t *)&mbuf0->buf_addr);
+		virt_addr1 = vld1q_u64((uint64_t *)&mbuf1->buf_addr);
+
+#if RTE_IOVA_IN_MBUF
+		dma_addr0 = vdupq_n_u64((uint64_t)vget_high_u64(virt_addr0));
+		dma_addr1 = vdupq_n_u64((uint64_t)vget_high_u64(virt_addr1));
+#else
+		dma_addr0 = vdupq_n_u64((uint64_t)vget_low_u64(virt_addr0));
+		dma_addr1 = vdupq_n_u64((uint64_t)vget_low_u64(virt_addr1));
+#endif
+		dma_addr0 = vaddq_u64(dma_addr0, hdr_room);
+		dma_addr1 = vaddq_u64(dma_addr1, hdr_room);
+
+		vst1q_u64(RTE_CAST_PTR(uint64_t *, &desc++->read), dma_addr0);
+		vst1q_u64(RTE_CAST_PTR(uint64_t *, &desc++->read), dma_addr1);
+	}
+
+	rxq->realloc_start += SXE2_RX_REARM_THRESH_VEC;
+	if (rxq->realloc_start >= rxq->ring_depth)
+		rxq->realloc_start = 0;
+	rxq->realloc_num -= SXE2_RX_REARM_THRESH_VEC;
+
+	new_tail = (rxq->realloc_start == 0) ?
+		(rxq->ring_depth - 1) : (rxq->realloc_start - 1);
+
+	SXE2_PCI_REG_WRITE_WC(rxq->rdt_reg_addr, new_tail);
+
+l_end:
+	return;
+}
+
+static __rte_always_inline uint16_t
+sxe2_rx_pkts_common_vec_neon(struct sxe2_rx_queue *rxq, struct rte_mbuf **rx_pkts,
+		uint16_t nb_pkts, uint8_t *split_rxe_flags, uint8_t *umbcast_flags,
+		bool do_offload)
+{
+	volatile union sxe2_rx_desc *desc;
+	struct rte_mbuf **buffer;
+	uint32_t i;
+	uint16_t done_num = 0;
+	const uint32_t *ptype_tbl = rxq->vsi->adapter->ptype_tbl;
+
+	uint8x16_t rvp_shuf_mask = {
+		0xFF, 0xFF, 0xFF, 0xFF,
+		12, 13, 0xFF, 0xFF,
+		12, 13,
+		2, 3,
+		4, 5, 6, 7
+	};
+
+	uint16x8_t crc_adjust = {
+		0, 0,
+		rxq->crc_len,
+		0, rxq->crc_len,
+		0, 0, 0
+	};
+
+	desc = &rxq->desc_ring[rxq->processing_idx];
+	rte_prefetch0(desc);
+
+	nb_pkts = RTE_ALIGN_FLOOR(nb_pkts, SXE2_RX_NUM_PER_LOOP_NEON);
+
+	if (rxq->realloc_num > SXE2_RX_REARM_THRESH_VEC)
+		sxe2_rx_queue_rearm_neon(rxq);
+
+	if ((rte_le_to_cpu_64(desc->wb.status_err_ptype_len) &
+			SXE2_RX_DESC_STATUS_DD_MASK) == 0) {
+		goto l_end;
+	}
+
+	buffer = &rxq->buffer_ring[rxq->processing_idx];
+	for (i = 0; i < nb_pkts; i += SXE2_RX_NUM_PER_LOOP_NEON,
+				desc += SXE2_RX_NUM_PER_LOOP_NEON) {
+		uint64x2_t descs[SXE2_RX_NUM_PER_LOOP_NEON];
+		uint8x16_t pkt_mb1, pkt_mb2, pkt_mb3, pkt_mb4;
+		uint64x2_t mbp1, mbp2;
+		uint16x8_t staterr;
+		uint16x8_t tmp;
+		uint16_t bit_num;
+
+		descs[3] = vld1q_u64(RTE_CAST_PTR(uint64_t *, desc + 3));
+		rte_atomic_thread_fence(rte_memory_order_acquire);
+		descs[2] = vld1q_u64(RTE_CAST_PTR(uint64_t *, desc + 2));
+		rte_atomic_thread_fence(rte_memory_order_acquire);
+		descs[1] = vld1q_u64(RTE_CAST_PTR(uint64_t *, desc + 1));
+		rte_atomic_thread_fence(rte_memory_order_acquire);
+		descs[0] = vld1q_u64(RTE_CAST_PTR(uint64_t *, desc));
+
+		rte_atomic_thread_fence(rte_memory_order_acquire);
+
+		descs[3] = vld1q_lane_u64(RTE_CAST_PTR(uint64_t *, desc + 3), descs[3], 0);
+		descs[2] = vld1q_lane_u64(RTE_CAST_PTR(uint64_t *, desc + 2), descs[2], 0);
+		descs[1] = vld1q_lane_u64(RTE_CAST_PTR(uint64_t *, desc + 1), descs[1], 0);
+		descs[0] = vld1q_lane_u64(RTE_CAST_PTR(uint64_t *, desc), descs[0], 0);
+
+		mbp1 = vld1q_u64((uint64_t *)&buffer[i]);
+		mbp2 = vld1q_u64((uint64_t *)&buffer[i + 2]);
+
+		vst1q_u64((uint64_t *)&rx_pkts[i], mbp1);
+		vst1q_u64((uint64_t *)&rx_pkts[i + 2], mbp2);
+
+		if (split_rxe_flags) {
+			rte_mbuf_prefetch_part2(rx_pkts[i]);
+			rte_mbuf_prefetch_part2(rx_pkts[i + 1]);
+			rte_mbuf_prefetch_part2(rx_pkts[i + 2]);
+			rte_mbuf_prefetch_part2(rx_pkts[i + 3]);
+		}
+
+		pkt_mb4 = vqtbl1q_u8(vreinterpretq_u8_u64(descs[3]), rvp_shuf_mask);
+		pkt_mb3 = vqtbl1q_u8(vreinterpretq_u8_u64(descs[2]), rvp_shuf_mask);
+		pkt_mb2 = vqtbl1q_u8(vreinterpretq_u8_u64(descs[1]), rvp_shuf_mask);
+		pkt_mb1 = vqtbl1q_u8(vreinterpretq_u8_u64(descs[0]), rvp_shuf_mask);
+
+		if (do_offload) {
+			sxe2_rx_desc_offloads_para_fill_neon(rxq, desc, descs, &rx_pkts[i]);
+		} else {
+			const uint64x2_t mbuf_init = {
+				rxq->mbuf_init_value,
+				0,
+			};
+
+			vst1q_u64((uint64_t *)&rx_pkts[i]->rearm_data, mbuf_init);
+			vst1q_u64((uint64_t *)&rx_pkts[i + 1]->rearm_data, mbuf_init);
+			vst1q_u64((uint64_t *)&rx_pkts[i + 2]->rearm_data, mbuf_init);
+			vst1q_u64((uint64_t *)&rx_pkts[i + 3]->rearm_data, mbuf_init);
+		}
+
+		tmp = vsubq_u16(vreinterpretq_u16_u8(pkt_mb4), crc_adjust);
+		pkt_mb4 = vreinterpretq_u8_u16(tmp);
+		tmp = vsubq_u16(vreinterpretq_u16_u8(pkt_mb3), crc_adjust);
+		pkt_mb3 = vreinterpretq_u8_u16(tmp);
+		tmp = vsubq_u16(vreinterpretq_u16_u8(pkt_mb2), crc_adjust);
+		pkt_mb2 = vreinterpretq_u8_u16(tmp);
+		tmp = vsubq_u16(vreinterpretq_u16_u8(pkt_mb1), crc_adjust);
+		pkt_mb1 = vreinterpretq_u8_u16(tmp);
+
+		vst1q_u8((void *)&rx_pkts[i + 3]->rx_descriptor_fields1,
+				pkt_mb4);
+		vst1q_u8((void *)&rx_pkts[i + 2]->rx_descriptor_fields1,
+				pkt_mb3);
+		vst1q_u8((void *)&rx_pkts[i + 1]->rx_descriptor_fields1,
+				pkt_mb2);
+		vst1q_u8((void *)&rx_pkts[i]->rx_descriptor_fields1,
+				pkt_mb1);
+
+		if (likely(i + SXE2_RX_NUM_PER_LOOP_NEON < nb_pkts))
+			rte_prefetch_non_temporal(desc + SXE2_RX_NUM_PER_LOOP_NEON);
+
+		{
+			uint32x4_t d0 = vreinterpretq_u32_u64(descs[0]);
+			uint32x4_t d1 = vreinterpretq_u32_u64(descs[1]);
+			uint32x4_t d2 = vreinterpretq_u32_u64(descs[2]);
+			uint32x4_t d3 = vreinterpretq_u32_u64(descs[3]);
+			uint32x4_t sterr_tmp1 = vzip2q_u32(d1, d0);
+			uint32x4_t sterr_tmp2 = vzip2q_u32(d3, d2);
+			uint32x4_t sterr_u32 = vzip1q_u32(sterr_tmp1, sterr_tmp2);
+
+			staterr = vreinterpretq_u16_u32(sterr_u32);
+		}
+
+		sxe2_rx_desc_ptype_fill_neon(staterr, &rx_pkts[i], ptype_tbl);
+
+		if (umbcast_flags != NULL) {
+			uint32x4_t umbcast_mask = {
+				SXE2_RX_DESC_STATUS_UMBCAST_MASK, SXE2_RX_DESC_STATUS_UMBCAST_MASK,
+				SXE2_RX_DESC_STATUS_UMBCAST_MASK, SXE2_RX_DESC_STATUS_UMBCAST_MASK,
+			};
+
+			uint8x16_t umbcast_shuf_mask = {
+				0x0B, 0x03, 0x0F, 0x07,
+				0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF,
+				0xFF, 0xFF, 0xFF, 0xFF,
+			};
+			uint8x16_t umbcast_bits =
+				vreinterpretq_u8_u32(vandq_u32(vreinterpretq_u32_u16(staterr),
+							       umbcast_mask));
+
+			umbcast_bits = vqtbl1q_u8(umbcast_bits, umbcast_shuf_mask);
+			vst1q_lane_u32((uint32_t *)umbcast_flags,
+					vreinterpretq_u32_u8(umbcast_bits), 0);
+			umbcast_flags += SXE2_RX_NUM_PER_LOOP_NEON;
+		}
+
+		if (split_rxe_flags) {
+			uint8x16_t eop_shuf_mask = {
+					0x08, 0x00, 0x0C, 0x04,
+					0xFF, 0xFF, 0xFF, 0xFF,
+					0xFF, 0xFF, 0xFF, 0xFF,
+					0xFF, 0xFF, 0xFF, 0xFF};
+			uint8x16_t eop_bits;
+			uint32x4_t rxe_mask = {
+				0x2080, 0x2080, 0x2080, 0x2080
+			};
+			uint32x4_t rxe_bits;
+			uint32x4_t eop_mask;
+
+			eop_mask = vshlq_n_u32(vdupq_n_u32(1), SXE2_RX_DESC_STATUS_EOP_SHIFT);
+			eop_bits = vandq_u8(vmvnq_u8(vreinterpretq_u8_u16(staterr)),
+					vreinterpretq_u8_u32(eop_mask));
+
+			rxe_bits = vandq_u32(vreinterpretq_u32_u16(staterr), rxe_mask);
+			rxe_bits = vshrq_n_u32(rxe_bits, 7);
+
+			eop_bits = vorrq_u8(eop_bits, vreinterpretq_u8_u32(rxe_bits));
+
+			eop_bits = vqtbl1q_u8(eop_bits, eop_shuf_mask);
+
+			vst1q_lane_u32((uint32_t *)split_rxe_flags,
+				       vreinterpretq_u32_u8(eop_bits), 0);
+			split_rxe_flags += SXE2_RX_NUM_PER_LOOP_NEON;
+
+#ifdef RTE_IOVA_IN_MBUF
+			rx_pkts[i]->next = NULL;
+			rx_pkts[i + 1]->next = NULL;
+			rx_pkts[i + 2]->next = NULL;
+			rx_pkts[i + 3]->next = NULL;
+#endif
+		}
+
+		{
+			uint32x4_t dd_mask = vdupq_n_u32(1);
+			uint32x4_t sterr_dd = vandq_u32(vreinterpretq_u32_u16(staterr), dd_mask);
+			uint16x4_t packed_lo = vmovn_u32(sterr_dd);
+			uint64_t dd64 = vget_lane_u64(vreinterpret_u64_u16(packed_lo), 0);
+
+			bit_num = (uint16_t)rte_popcount64(dd64);
+		}
+		done_num += bit_num;
+		if (likely(bit_num != SXE2_RX_NUM_PER_LOOP_NEON))
+			break;
+	}
+
+	rxq->processing_idx += done_num;
+	rxq->processing_idx &= (rxq->ring_depth - 1);
+	rxq->realloc_num    += done_num;
+
+l_end:
+	return done_num;
+}
+
+static __rte_always_inline uint16_t
+sxe2_rx_pkts_scattered_batch_vec_neon(struct sxe2_rx_queue *rxq,
+		struct rte_mbuf **rx_pkts, uint16_t nb_pkts, bool do_offload)
+{
+	const uint64_t *split_flags64;
+	uint8_t split_rxe_flags[SXE2_RX_PKTS_BURST_BATCH_NUM_VEC] = {0};
+	uint8_t umbcast_flags[SXE2_RX_PKTS_BURST_BATCH_NUM_VEC] = {0};
+	uint16_t rx_done_num;
+	uint16_t rx_pkt_done_num;
+
+	rx_pkt_done_num = 0;
+
+	if (rxq->vsi->adapter->devargs.sw_stats_en) {
+		rx_done_num = sxe2_rx_pkts_common_vec_neon((struct sxe2_rx_queue *)rxq,
+					rx_pkts, nb_pkts, split_rxe_flags, umbcast_flags,
+					do_offload);
+	} else {
+		rx_done_num = sxe2_rx_pkts_common_vec_neon((struct sxe2_rx_queue *)rxq,
+					rx_pkts, nb_pkts, split_rxe_flags, NULL,
+					do_offload);
+	}
+
+	if (rx_done_num == 0)
+		goto l_end;
+
+	if (!rxq->vsi->adapter->devargs.sw_stats_en) {
+		split_flags64 = (uint64_t *)split_rxe_flags;
+
+		if (rxq->pkt_first_seg == NULL &&
+				split_flags64[0] == 0 && split_flags64[1] == 0 &&
+				split_flags64[2] == 0 && split_flags64[3] == 0) {
+			rx_pkt_done_num = rx_done_num;
+			goto l_end;
+		}
+
+		if (rxq->pkt_first_seg == NULL) {
+			while (rx_pkt_done_num < rx_done_num &&
+					split_rxe_flags[rx_pkt_done_num] == 0) {
+				rx_pkt_done_num++;
+			}
+
+			if (rx_pkt_done_num == rx_done_num)
+				goto l_end;
+
+			rxq->pkt_first_seg = rx_pkts[rx_pkt_done_num];
+		}
+	}
+
+	rx_pkt_done_num += sxe2_rx_pkts_refactor(rxq, &rx_pkts[rx_pkt_done_num],
+			rx_done_num - rx_pkt_done_num, &split_rxe_flags[rx_pkt_done_num],
+			&umbcast_flags[rx_pkt_done_num]);
+
+l_end:
+	return rx_pkt_done_num;
+}
+
+uint16_t sxe2_rx_pkts_scattered_vec_neon_offload(void *rx_queue,
+			struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
+{
+	uint16_t done_num = 0;
+	uint16_t once_num;
+
+	while (nb_pkts > SXE2_RX_PKTS_BURST_BATCH_NUM_VEC) {
+		once_num = sxe2_rx_pkts_scattered_batch_vec_neon((struct sxe2_rx_queue *)rx_queue,
+								 rx_pkts + done_num,
+								 SXE2_RX_PKTS_BURST_BATCH_NUM_VEC,
+								 true);
+
+		done_num += once_num;
+		nb_pkts  -= once_num;
+
+		if (once_num < SXE2_RX_PKTS_BURST_BATCH_NUM_VEC)
+			goto l_end;
+	}
+
+	done_num += sxe2_rx_pkts_scattered_batch_vec_neon((struct sxe2_rx_queue *)rx_queue,
+							  rx_pkts + done_num,
+							  nb_pkts,
+							  true);
+l_end:
+	return done_num;
+}
+
+uint16_t sxe2_rx_pkts_scattered_vec_neon(void *rx_queue,
+			struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
+{
+	uint16_t done_num = 0;
+	uint16_t once_num;
+
+	while (nb_pkts > SXE2_RX_PKTS_BURST_BATCH_NUM_VEC) {
+		once_num = sxe2_rx_pkts_scattered_batch_vec_neon((struct sxe2_rx_queue *)rx_queue,
+								 rx_pkts + done_num,
+								 SXE2_RX_PKTS_BURST_BATCH_NUM_VEC,
+								 false);
+
+		done_num += once_num;
+		nb_pkts  -= once_num;
+
+		if (once_num < SXE2_RX_PKTS_BURST_BATCH_NUM_VEC)
+			goto l_end;
+	}
+
+	done_num += sxe2_rx_pkts_scattered_batch_vec_neon((struct sxe2_rx_queue *)rx_queue,
+							  rx_pkts + done_num,
+							  nb_pkts,
+							  false);
+l_end:
+	return done_num;
+}
+#endif
-- 
2.52.0


^ permalink raw reply related

* [PATCH v10 14/20] net/sxe2: implement get monitor address
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260606010726.2256170-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

This patch implements the 'get_monitor_addr' ethdev ops in the sxe2
PMD. This interface allows the Ethernet device to provide the
address of the next expected Rx descriptor to the power management
library.

The implementation calculates the address of the next Rx descriptor
based on the receive queue's current hardware ring position and
descriptor size. Applications can then use this address with
rte_power_monitor() to put the CPU into a low-power state until
new packets are written to the descriptor by the hardware.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/sxe2_ethdev.c |  2 ++
 drivers/net/sxe2/sxe2_rx.c     | 21 +++++++++++++++++++++
 drivers/net/sxe2/sxe2_rx.h     |  2 ++
 3 files changed, 25 insertions(+)

diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index 4842a8cdac..d1bc5b1500 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -184,6 +184,8 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.queue_stats_mapping_set    = sxe2_queue_stats_mapping_set,
 
 	.fw_version_get             = sxe2_fw_version_string_get,
+
+	.get_monitor_addr           = sxe2_get_monitor_addr,
 };
 
 static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
diff --git a/drivers/net/sxe2/sxe2_rx.c b/drivers/net/sxe2/sxe2_rx.c
index 007192c7d8..79e65cfbf1 100644
--- a/drivers/net/sxe2/sxe2_rx.c
+++ b/drivers/net/sxe2/sxe2_rx.c
@@ -557,3 +557,24 @@ void __rte_cold sxe2_rxqs_all_stop(struct rte_eth_dev *dev)
 		}
 	}
 }
+
+static int32_t sxe2_monitor_callback(const uint64_t value,
+				 const uint64_t arg[RTE_POWER_MONITOR_OPAQUE_SZ] __rte_unused)
+{
+	const uint64_t dd_state = rte_cpu_to_le_64(SXE2_RX_DESC_STATUS_DD_MASK);
+	return (value & dd_state) == dd_state ? -1 : 0;
+}
+
+int32_t sxe2_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc)
+{
+	volatile union sxe2_rx_desc *rxdp;
+	struct sxe2_rx_queue *rxq = (struct sxe2_rx_queue *)rx_queue;
+
+	rxdp = &rxq->desc_ring[rxq->processing_idx];
+
+	pmc->addr = &rxdp->wb.status_err_ptype_len;
+	pmc->fn   = sxe2_monitor_callback;
+	pmc->size = sizeof(uint16_t);
+
+	return 0;
+}
diff --git a/drivers/net/sxe2/sxe2_rx.h b/drivers/net/sxe2/sxe2_rx.h
index 295d9005e0..c2582bc571 100644
--- a/drivers/net/sxe2/sxe2_rx.h
+++ b/drivers/net/sxe2/sxe2_rx.h
@@ -29,4 +29,6 @@ int32_t __rte_cold sxe2_rxqs_all_start(struct rte_eth_dev *dev);
 
 void __rte_cold sxe2_rxqs_all_stop(struct rte_eth_dev *dev);
 
+int32_t sxe2_get_monitor_addr(void *rx_queue, struct rte_power_monitor_cond *pmc);
+
 #endif /* SXE2_RX_H */
-- 
2.52.0


^ permalink raw reply related

* [PATCH v10 12/20] net/sxe2: add support for custom UDP tunnel ports
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260606010726.2256170-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

This patch enables the configuration of custom UDP port numbers for
tunneling protocols in the SXE2 PMD.

The change includes:
- Adding a new entry in the tunnel port lookup table.
- Updating the hardware profile to recognize
  the custom UDP port as a tunnel type.
- Enabling inner header parsing for packets arriving on these ports.

This allows the Switch module to correctly apply recipes based on
inner packet fields (e.g., inner MAC or IP).

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/sxe2_cmd_chnl.c           |  96 ++++++++++
 drivers/net/sxe2/sxe2_cmd_chnl.h           |  17 ++
 drivers/net/sxe2/sxe2_drv_cmd.h            |  16 ++
 drivers/net/sxe2/sxe2_ethdev.c             | 206 ++++++++++++++++++++-
 drivers/net/sxe2/sxe2_ethdev.h             |  12 ++
 drivers/net/sxe2/sxe2_flow.c               |  54 ++++++
 drivers/net/sxe2/sxe2_flow.h               |   3 +-
 drivers/net/sxe2/sxe2_flow_define.h        |   1 +
 drivers/net/sxe2/sxe2_flow_parse_pattern.c | 113 +++++++++++
 drivers/net/sxe2/sxe2_flow_parse_pattern.h |   6 +
 drivers/net/sxe2/sxe2_txrx_poll.c          |  46 ++++-
 11 files changed, 566 insertions(+), 4 deletions(-)

diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.c b/drivers/net/sxe2/sxe2_cmd_chnl.c
index 6e2dd139a5..926eaee062 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.c
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.c
@@ -1455,6 +1455,102 @@ int32_t sxe2_drv_ipsec_txsa_delete(struct sxe2_adapter *adapter,
 	return ret;
 }
 
+int32_t sxe2_drv_udp_tunnel_add(struct sxe2_adapter *adapter,
+			    enum sxe2_udp_tunnel_protocol tunnel_proto,
+			    uint16_t udp_port)
+{
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_udp_tunnel_req req = {};
+	struct sxe2_drv_cmd_params cmd = {};
+	int32_t ret = -1;
+
+	req.type = tunnel_proto;
+	req.port = udp_port;
+	sxe2_drv_cmd_params_fill(adapter, &cmd, SXE2_DRV_CMD_UDPTUNNEL_ADD,
+				 &req, sizeof(req),
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &cmd);
+	if (ret)
+		PMD_LOG_ERR(DRV, "Failed to add udp proto %d port %d, ret=%d",
+				tunnel_proto, udp_port, ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_udp_tunnel_del(struct sxe2_adapter *adapter,
+			    enum sxe2_udp_tunnel_protocol tunnel_proto,
+			    uint16_t udp_port)
+{
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_udp_tunnel_req req = {};
+	struct sxe2_drv_cmd_params cmd = {};
+	int32_t ret = -1;
+
+	req.type = tunnel_proto;
+	req.port = udp_port;
+	sxe2_drv_cmd_params_fill(adapter, &cmd, SXE2_DRV_CMD_UDPTUNNEL_DEL,
+				 &req, sizeof(req),
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &cmd);
+	if (ret)
+		PMD_LOG_ERR(DRV, "Failed to del udp proto %d port %d, ret=%d",
+				tunnel_proto, udp_port, ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_get_udp_tunnel_port(struct sxe2_adapter *adapter,
+				 enum sxe2_flow_udp_tunnel_protocol proto,
+				 uint16_t *port)
+{
+	int32_t ret = 0;
+	static const uint16_t flow_proto_to_udp_tunnel_proto[SXE2_FLOW_UDP_TUNNEL_MAX] = {
+		[SXE2_FLOW_UDP_TUNNEL_PROTOCOL_VXLAN] = SXE2_UDP_TUNNEL_PROTOCOL_VXLAN,
+		[SXE2_FLOW_UDP_TUNNEL_PROTOCOL_VXLAN_GPE] = SXE2_UDP_TUNNEL_PROTOCOL_VXLAN_GPE,
+		[SXE2_FLOW_UDP_TUNNEL_PROTOCOL_GENEVE] = SXE2_UDP_TUNNEL_PROTOCOL_GENEVE,
+		[SXE2_FLOW_UDP_TUNNEL_PROTOCOL_GTP_U] = SXE2_UDP_TUNNEL_PROTOCOL_GTP_U,
+		[SXE2_FLOW_UDP_TUNNEL_PROTOCOL_NVGRE] = SXE2_UDP_TUNNEL_PROTOCOL_NVGRE,
+	};
+	struct sxe2_udp_tunnel_cfg tunnel_config = {};
+
+	tunnel_config.protocol = flow_proto_to_udp_tunnel_proto[proto];
+	ret = sxe2_drv_udp_tunnel_get(adapter, &tunnel_config);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to get udp tunnel port, ret=%d", ret);
+		goto l_end;
+	}
+
+	*port = tunnel_config.fw_port;
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_udp_tunnel_get(struct sxe2_adapter *adapter,
+			    struct sxe2_udp_tunnel_cfg *tunnel_config)
+{
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_udp_tunnel_req req = {};
+	struct sxe2_drv_udp_tunnel_resp resp = {};
+	struct sxe2_drv_cmd_params cmd = {};
+	int32_t ret = -1;
+
+	req.type = tunnel_config->protocol;
+	sxe2_drv_cmd_params_fill(adapter, &cmd, SXE2_DRV_CMD_UDPTUNNEL_GET,
+				 &req, sizeof(req),
+				 &resp, sizeof(resp));
+	ret = sxe2_drv_cmd_exec(cdev, &cmd);
+	if (ret)
+		PMD_LOG_ERR(DRV, "Failed to get udp proto %d port, ret=%d", req.type, ret);
+
+	tunnel_config->fw_port   = resp.port;
+	tunnel_config->fw_status = resp.enable;
+	tunnel_config->fw_dst_en = resp.dst;
+	tunnel_config->fw_src_en = resp.src;
+	tunnel_config->fw_used   = resp.fw_used;
+
+	return ret;
+}
+
 int32_t sxe2_drv_queue_info_get_update(struct sxe2_adapter *adapter, struct eth_queue_stats *qstats)
 {
 	struct sxe2_drv_cmd_params param = {0};
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.h b/drivers/net/sxe2/sxe2_cmd_chnl.h
index 52cd9922ad..97007c7cfa 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.h
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.h
@@ -67,6 +67,23 @@ int32_t sxe2_drv_ipsec_txsa_delete(struct sxe2_adapter *adapter,
 
 int32_t sxe2_drv_promisc_config(struct sxe2_adapter *adapter, bool set);
 
+int32_t sxe2_drv_udp_tunnel_add(struct sxe2_adapter *adapter,
+			    enum sxe2_udp_tunnel_protocol tunnel_proto,
+			    uint16_t udp_port);
+
+int32_t sxe2_drv_udp_tunnel_del(struct sxe2_adapter *adapter,
+			    enum sxe2_udp_tunnel_protocol tunnel_proto,
+			    uint16_t udp_port);
+
+int32_t sxe2_drv_udp_tunnel_get(struct sxe2_adapter *adapter,
+			    struct sxe2_udp_tunnel_cfg *tunnel_config);
+
+int32_t sxe2_drv_get_udp_tunnel_port(struct sxe2_adapter *adapter,
+				 enum sxe2_flow_udp_tunnel_protocol proto,
+				 uint16_t *port);
+
+int32_t sxe2_drv_vsi_info_get(struct sxe2_adapter *adapter, struct sxe2_vsi *vsi);
+
 int32_t sxe2_drv_vsi_info_get(struct sxe2_adapter *adapter, struct sxe2_vsi *vsi);
 
 int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter);
diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h
index 38eb2d5cac..f7acd20642 100644
--- a/drivers/net/sxe2/sxe2_drv_cmd.h
+++ b/drivers/net/sxe2/sxe2_drv_cmd.h
@@ -617,6 +617,22 @@ struct __rte_aligned(4) __rte_packed_begin sxe2_drv_flow_fnav_query_stat_resp {
 	uint64_t stat_bytes;
 } __rte_packed_end;
 
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_udp_tunnel_req {
+	uint8_t type;
+	uint8_t rsv;
+	uint16_t port;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_udp_tunnel_resp {
+	uint8_t type;
+	uint8_t enable;
+	uint8_t dst;
+	uint8_t src;
+	uint16_t port;
+	uint8_t fw_used;
+	uint8_t rsv;
+} __rte_packed_end;
+
 enum sxe2_drv_cmd_module {
 	SXE2_DRV_CMD_MODULE_HANDSHAKE = 0,
 	SXE2_DRV_CMD_MODULE_DEV = 1,
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index a2d41b4d1f..fd81a6db7b 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -40,10 +40,11 @@
 #include "sxe2_ioctl_chnl_func.h"
 #include "sxe2_ethdev_repr.h"
 #include "sxe2vf_regs.h"
+#include "sxe2_switchdev.h"
 
 #define SXE2_PCI_VENDOR_ID_1    0x1ff2
 #define SXE2_PCI_DEVICE_ID_PF_1 0x10b1
-#define SXE2_PCI_DEVICE_ID_VF_1 0x10b2
+#define SXE2_PCI_DEVICE_ID_VF_1 0x10b
 
 #define SXE2_PCI_VENDOR_ID_2    0x1d94
 #define SXE2_PCI_DEVICE_ID_PF_2 0x1260
@@ -115,6 +116,11 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev);
 static int32_t sxe2_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info);
 static const uint32_t *sxe2_buffer_split_supported_hdr_ptypes_get(struct rte_eth_dev *dev
 				__rte_unused, size_t *no_of_elements __rte_unused);
+static int32_t sxe2_udp_tunnel_port_add(struct rte_eth_dev *dev,
+					struct rte_eth_udp_tunnel *tunnel_udp);
+static int32_t sxe2_udp_tunnel_port_del(struct rte_eth_dev *dev,
+					struct rte_eth_udp_tunnel *tunnel_udp);
+
 
 static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.dev_configure              = sxe2_dev_configure,
@@ -162,6 +168,9 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.rss_hash_update            = sxe2_dev_rss_hash_update,
 	.rss_hash_conf_get          = sxe2_dev_rss_hash_conf_get,
 
+	.udp_tunnel_port_add        = sxe2_udp_tunnel_port_add,
+	.udp_tunnel_port_del        = sxe2_udp_tunnel_port_del,
+
 	.flow_ops_get               = sxe2_flow_ops_get,
 	.tm_ops_get                 = sxe2_tm_ops_get,
 
@@ -226,6 +235,12 @@ static int32_t sxe2_dev_start(struct rte_eth_dev *dev)
 	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
 	PMD_INIT_FUNC_TRACE();
 
+	ret = sxe2_flow_init_udp_tunnel_port(dev);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "Failed to init udp tunnel port, ret: %d.", ret);
+		goto l_end;
+	}
+
 	ret = sxe2_queues_init(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "Failed to init queues.");
@@ -271,6 +286,188 @@ static int32_t sxe2_dev_start(struct rte_eth_dev *dev)
 	return ret;
 }
 
+static enum sxe2_udp_tunnel_protocol
+sxe2_udp_tunnel_type_rte_to_sxe2(enum rte_eth_tunnel_type rte_type)
+{
+	static enum sxe2_udp_tunnel_protocol sxe2_udp_proto_map[RTE_ETH_TUNNEL_TYPE_MAX] = {
+		[RTE_ETH_TUNNEL_TYPE_NONE] = SXE2_UDP_TUNNEL_MAX,
+		[RTE_ETH_TUNNEL_TYPE_VXLAN] = SXE2_UDP_TUNNEL_PROTOCOL_VXLAN,
+		[RTE_ETH_TUNNEL_TYPE_GENEVE] = SXE2_UDP_TUNNEL_PROTOCOL_GENEVE,
+		[RTE_ETH_TUNNEL_TYPE_TEREDO] = SXE2_UDP_TUNNEL_PROTOCOL_TEREDO,
+		[RTE_ETH_TUNNEL_TYPE_NVGRE] = SXE2_UDP_TUNNEL_PROTOCOL_NVGRE,
+		[RTE_ETH_TUNNEL_TYPE_IP_IN_GRE] = SXE2_UDP_TUNNEL_MAX,
+		[RTE_ETH_L2_TUNNEL_TYPE_E_TAG] = SXE2_UDP_TUNNEL_MAX,
+		[RTE_ETH_TUNNEL_TYPE_VXLAN_GPE] = SXE2_UDP_TUNNEL_PROTOCOL_VXLAN_GPE,
+		[RTE_ETH_TUNNEL_TYPE_ECPRI]  = SXE2_UDP_TUNNEL_PROTOCOL_ECPRI
+	};
+
+	if (rte_type >= RTE_ETH_TUNNEL_TYPE_MAX) {
+		PMD_LOG_ERR(DRV, "Invalid rte_eth_tunnel_type %d!", rte_type);
+		rte_type = RTE_ETH_TUNNEL_TYPE_NONE;
+	}
+
+	return sxe2_udp_proto_map[rte_type];
+}
+
+int32_t sxe2_udp_tunnel_port_add_common(struct sxe2_adapter *ad,
+				    enum sxe2_udp_tunnel_protocol tunnel_proto,
+				    uint16_t udp_port)
+{
+	struct sxe2_udp_tunnel_cfg *tunnel_config;
+	int32_t ret = -1;
+
+	rte_spinlock_lock(&ad->udp_tunnel_ctx.lock);
+
+	tunnel_config = &ad->udp_tunnel_ctx.tunnel_conf[tunnel_proto];
+
+	if (tunnel_config->dev_status == SXE2_UDP_TUNNEL_ENABLE) {
+		if (udp_port == tunnel_config->dev_port &&
+			tunnel_config->dev_ref_cnt < 0xFFFFU) {
+			tunnel_config->dev_ref_cnt++;
+			ret = 0;
+			goto l_unlock_end;
+		} else {
+			PMD_LOG_ERR(DRV, "Adding multiple ports to the same protocol "
+				    "is not supported!");
+			ret = -EINVAL;
+			goto l_unlock_end;
+		}
+	} else {
+		ret = sxe2_drv_udp_tunnel_add(ad, tunnel_proto, udp_port);
+		if (ret != 0)
+			goto l_unlock_end;
+
+		tunnel_config->protocol = tunnel_proto;
+		tunnel_config->dev_port = udp_port;
+		tunnel_config->dev_status  = SXE2_UDP_TUNNEL_ENABLE;
+		tunnel_config->dev_ref_cnt++;
+	}
+
+l_unlock_end:
+	rte_spinlock_unlock(&ad->udp_tunnel_ctx.lock);
+	return ret;
+}
+
+int32_t sxe2_udp_tunnel_port_del_common(struct sxe2_adapter *ad,
+				    enum sxe2_udp_tunnel_protocol tunnel_proto,
+				    uint16_t udp_port)
+{
+	struct sxe2_udp_tunnel_cfg *tunnel_config;
+	int32_t ret = -1;
+
+	rte_spinlock_lock(&ad->udp_tunnel_ctx.lock);
+	tunnel_config = &ad->udp_tunnel_ctx.tunnel_conf[tunnel_proto];
+
+	if (tunnel_config->dev_status == SXE2_UDP_TUNNEL_ENABLE &&
+		udp_port == tunnel_config->dev_port) {
+		if (tunnel_config->dev_ref_cnt > 1) {
+			tunnel_config->dev_ref_cnt--;
+			ret = 0;
+			goto l_unlock_end;
+		} else {
+			ret = sxe2_drv_udp_tunnel_del(ad, tunnel_proto, udp_port);
+			if (ret != 0)
+				goto l_unlock_end;
+
+			tunnel_config->dev_status  = SXE2_UDP_TUNNEL_DISABLE;
+			tunnel_config->dev_ref_cnt = 0;
+		}
+		goto l_unlock_end;
+	}
+
+	ret = -EINVAL;
+
+l_unlock_end:
+	rte_spinlock_unlock(&ad->udp_tunnel_ctx.lock);
+	return ret;
+}
+
+int32_t sxe2_udp_tunnel_port_get_common(struct sxe2_adapter *ad,
+				    struct sxe2_udp_tunnel_cfg *tunnel_config)
+{
+	return sxe2_drv_udp_tunnel_get(ad, tunnel_config);
+}
+
+static int32_t sxe2_udp_tunnel_port_clear(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *ad = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_udp_tunnel_cfg *tunnel_config;
+	int32_t ret = 0;
+	uint16_t tunnel_proto = 0;
+
+	rte_spinlock_lock(&ad->udp_tunnel_ctx.lock);
+
+	for (tunnel_proto = 0; tunnel_proto < SXE2_UDP_TUNNEL_MAX; tunnel_proto++) {
+		tunnel_config = &ad->udp_tunnel_ctx.tunnel_conf[tunnel_proto];
+		if (tunnel_config->dev_status == SXE2_UDP_TUNNEL_ENABLE) {
+			ret = sxe2_drv_udp_tunnel_del(ad, tunnel_config->protocol,
+					tunnel_config->dev_port);
+			if (ret) {
+				PMD_LOG_ERR(DRV, "Failed to delete udp tunnel port %d, proto %d",
+					    tunnel_config->dev_port, tunnel_config->protocol);
+				goto l_unlock_end;
+			}
+
+			tunnel_config->dev_status  = SXE2_UDP_TUNNEL_DISABLE;
+			tunnel_config->dev_ref_cnt = 0;
+		}
+	}
+l_unlock_end:
+	rte_spinlock_unlock(&ad->udp_tunnel_ctx.lock);
+	return ret;
+}
+
+static int32_t sxe2_udp_tunnel_port_add(struct rte_eth_dev *dev,
+			struct rte_eth_udp_tunnel *tunnel_udp)
+{
+	int32_t ret = 0;
+	enum sxe2_udp_tunnel_protocol tunnel_proto;
+	struct sxe2_adapter *ad = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (tunnel_udp->udp_port == 0) {
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	tunnel_proto = sxe2_udp_tunnel_type_rte_to_sxe2(tunnel_udp->prot_type);
+	if (tunnel_proto >= SXE2_UDP_TUNNEL_MAX) {
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	ret = sxe2_udp_tunnel_port_add_common(ad, tunnel_proto, tunnel_udp->udp_port);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "Add tunnel port failed, ret = %d", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_udp_tunnel_port_del(struct rte_eth_dev *dev,
+			struct rte_eth_udp_tunnel *tunnel_udp)
+{
+	int32_t ret = 0;
+	enum sxe2_udp_tunnel_protocol tunnel_proto;
+	struct sxe2_adapter *ad = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	tunnel_proto = sxe2_udp_tunnel_type_rte_to_sxe2(tunnel_udp->prot_type);
+	if (tunnel_proto >= SXE2_UDP_TUNNEL_MAX) {
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	ret = sxe2_udp_tunnel_port_del_common(ad, tunnel_proto, tunnel_udp->udp_port);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "Delete tunnel port failed, ret = %d", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
 static int32_t sxe2_dev_infos_get(struct rte_eth_dev *dev,
 			struct rte_eth_dev_info *dev_info)
 {
@@ -1300,15 +1497,20 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
 	(void)sxe2_dev_stop(dev);
 	(void)sxe2_queues_release(dev);
 	sxe2_mp_uninit(dev);
-	(void)sxe2_rss_disable(dev);
 	(void)sxe2_sched_uinit(dev);
+	(void)sxe2_rss_disable(dev);
+	(void)sxe2_flow_uninit(dev);
+	(void)sxe2_udp_tunnel_port_clear(dev);
 	sxe2_vsi_uninit(dev);
 	sxe2_security_uinit(dev);
 	sxe2_intr_uninit(dev);
 	(void)sxe2_switchdev_uninit(dev);
 	sxe2_sw_uninit(dev);
+	(void)sxe2_switchdev_uninit(dev);
+	sxe2_dev_pci_map_uinit(dev);
 	sxe2_eth_uinit(dev);
 	sxe2_dev_pci_map_uinit(dev);
+	sxe2_free_repr_info(dev);
 
 l_end:
 	return 0;
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index ca4e23f5a8..32efa893d1 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -319,6 +319,7 @@ struct sxe2_adapter {
 	struct sxe2_sched_hw_cap      sched_ctxt;
 	struct sxe2_tm_context        tm_ctxt;
 	struct sxe2_devargs           devargs;
+	struct sxe2_udp_tunnel_ctx    udp_tunnel_ctx;
 	struct sxe2_security_ctx      security_ctx;
 	struct sxe2_repr_context      repr_ctxt;
 	struct sxe2_switchdev_info    switchdev_info;
@@ -374,6 +375,17 @@ void sxe2_dev_pci_seg_unmap(struct sxe2_adapter *adapter, uint32_t res_type);
 
 int32_t sxe2_dev_pci_map_init(struct rte_eth_dev *dev);
 
+void sxe2_dev_pci_seg_unmap(struct sxe2_adapter *adapter, uint32_t res_type);
+
+int32_t sxe2_udp_tunnel_port_get_common(struct sxe2_adapter *ad,
+		struct sxe2_udp_tunnel_cfg *tunnel_config);
+
+int32_t sxe2_udp_tunnel_port_del_common(struct sxe2_adapter *ad,
+		enum sxe2_udp_tunnel_protocol tunnel_proto, uint16_t udp_port);
+
+int32_t sxe2_udp_tunnel_port_add_common(struct sxe2_adapter *ad,
+		enum sxe2_udp_tunnel_protocol tunnel_proto, uint16_t udp_port);
+
 void sxe2_dev_pci_map_uinit(struct rte_eth_dev *dev);
 
 void sxe2_eth_uinit(struct rte_eth_dev *dev);
diff --git a/drivers/net/sxe2/sxe2_flow.c b/drivers/net/sxe2/sxe2_flow.c
index 6999cb0725..63cfc36968 100644
--- a/drivers/net/sxe2/sxe2_flow.c
+++ b/drivers/net/sxe2/sxe2_flow.c
@@ -523,6 +523,51 @@ static int32_t sxe2_flow_adjust_action(struct rte_eth_dev *dev __rte_unused,
 	return ret;
 }
 
+int32_t sxe2_flow_init_udp_tunnel_port(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	uint16_t i = 0;
+	uint16_t *flow_udp_tunnel_port = NULL;
+
+	memset(adapter->flow_ctxt.tunnel_port_list, 0,
+	       sizeof(adapter->flow_ctxt.tunnel_port_list));
+
+	flow_udp_tunnel_port = adapter->flow_ctxt.tunnel_port_list;
+	for (i = 0; i < SXE2_FLOW_UDP_TUNNEL_MAX; i++) {
+		if (flow_udp_tunnel_port[i] == 0) {
+			ret = sxe2_drv_get_udp_tunnel_port(adapter, i,
+							   &flow_udp_tunnel_port[i]);
+			if (ret != 0) {
+				PMD_LOG_ERR(DRV, "Failed to get udp tunnel port, proto: %d,"
+					    "ret: %d", i, ret);
+				goto l_end;
+			}
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_flowlist_add_tunnel_port(struct rte_eth_dev *dev,
+			struct rte_flow *flow_list,
+			struct rte_flow_error *error)
+{
+	struct sxe2_flow_list_t *sxe2_flow_list = &flow_list->sxe2_flow_list;
+	struct sxe2_flow *flow = TAILQ_FIRST(sxe2_flow_list);
+	enum sxe2_flow_tunnel_type tunnel_type = flow->meta.tunnel_type;
+	DECLARE_BITMAP(flow_type, SXE2_EXPANSION_MAX);
+	sxe2_bitmap_zero(flow_type, SXE2_EXPANSION_MAX);
+	sxe2_bitmap_copy(flow_type, flow->flow_type, SXE2_EXPANSION_MAX);
+	int32_t ret = 0;
+
+	if (flow->engine_type == SXE2_FLOW_ENGINE_FNAV)
+		return sxe2_flow_add_tunnel_port(dev, error, flow, flow_type, tunnel_type);
+
+	return ret;
+}
+
 static int32_t sxe2_flow_check_item_empty(uint8_t *item, uint16_t size)
 {
 	uint16_t i = 0;
@@ -679,6 +724,10 @@ static int32_t sxe2_flow_post_proc(struct rte_eth_dev *dev,
 {
 	int32_t ret = 0;
 
+	ret = sxe2_flowlist_add_tunnel_port(dev, flow_list, error);
+	if (ret)
+		goto l_end;
+
 	ret = sxe2_flowlist_add_proto_type(dev, flow_list, error);
 	if (ret)
 		goto l_end;
@@ -1308,6 +1357,11 @@ int32_t sxe2_flow_init(struct rte_eth_dev *dev)
 
 	adapter->flow_ctxt.fnav_inited = 1;
 	rte_spinlock_init(&adapter->flow_ctxt.flow_list_lock);
+
+	ret = sxe2_flow_init_udp_tunnel_port(dev);
+	if (ret)
+		PMD_LOG_ERR(DRV, "Failed to init udp tunnel port, ret: %d.", ret);
+
 	return ret;
 }
 
diff --git a/drivers/net/sxe2/sxe2_flow.h b/drivers/net/sxe2/sxe2_flow.h
index 9970fddcf0..daaeedd4dc 100644
--- a/drivers/net/sxe2/sxe2_flow.h
+++ b/drivers/net/sxe2/sxe2_flow.h
@@ -8,7 +8,6 @@
 #include "sxe2_osal.h"
 #include "sxe2_common.h"
 
-
 int32_t sxe2_flow_ops_get(struct rte_eth_dev *dev, const struct rte_flow_ops **ops);
 
 int32_t sxe2_flow_init(struct rte_eth_dev *dev);
@@ -26,4 +25,6 @@ int32_t sxe2_flow_query_mgr(struct sxe2_adapter *adapter,
 			struct sxe2_flow *flow,
 			struct sxe2_fnav_cid_mgr **mgr_ptr,
 			struct rte_flow_error *error);
+
+int32_t sxe2_flow_init_udp_tunnel_port(struct rte_eth_dev *dev);
 #endif /* __SXE2_FLOW_H__ */
diff --git a/drivers/net/sxe2/sxe2_flow_define.h b/drivers/net/sxe2/sxe2_flow_define.h
index d2f6000efa..263a573f04 100644
--- a/drivers/net/sxe2/sxe2_flow_define.h
+++ b/drivers/net/sxe2/sxe2_flow_define.h
@@ -119,6 +119,7 @@ struct sxe2_flow_context {
 	struct rte_flow_list_t rte_flow_list;
 	rte_spinlock_t flow_list_lock;
 	struct sxe2_fnav_count_resource hw_res;
+	uint16_t tunnel_port_list[SXE2_FLOW_UDP_TUNNEL_MAX];
 	uint32_t fnav_inited;
 };
 #define SXE2_INVALID_RSS_ATTR	\
diff --git a/drivers/net/sxe2/sxe2_flow_parse_pattern.c b/drivers/net/sxe2/sxe2_flow_parse_pattern.c
index 189abb1a33..f5bf8922c6 100644
--- a/drivers/net/sxe2/sxe2_flow_parse_pattern.c
+++ b/drivers/net/sxe2/sxe2_flow_parse_pattern.c
@@ -1637,6 +1637,119 @@ static int32_t sxe2_flow_parse_pattern_vxlan_gpe(const struct rte_flow_item *ite
 	return ret;
 }
 
+static int32_t sxe2_flow_parse_pattern_ipip(struct sxe2_flow *flow, BITMAP_TYPE *flow_type)
+{
+	sxe2_set_bit(SXE2_EXPANSION_IPIP, flow_type);
+	if (sxe2_test_bit(SXE2_EXPANSION_OUTER_IPV4, flow_type)) {
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_PROT, flow->pattern_outer.map_spec);
+		if (sxe2_test_bit(SXE2_EXPANSION_IPV4, flow_type))
+			flow->pattern_outer.item_spec.ipv4.protocol = SXE2_FLOW_IP_PROTOCOL_IPV4;
+		if (sxe2_test_bit(SXE2_EXPANSION_IPV6, flow_type))
+			flow->pattern_outer.item_spec.ipv4.protocol = SXE2_FLOW_IP_PROTOCOL_IPV6;
+	}
+	if (sxe2_test_bit(SXE2_EXPANSION_OUTER_IPV6, flow_type)) {
+		sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PROT, flow->pattern_outer.map_spec);
+		if (sxe2_test_bit(SXE2_EXPANSION_ETH, flow_type)) {
+			flow->pattern_outer.item_spec.ipv6.nexthdr = SXE2_FLOW_IP_PROTOCOL_ETH;
+		} else {
+			if (sxe2_test_bit(SXE2_EXPANSION_IPV4, flow_type))
+				flow->pattern_outer.item_spec.ipv6.nexthdr =
+					SXE2_FLOW_IP_PROTOCOL_IPV4;
+			if (sxe2_test_bit(SXE2_EXPANSION_IPV6, flow_type))
+				flow->pattern_outer.item_spec.ipv6.nexthdr =
+					SXE2_FLOW_IP_PROTOCOL_IPV6;
+		}
+	}
+	return 0;
+}
+
+static int32_t sxe2_flow_add_udp_tunnel_port(struct sxe2_adapter *adapter,
+					 enum sxe2_flow_udp_tunnel_protocol proto,
+					 struct sxe2_flow *flow,
+					 BITMAP_TYPE *flow_type)
+{
+	int32_t ret = 0;
+	uint16_t tun_port;
+
+	tun_port = adapter->flow_ctxt.tunnel_port_list[proto];
+	if (tun_port == 0xffff || tun_port == 0) {
+		ret = -EINVAL;
+		PMD_LOG_ERR(DRV, "UDP tunnel port not initialized, proto: %d", proto);
+		goto l_end;
+	}
+	if (!sxe2_test_bit(SXE2_EXPANSION_OUTER_UDP, flow_type)) {
+		ret = -EINVAL;
+		PMD_LOG_ERR(DRV, "UDP must be over tunnel");
+		goto l_end;
+	}
+	sxe2_set_bit(SXE2_FLOW_FLD_ID_UDP_DST_PORT, flow->pattern_outer.map_spec);
+	flow->pattern_outer.item_spec.udp.dest = rte_cpu_to_be_16(tun_port);
+l_end:
+	return ret;
+}
+
+int32_t sxe2_flow_add_tunnel_port(struct rte_eth_dev *dev,
+			struct rte_flow_error *error,
+			struct sxe2_flow *flow, BITMAP_TYPE *flow_type,
+			enum sxe2_flow_tunnel_type tunnel_type)
+{
+	int32_t ret = 0;
+	enum sxe2_flow_udp_tunnel_protocol proto = SXE2_FLOW_UDP_TUNNEL_MAX;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_flow_pattern *pattern = &flow->pattern_outer;
+	switch (tunnel_type) {
+	case SXE2_FLOW_TUNNEL_TYPE_VXLAN:
+		if (sxe2_test_bit(SXE2_EXPANSION_ETH, flow_type)) {
+			proto = SXE2_FLOW_UDP_TUNNEL_PROTOCOL_VXLAN;
+		} else if (sxe2_test_bit(SXE2_EXPANSION_IPV4, flow_type) ||
+			sxe2_test_bit(SXE2_EXPANSION_IPV6, flow_type)) {
+			proto = SXE2_FLOW_UDP_TUNNEL_PROTOCOL_VXLAN_GPE;
+		}
+		break;
+	case SXE2_FLOW_TUNNEL_TYPE_GTPU:
+		proto = SXE2_FLOW_UDP_TUNNEL_PROTOCOL_GTP_U;
+		break;
+	case SXE2_FLOW_TUNNEL_TYPE_GENEVE:
+		proto = SXE2_FLOW_UDP_TUNNEL_PROTOCOL_GENEVE;
+		break;
+	case SXE2_FLOW_TUNNEL_TYPE_GRE:
+		if (sxe2_test_bit(SXE2_EXPANSION_OUTER_UDP, flow_type)) {
+			proto = SXE2_FLOW_UDP_TUNNEL_PROTOCOL_NVGRE;
+		} else {
+			if (sxe2_test_bit(SXE2_EXPANSION_OUTER_IPV4, flow_type)) {
+				pattern->item_spec.ipv4.protocol = SXE2_FLOW_IP_PROTOCOL_GRE;
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV4_PROT, pattern->map_spec);
+			}
+			if (sxe2_test_bit(SXE2_EXPANSION_OUTER_IPV6, flow_type)) {
+				pattern->item_spec.ipv6.nexthdr = SXE2_FLOW_IP_PROTOCOL_GRE;
+				sxe2_set_bit(SXE2_FLOW_FLD_ID_IPV6_PROT, pattern->map_spec);
+			}
+		}
+		break;
+	case SXE2_FLOW_TUNNEL_TYPE_IPIP:
+		ret = sxe2_flow_parse_pattern_ipip(flow, flow_type);
+		break;
+	default:
+		break;
+	}
+	if (proto != SXE2_FLOW_UDP_TUNNEL_MAX) {
+		ret = sxe2_flow_add_udp_tunnel_port(adapter, proto, flow, flow_type);
+		if (ret != 0) {
+			rte_flow_error_set(error, EINVAL,
+					RTE_FLOW_ERROR_TYPE_ITEM,
+					NULL, "Failed to add udp port for tunnel.");
+			PMD_LOG_ERR(DRV, "Failed to add udp port for tunnel, ret %d.", ret);
+			goto l_end;
+		}
+	}
+	if (tunnel_type != SXE2_FLOW_TUNNEL_TYPE_NONE) {
+		if (!sxe2_test_bit(SXE2_EXPANSION_OUTER_UDP, flow_type))
+			sxe2_set_bit(SXE2_FLOW_HDR_IPV_OTHER, pattern->hdrs);
+	}
+l_end:
+	return ret;
+}
+
 struct sxe2_flow_parse_pattern_ops sxe2_flow_parse_pattern_list[] = {
 	[SXE2_EXPANSION_OUTER_ETH] = {
 		.is_inner = false,
diff --git a/drivers/net/sxe2/sxe2_flow_parse_pattern.h b/drivers/net/sxe2/sxe2_flow_parse_pattern.h
index 69d83a6ea6..8442c35cae 100644
--- a/drivers/net/sxe2/sxe2_flow_parse_pattern.h
+++ b/drivers/net/sxe2/sxe2_flow_parse_pattern.h
@@ -37,4 +37,10 @@ int32_t sxe2_flow_parse_pattern(struct rte_eth_dev *dev,
 			    struct rte_flow_error *error,
 			    struct sxe2_flow *flow);
 
+int32_t sxe2_flow_add_tunnel_port(struct rte_eth_dev *dev,
+			      struct rte_flow_error *error,
+			      struct sxe2_flow *flow,
+			      BITMAP_TYPE *flow_type,
+			      enum sxe2_flow_tunnel_type tunnel_type);
+
 #endif /* SXE2_FLOW_PARSE_PATTERN_H_ */
diff --git a/drivers/net/sxe2/sxe2_txrx_poll.c b/drivers/net/sxe2/sxe2_txrx_poll.c
index f3c4fa0d91..746f9cc2d5 100644
--- a/drivers/net/sxe2/sxe2_txrx_poll.c
+++ b/drivers/net/sxe2/sxe2_txrx_poll.c
@@ -234,6 +234,44 @@ sxe2_tx_pkt_data_desc_count(struct rte_mbuf *tx_pkt)
 	return count;
 }
 
+static __rte_always_inline void sxe2_tx_desc_tunneling_params_fill(uint64_t offloads,
+					union sxe2_tx_offload_info ol_info,
+					uint32_t *desc_tunneling_params)
+{
+	if (offloads & RTE_MBUF_F_TX_OUTER_IP_CKSUM)
+		*desc_tunneling_params |= SXE2_TX_CTXT_DESC_EIPT_IPV4;
+	else if (offloads & RTE_MBUF_F_TX_OUTER_IPV4)
+		*desc_tunneling_params |= SXE2_TX_CTXT_DESC_EIPT_IPV4_NO_CSUM;
+	else if (offloads & RTE_MBUF_F_TX_OUTER_IPV6)
+		*desc_tunneling_params |= SXE2_TX_CTXT_DESC_EIPT_IPV6;
+
+	*desc_tunneling_params |=
+			SXE2_TX_CTXT_DESC_EIPLEN_VAL(ol_info.outer_l3_len);
+	switch (offloads & RTE_MBUF_F_TX_TUNNEL_MASK) {
+	case RTE_MBUF_F_TX_TUNNEL_IPIP:
+		break;
+	case RTE_MBUF_F_TX_TUNNEL_VXLAN:
+	case RTE_MBUF_F_TX_TUNNEL_VXLAN_GPE:
+	case RTE_MBUF_F_TX_TUNNEL_GTP:
+	case RTE_MBUF_F_TX_TUNNEL_GENEVE:
+		*desc_tunneling_params |= SXE2_TX_CTXT_DESC_UDP_TUNNE;
+		break;
+	case RTE_MBUF_F_TX_TUNNEL_GRE:
+		*desc_tunneling_params |= SXE2_TX_CTXT_DESC_GRE_TUNNE;
+		break;
+	default:
+		PMD_LOG_ERR(TX, "Tunnel type [0x%" PRIx64 "] is not supported.",
+			    (uint64_t)(offloads & RTE_MBUF_F_TX_TUNNEL_MASK));
+		return;
+	}
+	*desc_tunneling_params |= SXE2_TX_CTXT_DESC_NATLEN_VAL(ol_info.l2_len);
+	if (!(*desc_tunneling_params & SXE2_TX_CTXT_DESC_EIPT_NONE) &&
+			(*desc_tunneling_params & SXE2_TX_CTXT_DESC_UDP_TUNNE) &&
+			(offloads & RTE_MBUF_F_TX_OUTER_UDP_CKSUM)) {
+		*desc_tunneling_params |= SXE2_TX_CTXT_DESC_L4T_CS_MASK;
+	}
+}
+
 static __rte_always_inline void
 sxe2_tx_desc_checksum_fill(uint64_t offloads, uint32_t *desc_cmd, uint32_t *desc_offset,
 		union sxe2_tx_offload_info ol_info)
@@ -414,7 +452,13 @@ uint16_t sxe2_tx_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkt
 			}
 		}
 
-		desc_offset |= SXE2_TX_DATA_DESC_MACLEN_VAL(ol_info.l2_len);
+		if ((offloads & RTE_MBUF_F_TX_TUNNEL_MASK) && ctxt_desc_num) {
+			desc_offset |= SXE2_TX_DATA_DESC_MACLEN_VAL(ol_info.outer_l2_len);
+			sxe2_tx_desc_tunneling_params_fill(offloads, ol_info,
+						&desc_tunneling_params);
+		} else {
+			desc_offset |= SXE2_TX_DATA_DESC_MACLEN_VAL(ol_info.l2_len);
+		}
 
 		if (offloads & SXE2_TX_OFFLOAD_CKSUM_MASK) {
 			sxe2_tx_desc_checksum_fill(offloads, &desc_cmd,
-- 
2.52.0


^ permalink raw reply related

* [PATCH v10 04/20] net/sxe2: support L2 filtering and MAC config
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260606010726.2256170-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

- Support primary/secondary MAC address setup.
- Enable L2 broadcast/multicast filter bits.
- Add multicast address update logic.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/meson.build      |   1 +
 drivers/net/sxe2/sxe2_cmd_chnl.c  | 198 ++++++++
 drivers/net/sxe2/sxe2_cmd_chnl.h  |  17 +
 drivers/net/sxe2/sxe2_drv_cmd.h   |  87 ++++
 drivers/net/sxe2/sxe2_ethdev.c    |  70 ++-
 drivers/net/sxe2/sxe2_ethdev.h    |  43 +-
 drivers/net/sxe2/sxe2_filter.c    | 782 ++++++++++++++++++++++++++++++
 drivers/net/sxe2/sxe2_filter.h    |  98 ++++
 drivers/net/sxe2/sxe2_mac.c       | 432 +++++++++++++++++
 drivers/net/sxe2/sxe2_mac.h       |  34 ++
 drivers/net/sxe2/sxe2_txrx_poll.c |  49 ++
 11 files changed, 1805 insertions(+), 6 deletions(-)
 create mode 100644 drivers/net/sxe2/sxe2_filter.c
 create mode 100644 drivers/net/sxe2/sxe2_filter.h

diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index b14b5120c1..b661e3cbf4 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -61,4 +61,5 @@ sources += files(
         'sxe2_txrx.c',
         'sxe2_txrx_vec.c',
         'sxe2_mac.c',
+        'sxe2_filter.c',
 )
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.c b/drivers/net/sxe2/sxe2_cmd_chnl.c
index 07eeb7f38c..1fa9ad718e 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.c
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.c
@@ -343,3 +343,201 @@ int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter)
 l_end:
 	return ret;
 }
+
+int32_t sxe2_drv_promisc_config(struct sxe2_adapter *adapter, bool set)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_promisc_filter_cfg_req promisc_filter_cfg_req = {0};
+
+	promisc_filter_cfg_req.vsi_id = adapter->vsi_ctxt.dpdk_vsi_id;
+	promisc_filter_cfg_req.is_add = set;
+	promisc_filter_cfg_req.type = SXE2_PROMISC_FILTER_TYPE_PROMISC;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_PROMISC_CFG,
+				 &promisc_filter_cfg_req,
+				 sizeof(promisc_filter_cfg_req),
+				 NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "promic config failed, ret=%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_allmulti_config(struct sxe2_adapter *adapter, bool set)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_promisc_filter_cfg_req promisc_filter_cfg_req = {0};
+
+	promisc_filter_cfg_req.vsi_id = adapter->vsi_ctxt.dpdk_vsi_id;
+	promisc_filter_cfg_req.is_add = set;
+	promisc_filter_cfg_req.type = SXE2_PROMISC_FILTER_TYPE_ALLMULTI;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_ALLMULTI_CFG,
+				 &promisc_filter_cfg_req,
+				 sizeof(promisc_filter_cfg_req),
+				 NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "allmulti config failed, ret=%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_uc_config(struct sxe2_adapter *adapter, struct rte_ether_addr *addr, bool add)
+{
+	int32_t ret = 0;
+	int32_t i;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_mac_filter_cfg_req mac_filter_cfg_req = {0};
+
+	mac_filter_cfg_req.vsi_id = adapter->vsi_ctxt.dpdk_vsi_id;
+	for (i = 0; i < SXE2_ETH_ALEN; i++)
+		mac_filter_cfg_req.addr[i] = addr->addr_bytes[i];
+	mac_filter_cfg_req.is_add = add;
+	mac_filter_cfg_req.type = SXE2_MAC_FILTER_TYPE_UC;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_MAC_ADDR_UC,
+		 &mac_filter_cfg_req, sizeof(mac_filter_cfg_req),
+		 NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "uc config query failed, ret=%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_mc_config(struct sxe2_adapter *adapter, struct rte_ether_addr *addr, bool add)
+{
+	int32_t ret = 0;
+	int32_t i;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_mac_filter_cfg_req mac_filter_cfg_req = {0};
+
+	mac_filter_cfg_req.vsi_id = adapter->vsi_ctxt.dpdk_vsi_id;
+	for (i = 0; i < SXE2_ETH_ALEN; i++)
+		mac_filter_cfg_req.addr[i] = addr->addr_bytes[i];
+
+	mac_filter_cfg_req.is_add = add;
+	mac_filter_cfg_req.type = SXE2_MAC_FILTER_TYPE_MC;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_MAC_ADDR_MC,
+		 &mac_filter_cfg_req, sizeof(mac_filter_cfg_req),
+		 NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "mac config query failed, ret=%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_vlan_config_query(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_drv_vlan_cfg_query_resp vlan_cfg_query_resp = {0};
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_VLAN_CFG_QUERY,
+				 NULL, 0,
+				 &vlan_cfg_query_resp,
+	 sizeof(vlan_cfg_query_resp));
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "vlan config query failed, ret=%d", ret);
+
+	adapter->filter_ctxt.vlan_info.port_vlan_exist = vlan_cfg_query_resp.port_vlan_exist;
+	adapter->filter_ctxt.vlan_info.is_switchdev = vlan_cfg_query_resp.is_switchdev;
+
+
+	adapter->filter_ctxt.vlan_info.tpid = vlan_cfg_query_resp.tpid;
+	adapter->filter_ctxt.vlan_info.vid = vlan_cfg_query_resp.vid;
+
+	adapter->filter_ctxt.vlan_info.outer_insert = vlan_cfg_query_resp.outer_insert;
+	adapter->filter_ctxt.vlan_info.outer_strip = vlan_cfg_query_resp.outer_strip;
+	adapter->filter_ctxt.vlan_info.inner_insert = vlan_cfg_query_resp.inner_insert;
+	adapter->filter_ctxt.vlan_info.inner_strip = vlan_cfg_query_resp.inner_strip;
+
+	return ret;
+}
+
+int32_t sxe2_drv_vlan_filter_id_config(struct sxe2_adapter *adapter,
+				       struct sxe2_vlan *vlan, bool on)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_vlan_filter_cfg_req vlan_filter_cfg_req = {0};
+
+	vlan_filter_cfg_req.vsi_id = adapter->vsi_ctxt.dpdk_vsi_id;
+	vlan_filter_cfg_req.tpid_id = vlan->tpid;
+	vlan_filter_cfg_req.vlan_id = vlan->vid;
+	vlan_filter_cfg_req.prio = vlan->prio;
+	vlan_filter_cfg_req.is_add = on;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_VLAN_FILTER_ADD_DEL,
+				 &vlan_filter_cfg_req, sizeof(vlan_filter_cfg_req),
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "vlan config failed, ret=%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_vlan_insert_strip_cfg(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_drv_vlan_offload_cfg_req vlan_offload_cfg_req = {0};
+
+	vlan_offload_cfg_req.vsi_id = adapter->vsi_ctxt.dpdk_vsi_id;
+	vlan_offload_cfg_req.tpid = adapter->filter_ctxt.vlan_info.tpid;
+	vlan_offload_cfg_req.outer_insert = adapter->filter_ctxt.vlan_info.outer_insert;
+	vlan_offload_cfg_req.outer_strip = adapter->filter_ctxt.vlan_info.outer_strip;
+	vlan_offload_cfg_req.inner_insert = adapter->filter_ctxt.vlan_info.inner_insert;
+	vlan_offload_cfg_req.inner_strip = adapter->filter_ctxt.vlan_info.inner_strip;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_VLAN_OFFLOAD_CFG,
+				 &vlan_offload_cfg_req,
+				 sizeof(vlan_offload_cfg_req),
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "vlan config query failed, ret=%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_vlan_filter_switch(struct sxe2_adapter *adapter, bool on)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_vlan_filter_switch_req vlan_filter_switch_req = {0};
+
+	vlan_filter_switch_req.vsi_id = adapter->vsi_ctxt.dpdk_vsi_id;
+	vlan_filter_switch_req.is_oper_enable = on;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_VLAN_FILTER_SWITCH,
+				 &vlan_filter_switch_req,
+				 sizeof(vlan_filter_switch_req),
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_WARN(adapter, DRV, "vlan config filter failed, ret=%d", ret);
+
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.h b/drivers/net/sxe2/sxe2_cmd_chnl.h
index cda676ed97..c93bc2b0c9 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.h
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.h
@@ -36,4 +36,21 @@ int32_t sxe2_drv_txq_ctxt_cfg(struct sxe2_adapter *adapter,
 
 int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter);
 
+int32_t sxe2_drv_promisc_config(struct sxe2_adapter *adapter, bool set);
+
+int32_t sxe2_drv_allmulti_config(struct sxe2_adapter *adapter, bool set);
+
+int32_t sxe2_drv_uc_config(struct sxe2_adapter *adapter, struct rte_ether_addr *addr, bool add);
+
+int32_t sxe2_drv_mc_config(struct sxe2_adapter *adapter, struct rte_ether_addr *addr, bool add);
+
+int32_t sxe2_drv_vlan_config_query(struct sxe2_adapter *adapter);
+
+int32_t sxe2_drv_vlan_filter_id_config(struct sxe2_adapter *adapter,
+				       struct sxe2_vlan *vlan, bool on);
+
+int32_t sxe2_drv_vlan_insert_strip_cfg(struct sxe2_adapter *adapter);
+
+int32_t sxe2_drv_vlan_filter_switch(struct sxe2_adapter *adapter, bool on);
+
 #endif /* SXE2_CMD_CHNL_H */
diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h
index a0f08b5184..d69d650148 100644
--- a/drivers/net/sxe2/sxe2_drv_cmd.h
+++ b/drivers/net/sxe2/sxe2_drv_cmd.h
@@ -233,6 +233,93 @@ struct __rte_aligned(4) __rte_packed_begin sxe2_drv_link_info_resp {
 	uint8_t rsv[3];
 } __rte_packed_end;
 
+struct __rte_aligned(4) __rte_packed_begin sxe2_switchdev_info {
+	uint8_t is_switchdev;
+	uint8_t primary;
+	uint8_t representor;
+	uint8_t port_name_type;
+	uint32_t ctrl_num;
+	uint32_t pf_num;
+	uint32_t vf_num;
+	uint32_t mpesw_owner;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vlan_cfg_query_resp {
+	uint16_t vsi_id;
+	uint8_t port_vlan_exist;
+	uint8_t is_switchdev;
+	uint16_t tpid;
+	uint16_t vid;
+	uint8_t outer_insert;
+	uint8_t outer_strip;
+	uint8_t inner_insert;
+	uint8_t inner_strip;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vlan_offload_cfg_req {
+	uint16_t vsi_id;
+	uint16_t tpid;
+	uint8_t outer_insert;
+	uint8_t outer_strip;
+	uint8_t inner_insert;
+	uint8_t inner_strip;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_port_vlan_cfg_req {
+	uint16_t vsi_id;
+	uint16_t tpid;
+	uint16_t vid;
+	uint8_t prio;
+	uint8_t rsv;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_mac_filter_cfg_req {
+	uint16_t vsi_id;
+	uint8_t addr[SXE2_ETH_ALEN];
+	uint8_t type;
+	uint8_t is_add;
+	uint8_t rsv[2];
+} __rte_packed_end;
+
+enum sxe2_promisc_filter_type {
+	SXE2_PROMISC_FILTER_TYPE_PROMISC = 0,
+	SXE2_PROMISC_FILTER_TYPE_ALLMULTI,
+	SXE2_PROMISC_FILTER_TYPE_MAX,
+};
+
+enum sxe2_mac_filter_type {
+	SXE2_MAC_FILTER_TYPE_UC = 0,
+	SXE2_MAC_FILTER_TYPE_MC,
+	SXE2_MAC_FILTER_TYPE_MAX,
+};
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_promisc_filter_cfg_req {
+	uint16_t vsi_id;
+	uint8_t type;
+	uint8_t is_add;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_srcvsi_ext_cfg_req {
+	uint16_t vsi_id;
+	uint16_t srcvsi_list[SXE2_SRCVSI_PRUNE_MAX_NUM];
+	uint8_t srcvsi_cnt;
+	uint8_t is_add;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_vlan_filter_cfg_req {
+	uint16_t vsi_id;
+	uint16_t vlan_id;
+	uint16_t tpid_id;
+	uint8_t prio;
+	uint8_t is_add;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_vlan_filter_switch_req {
+	uint16_t vsi_id;
+	uint8_t is_oper_enable;
+	uint8_t rsv;
+} __rte_packed_end;
+
 enum sxe2_drv_cmd_module {
 	SXE2_DRV_CMD_MODULE_HANDSHAKE = 0,
 	SXE2_DRV_CMD_MODULE_DEV = 1,
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index 01552a8202..9b117f097e 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -111,8 +111,20 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.tx_burst_mode_get          = sxe2_tx_burst_mode_get,
 	.tx_done_cleanup            = sxe2_tx_done_cleanup,
 
+	.promiscuous_enable         = sxe2_promisc_enable,
+	.promiscuous_disable        = sxe2_promisc_disable,
+	.allmulticast_enable        = sxe2_allmulti_enable,
+	.allmulticast_disable       = sxe2_allmulti_disable,
+
+	.mac_addr_add               = sxe2_mac_addr_add,
+	.mac_addr_remove            = sxe2_mac_addr_del,
+	.mac_addr_set               = sxe2_mac_addr_set,
+	.set_mc_addr_list           = sxe2_set_mc_addr_list,
 	.mtu_set                    = sxe2_mtu_set,
 	.buffer_split_supported_hdr_ptypes_get = sxe2_buffer_split_supported_hdr_ptypes_get,
+
+	.vlan_filter_set            = sxe2_dev_vlan_filter_set,
+	.vlan_offload_set           = sxe2_dev_vlan_offload_set,
 };
 
 static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
@@ -123,6 +135,13 @@ static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
 	if (dev->data->dev_conf.rxmode.mq_mode  & RTE_ETH_MQ_RX_RSS_FLAG)
 		dev->data->dev_conf.rxmode.offloads |= RTE_ETH_RX_OFFLOAD_RSS_HASH;
 
+	ret = sxe2_vlan_default_cfg(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to init vlan, ret=%d", ret);
+		goto end;
+	}
+
+end:
 	return ret;
 }
 
@@ -138,6 +157,8 @@ static int32_t sxe2_dev_stop(struct rte_eth_dev *dev)
 	sxe2_txqs_all_stop(dev);
 	sxe2_rxqs_all_stop(dev);
 
+	(void)sxe2_filter_rule_stop(dev);
+
 	dev->data->dev_started = 0;
 	adapter->started = 0;
 l_end:
@@ -165,16 +186,23 @@ static int32_t sxe2_dev_start(struct rte_eth_dev *dev)
 		goto l_end;
 	}
 
+	ret = sxe2_filter_rule_start(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to add all mc addr to fw.");
+		goto l_end;
+	}
+
 	ret = sxe2_queues_start(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "enable queues failed");
-		goto l_end;
+		goto l_start_queues_err;
 	}
 
 	dev->data->dev_started = 1;
 	adapter->started = 1;
 	goto l_end;
-
+l_start_queues_err:
+	(void)sxe2_filter_rule_stop(dev);
 l_end:
 	return ret;
 }
@@ -194,6 +222,7 @@ static int32_t sxe2_dev_infos_get(struct rte_eth_dev *dev,
 	dev_info->min_mtu = RTE_ETHER_MIN_MTU;
 
 	dev_info->rx_offload_capa =
+		RTE_ETH_RX_OFFLOAD_VLAN_STRIP |
 		RTE_ETH_RX_OFFLOAD_KEEP_CRC |
 		RTE_ETH_RX_OFFLOAD_SCATTER |
 		RTE_ETH_RX_OFFLOAD_IPV4_CKSUM |
@@ -202,9 +231,15 @@ static int32_t sxe2_dev_infos_get(struct rte_eth_dev *dev,
 		RTE_ETH_RX_OFFLOAD_SCTP_CKSUM |
 		RTE_ETH_RX_OFFLOAD_OUTER_IPV4_CKSUM |
 		RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT |
+#ifndef RTE_LIBRTE_SXE2_16BYTE_RX_DESC
+		RTE_ETH_RX_OFFLOAD_QINQ_STRIP |
+#endif
+		RTE_ETH_RX_OFFLOAD_VLAN_EXTEND |
 		RTE_ETH_RX_OFFLOAD_TCP_LRO;
 
 	dev_info->tx_offload_capa =
+		RTE_ETH_TX_OFFLOAD_VLAN_INSERT |
+		RTE_ETH_TX_OFFLOAD_QINQ_INSERT |
 		RTE_ETH_TX_OFFLOAD_MULTI_SEGS |
 		RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE |
 		RTE_ETH_TX_OFFLOAD_IPV4_CKSUM |
@@ -428,6 +463,12 @@ static int32_t sxe2_eth_init(struct rte_eth_dev *dev)
 {
 	int32_t ret = 0;
 
+	ret = sxe2_filter_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to initialize l2 filter, ret:%d", ret);
+		goto l_end;
+	}
+
 	ret = sxe2_link_update_init(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "Failed to initialize link update, ret:%d", ret);
@@ -439,12 +480,37 @@ static int32_t sxe2_eth_init(struct rte_eth_dev *dev)
 		PMD_LOG_ERR(INIT, "Failed to set mtu, ret=%d", ret);
 		goto l_end;
 	}
+
+	ret = sxe2_mac_addr_init(dev);
+	if (ret != 0) {
+		PMD_LOG_ERR(INIT, "Failed to initialize mac address, ret:%d", ret);
+		goto l_end;
+	}
+
+	ret = sxe2_mac_default_cfg(dev);
+	if (ret != 0) {
+		PMD_LOG_ERR(INIT, "Failed to configure default mac address, ret:%d", ret);
+		goto l_err;
+	}
+
+	ret = sxe2_vlan_cfg_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to initialize vlan config, ret:%d", ret);
+		goto l_err;
+	}
+	goto l_end;
+
+l_err:
+	sxe2_mac_addr_uinit(dev);
+	(void)sxe2_filter_uinit(dev);
 l_end:
 	return ret;
 }
 
 static void sxe2_eth_uinit(struct rte_eth_dev *dev __rte_unused)
 {
+	sxe2_mac_addr_uinit(dev);
+	(void)sxe2_filter_uinit(dev);
 }
 
 static void sxe2_drv_dev_caps_set(struct sxe2_adapter *adapter,
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index c188b41a0e..34a4a45e4f 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -15,9 +15,11 @@
 
 #include "sxe2_common.h"
 #include "sxe2_vsi.h"
-#include "sxe2_queue.h"
 #include "sxe2_irq.h"
+#include "sxe2_queue.h"
+#include "sxe2_mac.h"
 #include "sxe2_osal.h"
+#include "sxe2_filter.h"
 
 struct sxe2_link_msg {
 	uint32_t speed;
@@ -35,7 +37,7 @@ enum sxe2_fnav_tunnel_flag_type {
 #define SXE2_FRAME_SIZE_MAX    9832
 #define SXE2_VLAN_TAG_SIZE     4
 #define SXE2_ETH_OVERHEAD \
-	(RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN + SXE2_VLAN_TAG_SIZE)
+	(RTE_ETHER_HDR_LEN + RTE_ETHER_CRC_LEN + 2 * SXE2_VLAN_TAG_SIZE)
 #define SXE2_ETH_MAX_LEN (RTE_ETHER_MTU + SXE2_ETH_OVERHEAD)
 
 #ifdef SXE2_TEST
@@ -267,6 +269,27 @@ struct sxe2_link_context {
 	uint32_t  speed;
 };
 
+struct sxe2_filter_context {
+	rte_spinlock_t filter_lock;
+	struct sxe2_vlan_info               vlan_info;
+	struct sxe2_uc_filter_list_head    uc_list;
+	struct sxe2_mc_filter_list_head    mc_list;
+	struct sxe2_vlan_filter_list_head  vlan_list;
+	uint8_t                                 uc_num;
+	uint8_t                                 mc_num;
+	uint8_t                                 vlan_num;
+	uint8_t                                 rsv;
+	uint32_t hw_promisc_flags;
+	uint32_t cur_promisc_flags;
+
+	bool hw_uplink_config;
+	bool cur_uplink_config;
+	bool hw_repr_config;
+	bool cur_repr_config;
+	bool hw_l2_config;
+	bool cur_l2_config;
+};
+
 struct sxe2_adapter {
 	struct sxe2_common_device      *cdev;
 	struct sxe2_dev_info            dev_info;
@@ -276,10 +299,14 @@ struct sxe2_adapter {
 	struct sxe2_irq_context       irq_ctxt;
 	struct sxe2_queue_context     q_ctxt;
 	struct sxe2_vsi_context       vsi_ctxt;
+	struct sxe2_filter_context    filter_ctxt;
 	struct sxe2_link_context      link_ctxt;
 	struct sxe2_devargs           devargs;
-	uint16_t                      dev_port_id;
-	uint64_t                      cap_flags;
+	struct sxe2_switchdev_info    switchdev_info;
+	bool                          rule_started;
+	bool                          flow_isolated;
+	uint16_t                           dev_port_id;
+	uint64_t                           cap_flags;
 	enum sxe2_dev_type            dev_type;
 	uint32_t    ptype_tbl[SXE2_MAX_PTYPE_NUM];
 	struct rte_ether_addr           mac_addr;
@@ -318,4 +345,12 @@ int32_t sxe2_dev_pci_map_init(struct rte_eth_dev *dev);
 
 void sxe2_dev_pci_map_uinit(struct rte_eth_dev *dev);
 
+static inline bool
+sxe2_dev_port_vlan_check(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *ad = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	return ad->filter_ctxt.vlan_info.port_vlan_exist;
+}
+
 #endif /* SXE2_ETHDEV_H */
diff --git a/drivers/net/sxe2/sxe2_filter.c b/drivers/net/sxe2/sxe2_filter.c
new file mode 100644
index 0000000000..b2a726f77e
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_filter.c
@@ -0,0 +1,782 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <rte_os.h>
+#include <rte_tailq.h>
+#include "sxe2_osal.h"
+#include "sxe2_mac.h"
+#include "sxe2_common_log.h"
+#include "sxe2_ethdev.h"
+#include "sxe2_cmd_chnl.h"
+
+static struct sxe2_mac_filter *sxe2_uc_filter_find(struct sxe2_adapter *adapter,
+			struct rte_ether_addr *macaddr)
+{
+	struct sxe2_mac_filter *filter      = NULL;
+	struct sxe2_mac_filter *entry       = NULL;
+	struct sxe2_mac_filter *next_entry  = NULL;
+
+	rte_spinlock_lock(&adapter->filter_ctxt.filter_lock);
+	RTE_TAILQ_FOREACH_SAFE(entry, &adapter->filter_ctxt.uc_list, next, next_entry) {
+		if (rte_is_same_ether_addr(macaddr, &entry->mac_addr)) {
+			filter = entry;
+			break;
+		}
+	}
+	rte_spinlock_unlock(&adapter->filter_ctxt.filter_lock);
+
+	return filter;
+}
+
+int32_t sxe2_uc_filter_add(struct sxe2_adapter *adapter,
+			struct rte_ether_addr *mac_addr, bool default_config)
+{
+	struct sxe2_mac_filter *filter = NULL;
+	bool hw_config = false;
+	int32_t ret = 0;
+
+	filter = sxe2_uc_filter_find(adapter, mac_addr);
+	if (filter) {
+		if (default_config && !filter->default_config)
+			filter->default_config = true;
+		PMD_DEV_LOG_INFO(adapter, DRV, "This MAC filter already exists.");
+		goto l_end;
+	}
+
+	if (!adapter->rule_started) {
+		PMD_DEV_LOG_DEBUG(adapter, DRV, "cannot add hw uc addr in port stop status");
+	} else if (adapter->flow_isolated) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot add hw uc addr in flow isolation mode");
+	} else if (adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot add hw uc addr in switchdev mode");
+	} else {
+		ret = sxe2_drv_uc_config(adapter, mac_addr, true);
+		if (ret && ret != -EEXIST) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add uc rule");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		hw_config = true;
+	}
+
+	filter = rte_zmalloc("sxe2_uc_filter",
+			     sizeof(struct sxe2_mac_filter), 0);
+	if (!filter) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to allocate memory");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+	filter->hw_config = hw_config;
+	filter->default_config = default_config;
+	rte_ether_addr_copy(mac_addr, &filter->mac_addr);
+	rte_spinlock_lock(&adapter->filter_ctxt.filter_lock);
+	TAILQ_INSERT_TAIL(&adapter->filter_ctxt.uc_list, filter, next);
+	adapter->filter_ctxt.uc_num++;
+	rte_spinlock_unlock(&adapter->filter_ctxt.filter_lock);
+
+	PMD_DEV_LOG_INFO(adapter, DRV, "add mac rule, mac num %u.", adapter->filter_ctxt.uc_num);
+	ret = 0;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_uc_filter_del(struct sxe2_adapter *adapter,
+			struct rte_ether_addr *mac_addr)
+{
+	struct sxe2_mac_filter *filter = NULL;
+	int32_t ret                         = -1;
+
+	filter = sxe2_uc_filter_find(adapter, mac_addr);
+	if (!filter) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "This MAC filter not exists.");
+		ret = 0;
+		goto l_end;
+	}
+	if (filter->hw_config) {
+		ret = sxe2_drv_uc_config(adapter, mac_addr, false);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to delete mac rule");
+			if (ret == -EPERM)
+				goto l_free;
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+	PMD_DEV_LOG_INFO(adapter, DRV, "remove mac rule, uc num %u.", adapter->filter_ctxt.uc_num);
+	ret = 0;
+
+l_free:
+
+	rte_spinlock_lock(&adapter->filter_ctxt.filter_lock);
+	TAILQ_REMOVE(&adapter->filter_ctxt.uc_list, filter, next);
+	adapter->filter_ctxt.uc_num--;
+	rte_spinlock_unlock(&adapter->filter_ctxt.filter_lock);
+	rte_free(filter);
+	filter = NULL;
+l_end:
+	return ret;
+}
+
+void sxe2_uc_filter_clear(struct sxe2_adapter *adapter, bool default_config)
+{
+	struct sxe2_mac_filter *entry;
+	struct sxe2_mac_filter *next_entry;
+
+	RTE_TAILQ_FOREACH_SAFE(entry, &adapter->filter_ctxt.uc_list, next, next_entry) {
+		if (entry->default_config && !default_config)
+			continue;
+
+		if (sxe2_uc_filter_del(adapter, &entry->mac_addr))
+			PMD_DEV_LOG_ERR(adapter, DRV, "This MAC filter delete fail.");
+	}
+}
+
+static struct sxe2_mac_filter *sxe2_mc_filter_find(struct sxe2_adapter *adapter,
+			struct rte_ether_addr *macaddr)
+{
+	struct sxe2_mac_filter *filter      = NULL;
+	struct sxe2_mac_filter *entry       = NULL;
+	struct sxe2_mac_filter *next_entry  = NULL;
+
+	rte_spinlock_lock(&adapter->filter_ctxt.filter_lock);
+	RTE_TAILQ_FOREACH_SAFE(entry, &adapter->filter_ctxt.mc_list, next, next_entry) {
+		if (rte_is_same_ether_addr(macaddr, &entry->mac_addr)) {
+			filter = entry;
+			break;
+		}
+	}
+	rte_spinlock_unlock(&adapter->filter_ctxt.filter_lock);
+
+	return filter;
+}
+
+int32_t sxe2_mc_filter_add(struct sxe2_adapter *adapter,
+			struct rte_ether_addr *mac_addr, bool default_config)
+{
+	struct sxe2_mac_filter *filter = NULL;
+	bool hw_config = false;
+	int32_t ret = 0;
+
+	filter = sxe2_mc_filter_find(adapter, mac_addr);
+	if (filter) {
+		if (default_config && !filter->default_config)
+			filter->default_config = true;
+		PMD_DEV_LOG_INFO(adapter, DRV, "This MAC filter already exists.");
+		goto l_end;
+	}
+
+	if (!adapter->rule_started) {
+		PMD_DEV_LOG_DEBUG(adapter, DRV, "cannot add hw mc addr in port stop status");
+	} else if (adapter->flow_isolated) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot add hw mc addr in flow isolation mode");
+	} else if (adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot add hw mc addr in switchdev mode");
+	} else {
+		ret = sxe2_drv_mc_config(adapter, mac_addr, true);
+		if (ret && ret != -EEXIST) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add mac rule");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		hw_config = true;
+	}
+
+	filter = rte_zmalloc("sxe2_mc_filter",
+			     sizeof(struct sxe2_mac_filter), 0);
+	if (!filter) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to allocate memory");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+	filter->hw_config = hw_config;
+	filter->default_config = default_config;
+	rte_ether_addr_copy(mac_addr, &filter->mac_addr);
+	rte_spinlock_lock(&adapter->filter_ctxt.filter_lock);
+	TAILQ_INSERT_TAIL(&adapter->filter_ctxt.mc_list, filter, next);
+	adapter->filter_ctxt.mc_num++;
+	rte_spinlock_unlock(&adapter->filter_ctxt.filter_lock);
+
+	PMD_DEV_LOG_INFO(adapter, DRV, "add mc rule, mc num %u.", adapter->filter_ctxt.mc_num);
+	ret = 0;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_mc_filter_del(struct sxe2_adapter *adapter,
+			struct rte_ether_addr *mac_addr)
+{
+	struct sxe2_mac_filter *filter = NULL;
+	int32_t ret                         = -1;
+
+	filter = sxe2_mc_filter_find(adapter, mac_addr);
+	if (!filter) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "This MAC filter not exists.");
+		ret = 0;
+		goto l_end;
+	}
+
+	if (filter->hw_config) {
+		ret = sxe2_drv_mc_config(adapter, mac_addr, false);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to delete mc rule");
+			if (ret == -EPERM)
+				goto l_free;
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+	PMD_DEV_LOG_INFO(adapter, DRV, "remove mc rule, mc num %u.", adapter->filter_ctxt.mc_num);
+	ret = 0;
+
+l_free:
+
+	rte_spinlock_lock(&adapter->filter_ctxt.filter_lock);
+	TAILQ_REMOVE(&adapter->filter_ctxt.mc_list, filter, next);
+	adapter->filter_ctxt.mc_num--;
+	rte_spinlock_unlock(&adapter->filter_ctxt.filter_lock);
+	rte_free(filter);
+	filter = NULL;
+l_end:
+	return ret;
+}
+
+void sxe2_mc_filter_clear(struct sxe2_adapter *adapter, bool default_config)
+{
+	struct sxe2_mac_filter *entry;
+	struct sxe2_mac_filter *next_entry;
+
+	RTE_TAILQ_FOREACH_SAFE(entry, &adapter->filter_ctxt.mc_list, next, next_entry) {
+		if (entry->default_config && !default_config)
+			continue;
+		if (sxe2_mc_filter_del(adapter, &entry->mac_addr))
+			PMD_DEV_LOG_ERR(adapter, DRV, "This MAC filter delete fail.");
+	}
+}
+
+static struct sxe2_vlan_filter *sxe2_vlan_filter_find(struct sxe2_adapter *adapter,
+			struct sxe2_vlan *vlan)
+{
+	struct sxe2_vlan_filter *f;
+	struct sxe2_vlan_filter *save_f = NULL;
+
+	rte_spinlock_lock(&adapter->filter_ctxt.filter_lock);
+	TAILQ_FOREACH(f, &adapter->filter_ctxt.vlan_list, next)
+	{
+		if (vlan->tpid == f->vlan_info.tpid &&
+			vlan->vid == f->vlan_info.vid) {
+			save_f = f;
+			break;
+		}
+	}
+	rte_spinlock_unlock(&adapter->filter_ctxt.filter_lock);
+
+	return save_f;
+}
+
+int32_t sxe2_vlan_filter_add(struct sxe2_adapter *adapter,
+			     struct sxe2_vlan *vlan, bool default_config)
+{
+	struct sxe2_vlan_filter *filter = NULL;
+	bool hw_config                 = false;
+	int32_t ret                    = 0;
+
+	if (!vlan || vlan->vid > RTE_ETHER_MAX_VLAN_ID) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "This vlan filter is invalid.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	filter = sxe2_vlan_filter_find(adapter, vlan);
+	if (filter) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "This vlan filter already exists.");
+		ret = 0;
+		goto l_end;
+	}
+	if (!adapter->rule_started) {
+		PMD_DEV_LOG_DEBUG(adapter, DRV, "cannot add vlan in port stop status");
+	} else if (adapter->flow_isolated) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot add vlan in flow isolation mode");
+	} else if (adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot add vlan in switchdev mode");
+	} else {
+		ret = sxe2_drv_vlan_filter_id_config(adapter, vlan, true);
+		if (ret && ret != -EEXIST) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add vlan rule");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		hw_config = true;
+	}
+
+	filter = rte_zmalloc("sxe2_vlan_filter", sizeof(*filter), 0);
+	if (!filter) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to allocate memory");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+
+	filter->hw_config = hw_config;
+	filter->default_config = default_config;
+
+	filter->vlan_info.tpid = vlan->tpid;
+	filter->vlan_info.vid = vlan->vid;
+	filter->vlan_info.prio = vlan->prio;
+
+	rte_spinlock_lock(&adapter->filter_ctxt.filter_lock);
+	TAILQ_INSERT_TAIL(&adapter->filter_ctxt.vlan_list, filter, next);
+	adapter->filter_ctxt.vlan_num++;
+	rte_spinlock_unlock(&adapter->filter_ctxt.filter_lock);
+
+	ret = 0;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_vlan_filter_del(struct sxe2_adapter *adapter, struct sxe2_vlan *vlan)
+{
+	struct sxe2_vlan_filter *filter = NULL;
+	int32_t ret                         = -1;
+
+	if (!vlan || vlan->vid > RTE_ETHER_MAX_VLAN_ID) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "This vlan filter is invalid.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	filter = sxe2_vlan_filter_find(adapter, vlan);
+	if (!filter) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "This vlan filter not exists.");
+		ret = 0;
+		goto l_end;
+	}
+
+	if (filter->hw_config) {
+		ret = sxe2_drv_vlan_filter_id_config(adapter, vlan, false);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to delete vlan rule");
+			if (ret == -EPERM)
+				goto l_free;
+			ret = -EINVAL;
+			goto l_end;
+		}
+	}
+	ret = 0;
+
+l_free:
+
+	rte_spinlock_lock(&adapter->filter_ctxt.filter_lock);
+	TAILQ_REMOVE(&adapter->filter_ctxt.vlan_list, filter, next);
+	adapter->filter_ctxt.vlan_num--;
+	rte_spinlock_unlock(&adapter->filter_ctxt.filter_lock);
+	rte_free(filter);
+	filter = NULL;
+l_end:
+	return ret;
+}
+
+void sxe2_vlan_filters_clear(struct sxe2_adapter *adapter, bool default_config)
+{
+	int32_t ret = 0;
+	struct sxe2_vlan_filter *v_f;
+	void *temp;
+
+	if (adapter->filter_ctxt.vlan_num == 0)
+		return;
+
+	RTE_TAILQ_FOREACH_SAFE(v_f, &adapter->filter_ctxt.vlan_list, next, temp)
+	{
+		if (v_f->default_config && !default_config)
+			continue;
+		ret = sxe2_vlan_filter_del(adapter, &v_f->vlan_info);
+		if (ret)
+			PMD_DEV_LOG_ERR(adapter, DRV, "This vlan filter delete fail.");
+	}
+}
+
+int32_t sxe2_vlan_filter_ctrl(struct sxe2_adapter *adapter, bool flag)
+{
+	struct sxe2_vlan_info *vlan_info = &adapter->filter_ctxt.vlan_info;
+	int32_t ret = 0;
+
+	if (vlan_info->filter_on == flag)
+		goto l_end;
+	if (!adapter->rule_started) {
+		PMD_DEV_LOG_DEBUG(adapter, DRV, "cannot add vlan filter ctrl in port stop status");
+	} else if (adapter->flow_isolated) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot add vlan filter ctrl in flow isolation mode");
+	} else if (adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot add vlan filter ctrl in switchdev mode");
+	} else {
+		ret = sxe2_drv_vlan_filter_switch(adapter, flag);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add vlan filter ctrl");
+			goto l_end;
+		}
+		vlan_info->hw_filter_on = flag;
+	}
+	vlan_info->filter_on = flag;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_promisc_add(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (!adapter->rule_started) {
+		PMD_DEV_LOG_DEBUG(adapter, DRV, "cannot enable promiscuous in port stop status");
+	} else if (adapter->flow_isolated) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot enable promiscuous in flow isolation mode");
+	} else if (adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot enable promiscuous in switchdev mode");
+	} else if (!(adapter->filter_ctxt.hw_promisc_flags & SXE2_PROMISC)) {
+		ret = sxe2_drv_promisc_config(adapter, true);
+		if (ret && ret != -EEXIST) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "failed to cfg promiscuous, ret:%d", ret);
+			goto l_end;
+		}
+		adapter->filter_ctxt.hw_promisc_flags |= SXE2_PROMISC;
+	}
+	adapter->filter_ctxt.cur_promisc_flags |= SXE2_PROMISC;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_promisc_del(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (!adapter->flow_isolated &&
+	    (adapter->filter_ctxt.hw_promisc_flags & SXE2_PROMISC)) {
+		ret = sxe2_drv_promisc_config(adapter, false);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "failed to cfg promiscuous, ret:%d", ret);
+			goto l_end;
+		}
+		adapter->filter_ctxt.hw_promisc_flags &= ~SXE2_PROMISC;
+	}
+
+	adapter->filter_ctxt.cur_promisc_flags &= ~SXE2_PROMISC;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_allmulti_add(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (!adapter->rule_started) {
+		PMD_DEV_LOG_DEBUG(adapter, DRV, "cannot enable allmulticast in port stop status");
+	} else if (adapter->flow_isolated) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot enable allmulticast in flow isolation mode");
+	} else if (adapter->switchdev_info.is_switchdev) {
+		PMD_DEV_LOG_WARN(adapter, DRV, "cannot enable allmulticast in switchdev mode");
+	} else if (!(adapter->filter_ctxt.hw_promisc_flags & SXE2_PROMISC_MULTICAST)) {
+		ret = sxe2_drv_allmulti_config(adapter, true);
+		if (ret && ret != -EEXIST) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "failed to cfg allmulticast, ret:%d", ret);
+			goto l_end;
+		}
+		adapter->filter_ctxt.hw_promisc_flags |= SXE2_PROMISC_MULTICAST;
+	}
+	adapter->filter_ctxt.cur_promisc_flags |= SXE2_PROMISC_MULTICAST;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_allmulti_del(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (!adapter->flow_isolated &&
+	    (adapter->filter_ctxt.hw_promisc_flags & SXE2_PROMISC_MULTICAST)) {
+		ret = sxe2_drv_allmulti_config(adapter, false);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "failed to cfg allmulticast, ret:%d", ret);
+			goto l_end;
+		}
+		adapter->filter_ctxt.hw_promisc_flags &= ~SXE2_PROMISC_MULTICAST;
+	}
+
+	adapter->filter_ctxt.cur_promisc_flags &= ~SXE2_PROMISC_MULTICAST;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_all_filter_hw_clear(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	struct sxe2_mac_filter *mac_entry;
+	struct sxe2_mac_filter *next_mac_entry;
+	struct sxe2_vlan_filter *vlan_entry;
+	struct sxe2_vlan_filter *next_vlan_entry;
+
+	if (adapter->filter_ctxt.uc_num > 0) {
+		RTE_TAILQ_FOREACH_SAFE(mac_entry, &adapter->filter_ctxt.uc_list, next,
+			    next_mac_entry) {
+			if (mac_entry->hw_config) {
+				ret = sxe2_drv_uc_config(adapter, &mac_entry->mac_addr, false);
+				if (ret) {
+					PMD_DEV_LOG_ERR(adapter, DRV, "Failed to delete mac rule");
+					ret = -EINVAL;
+					goto l_end;
+				}
+				mac_entry->hw_config = false;
+			}
+		}
+	}
+
+	if (adapter->filter_ctxt.mc_num > 0) {
+		RTE_TAILQ_FOREACH_SAFE(mac_entry, &adapter->filter_ctxt.mc_list, next,
+			    next_mac_entry) {
+			if (mac_entry->hw_config) {
+				ret = sxe2_drv_mc_config(adapter, &mac_entry->mac_addr, false);
+				if (ret) {
+					PMD_DEV_LOG_ERR(adapter, DRV, "Failed to delete mc rule");
+					ret = -EINVAL;
+					goto l_end;
+				}
+				mac_entry->hw_config = false;
+			}
+		}
+	}
+
+	if (adapter->filter_ctxt.vlan_num > 0) {
+		RTE_TAILQ_FOREACH_SAFE(vlan_entry, &adapter->filter_ctxt.vlan_list, next,
+			    next_vlan_entry) {
+			if (vlan_entry->hw_config) {
+				ret = sxe2_drv_vlan_filter_id_config(adapter,
+				    &vlan_entry->vlan_info, false);
+				if (ret) {
+					PMD_DEV_LOG_ERR(adapter, DRV, "Failed to delete vlan rule");
+					ret = -EINVAL;
+					goto l_end;
+				}
+				vlan_entry->hw_config = false;
+			}
+		}
+	}
+
+	if (adapter->filter_ctxt.vlan_info.hw_filter_on) {
+		ret = sxe2_drv_vlan_filter_switch(adapter, false);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to delete vlan rule");
+			ret = -EINVAL;
+			goto l_end;
+		}
+		adapter->filter_ctxt.vlan_info.hw_filter_on = false;
+	}
+
+	if (adapter->filter_ctxt.hw_promisc_flags & SXE2_PROMISC) {
+		ret = sxe2_drv_promisc_config(adapter, false);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "failed to cfg promiscuous, ret:%d", ret);
+			goto l_end;
+		}
+		adapter->filter_ctxt.hw_promisc_flags &= ~SXE2_PROMISC;
+	}
+
+	if (adapter->filter_ctxt.hw_promisc_flags & SXE2_PROMISC_MULTICAST) {
+		ret = sxe2_drv_allmulti_config(adapter, false);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "failed to cfg allmulticast, ret:%d", ret);
+			goto l_end;
+		}
+		adapter->filter_ctxt.hw_promisc_flags &= ~SXE2_PROMISC_MULTICAST;
+	}
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_all_filter_hw_set(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	struct sxe2_mac_filter *mac_entry;
+	struct sxe2_mac_filter *next_mac_entry;
+	struct sxe2_vlan_filter *vlan_entry;
+	struct sxe2_vlan_filter *next_vlan_entry;
+
+	if (adapter->filter_ctxt.uc_num > 0) {
+		RTE_TAILQ_FOREACH_SAFE(mac_entry, &adapter->filter_ctxt.uc_list, next,
+				       next_mac_entry) {
+			if (!mac_entry->hw_config) {
+				ret = sxe2_drv_uc_config(adapter, &mac_entry->mac_addr,
+							 true);
+				if (ret && ret != -EEXIST) {
+					PMD_DEV_LOG_ERR(adapter, DRV,
+							"Failed to add uc rule, ret:%d", ret);
+					ret = -EINVAL;
+					goto l_end;
+				}
+				mac_entry->hw_config = true;
+				ret = 0;
+			}
+		}
+	}
+
+	if (adapter->filter_ctxt.mc_num > 0) {
+		RTE_TAILQ_FOREACH_SAFE(mac_entry, &adapter->filter_ctxt.mc_list, next,
+				       next_mac_entry) {
+			if (!mac_entry->hw_config) {
+				ret = sxe2_drv_mc_config(adapter, &mac_entry->mac_addr, true);
+				if (ret && ret != -EEXIST) {
+					PMD_DEV_LOG_ERR(adapter, DRV,
+							"Failed to add mc rule, ret:%d", ret);
+					ret = -EINVAL;
+					goto l_end;
+				}
+				mac_entry->hw_config = true;
+				ret = 0;
+			}
+		}
+	}
+
+	if (adapter->filter_ctxt.vlan_num > 0) {
+		RTE_TAILQ_FOREACH_SAFE(vlan_entry, &adapter->filter_ctxt.vlan_list, next,
+				       next_vlan_entry) {
+			if (!vlan_entry->hw_config) {
+				ret = sxe2_drv_vlan_filter_id_config(adapter,
+				    &vlan_entry->vlan_info, true);
+				if (ret && ret != -EEXIST) {
+					PMD_DEV_LOG_ERR(adapter, DRV,
+							"Failed to add vlan rule, ret:%d", ret);
+					ret = -EINVAL;
+					goto l_end;
+				}
+				vlan_entry->hw_config = true;
+				ret = 0;
+			}
+		}
+	}
+
+	if (adapter->filter_ctxt.vlan_info.filter_on) {
+		if (!(adapter->filter_ctxt.vlan_info.hw_filter_on)) {
+			ret = sxe2_drv_vlan_filter_switch(adapter, true);
+			if (ret && ret != -EEXIST) {
+				PMD_DEV_LOG_ERR(adapter, DRV,
+						"Failed to add vlan ctrl, ret:%d", ret);
+				ret = -EINVAL;
+				goto l_end;
+			}
+			adapter->filter_ctxt.vlan_info.hw_filter_on = true;
+			ret = 0;
+		}
+	}
+
+	if ((adapter->filter_ctxt.cur_promisc_flags & SXE2_PROMISC) &&
+	    (!(adapter->filter_ctxt.hw_promisc_flags & SXE2_PROMISC))) {
+		ret = sxe2_drv_promisc_config(adapter, true);
+		if (ret && ret != -EEXIST) {
+			PMD_DEV_LOG_ERR(adapter, DRV,
+					"Failed to set promisc, ret:%d", ret);
+			goto l_end;
+		}
+		adapter->filter_ctxt.hw_promisc_flags |= SXE2_PROMISC;
+		ret = 0;
+	}
+
+	if ((adapter->filter_ctxt.cur_promisc_flags & SXE2_PROMISC_MULTICAST) &&
+	    (!(adapter->filter_ctxt.hw_promisc_flags & SXE2_PROMISC_MULTICAST))) {
+		ret = sxe2_drv_allmulti_config(adapter, true);
+		if (ret && ret != -EEXIST) {
+			PMD_DEV_LOG_ERR(adapter, DRV,
+					"Failed to set allmulti, ret:%d", ret);
+			goto l_end;
+		}
+		adapter->filter_ctxt.hw_promisc_flags |= SXE2_PROMISC_MULTICAST;
+		ret = 0;
+	}
+l_end:
+	return ret;
+}
+
+int32_t sxe2_l2_rule_update(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	if (!adapter->flow_isolated && !adapter->switchdev_info.is_switchdev &&
+	    adapter->rule_started) {
+		adapter->filter_ctxt.cur_l2_config = true;
+	} else {
+		adapter->filter_ctxt.cur_l2_config = false;
+	}
+
+	if (adapter->filter_ctxt.cur_l2_config !=
+	    adapter->filter_ctxt.hw_l2_config) {
+		if (adapter->filter_ctxt.cur_l2_config) {
+			ret = sxe2_all_filter_hw_set(adapter);
+			if (!ret)
+				adapter->filter_ctxt.hw_l2_config = true;
+		} else {
+			ret = sxe2_all_filter_hw_clear(adapter);
+			if (!ret)
+				adapter->filter_ctxt.hw_l2_config = false;
+		}
+	}
+	return ret;
+}
+
+int32_t sxe2_filter_rule_stop(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	adapter->rule_started = 0;
+
+	ret = sxe2_l2_rule_update(adapter);
+	if (ret != 0)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to update l2 rule");
+
+	return ret;
+}
+
+int32_t sxe2_filter_rule_start(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	adapter->rule_started = 1;
+
+	ret = sxe2_l2_rule_update(adapter);
+	if (ret != 0)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to update l2 rule");
+
+	return ret;
+}
+
+int32_t sxe2_filter_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	rte_spinlock_init(&adapter->filter_ctxt.filter_lock);
+
+	TAILQ_INIT(&adapter->filter_ctxt.uc_list);
+	adapter->filter_ctxt.uc_num = 0;
+
+	TAILQ_INIT(&adapter->filter_ctxt.mc_list);
+	adapter->filter_ctxt.mc_num = 0;
+
+	TAILQ_INIT(&adapter->filter_ctxt.vlan_list);
+	adapter->filter_ctxt.vlan_num = 0;
+	return 0;
+}
+
+int32_t sxe2_filter_uinit(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	sxe2_uc_filter_clear(adapter, true);
+	adapter->filter_ctxt.uc_num = 0;
+
+	sxe2_mc_filter_clear(adapter, true);
+	adapter->filter_ctxt.mc_num = 0;
+
+	sxe2_vlan_filters_clear(adapter, true);
+	adapter->filter_ctxt.vlan_num = 0;
+	return 0;
+}
diff --git a/drivers/net/sxe2/sxe2_filter.h b/drivers/net/sxe2/sxe2_filter.h
new file mode 100644
index 0000000000..6262e8c845
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_filter.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_FILTER_H__
+#define __SXE2_FILTER_H__
+#include <ethdev_driver.h>
+
+#define SXE2_PROMISC  (1UL << 0UL)
+#define SXE2_PROMISC_MULTICAST  (1UL << 1UL)
+
+struct sxe2_vlan_info {
+	uint8_t port_vlan_exist;
+	uint8_t is_switchdev;
+	uint16_t max_cnt;
+	uint16_t cnt;
+
+	bool filter_on;
+	bool hw_filter_on;
+
+	uint16_t tpid;
+	uint16_t vid;
+
+	uint8_t outer_insert;
+	uint8_t outer_strip;
+	uint8_t inner_insert;
+	uint8_t inner_strip;
+};
+
+struct sxe2_vlan {
+	uint16_t tpid;
+	uint16_t vid;
+	uint8_t prio;
+};
+
+struct sxe2_vlan_filter {
+	TAILQ_ENTRY(sxe2_vlan_filter) next;
+	bool hw_config;
+	bool default_config;
+	struct sxe2_vlan vlan_info;
+};
+
+TAILQ_HEAD(sxe2_vlan_filter_list_head, sxe2_vlan_filter);
+
+struct sxe2_mac_filter {
+	TAILQ_ENTRY(sxe2_mac_filter) next;
+	bool hw_config;
+	bool default_config;
+	struct rte_ether_addr mac_addr;
+};
+
+TAILQ_HEAD(sxe2_uc_filter_list_head, sxe2_mac_filter);
+TAILQ_HEAD(sxe2_mc_filter_list_head, sxe2_mac_filter);
+
+int32_t sxe2_uc_filter_add(struct sxe2_adapter *adapter,
+			struct rte_ether_addr *mac_addr, bool default_config);
+
+int32_t sxe2_uc_filter_del(struct sxe2_adapter *adapter,
+			struct rte_ether_addr *mac_addr);
+
+void sxe2_uc_filter_clear(struct sxe2_adapter *adapter, bool default_config);
+
+int32_t sxe2_mc_filter_add(struct sxe2_adapter *adapter,
+			struct rte_ether_addr *mac_addr, bool default_config);
+
+int32_t sxe2_mc_filter_del(struct sxe2_adapter *adapter,
+			struct rte_ether_addr *mac_addr);
+
+void sxe2_mc_filter_clear(struct sxe2_adapter *adapter, bool default_config);
+
+int32_t sxe2_vlan_filter_add(struct sxe2_adapter *adapter,
+	struct sxe2_vlan *vlan, bool default_config);
+
+int32_t sxe2_vlan_filter_del(struct sxe2_adapter *adapter, struct sxe2_vlan *vlan);
+
+void sxe2_vlan_filters_clear(struct sxe2_adapter *adapter, bool default_config);
+
+int32_t sxe2_vlan_filter_ctrl(struct sxe2_adapter *adapter, bool flag);
+
+int32_t sxe2_promisc_add(struct sxe2_adapter *adapter);
+
+int32_t sxe2_promisc_del(struct sxe2_adapter *adapter);
+
+int32_t sxe2_allmulti_add(struct sxe2_adapter *adapter);
+
+int32_t sxe2_allmulti_del(struct sxe2_adapter *adapter);
+
+int32_t sxe2_l2_rule_update(struct sxe2_adapter *adapter);
+
+int32_t sxe2_filter_rule_stop(struct rte_eth_dev *dev);
+
+int32_t sxe2_filter_rule_start(struct rte_eth_dev *dev);
+
+int32_t sxe2_filter_init(struct rte_eth_dev *dev);
+
+int32_t sxe2_filter_uinit(struct rte_eth_dev *dev);
+
+#endif /* __SXE2_FILTER_H__ */
diff --git a/drivers/net/sxe2/sxe2_mac.c b/drivers/net/sxe2/sxe2_mac.c
index 3c2f909002..d94936a742 100644
--- a/drivers/net/sxe2/sxe2_mac.c
+++ b/drivers/net/sxe2/sxe2_mac.c
@@ -10,6 +10,438 @@
 #include "sxe2_cmd_chnl.h"
 #include "sxe2_host_regs.h"
 
+int32_t sxe2_mac_default_cfg(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t                     ret;
+	struct rte_ether_addr broadcast = {
+		.addr_bytes = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff} };
+	struct rte_ether_addr mac_addr;
+
+	rte_ether_addr_copy((struct rte_ether_addr *)
+		adapter->dev_info.mac.perm_addr, &mac_addr);
+	ret = sxe2_uc_filter_add(adapter, &mac_addr, true);
+	if (ret != 0) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add default MAC filter");
+		goto l_end;
+	}
+
+	rte_ether_addr_copy(&broadcast, &mac_addr);
+	ret = sxe2_mc_filter_add(adapter, &mac_addr, true);
+	if (ret != 0) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add broadcast MAC filter");
+		goto l_end;
+	}
+
+	ret = 0;
+l_end:
+	return ret;
+}
+
+int32_t sxe2_mac_addr_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret                         = -1;
+	PMD_INIT_FUNC_TRACE();
+
+	if (!rte_is_unicast_ether_addr
+		((struct rte_ether_addr *)adapter->dev_info.mac.perm_addr)) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Invalid MAC address");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	dev->data->mac_addrs = rte_zmalloc("sxe2_mac_adds",
+					sizeof(struct rte_ether_addr) * SXE2_NUM_MACADDR_MAX, 0);
+	if (!dev->data->mac_addrs) {
+		PMD_LOG_ERR(DRV, "Failed to allocate memory to store mac address");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+
+	rte_ether_addr_copy((struct rte_ether_addr *)adapter->dev_info.mac.perm_addr,
+		&dev->data->mac_addrs[0]);
+
+	ret = 0;
+
+l_end:
+	return ret;
+}
+
+void sxe2_mac_addr_uinit(struct rte_eth_dev *dev)
+{
+	PMD_INIT_FUNC_TRACE();
+	if (dev != NULL && dev->data->mac_addrs != NULL) {
+		rte_free(dev->data->mac_addrs);
+		dev->data->mac_addrs = NULL;
+	}
+}
+
+int32_t sxe2_mac_addr_add(struct rte_eth_dev *dev, struct rte_ether_addr *mac_addr,
+		      __rte_unused uint32_t index, __rte_unused uint32_t pool)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = -1;
+
+	if (rte_is_zero_ether_addr(mac_addr)) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Invalid MAC Address");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (rte_is_multicast_ether_addr(mac_addr))
+		ret = sxe2_mc_filter_add(adapter, mac_addr, true);
+	else
+		ret = sxe2_uc_filter_add(adapter, mac_addr, false);
+
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add MAC filter");
+
+l_end:
+	return ret;
+}
+
+void sxe2_mac_addr_del(struct rte_eth_dev *dev,  uint32_t index)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct rte_ether_addr *mac_addr = &dev->data->mac_addrs[index];
+	int32_t ret = -1;
+
+	if (rte_is_multicast_ether_addr(mac_addr))
+		ret = sxe2_mc_filter_del(adapter, mac_addr);
+	else
+		ret = sxe2_uc_filter_del(adapter, mac_addr);
+
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to remove MAC filter");
+}
+
+int32_t sxe2_mac_addr_set(struct rte_eth_dev *dev, struct rte_ether_addr *mac_addr)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	struct rte_ether_addr *old_addr = (struct rte_ether_addr *)&adapter->dev_info.mac.perm_addr;
+	struct rte_ether_addr temp_addr;
+
+	if (rte_is_same_ether_addr(old_addr, mac_addr))
+		goto l_end;
+
+	if (rte_is_multicast_ether_addr(mac_addr)) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to set multicast addr");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	ret = sxe2_uc_filter_del(adapter, old_addr);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to remove MAC filter");
+		goto l_end;
+	}
+
+	rte_ether_addr_copy(old_addr, &temp_addr);
+
+	rte_ether_addr_copy(mac_addr, old_addr);
+
+	ret = sxe2_uc_filter_add(adapter, mac_addr, true);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add MAC filter");
+		rte_ether_addr_copy(&temp_addr, old_addr);
+		(void)sxe2_uc_filter_add(adapter, old_addr, true);
+		goto l_end;
+	}
+l_end:
+	return ret;
+}
+
+int32_t sxe2_set_mc_addr_list(struct rte_eth_dev *dev,
+			struct rte_ether_addr *mc_addrs,
+			uint32_t mc_addrs_num)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	uint32_t i;
+	const uint8_t *mac;
+
+	if (mc_addrs_num > SXE2_NUM_MACADDR_MAX) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Too many multicast MAC addresses, ");
+		ret =  -1;
+		goto l_end;
+	}
+
+	sxe2_mc_filter_clear(adapter, false);
+
+	for (i = 0; i < mc_addrs_num; i++) {
+		if (!rte_is_multicast_ether_addr(&mc_addrs[i])) {
+			mac = mc_addrs[i].addr_bytes;
+			PMD_DEV_LOG_ERR(adapter, DRV,
+					"Invalid mac: %02x:%02x:%02x:%02x:%02x:%02x",
+					mac[0], mac[1], mac[2], mac[3], mac[4],
+					mac[5]);
+			ret = -EINVAL;
+			goto add_err;
+		}
+
+		ret = sxe2_mc_filter_add(adapter, &mc_addrs[i], false);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV,
+			    "Failed to remove old multicast MAC filter list");
+			goto add_err;
+		}
+	}
+	goto l_end;
+add_err:
+	sxe2_mc_filter_clear(adapter, false);
+l_end:
+	return ret;
+}
+
+int32_t sxe2_dev_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int32_t on)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_vlan vlan = {
+		.tpid = RTE_ETHER_TYPE_VLAN,
+		.vid = vlan_id,
+		.prio = 0
+	};
+	int32_t ret = 0;
+
+	if (sxe2_dev_port_vlan_check(dev)) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Filter not supported with Port VLAN");
+		ret = -ENOTSUP;
+		goto l_end;
+	}
+
+	if (vlan_id == 0)
+		goto l_end;
+
+	if (on) {
+		ret = sxe2_vlan_filter_add(adapter, &vlan, false);
+		if (ret < 0) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add vlan filter");
+			goto l_end;
+		}
+	} else {
+		ret = sxe2_vlan_filter_del(adapter, &vlan);
+		if (ret < 0) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to remove vlan filter");
+			goto l_end;
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_dev_vlan_offload_set(struct rte_eth_dev *dev, int32_t mask)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	struct rte_eth_rxmode *rxmode = &dev->data->dev_conf.rxmode;
+	struct rte_eth_txmode *txmode = &dev->data->dev_conf.txmode;
+	struct sxe2_vlan_info new_info = adapter->filter_ctxt.vlan_info;
+	bool port_vlan = new_info.port_vlan_exist;
+
+	uint8_t out_strip_mask = SXE2_DPDK_OFFLOAD_OUTER_STRIP_8021Q |
+			    SXE2_DPDK_OFFLOAD_OUTER_STRIP_8021AD |
+			    SXE2_DPDK_OFFLOAD_OUTER_STRIP_QINQ1;
+
+	if (txmode->offloads & RTE_ETH_TX_OFFLOAD_QINQ_INSERT) {
+		if (!(txmode->offloads & RTE_ETH_TX_OFFLOAD_VLAN_INSERT)) {
+			PMD_DEV_LOG_ERR(adapter, DRV,
+			    "VLAN INSERT must be enabled when QinQ INSERT is enabled");
+			return -EINVAL;
+		}
+		if (port_vlan) {
+			PMD_DEV_LOG_ERR(adapter, DRV,
+					"QINQ INSERT not supported with Port VLAN");
+			return -EINVAL;
+		}
+	}
+
+	if (mask & RTE_ETH_QINQ_STRIP_MASK) {
+		if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_QINQ_STRIP) {
+			if (port_vlan) {
+				PMD_DEV_LOG_ERR(adapter, DRV,
+						"QinQ strip not supported with Port VLAN");
+				return -EINVAL;
+			}
+			new_info.inner_strip = SXE2_VSI_TSR_ID_VLAN;
+		} else {
+			new_info.inner_strip = 0;
+		}
+	}
+
+	if (mask & RTE_ETH_VLAN_STRIP_MASK) {
+		if (rxmode->offloads & RTE_ETH_RX_OFFLOAD_VLAN_STRIP) {
+			new_info.outer_strip =
+				port_vlan ? 0 : out_strip_mask;
+			new_info.inner_strip =
+				port_vlan ? new_info.inner_strip : new_info.inner_strip;
+		} else {
+			if (new_info.inner_strip != 0) {
+				PMD_DEV_LOG_ERR(adapter, DRV,
+					"Must disable QinQ strip before disabling VLAN strip");
+				return -EINVAL;
+			}
+			new_info.outer_strip = 0;
+		}
+	}
+
+	if (mask & (RTE_ETH_VLAN_STRIP_MASK | RTE_ETH_QINQ_STRIP_MASK)) {
+		struct sxe2_vlan_info old_info = adapter->filter_ctxt.vlan_info;
+		adapter->filter_ctxt.vlan_info = new_info;
+
+		ret = sxe2_drv_vlan_insert_strip_cfg(adapter);
+		if (ret) {
+			adapter->filter_ctxt.vlan_info = old_info;
+			return ret;
+		}
+	}
+	if (mask & RTE_ETH_VLAN_FILTER_MASK) {
+		if (adapter->filter_ctxt.vlan_info.port_vlan_exist) {
+			ret = 0;
+			PMD_DEV_LOG_INFO(adapter, INIT, "vlan filter is not support when port vlan is enabled");
+			goto l_end;
+		}
+
+		ret = sxe2_vlan_filter_ctrl(adapter,
+			    !!(rxmode->offloads & RTE_ETH_RX_OFFLOAD_VLAN_FILTER));
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV,
+			    "sxe2_drv_vlan_filter_switch failed ret:%d", ret);
+			goto l_end;
+		}
+	}
+
+	PMD_DEV_LOG_DEBUG(adapter, DRV,
+	    "mask:0x%x rx mode offload:0x%" PRIx64 " vlan offload set done",
+	    mask, rxmode->offloads);
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_vlan_filter_zero(struct sxe2_adapter *adapter)
+{
+	struct sxe2_vlan vlan;
+	int32_t ret;
+	uint16_t tpids[] = {RTE_ETHER_TYPE_VLAN, RTE_ETHER_TYPE_QINQ, RTE_ETHER_TYPE_QINQ1};
+	uint8_t i;
+
+	vlan = (struct sxe2_vlan){0, 0, 0};
+	ret = sxe2_vlan_filter_add(adapter, &vlan, true);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add VLAN ID 0");
+		goto l_end;
+	}
+
+	for (i = 0; i < RTE_DIM(tpids); i++) {
+		vlan = (struct sxe2_vlan){tpids[i], 0, 0};
+		ret = sxe2_vlan_filter_add(adapter, &vlan, true);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add VLAN ID 0 when tpid:0x%x",
+					tpids[i]);
+			goto l_end;
+		}
+	}
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_vlan_cfg_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	ret = sxe2_drv_vlan_config_query(adapter);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to query vlan config, ret=%d", ret);
+		goto l_end;
+	}
+
+	if (!sxe2_dev_port_vlan_check(dev))
+		adapter->filter_ctxt.vlan_info.outer_insert =
+			SXE2_DPDK_OFFLOAD_OUTER_INSERT_8021Q |
+			SXE2_DPDK_OFFLOAD_INSERT_ENABLE;
+	else
+		adapter->filter_ctxt.vlan_info.outer_insert = 0;
+
+	adapter->filter_ctxt.vlan_info.inner_insert =
+			SXE2_DPDK_OFFLOAD_INNER_INSERT_QINQ1 | SXE2_DPDK_OFFLOAD_INSERT_ENABLE;
+
+	if (!sxe2_dev_port_vlan_check(dev)) {
+		ret = sxe2_vlan_filter_zero(adapter);
+		if (ret != 0)
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to add vlan filter switch:0 "
+					"for port:%d", adapter->port_idx);
+	}
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_vlan_default_cfg(struct rte_eth_dev *dev)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	ret = sxe2_dev_vlan_offload_set(dev, RTE_ETH_VLAN_STRIP_MASK |
+					RTE_ETH_QINQ_STRIP_MASK |
+					RTE_ETH_VLAN_FILTER_MASK);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to cfg vlan offload, ret:%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_promisc_enable(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	ret = sxe2_promisc_add(adapter);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to enable promisc, ret:%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_promisc_disable(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	ret = sxe2_promisc_del(adapter);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to disable promisc, ret:%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_allmulti_enable(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	ret = sxe2_allmulti_add(adapter);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to enable allmulti, ret:%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_allmulti_disable(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	ret = sxe2_allmulti_del(adapter);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to disable allmulti, ret:%d", ret);
+
+	return ret;
+}
+
 int32_t sxe2_link_update_init(struct rte_eth_dev *dev)
 {
 	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
diff --git a/drivers/net/sxe2/sxe2_mac.h b/drivers/net/sxe2/sxe2_mac.h
index f2f3edaeff..55fd1829a0 100644
--- a/drivers/net/sxe2/sxe2_mac.h
+++ b/drivers/net/sxe2/sxe2_mac.h
@@ -43,6 +43,40 @@ struct sxe2_mac_mc_list {
 
 int32_t sxe2_link_update_init(struct rte_eth_dev *dev);
 
+int32_t sxe2_mac_default_cfg(struct rte_eth_dev *dev);
+
+int32_t sxe2_vlan_cfg_init(struct rte_eth_dev *dev);
+
+int32_t sxe2_mac_addr_init(struct rte_eth_dev *dev);
+
+void sxe2_mac_addr_uinit(struct rte_eth_dev *dev);
+
+int32_t sxe2_mac_addr_add(struct rte_eth_dev *dev,
+			struct rte_ether_addr *mac_addr,
+			__rte_unused uint32_t index, __rte_unused uint32_t pool);
+
+void sxe2_mac_addr_del(struct rte_eth_dev *dev,  uint32_t index);
+
+int32_t sxe2_mac_addr_set(struct rte_eth_dev *dev, struct rte_ether_addr *mac_addr);
+
+int32_t sxe2_set_mc_addr_list(struct rte_eth_dev *dev,
+	struct rte_ether_addr *mc_addrs,
+	uint32_t mc_addrs_num);
+
+int32_t sxe2_promisc_enable(struct rte_eth_dev *dev);
+
+int32_t sxe2_promisc_disable(struct rte_eth_dev *dev);
+
+int32_t sxe2_allmulti_enable(struct rte_eth_dev *dev);
+
+int32_t sxe2_allmulti_disable(struct rte_eth_dev *dev);
+
+int32_t sxe2_dev_vlan_filter_set(struct rte_eth_dev *dev, uint16_t vlan_id, int32_t on);
+
+int32_t sxe2_dev_vlan_offload_set(struct rte_eth_dev *dev, int32_t mask);
+
+int32_t sxe2_vlan_default_cfg(struct rte_eth_dev *dev);
+
 int32_t sxe2_link_update(struct rte_eth_dev *dev, __rte_unused int32_t wait_to_complete);
 
 int32_t sxe2_mtu_set(struct rte_eth_dev *dev, uint16_t mtu);
diff --git a/drivers/net/sxe2/sxe2_txrx_poll.c b/drivers/net/sxe2/sxe2_txrx_poll.c
index b9d34afb31..21d5c38725 100644
--- a/drivers/net/sxe2/sxe2_txrx_poll.c
+++ b/drivers/net/sxe2/sxe2_txrx_poll.c
@@ -660,6 +660,53 @@ sxe2_rx_desc_error_para(__rte_unused struct sxe2_rx_queue *rxq,
 	return flags;
 }
 
+static inline void sxe2_rx_desc_vlan_para_fill(struct rte_mbuf *mbuf,
+			union sxe2_rx_desc *desc)
+{
+	if (0 == (rte_le_to_cpu_64(desc->wb.status_err_ptype_len) &
+		  SXE2_RX_DESC_STATUS_L2TAG1_P_MASK)) {
+		mbuf->vlan_tci = 0;
+	} else {
+		mbuf->ol_flags |= (RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED);
+		mbuf->vlan_tci = rte_le_to_cpu_16(desc->wb.l2tag1);
+		PMD_LOG_DEBUG(RX, "Rx desc mbuf vlan, vlan_tci:%u",
+			mbuf->vlan_tci);
+	}
+#ifndef RTE_LIBRTE_SXE2_16BYTE_RX_DESC
+	if (0 == (rte_le_to_cpu_32(desc->wb.status_lrocnt_fdpf_id) &
+				SXE2_RX_DESC_EXT_STATUS_L2TAG2P_MASK)) {
+		mbuf->vlan_tci_outer = 0;
+	} else {
+		mbuf->ol_flags |= RTE_MBUF_F_RX_QINQ_STRIPPED | RTE_MBUF_F_RX_QINQ |
+				RTE_MBUF_F_RX_VLAN_STRIPPED | RTE_MBUF_F_RX_VLAN;
+		mbuf->vlan_tci_outer = mbuf->vlan_tci;
+		mbuf->vlan_tci = rte_le_to_cpu_16(desc->wb.l2tag2_2nd);
+		PMD_LOG_DEBUG(RX, "Rx desc out vlan, l2tag2_1st:%u l2tag2_2nd:%u.",
+				rte_le_to_cpu_16(desc->wb.l2tag2_1st),
+				rte_le_to_cpu_16(desc->wb.l2tag2_2nd));
+	}
+#endif
+}
+
+static inline void
+sxe2_rx_desc_filter_para_fill(struct sxe2_rx_queue *rxq __rte_unused,
+		struct rte_mbuf *mbuf, union sxe2_rx_desc *desc)
+{
+	if (SXE2_RX_DESC_STATUS_RSS_VLD_MASK &
+				rte_le_to_cpu_64(desc->wb.status_err_ptype_len)) {
+		mbuf->ol_flags |= RTE_MBUF_F_RX_RSS_HASH;
+		mbuf->hash.rss = rte_le_to_cpu_32(desc->wb.filter_status);
+		PMD_LOG_DEBUG(RX, "rss id:%u", mbuf->hash.rss);
+	}
+#ifndef RTE_LIBRTE_SXE2_16BYTE_RX_DESC
+	if (SXE2_RX_DESC_FD_VLD_MASK & desc->wb.rxdid_src) {
+		mbuf->ol_flags |= (RTE_MBUF_F_RX_FDIR | RTE_MBUF_F_RX_FDIR_ID);
+		mbuf->hash.fdir.hi = rte_le_to_cpu_32(desc->wb.fd_filter_id);
+		PMD_LOG_DEBUG(RX, "fdir id:%u", mbuf->hash.fdir.hi);
+	}
+#endif
+}
+
 static __rte_always_inline void
 sxe2_rx_mbuf_common_fields_fill(struct sxe2_rx_queue *rxq, struct rte_mbuf *mbuf,
 		union sxe2_rx_desc *rxd)
@@ -673,6 +720,8 @@ sxe2_rx_mbuf_common_fields_fill(struct sxe2_rx_queue *rxq, struct rte_mbuf *mbuf
 	mbuf->packet_type = ptype_tbl[SXE2_RX_DESC_PTYPE_VAL_GET(qword1)];
 
 	pkt_flags = sxe2_rx_desc_error_para(rxq, rxd);
+	sxe2_rx_desc_vlan_para_fill(mbuf, rxd);
+	sxe2_rx_desc_filter_para_fill(rxq, mbuf, rxd);
 
 	mbuf->ol_flags |= pkt_flags;
 }
-- 
2.52.0


^ permalink raw reply related

* [PATCH v10 08/20] net/sxe2: support statistics and multi-process
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260606010726.2256170-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

- The statistics support includes:
- Basic statistics (ipackets, opackets, ibytes, obytes, etc.)
- Extended statistics (xstats) for detailed hardware counters.
- Per-queue statistics for both RX and TX.

The multi-process support allows secondary processes to retrieve
statistics. Since secondary processes cannot access hardware registers
directly, an IPC mechanism is implemented using the DPDK MP API.

Atomic operations are used when reading 64-bit counters to ensure
data consistency between processes.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/meson.build     |   2 +
 drivers/net/sxe2/sxe2_cmd_chnl.c | 340 +++++++++++++++++-
 drivers/net/sxe2/sxe2_cmd_chnl.h |  23 ++
 drivers/net/sxe2/sxe2_drv_cmd.h  | 120 +++++++
 drivers/net/sxe2/sxe2_ethdev.c   |  38 +-
 drivers/net/sxe2/sxe2_mp.c       | 414 ++++++++++++++++++++++
 drivers/net/sxe2/sxe2_mp.h       |  67 ++++
 drivers/net/sxe2/sxe2_stats.c    | 586 +++++++++++++++++++++++++++++++
 drivers/net/sxe2/sxe2_stats.h    |  39 ++
 9 files changed, 1616 insertions(+), 13 deletions(-)
 create mode 100644 drivers/net/sxe2/sxe2_mp.c
 create mode 100644 drivers/net/sxe2/sxe2_mp.h
 create mode 100644 drivers/net/sxe2/sxe2_stats.c
 create mode 100644 drivers/net/sxe2/sxe2_stats.h

diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index 86973edc99..8c8f16863e 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -66,4 +66,6 @@ sources += files(
         'sxe2_tm.c',
         'sxe2_ipsec.c',
         'sxe2_security.c',
+        'sxe2_mp.c',
+        'sxe2_stats.c',
 )
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.c b/drivers/net/sxe2/sxe2_cmd_chnl.c
index 7711e8e57d..a1fc8a50e3 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.c
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.c
@@ -348,6 +348,184 @@ int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter)
 	return ret;
 }
 
+int32_t sxe2_drv_get_mac_stats(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	uint8_t i = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_vsi *vsi = adapter->vsi_ctxt.main_vsi;
+	struct sxe2_stats *stats = &vsi->vsi_stats.vsi_hw_stats;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_drv_mac_stats_resp resp = {0};
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_MAC_STATS_GET,
+				 NULL, 0,
+				 &resp, sizeof(resp));
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "vsi stats get failed, ret=%d", ret);
+		goto l_end;
+	}
+
+	stats->rx_out_of_buffer        = rte_le_to_cpu_64(resp.rx_out_of_buffer);
+	stats->rx_qblock_drop          = rte_le_to_cpu_64(resp.rx_qblock_drop);
+	stats->tx_frame_good           = rte_le_to_cpu_64(resp.tx_frame_good);
+	stats->rx_frame_good           = rte_le_to_cpu_64(resp.rx_frame_good);
+	stats->rx_crc_errors           = rte_le_to_cpu_64(resp.rx_crc_errors);
+	stats->tx_bytes_good           = rte_le_to_cpu_64(resp.tx_bytes_good);
+	stats->rx_bytes_good           = rte_le_to_cpu_64(resp.rx_bytes_good);
+	stats->tx_multicast_good       = rte_le_to_cpu_64(resp.tx_multicast_good);
+	stats->tx_broadcast_good       = rte_le_to_cpu_64(resp.tx_broadcast_good);
+	stats->rx_multicast_good       = rte_le_to_cpu_64(resp.rx_multicast_good);
+	stats->rx_broadcast_good       = rte_le_to_cpu_64(resp.rx_broadcast_good);
+	stats->rx_len_errors           = rte_le_to_cpu_64(resp.rx_len_errors);
+	stats->rx_out_of_range_errors  = rte_le_to_cpu_64(resp.rx_out_of_range_errors);
+	stats->rx_oversize_pkts_phy    = rte_le_to_cpu_64(resp.rx_oversize_pkts_phy);
+	stats->rx_symbol_err	       = rte_le_to_cpu_64(resp.rx_symbol_err);
+	stats->rx_pause_frame	       = rte_le_to_cpu_64(resp.rx_pause_frame);
+	stats->tx_pause_frame	       = rte_le_to_cpu_64(resp.tx_pause_frame);
+	stats->rx_discards_phy	       = rte_le_to_cpu_64(resp.rx_discards_phy);
+	stats->rx_discards_ips_phy     = rte_le_to_cpu_64(resp.rx_discards_ips_phy);
+	stats->tx_dropped_link_down    = rte_le_to_cpu_64(resp.tx_dropped_link_down);
+	stats->rx_undersize_good       = rte_le_to_cpu_64(resp.rx_undersize_good);
+	stats->rx_runt_error	       = rte_le_to_cpu_64(resp.rx_runt_error);
+	stats->tx_bytes_good_bad       = rte_le_to_cpu_64(resp.tx_bytes_good_bad);
+	stats->tx_frame_good_bad       = rte_le_to_cpu_64(resp.tx_frame_good_bad);
+	stats->rx_jabbers              = rte_le_to_cpu_64(resp.rx_jabbers);
+	stats->rx_size_64              = rte_le_to_cpu_64(resp.rx_size_64);
+	stats->rx_size_65_127          = rte_le_to_cpu_64(resp.rx_size_65_127);
+	stats->rx_size_128_255         = rte_le_to_cpu_64(resp.rx_size_128_255);
+	stats->rx_size_256_511         = rte_le_to_cpu_64(resp.rx_size_256_511);
+	stats->rx_size_512_1023        = rte_le_to_cpu_64(resp.rx_size_512_1023);
+	stats->rx_size_1024_1522       = rte_le_to_cpu_64(resp.rx_size_1024_1522);
+	stats->rx_size_1523_max        = rte_le_to_cpu_64(resp.rx_size_1523_max);
+	stats->rx_pcs_symbol_err_phy   = rte_le_to_cpu_64(resp.rx_pcs_symbol_err_phy);
+	stats->rx_corrected_bits_phy   = rte_le_to_cpu_64(resp.rx_corrected_bits_phy);
+	stats->rx_err_lane_0_phy       = rte_le_to_cpu_64(resp.rx_err_lane_0_phy);
+	stats->rx_err_lane_1_phy       = rte_le_to_cpu_64(resp.rx_err_lane_1_phy);
+	stats->rx_err_lane_2_phy       = rte_le_to_cpu_64(resp.rx_err_lane_2_phy);
+	stats->rx_err_lane_3_phy       = rte_le_to_cpu_64(resp.rx_err_lane_3_phy);
+	stats->rx_illegal_bytes        = rte_le_to_cpu_64(resp.rx_illegal_bytes);
+	stats->rx_oversize_good        = rte_le_to_cpu_64(resp.rx_oversize_good);
+	stats->tx_unicast              = rte_le_to_cpu_64(resp.tx_unicast);
+	stats->tx_broadcast            = rte_le_to_cpu_64(resp.tx_broadcast);
+	stats->tx_multicast            = rte_le_to_cpu_64(resp.tx_multicast);
+	stats->tx_vlan_packet_good     = rte_le_to_cpu_64(resp.tx_vlan_packet_good);
+	stats->tx_size_64              = rte_le_to_cpu_64(resp.tx_size_64);
+	stats->tx_size_65_127          = rte_le_to_cpu_64(resp.tx_size_65_127);
+	stats->tx_size_128_255         = rte_le_to_cpu_64(resp.tx_size_128_255);
+	stats->tx_size_256_511         = rte_le_to_cpu_64(resp.tx_size_256_511);
+	stats->tx_size_512_1023        = rte_le_to_cpu_64(resp.tx_size_512_1023);
+	stats->tx_size_1024_1522       = rte_le_to_cpu_64(resp.tx_size_1024_1522);
+	stats->tx_size_1523_max        = rte_le_to_cpu_64(resp.tx_size_1523_max);
+	stats->tx_underflow_error      = rte_le_to_cpu_64(resp.tx_underflow_error);
+	stats->rx_byte_good_bad        = rte_le_to_cpu_64(resp.rx_byte_good_bad);
+	stats->rx_frame_good_bad       = rte_le_to_cpu_64(resp.rx_frame_good_bad);
+	stats->rx_unicast_good         = rte_le_to_cpu_64(resp.rx_unicast_good);
+	stats->rx_vlan_packets         = rte_le_to_cpu_64(resp.rx_vlan_packets);
+
+	for (i = 0; i < SXE2_MAX_USER_PRIORITY; i++) {
+		stats->rx_prio_buf_discard[i]  =
+			rte_le_to_cpu_64(resp.rx_prio_buf_discard[i]);
+		stats->prio_xoff_rx[i]  =
+			rte_le_to_cpu_64(resp.prio_xoff_rx[i]);
+		stats->prio_xoff_tx[i]  =
+			rte_le_to_cpu_64(resp.prio_xoff_tx[i]);
+		stats->prio_xon_rx[i]  =
+			rte_le_to_cpu_64(resp.prio_xon_rx[i]);
+		stats->prio_xon_tx[i]  =
+			rte_le_to_cpu_64(resp.prio_xon_tx[i]);
+		stats->prio_xon_2_xoff[i]  =
+			rte_le_to_cpu_64(resp.prio_xon_2_xoff[i]);
+	}
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_mac_stats_reset(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_MAC_STATS_CLEAR,
+				 NULL, 0,
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "mac stats reset failed, ret=%d", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_get_vsi_stats(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_vsi *vsi = adapter->vsi_ctxt.main_vsi;
+	struct sxe2_stats *new_stats = &vsi->vsi_stats.vsi_hw_stats;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_drv_vsi_stats_req req = {0};
+	struct sxe2_drv_vsi_stats_resp resp = {0};
+
+	req.vsi_id = rte_cpu_to_le_16(vsi->vsi_id);
+	req.sw_stats.rx_bytes = rte_cpu_to_le_64(vsi->vsi_stats.stats.ibytes);
+	req.sw_stats.rx_packets = rte_cpu_to_le_64(vsi->vsi_stats.stats.ipackets);
+	req.sw_stats.tx_bytes = rte_cpu_to_le_64(vsi->vsi_stats.stats.obytes);
+	req.sw_stats.tx_packets = rte_cpu_to_le_64(vsi->vsi_stats.stats.opackets);
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_VSI_STATS_GET,
+				 &req, sizeof(req),
+				 &resp, sizeof(resp));
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "vsi stats get failed, ret=%d", ret);
+		goto l_end;
+	}
+
+	new_stats->rx_vsi_unicast_packets   = rte_le_to_cpu_64(resp.rx_vsi_unicast_packets);
+	new_stats->rx_vsi_bytes             = rte_le_to_cpu_64(resp.rx_vsi_bytes);
+	new_stats->tx_vsi_unicast_packets   = rte_le_to_cpu_64(resp.tx_vsi_unicast_packets);
+	new_stats->tx_vsi_bytes             = rte_le_to_cpu_64(resp.tx_vsi_bytes);
+	new_stats->rx_vsi_multicast_packets = rte_le_to_cpu_64(resp.rx_vsi_multicast_packets);
+	new_stats->tx_vsi_multicast_packets = rte_le_to_cpu_64(resp.tx_vsi_multicast_packets);
+	new_stats->rx_vsi_broadcast_packets = rte_le_to_cpu_64(resp.rx_vsi_broadcast_packets);
+	new_stats->tx_vsi_broadcast_packets = rte_le_to_cpu_64(resp.tx_vsi_broadcast_packets);
+	new_stats->opackets = new_stats->tx_vsi_unicast_packets +
+			      new_stats->tx_vsi_multicast_packets +
+			      new_stats->tx_vsi_broadcast_packets;
+	new_stats->obytes = new_stats->tx_vsi_bytes;
+	new_stats->ipackets = new_stats->rx_vsi_unicast_packets +
+			      new_stats->rx_vsi_multicast_packets +
+			      new_stats->rx_vsi_broadcast_packets;
+	new_stats->ibytes = new_stats->rx_vsi_bytes;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_vsi_stats_reset(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_VSI_STATS_CLEAR,
+				 NULL, 0, NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "vsi stats reset failed, ret=%d", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
 int32_t sxe2_drv_promisc_config(struct sxe2_adapter *adapter, bool set)
 {
 	int32_t ret = 0;
@@ -409,8 +587,9 @@ int32_t sxe2_drv_uc_config(struct sxe2_adapter *adapter, struct rte_ether_addr *
 	mac_filter_cfg_req.type = SXE2_MAC_FILTER_TYPE_UC;
 
 	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_MAC_ADDR_UC,
-		 &mac_filter_cfg_req, sizeof(mac_filter_cfg_req),
-		 NULL, 0);
+				 &mac_filter_cfg_req,
+				 sizeof(mac_filter_cfg_req),
+				 NULL, 0);
 
 	ret = sxe2_drv_cmd_exec(cdev, &param);
 	if (ret)
@@ -435,8 +614,8 @@ int32_t sxe2_drv_mc_config(struct sxe2_adapter *adapter, struct rte_ether_addr *
 	mac_filter_cfg_req.type = SXE2_MAC_FILTER_TYPE_MC;
 
 	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_MAC_ADDR_MC,
-		 &mac_filter_cfg_req, sizeof(mac_filter_cfg_req),
-		 NULL, 0);
+				  &mac_filter_cfg_req, sizeof(mac_filter_cfg_req),
+				 NULL, 0);
 
 	ret = sxe2_drv_cmd_exec(cdev, &param);
 	if (ret)
@@ -455,7 +634,7 @@ int32_t sxe2_drv_vlan_config_query(struct sxe2_adapter *adapter)
 	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_VLAN_CFG_QUERY,
 				 NULL, 0,
 				 &vlan_cfg_query_resp,
-	 sizeof(vlan_cfg_query_resp));
+				 sizeof(vlan_cfg_query_resp));
 
 	ret = sxe2_drv_cmd_exec(cdev, &param);
 	if (ret)
@@ -566,7 +745,8 @@ int32_t sxe2_drv_rss_key_set(struct sxe2_adapter *adapter, uint8_t *key, uint16_
 	rte_memcpy(req->key, key, key_size);
 
 	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RSS_KEY_SET,
-		req, buf_size, NULL, 0);
+				 req, buf_size,
+				 NULL, 0);
 
 	ret = sxe2_drv_cmd_exec(cdev, &param);
 	if (ret) {
@@ -602,7 +782,8 @@ int32_t sxe2_drv_rss_lut_set(struct sxe2_adapter *adapter, uint8_t *lut, uint16_
 	rte_memcpy(req->lut, lut, lut_size);
 
 	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RSS_LUT_SET,
-		req, buf_size, NULL, 0);
+				 req, buf_size,
+				 NULL, 0);
 
 	ret = sxe2_drv_cmd_exec(cdev, &param);
 	if (ret) {
@@ -629,7 +810,8 @@ int32_t sxe2_drv_rss_hash_ctrl_func(struct sxe2_adapter *adapter, enum sxe2_rss_
 	req.func = func;
 
 	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RSS_FUNC_SET,
-		&req, sizeof(req), NULL, 0);
+				 &req, sizeof(req),
+				 NULL, 0);
 
 	ret = sxe2_drv_cmd_exec(cdev, &param);
 	if (ret)
@@ -665,7 +847,8 @@ int32_t sxe2_drv_rss_hf_add(struct sxe2_adapter *adapter,
 	sxe2_drv_flow_bitmap_fill(req.hash_flds, rss_conf->flds);
 
 	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RSS_HF_ADD,
-		&req, sizeof(req), NULL, 0);
+				 &req, sizeof(req),
+				 NULL, 0);
 
 	ret = sxe2_drv_cmd_exec(cdev, &param);
 	if (ret)
@@ -703,7 +886,8 @@ int32_t sxe2_drv_rss_hf_clear(struct sxe2_adapter *adapter)
 	int32_t ret = 0;
 
 	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RSS_HF_CLEAR,
-		NULL, 0, NULL, 0);
+				 NULL, 0,
+				 NULL, 0);
 
 	ret = sxe2_drv_cmd_exec(cdev, &param);
 	if (ret)
@@ -878,7 +1062,6 @@ int32_t sxe2_drv_tm_commit(struct sxe2_adapter *adapter)
 	return ret;
 }
 
-
 int32_t sxe2_drv_ipsec_get_capa(struct sxe2_adapter *adapter)
 {
 	int32_t ret = -1;
@@ -1074,3 +1257,138 @@ int32_t sxe2_drv_ipsec_txsa_delete(struct sxe2_adapter *adapter,
 	return ret;
 }
 
+int32_t sxe2_drv_queue_info_get_update(struct sxe2_adapter *adapter, struct eth_queue_stats *qstats)
+{
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_queue_map_info resp = {0};
+	struct sxe2_common_device *cdev = adapter->cdev;
+	uint8_t pool_idx;
+	uint8_t index;
+	int32_t ret;
+
+	if (!(adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_Q_MAP)) {
+		ret = 0;
+		goto l_end;
+	}
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_TX_RX_MAP_GET,
+				 NULL, 0,
+				 &resp, sizeof(resp));
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "get queue info map failed, ret=%d", ret);
+		goto l_end;
+	}
+
+	for (pool_idx = 0; pool_idx < SXE2_RXQ_STATS_MAP_MAX_NUM; pool_idx++) {
+		qstats->q_ipackets[pool_idx] = resp.rxq_stats_map_info[pool_idx].rxq_lan_in_pkt_cnt;
+		qstats->q_ibytes[pool_idx] = resp.rxq_stats_map_info[pool_idx].rxq_lan_in_byte_cnt;
+	}
+
+	for (index = 0; index < SXE2_TXQ_STATS_MAP_MAX_NUM; index++) {
+		qstats->q_opackets[index] = resp.txq_stats_map_info[index].txq_lan_pkt_cnt;
+		qstats->q_obytes[index] = resp.txq_stats_map_info[index].txq_lan_byte_cnt;
+	}
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_rxq_mapping_set(struct rte_eth_dev *eth_dev, uint16_t queue_id, uint8_t pool_idx)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(eth_dev);
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_drv_rx_map_req req = {0};
+	struct sxe2_rx_queue *rxq = NULL;
+
+	rxq = eth_dev->data->rx_queues[queue_id];
+	if (rxq == NULL) {
+		PMD_LOG_ERR(DRV, "Rx queue %u is not available or setup",
+				queue_id);
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	req.queue_id = rxq->queue_id;
+	req.pool_idx = pool_idx;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RX_MAP_SET,
+				 &req, sizeof(req),
+				 NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_LOG_ERR(DRV, "get dev caps failed, ret=%d", ret);
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_txq_mapping_set(struct rte_eth_dev *eth_dev, uint16_t queue_id, uint8_t pool_idx)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(eth_dev);
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_drv_tx_map_req req = {0};
+	struct sxe2_tx_queue *txq = NULL;
+
+	txq = eth_dev->data->tx_queues[queue_id];
+	if (txq == NULL) {
+		PMD_LOG_ERR(DRV, "Rx queue %u is not available or setup", queue_id);
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	req.queue_id = txq->queue_id;
+	req.pool_idx = pool_idx;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_TX_MAP_SET,
+				 &req, sizeof(req),
+				 NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_LOG_ERR(DRV, "get dev caps failed, ret=%d", ret);
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_mapping_reset(struct rte_eth_dev *eth_dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(eth_dev);
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_common_device *cdev = adapter->cdev;
+	int32_t ret;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_TX_RX_MAP_RESET,
+				 NULL, 0,
+				 NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_LOG_ERR(DRV, "Reset queue mapping failed, ret=%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_mapping_stats_info_clear(struct rte_eth_dev *eth_dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(eth_dev);
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_common_device *cdev = adapter->cdev;
+	int32_t ret;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_TX_RX_MAP_INFO_CLEAR,
+				 NULL, 0,
+				 NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_LOG_ERR(DRV, "Clear map stats info failed, ret=%d", ret);
+
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.h b/drivers/net/sxe2/sxe2_cmd_chnl.h
index dac487fe7d..80ad10ac00 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.h
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.h
@@ -64,6 +64,29 @@ int32_t sxe2_drv_ipsec_txsa_delete(struct sxe2_adapter *adapter,
 
 int32_t sxe2_drv_promisc_config(struct sxe2_adapter *adapter, bool set);
 
+int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter);
+
+int32_t sxe2_drv_get_mac_stats(struct sxe2_adapter *adapter);
+
+int32_t sxe2_drv_mac_stats_reset(struct sxe2_adapter *adapter);
+
+int32_t sxe2_drv_get_vsi_stats(struct sxe2_adapter *adapter);
+
+int32_t sxe2_drv_vsi_stats_reset(struct sxe2_adapter *adapter);
+
+int32_t sxe2_drv_queue_info_get_update(struct sxe2_adapter *adapter,
+				       struct eth_queue_stats *qstats);
+
+int32_t sxe2_drv_rxq_mapping_set(struct rte_eth_dev *eth_dev, uint16_t queue_id, uint8_t pool_idx);
+
+int32_t sxe2_drv_txq_mapping_set(struct rte_eth_dev *eth_dev, uint16_t queue_id, uint8_t pool_idx);
+
+int32_t sxe2_drv_mapping_reset(struct rte_eth_dev *eth_dev);
+
+int32_t sxe2_drv_mapping_stats_info_clear(struct rte_eth_dev *eth_dev);
+
+int32_t sxe2_drv_rxq_mapping_set(struct rte_eth_dev *eth_dev, uint16_t queue_id, uint8_t pool_idx);
+
 int32_t sxe2_drv_allmulti_config(struct sxe2_adapter *adapter, bool set);
 
 int32_t sxe2_drv_uc_config(struct sxe2_adapter *adapter, struct rte_ether_addr *addr, bool add);
diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h
index 39a108d76a..7935a28dc1 100644
--- a/drivers/net/sxe2/sxe2_drv_cmd.h
+++ b/drivers/net/sxe2/sxe2_drv_cmd.h
@@ -436,6 +436,126 @@ struct __rte_aligned(4) __rte_packed_begin sxe2_drv_ipsec_rxsa_del_req {
 	uint8_t drv_id;
 } __rte_packed_end;
 
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vsi_sw_stats {
+	uint64_t rx_packets;
+	uint64_t rx_bytes;
+	uint64_t tx_packets;
+	uint64_t tx_bytes;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vsi_stats_req {
+	uint16_t vsi_id;
+	uint8_t  rsv[2];
+	struct sxe2_drv_vsi_sw_stats sw_stats;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vsi_stats_resp {
+	uint64_t rx_vsi_unicast_packets;
+	uint64_t rx_vsi_bytes;
+	uint64_t tx_vsi_unicast_packets;
+	uint64_t tx_vsi_bytes;
+	uint64_t rx_vsi_multicast_packets;
+	uint64_t tx_vsi_multicast_packets;
+	uint64_t rx_vsi_broadcast_packets;
+	uint64_t tx_vsi_broadcast_packets;
+} __rte_packed_end;
+
+#define SXE2_MAX_USER_PRIORITY         (8)
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_mac_stats_resp {
+	uint64_t rx_out_of_buffer;
+	uint64_t rx_qblock_drop;
+	uint64_t tx_frame_good;
+	uint64_t rx_frame_good;
+	uint64_t rx_crc_errors;
+	uint64_t tx_bytes_good;
+	uint64_t rx_bytes_good;
+	uint64_t tx_multicast_good;
+	uint64_t tx_broadcast_good;
+	uint64_t rx_multicast_good;
+	uint64_t rx_broadcast_good;
+	uint64_t rx_len_errors;
+	uint64_t rx_out_of_range_errors;
+	uint64_t rx_oversize_pkts_phy;
+	uint64_t rx_symbol_err;
+	uint64_t rx_pause_frame;
+	uint64_t tx_pause_frame;
+	uint64_t rx_discards_phy;
+	uint64_t rx_discards_ips_phy;
+	uint64_t tx_dropped_link_down;
+	uint64_t rx_undersize_good;
+	uint64_t rx_runt_error;
+	uint64_t tx_bytes_good_bad;
+	uint64_t tx_frame_good_bad;
+	uint64_t rx_jabbers;
+	uint64_t rx_size_64;
+	uint64_t rx_size_65_127;
+	uint64_t rx_size_128_255;
+	uint64_t rx_size_256_511;
+	uint64_t rx_size_512_1023;
+	uint64_t rx_size_1024_1522;
+	uint64_t rx_size_1523_max;
+	uint64_t rx_pcs_symbol_err_phy;
+	uint64_t rx_corrected_bits_phy;
+	uint64_t rx_err_lane_0_phy;
+	uint64_t rx_err_lane_1_phy;
+	uint64_t rx_err_lane_2_phy;
+	uint64_t rx_err_lane_3_phy;
+	uint64_t rx_prio_buf_discard[SXE2_MAX_USER_PRIORITY];
+	uint64_t rx_illegal_bytes;
+	uint64_t rx_oversize_good;
+	uint64_t tx_unicast;
+	uint64_t tx_broadcast;
+	uint64_t tx_multicast;
+	uint64_t tx_vlan_packet_good;
+	uint64_t tx_size_64;
+	uint64_t tx_size_65_127;
+	uint64_t tx_size_128_255;
+	uint64_t tx_size_256_511;
+	uint64_t tx_size_512_1023;
+	uint64_t tx_size_1024_1522;
+	uint64_t tx_size_1523_max;
+	uint64_t tx_underflow_error;
+	uint64_t rx_byte_good_bad;
+	uint64_t rx_frame_good_bad;
+	uint64_t rx_unicast_good;
+	uint64_t rx_vlan_packets;
+	uint64_t prio_xoff_rx[SXE2_MAX_USER_PRIORITY];
+	uint64_t prio_xon_rx[SXE2_MAX_USER_PRIORITY];
+	uint64_t prio_xon_tx[SXE2_MAX_USER_PRIORITY];
+	uint64_t prio_xoff_tx[SXE2_MAX_USER_PRIORITY];
+	uint64_t prio_xon_2_xoff[SXE2_MAX_USER_PRIORITY];
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_txq_map_info {
+	uint32_t txq_lan_pkt_cnt;
+	uint32_t txq_lan_byte_cnt;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_rxq_map_info {
+	uint64_t rxq_lan_in_pkt_cnt;
+	uint64_t rxq_lan_in_byte_cnt;
+	uint64_t rxq_fd_in_pkt_cnt;
+	uint64_t rxq_mng_in_pkt_cnt;
+	uint64_t rxq_mng_in_byte_cnt;
+	uint64_t rxq_mng_out_pkt_cnt;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_queue_map_info {
+	struct sxe2_rxq_map_info rxq_stats_map_info[SXE2_RXQ_STATS_MAP_MAX_NUM];
+	struct sxe2_txq_map_info txq_stats_map_info[SXE2_TXQ_STATS_MAP_MAX_NUM];
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_rx_map_req {
+	uint16_t queue_id;
+	uint8_t pool_idx;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_tx_map_req {
+	uint16_t queue_id;
+	uint8_t pool_idx;
+} __rte_packed_end;
+
 enum sxe2_drv_cmd_module {
 	SXE2_DRV_CMD_MODULE_HANDSHAKE = 0,
 	SXE2_DRV_CMD_MODULE_DEV = 1,
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index 00c0552d4a..dbe1a2bce1 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -31,6 +31,8 @@
 #include "sxe2_common.h"
 #include "sxe2_ptype.h"
 #include "sxe2_common_log.h"
+#include "sxe2_mp.h"
+#include "sxe2_stats.h"
 #include "sxe2_host_regs.h"
 #include "sxe2_ioctl_chnl_func.h"
 
@@ -132,6 +134,14 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.rss_hash_conf_get          = sxe2_dev_rss_hash_conf_get,
 
 	.tm_ops_get                 = sxe2_tm_ops_get,
+
+	.stats_get                  = sxe2_stats_info_get,
+	.stats_reset                = sxe2_stats_info_reset,
+	.xstats_get                 = sxe2_xstats_info_get,
+	.xstats_get_names           = sxe2_xstats_names_get,
+	.xstats_reset               = sxe2_stats_info_reset,
+
+	.queue_stats_mapping_set    = sxe2_queue_stats_mapping_set,
 };
 
 static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
@@ -1023,6 +1033,9 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 	if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
 		sxe2_rx_mode_func_set(dev);
 		sxe2_tx_mode_func_set(dev);
+		ret = sxe2_mp_init(dev);
+		if (ret != 0)
+			PMD_LOG_ERR(INIT, "Failed to mp init (secondary), ret=%d", ret);
 		goto l_end;
 	}
 
@@ -1076,12 +1089,27 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 		goto init_sched_err;
 	}
 
+	ret = sxe2_stats_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to stats init, ret=%d", ret);
+		goto init_xstats_err;
+	}
+
+	ret = sxe2_mp_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to mp init, ret=%d", ret);
+		goto init_xstats_err;
+	}
+
 	goto l_end;
 
-init_security_err:
-	sxe2_eth_uinit(dev);
+init_xstats_err:
+	(void)sxe2_sched_uinit(dev);
 init_sched_err:
 init_rss_err:
+	sxe2_security_uinit(dev);
+init_security_err:
+	sxe2_eth_uinit(dev);
 init_eth_err:
 init_dev_info_err:
 	sxe2_vsi_uninit(dev);
@@ -1093,8 +1121,13 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 
 static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
 {
+	if (rte_eal_process_type() != RTE_PROC_PRIMARY) {
+		sxe2_mp_uninit(dev);
+		goto l_end;
+	}
 	(void)sxe2_dev_stop(dev);
 	(void)sxe2_queues_release(dev);
+	sxe2_mp_uninit(dev);
 	(void)sxe2_rss_disable(dev);
 	(void)sxe2_sched_uinit(dev);
 	sxe2_vsi_uninit(dev);
@@ -1102,6 +1135,7 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
 	sxe2_dev_pci_map_uinit(dev);
 	sxe2_eth_uinit(dev);
 
+l_end:
 	return 0;
 }
 
diff --git a/drivers/net/sxe2/sxe2_mp.c b/drivers/net/sxe2/sxe2_mp.c
new file mode 100644
index 0000000000..a4a5c76495
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_mp.c
@@ -0,0 +1,414 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+ #include <stdlib.h>
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <ethdev_driver.h>
+#include <rte_log.h>
+#include <rte_malloc.h>
+#include <rte_memzone.h>
+#include <rte_string_fns.h>
+
+#include "sxe2_mp.h"
+#include "sxe2_stats.h"
+#include "sxe2_common_log.h"
+
+static RTE_ATOMIC(uint16_t)primary_ethdev_cnt;
+static RTE_ATOMIC(uint16_t)secondary_ethdev_cnt;
+static const struct rte_memzone *sxe2_mp_mz;
+
+static int32_t sxe2_mp_acquire_token(void);
+static void sxe2_mp_release_token(void);
+
+static int32_t sxe2_mp_primary_handle(const struct rte_mp_msg *mp_msg,
+				   const void *peer);
+
+static int32_t sxe2_mp_secondary_handle(const struct rte_mp_msg *mp_msg,
+					 const void *peer);
+
+static int32_t
+sxe2_mp_primary_handle(const struct rte_mp_msg *mp_msg, const void *peer)
+{
+	struct rte_mp_msg reply;
+	const struct sxe2_mp_param *param =
+			(const struct sxe2_mp_param *)mp_msg->param;
+	struct sxe2_mp_param *reply_param = (struct sxe2_mp_param *)reply.param;
+	struct rte_eth_dev *dev;
+	int32_t ret = 0;
+	struct sxe2_mp_shared_data *mz_data;
+	int32_t send_reply = 0;
+	int32_t cnt = 0;
+
+	if (!rte_eth_dev_is_valid_port(param->port_id)) {
+		PMD_LOG_ERR(DRV, "primary process: invalid port_id %u",
+				param->port_id);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	dev = &rte_eth_devices[param->port_id];
+	sxe2_mp_mz = rte_memzone_lookup(SXE2_MP_MZ_NAME);
+	if (sxe2_mp_mz == NULL) {
+		PMD_LOG_ERR(DRV, "Failed to lookup memzone %s", SXE2_MP_MZ_NAME);
+		ret = -ENOENT;
+		goto out;
+	}
+
+	mz_data = (struct sxe2_mp_shared_data *)sxe2_mp_mz->addr;
+	send_reply = 1;
+
+	memset(&reply, 0, sizeof(reply));
+	(void)strlcpy(reply.name, SXE2_MP_NAME, sizeof(reply.name));
+	reply.len_param = sizeof(*reply_param);
+
+	switch (param->type) {
+	case SXE2_MP_REQ_GET_STATS:
+		ret = sxe2_stats_info_get(dev,
+					  &mz_data->payload.stats_blk.stats,
+					  &mz_data->payload.stats_blk.qstats);
+		break;
+	case SXE2_MP_REQ_GET_XSTATS:
+		cnt = sxe2_xstats_info_get(dev,
+				mz_data->payload.xstats_blk.xstats,
+				SXE2_MP_MAX_XSTATS);
+
+		if (cnt >= 0) {
+			mz_data->payload.xstats_blk.xstats_num = (uint32_t)cnt;
+			ret = 0;
+		} else {
+			mz_data->payload.xstats_blk.xstats_num = 0;
+			ret = cnt;
+		}
+		break;
+	case SXE2_MP_REQ_RESET_STATS:
+		ret = sxe2_stats_hw_reset(dev);
+		break;
+	default:
+		PMD_LOG_ERR(DRV, "primary process: unrecognized msg type: %d",
+				param->type);
+		send_reply = false;
+		ret = -EINVAL;
+		goto out;
+	}
+out:
+	if (!send_reply)
+		return ret;
+
+	reply_param->result = ret;
+	reply_param->type = param->type;
+	reply_param->port_id = param->port_id;
+
+	return rte_mp_reply(&reply, peer);
+}
+
+static int32_t
+sxe2_mp_secondary_handle(const struct rte_mp_msg *mp_msg __rte_unused,
+			 const void *peer __rte_unused)
+{
+	PMD_LOG_WARN(DRV, "the secondary process handler should not be called");
+	return 0;
+}
+
+static int32_t
+sxe2_mp_init_primary(__rte_unused struct rte_eth_dev *dev)
+{
+	int32_t ret;
+
+	if (sxe2_mp_mz == NULL) {
+		sxe2_mp_mz = rte_memzone_reserve(SXE2_MP_MZ_NAME,
+				sizeof(struct sxe2_mp_shared_data),
+				rte_socket_id(), 0);
+		if (sxe2_mp_mz == NULL && rte_errno != EEXIST) {
+			PMD_LOG_ERR(DRV, "Failed to reserve memzone %s, error: %d",
+					SXE2_MP_MZ_NAME, -rte_errno);
+			ret = -rte_errno;
+			goto out;
+		}
+
+		sxe2_mp_mz = rte_memzone_lookup(SXE2_MP_MZ_NAME);
+		if (sxe2_mp_mz == NULL) {
+			PMD_LOG_ERR(DRV, "Failed to lookup memzone %s", SXE2_MP_MZ_NAME);
+			ret = -ENOENT;
+			goto out;
+		}
+
+		struct sxe2_mp_shared_data *mz =
+			(struct sxe2_mp_shared_data *)sxe2_mp_mz->addr;
+		rte_atomic_store_explicit(&mz->in_use, 0, rte_memory_order_release);
+	}
+
+	ret = rte_mp_action_register(SXE2_MP_NAME, sxe2_mp_primary_handle);
+	if (ret && rte_errno == ENOTSUP) {
+		PMD_LOG_INFO(DRV, "Primary not support IPC.");
+		ret = 0;
+		goto out;
+	} else if (ret && rte_errno != EEXIST) {
+		PMD_LOG_ERR(DRV, "Failed to register MP primary handle, error: %d",
+			-rte_errno);
+		goto out;
+	}
+
+	rte_atomic_fetch_add_explicit(&primary_ethdev_cnt, 1, rte_memory_order_relaxed);
+
+	ret = 0;
+out:
+	return ret;
+}
+
+static int32_t
+sxe2_mp_init_secondary(__rte_unused struct rte_eth_dev *dev)
+{
+	int32_t ret;
+
+	sxe2_mp_mz = rte_memzone_lookup(SXE2_MP_MZ_NAME);
+	if (sxe2_mp_mz == NULL) {
+		PMD_LOG_ERR(DRV, "Failed to lookup memzone %s", SXE2_MP_MZ_NAME);
+		ret = -ENOENT;
+		goto out;
+	}
+
+	ret = rte_mp_action_register(SXE2_MP_NAME, sxe2_mp_secondary_handle);
+	if (ret && rte_errno != EEXIST) {
+		PMD_LOG_ERR(DRV, "Failed to register MP secondary handle, error: %d",
+				-rte_errno);
+		goto out;
+	}
+
+	rte_atomic_fetch_add_explicit(&secondary_ethdev_cnt, 1, rte_memory_order_relaxed);
+
+	ret = 0;
+out:
+	return ret;
+}
+
+int32_t
+sxe2_mp_init(struct rte_eth_dev *dev)
+{
+	if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+		return sxe2_mp_init_primary(dev);
+	else
+		return sxe2_mp_init_secondary(dev);
+}
+
+void
+sxe2_mp_uninit(__rte_unused struct rte_eth_dev *dev)
+{
+	if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+		if (rte_atomic_fetch_sub_explicit(&primary_ethdev_cnt, 1,
+			rte_memory_order_acq_rel) == 1) {
+			rte_mp_action_unregister(SXE2_MP_NAME);
+			if (sxe2_mp_mz != NULL) {
+				rte_memzone_free(sxe2_mp_mz);
+				sxe2_mp_mz = NULL;
+			}
+		}
+	} else {
+		if (rte_atomic_fetch_sub_explicit(&secondary_ethdev_cnt, 1,
+			rte_memory_order_acq_rel) == 1)
+			rte_mp_action_unregister(SXE2_MP_NAME);
+	}
+}
+
+static int32_t sxe2_mp_acquire_token(void)
+{
+	struct sxe2_mp_shared_data *mz;
+	uint16_t expected;
+
+	if (sxe2_mp_mz == NULL)
+		return -EINVAL;
+
+	mz = (struct sxe2_mp_shared_data *)sxe2_mp_mz->addr;
+
+	for (int32_t i = 0; i < SXE2_MP_MAX_SPIN; i++) {
+		expected = 0;
+		if (rte_atomic_compare_exchange_strong_explicit(&mz->in_use, &expected, 1,
+			rte_memory_order_acquire, rte_memory_order_relaxed))
+			return 0;
+
+		rte_pause();
+	}
+	return -EBUSY;
+}
+
+static void sxe2_mp_release_token(void)
+{
+	struct sxe2_mp_shared_data *mz;
+
+	if (sxe2_mp_mz == NULL)
+		return;
+
+	mz = (struct sxe2_mp_shared_data *)sxe2_mp_mz->addr;
+	rte_atomic_store_explicit(&mz->in_use, 0, rte_memory_order_release);
+}
+
+int32_t sxe2_mp_request_simple(struct rte_eth_dev *dev,
+			   enum sxe2_mp_req_type type,
+			   int32_t *result_out)
+{
+	struct rte_mp_msg msg;
+	struct rte_mp_reply reply = { 0 };
+	struct timespec ts = { .tv_sec = SXE2_MP_MSG_TIMEOUT, .tv_nsec = 0 };
+	struct sxe2_mp_param *param = (struct sxe2_mp_param *)msg.param;
+	struct sxe2_mp_shared_data *mz_data;
+	int32_t ret = 0;
+
+	mz_data = (struct sxe2_mp_shared_data *)sxe2_mp_mz->addr;
+
+	memset(&mz_data->payload, 0, sizeof(mz_data->payload));
+	memset(&msg, 0, sizeof(msg));
+	(void)strlcpy(msg.name, SXE2_MP_NAME, sizeof(msg.name));
+	msg.len_param = sizeof(*param);
+	param->type = type;
+	param->port_id = dev->data->port_id;
+
+	ret = rte_mp_request_sync(&msg, &reply, &ts);
+	if (ret != 0) {
+		PMD_LOG_ERR(DRV,
+				"IPC request(type=%d) failed for port %u: %s",
+				type, dev->data->port_id, rte_strerror(rte_errno));
+		ret = -rte_errno;
+		goto out;
+	}
+
+	if (reply.nb_received == 0) {
+		PMD_LOG_ERR(DRV, "No response received from primary for type=%d, port %u",
+			type, dev->data->port_id);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	*result_out = ((struct sxe2_mp_param *)reply.msgs[0].param)->result;
+
+out:
+	if (reply.msgs != NULL)
+		free(reply.msgs);
+
+	return ret;
+}
+
+int32_t sxe2_mp_req_get_stats(struct rte_eth_dev *dev,
+		      struct rte_eth_stats *stats,
+		      struct eth_queue_stats *qstats)
+{
+	struct sxe2_mp_shared_data *mz_data;
+	int32_t mp_ret;
+	int32_t ret;
+	int32_t token_acquired = 0;
+
+	if (sxe2_mp_mz == NULL)
+		return -EINVAL;
+
+	if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+		PMD_LOG_WARN(DRV, "Primary process direct execution for port %u",
+			     dev->data->port_id);
+		return sxe2_stats_info_get(dev, stats, qstats);
+	}
+
+	int32_t token_ret = sxe2_mp_acquire_token();
+	if (token_ret != 0)
+		return token_ret;
+	token_acquired = 1;
+
+	mp_ret = sxe2_mp_request_simple(dev, SXE2_MP_REQ_GET_STATS, &ret);
+	if (mp_ret != 0) {
+		ret = mp_ret;
+		goto out;
+	}
+
+	if (ret != 0) {
+		PMD_LOG_ERR(DRV, "Primary failed to exec request (type=%d), result: %d for port %u",
+			    SXE2_MP_REQ_GET_STATS, ret, dev->data->port_id);
+		goto out;
+	}
+
+	mz_data = (struct sxe2_mp_shared_data *)sxe2_mp_mz->addr;
+	memcpy(stats, &mz_data->payload.stats_blk.stats, sizeof(*stats));
+	memcpy(qstats, &mz_data->payload.stats_blk.qstats, sizeof(*qstats));
+	PMD_LOG_DEBUG(DRV, "sxe2_mp: stats received via IPC for port %u",
+			  dev->data->port_id);
+	ret = 0;
+out:
+	if (token_acquired)
+		sxe2_mp_release_token();
+	return ret;
+}
+
+int32_t sxe2_mp_req_get_xstats(struct rte_eth_dev *dev,
+			   struct rte_eth_xstat *xstats, uint32_t usr_cnt)
+{
+	struct sxe2_mp_shared_data *mz_data;
+	int32_t ret;
+	int32_t mp_ret;
+	int32_t token_acquired = 0;
+
+	if (sxe2_mp_mz == NULL)
+		return -EINVAL;
+
+	if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+		PMD_LOG_WARN(DRV, "Primary process direct execution for port %u",
+			     dev->data->port_id);
+		return sxe2_xstats_info_get(dev, xstats, usr_cnt);
+	}
+
+	int32_t token_ret = sxe2_mp_acquire_token();
+	if (token_ret != 0)
+		return token_ret;
+	token_acquired = 1;
+
+	mp_ret = sxe2_mp_request_simple(dev, SXE2_MP_REQ_GET_XSTATS, &ret);
+	if (mp_ret != 0) {
+		ret = mp_ret;
+		goto out;
+	}
+
+	if (ret != 0) {
+		PMD_LOG_ERR(DRV, "Primary failed to exec request (type=%d), result: %d for port %u",
+			    SXE2_MP_REQ_GET_XSTATS, ret, dev->data->port_id);
+		goto out;
+	}
+
+	mz_data = (struct sxe2_mp_shared_data *)sxe2_mp_mz->addr;
+	if (usr_cnt < mz_data->payload.xstats_blk.xstats_num) {
+		PMD_LOG_ERR(DRV, "user usr_cnt:%u less than xstats cnt:%u.",
+			    usr_cnt, mz_data->payload.xstats_blk.xstats_num);
+		ret = (int32_t)mz_data->payload.xstats_blk.xstats_num;
+		goto out;
+	}
+
+	memcpy(xstats, mz_data->payload.xstats_blk.xstats,
+		   mz_data->payload.xstats_blk.xstats_num *
+			   sizeof(struct rte_eth_xstat));
+	ret = (int32_t)mz_data->payload.xstats_blk.xstats_num;
+
+	PMD_LOG_DEBUG(DRV,
+			  "xstats received via IPC for port %u (cnt=%d)",
+			  dev->data->port_id, ret);
+out:
+	if (token_acquired)
+		sxe2_mp_release_token();
+	return ret;
+}
+
+int32_t
+sxe2_mp_req_reset_stats(struct rte_eth_dev *dev)
+{
+	int32_t mp_ret;
+	int32_t ret = 0;
+
+	if (sxe2_mp_mz == NULL)
+		return -EINVAL;
+
+	mp_ret = sxe2_mp_request_simple(dev, SXE2_MP_REQ_RESET_STATS, &ret);
+	if (mp_ret != 0)
+		return mp_ret;
+
+	if (ret != 0) {
+		PMD_LOG_ERR(DRV,
+				"Primary failed SXE2_MP_REQ_RESET_STATS, result: %d for port %u",
+				ret, dev->data->port_id);
+		return ret;
+	}
+	return 0;
+}
diff --git a/drivers/net/sxe2/sxe2_mp.h b/drivers/net/sxe2/sxe2_mp.h
new file mode 100644
index 0000000000..da9cc91d8d
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_mp.h
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef SXE2_MP_H
+#define SXE2_MP_H
+
+#include <rte_eal.h>
+#include <rte_ethdev.h>
+#include <rte_memzone.h>
+#include <rte_stdatomic.h>
+
+#define SXE2_MP_NAME		"sxe2_mp_msg"
+#define SXE2_MP_MZ_NAME		"sxe2_stats_mz"
+
+#define SXE2_MP_MSG_TIMEOUT 30
+
+#define SXE2_MP_MAX_XSTATS	128
+
+#define SXE2_MP_MAX_SPIN	100000
+
+enum sxe2_mp_req_type {
+	SXE2_MP_REQ_GET_STATS = 1,
+	SXE2_MP_REQ_GET_XSTATS,
+	SXE2_MP_REQ_RESET_STATS,
+};
+
+struct sxe2_mp_param {
+	enum sxe2_mp_req_type type;
+	uint32_t port_id;
+	int result;
+};
+
+union sxe2_mp_shared_payload {
+	struct {
+		struct rte_eth_stats stats;
+		struct eth_queue_stats qstats;
+	} stats_blk;
+	struct {
+		struct rte_eth_xstat xstats[SXE2_MP_MAX_XSTATS];
+		uint32_t xstats_num;
+	} xstats_blk;
+};
+
+struct sxe2_mp_shared_data {
+	RTE_ATOMIC(uint16_t)in_use;
+	union sxe2_mp_shared_payload payload;
+};
+
+int sxe2_mp_init(struct rte_eth_dev *dev);
+
+void sxe2_mp_uninit(struct rte_eth_dev *dev);
+
+int sxe2_mp_request_simple(struct rte_eth_dev *dev,
+			   enum sxe2_mp_req_type type,
+			   int *result_out);
+
+int sxe2_mp_req_get_stats(struct rte_eth_dev *dev,
+			  struct rte_eth_stats *stats,
+			  struct eth_queue_stats *qstats);
+
+int sxe2_mp_req_get_xstats(struct rte_eth_dev *dev,
+			   struct rte_eth_xstat *xstats, uint32_t usr_cnt);
+
+int sxe2_mp_req_reset_stats(struct rte_eth_dev *dev);
+
+#endif
diff --git a/drivers/net/sxe2/sxe2_stats.c b/drivers/net/sxe2/sxe2_stats.c
new file mode 100644
index 0000000000..7ea2815fa3
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_stats.c
@@ -0,0 +1,586 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <rte_eal.h>
+
+#include "sxe2_ethdev.h"
+#include "sxe2_vsi.h"
+#include "sxe2_common_log.h"
+#include "sxe2_stats.h"
+#include "sxe2_queue.h"
+#include "sxe2_cmd_chnl.h"
+#include "sxe2_mp.h"
+
+#define SXE2_XSTAT_CNT_PF RTE_DIM(sxe2_xstats_field_pf)
+#define SXE2_XSTAT_CNT_VF RTE_DIM(sxe2_xstats_field_vf)
+
+static struct sxe2_stats_field sxe2_xstats_field_pf[] = {
+	{"rx_qblock_drop", offsetof(struct sxe2_stats, rx_qblock_drop)},
+	{"rx_out_of_buffer", offsetof(struct sxe2_stats, rx_out_of_buffer)},
+	{"tx_packets_phy", offsetof(struct sxe2_stats, tx_frame_good)},
+	{"rx_packets_phy", offsetof(struct sxe2_stats, rx_frame_good)},
+	{"rx_crc_errors_phy", offsetof(struct sxe2_stats, rx_crc_errors)},
+	{"tx_bytes_phy", offsetof(struct sxe2_stats, tx_bytes_good)},
+	{"rx_bytes_phy", offsetof(struct sxe2_stats, rx_bytes_good)},
+	{"tx_multicast_phy", offsetof(struct sxe2_stats, tx_multicast_good)},
+	{"tx_broadcast_phy", offsetof(struct sxe2_stats, tx_broadcast_good)},
+	{"rx_multicast_phy", offsetof(struct sxe2_stats, rx_multicast_good)},
+	{"rx_broadcast_phy", offsetof(struct sxe2_stats, rx_broadcast_good)},
+	{"rx_in_range_len_errs_phy", offsetof(struct sxe2_stats, rx_len_errors)},
+	{"rx_out_of_range_len_phy", offsetof(struct sxe2_stats, rx_out_of_range_errors)},
+	{"rx_oversize_pkts_phy", offsetof(struct sxe2_stats, rx_oversize_pkts_phy)},
+	{"rx_symbol_err_phy", offsetof(struct sxe2_stats, rx_symbol_err)},
+	{"rx_pause_ctrl_phy", offsetof(struct sxe2_stats, rx_pause_frame)},
+	{"tx_pause_ctrl_phy", offsetof(struct sxe2_stats, tx_pause_frame)},
+	{"rx_discards_phy", offsetof(struct sxe2_stats, rx_discards_phy)},
+	{"rx_discards_ips_phy", offsetof(struct sxe2_stats, rx_discards_ips_phy)},
+	{"tx_dropped_link_down_phy", offsetof(struct sxe2_stats, tx_dropped_link_down)},
+	{"rx_undersize_pkts_phy", offsetof(struct sxe2_stats, rx_undersize_good)},
+	{"rx_fragments_phy", offsetof(struct sxe2_stats, rx_runt_error)},
+	{"tx_bytes_all_phy", offsetof(struct sxe2_stats, tx_bytes_good_bad)},
+	{"tx_packets_all_phy", offsetof(struct sxe2_stats, tx_frame_good_bad)},
+	{"rx_jabbers_phy", offsetof(struct sxe2_stats, rx_jabbers)},
+	{"rx_64_bytes_phy", offsetof(struct sxe2_stats, rx_size_64)},
+	{"rx_65_to_127_bytes_phy", offsetof(struct sxe2_stats, rx_size_65_127)},
+	{"rx_128_to_255_bytes_phy", offsetof(struct sxe2_stats, rx_size_128_255)},
+	{"rx_256_to_511_bytes_phy", offsetof(struct sxe2_stats, rx_size_256_511)},
+	{"rx_512_to_1023_bytes_phy", offsetof(struct sxe2_stats, rx_size_512_1023)},
+	{"rx_1024_to_1522_bytes_phy", offsetof(struct sxe2_stats, rx_size_1024_1522)},
+	{"rx_1523_to_max_bytes_phy", offsetof(struct sxe2_stats, rx_size_1523_max)},
+	{"rx_pcs_symbol_err_phy", offsetof(struct sxe2_stats, rx_pcs_symbol_err_phy)},
+	{"rx_corrected_bits_phy", offsetof(struct sxe2_stats, rx_corrected_bits_phy)},
+	{"rx_err_lane_0_phy", offsetof(struct sxe2_stats, rx_err_lane_0_phy)},
+	{"rx_err_lane_1_phy", offsetof(struct sxe2_stats, rx_err_lane_1_phy)},
+	{"rx_err_lane_2_phy", offsetof(struct sxe2_stats, rx_err_lane_2_phy)},
+	{"rx_err_lane_3_phy", offsetof(struct sxe2_stats, rx_err_lane_3_phy)},
+	{"rx_illegal_bytes_phy", offsetof(struct sxe2_stats, rx_illegal_bytes)},
+	{"rx_oversize_good_phy", offsetof(struct sxe2_stats, rx_oversize_good)},
+	{"tx_unicast_all_phy", offsetof(struct sxe2_stats, tx_unicast)},
+	{"tx_broadcast_all_phy", offsetof(struct sxe2_stats, tx_broadcast)},
+	{"tx_multicast_all_phy", offsetof(struct sxe2_stats, tx_multicast)},
+	{"tx_vlan_packets_good_phy", offsetof(struct sxe2_stats, tx_vlan_packet_good)},
+	{"tx_64_bytes_phy", offsetof(struct sxe2_stats, tx_size_64)},
+	{"tx_65_to_127_bytes_phy", offsetof(struct sxe2_stats, tx_size_65_127)},
+	{"tx_128_to_255_bytes_phy", offsetof(struct sxe2_stats, tx_size_128_255)},
+	{"tx_256_to_511_bytes_phy", offsetof(struct sxe2_stats, tx_size_256_511)},
+	{"tx_512_to_1023_bytes_phy", offsetof(struct sxe2_stats, tx_size_512_1023)},
+	{"tx_1024_to_1522_bytes_phy", offsetof(struct sxe2_stats, tx_size_1024_1522)},
+	{"tx_1523_to_max_bytes_phy", offsetof(struct sxe2_stats, tx_size_1523_max)},
+	{"tx_underflow_error_phy", offsetof(struct sxe2_stats, tx_underflow_error)},
+	{"rx_bytes_all_phy", offsetof(struct sxe2_stats, rx_byte_good_bad)},
+	{"rx_packets_all_phy", offsetof(struct sxe2_stats, rx_frame_good_bad)},
+	{"rx_unicast_phy", offsetof(struct sxe2_stats, rx_unicast_good)},
+	{"rx_vlan_packets_phy", offsetof(struct sxe2_stats, rx_vlan_packets)},
+
+	{"rx_vport_bytes",             offsetof(struct sxe2_stats, rx_vsi_bytes)},
+	{"rx_vport_unicast_packets",   offsetof(struct sxe2_stats, rx_vsi_unicast_packets)},
+	{"rx_vport_broadcast_packets", offsetof(struct sxe2_stats, rx_vsi_broadcast_packets)},
+	{"rx_vport_multicast_packets", offsetof(struct sxe2_stats, rx_vsi_multicast_packets)},
+	{"rx_sw_unicast_packets",      offsetof(struct sxe2_stats, rx_sw_unicast_packets)},
+	{"rx_sw_broadcast_packets",    offsetof(struct sxe2_stats, rx_sw_broadcast_packets)},
+	{"rx_sw_multicast_packets",    offsetof(struct sxe2_stats, rx_sw_multicast_packets)},
+	{"rx_sw_drop_packets",         offsetof(struct sxe2_stats, rx_sw_drop_packets)},
+	{"rx_sw_drop_bytes",           offsetof(struct sxe2_stats, rx_sw_drop_bytes)},
+
+	{"tx_vport_bytes",             offsetof(struct sxe2_stats, tx_vsi_bytes)},
+	{"tx_vport_unicast_packets",   offsetof(struct sxe2_stats, tx_vsi_unicast_packets)},
+	{"tx_vport_broadcast_packets", offsetof(struct sxe2_stats, tx_vsi_broadcast_packets)},
+	{"tx_vport_multicast_packets", offsetof(struct sxe2_stats, tx_vsi_multicast_packets)},
+};
+
+static struct sxe2_stats_field sxe2_xstats_field_vf[] = {
+	{"rx_vport_bytes",             offsetof(struct sxe2_stats, rx_vsi_bytes)},
+	{"rx_vport_unicast_packets",   offsetof(struct sxe2_stats, rx_vsi_unicast_packets)},
+	{"rx_vport_broadcast_packets", offsetof(struct sxe2_stats, rx_vsi_broadcast_packets)},
+	{"rx_vport_multicast_packets", offsetof(struct sxe2_stats, rx_vsi_multicast_packets)},
+	{"rx_sw_unicast_packets",      offsetof(struct sxe2_stats, rx_sw_unicast_packets)},
+	{"rx_sw_broadcast_packets",    offsetof(struct sxe2_stats, rx_sw_broadcast_packets)},
+	{"rx_sw_multicast_packets",    offsetof(struct sxe2_stats, rx_sw_multicast_packets)},
+	{"rx_sw_drop_packets",         offsetof(struct sxe2_stats, rx_sw_drop_packets)},
+	{"rx_sw_drop_bytes",           offsetof(struct sxe2_stats, rx_sw_drop_bytes)},
+
+	{"tx_vport_bytes",             offsetof(struct sxe2_stats, tx_vsi_bytes)},
+	{"tx_vport_unicast_packets",   offsetof(struct sxe2_stats, tx_vsi_unicast_packets)},
+	{"tx_vport_broadcast_packets", offsetof(struct sxe2_stats, tx_vsi_broadcast_packets)},
+	{"tx_vport_multicast_packets", offsetof(struct sxe2_stats, tx_vsi_multicast_packets)},
+};
+
+static int32_t sxe2_xstat_pf_offset_get(uint32_t id, uint32_t *offset)
+{
+	int32_t ret  = 0;
+	uint32_t size = SXE2_XSTAT_CNT_PF;
+
+	if (id < size) {
+		*offset = sxe2_xstats_field_pf[id].offset;
+	} else {
+		ret = -EINVAL;
+		PMD_LOG_ERR(DRV, "invalid id:%u exceed stats size cnt:%u", id, size);
+	}
+	return ret;
+}
+
+static int32_t sxe2_xstat_vf_offset_get(uint32_t id, uint32_t *offset)
+{
+	int32_t ret  = 0;
+	uint32_t size = SXE2_XSTAT_CNT_VF;
+
+	if (id < size) {
+		*offset = sxe2_xstats_field_vf[id].offset;
+	} else {
+		ret = -EINVAL;
+		PMD_LOG_ERR(DRV, "invalid id:%u exceed stats size cnt:%u", id, size);
+	}
+	return ret;
+}
+
+static int32_t sxe2_mac_hw_stats_get_update(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	ret = sxe2_drv_get_mac_stats(adapter);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "get mac stats failed, ret:%d.", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_vsi_hw_stats_get_update(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+
+	ret = sxe2_drv_get_vsi_stats(adapter);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "get vsi stats failed, ret:%d.", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_vsi_sw_stats_get_update(struct sxe2_adapter *adapter)
+{
+	int32_t ret  = 0;
+	struct sxe2_vsi          *vsi      = adapter->vsi_ctxt.main_vsi;
+	struct sxe2_stats        *sw_stats = &vsi->vsi_stats.vsi_sw_stats;
+	struct sxe2_rx_queue     *rxq;
+	uint32_t rx_queue_id;
+	memset(sw_stats, 0, sizeof(struct sxe2_stats));
+
+	for (rx_queue_id = 0; rx_queue_id < adapter->dev_info.dev_data->nb_rx_queues;
+		     rx_queue_id++) {
+		rxq = adapter->dev_info.dev_data->rx_queues[rx_queue_id];
+		if (rxq) {
+			sw_stats->ipackets += rxq->sw_stats.pkts;
+			sw_stats->ierrors += rxq->sw_stats.drop_pkts;
+			sw_stats->ibytes += rxq->sw_stats.bytes;
+
+			sw_stats->rx_sw_unicast_packets += rxq->sw_stats.unicast_pkts;
+			sw_stats->rx_sw_broadcast_packets += rxq->sw_stats.broadcast_pkts;
+			sw_stats->rx_sw_multicast_packets += rxq->sw_stats.multicast_pkts;
+			sw_stats->rx_sw_drop_packets += rxq->sw_stats.drop_pkts;
+			sw_stats->rx_sw_drop_bytes += rxq->sw_stats.drop_bytes;
+		}
+	}
+
+	return ret;
+}
+
+static void sxe2_stats_update(struct sxe2_adapter *adapter)
+{
+	struct sxe2_vsi          *vsi      = adapter->vsi_ctxt.main_vsi;
+	struct sxe2_stats        *stats = &vsi->vsi_stats.stats;
+	struct sxe2_stats        *hw_stats = &vsi->vsi_stats.vsi_hw_stats;
+	struct sxe2_stats        *sw_stats = &vsi->vsi_stats.vsi_sw_stats;
+	struct sxe2_stats        *sw_stats_prev = &vsi->vsi_stats.vsi_sw_stats_prev;
+	uint8_t i = 0;
+
+	memset(stats, 0, sizeof(struct sxe2_stats));
+
+	stats->opackets = hw_stats->opackets;
+	stats->obytes   = hw_stats->tx_vsi_bytes;
+	stats->tx_vsi_bytes             = hw_stats->tx_vsi_bytes;
+	stats->tx_vsi_unicast_packets   = hw_stats->tx_vsi_unicast_packets;
+	stats->tx_vsi_broadcast_packets = hw_stats->tx_vsi_broadcast_packets;
+	stats->tx_vsi_multicast_packets = hw_stats->tx_vsi_multicast_packets;
+
+	stats->ierrors = sw_stats->ierrors + sw_stats_prev->ierrors;
+	if (adapter->devargs.sw_stats_en) {
+		stats->ipackets = sw_stats->ipackets + sw_stats_prev->ipackets;
+		stats->ibytes = sw_stats->ibytes + sw_stats_prev->ibytes;
+	} else {
+		stats->ipackets = hw_stats->ipackets;
+		stats->ibytes   = hw_stats->rx_vsi_bytes;
+	}
+	stats->rx_vsi_bytes             = hw_stats->rx_vsi_bytes;
+	stats->rx_vsi_unicast_packets   = hw_stats->rx_vsi_unicast_packets;
+	stats->rx_vsi_broadcast_packets = hw_stats->rx_vsi_broadcast_packets;
+	stats->rx_vsi_multicast_packets = hw_stats->rx_vsi_multicast_packets;
+	stats->rx_sw_unicast_packets = sw_stats->rx_sw_unicast_packets +
+			sw_stats_prev->rx_sw_unicast_packets;
+	stats->rx_sw_broadcast_packets = sw_stats->rx_sw_broadcast_packets +
+			sw_stats_prev->rx_sw_broadcast_packets;
+	stats->rx_sw_multicast_packets = sw_stats->rx_sw_multicast_packets +
+			sw_stats_prev->rx_sw_multicast_packets;
+	stats->rx_sw_drop_packets = sw_stats->rx_sw_drop_packets +
+			sw_stats_prev->rx_sw_drop_packets;
+	stats->rx_sw_drop_bytes = sw_stats->rx_sw_drop_bytes +
+			sw_stats_prev->rx_sw_drop_bytes;
+
+	if (adapter->dev_type != SXE2_DEV_T_VF) {
+		stats->rx_out_of_buffer = hw_stats->rx_out_of_buffer;
+		stats->rx_qblock_drop = hw_stats->rx_qblock_drop;
+		stats->tx_frame_good = hw_stats->tx_frame_good;
+		stats->rx_frame_good = hw_stats->rx_frame_good;
+		stats->rx_crc_errors = hw_stats->rx_crc_errors;
+		stats->tx_bytes_good = hw_stats->tx_bytes_good;
+		stats->rx_bytes_good = hw_stats->rx_bytes_good;
+		stats->tx_multicast_good = hw_stats->tx_multicast_good;
+		stats->tx_broadcast_good = hw_stats->tx_broadcast_good;
+		stats->rx_multicast_good = hw_stats->rx_multicast_good;
+		stats->rx_broadcast_good = hw_stats->rx_broadcast_good;
+		stats->rx_len_errors = hw_stats->rx_len_errors;
+		stats->rx_out_of_range_errors = hw_stats->rx_out_of_range_errors;
+		stats->rx_oversize_pkts_phy = hw_stats->rx_oversize_pkts_phy;
+		stats->rx_symbol_err = hw_stats->rx_symbol_err;
+		stats->rx_pause_frame = hw_stats->rx_pause_frame;
+		stats->tx_pause_frame = hw_stats->tx_pause_frame;
+		stats->rx_discards_phy = hw_stats->rx_discards_phy;
+		stats->rx_discards_ips_phy = hw_stats->rx_discards_ips_phy;
+		stats->tx_dropped_link_down = hw_stats->tx_dropped_link_down;
+		stats->rx_undersize_good = hw_stats->rx_undersize_good;
+		stats->rx_runt_error = hw_stats->rx_runt_error;
+		stats->tx_bytes_good_bad = hw_stats->tx_bytes_good_bad;
+		stats->tx_frame_good_bad = hw_stats->tx_frame_good_bad;
+		stats->rx_jabbers = hw_stats->rx_jabbers;
+		stats->rx_size_64 = hw_stats->rx_size_64;
+		stats->rx_size_65_127 = hw_stats->rx_size_65_127;
+		stats->rx_size_128_255 = hw_stats->rx_size_128_255;
+		stats->rx_size_256_511 = hw_stats->rx_size_256_511;
+		stats->rx_size_512_1023 = hw_stats->rx_size_512_1023;
+		stats->rx_size_1024_1522 = hw_stats->rx_size_1024_1522;
+		stats->rx_size_1523_max = hw_stats->rx_size_1523_max;
+		stats->rx_pcs_symbol_err_phy = hw_stats->rx_pcs_symbol_err_phy;
+		stats->rx_corrected_bits_phy = hw_stats->rx_corrected_bits_phy;
+		stats->rx_err_lane_0_phy = hw_stats->rx_err_lane_0_phy;
+		stats->rx_err_lane_1_phy = hw_stats->rx_err_lane_1_phy;
+		stats->rx_err_lane_2_phy = hw_stats->rx_err_lane_2_phy;
+		stats->rx_err_lane_3_phy = hw_stats->rx_err_lane_3_phy;
+		stats->rx_illegal_bytes = hw_stats->rx_illegal_bytes;
+		stats->rx_oversize_good = hw_stats->rx_oversize_good;
+		stats->tx_unicast = hw_stats->tx_unicast;
+		stats->tx_broadcast = hw_stats->tx_broadcast;
+		stats->tx_multicast = hw_stats->tx_multicast;
+		stats->tx_vlan_packet_good = hw_stats->tx_vlan_packet_good;
+		stats->tx_size_64 = hw_stats->tx_size_64;
+		stats->tx_size_65_127 = hw_stats->tx_size_65_127;
+		stats->tx_size_128_255 = hw_stats->tx_size_128_255;
+		stats->tx_size_256_511 = hw_stats->tx_size_256_511;
+		stats->tx_size_512_1023 = hw_stats->tx_size_512_1023;
+		stats->tx_size_1024_1522 = hw_stats->tx_size_1024_1522;
+		stats->tx_size_1523_max = hw_stats->tx_size_1523_max;
+		stats->tx_underflow_error = hw_stats->tx_underflow_error;
+		stats->rx_byte_good_bad = hw_stats->rx_byte_good_bad;
+		stats->rx_frame_good_bad = hw_stats->rx_frame_good_bad;
+		stats->rx_unicast_good = hw_stats->rx_unicast_good;
+		stats->rx_vlan_packets = hw_stats->rx_vlan_packets;
+		rte_memcpy(stats->rx_prio_buf_discard, hw_stats->rx_prio_buf_discard,
+				sizeof(hw_stats->rx_prio_buf_discard));
+		rte_memcpy(stats->prio_xoff_rx, hw_stats->prio_xoff_rx,
+				sizeof(hw_stats->prio_xoff_rx));
+		rte_memcpy(stats->prio_xon_rx, hw_stats->prio_xon_rx,
+				sizeof(hw_stats->prio_xon_rx));
+		rte_memcpy(stats->prio_xon_tx, hw_stats->prio_xon_tx,
+				sizeof(hw_stats->prio_xon_tx));
+		rte_memcpy(stats->prio_xoff_tx, hw_stats->prio_xoff_tx,
+				sizeof(hw_stats->prio_xoff_tx));
+		rte_memcpy(stats->prio_xon_2_xoff, hw_stats->prio_xon_2_xoff,
+				sizeof(hw_stats->prio_xon_2_xoff));
+
+		stats->imissed = hw_stats->rx_out_of_buffer +
+				hw_stats->rx_qblock_drop;
+		for (i = 0; i < SXE2_MAX_USER_PRIORITY; i++)
+			stats->imissed += hw_stats->rx_prio_buf_discard[i];
+	}
+}
+
+int32_t sxe2_stats_info_get(struct rte_eth_dev *dev,
+			struct rte_eth_stats *stats,
+			struct eth_queue_stats *qstats)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter  = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_vsi     *vsi      = adapter->vsi_ctxt.main_vsi;
+	struct sxe2_stats   *stats_out = &vsi->vsi_stats.stats;
+
+	if (rte_eal_process_type() == RTE_PROC_SECONDARY)
+		return sxe2_mp_req_get_stats(dev, stats, qstats);
+
+	ret = sxe2_vsi_hw_stats_get_update(adapter);
+	if (ret)
+		goto end;
+
+	ret = sxe2_vsi_sw_stats_get_update(adapter);
+	if (ret)
+		goto end;
+
+	ret = sxe2_drv_queue_info_get_update(adapter, qstats);
+	if (ret)
+		goto end;
+
+	sxe2_stats_update(adapter);
+
+	stats->ipackets = stats_out->ipackets;
+	stats->ibytes   = stats_out->ibytes;
+	stats->ierrors  = stats_out->ierrors;
+	stats->imissed  = stats_out->imissed;
+	stats->rx_nombuf = dev->data->rx_mbuf_alloc_failed;
+
+	stats->opackets = stats_out->opackets;
+	stats->obytes   = stats_out->obytes;
+
+	ret = 0;
+
+end:
+	return ret;
+}
+
+int32_t sxe2_xstats_info_get(struct rte_eth_dev *dev,
+		struct rte_eth_xstat *xstats, uint32_t usr_cnt)
+{
+	uint32_t i      = 0;
+	uint32_t cnt    = 0;
+	int32_t ret    = 0;
+	uint32_t offset = 0;
+	uint32_t xstats_cnt = 0;
+	struct sxe2_adapter    *adapter   = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_vsi        *vsi       = adapter->vsi_ctxt.main_vsi;
+	struct sxe2_vsi_stats  *xstats_out = &vsi->vsi_stats;
+
+	if (rte_eal_process_type() == RTE_PROC_SECONDARY)
+		return sxe2_mp_req_get_xstats(dev, xstats, usr_cnt);
+
+	if (adapter->dev_type == SXE2_DEV_T_VF)
+		xstats_cnt = SXE2_XSTAT_CNT_VF;
+	else
+		xstats_cnt = SXE2_XSTAT_CNT_PF;
+
+	if (usr_cnt < xstats_cnt) {
+		ret = xstats_cnt;
+		PMD_LOG_ERR(DRV, "user usr_cnt:%u less than stats cnt:%u", usr_cnt, ret);
+		goto end;
+	}
+
+	ret = sxe2_vsi_hw_stats_get_update(adapter);
+	if (ret) {
+		ret = xstats_cnt;
+		goto end;
+	}
+
+	ret = sxe2_vsi_sw_stats_get_update(adapter);
+	if (ret) {
+		ret = xstats_cnt;
+		goto end;
+	}
+
+	if (adapter->dev_type == SXE2_DEV_T_VF) {
+		sxe2_stats_update(adapter);
+		for (i = 0; i < xstats_cnt; i++) {
+			(void)sxe2_xstat_vf_offset_get(i, &offset);
+			xstats[cnt].value = *(uint64_t *)(((int8_t *)(&xstats_out->stats)) +
+							  offset);
+			xstats[cnt].id    = cnt;
+			cnt++;
+		}
+	} else {
+		ret = sxe2_mac_hw_stats_get_update(adapter);
+		if (ret) {
+			ret = xstats_cnt;
+			goto end;
+		}
+
+		sxe2_stats_update(adapter);
+
+		for (i = 0; i < xstats_cnt; i++) {
+			(void)sxe2_xstat_pf_offset_get(i, &offset);
+			xstats[cnt].value = *(uint64_t *)(((int8_t *)(&xstats_out->stats)) +
+							  offset);
+			xstats[cnt].id = cnt;
+			cnt++;
+		}
+	}
+	ret = cnt;
+	PMD_LOG_DEBUG(DRV, "usr_cnt:%u stats cnt:%u stats done", usr_cnt, cnt);
+
+end:
+	return ret;
+}
+
+int32_t sxe2_xstats_names_get(__rte_unused struct rte_eth_dev *dev,
+	struct rte_eth_xstat_name *xstats_names,
+	__rte_unused unsigned int usr_cnt)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_stats_field *field = NULL;
+	uint32_t i   = 0;
+	uint32_t cnt = 0;
+	int32_t ret = -1;
+	uint32_t xstats_cnt = 0;
+
+	if (adapter->dev_type == SXE2_DEV_T_VF) {
+		field = sxe2_xstats_field_vf;
+		xstats_cnt = SXE2_XSTAT_CNT_VF;
+	} else {
+		field = sxe2_xstats_field_pf;
+		xstats_cnt = SXE2_XSTAT_CNT_PF;
+	}
+
+	if (!xstats_names) {
+		ret = xstats_cnt;
+		PMD_LOG_DEBUG(DRV, "xstats field size:%u", ret);
+		goto l_out;
+	}
+
+	if (usr_cnt < xstats_cnt) {
+		ret = -EINVAL;
+		PMD_LOG_ERR(DRV, "max:%d usr_cnt:%u invalid (err:%d)", xstats_cnt, usr_cnt, ret);
+		goto l_out;
+	}
+
+	for (i = 0; i < xstats_cnt; i++) {
+		(void)strlcpy(xstats_names[cnt].name, field[i].name,
+			      sizeof(xstats_names[cnt].name));
+		cnt++;
+	}
+
+	ret = cnt;
+
+l_out:
+	return ret;
+}
+
+int32_t sxe2_stats_hw_reset(struct rte_eth_dev *dev)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter    *adapter   = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_vsi        *vsi       = adapter->vsi_ctxt.main_vsi;
+	struct sxe2_rx_queue   *rxq;
+	uint32_t rx_queue_id;
+
+	ret = sxe2_drv_vsi_stats_reset(adapter);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "reset vsi stats failed, ret:%d.", ret);
+		goto l_end;
+	}
+	if (adapter->dev_type != SXE2_DEV_T_VF) {
+		ret = sxe2_drv_mac_stats_reset(adapter);
+		if (ret) {
+			PMD_LOG_ERR(DRV, "reset mac stats failed, ret:%d.", ret);
+			goto l_end;
+		}
+	}
+
+	memset(&vsi->vsi_stats, 0, sizeof(vsi->vsi_stats));
+	for (rx_queue_id = 0; rx_queue_id < dev->data->nb_rx_queues; rx_queue_id++) {
+		rxq = dev->data->rx_queues[rx_queue_id];
+		if (rxq)
+			memset(&rxq->sw_stats, 0, sizeof(rxq->sw_stats));
+	}
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_stats_info_reset(struct rte_eth_dev *dev)
+{
+	int32_t ret;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (rte_eal_process_type() == RTE_PROC_SECONDARY)
+		return sxe2_mp_req_reset_stats(dev);
+
+	if (adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_Q_MAP) {
+		ret = sxe2_drv_mapping_stats_info_clear(dev);
+		if (ret)
+			goto l_end;
+	}
+
+	ret = sxe2_stats_hw_reset(dev);
+	if (ret)
+		goto l_end;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_stats_init(struct rte_eth_dev *dev)
+{
+	PMD_INIT_FUNC_TRACE();
+	int32_t ret;
+
+	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
+		return 0;
+
+	ret = sxe2_queue_stats_map_init(dev);
+	if (ret)
+		goto l_end;
+
+	ret = sxe2_stats_hw_reset(dev);
+	if (ret)
+		goto l_end;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_queue_stats_mapping_set(struct rte_eth_dev *eth_dev,
+				  uint16_t queue_id, uint8_t pool_idx, uint8_t is_rx)
+{
+	int32_t ret = -1;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(eth_dev);
+
+	if (!(adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_Q_MAP)) {
+		PMD_LOG_ERR(DRV, "VF does not support queue mapping! ");
+		goto l_end;
+	}
+
+	if (is_rx)
+		ret = sxe2_drv_rxq_mapping_set(eth_dev, queue_id, pool_idx);
+	else
+		ret = sxe2_drv_txq_mapping_set(eth_dev, queue_id, pool_idx);
+
+	if (ret) {
+		PMD_LOG_ERR(DRV, "Queue stats mapping failed ! "
+			"queue_id:%u pool_idx:%u", queue_id, pool_idx);
+		goto l_end;
+	}
+
+	PMD_LOG_DEBUG(DRV, "port %u %s queue_id %d stat map to pool[%u] ",
+		     (uint16_t)(eth_dev->data->port_id), is_rx ? "RX" : "TX",
+		     queue_id, pool_idx);
+l_end:
+	return ret;
+}
+
+int32_t sxe2_queue_stats_map_init(struct rte_eth_dev *dev)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_Q_MAP) {
+		dev->data->dev_flags |= RTE_ETH_DEV_AUTOFILL_QUEUE_XSTATS;
+
+		ret = sxe2_drv_mapping_reset(dev);
+		if (ret) {
+			PMD_LOG_ERR(DRV, "Queue stats mapping init failed !");
+			goto l_end;
+		}
+	}
+
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_stats.h b/drivers/net/sxe2/sxe2_stats.h
new file mode 100644
index 0000000000..64ac2bb11d
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_stats.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_STATS_H__
+#define __SXE2_STATS_H__
+
+#define SXE2_STATS_FIELD_NAME_SIZE  50
+
+struct sxe2_stats_field {
+	char name[SXE2_STATS_FIELD_NAME_SIZE];
+	uint32_t offset;
+};
+
+struct sxe2_adapter;
+
+int32_t sxe2_stats_info_get(struct rte_eth_dev *dev,
+			struct rte_eth_stats *stats,
+			struct eth_queue_stats *qstats);
+
+int32_t sxe2_xstats_info_get(struct rte_eth_dev *dev,
+	struct rte_eth_xstat *xstats, uint32_t usr_cnt);
+
+int32_t sxe2_xstats_names_get(__rte_unused struct rte_eth_dev *dev,
+			  struct rte_eth_xstat_name *xstats_names,
+			  __rte_unused unsigned int usr_cnt);
+
+int32_t sxe2_stats_hw_reset(struct rte_eth_dev *dev);
+
+int32_t sxe2_stats_info_reset(struct rte_eth_dev *dev);
+
+int32_t sxe2_stats_init(struct rte_eth_dev *dev);
+
+int32_t sxe2_queue_stats_mapping_set(struct rte_eth_dev *eth_dev,
+				  uint16_t queue_id, uint8_t pool_idx, uint8_t is_rx);
+
+int32_t sxe2_queue_stats_map_init(struct rte_eth_dev *dev);
+
+#endif
-- 
2.52.0


^ permalink raw reply related

* [PATCH v10 09/20] drivers: interrupt handling
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260606010726.2256170-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

This patch includes:
- MSI-X vector allocation and mapping logic.
- Support for rx_queue_intr_enable and rx_queue_intr_disable ops.
- Interrupt handler to process admin and queue events.
- Integration with EAL interrupt framework.

RX queue interrupts allow applications to use rte_eth_dev_rx_intr_wait()
for power-efficient packet processing.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/common/sxe2/sxe2_ioctl_chnl.c      | 176 +++-
 drivers/common/sxe2/sxe2_ioctl_chnl_func.h |  18 +
 drivers/net/sxe2/meson.build               |   1 +
 drivers/net/sxe2/sxe2_cmd_chnl.c           |  42 +
 drivers/net/sxe2/sxe2_cmd_chnl.h           |   4 +
 drivers/net/sxe2/sxe2_drv_cmd.h            |   8 +
 drivers/net/sxe2/sxe2_ethdev.c             |  93 +-
 drivers/net/sxe2/sxe2_ethdev.h             |   6 +
 drivers/net/sxe2/sxe2_irq.c                | 942 +++++++++++++++++++++
 drivers/net/sxe2/sxe2_irq.h                |  21 +
 drivers/net/sxe2/sxe2vf_regs.h             |  85 ++
 11 files changed, 1393 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/sxe2/sxe2_irq.c
 create mode 100644 drivers/net/sxe2/sxe2vf_regs.h

diff --git a/drivers/common/sxe2/sxe2_ioctl_chnl.c b/drivers/common/sxe2/sxe2_ioctl_chnl.c
index 51aea327e5..173d8d57ae 100644
--- a/drivers/common/sxe2/sxe2_ioctl_chnl.c
+++ b/drivers/common/sxe2/sxe2_ioctl_chnl.c
@@ -67,6 +67,7 @@ sxe2_drv_cmd_exec(struct sxe2_common_device *cdev,
 	return ret;
 }
 
+
 RTE_EXPORT_INTERNAL_SYMBOL(sxe2_drv_dev_open)
 int32_t
 sxe2_drv_dev_open(struct sxe2_common_device *cdev, struct rte_pci_device *pci_dev)
@@ -87,7 +88,7 @@ sxe2_drv_dev_open(struct sxe2_common_device *cdev, struct rte_pci_device *pci_de
 	if (fd < 0) {
 		ret = -EBADF;
 		PMD_LOG_ERR(COM, "Fail to open device:%s, ret=%d, err:%s",
-				drv_name, ret, strerror(errno));
+			    drv_name, ret, strerror(errno));
 		goto l_end;
 	}
 
@@ -159,6 +160,177 @@ sxe2_drv_dev_handshake(struct sxe2_common_device *cdev)
 	return ret;
 }
 
+RTE_EXPORT_INTERNAL_SYMBOL(sxe2_drv_dev_rxq_irq_set)
+int32_t
+sxe2_drv_dev_rxq_irq_set(struct sxe2_common_device *cdev,
+		       uint16_t base_irq, int32_t *efd, uint16_t nb_irq)
+{
+	struct sxe2_ioctl_irq_set cmd_params;
+	int32_t ret = 0;
+	int32_t cmd_fd = 0;
+
+	if (cdev->config.kernel_reset) {
+		ret = -EPERM;
+		PMD_LOG_WARN(COM, "kernel reset, need restart app.");
+		goto l_end;
+	}
+
+	cmd_fd = SXE2_CDEV_TO_CMD_FD(cdev);
+	if (cmd_fd < 0) {
+		ret = -EBADF;
+		PMD_LOG_ERR(COM, "Failed to exec cmd, fd=%d", cmd_fd);
+		goto l_end;
+	}
+
+	PMD_LOG_DEBUG(COM, "Open fd=%d to set rxq irq, base_queue=%d, efds=%p, nb_irq=%d",
+		cmd_fd, base_irq, efd, nb_irq);
+
+	memset(&cmd_params, 0, sizeof(struct sxe2_ioctl_irq_set));
+	cmd_params.base_irq_in_com = base_irq;
+	cmd_params.cnt = nb_irq;
+	cmd_params.event_fd = efd;
+
+	pthread_mutex_lock(&cdev->config.lock);
+	ret = ioctl(cmd_fd, SXE2_COM_CMD_IO_IRQS_REQ, &cmd_params);
+	if (ret < 0) {
+		PMD_LOG_ERR(COM, "Failed to set io irqs, fd=%d, ret=%d, err:%s",
+			    cmd_fd, ret, strerror(errno));
+		pthread_mutex_unlock(&cdev->config.lock);
+		goto l_end;
+	}
+	pthread_mutex_unlock(&cdev->config.lock);
+
+l_end:
+	return ret;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(sxe2_drv_dev_other_irq_set)
+int32_t
+sxe2_drv_dev_other_irq_set(struct sxe2_common_device *cdev,
+			int32_t efd, uint64_t event)
+{
+	int32_t ret = 0;
+	int32_t cmd_fd = 0;
+	struct sxe2_ioctl_other_evt_set cmd_params;
+
+	if (cdev->config.kernel_reset) {
+		ret = -EPERM;
+		PMD_LOG_WARN(COM, "kernel reset, need restart app.");
+		goto l_end;
+	}
+
+	cmd_fd = SXE2_CDEV_TO_CMD_FD(cdev);
+	if (cmd_fd < 0) {
+		ret = -EBADF;
+		PMD_LOG_ERR(COM, "Failed to exec cmd, fd=%d", cmd_fd);
+		goto l_end;
+	}
+
+	PMD_LOG_DEBUG(COM, "Open fd=%d to set other irq, efd=%d, event=%"PRIu64"",
+		cmd_fd, efd, event);
+
+	memset(&cmd_params, 0, sizeof(struct sxe2_ioctl_other_evt_set));
+	cmd_params.eventfd = efd;
+	cmd_params.filter_table = event;
+
+	pthread_mutex_lock(&cdev->config.lock);
+	ret = ioctl(cmd_fd, SXE2_COM_CMD_EVT_IRQ_REQ, &cmd_params);
+	if (ret < 0) {
+		PMD_LOG_ERR(COM, "Failed to set others evt, fd=%d, ret=%d, err:%s",
+			   cmd_fd, ret, strerror(errno));
+		ret = -EIO;
+		pthread_mutex_unlock(&cdev->config.lock);
+		goto l_end;
+	}
+	pthread_mutex_unlock(&cdev->config.lock);
+
+l_end:
+	return ret;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(sxe2_drv_dev_other_irq_get)
+int32_t
+sxe2_drv_dev_other_irq_get(struct sxe2_common_device *cdev, uint64_t *event)
+{
+	int32_t ret = 0;
+	int32_t cmd_fd = 0;
+	struct sxe2_ioctl_other_evt_get cmd_params;
+
+	if (cdev->config.kernel_reset) {
+		ret = -EPERM;
+		PMD_LOG_WARN(COM, "kernel reset, need restart app.");
+		goto l_end;
+	}
+
+	cmd_fd = SXE2_CDEV_TO_CMD_FD(cdev);
+	if (cmd_fd < 0) {
+		ret = -EBADF;
+		PMD_LOG_ERR(COM, "Failed to exec cmd, fd=%d", cmd_fd);
+		goto l_end;
+	}
+
+	PMD_LOG_DEBUG(COM, "Open fd=%d to get other irq", cmd_fd);
+
+	memset(&cmd_params, 0, sizeof(struct sxe2_ioctl_other_evt_get));
+
+	pthread_mutex_lock(&cdev->config.lock);
+	ret = ioctl(cmd_fd, SXE2_COM_CMD_EVT_CAUSE_GET, &cmd_params);
+	if (ret < 0) {
+		PMD_LOG_ERR(COM, "Failed to set others evt, fd=%d, ret=%d, err:%s",
+			   cmd_fd, ret, strerror(errno));
+		ret = -EIO;
+		pthread_mutex_unlock(&cdev->config.lock);
+		goto l_end;
+	}
+	pthread_mutex_unlock(&cdev->config.lock);
+	*event = cmd_params.evt_cause;
+
+l_end:
+	return ret;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(sxe2_drv_dev_reset_irq_set)
+int32_t
+sxe2_drv_dev_reset_irq_set(struct sxe2_common_device *cdev, int32_t efd)
+{
+	int32_t ret = 0;
+	int32_t cmd_fd = 0;
+	struct sxe2_ioctl_reset_sub_set cmd_params;
+
+	if (cdev->config.kernel_reset) {
+		ret = -EPERM;
+		PMD_LOG_WARN(COM, "kernel reset, need restart app.");
+		goto l_end;
+	}
+
+	cmd_fd = SXE2_CDEV_TO_CMD_FD(cdev);
+	if (cmd_fd < 0) {
+		ret = -EBADF;
+		PMD_LOG_ERR(COM, "Failed to exec cmd, fd=%d", cmd_fd);
+		goto l_end;
+	}
+
+	PMD_LOG_DEBUG(COM, "Open fd=%d to set reset irq, efd=%d",
+		cmd_fd, efd);
+
+	memset(&cmd_params, 0, sizeof(struct sxe2_ioctl_reset_sub_set));
+	cmd_params.eventfd = efd;
+
+	pthread_mutex_lock(&cdev->config.lock);
+	ret = ioctl(cmd_fd, SXE2_COM_CMD_RST_IRQ_REQ, &cmd_params);
+	if (ret < 0) {
+		PMD_LOG_ERR(COM, "Failed to set reset irqs, fd=%d, ret=%d, err:%s",
+			   cmd_fd, ret, strerror(errno));
+		ret = -EIO;
+		pthread_mutex_unlock(&cdev->config.lock);
+		goto l_end;
+	}
+	pthread_mutex_unlock(&cdev->config.lock);
+
+l_end:
+	return ret;
+}
+
 RTE_EXPORT_INTERNAL_SYMBOL(sxe2_drv_dev_mmap)
 void
 *sxe2_drv_dev_mmap(struct sxe2_common_device *cdev, uint8_t bar_idx, uint64_t len, uint64_t offset)
@@ -223,7 +395,7 @@ sxe2_drv_dev_munmap(struct sxe2_common_device *cdev, void *virt, uint64_t len)
 RTE_EXPORT_INTERNAL_SYMBOL(sxe2_drv_dev_dma_map)
 int32_t
 sxe2_drv_dev_dma_map(struct sxe2_common_device *cdev, uint64_t vaddr,
-			uint64_t iova, uint64_t size)
+		     uint64_t iova, uint64_t size)
 {
 	struct sxe2_ioctl_iommu_dma_map cmd_params;
 	enum rte_iova_mode iova_mode;
diff --git a/drivers/common/sxe2/sxe2_ioctl_chnl_func.h b/drivers/common/sxe2/sxe2_ioctl_chnl_func.h
index e5d8cad1e0..368be16d4f 100644
--- a/drivers/common/sxe2/sxe2_ioctl_chnl_func.h
+++ b/drivers/common/sxe2/sxe2_ioctl_chnl_func.h
@@ -29,6 +29,7 @@ int32_t
 sxe2_drv_dev_open(struct sxe2_common_device *cdev,
 		struct rte_pci_device *pci_dev);
 
+
 __rte_internal
 void
 sxe2_drv_dev_close(struct sxe2_common_device *cdev);
@@ -37,6 +38,23 @@ __rte_internal
 int32_t
 sxe2_drv_dev_handshake(struct sxe2_common_device *cdev);
 
+__rte_internal
+int32_t
+sxe2_drv_dev_rxq_irq_set(struct sxe2_common_device *cdev, uint16_t base_irq,
+			 int32_t *efd, uint16_t nb_irq);
+
+__rte_internal
+int32_t
+sxe2_drv_dev_other_irq_set(struct sxe2_common_device *cdev, int32_t efd, uint64_t event);
+
+__rte_internal
+int32_t
+sxe2_drv_dev_other_irq_get(struct sxe2_common_device *cdev, uint64_t *event);
+
+__rte_internal
+int32_t
+sxe2_drv_dev_reset_irq_set(struct sxe2_common_device *cdev, int32_t fd);
+
 __rte_internal
 void
 *sxe2_drv_dev_mmap(struct sxe2_common_device *cdev, uint8_t bar_idx,
diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index 8c8f16863e..fc4466556b 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -68,4 +68,5 @@ sources += files(
         'sxe2_security.c',
         'sxe2_mp.c',
         'sxe2_stats.c',
+        'sxe2_irq.c',
 )
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.c b/drivers/net/sxe2/sxe2_cmd_chnl.c
index a1fc8a50e3..d1f15084ed 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.c
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.c
@@ -348,6 +348,48 @@ int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter)
 	return ret;
 }
 
+int32_t sxe2_drv_rxq_bind_irq(struct sxe2_adapter *adapter, uint16_t rxq_idx, uint16_t msix_idx)
+{
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_drv_queue_irq_bind_req req = {0};
+	int32_t ret = 0;
+
+	req.msix_idx = msix_idx;
+	req.q_idx = rxq_idx;
+	req.itr_idx = 0;
+	req.bind = true;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_EVT_IRQ_BAND_RXQ,
+				 &req, sizeof(req),
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "rxq bind irq failed, ret=%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_rxq_unbind_irq(struct sxe2_adapter *adapter, uint16_t rxq_idx)
+{
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_drv_queue_irq_bind_req req = {0};
+	int32_t ret = 0;
+
+	req.bind = false;
+	req.q_idx = rxq_idx;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_EVT_IRQ_BAND_RXQ,
+				 &req, sizeof(req),
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "rxq unbind irq failed, ret=%d", ret);
+
+	return ret;
+}
+
 int32_t sxe2_drv_get_mac_stats(struct sxe2_adapter *adapter)
 {
 	int32_t ret = 0;
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.h b/drivers/net/sxe2/sxe2_cmd_chnl.h
index 80ad10ac00..3eb30078e1 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.h
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.h
@@ -118,4 +118,8 @@ int32_t sxe2_drv_rss_hf_clear(struct sxe2_adapter *adapter);
 
 int32_t sxe2_drv_ptp_gettime(struct sxe2_adapter *adapter, struct sxe2_rx_queue *rxq);
 
+int32_t sxe2_drv_rxq_bind_irq(struct sxe2_adapter *adapter, uint16_t rxq_idx, uint16_t msix_idx);
+
+int32_t sxe2_drv_rxq_unbind_irq(struct sxe2_adapter *adapter, uint16_t rxq_idx);
+
 #endif /* SXE2_CMD_CHNL_H */
diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h
index 7935a28dc1..807edfc4d6 100644
--- a/drivers/net/sxe2/sxe2_drv_cmd.h
+++ b/drivers/net/sxe2/sxe2_drv_cmd.h
@@ -556,6 +556,14 @@ struct __rte_aligned(4) __rte_packed_begin sxe2_drv_tx_map_req {
 	uint8_t pool_idx;
 } __rte_packed_end;
 
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_queue_irq_bind_req {
+	uint16_t q_idx;
+	uint16_t msix_idx;
+	uint8_t itr_idx;
+	uint8_t bind;
+	uint8_t rsv[2];
+} __rte_packed_end;
+
 enum sxe2_drv_cmd_module {
 	SXE2_DRV_CMD_MODULE_HANDSHAKE = 0,
 	SXE2_DRV_CMD_MODULE_DEV = 1,
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index dbe1a2bce1..f3fee74ddf 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -22,6 +22,7 @@
 #include <rte_eal_paging.h>
 
 #include "sxe2_ethdev.h"
+#include "sxe2_irq.h"
 #include "sxe2_drv_cmd.h"
 #include "sxe2_cmd_chnl.h"
 #include "sxe2_tx.h"
@@ -107,6 +108,9 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.rx_queue_release           = sxe2_rx_queue_release,
 	.tx_queue_setup             = sxe2_tx_queue_setup,
 	.tx_queue_release           = sxe2_tx_queue_release,
+	.rx_queue_intr_enable       = sxe2_rx_queue_intr_enable,
+	.rx_queue_intr_disable      = sxe2_rx_queue_intr_disable,
+
 	.rxq_info_get               = sxe2_rx_queue_info_get,
 	.txq_info_get               = sxe2_tx_queue_info_get,
 	.rx_burst_mode_get          = sxe2_rx_burst_mode_get,
@@ -177,6 +181,8 @@ static int32_t sxe2_dev_stop(struct rte_eth_dev *dev)
 	if (adapter->started == 0)
 		goto l_end;
 
+	sxe2_rxq_intr_disable(dev);
+
 	sxe2_txqs_all_stop(dev);
 	sxe2_rxqs_all_stop(dev);
 
@@ -215,6 +221,12 @@ static int32_t sxe2_dev_start(struct rte_eth_dev *dev)
 		goto l_end;
 	}
 
+	ret = sxe2_rxq_intr_enable(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to enable rx queue intr");
+		goto l_rxq_intr_err;
+	}
+
 	ret = sxe2_queues_start(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "enable queues failed");
@@ -224,7 +236,10 @@ static int32_t sxe2_dev_start(struct rte_eth_dev *dev)
 	dev->data->dev_started = 1;
 	adapter->started = 1;
 	goto l_end;
+
 l_start_queues_err:
+	(void)sxe2_rxq_intr_disable(dev);
+l_rxq_intr_err:
 	(void)sxe2_filter_rule_stop(dev);
 l_end:
 	return ret;
@@ -615,6 +630,8 @@ static int32_t sxe2_func_caps_get(struct sxe2_adapter *adapter)
 
 	sxe2_sw_queue_ctx_hw_cap_set(adapter, &dev_caps.queue_caps);
 
+	sxe2_sw_irq_ctx_hw_cap_set(adapter, &dev_caps.msix_caps);
+
 	sxe2_sw_rss_ctx_hw_cap_set(adapter, &dev_caps.rss_hash_caps);
 
 	sxe2_sw_vsi_ctx_hw_cap_set(adapter, &dev_caps.vsi_caps);
@@ -636,6 +653,41 @@ static int32_t sxe2_dev_caps_get(struct sxe2_adapter *adapter)
 	return ret;
 }
 
+uint32_t sxe2_pci_map_read_reg(struct sxe2_adapter *adapter,
+		enum sxe2_pci_map_resource res_type, uint16_t idx_in_func)
+{
+	void *reg_addr;
+	uint32_t value;
+
+	reg_addr = sxe2_pci_map_addr_get(adapter, res_type, idx_in_func);
+	if (unlikely(reg_addr == NULL)) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "reg addr:0x%p is error.", reg_addr);
+		value = SXE2_PCI_MAP_INVALID_VAL;
+		goto l_ret;
+	}
+
+	value = SXE2_PCI_REG_READ(reg_addr);
+
+l_ret:
+	return value;
+}
+
+void sxe2_pci_map_write_reg(struct sxe2_adapter *adapter,
+		enum sxe2_pci_map_resource res_type, uint16_t idx_in_func, uint32_t value)
+{
+	void *reg_addr;
+
+	reg_addr = sxe2_pci_map_addr_get(adapter, res_type, idx_in_func);
+	if (unlikely(reg_addr == NULL)) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "reg addr:0x%p is error.", reg_addr);
+		goto l_ret;
+	}
+
+	SXE2_PCI_REG_WRITE_WC(reg_addr, value);
+l_ret:
+	return;
+}
+
 int32_t sxe2_dev_pci_seg_map(struct sxe2_adapter *adapter,
 			     enum sxe2_pci_map_resource res_type,
 			     uint64_t org_len,
@@ -715,6 +767,27 @@ static int32_t sxe2_hw_init(struct rte_eth_dev *dev)
 	return ret;
 }
 
+static int32_t sxe2_sw_init(struct rte_eth_dev *dev)
+{
+	int32_t ret = -1;
+
+	PMD_INIT_FUNC_TRACE();
+
+	ret = sxe2_sw_irq_ctxt_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to sw irq ctxt init, ret=[%d]", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+static void sxe2_sw_uninit(struct rte_eth_dev *dev)
+{
+	sxe2_sw_irq_ctxt_uninit(dev);
+}
+
 int32_t sxe2_dev_pci_res_seg_map(struct sxe2_adapter *adapter,
 				 uint32_t res_type,
 				 uint32_t item_cnt,
@@ -1065,6 +1138,18 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 		goto init_dev_info_err;
 	}
 
+	ret = sxe2_sw_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to initialize sw parameters, ret=[%d]", ret);
+		goto init_sw_err;
+	}
+
+	ret = sxe2_intr_init(dev);
+	if (ret != 0) {
+		PMD_LOG_ERR(INIT, "Failed to initialize interrupt, ret:%d", ret);
+		goto init_irq_err;
+	}
+
 	ret = sxe2_eth_init(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "Failed to initialize eth parameters, ret=%d", ret);
@@ -1109,6 +1194,10 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 init_rss_err:
 	sxe2_security_uinit(dev);
 init_security_err:
+	sxe2_intr_uninit(dev);
+init_irq_err:
+	sxe2_sw_uninit(dev);
+init_sw_err:
 	sxe2_eth_uinit(dev);
 init_eth_err:
 init_dev_info_err:
@@ -1132,8 +1221,10 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
 	(void)sxe2_sched_uinit(dev);
 	sxe2_vsi_uninit(dev);
 	sxe2_security_uinit(dev);
-	sxe2_dev_pci_map_uinit(dev);
+	sxe2_intr_uninit(dev);
+	sxe2_sw_uninit(dev);
 	sxe2_eth_uinit(dev);
+	sxe2_dev_pci_map_uinit(dev);
 
 l_end:
 	return 0;
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index f226d6d5f9..8dcff8af37 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -355,6 +355,12 @@ int32_t sxe2_dev_pci_res_seg_map(struct sxe2_adapter *adapter,
 				 uint32_t item_cnt,
 				 uint32_t item_base);
 
+void sxe2_pci_map_write_reg(struct sxe2_adapter *adapter,
+		enum sxe2_pci_map_resource res_type, uint16_t idx_in_func, uint32_t value);
+
+uint32_t sxe2_pci_map_read_reg(struct sxe2_adapter *adapter,
+		enum sxe2_pci_map_resource res_type, uint16_t idx_in_func);
+
 void sxe2_dev_pci_seg_unmap(struct sxe2_adapter *adapter, uint32_t res_type);
 
 int32_t sxe2_dev_pci_map_init(struct rte_eth_dev *dev);
diff --git a/drivers/net/sxe2/sxe2_irq.c b/drivers/net/sxe2/sxe2_irq.c
new file mode 100644
index 0000000000..fd9cd4b1ff
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_irq.c
@@ -0,0 +1,942 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <stdint.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+#include <ethdev_pci.h>
+#include <ethdev_driver.h>
+#include <rte_alarm.h>
+#include <fcntl.h>
+#include <rte_stdatomic.h>
+
+#include "sxe2_ethdev.h"
+#include "sxe2_irq.h"
+#include "sxe2_common_log.h"
+#include "sxe2_queue.h"
+#include "sxe2_drv_cmd.h"
+#include "sxe2_ioctl_chnl_func.h"
+#include "sxe2vf_regs.h"
+#include "sxe2_host_regs.h"
+#include "sxe2_cmd_chnl.h"
+
+#define SXE2_INT_EVENT_OICR_ALL (SXE2_PF_INT_OICR_SWINT | \
+					SXE2_PF_INT_OICR_LAN_TX_ERR | \
+					SXE2_PF_INT_OICR_LAN_RX_ERR | \
+					SXE2_PF_INT_OICR_FW)
+
+#define MAX_EVENT_PENDING (16)
+
+struct sxe2_event_element {
+	TAILQ_ENTRY(sxe2_event_element) next;
+	struct rte_eth_dev *dev;
+};
+
+struct sxe2_event_handler {
+	RTE_ATOMIC(uint16_t)ndev;
+	rte_thread_t tid;
+	int32_t fd[2];
+	rte_spinlock_t  lock;
+	TAILQ_HEAD(event_list, sxe2_event_element) pending;
+};
+static struct sxe2_event_handler event_handler = {
+	.fd = {-1, -1},
+};
+
+static RTE_ATOMIC(uint32_t)event_thread_run;
+
+
+static void sxe2_event_irq_common_handler(struct sxe2_adapter *adapter, uint64_t oicr)
+{
+	struct rte_eth_dev *dev = &rte_eth_devices[adapter->dev_info.dev_data->port_id];
+
+	if (oicr & RTE_BIT32(SXE2_COM_EC_LINK_CHG)) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "OICR=%" PRIu64, oicr);
+		(void)sxe2_drv_mac_link_status_get(adapter);
+		if (rte_eal_process_type() == RTE_PROC_PRIMARY)
+			rte_eth_dev_callback_process(dev,
+						     RTE_ETH_EVENT_INTR_LSC,
+						     NULL);
+	}
+}
+
+static uint32_t sxe2_event_intr_handle(void *param __rte_unused)
+{
+	struct sxe2_event_handler *handler = &event_handler;
+	struct sxe2_adapter *adapter;
+	struct sxe2_event_element *pos;
+	struct sxe2_event_element *tmp;
+	int32_t ret = 0;
+	uint64_t oicr = 0;
+	TAILQ_HEAD(event_list, sxe2_event_element) pending;
+	int8_t unused[MAX_EVENT_PENDING];
+	ssize_t nr;
+
+	while (rte_atomic_load_explicit(&event_thread_run, rte_memory_order_relaxed)) {
+		nr = read(handler->fd[0], &unused, sizeof(unused));
+		if (nr <= 0)
+			break;
+
+		rte_spinlock_lock(&handler->lock);
+		TAILQ_INIT(&pending);
+		TAILQ_CONCAT(&pending, &handler->pending, next);
+		rte_spinlock_unlock(&handler->lock);
+
+		TAILQ_FOREACH_SAFE(pos, &pending, next, tmp) {
+			TAILQ_REMOVE(&pending, pos, next);
+			adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(pos->dev);
+
+			ret = sxe2_drv_dev_other_irq_get(adapter->cdev, &oicr);
+			sxe2_event_irq_common_handler(adapter, oicr);
+
+			rte_free(pos);
+		}
+	}
+
+	return ret;
+}
+
+static int32_t sxe2_event_intr_handler_init(void)
+{
+	struct sxe2_event_handler *handler = &event_handler;
+	int32_t ret = 0;
+	int err = 0;
+
+	PMD_INIT_FUNC_TRACE();
+	if (rte_atomic_fetch_add_explicit(&handler->ndev, 1, rte_memory_order_acq_rel) >= 1) {
+		ret = 0;
+		PMD_LOG_DEBUG(INIT, "%s: ndev > 1", __func__);
+		goto l_end;
+	}
+
+#if defined(RTE_EXEC_ENV_IS_WINDOWS) && RTE_EXEC_ENV_IS_WINDOWS != 0
+	err = _pipe(handler->fd, MAX_EVENT_PENDING, O_BINARY);
+#else
+	err = pipe(handler->fd);
+#endif
+	if (err != 0) {
+		ret = -ECHILD;
+		rte_atomic_fetch_sub_explicit(&handler->ndev, 1, rte_memory_order_release);
+		PMD_LOG_ERR(INIT, "%s: pipe failed", __func__);
+		goto l_end;
+	}
+
+	rte_atomic_store_explicit(&event_thread_run, 1, rte_memory_order_relaxed);
+
+	TAILQ_INIT(&handler->pending);
+	rte_spinlock_init(&handler->lock);
+
+	if (rte_thread_create_internal_control(&handler->tid, "sxe2-event",
+				sxe2_event_intr_handle, NULL)) {
+		PMD_LOG_ERR(INIT, "%s: thread create failed", __func__);
+		rte_atomic_fetch_sub_explicit(&handler->ndev, 1, rte_memory_order_release);
+		ret = -ECHILD;
+		goto l_end;
+	}
+	ret = 0;
+l_end:
+	return ret;
+}
+
+static void sxe2_event_intr_handler_uinit(void)
+{
+	struct sxe2_event_handler *handler = &event_handler;
+	struct sxe2_event_element *pos;
+	struct sxe2_event_element *tmp;
+	ssize_t nw = 0;
+	int8_t notify_byte = 0;
+
+	PMD_INIT_FUNC_TRACE();
+	if (rte_atomic_fetch_sub_explicit(&handler->ndev, 1, rte_memory_order_acq_rel) > 1) {
+		PMD_LOG_DEBUG(INIT, "event handler uinit, ndev > 0");
+		return;
+	}
+
+	rte_atomic_store_explicit(&event_thread_run, 0, rte_memory_order_relaxed);
+	nw = write(handler->fd[1], &notify_byte, 1);
+	RTE_SET_USED(nw);
+
+	(void)rte_thread_join(handler->tid, NULL);
+
+	if (handler->fd[0] != -1) {
+		close(handler->fd[0]);
+		handler->fd[0] = -1;
+	}
+	if (handler->fd[1] != -1) {
+		close(handler->fd[1]);
+		handler->fd[1] = -1;
+	}
+
+	rte_spinlock_lock(&handler->lock);
+	TAILQ_FOREACH_SAFE(pos, &handler->pending, next, tmp) {
+		TAILQ_REMOVE(&handler->pending, pos, next);
+		rte_free(pos);
+	}
+	rte_spinlock_unlock(&handler->lock);
+}
+
+static void sxe2_event_intr_post(struct rte_eth_dev *dev)
+{
+	struct sxe2_event_handler *handler = &event_handler;
+	struct sxe2_event_element *elem = rte_malloc(NULL, sizeof(struct sxe2_event_element), 0);
+	int8_t notify_byte = 0;
+	ssize_t nw = 0;
+
+	if (!elem)
+		goto l_end;
+
+	elem->dev = dev;
+
+	rte_spinlock_lock(&handler->lock);
+	TAILQ_INSERT_TAIL(&handler->pending, elem, next);
+	rte_spinlock_unlock(&handler->lock);
+
+	nw = write(handler->fd[1], &notify_byte, 1);
+	RTE_SET_USED(nw);
+
+l_end:
+	return;
+}
+
+static void sxe2_interrupt_handler_other(void *arg)
+{
+	struct rte_eth_dev *dev = (struct rte_eth_dev *)arg;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t eventfd = adapter->irq_ctxt.other_event_fd;
+	int32_t ret = 0;
+	uint64_t buf = 0;
+
+	ret = read(eventfd, &buf, sizeof(buf));
+	if (ret != sizeof(buf)) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "read eventfd[%d] failed, ret:%d, errno:%d.",
+				eventfd, ret, errno);
+	}
+
+	sxe2_event_intr_post(dev);
+}
+
+static void sxe2_interrupt_handler_reset(void *arg)
+{
+	struct rte_eth_dev *dev = (struct rte_eth_dev *)arg;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t resetfd = adapter->irq_ctxt.reset_event_fd;
+	int32_t ret = 0;
+	uint64_t buf = 0;
+
+	ret = read(resetfd, &buf, sizeof(buf));
+	if (ret != sizeof(buf)) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "read resetfd[%d] failed, ret:%d, errno:%d.",
+				resetfd, ret, errno);
+	}
+
+	sxe2_drv_cmd_close(adapter->cdev);
+
+	rte_eth_dev_callback_process(dev, RTE_ETH_EVENT_INTR_RMV, NULL);
+}
+
+int32_t sxe2_sw_irq_ctxt_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_irq_context *irq_ctxt = &adapter->irq_ctxt;
+	int32_t ret = 0;
+
+	irq_ctxt->rxq_avail_cnt = irq_ctxt->max_cnt_hw;
+	irq_ctxt->rxq_base_idx_in_pf = irq_ctxt->base_idx_in_func;
+	irq_ctxt->reset_event_fd = -1;
+	irq_ctxt->other_event_fd = -1;
+
+	return ret;
+}
+
+void sxe2_sw_irq_ctxt_uninit(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	memset(&adapter->irq_ctxt, 0, sizeof(adapter->irq_ctxt));
+	adapter->irq_ctxt.reset_event_fd = -1;
+	adapter->irq_ctxt.other_event_fd = -1;
+}
+
+void sxe2_sw_irq_ctx_hw_cap_set(struct sxe2_adapter *adapter,
+				struct sxe2_drv_msix_caps *msix_caps)
+{
+	adapter->irq_ctxt.max_cnt_hw = msix_caps->msix_vectors_cnt;
+	adapter->irq_ctxt.base_idx_in_func = msix_caps->base_idx_in_func;
+}
+
+static int32_t sxe2_intr_handler_cfg(struct rte_intr_handle *handle,
+		int32_t fd, rte_intr_callback_fn cb, void *cb_arg)
+{
+	int32_t ret = 0;
+
+	ret = rte_intr_fd_set(handle, fd);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to set intr_handle->fd, error %i (%s)",
+			errno, strerror(errno));
+		goto err;
+	}
+
+	ret = rte_intr_type_set(handle, RTE_INTR_HANDLE_EXT);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to set intr_handle->type, error %i (%s)",
+			errno, strerror(errno));
+		goto err;
+	}
+
+	ret = rte_intr_callback_register(handle, cb, cb_arg);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to initialize register intr callback, ret=%d", ret);
+		goto err;
+	}
+err:
+	return ret;
+}
+
+static struct rte_intr_handle *
+sxe2_intr_handler_create(int32_t fd, rte_intr_callback_fn cb, void *cb_arg)
+{
+	struct rte_intr_handle *tmp_intr_handle = NULL;
+	int32_t ret = 0;
+
+	tmp_intr_handle = rte_intr_instance_alloc(RTE_INTR_INSTANCE_F_PRIVATE);
+	if (!tmp_intr_handle) {
+		PMD_LOG_ERR(INIT, "Failed to alloc memory for intr_handler, error %i (%s)",
+			errno, strerror(errno));
+		goto err;
+	}
+
+	ret = rte_intr_fd_set(tmp_intr_handle, fd);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to set intr_handle->fd, error %i (%s)",
+				errno, strerror(errno));
+		goto err;
+	}
+
+	ret = rte_intr_type_set(tmp_intr_handle, RTE_INTR_HANDLE_EXT);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to set intr_handle->type, error %i (%s)",
+				errno, strerror(errno));
+		goto err;
+	}
+
+	ret = rte_intr_callback_register(tmp_intr_handle, cb, cb_arg);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to initialize register intr callback, ret=%d", ret);
+		goto err;
+	}
+
+	return tmp_intr_handle;
+err:
+	rte_intr_instance_free(tmp_intr_handle);
+	return NULL;
+}
+
+static void sxe2_intr_handler_destroy(struct rte_intr_handle *intr_handle,
+				  rte_intr_callback_fn cb, void *cb_arg)
+{
+	if (!intr_handle)
+		return;
+
+	if (rte_intr_fd_get(intr_handle) >= 0)
+		(void)rte_intr_callback_unregister(intr_handle, cb, cb_arg);
+	rte_intr_instance_free(intr_handle);
+}
+
+static int32_t sxe2_event_intr_fd_create(void)
+{
+	int32_t fd = 0;
+
+	fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+	if (fd < 0) {
+		PMD_LOG_ERR(INIT, "Can't setup eventfd, error %i (%s)",
+			errno, strerror(errno));
+		goto err;
+	}
+
+	return fd;
+err:
+	return -EBADF;
+}
+
+static void sxe2_event_intr_fd_destroy(int32_t fd)
+{
+	if (fd >= 0)
+		close(fd);
+}
+
+static int32_t sxe2_other_intr_register(struct rte_eth_dev *dev, int32_t fd)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	uint64_t event = RTE_BIT32(SXE2_COM_EC_LINK_CHG) |
+				RTE_BIT32(SXE2_COM_SW_MODE_LEGACY) |
+				RTE_BIT32(SXE2_COM_SW_MODE_SWITCHDEV) |
+				RTE_BIT32(SXE2_COM_FC_ST_CHANGE);
+
+	ret = sxe2_drv_dev_other_irq_set(adapter->cdev, fd, event);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set other irq, ret=%d", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+static void sxe2_other_intr_unregister(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	ret = sxe2_drv_dev_other_irq_set(adapter->cdev, -1, 0);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set other irq, ret=%d", ret);
+}
+
+static int32_t sxe2_reset_intr_register(struct rte_eth_dev *dev, int32_t fd)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	ret = sxe2_drv_dev_reset_irq_set(adapter->cdev, fd);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set reset irq, ret=%d", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+static void sxe2_reset_intr_unregister(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	ret = sxe2_drv_dev_reset_irq_set(adapter->cdev, -1);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set reset irq, ret=%d", ret);
+}
+
+int32_t sxe2_intr_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct rte_pci_device *pci_dev = SXE2_DEV_TO_PCI(dev);
+	struct rte_intr_handle *reset_handle = NULL;
+	int32_t ofd = -1;
+	int32_t rfd = -1;
+	int32_t ret = 0;
+
+	PMD_INIT_FUNC_TRACE();
+
+	ofd = sxe2_event_intr_fd_create();
+	if (ofd < 0) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to create event intr fd");
+		ret = -EBADF;
+		goto l_end;
+	}
+
+	ret = sxe2_intr_handler_cfg(pci_dev->intr_handle,
+			ofd, sxe2_interrupt_handler_other, dev);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to create intr handler");
+		goto l_err_create_other_handler;
+	}
+
+	ret = sxe2_event_intr_handler_init();
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Event handler init failed, ret=%d", ret);
+		goto l_err_event_intr_handler_init;
+	}
+
+	ret = sxe2_other_intr_register(dev, ofd);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to register other intr, ret=%d", ret);
+		goto l_err_register_other_intr;
+	}
+	adapter->irq_ctxt.other_event_fd = ofd;
+
+	rfd = sxe2_event_intr_fd_create();
+	if (rfd < 0) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to create event intr fd");
+		ret = -EBADF;
+		goto l_err_create_reset_fd;
+	}
+
+	reset_handle = sxe2_intr_handler_create(rfd, sxe2_interrupt_handler_reset, dev);
+	if (!reset_handle) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to create intr handler");
+		ret = -ENOMEM;
+		goto l_err_create_reset_handler;
+	}
+	adapter->irq_ctxt.reset_handle = reset_handle;
+
+	ret = sxe2_reset_intr_register(dev, rfd);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to register reset intr, ret=%d", ret);
+		goto l_err_register_reset_intr;
+	}
+	adapter->irq_ctxt.reset_event_fd = rfd;
+
+	goto l_end;
+l_err_register_reset_intr:
+	sxe2_intr_handler_destroy(reset_handle, sxe2_interrupt_handler_reset, dev);
+	adapter->irq_ctxt.reset_handle = NULL;
+l_err_create_reset_handler:
+	sxe2_event_intr_fd_destroy(rfd);
+l_err_create_reset_fd:
+	sxe2_other_intr_unregister(dev);
+	adapter->irq_ctxt.other_event_fd = -1;
+l_err_register_other_intr:
+	sxe2_event_intr_handler_uinit();
+l_err_event_intr_handler_init:
+	sxe2_intr_handler_destroy(pci_dev->intr_handle,
+			sxe2_interrupt_handler_other, dev);
+	pci_dev->intr_handle = NULL;
+l_err_create_other_handler:
+	sxe2_event_intr_fd_destroy(ofd);
+l_end:
+	return ret;
+}
+
+void sxe2_intr_uninit(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct rte_pci_device *pci_dev = SXE2_DEV_TO_PCI(dev);
+
+	sxe2_reset_intr_unregister(dev);
+	sxe2_intr_handler_destroy(adapter->irq_ctxt.reset_handle,
+				sxe2_interrupt_handler_reset, dev);
+	sxe2_event_intr_fd_destroy(adapter->irq_ctxt.reset_event_fd);
+	sxe2_other_intr_unregister(dev);
+	sxe2_event_intr_handler_uinit();
+	sxe2_intr_handler_destroy(pci_dev->intr_handle,
+				sxe2_interrupt_handler_other, dev);
+	sxe2_event_intr_fd_destroy(adapter->irq_ctxt.other_event_fd);
+
+	adapter->irq_ctxt.other_event_fd = -1;
+	adapter->irq_ctxt.reset_event_fd = -1;
+	pci_dev->intr_handle = NULL;
+	adapter->irq_ctxt.reset_handle = NULL;
+}
+
+static int32_t sxe2_rxq_intr_efd_alloc(struct rte_eth_dev *dev, int32_t *efd)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	int32_t fd = 0;
+
+	fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
+	if (fd < 0) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Can't setup eventfd, error %i (%s)",
+			errno, strerror(errno));
+		ret = -EBADF;
+		goto l_end;
+	}
+
+	*efd = fd;
+
+l_end:
+	return ret;
+}
+
+static void sxe2_rxq_intr_efd_free(int32_t efd)
+{
+	close(efd);
+}
+
+static void sxe2_pci_hw_int_itr_set(struct sxe2_adapter *adapter, uint16_t msix_idx, uint16_t itr)
+{
+	sxe2_pci_map_write_reg(adapter, SXE2_PCI_MAP_RES_IRQ_ITR, msix_idx, itr);
+}
+
+static void sxe2_pci_hw_irq_disable(struct sxe2_adapter *adapter, uint16_t irq_idx)
+{
+	uint32_t value = (SXE2_ITR_IDX_NONE << SXE2_VF_DYN_CTL_ITR_IDX_S);
+
+	sxe2_pci_map_write_reg(adapter, SXE2_PCI_MAP_RES_IRQ_DYN, irq_idx, value);
+}
+
+static void sxe2_pci_hw_irq_enable(struct sxe2_adapter *adapter, uint16_t irq_idx)
+{
+	uint32_t value = SXE2_VF_DYN_CTL_INTENABLE |
+		SXE2_VF_DYN_CTL_CLEARPBA |
+			(SXE2_ITR_IDX_NONE << SXE2_VF_DYN_CTL_ITR_IDX_S);
+
+	sxe2_pci_map_write_reg(adapter, SXE2_PCI_MAP_RES_IRQ_DYN, irq_idx, value);
+}
+
+static uint32_t sxe2_pci_hw_irq_dyn_ctl_read(struct sxe2_adapter *adapter, uint16_t irq_idx)
+{
+	return sxe2_pci_map_read_reg(adapter, SXE2_PCI_MAP_RES_IRQ_DYN, irq_idx);
+}
+
+static void sxe2_pci_hw_msix_disable(struct sxe2_adapter *adapter, uint16_t irq_idx)
+{
+	sxe2_pci_map_write_reg(adapter, SXE2_PCI_MAP_RES_IRQ_MSIX,
+			irq_idx, SXE2VF_BAR4_MSIX_DISABLE);
+}
+
+static void sxe2_pci_hw_msix_enable(struct sxe2_adapter *adapter, uint16_t irq_idx)
+{
+	sxe2_pci_map_write_reg(adapter, SXE2_PCI_MAP_RES_IRQ_MSIX,
+			irq_idx, SXE2VF_BAR4_MSIX_ENABLE);
+}
+
+static void sxe2_pci_hw_irq_trigger(struct sxe2_adapter *adapter, uint16_t irq_idx)
+{
+	sxe2_pci_map_write_reg(adapter, SXE2_PCI_MAP_RES_IRQ_DYN, irq_idx,
+			(SXE2VF_ITR_IDX_NONE << SXE2VF_DYN_CTL_ITR_IDX_SHIFT) |
+			SXE2VF_DYN_CTL_SWINT_TRIG | SXE2VF_DYN_CTL_INTENABLE_MSK);
+}
+
+static void sxe2_pci_hw_irq_clear_pba(struct sxe2_adapter *adapter, uint16_t irq_idx)
+{
+	sxe2_pci_map_write_reg(adapter, SXE2_PCI_MAP_RES_IRQ_DYN, irq_idx,
+		(SXE2VF_ITR_IDX_NONE << SXE2VF_DYN_CTL_ITR_IDX_SHIFT) |
+		SXE2VF_DYN_CTL_CLEARPBA | SXE2VF_DYN_CTL_INTENABLE_MSK);
+}
+
+static void sxe2_rxq_msix_cfg_unmap(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_irq_context *irq_ctxt = &adapter->irq_ctxt;
+	uint16_t rxq_cnt = adapter->q_ctxt.qp_cnt_assign;
+	uint16_t i = 0;
+
+	for (i = 0; i < irq_ctxt->rxq_irq_cnt; i++)
+		sxe2_pci_hw_irq_disable(adapter, irq_ctxt->rxq_msix_idx[i]);
+
+	for (i = 0; i < rxq_cnt; i++)
+		(void)sxe2_drv_rxq_unbind_irq(adapter, i);
+}
+
+static int32_t sxe2_rxq_msix_cfg_map(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_irq_context *irq_ctxt = &adapter->irq_ctxt;
+	int32_t ret = 0;
+	uint32_t val;
+	uint16_t rxq_cnt = dev->data->nb_rx_queues;
+	uint16_t i = 0;
+	uint8_t rx_low_latency = adapter->devargs.rx_low_latency;
+
+	for (i = 0; i < irq_ctxt->rxq_irq_cnt; i++) {
+		sxe2_pci_hw_irq_disable(adapter, irq_ctxt->rxq_msix_idx[i]);
+		if (rx_low_latency) {
+			sxe2_pci_hw_int_itr_set(adapter, irq_ctxt->rxq_msix_idx[i],
+					SXE2_ITR_INTERVAL_LOW);
+		} else {
+			sxe2_pci_hw_int_itr_set(adapter, irq_ctxt->rxq_msix_idx[i],
+					SXE2_ITR_INTERVAL_NORMAL);
+		}
+	}
+
+	for (i = 0; i < rxq_cnt; i++) {
+		ret = sxe2_drv_rxq_bind_irq(adapter, i, irq_ctxt->rxq_msix_idx[i]);
+		if (ret != 0) {
+			PMD_DEV_LOG_ERR(adapter, INIT, "RXQ[%u] bind IRQ[%u] failed.",
+				i, irq_ctxt->rxq_msix_idx[i]);
+			goto l_end;
+		}
+	}
+
+	for (i = 0; i < irq_ctxt->rxq_irq_cnt; i++)
+		sxe2_pci_hw_irq_enable(adapter, irq_ctxt->rxq_msix_idx[i]);
+
+	for (i = 0; i < irq_ctxt->rxq_irq_cnt; i++) {
+		val = sxe2_pci_hw_irq_dyn_ctl_read(adapter, i);
+		if ((val & SXE2VF_DYN_CTL_INTENABLE) == 0)
+			continue;
+
+		sxe2_pci_hw_msix_disable(adapter, i);
+		sxe2_pci_hw_irq_trigger(adapter, i);
+		val = sxe2_pci_hw_irq_dyn_ctl_read(adapter, i);
+		sxe2_pci_hw_irq_clear_pba(adapter, i);
+		val = sxe2_pci_hw_irq_dyn_ctl_read(adapter, i);
+		sxe2_pci_hw_msix_enable(adapter, i);
+	}
+
+l_end:
+	if (ret != 0)
+		sxe2_rxq_msix_cfg_unmap(dev);
+	return ret;
+}
+
+static int32_t sxe2_rxq_map_msix_intr(struct rte_eth_dev *dev,
+		uint16_t msix_base __rte_unused, uint16_t nb_msix,
+		uint16_t base_queue, uint16_t nb_queue)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_irq_context *irq_ctxt = &adapter->irq_ctxt;
+	uint32_t *msix_tbl = NULL;
+	int32_t ret = 0;
+	uint16_t i;
+
+	if (!nb_queue || !nb_msix || nb_queue < nb_msix) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Queue num[%u] or msix num[%u] is invalid.",
+				nb_queue, nb_msix);
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	msix_tbl = rte_zmalloc(NULL, sizeof(uint32_t) * nb_queue, 0);
+	if (!msix_tbl) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to alloc msix_tbl memory.");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+	for (i = 0; i < nb_queue; i++) {
+		msix_tbl[i] = i % nb_msix;
+		PMD_DEV_LOG_INFO(adapter, INIT, "Queue %u is binding to vect %u",
+				base_queue + i, msix_tbl[i]);
+	}
+
+	irq_ctxt->rxq_irq_cnt = nb_msix;
+	irq_ctxt->rxq_msix_idx = msix_tbl;
+
+l_end:
+	return ret;
+}
+
+static void sxe2_rxq_unmap_msix_intr(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_irq_context *irq_ctxt = &adapter->irq_ctxt;
+
+	rte_free(irq_ctxt->rxq_msix_idx);
+	irq_ctxt->rxq_msix_idx = NULL;
+	irq_ctxt->rxq_irq_cnt = 0;
+}
+
+static int32_t sxe2_rxq_intr_register(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_irq_context *irq_ctxt = &adapter->irq_ctxt;
+	struct rte_intr_handle *intr_handle = dev->intr_handle;
+	int32_t *efd_tbl = NULL;
+	uint16_t rxq_cnt = dev->data->nb_rx_queues;
+	uint16_t nb_msix = irq_ctxt->rxq_irq_cnt;
+	uint16_t i;
+	int32_t ret = 0;
+
+	if (rte_intr_type_set(intr_handle, RTE_INTR_HANDLE_EXT)) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set intr_handle->type, error %i (%s)",
+				errno, strerror(errno));
+		ret = -EPERM;
+		goto l_end;
+	}
+
+	efd_tbl = rte_zmalloc(NULL, sizeof(int32_t) * nb_msix, 0);
+	if (!efd_tbl) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to alloc efd_tbl memory.");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+
+	for (i = 0; i < nb_msix; i++) {
+		ret = sxe2_rxq_intr_efd_alloc(dev, &efd_tbl[i]);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, INIT, "Failed to alloc efd_tbl, ret=%d", ret);
+			goto l_free_efd_tbl;
+		}
+	}
+
+	if (rte_intr_vec_list_alloc(intr_handle, "sxe2 rxq int", rxq_cnt)) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to allocate %d rx_queues intr_vec",
+				rxq_cnt);
+		ret = -ENOMEM;
+		goto l_free_efd_tbl;
+	}
+
+	for	(i = 0; i < rxq_cnt; i++) {
+		ret = rte_intr_vec_list_index_set(intr_handle, i,
+				irq_ctxt->rxq_msix_idx[i] + RTE_INTR_VEC_RXTX_OFFSET);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set msix_tbl, ret=%d", ret);
+			goto l_free_efd_tbl;
+		}
+	}
+
+	for (i = 0; i < irq_ctxt->rxq_irq_cnt; i++) {
+		ret = rte_intr_efds_index_set(intr_handle, i, efd_tbl[i]);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set efd_tbl, ret=%d", ret);
+			goto l_free_efd_tbl;
+		}
+	}
+
+	if (rte_intr_nb_efd_set(intr_handle, rxq_cnt)) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Set intr nb efd failed, error %i (%s)",
+			errno, strerror(errno));
+		ret = -EPERM;
+		goto l_free_efd_tbl;
+	}
+
+	ret = sxe2_drv_dev_rxq_irq_set(adapter->cdev, 0, efd_tbl, nb_msix);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set rxq irq, ret=%d", ret);
+		goto l_free_efd_tbl;
+	}
+	irq_ctxt->rxq_event_fd = efd_tbl;
+
+	goto l_end;
+
+l_free_efd_tbl:
+	if (efd_tbl) {
+		for (i = 0; i < nb_msix; i++)
+			if (efd_tbl[i] >= 0)
+				sxe2_rxq_intr_efd_free(efd_tbl[i]);
+		rte_free(efd_tbl);
+	}
+	irq_ctxt->rxq_event_fd = NULL;
+
+	rte_intr_vec_list_free(intr_handle);
+l_end:
+	return ret;
+}
+
+static void sxe2_rxq_intr_unregister(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_irq_context *irq_ctxt = &adapter->irq_ctxt;
+	struct rte_intr_handle *intr_handle = dev->intr_handle;
+	int32_t efd = -1;
+	uint16_t msix_cnt = irq_ctxt->rxq_irq_cnt;
+	uint16_t i;
+
+	if (irq_ctxt->rxq_event_fd) {
+		for (i = 0; i < msix_cnt; i++) {
+			(void)sxe2_drv_dev_rxq_irq_set(adapter->cdev, i, &efd, 1);
+			sxe2_rxq_intr_efd_free(irq_ctxt->rxq_event_fd[i]);
+		}
+	}
+	rte_free(irq_ctxt->rxq_event_fd);
+	irq_ctxt->rxq_event_fd = NULL;
+
+	rte_intr_vec_list_free(intr_handle);
+
+	rte_intr_nb_efd_set(intr_handle, 0);
+	rte_intr_max_intr_set(intr_handle, 0);
+}
+
+int32_t sxe2_rxq_intr_enable(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	uint16_t msix_vect = adapter->irq_ctxt.rxq_base_idx_in_pf;
+	uint16_t msix_cnt = adapter->irq_ctxt.rxq_avail_cnt;
+	uint16_t rxq_cnt = dev->data->nb_rx_queues;
+	uint16_t rxq_base = adapter->q_ctxt.base_idx_in_pf;
+	int32_t ret = 0;
+
+	if (!rxq_cnt)
+		goto l_end;
+
+	msix_cnt = RTE_MIN(msix_cnt, rxq_cnt);
+
+	ret = sxe2_rxq_map_msix_intr(dev, msix_vect, msix_cnt, rxq_base, rxq_cnt);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to rxq[%d] map msix[%d] intr, cnt=%d, ret=%d",
+					rxq_base, msix_vect, rxq_cnt, ret);
+		goto l_end;
+	}
+
+	if (dev->data->dev_conf.intr_conf.rxq) {
+		ret = sxe2_rxq_intr_register(dev);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, INIT, "Failed to register rxq[%d] intr, ret=%d",
+					rxq_base, ret);
+			goto l_err_unmap;
+		}
+	}
+
+	ret = sxe2_rxq_msix_cfg_map(dev);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to rxq[%d] map msix[%d] intr, ret=%d",
+					rxq_base, msix_vect, ret);
+		goto l_err_unregister;
+	}
+
+	goto l_end;
+l_err_unregister:
+	if (dev->data->dev_conf.intr_conf.rxq)
+		sxe2_rxq_intr_unregister(dev);
+l_err_unmap:
+	sxe2_rxq_unmap_msix_intr(dev);
+l_end:
+	return ret;
+}
+
+void sxe2_rxq_intr_disable(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_irq_context *irq_ctxt = &adapter->irq_ctxt;
+
+	if (!irq_ctxt->rxq_irq_cnt)
+		goto l_end;
+
+	sxe2_rxq_msix_cfg_unmap(dev);
+
+	if (dev->data->dev_conf.intr_conf.rxq)
+		sxe2_rxq_intr_unregister(dev);
+
+	sxe2_rxq_unmap_msix_intr(dev);
+
+l_end:
+	return;
+}
+
+int32_t sxe2_rx_queue_intr_enable(struct rte_eth_dev *dev, uint16_t queue_id)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_irq_context *irq_ctxt = &adapter->irq_ctxt;
+	uint64_t buf;
+	uint16_t irq_idx = irq_ctxt->rxq_msix_idx[queue_id];
+	size_t read_ret;
+
+	read_ret = read(irq_ctxt->rxq_event_fd[irq_idx], &buf, sizeof(buf));
+	(void)read_ret;
+	sxe2_pci_hw_irq_enable(adapter, irq_idx);
+	return 0;
+}
+
+int32_t sxe2_rx_queue_intr_disable(struct rte_eth_dev *dev, uint16_t queue_id)
+{
+	struct sxe2_adapter *adapter =
+		SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	uint32_t val;
+	int32_t ret = 0;
+	uint16_t irq_idx = adapter->irq_ctxt.rxq_msix_idx[queue_id];
+
+	val = sxe2_pci_hw_irq_dyn_ctl_read(adapter, irq_idx);
+	if ((val & SXE2VF_DYN_CTL_INTENABLE) == 0) {
+		PMD_DEV_LOG_DEBUG(adapter, INIT, "rxq [%d] interrupt is disabled.", queue_id);
+		goto l_end;
+	}
+
+	sxe2_pci_hw_msix_disable(adapter, irq_idx);
+	sxe2_pci_hw_irq_trigger(adapter, irq_idx);
+	val = sxe2_pci_hw_irq_dyn_ctl_read(adapter, irq_idx);
+	sxe2_pci_hw_irq_clear_pba(adapter, irq_idx);
+	val = sxe2_pci_hw_irq_dyn_ctl_read(adapter, irq_idx);
+	sxe2_pci_hw_msix_enable(adapter, irq_idx);
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_irq.h b/drivers/net/sxe2/sxe2_irq.h
index 4ca2118b92..31216240e6 100644
--- a/drivers/net/sxe2/sxe2_irq.h
+++ b/drivers/net/sxe2/sxe2_irq.h
@@ -45,4 +45,25 @@ struct sxe2_irq_context {
 	int32_t *rxq_event_fd;
 };
 
+uint32_t sxe2_drv_dev_other_cause_get(struct sxe2_adapter *adapter);
+
+int32_t sxe2_intr_init(struct rte_eth_dev *dev);
+
+void sxe2_intr_uninit(struct rte_eth_dev *dev);
+
+int32_t sxe2_sw_irq_ctxt_init(struct rte_eth_dev *dev);
+
+void sxe2_sw_irq_ctxt_uninit(struct rte_eth_dev *dev);
+
+void sxe2_sw_irq_ctx_hw_cap_set(struct sxe2_adapter *adapter,
+		struct sxe2_drv_msix_caps *msix_caps);
+
+int32_t sxe2_rxq_intr_enable(struct rte_eth_dev *dev);
+
+void sxe2_rxq_intr_disable(struct rte_eth_dev *dev);
+
+int32_t sxe2_rx_queue_intr_enable(struct rte_eth_dev *dev, uint16_t queue_id);
+
+int32_t sxe2_rx_queue_intr_disable(struct rte_eth_dev *dev, uint16_t queue_id);
+
 #endif /* SXE2_IRQ_H */
diff --git a/drivers/net/sxe2/sxe2vf_regs.h b/drivers/net/sxe2/sxe2vf_regs.h
new file mode 100644
index 0000000000..854f1d3ae8
--- /dev/null
+++ b/drivers/net/sxe2/sxe2vf_regs.h
@@ -0,0 +1,85 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2VF_REGS_H__
+#define __SXE2VF_REGS_H__
+
+#define SXE2VF_MBX_Q_LEN_M      0x3FF
+#define SXE2VF_MBX_Q_LEN_VFE_M  RTE_BIT32(28)
+
+#define SXE2VF_MBX_Q_LEN_OVFL_M RTE_BIT32(29)
+#define SXE2VF_MBX_Q_LEN_CRIT_M RTE_BIT32(30)
+#define SXE2VF_MBX_Q_LEN_ENA_M  RTE_BIT32(31)
+
+#define SXE2VF_MBX_RQ_HEAD      (0x00008000)
+#define SXE2VF_MBX_RQ_TAIL      (0x00008400)
+#define SXE2VF_MBX_RQ_LEN       (0x00007C00)
+#define SXE2VF_MBX_RQ_BAH       (0x00007800)
+#define SXE2VF_MBX_RQ_BAL       (0x00007400)
+
+#define SXE2VF_MBX_TQ_HEAD      (0x00006C00)
+#define SXE2VF_MBX_TQ_TAIL      (0x00007000)
+#define SXE2VF_MBX_TQ_LEN       (0x00006800)
+#define SXE2VF_MBX_TQ_BAH       (0x00006400)
+#define SXE2VF_MBX_TQ_BAL       (0x00006000)
+
+#define SXE2VF_RXQ_TAIL(_QRX)                 (0x2000 + ((_QRX) * 4))
+#define SXE2VF_TXQ_TAIL(_QRX)                 (0x1000 + ((_QRX) * 4))
+
+#define SXE2VF_INT_BASE 0x00002800
+
+#define SXE2VF_DYN_CTL0 (SXE2VF_INT_BASE + 0x0)
+
+#define SXE2VF_DYN_CTL(_idx) (SXE2VF_INT_BASE + 0x4 + ((_idx) * 4))
+#define SXE2VF_VF_DYN_CTL(_idx) (SXE2VF_INT_BASE + ((_idx) * 4))
+
+#define SXE2VF_BAR4_MSIX_BASE 0
+#define SXE2VF_BAR4_MSIX_CTL(_idx) (SXE2VF_BAR4_MSIX_BASE + 0xC + ((_idx) * 0x10))
+#define SXE2VF_BAR4_MSIX_ENABLE 0
+#define SXE2VF_BAR4_MSIX_DISABLE 1
+
+#define SXE2VF_DYN_CTL_INTENABLE RTE_BIT32(0)
+#define SXE2VF_DYN_CTL_CLEARPBA   RTE_BIT32(1)
+#define SXE2VF_DYN_CTL_SWINT_TRIG RTE_BIT32(2)
+#define SXE2VF_DYN_CTL_SW_ITR_IDX_ENABLE RTE_BIT32(24)
+
+#define SXE2VF_DYN_CTL_INTENABLE_MSK \
+	RTE_BIT32(31)
+
+#define SXE2VF_DYN_CTL_ITR_IDX_SHIFT 3
+
+enum sxe2vf_itr_idx {
+	SXE2VF_ITR_IDX_0 = 0,
+	SXE2VF_ITR_IDX_1,
+	SXE2VF_ITR_IDX_2,
+	SXE2VF_ITR_IDX_NONE,
+};
+
+#define SXE2VF_INT_ITR0   (0x00002800 + 65 * 0x4)
+#define SXE2VF_INT_ITR(_i, _irq_idx) (SXE2VF_INT_ITR0 + \
+			0x4 + (_i) * 0x104 + ((_irq_idx) * 4))
+#define SXE2VF_VF_INT_ITR(_itr_idx, _irq_idx) (0x00002800 + \
+			(0x104 * (_itr_idx)) + ((_irq_idx) * 4))
+#define SXE2VF_PFG_INT_CTL_ITR_GRAN_0    (2)
+
+#define SXE2VF_PCIE_SYS_READY              0x38c
+#define SXE2VF_PCIE_SYS_READY_CORER_ASSERT RTE_BIT32(0)
+#define SXE2VF_PCIE_SYS_READY_STOP_DROP_DONE RTE_BIT32(2)
+#define SXE2VF_PCIE_SYS_READY_R5        RTE_BIT32(3)
+#define SXE2VF_PCIE_SYS_READY_STOP_DROP RTE_BIT32(16)
+
+#define SXE2VF_PCIE_DEV_CTRL_DEV_STATUS               0x78
+#define SXE2VF_PCIE_DEV_CTRL_DEV_STATUS_TRANS_PENDING RTE_BIT32(21)
+
+#define SXE2VF_VF_VRC_VFGEN_RSTAT             (0x5800)
+#define SXE2VF_VF_VRC_VFGEN_VFRSTAT           GENMASK(1, 0)
+#define SXE2VF_VF_VRC_VFGEN_VFRSTAT_VFR       (0)
+#define SXE2VF_VF_VRC_VFGEN_VFRSTAT_COMPLETE  (RTE_BIT32(0))
+#define SXE2VF_VF_VRC_VFGEN_VFRSTAT_VF_ACTIVE (RTE_BIT32(1))
+
+#define SXE2VF_VF_VRC_VFGEN_VFRSTAT_FORVF_VFR (1)
+#define SXE2VF_VF_VRC_VFGEN_VFRSTAT_FORVF_MASK \
+	(RTE_BIT32(10))
+
+#endif /* SXE2VF_VF_INT_ITR */
-- 
2.52.0


^ permalink raw reply related

* [PATCH v10 05/20] drivers: support RSS feature
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260606010726.2256170-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

Add support for Receive Side Scaling (RSS) to distribute incoming
traffic across multiple receive queues.

- Implement rss_hash_update and rss_hash_conf_get.
- Implement reta_update and reta_query.
- Support RSS hash configuration for IPv4, IPv6, TCP and UDP.
- Default hash key is initialized during port start.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/common/sxe2/sxe2_flow_public.h | 633 +++++++++++++++++++++++++
 drivers/net/sxe2/meson.build           |   1 +
 drivers/net/sxe2/sxe2_cmd_chnl.c       | 173 +++++++
 drivers/net/sxe2/sxe2_cmd_chnl.h       |  16 +
 drivers/net/sxe2/sxe2_drv_cmd.h        |  29 ++
 drivers/net/sxe2/sxe2_ethdev.c         |  37 ++
 drivers/net/sxe2/sxe2_ethdev.h         |   8 +
 drivers/net/sxe2/sxe2_flow_define.h    | 143 ++++++
 drivers/net/sxe2/sxe2_queue.c          |  11 +
 drivers/net/sxe2/sxe2_rss.c            | 584 +++++++++++++++++++++++
 drivers/net/sxe2/sxe2_rss.h            |  81 ++++
 drivers/net/sxe2/sxe2_txrx.h           |   4 +
 drivers/net/sxe2/sxe2_txrx_poll.c      |  85 +++-
 13 files changed, 1804 insertions(+), 1 deletion(-)
 create mode 100644 drivers/common/sxe2/sxe2_flow_public.h
 create mode 100644 drivers/net/sxe2/sxe2_flow_define.h
 create mode 100644 drivers/net/sxe2/sxe2_rss.c
 create mode 100644 drivers/net/sxe2/sxe2_rss.h

diff --git a/drivers/common/sxe2/sxe2_flow_public.h b/drivers/common/sxe2/sxe2_flow_public.h
new file mode 100644
index 0000000000..32ab2a9713
--- /dev/null
+++ b/drivers/common/sxe2/sxe2_flow_public.h
@@ -0,0 +1,633 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_FLOW_PUBLIC_H__
+#define __SXE2_FLOW_PUBLIC_H__
+#include "sxe2_osal.h"
+
+enum sxe2_flow_type {
+	SXE2_FLOW_TYPE_NONE                               = 0,
+	SXE2_FLOW_MAC_PAY                                 = 1,
+	SXE2_FLOW_MAC_IPV4_FRAG_PAY                       = 22,
+	SXE2_FLOW_MAC_IPV4_PAY                            = 23,
+	SXE2_FLOW_MAC_IPV4_UDP_PAY                        = 24,
+	SXE2_FLOW_MAC_IPV4_TCP_PAY                        = 26,
+	SXE2_FLOW_MAC_IPV4_SCTP_PAY                       = 27,
+	SXE2_FLOW_MAC_IPV4_IPV4_FRAG_PAY                  = 29,
+	SXE2_FLOW_MAC_IPV4_IPV4_PAY                       = 30,
+	SXE2_FLOW_MAC_IPV4_IPV4_UDP_PAY                   = 31,
+	SXE2_FLOW_MAC_IPV4_IPV4_TCP_PAY                   = 33,
+	SXE2_FLOW_MAC_IPV4_IPV4_SCTP_PAY                  = 34,
+	SXE2_FLOW_MAC_IPV4_IPV6_FRAG_PAY                  = 36,
+	SXE2_FLOW_MAC_IPV4_IPV6_PAY                       = 37,
+	SXE2_FLOW_MAC_IPV4_IPV6_UDP_PAY                   = 38,
+	SXE2_FLOW_MAC_IPV4_IPV6_TCP_PAY                   = 40,
+	SXE2_FLOW_MAC_IPV4_IPV6_SCTP_PAY                  = 41,
+	SXE2_FLOW_MAC_IPV4_GRE_PAY                        = 43,
+	SXE2_FLOW_MAC_IPV4_GRE_IPV4_FRAG_PAY              = 44,
+	SXE2_FLOW_MAC_IPV4_GRE_IPV4_PAY                   = 45,
+	SXE2_FLOW_MAC_IPV4_GRE_IPV4_UDP_PAY               = 46,
+	SXE2_FLOW_MAC_IPV4_GRE_IPV4_TCP_PAY               = 48,
+	SXE2_FLOW_MAC_IPV4_GRE_IPV4_SCTP_PAY              = 49,
+	SXE2_FLOW_MAC_IPV4_GRE_IPV6_FRAG_PAY              = 51,
+	SXE2_FLOW_MAC_IPV4_GRE_IPV6_PAY                   = 52,
+	SXE2_FLOW_MAC_IPV4_GRE_IPV6_UDP_PAY               = 53,
+	SXE2_FLOW_MAC_IPV4_GRE_IPV6_TCP_PAY               = 55,
+	SXE2_FLOW_MAC_IPV4_GRE_IPV6_SCTP_PAY              = 56,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_PAY                    = 58,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_FRAG_PAY          = 59,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_PAY               = 60,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_UDP_PAY           = 61,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_TCP_PAY           = 63,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV4_SCTP_PAY          = 64,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_FRAG_PAY          = 66,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_PAY               = 67,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_UDP_PAY           = 68,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_TCP_PAY           = 70,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_IPV6_SCTP_PAY          = 71,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_PAY               = 73,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_FRAG_PAY     = 74,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_PAY          = 75,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_UDP_PAY      = 76,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_TCP_PAY      = 78,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV4_SCTP_PAY     = 79,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_FRAG_PAY     = 81,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_PAY          = 82,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_UDP_PAY      = 83,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_TCP_PAY      = 85,
+	SXE2_FLOW_MAC_IPV4_GRE_MAC_VLAN_IPV6_SCTP_PAY     = 86,
+	SXE2_FLOW_MAC_IPV6_FRAG_PAY                       = 88,
+	SXE2_FLOW_MAC_IPV6_PAY                            = 89,
+	SXE2_FLOW_MAC_IPV6_UDP_PAY                        = 90,
+	SXE2_FLOW_MAC_IPV6_TCP_PAY                        = 92,
+	SXE2_FLOW_MAC_IPV6_SCTP_PAY                       = 93,
+	SXE2_FLOW_MAC_IPV6_IPV4_FRAG_PAY                  = 95,
+	SXE2_FLOW_MAC_IPV6_IPV4_PAY                       = 96,
+	SXE2_FLOW_MAC_IPV6_IPV4_UDP_PAY                   = 97,
+	SXE2_FLOW_MAC_IPV6_IPV4_TCP_PAY                   = 99,
+	SXE2_FLOW_MAC_IPV6_IPV4_SCTP_PAY                  = 100,
+	SXE2_FLOW_MAC_IPV6_IPV6_FRAG_PAY                  = 102,
+	SXE2_FLOW_MAC_IPV6_IPV6_PAY                       = 103,
+	SXE2_FLOW_MAC_IPV6_IPV6_UDP_PAY                   = 104,
+	SXE2_FLOW_MAC_IPV6_IPV6_TCP_PAY                   = 106,
+	SXE2_FLOW_MAC_IPV6_IPV6_SCTP_PAY                  = 107,
+	SXE2_FLOW_MAC_IPV6_GRE_PAY                        = 109,
+	SXE2_FLOW_MAC_IPV6_GRE_IPV4_FRAG_PAY              = 110,
+	SXE2_FLOW_MAC_IPV6_GRE_IPV4_PAY                   = 111,
+	SXE2_FLOW_MAC_IPV6_GRE_IPV4_UDP_PAY               = 112,
+	SXE2_FLOW_MAC_IPV6_GRE_IPV4_TCP_PAY               = 114,
+	SXE2_FLOW_MAC_IPV6_GRE_IPV4_SCTP_PAY              = 115,
+	SXE2_FLOW_MAC_IPV6_GRE_IPV6_FRAG_PAY              = 117,
+	SXE2_FLOW_MAC_IPV6_GRE_IPV6_PAY                   = 118,
+	SXE2_FLOW_MAC_IPV6_GRE_IPV6_UDP_PAY               = 119,
+	SXE2_FLOW_MAC_IPV6_GRE_IPV6_TCP_PAY               = 121,
+	SXE2_FLOW_MAC_IPV6_GRE_IPV6_SCTP_PAY              = 122,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_PAY                    = 124,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_FRAG_PAY          = 125,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_PAY               = 126,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_UDP_PAY           = 127,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_TCP_PAY           = 129,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV4_SCTP_PAY          = 130,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_FRAG_PAY          = 132,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_PAY               = 133,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_UDP_PAY           = 134,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_TCP_PAY           = 136,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_IPV6_SCTP_PAY          = 137,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_PAY               = 139,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_FRAG_PAY     = 140,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_PAY          = 141,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_UDP_PAY      = 142,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_TCP_PAY      = 144,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV4_SCTP_PAY     = 145,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_FRAG_PAY     = 147,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_PAY          = 148,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_UDP_PAY      = 149,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_TCP_PAY      = 151,
+	SXE2_FLOW_MAC_IPV6_GRE_MAC_VLAN_IPV6_SCTP_PAY     = 152,
+	SXE2_FLOW_MAC_IPV4_UDP_GTPU_PAY                   = 329,
+	SXE2_FLOW_MAC_IPV6_UDP_GTPU_PAY                   = 330,
+	SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_FRAG_PAY         = 331,
+	SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_PAY              = 332,
+	SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_UDP_PAY          = 333,
+	SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_TCP_PAY          = 334,
+	SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV4_SCTP_PAY         = 335,
+	SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_FRAG_PAY         = 336,
+	SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_PAY              = 337,
+	SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_UDP_PAY          = 338,
+	SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_TCP_PAY          = 339,
+	SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV4_SCTP_PAY         = 340,
+	SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_FRAG_PAY         = 341,
+	SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_PAY              = 342,
+	SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_UDP_PAY          = 343,
+	SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_TCP_PAY          = 344,
+	SXE2_FLOW_MAC_IPV4_UDP_GTPU_IPV6_SCTP_PAY         = 345,
+	SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_FRAG_PAY         = 346,
+	SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_PAY              = 347,
+	SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_UDP_PAY          = 348,
+	SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_TCP_PAY          = 349,
+	SXE2_FLOW_MAC_IPV6_UDP_GTPU_IPV6_SCTP_PAY         = 350,
+	SXE2_FLOW_MAC_IPV6_MAC_PAY                        = 820,
+	SXE2_FLOW_MAC_IPV6_MAC_IPV4_FRAG_PAY              = 821,
+	SXE2_FLOW_MAC_IPV6_MAC_IPV4_PAY                   = 822,
+	SXE2_FLOW_MAC_IPV6_MAC_IPV4_UDP_PAY               = 823,
+	SXE2_FLOW_MAC_IPV6_MAC_IPV4_TCP_PAY               = 824,
+	SXE2_FLOW_MAC_IPV6_MAC_IPV4_SCTP_PAY              = 825,
+	SXE2_FLOW_MAC_IPV6_MAC_IPV6_FRAG_PAY              = 827,
+	SXE2_FLOW_MAC_IPV6_MAC_IPV6_PAY                   = 828,
+	SXE2_FLOW_MAC_IPV6_MAC_IPV6_UDP_PAY               = 829,
+	SXE2_FLOW_MAC_IPV6_MAC_IPV6_TCP_PAY               = 830,
+	SXE2_FLOW_MAC_IPV6_MAC_IPV6_SCTP_PAY              = 831,
+	SXE2_FLOW_MAC_IPV6_MAC_VLAN_PAY                   = 835,
+	SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_FRAG_PAY         = 836,
+	SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_PAY              = 837,
+	SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_UDP_PAY          = 838,
+	SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_TCP_PAY          = 839,
+	SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV4_SCTP_PAY         = 840,
+	SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_FRAG_PAY         = 842,
+	SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_PAY              = 843,
+	SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_UDP_PAY          = 844,
+	SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_TCP_PAY          = 845,
+	SXE2_FLOW_MAC_IPV6_MAC_VLAN_IPV6_SCTP_PAY         = 846,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_PAY                  = 878,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_FRAG_PAY        = 877,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_PAY             = 876,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_UDP_PAY         = 879,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_TCP_PAY         = 880,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV4_SCTP_PAY        = 875,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_FRAG_PAY        = 871,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_PAY             = 870,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_UDP_PAY         = 872,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_TCP_PAY         = 873,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_IPV6_SCTP_PAY        = 869,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_PAY                  = 891,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_FRAG_PAY        = 890,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_PAY             = 889,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_UDP_PAY         = 892,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_TCP_PAY         = 893,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV4_SCTP_PAY        = 888,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_FRAG_PAY        = 884,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_PAY             = 883,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_UDP_PAY         = 885,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_TCP_PAY         = 886,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_IPV6_SCTP_PAY        = 882,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_PAY                    = 904,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_FRAG_PAY          = 903,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_PAY               = 902,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_UDP_PAY           = 905,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_TCP_PAY           = 906,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV4_SCTP_PAY          = 901,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_FRAG_PAY          = 897,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_PAY               = 896,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_UDP_PAY           = 898,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_TCP_PAY           = 899,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_IPV6_SCTP_PAY          = 895,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_PAY                    = 917,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_FRAG_PAY          = 916,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_PAY               = 915,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_UDP_PAY           = 918,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_TCP_PAY           = 919,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV4_SCTP_PAY          = 914,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_FRAG_PAY          = 910,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_PAY               = 909,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_UDP_PAY           = 911,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_TCP_PAY           = 912,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_IPV6_SCTP_PAY          = 908,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_PAY         = 930,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_FRAG_PAY = 929,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_PAY    = 928,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_UDP_PAY = 931,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_TCP_PAY = 932,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV4_SCTP_PAY = 927,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_FRAG_PAY = 923,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_PAY    = 922,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_UDP_PAY = 924,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_TCP_PAY = 925,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_VLAN_IPV6_SCTP_PAY = 921,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_PAY         = 943,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_FRAG_PAY = 942,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_PAY    = 941,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_UDP_PAY = 944,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_TCP_PAY = 945,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV4_SCTP_PAY = 940,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_FRAG_PAY = 936,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_PAY    = 935,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_UDP_PAY = 937,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_TCP_PAY = 938,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_VLAN_IPV6_SCTP_PAY = 934,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_PAY           = 956,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_FRAG_PAY = 955,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_PAY      = 954,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_UDP_PAY  = 957,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_TCP_PAY  = 958,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV4_SCTP_PAY = 953,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_FRAG_PAY = 949,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_PAY      = 948,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_UDP_PAY  = 950,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_TCP_PAY  = 951,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_VLAN_IPV6_SCTP_PAY = 947,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_PAY           = 969,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_FRAG_PAY = 968,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_PAY      = 967,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_UDP_PAY  = 970,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_TCP_PAY  = 971,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV4_SCTP_PAY = 966,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_FRAG_PAY = 962,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_PAY      = 961,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_UDP_PAY  = 963,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_TCP_PAY  = 964,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_VLAN_IPV6_SCTP_PAY = 960,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_PAY              = 982,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_FRAG_PAY    = 981,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_PAY         = 980,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_UDP_PAY     = 983,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_TCP_PAY     = 984,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV4_SCTP_PAY    = 979,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_FRAG_PAY    = 975,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_PAY         = 974,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_UDP_PAY     = 976,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_TCP_PAY     = 977,
+	SXE2_FLOW_MAC_IPV6_UDP_VXGEN_MAC_IPV6_SCTP_PAY    = 973,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_PAY              = 995,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_FRAG_PAY    = 994,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_PAY         = 993,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_UDP_PAY     = 996,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_TCP_PAY     = 997,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV4_SCTP_PAY    = 992,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_FRAG_PAY    = 988,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_PAY         = 987,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_UDP_PAY     = 989,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_TCP_PAY     = 990,
+	SXE2_FLOW_MAC_IPV4_UDP_VXGEN_MAC_IPV6_SCTP_PAY    = 986,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_PAY                = 1008,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_FRAG_PAY      = 1007,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_PAY           = 1006,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_UDP_PAY       = 1009,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_TCP_PAY       = 1010,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV4_SCTP_PAY      = 1005,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_FRAG_PAY      = 1001,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_PAY           = 1000,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_UDP_PAY       = 1002,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_TCP_PAY       = 1003,
+	SXE2_FLOW_MAC_IPV6_UDP_GRE_MAC_IPV6_SCTP_PAY      = 999,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_PAY                = 1021,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_FRAG_PAY      = 1020,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_PAY           = 1019,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_UDP_PAY       = 1022,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_TCP_PAY       = 1023,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV4_SCTP_PAY      = 1018,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_FRAG_PAY      = 1014,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_PAY           = 1013,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_UDP_PAY       = 1015,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_TCP_PAY       = 1016,
+	SXE2_FLOW_MAC_IPV4_UDP_GRE_MAC_IPV6_SCTP_PAY      = 1012,
+	SXE2_FLOW_TYPE_MAX                                = 2048,
+};
+
+enum sxe2_rss_cfg_hdr_type {
+	SXE2_RSS_OUTER_HEADERS,
+	SXE2_RSS_INNER_HEADERS,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4_GRE,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6_GRE,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4_UDP_GRE,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6_UDP_GRE,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4_UDP_VXLAN,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6_UDP_VXLAN,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4_UDP_GENEVE,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6_UDP_GENEVE,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV4_UDP_GTPU,
+
+	SXE2_RSS_INNER_HEADERS_WITH_OUTER_IPV6_UDP_GTPU,
+	SXE2_RSS_ANY_HEADERS
+};
+
+enum sxe2_flow_hdr {
+	SXE2_FLOW_HDR_ETH = 0,
+	SXE2_FLOW_HDR_VLAN,
+	SXE2_FLOW_HDR_QINQ,
+	SXE2_FLOW_HDR_IPV4,
+	SXE2_FLOW_HDR_IPV6,
+	SXE2_FLOW_HDR_ICMP = 5,
+	SXE2_FLOW_HDR_TCP,
+	SXE2_FLOW_HDR_UDP,
+	SXE2_FLOW_HDR_SCTP,
+	SXE2_FLOW_HDR_GRE,
+	SXE2_FLOW_HDR_VXLAN = 10,
+	SXE2_FLOW_HDR_GENEVE,
+	SXE2_FLOW_HDR_GTPU,
+
+	SXE2_FLOW_HDR_IPV_FRAG,
+
+	SXE2_FLOW_HDR_IPV_OTHER,
+
+	SXE2_FLOW_HDR_ETH_NON_IP = 15,
+	SXE2_FLOW_HDR_MAX = 128,
+};
+
+enum sxe2_flow_fld_id {
+	SXE2_FLOW_FLD_ID_ETH_DA = 0,
+	SXE2_FLOW_FLD_ID_ETH_SA,
+	SXE2_FLOW_FLD_ID_S_TCI,
+	SXE2_FLOW_FLD_ID_C_TCI,
+	SXE2_FLOW_FLD_ID_S_TPID,
+	SXE2_FLOW_FLD_ID_C_TPID = 5,
+	SXE2_FLOW_FLD_ID_S_VID,
+	SXE2_FLOW_FLD_ID_C_VID,
+	SXE2_FLOW_FLD_ID_ETH_TYPE,
+
+	SXE2_FLOW_FLD_ID_IPV4_TOS,
+	SXE2_FLOW_FLD_ID_IPV6_DSCP = 10,
+	SXE2_FLOW_FLD_ID_IPV4_TTL,
+	SXE2_FLOW_FLD_ID_IPV4_PROT,
+	SXE2_FLOW_FLD_ID_IPV6_TTL,
+	SXE2_FLOW_FLD_ID_IPV6_PROT,
+	SXE2_FLOW_FLD_ID_IPV4_SA = 15,
+	SXE2_FLOW_FLD_ID_IPV4_DA,
+	SXE2_FLOW_FLD_ID_IPV6_SA,
+	SXE2_FLOW_FLD_ID_IPV6_DA,
+	SXE2_FLOW_FLD_ID_IPV4_CHKSUM,
+	SXE2_FLOW_FLD_ID_IPV4_ID = 20,
+	SXE2_FLOW_FLD_ID_IPV6_ID,
+	SXE2_FLOW_FLD_ID_IPV6_PRE32_SA,
+	SXE2_FLOW_FLD_ID_IPV6_PRE32_DA,
+	SXE2_FLOW_FLD_ID_IPV6_PRE48_SA,
+	SXE2_FLOW_FLD_ID_IPV6_PRE48_DA = 25,
+	SXE2_FLOW_FLD_ID_IPV6_PRE64_SA,
+	SXE2_FLOW_FLD_ID_IPV6_PRE64_DA,
+
+	SXE2_FLOW_FLD_ID_TCP_SRC_PORT,
+	SXE2_FLOW_FLD_ID_TCP_DST_PORT,
+	SXE2_FLOW_FLD_ID_UDP_SRC_PORT = 30,
+	SXE2_FLOW_FLD_ID_UDP_DST_PORT,
+	SXE2_FLOW_FLD_ID_SCTP_SRC_PORT,
+	SXE2_FLOW_FLD_ID_SCTP_DST_PORT,
+	SXE2_FLOW_FLD_ID_TCP_FLAGS,
+	SXE2_FLOW_FLD_ID_TCP_CHKSUM = 35,
+	SXE2_FLOW_FLD_ID_UDP_CHKSUM,
+	SXE2_FLOW_FLD_ID_SCTP_CHKSUM,
+
+	SXE2_FLOW_FLD_ID_VXLAN_VNI,
+
+	SXE2_FLOW_FLD_ID_GENEVE_VNI,
+
+	SXE2_FLOW_FLD_ID_GTPU_TEID = 40,
+
+	SXE2_FLOW_FLD_ID_NVGRE_TNI,
+
+	SXE2_FLOW_FLD_ID_MAX = 128,
+};
+
+struct sxe2_ether_hdr {
+	uint8_t dst_addr[SXE2_ETH_ALEN];
+	uint8_t src_addr[SXE2_ETH_ALEN];
+	uint16_t  ether_type;
+};
+
+struct sxe2_vlan_hdr {
+	uint16_t  type;
+	uint16_t  vlan;
+};
+
+struct sxe2_ipv4_hdr {
+	uint8_t ver_ihl;
+	uint8_t tos;
+	uint16_t  tot_len;
+	uint16_t  id;
+	uint16_t  frag_off;
+	uint8_t ttl;
+	uint8_t protocol;
+	uint16_t  check;
+	uint32_t saddr;
+	uint32_t daddr;
+};
+#define SXE2_IPV6_ADDR_LENGTH        (16)
+#define SXE2_IPV6_TC_SHIFT (20)
+#define SXE2_IPV6_TC_MASK  (0xFF)
+
+struct sxe2_ipv6_hdr {
+	uint32_t pri_ver_flow;
+	uint16_t  payload_len;
+	uint8_t nexthdr;
+	uint8_t hop_limit;
+	union {
+		uint8_t saddr[16];
+		uint16_t	saddr16[8];
+		uint32_t	saddr32[4];
+	};
+	union {
+		uint8_t daddr[16];
+		uint16_t	daddr16[8];
+		uint32_t	daddr32[4];
+	};
+};
+
+struct sxe2_tcp_hdr {
+	uint16_t  source;
+	uint16_t  dest;
+	uint32_t seq;
+	uint32_t ack_seq;
+	uint16_t  flag;
+	uint16_t  window;
+	uint16_t  check;
+	uint16_t  urg_ptr;
+};
+
+struct sxe2_udp_hdr {
+	uint16_t  source;
+	uint16_t  dest;
+	uint16_t  len;
+	uint16_t  check;
+};
+
+struct sxe2_sctp_hdr {
+	uint16_t src_port;
+	uint16_t dst_port;
+};
+
+struct sxe2_nvgre_hdr {
+	uint16_t flags;
+	uint16_t protocol;
+	uint32_t tni;
+};
+struct sxe2_geneve_hdr {
+	uint16_t flags;
+	uint16_t protocol;
+	uint32_t vni;
+};
+struct sxe2_gtpu_hdr {
+	uint8_t flag;
+	uint8_t msg_type;
+	uint16_t msg_len;
+	uint32_t teid;
+};
+struct sxe2_vxlan_hdr {
+	uint8_t flag;
+	uint8_t resvd0;
+	uint8_t resvd1;
+	uint8_t protocol;
+	uint32_t vni;
+};
+
+enum sxe2_flow_act_type {
+	SXE2_FLOW_ACTION_DROP = 0,
+	SXE2_FLOW_ACTION_TC_REDIRECT,
+	SXE2_FLOW_ACTION_TO_VSI,
+	SXE2_FLOW_ACTION_TO_VSI_LIST,
+	SXE2_FLOW_ACTION_PASSTHRU,
+	SXE2_FLOW_ACTION_QUEUE,
+	SXE2_FLOW_ACTION_Q_REGION,
+	SXE2_FLOW_ACTION_MARK,
+	SXE2_FLOW_ACTION_COUNT,
+	SXE2_FLOW_ACTION_RSS,
+	SXE2_FLOW_ACTION_MAX = 32,
+};
+
+enum sxe2_rss_hash_key_func {
+	SXE2_RSS_HASH_FUNC_TOEPLITZ     = 0,
+	SXE2_RSS_HASH_FUNC_SYM_TOEPLITZ = 1,
+	SXE2_RSS_HASH_FUNC_XOR          = 2,
+	SXE2_RSS_HASH_FUNC_JEKINS       = 3
+};
+
+struct sxe2_flow_action_rss {
+	DECLARE_BITMAP(hdr_out, SXE2_FLOW_HDR_MAX);
+	DECLARE_BITMAP(hdr_in, SXE2_FLOW_HDR_MAX);
+	DECLARE_BITMAP(fld, SXE2_FLOW_FLD_ID_MAX);
+	uint8_t is_inner;
+	uint8_t func;
+	uint8_t hdr_type;
+};
+
+struct sxe2_flow_action_queue {
+	uint16_t vsi_index;
+	uint16_t q_index;
+};
+
+struct sxe2_flow_action_queue_region {
+	uint16_t vsi_index;
+	uint16_t q_index;
+	uint8_t region;
+};
+
+struct sxe2_flow_action_passthru {
+	uint16_t vsi_index;
+};
+
+struct sxe2_flow_action_mark {
+	uint32_t mark_id;
+};
+
+#define SXE2_VSI_MAX (2048)
+struct sxe2_flow_action_vsi {
+	uint16_t vsi_index;
+};
+
+struct sxe2_flow_action_vsi_list {
+	DECLARE_BITMAP(vsi_list_map, SXE2_VSI_MAX);
+	uint16_t vsi_cnt;
+};
+
+enum sxe2_fnav_stat_ctrl_type {
+	SXE2_FNAV_STAT_ENA_NONE = 0,
+	SXE2_FNAV_STAT_ENA_PKTS,
+	SXE2_FNAV_STAT_ENA_BYTES,
+	SXE2_FNAV_STAT_ENA_ALL,
+};
+
+struct sxe2_flow_action_count {
+	uint32_t user_id;
+	uint32_t driver_id;
+	uint32_t stat_index;
+	uint32_t stat_ctrl;
+};
+
+enum sxe2_flow_engine_type {
+	SXE2_FLOW_ENGINE_ACL,
+	SXE2_FLOW_ENGINE_SWITCH,
+	SXE2_FLOW_ENGINE_FNAV,
+	SXE2_FLOW_ENGINE_RSS,
+	SXE2_FLOW_ENGINE_MAX,
+};
+
+struct sxe2_flow_item {
+	struct sxe2_ether_hdr eth;
+	struct sxe2_vlan_hdr vlan;
+	struct sxe2_vlan_hdr qinq;
+	struct sxe2_ipv4_hdr ipv4;
+	struct sxe2_ipv6_hdr ipv6;
+	struct sxe2_udp_hdr udp;
+	struct sxe2_tcp_hdr tcp;
+	struct sxe2_sctp_hdr sctp;
+	struct sxe2_gtpu_hdr gtpu;
+	struct sxe2_vxlan_hdr vxlan;
+	struct sxe2_nvgre_hdr nvgre;
+	struct sxe2_geneve_hdr geneve;
+};
+
+enum sxe2_flow_sw_direct_type {
+	SXE2_FLOW_SW_DIRECT_TX,
+	SXE2_FLOW_SW_DIRECT_RX,
+	SXE2_FLOW_SW_DIRECT_MAX,
+};
+enum sxe2_flow_sw_pattern_type {
+	SXE2_FLOW_SW_PATTERN_ONLY,
+	SXE2_FLOW_SW_PATTERN_LAST,
+	SXE2_FLOW_SW_PATTERN_FIRST,
+	SXE2_FLOW_SW_PATTERN_MAX,
+};
+
+enum sxe2_flow_tunnel_type {
+	SXE2_FLOW_TUNNEL_TYPE_NONE,
+	SXE2_FLOW_TUNNEL_TYPE_PARENT,
+	SXE2_FLOW_TUNNEL_TYPE_VXLAN,
+	SXE2_FLOW_TUNNEL_TYPE_GTPU,
+	SXE2_FLOW_TUNNEL_TYPE_GENEVE,
+	SXE2_FLOW_TUNNEL_TYPE_GRE,
+	SXE2_FLOW_TUNNEL_TYPE_IPIP,
+};
+
+struct sxe2_flow_meta {
+	uint8_t switch_pattern_dup_allow;
+	uint8_t switch_src_direct;
+	uint16_t flow_src_vsi;
+	uint16_t flow_rule_vsi;
+	uint32_t flow_prio;
+	uint16_t flow_type;
+	uint8_t tunnel_type;
+	uint8_t rsv;
+};
+
+struct sxe2_flow_pattern {
+	DECLARE_BITMAP(hdrs, SXE2_FLOW_HDR_MAX);
+	DECLARE_BITMAP(map_spec, SXE2_FLOW_FLD_ID_MAX);
+	DECLARE_BITMAP(map_mask, SXE2_FLOW_FLD_ID_MAX);
+	struct sxe2_flow_item item_spec;
+	struct sxe2_flow_item item_mask;
+	uint64_t rss_type_allow;
+};
+
+struct sxe2_flow_action {
+	DECLARE_BITMAP(act_types, SXE2_FLOW_ACTION_MAX);
+	struct sxe2_flow_action_rss rss;
+	struct sxe2_flow_action_queue queue;
+	struct sxe2_flow_action_queue_region q_region;
+	struct sxe2_flow_action_passthru passthru;
+	struct sxe2_flow_action_vsi vsi;
+	struct sxe2_flow_action_vsi_list vsi_list;
+	struct sxe2_flow_action_mark mark;
+	struct sxe2_flow_action_count count;
+};
+#endif /* __SXE2_FLOW_PUBLIC_H__ */
diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index b661e3cbf4..da7a690063 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -62,4 +62,5 @@ sources += files(
         'sxe2_txrx_vec.c',
         'sxe2_mac.c',
         'sxe2_filter.c',
+        'sxe2_rss.c',
 )
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.c b/drivers/net/sxe2/sxe2_cmd_chnl.c
index 1fa9ad718e..b997e7b044 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.c
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.c
@@ -541,3 +541,176 @@ int32_t sxe2_drv_vlan_filter_switch(struct sxe2_adapter *adapter, bool on)
 
 	return ret;
 }
+
+int32_t sxe2_drv_rss_key_set(struct sxe2_adapter *adapter, uint8_t *key, uint16_t key_size)
+{
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_rss_key_req *req = NULL;
+	int32_t ret = 0;
+	uint16_t buf_size = sizeof(*req) + key_size;
+
+	req = rte_zmalloc("drv_cmd_rss_key", buf_size, 0);
+	if (!req) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to alloc rss key");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+
+	req->vsi_id = rte_cpu_to_le_16(adapter->vsi_ctxt.dpdk_vsi_id);
+	req->key_size = rte_cpu_to_le_16(key_size);
+	rte_memcpy(req->key, key, key_size);
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RSS_KEY_SET,
+		req, buf_size, NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to cmd set rss key, ret=%d", ret);
+		goto l_end;
+	}
+
+l_end:
+	if (req) {
+		rte_free(req);
+		req = NULL;
+	}
+	return ret;
+}
+
+int32_t sxe2_drv_rss_lut_set(struct sxe2_adapter *adapter, uint8_t *lut, uint16_t lut_size)
+{
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_rss_lut_req *req = NULL;
+	int32_t ret = 0;
+	uint16_t buf_size = sizeof(struct sxe2_rss_lut_req) + lut_size;
+
+	req = rte_zmalloc("drv_cmd_rss_lut", buf_size, 0);
+	if (!req) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to alloc rss lut");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+
+	req->vsi_id = rte_cpu_to_le_16(adapter->vsi_ctxt.dpdk_vsi_id);
+	req->lut_size = rte_cpu_to_le_16(lut_size);
+	rte_memcpy(req->lut, lut, lut_size);
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RSS_LUT_SET,
+		req, buf_size, NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to cmd set rss lut, ret=%d", ret);
+		goto l_end;
+	}
+
+l_end:
+	if (req) {
+		rte_free(req);
+		req = NULL;
+	}
+	return ret;
+}
+
+int32_t sxe2_drv_rss_hash_ctrl_func(struct sxe2_adapter *adapter, enum sxe2_rss_hash_key_func func)
+{
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_rss_func_req req = {0};
+	int32_t ret = 0;
+
+	req.vsi_id = rte_cpu_to_le_16(adapter->vsi_ctxt.dpdk_vsi_id);
+	req.func = func;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RSS_FUNC_SET,
+		&req, sizeof(req), NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to cmd set rss func, ret=%d", ret);
+	return ret;
+}
+
+static void sxe2_drv_flow_bitmap_fill(uint32_t *bitmap, uint16_t *list)
+{
+	uint16_t index = 0;
+	uint16_t i = 0;
+	uint16_t map_size = sizeof(*bitmap) * SXE2_BITS_PER_BYTE;
+
+	while (list[i] != SXE2_FLOW_END) {
+		index = list[i] / map_size;
+		bitmap[index] |= (1UL << (list[i] % map_size));
+		i++;
+	}
+}
+
+int32_t sxe2_drv_rss_hf_add(struct sxe2_adapter *adapter,
+			struct sxe2_rss_hf_config *rss_conf)
+{
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_rss_hf_req req = {0};
+	int32_t ret = 0;
+
+	req.vsi_id = rte_cpu_to_le_16(adapter->vsi_ctxt.dpdk_vsi_id);
+	req.symm = rss_conf->symm;
+	req.hdr_type = rte_cpu_to_le_32(SXE2_RSS_OUTER_HEADERS);
+	sxe2_drv_flow_bitmap_fill(req.headers, rss_conf->hdrs);
+	sxe2_drv_flow_bitmap_fill(req.hash_flds, rss_conf->flds);
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RSS_HF_ADD,
+		&req, sizeof(req), NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to cmd add rss hf, ret=%d", ret);
+	return ret;
+}
+
+int32_t sxe2_drv_rss_hf_del(struct sxe2_adapter *adapter,
+				struct sxe2_rss_hf_config *rss_conf)
+{
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_rss_hf_req req = {0};
+	int32_t ret = 0;
+
+	req.vsi_id = rte_cpu_to_le_16(adapter->vsi_ctxt.dpdk_vsi_id);
+	req.symm = rss_conf->symm;
+	req.hdr_type = rte_cpu_to_le_32(SXE2_RSS_OUTER_HEADERS);
+	sxe2_drv_flow_bitmap_fill(req.headers, rss_conf->hdrs);
+	sxe2_drv_flow_bitmap_fill(req.hash_flds, rss_conf->flds);
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RSS_HF_DEL,
+		&req, sizeof(req), NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to cmd del rss hf, ret=%d", ret);
+	return ret;
+}
+
+int32_t sxe2_drv_rss_hf_clear(struct sxe2_adapter *adapter)
+{
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	int32_t ret = 0;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_RSS_HF_CLEAR,
+		NULL, 0, NULL, 0);
+
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret)
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to cmd clear rss hf, ret=%d", ret);
+
+	return ret;
+}
+
+int32_t sxe2_drv_ptp_gettime(struct sxe2_adapter *adapter, struct sxe2_rx_queue *rxq)
+{
+	(void)adapter;
+	(void)rxq;
+	return 0;
+}
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.h b/drivers/net/sxe2/sxe2_cmd_chnl.h
index c93bc2b0c9..2546c65a6c 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.h
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.h
@@ -53,4 +53,20 @@ int32_t sxe2_drv_vlan_insert_strip_cfg(struct sxe2_adapter *adapter);
 
 int32_t sxe2_drv_vlan_filter_switch(struct sxe2_adapter *adapter, bool on);
 
+int32_t sxe2_drv_rss_key_set(struct sxe2_adapter *adapter, uint8_t *key, uint16_t key_size);
+
+int32_t sxe2_drv_rss_lut_set(struct sxe2_adapter *adapter, uint8_t *lut, uint16_t lut_size);
+
+int32_t sxe2_drv_rss_hash_ctrl_func(struct sxe2_adapter *adapter, enum sxe2_rss_hash_key_func func);
+
+int32_t sxe2_drv_rss_hf_add(struct sxe2_adapter *adapter,
+			struct sxe2_rss_hf_config *rss_conf);
+
+int32_t sxe2_drv_rss_hf_del(struct sxe2_adapter *adapter,
+				struct sxe2_rss_hf_config *rss_conf);
+
+int32_t sxe2_drv_rss_hf_clear(struct sxe2_adapter *adapter);
+
+int32_t sxe2_drv_ptp_gettime(struct sxe2_adapter *adapter, struct sxe2_rx_queue *rxq);
+
 #endif /* SXE2_CMD_CHNL_H */
diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h
index d69d650148..9998f241f0 100644
--- a/drivers/net/sxe2/sxe2_drv_cmd.h
+++ b/drivers/net/sxe2/sxe2_drv_cmd.h
@@ -6,6 +6,7 @@
 #define SXE2_DRV_CMD_H
 
 #include "sxe2_osal.h"
+#include "sxe2_flow_public.h"
 
 #define SXE2_DRV_CMD_MODULE_S        (16)
 #define SXE2_MK_DRV_CMD(module, cmd) (((module) << SXE2_DRV_CMD_MODULE_S) | ((cmd) & 0xFFFF))
@@ -320,6 +321,34 @@ struct __rte_aligned(4) __rte_packed_begin sxe2_vlan_filter_switch_req {
 	uint8_t rsv;
 } __rte_packed_end;
 
+struct __rte_aligned(4) __rte_packed_begin sxe2_rss_key_req {
+	uint16_t vsi_id;
+	uint16_t key_size;
+	uint8_t key[];
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_rss_lut_req {
+	uint16_t vsi_id;
+	uint16_t lut_size;
+	uint8_t lut[];
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_rss_func_req {
+	uint16_t vsi_id;
+	uint8_t func;
+	uint8_t rsv[1];
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_rss_hf_req {
+	uint16_t vsi_id;
+	uint8_t rsv[2];
+	uint32_t headers[BITS_TO_U32(SXE2_FLOW_HDR_MAX)];
+	uint32_t hash_flds[BITS_TO_U32(SXE2_FLOW_FLD_ID_MAX)];
+	uint32_t hdr_type;
+	uint8_t symm;
+	uint8_t rsv1[3];
+} __rte_packed_end;
+
 enum sxe2_drv_cmd_module {
 	SXE2_DRV_CMD_MODULE_HANDSHAKE = 0,
 	SXE2_DRV_CMD_MODULE_DEV = 1,
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index 9b117f097e..d48841b8e4 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -125,6 +125,11 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
 
 	.vlan_filter_set            = sxe2_dev_vlan_filter_set,
 	.vlan_offload_set           = sxe2_dev_vlan_offload_set,
+
+	.reta_update                = sxe2_dev_rss_reta_update,
+	.reta_query                 = sxe2_dev_rss_reta_query,
+	.rss_hash_update            = sxe2_dev_rss_hash_update,
+	.rss_hash_conf_get          = sxe2_dev_rss_hash_conf_get,
 };
 
 static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
@@ -141,6 +146,12 @@ static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
 		goto end;
 	}
 
+	ret = sxe2_rss_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to init rss, ret=%d", ret);
+		goto end;
+	}
+
 end:
 	return ret;
 }
@@ -281,6 +292,22 @@ static int32_t sxe2_dev_infos_get(struct rte_eth_dev *dev,
 		RTE_ETH_TX_OFFLOAD_IPIP_TNL_TSO |
 		RTE_ETH_TX_OFFLOAD_GENEVE_TNL_TSO;
 
+
+	if (adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_PTP)
+		dev_info->rx_offload_capa |= RTE_ETH_RX_OFFLOAD_TIMESTAMP;
+
+	if (adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_RSS) {
+		dev_info->rx_offload_capa |= RTE_ETH_RX_OFFLOAD_RSS_HASH;
+		dev_info->flow_type_rss_offloads  |= SXE2_RSS_HF_SUPPORT_ALL;
+		dev_info->reta_size = adapter->rss_ctxt.rss_lut_size;
+		dev_info->hash_key_size = adapter->rss_ctxt.rss_key_size;
+		dev_info->rss_algo_capa =
+			RTE_ETH_HASH_ALGO_TO_CAPA(RTE_ETH_HASH_FUNCTION_DEFAULT) |
+			RTE_ETH_HASH_ALGO_TO_CAPA(RTE_ETH_HASH_FUNCTION_TOEPLITZ) |
+			RTE_ETH_HASH_ALGO_TO_CAPA(RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ) |
+			RTE_ETH_HASH_ALGO_TO_CAPA(RTE_ETH_HASH_FUNCTION_SIMPLE_XOR);
+	}
+
 	dev_info->default_rxconf = (struct rte_eth_rxconf) {
 		.rx_thresh = {
 			.pthresh = SXE2_DEFAULT_RX_PTHRESH,
@@ -563,6 +590,8 @@ static int32_t sxe2_func_caps_get(struct sxe2_adapter *adapter)
 
 	sxe2_sw_queue_ctx_hw_cap_set(adapter, &dev_caps.queue_caps);
 
+	sxe2_sw_rss_ctx_hw_cap_set(adapter, &dev_caps.rss_hash_caps);
+
 	sxe2_sw_vsi_ctx_hw_cap_set(adapter, &dev_caps.vsi_caps);
 
 l_end:
@@ -950,8 +979,15 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 		goto init_eth_err;
 	}
 
+	ret = sxe2_rss_disable(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to disable rss, ret=%d", ret);
+		goto init_rss_err;
+	}
+
 	goto l_end;
 
+init_rss_err:
 init_eth_err:
 init_dev_info_err:
 	sxe2_vsi_uninit(dev);
@@ -965,6 +1001,7 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
 {
 	(void)sxe2_dev_stop(dev);
 	(void)sxe2_queues_release(dev);
+	(void)sxe2_rss_disable(dev);
 	sxe2_vsi_uninit(dev);
 	sxe2_dev_pci_map_uinit(dev);
 	sxe2_eth_uinit(dev);
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index 34a4a45e4f..3955788634 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -15,6 +15,7 @@
 
 #include "sxe2_common.h"
 #include "sxe2_vsi.h"
+#include "sxe2_rss.h"
 #include "sxe2_irq.h"
 #include "sxe2_queue.h"
 #include "sxe2_mac.h"
@@ -122,6 +123,11 @@ enum {
 	SXE2_FLAGS_NBITS
 };
 
+struct sxe2_ptp_context {
+	uint64_t mbuf_rx_ts_flag;
+	int32_t mbuf_rx_ts_offset;
+};
+
 struct sxe2_devargs {
 	uint8_t flow_dup_pattern_mode;
 	uint8_t func_flow_direct_en;
@@ -300,7 +306,9 @@ struct sxe2_adapter {
 	struct sxe2_queue_context     q_ctxt;
 	struct sxe2_vsi_context       vsi_ctxt;
 	struct sxe2_filter_context    filter_ctxt;
+	struct sxe2_rss_context       rss_ctxt;
 	struct sxe2_link_context      link_ctxt;
+	struct sxe2_ptp_context       ptp_ctxt;
 	struct sxe2_devargs           devargs;
 	struct sxe2_switchdev_info    switchdev_info;
 	bool                          rule_started;
diff --git a/drivers/net/sxe2/sxe2_flow_define.h b/drivers/net/sxe2/sxe2_flow_define.h
new file mode 100644
index 0000000000..d2f6000efa
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_flow_define.h
@@ -0,0 +1,143 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_FLOW_DEFINE_H__
+#define __SXE2_FLOW_DEFINE_H__
+#include <rte_tailq.h>
+#include <rte_eal.h>
+#include <rte_flow_driver.h>
+
+#include "sxe2_osal.h"
+#include "sxe2_flow_public.h"
+
+#define SXE2_FLOW_ETH_TYPE_MIN        (1500)
+
+enum sxe2_expansion {
+	SXE2_EXPANSION_ERROR = 0,
+	SXE2_EXPANSION_OUTER_ETH,
+	SXE2_EXPANSION_OUTER_VLAN,
+	SXE2_EXPANSION_OUTER_QINQ,
+	SXE2_EXPANSION_OUTER_IPV4,
+	SXE2_EXPANSION_OUTER_IPV4_FRAG_EXT,
+	SXE2_EXPANSION_OUTER_IPV6,
+	SXE2_EXPANSION_OUTER_IPV6_FRAG_EXT,
+	SXE2_EXPANSION_OUTER_UDP,
+	SXE2_EXPANSION_OUTER_TCP,
+	SXE2_EXPANSION_OUTER_SCTP,
+
+	SXE2_EXPANSION_VXLAN,
+	SXE2_EXPANSION_VXLAN_GPE,
+	SXE2_EXPANSION_GRE,
+	SXE2_EXPANSION_NVGRE,
+	SXE2_EXPANSION_GENEVE,
+	SXE2_EXPANSION_GTPU,
+	SXE2_EXPANSION_IPIP,
+	SXE2_EXPANSION_OUTER_END,
+
+	SXE2_EXPANSION_ETH,
+	SXE2_EXPANSION_VLAN,
+	SXE2_EXPANSION_IPV4,
+	SXE2_EXPANSION_IPV4_FRAG_EXT,
+	SXE2_EXPANSION_IPV6,
+	SXE2_EXPANSION_IPV6_FRAG_EXT,
+	SXE2_EXPANSION_UDP,
+	SXE2_EXPANSION_TCP,
+	SXE2_EXPANSION_SCTP,
+
+	SXE2_EXPANSION_END,
+	SXE2_EXPANSION_MAX,
+};
+
+enum sxe2_flow_udp_tunnel_protocol {
+	SXE2_FLOW_UDP_TUNNEL_PROTOCOL_VXLAN,
+	SXE2_FLOW_UDP_TUNNEL_PROTOCOL_VXLAN_GPE,
+	SXE2_FLOW_UDP_TUNNEL_PROTOCOL_GENEVE,
+	SXE2_FLOW_UDP_TUNNEL_PROTOCOL_GTP_U,
+	SXE2_FLOW_UDP_TUNNEL_PROTOCOL_NVGRE,
+	SXE2_FLOW_UDP_TUNNEL_MAX,
+};
+
+enum {
+	SXE2_FLOW_ETH_TYPE_IPV4 = 0x0800,
+	SXE2_FLOW_ETH_TYPE_IPV6 = 0x86DD,
+	SXE2_FLOW_IP_PROTOCOL_GRE = 0x2F,
+	SXE2_FLOW_IP_PROTOCOL_IPV4 = 0x04,
+	SXE2_FLOW_IP_PROTOCOL_IPV6 = 0x29,
+	SXE2_FLOW_IP_PROTOCOL_ETH = 0x3B,
+	SXE2_FLOW_IP_PROTOCOL_UDP = 0x11,
+	SXE2_FLOW_IP_PROTOCOL_TCP = 0x06,
+	SXE2_FLOW_IP_PROTOCOL_SCTP = 0x84,
+};
+
+union sxe2_flow_item_raw {
+	struct sxe2_flow_item item;
+	uint8_t raw[sizeof(struct sxe2_flow_item)];
+};
+
+struct sxe2_flow {
+	TAILQ_ENTRY(sxe2_flow) next;
+	enum sxe2_flow_engine_type engine_type;
+	struct sxe2_flow_pattern pattern_outer;
+	struct sxe2_flow_pattern pattern_inner;
+	uint8_t has_mask;
+	uint8_t has_spec;
+	uint8_t has_hdr;
+	struct sxe2_flow_meta meta;
+	struct sxe2_flow_action action;
+	uint32_t flow_id;
+	int32_t create_err;
+	DECLARE_BITMAP(flow_type, SXE2_EXPANSION_MAX);
+};
+
+TAILQ_HEAD(sxe2_flow_list_t, sxe2_flow);
+
+struct rte_flow {
+	TAILQ_ENTRY(rte_flow) next;
+	struct sxe2_flow_list_t sxe2_flow_list;
+};
+TAILQ_HEAD(rte_flow_list_t, rte_flow);
+
+struct sxe2_fnav_cid_mgr {
+	TAILQ_ENTRY(sxe2_fnav_cid_mgr) next;
+	uint16_t stat_index;
+	uint32_t user_id;
+	uint32_t driver_id;
+	uint32_t count_type;
+	uint64_t hits;
+	uint64_t bytes;
+};
+TAILQ_HEAD(sxe2_fnav_cid_mgr_list_t, sxe2_fnav_cid_mgr);
+
+struct sxe2_fnav_count_resource {
+	uint32_t count_type;
+	uint32_t global_index;
+	struct sxe2_fnav_cid_mgr_list_t fnav_cid_mgr_list;
+};
+
+struct sxe2_flow_context {
+	struct rte_flow_list_t rte_flow_list;
+	rte_spinlock_t flow_list_lock;
+	struct sxe2_fnav_count_resource hw_res;
+	uint32_t fnav_inited;
+};
+#define SXE2_INVALID_RSS_ATTR	\
+			(RTE_ETH_RSS_L3_PRE40 | RTE_ETH_RSS_L3_PRE56 | RTE_ETH_RSS_L3_PRE96)
+#define SXE2_VALID_RSS_IPV4_L4	 \
+			(RTE_ETH_RSS_NONFRAG_IPV4_UDP | RTE_ETH_RSS_NONFRAG_IPV4_TCP | \
+			 RTE_ETH_RSS_NONFRAG_IPV4_SCTP)
+
+#define SXE2_VALID_RSS_IPV6_L4	\
+			(RTE_ETH_RSS_NONFRAG_IPV6_UDP | RTE_ETH_RSS_NONFRAG_IPV6_TCP | \
+			 RTE_ETH_RSS_NONFRAG_IPV6_SCTP)
+#define SXE2_VALID_RSS_IPV4	\
+			(RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_FRAG_IPV4  | \
+			 RTE_ETH_RSS_NONFRAG_IPV4_OTHER | SXE2_VALID_RSS_IPV4_L4)
+#define SXE2_VALID_RSS_IPV6	\
+			(RTE_ETH_RSS_IPV6 | RTE_ETH_RSS_FRAG_IPV6 | \
+			 RTE_ETH_RSS_NONFRAG_IPV6_OTHER | SXE2_VALID_RSS_IPV6_L4)
+
+#define SXE2_VALID_RSS_L3	(SXE2_VALID_RSS_IPV4 | SXE2_VALID_RSS_IPV6)
+#define SXE2_VALID_RSS_L4	(SXE2_VALID_RSS_IPV4_L4 | SXE2_VALID_RSS_IPV6_L4)
+
+#endif /* __SXE2_FLOW_DEFINE_H__ */
diff --git a/drivers/net/sxe2/sxe2_queue.c b/drivers/net/sxe2/sxe2_queue.c
index 1786d6ea4f..220cab6fce 100644
--- a/drivers/net/sxe2/sxe2_queue.c
+++ b/drivers/net/sxe2/sxe2_queue.c
@@ -17,6 +17,7 @@ void sxe2_sw_queue_ctx_hw_cap_set(struct sxe2_adapter *adapter,
 
 int32_t sxe2_queues_init(struct rte_eth_dev *dev)
 {
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
 	int32_t ret = 0;
 	uint16_t buf_size;
 	uint16_t frame_size;
@@ -36,6 +37,16 @@ int32_t sxe2_queues_init(struct rte_eth_dev *dev)
 			dev->data->scattered_rx = 1;
 	}
 
+	adapter->ptp_ctxt.mbuf_rx_ts_offset = -1;
+	adapter->ptp_ctxt.mbuf_rx_ts_flag = 0;
+	if (dev->data->dev_conf.rxmode.offloads & RTE_ETH_RX_OFFLOAD_TIMESTAMP) {
+		ret = rte_mbuf_dyn_rx_timestamp_register
+			(&adapter->ptp_ctxt.mbuf_rx_ts_offset,
+			 (uint64_t *)&adapter->ptp_ctxt.mbuf_rx_ts_flag);
+		if (ret)
+			PMD_LOG_ERR(INIT, "Failed to enable timestamp offloads, ret=%d", ret);
+	}
+
 	return ret;
 }
 
diff --git a/drivers/net/sxe2/sxe2_rss.c b/drivers/net/sxe2/sxe2_rss.c
new file mode 100644
index 0000000000..1d56613043
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_rss.c
@@ -0,0 +1,584 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include "sxe2_rss.h"
+#include "sxe2_common_log.h"
+#include "sxe2_ethdev.h"
+#include "sxe2_cmd_chnl.h"
+
+void sxe2_sw_rss_ctx_hw_cap_set(struct sxe2_adapter *adapter,
+		struct sxe2_drv_rss_hash_caps *rss_caps)
+{
+	adapter->rss_ctxt.rss_key_size = rss_caps->hash_key_size;
+	adapter->rss_ctxt.rss_lut_size = rss_caps->lut_key_size;
+}
+
+int32_t sxe2_rss_hash_key_init(struct rte_eth_dev *dev)
+{
+	struct rte_eth_rss_conf *rss_conf = &dev->data->dev_conf.rx_adv_conf.rss_conf;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_rss_context *rss_ctxt = &adapter->rss_ctxt;
+	int32_t ret = 0;
+	uint16_t i = 0;
+
+	if (rss_ctxt->rss_key == NULL) {
+		rss_ctxt->rss_key = (uint8_t *)rte_zmalloc("rss_key", rss_ctxt->rss_key_size, 0);
+		if (rss_ctxt->rss_key == NULL) {
+			PMD_LOG_ERR(INIT, "Failed to allocate rss key");
+			ret = -ENOMEM;
+			goto l_end;
+		}
+	}
+
+	if (!rss_conf->rss_key) {
+		for (i = 0; i < rss_ctxt->rss_key_size; i++)
+			rss_ctxt->rss_key[i] = (uint8_t)rte_rand();
+	} else {
+		rte_memcpy(rss_ctxt->rss_key, rss_conf->rss_key,
+			   RTE_MIN(rss_conf->rss_key_len, rss_ctxt->rss_key_size));
+	}
+
+	ret = sxe2_drv_rss_key_set(adapter, rss_ctxt->rss_key,
+				   rss_ctxt->rss_key_size);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set rss key, ret:%d", ret);
+		rte_free(rss_ctxt->rss_key);
+		rss_ctxt->rss_key = NULL;
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+void sxe2_rss_hash_key_uninit(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_rss_context *rss_ctxt = &adapter->rss_ctxt;
+
+	if (rss_ctxt->rss_key) {
+		rte_free(rss_ctxt->rss_key);
+		rss_ctxt->rss_key = NULL;
+	}
+}
+
+int32_t sxe2_rss_lut_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_rss_context *rss_ctxt = &adapter->rss_ctxt;
+	int32_t ret = 0;
+	uint16_t i;
+
+	if (rss_ctxt->rss_lut == NULL) {
+		rss_ctxt->rss_lut = (uint8_t *)rte_zmalloc("rss_lut", rss_ctxt->rss_lut_size, 0);
+		if (rss_ctxt->rss_lut == NULL) {
+			PMD_DEV_LOG_ERR(adapter, INIT, "Failed to allocate rss lut");
+			ret = -ENOMEM;
+			goto l_end;
+		}
+	}
+
+	for (i = 0; i < rss_ctxt->rss_lut_size; i++)
+		rss_ctxt->rss_lut[i] = (uint8_t)(i % dev->data->nb_rx_queues);
+
+	ret = sxe2_drv_rss_lut_set(adapter, rss_ctxt->rss_lut, rss_ctxt->rss_lut_size);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set rss lut, ret:%d", ret);
+		rte_free(rss_ctxt->rss_lut);
+		rss_ctxt->rss_lut = NULL;
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+void sxe2_rss_lut_uninit(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_rss_context *rss_ctxt = &adapter->rss_ctxt;
+
+	if (rss_ctxt->rss_lut) {
+		rte_free(rss_ctxt->rss_lut);
+		rss_ctxt->rss_lut = NULL;
+	}
+}
+
+static struct sxe2_rss_hf_config sxe2_rss_default_hf_config[] = {
+	{
+		.rss_hf = RTE_ETH_RSS_L2_PAYLOAD,
+		.hdrs = {SXE2_FLOW_HDR_ETH,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_ETH_TYPE,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_IPV4,
+		.hdrs = {SXE2_FLOW_HDR_IPV4,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV4_SA,
+				 SXE2_FLOW_FLD_ID_IPV4_DA,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_IPV6,
+		.hdrs = {SXE2_FLOW_HDR_IPV6,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV6_SA,
+				 SXE2_FLOW_FLD_ID_IPV6_DA,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_FRAG_IPV4,
+		.hdrs = {SXE2_FLOW_HDR_IPV4,
+				 SXE2_FLOW_HDR_IPV_FRAG,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV4_SA,
+				 SXE2_FLOW_FLD_ID_IPV4_DA,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_FRAG_IPV6,
+		.hdrs = {SXE2_FLOW_HDR_IPV6,
+				 SXE2_FLOW_HDR_IPV_FRAG,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV6_SA,
+				 SXE2_FLOW_FLD_ID_IPV6_DA,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_NONFRAG_IPV4_OTHER,
+		.hdrs = {SXE2_FLOW_HDR_IPV4,
+				 SXE2_FLOW_HDR_IPV_OTHER,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV4_SA,
+				 SXE2_FLOW_FLD_ID_IPV4_DA,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_NONFRAG_IPV6_OTHER,
+		.hdrs = {SXE2_FLOW_HDR_IPV6,
+				 SXE2_FLOW_HDR_IPV_OTHER,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV6_SA,
+				 SXE2_FLOW_FLD_ID_IPV6_DA,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_NONFRAG_IPV4_UDP,
+		.hdrs = {SXE2_FLOW_HDR_IPV4,
+				 SXE2_FLOW_HDR_UDP,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV4_SA,
+				 SXE2_FLOW_FLD_ID_IPV4_DA,
+				 SXE2_FLOW_FLD_ID_UDP_SRC_PORT,
+				 SXE2_FLOW_FLD_ID_UDP_DST_PORT,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_NONFRAG_IPV6_UDP,
+		.hdrs = {SXE2_FLOW_HDR_IPV6,
+				 SXE2_FLOW_HDR_UDP,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV6_SA,
+				 SXE2_FLOW_FLD_ID_IPV6_DA,
+				 SXE2_FLOW_FLD_ID_UDP_SRC_PORT,
+				 SXE2_FLOW_FLD_ID_UDP_DST_PORT,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_NONFRAG_IPV4_TCP,
+		.hdrs = {SXE2_FLOW_HDR_IPV4,
+				 SXE2_FLOW_HDR_TCP,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV4_SA,
+				 SXE2_FLOW_FLD_ID_IPV4_DA,
+				 SXE2_FLOW_FLD_ID_TCP_SRC_PORT,
+				 SXE2_FLOW_FLD_ID_TCP_DST_PORT,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_NONFRAG_IPV6_TCP,
+		.hdrs = {SXE2_FLOW_HDR_IPV6,
+				 SXE2_FLOW_HDR_TCP,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV6_SA,
+				 SXE2_FLOW_FLD_ID_IPV6_DA,
+				 SXE2_FLOW_FLD_ID_TCP_SRC_PORT,
+				 SXE2_FLOW_FLD_ID_TCP_DST_PORT,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_NONFRAG_IPV4_SCTP,
+		.hdrs = {SXE2_FLOW_HDR_IPV4,
+				 SXE2_FLOW_HDR_SCTP,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV4_SA,
+				 SXE2_FLOW_FLD_ID_IPV4_DA,
+				 SXE2_FLOW_FLD_ID_SCTP_SRC_PORT,
+				 SXE2_FLOW_FLD_ID_SCTP_DST_PORT,
+				 SXE2_FLOW_END},
+	},
+	{
+		.rss_hf = RTE_ETH_RSS_NONFRAG_IPV6_SCTP,
+		.hdrs = {SXE2_FLOW_HDR_IPV6,
+				 SXE2_FLOW_HDR_SCTP,
+				 SXE2_FLOW_END},
+		.flds = {SXE2_FLOW_FLD_ID_IPV6_SA,
+				 SXE2_FLOW_FLD_ID_IPV6_DA,
+				 SXE2_FLOW_FLD_ID_SCTP_SRC_PORT,
+				 SXE2_FLOW_FLD_ID_SCTP_DST_PORT,
+				 SXE2_FLOW_END},
+	},
+};
+
+int32_t sxe2_rss_hf_type_set(struct rte_eth_dev *dev, uint64_t rss_hf)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_rss_context *rss_ctxt = &adapter->rss_ctxt;
+	int32_t ret = 0;
+	uint32_t i;
+	uint8_t symm = 0;
+
+	if (0 == (rss_hf & SXE2_RSS_HF_SUPPORT_ALL) && rss_hf != 0) {
+		PMD_DEV_LOG_ERR(adapter, DRV,
+			"Failed to set unsupported rss_hf:0x%016" PRIx64,
+			rss_hf);
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	for (i = 0; i < RTE_DIM(sxe2_rss_default_hf_config); i++) {
+		if (rss_ctxt->rss_hf & sxe2_rss_default_hf_config[i].rss_hf) {
+			sxe2_rss_default_hf_config[i].symm = rss_ctxt->symm;
+			ret = sxe2_drv_rss_hf_del(adapter, &sxe2_rss_default_hf_config[i]);
+			if (ret) {
+				PMD_DEV_LOG_ERR(adapter, INIT,
+					"Failed to del rss hf cfg[%d], ret:%d", i, ret);
+				goto l_end;
+			}
+		}
+	}
+
+	if (rss_ctxt->hash_func == RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ)
+		symm = 1;
+
+	for (i = 0; i < RTE_DIM(sxe2_rss_default_hf_config); i++) {
+		if (rss_hf & sxe2_rss_default_hf_config[i].rss_hf) {
+			sxe2_rss_default_hf_config[i].symm = symm;
+			ret = sxe2_drv_rss_hf_add(adapter, &sxe2_rss_default_hf_config[i]);
+			if (ret) {
+				PMD_DEV_LOG_ERR(adapter, INIT,
+					"Failed to add rss hf cfg[%d], ret:%d", i, ret);
+				goto l_end;
+			}
+		}
+	}
+
+	rss_ctxt->rss_hf = rss_hf & SXE2_RSS_HF_SUPPORT_ALL;
+	rss_ctxt->symm = symm;
+l_end:
+	return ret;
+}
+
+int32_t sxe2_rss_hash_function_set(struct rte_eth_dev *dev, enum rte_eth_hash_function func)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	enum sxe2_rss_hash_key_func hash_func = SXE2_RSS_HASH_FUNC_SYM_TOEPLITZ;
+	int32_t ret = 0;
+
+	switch (func) {
+	case RTE_ETH_HASH_FUNCTION_DEFAULT:
+	case RTE_ETH_HASH_FUNCTION_TOEPLITZ:
+	case RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ:
+		hash_func = SXE2_RSS_HASH_FUNC_SYM_TOEPLITZ;
+		break;
+	case RTE_ETH_HASH_FUNCTION_SIMPLE_XOR:
+		hash_func = SXE2_RSS_HASH_FUNC_XOR;
+		break;
+	default:
+		PMD_DEV_LOG_ERR(adapter, DRV, "RSS hash function[%d] not support.", func);
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	ret = sxe2_drv_rss_hash_ctrl_func(adapter, hash_func);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set rss hash function, ret=[%d]", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_rss_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct rte_eth_rss_conf *rss_conf = &dev->data->dev_conf.rx_adv_conf.rss_conf;
+	enum rte_eth_hash_function rss_func = RTE_ETH_HASH_FUNCTION_SYMMETRIC_TOEPLITZ;
+	int32_t ret = 0;
+
+	adapter->rss_ctxt.inited = false;
+
+	if (dev->data->nb_rx_queues <= 1) {
+		PMD_DEV_LOG_DEBUG(adapter, INIT, "No need to init rss, rx queues %d.",
+				dev->data->nb_rx_queues);
+		goto l_end;
+	}
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_RSS) == 0) {
+		PMD_DEV_LOG_WARN(adapter, INIT, "RSS not supported");
+		goto l_end;
+	}
+
+	ret = sxe2_rss_hash_key_init(dev);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to init rss key");
+		goto l_end;
+	}
+
+	ret = sxe2_rss_lut_init(dev);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to init rss lut");
+		goto l_err_key;
+	}
+
+	rss_func = rss_conf->algorithm;
+	ret = sxe2_rss_hash_function_set(dev, rss_func);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to init rss hash function");
+		goto l_err_lut;
+	}
+	ret = sxe2_rss_hf_type_set(dev, rss_conf->rss_hf);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, INIT, "Failed to set rss hf type");
+		goto l_err_lut;
+	}
+	adapter->rss_ctxt.inited = true;
+	goto l_end;
+
+l_err_lut:
+	sxe2_rss_lut_uninit(dev);
+l_err_key:
+	sxe2_rss_hash_key_uninit(dev);
+l_end:
+	return ret;
+}
+
+int32_t sxe2_rss_disable(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	PMD_INIT_FUNC_TRACE();
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_RSS) == 0)
+		goto l_end;
+
+	ret = sxe2_drv_rss_hf_clear(adapter);
+	if (ret)
+		PMD_LOG_ERR(INIT, "Failed to clear rss hf");
+
+	sxe2_rss_hash_key_uninit(dev);
+
+	sxe2_rss_lut_uninit(dev);
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_dev_rss_reta_update(struct rte_eth_dev *dev,
+		struct rte_eth_rss_reta_entry64 *reta_conf,
+		uint16_t reta_size)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_rss_context *rss_ctxt = &adapter->rss_ctxt;
+	uint8_t *lut_tmp = NULL;
+	int32_t ret = 0;
+	uint16_t i;
+	uint16_t shift;
+	uint16_t idx;
+
+	if (!adapter->rss_ctxt.inited) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "RSS not inited.");
+		ret = -ENOTSUP;
+		goto l_end;
+	}
+
+	if (reta_size != rss_ctxt->rss_lut_size) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "The size of hash lookup table configured "
+				"(%d) doesn't match the number of hardware can "
+			"support (%d)", reta_size, rss_ctxt->rss_lut_size);
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	lut_tmp = rte_zmalloc("rss_lut_temp", reta_size, 0);
+	if (!lut_tmp) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "No memory can be allocated");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+	rte_memcpy(lut_tmp, rss_ctxt->rss_lut, reta_size);
+
+	for (i = 0; i < reta_size; i++) {
+		idx = i / RTE_ETH_RETA_GROUP_SIZE;
+		shift = i % RTE_ETH_RETA_GROUP_SIZE;
+		if (reta_conf[idx].mask & (1ULL << shift))
+			lut_tmp[i] = reta_conf[idx].reta[shift];
+	}
+
+	ret = sxe2_drv_rss_lut_set(adapter, lut_tmp, reta_size);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to set rss lut");
+		goto l_end;
+	}
+
+	rte_memcpy(rss_ctxt->rss_lut, lut_tmp, reta_size);
+
+l_end:
+	if (lut_tmp)
+		rte_free(lut_tmp);
+	return ret;
+}
+
+int32_t sxe2_dev_rss_reta_query(struct rte_eth_dev *dev,
+		struct rte_eth_rss_reta_entry64 *reta_conf,
+		uint16_t reta_size)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_rss_context *rss_ctxt = &adapter->rss_ctxt;
+	int32_t ret = 0;
+	uint16_t i;
+	uint16_t shift;
+	uint16_t idx;
+
+	if (!adapter->rss_ctxt.inited) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "RSS not inited.");
+		ret = -ENOTSUP;
+		goto l_end;
+	}
+
+	if (reta_size != rss_ctxt->rss_lut_size) {
+		PMD_LOG_ERR(INIT, "The size of hash lookup table configured "
+			"(%d) doesn't match the number of hardware can "
+			"support (%d)", reta_size, rss_ctxt->rss_lut_size);
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	for (i = 0; i < reta_size; i++) {
+		idx = i / RTE_ETH_RETA_GROUP_SIZE;
+		shift = i % RTE_ETH_RETA_GROUP_SIZE;
+		if (reta_conf[idx].mask & (1ULL << shift))
+			reta_conf[idx].reta[shift] = rss_ctxt->rss_lut[i];
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_rss_hash_key_update(struct rte_eth_dev *dev,
+		struct rte_eth_rss_conf *rss_conf)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_rss_context *rss_ctxt = &adapter->rss_ctxt;
+	int32_t ret = 0;
+	uint16_t i;
+
+	if (rss_conf->rss_key_len == 0 || rss_conf->rss_key == NULL)
+		goto l_end;
+
+	if (rss_conf->rss_key_len != rss_ctxt->rss_key_size) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "The size of hash key configured "
+			"(%d) doesn't match the size of hardware can "
+			"support (%d)", rss_conf->rss_key_len,
+			rss_ctxt->rss_key_size);
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	for (i = 0; i < rss_conf->rss_key_len; i++) {
+		if (rss_conf->rss_key[i] != rss_ctxt->rss_key[i])
+			break;
+	}
+	if (i == rss_conf->rss_key_len)
+		goto l_end;
+
+	ret = sxe2_drv_rss_key_set(adapter, rss_conf->rss_key,
+				   rss_conf->rss_key_len);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to set rss key");
+		goto l_end;
+	}
+
+	rte_memcpy(rss_ctxt->rss_key, rss_conf->rss_key, rss_conf->rss_key_len);
+l_end:
+	return ret;
+}
+
+int32_t sxe2_dev_rss_hash_update(struct rte_eth_dev *dev,
+		struct rte_eth_rss_conf *rss_conf)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_rss_context *rss_ctxt = &adapter->rss_ctxt;
+	int32_t ret = -1;
+
+	if (!adapter->rss_ctxt.inited) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "RSS not inited.");
+		ret = -ENOTSUP;
+		goto l_end;
+	}
+
+	ret = sxe2_rss_hash_key_update(dev, rss_conf);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to set rss hash key");
+		goto l_end;
+	}
+
+	if (rss_conf->algorithm != rss_ctxt->hash_func) {
+		ret = sxe2_rss_hash_function_set(dev, rss_conf->algorithm);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to set rss hash function");
+			goto l_end;
+		}
+		rss_ctxt->hash_func = rss_conf->algorithm;
+	}
+
+	if ((rss_conf->rss_hf & SXE2_RSS_HF_SUPPORT_ALL)
+			!= rss_ctxt->rss_hf) {
+		ret = sxe2_rss_hf_type_set(dev, rss_conf->rss_hf);
+		if (ret) {
+			PMD_DEV_LOG_ERR(adapter, DRV, "Failed to set rss hf type");
+			goto l_end;
+		}
+	}
+	ret = 0;
+l_end:
+	return ret;
+}
+
+int32_t sxe2_dev_rss_hash_conf_get(struct rte_eth_dev *dev,
+		struct rte_eth_rss_conf *rss_conf)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_rss_context *rss_ctxt = &adapter->rss_ctxt;
+	int32_t ret = 0;
+
+	if (adapter->rss_ctxt.inited == 0) {
+		PMD_DEV_LOG_INFO(adapter, DRV, "RSS not inited.");
+		ret = -ENOTSUP;
+		goto l_end;
+	}
+
+	if (rss_conf->rss_key) {
+		rss_conf->rss_key_len = rss_ctxt->rss_key_size;
+		rte_memcpy(rss_conf->rss_key, rss_ctxt->rss_key, rss_ctxt->rss_key_size);
+	}
+	rss_conf->rss_hf = rss_ctxt->rss_hf;
+	rss_conf->algorithm = rss_ctxt->hash_func;
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_rss.h b/drivers/net/sxe2/sxe2_rss.h
new file mode 100644
index 0000000000..2a454ac1b3
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_rss.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_RSS_H__
+#define __SXE2_RSS_H__
+#include <ethdev_driver.h>
+#include "sxe2_osal.h"
+#include "sxe2_drv_cmd.h"
+#include "sxe2_flow_define.h"
+
+#define SXE2_FLOW_END (0xFFFF)
+
+struct sxe2_rss_context {
+	enum rte_eth_hash_function hash_func;
+	uint16_t rss_key_size;
+	uint16_t rss_lut_size;
+	uint8_t  *rss_key;
+	uint8_t  *rss_lut;
+	uint64_t rss_hf;
+	uint8_t symm;
+	bool inited;
+};
+
+struct sxe2_rss_hf_config {
+	uint64_t rss_hf;
+	uint16_t hdrs[SXE2_FLOW_HDR_MAX];
+	uint16_t flds[SXE2_FLOW_FLD_ID_MAX];
+	uint8_t symm;
+};
+
+#define SXE2_RSS_HF_SUPPORT_ALL ( \
+	RTE_ETH_RSS_IPV4 | \
+	RTE_ETH_RSS_FRAG_IPV4 | \
+	RTE_ETH_RSS_NONFRAG_IPV4_TCP | \
+	RTE_ETH_RSS_NONFRAG_IPV4_UDP | \
+	RTE_ETH_RSS_NONFRAG_IPV4_SCTP | \
+	RTE_ETH_RSS_NONFRAG_IPV4_OTHER | \
+	RTE_ETH_RSS_IPV6 | \
+	RTE_ETH_RSS_FRAG_IPV6 | \
+	RTE_ETH_RSS_NONFRAG_IPV6_TCP | \
+	RTE_ETH_RSS_NONFRAG_IPV6_UDP | \
+	RTE_ETH_RSS_NONFRAG_IPV6_SCTP | \
+	RTE_ETH_RSS_NONFRAG_IPV6_OTHER | \
+	RTE_ETH_RSS_L2_PAYLOAD)
+
+struct sxe2_adapter;
+
+void sxe2_sw_rss_ctx_hw_cap_set(struct sxe2_adapter *adapter,
+		struct sxe2_drv_rss_hash_caps *rss_caps);
+
+int32_t sxe2_rss_hash_key_init(struct rte_eth_dev *dev);
+
+void sxe2_rss_hash_key_uninit(struct rte_eth_dev *dev);
+
+int32_t sxe2_rss_lut_init(struct rte_eth_dev *dev);
+
+void sxe2_rss_lut_uninit(struct rte_eth_dev *dev);
+
+int32_t sxe2_rss_init(struct rte_eth_dev *dev);
+
+int32_t sxe2_rss_hash_function_set(struct rte_eth_dev *dev, enum rte_eth_hash_function func);
+
+int32_t sxe2_rss_hf_type_set(struct rte_eth_dev *dev, uint64_t rss_hf);
+
+int32_t sxe2_rss_disable(struct rte_eth_dev *dev);
+
+int32_t sxe2_dev_rss_reta_update(struct rte_eth_dev *dev,
+		struct rte_eth_rss_reta_entry64 *reta_conf,
+		uint16_t reta_size);
+
+int32_t sxe2_dev_rss_reta_query(struct rte_eth_dev *dev,
+		struct rte_eth_rss_reta_entry64 *reta_conf,
+		uint16_t reta_size);
+
+int32_t sxe2_dev_rss_hash_update(struct rte_eth_dev *dev,
+		struct rte_eth_rss_conf *rss_conf);
+
+int32_t sxe2_dev_rss_hash_conf_get(struct rte_eth_dev *dev,
+		struct rte_eth_rss_conf *rss_conf);
+#endif /* __SXE2_RSS_H__ */
diff --git a/drivers/net/sxe2/sxe2_txrx.h b/drivers/net/sxe2/sxe2_txrx.h
index 6f6ff3e3d1..2ff5fee7c7 100644
--- a/drivers/net/sxe2/sxe2_txrx.h
+++ b/drivers/net/sxe2/sxe2_txrx.h
@@ -23,4 +23,8 @@ int32_t sxe2_tx_burst_mode_get(struct rte_eth_dev *dev,
 int32_t sxe2_rx_burst_mode_get(struct rte_eth_dev *dev,
 			__rte_unused uint16_t queue_id, struct rte_eth_burst_mode *mode);
 
+#ifndef RTE_LIBRTE_SXE2_16BYTE_RX_DESC
+int32_t sxe2_rx_update_ptp_time(struct sxe2_rx_queue *rxq);
+#endif
+
 #endif /* SXE2_TXRX_H */
diff --git a/drivers/net/sxe2/sxe2_txrx_poll.c b/drivers/net/sxe2/sxe2_txrx_poll.c
index 21d5c38725..3c6fe37404 100644
--- a/drivers/net/sxe2/sxe2_txrx_poll.c
+++ b/drivers/net/sxe2/sxe2_txrx_poll.c
@@ -17,6 +17,7 @@
 #include "sxe2_queue.h"
 #include "sxe2_ethdev.h"
 #include "sxe2_common_log.h"
+#include "sxe2_cmd_chnl.h"
 
 static __rte_always_inline int32_t
 sxe2_tx_bufs_free(struct sxe2_tx_queue *txq)
@@ -282,6 +283,30 @@ sxe2_tx_desc_checksum_fill(uint64_t offloads, uint32_t *desc_cmd, uint32_t *desc
 	return;
 }
 
+static __rte_always_inline void sxe2_desc_tso_fill(struct rte_mbuf *tx_pkt,
+						   uint64_t *desc_type_cmd_tso_mss,
+						   union sxe2_tx_offload_info ol_info)
+{
+	uint32_t hdr_len;
+	uint32_t tso_len;
+
+	if (!ol_info.l4_len) {
+		PMD_LOG_DEBUG(TX, "TSO ERROR: L4 length is 0");
+		goto l_end;
+	}
+	hdr_len = ol_info.l2_len + ol_info.l3_len + ol_info.l4_len;
+	if (tx_pkt->ol_flags & RTE_MBUF_F_TX_TUNNEL_MASK)
+		hdr_len += ol_info.outer_l2_len + ol_info.outer_l3_len;
+
+	tso_len = tx_pkt->pkt_len - hdr_len;
+	*desc_type_cmd_tso_mss |=
+			((uint64_t)SXE2_TX_CTXT_DESC_CMD_TSO << SXE2_TX_CTXT_DESC_CMD_SHIFT) |
+			((uint64_t)tso_len << SXE2_TX_CTXT_DESC_TSO_LEN_SHIFT) |
+			((uint64_t)tx_pkt->tso_segsz << SXE2_TX_CTXT_DESC_MSS_SHIFT);
+l_end:
+	return;
+}
+
 static __rte_always_inline uint64_t
 sxe2_tx_data_desc_build_cobt(uint32_t cmd, uint32_t offset, uint16_t buf_size, uint16_t l2tag)
 {
@@ -395,6 +420,11 @@ uint16_t sxe2_tx_pkts(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkt
 				rte_pktmbuf_free_seg(buffer->mbuf);
 				buffer->mbuf = NULL;
 			}
+			if (offloads & (RTE_MBUF_F_TX_TCP_SEG | RTE_MBUF_F_TX_UDP_SEG))
+				sxe2_desc_tso_fill(tx_pkt,
+					&desc_type_cmd_tso_mss, ol_info);
+			else if (offloads & RTE_MBUF_F_TX_IEEE1588_TMST)
+				desc_type_cmd_tso_mss |= SXE2_TX_CTXT_DESC_CMD_TSYN_MASK;
 
 			if (offloads & RTE_MBUF_F_TX_QINQ) {
 				desc_l2tag2 = tx_pkt->vlan_tci_outer;
@@ -707,6 +737,57 @@ sxe2_rx_desc_filter_para_fill(struct sxe2_rx_queue *rxq __rte_unused,
 #endif
 }
 
+#ifndef RTE_LIBRTE_SXE2_16BYTE_RX_DESC
+int32_t sxe2_rx_update_ptp_time(struct sxe2_rx_queue *rxq)
+{
+	struct sxe2_adapter *adapter;
+	uint64_t cur_time_ms;
+	int32_t ret = 0;
+	cur_time_ms = rte_get_timer_cycles() / (rte_get_timer_hz() / 1000);
+
+	if (likely((cur_time_ms - rxq->update_time) < SXE2_RX_PKTS_TS_TIMEOUT_VAL))
+		goto l_end;
+	rxq->update_time = cur_time_ms;
+	adapter = rxq->vsi->adapter;
+	rxq->ts_need_update = true;
+	ret = sxe2_drv_ptp_gettime(adapter, rxq);
+	if (rxq->desc_ts < rxq->ts_low)
+		rxq->ts_need_update = false;
+
+	PMD_LOG_INFO(RX, "rxq update time ret=%d, cur time=%" PRIu64 ", rxqh=%" PRIu64 ", rxql=%d",
+		     ret, cur_time_ms, rxq->ts_high, rxq->ts_low);
+l_end:
+	return ret;
+}
+
+static inline void sxe2_rx_desc_ptp_para_fill(struct sxe2_rx_queue *rxq,
+					      struct rte_mbuf *mbuf,
+					      union sxe2_rx_desc *desc)
+{
+	struct sxe2_adapter *adapter = rxq->vsi->adapter;
+	uint64_t ts_ns;
+
+	if (adapter->ptp_ctxt.mbuf_rx_ts_flag != 0 &&
+	    (rxq->offloads & RTE_ETH_RX_OFFLOAD_TIMESTAMP) &&
+	    SXE2_RX_DESC_RXDID_VAL_GET(desc->wb.rxdid_src) == SXE2_RX_DESC_RXDID_1588) {
+		rxq->desc_ts = rte_le_to_cpu_32(desc->wb_ts.ts_h);
+		(void)sxe2_rx_update_ptp_time(rxq);
+		if (rxq->ts_need_update && rxq->desc_ts < rxq->ts_low)
+			rxq->ts_high += 1;
+
+		rxq->ts_need_update = true;
+		rxq->ts_low = rxq->desc_ts;
+		rxq->update_time = rte_get_timer_cycles() /
+			(rte_get_timer_hz() / 1000);
+		ts_ns = rxq->ts_high * NSEC_PER_SEC + rxq->ts_low;
+		*RTE_MBUF_DYNFIELD(mbuf, adapter->ptp_ctxt.mbuf_rx_ts_offset, uint64_t *) = ts_ns;
+		mbuf->ol_flags |= adapter->ptp_ctxt.mbuf_rx_ts_flag;
+		PMD_LOG_INFO(RX, "receive ptp pkt,ts_s=%" PRIu64 ", ts_ns=%d", rxq->ts_high,
+			     rxq->ts_low);
+	}
+}
+#endif
+
 static __rte_always_inline void
 sxe2_rx_mbuf_common_fields_fill(struct sxe2_rx_queue *rxq, struct rte_mbuf *mbuf,
 		union sxe2_rx_desc *rxd)
@@ -718,10 +799,12 @@ sxe2_rx_mbuf_common_fields_fill(struct sxe2_rx_queue *rxq, struct rte_mbuf *mbuf
 
 	mbuf->ol_flags = 0;
 	mbuf->packet_type = ptype_tbl[SXE2_RX_DESC_PTYPE_VAL_GET(qword1)];
-
 	pkt_flags = sxe2_rx_desc_error_para(rxq, rxd);
 	sxe2_rx_desc_vlan_para_fill(mbuf, rxd);
 	sxe2_rx_desc_filter_para_fill(rxq, mbuf, rxd);
+#ifndef RTE_LIBRTE_SXE2_16BYTE_RX_DESC
+	sxe2_rx_desc_ptp_para_fill(rxq, mbuf, rxd);
+#endif
 
 	mbuf->ol_flags |= pkt_flags;
 }
-- 
2.52.0


^ permalink raw reply related

* [PATCH v10 06/20] net/sxe2: support TM hierarchy and shaping
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260606010726.2256170-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

This patch implements the traffic management ops for example PMD.
It supports a 4-level hierarchy: port, vsi, queue group and queue.

- Support node add/delete and hierarchy commit.
- Support private shaper and rate limiting on each node.

The hardware requires all nodes to be configured before the hierarchy
is committed to the global registers.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/meson.build     |    1 +
 drivers/net/sxe2/sxe2_cmd_chnl.c |  163 +++++
 drivers/net/sxe2/sxe2_cmd_chnl.h |    6 +
 drivers/net/sxe2/sxe2_drv_cmd.h  |   26 +
 drivers/net/sxe2/sxe2_ethdev.c   |   82 +++
 drivers/net/sxe2/sxe2_ethdev.h   |    5 +
 drivers/net/sxe2/sxe2_tm.c       | 1151 ++++++++++++++++++++++++++++++
 drivers/net/sxe2/sxe2_tm.h       |   76 ++
 drivers/net/sxe2/sxe2_tx.c       |    1 -
 9 files changed, 1510 insertions(+), 1 deletion(-)
 create mode 100644 drivers/net/sxe2/sxe2_tm.c
 create mode 100644 drivers/net/sxe2/sxe2_tm.h

diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index da7a690063..f03ea15356 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -63,4 +63,5 @@ sources += files(
         'sxe2_mac.c',
         'sxe2_filter.c',
         'sxe2_rss.c',
+        'sxe2_tm.c',
 )
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.c b/drivers/net/sxe2/sxe2_cmd_chnl.c
index b997e7b044..19323ffcc4 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.c
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.c
@@ -230,6 +230,7 @@ static void sxe2_txq_ctxt_cfg_fill(struct sxe2_tx_queue *txq,
 				   struct sxe2_drv_txq_cfg_req *req,
 				   uint16_t txq_cnt)
 {
+	struct sxe2_adapter *adapter = txq->vsi->adapter;
 	struct sxe2_drv_txq_ctxt *ctxt = req->cfg;
 	uint16_t q_idx = 0;
 
@@ -241,6 +242,8 @@ static void sxe2_txq_ctxt_cfg_fill(struct sxe2_tx_queue *txq,
 		ctxt->depth = txq[q_idx].ring_depth;
 		ctxt->dma_addr = txq[q_idx].base_addr;
 		ctxt->queue_id = txq[q_idx].queue_id;
+
+		ctxt->sched_mode = sxe2_sched_mode_get(adapter);
 	}
 }
 
@@ -310,6 +313,7 @@ int32_t sxe2_drv_txq_switch(struct sxe2_adapter *adapter, struct sxe2_tx_queue *
 	req.q_idx = txq->queue_id;
 
 	req.is_enable  = (uint8_t)enable;
+	req.sched_mode = sxe2_sched_mode_get(adapter);
 	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_TXQ_DISABLE,
 			&req, sizeof(req), NULL, 0);
 
@@ -714,3 +718,162 @@ int32_t sxe2_drv_ptp_gettime(struct sxe2_adapter *adapter, struct sxe2_rx_queue
 	(void)rxq;
 	return 0;
 }
+
+int32_t sxe2_drv_root_tree_alloc(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_tm_res tm_resp;
+	int32_t ret;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_SCHED_ROOT_TREE_ALLOC,
+				 NULL, 0,
+				 &tm_resp, sizeof(tm_resp));
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "add sched root failed, ret:%d", ret);
+		goto l_end;
+	}
+
+	tm_ctxt->root_teid = tm_resp.teid;
+
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_root_tree_release(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_tm_res tm_res = {0};
+	int32_t ret;
+
+	tm_res.teid = adapter->tm_ctxt.root_teid;
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_SCHED_ROOT_TREE_RELEASE,
+				 &tm_res, sizeof(tm_res),
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "release sched root failed, ret:%d", ret);
+		goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+static void sxe2_drv_tm_node_to_info(struct sxe2_adapter *adapter,
+		struct sxe2_tm_node *node, struct sxe2_tm_info *info)
+{
+	uint32_t rate = 0;
+
+	if (node->shaper_profile->profile.committed.rate == UINT64_MAX)
+		rate = UINT32_MAX;
+	else
+		rate = (uint32_t)(node->shaper_profile->profile.committed.rate * 8 / 1000);
+
+	info->committed = rte_cpu_to_le_32(rate);
+
+	if (node->shaper_profile->profile.peak.rate == UINT64_MAX)
+		rate = UINT32_MAX;
+	else
+		rate = (uint32_t)(node->shaper_profile->profile.peak.rate * 8 / 1000);
+
+	info->peak = rte_cpu_to_le_32(rate);
+
+	info->priority = (adapter->tm_ctxt.prio_max - 1 - node->priority);
+
+	info->weight = rte_cpu_to_le_16(node->hw_weight);
+}
+
+static int32_t sxe2_drv_tm_commit_node(struct sxe2_adapter *adapter,
+		struct sxe2_tm_node *tm_node)
+{
+	struct sxe2_drv_cmd_params cmd = { 0 };
+	struct sxe2_tm_add_mid_msg msg_mid = {0};
+	struct sxe2_tm_add_queue_msg msg_queue = {0};
+	struct sxe2_tm_res res = {0};
+	struct sxe2_common_device *cdev = adapter->cdev;
+	int32_t ret;
+	uint32_t i;
+
+	if (tm_node->type == SXE2_TM_NODE_TYPE_VSIG) {
+		goto l_add;
+	} else if (tm_node->type == SXE2_TM_NODE_TYPE_MID) {
+		sxe2_drv_tm_node_to_info(adapter, tm_node, &msg_mid.info);
+		msg_mid.parent_teid = rte_cpu_to_le_16(tm_node->parent->teid);
+		msg_mid.adj_lvl = adapter->sched_ctxt.adj_lvl;
+
+		sxe2_drv_cmd_params_fill(adapter, &cmd,
+					 SXE2_DRV_CMD_SCHED_TM_ADD_MID_NODE,
+					 &msg_mid, sizeof(msg_mid),
+					 &res, sizeof(res));
+		ret = sxe2_drv_cmd_exec(cdev, &cmd);
+		if (ret) {
+			PMD_LOG_ERR(DRV, "add tm mid node failed, ret:%d", ret);
+			goto l_end;
+		}
+	} else if (tm_node->type == SXE2_TM_NODE_TYPE_QUEUE) {
+		sxe2_drv_tm_node_to_info(adapter, tm_node, &msg_queue.info);
+		msg_queue.parent_teid = rte_cpu_to_le_16(tm_node->parent->teid);
+		msg_queue.queue_id = rte_cpu_to_le_16(tm_node->id);
+		msg_queue.adj_lvl = adapter->sched_ctxt.adj_lvl;
+
+		sxe2_drv_cmd_params_fill(adapter, &cmd,
+					 SXE2_DRV_CMD_SCHED_TM_ADD_QUEUE_NODE,
+					 &msg_queue, sizeof(msg_queue),
+					 &res, sizeof(res));
+		ret = sxe2_drv_cmd_exec(cdev, &cmd);
+		if (ret) {
+			PMD_LOG_ERR(DRV, "add tm queue failed, ret:%d", ret);
+			goto l_end;
+		}
+	} else {
+		PMD_LOG_ERR(DRV, "commit tm node failed, type:%d", tm_node->type);
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	tm_node->teid = rte_le_to_cpu_16(res.teid);
+
+l_add:
+	for (i = 0; i < tm_node->child_cnt; i++) {
+		ret = sxe2_drv_tm_commit_node(adapter, tm_node->children[i]);
+		if (ret)
+			goto l_end;
+	}
+l_end:
+	return ret;
+}
+
+int32_t sxe2_drv_tm_commit(struct sxe2_adapter *adapter)
+{
+	struct sxe2_drv_cmd_params cmd = { 0 };
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_tm_res tm_res = {0};
+	int32_t ret;
+
+	tm_res.teid = adapter->tm_ctxt.root_teid;
+	sxe2_drv_cmd_params_fill(adapter, &cmd, SXE2_DRV_CMD_SCHED_ROOT_CHILDREN_DELETE,
+				 &tm_res, sizeof(tm_res),
+				 NULL, 0);
+	ret = sxe2_drv_cmd_exec(cdev, &cmd);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "del tm root failed, ret:%d", ret);
+		goto l_end;
+	}
+
+	ret = sxe2_drv_tm_commit_node(adapter, adapter->tm_ctxt.root);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "commit tm node failed, ret:%d", ret);
+		goto l_end;
+	}
+
+	PMD_LOG_DEBUG(DRV, "commit tm success");
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.h b/drivers/net/sxe2/sxe2_cmd_chnl.h
index 2546c65a6c..77e689abcd 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.h
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.h
@@ -38,6 +38,12 @@ int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter);
 
 int32_t sxe2_drv_promisc_config(struct sxe2_adapter *adapter, bool set);
 
+int32_t sxe2_drv_root_tree_release(struct rte_eth_dev *dev);
+
+int32_t sxe2_drv_root_tree_alloc(struct rte_eth_dev *dev);
+
+int32_t sxe2_drv_tm_commit(struct sxe2_adapter *adapter);
+
 int32_t sxe2_drv_allmulti_config(struct sxe2_adapter *adapter, bool set);
 
 int32_t sxe2_drv_uc_config(struct sxe2_adapter *adapter, struct rte_ether_addr *addr, bool add);
diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h
index 9998f241f0..67c6885cae 100644
--- a/drivers/net/sxe2/sxe2_drv_cmd.h
+++ b/drivers/net/sxe2/sxe2_drv_cmd.h
@@ -349,6 +349,32 @@ struct __rte_aligned(4) __rte_packed_begin sxe2_rss_hf_req {
 	uint8_t rsv1[3];
 } __rte_packed_end;
 
+struct __rte_aligned(4) __rte_packed_begin sxe2_tm_res {
+	uint16_t teid;
+	uint8_t rsv[2];
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_tm_info {
+	uint32_t committed;
+	uint32_t peak;
+	uint8_t priority;
+	uint8_t reserve;
+	uint16_t weight;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_tm_add_mid_msg {
+	uint16_t parent_teid;
+	uint8_t adj_lvl;
+	struct sxe2_tm_info info;
+} __rte_packed_end;
+
+struct __rte_aligned(4) __rte_packed_begin sxe2_tm_add_queue_msg {
+	uint16_t parent_teid;
+	uint16_t queue_id;
+	uint8_t adj_lvl;
+	struct sxe2_tm_info info;
+} __rte_packed_end;
+
 enum sxe2_drv_cmd_module {
 	SXE2_DRV_CMD_MODULE_HANDSHAKE = 0,
 	SXE2_DRV_CMD_MODULE_DEV = 1,
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index d48841b8e4..a095888c00 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -130,6 +130,8 @@ static const struct eth_dev_ops sxe2_eth_dev_ops = {
 	.reta_query                 = sxe2_dev_rss_reta_query,
 	.rss_hash_update            = sxe2_dev_rss_hash_update,
 	.rss_hash_conf_get          = sxe2_dev_rss_hash_conf_get,
+
+	.tm_ops_get                 = sxe2_tm_ops_get,
 };
 
 static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
@@ -575,6 +577,14 @@ static void sxe2_drv_dev_caps_set(struct sxe2_adapter *adapter,
 		adapter->cap_flags |= SXE2_DEV_CAPS_OFFLOAD_FC_STATE;
 }
 
+static void sxe2_sw_sched_hw_cap_set(struct sxe2_adapter *adapter,
+				     struct sxe2_txsch_caps *txsch_caps)
+{
+	adapter->sched_ctxt.tm_layers = txsch_caps->layer_cap;
+	adapter->sched_ctxt.root_max_children = txsch_caps->tm_mid_node_num;
+	adapter->sched_ctxt.prio_max = txsch_caps->prio_num;
+}
+
 static int32_t sxe2_func_caps_get(struct sxe2_adapter *adapter)
 {
 	int32_t ret = -1;
@@ -594,6 +604,8 @@ static int32_t sxe2_func_caps_get(struct sxe2_adapter *adapter)
 
 	sxe2_sw_vsi_ctx_hw_cap_set(adapter, &dev_caps.vsi_caps);
 
+	sxe2_sw_sched_hw_cap_set(adapter, &dev_caps.txsch_caps);
+
 l_end:
 	return ret;
 }
@@ -930,6 +942,68 @@ void sxe2_dev_pci_map_uinit(struct rte_eth_dev *dev)
 	adapter->dev_info.dev_data = NULL;
 }
 
+uint32_t sxe2_sched_mode_get(struct sxe2_adapter *adapter)
+{
+	uint32_t ret_mode = SXE2_SCHED_MODE_INVALID;
+	struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt;
+
+	if (adapter->devargs.high_performance_mode)
+		ret_mode = SXE2_SCHED_MODE_HIGH_PERFORMANCE;
+	else if (tm_ctxt->committed)
+		ret_mode = SXE2_SCHED_MODE_TM;
+	else
+		ret_mode = SXE2_SCHED_MODE_DEFAULT;
+
+	return ret_mode;
+}
+
+static int32_t sxe2_sched_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	adapter->sched_ctxt.adj_lvl = adapter->devargs.sched_layer_mode;
+
+	if (adapter->devargs.high_performance_mode) {
+		PMD_LOG_DEBUG(DRV, "TM feature will be disabled in high-performance mode.");
+		adapter->cap_flags &= ~(SXE2_DEV_CAPS_OFFLOAD_TM);
+	} else {
+		ret = sxe2_tm_init(dev);
+		if (ret)
+			goto l_end;
+
+		ret = sxe2_drv_root_tree_alloc(dev);
+		if (ret)
+			goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_sched_uinit(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+
+	if (adapter->devargs.high_performance_mode == 0) {
+		ret = sxe2_tm_uninit(dev);
+		if (ret) {
+			PMD_LOG_ERR(INIT, "Failed to uninit tm, ret=%d", ret);
+			goto l_end;
+		}
+
+		ret = sxe2_drv_root_tree_release(dev);
+		if (ret) {
+			PMD_LOG_ERR(INIT, "Failed to release root tree, ret=%d", ret);
+			goto l_end;
+		}
+	}
+
+l_end:
+	return ret;
+}
+
 static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 			     struct sxe2_dev_kvargs_info *kvargs __rte_unused)
 {
@@ -985,8 +1059,15 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 		goto init_rss_err;
 	}
 
+	ret = sxe2_sched_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to init sched, ret=%d", ret);
+		goto init_sched_err;
+	}
+
 	goto l_end;
 
+init_sched_err:
 init_rss_err:
 init_eth_err:
 init_dev_info_err:
@@ -1002,6 +1083,7 @@ static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
 	(void)sxe2_dev_stop(dev);
 	(void)sxe2_queues_release(dev);
 	(void)sxe2_rss_disable(dev);
+	(void)sxe2_sched_uinit(dev);
 	sxe2_vsi_uninit(dev);
 	sxe2_dev_pci_map_uinit(dev);
 	sxe2_eth_uinit(dev);
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index 3955788634..76e4cc8b33 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -20,6 +20,7 @@
 #include "sxe2_queue.h"
 #include "sxe2_mac.h"
 #include "sxe2_osal.h"
+#include "sxe2_tm.h"
 #include "sxe2_filter.h"
 
 struct sxe2_link_msg {
@@ -309,6 +310,8 @@ struct sxe2_adapter {
 	struct sxe2_rss_context       rss_ctxt;
 	struct sxe2_link_context      link_ctxt;
 	struct sxe2_ptp_context       ptp_ctxt;
+	struct sxe2_sched_hw_cap      sched_ctxt;
+	struct sxe2_tm_context        tm_ctxt;
 	struct sxe2_devargs           devargs;
 	struct sxe2_switchdev_info    switchdev_info;
 	bool                          rule_started;
@@ -335,6 +338,8 @@ void *sxe2_pci_map_addr_get(struct sxe2_adapter *adapter,
 			    enum sxe2_pci_map_resource res_type,
 			    uint16_t idx_in_func);
 
+uint32_t sxe2_sched_mode_get(struct sxe2_adapter *adapter);
+
 struct sxe2_pci_map_bar_info *sxe2_dev_get_bar_info(struct sxe2_adapter *adapter,
 						    enum sxe2_pci_map_resource res_type);
 
diff --git a/drivers/net/sxe2/sxe2_tm.c b/drivers/net/sxe2/sxe2_tm.c
new file mode 100644
index 0000000000..4c4f793cd5
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_tm.c
@@ -0,0 +1,1151 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <rte_ethdev.h>
+#include <rte_tm_driver.h>
+
+#include "sxe2_tm.h"
+#include "sxe2_ethdev.h"
+#include "sxe2_common_log.h"
+#include "sxe2_cmd_chnl.h"
+
+static uint16_t sxe2_tm_level_node_num_get(uint8_t level)
+{
+	uint16_t node_num = 0;
+
+	switch (level) {
+	case 0:
+		node_num = SXE2_TM_1L_NODE_NUM_MAX;
+		break;
+	case 1:
+		node_num = SXE2_TM_2L_NODE_NUM_MAX;
+		break;
+	case 2:
+		node_num = SXE2_TM_3L_NODE_NUM_MAX;
+	break;
+	case 3:
+		node_num = SXE2_TM_4L_NODE_NUM_MAX;
+		break;
+	}
+	return node_num;
+}
+
+static int32_t sxe2_tm_capabilities_get(struct rte_eth_dev *dev,
+			  struct rte_tm_capabilities *cap,
+			  struct rte_tm_error *error)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = 0;
+	uint32_t i;
+
+	if (!cap || !error) {
+		PMD_LOG_ERR(DRV, "sxe2 get tm cap failed, cap or error is NULL.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) {
+		PMD_LOG_ERR(DRV, "The TM capability is not supported.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "The TM capability is not supported.";
+		ret = ENOTSUP;
+		goto l_end;
+	}
+
+	error->type = RTE_TM_ERROR_TYPE_NONE;
+	memset(cap, 0, sizeof(struct rte_tm_capabilities));
+
+	for (i = 0; i < adapter->tm_ctxt.tm_layers; i++)
+		cap->n_nodes_max += sxe2_tm_level_node_num_get(i);
+
+	cap->n_levels_max = adapter->tm_ctxt.tm_layers;
+
+	cap->non_leaf_nodes_identical = 1;
+
+	cap->leaf_nodes_identical = 1;
+
+	cap->shaper_n_max = cap->n_nodes_max;
+
+	cap->shaper_private_n_max = cap->n_nodes_max;
+
+	cap->shaper_private_dual_rate_n_max = cap->n_nodes_max;
+
+	cap->shaper_private_rate_min = 0;
+
+	cap->shaper_private_rate_max = 12500000000ull;
+
+	cap->shaper_private_packet_mode_supported = 0;
+	cap->shaper_private_byte_mode_supported = 1;
+
+	cap->shaper_pkt_length_adjust_min = RTE_TM_ETH_FRAMING_OVERHEAD;
+
+	cap->shaper_pkt_length_adjust_max = RTE_TM_ETH_FRAMING_OVERHEAD_FCS;
+
+	cap->shaper_shared_n_max = 0;
+	cap->shaper_shared_n_nodes_per_shaper_max = 0;
+	cap->shaper_shared_n_shapers_per_node_max = 0;
+	cap->shaper_shared_dual_rate_n_max = 0;
+	cap->shaper_shared_rate_min = 0;
+	cap->shaper_shared_rate_max = 0;
+	cap->shaper_shared_packet_mode_supported = 0;
+	cap->shaper_shared_byte_mode_supported = 0;
+
+	cap->sched_n_children_max = dev->data->nb_tx_queues;
+
+	cap->sched_sp_n_priorities_max = 7;
+
+	cap->sched_wfq_n_children_per_group_max = 1;
+	cap->sched_wfq_n_groups_max = 0;
+	cap->sched_wfq_weight_max = 0;
+	cap->sched_wfq_packet_mode_supported = 0;
+	cap->sched_wfq_byte_mode_supported = 0;
+
+	cap->cman_wred_packet_mode_supported = 0;
+	cap->cman_wred_byte_mode_supported = 0;
+	cap->cman_head_drop_supported = 0;
+	cap->cman_wred_context_n_max = 0;
+	cap->cman_wred_context_private_n_max = 0;
+	cap->cman_wred_context_shared_n_max = 0;
+	cap->cman_wred_context_shared_n_nodes_per_context_max = 0;
+	cap->cman_wred_context_shared_n_contexts_per_node_max = 0;
+
+	cap->dynamic_update_mask = 0;
+
+	cap->stats_mask = 0;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_level_capabilities_get(struct rte_eth_dev *dev,
+		uint32_t level_id, struct rte_tm_level_capabilities *cap,
+		struct rte_tm_error *error)
+{
+	int32_t ret = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (!cap || !error) {
+		PMD_LOG_ERR(DRV, "sxe2 get tm cap failed, cap or error is NULL.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) {
+		PMD_LOG_ERR(DRV, "The TM capability is not supported.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "The TM capability is not supported.";
+		ret = ENOTSUP;
+		goto l_end;
+	}
+
+	if (level_id >= adapter->tm_ctxt.tm_layers) {
+		ret = -EINVAL;
+		error->type = RTE_TM_ERROR_TYPE_LEVEL_ID;
+		error->message = "too deep level";
+		goto l_end;
+	}
+
+	cap->n_nodes_max = sxe2_tm_level_node_num_get(level_id);
+
+	cap->non_leaf_nodes_identical = true;
+
+	cap->leaf_nodes_identical = true;
+
+	if (level_id != adapter->tm_ctxt.tm_layers - 1) {
+		cap->n_nodes_nonleaf_max = cap->n_nodes_max;
+		cap->n_nodes_leaf_max = 0;
+
+		cap->nonleaf.shaper_private_supported = true;
+		cap->nonleaf.shaper_private_dual_rate_supported = true;
+		cap->nonleaf.shaper_private_rate_min = 0;
+
+		cap->nonleaf.shaper_private_rate_max = 12500000000ull;
+		cap->nonleaf.shaper_private_packet_mode_supported = 0;
+		cap->nonleaf.shaper_private_byte_mode_supported = 1;
+
+		cap->nonleaf.shaper_shared_n_max = 0;
+		cap->nonleaf.shaper_shared_packet_mode_supported = 0;
+		cap->nonleaf.shaper_shared_byte_mode_supported = 0;
+
+		cap->nonleaf.sched_n_children_max = SXE2_TM_MAX_CHILDREN_COUNT;
+		cap->nonleaf.sched_sp_n_priorities_max = 7;
+
+		cap->nonleaf.sched_wfq_n_children_per_group_max = 0;
+		cap->nonleaf.sched_wfq_n_groups_max = 0;
+		cap->nonleaf.sched_wfq_weight_max = 0;
+		cap->nonleaf.sched_wfq_packet_mode_supported = 0;
+		cap->nonleaf.sched_wfq_byte_mode_supported = 0;
+
+		cap->nonleaf.stats_mask = 0;
+	} else {
+		cap->n_nodes_nonleaf_max = 0;
+		cap->n_nodes_leaf_max = cap->n_nodes_max;
+
+		cap->leaf.shaper_private_supported = true;
+		cap->leaf.shaper_private_dual_rate_supported = true;
+		cap->leaf.shaper_private_rate_min = 0;
+		cap->leaf.shaper_private_rate_max = 12500000000ull;
+		cap->leaf.shaper_private_packet_mode_supported = 0;
+		cap->leaf.shaper_private_byte_mode_supported = 1;
+
+		cap->leaf.shaper_shared_n_max = 0;
+		cap->leaf.shaper_shared_packet_mode_supported = 0;
+		cap->leaf.shaper_shared_byte_mode_supported = 0;
+
+		cap->leaf.cman_head_drop_supported = false;
+		cap->leaf.cman_wred_context_private_supported = false;
+		cap->leaf.cman_wred_context_shared_n_max = 0;
+
+		cap->leaf.stats_mask = 0;
+	}
+
+l_end:
+	return ret;
+}
+
+static struct sxe2_tm_node *sxe2_tm_find_node(struct sxe2_tm_node *parent, uint32_t id)
+{
+	struct sxe2_tm_node *node = NULL;
+	uint32_t i;
+
+	if (parent == NULL || parent->id == id) {
+		node = parent;
+		goto l_end;
+	}
+
+	for (i = 0; i < parent->child_cnt; i++) {
+		node = sxe2_tm_find_node(parent->children[i], id);
+		if (node)
+			goto l_end;
+	}
+
+l_end:
+	return node;
+}
+
+static int32_t sxe2_node_capabilities_get(struct rte_eth_dev *dev, uint32_t node_id,
+				      struct rte_tm_node_capabilities *cap,
+				      struct rte_tm_error *error)
+{
+	int32_t ret = -EINVAL;
+	struct sxe2_tm_node *tm_node;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (!cap || !error) {
+		PMD_LOG_ERR(DRV, "sxe2 get tm cap failed, cap or error is NULL.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) {
+		PMD_LOG_ERR(DRV, "The TM capability is not supported.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "The TM capability is not supported.";
+		ret = ENOTSUP;
+		goto l_end;
+	}
+
+	if (node_id == RTE_TM_NODE_ID_NULL) {
+		PMD_LOG_ERR(DRV, "invalid node id");
+		error->type = RTE_TM_ERROR_TYPE_NODE_ID;
+		error->message = "invalid node id";
+		goto l_end;
+	}
+
+	tm_node = sxe2_tm_find_node(adapter->tm_ctxt.root, node_id);
+	if (!tm_node) {
+		PMD_LOG_ERR(DRV, "no such node");
+		error->type = RTE_TM_ERROR_TYPE_NODE_ID;
+		error->message = "no such node";
+		goto l_end;
+	}
+
+	cap->shaper_private_supported = true;
+	cap->shaper_private_dual_rate_supported = true;
+	cap->shaper_private_rate_min = 0;
+	cap->shaper_private_rate_max = 12500000000ull;
+	cap->shaper_private_packet_mode_supported = 0;
+	cap->shaper_private_byte_mode_supported = 1;
+
+	cap->shaper_shared_n_max = 0;
+	cap->shaper_shared_packet_mode_supported = 0;
+	cap->shaper_shared_byte_mode_supported = 0;
+
+	if (tm_node->level == adapter->tm_ctxt.tm_layers - 1) {
+		cap->leaf.cman_head_drop_supported = false;
+		cap->leaf.cman_wred_context_private_supported = false;
+		cap->leaf.cman_wred_context_shared_n_max = 0;
+	} else {
+		cap->nonleaf.sched_n_children_max = SXE2_TM_MAX_CHILDREN_COUNT;
+		cap->nonleaf.sched_sp_n_priorities_max = 7;
+		cap->nonleaf.sched_wfq_n_children_per_group_max = 0;
+		cap->nonleaf.sched_wfq_n_groups_max = 0;
+		cap->nonleaf.sched_wfq_weight_max = 0;
+		cap->nonleaf.sched_wfq_packet_mode_supported = 0;
+		cap->nonleaf.sched_wfq_byte_mode_supported = 0;
+	}
+	cap->stats_mask = 0;
+
+	ret = 0;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_tm_shaper_profile_param_check(const struct rte_tm_shaper_params *profile,
+					      struct rte_tm_error *error)
+{
+	int32_t ret = 0;
+
+	if (profile->committed.size) {
+		PMD_LOG_ERR(DRV, "committed bucket size not supported.");
+		error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_COMMITTED_SIZE;
+		error->message = "committed bucket size not supported";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (profile->peak.size) {
+		PMD_LOG_ERR(DRV, "peak bucket size not supported.");
+		error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PEAK_SIZE;
+		error->message = "peak bucket size not supported";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (profile->pkt_length_adjust) {
+		PMD_LOG_ERR(DRV, "packet length adjustment not supported.");
+		error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PKT_ADJUST_LEN;
+		error->message = "packet length adjustment not supported";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (profile->committed.rate > SXE2_HW_RATE_MAX ||
+		profile->committed.rate < SXE2_HW_RATE_MIN) {
+		PMD_LOG_ERR(DRV, "The committed rate limit value is required to be in "
+			"the range [%" PRIu64 ", %" PRIu64 "].",
+			(uint64_t)SXE2_HW_RATE_MIN, (uint64_t)SXE2_HW_RATE_MAX);
+		error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_COMMITTED_RATE;
+		error->message = "invalid rate limit: value out of range.";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (profile->peak.rate > SXE2_HW_RATE_MAX ||
+		profile->peak.rate < SXE2_HW_RATE_MIN) {
+		PMD_LOG_ERR(DRV, "The peak rate limit value is required to be in "
+			"the range [%" PRIu64 ", %" PRIu64 "].",
+			(uint64_t)SXE2_HW_RATE_MIN, (uint64_t)SXE2_HW_RATE_MAX);
+		error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_PEAK_RATE;
+		error->message = "invalid rate limit: value out of range.";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (profile->committed.rate > profile->peak.rate) {
+		PMD_LOG_ERR(DRV, "committed rate can't be greater than peak rate.");
+		error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_COMMITTED_RATE;
+		error->message = "committed rate can't be greater than peak rate.";
+		ret = -EINVAL;
+		goto l_end;
+	}
+l_end:
+	return ret;
+}
+
+static inline struct sxe2_tm_shaper_profile *
+sxe2_tm_shaper_profile_search(struct rte_eth_dev *dev, uint32_t id)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_shaper_profile_list *shaper_profile_list =
+		&adapter->tm_ctxt.profile_list;
+	struct sxe2_tm_shaper_profile *shaper_profile = NULL;
+
+	TAILQ_FOREACH(shaper_profile, shaper_profile_list, node) {
+		if (id == shaper_profile->id)
+			goto l_end;
+	}
+
+	shaper_profile = NULL;
+
+l_end:
+	return shaper_profile;
+}
+
+static int32_t sxe2_tm_shaper_profile_add(struct rte_eth_dev *dev, uint32_t shaper_profile_id,
+				      const struct rte_tm_shaper_params *profile,
+				      struct rte_tm_error *error)
+{
+	int32_t ret = -EINVAL;
+	struct sxe2_tm_shaper_profile *shaper_profile = NULL;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (!profile || !error) {
+		PMD_LOG_ERR(DRV, "Invalid input: profile:0x%p or error:0x%p is null.",
+			profile, error);
+		if (error) {
+			error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+			error->message = "Invalid input: profile or error is null.";
+		}
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) {
+		PMD_LOG_ERR(DRV, "The TM capability is not supported.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "The TM capability is not supported.";
+		ret = ENOTSUP;
+		goto l_end;
+	}
+
+	ret = sxe2_tm_shaper_profile_param_check(profile, error);
+	if (ret)
+		goto l_end;
+
+	shaper_profile = sxe2_tm_shaper_profile_search(dev, shaper_profile_id);
+	if (shaper_profile) {
+		PMD_LOG_ERR(DRV, "profile ID exist.");
+		error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID;
+		error->message = "profile ID exist";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	shaper_profile = rte_zmalloc("sxe2_tm_shaper_profile",
+							sizeof(struct sxe2_tm_shaper_profile), 0);
+	if (!shaper_profile) {
+		PMD_LOG_ERR(DRV, "Alloc shaper_profile memory failed.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "Alloc shaper_profile memory failed";
+		ret = -ENOMEM;
+		goto l_end;
+	}
+
+	rte_memcpy(&shaper_profile->profile, profile,
+					sizeof(struct rte_tm_shaper_params));
+	shaper_profile->id = shaper_profile_id;
+
+	TAILQ_INSERT_TAIL(&adapter->tm_ctxt.profile_list, shaper_profile, node);
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_tm_shaper_profile_del(struct rte_eth_dev *dev,
+		uint32_t id, struct rte_tm_error *error)
+{
+	int32_t ret = -EINVAL;
+	struct sxe2_tm_shaper_profile *shaper_profile = NULL;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	if (!error) {
+		PMD_LOG_ERR(DRV, "Error param is null.");
+		goto l_end;
+	}
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) {
+		PMD_LOG_ERR(DRV, "The TM capability is not supported.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "The TM capability is not supported.";
+		ret = ENOTSUP;
+		goto l_end;
+	}
+
+	shaper_profile = sxe2_tm_shaper_profile_search(dev, id);
+	if (!shaper_profile) {
+		PMD_LOG_ERR(DRV, "profile ID not exist.");
+		error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE_ID;
+		error->message = "profile ID not exist";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (shaper_profile->ref_cnt) {
+		PMD_LOG_ERR(DRV, "profile in use.");
+		error->type = RTE_TM_ERROR_TYPE_SHAPER_PROFILE;
+		error->message = "profile in use";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	TAILQ_REMOVE(&adapter->tm_ctxt.profile_list, shaper_profile, node);
+	rte_free(shaper_profile);
+
+	ret = 0;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_tm_node_param_check(struct rte_eth_dev *dev,
+			uint32_t parent_node_type,
+			uint32_t node_id, uint32_t priority, uint32_t weight,
+			const struct rte_tm_node_params *params,
+			bool is_leaf, struct rte_tm_error *error)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt;
+	int32_t ret = -EINVAL;
+
+	if (node_id == RTE_TM_NODE_ID_NULL) {
+		PMD_LOG_ERR(DRV, "Invalid node id.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_ID;
+		error->message = "invalid node id";
+		goto l_end;
+	}
+
+	if (parent_node_type == SXE2_TM_NODE_TYPE_VSIG &&
+			priority >= tm_ctxt->prio_max) {
+		PMD_LOG_ERR(DRV, "Priority should be less than %u.", tm_ctxt->prio_max);
+		error->type = RTE_TM_ERROR_TYPE_NODE_PRIORITY;
+		error->message = "The priority is too high.";
+		goto l_end;
+	}
+
+	if (priority > SXE2_TM_PRIO_MAX) {
+		PMD_LOG_ERR(DRV, "Priority should be less than %u.", SXE2_TM_PRIO_MAX);
+		error->type = RTE_TM_ERROR_TYPE_NODE_PRIORITY;
+		error->message = "The priority is too high.";
+		goto l_end;
+	}
+
+	if (weight > SXE2_TM_WEIGHT_MAX || weight < SXE2_TM_WEIGHT_MIN) {
+		PMD_LOG_ERR(DRV, "Weight must be between 1 and 200.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_WEIGHT;
+		error->message = "weight must be between 1 and 200";
+		goto l_end;
+	}
+
+	if (params->shared_shaper_id) {
+		PMD_LOG_ERR(DRV, "Shared shaper not supported.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_SHARED_SHAPER_ID;
+		error->message = "shared shaper not supported";
+		goto l_end;
+	}
+	if (params->n_shared_shapers) {
+		PMD_LOG_ERR(DRV, "Shared shaper not supported..");
+		error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_N_SHARED_SHAPERS;
+		error->message = "shared shaper not supported";
+		goto l_end;
+	}
+
+	if (!is_leaf) {
+		if (node_id <= dev->data->nb_tx_queues) {
+			PMD_LOG_ERR(DRV, "no leaf node id must bigger than queue id.");
+			error->type = RTE_TM_ERROR_TYPE_NODE_ID;
+			error->message = "no leaf node id must bigger than queue id.";
+			goto l_end;
+		}
+
+		if (params->nonleaf.wfq_weight_mode) {
+			PMD_LOG_ERR(DRV, "WFQ not supported.");
+			error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_WFQ_WEIGHT_MODE;
+			error->message = "WFQ not supported";
+			goto l_end;
+		}
+
+		if (params->nonleaf.n_sp_priorities != 1) {
+			PMD_LOG_ERR(DRV, "SP priority not supported.");
+			error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_N_SP_PRIORITIES;
+			error->message = "SP priority not supported";
+			goto l_end;
+		}
+	} else {
+		if (node_id >= dev->data->nb_tx_queues) {
+			PMD_LOG_ERR(DRV, "leaf node id must be queue id.");
+			error->type = RTE_TM_ERROR_TYPE_NODE_ID;
+			error->message = "leaf node id must be queue id.";
+			goto l_end;
+		}
+
+		if (params->leaf.cman) {
+			PMD_LOG_ERR(DRV, "Congestion management not supported.");
+			error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_CMAN;
+			error->message = "Congestion management not supported";
+			goto l_end;
+		}
+		if (params->leaf.wred.wred_profile_id != RTE_TM_WRED_PROFILE_ID_NONE) {
+			PMD_LOG_ERR(DRV, "WRED not supported.");
+			error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_WRED_PROFILE_ID;
+			error->message = "WRED not supported";
+			goto l_end;
+		}
+		if (params->leaf.wred.shared_wred_context_id) {
+			PMD_LOG_ERR(DRV, "WRED not supported.");
+			error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_SHARED_WRED_CONTEXT_ID;
+			error->message = "WRED not supported";
+			goto l_end;
+		}
+		if (params->leaf.wred.n_shared_wred_contexts) {
+			PMD_LOG_ERR(DRV, "WRED not supported.");
+			error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_N_SHARED_WRED_CONTEXTS;
+			error->message = "WRED not supported";
+			goto l_end;
+		}
+	}
+	ret = 0;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_tm_add_child(struct sxe2_tm_node *parent,
+		struct sxe2_tm_node *child)
+{
+	int32_t ret = -1;
+	uint32_t i;
+	for (i = 0; i < SXE2_TM_MAX_CHILDREN_COUNT; i++) {
+		if (parent->children[i] == NULL) {
+			parent->children[i] = child;
+			child->index_in_parent = i;
+			parent->child_cnt++;
+			ret = 0;
+			break;
+		}
+	}
+	return ret;
+}
+
+static int32_t sxe2_tm_node_add(struct rte_eth_dev *dev, uint32_t node_id, uint32_t parent_node_id,
+			    uint32_t priority, uint32_t weight, uint32_t level_id,
+			    const struct rte_tm_node_params *params,
+			    struct rte_tm_error *error)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_tm_shaper_profile *shaper_profile = NULL;
+	struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt;
+	struct sxe2_tm_node *tm_node = NULL;
+	struct sxe2_tm_node *parent_node = NULL;
+	int32_t ret = -EINVAL;
+	bool is_leaf;
+
+	if (!params || !error) {
+		PMD_LOG_ERR(DRV, "Invalid input: params:0x%p or error:0x%p is null.",
+			params, error);
+		if (error) {
+			error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+			error->message = "Invalid input: params or error is null.";
+		}
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) {
+		PMD_LOG_ERR(DRV, "The TM capability is not supported.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "The TM capability is not supported.";
+		ret = ENOTSUP;
+		goto l_end;
+	}
+
+	shaper_profile = sxe2_tm_shaper_profile_search(dev, params->shaper_profile_id);
+	if (!shaper_profile) {
+		PMD_LOG_ERR(DRV, "Shaper profile does not exist.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS_SHAPER_PROFILE_ID;
+		error->message = "shaper profile does not exist";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (parent_node_id == RTE_TM_NODE_ID_NULL) {
+		if (level_id != 0) {
+			PMD_LOG_ERR(DRV, "Wrong level, root node (NULL parent) must be at level 0.");
+			error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS;
+			error->message = "Wrong level, root node (NULL parent) must be at level 0";
+			ret = -EINVAL;
+			goto l_end;
+		}
+
+		if (tm_ctxt->root) {
+			PMD_LOG_ERR(DRV, "Already have a root.");
+			error->type = RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID;
+			error->message = "already have a root";
+			ret = -EINVAL;
+			goto l_end;
+		}
+
+		ret = sxe2_tm_node_param_check(dev, SXE2_TM_NODE_TYPE_INVALID, node_id, priority,
+					       weight, params, false, error);
+		if (ret)
+			goto l_end;
+
+		tm_node = rte_zmalloc("tm_node_root", sizeof(struct sxe2_tm_node), 0);
+		if (!tm_node) {
+			PMD_LOG_ERR(DRV, "Alloc tm_node memory failed.");
+			error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+			error->message = "Alloc tm_node memory failed";
+			ret = -ENOMEM;
+			goto l_end;
+		}
+
+		tm_node->id = node_id;
+		tm_node->level = 0;
+		tm_node->parent = NULL;
+		tm_node->child_cnt = 0;
+		tm_node->weight = weight;
+		tm_node->hw_weight = SXE2_TM_WEIGHT_SUM;
+		tm_node->type = SXE2_TM_NODE_TYPE_VSIG;
+		tm_node->priority = priority;
+		tm_node->shaper_profile = shaper_profile;
+
+		tm_node->teid = tm_ctxt->root_teid;
+
+		shaper_profile->ref_cnt++;
+		tm_ctxt->root = tm_node;
+		ret = 0;
+		goto l_end;
+	}
+
+	parent_node = sxe2_tm_find_node(tm_ctxt->root, parent_node_id);
+	if (!parent_node) {
+		PMD_LOG_ERR(DRV, "Parent not exist.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_PARENT_NODE_ID;
+		error->message = "parent not exist";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (parent_node->child_cnt >= SXE2_TM_MAX_CHILDREN_COUNT ||
+		(parent_node->type == SXE2_TM_NODE_TYPE_VSIG &&
+		 parent_node->child_cnt >= tm_ctxt->root_max_children)) {
+		PMD_LOG_ERR(DRV, "Parent node is full.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS;
+		error->message = "parent node is full";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (level_id == RTE_TM_NODE_LEVEL_ID_ANY) {
+		level_id = parent_node->level + 1;
+	} else if (level_id != parent_node->level + 1) {
+		PMD_LOG_ERR(DRV, "Wrong level.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS;
+		error->message = "Wrong level";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (level_id >= tm_ctxt->tm_layers) {
+		PMD_LOG_ERR(DRV, "The maximum number of TM configuration levels is %d",
+				tm_ctxt->tm_layers);
+		error->type = RTE_TM_ERROR_TYPE_NODE_PARAMS;
+		error->message = "TM level exceeds supported hardware limit";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (level_id + 1 == tm_ctxt->tm_layers)
+		is_leaf = true;
+	else
+		is_leaf = false;
+
+	ret = sxe2_tm_node_param_check(dev, parent_node->type, node_id, priority, weight,
+				       params, is_leaf, error);
+	if (ret)
+		goto l_end;
+
+	if (sxe2_tm_find_node(tm_ctxt->root, node_id)) {
+		PMD_LOG_ERR(DRV, "Node id already used.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_ID;
+		error->message = "node id already used";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	tm_node = rte_zmalloc("tm_node_no_root", sizeof(struct sxe2_tm_node), 0);
+	if (!tm_node) {
+		PMD_LOG_ERR(DRV, "Alloc tm_node memory failed.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "Alloc tm_node memory failed";
+		ret = -ENOMEM;
+		goto l_end;
+	}
+
+	if (level_id + 1 != tm_ctxt->tm_layers)
+		tm_node->type = SXE2_TM_NODE_TYPE_MID;
+	else
+		tm_node->type = SXE2_TM_NODE_TYPE_QUEUE;
+	tm_node->id = node_id;
+	tm_node->level = level_id;
+	tm_node->parent = parent_node;
+	tm_node->child_cnt = 0;
+	tm_node->weight = weight;
+	tm_node->priority = priority;
+	tm_node->shaper_profile = shaper_profile;
+	shaper_profile->ref_cnt++;
+
+	ret = sxe2_tm_add_child(parent_node, tm_node);
+	if (ret) {
+		shaper_profile->ref_cnt--;
+		rte_free(tm_node);
+	}
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_tm_tree_delete(struct sxe2_tm_node *tm_node)
+{
+	int32_t ret = 0;
+	uint32_t i, j;
+	struct sxe2_tm_node *parent = NULL;
+
+	if (!tm_node)
+		goto l_end;
+
+	parent = tm_node->parent;
+
+	if (tm_node->child_cnt != 0) {
+		for (i = SXE2_TM_MAX_CHILDREN_COUNT; i > 0; i--) {
+			if (tm_node->children[i - 1])
+				ret = sxe2_tm_tree_delete(tm_node->children[i - 1]);
+		}
+	}
+
+	if (tm_node->shaper_profile)
+		tm_node->shaper_profile->ref_cnt--;
+
+	if (tm_node->type != SXE2_TM_NODE_TYPE_VSIG && parent) {
+		for (i = 0; i < parent->child_cnt; i++) {
+			if (parent->children[i] == tm_node)
+				break;
+		}
+		for (j = i; j < parent->child_cnt - 1; j++)
+			parent->children[j] = parent->children[j + 1];
+
+		parent->children[parent->child_cnt - 1] = NULL;
+		parent->child_cnt--;
+	}
+	rte_free(tm_node);
+	tm_node = NULL;
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_tm_node_delete(struct rte_eth_dev *dev, uint32_t node_id,
+		struct rte_tm_error *error)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt;
+	struct sxe2_tm_node *tm_node = NULL;
+	int32_t ret = -EINVAL;
+
+	if (!error) {
+		PMD_LOG_ERR(DRV, "Invalid input: error is null.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) {
+		PMD_LOG_ERR(DRV, "The TM capability is not supported.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "The TM capability is not supported.";
+		ret = ENOTSUP;
+		goto l_end;
+	}
+
+	if (node_id == RTE_TM_NODE_ID_NULL) {
+		PMD_LOG_ERR(DRV, "Invalid node id. node_id = %u", node_id);
+		error->type = RTE_TM_ERROR_TYPE_NODE_ID;
+		error->message = "invalid node id";
+		goto l_end;
+	}
+
+	tm_node = sxe2_tm_find_node(tm_ctxt->root, node_id);
+	if (!tm_node) {
+		PMD_LOG_ERR(DRV, "No such node.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_ID;
+		error->message = "no such node";
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	ret = sxe2_tm_tree_delete(tm_node);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "Delete node failed.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_ID;
+		error->message = "Delete node failed";
+		goto l_end;
+	}
+
+	if (tm_node == tm_ctxt->root)
+		tm_ctxt->root = NULL;
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_tm_node_type_get(struct rte_eth_dev *dev, uint32_t node_id,
+		int32_t *is_leaf, struct rte_tm_error *error)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt;
+	struct sxe2_tm_node *tm_node = NULL;
+	int32_t ret = -EINVAL;
+
+	if (!is_leaf || !error) {
+		PMD_LOG_ERR(DRV, "Invalid input: is_leaf:0x%p or error:0x%p is null.",
+			is_leaf, error);
+		if (error) {
+			error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+			error->message = "Invalid input: is_leaf or error is null";
+		}
+		goto l_end;
+	}
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) {
+		PMD_LOG_ERR(DRV, "The TM capability is not supported.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "The TM capability is not supported.";
+		ret = ENOTSUP;
+		goto l_end;
+	}
+
+	if (node_id == RTE_TM_NODE_ID_NULL) {
+		PMD_LOG_ERR(DRV, "Invalid node id.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_ID;
+		error->message = "invalid node id";
+		goto l_end;
+	}
+
+	tm_node = sxe2_tm_find_node(tm_ctxt->root, node_id);
+	if (!tm_node) {
+		PMD_LOG_ERR(DRV, "No such node.");
+		error->type = RTE_TM_ERROR_TYPE_NODE_ID;
+		error->message = "no such node";
+		goto l_end;
+	}
+
+	if (tm_node->level + 1 == tm_ctxt->tm_layers)
+		*is_leaf = true;
+	else
+		*is_leaf = false;
+	ret = 0;
+l_end:
+	return ret;
+}
+
+int32_t sxe2_tm_uninit(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt;
+	struct sxe2_tm_shaper_profile *shaper_profile = NULL;
+	int32_t ret = 0;
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) {
+		ret = 0;
+		goto l_end;
+	}
+
+	tm_ctxt->tm_layers = 0;
+	tm_ctxt->root_max_children = 0;
+	tm_ctxt->committed = false;
+
+	(void)sxe2_tm_tree_delete(tm_ctxt->root);
+
+	while ((shaper_profile = TAILQ_FIRST(&tm_ctxt->profile_list))) {
+		TAILQ_REMOVE(&tm_ctxt->profile_list, shaper_profile, node);
+		rte_free(shaper_profile);
+	}
+l_end:
+	return ret;
+}
+
+int32_t sxe2_tm_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	struct sxe2_sched_hw_cap *sched_ctxt = &adapter->sched_ctxt;
+	struct sxe2_tm_context *tm_ctxt = &adapter->tm_ctxt;
+	int32_t ret = 0;
+	struct sxe2_tm_shaper_profile *shaper_profile = NULL;
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0)
+		goto l_end;
+
+	tm_ctxt->tm_layers = sched_ctxt->tm_layers;
+	tm_ctxt->root_max_children = sched_ctxt->root_max_children;
+	tm_ctxt->prio_max = sched_ctxt->prio_max;
+	tm_ctxt->committed = false;
+	TAILQ_INIT(&tm_ctxt->profile_list);
+	tm_ctxt->root = NULL;
+
+	shaper_profile = rte_zmalloc("sxe2_tm_shaper_profile",
+		sizeof(struct sxe2_tm_shaper_profile), 0);
+	if (!shaper_profile) {
+		PMD_LOG_ERR(DRV, "Alloc shaper_profile memory failed.");
+		ret = -ENOMEM;
+		goto l_end;
+	}
+	shaper_profile->id = RTE_TM_SHAPER_PROFILE_ID_NONE;
+	shaper_profile->ref_cnt = 1;
+	shaper_profile->profile.committed.rate = UINT64_MAX;
+	shaper_profile->profile.peak.rate = UINT64_MAX;
+	TAILQ_INSERT_TAIL(&tm_ctxt->profile_list, shaper_profile, node);
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_tm_chk_all_leaf(struct rte_eth_dev *dev)
+{
+	int32_t ret = 0;
+	uint32_t i = 0;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	for (i = 0; i < dev->data->nb_tx_queues; i++) {
+		if (!sxe2_tm_find_node(adapter->tm_ctxt.root, i)) {
+			ret = -1;
+			break;
+		}
+	}
+	return ret;
+}
+
+static int32_t sxe2_tm_weight_calc(struct sxe2_tm_node *tm_node)
+{
+	int32_t ret = 0;
+	int32_t total_weight = 0;
+	int32_t total_weight2 = 0;
+	uint32_t i = 0;
+	uint32_t j = 0;
+	uint32_t k = 0;
+	uint32_t maxindex = 0;
+	uint32_t maxweight = 0;
+	struct sxe2_tm_node *cacl_node[SXE2_TM_MAX_CHILDREN_COUNT] = {NULL};
+
+	if (!tm_node) {
+		PMD_LOG_ERR(DRV, "Invalid input: tm_node is null.");
+		ret = -EINVAL;
+		goto l_end;
+	}
+
+	if (tm_node->child_cnt == 0)
+		goto l_end;
+
+	for (j = SXE2_TM_PRIO_MIN; j <= SXE2_TM_PRIO_MAX; j++) {
+		k = 0;
+		total_weight = 0;
+		total_weight2 = 0;
+		maxindex = 0;
+		maxweight = 0;
+
+		for (i = 0; i < tm_node->child_cnt; i++) {
+			if (tm_node->children[i]->priority == j)
+				cacl_node[k++] = tm_node->children[i];
+		}
+		if (k == 0)
+			continue;
+
+		for (i = 0; i < k; i++)
+			total_weight += cacl_node[i]->weight;
+
+		for (i = 0; i < k; i++) {
+			cacl_node[i]->hw_weight = cacl_node[i]->weight *
+				SXE2_TM_WEIGHT_SUM / total_weight;
+			total_weight2 += cacl_node[i]->hw_weight;
+			if (cacl_node[i]->hw_weight > maxweight) {
+				maxweight = cacl_node[i]->hw_weight;
+				maxindex = i;
+			}
+		}
+
+		cacl_node[maxindex]->hw_weight += SXE2_TM_WEIGHT_SUM - total_weight2;
+	}
+
+	for (i = 0; i < tm_node->child_cnt; i++) {
+		ret = sxe2_tm_weight_calc(tm_node->children[i]);
+		if (ret)
+			goto l_end;
+	}
+
+l_end:
+	return ret;
+}
+
+static int32_t sxe2_tm_hierarchy_commit(struct rte_eth_dev *dev,
+				     int32_t clear_on_fail, struct rte_tm_error *error)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret = -EINVAL;
+
+	if (!error) {
+		PMD_LOG_ERR(DRV, "Invalid input: error is null.");
+		ret = -EINVAL;
+		goto l_clear_on_fail;
+	}
+
+	if ((adapter->cap_flags & SXE2_DEV_CAPS_OFFLOAD_TM) == 0) {
+		PMD_LOG_ERR(DRV, "The TM capability is not supported.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "The TM capability is not supported.";
+		ret = ENOTSUP;
+		goto l_clear_on_fail;
+	}
+
+	if (dev->data->dev_started) {
+		PMD_LOG_ERR(DRV, "Device failed to Stop.");
+		error->message = "Device failed to Stop";
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		ret = -EPERM;
+		goto l_clear_on_fail;
+	}
+
+	ret = sxe2_tm_chk_all_leaf(dev);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "All tx queues need config.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "All tx queues need config.";
+		goto l_clear_on_fail;
+	}
+
+	ret = sxe2_tm_weight_calc(adapter->tm_ctxt.root);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "The weight in tree is wrong.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "The weight in tree is wrong.";
+		goto l_clear_on_fail;
+	}
+
+	ret = sxe2_drv_tm_commit(adapter);
+	if (ret) {
+		PMD_LOG_ERR(DRV, "Commit tree to fw failed.");
+		error->type = RTE_TM_ERROR_TYPE_UNSPECIFIED;
+		error->message = "Commit tree to fw failed.";
+		goto l_clear_on_fail;
+	}
+
+	adapter->tm_ctxt.committed = true;
+	ret = 0;
+	goto l_end;
+
+l_clear_on_fail:
+	if (clear_on_fail) {
+		(void)sxe2_tm_uninit(dev);
+		(void)sxe2_tm_init(dev);
+	}
+
+l_end:
+	return ret;
+}
+
+static const struct rte_tm_ops sxe2_tm_ops = {
+	.capabilities_get = sxe2_tm_capabilities_get,
+	.level_capabilities_get = sxe2_level_capabilities_get,
+	.node_capabilities_get = sxe2_node_capabilities_get,
+	.shaper_profile_add = sxe2_tm_shaper_profile_add,
+	.shaper_profile_delete = sxe2_tm_shaper_profile_del,
+	.node_add = sxe2_tm_node_add,
+	.node_delete = sxe2_tm_node_delete,
+	.node_type_get = sxe2_tm_node_type_get,
+
+	.hierarchy_commit = sxe2_tm_hierarchy_commit,
+};
+
+int32_t sxe2_tm_ops_get(struct rte_eth_dev *dev __rte_unused, void *arg)
+{
+	int32_t ret = 0;
+
+	if (!arg) {
+		ret = -EINVAL;
+		PMD_LOG_ERR(DRV, "%s failed because arg is NULL", __func__);
+		goto l_end;
+	}
+	*(const void **)arg = &sxe2_tm_ops;
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_tm.h b/drivers/net/sxe2/sxe2_tm.h
new file mode 100644
index 0000000000..c4f8da6a8e
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_tm.h
@@ -0,0 +1,76 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_TM_H__
+#define __SXE2_TM_H__
+#include <ethdev_driver.h>
+#include <rte_flow_driver.h>
+#include "sxe2_osal.h"
+
+#define SXE2_TM_MAX_LEVEL 7
+#define SXE2_TM_1L_NODE_NUM_MAX 1
+#define SXE2_TM_2L_NODE_NUM_MAX 8
+#define SXE2_TM_3L_NODE_NUM_MAX 64
+#define SXE2_TM_4L_NODE_NUM_MAX 256
+
+#define SXE2_TM_MAX_CHILDREN_COUNT 8
+
+#define SXE2_TM_WEIGHT_MAX  (200)
+#define SXE2_TM_WEIGHT_MIN  (1)
+#define SXE2_TM_WEIGHT_SUM  (32768)
+
+#define SXE2_HW_RATE_MIN 62500ull
+#define SXE2_HW_RATE_MAX 12500000000ull
+
+#define SXE2_TM_PRIO_MAX (7)
+#define SXE2_TM_PRIO_MIN (0)
+
+enum sxe2_tm_node_type {
+	SXE2_TM_NODE_TYPE_VSIG = 0,
+	SXE2_TM_NODE_TYPE_MID,
+	SXE2_TM_NODE_TYPE_QUEUE,
+	SXE2_TM_NODE_TYPE_INVALID,
+};
+
+struct sxe2_tm_shaper_profile {
+	TAILQ_ENTRY(sxe2_tm_shaper_profile) node;
+	uint32_t id;
+	uint32_t ref_cnt;
+	struct rte_tm_shaper_params profile;
+};
+
+TAILQ_HEAD(sxe2_shaper_profile_list, sxe2_tm_shaper_profile);
+
+struct sxe2_tm_node {
+	uint16_t id;
+	uint16_t teid;
+	uint32_t level;
+	uint32_t child_cnt;
+	uint32_t type;
+	uint16_t hw_weight;
+	uint16_t weight;
+	uint8_t priority;
+	struct sxe2_tm_node *parent;
+	uint8_t index_in_parent;
+	struct sxe2_tm_node *children[SXE2_TM_MAX_CHILDREN_COUNT];
+	struct sxe2_tm_shaper_profile *shaper_profile;
+};
+
+struct sxe2_tm_context {
+	uint32_t tm_layers;
+	uint16_t root_teid;
+	uint8_t root_max_children;
+	uint8_t prio_max;
+	bool committed;
+	struct sxe2_tm_node *root;
+	struct sxe2_shaper_profile_list profile_list;
+};
+
+int32_t sxe2_tm_ops_get(struct rte_eth_dev *dev __rte_unused, void *arg);
+
+int32_t sxe2_tm_init(struct rte_eth_dev *dev);
+
+int32_t sxe2_tm_uninit(struct rte_eth_dev *dev);
+
+#endif /* __SXE2_TM_H__ */
diff --git a/drivers/net/sxe2/sxe2_tx.c b/drivers/net/sxe2/sxe2_tx.c
index a05beb8c7a..a280edc9c5 100644
--- a/drivers/net/sxe2/sxe2_tx.c
+++ b/drivers/net/sxe2/sxe2_tx.c
@@ -304,7 +304,6 @@ int32_t __rte_cold sxe2_tx_queue_setup(struct rte_eth_dev *dev,
 	}
 
 	offloads = tx_conf->offloads | dev->data->dev_conf.txmode.offloads;
-
 	txq = sxe2_tx_queue_alloc(dev, queue_idx, nb_desc, socket_id);
 	if (txq == NULL) {
 		PMD_LOG_ERR(TX, "failed to alloc sxe2vf tx queue:%u resource", queue_idx);
-- 
2.52.0


^ permalink raw reply related

* [PATCH v10 03/20] drivers: add supported packet types get callback
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260606010726.2256170-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

Implement dev_supported_ptypes_get ethdev callback for sxe2 PMD.
This allows applications to query the packet types the driver
is capable of identifying, such as L2, L3 (IPv4/IPv6), and
L4 (TCP/UDP/SCTP) layers.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/common/sxe2/sxe2_ptype.h | 1793 ++++++++++++++++++++++++++++++
 drivers/net/sxe2/meson.build     |    1 +
 drivers/net/sxe2/sxe2_cmd_chnl.c |   22 +
 drivers/net/sxe2/sxe2_cmd_chnl.h |    2 +
 drivers/net/sxe2/sxe2_drv_cmd.h  |    6 +
 drivers/net/sxe2/sxe2_ethdev.c   |  169 ++-
 drivers/net/sxe2/sxe2_ethdev.h   |   19 +-
 drivers/net/sxe2/sxe2_mac.c      |  103 ++
 drivers/net/sxe2/sxe2_mac.h      |   50 +
 9 files changed, 2126 insertions(+), 39 deletions(-)
 create mode 100644 drivers/common/sxe2/sxe2_ptype.h
 create mode 100644 drivers/net/sxe2/sxe2_mac.c
 create mode 100644 drivers/net/sxe2/sxe2_mac.h

diff --git a/drivers/common/sxe2/sxe2_ptype.h b/drivers/common/sxe2/sxe2_ptype.h
new file mode 100644
index 0000000000..9c19570979
--- /dev/null
+++ b/drivers/common/sxe2/sxe2_ptype.h
@@ -0,0 +1,1793 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef _SXE2_PTYPE_H_
+#define _SXE2_PTYPE_H_
+#include <rte_ethdev.h>
+
+static inline const uint32_t *
+sxe2_dev_supported_ptypes_get(struct rte_eth_dev *dev, size_t *no_of_elements)
+{
+	const uint32_t *ret = NULL;
+
+	static const uint32_t ptypes[] = {
+		RTE_PTYPE_L2_ETHER,
+		RTE_PTYPE_L2_ETHER_TIMESYNC,
+		RTE_PTYPE_L2_ETHER_LLDP,
+		RTE_PTYPE_L2_ETHER_ARP,
+		RTE_PTYPE_L3_IPV4_EXT_UNKNOWN,
+		RTE_PTYPE_L3_IPV6_EXT_UNKNOWN,
+		RTE_PTYPE_L4_FRAG,
+		RTE_PTYPE_L4_ICMP,
+		RTE_PTYPE_L4_NONFRAG,
+		RTE_PTYPE_L4_SCTP,
+		RTE_PTYPE_L4_TCP,
+		RTE_PTYPE_L4_UDP,
+		RTE_PTYPE_TUNNEL_GRENAT,
+		RTE_PTYPE_TUNNEL_IP,
+		RTE_PTYPE_INNER_L2_ETHER,
+		RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN,
+		RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN,
+		RTE_PTYPE_INNER_L4_FRAG,
+		RTE_PTYPE_INNER_L4_ICMP,
+		RTE_PTYPE_INNER_L4_NONFRAG,
+		RTE_PTYPE_INNER_L4_SCTP,
+		RTE_PTYPE_INNER_L4_TCP,
+		RTE_PTYPE_INNER_L4_UDP,
+		RTE_PTYPE_UNKNOWN
+	};
+
+	if (dev->rx_pkt_burst != NULL) {
+		*no_of_elements = RTE_DIM(ptypes);
+		ret = ptypes;
+	} else {
+		ret = NULL;
+	}
+
+	return ret;
+}
+
+static inline void sxe2_init_ptype_list(uint32_t *ptype)
+{
+	/* ptype[0] reserved */
+	ptype[1] = RTE_PTYPE_L2_ETHER;
+	ptype[2] = RTE_PTYPE_L2_ETHER_TIMESYNC;
+	/* ptype[3] - ptype[5] reserved */
+	ptype[6] = RTE_PTYPE_L2_ETHER_LLDP;
+	/* ECP */
+	ptype[7] = RTE_PTYPE_UNKNOWN;
+	/* ptype[8] - ptype[9] reserved */
+	/* EAPol */
+	ptype[10] = RTE_PTYPE_UNKNOWN;
+	ptype[11] = RTE_PTYPE_L2_ETHER_ARP;
+	/* ptype[12] - ptype[21] reserved */
+
+	/* Non tunneled IPv4 */
+	ptype[22] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_L4_FRAG;
+	ptype[23] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_L4_NONFRAG;
+	ptype[24] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_L4_UDP;
+	/* ptype[25] reserved */
+	ptype[26] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_L4_TCP;
+	ptype[27] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_L4_SCTP;
+	ptype[28] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_L4_ICMP;
+
+	/* IPv4 --> IPv4 */
+	ptype[29] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[30] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[31] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[32] reserved */
+	ptype[33] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[34] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[35] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv4 --> IPv6 */
+	ptype[36] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[37] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[38] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[39] reserved */
+	ptype[40] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[41] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[42] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv4 --> GRE/GENEVE/VXLAN */
+	ptype[43] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT;
+
+	/* IPv4 --> GRE/GENEVE/VXLAN --> IPv4 */
+	ptype[44] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[45] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[46] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[47] reserved */
+	ptype[48] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[49] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[50] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv4 --> GRE/GENEVE/VXLAN --> IPv6 */
+	ptype[51] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[52] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[53] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[54] reserved */
+	ptype[55] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[56] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[57] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv4 --> GRE/GENEVE/VXLAN --> MAC */
+	ptype[58] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER;
+
+	/* IPv4 --> GRE/GENEVE/VXLAN --> MAC --> IPv4 */
+	ptype[59] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[60] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[61] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[62] reserved */
+	ptype[63] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[64] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[65] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv4 --> GRE/GENEVE/VXLAN --> MAC --> IPv6 */
+	ptype[66] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[67] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[68] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[69] reserved */
+	ptype[70] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[71] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[72] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+	/* IPv4 --> GRE/GENEVE/VXLAN --> MAC/VLAN */
+	ptype[73] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN;
+	/* IPv4 --> GRE/GENEVE/VXLAN --> MAC/VLAN --> IPv4 */
+	ptype[74] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[75] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[76] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[77] reserved */
+	ptype[78] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_TCP;
+	ptype[79] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_SCTP;
+	ptype[80] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_ICMP;
+	/* IPv4 --> GRE/GENEVE/VXLAN --> MAC/VLAN --> IPv6 */
+	ptype[81] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_FRAG;
+	ptype[82] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[83] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[64] reserved */
+	ptype[85] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_TCP;
+	ptype[86] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_SCTP;
+	ptype[87] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_ICMP;
+	/* Non tunneled IPv6 */
+	ptype[88] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_L4_FRAG;
+	ptype[89] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_L4_NONFRAG;
+	ptype[90] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_L4_UDP;
+	/* ptype[91] reserved */
+	ptype[92] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_L4_TCP;
+	ptype[93] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_L4_SCTP;
+	ptype[94] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_L4_ICMP;
+
+	/* IPv6 --> IPv4 */
+	ptype[95] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[96] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[97] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[98] reserved */
+	ptype[99] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_IP |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[100] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_IP |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[101] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_IP |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 --> IPv6 */
+	ptype[102] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_IP |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[103] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_IP |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[104] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_IP |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[105] reserved */
+	ptype[106] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_IP |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_TCP;
+	ptype[107] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_IP |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[108] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_IP |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 --> GRE/GENEVE/VXLAN */
+	ptype[109] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT;
+
+	/* IPv6 --> GRE/GENEVE/VXLAN --> IPv4 */
+	ptype[110] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[111] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[112] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[113] reserved */
+	ptype[114] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_TCP;
+	ptype[115] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[116] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 --> GRE/GENEVE/VXLAN --> IPv6 */
+	ptype[117] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[118] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[119] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[120] reserved */
+	ptype[121] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_TCP;
+	ptype[122] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[123] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 --> GRE/GENEVE/VXLAN --> MAC */
+	ptype[124] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER;
+
+	/* IPv6 --> GRE/GENEVE/VXLAN --> MAC --> IPv4 */
+	ptype[125] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[126] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[127] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[128] reserved */
+	ptype[129] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_TCP;
+	ptype[130] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[131] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 --> GRE/GENEVE/VXLAN --> MAC --> IPv6 */
+	ptype[132] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[133] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[134] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[135] reserved */
+	ptype[136] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_TCP;
+	ptype[137] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[138] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 --> GRE/GENEVE/VXLAN --> MAC/VLAN */
+	ptype[139] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN;
+
+	/* IPv6 --> GRE/GENEVE/VXLAN --> MAC/VLAN --> IPv4 */
+	ptype[140] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[141] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[142] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[143] reserved */
+	ptype[144] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_TCP;
+	ptype[145] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_SCTP;
+	ptype[146] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 --> GRE/GENEVE/VXLAN --> MAC/VLAN --> IPv6 */
+	ptype[147] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[148] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[149] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[150] reserved */
+	ptype[151] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_TCP;
+	ptype[152] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_SCTP;
+	ptype[153] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_ICMP;
+	/* ptype[154] - ptype[159] reserved */
+	/* IPSec */
+	ptype[160] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_ESP;
+	ptype[161] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_ESP;
+	/* AH */
+	ptype[162] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[163] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* NAT-T-ESP */
+	ptype[164] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_ESP;
+	ptype[165] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_ESP;
+	/* SDN-ESP */
+	ptype[166] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_ESP;
+	ptype[167] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_ESP;
+	/* ptype[168] - ptype[271] reserved */
+	/* IPV4 --> VRRP */
+	ptype[272] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* IPV4 --> OSPF */
+	ptype[273] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* IPV6 --> VRRP */
+	ptype[274] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* IPV6 --> VRRP */
+	ptype[275] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* ATAoE */
+	ptype[276] = RTE_PTYPE_UNKNOWN;
+	/* Control */
+	ptype[278] = RTE_PTYPE_UNKNOWN;
+	/* ptype[279] - ptype[324] reserved */
+	/* GTP */
+	ptype[325] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPC;
+	ptype[326] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPC;
+	ptype[327] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPC;
+	ptype[328] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPC;
+	ptype[329] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU;
+	ptype[330] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU;
+	ptype[331] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_FRAG;
+	ptype[332] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[333] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[334] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_TCP;
+	ptype[335] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_ICMP;
+	ptype[336] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_FRAG;
+	ptype[337] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[338] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[339] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_TCP;
+	ptype[340] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_ICMP;
+	ptype[341] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_FRAG;
+	ptype[342] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[343] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[344] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_TCP;
+	ptype[345] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_ICMP;
+	ptype[346] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_FRAG;
+	ptype[347] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[348] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[349] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_TCP;
+	ptype[350] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_GTPU |
+		RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_ICMP;
+	/* PFCP */
+	ptype[351] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP;
+	ptype[352] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP;
+	ptype[353] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP;
+	ptype[354] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP;
+	/* ptype[355] - ptype[359] reserved */
+	/* L2TPv3 */
+	ptype[360] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_L2TP;
+	ptype[361] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_L2TP;
+	/* ptype[362] - ptype[370] reserved */
+	/* eCPRI */
+	ptype[371] = RTE_PTYPE_UNKNOWN;
+	ptype[381] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[391] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[396] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv4, IGMP */
+	ptype[397] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv4, EIGRP */
+	ptype[398] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv4, EIGRP */
+	ptype[399] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv4, PIM */
+	ptype[400] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv4, PIM */
+	ptype[401] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv6, IGMP */
+	ptype[402] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv6, IGMP */
+	ptype[403] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv6, EIGRP */
+	ptype[404] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv6, EIGRP */
+	ptype[405] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv6, PIM */
+	ptype[406] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv6, PIM */
+	ptype[407] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv4, VRRP */
+	ptype[408] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv4, VRRP */
+	ptype[409] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv6, VRRP */
+	ptype[410] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv6, VRRP */
+	ptype[411] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv4, OSPF */
+	ptype[412] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv4, OSPF */
+	ptype[413] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv6, OSPF */
+	ptype[414] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv6, OSPF */
+	ptype[415] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv4, L2_TP_V3 */
+	ptype[416] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv4, L2_TP_V3 */
+	ptype[417] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv6, L2_TP_V3 */
+	ptype[418] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv6, L2_TP_V3 */
+	ptype[419] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv4, AH */
+	ptype[420] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv4, AH */
+	ptype[421] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv6, AH */
+	ptype[422] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv6, AH */
+	ptype[423] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv4, ESP */
+	ptype[424] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv4, ESP */
+	ptype[425] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv4, UDP, TUN, MAC, IPv6, ESP */
+	ptype[426] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* MAC, IPv6, UDP, TUN, MAC, IPv6, ESP */
+	ptype[427] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	/* TP-TUN GTPU */
+	ptype[450] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[451] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[452] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[453] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[454] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[455] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[456] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[457] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[458] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[459] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[460] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[461] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[462] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[463] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[464] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[465] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[466] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[467] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[468] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[469] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[470] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[471] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[472] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[473] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[474] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[475] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[476] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[477] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[478] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[479] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[480] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[481] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[482] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[483] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[484] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[485] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[486] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[487] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[488] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[489] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[490] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[491] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[492] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[493] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[494] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[495] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[496] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	ptype[497] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_TUNNEL_IP | RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_INNER_L4_UDP;
+	/* ptype[498] - ptype[767] reserved */
+	/* L2(NETWORK CPU) */
+	/* ISIS */
+	ptype[768] = RTE_PTYPE_UNKNOWN;
+	/* SDF */
+	ptype[769] = RTE_PTYPE_UNKNOWN;
+	/* PPoE_NEGO */
+	ptype[770] = RTE_PTYPE_L2_ETHER_PPPOE;
+	/* PPoE_PROTOCOL */
+	ptype[771] = RTE_PTYPE_L2_ETHER_PPPOE;
+	ptype[772] = RTE_PTYPE_L2_ETHER_PPPOE;
+	/* LACP */
+	ptype[773] = RTE_PTYPE_UNKNOWN;
+	/* ptype[774] - ptype[775] reserved */
+	/* IPv4 L3(NETWORK CPU) */
+	ptype[776] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_IGMP;
+	/* EIGRP */
+	ptype[777] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* PIM */
+	ptype[778] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[779] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_L2TP;
+	ptype[780] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_L2TP;
+	ptype[781] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_L2TP;
+	/* ptype[782] - ptype[783] reserved */
+	/* IPv6 L3(NETWORK CPU) */
+	ptype[784] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_IGMP;
+	/* EIGRP */
+	ptype[785] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* PIM */
+	ptype[786] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[787] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_L2TP;
+	ptype[788] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_L2TP;
+	ptype[789] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP | RTE_PTYPE_TUNNEL_L2TP;
+	/* ptype[790] - ptype[791] reserved */
+	/* IPv4 L4(NETWORK CPU) */
+	ptype[792] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_TCP;
+	ptype[793] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_TCP;
+	ptype[794] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[795] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[796] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[797] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP;
+	ptype[798] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[799] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[800] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[801] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	/* ptype[802] - ptype[807] reserved */
+	/* IPv6 L4(NETWORK CPU) */
+	ptype[808] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_TCP;
+	ptype[809] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_TCP;
+	ptype[810] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[811] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[812] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+		RTE_PTYPE_L4_UDP;
+	ptype[813] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[814] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[815] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[816] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	ptype[817] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_UDP;
+	/* ptype[818] - ptype[819] reserved */
+	/* IPv6 -> MAC */
+	ptype[820] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* IPv6 -> MAC -> IPv4*/
+	ptype[821] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[822] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[823] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[824] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[825] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[826] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* IPv6 -> MAC -> IPv4*/
+	ptype[827] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[828] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[829] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[830] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[831] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[832] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* ptype[833] - ptype[834] reserved */
+	/* IPv6 -> MAC/VLAN */
+	ptype[835] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* IPv6 -> MAC/VLAN -> IPv4 */
+	ptype[836] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[837] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[838] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[839] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[840] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[841] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	/* IPv6 -> MAC/VLAN -> IPv6 */
+	ptype[842] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[843] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[844] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[845] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[846] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+	ptype[847] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_L4_NONFRAG;
+
+	/* IPv6 -> UDP -> VXLAN/GENEVE -> PAY */
+	ptype[878] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT;
+
+	/* IPv6 -> UDP -> VXLAN/GENEVE -> IPv4 */
+	ptype[877] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[876] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[879] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_UDP;
+	ptype[880] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_TCP;
+	ptype[875] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[874] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 -> UDP -> VXLAN/GENEVE -> IPv6 */
+	ptype[871] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[870] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[872] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_UDP;
+	ptype[873] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_TCP;
+	ptype[869] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[868] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv4 -> UDP -> VXLAN/GENEVE -> PAY */
+	ptype[891] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT;
+	/* IPv4 -> UDP -> VXLAN/GENEVE -> IPv4 */
+	ptype[890] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[889] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[892] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[893] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[888] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[887] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+	/* IPv4 -> UDP -> VXLAN/GENEVE -> IPv6 */
+	ptype[884] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[883] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[885] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[886] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[882] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[881] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 -> UDP -> GRE -> PAY */
+	ptype[904] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT;
+	/* IPv6 -> UDP -> GRE -> IPv4 */
+	ptype[903] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[902] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[905] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[906] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[901] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[900] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+	/* IPv6 -> UDP -> GRE -> IPv6 */
+	ptype[897] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[896] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[898] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[899] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[895] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[894] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv4 -> UDP -> GRE -> PAY */
+	ptype[917] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT;
+	/* IPv4 -> UDP -> GRE -> IPv4 */
+	ptype[916] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[915] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[918] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[919] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[914] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[913] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+	/* IPv4 -> UDP -> GRE -> IPv6 */
+	ptype[910] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[909] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[911] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[912] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[908] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[907] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 -> UDP -> VXLAN/GENEVE -> MACVLAN -> PAY */
+	ptype[930] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN;
+
+	/* IPv6 -> UDP -> VXLAN/GENEVE -> MACVLAN -> IPv4 */
+	ptype[929] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[928] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[931] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_UDP;
+	ptype[932] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_TCP;
+	ptype[927] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[926] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 -> UDP -> VXLAN/GENEVE -> MACVLAN -> IPv6 */
+	ptype[923] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[922] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[924] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_UDP;
+	ptype[925] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_TCP;
+	ptype[921] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[920] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv4 -> UDP -> VXLAN/GENEVE -> MACVLAN -> PAY */
+	ptype[943] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN;
+	/* IPv4 -> UDP -> VXLAN/GENEVE -> MACVLAN -> IPv4 */
+	ptype[942] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[941] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[944] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[945] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[940] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[939] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+	/* IPv4 -> UDP -> VXLAN/GENEVE -> MACVLAN -> IPv6 */
+	ptype[936] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[935] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[937] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[938] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[934] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[933] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 > UDP -> GRE -> MACVLAN -> PAY */
+	ptype[956] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN;
+	/* IPv6 -> UDP -> GRE -> MACVLAN -> IPv4 */
+	ptype[955] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[954] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[957] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[958] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[953] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[952] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+	/* IPv6 -> UDP -> GRE -> MACVLAN -> IPv6 */
+	ptype[949] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[948] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[950] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[951] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[947] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[946] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv4 -> UDP -> GRE -> MACVLAN -> PAY */
+	ptype[969] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN;
+	/* IPv4 -> UDP -> GRE -> MACVLAN -> IPv4 */
+	ptype[968] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[967] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[970] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[971] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[966] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[965] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+	/* IPv4 -> UDP -> GRE -> MACVLAN -> IPv6 */
+	ptype[962] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[961] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[963] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[964] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[960] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[959] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER_VLAN |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 -> UDP -> VXLAN/GENEVE -> MAC -> PAY */
+	ptype[982] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER;
+
+	/* IPv6 -> UDP -> VXLAN/GENEVE -> MAC -> IPv4 */
+	ptype[981] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[980] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[983] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_UDP;
+	ptype[984] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_TCP;
+	ptype[979] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[978] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 -> UDP -> VXLAN/GENEVE -> MAC -> IPv6 */
+	ptype[975] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_FRAG;
+	ptype[974] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[976] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_UDP;
+	ptype[977] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_TCP;
+	ptype[973] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_SCTP;
+	ptype[972] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				 RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv4 -> UDP -> VXLAN/GENEVE -> MAC -> PAY */
+	ptype[995] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER;
+	/* IPv4 -> UDP -> VXLAN/GENEVE -> MAC -> IPv4 */
+	ptype[994] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[993] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[996] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[997] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[992] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[991] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+	/* IPv4 -> UDP -> VXLAN/GENEVE -> MAC -> IPv6 */
+	ptype[988] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[987] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[989] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[990] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[986] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[985] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv6 > UDP -> GRE -> MAC -> PAY */
+	ptype[1008] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER;
+	/* IPv6 -> UDP -> GRE -> MAC -> IPv4 */
+	ptype[1007] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[1006] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[1009] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[1010] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[1005] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[1004] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+	/* IPv6 -> UDP -> GRE -> MAC -> IPv6 */
+	ptype[1001] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[1000] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[1002] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[1003] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[999] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[998] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+
+	/* IPv4 -> UDP -> GRE -> MAC -> PAY */
+	ptype[1021] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				 RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER;
+	/* IPv4 -> UDP -> GRE -> MAC -> IPv4 */
+	ptype[1020] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[1019] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[1022] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[1023] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[1018] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[1017] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+	/* IPv4 -> UDP -> GRE -> MAC -> IPv6 */
+	ptype[1014] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_FRAG;
+	ptype[1013] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_NONFRAG;
+	ptype[1015] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_UDP;
+	ptype[1016] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_TCP;
+	ptype[1012] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_SCTP;
+	ptype[1011] = RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN |
+				RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+				RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN |
+				RTE_PTYPE_INNER_L4_ICMP;
+}
+
+#endif /* _RTE_PTYPE_TUNNEL_GRENAT_H_ */
diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index c225dd7cd8..b14b5120c1 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -60,4 +60,5 @@ sources += files(
         'sxe2_txrx_poll.c',
         'sxe2_txrx.c',
         'sxe2_txrx_vec.c',
+        'sxe2_mac.c',
 )
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.c b/drivers/net/sxe2/sxe2_cmd_chnl.c
index d16b6528d0..07eeb7f38c 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.c
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.c
@@ -321,3 +321,25 @@ int32_t sxe2_drv_txq_switch(struct sxe2_adapter *adapter, struct sxe2_tx_queue *
 
 	return ret;
 }
+
+int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter)
+{
+	int32_t ret = 0;
+	struct sxe2_common_device *cdev = adapter->cdev;
+	struct sxe2_drv_cmd_params param = {0};
+	struct sxe2_drv_link_info_resp resp = {0};
+
+	sxe2_drv_cmd_params_fill(adapter, &param, SXE2_DRV_CMD_LINK_STATUS_GET,
+				 NULL, 0,
+				 &resp, sizeof(resp));
+	ret = sxe2_drv_cmd_exec(cdev, &param);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "link status get failed, ret=%d", ret);
+		goto l_end;
+	}
+	adapter->link_ctxt.speed = resp.speed;
+	adapter->link_ctxt.link_up = resp.status;
+
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_cmd_chnl.h b/drivers/net/sxe2/sxe2_cmd_chnl.h
index ed5e842346..cda676ed97 100644
--- a/drivers/net/sxe2/sxe2_cmd_chnl.h
+++ b/drivers/net/sxe2/sxe2_cmd_chnl.h
@@ -34,4 +34,6 @@ int32_t sxe2_drv_txq_ctxt_cfg(struct sxe2_adapter *adapter,
 			      struct sxe2_tx_queue *txq,
 			      uint16_t txq_cnt);
 
+int32_t sxe2_drv_mac_link_status_get(struct sxe2_adapter *adapter);
+
 #endif /* SXE2_CMD_CHNL_H */
diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h
index ccc9c20ef4..a0f08b5184 100644
--- a/drivers/net/sxe2/sxe2_drv_cmd.h
+++ b/drivers/net/sxe2/sxe2_drv_cmd.h
@@ -227,6 +227,12 @@ struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vsi_info_get_resp {
 	struct sxe2_drv_msix_caps used_msix;
 } __rte_packed_end;
 
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_link_info_resp {
+	uint32_t speed;
+	uint8_t status;
+	uint8_t rsv[3];
+} __rte_packed_end;
+
 enum sxe2_drv_cmd_module {
 	SXE2_DRV_CMD_MODULE_HANDSHAKE = 0,
 	SXE2_DRV_CMD_MODULE_DEV = 1,
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index e0f7002138..01552a8202 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -27,7 +27,9 @@
 #include "sxe2_tx.h"
 #include "sxe2_rx.h"
 #include "sxe2_txrx.h"
+#include "sxe2_mac.h"
 #include "sxe2_common.h"
+#include "sxe2_ptype.h"
 #include "sxe2_common_log.h"
 #include "sxe2_host_regs.h"
 #include "sxe2_ioctl_chnl_func.h"
@@ -78,6 +80,41 @@ static struct sxe2_pci_map_addr_info sxe2_net_map_addr_info_pf[SXE2_PCI_MAP_RES_
 				      .reg_width = 10},
 };
 
+static int32_t sxe2_dev_configure(struct rte_eth_dev *dev);
+static int32_t sxe2_dev_start(struct rte_eth_dev *dev);
+static int32_t sxe2_dev_stop(struct rte_eth_dev *dev);
+static int32_t sxe2_dev_close(struct rte_eth_dev *dev);
+static int32_t sxe2_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *dev_info);
+static const uint32_t *sxe2_buffer_split_supported_hdr_ptypes_get(struct rte_eth_dev *dev
+				__rte_unused, size_t *no_of_elements __rte_unused);
+
+static const struct eth_dev_ops sxe2_eth_dev_ops = {
+	.dev_configure              = sxe2_dev_configure,
+	.dev_start                  = sxe2_dev_start,
+	.dev_stop                   = sxe2_dev_stop,
+	.dev_close                  = sxe2_dev_close,
+	.dev_infos_get              = sxe2_dev_infos_get,
+	.dev_supported_ptypes_get   = sxe2_dev_supported_ptypes_get,
+	.link_update                = sxe2_link_update,
+
+	.rx_queue_start             = sxe2_rx_queue_start,
+	.rx_queue_stop              = sxe2_rx_queue_stop,
+	.tx_queue_start             = sxe2_tx_queue_start,
+	.tx_queue_stop              = sxe2_tx_queue_stop,
+	.rx_queue_setup             = sxe2_rx_queue_setup,
+	.rx_queue_release           = sxe2_rx_queue_release,
+	.tx_queue_setup             = sxe2_tx_queue_setup,
+	.tx_queue_release           = sxe2_tx_queue_release,
+	.rxq_info_get               = sxe2_rx_queue_info_get,
+	.txq_info_get               = sxe2_tx_queue_info_get,
+	.rx_burst_mode_get          = sxe2_rx_burst_mode_get,
+	.tx_burst_mode_get          = sxe2_tx_burst_mode_get,
+	.tx_done_cleanup            = sxe2_tx_done_cleanup,
+
+	.mtu_set                    = sxe2_mtu_set,
+	.buffer_split_supported_hdr_ptypes_get = sxe2_buffer_split_supported_hdr_ptypes_get,
+};
+
 static int32_t sxe2_dev_configure(struct rte_eth_dev *dev)
 {
 	int32_t ret = 0;
@@ -122,6 +159,12 @@ static int32_t sxe2_dev_start(struct rte_eth_dev *dev)
 	sxe2_rx_mode_func_set(dev);
 	sxe2_tx_mode_func_set(dev);
 
+	ret = sxe2_link_update_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to initialize link update, ret:%d", ret);
+		goto l_end;
+	}
+
 	ret = sxe2_queues_start(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "enable queues failed");
@@ -136,16 +179,6 @@ static int32_t sxe2_dev_start(struct rte_eth_dev *dev)
 	return ret;
 }
 
-static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
-{
-	(void)sxe2_dev_stop(dev);
-	(void)sxe2_queues_release(dev);
-	sxe2_vsi_uninit(dev);
-	sxe2_dev_pci_map_uinit(dev);
-
-	return 0;
-}
-
 static int32_t sxe2_dev_infos_get(struct rte_eth_dev *dev,
 			struct rte_eth_dev_info *dev_info)
 {
@@ -270,28 +303,59 @@ static int32_t sxe2_dev_infos_get(struct rte_eth_dev *dev,
 	return 0;
 }
 
-static const struct eth_dev_ops sxe2_eth_dev_ops = {
-	.dev_configure              = sxe2_dev_configure,
-	.dev_start                  = sxe2_dev_start,
-	.dev_stop                   = sxe2_dev_stop,
-	.dev_close                  = sxe2_dev_close,
-	.dev_infos_get              = sxe2_dev_infos_get,
+static const uint32_t *
+sxe2_buffer_split_supported_hdr_ptypes_get(struct rte_eth_dev *dev __rte_unused,
+					   size_t *no_of_elements __rte_unused)
+{
+	static const uint32_t ptypes[] = {
+		RTE_PTYPE_L2_ETHER,
+		RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN,
+		RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_L4_UDP,
+		RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_L4_TCP,
+		RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_L4_SCTP,
+		RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN,
+		RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_L4_UDP,
+		RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_L4_TCP,
+		RTE_PTYPE_L2_ETHER | RTE_PTYPE_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_L4_SCTP,
+
+		RTE_PTYPE_TUNNEL_GRENAT,
+
+		RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER,
+
+		RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+			RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN,
+		RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+			RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN,
+
+		RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+			RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_UDP,
+		RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+			RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_TCP,
+		RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+			RTE_PTYPE_INNER_L3_IPV4_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_SCTP,
+
+		RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+			RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_UDP,
+		RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+			RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_TCP,
+		RTE_PTYPE_TUNNEL_GRENAT | RTE_PTYPE_INNER_L2_ETHER |
+			RTE_PTYPE_INNER_L3_IPV6_EXT_UNKNOWN | RTE_PTYPE_INNER_L4_SCTP,
+
+		RTE_PTYPE_UNKNOWN
+	};
 
-	.rx_queue_start             = sxe2_rx_queue_start,
-	.rx_queue_stop              = sxe2_rx_queue_stop,
-	.tx_queue_start             = sxe2_tx_queue_start,
-	.tx_queue_stop              = sxe2_tx_queue_stop,
-	.rx_queue_setup             = sxe2_rx_queue_setup,
-	.rx_queue_release           = sxe2_rx_queue_release,
-	.tx_queue_setup             = sxe2_tx_queue_setup,
-	.tx_queue_release           = sxe2_tx_queue_release,
+	return ptypes;
+}
 
-	.rxq_info_get               = sxe2_rx_queue_info_get,
-	.txq_info_get               = sxe2_tx_queue_info_get,
-	.rx_burst_mode_get          = sxe2_rx_burst_mode_get,
-	.tx_burst_mode_get          = sxe2_tx_burst_mode_get,
-	.tx_done_cleanup            = sxe2_tx_done_cleanup,
-};
+static inline void sxe2_init_ptype_tbl(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	uint32_t *ptype = adapter->ptype_tbl;
+
+	PMD_INIT_FUNC_TRACE();
+
+	sxe2_init_ptype_list(ptype);
+}
 
 struct sxe2_pci_map_bar_info *sxe2_dev_get_bar_info(struct sxe2_adapter *adapter,
 						    enum sxe2_pci_map_resource res_type)
@@ -360,6 +424,29 @@ void *sxe2_pci_map_addr_get(struct sxe2_adapter *adapter,
 	return addr;
 }
 
+static int32_t sxe2_eth_init(struct rte_eth_dev *dev)
+{
+	int32_t ret = 0;
+
+	ret = sxe2_link_update_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to initialize link update, ret:%d", ret);
+		goto l_end;
+	}
+
+	ret = sxe2_mtu_set(dev, dev->data->mtu);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to set mtu, ret=%d", ret);
+		goto l_end;
+	}
+l_end:
+	return ret;
+}
+
+static void sxe2_eth_uinit(struct rte_eth_dev *dev __rte_unused)
+{
+}
+
 static void sxe2_drv_dev_caps_set(struct sxe2_adapter *adapter,
 			struct sxe2_drv_dev_caps_resp *dev_caps)
 {
@@ -765,6 +852,8 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 		goto l_end;
 	}
 
+	sxe2_init_ptype_tbl(dev);
+
 	ret = sxe2_hw_init(dev);
 	if (ret) {
 		PMD_LOG_ERR(INIT, "Failed to initialize hw, ret=[%d]", ret);
@@ -789,18 +878,38 @@ static int32_t sxe2_dev_init(struct rte_eth_dev *dev,
 		goto init_dev_info_err;
 	}
 
+	ret = sxe2_eth_init(dev);
+	if (ret) {
+		PMD_LOG_ERR(INIT, "Failed to initialize eth parameters, ret=%d", ret);
+		goto init_eth_err;
+	}
+
 	goto l_end;
 
+init_eth_err:
 init_dev_info_err:
 	sxe2_vsi_uninit(dev);
 init_vsi_err:
+	sxe2_dev_pci_map_uinit(dev);
 l_end:
 	return ret;
 }
 
+static int32_t sxe2_dev_close(struct rte_eth_dev *dev)
+{
+	(void)sxe2_dev_stop(dev);
+	(void)sxe2_queues_release(dev);
+	sxe2_vsi_uninit(dev);
+	sxe2_dev_pci_map_uinit(dev);
+	sxe2_eth_uinit(dev);
+
+	return 0;
+}
+
 static int32_t sxe2_dev_uninit(struct rte_eth_dev *dev)
 {
 	int32_t ret = 0;
+
 	if (rte_eal_process_type() != RTE_PROC_PRIMARY)
 		goto l_end;
 
diff --git a/drivers/net/sxe2/sxe2_ethdev.h b/drivers/net/sxe2/sxe2_ethdev.h
index ff0876cd4c..c188b41a0e 100644
--- a/drivers/net/sxe2/sxe2_ethdev.h
+++ b/drivers/net/sxe2/sxe2_ethdev.h
@@ -120,12 +120,6 @@ enum {
 	SXE2_FLAGS_NBITS
 };
 
-struct sxe2_link_context {
-	rte_spinlock_t link_lock;
-	bool link_up;
-	uint32_t  speed;
-};
-
 struct sxe2_devargs {
 	uint8_t flow_dup_pattern_mode;
 	uint8_t func_flow_direct_en;
@@ -267,6 +261,12 @@ struct sxe2_sched_hw_cap {
 	uint8_t adj_lvl;
 };
 
+struct sxe2_link_context {
+	rte_spinlock_t link_lock;
+	bool link_up;
+	uint32_t  speed;
+};
+
 struct sxe2_adapter {
 	struct sxe2_common_device      *cdev;
 	struct sxe2_dev_info            dev_info;
@@ -276,9 +276,10 @@ struct sxe2_adapter {
 	struct sxe2_irq_context       irq_ctxt;
 	struct sxe2_queue_context     q_ctxt;
 	struct sxe2_vsi_context       vsi_ctxt;
-	struct sxe2_devargs			  devargs;
-	uint16_t                           dev_port_id;
-	uint64_t                           cap_flags;
+	struct sxe2_link_context      link_ctxt;
+	struct sxe2_devargs           devargs;
+	uint16_t                      dev_port_id;
+	uint64_t                      cap_flags;
 	enum sxe2_dev_type            dev_type;
 	uint32_t    ptype_tbl[SXE2_MAX_PTYPE_NUM];
 	struct rte_ether_addr           mac_addr;
diff --git a/drivers/net/sxe2/sxe2_mac.c b/drivers/net/sxe2/sxe2_mac.c
new file mode 100644
index 0000000000..3c2f909002
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_mac.c
@@ -0,0 +1,103 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <rte_os.h>
+#include "sxe2_osal.h"
+#include "sxe2_mac.h"
+#include "sxe2_common_log.h"
+#include "sxe2_ethdev.h"
+#include "sxe2_cmd_chnl.h"
+#include "sxe2_host_regs.h"
+
+int32_t sxe2_link_update_init(struct rte_eth_dev *dev)
+{
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+	int32_t ret;
+
+	PMD_INIT_FUNC_TRACE();
+
+	rte_spinlock_init(&adapter->link_ctxt.link_lock);
+
+	ret = sxe2_drv_mac_link_status_get(adapter);
+	if (ret) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "Failed to get link status, ret=%d", ret);
+		goto l_end;
+	}
+
+	(void)sxe2_link_update(dev, 0);
+
+l_end:
+	return ret;
+}
+int32_t sxe2_link_update(struct rte_eth_dev *dev, __rte_unused int32_t wait_to_complete)
+{
+	struct rte_eth_link new_link;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	memset(&new_link, 0, sizeof(new_link));
+
+	switch (adapter->link_ctxt.speed) {
+	case 0:
+		new_link.link_speed = RTE_ETH_SPEED_NUM_NONE;
+		break;
+	case 10:
+		new_link.link_speed = RTE_ETH_SPEED_NUM_10M;
+		break;
+	case 100:
+		new_link.link_speed = RTE_ETH_SPEED_NUM_100M;
+		break;
+	case 1000:
+		new_link.link_speed = RTE_ETH_SPEED_NUM_1G;
+		break;
+	case 10000:
+		new_link.link_speed = RTE_ETH_SPEED_NUM_10G;
+		break;
+	case 20000:
+		new_link.link_speed = RTE_ETH_SPEED_NUM_20G;
+		break;
+	case 25000:
+		new_link.link_speed = RTE_ETH_SPEED_NUM_25G;
+		break;
+	case 40000:
+		new_link.link_speed = RTE_ETH_SPEED_NUM_40G;
+		break;
+	case 50000:
+		new_link.link_speed = RTE_ETH_SPEED_NUM_50G;
+		break;
+	case 100000:
+		new_link.link_speed = RTE_ETH_SPEED_NUM_100G;
+		break;
+	default:
+		new_link.link_speed = RTE_ETH_SPEED_NUM_NONE;
+		break;
+	}
+
+	new_link.link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
+	new_link.link_status = adapter->link_ctxt.link_up ? RTE_ETH_LINK_UP :
+					     RTE_ETH_LINK_DOWN;
+	new_link.link_autoneg = !(dev->data->dev_conf.link_speeds &
+				RTE_ETH_LINK_SPEED_FIXED);
+
+	return rte_eth_linkstatus_set(dev, &new_link);
+}
+
+int32_t sxe2_mtu_set(struct rte_eth_dev *dev, uint16_t mtu __rte_unused)
+{
+	int32_t ret                      = -1;
+	struct sxe2_adapter *adapter = SXE2_DEV_PRIVATE_TO_ADAPTER(dev);
+
+	PMD_INIT_FUNC_TRACE();
+
+	if (dev->data->dev_started != 0) {
+		PMD_DEV_LOG_ERR(adapter, DRV, "port %d must be stopped before configuration",
+				dev->data->port_id);
+		ret = -1;
+		goto l_end;
+	}
+
+	ret = 0;
+
+l_end:
+	return ret;
+}
diff --git a/drivers/net/sxe2/sxe2_mac.h b/drivers/net/sxe2/sxe2_mac.h
new file mode 100644
index 0000000000..f2f3edaeff
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_mac.h
@@ -0,0 +1,50 @@
+
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef __SXE2_MAC_H__
+#define __SXE2_MAC_H__
+#include <ethdev_driver.h>
+#include "sxe2_host_regs.h"
+
+#define SXE2_NUM_MACADDR_MAX   64
+
+#define SXE2_DPDK_OFFLOAD_OUTER_INSERT_8021Q SXE2_VSI_L2TAGSTXVALID_ID_OUT_VLAN1
+#define SXE2_DPDK_OFFLOAD_OUTER_INSERT_8021AD SXE2_VSI_L2TAGSTXVALID_ID_STAG
+#define SXE2_DPDK_OFFLOAD_OUTER_INSERT_QINQ1 SXE2_VSI_L2TAGSTXVALID_ID_OUT_VLAN2
+#define SXE2_DPDK_OFFLOAD_OUTER_INSERT_VLAN  SXE2_VSI_L2TAGSTXVALID_ID_VLAN
+
+#define SXE2_DPDK_OFFLOAD_OUTER_INSERT_ENABLE SXE2_VSI_L2TAGSTXVALID_L2TAG1_VALID
+
+#define SXE2_DPDK_OFFLOAD_OUTER_STRIP_8021Q SXE2_VSI_TSR_ID_OUT_VLAN1
+#define SXE2_DPDK_OFFLOAD_OUTER_STRIP_8021AD SXE2_VSI_TSR_ID_STAG
+#define SXE2_DPDK_OFFLOAD_OUTER_STRIP_QINQ1 SXE2_VSI_TSR_ID_OUT_VLAN2
+
+#define SXE2_DPDK_OFFLOAD_INNER_INSERT_QINQ1  SXE2_VSI_L2TAGSTXVALID_ID_VLAN
+#define SXE2_DPDK_OFFLOAD_INNER_INSERT_ENABLE SXE2_VSI_L2TAGSTXVALID_L2TAG2_VALID
+
+#define SXE2_DPDK_OFFLOAD_INNER_STRIP_QINQ1 SXE2_VSI_TSR_ID_VLAN
+
+#define SXE2_DPDK_OFFLOAD_FIELD                (0X0F)
+#define SXE2_DPDK_OFFLOAD_TAGID_FIELD          (0X07)
+
+#define SXE2_DPDK_OFFLOAD_OUTER_STRIP_MASK (SXE2_DPDK_OFFLOAD_OUTER_STRIP_8021Q | \
+					SXE2_DPDK_OFFLOAD_OUTER_STRIP_8021AD | \
+					SXE2_DPDK_OFFLOAD_OUTER_STRIP_QINQ1)
+#define SXE2_DPDK_OFFLOAD_STRIP_OFFSET SXE2_VSI_TSR_SHOW_TAG_S
+
+#define SXE2_DPDK_OFFLOAD_INSERT_ENABLE (RTE_BIT32(3))
+
+struct sxe2_mac_mc_list {
+	uint32_t count;
+	struct rte_ether_addr addr[SXE2_NUM_MACADDR_MAX];
+};
+
+int32_t sxe2_link_update_init(struct rte_eth_dev *dev);
+
+int32_t sxe2_link_update(struct rte_eth_dev *dev, __rte_unused int32_t wait_to_complete);
+
+int32_t sxe2_mtu_set(struct rte_eth_dev *dev, uint16_t mtu);
+
+#endif /* __SXE2_MAC_H__ */
-- 
2.52.0


^ permalink raw reply related

* [PATCH v10 02/20] net/sxe2: add AVX2 vector data path for Rx and Tx
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260606010726.2256170-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

Added AVX256 vectorized versions of Rx and Tx data path functions to
improve packet processing performance.

The vector path uses AVX2 SIMD instructions to process multiple
descriptors per loop, significantly reducing the per-packet overhead.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>

net/sxe2: support AVX512 vectorized path for Rx and Tx
---
 drivers/net/sxe2/meson.build          |   9 +
 drivers/net/sxe2/sxe2_txrx.c          |  38 +-
 drivers/net/sxe2/sxe2_txrx_vec.h      |  12 +-
 drivers/net/sxe2/sxe2_txrx_vec_avx2.c | 776 ++++++++++++++++++++++++++
 4 files changed, 830 insertions(+), 5 deletions(-)
 create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_avx2.c

diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index 7bd0d8120c..c225dd7cd8 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -39,6 +39,15 @@ if arch_subdir == 'x86'
                         c_args: avx512_args)
                 objs += sxe2_avx512_lib.extract_objects('sxe2_txrx_vec_avx512.c')
         endif
+        sxe2_avx2_lib = static_library('sxe2_avx2_lib',
+                'sxe2_txrx_vec_avx2.c',
+                dependencies: [static_rte_ethdev,
+                        static_rte_kvargs, static_rte_hash,
+                        static_rte_security, static_rte_cryptodev,
+                        static_rte_bus_pci],
+                include_directories: includes,
+                c_args: [cflags, '-mavx2'])
+        objs += sxe2_avx2_lib.extract_objects('sxe2_txrx_vec_avx2.c')
 endif
 
 sources += files(
diff --git a/drivers/net/sxe2/sxe2_txrx.c b/drivers/net/sxe2/sxe2_txrx.c
index aa1c474088..eaf95259a5 100644
--- a/drivers/net/sxe2/sxe2_txrx.c
+++ b/drivers/net/sxe2/sxe2_txrx.c
@@ -167,8 +167,14 @@ void sxe2_tx_mode_func_set(struct rte_eth_dev *dev)
 				PMD_LOG_INFO(TX, "AVX512 is not supported in build env.");
 #endif
 			}
-			if ((tx_mode_flags & SXE2_TX_MODE_VEC_SET_MASK) == 0)
-				tx_mode_flags |= SXE2_TX_MODE_VEC_SSE;
+			if (((tx_mode_flags & SXE2_TX_MODE_VEC_SET_MASK) == 0) &&
+			    ((rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2) == 1) ||
+			    (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512F) == 1)) &&
+			    (rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_256))
+				tx_mode_flags |= SXE2_TX_MODE_VEC_AVX2;
+
+			if ((0 == (tx_mode_flags & SXE2_TX_MODE_VEC_SET_MASK)))
+				tx_mode_flags |=  SXE2_TX_MODE_VEC_SSE;
 #endif
 			if (tx_mode_flags & SXE2_TX_MODE_VEC_SET_MASK) {
 				ret = sxe2_tx_queues_vec_prepare(dev);
@@ -197,6 +203,13 @@ void sxe2_tx_mode_func_set(struct rte_eth_dev *dev)
 				dev->tx_pkt_burst = sxe2_tx_pkts_vec_avx512_simple;
 			}
 #endif
+		} else if (tx_mode_flags & SXE2_TX_MODE_VEC_AVX2) {
+			if (tx_mode_flags & SXE2_TX_MODE_VEC_OFFLOAD) {
+				dev->tx_pkt_prepare = sxe2_tx_pkts_prepare;
+				dev->tx_pkt_burst = sxe2_tx_pkts_vec_avx2;
+			} else {
+				dev->tx_pkt_burst = sxe2_tx_pkts_vec_avx2_simple;
+			}
 		} else {
 			if (tx_mode_flags & SXE2_TX_MODE_VEC_OFFLOAD) {
 				dev->tx_pkt_prepare = sxe2_tx_pkts_prepare;
@@ -231,6 +244,10 @@ static const struct {
 	{ sxe2_tx_pkts_vec_avx512_simple,
 	      "Vector AVX512 Simple" },
 #endif
+	{ sxe2_tx_pkts_vec_avx2,
+	      "Vector AVX2" },
+	{ sxe2_tx_pkts_vec_avx2_simple,
+	      "Vector AVX2 Simple" },
 	{ sxe2_tx_pkts_vec_sse,
 	      "Vector SSE" },
 	{ sxe2_tx_pkts_vec_sse_simple,
@@ -330,7 +347,13 @@ void sxe2_rx_mode_func_set(struct rte_eth_dev *dev)
 				PMD_LOG_INFO(RX, "AVX512 support detected but not enabled");
 #endif
 			}
-			if ((rx_mode_flags & SXE2_RX_MODE_VEC_SET_MASK) == 0 &&
+			if (((rx_mode_flags & SXE2_RX_MODE_VEC_SET_MASK) == 0) &&
+				((rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX2) == 1) ||
+				(rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512F) == 1)) &&
+				(rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_256))
+				rx_mode_flags |= SXE2_RX_MODE_VEC_AVX2;
+
+			if (((rx_mode_flags & SXE2_RX_MODE_VEC_SET_MASK) == 0) &&
 				rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128)
 				rx_mode_flags |= SXE2_RX_MODE_VEC_SSE;
 #endif
@@ -354,6 +377,11 @@ void sxe2_rx_mode_func_set(struct rte_eth_dev *dev)
 			else
 				dev->rx_pkt_burst = sxe2_rx_pkts_scattered_vec_avx512;
 #endif
+		} else if (rx_mode_flags & SXE2_RX_MODE_VEC_AVX2) {
+			if (rx_mode_flags & SXE2_RX_MODE_VEC_OFFLOAD)
+				dev->rx_pkt_burst = sxe2_rx_pkts_scattered_vec_avx2_offload;
+			else
+				dev->rx_pkt_burst = sxe2_rx_pkts_scattered_vec_avx2;
 		} else {
 			dev->rx_pkt_burst = sxe2_rx_pkts_scattered_vec_sse_offload;
 		}
@@ -381,6 +409,10 @@ static const struct {
 	{ sxe2_rx_pkts_scattered_vec_avx512_offload,
 	      "Offload Vector AVX512 Scattered" },
 #endif
+	{ sxe2_rx_pkts_scattered_vec_avx2,
+	      "Vector AVX2 Scattered" },
+	{ sxe2_rx_pkts_scattered_vec_avx2_offload,
+	      "Offload Vector AVX2 Scattered" },
 	{ sxe2_rx_pkts_scattered_vec_sse_offload,
 	      "Vector SSE Scattered" },
 #endif
diff --git a/drivers/net/sxe2/sxe2_txrx_vec.h b/drivers/net/sxe2/sxe2_txrx_vec.h
index af7c8d12b2..369777606f 100644
--- a/drivers/net/sxe2/sxe2_txrx_vec.h
+++ b/drivers/net/sxe2/sxe2_txrx_vec.h
@@ -11,19 +11,21 @@
 #define SXE2_RX_MODE_VEC_SIMPLE    RTE_BIT32(0)
 #define SXE2_RX_MODE_VEC_OFFLOAD   RTE_BIT32(1)
 #define SXE2_RX_MODE_VEC_SSE       RTE_BIT32(2)
+#define SXE2_RX_MODE_VEC_AVX2      RTE_BIT32(3)
 #define SXE2_RX_MODE_VEC_AVX512    RTE_BIT32(4)
 #define SXE2_RX_MODE_BATCH_ALLOC   RTE_BIT32(10)
 #define SXE2_RX_MODE_VEC_SET_MASK	(SXE2_RX_MODE_VEC_SIMPLE | \
 			SXE2_RX_MODE_VEC_OFFLOAD | SXE2_RX_MODE_VEC_SSE | \
-			SXE2_RX_MODE_VEC_AVX512)
+			SXE2_RX_MODE_VEC_AVX2 | SXE2_RX_MODE_VEC_AVX512)
 #define SXE2_TX_MODE_VEC_SIMPLE   RTE_BIT32(0)
 #define SXE2_TX_MODE_VEC_OFFLOAD  RTE_BIT32(1)
 #define SXE2_TX_MODE_VEC_SSE      RTE_BIT32(2)
+#define SXE2_TX_MODE_VEC_AVX2     RTE_BIT32(3)
 #define SXE2_TX_MODE_VEC_AVX512   RTE_BIT32(4)
 #define SXE2_TX_MODE_SIMPLE_BATCH RTE_BIT32(10)
 #define SXE2_TX_MODE_VEC_SET_MASK	(SXE2_TX_MODE_VEC_SIMPLE | \
 			SXE2_TX_MODE_VEC_OFFLOAD | SXE2_TX_MODE_VEC_SSE | \
-			SXE2_TX_MODE_VEC_AVX512)
+			SXE2_TX_MODE_VEC_AVX2 | SXE2_TX_MODE_VEC_AVX512)
 #define SXE2_TX_VEC_NO_SUPPORT_OFFLOAD (		  \
 			RTE_ETH_TX_OFFLOAD_MULTI_SEGS |		  \
 			RTE_ETH_TX_OFFLOAD_QINQ_INSERT |	  \
@@ -68,6 +70,12 @@ uint16_t sxe2_rx_pkts_scattered_vec_avx512(void *rx_queue,
 		struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
 uint16_t sxe2_rx_pkts_scattered_vec_avx512_offload(void *rx_queue,
 		struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
+uint16_t sxe2_tx_pkts_vec_avx2_simple(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
+uint16_t sxe2_tx_pkts_vec_avx2(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
+uint16_t sxe2_rx_pkts_scattered_vec_avx2(void *rx_queue,
+		struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
+uint16_t sxe2_rx_pkts_scattered_vec_avx2_offload(void *rx_queue,
+		struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
 #endif
 int32_t __rte_cold sxe2_tx_vec_support_check(struct rte_eth_dev *dev, uint32_t *vec_flags);
 int32_t __rte_cold sxe2_tx_queues_vec_prepare(struct rte_eth_dev *dev);
diff --git a/drivers/net/sxe2/sxe2_txrx_vec_avx2.c b/drivers/net/sxe2/sxe2_txrx_vec_avx2.c
new file mode 100644
index 0000000000..72b09850b6
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_txrx_vec_avx2.c
@@ -0,0 +1,776 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#include <rte_vect.h>
+
+#include "sxe2_ethdev.h"
+#include "sxe2_common_log.h"
+#include "sxe2_queue.h"
+#include "sxe2_txrx_vec.h"
+#include "sxe2_txrx_vec_common.h"
+#include "sxe2_vsi.h"
+
+static inline void
+sxe2_tx_desc_fill_one_avx2(volatile union sxe2_tx_data_desc *desc, struct rte_mbuf *pkt,
+			   uint64_t desc_cmd, bool with_offloads)
+{
+	__m128i data_desc;
+	uint64_t desc_qw1;
+	uint32_t desc_offset;
+
+	desc_qw1 = (SXE2_TX_DESC_DTYPE_DATA |
+		   ((uint64_t)desc_cmd) << SXE2_TX_DATA_DESC_CMD_SHIFT |
+		   ((uint64_t)pkt->data_len) << SXE2_TX_DATA_DESC_BUF_SZ_SHIFT);
+
+	desc_offset = SXE2_TX_DATA_DESC_MACLEN_VAL(pkt->l2_len);
+	desc_qw1 |= ((uint64_t)desc_offset) << SXE2_TX_DATA_DESC_OFFSET_SHIFT;
+	if (with_offloads)
+		sxe2_tx_desc_fill_offloads(pkt, &desc_qw1);
+
+	data_desc = _mm_set_epi64x(desc_qw1, rte_pktmbuf_iova(pkt));
+	_mm_store_si128(RTE_CAST_PTR(__m128i *, desc), data_desc);
+}
+
+static __rte_always_inline void
+sxe2_tx_desc_fill_avx2(volatile union sxe2_tx_data_desc *desc, struct rte_mbuf **pkts,
+		       uint16_t pkts_num, uint64_t desc_cmd, bool with_offloads)
+{
+	__m256i desc_group0;
+	__m256i desc_group1;
+	uint64_t desc0_qw1;
+	uint64_t desc1_qw1;
+	uint64_t desc2_qw1;
+	uint64_t desc3_qw1;
+
+	const uint64_t desc_qw1_com = (SXE2_TX_DESC_DTYPE_DATA |
+					((uint64_t)desc_cmd) << SXE2_TX_DATA_DESC_CMD_SHIFT);
+	uint32_t desc_offset[4] = {0};
+
+	if (((uint64_t)desc & 0x1F) != 0 && pkts_num != 0) {
+		sxe2_tx_desc_fill_one_avx2(desc, *pkts, desc_cmd, with_offloads);
+		pkts_num--;
+		desc++;
+		pkts++;
+	}
+
+	while (pkts_num > 3) {
+		desc3_qw1 = (desc_qw1_com |
+					((uint64_t)pkts[3]->data_len)
+					<< SXE2_TX_DATA_DESC_BUF_SZ_SHIFT);
+
+		desc_offset[3] = SXE2_TX_DATA_DESC_MACLEN_VAL(pkts[3]->l2_len);
+		desc3_qw1 |= ((uint64_t)desc_offset[3]) << SXE2_TX_DATA_DESC_OFFSET_SHIFT;
+		if (with_offloads)
+			sxe2_tx_desc_fill_offloads(pkts[3], &desc3_qw1);
+
+		desc2_qw1 = (desc_qw1_com |
+					((uint64_t)pkts[2]->data_len)
+					<< SXE2_TX_DATA_DESC_BUF_SZ_SHIFT);
+		desc_offset[2] = SXE2_TX_DATA_DESC_MACLEN_VAL(pkts[2]->l2_len);
+		desc2_qw1 |= ((uint64_t)desc_offset[2]) << SXE2_TX_DATA_DESC_OFFSET_SHIFT;
+		if (with_offloads)
+			sxe2_tx_desc_fill_offloads(pkts[2], &desc2_qw1);
+
+		desc1_qw1 = (desc_qw1_com |
+					((uint64_t)pkts[1]->data_len)
+					<< SXE2_TX_DATA_DESC_BUF_SZ_SHIFT);
+		desc_offset[1] = SXE2_TX_DATA_DESC_MACLEN_VAL(pkts[1]->l2_len);
+		desc1_qw1 |= ((uint64_t)desc_offset[1]) << SXE2_TX_DATA_DESC_OFFSET_SHIFT;
+		if (with_offloads)
+			sxe2_tx_desc_fill_offloads(pkts[1], &desc1_qw1);
+
+		desc0_qw1 = (desc_qw1_com |
+					((uint64_t)pkts[0]->data_len)
+					<< SXE2_TX_DATA_DESC_BUF_SZ_SHIFT);
+		desc_offset[0] = SXE2_TX_DATA_DESC_MACLEN_VAL(pkts[0]->l2_len);
+		desc0_qw1 |= ((uint64_t)desc_offset[0]) << SXE2_TX_DATA_DESC_OFFSET_SHIFT;
+		if (with_offloads)
+			sxe2_tx_desc_fill_offloads(pkts[0], &desc0_qw1);
+
+		desc_group1 = _mm256_set_epi64x(desc3_qw1, rte_pktmbuf_iova(pkts[3]),
+					desc2_qw1, rte_pktmbuf_iova(pkts[2]));
+
+		desc_group0 = _mm256_set_epi64x(desc1_qw1, rte_pktmbuf_iova(pkts[1]),
+					desc0_qw1, rte_pktmbuf_iova(pkts[0]));
+
+		_mm256_store_si256(RTE_CAST_PTR(__m256i *, desc + 2), desc_group1);
+		_mm256_store_si256(RTE_CAST_PTR(__m256i *, desc), desc_group0);
+
+		pkts_num -= 4;
+		desc     += 4;
+		pkts     += 4;
+	}
+
+	while (pkts_num) {
+		sxe2_tx_desc_fill_one_avx2(desc, *pkts, desc_cmd, with_offloads);
+		pkts_num--;
+		desc++;
+		pkts++;
+	}
+}
+
+static __rte_always_inline uint16_t
+sxe2_tx_pkts_vec_avx2_batch(struct sxe2_tx_queue *txq, struct rte_mbuf **tx_pkts,
+			    uint16_t nb_pkts, bool with_offloads)
+{
+	volatile union sxe2_tx_data_desc *desc;
+	struct sxe2_tx_buffer *buffer;
+	uint16_t next_use;
+	uint16_t res_num;
+	uint16_t tx_num;
+
+	if (txq->desc_free_num < txq->free_thresh)
+		(void)sxe2_tx_bufs_free_vec(txq);
+
+	nb_pkts = RTE_MIN(txq->desc_free_num, nb_pkts);
+	if (unlikely(nb_pkts == 0)) {
+		PMD_LOG_DEBUG(TX, "Tx pkts avx2 batch: may not enough free desc, "
+				"free_desc=%u, need_tx_pkts=%u",
+				txq->desc_free_num, nb_pkts);
+		goto l_end;
+	}
+	tx_num = nb_pkts;
+
+	next_use = txq->next_use;
+	desc     = &txq->desc_ring[next_use];
+	buffer   = &txq->buffer_ring[next_use];
+
+	txq->desc_free_num -= nb_pkts;
+
+	res_num = txq->ring_depth - txq->next_use;
+
+	if (tx_num >= res_num) {
+		sxe2_tx_pkts_mbuf_fill(buffer, tx_pkts, res_num);
+
+		sxe2_tx_desc_fill_avx2(desc, tx_pkts, res_num,
+				SXE2_TX_DATA_DESC_CMD_EOP, with_offloads);
+		tx_pkts += (res_num - 1);
+		desc    += (res_num - 1);
+
+		sxe2_tx_desc_fill_one_avx2(desc, *tx_pkts++,
+				(SXE2_TX_DATA_DESC_CMD_EOP | SXE2_TX_DATA_DESC_CMD_RS),
+				with_offloads);
+
+		tx_num -= res_num;
+
+		next_use     = 0;
+		txq->next_rs = txq->rs_thresh - 1;
+		desc         = &txq->desc_ring[next_use];
+		buffer       = &txq->buffer_ring[next_use];
+	}
+
+	sxe2_tx_pkts_mbuf_fill(buffer, tx_pkts, tx_num);
+
+	sxe2_tx_desc_fill_avx2(desc, tx_pkts, tx_num,
+			SXE2_TX_DATA_DESC_CMD_EOP, with_offloads);
+
+	next_use += tx_num;
+	if (next_use > txq->next_rs) {
+		txq->desc_ring[txq->next_rs].read.type_cmd_off_bsz_l2t |=
+			rte_cpu_to_le_64(SXE2_TX_DATA_DESC_CMD_RS_MASK);
+
+		txq->next_rs += txq->rs_thresh;
+	}
+	txq->next_use = next_use;
+	SXE2_PCI_REG_WRITE_WC(txq->tdt_reg_addr, next_use);
+	PMD_LOG_DEBUG(TX, "port_id=%u queue_id=%u next_use=%u send_pkts=%u",
+			txq->port_id, txq->queue_id, next_use, nb_pkts);
+l_end:
+	return nb_pkts;
+}
+
+static __rte_always_inline uint16_t
+sxe2_tx_pkts_vec_avx2_common(struct sxe2_tx_queue *txq, struct rte_mbuf **tx_pkts,
+			     uint16_t nb_pkts, bool with_offloads)
+{
+	uint16_t tx_done_num = 0;
+	uint16_t tx_once_num;
+	uint16_t tx_need_num;
+
+	while (nb_pkts) {
+		tx_need_num = RTE_MIN(nb_pkts, txq->rs_thresh);
+		tx_once_num = sxe2_tx_pkts_vec_avx2_batch(txq,
+					tx_pkts + tx_done_num, tx_need_num, with_offloads);
+
+		nb_pkts     -= tx_once_num;
+		tx_done_num += tx_once_num;
+
+		if (tx_once_num < tx_need_num)
+			break;
+	}
+	return tx_done_num;
+}
+
+uint16_t sxe2_tx_pkts_vec_avx2_simple(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
+{
+	return sxe2_tx_pkts_vec_avx2_common(tx_queue, tx_pkts, nb_pkts, false);
+}
+
+uint16_t sxe2_tx_pkts_vec_avx2(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
+{
+	return sxe2_tx_pkts_vec_avx2_common(tx_queue, tx_pkts, nb_pkts, true);
+}
+
+static inline void sxe2_rx_queue_rearm_avx2(struct sxe2_rx_queue *rxq)
+{
+	volatile union sxe2_rx_desc *desc;
+	struct rte_mbuf **buffer;
+	struct rte_mbuf *mbuf0, *mbuf1;
+	__m128i dma_addr0, dma_addr1;
+	__m128i virt_addr0, virt_addr1;
+	__m128i hdr_room = _mm_set_epi64x(RTE_PKTMBUF_HEADROOM, RTE_PKTMBUF_HEADROOM);
+	int32_t ret;
+	uint16_t i;
+	uint16_t new_tail;
+
+	buffer = &rxq->buffer_ring[rxq->realloc_start];
+	desc   = &rxq->desc_ring[rxq->realloc_start];
+
+	ret = rte_mempool_get_bulk(rxq->mb_pool, (void *)buffer, SXE2_RX_REARM_THRESH_VEC);
+	if (ret != 0) {
+		if ((rxq->realloc_num + SXE2_RX_REARM_THRESH_VEC) >= rxq->ring_depth) {
+			dma_addr0 = _mm_setzero_si128();
+			for (i = 0; i < SXE2_RX_NUM_PER_LOOP_AVX; ++i) {
+				buffer[i] = &rxq->fake_mbuf;
+				_mm_store_si128(RTE_CAST_PTR(__m128i *, &desc[i].read), dma_addr0);
+			}
+		}
+
+		rxq->vsi->adapter->dev_info.dev_data->rx_mbuf_alloc_failed +=
+				SXE2_RX_REARM_THRESH_VEC;
+		return;
+	}
+
+	for (i = 0; i < SXE2_RX_REARM_THRESH_VEC; i += 2, buffer += 2) {
+		mbuf0 = buffer[0];
+		mbuf1 = buffer[1];
+#if RTE_IOVA_IN_MBUF
+
+		RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, buf_iova) !=
+				 offsetof(struct rte_mbuf, buf_addr) + 8);
+#endif
+		virt_addr0 = _mm_loadu_si128((__m128i *)&mbuf0->buf_addr);
+		virt_addr1 = _mm_loadu_si128((__m128i *)&mbuf1->buf_addr);
+
+#if RTE_IOVA_IN_MBUF
+
+		dma_addr0 = _mm_unpackhi_epi64(virt_addr0, virt_addr0);
+		dma_addr1 = _mm_unpackhi_epi64(virt_addr1, virt_addr1);
+#else
+
+		dma_addr0 = _mm_unpacklo_epi64(virt_addr0, virt_addr0);
+		dma_addr1 = _mm_unpacklo_epi64(virt_addr1, virt_addr1);
+#endif
+
+		dma_addr0 = _mm_add_epi64(dma_addr0, hdr_room);
+		dma_addr1 = _mm_add_epi64(dma_addr1, hdr_room);
+
+		_mm_store_si128(RTE_CAST_PTR(__m128i *, &desc++->read), dma_addr0);
+		_mm_store_si128(RTE_CAST_PTR(__m128i *, &desc++->read), dma_addr1);
+	}
+
+	rxq->realloc_start += SXE2_RX_REARM_THRESH_VEC;
+	if (rxq->realloc_start >= rxq->ring_depth)
+		rxq->realloc_start = 0;
+	rxq->realloc_num -= SXE2_RX_REARM_THRESH_VEC;
+
+	new_tail = (rxq->realloc_start == 0) ?
+		(rxq->ring_depth - 1) : (rxq->realloc_start - 1);
+	SXE2_PCI_REG_WRITE_WC(rxq->rdt_reg_addr, new_tail);
+}
+
+static __rte_always_inline uint16_t
+sxe2_rx_pkts_common_vec_avx2(struct sxe2_rx_queue *rxq,
+			     struct rte_mbuf **rx_pkts, uint16_t nb_pkts, uint8_t *split_rxe_flags,
+			     uint8_t *umbcast_flags, bool do_offload)
+{
+	const uint32_t *ptype_tbl = rxq->vsi->adapter->ptype_tbl;
+	const __m256i mbuf_init   = _mm256_set_epi64x(0, 0, 0, rxq->mbuf_init_value);
+	struct rte_mbuf **buffer;
+	volatile union sxe2_rx_desc *desc;
+	__m256i mbufs6_7, mbufs4_5, mbufs2_3, mbufs0_1;
+	uint32_t bit_num;
+	uint16_t done_num;
+	uint16_t i = 0;
+	uint16_t j = 0;
+
+	buffer   = &rxq->buffer_ring[rxq->processing_idx];
+	desc     = &rxq->desc_ring[rxq->processing_idx];
+	done_num = 0;
+
+	rte_prefetch0(desc);
+
+	nb_pkts = RTE_ALIGN_FLOOR(nb_pkts, SXE2_RX_NUM_PER_LOOP_AVX);
+
+	if (rxq->realloc_num > SXE2_RX_REARM_THRESH_VEC)
+		sxe2_rx_queue_rearm_avx2(rxq);
+
+	if (0 == (rte_le_to_cpu_64(desc->wb.status_err_ptype_len) &
+				SXE2_RX_DESC_STATUS_DD_MASK))
+		goto l_end;
+
+	const __m256i crc_adjust =
+		_mm256_set_epi16(0, 0, 0, -rxq->crc_len,
+				 0, -rxq->crc_len, 0,
+				 0, 0, 0, 0,
+				 -rxq->crc_len, 0, -rxq->crc_len, 0, 0);
+
+	const __m256i dd_mask = _mm256_set1_epi32(1);
+	const __m256i rvp_shuf_mask =
+		_mm256_set_epi8(7, 6, 5, 4,
+				3, 2, 13, 12,
+				0xFF, 0xFF, 13, 12,
+				0xFF, 0xFF, 0xFF, 0xFF,
+				7, 6, 5, 4,
+				3, 2, 13, 12,
+				0xFF, 0xFF, 13, 12,
+				0xFF, 0xFF, 0xFF, 0xFF);
+
+	const __m128i eop_shuf_mask =
+		_mm_set_epi8(0xFF, 0xFF, 0xFF, 0xFF,
+			     0xFF, 0xFF, 0xFF, 0xFF,
+			     8, 0, 10, 2,
+			     12, 4, 14, 6);
+
+	RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, pkt_len) !=
+			offsetof(struct rte_mbuf, rx_descriptor_fields1) + 4);
+	RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, data_len) !=
+			offsetof(struct rte_mbuf, rx_descriptor_fields1) + 8);
+	RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, vlan_tci) !=
+			offsetof(struct rte_mbuf, rx_descriptor_fields1) + 10);
+	RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, hash) !=
+			offsetof(struct rte_mbuf, rx_descriptor_fields1) + 12);
+
+	for (i = 0; i < nb_pkts; i += SXE2_RX_NUM_PER_LOOP_AVX,
+				desc += SXE2_RX_NUM_PER_LOOP_AVX) {
+		_mm256_storeu_si256((void *)&rx_pkts[i],
+					_mm256_loadu_si256((void *)&buffer[i]));
+#ifdef RTE_ARCH_X86_64
+		_mm256_storeu_si256((void *)&rx_pkts[i + 4],
+					_mm256_loadu_si256((void *)&buffer[i + 4]));
+#endif
+
+		const __m128i desc7 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 7));
+		rte_compiler_barrier();
+		const __m128i desc6 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 6));
+		rte_compiler_barrier();
+		const __m128i desc5 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 5));
+		rte_compiler_barrier();
+		const __m128i desc4 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 4));
+		rte_compiler_barrier();
+		const __m128i desc3 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 3));
+		rte_compiler_barrier();
+		const __m128i desc2 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 2));
+		rte_compiler_barrier();
+		const __m128i desc1 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 1));
+		rte_compiler_barrier();
+		const __m128i desc0 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 0));
+
+		const __m256i descs6_7 =
+			_mm256_inserti128_si256(_mm256_castsi128_si256(desc6), desc7, 1);
+		const __m256i descs4_5 =
+			_mm256_inserti128_si256(_mm256_castsi128_si256(desc4), desc5, 1);
+		const __m256i descs2_3 =
+			_mm256_inserti128_si256(_mm256_castsi128_si256(desc2), desc3, 1);
+		const __m256i descs0_1 =
+			_mm256_inserti128_si256(_mm256_castsi128_si256(desc0), desc1, 1);
+
+		if (split_rxe_flags) {
+			for (j = 0; j < SXE2_RX_NUM_PER_LOOP_AVX; j++)
+				rte_mbuf_prefetch_part2(rx_pkts[i + j]);
+		}
+
+		mbufs6_7 = _mm256_shuffle_epi8(descs6_7, rvp_shuf_mask);
+		mbufs4_5 = _mm256_shuffle_epi8(descs4_5, rvp_shuf_mask);
+
+		mbufs6_7 = _mm256_add_epi16(mbufs6_7, crc_adjust);
+		mbufs4_5 = _mm256_add_epi16(mbufs4_5, crc_adjust);
+
+		const __m256i ptype_mask = _mm256_set1_epi32(SXE2_RX_DESC_PTYPE_MASK);
+
+		const __m256i staterrs4_7 = _mm256_unpackhi_epi32(descs6_7, descs4_5);
+
+		__m256i ptypes4_7 = _mm256_and_si256(staterrs4_7, ptype_mask);
+
+		const uint16_t ptype7 = _mm256_extract_epi16(ptypes4_7, 9);
+		const uint16_t ptype6 = _mm256_extract_epi16(ptypes4_7, 1);
+		const uint16_t ptype5 = _mm256_extract_epi16(ptypes4_7, 11);
+		const uint16_t ptype4 = _mm256_extract_epi16(ptypes4_7, 3);
+
+		mbufs6_7 = _mm256_insert_epi32(mbufs6_7, ptype_tbl[ptype7], 4);
+		mbufs6_7 = _mm256_insert_epi32(mbufs6_7, ptype_tbl[ptype6], 0);
+		mbufs4_5 = _mm256_insert_epi32(mbufs4_5, ptype_tbl[ptype5], 4);
+		mbufs4_5 = _mm256_insert_epi32(mbufs4_5, ptype_tbl[ptype4], 0);
+
+		mbufs2_3 = _mm256_shuffle_epi8(descs2_3, rvp_shuf_mask);
+		mbufs0_1 = _mm256_shuffle_epi8(descs0_1, rvp_shuf_mask);
+
+		mbufs2_3 = _mm256_add_epi16(mbufs2_3, crc_adjust);
+		mbufs0_1 = _mm256_add_epi16(mbufs0_1, crc_adjust);
+
+		const __m256i staterrs0_3 = _mm256_unpackhi_epi32(descs2_3, descs0_1);
+
+		__m256i ptypes0_3 = _mm256_and_si256(staterrs0_3, ptype_mask);
+
+		const uint16_t ptype3 = _mm256_extract_epi16(ptypes0_3, 9);
+		const uint16_t ptype2 = _mm256_extract_epi16(ptypes0_3, 1);
+		const uint16_t ptype1 = _mm256_extract_epi16(ptypes0_3, 11);
+		const uint16_t ptype0 = _mm256_extract_epi16(ptypes0_3, 3);
+
+		mbufs2_3 = _mm256_insert_epi32(mbufs2_3, ptype_tbl[ptype3], 4);
+		mbufs2_3 = _mm256_insert_epi32(mbufs2_3, ptype_tbl[ptype2], 0);
+		mbufs0_1 = _mm256_insert_epi32(mbufs0_1, ptype_tbl[ptype1], 4);
+		mbufs0_1 = _mm256_insert_epi32(mbufs0_1, ptype_tbl[ptype0], 0);
+
+		__m256i staterrs0_7 = _mm256_unpacklo_epi64(staterrs4_7, staterrs0_3);
+
+		__m256i stu_len0_7 = _mm256_unpackhi_epi64(staterrs4_7, staterrs0_3);
+		__m256i mbuf_flags = _mm256_setzero_si256();
+
+		if (do_offload) {
+			const __m256i desc_flags_mask = _mm256_set1_epi32(0x00001C04);
+			const __m256i desc_flags_rss_mask = _mm256_set1_epi32(0x20000000);
+			const __m256i vlan_flags =
+				_mm256_set_epi8
+				(0, 0, 0, 0, 0, 0, 0, 0,
+				 0, 0, 0,
+				 RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED,
+				 0, 0, 0, 0,
+				 0, 0, 0, 0, 0, 0, 0, 0,
+				 0, 0, 0,
+				 RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED,
+				 0, 0, 0, 0);
+
+			const __m256i rss_flags =
+				_mm256_set_epi8
+				(0, 0, 0, 0, 0, 0, 0, 0,
+				 0, 0, 0, RTE_MBUF_F_RX_RSS_HASH, 0, 0, 0, 0,
+				 0, 0, 0, 0, 0, 0, 0, 0,
+				 0, 0, 0, RTE_MBUF_F_RX_RSS_HASH, 0, 0, 0, 0);
+
+			const __m256i cksum_flags =
+				_mm256_set_epi8
+				(0, 0, 0, 0, 0, 0, 0, 0,
+				 ((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD |
+					RTE_MBUF_F_RX_L4_CKSUM_BAD |
+					RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+				 ((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD |
+					RTE_MBUF_F_RX_L4_CKSUM_BAD |
+					RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+				 ((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD |
+					RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+					RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+				 ((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD |
+					RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+					RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+				 ((RTE_MBUF_F_RX_L4_CKSUM_BAD |
+					RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+				 ((RTE_MBUF_F_RX_L4_CKSUM_BAD |
+					RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+				 ((RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+					RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+				 ((RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+					RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+
+				 0, 0, 0, 0, 0, 0, 0, 0,
+				 ((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD |
+					RTE_MBUF_F_RX_L4_CKSUM_BAD |
+					RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+				 ((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD |
+					RTE_MBUF_F_RX_L4_CKSUM_BAD |
+					RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+				 ((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD |
+					RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+					RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+				 ((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD |
+					RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+					RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+				 ((RTE_MBUF_F_RX_L4_CKSUM_BAD |
+					RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+				 ((RTE_MBUF_F_RX_L4_CKSUM_BAD |
+					RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+				 ((RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+					RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+				 ((RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+					RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1));
+
+			const __m256i cksum_mask =
+				_mm256_set1_epi32
+				(RTE_MBUF_F_RX_IP_CKSUM_MASK |
+				RTE_MBUF_F_RX_L4_CKSUM_MASK |
+				RTE_MBUF_F_RX_OUTER_L4_CKSUM_MASK |
+				RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD);
+			const __m256i vlan_mask =
+				_mm256_set1_epi32
+				(RTE_MBUF_F_RX_VLAN | RTE_MBUF_F_RX_VLAN_STRIPPED);
+
+			__m256i tmp_flags;
+			__m256i descs_flags = _mm256_and_si256(staterrs0_7, desc_flags_mask);
+			stu_len0_7 = _mm256_and_si256(stu_len0_7, desc_flags_rss_mask);
+
+			tmp_flags = _mm256_shuffle_epi8(vlan_flags, descs_flags);
+			mbuf_flags = _mm256_and_si256(tmp_flags, vlan_mask);
+
+			descs_flags = _mm256_srli_epi32(descs_flags, 10);
+			tmp_flags = _mm256_shuffle_epi8(cksum_flags, descs_flags);
+			tmp_flags = _mm256_slli_epi32(tmp_flags, 1);
+			tmp_flags = _mm256_and_si256(tmp_flags, cksum_mask);
+			mbuf_flags = _mm256_or_si256(mbuf_flags, tmp_flags);
+
+			descs_flags = _mm256_srli_epi32(stu_len0_7, 27);
+			tmp_flags = _mm256_shuffle_epi8(rss_flags, descs_flags);
+			mbuf_flags = _mm256_or_si256(mbuf_flags, tmp_flags);
+
+#ifndef RTE_LIBRTE_SXE2_16BYTE_RX_DESC
+
+			if (rxq->fnav_enable) {
+				__m256i fnav_vld0_3, fnav_vld4_7;
+				__m256i fnav_vld0_7;
+				__m256i v_zeros, v_ffff, v_u32_one;
+				const __m256i fdir_flags =
+					_mm256_set1_epi32
+					(RTE_MBUF_F_RX_FDIR | RTE_MBUF_F_RX_FDIR_ID);
+				fnav_vld0_3 = _mm256_unpacklo_epi32(descs2_3, descs0_1);
+				fnav_vld4_7 = _mm256_unpacklo_epi32(descs6_7, descs4_5);
+
+				fnav_vld0_7 = _mm256_unpacklo_epi64(fnav_vld4_7, fnav_vld0_3);
+
+				fnav_vld0_7 = _mm256_slli_epi32(fnav_vld0_7, 26);
+				fnav_vld0_7 = _mm256_srli_epi32(fnav_vld0_7, 31);
+
+				v_zeros = _mm256_setzero_si256();
+				v_ffff = _mm256_cmpeq_epi32(v_zeros, v_zeros);
+				v_u32_one = _mm256_srli_epi32(v_ffff, 31);
+
+				tmp_flags = _mm256_cmpeq_epi32(fnav_vld0_7, v_u32_one);
+
+				tmp_flags = _mm256_and_si256(tmp_flags, fdir_flags);
+
+				mbuf_flags = _mm256_or_si256(mbuf_flags, tmp_flags);
+
+				rx_pkts[i + 0]->hash.fdir.hi = desc[0].wb.fd_filter_id;
+				rx_pkts[i + 1]->hash.fdir.hi = desc[1].wb.fd_filter_id;
+				rx_pkts[i + 2]->hash.fdir.hi = desc[2].wb.fd_filter_id;
+				rx_pkts[i + 3]->hash.fdir.hi = desc[3].wb.fd_filter_id;
+				rx_pkts[i + 4]->hash.fdir.hi = desc[4].wb.fd_filter_id;
+				rx_pkts[i + 5]->hash.fdir.hi = desc[5].wb.fd_filter_id;
+				rx_pkts[i + 6]->hash.fdir.hi = desc[6].wb.fd_filter_id;
+				rx_pkts[i + 7]->hash.fdir.hi = desc[7].wb.fd_filter_id;
+			}
+#endif
+		}
+
+		RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, ol_flags) !=
+				 offsetof(struct rte_mbuf, rearm_data) + 8);
+		RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, rx_descriptor_fields1) !=
+				 offsetof(struct rte_mbuf, rearm_data) + 16);
+		RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, rearm_data) !=
+				 RTE_ALIGN(offsetof(struct rte_mbuf, rearm_data), 16));
+
+		__m256i rearm_arr[8];
+
+		rearm_arr[6] = _mm256_blend_epi32(mbuf_init, _mm256_slli_si256(mbuf_flags, 8), 4);
+		rearm_arr[4] = _mm256_blend_epi32(mbuf_init, _mm256_slli_si256(mbuf_flags, 4), 4);
+		rearm_arr[2] = _mm256_blend_epi32(mbuf_init, mbuf_flags, 4);
+		rearm_arr[0] = _mm256_blend_epi32(mbuf_init, _mm256_srli_si256(mbuf_flags, 4), 4);
+
+		rearm_arr[6] = _mm256_permute2f128_si256(rearm_arr[6], mbufs6_7, 0x20);
+		rearm_arr[4] = _mm256_permute2f128_si256(rearm_arr[4], mbufs4_5, 0x20);
+		rearm_arr[2] = _mm256_permute2f128_si256(rearm_arr[2], mbufs2_3, 0x20);
+		rearm_arr[0] = _mm256_permute2f128_si256(rearm_arr[0], mbufs0_1, 0x20);
+
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 6]->rearm_data, rearm_arr[6]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 4]->rearm_data, rearm_arr[4]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 2]->rearm_data, rearm_arr[2]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 0]->rearm_data, rearm_arr[0]);
+
+		const __m256i tmp_mbuf_flags =
+			_mm256_castsi128_si256(_mm256_extracti128_si256(mbuf_flags, 1));
+
+		rearm_arr[7] =
+			_mm256_blend_epi32(mbuf_init, _mm256_slli_si256(tmp_mbuf_flags, 8), 4);
+		rearm_arr[5] =
+			_mm256_blend_epi32(mbuf_init, _mm256_slli_si256(tmp_mbuf_flags, 4), 4);
+		rearm_arr[3] =
+			_mm256_blend_epi32(mbuf_init, tmp_mbuf_flags, 4);
+		rearm_arr[1] =
+			_mm256_blend_epi32(mbuf_init, _mm256_srli_si256(tmp_mbuf_flags, 4), 4);
+
+		rearm_arr[7] = _mm256_blend_epi32(rearm_arr[7], mbufs6_7, 0XF0);
+		rearm_arr[5] = _mm256_blend_epi32(rearm_arr[5], mbufs4_5, 0XF0);
+		rearm_arr[3] = _mm256_blend_epi32(rearm_arr[3], mbufs2_3, 0XF0);
+		rearm_arr[1] = _mm256_blend_epi32(rearm_arr[1], mbufs0_1, 0XF0);
+
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 7]->rearm_data, rearm_arr[7]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 5]->rearm_data, rearm_arr[5]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 3]->rearm_data, rearm_arr[3]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 1]->rearm_data, rearm_arr[1]);
+
+		if (umbcast_flags != NULL) {
+			const __m256i umbcast_mask =
+					_mm256_set1_epi32(SXE2_RX_DESC_STATUS_UMBCAST_MASK);
+			__m256i umbcast_bits_256 = _mm256_and_si256(staterrs0_7,
+								    umbcast_mask);
+
+			umbcast_bits_256 = _mm256_srli_epi32(umbcast_bits_256, 24);
+
+			__m128i umbcast_bits_128 = _mm_packs_epi32
+							(_mm256_castsi256_si128(umbcast_bits_256),
+							 _mm256_extractf128_si256
+								(umbcast_bits_256, 1));
+
+			umbcast_bits_128 = _mm_shuffle_epi8(umbcast_bits_128, eop_shuf_mask);
+
+			*(uint64_t *)umbcast_flags = _mm_cvtsi128_si64(umbcast_bits_128);
+			umbcast_flags += SXE2_RX_NUM_PER_LOOP_AVX;
+		}
+
+		if (split_rxe_flags != NULL) {
+			const __m256i eop_rxe_mask = _mm256_set1_epi32
+							(SXE2_RX_DESC_STATUS_EOP_MASK |
+							 SXE2_RX_DESC_ERROR_RXE_MASK |
+							 SXE2_RX_DESC_ERROR_OVERSIZE_MASK);
+
+			const __m128i eop_mask_128 = _mm_set1_epi16(SXE2_RX_DESC_STATUS_EOP_MASK);
+			const __m128i rxe_mask_128 = _mm_set1_epi16(SXE2_RX_DESC_ERROR_RXE_MASK |
+					SXE2_RX_DESC_ERROR_OVERSIZE_MASK);
+
+			const __m256i tmp_stats = _mm256_and_si256(staterrs0_7, eop_rxe_mask);
+
+			const __m128i eop_rxe_bits = _mm_packs_epi32
+							(_mm256_castsi256_si128(tmp_stats),
+							 _mm256_extractf128_si256(tmp_stats, 1));
+
+			__m128i not_eop_bits = _mm_andnot_si128(eop_rxe_bits, eop_mask_128);
+
+			not_eop_bits = _mm_or_si128
+					(not_eop_bits,
+					 _mm_srli_epi16
+					(_mm_and_si128(eop_rxe_bits, rxe_mask_128),
+					7));
+
+			not_eop_bits = _mm_shuffle_epi8(not_eop_bits, eop_shuf_mask);
+
+			*(uint64_t *)split_rxe_flags = _mm_cvtsi128_si64(not_eop_bits);
+			split_rxe_flags += SXE2_RX_NUM_PER_LOOP_AVX;
+		}
+
+		staterrs0_7 = _mm256_and_si256(staterrs0_7, dd_mask);
+
+		staterrs0_7 = _mm256_packs_epi32(staterrs0_7, _mm256_setzero_si256());
+		bit_num = rte_popcount64
+				(_mm_cvtsi128_si64(_mm256_extracti128_si256(staterrs0_7, 1)));
+		bit_num += rte_popcount64
+				(_mm_cvtsi128_si64(_mm256_castsi256_si128(staterrs0_7)));
+
+		done_num += bit_num;
+
+		if (bit_num != SXE2_RX_NUM_PER_LOOP_AVX)
+			break;
+	}
+
+	rxq->processing_idx += done_num;
+	rxq->processing_idx &= (rxq->ring_depth - 1);
+	if ((1 == (rxq->processing_idx & 1)) && done_num > 1) {
+		rxq->processing_idx--;
+		done_num--;
+	}
+	rxq->realloc_num     += done_num;
+
+l_end:
+	PMD_LOG_DEBUG(RX, "port_id=%u queue_id=%u last_id=%u recv_pkts=%d",
+		rxq->port_id, rxq->queue_id, rxq->processing_idx, done_num);
+	return done_num;
+}
+
+static __rte_always_inline uint16_t
+sxe2_rx_pkts_scattered_batch_vec_avx2(struct sxe2_rx_queue *rxq, struct rte_mbuf **rx_pkts,
+		uint16_t nb_pkts, bool do_offload)
+{
+	const uint64_t *split_rxe_flags64;
+	uint8_t split_rxe_flags[SXE2_RX_PKTS_BURST_BATCH_NUM_VEC] = {0};
+	uint8_t umbcast_flags[SXE2_RX_PKTS_BURST_BATCH_NUM_VEC] = {0};
+	uint16_t rx_done_num;
+	uint16_t rx_pkt_done_num;
+
+	rx_pkt_done_num = 0;
+
+	if (rxq->vsi->adapter->devargs.sw_stats_en) {
+		rx_done_num = sxe2_rx_pkts_common_vec_avx2(rxq, rx_pkts, nb_pkts,
+				split_rxe_flags, umbcast_flags, do_offload);
+	} else {
+		rx_done_num = sxe2_rx_pkts_common_vec_avx2(rxq, rx_pkts, nb_pkts,
+				split_rxe_flags, NULL, do_offload);
+	}
+	if (rx_done_num == 0)
+		goto l_end;
+
+	if (!rxq->vsi->adapter->devargs.sw_stats_en) {
+		split_rxe_flags64 = (uint64_t *)split_rxe_flags;
+
+		if (rxq->pkt_first_seg == NULL &&
+				split_rxe_flags64[0] == 0 && split_rxe_flags64[1] == 0 &&
+				split_rxe_flags64[2] == 0 && split_rxe_flags64[3] == 0) {
+			rx_pkt_done_num = rx_done_num;
+			goto l_end;
+		}
+
+		if (rxq->pkt_first_seg == NULL) {
+			while (rx_pkt_done_num < rx_done_num &&
+					split_rxe_flags[rx_pkt_done_num] == 0)
+				rx_pkt_done_num++;
+
+			if (rx_pkt_done_num == rx_done_num)
+				goto l_end;
+
+			rxq->pkt_first_seg = rx_pkts[rx_pkt_done_num];
+		}
+	}
+
+	rx_pkt_done_num += sxe2_rx_pkts_refactor(rxq, &rx_pkts[rx_pkt_done_num],
+			rx_done_num - rx_pkt_done_num, &split_rxe_flags[rx_pkt_done_num],
+			&umbcast_flags[rx_pkt_done_num]);
+
+l_end:
+	return rx_pkt_done_num;
+}
+
+static __rte_always_inline uint16_t
+sxe2_rx_pkts_scattered_common_vec_avx2(struct sxe2_rx_queue *rxq, struct rte_mbuf **rx_pkts,
+		uint16_t nb_pkts, bool do_offload)
+{
+	uint16_t done_num = 0;
+	uint16_t once_num;
+
+	while (nb_pkts > SXE2_RX_PKTS_BURST_BATCH_NUM) {
+		once_num =
+		sxe2_rx_pkts_scattered_batch_vec_avx2(rxq,
+						      rx_pkts + done_num,
+						      SXE2_RX_PKTS_BURST_BATCH_NUM,
+						      do_offload);
+		done_num += once_num;
+		nb_pkts -= once_num;
+		if (once_num < SXE2_RX_PKTS_BURST_BATCH_NUM)
+			goto l_end;
+	}
+
+	done_num += sxe2_rx_pkts_scattered_batch_vec_avx2(rxq,
+			rx_pkts + done_num, nb_pkts, do_offload);
+l_end:
+	return done_num;
+}
+
+uint16_t sxe2_rx_pkts_scattered_vec_avx2(void *rx_queue,
+		struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
+{
+	return sxe2_rx_pkts_scattered_common_vec_avx2(rx_queue,
+			rx_pkts, nb_pkts, false);
+}
+
+uint16_t sxe2_rx_pkts_scattered_vec_avx2_offload(void *rx_queue,
+		struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
+{
+	return sxe2_rx_pkts_scattered_common_vec_avx2(rx_queue,
+			rx_pkts, nb_pkts, true);
+}
-- 
2.52.0


^ permalink raw reply related

* [PATCH v10 01/20] net/sxe2: support AVX512 vectorized path for Rx and Tx
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260606010726.2256170-1-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

Add AVX512 vector data path for Rx and Tx burst functions.
The decision to use AVX512 is based on:
1. CPU hardware flags (AVX512F, AVX512BW).
2. Compiler support (CC_AVX512_SUPPORT).
3. Max SIMD bitwidth configuration.

Performance shows approximately X% improvement in small packet
forwarding scenarios.

Signed-off-by: Jie Liu <liujie5@linkdatatechnology.com>
---
 drivers/net/sxe2/meson.build            |  24 +
 drivers/net/sxe2/sxe2_drv_cmd.h         |  80 +--
 drivers/net/sxe2/sxe2_ethdev.c          |   2 +-
 drivers/net/sxe2/sxe2_txrx.c            |  92 ++-
 drivers/net/sxe2/sxe2_txrx_vec.c        |  46 +-
 drivers/net/sxe2/sxe2_txrx_vec.h        |  18 +-
 drivers/net/sxe2/sxe2_txrx_vec_avx512.c | 897 ++++++++++++++++++++++++
 7 files changed, 1099 insertions(+), 60 deletions(-)
 create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_avx512.c

diff --git a/drivers/net/sxe2/meson.build b/drivers/net/sxe2/meson.build
index 6b2eb75b0e..7bd0d8120c 100644
--- a/drivers/net/sxe2/meson.build
+++ b/drivers/net/sxe2/meson.build
@@ -15,6 +15,30 @@ includes += include_directories('../../common/sxe2')
 
 if arch_subdir == 'x86'
         sources += files('sxe2_txrx_vec_sse.c')
+
+        sxe2_avx512_cpu_support =(
+                cc.get_define('__AVX512F__', args: machine_args) != '' and
+                cc.get_define('__AVX512BW__', args: machine_args) != '')
+
+        sxe2_avx512_cc_support = (
+                not machine_args.contains('-mno-avx512f') and
+                cc.has_argument('-mavx512f') and
+                cc.has_argument('-mavx512bw'))
+
+        if sxe2_avx512_cpu_support == true or sxe2_avx512_cc_support == true
+                cflags += ['-DCC_AVX512_SUPPORT']
+                avx512_args = [cflags, '-mavx512f', '-mavx512bw']
+                if cc.has_argument('-march=skylake-avx512')
+                        avx512_args += '-march=skylake-avx512'
+                endif
+                sxe2_avx512_lib = static_library('sxe2_avx512_lib', 'sxe2_txrx_vec_avx512.c',
+                        dependencies: [static_rte_ethdev,
+                        static_rte_kvargs, static_rte_hash,
+                        static_rte_security, static_rte_cryptodev, static_rte_bus_pci],
+                        include_directories: includes,
+                        c_args: avx512_args)
+                objs += sxe2_avx512_lib.extract_objects('sxe2_txrx_vec_avx512.c')
+        endif
 endif
 
 sources += files(
diff --git a/drivers/net/sxe2/sxe2_drv_cmd.h b/drivers/net/sxe2/sxe2_drv_cmd.h
index bba6476c2e..ccc9c20ef4 100644
--- a/drivers/net/sxe2/sxe2_drv_cmd.h
+++ b/drivers/net/sxe2/sxe2_drv_cmd.h
@@ -67,20 +67,20 @@ enum sxe2_dev_type {
 	SXE2_DEV_T_MAX,
 };
 
-struct sxe2_drv_queue_caps {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_queue_caps {
 	uint16_t queues_cnt;
 	uint16_t base_idx_in_pf;
-};
+} __rte_packed_end;
 
-struct sxe2_drv_msix_caps {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_msix_caps {
 	uint16_t msix_vectors_cnt;
 	uint16_t base_idx_in_func;
-};
+} __rte_packed_end;
 
-struct sxe2_drv_rss_hash_caps {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_rss_hash_caps {
 	uint16_t hash_key_size;
 	uint16_t lut_key_size;
-};
+} __rte_packed_end;
 
 enum sxe2_vf_vsi_valid {
 	SXE2_VF_VSI_BOTH = 0,
@@ -89,18 +89,18 @@ enum sxe2_vf_vsi_valid {
 	SXE2_VF_VSI_MAX,
 };
 
-struct sxe2_drv_vsi_caps {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vsi_caps {
 	uint16_t func_id;
 	uint16_t dpdk_vsi_id;
 	uint16_t kernel_vsi_id;
 	uint16_t vsi_type;
-};
+} __rte_packed_end;
 
-struct sxe2_drv_representor_caps {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_representor_caps {
 	uint16_t cnt_repr_vf;
 	uint8_t rsv[2];
 	struct sxe2_drv_vsi_caps repr_vf_id[256];
-};
+} __rte_packed_end;
 
 enum sxe2_phys_port_name_type {
 	SXE2_PHYS_PORT_NAME_TYPE_NOTSET = 0,
@@ -111,25 +111,25 @@ enum sxe2_phys_port_name_type {
 	SXE2_PHYS_PORT_NAME_TYPE_UNKNOWN,
 };
 
-struct sxe2_switchdev_mode_info {
+struct __rte_aligned(4) __rte_packed_begin sxe2_switchdev_mode_info {
 	uint8_t pf_id;
 	uint8_t is_switchdev;
 	uint8_t rsv[2];
-};
+} __rte_packed_end;
 
-struct sxe2_switchdev_cpvsi_info {
+struct __rte_aligned(4) __rte_packed_begin sxe2_switchdev_cpvsi_info {
 	uint16_t cp_vsi_id;
 	uint8_t rsv[2];
-};
+} __rte_packed_end;
 
-struct sxe2_txsch_caps {
+struct __rte_aligned(4) __rte_packed_begin sxe2_txsch_caps {
 	uint8_t layer_cap;
 	uint8_t tm_mid_node_num;
 	uint8_t prio_num;
 	uint8_t rev;
-};
+} __rte_packed_end;
 
-struct sxe2_drv_dev_caps_resp {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_dev_caps_resp {
 	struct sxe2_drv_queue_caps queue_caps;
 	struct sxe2_drv_msix_caps msix_caps;
 	struct sxe2_drv_rss_hash_caps rss_hash_caps;
@@ -141,24 +141,24 @@ struct sxe2_drv_dev_caps_resp {
 	uint8_t dev_type;
 	uint8_t rev;
 	uint32_t cap_flags;
-};
+} __rte_packed_end;
 
-struct sxe2_drv_dev_info_resp {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_dev_info_resp {
 	uint64_t dsn;
 	uint16_t vsi_id;
 	uint8_t rsv[2];
 	uint8_t mac_addr[SXE2_ETH_ALEN];
 	uint8_t rsv2[2];
-};
+} __rte_packed_end;
 
-struct sxe2_drv_dev_fw_info_resp {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_dev_fw_info_resp {
 	uint8_t main_version_id;
 	uint8_t sub_version_id;
 	uint8_t fix_version_id;
 	uint8_t build_id;
-};
+} __rte_packed_end;
 
-struct sxe2_drv_rxq_ctxt {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_rxq_ctxt {
 	uint64_t dma_addr;
 	uint32_t max_lro_size;
 	uint32_t split_type_mask;
@@ -170,62 +170,62 @@ struct sxe2_drv_rxq_ctxt {
 	uint8_t keep_crc_en;
 	uint8_t split_en;
 	uint8_t desc_size;
-};
+} __rte_packed_end;
 
-struct sxe2_drv_rxq_cfg_req {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_rxq_cfg_req {
 	uint16_t q_cnt;
 	uint16_t vsi_id;
 	uint16_t max_frame_size;
 	uint8_t rsv[2];
 	struct sxe2_drv_rxq_ctxt cfg[];
-};
+} __rte_packed_end;
 
-struct sxe2_drv_txq_ctxt {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_txq_ctxt {
 	uint64_t dma_addr;
 	uint32_t sched_mode;
 	uint16_t queue_id;
 	uint16_t depth;
 	uint16_t vsi_id;
 	uint8_t rsv[2];
-};
+} __rte_packed_end;
 
-struct sxe2_drv_txq_cfg_req {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_txq_cfg_req {
 	uint16_t q_cnt;
 	uint16_t vsi_id;
 	struct sxe2_drv_txq_ctxt cfg[];
-};
+} __rte_packed_end;
 
-struct sxe2_drv_q_switch_req {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_q_switch_req {
 	uint16_t q_idx;
 	uint16_t vsi_id;
 	uint8_t is_enable;
 	uint8_t sched_mode;
 	uint8_t rsv[2];
-};
+} __rte_packed_end;
 
-struct sxe2_drv_vsi_create_req_resp {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vsi_create_req_resp {
 	uint16_t vsi_id;
 	uint16_t vsi_type;
 	struct sxe2_drv_queue_caps used_queues;
 	struct sxe2_drv_msix_caps used_msix;
-};
+} __rte_packed_end;
 
-struct sxe2_drv_vsi_free_req {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vsi_free_req {
 	uint16_t vsi_id;
 	uint8_t rsv[2];
-};
+} __rte_packed_end;
 
-struct sxe2_drv_vsi_info_get_req {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vsi_info_get_req {
 	uint16_t vsi_id;
 	uint8_t rsv[2];
-};
+} __rte_packed_end;
 
-struct sxe2_drv_vsi_info_get_resp {
+struct __rte_aligned(4) __rte_packed_begin sxe2_drv_vsi_info_get_resp {
 	uint16_t vsi_id;
 	uint16_t vsi_type;
 	struct sxe2_drv_queue_caps used_queues;
 	struct sxe2_drv_msix_caps used_msix;
-};
+} __rte_packed_end;
 
 enum sxe2_drv_cmd_module {
 	SXE2_DRV_CMD_MODULE_HANDSHAKE = 0,
diff --git a/drivers/net/sxe2/sxe2_ethdev.c b/drivers/net/sxe2/sxe2_ethdev.c
index 8d66e5d8c5..e0f7002138 100644
--- a/drivers/net/sxe2/sxe2_ethdev.c
+++ b/drivers/net/sxe2/sxe2_ethdev.c
@@ -891,7 +891,7 @@ static int32_t sxe2_eth_pmd_probe_pf(struct sxe2_common_device *cdev,
 static int32_t sxe2_parse_eth_devargs(struct rte_device *dev,
 			  struct rte_eth_devargs *eth_da)
 {
-	int ret = 0;
+	int32_t ret = 0;
 
 	if (dev->devargs == NULL)
 		return 0;
diff --git a/drivers/net/sxe2/sxe2_txrx.c b/drivers/net/sxe2/sxe2_txrx.c
index 8d17535301..aa1c474088 100644
--- a/drivers/net/sxe2/sxe2_txrx.c
+++ b/drivers/net/sxe2/sxe2_txrx.c
@@ -157,6 +157,19 @@ void sxe2_tx_mode_func_set(struct rte_eth_dev *dev)
 		if (ret == 0 &&
 		    rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128) {
 			tx_mode_flags = vec_flags;
+#ifdef RTE_ARCH_X86
+			if ((rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_512) &&
+			    (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512F) == 1) &&
+			    (rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512BW) == 1)) {
+#ifdef CC_AVX512_SUPPORT
+				tx_mode_flags |= SXE2_TX_MODE_VEC_AVX512;
+#else
+				PMD_LOG_INFO(TX, "AVX512 is not supported in build env.");
+#endif
+			}
+			if ((tx_mode_flags & SXE2_TX_MODE_VEC_SET_MASK) == 0)
+				tx_mode_flags |= SXE2_TX_MODE_VEC_SSE;
+#endif
 			if (tx_mode_flags & SXE2_TX_MODE_VEC_SET_MASK) {
 				ret = sxe2_tx_queues_vec_prepare(dev);
 				if (ret != 0)
@@ -172,14 +185,25 @@ void sxe2_tx_mode_func_set(struct rte_eth_dev *dev)
 		tx_mode_flags = adapter->q_ctxt.tx_mode_flags;
 	}
 
-#ifdef RTE_ARCH_X86
 	if (tx_mode_flags & SXE2_TX_MODE_VEC_SET_MASK) {
-		if (tx_mode_flags & SXE2_TX_MODE_VEC_OFFLOAD) {
-			dev->tx_pkt_prepare = sxe2_tx_pkts_prepare;
-			dev->tx_pkt_burst = sxe2_tx_pkts_vec_sse;
+		dev->tx_pkt_prepare = NULL;
+#ifdef RTE_ARCH_X86
+		if (tx_mode_flags & SXE2_TX_MODE_VEC_AVX512) {
+#ifdef CC_AVX512_SUPPORT
+			if (tx_mode_flags & SXE2_TX_MODE_VEC_OFFLOAD) {
+				dev->tx_pkt_prepare = sxe2_tx_pkts_prepare;
+				dev->tx_pkt_burst = sxe2_tx_pkts_vec_avx512;
+			} else {
+				dev->tx_pkt_burst = sxe2_tx_pkts_vec_avx512_simple;
+			}
+#endif
 		} else {
-			dev->tx_pkt_prepare = NULL;
-			dev->tx_pkt_burst = sxe2_tx_pkts_vec_sse_simple;
+			if (tx_mode_flags & SXE2_TX_MODE_VEC_OFFLOAD) {
+				dev->tx_pkt_prepare = sxe2_tx_pkts_prepare;
+				dev->tx_pkt_burst = sxe2_tx_pkts_vec_sse;
+			} else {
+				dev->tx_pkt_burst = sxe2_tx_pkts_vec_sse_simple;
+			}
 		}
 	} else {
 #endif
@@ -201,8 +225,16 @@ static const struct {
 } sxe2_tx_burst_infos[] = {
 	{ sxe2_tx_pkts,   "Scalar" },
 #ifdef RTE_ARCH_X86
-	{ sxe2_tx_pkts_vec_sse,        "Vector SSE" },
-	{ sxe2_tx_pkts_vec_sse_simple, "Vector SSE Simple" },
+#ifdef CC_AVX512_SUPPORT
+	{ sxe2_tx_pkts_vec_avx512,
+	      "Vector AVX512" },
+	{ sxe2_tx_pkts_vec_avx512_simple,
+	      "Vector AVX512 Simple" },
+#endif
+	{ sxe2_tx_pkts_vec_sse,
+	      "Vector SSE" },
+	{ sxe2_tx_pkts_vec_sse_simple,
+	      "Vector SSE Simple" },
 #endif
 };
 
@@ -288,6 +320,20 @@ void sxe2_rx_mode_func_set(struct rte_eth_dev *dev)
 		if (ret == 0 &&
 		    rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128) {
 			rx_mode_flags = vec_flags;
+#ifdef RTE_ARCH_X86
+			if ((rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_512) &&
+				(rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512F) == 1) &&
+				(rte_cpu_get_flag_enabled(RTE_CPUFLAG_AVX512BW) == 1)) {
+#ifdef CC_AVX512_SUPPORT
+				rx_mode_flags |= SXE2_RX_MODE_VEC_AVX512;
+#else
+				PMD_LOG_INFO(RX, "AVX512 support detected but not enabled");
+#endif
+			}
+			if ((rx_mode_flags & SXE2_RX_MODE_VEC_SET_MASK) == 0 &&
+				rte_vect_get_max_simd_bitwidth() >= RTE_VECT_SIMD_128)
+				rx_mode_flags |= SXE2_RX_MODE_VEC_SSE;
+#endif
 			if ((rx_mode_flags & SXE2_RX_MODE_VEC_SET_MASK) != 0) {
 				ret = sxe2_rx_queues_vec_prepare(dev);
 				if (ret != 0)
@@ -301,7 +347,16 @@ void sxe2_rx_mode_func_set(struct rte_eth_dev *dev)
 
 #ifdef RTE_ARCH_X86
 	if (rx_mode_flags & SXE2_RX_MODE_VEC_SET_MASK) {
-		dev->rx_pkt_burst = sxe2_rx_pkts_scattered_vec_sse_offload;
+		if (rx_mode_flags & SXE2_RX_MODE_VEC_AVX512) {
+#ifdef CC_AVX512_SUPPORT
+			if (rx_mode_flags & SXE2_RX_MODE_VEC_OFFLOAD)
+				dev->rx_pkt_burst = sxe2_rx_pkts_scattered_vec_avx512_offload;
+			else
+				dev->rx_pkt_burst = sxe2_rx_pkts_scattered_vec_avx512;
+#endif
+		} else {
+			dev->rx_pkt_burst = sxe2_rx_pkts_scattered_vec_sse_offload;
+		}
 		return;
 	}
 #endif
@@ -315,19 +370,30 @@ static const struct {
 	eth_rx_burst_t rx_burst;
 	const char *info;
 } sxe2_rx_burst_infos[] = {
-	{ sxe2_rx_pkts_scattered,          "Scalar Scattered" },
-	{ sxe2_rx_pkts_scattered_split,          "Scalar Scattered split" },
+	{ sxe2_rx_pkts_scattered,
+	      "Scalar Scattered" },
+	{ sxe2_rx_pkts_scattered_split,
+	      "Scalar Scattered split" },
 #ifdef RTE_ARCH_X86
-	{ sxe2_rx_pkts_scattered_vec_sse_offload,      "Vector SSE Scattered" },
+#ifdef CC_AVX512_SUPPORT
+	{ sxe2_rx_pkts_scattered_vec_avx512,
+	      "Vector AVX512 Scattered" },
+	{ sxe2_rx_pkts_scattered_vec_avx512_offload,
+	      "Offload Vector AVX512 Scattered" },
+#endif
+	{ sxe2_rx_pkts_scattered_vec_sse_offload,
+	      "Vector SSE Scattered" },
 #endif
 };
 
 int32_t sxe2_rx_burst_mode_get(struct rte_eth_dev *dev,
-			__rte_unused uint16_t queue_id, struct rte_eth_burst_mode *mode)
+			       __rte_unused uint16_t queue_id,
+			       struct rte_eth_burst_mode *mode)
 {
 	eth_rx_burst_t pkt_burst = dev->rx_pkt_burst;
 	int32_t ret = -EINVAL;
 	uint32_t i, size;
+
 	size = RTE_DIM(sxe2_rx_burst_infos);
 	for (i = 0; i < size; ++i) {
 		if (pkt_burst == sxe2_rx_burst_infos[i].rx_burst) {
diff --git a/drivers/net/sxe2/sxe2_txrx_vec.c b/drivers/net/sxe2/sxe2_txrx_vec.c
index 8df4954d86..cf004f5eb2 100644
--- a/drivers/net/sxe2/sxe2_txrx_vec.c
+++ b/drivers/net/sxe2/sxe2_txrx_vec.c
@@ -165,16 +165,54 @@ static void sxe2_tx_queue_mbufs_release_vec(struct sxe2_tx_queue *txq)
 		return;
 	}
 	i = txq->next_dd - (txq->rs_thresh - 1);
-	buffer = txq->buffer_ring;
-	if (txq->next_use < i) {
-		for ( ; i < txq->ring_depth; ++i) {
+#ifdef CC_AVX512_SUPPORT
+	struct rte_eth_dev *dev;
+	struct sxe2_tx_buffer_vec *buffer_vec;
+
+	dev = &rte_eth_devices[txq->port_id];
+
+	if (dev->tx_pkt_burst == sxe2_tx_pkts_vec_avx512 ||
+		dev->tx_pkt_burst == sxe2_tx_pkts_vec_avx512_simple) {
+		buffer_vec = (struct sxe2_tx_buffer_vec *)txq->buffer_ring;
+
+		if (txq->next_use < i) {
+			for ( ; i < txq->ring_depth; ++i) {
+				if (buffer_vec[i].mbuf != NULL) {
+					rte_pktmbuf_free_seg(buffer_vec[i].mbuf);
+					buffer_vec[i].mbuf = NULL;
+				}
+			}
+			i = 0;
+		}
+		for ( ; i < txq->next_use; ++i) {
+			if (buffer_vec[i].mbuf != NULL) {
+				rte_pktmbuf_free_seg(buffer_vec[i].mbuf);
+				buffer_vec[i].mbuf = NULL;
+			}
+		}
+	} else {
+#endif
+		buffer = txq->buffer_ring;
+		buffer = txq->buffer_ring;
+		if (txq->next_use < i) {
+			for ( ; i < txq->ring_depth; ++i) {
+				if (buffer[i].mbuf != NULL) {
+					rte_pktmbuf_free_seg(buffer[i].mbuf);
+					buffer[i].mbuf = NULL;
+				}
+			}
+			i = 0;
+		}
+		for (; i < txq->next_use; ++i) {
 			if (buffer[i].mbuf != NULL) {
 				rte_pktmbuf_free_seg(buffer[i].mbuf);
 				buffer[i].mbuf = NULL;
 			}
 		}
-		i = 0;
+#ifdef CC_AVX512_SUPPORT
 	}
+#endif
+
 	for (; i < txq->next_use; ++i) {
 		if (buffer[i].mbuf != NULL) {
 			rte_pktmbuf_free_seg(buffer[i].mbuf);
diff --git a/drivers/net/sxe2/sxe2_txrx_vec.h b/drivers/net/sxe2/sxe2_txrx_vec.h
index 04ff4d96a5..af7c8d12b2 100644
--- a/drivers/net/sxe2/sxe2_txrx_vec.h
+++ b/drivers/net/sxe2/sxe2_txrx_vec.h
@@ -11,15 +11,19 @@
 #define SXE2_RX_MODE_VEC_SIMPLE    RTE_BIT32(0)
 #define SXE2_RX_MODE_VEC_OFFLOAD   RTE_BIT32(1)
 #define SXE2_RX_MODE_VEC_SSE       RTE_BIT32(2)
+#define SXE2_RX_MODE_VEC_AVX512    RTE_BIT32(4)
 #define SXE2_RX_MODE_BATCH_ALLOC   RTE_BIT32(10)
 #define SXE2_RX_MODE_VEC_SET_MASK	(SXE2_RX_MODE_VEC_SIMPLE | \
-			SXE2_RX_MODE_VEC_OFFLOAD | SXE2_RX_MODE_VEC_SSE)
+			SXE2_RX_MODE_VEC_OFFLOAD | SXE2_RX_MODE_VEC_SSE | \
+			SXE2_RX_MODE_VEC_AVX512)
 #define SXE2_TX_MODE_VEC_SIMPLE   RTE_BIT32(0)
 #define SXE2_TX_MODE_VEC_OFFLOAD  RTE_BIT32(1)
 #define SXE2_TX_MODE_VEC_SSE      RTE_BIT32(2)
+#define SXE2_TX_MODE_VEC_AVX512   RTE_BIT32(4)
 #define SXE2_TX_MODE_SIMPLE_BATCH RTE_BIT32(10)
 #define SXE2_TX_MODE_VEC_SET_MASK	(SXE2_TX_MODE_VEC_SIMPLE | \
-			SXE2_TX_MODE_VEC_OFFLOAD | SXE2_TX_MODE_VEC_SSE)
+			SXE2_TX_MODE_VEC_OFFLOAD | SXE2_TX_MODE_VEC_SSE | \
+			SXE2_TX_MODE_VEC_AVX512)
 #define SXE2_TX_VEC_NO_SUPPORT_OFFLOAD (		  \
 			RTE_ETH_TX_OFFLOAD_MULTI_SEGS |		  \
 			RTE_ETH_TX_OFFLOAD_QINQ_INSERT |	  \
@@ -54,6 +58,16 @@ uint16_t sxe2_tx_pkts_vec_sse(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_
 uint16_t sxe2_tx_pkts_vec_sse_simple(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
 uint16_t sxe2_rx_pkts_scattered_vec_sse_offload(void *rx_queue,
 		struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
+uint16_t sxe2_tx_pkts_vec_avx512_simple(void *tx_queue,
+		struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
+uint16_t sxe2_tx_pkts_vec_avx512(void *tx_queue,
+		struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
+uint16_t sxe2_tx_pkts_vec_avx512_ctx_offload(void *tx_queue,
+		struct rte_mbuf **tx_pkts, uint16_t nb_pkts);
+uint16_t sxe2_rx_pkts_scattered_vec_avx512(void *rx_queue,
+		struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
+uint16_t sxe2_rx_pkts_scattered_vec_avx512_offload(void *rx_queue,
+		struct rte_mbuf **rx_pkts, uint16_t nb_pkts);
 #endif
 int32_t __rte_cold sxe2_tx_vec_support_check(struct rte_eth_dev *dev, uint32_t *vec_flags);
 int32_t __rte_cold sxe2_tx_queues_vec_prepare(struct rte_eth_dev *dev);
diff --git a/drivers/net/sxe2/sxe2_txrx_vec_avx512.c b/drivers/net/sxe2/sxe2_txrx_vec_avx512.c
new file mode 100644
index 0000000000..2aec8037dd
--- /dev/null
+++ b/drivers/net/sxe2/sxe2_txrx_vec_avx512.c
@@ -0,0 +1,897 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright (C), 2025, Wuxi Stars Micro System Technologies Co., Ltd.
+ */
+
+#ifndef SXE2_TEST
+#include <rte_vect.h>
+
+#include "sxe2_ethdev.h"
+#include "sxe2_common_log.h"
+#include "sxe2_queue.h"
+#include "sxe2_txrx_vec.h"
+#include "sxe2_txrx_vec_common.h"
+#include "sxe2_vsi.h"
+
+static __rte_always_inline int32_t sxe2_tx_bufs_free_vec_avx512(struct sxe2_tx_queue *txq)
+{
+	struct sxe2_tx_buffer_vec *buffer;
+	struct rte_mbuf *mbuf;
+	struct rte_mbuf *mbuf_free_arr[SXE2_TX_FREE_BUFFER_SIZE_MAX_VEC];
+	struct rte_mempool *mp;
+	struct rte_mempool_cache *cache;
+	void **cache_objs;
+	uint32_t copied;
+	uint32_t i;
+	int32_t ret;
+	uint16_t rs_thresh;
+	uint16_t free_num;
+
+	if (rte_cpu_to_le_64(SXE2_TX_DESC_DTYPE_DESC_DONE) !=
+		(txq->desc_ring[txq->next_dd].wb.dd &
+			rte_cpu_to_le_64(SXE2_TX_DESC_DTYPE_MASK))) {
+		ret = 0;
+		goto l_end;
+	}
+
+	rs_thresh = txq->rs_thresh;
+
+	buffer = (struct sxe2_tx_buffer_vec *)txq->buffer_ring;
+	buffer += txq->next_dd - (rs_thresh - 1);
+
+	if ((txq->offloads & RTE_ETH_TX_OFFLOAD_MBUF_FAST_FREE) &&
+			(rs_thresh & 31) == 0) {
+		mp = buffer[0].mbuf->pool;
+		cache = rte_mempool_default_cache(mp, rte_lcore_id());
+
+		if (cache == NULL || cache->len)
+			goto normal;
+
+		if (rs_thresh > RTE_MEMPOOL_CACHE_MAX_SIZE) {
+			(void)rte_mempool_ops_enqueue_bulk(mp, (void *)buffer, rs_thresh);
+			goto done;
+		}
+		cache_objs = &cache->objs[cache->len];
+
+		copied = 0;
+		while (copied < rs_thresh) {
+			const __m512i objs0 = _mm512_loadu_si512(&buffer[copied]);
+			const __m512i objs1 = _mm512_loadu_si512(&buffer[copied + 8]);
+			const __m512i objs2 = _mm512_loadu_si512(&buffer[copied + 16]);
+			const __m512i objs3 = _mm512_loadu_si512(&buffer[copied + 24]);
+
+			_mm512_storeu_si512(&cache_objs[copied], objs0);
+			_mm512_storeu_si512(&cache_objs[copied + 8], objs1);
+			_mm512_storeu_si512(&cache_objs[copied + 16], objs2);
+			_mm512_storeu_si512(&cache_objs[copied + 24], objs3);
+			copied += 32;
+		}
+		cache->len += rs_thresh;
+
+		if (cache->len >= cache->flushthresh) {
+			(void)rte_mempool_ops_enqueue_bulk(mp,
+					&cache->objs[cache->size], cache->len - cache->size);
+			cache->len = cache->size;
+		}
+		goto done;
+	}
+
+normal:
+	mbuf = rte_pktmbuf_prefree_seg(buffer[0].mbuf);
+
+	if (likely(mbuf)) {
+		mbuf_free_arr[0] = mbuf;
+		free_num = 1;
+
+		for (i = 1; i < rs_thresh; ++i) {
+			mbuf = rte_pktmbuf_prefree_seg(buffer[i].mbuf);
+
+			if (likely(mbuf)) {
+				if (likely(mbuf->pool == mbuf_free_arr[0]->pool)) {
+					mbuf_free_arr[free_num] = mbuf;
+					free_num++;
+				} else {
+					rte_mempool_put_bulk(mbuf_free_arr[0]->pool,
+						(void *)mbuf_free_arr, free_num);
+
+				mbuf_free_arr[0] = mbuf;
+				free_num = 1;
+			}
+			}
+		}
+
+		rte_mempool_put_bulk(mbuf_free_arr[0]->pool,
+						(void *)mbuf_free_arr, free_num);
+	} else {
+		for (i = 1; i < rs_thresh; ++i) {
+			mbuf = rte_pktmbuf_prefree_seg(buffer[i].mbuf);
+			if (mbuf != NULL)
+				rte_mempool_put(mbuf->pool, mbuf);
+		}
+	}
+
+done:
+	txq->desc_free_num += txq->rs_thresh;
+	txq->next_dd       += txq->rs_thresh;
+	if (txq->next_dd >= txq->ring_depth)
+		txq->next_dd = txq->rs_thresh - 1;
+	ret = rs_thresh;
+
+l_end:
+	return ret;
+}
+
+static __rte_always_inline void
+sxe2_tx_desc_fill_one_avx512(volatile union sxe2_tx_data_desc *desc, struct rte_mbuf *pkt,
+	uint64_t desc_cmd, bool with_offloads)
+{
+	__m128i data_desc;
+	uint64_t desc_qw1;
+	uint32_t desc_offset;
+
+	desc_qw1 = (SXE2_TX_DESC_DTYPE_DATA |
+				((uint64_t)desc_cmd) << SXE2_TX_DATA_DESC_CMD_SHIFT |
+				((uint64_t)pkt->data_len) << SXE2_TX_DATA_DESC_BUF_SZ_SHIFT);
+	desc_offset = SXE2_TX_DATA_DESC_MACLEN_VAL(pkt->l2_len);
+	desc_qw1 |= ((uint64_t)desc_offset) << SXE2_TX_DATA_DESC_OFFSET_SHIFT;
+	if (with_offloads)
+		sxe2_tx_desc_fill_offloads(pkt, &desc_qw1);
+
+	data_desc = _mm_set_epi64x(desc_qw1, rte_pktmbuf_iova(pkt));
+
+	_mm_store_si128(RTE_CAST_PTR(__m128i *, desc), data_desc);
+}
+
+static __rte_always_inline
+void sxe2_tx_desc_fill_avx512(volatile union sxe2_tx_data_desc *desc, struct rte_mbuf **pkts,
+	uint16_t pkts_num, uint64_t desc_cmd, bool with_offloads)
+{
+	__m512i desc_group;
+	uint64_t desc0_qw1;
+	uint64_t desc1_qw1;
+	uint64_t desc2_qw1;
+	uint64_t desc3_qw1;
+
+	const uint64_t desc_qw1_com = (SXE2_TX_DESC_DTYPE_DATA |
+					((uint64_t)desc_cmd) << SXE2_TX_DATA_DESC_CMD_SHIFT);
+	uint32_t desc_offset[4] = {0};
+
+	while (pkts_num > 3) {
+		desc3_qw1 = desc_qw1_com |
+				((uint64_t)pkts[3]->data_len) << SXE2_TX_DATA_DESC_BUF_SZ_SHIFT;
+
+		desc_offset[3] = SXE2_TX_DATA_DESC_MACLEN_VAL(pkts[3]->l2_len);
+		desc3_qw1 |= ((uint64_t)desc_offset[3]) << SXE2_TX_DATA_DESC_OFFSET_SHIFT;
+		if (with_offloads)
+			sxe2_tx_desc_fill_offloads(pkts[3], &desc3_qw1);
+
+		desc2_qw1 = desc_qw1_com |
+				((uint64_t)pkts[2]->data_len) << SXE2_TX_DATA_DESC_BUF_SZ_SHIFT;
+		desc_offset[2] = SXE2_TX_DATA_DESC_MACLEN_VAL(pkts[2]->l2_len);
+		desc2_qw1 |= ((uint64_t)desc_offset[2]) << SXE2_TX_DATA_DESC_OFFSET_SHIFT;
+		if (with_offloads)
+			sxe2_tx_desc_fill_offloads(pkts[2], &desc2_qw1);
+
+		desc1_qw1 = (desc_qw1_com |
+				((uint64_t)pkts[1]->data_len) << SXE2_TX_DATA_DESC_BUF_SZ_SHIFT);
+		desc_offset[1] = SXE2_TX_DATA_DESC_MACLEN_VAL(pkts[1]->l2_len);
+		desc1_qw1 |= ((uint64_t)desc_offset[1]) << SXE2_TX_DATA_DESC_OFFSET_SHIFT;
+		if (with_offloads)
+			sxe2_tx_desc_fill_offloads(pkts[1], &desc1_qw1);
+
+		desc0_qw1 = (desc_qw1_com |
+				((uint64_t)pkts[0]->data_len) << SXE2_TX_DATA_DESC_BUF_SZ_SHIFT);
+		desc_offset[0] = SXE2_TX_DATA_DESC_MACLEN_VAL(pkts[0]->l2_len);
+		desc0_qw1 |= ((uint64_t)desc_offset[0]) << SXE2_TX_DATA_DESC_OFFSET_SHIFT;
+		if (with_offloads)
+			sxe2_tx_desc_fill_offloads(pkts[0], &desc0_qw1);
+
+		desc_group =
+			_mm512_set_epi64(desc3_qw1, rte_pktmbuf_iova(pkts[3]),
+					 desc2_qw1, rte_pktmbuf_iova(pkts[2]),
+					 desc1_qw1, rte_pktmbuf_iova(pkts[1]),
+					 desc0_qw1, rte_pktmbuf_iova(pkts[0]));
+
+		_mm512_storeu_si512(RTE_CAST_PTR(void *, desc), desc_group);
+
+		pkts_num -= 4;
+		desc     += 4;
+		pkts     += 4;
+	}
+
+	while (pkts_num) {
+		sxe2_tx_desc_fill_one_avx512(desc, *pkts, desc_cmd, with_offloads);
+
+		pkts_num--;
+		desc++;
+		pkts++;
+	}
+}
+
+static __rte_always_inline void
+sxe2_tx_pkts_mbuf_fill_avx512(struct sxe2_tx_buffer_vec *buffer,
+	struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
+{
+	uint16_t i;
+
+	for (i = 0; i < nb_pkts; ++i)
+		buffer[i].mbuf = tx_pkts[i];
+}
+
+static __rte_always_inline uint16_t
+sxe2_tx_pkts_vec_avx512_batch(struct sxe2_tx_queue *txq, struct rte_mbuf **tx_pkts,
+	uint16_t nb_pkts, bool with_offloads)
+{
+	volatile union sxe2_tx_data_desc *desc;
+	struct sxe2_tx_buffer_vec *buffer;
+	uint16_t next_use;
+	uint16_t res_num;
+	uint16_t tx_num;
+
+	if (txq->desc_free_num < txq->free_thresh)
+		(void)sxe2_tx_bufs_free_vec_avx512(txq);
+
+	nb_pkts = RTE_MIN(txq->desc_free_num, nb_pkts);
+	if (unlikely(nb_pkts == 0)) {
+		PMD_LOG_DEBUG(TX, "Tx pkts avx512 batch: may not enough free desc, "
+				"free_desc=%u, need_tx_pkts=%u",
+				txq->desc_free_num, nb_pkts);
+		goto l_end;
+	}
+	tx_num = nb_pkts;
+
+	next_use = txq->next_use;
+	desc     = &txq->desc_ring[next_use];
+	buffer   = (struct sxe2_tx_buffer_vec *)txq->buffer_ring;
+	buffer  += next_use;
+
+	txq->desc_free_num -= nb_pkts;
+
+	res_num = txq->ring_depth - txq->next_use;
+
+	if (tx_num >= res_num) {
+		sxe2_tx_pkts_mbuf_fill_avx512(buffer, tx_pkts, res_num);
+
+		sxe2_tx_desc_fill_avx512(desc, tx_pkts, res_num,
+					SXE2_TX_DATA_DESC_CMD_EOP, with_offloads);
+		tx_pkts += (res_num - 1);
+		desc    += (res_num - 1);
+
+		sxe2_tx_desc_fill_one_avx512(desc, *tx_pkts++,
+					(SXE2_TX_DATA_DESC_CMD_EOP | SXE2_TX_DATA_DESC_CMD_RS),
+					with_offloads);
+
+		tx_num -= res_num;
+
+		next_use     = 0;
+		txq->next_rs = txq->rs_thresh - 1;
+		desc         = txq->desc_ring;
+		buffer       = (struct sxe2_tx_buffer_vec *)txq->buffer_ring;
+	}
+
+	sxe2_tx_pkts_mbuf_fill_avx512(buffer, tx_pkts, tx_num);
+
+	sxe2_tx_desc_fill_avx512(desc, tx_pkts, tx_num,
+			SXE2_TX_DATA_DESC_CMD_EOP, with_offloads);
+
+	next_use += tx_num;
+	if (next_use > txq->next_rs) {
+		txq->desc_ring[txq->next_rs].read.type_cmd_off_bsz_l2t |=
+			rte_cpu_to_le_64(SXE2_TX_DATA_DESC_CMD_RS_MASK);
+
+		txq->next_rs += txq->rs_thresh;
+	}
+	txq->next_use = next_use;
+
+	SXE2_PCI_REG_WRITE_WC(txq->tdt_reg_addr, next_use);
+	PMD_LOG_DEBUG(TX, "port_id=%u queue_id=%u next_use=%u send_pkts=%u",
+			txq->port_id, txq->queue_id, next_use, nb_pkts);
+l_end:
+	return nb_pkts;
+}
+
+static __rte_always_inline uint16_t
+sxe2_tx_pkts_vec_avx512_common(struct sxe2_tx_queue *txq, struct rte_mbuf **tx_pkts,
+	uint16_t nb_pkts, bool with_offloads)
+{
+	uint16_t tx_done_num = 0;
+	uint16_t tx_once_num;
+	uint16_t tx_need_num;
+
+	while (nb_pkts) {
+		tx_need_num = RTE_MIN(nb_pkts, txq->rs_thresh);
+		tx_once_num =
+			sxe2_tx_pkts_vec_avx512_batch(txq, tx_pkts + tx_done_num,
+					     tx_need_num, with_offloads);
+		nb_pkts     -= tx_once_num;
+		tx_done_num += tx_once_num;
+		if (tx_once_num < tx_need_num)
+			break;
+	}
+
+	return tx_done_num;
+}
+
+uint16_t sxe2_tx_pkts_vec_avx512_simple(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
+{
+	return sxe2_tx_pkts_vec_avx512_common((struct sxe2_tx_queue *)tx_queue,
+					      tx_pkts, nb_pkts, false);
+}
+
+uint16_t sxe2_tx_pkts_vec_avx512(void *tx_queue, struct rte_mbuf **tx_pkts, uint16_t nb_pkts)
+{
+	return sxe2_tx_pkts_vec_avx512_common((struct sxe2_tx_queue *)tx_queue,
+					      tx_pkts, nb_pkts, true);
+}
+
+static inline void sxe2_rx_queue_rearm_avx512(struct sxe2_rx_queue *rxq)
+{
+	volatile union sxe2_rx_desc *desc;
+	struct rte_mbuf **buffer;
+	struct rte_mbuf *mbuf0, *mbuf1;
+	__m128i dma_addr0, dma_addr1;
+	__m128i virt_addr0, virt_addr1;
+	__m128i hdr_room = _mm_set_epi64x(RTE_PKTMBUF_HEADROOM, RTE_PKTMBUF_HEADROOM);
+	int32_t ret;
+	uint16_t i;
+	uint16_t new_tail;
+
+	buffer = &rxq->buffer_ring[rxq->realloc_start];
+	desc   = &rxq->desc_ring[rxq->realloc_start];
+
+	ret = rte_mempool_get_bulk(rxq->mb_pool, (void *)buffer, SXE2_RX_REARM_THRESH_VEC);
+	if (ret != 0) {
+		if ((rxq->realloc_num + SXE2_RX_REARM_THRESH_VEC) >= rxq->ring_depth) {
+			dma_addr0 = _mm_setzero_si128();
+			for (i = 0; i < SXE2_RX_NUM_PER_LOOP_AVX; ++i) {
+				buffer[i] = &rxq->fake_mbuf;
+				_mm_store_si128(RTE_CAST_PTR(__m128i *, &desc[i].read),
+						dma_addr0);
+			}
+		}
+
+		rxq->vsi->adapter->dev_info.dev_data->rx_mbuf_alloc_failed +=
+			SXE2_RX_REARM_THRESH_VEC;
+		goto l_end;
+	}
+
+	for (i = 0; i < SXE2_RX_REARM_THRESH_VEC; i += 2, buffer += 2) {
+		mbuf0 = buffer[0];
+		mbuf1 = buffer[1];
+
+#if RTE_IOVA_IN_MBUF
+
+		RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, buf_iova) !=
+				 offsetof(struct rte_mbuf, buf_addr) + 8);
+#endif
+		virt_addr0 = _mm_loadu_si128((__m128i *)&mbuf0->buf_addr);
+		virt_addr1 = _mm_loadu_si128((__m128i *)&mbuf1->buf_addr);
+
+#if RTE_IOVA_IN_MBUF
+
+		dma_addr0 = _mm_unpackhi_epi64(virt_addr0, virt_addr0);
+		dma_addr1 = _mm_unpackhi_epi64(virt_addr1, virt_addr1);
+#else
+
+		dma_addr0 = _mm_unpacklo_epi64(virt_addr0, virt_addr0);
+		dma_addr1 = _mm_unpacklo_epi64(virt_addr1, virt_addr1);
+#endif
+
+		dma_addr0 = _mm_add_epi64(dma_addr0, hdr_room);
+		dma_addr1 = _mm_add_epi64(dma_addr1, hdr_room);
+
+		_mm_store_si128(RTE_CAST_PTR(__m128i *, &desc++->read), dma_addr0);
+		_mm_store_si128(RTE_CAST_PTR(__m128i *, &desc++->read), dma_addr1);
+	}
+
+	rxq->realloc_start += SXE2_RX_REARM_THRESH_VEC;
+	if (rxq->realloc_start >= rxq->ring_depth)
+		rxq->realloc_start = 0;
+	rxq->realloc_num -= SXE2_RX_REARM_THRESH_VEC;
+
+	new_tail = (rxq->realloc_start == 0) ? (rxq->ring_depth - 1) :
+		(rxq->realloc_start - 1);
+
+	SXE2_PCI_REG_WRITE_WC(rxq->rdt_reg_addr, new_tail);
+
+l_end:
+	return;
+}
+
+static __rte_always_inline uint16_t
+sxe2_rx_pkts_common_vec_avx512(struct sxe2_rx_queue *rxq, struct rte_mbuf **rx_pkts,
+	uint16_t nb_pkts, uint8_t *split_rxe_flags,
+	uint8_t *umbcast_flags, bool do_offload)
+{
+	const uint32_t *ptype_tbl = rxq->vsi->adapter->ptype_tbl;
+	const __m256i mbuf_init = _mm256_set_epi64x(0, 0, 0, rxq->mbuf_init_value);
+	struct rte_mbuf **buffer;
+	volatile union sxe2_rx_desc *desc;
+	__m512i mbufs4_7;
+	__m512i mbufs0_3;
+	__m256i mbufs6_7;
+	__m256i mbufs4_5;
+	__m256i mbufs2_3;
+	__m256i mbufs0_1;
+	uint32_t bit_num  = 0;
+	uint16_t done_num = 0;
+	uint16_t i = 0;
+	uint16_t j = 0;
+
+	buffer   = &rxq->buffer_ring[rxq->processing_idx];
+	desc     = &rxq->desc_ring[rxq->processing_idx];
+
+	rte_prefetch0(desc);
+
+	nb_pkts = RTE_ALIGN_FLOOR(nb_pkts, SXE2_RX_NUM_PER_LOOP_AVX);
+
+	if (rxq->realloc_num > SXE2_RX_REARM_THRESH_VEC)
+		sxe2_rx_queue_rearm_avx512(rxq);
+
+	if (0 == (rte_le_to_cpu_64(desc->wb.status_err_ptype_len) & SXE2_RX_DESC_STATUS_DD_MASK))
+		goto l_end;
+
+	const __m512i crc_adjust =
+			_mm512_set4_epi32(0, -rxq->crc_len, -rxq->crc_len, 0);
+
+	const __m256i dd_mask = _mm256_set1_epi32(1);
+
+	const __m512i rvp_shuf_mask =
+			_mm512_set4_epi32((7 << 24) | (6 << 16) | (5 << 8) | 4,
+					  (3 << 24) | (2 << 16) | (13 << 8) | 12,
+					  (0xFFU << 24) | (0xFF << 16) | (13 << 8) | 12,
+					  0xFFFFFFFF);
+
+	const __m128i eop_shuf_mask =
+		_mm_set_epi8(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+			     0xFF, 0xFF, 8, 0, 10, 2, 12, 4, 14, 6);
+
+	RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, pkt_len) !=
+			offsetof(struct rte_mbuf, rx_descriptor_fields1) + 4);
+	RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, data_len) !=
+			offsetof(struct rte_mbuf, rx_descriptor_fields1) + 8);
+	RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, vlan_tci) !=
+			offsetof(struct rte_mbuf, rx_descriptor_fields1) + 10);
+	RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, hash) !=
+			offsetof(struct rte_mbuf, rx_descriptor_fields1) + 12);
+
+	for (i = 0; i < nb_pkts; i += SXE2_RX_NUM_PER_LOOP_AVX,
+				desc += SXE2_RX_NUM_PER_LOOP_AVX) {
+		_mm256_storeu_si256((void *)&rx_pkts[i],
+			_mm256_loadu_si256((void *)&buffer[i]));
+#ifdef RTE_ARCH_X86_64
+		_mm256_storeu_si256((void *)&rx_pkts[i + 4],
+			_mm256_loadu_si256((void *)&buffer[i + 4]));
+#endif
+
+		const __m128i desc7 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 7));
+		rte_compiler_barrier();
+		const __m128i desc6 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 6));
+		rte_compiler_barrier();
+		const __m128i desc5 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 5));
+		rte_compiler_barrier();
+		const __m128i desc4 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 4));
+		rte_compiler_barrier();
+		const __m128i desc3 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 3));
+		rte_compiler_barrier();
+		const __m128i desc2 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 2));
+		rte_compiler_barrier();
+		const __m128i desc1 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 1));
+		rte_compiler_barrier();
+		const __m128i desc0 = _mm_loadu_si128(RTE_CAST_PTR(const __m128i *, desc + 0));
+
+		const __m256i descs6_7 =
+			_mm256_inserti128_si256(_mm256_castsi128_si256(desc6), desc7, 1);
+		const __m256i descs4_5 =
+			_mm256_inserti128_si256(_mm256_castsi128_si256(desc4), desc5, 1);
+		const __m256i descs2_3 =
+			_mm256_inserti128_si256(_mm256_castsi128_si256(desc2), desc3, 1);
+		const __m256i descs0_1 =
+			_mm256_inserti128_si256(_mm256_castsi128_si256(desc0), desc1, 1);
+
+		const __m512i descs4_7 =
+			_mm512_inserti64x4(_mm512_castsi256_si512(descs4_5), descs6_7, 1);
+		const __m512i descs0_3 =
+			_mm512_inserti64x4(_mm512_castsi256_si512(descs0_1), descs2_3, 1);
+
+		if (split_rxe_flags != NULL) {
+			for (j = 0; j < SXE2_RX_NUM_PER_LOOP_AVX; j++)
+				rte_mbuf_prefetch_part2(rx_pkts[i + j]);
+		}
+
+		mbufs4_7 = _mm512_shuffle_epi8(descs4_7, rvp_shuf_mask);
+		mbufs0_3 = _mm512_shuffle_epi8(descs0_3, rvp_shuf_mask);
+
+		mbufs4_7 = _mm512_add_epi32(mbufs4_7, crc_adjust);
+		mbufs0_3 = _mm512_add_epi32(mbufs0_3, crc_adjust);
+
+		const __m512i ptype_mask = _mm512_set1_epi64(SXE2_RX_FLEX_DESC_PTYPE_M <<
+					SXE2_RX_FLEX_DESC_PTYPE_S);
+
+		__m512i ptypes4_7 = _mm512_and_si512(descs4_7, ptype_mask);
+		__m512i ptypes0_3 = _mm512_and_si512(descs0_3, ptype_mask);
+
+		const __m256i ptypes6_7 = _mm512_extracti64x4_epi64(ptypes4_7, 1);
+		const __m256i ptypes4_5 = _mm512_extracti64x4_epi64(ptypes4_7, 0);
+		const __m256i ptypes2_3 = _mm512_extracti64x4_epi64(ptypes0_3, 1);
+		const __m256i ptypes0_1 = _mm512_extracti64x4_epi64(ptypes0_3, 0);
+
+		const uint16_t ptype7 = _mm256_extract_epi16(ptypes6_7, 13);
+		const uint16_t ptype6 = _mm256_extract_epi16(ptypes6_7, 5);
+		const uint16_t ptype5 = _mm256_extract_epi16(ptypes4_5, 13);
+		const uint16_t ptype4 = _mm256_extract_epi16(ptypes4_5, 5);
+		const uint16_t ptype3 = _mm256_extract_epi16(ptypes2_3, 13);
+		const uint16_t ptype2 = _mm256_extract_epi16(ptypes2_3, 5);
+		const uint16_t ptype1 = _mm256_extract_epi16(ptypes0_1, 13);
+		const uint16_t ptype0 = _mm256_extract_epi16(ptypes0_1, 5);
+
+		const __m512i ptype_mask4_7 =
+				_mm512_set_epi32(0, 0, 0, ptype_tbl[ptype7],
+						 0, 0, 0, ptype_tbl[ptype6],
+						 0, 0, 0, ptype_tbl[ptype5],
+						 0, 0, 0, ptype_tbl[ptype4]);
+		const __m512i ptype_mask0_3 =
+				_mm512_set_epi32(0, 0, 0, ptype_tbl[ptype3],
+						 0, 0, 0, ptype_tbl[ptype2],
+						 0, 0, 0, ptype_tbl[ptype1],
+						 0, 0, 0, ptype_tbl[ptype0]);
+
+		mbufs4_7 = _mm512_or_si512(mbufs4_7, ptype_mask4_7);
+		mbufs0_3 = _mm512_or_si512(mbufs0_3, ptype_mask0_3);
+
+		mbufs6_7 = _mm512_extracti64x4_epi64(mbufs4_7, 1);
+		mbufs4_5 = _mm512_extracti64x4_epi64(mbufs4_7, 0);
+		mbufs2_3 = _mm512_extracti64x4_epi64(mbufs0_3, 1);
+		mbufs0_1 = _mm512_extracti64x4_epi64(mbufs0_3, 0);
+
+		const __m512i staterr_per_mask =
+			_mm512_set_epi32(0x17, 0x1F, 0x07, 0x0F,
+					 0x13, 0x1B, 0x03, 0x0B,
+					 0x16, 0x1E, 0x06, 0x0E,
+					 0x12, 0x1A, 0x02, 0x0A);
+		__m512i qw1_0_7 = _mm512_permutex2var_epi32(descs4_7,
+							    staterr_per_mask,
+							    descs0_3);
+
+		__m256i staterrs0_7 = _mm512_extracti64x4_epi64(qw1_0_7, 0);
+
+		__m256i stu_len0_7 = _mm512_extracti64x4_epi64(qw1_0_7, 1);
+		__m256i mbuf_flags = _mm256_setzero_si256();
+
+		if (do_offload) {
+			const __m256i desc_flags_mask = _mm256_set1_epi32(0xC0001C04);
+			const __m256i desc_flags_rss_mask = _mm256_set1_epi32(0x20000000);
+			const __m256i vlan_flags =
+				_mm256_set_epi8(0, 0, 0, 0,
+					0, 0, 0, 0,
+					0, 0, 0, RTE_MBUF_F_RX_VLAN |
+						RTE_MBUF_F_RX_VLAN_STRIPPED,
+					0, 0, 0, 0,
+					0, 0, 0, 0,
+					0, 0, 0, 0,
+					0, 0, 0, RTE_MBUF_F_RX_VLAN |
+						RTE_MBUF_F_RX_VLAN_STRIPPED,
+					0, 0, 0, 0);
+
+			const __m256i rss_flags =
+				_mm256_set_epi8(0, 0, 0, 0,
+					0, 0, 0, 0,
+					0, 0, 0, RTE_MBUF_F_RX_RSS_HASH,
+					0, 0, 0, 0,
+					0, 0, 0, 0,
+					0, 0, 0, 0,
+					0, 0, 0, RTE_MBUF_F_RX_RSS_HASH,
+					0, 0, 0, 0);
+
+			const __m256i cksum_flags =
+			_mm256_set_epi8(0, 0, 0, 0, 0, 0, 0,
+			0,
+			((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_BAD |
+				RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+			((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_BAD |
+				RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+			((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+				RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+			((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+				RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+			((RTE_MBUF_F_RX_L4_CKSUM_BAD | RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+			((RTE_MBUF_F_RX_L4_CKSUM_BAD | RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+			((RTE_MBUF_F_RX_L4_CKSUM_GOOD | RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+			((RTE_MBUF_F_RX_L4_CKSUM_GOOD | RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+			0, 0, 0, 0, 0, 0, 0, 0,
+			((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_BAD |
+				RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+			((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_BAD |
+				RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+			((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+				RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+			((RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD | RTE_MBUF_F_RX_L4_CKSUM_GOOD |
+				RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+			((RTE_MBUF_F_RX_L4_CKSUM_BAD | RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+			((RTE_MBUF_F_RX_L4_CKSUM_BAD | RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1),
+			((RTE_MBUF_F_RX_L4_CKSUM_GOOD | RTE_MBUF_F_RX_IP_CKSUM_BAD) >> 1),
+			((RTE_MBUF_F_RX_L4_CKSUM_GOOD | RTE_MBUF_F_RX_IP_CKSUM_GOOD) >> 1));
+
+			const __m256i cksum_mask =
+				_mm256_set1_epi32(RTE_MBUF_F_RX_IP_CKSUM_MASK |
+						  RTE_MBUF_F_RX_L4_CKSUM_MASK |
+						  RTE_MBUF_F_RX_OUTER_L4_CKSUM_MASK |
+						  RTE_MBUF_F_RX_OUTER_IP_CKSUM_BAD);
+
+			const __m256i vlan_mask =
+				_mm256_set1_epi32(RTE_MBUF_F_RX_VLAN |
+						  RTE_MBUF_F_RX_VLAN_STRIPPED);
+
+			__m256i tmp_flags;
+			__m256i descs_flags = _mm256_and_si256(staterrs0_7, desc_flags_mask);
+			stu_len0_7 = _mm256_and_si256(stu_len0_7, desc_flags_rss_mask);
+
+			tmp_flags = _mm256_shuffle_epi8(vlan_flags, descs_flags);
+			mbuf_flags = _mm256_and_si256(tmp_flags, vlan_mask);
+
+			descs_flags = _mm256_srli_epi32(descs_flags, 10);
+			tmp_flags   = _mm256_shuffle_epi8(cksum_flags, descs_flags);
+			tmp_flags   = _mm256_slli_epi32(tmp_flags, 1);
+			tmp_flags   = _mm256_and_si256(tmp_flags, cksum_mask);
+			mbuf_flags  = _mm256_or_si256(mbuf_flags, tmp_flags);
+
+			descs_flags = _mm256_srli_epi32(stu_len0_7, 27);
+			tmp_flags   = _mm256_shuffle_epi8(rss_flags, descs_flags);
+			mbuf_flags  = _mm256_or_si256(mbuf_flags, tmp_flags);
+
+#ifndef RTE_LIBRTE_SXE2_16BYTE_RX_DESC
+			if (rxq->fnav_enable) {
+				__m256i fnav_vld0_3, fnav_vld4_7;
+				__m256i fnav_vld0_7;
+				__m256i v_zeros, v_ffff, v_u32_one;
+				const __m256i fdir_flags =
+					_mm256_set1_epi32(RTE_MBUF_F_RX_FDIR |
+							  RTE_MBUF_F_RX_FDIR_ID);
+				fnav_vld0_3 = _mm256_unpacklo_epi32(descs2_3, descs0_1);
+				fnav_vld4_7 = _mm256_unpacklo_epi32(descs6_7, descs4_5);
+
+				fnav_vld0_7 = _mm256_unpacklo_epi64(fnav_vld4_7, fnav_vld0_3);
+
+				fnav_vld0_7 = _mm256_slli_epi32(fnav_vld0_7, 26);
+				fnav_vld0_7 = _mm256_srli_epi32(fnav_vld0_7, 31);
+
+				v_zeros = _mm256_setzero_si256();
+				v_ffff = _mm256_cmpeq_epi32(v_zeros, v_zeros);
+				v_u32_one = _mm256_srli_epi32(v_ffff, 31);
+
+				tmp_flags = _mm256_cmpeq_epi32(fnav_vld0_7, v_u32_one);
+
+				tmp_flags = _mm256_and_si256(tmp_flags, fdir_flags);
+
+				mbuf_flags = _mm256_or_si256(mbuf_flags, tmp_flags);
+
+				rx_pkts[i + 0]->hash.fdir.hi = desc[0].wb.fd_filter_id;
+				rx_pkts[i + 1]->hash.fdir.hi = desc[1].wb.fd_filter_id;
+				rx_pkts[i + 2]->hash.fdir.hi = desc[2].wb.fd_filter_id;
+				rx_pkts[i + 3]->hash.fdir.hi = desc[3].wb.fd_filter_id;
+				rx_pkts[i + 4]->hash.fdir.hi = desc[4].wb.fd_filter_id;
+				rx_pkts[i + 5]->hash.fdir.hi = desc[5].wb.fd_filter_id;
+				rx_pkts[i + 6]->hash.fdir.hi = desc[6].wb.fd_filter_id;
+				rx_pkts[i + 7]->hash.fdir.hi = desc[7].wb.fd_filter_id;
+			}
+#endif
+		}
+
+		RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, ol_flags) !=
+				offsetof(struct rte_mbuf, rearm_data) + 8);
+		RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, rx_descriptor_fields1) !=
+				offsetof(struct rte_mbuf, rearm_data) + 16);
+		RTE_BUILD_BUG_ON(offsetof(struct rte_mbuf, rearm_data) !=
+				RTE_ALIGN(offsetof(struct rte_mbuf, rearm_data), 16));
+
+		__m256i rearm_arr[8];
+
+		rearm_arr[6] = _mm256_blend_epi32(mbuf_init,
+					_mm256_slli_si256(mbuf_flags, 8), 0x04);
+		rearm_arr[4] = _mm256_blend_epi32(mbuf_init,
+					_mm256_slli_si256(mbuf_flags, 4), 0x04);
+		rearm_arr[2] = _mm256_blend_epi32(mbuf_init, mbuf_flags, 0x04);
+		rearm_arr[0] = _mm256_blend_epi32(mbuf_init,
+					_mm256_srli_si256(mbuf_flags, 4), 0x04);
+
+		rearm_arr[6] = _mm256_permute2f128_si256(rearm_arr[6], mbufs6_7, 0x20);
+		rearm_arr[4] = _mm256_permute2f128_si256(rearm_arr[4], mbufs4_5, 0x20);
+		rearm_arr[2] = _mm256_permute2f128_si256(rearm_arr[2], mbufs2_3, 0x20);
+		rearm_arr[0] = _mm256_permute2f128_si256(rearm_arr[0], mbufs0_1, 0x20);
+
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 6]->rearm_data, rearm_arr[6]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 4]->rearm_data, rearm_arr[4]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 2]->rearm_data, rearm_arr[2]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 0]->rearm_data, rearm_arr[0]);
+
+		const __m256i tmp_mbuf_flags =
+				_mm256_castsi128_si256(_mm256_extracti128_si256(mbuf_flags, 1));
+
+		rearm_arr[7] = _mm256_blend_epi32(mbuf_init,
+					_mm256_slli_si256(tmp_mbuf_flags, 8), 4);
+		rearm_arr[5] = _mm256_blend_epi32(mbuf_init,
+					_mm256_slli_si256(tmp_mbuf_flags, 4), 4);
+		rearm_arr[3] = _mm256_blend_epi32(mbuf_init, tmp_mbuf_flags, 4);
+		rearm_arr[1] = _mm256_blend_epi32(mbuf_init,
+					_mm256_srli_si256(tmp_mbuf_flags, 4), 4);
+
+		rearm_arr[7] = _mm256_blend_epi32(rearm_arr[7], mbufs6_7, 0XF0);
+		rearm_arr[5] = _mm256_blend_epi32(rearm_arr[5], mbufs4_5, 0XF0);
+		rearm_arr[3] = _mm256_blend_epi32(rearm_arr[3], mbufs2_3, 0XF0);
+		rearm_arr[1] = _mm256_blend_epi32(rearm_arr[1], mbufs0_1, 0XF0);
+
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 7]->rearm_data, rearm_arr[7]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 5]->rearm_data, rearm_arr[5]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 3]->rearm_data, rearm_arr[3]);
+		_mm256_storeu_si256((__m256i *)&rx_pkts[i + 1]->rearm_data, rearm_arr[1]);
+
+		if (umbcast_flags) {
+			const __m256i umbcast_mask =
+				_mm256_set1_epi32(SXE2_RX_DESC_STATUS_UMBCAST_MASK);
+			__m256i umbcast_bits_256 =
+				_mm256_and_si256(staterrs0_7, umbcast_mask);
+
+			umbcast_bits_256 = _mm256_srli_epi32(umbcast_bits_256, 24);
+			__m128i umbcast_bits_128 =
+				_mm_packs_epi32(_mm256_castsi256_si128(umbcast_bits_256),
+						_mm256_extractf128_si256(umbcast_bits_256, 1));
+
+			umbcast_bits_128 = _mm_shuffle_epi8(umbcast_bits_128, eop_shuf_mask);
+
+			*(uint64_t *)umbcast_flags = _mm_cvtsi128_si64(umbcast_bits_128);
+			umbcast_flags += SXE2_RX_NUM_PER_LOOP_AVX;
+		}
+
+		if (split_rxe_flags) {
+			const __m256i eop_rxe_mask =
+					_mm256_set1_epi32(SXE2_RX_DESC_STATUS_EOP_MASK |
+								SXE2_RX_DESC_ERROR_RXE_MASK |
+								SXE2_RX_DESC_ERROR_OVERSIZE_MASK);
+			const __m128i eop_mask_128 =
+					_mm_set1_epi16(SXE2_RX_DESC_STATUS_EOP_MASK);
+			const __m128i rxe_mask_128 =
+					_mm_set1_epi16(SXE2_RX_DESC_ERROR_RXE_MASK |
+							SXE2_RX_DESC_ERROR_OVERSIZE_MASK);
+
+			const __m256i tmp_stats = _mm256_and_si256(staterrs0_7, eop_rxe_mask);
+
+			const __m128i eop_rxe_bits = _mm_packs_epi32
+							(_mm256_castsi256_si128(tmp_stats),
+							 _mm256_extractf128_si256(tmp_stats, 1));
+
+			__m128i not_eop_bits = _mm_andnot_si128(eop_rxe_bits, eop_mask_128);
+
+			not_eop_bits =
+				_mm_or_si128(not_eop_bits,
+					     _mm_srli_epi16(_mm_and_si128(eop_rxe_bits,
+									       rxe_mask_128),
+							      7));
+
+			not_eop_bits = _mm_shuffle_epi8(not_eop_bits, eop_shuf_mask);
+
+			*(uint64_t *)split_rxe_flags = _mm_cvtsi128_si64(not_eop_bits);
+			split_rxe_flags += SXE2_RX_NUM_PER_LOOP_AVX;
+		}
+
+		staterrs0_7 = _mm256_and_si256(staterrs0_7, dd_mask);
+
+		staterrs0_7 = _mm256_packs_epi32(staterrs0_7, _mm256_setzero_si256());
+
+		bit_num = rte_popcount64
+				(_mm_cvtsi128_si64(_mm256_extracti128_si256(staterrs0_7, 1)));
+		bit_num += rte_popcount64
+				(_mm_cvtsi128_si64(_mm256_castsi256_si128(staterrs0_7)));
+		done_num += bit_num;
+
+		if (bit_num != SXE2_RX_NUM_PER_LOOP_AVX)
+			break;
+	}
+
+	rxq->processing_idx += done_num;
+	rxq->processing_idx &= (rxq->ring_depth - 1);
+	if ((rxq->processing_idx & 1) == 1 && done_num > 1) {
+		rxq->processing_idx--;
+		done_num--;
+	}
+	rxq->realloc_num     += done_num;
+
+l_end:
+	PMD_LOG_DEBUG(RX, "port_id=%u queue_id=%u last_id=%u recv_pkts=%d",
+			rxq->port_id, rxq->queue_id, rxq->processing_idx, done_num);
+	return done_num;
+}
+
+static __rte_always_inline uint16_t
+sxe2_rx_pkts_scattered_batch_vec_avx512(struct sxe2_rx_queue *rxq, struct rte_mbuf **rx_pkts,
+	uint16_t nb_pkts, bool do_offload)
+{
+	const uint64_t *split_rxe_flags64;
+	uint8_t split_rxe_flags[SXE2_RX_PKTS_BURST_BATCH_NUM_VEC] = {0};
+	uint8_t umbcast_flags[SXE2_RX_PKTS_BURST_BATCH_NUM_VEC] = {0};
+	uint16_t rx_done_num;
+	uint16_t rx_pkt_done_num;
+
+	rx_pkt_done_num = 0;
+
+	if (rxq->vsi->adapter->devargs.sw_stats_en) {
+		rx_done_num = sxe2_rx_pkts_common_vec_avx512(rxq, rx_pkts,
+				nb_pkts, split_rxe_flags,
+				umbcast_flags, do_offload);
+	} else {
+		rx_done_num = sxe2_rx_pkts_common_vec_avx512(rxq, rx_pkts,
+				nb_pkts, split_rxe_flags,
+			    NULL, do_offload);
+	}
+	if (rx_done_num == 0)
+		goto l_end;
+
+	if (!rxq->vsi->adapter->devargs.sw_stats_en) {
+		split_rxe_flags64 = (uint64_t *)split_rxe_flags;
+
+		if (rxq->pkt_first_seg == NULL &&
+				!split_rxe_flags64[0] && !split_rxe_flags64[1] &&
+				!split_rxe_flags64[2] && !split_rxe_flags64[3]) {
+			rx_pkt_done_num = rx_done_num;
+			goto l_end;
+		}
+
+		if (rxq->pkt_first_seg == NULL) {
+			while (rx_pkt_done_num < rx_done_num &&
+			       split_rxe_flags[rx_pkt_done_num] == 0)
+				rx_pkt_done_num++;
+
+			if (rx_pkt_done_num == rx_done_num)
+				goto l_end;
+
+			rxq->pkt_first_seg = rx_pkts[rx_pkt_done_num];
+		}
+	}
+
+	rx_pkt_done_num += sxe2_rx_pkts_refactor(rxq, &rx_pkts[rx_pkt_done_num],
+			rx_done_num - rx_pkt_done_num, &split_rxe_flags[rx_pkt_done_num],
+			&umbcast_flags[rx_pkt_done_num]);
+
+l_end:
+
+	return rx_pkt_done_num;
+}
+
+static __rte_always_inline uint16_t
+sxe2_rx_pkts_scattered_common_vec_avx512(void *rx_queue,
+	struct rte_mbuf **rx_pkts, uint16_t nb_pkts, bool offload)
+{
+	uint16_t done_num = 0;
+	uint16_t once_num  = 0;
+
+	while (nb_pkts > SXE2_RX_PKTS_BURST_BATCH_NUM) {
+		once_num = sxe2_rx_pkts_scattered_batch_vec_avx512(rx_queue, rx_pkts + done_num,
+			SXE2_RX_PKTS_BURST_BATCH_NUM, offload);
+
+		done_num  += once_num;
+		nb_pkts -= once_num;
+
+		if (once_num < SXE2_RX_PKTS_BURST_BATCH_NUM)
+			goto end;
+	}
+
+	done_num += sxe2_rx_pkts_scattered_batch_vec_avx512(rx_queue,
+		rx_pkts + done_num, nb_pkts, offload);
+
+end:
+	return done_num;
+}
+
+uint16_t sxe2_rx_pkts_scattered_vec_avx512(void *rx_queue,
+		struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
+{
+	return sxe2_rx_pkts_scattered_common_vec_avx512(rx_queue,
+			rx_pkts, nb_pkts, false);
+}
+
+uint16_t sxe2_rx_pkts_scattered_vec_avx512_offload(void *rx_queue,
+		struct rte_mbuf **rx_pkts, uint16_t nb_pkts)
+{
+	return sxe2_rx_pkts_scattered_common_vec_avx512(rx_queue,
+			rx_pkts, nb_pkts, true);
+}
+
+#endif
-- 
2.52.0


^ permalink raw reply related

* [PATCH v10 00/20] net/sxe2: added Linkdata sxe2 ethernet driver
From: liujie5 @ 2026-06-06  1:07 UTC (permalink / raw)
  To: stephen; +Cc: dev, Jie Liu
In-Reply-To: <20260604015404.1552953-21-liujie5@linkdatatechnology.com>

From: Jie Liu <liujie5@linkdatatechnology.com>

This patch set implements core functionality for the SXE2 PMD,
including basic driver framework, data path setup, and advanced
offload features (VLAN, RSS,TM, PTP etc.).

V10:
 - Addressed AI comments

Jie Liu (20):
  net/sxe2: support AVX512 vectorized path for Rx and Tx
  net/sxe2: add AVX2 vector data path for Rx and Tx
  drivers: add supported packet types get callback
  net/sxe2: support L2 filtering and MAC config
  drivers: support RSS feature
  net/sxe2: support TM hierarchy and shaping
  net/sxe2: support IPsec inline protocol offload
  net/sxe2: support statistics and multi-process
  drivers: interrupt handling
  net/sxe2: add NEON vec Rx/Tx burst functions
  drivers: add support for VF representors
  net/sxe2: add support for custom UDP tunnel ports
  net/sxe2: support firmware version reading
  net/sxe2: implement get monitor address
  common/sxe2: add shared SFP module definitions
  net/sxe2: support SFP module info and EEPROM access
  net/sxe2: implement private dump info
  net/sxe2: add mbuf validation in Tx debug mode
  drivers: add testpmd commands for private features
  net/sxe2: update sxe2 feature matrix docs

 doc/guides/nics/features/sxe2.ini          |   56 +
 drivers/common/sxe2/sxe2_common.c          |  156 ++
 drivers/common/sxe2/sxe2_common.h          |    4 +
 drivers/common/sxe2/sxe2_flow_public.h     |  633 +++++++
 drivers/common/sxe2/sxe2_ioctl_chnl.c      |  178 +-
 drivers/common/sxe2/sxe2_ioctl_chnl_func.h |   18 +
 drivers/common/sxe2/sxe2_msg.h             |  118 ++
 drivers/common/sxe2/sxe2_ptype.h           | 1793 ++++++++++++++++++
 drivers/net/sxe2/meson.build               |   56 +-
 drivers/net/sxe2/sxe2_cmd_chnl.c           | 1587 +++++++++++++++-
 drivers/net/sxe2/sxe2_cmd_chnl.h           |  139 ++
 drivers/net/sxe2/sxe2_drv_cmd.h            |  521 +++++-
 drivers/net/sxe2/sxe2_dump.c               |  304 +++
 drivers/net/sxe2/sxe2_dump.h               |   12 +
 drivers/net/sxe2/sxe2_ethdev.c             | 1531 +++++++++++++++-
 drivers/net/sxe2/sxe2_ethdev.h             |  115 +-
 drivers/net/sxe2/sxe2_ethdev_repr.c        |  610 ++++++
 drivers/net/sxe2/sxe2_ethdev_repr.h        |   32 +
 drivers/net/sxe2/sxe2_filter.c             |  895 +++++++++
 drivers/net/sxe2/sxe2_filter.h             |  100 +
 drivers/net/sxe2/sxe2_flow.c               | 1391 ++++++++++++++
 drivers/net/sxe2/sxe2_flow.h               |   30 +
 drivers/net/sxe2/sxe2_flow_define.h        |  144 ++
 drivers/net/sxe2/sxe2_flow_parse_action.c  | 1182 ++++++++++++
 drivers/net/sxe2/sxe2_flow_parse_action.h  |   23 +
 drivers/net/sxe2/sxe2_flow_parse_engine.c  |  106 ++
 drivers/net/sxe2/sxe2_flow_parse_engine.h  |   13 +
 drivers/net/sxe2/sxe2_flow_parse_pattern.c | 1935 ++++++++++++++++++++
 drivers/net/sxe2/sxe2_flow_parse_pattern.h |   46 +
 drivers/net/sxe2/sxe2_ipsec.c              | 1565 ++++++++++++++++
 drivers/net/sxe2/sxe2_ipsec.h              |  254 +++
 drivers/net/sxe2/sxe2_irq.c                | 1025 +++++++++++
 drivers/net/sxe2/sxe2_irq.h                |   25 +
 drivers/net/sxe2/sxe2_mac.c                |  535 ++++++
 drivers/net/sxe2/sxe2_mac.h                |   84 +
 drivers/net/sxe2/sxe2_mp.c                 |  414 +++++
 drivers/net/sxe2/sxe2_mp.h                 |   67 +
 drivers/net/sxe2/sxe2_queue.c              |   17 +-
 drivers/net/sxe2/sxe2_rss.c                |  584 ++++++
 drivers/net/sxe2/sxe2_rss.h                |   81 +
 drivers/net/sxe2/sxe2_rx.c                 |   38 +
 drivers/net/sxe2/sxe2_rx.h                 |    2 +
 drivers/net/sxe2/sxe2_security.c           |  335 ++++
 drivers/net/sxe2/sxe2_security.h           |   77 +
 drivers/net/sxe2/sxe2_stats.c              |  591 ++++++
 drivers/net/sxe2/sxe2_stats.h              |   39 +
 drivers/net/sxe2/sxe2_switchdev.c          |  332 ++++
 drivers/net/sxe2/sxe2_switchdev.h          |   33 +
 drivers/net/sxe2/sxe2_testpmd.c            |  733 ++++++++
 drivers/net/sxe2/sxe2_testpmd_lib.c        |  969 ++++++++++
 drivers/net/sxe2/sxe2_testpmd_lib.h        |  142 ++
 drivers/net/sxe2/sxe2_tm.c                 | 1169 ++++++++++++
 drivers/net/sxe2/sxe2_tm.h                 |   78 +
 drivers/net/sxe2/sxe2_tx.c                 |    7 +
 drivers/net/sxe2/sxe2_txrx.c               |  176 +-
 drivers/net/sxe2/sxe2_txrx.h               |    4 +
 drivers/net/sxe2/sxe2_txrx_check_mbuf.c    |  595 ++++++
 drivers/net/sxe2/sxe2_txrx_check_mbuf.h    |   38 +
 drivers/net/sxe2/sxe2_txrx_poll.c          |  243 ++-
 drivers/net/sxe2/sxe2_txrx_vec.c           |   46 +-
 drivers/net/sxe2/sxe2_txrx_vec.h           |   38 +-
 drivers/net/sxe2/sxe2_txrx_vec_avx2.c      |  776 ++++++++
 drivers/net/sxe2/sxe2_txrx_vec_avx512.c    |  897 +++++++++
 drivers/net/sxe2/sxe2_txrx_vec_common.h    |    1 +
 drivers/net/sxe2/sxe2_txrx_vec_neon.c      |  721 ++++++++
 drivers/net/sxe2/sxe2_vsi.c                |  146 ++
 drivers/net/sxe2/sxe2_vsi.h                |   12 +-
 drivers/net/sxe2/sxe2vf_regs.h             |   85 +
 68 files changed, 26575 insertions(+), 127 deletions(-)
 create mode 100644 drivers/common/sxe2/sxe2_flow_public.h
 create mode 100644 drivers/common/sxe2/sxe2_msg.h
 create mode 100644 drivers/common/sxe2/sxe2_ptype.h
 create mode 100644 drivers/net/sxe2/sxe2_dump.c
 create mode 100644 drivers/net/sxe2/sxe2_dump.h
 create mode 100644 drivers/net/sxe2/sxe2_ethdev_repr.c
 create mode 100644 drivers/net/sxe2/sxe2_ethdev_repr.h
 create mode 100644 drivers/net/sxe2/sxe2_filter.c
 create mode 100644 drivers/net/sxe2/sxe2_filter.h
 create mode 100644 drivers/net/sxe2/sxe2_flow.c
 create mode 100644 drivers/net/sxe2/sxe2_flow.h
 create mode 100644 drivers/net/sxe2/sxe2_flow_define.h
 create mode 100644 drivers/net/sxe2/sxe2_flow_parse_action.c
 create mode 100644 drivers/net/sxe2/sxe2_flow_parse_action.h
 create mode 100644 drivers/net/sxe2/sxe2_flow_parse_engine.c
 create mode 100644 drivers/net/sxe2/sxe2_flow_parse_engine.h
 create mode 100644 drivers/net/sxe2/sxe2_flow_parse_pattern.c
 create mode 100644 drivers/net/sxe2/sxe2_flow_parse_pattern.h
 create mode 100644 drivers/net/sxe2/sxe2_ipsec.c
 create mode 100644 drivers/net/sxe2/sxe2_ipsec.h
 create mode 100644 drivers/net/sxe2/sxe2_irq.c
 create mode 100644 drivers/net/sxe2/sxe2_mac.c
 create mode 100644 drivers/net/sxe2/sxe2_mac.h
 create mode 100644 drivers/net/sxe2/sxe2_mp.c
 create mode 100644 drivers/net/sxe2/sxe2_mp.h
 create mode 100644 drivers/net/sxe2/sxe2_rss.c
 create mode 100644 drivers/net/sxe2/sxe2_rss.h
 create mode 100644 drivers/net/sxe2/sxe2_security.c
 create mode 100644 drivers/net/sxe2/sxe2_security.h
 create mode 100644 drivers/net/sxe2/sxe2_stats.c
 create mode 100644 drivers/net/sxe2/sxe2_stats.h
 create mode 100644 drivers/net/sxe2/sxe2_switchdev.c
 create mode 100644 drivers/net/sxe2/sxe2_switchdev.h
 create mode 100644 drivers/net/sxe2/sxe2_testpmd.c
 create mode 100644 drivers/net/sxe2/sxe2_testpmd_lib.c
 create mode 100644 drivers/net/sxe2/sxe2_testpmd_lib.h
 create mode 100644 drivers/net/sxe2/sxe2_tm.c
 create mode 100644 drivers/net/sxe2/sxe2_tm.h
 create mode 100644 drivers/net/sxe2/sxe2_txrx_check_mbuf.c
 create mode 100644 drivers/net/sxe2/sxe2_txrx_check_mbuf.h
 create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_avx2.c
 create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_avx512.c
 create mode 100644 drivers/net/sxe2/sxe2_txrx_vec_neon.c
 create mode 100644 drivers/net/sxe2/sxe2vf_regs.h

-- 
2.52.0


^ permalink raw reply

* [PATCH v9 10/10] dts: add selective Rx tests
From: Thomas Monjalon @ 2026-06-05 23:33 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Luca Vizzarro, Patrick Robb
In-Reply-To: <20260605233456.3017423-1-thomas@monjalon.net>

Add TestSuite_rx_split with 7 test cases:
- 3 positive: headers only, payload only, two non-contiguous segments
- 4 negative: missing offload flag, out-of-range, overlap, all-discard

Add selective Rx capability detection via testpmd "show port info".

The test suite could be completed later for the basic buffer split
configuration based on offsets or protocols.

Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 dts/api/capabilities.py                   |   2 +
 dts/api/testpmd/__init__.py               |  17 ++
 dts/api/testpmd/types.py                  |   6 +
 dts/framework/testbed_model/capability.py |   2 +
 dts/tests/TestSuite_rx_split.py           | 277 ++++++++++++++++++++++
 5 files changed, 304 insertions(+)
 create mode 100644 dts/tests/TestSuite_rx_split.py

diff --git a/dts/api/capabilities.py b/dts/api/capabilities.py
index 09bc538523..b0c1d81d36 100644
--- a/dts/api/capabilities.py
+++ b/dts/api/capabilities.py
@@ -136,6 +136,8 @@ class NicCapability(IntEnum):
     #: Device supports all VLAN capabilities.
     PORT_RX_OFFLOAD_VLAN = auto()
     QUEUE_RX_OFFLOAD_VLAN = auto()
+    #: Device supports selective Rx.
+    SELECTIVE_RX = auto()
     #: Device supports Rx queue setup after device started.
     RUNTIME_RX_QUEUE_SETUP = auto()
     #: Device supports Tx queue setup after device started.
diff --git a/dts/api/testpmd/__init__.py b/dts/api/testpmd/__init__.py
index e9187440bb..6973a64573 100644
--- a/dts/api/testpmd/__init__.py
+++ b/dts/api/testpmd/__init__.py
@@ -1409,6 +1409,23 @@ def get_capabilities_show_port_info(
             self.ports[0].device_capabilities,
         )
 
+    def get_capabilities_selective_rx(
+        self,
+        supported_capabilities: MutableSet["NicCapability"],
+        unsupported_capabilities: MutableSet["NicCapability"],
+    ) -> None:
+        """Get selective Rx capability from show port info.
+
+        Args:
+            supported_capabilities: Supported capabilities will be added to this set.
+            unsupported_capabilities: Unsupported capabilities will be added to this set.
+        """
+        port_info = self.show_port_info(self.ports[0].id)
+        if port_info.selective_rx:
+            supported_capabilities.add(NicCapability.SELECTIVE_RX)
+        else:
+            unsupported_capabilities.add(NicCapability.SELECTIVE_RX)
+
     def get_capabilities_mcast_filtering(
         self,
         supported_capabilities: MutableSet["NicCapability"],
diff --git a/dts/api/testpmd/types.py b/dts/api/testpmd/types.py
index 0d322aece2..6f1eaf47cc 100644
--- a/dts/api/testpmd/types.py
+++ b/dts/api/testpmd/types.py
@@ -614,6 +614,12 @@ def _validate(info: str) -> str | None:
         metadata=VLANOffloadFlag.make_parser(),
     )
 
+    #: Selective Rx support
+    selective_rx: bool = field(
+        default=False,
+        metadata=TextParser.find(r"Selective Rx: supported"),
+    )
+
     #: Maximum size of RX buffer
     max_rx_bufsize: int | None = field(
         default=None, metadata=TextParser.find_int(r"Maximum size of RX buffer: (\d+)")
diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py
index 96e1cd449f..b10799ea4b 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/framework/testbed_model/capability.py
@@ -324,6 +324,8 @@ def mapping(cap: NicCapability) -> TestPmdNicCapability:
                     | NicCapability.FLOW_SHARED_OBJECT_KEEP
                 ):
                     return (TestPmd.get_capabilities_show_port_info, None)
+                case NicCapability.SELECTIVE_RX:
+                    return (TestPmd.get_capabilities_selective_rx, None)
                 case NicCapability.MCAST_FILTERING:
                     return (TestPmd.get_capabilities_mcast_filtering, None)
                 case NicCapability.FLOW_CTRL:
diff --git a/dts/tests/TestSuite_rx_split.py b/dts/tests/TestSuite_rx_split.py
new file mode 100644
index 0000000000..0c7913bbd8
--- /dev/null
+++ b/dts/tests/TestSuite_rx_split.py
@@ -0,0 +1,277 @@
+# SPDX-License-Identifier: BSD-3-Clause
+# Copyright(c) 2026 NVIDIA Corporation & Affiliates
+
+"""Rx split test suite.
+
+Test configuring a packet split on Rx,
+and discarding some segments (selective Rx) at NIC level.
+"""
+
+from collections.abc import Callable
+from typing import Any
+
+from scapy.layers.inet import IP
+from scapy.layers.l2 import Ether
+from scapy.packet import Packet, Raw
+
+from api.capabilities import (
+    NicCapability,
+    requires_nic_capability,
+)
+from api.packet import adjust_addresses, send_packet_and_capture
+from api.test import fail, verify
+from api.testpmd import TestPmd
+from api.testpmd.config import SimpleForwardingModes
+from api.testpmd.types import RxOffloadCapability, TxOffloadCapability
+from framework.exception import InteractiveCommandExecutionError
+from framework.test_suite import TestSuite, func_test
+
+PAYLOAD = bytes(range(256))
+ETHER_HDR_LEN = len(Ether())
+IP_HDR_LEN = len(IP())
+ETHER_IP_HDR_LEN = ETHER_HDR_LEN + IP_HDR_LEN
+ETHER_MIN_FRAME_LEN = 60
+
+
+@requires_nic_capability(NicCapability.PORT_RX_OFFLOAD_BUFFER_SPLIT)
+@requires_nic_capability(NicCapability.SELECTIVE_RX)
+class TestRxSplit(TestSuite):
+    """Rx split test suite.
+
+    Configure testpmd with various Rx segment offset/length combinations
+    and verify that only the requested portions of the packet are received
+    and forwarded.
+    """
+
+    def _create_testpmd(self, **kwargs: Any) -> TestPmd:
+        """Create a TestPmd instance with defaults overridden by kwargs."""
+        defaults: dict[str, Any] = {
+            "forward_mode": SimpleForwardingModes.io,
+            "rx_offloads": RxOffloadCapability.BUFFER_SPLIT | RxOffloadCapability.SCATTER,
+        }
+        return TestPmd(**{**defaults, **kwargs})
+
+    def _build_packet(self) -> Packet:
+        """Build a test packet with an incrementing byte pattern payload."""
+        packet = Ether() / IP() / Raw(load=PAYLOAD)
+        return adjust_addresses([packet])[0]
+
+    def _start_and_verify(self, testpmd: TestPmd, expected: Callable[[bytes], bytes]) -> None:
+        """Start testpmd, send the default packet, and verify received bytes."""
+        testpmd.start()
+        packet = self._build_packet()
+        self._send_and_verify(testpmd, packet, expected(bytes(packet)))
+
+    def _send_and_verify(self, testpmd: TestPmd, tg_packet: Packet, expected: bytes) -> None:
+        """Clear stats, send a packet, and verify received content and stats.
+
+        Args:
+            testpmd: The running testpmd instance.
+            tg_packet: The packet to send by Scapy on the TG.
+            expected: Expected raw bytes received by testpmd on the SUT.
+        """
+        testpmd.clear_port_stats_all(verify=False)
+
+        # TG send, SUT receive and forward back, then TG capture
+        sut_len = len(expected)
+        capture_len = max(sut_len, ETHER_MIN_FRAME_LEN)
+        received = send_packet_and_capture(tg_packet)
+        verify(
+            len(received) > 0,
+            "Did not receive any packets.",
+        )
+
+        recv_bytes = bytes(received[0])
+        verify(
+            len(recv_bytes) == capture_len,
+            f"Expected packet length {capture_len}, got {len(recv_bytes)}.",
+        )
+        verify(
+            recv_bytes[:sut_len] == expected,
+            "Received packet content does not match expected bytes.",
+        )
+
+        all_stats, _ = testpmd.show_port_stats_all()
+        total_rx_packets = sum(s.rx_packets for s in all_stats)
+        total_rx_bytes = sum(s.rx_bytes for s in all_stats)
+        verify(
+            total_rx_packets == 1,
+            f"Expected 1 Rx packet, got {total_rx_packets}.",
+        )
+        verify(
+            total_rx_bytes == sut_len,
+            f"Expected {sut_len} Rx bytes, got {total_rx_bytes}.",
+        )
+
+    def _verify_port_start_fails(self, testpmd: TestPmd, message: str) -> None:
+        """Verify that starting ports fails, then drain testpmd output."""
+        try:
+            testpmd.start_all_ports()
+            fail(message)
+        except InteractiveCommandExecutionError:
+            testpmd.stop(verify=False)
+
+    @func_test
+    def selective_rx_headers(self) -> None:
+        """Keep only the Ethernet + IP headers, discard the rest.
+
+        Steps:
+            Start testpmd with rxpkts, mbuf-size and buffer split enabled.
+            Configure the payload discard segment with length 0 (rest).
+            Send an Ether/IP/payload packet.
+
+        Verify:
+            Received packet has Ether + IP headers only.
+            Port stats show expected rx_packets and rx_bytes.
+        """
+        with self._create_testpmd(
+            rx_segments_length=[ETHER_IP_HDR_LEN, 0],
+            mbuf_size=[256, 0],
+        ) as testpmd:
+
+            def expected(packet: bytes) -> bytes:
+                return packet[:ETHER_IP_HDR_LEN]
+
+            self._start_and_verify(testpmd, expected)
+
+    @func_test
+    def selective_rx_headers_discard_length(self) -> None:
+        """Keep only the Ethernet + IP headers, discard the remaining length.
+
+        Steps:
+            Start testpmd with rxpkts, mbuf-size and buffer split enabled.
+            Configure the payload discard segment with an explicit length.
+            Send an Ether/IP/payload packet.
+
+        Verify:
+            Received packet has Ether + IP headers only.
+            Port stats show expected rx_packets and rx_bytes.
+        """
+        with self._create_testpmd(
+            rx_segments_length=[ETHER_IP_HDR_LEN, len(PAYLOAD)],
+            mbuf_size=[256, 0],
+        ) as testpmd:
+
+            def expected(packet: bytes) -> bytes:
+                return packet[:ETHER_IP_HDR_LEN]
+
+            self._start_and_verify(testpmd, expected)
+
+    @func_test
+    def selective_rx_payload_only(self) -> None:
+        """Skip the Ethernet + IP headers, keep only the payload.
+
+        Steps:
+            Start testpmd with rxpkts, mbuf-size and buffer split enabled.
+            Send an Ether/IP/payload packet.
+
+        Verify:
+            Received packet is matching the original payload.
+            Port stats show expected rx_packets and rx_bytes.
+        """
+        with self._create_testpmd(
+            rx_segments_length=[ETHER_IP_HDR_LEN, len(PAYLOAD)],
+            mbuf_size=[0, 512],
+        ) as testpmd:
+
+            def expected(_: bytes) -> bytes:
+                return PAYLOAD
+
+            self._start_and_verify(testpmd, expected)
+
+    @func_test
+    def selective_rx_two_segments(self) -> None:
+        """Keep the IP header and the middle of the payload, skip the rest.
+
+        Steps:
+            Start testpmd with rxpkts, mbuf-size, buffer split
+            and multi-segment Tx enabled.
+            Send an Ether/IP/payload packet.
+
+        Verify:
+            Received packet is matching the IP header and middle of payload.
+            Port stats show expected rx_packets and rx_bytes.
+        """
+        payload_offset = 100
+        payload_length = 100
+        with self._create_testpmd(
+            tx_offloads=TxOffloadCapability.MULTI_SEGS,
+            rx_segments_length=[ETHER_HDR_LEN, IP_HDR_LEN, payload_offset, payload_length, 0],
+            mbuf_size=[0, 256, 0, 256, 0],
+        ) as testpmd:
+
+            def expected(packet: bytes) -> bytes:
+                payload_start = ETHER_IP_HDR_LEN + payload_offset
+                return (
+                    packet[ETHER_HDR_LEN:ETHER_IP_HDR_LEN]
+                    + packet[payload_start : payload_start + payload_length]
+                )
+
+            self._start_and_verify(testpmd, expected)
+
+    @func_test
+    def selective_rx_no_offload(self) -> None:
+        """Configure selective Rx with buffer split disabled.
+
+        Steps:
+            Start testpmd with rxpkts, mbuf-size including a discard segment,
+            buffer split disabled, and device start disabled.
+            Attempt to start ports.
+
+        Verify:
+            Queue configuration fails.
+        """
+        with self._create_testpmd(
+            rx_offloads=None,
+            rx_segments_length=[ETHER_IP_HDR_LEN, 0],
+            mbuf_size=[256, 0],
+            disable_device_start=True,
+        ) as testpmd:
+            self._verify_port_start_fails(
+                testpmd,
+                "Expected configuration to fail with buffer split disabled.",
+            )
+
+    @func_test
+    def selective_rx_segment_exceeds_mbuf(self) -> None:
+        """Configure selective Rx with segment length exceeding mbuf capacity.
+
+        Steps:
+            Start testpmd with rxpkts larger than mbuf-size,
+            buffer split enabled, and device start disabled.
+            Attempt to start ports.
+
+        Verify:
+            Queue configuration fails.
+        """
+        with self._create_testpmd(
+            rx_segments_length=[4096, 0],
+            mbuf_size=[128, 0],
+            disable_device_start=True,
+        ) as testpmd:
+            self._verify_port_start_fails(
+                testpmd,
+                "Expected configuration to fail with segment > mbuf size.",
+            )
+
+    @func_test
+    def selective_rx_all_discard(self) -> None:
+        """Configure selective Rx with only discard segment.
+
+        Steps:
+            Start testpmd with only discard segment,
+            buffer split enabled, and device start disabled.
+            Attempt to start ports.
+
+        Verify:
+            Queue configuration fails.
+        """
+        with self._create_testpmd(
+            rx_segments_length=[0],
+            mbuf_size=[0],
+            disable_device_start=True,
+        ) as testpmd:
+            self._verify_port_start_fails(
+                testpmd,
+                "Expected configuration to fail with only discard segment.",
+            )
-- 
2.54.0


^ permalink raw reply related

* [PATCH v9 09/10] dts: use specific types for Rx/Tx offloads
From: Thomas Monjalon @ 2026-06-05 23:33 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Luca Vizzarro, Patrick Robb
In-Reply-To: <20260605233456.3017423-1-thomas@monjalon.net>

Testpmd Rx and Tx offload parameters only accepted integer masks.
This forced tests to pass enum values through .value
when using RxOffloadCapability or TxOffloadCapability.

Allow these parameters to take either typed offload flags
or raw integer masks,
and convert both forms to the hexadecimal mask
expected by the testpmd command line.

Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 dts/api/testpmd/config.py        | 11 +++++++++--
 dts/framework/params/__init__.py | 14 ++++++++++++++
 dts/framework/params/types.py    |  5 +++--
 3 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/dts/api/testpmd/config.py b/dts/api/testpmd/config.py
index 1e59bccd08..f0581843ca 100644
--- a/dts/api/testpmd/config.py
+++ b/dts/api/testpmd/config.py
@@ -19,6 +19,7 @@
     YesNoSwitch,
     bracketed,
     comma_separated,
+    hex_from_flag_or_int,
     hex_from_flag_value,
     modify_str,
     str_from_flag_value,
@@ -26,6 +27,8 @@
 from framework.params.eal import EalParams
 from framework.utils import StrEnum
 
+from .types import RxOffloadCapability, TxOffloadCapability
+
 
 class PortTopology(StrEnum):
     """Enum representing the port topology."""
@@ -577,12 +580,16 @@ class TestPmdParams(EalParams):
     )
     multi_rx_mempool: Switch = None
     rx_shared_queue: Switch | int = field(default=None, metadata=Params.long("rxq-share"))
-    rx_offloads: int | None = field(default=None, metadata=Params.convert_value(hex))
+    rx_offloads: RxOffloadCapability | int | None = field(
+        default=None, metadata=Params.convert_value(hex_from_flag_or_int)
+    )
     rx_mq_mode: RXMultiQueueMode | None = None
 
     tx_queues: int | None = field(default=None, metadata=Params.long("txq"))
     tx_ring: TXRingParams | None = None
-    tx_offloads: int | None = field(default=None, metadata=Params.convert_value(hex))
+    tx_offloads: TxOffloadCapability | int | None = field(
+        default=None, metadata=Params.convert_value(hex_from_flag_or_int)
+    )
 
     eth_link_speed: int | None = None
     disable_link_check: Switch = None
diff --git a/dts/framework/params/__init__.py b/dts/framework/params/__init__.py
index e6a2d3c903..b5bae9dad9 100644
--- a/dts/framework/params/__init__.py
+++ b/dts/framework/params/__init__.py
@@ -130,6 +130,20 @@ def hex_from_flag_value(flag: Flag) -> str:
     return hex(flag.value)
 
 
+def hex_from_flag_or_int(value: Flag | int) -> str:
+    """Returns a :class:`enum.Flag` or integer value converted to hexadecimal.
+
+    Args:
+        value: An instance of :class:`Flag` or an integer.
+
+    Returns:
+        The value in hexadecimal representation.
+    """
+    if isinstance(value, Flag):
+        return hex_from_flag_value(value)
+    return hex(value)
+
+
 class ParamsModifier(TypedDict, total=False):
     """Params modifiers dict compatible with the :func:`dataclasses.field` metadata parameter."""
 
diff --git a/dts/framework/params/types.py b/dts/framework/params/types.py
index 3c7650474c..a9f3749083 100644
--- a/dts/framework/params/types.py
+++ b/dts/framework/params/types.py
@@ -53,6 +53,7 @@ def create_testpmd(**kwargs: Unpack[TestPmdParamsDict]):
     TXRingParams,
     TxUDPPortPair,
 )
+from api.testpmd.types import RxOffloadCapability, TxOffloadCapability
 from framework.params import Switch, YesNoSwitch
 from framework.testbed_model.cpu import LogicalCoreList
 from framework.testbed_model.port import Port
@@ -175,11 +176,11 @@ class TestPmdParamsDict(EalParamsDict, total=False):
     rx_segments_length: list[int] | None
     multi_rx_mempool: Switch
     rx_shared_queue: Switch | int
-    rx_offloads: int | None
+    rx_offloads: RxOffloadCapability | int | None
     rx_mq_mode: RXMultiQueueMode | None
     tx_queues: int | None
     tx_ring: TXRingParams | None
-    tx_offloads: int | None
+    tx_offloads: TxOffloadCapability | int | None
     eth_link_speed: int | None
     disable_link_check: Switch
     disable_device_start: Switch
-- 
2.54.0


^ permalink raw reply related

* [PATCH v9 08/10] dts: fix topology capability comparison
From: Thomas Monjalon @ 2026-06-05 23:33 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, stable, Luca Vizzarro, Patrick Robb,
	Juraj Linkeš, Dean Marx, Jeremy Spewock
In-Reply-To: <20260605233456.3017423-1-thomas@monjalon.net>

TopologyCapability.__gt__() was delegating to __lt__(),
which caused infinite recursion when "other" is not a TopologyCapability:
other.__lt__(self) returns NotImplemented,
Python retries with self.__gt__(other),
and the cycle repeats.

dts/framework/testbed_model/capability.py", line 579, in __gt__
        return other < self
               ^^^^^^^^^^^^
    RecursionError: maximum recursion depth exceeded

Similarly, __le__() was delegating to "not __gt__()",
which returns True for non-comparable types instead of False.

Fix both by checking is_comparable_with() first
and comparing topology_type directly, consistent with __lt__().

Fixes: 039256daa8bf ("dts: add topology capability")
Cc: stable@dpdk.org

Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 dts/framework/testbed_model/capability.py | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/dts/framework/testbed_model/capability.py b/dts/framework/testbed_model/capability.py
index 960370fc72..96e1cd449f 100644
--- a/dts/framework/testbed_model/capability.py
+++ b/dts/framework/testbed_model/capability.py
@@ -574,7 +574,9 @@ def __gt__(self, other: Any) -> bool:
         Returns:
             :data:`True` if the instance's topology type is more complex than the compared object's.
         """
-        return other < self
+        if not self.is_comparable_with(other):
+            return False
+        return self.topology_type > other.topology_type
 
     def __le__(self, other: Any) -> bool:
         """Compare the :attr:`~TopologyCapability.topology_type`s.
@@ -586,7 +588,9 @@ def __le__(self, other: Any) -> bool:
             :data:`True` if the instance's topology type is less complex or equal than
             the compared object's.
         """
-        return not self > other
+        if not self.is_comparable_with(other):
+            return False
+        return self.topology_type <= other.topology_type
 
     def __hash__(self):
         """Each instance is identified by :attr:`topology_type`."""
-- 
2.54.0


^ permalink raw reply related

* [PATCH v9 07/10] common/mlx5: remove callbacks for MR registration
From: Thomas Monjalon @ 2026-06-05 23:33 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, Dariusz Sosnowski, Viacheslav Ovsiienko,
	Bing Zhao, Ori Kam, Suanming Mou, Matan Azrad, Fan Zhang,
	Ashish Gupta
In-Reply-To: <20260605233456.3017423-1-thomas@monjalon.net>

The functions register/unregister for a Memory Region (MR)
were not called directly.
There are only 2 implementations for Linux and Windows,
no need of handling this difference with function pointers.
The callback pointers are replaced with direct calls
and link time decision based on the Operating System.

Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 drivers/common/mlx5/linux/mlx5_common_verbs.c | 26 +++----------
 drivers/common/mlx5/mlx5_common.c             |  6 +--
 drivers/common/mlx5/mlx5_common_mr.c          | 37 ++++++++-----------
 drivers/common/mlx5/mlx5_common_mr.h          | 26 +++----------
 drivers/common/mlx5/windows/mlx5_common_os.c  | 23 ++----------
 drivers/compress/mlx5/mlx5_compress.c         |  4 +-
 drivers/crypto/mlx5/mlx5_crypto.h             |  2 -
 drivers/crypto/mlx5/mlx5_crypto_gcm.c         |  6 +--
 drivers/net/mlx5/mlx5.h                       |  3 +-
 drivers/net/mlx5/mlx5_flow_aso.c              | 21 +++++------
 drivers/net/mlx5/mlx5_flow_hw.c               | 11 ++----
 drivers/net/mlx5/mlx5_flow_quota.c            |  6 +--
 drivers/net/mlx5/mlx5_hws_cnt.c               | 19 ++++------
 13 files changed, 61 insertions(+), 129 deletions(-)

diff --git a/drivers/common/mlx5/linux/mlx5_common_verbs.c b/drivers/common/mlx5/linux/mlx5_common_verbs.c
index 6d44e1f566..5e23c5844d 100644
--- a/drivers/common/mlx5/linux/mlx5_common_verbs.c
+++ b/drivers/common/mlx5/linux/mlx5_common_verbs.c
@@ -106,10 +106,10 @@ mlx5_set_context_attr(struct rte_device *dev, struct ibv_context *ctx)
  * @return
  *   0 on successful registration, -1 otherwise
  */
-RTE_EXPORT_INTERNAL_SYMBOL(mlx5_common_verbs_reg_mr)
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_reg_mr)
 int
-mlx5_common_verbs_reg_mr(void *pd, void *addr, size_t length,
-			 struct mlx5_pmd_mr *pmd_mr)
+mlx5_os_reg_mr(void *pd, void *addr, size_t length,
+		struct mlx5_pmd_mr *pmd_mr)
 {
 	struct ibv_mr *ibv_mr;
 
@@ -136,9 +136,9 @@ mlx5_common_verbs_reg_mr(void *pd, void *addr, size_t length,
  *   pmd_mr struct set with lkey, address, length and pointer to mr object
  *
  */
-RTE_EXPORT_INTERNAL_SYMBOL(mlx5_common_verbs_dereg_mr)
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_dereg_mr)
 void
-mlx5_common_verbs_dereg_mr(struct mlx5_pmd_mr *pmd_mr)
+mlx5_os_dereg_mr(struct mlx5_pmd_mr *pmd_mr)
 {
 	if (pmd_mr && pmd_mr->obj != NULL) {
 		claim_zero(mlx5_glue->dereg_mr(pmd_mr->obj));
@@ -146,22 +146,6 @@ mlx5_common_verbs_dereg_mr(struct mlx5_pmd_mr *pmd_mr)
 	}
 }
 
-/**
- * Set the reg_mr and dereg_mr callbacks.
- *
- * @param[out] reg_mr_cb
- *   Pointer to reg_mr func
- * @param[out] dereg_mr_cb
- *   Pointer to dereg_mr func
- */
-RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_set_reg_mr_cb)
-void
-mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb)
-{
-	*reg_mr_cb = mlx5_common_verbs_reg_mr;
-	*dereg_mr_cb = mlx5_common_verbs_dereg_mr;
-}
-
 RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_alloc_null_mr)
 struct mlx5_pmd_mr *
 mlx5_os_alloc_null_mr(struct rte_device *dev, void *pd)
diff --git a/drivers/common/mlx5/mlx5_common.c b/drivers/common/mlx5/mlx5_common.c
index f87dc9d773..f4b8904366 100644
--- a/drivers/common/mlx5/mlx5_common.c
+++ b/drivers/common/mlx5/mlx5_common.c
@@ -1135,7 +1135,7 @@ mlx5_common_dev_dma_map(struct rte_device *rte_dev, void *addr,
 		return -1;
 	}
 	mr = mlx5_create_mr_ext(dev->pd, (uintptr_t)addr, len,
-				SOCKET_ID_ANY, dev->mr_scache.reg_mr_cb);
+				SOCKET_ID_ANY);
 	if (!mr) {
 		DRV_LOG(WARNING, "Device %s unable to DMA map", rte_dev->name);
 		rte_errno = EINVAL;
@@ -1165,7 +1165,7 @@ mlx5_common_dev_dma_map(struct rte_device *rte_dev, void *addr,
 		ret = mlx5_mr_expand_cache(&dev->mr_scache, size,
 					   rte_dev->numa_node);
 		if (ret < 0) {
-			mlx5_mr_free(mr, dev->mr_scache.dereg_mr_cb);
+			mlx5_mr_free(mr);
 			rte_errno = ret;
 			return -1;
 		}
@@ -1221,7 +1221,7 @@ mlx5_common_dev_dma_unmap(struct rte_device *rte_dev, void *addr,
 	}
 	LIST_REMOVE(mr, mr);
 	DRV_LOG(DEBUG, "MR(%p) is removed from list.", (void *)mr);
-	mlx5_mr_free(mr, dev->mr_scache.dereg_mr_cb);
+	mlx5_mr_free(mr);
 	mlx5_mr_rebuild_cache(&dev->mr_scache);
 	/*
 	 * No explicit wmb is needed after updating dev_gen due to
diff --git a/drivers/common/mlx5/mlx5_common_mr.c b/drivers/common/mlx5/mlx5_common_mr.c
index 64ffc7f4ea..aa2d5e88a4 100644
--- a/drivers/common/mlx5/mlx5_common_mr.c
+++ b/drivers/common/mlx5/mlx5_common_mr.c
@@ -492,12 +492,12 @@ mlx5_mr_lookup_cache(struct mlx5_mr_share_cache *share_cache,
  *   Pointer to MR to free.
  */
 void
-mlx5_mr_free(struct mlx5_mr *mr, mlx5_dereg_mr_t dereg_mr_cb)
+mlx5_mr_free(struct mlx5_mr *mr)
 {
 	if (mr == NULL)
 		return;
 	DRV_LOG(DEBUG, "freeing MR(%p):", (void *)mr);
-	dereg_mr_cb(&mr->pmd_mr);
+	mlx5_os_dereg_mr(&mr->pmd_mr);
 	rte_bitmap_free(mr->ms_bmp);
 	mlx5_free(mr);
 }
@@ -545,7 +545,7 @@ mlx5_mr_garbage_collect(struct mlx5_mr_share_cache *share_cache)
 		struct mlx5_mr *mr = mr_next;
 
 		mr_next = LIST_NEXT(mr, mr);
-		mlx5_mr_free(mr, share_cache->dereg_mr_cb);
+		mlx5_mr_free(mr);
 	}
 }
 
@@ -821,7 +821,7 @@ mlx5_mr_create_primary(void *pd,
 		data.start = RTE_ALIGN_FLOOR(addr, msl->page_sz);
 		data.end = data.start + msl->page_sz;
 		rte_mcfg_mem_read_unlock();
-		mlx5_mr_free(mr, share_cache->dereg_mr_cb);
+		mlx5_mr_free(mr);
 		goto alloc_resources;
 	}
 	MLX5_ASSERT(data.msl == data_re.msl);
@@ -845,7 +845,7 @@ mlx5_mr_create_primary(void *pd,
 		 * Must be unlocked before calling rte_free() because
 		 * mlx5_mr_mem_event_free_cb() can be called inside.
 		 */
-		mlx5_mr_free(mr, share_cache->dereg_mr_cb);
+		mlx5_mr_free(mr);
 		return entry->lkey;
 	}
 	/*
@@ -912,7 +912,7 @@ mlx5_mr_create_primary(void *pd,
 	 * mlx5_alloc_buf_extern() which eventually calls rte_malloc_socket()
 	 * through mlx5_alloc_verbs_buf().
 	 */
-	share_cache->reg_mr_cb(pd, (void *)data.start, len, &mr->pmd_mr);
+	mlx5_os_reg_mr(pd, (void *)data.start, len, &mr->pmd_mr);
 	if (mr->pmd_mr.obj == NULL) {
 		DRV_LOG(DEBUG, "Fail to create an MR for address (%p)",
 		      (void *)addr);
@@ -948,7 +948,7 @@ mlx5_mr_create_primary(void *pd,
 	 * calling rte_free() because mlx5_mr_mem_event_free_cb() can be called
 	 * inside.
 	 */
-	mlx5_mr_free(mr, share_cache->dereg_mr_cb);
+	mlx5_mr_free(mr);
 	return UINT32_MAX;
 }
 
@@ -1139,9 +1139,6 @@ mlx5_mr_release_cache(struct mlx5_mr_share_cache *share_cache)
 int
 mlx5_mr_create_cache(struct mlx5_mr_share_cache *share_cache, int socket)
 {
-	/* Set the reg_mr and dereg_mr callback functions */
-	mlx5_os_set_reg_mr_cb(&share_cache->reg_mr_cb,
-			      &share_cache->dereg_mr_cb);
 	rte_rwlock_init(&share_cache->rwlock);
 	rte_rwlock_init(&share_cache->mprwlock);
 	/* Initialize B-tree and allocate memory for global MR cache table. */
@@ -1189,8 +1186,7 @@ mlx5_mr_flush_local_cache(struct mlx5_mr_ctrl *mr_ctrl)
  *   Pointer to MR structure on success, NULL otherwise.
  */
 struct mlx5_mr *
-mlx5_create_mr_ext(void *pd, uintptr_t addr, size_t len, int socket_id,
-		   mlx5_reg_mr_t reg_mr_cb)
+mlx5_create_mr_ext(void *pd, uintptr_t addr, size_t len, int socket_id)
 {
 	struct mlx5_mr *mr = NULL;
 
@@ -1199,7 +1195,7 @@ mlx5_create_mr_ext(void *pd, uintptr_t addr, size_t len, int socket_id,
 			 RTE_CACHE_LINE_SIZE, socket_id);
 	if (mr == NULL)
 		return NULL;
-	reg_mr_cb(pd, (void *)addr, len, &mr->pmd_mr);
+	mlx5_os_reg_mr(pd, (void *)addr, len, &mr->pmd_mr);
 	if (mr->pmd_mr.obj == NULL) {
 		DRV_LOG(WARNING,
 			"Fail to create MR for address (%p)",
@@ -1624,14 +1620,13 @@ mlx5_mempool_reg_create(struct rte_mempool *mp, unsigned int mrs_n,
  *   Whether @p mpr owns its MRs exclusively, i.e. they are not shared.
  */
 static void
-mlx5_mempool_reg_destroy(struct mlx5_mr_share_cache *share_cache,
-			 struct mlx5_mempool_reg *mpr, bool standalone)
+mlx5_mempool_reg_destroy(struct mlx5_mempool_reg *mpr, bool standalone)
 {
 	if (standalone) {
 		unsigned int i;
 
 		for (i = 0; i < mpr->mrs_n; i++)
-			share_cache->dereg_mr_cb(&mpr->mrs[i].pmd_mr);
+			mlx5_os_dereg_mr(&mpr->mrs[i].pmd_mr);
 		mlx5_free(mpr->mrs);
 	}
 	mlx5_free(mpr);
@@ -1748,7 +1743,7 @@ mlx5_mr_mempool_register_primary(struct mlx5_mr_share_cache *share_cache,
 		const struct mlx5_range *range = &ranges[i];
 		size_t len = range->end - range->start;
 
-		if (share_cache->reg_mr_cb(pd, (void *)range->start, len,
+		if (mlx5_os_reg_mr(pd, (void *)range->start, len,
 		    &mr->pmd_mr) < 0) {
 			DRV_LOG(ERR,
 				"Failed to create an MR in PD %p for address range "
@@ -1763,7 +1758,7 @@ mlx5_mr_mempool_register_primary(struct mlx5_mr_share_cache *share_cache,
 			mp->name);
 	}
 	if (i != ranges_n) {
-		mlx5_mempool_reg_destroy(share_cache, new_mpr, true);
+		mlx5_mempool_reg_destroy(new_mpr, true);
 		rte_errno = EINVAL;
 		goto exit;
 	}
@@ -1785,13 +1780,13 @@ mlx5_mr_mempool_register_primary(struct mlx5_mr_share_cache *share_cache,
 	if (mpr != NULL) {
 		DRV_LOG(DEBUG, "Mempool %s is already registered for PD %p",
 			mp->name, pd);
-		mlx5_mempool_reg_destroy(share_cache, new_mpr, true);
+		mlx5_mempool_reg_destroy(new_mpr, true);
 		rte_errno = EEXIST;
 		goto exit;
 	} else if (old_mpr != NULL) {
 		DRV_LOG(DEBUG, "Mempool %s registration for PD %p updated for external memory",
 			mp->name, pd);
-		mlx5_mempool_reg_destroy(share_cache, old_mpr, standalone);
+		mlx5_mempool_reg_destroy(old_mpr, standalone);
 	}
 exit:
 	free(ranges);
@@ -1860,7 +1855,7 @@ mlx5_mr_mempool_unregister_primary(struct mlx5_mr_share_cache *share_cache,
 		rte_errno = ENOENT;
 		return -1;
 	}
-	mlx5_mempool_reg_destroy(share_cache, mpr, standalone);
+	mlx5_mempool_reg_destroy(mpr, standalone);
 	return 0;
 }
 
diff --git a/drivers/common/mlx5/mlx5_common_mr.h b/drivers/common/mlx5/mlx5_common_mr.h
index 00f3d832c3..5fb931a1b5 100644
--- a/drivers/common/mlx5/mlx5_common_mr.h
+++ b/drivers/common/mlx5/mlx5_common_mr.h
@@ -32,13 +32,6 @@ struct mlx5_pmd_mr {
 	struct mlx5_devx_obj *mkey; /* devx mkey object. */
 };
 
-/**
- * mr operations typedef
- */
-typedef int (*mlx5_reg_mr_t)(void *pd, void *addr, size_t length,
-			     struct mlx5_pmd_mr *pmd_mr);
-typedef void (*mlx5_dereg_mr_t)(struct mlx5_pmd_mr *pmd_mr);
-
 /* Memory Region object. */
 struct mlx5_mr {
 	LIST_ENTRY(mlx5_mr) mr; /**< Pointer to the prev/next entry. */
@@ -88,8 +81,6 @@ struct __rte_packed_begin mlx5_mr_share_cache {
 	struct mlx5_mr_list mr_list; /* Registered MR list. */
 	struct mlx5_mr_list mr_free_list; /* Freed MR list. */
 	struct mlx5_mempool_reg_list mempool_reg_list; /* Mempool database. */
-	mlx5_reg_mr_t reg_mr_cb; /* Callback to reg_mr func */
-	mlx5_dereg_mr_t dereg_mr_cb; /* Callback to dereg_mr func */
 } __rte_packed_end;
 
 /* Multi-Packet RQ buffer header. */
@@ -233,9 +224,8 @@ struct mlx5_mr *
 mlx5_mr_lookup_list(struct mlx5_mr_share_cache *share_cache,
 		    struct mr_cache_entry *entry, uintptr_t addr);
 struct mlx5_mr *
-mlx5_create_mr_ext(void *pd, uintptr_t addr, size_t len, int socket_id,
-		   mlx5_reg_mr_t reg_mr_cb);
-void mlx5_mr_free(struct mlx5_mr *mr, mlx5_dereg_mr_t dereg_mr_cb);
+mlx5_create_mr_ext(void *pd, uintptr_t addr, size_t len, int socket_id);
+void mlx5_mr_free(struct mlx5_mr *mr);
 __rte_internal
 uint32_t
 mlx5_mr_create(struct mlx5_common_device *cdev,
@@ -246,19 +236,13 @@ __rte_internal
 uint32_t
 mlx5_mr_addr2mr_bh(struct mlx5_mr_ctrl *mr_ctrl, uintptr_t addr);
 
-/* mlx5_common_verbs.c */
-
 __rte_internal
 int
-mlx5_common_verbs_reg_mr(void *pd, void *addr, size_t length,
-			 struct mlx5_pmd_mr *pmd_mr);
+mlx5_os_reg_mr(void *pd, void *addr, size_t length,
+		struct mlx5_pmd_mr *pmd_mr);
 __rte_internal
 void
-mlx5_common_verbs_dereg_mr(struct mlx5_pmd_mr *pmd_mr);
-
-__rte_internal
-void
-mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb);
+mlx5_os_dereg_mr(struct mlx5_pmd_mr *pmd_mr);
 
 __rte_internal
 struct mlx5_pmd_mr *
diff --git a/drivers/common/mlx5/windows/mlx5_common_os.c b/drivers/common/mlx5/windows/mlx5_common_os.c
index fb2bbae578..c790c9a4ae 100644
--- a/drivers/common/mlx5/windows/mlx5_common_os.c
+++ b/drivers/common/mlx5/windows/mlx5_common_os.c
@@ -377,7 +377,8 @@ mlx5_os_umem_dereg(void *pumem)
  * @return
  *   0 on successful registration, -1 otherwise
  */
-static int
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_reg_mr)
+int
 mlx5_os_reg_mr(void *pd,
 	       void *addr, size_t length, struct mlx5_pmd_mr *pmd_mr)
 {
@@ -425,7 +426,8 @@ mlx5_os_reg_mr(void *pd,
  * @param[in] pmd_mr
  *  Pointer to PMD mr object
  */
-static void
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_dereg_mr)
+void
 mlx5_os_dereg_mr(struct mlx5_pmd_mr *pmd_mr)
 {
 	if (!pmd_mr)
@@ -437,23 +439,6 @@ mlx5_os_dereg_mr(struct mlx5_pmd_mr *pmd_mr)
 	memset(pmd_mr, 0, sizeof(*pmd_mr));
 }
 
-/**
- * Set the reg_mr and dereg_mr callbacks.
- *
- * @param[out] reg_mr_cb
- *   Pointer to reg_mr func
- * @param[out] dereg_mr_cb
- *   Pointer to dereg_mr func
- *
- */
-RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_set_reg_mr_cb)
-void
-mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb)
-{
-	*reg_mr_cb = mlx5_os_reg_mr;
-	*dereg_mr_cb = mlx5_os_dereg_mr;
-}
-
 RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_alloc_null_mr)
 struct mlx5_pmd_mr *
 mlx5_os_alloc_null_mr(struct rte_device *dev, void *pd)
diff --git a/drivers/compress/mlx5/mlx5_compress.c b/drivers/compress/mlx5/mlx5_compress.c
index e5325c6150..1361dab630 100644
--- a/drivers/compress/mlx5/mlx5_compress.c
+++ b/drivers/compress/mlx5/mlx5_compress.c
@@ -117,7 +117,7 @@ mlx5_compress_qp_release(struct rte_compressdev *dev, uint16_t qp_id)
 	if (qp->opaque_mr.obj != NULL) {
 		void *opaq = qp->opaque_mr.addr;
 
-		mlx5_common_verbs_dereg_mr(&qp->opaque_mr);
+		mlx5_os_dereg_mr(&qp->opaque_mr);
 		rte_free(opaq);
 	}
 	mlx5_mr_btree_free(&qp->mr_ctrl.cache_bh);
@@ -199,7 +199,7 @@ mlx5_compress_qp_setup(struct rte_compressdev *dev, uint16_t qp_id,
 	qp->priv = priv;
 	qp->ops = (struct rte_comp_op **)RTE_ALIGN((uintptr_t)(qp + 1),
 						   RTE_CACHE_LINE_SIZE);
-	if (mlx5_common_verbs_reg_mr(priv->cdev->pd, opaq_buf, qp->entries_n *
+	if (mlx5_os_reg_mr(priv->cdev->pd, opaq_buf, qp->entries_n *
 					sizeof(union mlx5_gga_compress_opaque),
 							 &qp->opaque_mr) != 0) {
 		rte_free(opaq_buf);
diff --git a/drivers/crypto/mlx5/mlx5_crypto.h b/drivers/crypto/mlx5/mlx5_crypto.h
index f9f127e9e6..93a2bb2c78 100644
--- a/drivers/crypto/mlx5/mlx5_crypto.h
+++ b/drivers/crypto/mlx5/mlx5_crypto.h
@@ -40,8 +40,6 @@ struct mlx5_crypto_priv {
 	TAILQ_ENTRY(mlx5_crypto_priv) next;
 	struct mlx5_common_device *cdev; /* Backend mlx5 device. */
 	struct rte_cryptodev *crypto_dev;
-	mlx5_reg_mr_t reg_mr_cb; /* Callback to reg_mr func */
-	mlx5_dereg_mr_t dereg_mr_cb; /* Callback to dereg_mr func */
 	struct mlx5_uar uar; /* User Access Region. */
 	uint32_t max_segs_num; /* Maximum supported data segs. */
 	uint32_t max_klm_num; /* Maximum supported klm. */
diff --git a/drivers/crypto/mlx5/mlx5_crypto_gcm.c b/drivers/crypto/mlx5/mlx5_crypto_gcm.c
index 89f32c7722..1a2600655a 100644
--- a/drivers/crypto/mlx5/mlx5_crypto_gcm.c
+++ b/drivers/crypto/mlx5/mlx5_crypto_gcm.c
@@ -219,7 +219,6 @@ mlx5_crypto_gcm_mkey_klm_update(struct mlx5_crypto_priv *priv,
 static int
 mlx5_crypto_gcm_qp_release(struct rte_cryptodev *dev, uint16_t qp_id)
 {
-	struct mlx5_crypto_priv *priv = dev->data->dev_private;
 	struct mlx5_crypto_qp *qp = dev->data->queue_pairs[qp_id];
 
 	if (qp->umr_qp_obj.qp != NULL)
@@ -231,7 +230,7 @@ mlx5_crypto_gcm_qp_release(struct rte_cryptodev *dev, uint16_t qp_id)
 	if (qp->mr.obj != NULL) {
 		void *opaq = qp->mr.addr;
 
-		priv->dereg_mr_cb(&qp->mr);
+		mlx5_os_dereg_mr(&qp->mr);
 		rte_free(opaq);
 	}
 	mlx5_crypto_indirect_mkeys_release(qp, qp->entries_n);
@@ -363,7 +362,7 @@ mlx5_crypto_gcm_qp_setup(struct rte_cryptodev *dev, uint16_t qp_id,
 		rte_errno = ENOMEM;
 		goto err;
 	}
-	if (priv->reg_mr_cb(priv->cdev->pd, mr_buf, mr_size, &qp->mr) != 0) {
+	if (mlx5_os_reg_mr(priv->cdev->pd, mr_buf, mr_size, &qp->mr) != 0) {
 		rte_free(mr_buf);
 		DRV_LOG(ERR, "Failed to register opaque MR.");
 		rte_errno = ENOMEM;
@@ -1186,7 +1185,6 @@ mlx5_crypto_gcm_init(struct mlx5_crypto_priv *priv)
 
 	/* Override AES-GCM specified ops. */
 	dev_ops->sym_session_configure = mlx5_crypto_sym_gcm_session_configure;
-	mlx5_os_set_reg_mr_cb(&priv->reg_mr_cb, &priv->dereg_mr_cb);
 	dev_ops->queue_pair_setup = mlx5_crypto_gcm_qp_setup;
 	dev_ops->queue_pair_release = mlx5_crypto_gcm_qp_release;
 	if (mlx5_crypto_is_ipsec_opt(priv)) {
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index bd6ef35b53..a4d5392e8f 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -2706,8 +2706,7 @@ int mlx5_aso_cnt_query(struct mlx5_dev_ctx_shared *sh,
 int mlx5_aso_ct_queue_init(struct mlx5_dev_ctx_shared *sh,
 			   struct mlx5_aso_ct_pools_mng *ct_mng,
 			   uint32_t nb_queues);
-int mlx5_aso_ct_queue_uninit(struct mlx5_dev_ctx_shared *sh,
-			     struct mlx5_aso_ct_pools_mng *ct_mng);
+int mlx5_aso_ct_queue_uninit(struct mlx5_aso_ct_pools_mng *ct_mng);
 int
 mlx5_aso_sq_create(struct mlx5_common_device *cdev, struct mlx5_aso_sq *sq,
 		   void *uar, uint16_t log_desc_n);
diff --git a/drivers/net/mlx5/mlx5_flow_aso.c b/drivers/net/mlx5/mlx5_flow_aso.c
index 5e2a81ef9c..cd84ab1966 100644
--- a/drivers/net/mlx5/mlx5_flow_aso.c
+++ b/drivers/net/mlx5/mlx5_flow_aso.c
@@ -19,17 +19,15 @@
 /**
  * Free MR resources.
  *
- * @param[in] cdev
- *   Pointer to the mlx5 common device.
  * @param[in] mr
  *   MR to free.
  */
 static void
-mlx5_aso_dereg_mr(struct mlx5_common_device *cdev, struct mlx5_pmd_mr *mr)
+mlx5_aso_dereg_mr(struct mlx5_pmd_mr *mr)
 {
 	void *addr = mr->addr;
 
-	cdev->mr_scache.dereg_mr_cb(mr);
+	mlx5_os_dereg_mr(mr);
 	mlx5_free(addr);
 	memset(mr, 0, sizeof(*mr));
 }
@@ -59,7 +57,7 @@ mlx5_aso_reg_mr(struct mlx5_common_device *cdev, size_t length,
 		DRV_LOG(ERR, "Failed to create ASO bits mem for MR.");
 		return -1;
 	}
-	ret = cdev->mr_scache.reg_mr_cb(cdev->pd, mr->addr, length, mr);
+	ret = mlx5_os_reg_mr(cdev->pd, mr->addr, length, mr);
 	if (ret) {
 		DRV_LOG(ERR, "Failed to create direct Mkey.");
 		mlx5_free(mr->addr);
@@ -362,7 +360,7 @@ mlx5_aso_queue_init(struct mlx5_dev_ctx_shared *sh,
 		if (mlx5_aso_sq_create(cdev, &sh->aso_age_mng->aso_sq,
 				       sh->tx_uar.obj,
 				       MLX5_ASO_QUEUE_LOG_DESC)) {
-			mlx5_aso_dereg_mr(cdev, &sh->aso_age_mng->aso_sq.mr);
+			mlx5_aso_dereg_mr(&sh->aso_age_mng->aso_sq.mr);
 			return -1;
 		}
 		mlx5_aso_age_init_sq(&sh->aso_age_mng->aso_sq);
@@ -399,14 +397,14 @@ mlx5_aso_queue_uninit(struct mlx5_dev_ctx_shared *sh,
 
 	switch (aso_opc_mod) {
 	case ASO_OPC_MOD_FLOW_HIT:
-		mlx5_aso_dereg_mr(sh->cdev, &sh->aso_age_mng->aso_sq.mr);
+		mlx5_aso_dereg_mr(&sh->aso_age_mng->aso_sq.mr);
 		sq = &sh->aso_age_mng->aso_sq;
 		break;
 	case ASO_OPC_MOD_POLICER:
 		mlx5_aso_mtr_queue_uninit(sh, NULL, &sh->mtrmng->pools_mng);
 		break;
 	case ASO_OPC_MOD_CONNECTION_TRACKING:
-		mlx5_aso_ct_queue_uninit(sh, sh->ct_mng);
+		mlx5_aso_ct_queue_uninit(sh->ct_mng);
 		break;
 	default:
 		DRV_LOG(ERR, "Unknown ASO operation mode");
@@ -1147,15 +1145,14 @@ __mlx5_aso_ct_get_pool(struct mlx5_dev_ctx_shared *sh,
 }
 
 int
-mlx5_aso_ct_queue_uninit(struct mlx5_dev_ctx_shared *sh,
-			 struct mlx5_aso_ct_pools_mng *ct_mng)
+mlx5_aso_ct_queue_uninit(struct mlx5_aso_ct_pools_mng *ct_mng)
 {
 	uint32_t i;
 
 	/* 64B per object for query. */
 	for (i = 0; i < ct_mng->nb_sq; i++) {
 		if (ct_mng->aso_sqs[i].mr.addr)
-			mlx5_aso_dereg_mr(sh->cdev, &ct_mng->aso_sqs[i].mr);
+			mlx5_aso_dereg_mr(&ct_mng->aso_sqs[i].mr);
 		mlx5_aso_destroy_sq(&ct_mng->aso_sqs[i]);
 	}
 	return 0;
@@ -1197,7 +1194,7 @@ mlx5_aso_ct_queue_init(struct mlx5_dev_ctx_shared *sh,
 error:
 	do {
 		if (ct_mng->aso_sqs[i].mr.addr)
-			mlx5_aso_dereg_mr(sh->cdev, &ct_mng->aso_sqs[i].mr);
+			mlx5_aso_dereg_mr(&ct_mng->aso_sqs[i].mr);
 		mlx5_aso_destroy_sq(&ct_mng->aso_sqs[i]);
 	} while (i--);
 	ct_mng->nb_sq = 0;
diff --git a/drivers/net/mlx5/mlx5_flow_hw.c b/drivers/net/mlx5/mlx5_flow_hw.c
index b6bb9f12a6..7cc601d681 100644
--- a/drivers/net/mlx5/mlx5_flow_hw.c
+++ b/drivers/net/mlx5/mlx5_flow_hw.c
@@ -11086,12 +11086,9 @@ flow_hw_create_nic_ctrl_tables(struct rte_eth_dev *dev, struct rte_flow_error *e
 }
 
 static void
-flow_hw_ct_mng_destroy(struct rte_eth_dev *dev,
-		       struct mlx5_aso_ct_pools_mng *ct_mng)
+flow_hw_ct_mng_destroy(struct mlx5_aso_ct_pools_mng *ct_mng)
 {
-	struct mlx5_priv *priv = dev->data->dev_private;
-
-	mlx5_aso_ct_queue_uninit(priv->sh, ct_mng);
+	mlx5_aso_ct_queue_uninit(ct_mng);
 	mlx5_free(ct_mng);
 }
 
@@ -11230,7 +11227,7 @@ mlx5_flow_ct_init(struct rte_eth_dev *dev,
 		priv->hws_ctpool = NULL;
 	}
 	if (priv->ct_mng) {
-		flow_hw_ct_mng_destroy(dev, priv->ct_mng);
+		flow_hw_ct_mng_destroy(priv->ct_mng);
 		priv->ct_mng = NULL;
 	}
 	return ret;
@@ -11804,7 +11801,7 @@ __mlx5_flow_hw_resource_release(struct rte_eth_dev *dev, bool ctx_close)
 		priv->hws_ctpool = NULL;
 	}
 	if (priv->ct_mng) {
-		flow_hw_ct_mng_destroy(dev, priv->ct_mng);
+		flow_hw_ct_mng_destroy(priv->ct_mng);
 		priv->ct_mng = NULL;
 	}
 	mlx5_flow_quota_destroy(dev);
diff --git a/drivers/net/mlx5/mlx5_flow_quota.c b/drivers/net/mlx5/mlx5_flow_quota.c
index d94167d0b0..b661bd376e 100644
--- a/drivers/net/mlx5/mlx5_flow_quota.c
+++ b/drivers/net/mlx5/mlx5_flow_quota.c
@@ -412,12 +412,11 @@ mlx5_quota_alloc_sq(struct mlx5_priv *priv)
 static void
 mlx5_quota_destroy_read_buf(struct mlx5_priv *priv)
 {
-	struct mlx5_dev_ctx_shared *sh = priv->sh;
 	struct mlx5_quota_ctx *qctx = &priv->quota_ctx;
 
 	if (qctx->mr.lkey) {
 		void *addr = qctx->mr.addr;
-		sh->cdev->mr_scache.dereg_mr_cb(&qctx->mr);
+		mlx5_os_dereg_mr(&qctx->mr);
 		mlx5_free(addr);
 	}
 	if (qctx->read_buf)
@@ -446,8 +445,7 @@ mlx5_quota_alloc_read_buf(struct mlx5_priv *priv)
 		DRV_LOG(DEBUG, "QUOTA: failed to allocate MTR ASO READ buffer [1]");
 		return -ENOMEM;
 	}
-	ret = sh->cdev->mr_scache.reg_mr_cb(sh->cdev->pd, buf,
-					    rd_buf_size, &qctx->mr);
+	ret = mlx5_os_reg_mr(sh->cdev->pd, buf, rd_buf_size, &qctx->mr);
 	if (ret) {
 		DRV_LOG(DEBUG, "QUOTA: failed to register MTR ASO READ MR");
 		return -errno;
diff --git a/drivers/net/mlx5/mlx5_hws_cnt.c b/drivers/net/mlx5/mlx5_hws_cnt.c
index 1b6acb7a3b..d0c4ead71b 100644
--- a/drivers/net/mlx5/mlx5_hws_cnt.c
+++ b/drivers/net/mlx5/mlx5_hws_cnt.c
@@ -259,12 +259,11 @@ mlx5_hws_aging_check(struct mlx5_priv *priv, struct mlx5_hws_cnt_pool *cpool)
 }
 
 static void
-mlx5_hws_cnt_raw_data_free(struct mlx5_dev_ctx_shared *sh,
-			   struct mlx5_hws_cnt_raw_data_mng *mng)
+mlx5_hws_cnt_raw_data_free(struct mlx5_hws_cnt_raw_data_mng *mng)
 {
 	if (mng == NULL)
 		return;
-	sh->cdev->mr_scache.dereg_mr_cb(&mng->mr);
+	mlx5_os_dereg_mr(&mng->mr);
 	mlx5_free(mng->raw);
 	mlx5_free(mng);
 }
@@ -296,8 +295,7 @@ mlx5_hws_cnt_raw_data_alloc(struct mlx5_dev_ctx_shared *sh, uint32_t n,
 				   NULL, "failed to allocate raw counters memory");
 		goto error;
 	}
-	ret = sh->cdev->mr_scache.reg_mr_cb(sh->cdev->pd, mng->raw, sz,
-					    &mng->mr);
+	ret = mlx5_os_reg_mr(sh->cdev->pd, mng->raw, sz, &mng->mr);
 	if (ret) {
 		rte_flow_error_set(error, errno,
 				   RTE_FLOW_ERROR_TYPE_UNSPECIFIED,
@@ -306,7 +304,7 @@ mlx5_hws_cnt_raw_data_alloc(struct mlx5_dev_ctx_shared *sh, uint32_t n,
 	}
 	return mng;
 error:
-	mlx5_hws_cnt_raw_data_free(sh, mng);
+	mlx5_hws_cnt_raw_data_free(mng);
 	return NULL;
 }
 
@@ -639,8 +637,7 @@ mlx5_hws_cnt_pool_dcs_alloc(struct mlx5_dev_ctx_shared *sh,
 }
 
 static void
-mlx5_hws_cnt_pool_dcs_free(struct mlx5_dev_ctx_shared *sh,
-			   struct mlx5_hws_cnt_pool *cpool)
+mlx5_hws_cnt_pool_dcs_free(struct mlx5_hws_cnt_pool *cpool)
 {
 	uint32_t idx;
 
@@ -649,7 +646,7 @@ mlx5_hws_cnt_pool_dcs_free(struct mlx5_dev_ctx_shared *sh,
 	for (idx = 0; idx < MLX5_HWS_CNT_DCS_NUM; idx++)
 		mlx5_devx_cmd_destroy(cpool->dcs_mng.dcs[idx].obj);
 	if (cpool->raw_mng) {
-		mlx5_hws_cnt_raw_data_free(sh, cpool->raw_mng);
+		mlx5_hws_cnt_raw_data_free(cpool->raw_mng);
 		cpool->raw_mng = NULL;
 	}
 }
@@ -842,8 +839,8 @@ mlx5_hws_cnt_pool_destroy(struct mlx5_dev_ctx_shared *sh,
 	}
 	mlx5_hws_cnt_pool_action_destroy(cpool);
 	if (cpool->cfg.host_cpool == NULL) {
-		mlx5_hws_cnt_pool_dcs_free(sh, cpool);
-		mlx5_hws_cnt_raw_data_free(sh, cpool->raw_mng);
+		mlx5_hws_cnt_pool_dcs_free(cpool);
+		mlx5_hws_cnt_raw_data_free(cpool->raw_mng);
 	}
 	mlx5_free((void *)cpool->cfg.name);
 	mlx5_hws_cnt_pool_deinit(cpool);
-- 
2.54.0


^ permalink raw reply related

* [PATCH v9 06/10] net/mlx5: support selective Rx
From: Thomas Monjalon @ 2026-06-05 23:33 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, Gregory Etelson, Dariusz Sosnowski,
	Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
	Matan Azrad
In-Reply-To: <20260605233456.3017423-1-thomas@monjalon.net>

From: Gregory Etelson <getelson@nvidia.com>

Selective Rx may save some PCI bandwidth.
Implement selective Rx in the (quite slow) scalar SPRQ Rx path
mlx5_rx_burst() where the performance impact
of the added condition branches is acceptable.
Other Rx functions do not support this feature.
When using selective Rx, mlx5_rx_burst will be selected.

A null Memory Region (MR) is always allocated
at shared device context initialization.
The selective Rx capability is not advertised
if this special MR allocation fails.

For each Rx segment configured with a NULL mempool,
a "null mbuf" is created.
It is a fake mbuf allocated outside any mempool,
used as a placeholder in the Rx ring.
The null MR lkey is used in the WQE for these segments
so the NIC writes received data to a discard buffer.
The mbuf data room size is resolved from the first segment having a pool.
For null segments, the buffer length is from the last seen pool,
so that the WQE stride size remains consistent.

In mlx5_rx_burst, discarded segments are not chained
into the packet mbuf list, NB_SEGS is decremented accordingly,
and no replacement buffer is allocated.
A separate data_seg_len accumulator tracks the total length
of delivered segments only.
The packet length is adjusted to reflect only the data
actually delivered to the application.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 doc/guides/nics/features/mlx5.ini      |   1 +
 doc/guides/nics/mlx5.rst               |  86 +++++++++---
 doc/guides/rel_notes/release_26_07.rst |   4 +
 drivers/net/mlx5/mlx5.c                |   7 +
 drivers/net/mlx5/mlx5.h                |   1 +
 drivers/net/mlx5/mlx5_ethdev.c         |  25 ++++
 drivers/net/mlx5/mlx5_rx.c             | 187 +++++++++++++++----------
 drivers/net/mlx5/mlx5_rx.h             |   1 +
 drivers/net/mlx5/mlx5_rxq.c            |  95 +++++++++----
 drivers/net/mlx5/mlx5_trigger.c        |  64 +++++++--
 10 files changed, 330 insertions(+), 141 deletions(-)

diff --git a/doc/guides/nics/features/mlx5.ini b/doc/guides/nics/features/mlx5.ini
index 3b3eda28b8..ae8c83057b 100644
--- a/doc/guides/nics/features/mlx5.ini
+++ b/doc/guides/nics/features/mlx5.ini
@@ -16,6 +16,7 @@ Burst mode info      = Y
 Power mgmt address monitor = Y
 MTU update           = Y
 Buffer split on Rx   = Y
+Selective Rx         = Y
 Scattered Rx         = Y
 LRO                  = Y
 TSO                  = Y
diff --git a/doc/guides/nics/mlx5.rst b/doc/guides/nics/mlx5.rst
index 00bfb31370..afbf040e66 100644
--- a/doc/guides/nics/mlx5.rst
+++ b/doc/guides/nics/mlx5.rst
@@ -84,6 +84,9 @@ The Rx / Tx data path use different techniques to offer the best performance.
   with :ref:`multi-packet Rx queues (MPRQ) <mlx5_mprq_params>`.
   This feature is disabled by default.
 
+- Some PCI bandwidth is saved by receiving partial packets
+  with :ref:`selective Rx <mlx5_selective_rx>`.
+
 More details about Rx implementations and their configurations are provided
 in the chapter about :ref:`mlx5_rx_functions`.
 
@@ -879,6 +882,8 @@ MLX5 supports various methods to report statistics:
 Basic port statistics can be queried using ``rte_eth_stats_get()``.
 The received and sent statistics are through SW only
 and counts the number of packets received or sent successfully by the PMD.
+In the case of :ref:`selective Rx <mlx5_selective_rx>`,
+the ``ibytes`` counter matches segments delivered, not the skipped ones.
 The ``imissed`` counter is the amount of packets that could not be delivered
 to SW because a queue was full.
 Packets not received due to congestion in the bus or on the NIC
@@ -992,25 +997,26 @@ These configurations may also have an impact on the behavior:
 
 .. table:: Rx burst functions
 
-   +-------------------+------------------------+---------+-----------------+------+-------+---------+
-   || Function Name    || Parameters to Enable  || Scatter|| Error Recovery || CQE || Large|| Shared |
-   |                   |                        |         |                 || comp|| MTU  |  RxQ    |
-   +===================+========================+=========+=================+======+=======+=========+
-   | rx_burst          | rx_vec_en=0            |   Yes   | Yes             |  Yes |  Yes  | No      |
-   +-------------------+------------------------+---------+-----------------+------+-------+---------+
-   | rx_burst_vec      | rx_vec_en=1 (default)  |   No    | if CQE comp off |  Yes |  No   | No      |
-   +-------------------+------------------------+---------+-----------------+------+-------+---------+
-   | rx_burst_mprq     || mprq_en=1             |   No    | Yes             |  Yes |  Yes  | No      |
-   |                   || RxQs >= rxqs_min_mprq |         |                 |      |       |         |
-   +-------------------+------------------------+---------+-----------------+------+-------+---------+
-   | rx_burst_mprq_vec || rx_vec_en=1 (default) |   No    | if CQE comp off |  Yes |  Yes  | No      |
-   |                   || mprq_en=1             |         |                 |      |       |         |
-   |                   || RxQs >= rxqs_min_mprq |         |                 |      |       |         |
-   +-------------------+------------------------+---------+-----------------+------+-------+---------+
-   | rx_burst          | at least one Rx queue  |   Yes   | Yes             |  Yes |  Yes  | Yes     |
-   |  (out of order)   | on the device          |         |                 |      |       |         |
-   |                   | is shared              |         |                 |      |       |         |
-   +-------------------+------------------------+---------+-----------------+------+-------+---------+
+   +----------+-----------------------+---------+--------+----------+------+-------+--------+
+   || Function|| Parameters to Enable || Scatter|| Selec-|| Error   || CQE || Large|| Shared|
+   || Name    |                       |         || tive  || Recovery|| comp|| MTU  || RxQ   |
+   +==========+=======================+=========+========+==========+======+=======+========+
+   | rx_burst | rx_vec_en=0           |   Yes   |   Yes  | Yes      |  Yes |  Yes  |   No   |
+   +----------+-----------------------+---------+--------+----------+------+-------+--------+
+   | _vec     | rx_vec_en=1 (default) |   No    |   No   || if CQE  |  Yes |  No   |   No   |
+   |          |                       |         |        || comp off|      |       |        |
+   +----------+-----------------------+---------+--------+----------+------+-------+--------+
+   | _mprq    || mprq_en=1            |   No    |   No   | Yes      |  Yes |  Yes  |   No   |
+   |          || RxQs >= rxqs_min_mprq|         |        |          |      |       |        |
+   +----------+-----------------------+---------+--------+----------+------+-------+--------+
+   | _mprq_vec|| rx_vec_en=1 (default)|   No    |   No   || if CQE  |  Yes |  Yes  |   No   |
+   |          || mprq_en=1            |         |        || comp off|      |       |        |
+   |          || RxQs >= rxqs_min_mprq|         |        |          |      |       |        |
+   +----------+-----------------------+---------+--------+----------+------+-------+--------+
+   || _out_of || at least one Rx queue|   Yes   |   No   | Yes      |  Yes |  Yes  |   Yes  |
+   || _order  || on the device        |         |        |          |      |       |        |
+   |          || is shared            |         |        |          |      |       |        |
+   +----------+-----------------------+---------+--------+----------+------+-------+--------+
 
 
 Rx/Tx Tuning
@@ -1105,13 +1111,14 @@ Rx interrupt                                X
 :ref:`Rx threshold <mlx5_rx_threshold>`     X        X
 :ref:`Rx drop delay <mlx5_drop>`            X        X
 :ref:`Rx timestamp <mlx5_rx_timstp>`        X        X
+:ref:`buffer split <mlx5_buf_split>`        X        X
+:ref:`selective Rx <mlx5_selective_rx>`     X
+:ref:`multi-segment <mlx5_multiseg>`        X        X
 :ref:`Tx scheduling <mlx5_tx_sched>`        X
 :ref:`Tx rate limit <mlx5_rate_limit>`      X
 :ref:`Tx inline <mlx5_tx_inline>`           X        X
 :ref:`Tx fast free <mlx5_tx_fast_free>`     X        X
 :ref:`Tx affinity <mlx5_aggregated>`        X
-:ref:`buffer split <mlx5_buf_split>`        X        X
-:ref:`multi-segment <mlx5_multiseg>`        X        X
 promiscuous                                 X        X
 multicast promiscuous                       X        X
 multiple MAC addresses                      X
@@ -2248,13 +2255,50 @@ OFED       5.1-2
 DPDK       20.11
 =========  ==========
 
+Runtime configuration
+^^^^^^^^^^^^^^^^^^^^^
+
+The offload flag ``RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT`` is required.
+
+When calling ``rte_eth_rx_queue_setup()``,
+the input ``rte_eth_rxconf::rx_seg`` defines the configuration of the segments,
+mainly offset and length.
+
 Limitations
 ^^^^^^^^^^^
 
+#. Splitting per protocol header is not supported.
+
 #. Buffer split offload is supported with regular Rx burst routine only,
    no MPRQ feature or vectorized code can be engaged.
 
 
+.. _mlx5_selective_rx:
+
+Selective Rx
+~~~~~~~~~~~~
+
+Some PCI bandwidth can be saved
+by :ref:`skipping some parts of Rx data <nic_features_selective_rx>`.
+It is enabled when using :ref:`buffer split <mlx5_buf_split>`
+and configuring no mempool in some segments to discard.
+
+Runtime configuration
+^^^^^^^^^^^^^^^^^^^^^
+
+The offload flag ``RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT`` is required.
+
+When calling ``rte_eth_rx_queue_setup()``,
+the segment to discard (``rte_eth_rxconf::rx_seg::split``)
+is marked by the absence of mempool (``mp = NULL``).
+
+Limitations
+^^^^^^^^^^^
+
+#. Selective Rx is supported with regular Rx burst routine only,
+   no MPRQ feature or vectorized code can be engaged.
+
+
 .. _mlx5_multiseg:
 
 Multi-Segment Scatter/Gather
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index 46a8fe2cc1..0ac9816a85 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -103,6 +103,10 @@ New Features
   * Added support for transmitting LLDP packets based on mbuf packet type.
   * Implemented AVX2 context descriptor transmit paths.
 
+* **Updated NVIDIA mlx5 ethernet driver.**
+
+  * Added support for selective Rx in scalar SPRQ Rx path.
+
 * **Updated PCAP ethernet driver.**
 
   * Added support for VLAN insertion and stripping.
diff --git a/drivers/net/mlx5/mlx5.c b/drivers/net/mlx5/mlx5.c
index f190654756..61c26d1206 100644
--- a/drivers/net/mlx5/mlx5.c
+++ b/drivers/net/mlx5/mlx5.c
@@ -1975,6 +1975,9 @@ mlx5_alloc_shared_dev_ctx(const struct mlx5_dev_spawn_data *spawn,
 	/* Init counter pool list header and lock. */
 	LIST_INIT(&sh->hws_cpool_list);
 	rte_spinlock_init(&sh->cpool_lock);
+	sh->null_mr = mlx5_os_alloc_null_mr(sh->cdev->dev, sh->cdev->pd);
+	if (!sh->null_mr)
+		DRV_LOG(DEBUG, "Fail to initialize NULL MR, selective Rx is disabled.");
 exit:
 	pthread_mutex_unlock(&mlx5_dev_ctx_list_mutex);
 	return sh;
@@ -2139,6 +2142,10 @@ mlx5_free_shared_dev_ctx(struct mlx5_dev_ctx_shared *sh)
 	MLX5_ASSERT(sh->geneve_tlv_option_resource == NULL);
 	pthread_mutex_destroy(&sh->txpp.mutex);
 	mlx5_lwm_unset(sh);
+	if (sh->null_mr) {
+		mlx5_os_free_null_mr(sh->null_mr);
+		sh->null_mr = NULL;
+	}
 	mlx5_physical_device_destroy(sh->phdev);
 	mlx5_free(sh);
 	return;
diff --git a/drivers/net/mlx5/mlx5.h b/drivers/net/mlx5/mlx5.h
index 92a00cfaa8..bd6ef35b53 100644
--- a/drivers/net/mlx5/mlx5.h
+++ b/drivers/net/mlx5/mlx5.h
@@ -1674,6 +1674,7 @@ struct mlx5_dev_ctx_shared {
 	rte_spinlock_t cpool_lock;
 	LIST_HEAD(hws_cpool_list, mlx5_hws_cnt_pool) hws_cpool_list; /* Count pool list. */
 	struct mlx5_dev_registers registers;
+	struct mlx5_pmd_mr *null_mr;
 	struct mlx5_dev_shared_port port[]; /* per device port data array. */
 };
 
diff --git a/drivers/net/mlx5/mlx5_ethdev.c b/drivers/net/mlx5/mlx5_ethdev.c
index a29cdeeb50..7b7536fa1e 100644
--- a/drivers/net/mlx5/mlx5_ethdev.c
+++ b/drivers/net/mlx5/mlx5_ethdev.c
@@ -381,6 +381,7 @@ mlx5_dev_infos_get(struct rte_eth_dev *dev, struct rte_eth_dev_info *info)
 	info->rx_seg_capa.multi_pools = !priv->config.mprq.enabled;
 	info->rx_seg_capa.offset_allowed = !priv->config.mprq.enabled;
 	info->rx_seg_capa.offset_align_log2 = 0;
+	info->rx_seg_capa.selective_rx = !!priv->sh->null_mr;
 	info->rx_offload_capa = (mlx5_get_rx_port_offloads() |
 				 info->rx_queue_offload_capa);
 	info->tx_offload_capa = mlx5_get_tx_port_offloads(dev);
@@ -708,6 +709,25 @@ mlx5_dev_set_mtu(struct rte_eth_dev *dev, uint16_t mtu)
 	return -rte_errno;
 }
 
+static bool
+mlx5_selective_rx_enabled(struct rte_eth_dev *dev)
+{
+	struct mlx5_priv *priv = dev->data->dev_private;
+
+	for (uint32_t q = 0; q < priv->rxqs_n; ++q) {
+		struct mlx5_rxq_ctrl *rxq_ctrl = mlx5_rxq_ctrl_get(dev, q);
+
+		if (rxq_ctrl == NULL || rxq_ctrl->is_hairpin)
+			continue;
+		for (uint16_t s = 0; s < rxq_ctrl->rxq.rxseg_n; s++) {
+			if (rxq_ctrl->rxq.rxseg[s].mp == NULL)
+				return true;
+		}
+	}
+
+	return false;
+}
+
 /**
  * Configure the RX function to use.
  *
@@ -723,6 +743,11 @@ mlx5_select_rx_function(struct rte_eth_dev *dev)
 	eth_rx_burst_t rx_pkt_burst = mlx5_rx_burst;
 
 	MLX5_ASSERT(dev != NULL);
+	if (mlx5_selective_rx_enabled(dev)) {
+		DRV_LOG(DEBUG, "port %u forced to scalar SPRQ Rx (selective Rx configured)",
+			dev->data->port_id);
+		return rx_pkt_burst;
+	}
 	if (mlx5_shared_rq_enabled(dev)) {
 		rx_pkt_burst = mlx5_rx_burst_out_of_order;
 		DRV_LOG(DEBUG, "port %u forced to use SPRQ"
diff --git a/drivers/net/mlx5/mlx5_rx.c b/drivers/net/mlx5/mlx5_rx.c
index 185bfd4fff..9812bc7929 100644
--- a/drivers/net/mlx5/mlx5_rx.c
+++ b/drivers/net/mlx5/mlx5_rx.c
@@ -486,7 +486,7 @@ mlx5_rxq_initialize(struct mlx5_rxq_data *rxq)
 					rxq->wqes)[i];
 			addr = rte_pktmbuf_mtod(buf, uintptr_t);
 			byte_count = DATA_LEN(buf);
-			lkey = mlx5_rx_mb2mr(rxq, buf);
+			lkey = buf->pool ? mlx5_rx_mb2mr(rxq, buf) : rxq->sh->null_mr->lkey;
 		}
 		/* scat->addr must be able to store a pointer. */
 		MLX5_ASSERT(sizeof(scat->addr) >= sizeof(uintptr_t));
@@ -1044,11 +1044,14 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
 	const unsigned int sges_n = rxq->sges_n;
 	struct rte_mbuf *pkt = NULL;
 	struct rte_mbuf *seg = NULL;
+	struct rte_mbuf *tail = NULL;
 	volatile struct mlx5_cqe *cqe =
 		&(*rxq->cqes)[rxq->cq_ci & cqe_mask];
+	volatile struct mlx5_mini_cqe8 *mcqe = NULL;
 	unsigned int i = 0;
 	unsigned int rq_ci = rxq->rq_ci << sges_n;
 	int len = 0; /* keep its value across iterations. */
+	uint32_t data_seg_len = 0;
 
 	while (pkts_n) {
 		uint16_t skip_cnt;
@@ -1056,105 +1059,137 @@ mlx5_rx_burst(void *dpdk_rxq, struct rte_mbuf **pkts, uint16_t pkts_n)
 		volatile struct mlx5_wqe_data_seg *wqe =
 			&((volatile struct mlx5_wqe_data_seg *)rxq->wqes)[idx];
 		struct rte_mbuf *rep = (*rxq->elts)[idx];
-		volatile struct mlx5_mini_cqe8 *mcqe = NULL;
 
-		if (pkt)
-			NEXT(seg) = rep;
+		if (pkt) {
+			if (rep->pool)
+				NEXT(tail) = rep;
+			else
+				--NB_SEGS(pkt);
+		}
 		seg = rep;
 		rte_prefetch0(seg);
 		rte_prefetch0(cqe);
 		rte_prefetch0(wqe);
-		/* Allocate the buf from the same pool. */
-		rep = rte_mbuf_raw_alloc(seg->pool);
-		if (unlikely(rep == NULL)) {
-			++rxq->stats.rx_nombuf;
-			if (!pkt) {
-				/*
-				 * no buffers before we even started,
-				 * bail out silently.
-				 */
-				break;
-			}
-			while (pkt != seg) {
-				MLX5_ASSERT(pkt != (*rxq->elts)[idx]);
-				rep = NEXT(pkt);
-				NEXT(pkt) = NULL;
-				NB_SEGS(pkt) = 1;
-				rte_mbuf_raw_free(pkt);
-				pkt = rep;
-			}
-			rq_ci >>= sges_n;
-			++rq_ci;
-			rq_ci <<= sges_n;
-			break;
-		}
-		if (!pkt) {
-			cqe = &(*rxq->cqes)[rxq->cq_ci & cqe_mask];
-			len = mlx5_rx_poll_len(rxq, cqe, cqe_n, cqe_mask,
-					       &mcqe, &skip_cnt, false, NULL);
-			if (unlikely(len & MLX5_ERROR_CQE_MASK)) {
-				/* We drop packets with non-critical errors */
-				rte_mbuf_raw_free(rep);
-				if (len == MLX5_CRITICAL_ERROR_CQE_RET) {
-					rq_ci = rxq->rq_ci << sges_n;
+		if (seg->pool) {
+			/* Allocate the buf from the same pool. */
+			rep = rte_mbuf_raw_alloc(seg->pool);
+			if (unlikely(rep == NULL)) {
+				++rxq->stats.rx_nombuf;
+				if (!pkt) {
+					/*
+					 * no buffers before we even started,
+					 * bail out silently.
+					 */
 					break;
 				}
-				/* Skip specified amount of error CQEs packets */
+				while (pkt != seg) {
+					MLX5_ASSERT(pkt != (*rxq->elts)[idx]);
+					rep = NEXT(pkt);
+					NEXT(pkt) = NULL;
+					NB_SEGS(pkt) = 1;
+					rte_mbuf_raw_free(pkt);
+					pkt = rep;
+				}
 				rq_ci >>= sges_n;
-				rq_ci += skip_cnt;
+				++rq_ci;
 				rq_ci <<= sges_n;
-				MLX5_ASSERT(!pkt);
-				continue;
-			}
-			if (len == 0) {
-				rte_mbuf_raw_free(rep);
 				break;
 			}
-			pkt = seg;
-			MLX5_ASSERT(len >= (int)(rxq->crc_present << 2));
-			pkt->ol_flags &= RTE_MBUF_F_EXTERNAL;
-			if (rxq->cqe_comp_layout && mcqe)
-				cqe = &rxq->title_cqe;
-			rxq_cq_to_mbuf(rxq, pkt, cqe, mcqe);
-			if (rxq->crc_present)
-				len -= RTE_ETHER_CRC_LEN;
-			PKT_LEN(pkt) = len;
-			if (cqe->lro_num_seg > 1) {
-				mlx5_lro_update_hdr
-					(rte_pktmbuf_mtod(pkt, uint8_t *), cqe,
-					 mcqe, rxq, len);
-				pkt->ol_flags |= RTE_MBUF_F_RX_LRO;
-				pkt->tso_segsz = len / cqe->lro_num_seg;
+		}
+		if (!pkt) { /* new packet */
+			if (len == 0) { /* no CQE polled yet */
+				mcqe = NULL;
+				cqe = &(*rxq->cqes)[rxq->cq_ci & cqe_mask];
+				len = mlx5_rx_poll_len(rxq, cqe, cqe_n, cqe_mask,
+							   &mcqe, &skip_cnt, false, NULL);
+				if (unlikely(len & MLX5_ERROR_CQE_MASK)) {
+					/* We drop packets with non-critical errors */
+					if (seg->pool)
+						rte_mbuf_raw_free(rep);
+					if (len == MLX5_CRITICAL_ERROR_CQE_RET) {
+						rq_ci = rxq->rq_ci << sges_n;
+						break;
+					}
+					/* Skip specified amount of error CQEs packets */
+					rq_ci >>= sges_n;
+					rq_ci += skip_cnt;
+					rq_ci <<= sges_n;
+					MLX5_ASSERT(!pkt);
+					len = 0;
+					continue;
+				}
+				if (len == 0) {
+					if (seg->pool)
+						rte_mbuf_raw_free(rep);
+					break;
+				}
+				MLX5_ASSERT(len >= (int)(rxq->crc_present << 2));
+				if (rxq->crc_present)
+					len -= RTE_ETHER_CRC_LEN;
+			}
+			if (seg->pool) { /* first real segment */
+				pkt = seg;
+				pkt->ol_flags &= RTE_MBUF_F_EXTERNAL;
+				if (rxq->cqe_comp_layout && mcqe)
+					cqe = &rxq->title_cqe;
+				rxq_cq_to_mbuf(rxq, pkt, cqe, mcqe);
+				PKT_LEN(pkt) = len;
+				if (cqe->lro_num_seg > 1) {
+					mlx5_lro_update_hdr
+						(rte_pktmbuf_mtod(pkt, uint8_t *), cqe,
+						 mcqe, rxq, len);
+					pkt->ol_flags |= RTE_MBUF_F_RX_LRO;
+					pkt->tso_segsz = len / cqe->lro_num_seg;
+				}
 			}
 		}
-		DATA_LEN(rep) = DATA_LEN(seg);
-		PKT_LEN(rep) = PKT_LEN(seg);
-		SET_DATA_OFF(rep, DATA_OFF(seg));
-		PORT(rep) = PORT(seg);
-		(*rxq->elts)[idx] = rep;
-		/*
-		 * Fill NIC descriptor with the new buffer. The lkey and size
-		 * of the buffers are already known, only the buffer address
-		 * changes.
-		 */
-		wqe->addr = rte_cpu_to_be_64(rte_pktmbuf_mtod(rep, uintptr_t));
-		/* If there's only one MR, no need to replace LKey in WQE. */
-		if (unlikely(mlx5_mr_btree_len(&rxq->mr_ctrl.cache_bh) > 1))
-			wqe->lkey = mlx5_rx_mb2mr(rxq, rep);
-		if (len > DATA_LEN(seg)) {
+		if (seg->pool) { /* real segment: replenish WQE */
+			tail = seg;
+			DATA_LEN(rep) = DATA_LEN(seg);
+			PKT_LEN(rep) = PKT_LEN(seg);
+			SET_DATA_OFF(rep, DATA_OFF(seg));
+			PORT(rep) = PORT(seg);
+			(*rxq->elts)[idx] = rep;
+			/*
+			 * Fill NIC descriptor with the new buffer. The lkey and size
+			 * of the buffers are already known, only the buffer address
+			 * changes.
+			 */
+			wqe->addr = rte_cpu_to_be_64(rte_pktmbuf_mtod(rep, uintptr_t));
+			/* If there's only one MR, no need to replace LKey in WQE. */
+			if (unlikely(mlx5_mr_btree_len(&rxq->mr_ctrl.cache_bh) > 1))
+				wqe->lkey = mlx5_rx_mb2mr(rxq, rep);
+		}
+		if (len > DATA_LEN(seg)) { /* more data: move to next segment */
+			if (seg->pool)
+				data_seg_len += DATA_LEN(seg);
 			len -= DATA_LEN(seg);
-			++NB_SEGS(pkt);
+			if (pkt)
+				++NB_SEGS(pkt);
 			++rq_ci;
 			continue;
 		}
-		DATA_LEN(seg) = len;
+		if (seg->pool) { /* last segment */
+			DATA_LEN(seg) = len;
+			data_seg_len += len;
+		}
+		if (unlikely(!pkt)) { /* no real segment found, skip packet */
+			len = 0;
+			rq_ci >>= sges_n;
+			++rq_ci;
+			rq_ci <<= sges_n;
+			continue;
+		}
+		PKT_LEN(pkt) = RTE_MIN(PKT_LEN(pkt), data_seg_len);
 #ifdef MLX5_PMD_SOFT_COUNTERS
 		/* Increment bytes counter. */
 		rxq->stats.ibytes += PKT_LEN(pkt);
 #endif
+		data_seg_len = 0;
 		/* Return packet. */
 		*(pkts++) = pkt;
 		pkt = NULL;
+		len = 0;
 		--pkts_n;
 		++i;
 		/* Align consumer index to the next stride. */
diff --git a/drivers/net/mlx5/mlx5_rx.h b/drivers/net/mlx5/mlx5_rx.h
index 01b563d981..cd48ee37ef 100644
--- a/drivers/net/mlx5/mlx5_rx.h
+++ b/drivers/net/mlx5/mlx5_rx.h
@@ -96,6 +96,7 @@ struct mlx5_eth_rxseg {
 	uint16_t length; /**< Segment data length, configures split point. */
 	uint16_t offset; /**< Data offset from beginning of mbuf data buffer. */
 	uint32_t reserved; /**< Reserved field. */
+	struct rte_mbuf *null_mbuf; /**< For selective Rx. */
 };
 
 /* RX queue descriptor. */
diff --git a/drivers/net/mlx5/mlx5_rxq.c b/drivers/net/mlx5/mlx5_rxq.c
index 48d982a8c2..25dba7f4d9 100644
--- a/drivers/net/mlx5/mlx5_rxq.c
+++ b/drivers/net/mlx5/mlx5_rxq.c
@@ -151,26 +151,30 @@ rxq_alloc_elts_sprq(struct mlx5_rxq_ctrl *rxq_ctrl)
 		struct mlx5_eth_rxseg *seg = &rxq_ctrl->rxq.rxseg[i % sges_n];
 		struct rte_mbuf *buf;
 
-		buf = rte_pktmbuf_alloc(seg->mp);
-		if (buf == NULL) {
-			if (rxq_ctrl->share_group == 0)
-				DRV_LOG(ERR, "port %u queue %u empty mbuf pool",
-					RXQ_PORT_ID(rxq_ctrl),
-					rxq_ctrl->rxq.idx);
-			else
-				DRV_LOG(ERR, "share group %u queue %u empty mbuf pool",
-					rxq_ctrl->share_group,
-					rxq_ctrl->share_qid);
-			rte_errno = ENOMEM;
-			goto error;
+		if (seg->mp) {
+			buf = rte_pktmbuf_alloc(seg->mp);
+			if (buf == NULL) {
+				if (rxq_ctrl->share_group == 0)
+					DRV_LOG(ERR, "port %u queue %u empty mbuf pool",
+						RXQ_PORT_ID(rxq_ctrl),
+						rxq_ctrl->rxq.idx);
+				else
+					DRV_LOG(ERR, "share group %u queue %u empty mbuf pool",
+						rxq_ctrl->share_group,
+						rxq_ctrl->share_qid);
+				rte_errno = ENOMEM;
+				goto error;
+			}
+			/* Only vectored Rx routines rely on headroom size. */
+			MLX5_ASSERT(!has_vec_support ||
+				    DATA_OFF(buf) >= RTE_PKTMBUF_HEADROOM);
+			/* Buffer is supposed to be empty. */
+			MLX5_ASSERT(rte_pktmbuf_data_len(buf) == 0);
+			MLX5_ASSERT(rte_pktmbuf_pkt_len(buf) == 0);
+			MLX5_ASSERT(!buf->next);
+		} else {
+			buf = seg->null_mbuf;
 		}
-		/* Only vectored Rx routines rely on headroom size. */
-		MLX5_ASSERT(!has_vec_support ||
-			    DATA_OFF(buf) >= RTE_PKTMBUF_HEADROOM);
-		/* Buffer is supposed to be empty. */
-		MLX5_ASSERT(rte_pktmbuf_data_len(buf) == 0);
-		MLX5_ASSERT(rte_pktmbuf_pkt_len(buf) == 0);
-		MLX5_ASSERT(!buf->next);
 		SET_DATA_OFF(buf, seg->offset);
 		PORT(buf) = rxq_ctrl->rxq.port_id;
 		DATA_LEN(buf) = seg->length;
@@ -324,10 +328,14 @@ rxq_free_elts_sprq(struct mlx5_rxq_ctrl *rxq_ctrl)
 		rxq->rq_pi = elts_ci;
 	}
 	for (i = 0; i != q_n; ++i) {
-		if ((*rxq->elts)[i] != NULL)
+		if ((*rxq->elts)[i] != NULL && (*rxq->elts)[i]->pool != NULL)
 			rte_pktmbuf_free_seg((*rxq->elts)[i]);
 		(*rxq->elts)[i] = NULL;
 	}
+	for (i = 0; i < rxq->rxseg_n; i++) {
+		mlx5_free(rxq->rxseg[i].null_mbuf);
+		rxq->rxseg[i].null_mbuf = NULL;
+	}
 }
 
 /**
@@ -1815,7 +1823,9 @@ mlx5_rxq_new(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
 	int ret;
 	struct mlx5_priv *priv = dev->data->dev_private;
 	struct mlx5_rxq_ctrl *tmpl;
-	unsigned int mb_len = rte_pktmbuf_data_room_size(rx_seg[0].mp);
+	struct rte_mempool *first_mp = NULL;
+	struct rte_mempool *last_mp = NULL;
+	unsigned int mb_len;
 	struct mlx5_port_config *config = &priv->config;
 	uint64_t offloads = conf->offloads |
 			   dev->data->dev_conf.rxmode.offloads;
@@ -1827,7 +1837,7 @@ mlx5_rxq_new(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
 	unsigned int non_scatter_min_mbuf_size = max_rx_pktlen +
 							RTE_PKTMBUF_HEADROOM;
 	unsigned int max_lro_size = 0;
-	unsigned int first_mb_free_size = mb_len - RTE_PKTMBUF_HEADROOM;
+	unsigned int first_mb_free_size;
 	uint32_t mprq_log_actual_stride_num = 0;
 	uint32_t mprq_log_actual_stride_size = 0;
 	bool rx_seg_en = n_seg != 1 || rx_seg[0].offset || rx_seg[0].length;
@@ -1845,6 +1855,21 @@ mlx5_rxq_new(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
 	const struct rte_eth_rxseg_split *qs_seg = rx_seg;
 	unsigned int tail_len;
 
+	/* Find first segment with a mempool. */
+	for (uint16_t seg = 0; seg < n_seg; seg++) {
+		if (rx_seg[seg].mp != NULL) {
+			first_mp = rx_seg[seg].mp;
+			break;
+		}
+	}
+	if (first_mp == NULL) {
+		DRV_LOG(ERR, "port %u Rx queue %u has no mempool", dev->data->port_id, idx);
+		rte_errno = EINVAL;
+		return NULL;
+	}
+	mb_len = rte_pktmbuf_data_room_size(first_mp);
+	first_mb_free_size = mb_len - RTE_PKTMBUF_HEADROOM;
+
 	if (mprq_en) {
 		/* Trim the number of descs needed. */
 		desc >>= mprq_log_actual_stride_num;
@@ -1884,35 +1909,44 @@ mlx5_rxq_new(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
 	do {
 		struct mlx5_eth_rxseg *hw_seg =
 					&tmpl->rxq.rxseg[tmpl->rxq.rxseg_n];
-		uint32_t buf_len, offset, seg_len;
+		uint32_t buf_len = 0, offset, seg_len;
 
 		/*
 		 * For the buffers beyond descriptions offset is zero,
 		 * the first buffer contains head room.
 		 */
-		buf_len = rte_pktmbuf_data_room_size(qs_seg->mp);
+		if (qs_seg->mp != NULL) {
+			last_mp = qs_seg->mp;
+			buf_len = rte_pktmbuf_data_room_size(qs_seg->mp);
+		} else if (last_mp != NULL) {
+			buf_len = rte_pktmbuf_data_room_size(last_mp);
+		} else {
+			buf_len = mb_len;
+		}
 		offset = (tmpl->rxq.rxseg_n >= n_seg ? 0 : qs_seg->offset) +
 			 (tmpl->rxq.rxseg_n ? 0 : RTE_PKTMBUF_HEADROOM);
 		/*
 		 * For the buffers beyond descriptions the length is
 		 * pool buffer length, zero lengths are replaced with
-		 * pool buffer length either.
+		 * pool buffer length for real segments,
+		 * or remaining packet length for discard segments.
 		 */
 		seg_len = tmpl->rxq.rxseg_n >= n_seg ? buf_len :
 						       qs_seg->length ?
 						       qs_seg->length :
-						       (buf_len - offset);
+						       qs_seg->mp != NULL ?
+						       (buf_len - offset) : tail_len;
 		/* Check is done in long int, now overflows. */
-		if (buf_len < seg_len + offset) {
+		if (qs_seg->mp != NULL && buf_len < seg_len + offset) {
 			DRV_LOG(ERR, "port %u Rx queue %u: Split offset/length "
 				     "%u/%u can't be satisfied",
 				     dev->data->port_id, idx,
-				     qs_seg->length, qs_seg->offset);
+				     qs_seg->offset, qs_seg->length);
 			rte_errno = EINVAL;
 			goto error;
 		}
 		if (seg_len > tail_len)
-			seg_len = buf_len - offset;
+			seg_len = qs_seg->mp != NULL ? buf_len - offset : tail_len;
 		if (++tmpl->rxq.rxseg_n > MLX5_MAX_RXQ_NSEG) {
 			DRV_LOG(ERR,
 				"port %u too many SGEs (%u) needed to handle"
@@ -2077,7 +2111,8 @@ mlx5_rxq_new(struct rte_eth_dev *dev, uint16_t idx, uint16_t desc,
 	/* Save port ID. */
 	tmpl->rxq.port_id = dev->data->port_id;
 	tmpl->sh = priv->sh;
-	tmpl->rxq.mp = rx_seg[0].mp;
+	tmpl->rxq.sh = priv->sh;
+	tmpl->rxq.mp = first_mp;
 	tmpl->rxq.elts_n = log2above(desc);
 	tmpl->rxq.rq_repl_thresh = MLX5_VPMD_RXQ_RPLNSH_THRESH(desc_n);
 	tmpl->rxq.elts = (struct rte_mbuf *(*)[])(tmpl + 1);
diff --git a/drivers/net/mlx5/mlx5_trigger.c b/drivers/net/mlx5/mlx5_trigger.c
index a070aaecfd..ac966c51b4 100644
--- a/drivers/net/mlx5/mlx5_trigger.c
+++ b/drivers/net/mlx5/mlx5_trigger.c
@@ -116,6 +116,27 @@ mlx5_txq_start(struct rte_eth_dev *dev)
 	return -rte_errno;
 }
 
+static struct rte_mbuf *
+mlx5_alloc_null_mbuf(uint32_t data_len)
+{
+	size_t alloc_size = sizeof(struct rte_mbuf) + RTE_PKTMBUF_HEADROOM +
+		rte_align32pow2(data_len);
+	struct rte_mbuf *m;
+
+	m = mlx5_malloc(MLX5_MEM_ZERO, alloc_size, 0, SOCKET_ID_ANY);
+	if (m == NULL)
+		return NULL;
+	m->buf_addr = RTE_PTR_ADD(m, sizeof(*m));
+	m->buf_len = alloc_size - sizeof(*m);
+	rte_mbuf_iova_set(m, rte_mem_virt2iova(m->buf_addr));
+	m->data_off = RTE_PKTMBUF_HEADROOM;
+	m->refcnt = 1;
+	m->nb_segs = 1;
+	m->port = RTE_MBUF_PORT_INVALID;
+	m->pool = NULL;
+	return m;
+}
+
 /**
  * Register Rx queue mempools and fill the Rx queue cache.
  * This function tolerates repeated mempool registration.
@@ -130,7 +151,8 @@ static int
 mlx5_rxq_mempool_register(struct mlx5_rxq_ctrl *rxq_ctrl)
 {
 	struct rte_mempool *mp;
-	uint32_t s;
+	struct mlx5_eth_rxseg *seg;
+	uint16_t s;
 	int ret = 0;
 
 	mlx5_mr_flush_local_cache(&rxq_ctrl->rxq.mr_ctrl);
@@ -139,21 +161,35 @@ mlx5_rxq_mempool_register(struct mlx5_rxq_ctrl *rxq_ctrl)
 		return mlx5_mr_mempool_populate_cache(&rxq_ctrl->rxq.mr_ctrl,
 						      rxq_ctrl->rxq.mprq_mp);
 	for (s = 0; s < rxq_ctrl->rxq.rxseg_n; s++) {
-		bool is_extmem;
-
-		mp = rxq_ctrl->rxq.rxseg[s].mp;
-		is_extmem = (rte_pktmbuf_priv_flags(mp) &
-			     RTE_PKTMBUF_POOL_F_PINNED_EXT_BUF) != 0;
-		ret = mlx5_mr_mempool_register(rxq_ctrl->sh->cdev, mp,
-					       is_extmem);
-		if (ret < 0 && rte_errno != EEXIST)
-			return ret;
-		ret = mlx5_mr_mempool_populate_cache(&rxq_ctrl->rxq.mr_ctrl,
-						     mp);
-		if (ret < 0)
-			return ret;
+		seg = &rxq_ctrl->rxq.rxseg[s];
+		mp = seg->mp;
+		if (mp) { /* Regular segment */
+			bool is_extmem = (rte_pktmbuf_priv_flags(mp) &
+					RTE_PKTMBUF_POOL_F_PINNED_EXT_BUF) != 0;
+			ret = mlx5_mr_mempool_register(rxq_ctrl->sh->cdev, mp, is_extmem);
+			if (ret < 0 && rte_errno != EEXIST)
+				goto error;
+			ret = mlx5_mr_mempool_populate_cache(&rxq_ctrl->rxq.mr_ctrl, mp);
+			if (ret < 0)
+				goto error;
+		} else { /* NULL segment used in selective Rx */
+			seg->null_mbuf = mlx5_alloc_null_mbuf(seg->length);
+			if (seg->null_mbuf == NULL) {
+				rte_errno = ENOMEM;
+				ret = -rte_errno;
+				goto error;
+			}
+		}
 	}
 	return 0;
+
+error:
+	while (s-- > 0) {
+		seg = &rxq_ctrl->rxq.rxseg[s];
+		mlx5_free(seg->null_mbuf);
+		seg->null_mbuf = NULL;
+	}
+	return ret;
 }
 
 /**
-- 
2.54.0


^ permalink raw reply related

* [PATCH v9 05/10] net/mlx5: fix Rx split segment counter type
From: Thomas Monjalon @ 2026-06-05 23:33 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, stable, Dariusz Sosnowski,
	Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
	Matan Azrad
In-Reply-To: <20260605233456.3017423-1-thomas@monjalon.net>

In the API, rx_nseg and max_nseg are uint16_t.
In mlx5, MLX5_MAX_RXQ_NSEG is 32.
So there is no reason to have rxseg_n as uint32_t.
Reduce the fields to uint16_t and move them to avoid struct holes.

Fixes: 9f209b59c8b0 ("net/mlx5: support Rx buffer split description")
Fixes: 572c9d4bda08 ("net/mlx5: fix shared Rx queue segment configuration match")
Cc: stable@dpdk.org

Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 drivers/net/mlx5/mlx5_rx.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/mlx5/mlx5_rx.h b/drivers/net/mlx5/mlx5_rx.h
index dffab3955b..01b563d981 100644
--- a/drivers/net/mlx5/mlx5_rx.h
+++ b/drivers/net/mlx5/mlx5_rx.h
@@ -164,9 +164,9 @@ struct __rte_cache_aligned mlx5_rxq_data {
 	uint64_t flow_meta_mask;
 	int32_t flow_meta_offset;
 	uint32_t flow_meta_port_mask;
-	uint32_t rxseg_n; /* Number of split segment descriptions. */
 	struct mlx5_eth_rxseg rxseg[MLX5_MAX_RXQ_NSEG];
 	/* Buffer split segment descriptions - sizes, offsets, pools. */
+	uint16_t rxseg_n; /* Number of split segment descriptions. */
 	uint16_t rq_win_cnt; /* Number of packets in the sliding window data. */
 	uint16_t rq_win_idx_mask; /* Sliding window index wrapping mask. */
 	uint16_t rq_win_idx; /* Index of the first element in sliding window. */
@@ -191,9 +191,9 @@ struct mlx5_rxq_ctrl {
 	unsigned int irq:1; /* Whether IRQ is enabled. */
 	uint32_t flow_tunnels_n[MLX5_FLOW_TUNNEL]; /* Tunnels counters. */
 	uint32_t wqn; /* WQ number. */
-	uint32_t rxseg_n; /* Number of split segment descriptions. */
 	struct rte_eth_rxseg_split rxseg[MLX5_MAX_RXQ_NSEG];
 	/* Saved original buffer split segment configuration. */
+	uint16_t rxseg_n; /* Number of split segment descriptions. */
 	uint16_t dump_file_n; /* Number of dump files. */
 };
 
-- 
2.54.0


^ permalink raw reply related

* [PATCH v9 04/10] common/mlx5: add null MR functions
From: Thomas Monjalon @ 2026-06-05 23:33 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, Gregory Etelson, Dariusz Sosnowski,
	Viacheslav Ovsiienko, Bing Zhao, Ori Kam, Suanming Mou,
	Matan Azrad
In-Reply-To: <20260605233456.3017423-1-thomas@monjalon.net>

From: Gregory Etelson <getelson@nvidia.com>

Add functions to allocate and free a null Memory Region (MR)
using ibverbs on Linux.

There is no implementation for DevX on Windows.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 drivers/common/mlx5/linux/mlx5_common_verbs.c | 35 +++++++++++++++++++
 drivers/common/mlx5/mlx5_common_mr.h          |  9 +++++
 drivers/common/mlx5/windows/mlx5_common_os.c  | 16 +++++++++
 3 files changed, 60 insertions(+)

diff --git a/drivers/common/mlx5/linux/mlx5_common_verbs.c b/drivers/common/mlx5/linux/mlx5_common_verbs.c
index 2322d9d033..6d44e1f566 100644
--- a/drivers/common/mlx5/linux/mlx5_common_verbs.c
+++ b/drivers/common/mlx5/linux/mlx5_common_verbs.c
@@ -161,3 +161,38 @@ mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb)
 	*reg_mr_cb = mlx5_common_verbs_reg_mr;
 	*dereg_mr_cb = mlx5_common_verbs_dereg_mr;
 }
+
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_alloc_null_mr)
+struct mlx5_pmd_mr *
+mlx5_os_alloc_null_mr(struct rte_device *dev, void *pd)
+{
+	struct ibv_mr *ibv_mr;
+	struct mlx5_pmd_mr *null_mr;
+
+	null_mr = mlx5_malloc(MLX5_MEM_ZERO, sizeof(*null_mr), 0, dev->numa_node);
+	if (!null_mr)
+		return NULL;
+	ibv_mr = mlx5_glue->alloc_null_mr(pd);
+	if (!ibv_mr) {
+		mlx5_free(null_mr);
+		return NULL;
+	}
+	*null_mr = (struct mlx5_pmd_mr) {
+		.lkey = rte_cpu_to_be_32(ibv_mr->lkey),
+		.addr = ibv_mr->addr,
+		.len = ibv_mr->length,
+		.obj = (void *)ibv_mr,
+	};
+	return null_mr;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_free_null_mr)
+void
+mlx5_os_free_null_mr(struct mlx5_pmd_mr *null_mr)
+{
+	if (!null_mr)
+		return;
+	if (null_mr->obj)
+		claim_zero(mlx5_glue->dereg_mr(null_mr->obj));
+	mlx5_free(null_mr);
+}
diff --git a/drivers/common/mlx5/mlx5_common_mr.h b/drivers/common/mlx5/mlx5_common_mr.h
index cf7c685e9b..00f3d832c3 100644
--- a/drivers/common/mlx5/mlx5_common_mr.h
+++ b/drivers/common/mlx5/mlx5_common_mr.h
@@ -21,6 +21,8 @@
 #include "mlx5_common_mp.h"
 #include "mlx5_common_defs.h"
 
+struct rte_device;
+
 /* mlx5 PMD MR struct. */
 struct mlx5_pmd_mr {
 	uint32_t	     lkey;
@@ -258,6 +260,13 @@ __rte_internal
 void
 mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb);
 
+__rte_internal
+struct mlx5_pmd_mr *
+mlx5_os_alloc_null_mr(struct rte_device *dev, void *pd);
+__rte_internal
+void
+mlx5_os_free_null_mr(struct mlx5_pmd_mr *null_mr);
+
 __rte_internal
 int
 mlx5_mr_mempool_register(struct mlx5_common_device *cdev,
diff --git a/drivers/common/mlx5/windows/mlx5_common_os.c b/drivers/common/mlx5/windows/mlx5_common_os.c
index a3033f5305..fb2bbae578 100644
--- a/drivers/common/mlx5/windows/mlx5_common_os.c
+++ b/drivers/common/mlx5/windows/mlx5_common_os.c
@@ -454,6 +454,22 @@ mlx5_os_set_reg_mr_cb(mlx5_reg_mr_t *reg_mr_cb, mlx5_dereg_mr_t *dereg_mr_cb)
 	*dereg_mr_cb = mlx5_os_dereg_mr;
 }
 
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_alloc_null_mr)
+struct mlx5_pmd_mr *
+mlx5_os_alloc_null_mr(struct rte_device *dev, void *pd)
+{
+	RTE_SET_USED(dev);
+	RTE_SET_USED(pd);
+	return NULL;
+}
+
+RTE_EXPORT_INTERNAL_SYMBOL(mlx5_os_free_null_mr)
+void
+mlx5_os_free_null_mr(struct mlx5_pmd_mr *null_mr)
+{
+	RTE_SET_USED(null_mr);
+}
+
 /*
  * In Windows, no need to wrap the MR, no known issue for it in kernel.
  * Use the regular function to create direct MR.
-- 
2.54.0


^ permalink raw reply related

* [PATCH v9 03/10] app/testpmd: support selective Rx
From: Thomas Monjalon @ 2026-06-05 23:33 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Gregory Etelson, Aman Singh
In-Reply-To: <20260605233456.3017423-1-thomas@monjalon.net>

From: Gregory Etelson <getelson@nvidia.com>

Add support for selective Rx using existing rxpkts and mbuf-size
command line parameters.

When a segment is specified with rxpkts and a matching 0 mbuf-size
on PMDs supporting selective Rx,
testpmd set the mempool of the segment to NULL,
meaning the segment won't be received.

Example usage to receive only Ethernet header and 64 bytes at offset 128:

  --rxpkts=14,114,64,0 --mbuf-size=256,0,256,0

This creates segments:
- [0-13]: 14 bytes with mempool (received)
- [14-127]: 114 bytes with NULL mempool (discarded)
- [128-191]: 64 bytes with mempool (received)
- [192-max]: remaining bytes with NULL mempool (discarded)

If the first segment has no mempool,
there will be no mempool created with the index 0.
That's why the lookup of the first mempool is now achieved
in the new function mbuf_pool_find_first(socket)
instead of mbuf_pool_find(socket, index 0)

Note: RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT is required for this feature
and is checked at ethdev API level.
This check is removed from testpmd to allow negative testing of the API.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 app/test-pmd/cmdline.c                      |  2 +-
 app/test-pmd/parameters.c                   |  5 +--
 app/test-pmd/testpmd.c                      | 48 +++++++++++++--------
 app/test-pmd/testpmd.h                      | 16 +++++++
 doc/guides/testpmd_app_ug/run_app.rst       | 16 +++++++
 doc/guides/testpmd_app_ug/testpmd_funcs.rst |  3 +-
 6 files changed, 66 insertions(+), 24 deletions(-)

diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index cc9c462498..3c39e27aa8 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -3076,7 +3076,7 @@ cmd_setup_rxtx_queue_parsed(
 		if (!numa_support || socket_id == NUMA_NO_CONFIG)
 			socket_id = port->socket_id;
 
-		mp = mbuf_pool_find(socket_id, 0);
+		mp = mbuf_pool_find_first(socket_id);
 		if (mp == NULL) {
 			fprintf(stderr,
 				"Failed to setup RX queue: No mempool allocation on the socket %d\n",
diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c
index ecbd618f00..337d8fc8ac 100644
--- a/app/test-pmd/parameters.c
+++ b/app/test-pmd/parameters.c
@@ -1170,10 +1170,9 @@ launch_args_parse(int argc, char** argv)
 				rte_exit(EXIT_FAILURE,
 					"bad mbuf-size\n");
 			for (i = 0; i < nb_segs; i++) {
-				if (mb_sz[i] <= 0 || mb_sz[i] > 0xFFFF)
+				if (mb_sz[i] > 0xFFFF)
 					rte_exit(EXIT_FAILURE,
-						"mbuf-size should be "
-						"> 0 and < 65536\n");
+						"mbuf-size should be < 65536\n");
 				mbuf_data_size[i] = (uint16_t) mb_sz[i];
 			}
 			mbuf_data_size_n = nb_segs;
diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c
index a9b35f530a..fcd8a90967 100644
--- a/app/test-pmd/testpmd.c
+++ b/app/test-pmd/testpmd.c
@@ -1806,19 +1806,25 @@ init_config(void)
 		uint8_t i, j;
 
 		for (i = 0; i < num_sockets; i++)
-			for (j = 0; j < mbuf_data_size_n; j++)
+			for (j = 0; j < mbuf_data_size_n; j++) {
+				if (mbuf_data_size[j] == 0)
+					continue;
 				mempools[i * MAX_SEGS_BUFFER_SPLIT + j] =
 					mbuf_pool_create(mbuf_data_size[j],
 							  nb_mbuf_per_pool,
 							  socket_ids[i], j);
+			}
 	} else {
 		uint8_t i;
 
-		for (i = 0; i < mbuf_data_size_n; i++)
+		for (i = 0; i < mbuf_data_size_n; i++) {
+			if (mbuf_data_size[i] == 0)
+				continue;
 			mempools[i] = mbuf_pool_create
 					(mbuf_data_size[i],
 					 nb_mbuf_per_pool,
 					 SOCKET_ID_ANY, i);
+		}
 	}
 
 	init_port_config();
@@ -1831,11 +1837,11 @@ init_config(void)
 	 * Records which Mbuf pool to use by each logical core, if needed.
 	 */
 	for (lc_id = 0; lc_id < nb_lcores; lc_id++) {
-		mbp = mbuf_pool_find(
-			rte_lcore_to_socket_id(fwd_lcores_cpuids[lc_id]), 0);
+		mbp = mbuf_pool_find_first(
+			rte_lcore_to_socket_id(fwd_lcores_cpuids[lc_id]));
 
 		if (mbp == NULL)
-			mbp = mbuf_pool_find(0, 0);
+			mbp = mbuf_pool_find_first(0);
 		fwd_lcores[lc_id]->mbp = mbp;
 #ifdef RTE_LIB_GSO
 		/* initialize GSO context */
@@ -2744,31 +2750,35 @@ rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,
 	uint32_t prev_hdrs = 0;
 	int ret;
 
-	if ((rx_pkt_nb_segs > 1) &&
-	    (rx_conf->offloads & RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT)) {
+	if (multi_rx_mempool == 0 &&
+	    (rx_pkt_nb_segs > 1 || mbuf_data_size_n > 1)) {
+		unsigned int nb_segs = RTE_MAX(rx_pkt_nb_segs, (uint8_t)mbuf_data_size_n);
+
 		/* multi-segment configuration */
-		for (i = 0; i < rx_pkt_nb_segs; i++) {
+		for (i = 0; i < nb_segs; i++) {
 			struct rte_eth_rxseg_split *rx_seg = &rx_useg[i].split;
 			/*
 			 * Use last valid pool for the segments with number
 			 * exceeding the pool index.
 			 */
 			mp_n = (i >= mbuf_data_size_n) ? mbuf_data_size_n - 1 : i;
-			mpx = mbuf_pool_find(socket_id, mp_n);
-			/* Handle zero as mbuf data buffer size. */
 			rx_seg->offset = i < rx_pkt_nb_offs ?
 					   rx_pkt_seg_offsets[i] : 0;
-			rx_seg->mp = mpx ? mpx : mp;
+			if (mbuf_data_size[mp_n] == 0) {
+				rx_seg->mp = NULL;
+			} else {
+				mpx = mbuf_pool_find(socket_id, mp_n);
+				rx_seg->mp = mpx ? mpx : mp;
+			}
 			if (rx_pkt_hdr_protos[i] != 0 && rx_pkt_seg_lengths[i] == 0) {
 				rx_seg->proto_hdr = rx_pkt_hdr_protos[i] & ~prev_hdrs;
 				prev_hdrs |= rx_seg->proto_hdr;
 			} else {
-				rx_seg->length = rx_pkt_seg_lengths[i] ?
-						rx_pkt_seg_lengths[i] :
-						mbuf_data_size[mp_n];
+				rx_seg->length = i < rx_pkt_nb_segs ?
+						rx_pkt_seg_lengths[i] : 0;
 			}
 		}
-		rx_conf->rx_nseg = rx_pkt_nb_segs;
+		rx_conf->rx_nseg = nb_segs;
 		rx_conf->rx_seg = rx_useg;
 		rx_conf->rx_mempools = NULL;
 		rx_conf->rx_nmempool = 0;
@@ -3126,8 +3136,8 @@ start_port(portid_t pid)
 				if ((numa_support) &&
 					(rxring_numa[pi] != NUMA_NO_CONFIG)) {
 					struct rte_mempool * mp =
-						mbuf_pool_find
-							(rxring_numa[pi], 0);
+						mbuf_pool_find_first
+							(rxring_numa[pi]);
 					if (mp == NULL) {
 						fprintf(stderr,
 							"Failed to setup RX queue: No mempool allocation on the socket %d\n",
@@ -3142,9 +3152,9 @@ start_port(portid_t pid)
 					     mp);
 				} else {
 					struct rte_mempool *mp =
-						mbuf_pool_find
+						mbuf_pool_find_first
 							((numa_support ? port->socket_id :
-							(unsigned int)SOCKET_ID_ANY), 0);
+							(unsigned int)SOCKET_ID_ANY));
 					if (mp == NULL) {
 						fprintf(stderr,
 							"Failed to setup RX queue: No mempool allocation on the socket %d\n",
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index 1a54535470..3d4b36d668 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -895,6 +895,22 @@ mbuf_pool_find(unsigned int sock_id, uint16_t idx)
 	return rte_mempool_lookup((const char *)pool_name);
 }
 
+static inline struct rte_mempool *
+mbuf_pool_find_first(unsigned int sock_id)
+{
+	struct rte_mempool *mp;
+	uint16_t idx;
+
+	for (idx = 0; idx < mbuf_data_size_n; idx++) {
+		if (mbuf_data_size[idx] == 0) /* no mempool with this index */
+			continue;
+		mp = mbuf_pool_find(sock_id, idx);
+		if (mp != NULL)
+			return mp;
+	}
+	return NULL;
+}
+
 static inline uint16_t
 common_fwd_stream_receive(struct fwd_stream *fs, struct rte_mbuf **burst,
 	unsigned int nb_pkts)
diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst
index 1a4a4b6c12..d654484546 100644
--- a/doc/guides/testpmd_app_ug/run_app.rst
+++ b/doc/guides/testpmd_app_ug/run_app.rst
@@ -127,6 +127,7 @@ The command line options are:
     The default value is 2048. If multiple mbuf-size values are specified the
     extra memory pools will be created for allocating mbufs to receive packets
     with buffer splitting features.
+    A value of 0 indicates a discarded segment in buffer split.
 
 *   ``--total-num-mbufs=N``
 
@@ -372,6 +373,21 @@ The command line options are:
     Optionally the multiple memory pools can be specified with --mbuf-size
     command line parameter and the mbufs to receive will be allocated
     sequentially from these extra memory pools.
+    A length of 0 means maximum length: rest of the segment
+    or all remaining packet data in case of a discard segment.
+
+    To receive only the Ethernet header (14 bytes)
+    and a 64-byte segment starting at offset 128,
+    while discarding the rest::
+
+       --rxpkts=14,114,64,0 --mbuf-size=256,0,256,0
+
+    This configuration will:
+
+    * Receive 14 bytes (Ethernet header)
+    * Discard 114 bytes (NULL mempool segment)
+    * Receive 64 bytes
+    * Discard remaining bytes (NULL mempool segment, length=0)
 
 *   ``--txpkts=X[,Y]``
 
diff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
index d50921258a..f0f2b0758b 100644
--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst
+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst
@@ -850,7 +850,8 @@ mbuf for remaining segments will be allocated from the last valid pool).
    testpmd> set rxpkts (x[,y]*)
 
 Where x[,y]* represents a CSV list of values, without white space. Zero value
-means to use the corresponding memory pool data buffer size.
+means to use the corresponding memory pool data buffer size,
+or to discard all remaining packet data for a discard segment (mbuf-size=0).
 
 set rxhdrs
 ~~~~~~~~~~
-- 
2.54.0


^ permalink raw reply related

* [PATCH v9 02/10] ethdev: introduce selective Rx
From: Thomas Monjalon @ 2026-06-05 23:33 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Gregory Etelson, Andrew Rybchenko, Aman Singh
In-Reply-To: <20260605233456.3017423-1-thomas@monjalon.net>

From: Gregory Etelson <getelson@nvidia.com>

Receiving an entire packet is not always needed.
The Rx performance can be improved by receiving only partial data
and safely discard the rest of the packet data,
because it reduces the PCI bandwidth and the memory consumption.

Selective Rx allows an application to receive
only pre-configured packet segments and discard the rest.
For example:
- Deliver the first N bytes only.
- Deliver the last N bytes only.
- Deliver N1 bytes from offset Off1 and N2 bytes from offset Off2.

Selective Rx is implemented on top of the Rx buffer split API:
- rte_eth_rxseg_split uses the null mempool for segments
that should be discarded.
- the PMD does not create mbuf segments if no data read.

For example: Deliver Ethernet header only

Rx queue segments configuration:
struct rte_eth_rxseg_split split[2] = {
    {
        .mp = <some mempool>,
        .length = sizeof(struct rte_ether_hdr)
    },
    {
        .mp = NULL, /* discard data */
        .length = 0 /* default to buffer size */
    }
};

Received mbuf:
    pkt_len = sizeof(struct rte_ether_hdr);
    data_len = sizeof(struct rte_ether_hdr);
    next = NULL; /* The next segment did not deliver data */

After selective Rx, the mbuf packet length reflects only the data
that was actually received,
and can be less than the original wire packet length.

A PMD activates the selective Rx capability by setting
the rte_eth_rxseg_capa.selective_rx bit.

This new capability bit is inserted in a bitmap hole
of the struct rte_eth_rxseg_capa,
but it needs to be ignored in the ABI check as libabigail sees a change.

Signed-off-by: Gregory Etelson <getelson@nvidia.com>
Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
Reviewed-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
---
 app/test-pmd/config.c                  |  1 +
 devtools/libabigail.abignore           |  7 +++++++
 doc/guides/nics/features.rst           | 14 ++++++++++++++
 doc/guides/nics/features/default.ini   |  1 +
 doc/guides/rel_notes/release_26_07.rst |  7 +++++++
 lib/ethdev/rte_ethdev.c                | 24 ++++++++++++++++--------
 lib/ethdev/rte_ethdev.h                | 17 +++++++++++++++--
 7 files changed, 61 insertions(+), 10 deletions(-)

diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index 55d1c6d696..9d457ca88e 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -925,6 +925,7 @@ port_infos_display(portid_t port_id)
 		print_bool_capa("\tBuffer offset", dev_info.rx_seg_capa.offset_allowed);
 		printf("\tOffset alignment: %u\n",
 				RTE_BIT32(dev_info.rx_seg_capa.offset_align_log2));
+		print_bool_capa("\tSelective Rx", dev_info.rx_seg_capa.selective_rx);
 	}
 
 	if (dev_info.max_vfs)
diff --git a/devtools/libabigail.abignore b/devtools/libabigail.abignore
index 21b8cd6113..2a0efd718e 100644
--- a/devtools/libabigail.abignore
+++ b/devtools/libabigail.abignore
@@ -33,3 +33,10 @@
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Temporary exceptions till next major ABI version ;
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+; Ignore new bit selective_rx in rte_eth_rxseg_capa bitmap hole
+[suppress_type]
+        name = rte_eth_rxseg_capa
+        type_kind = struct
+        has_size_change = no
+        has_data_member_inserted_at = 6
diff --git a/doc/guides/nics/features.rst b/doc/guides/nics/features.rst
index a075c057ec..26357036ca 100644
--- a/doc/guides/nics/features.rst
+++ b/doc/guides/nics/features.rst
@@ -199,6 +199,20 @@ Scatters the packets being received on specified boundaries to segmented mbufs.
 * **[related] API**: ``rte_eth_rx_queue_setup()``, ``rte_eth_buffer_split_get_supported_hdr_ptypes()``.
 
 
+.. _nic_features_selective_rx:
+
+Selective Rx
+------------
+
+Discards some segments of buffer split on Rx.
+
+* **[uses]     rte_eth_rxconf,rte_eth_rxmode**: ``offloads:RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT``.
+* **[uses]     rte_eth_rxconf**: ``rx_seg.mp = NULL`` to discard segments.
+* **[provides] rte_eth_dev_info**: ``rx_offload_capa:RTE_ETH_RX_OFFLOAD_BUFFER_SPLIT``.
+* **[provides] rte_eth_dev_info**: ``rx_seg_capa.selective_rx``.
+* **[related]  API**: ``rte_eth_rx_queue_setup()``.
+
+
 .. _nic_features_lro:
 
 LRO
diff --git a/doc/guides/nics/features/default.ini b/doc/guides/nics/features/default.ini
index e50514d750..8303a530c1 100644
--- a/doc/guides/nics/features/default.ini
+++ b/doc/guides/nics/features/default.ini
@@ -25,6 +25,7 @@ Burst mode info      =
 Power mgmt address monitor =
 MTU update           =
 Buffer split on Rx   =
+Selective Rx         =
 Scattered Rx         =
 LRO                  =
 TSO                  =
diff --git a/doc/guides/rel_notes/release_26_07.rst b/doc/guides/rel_notes/release_26_07.rst
index d2563ac503..46a8fe2cc1 100644
--- a/doc/guides/rel_notes/release_26_07.rst
+++ b/doc/guides/rel_notes/release_26_07.rst
@@ -87,6 +87,13 @@ New Features
 
   Added no-IOMMU mode for devices without or not enabling IOMMU/SVA.
 
+* **Added selective Rx in ethdev API.**
+
+  Some parts of packets may be discarded in Rx
+  by configuring a split of packets received in a queue,
+  and assigning no mempool to some configuration segments.
+  This is a driver capability advertised in the ``selective_rx`` bit.
+
 * **Added LinkData sxe2 ethernet driver.**
 
   Added network driver for the LinkData network adapters.
diff --git a/lib/ethdev/rte_ethdev.c b/lib/ethdev/rte_ethdev.c
index ce0407b67f..9efeaf77cb 100644
--- a/lib/ethdev/rte_ethdev.c
+++ b/lib/ethdev/rte_ethdev.c
@@ -2129,7 +2129,7 @@ rte_eth_rx_queue_check_split(uint16_t port_id,
 			const struct rte_eth_dev_info *dev_info)
 {
 	const struct rte_eth_rxseg_capa *seg_capa = &dev_info->rx_seg_capa;
-	struct rte_mempool *mp_first;
+	struct rte_mempool *mp_first = NULL;
 	uint32_t offset_mask;
 	uint16_t seg_idx;
 	int ret = 0;
@@ -2148,7 +2148,6 @@ rte_eth_rx_queue_check_split(uint16_t port_id,
 	 * Check the sizes and offsets against buffer sizes
 	 * for each segment specified in extended configuration.
 	 */
-	mp_first = rx_seg[0].mp;
 	offset_mask = RTE_BIT32(seg_capa->offset_align_log2) - 1;
 
 	ptypes = NULL;
@@ -2160,13 +2159,17 @@ rte_eth_rx_queue_check_split(uint16_t port_id,
 		uint32_t offset = rx_seg[seg_idx].offset;
 		uint32_t proto_hdr = rx_seg[seg_idx].proto_hdr;
 
-		if (mpl == NULL) {
-			RTE_ETHDEV_LOG_LINE(ERR, "null mempool pointer");
-			ret = -EINVAL;
-			goto out;
+		if (mpl == NULL) { /* discarded segment */
+			if (seg_capa->selective_rx == 0) { /* not supported */
+				RTE_ETHDEV_LOG_LINE(ERR, "null mempool pointer");
+				ret = -EINVAL;
+				goto out;
+			}
+			continue; /* next checks are not relevant if no mempool */
 		}
-		if (seg_idx != 0 && mp_first != mpl &&
-		    seg_capa->multi_pools == 0) {
+		if (mp_first == NULL)
+			mp_first = mpl;
+		if (mp_first != mpl && seg_capa->multi_pools == 0) {
 			RTE_ETHDEV_LOG_LINE(ERR, "Receiving to multiple pools is not supported");
 			ret = -ENOTSUP;
 			goto out;
@@ -2233,6 +2236,11 @@ rte_eth_rx_queue_check_split(uint16_t port_id,
 		if (ret != 0)
 			goto out;
 	}
+	if (mp_first == NULL) {
+		RTE_ETHDEV_LOG_LINE(ERR, "At least one Rx segment must have a mempool");
+		ret = -EINVAL;
+		goto out;
+	}
 out:
 	free(ptypes);
 	return ret;
diff --git a/lib/ethdev/rte_ethdev.h b/lib/ethdev/rte_ethdev.h
index dedbc05554..ee400b386f 100644
--- a/lib/ethdev/rte_ethdev.h
+++ b/lib/ethdev/rte_ethdev.h
@@ -1073,6 +1073,7 @@ struct rte_eth_txmode {
  * - The first network buffer will be allocated from the memory pool,
  *   specified in the first array element, the second buffer, from the
  *   pool in the second element, and so on.
+ *   If the pool is NULL, the segment will be discarded, i.e. not received.
  *
  * - The proto_hdrs in the elements define the split position of
  *   received packets.
@@ -1090,7 +1091,8 @@ struct rte_eth_txmode {
  *
  * - If the length in the segment description element is zero
  *   the actual buffer size will be deduced from the appropriate
- *   memory pool properties.
+ *   memory pool properties, or from the remaining packet length
+ *   in case of no memory pool to discard the end of the packet.
  *
  * - If there is not enough elements to describe the buffer for entire
  *   packet of maximal length the following parameters will be used
@@ -1121,7 +1123,15 @@ struct rte_eth_txmode {
  *   The rest will be put into the last valid pool.
  */
 struct rte_eth_rxseg_split {
-	struct rte_mempool *mp; /**< Memory pool to allocate segment from. */
+	/**
+	 * Memory pool to allocate segment from.
+	 *
+	 * NULL means discarded segment.
+	 * Length of discarded segment is not reflected in mbuf packet length
+	 * and not accounted in ibytes statistics.
+	 * @see rte_eth_rxseg_capa::selective_rx
+	 */
+	struct rte_mempool *mp;
 	uint16_t length; /**< Segment data length, configures split point. */
 	uint16_t offset; /**< Data offset from beginning of mbuf data buffer. */
 	/**
@@ -1752,12 +1762,15 @@ struct rte_eth_switch_info {
  * @b EXPERIMENTAL: this structure may change without prior notice.
  *
  * Ethernet device Rx buffer segmentation capabilities.
+ *
+ * @see rte_eth_rxseg_split
  */
 struct rte_eth_rxseg_capa {
 	__extension__
 	uint32_t multi_pools:1; /**< Supports receiving to multiple pools.*/
 	uint32_t offset_allowed:1; /**< Supports buffer offsets. */
 	uint32_t offset_align_log2:4; /**< Required offset alignment. */
+	uint32_t selective_rx:1; /**< Supports discarding segment. */
 	uint16_t max_nseg; /**< Maximum amount of segments to split. */
 	uint16_t reserved; /**< Reserved field. */
 };
-- 
2.54.0


^ permalink raw reply related

* [PATCH v9 01/10] app/testpmd: print Rx split capabilities
From: Thomas Monjalon @ 2026-06-05 23:33 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger, Aman Singh
In-Reply-To: <20260605233456.3017423-1-thomas@monjalon.net>

The capabilities from rte_eth_rxseg_capa are added
to the command "show port info".

Signed-off-by: Thomas Monjalon <thomas@monjalon.net>
---
 app/test-pmd/config.c | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index c950793aaf..55d1c6d696 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -790,6 +790,12 @@ rss_offload_types_display(uint64_t offload_types, uint16_t char_num_per_line)
 	printf("\n");
 }
 
+static void
+print_bool_capa(const char *label, int value)
+{
+	printf("%s: %s\n", label, value ? "supported" : "not supported");
+}
+
 void
 port_infos_display(portid_t port_id)
 {
@@ -911,6 +917,16 @@ port_infos_display(portid_t port_id)
 		dev_info.max_rx_pktlen);
 	printf("Maximum configurable size of LRO aggregated packet: %u\n",
 		dev_info.max_lro_pkt_size);
+
+	printf("Rx split:\n");
+	printf("\tMax segments: %hu\n", dev_info.rx_seg_capa.max_nseg);
+	if (dev_info.rx_seg_capa.max_nseg > 0) {
+		print_bool_capa("\tMulti-pool", dev_info.rx_seg_capa.multi_pools);
+		print_bool_capa("\tBuffer offset", dev_info.rx_seg_capa.offset_allowed);
+		printf("\tOffset alignment: %u\n",
+				RTE_BIT32(dev_info.rx_seg_capa.offset_align_log2));
+	}
+
 	if (dev_info.max_vfs)
 		printf("Maximum number of VFs: %u\n", dev_info.max_vfs);
 	if (dev_info.max_vmdq_pools)
-- 
2.54.0


^ permalink raw reply related

* [PATCH v9 00/10] selective Rx
From: Thomas Monjalon @ 2026-06-05 23:33 UTC (permalink / raw)
  To: dev; +Cc: Stephen Hemminger
In-Reply-To: <20260202160903.254621-1-getelson@nvidia.com>

This is a new feature in ethdev with tests and mlx5 implementation.
Selective Rx allows to receive partial data,
saving some hardware bandwidth.

v2: rework after Gregory
v3: fix bugs found with AI by Stephen
v4: fix packet type in DTS test
v5: fix mlx5 Rx to handle discarding first segment
v6: fix reindent patch
v7: fix mlx5 CQE error handling + outdated mcqe + redundant assignment
v8: use --mbuf-size 0 in testpmd instead of changing --rxoffs behaviour
v9: fix testpmd and DTS

Gregory Etelson (4):
  ethdev: introduce selective Rx
  app/testpmd: support selective Rx
  common/mlx5: add null MR functions
  net/mlx5: support selective Rx

Thomas Monjalon (6):
  app/testpmd: print Rx split capabilities
  net/mlx5: fix Rx split segment counter type
  common/mlx5: remove callbacks for MR registration
  dts: fix topology capability comparison
  dts: use specific types for Rx/Tx offloads
  dts: add selective Rx tests

 app/test-pmd/cmdline.c                        |   2 +-
 app/test-pmd/config.c                         |  17 ++
 app/test-pmd/parameters.c                     |   5 +-
 app/test-pmd/testpmd.c                        |  48 +--
 app/test-pmd/testpmd.h                        |  16 +
 devtools/libabigail.abignore                  |   7 +
 doc/guides/nics/features.rst                  |  14 +
 doc/guides/nics/features/default.ini          |   1 +
 doc/guides/nics/features/mlx5.ini             |   1 +
 doc/guides/nics/mlx5.rst                      |  86 ++++--
 doc/guides/rel_notes/release_26_07.rst        |  11 +
 doc/guides/testpmd_app_ug/run_app.rst         |  16 +
 doc/guides/testpmd_app_ug/testpmd_funcs.rst   |   3 +-
 drivers/common/mlx5/linux/mlx5_common_verbs.c |  53 ++--
 drivers/common/mlx5/mlx5_common.c             |   6 +-
 drivers/common/mlx5/mlx5_common_mr.c          |  37 +--
 drivers/common/mlx5/mlx5_common_mr.h          |  29 +-
 drivers/common/mlx5/windows/mlx5_common_os.c  |  31 +-
 drivers/compress/mlx5/mlx5_compress.c         |   4 +-
 drivers/crypto/mlx5/mlx5_crypto.h             |   2 -
 drivers/crypto/mlx5/mlx5_crypto_gcm.c         |   6 +-
 drivers/net/mlx5/mlx5.c                       |   7 +
 drivers/net/mlx5/mlx5.h                       |   4 +-
 drivers/net/mlx5/mlx5_ethdev.c                |  25 ++
 drivers/net/mlx5/mlx5_flow_aso.c              |  21 +-
 drivers/net/mlx5/mlx5_flow_hw.c               |  11 +-
 drivers/net/mlx5/mlx5_flow_quota.c            |   6 +-
 drivers/net/mlx5/mlx5_hws_cnt.c               |  19 +-
 drivers/net/mlx5/mlx5_rx.c                    | 187 +++++++-----
 drivers/net/mlx5/mlx5_rx.h                    |   5 +-
 drivers/net/mlx5/mlx5_rxq.c                   |  95 ++++--
 drivers/net/mlx5/mlx5_trigger.c               |  64 +++-
 dts/api/capabilities.py                       |   2 +
 dts/api/testpmd/__init__.py                   |  17 ++
 dts/api/testpmd/config.py                     |  11 +-
 dts/api/testpmd/types.py                      |   6 +
 dts/framework/params/__init__.py              |  14 +
 dts/framework/params/types.py                 |   5 +-
 dts/framework/testbed_model/capability.py     |  10 +-
 dts/tests/TestSuite_rx_split.py               | 277 ++++++++++++++++++
 lib/ethdev/rte_ethdev.c                       |  24 +-
 lib/ethdev/rte_ethdev.h                       |  17 +-
 42 files changed, 921 insertions(+), 301 deletions(-)
 create mode 100644 dts/tests/TestSuite_rx_split.py

-- 
2.54.0


^ permalink raw reply

* Re: [PATCH v8 9/9] dts: add selective Rx tests
From: Thomas Monjalon @ 2026-06-05 23:31 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: dev, Luca Vizzarro, Patrick Robb
In-Reply-To: <20260605142815.1aaf727b@phoenix.local>

Sorry I should have told you that I was working on this today.
I fully agree with the comments you sent as I was fixing it.

I'm going to send a new version which is better reviewed
and properly tested with DTS.

Results
=======
test_suites: PASS
  rx_split: PASS
    selective_rx_all_discard: PASS
    selective_rx_headers: PASS
    selective_rx_headers_discard_length: PASS
    selective_rx_no_offload: PASS
    selective_rx_payload_only: PASS
    selective_rx_segment_exceeds_mbuf: PASS
    selective_rx_two_segments: PASS

Test Cases Summary
==================
SKIP      = 0
PASS      = 7
BLOCK     = 0
FAIL      = 0
ERROR     = 0
PASS RATE = 100%



05/06/2026 23:28, Stephen Hemminger:
> AI review found:
> Patch 9 (dts: add selective Rx tests)
> 
> selective_rx_out_of_range expects a rejection that never happens, so the
> negative test will fail. It configures a real segment plus an oversized
> discard segment:
> 
> 	rx_segments_length=[ETHER_IP_HDR_LEN, 20000],
> 	mbuf_size=[256, 0],
> 
> and expects start_all_ports() to fail. But an over-range length on a discard
> segment is not rejected anywhere: rte_eth_rx_queue_check_split() does
> "continue" for mp == NULL segments, so it never length-checks them, and
> mlx5_rxq_new() clamps it:
> 
> 	if (seg_len > tail_len)
> 		seg_len = qs_seg->mp != NULL ? buf_len - offset : tail_len;
> 
> The discard seg_len becomes the remaining frame length, the queue is built,
> the port starts, and the test hits its fail().
> 
> Clamping an over-long discard to "the rest" is harmless (the bytes are
> discarded anyway), so the cleanest fix is probably to drop or rework this
> test rather than add a rejection path. If rejection is the intended
> contract, it would have to be added for discard segments in patch 2 or
> patch 6 -- a behavior choice, not a correctness requirement.
> 
> Minor: expressing a leading discard as --mbuf-size=0,... puts 0 at index 0,
> and testpmd treats mbuf_data_size[0] as the primary pool size elsewhere (the
> max_rx_pkt_len > mbuf_data_size[0] check, the default mbuf_pool_find(socket,
> 0)). Only bites an unusual config, but it is a latent foot-gun.




^ permalink raw reply

* [PATCH 2/2] common/cnxk: fix thread-unsafe NIX telemetry parsing
From: Stephen Hemminger @ 2026-06-05 22:44 UTC (permalink / raw)
  To: dev
  Cc: Stephen Hemminger, stable, Nithin Dabilpuram, Kiran Kumar K,
	Sunil Kumar Kori, Satha Rao, Harman Kalra,
	Gowrishankar Muthukrishnan, Jerin Jacob
In-Reply-To: <20260605224514.651081-1-stephen@networkplumber.org>

cnxk_nix_tel_handle_info_x() backs the /cnxk/nix/{rq,cq,sq}/{info,ctx}
telemetry commands and parsed its "<pcidev>,<queue_id>" parameter with
strtok(), which keeps non-reentrant state and races when telemetry
callbacks run on per-connection threads.

Split the parameter with strchr() and parse the queue id with strtoul().
While here, copy the full parameter (the length was capped at
PCI_PRI_STR_SIZE + 1, truncating the id for longer device addresses) and
reject non-numeric or out-of-range ids instead of letting strtol() alias
them to queue 0.

Fixes: af75aac78978 ("common/cnxk: support telemetry for NIX")
Cc: stable@dpdk.org

Signed-off-by: Stephen Hemminger <stephen@networkplumber.org>
---
 drivers/common/cnxk/cnxk_telemetry_nix.c | 80 +++++++++---------------
 1 file changed, 30 insertions(+), 50 deletions(-)

diff --git a/drivers/common/cnxk/cnxk_telemetry_nix.c b/drivers/common/cnxk/cnxk_telemetry_nix.c
index abeefafe1e..82a146c139 100644
--- a/drivers/common/cnxk/cnxk_telemetry_nix.c
+++ b/drivers/common/cnxk/cnxk_telemetry_nix.c
@@ -1015,76 +1015,56 @@ cnxk_nix_tel_handle_info_x(const char *cmd, const char *params,
 			   struct plt_tel_data *d)
 {
 	struct nix_tel_node *node;
-	char *name, *param;
 	char buf[1024];
+	char *comma, *end;
+	unsigned long qid;
 	int rc = -1;
 
-	if (params == NULL || strlen(params) == 0 || !isdigit(*params))
-		goto exit;
+	if (params == NULL || !isdigit((unsigned char)params[0]))
+		return -1;
 
-	plt_strlcpy(buf, params, PCI_PRI_STR_SIZE + 1);
-	name = strtok(buf, ",");
-	if (name == NULL)
-		goto exit;
+	plt_strlcpy(buf, params, sizeof(buf));	/* was PCI_PRI_STR_SIZE + 1 */
 
-	param = strtok(NULL, "\0");
+	/* params is "<pcidev_name>,<queue_id>" */
+	comma = strchr(buf, ',');
+	if (comma == NULL || !isdigit((unsigned char)comma[1]))
+		return -1;
+	*comma = '\0';
 
-	node = nix_tel_node_get_by_pcidev_name(name);
-	if (!node)
-		goto exit;
+	errno = 0;
+	qid = strtoul(comma + 1, &end, 10);
+	if (errno != 0 || (*end != '\0' && *end != ','))
+		return -1;
+
+	node = nix_tel_node_get_by_pcidev_name(buf);
+	if (node == NULL)
+		return -1;
 
 	plt_tel_data_start_dict(d);
 
 	if (strstr(cmd, "rq")) {
-		char *tok = strtok(param, ",");
-		int rq;
-
-		if (!tok)
-			goto exit;
-
-		rq = strtol(tok, NULL, 10);
-		if ((node->n_rq <= rq) || (rq < 0))
-			goto exit;
-
+		if (qid >= node->n_rq)
+			return -1;
 		if (strstr(cmd, "ctx"))
-			rc = cnxk_tel_nix_rq_ctx(node->nix, rq, d);
+			rc = cnxk_tel_nix_rq_ctx(node->nix, qid, d);
 		else
-			rc = cnxk_tel_nix_rq(node->rqs[rq], d);
-
+			rc = cnxk_tel_nix_rq(node->rqs[qid], d);
 	} else if (strstr(cmd, "cq")) {
-		char *tok = strtok(param, ",");
-		int cq;
-
-		if (!tok)
-			goto exit;
-
-		cq = strtol(tok, NULL, 10);
-		if ((node->n_cq <= cq) || (cq < 0))
-			goto exit;
-
+		if (qid >= node->n_cq)
+			return -1;
 		if (strstr(cmd, "ctx"))
-			rc = cnxk_tel_nix_cq_ctx(node->nix, cq, d);
+			rc = cnxk_tel_nix_cq_ctx(node->nix, qid, d);
 		else
-			rc = cnxk_tel_nix_cq(node->cqs[cq], d);
-
+			rc = cnxk_tel_nix_cq(node->cqs[qid], d);
 	} else if (strstr(cmd, "sq")) {
-		char *tok = strtok(param, ",");
-		int sq;
-
-		if (!tok)
-			goto exit;
-
-		sq = strtol(tok, NULL, 10);
-		if ((node->n_sq <= sq) || (sq < 0))
-			goto exit;
-
+		if (qid >= node->n_sq)
+			return -1;
 		if (strstr(cmd, "ctx"))
-			rc = cnxk_tel_nix_sq_ctx(node->nix, sq, d);
+			rc = cnxk_tel_nix_sq_ctx(node->nix, qid, d);
 		else
-			rc = cnxk_tel_nix_sq(node->sqs[sq], d);
+			rc = cnxk_tel_nix_sq(node->sqs[qid], d);
 	}
 
-exit:
 	return rc;
 }
 
-- 
2.53.0


^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox